本文承接上一篇分為如下幾個部分日志輸出
捕獲異常
配置共享
配置到檔案
使用規範
參考資料
日志輸出
我們之前都是将日志輸出到控制台,而實際項目中常常需要将日志存儲為檔案,我們直接看代碼
import logging
logging.basicConfig(level=logging.INFO,
format='%(asctime)s%(message)s',
datefmt='%a,%d%b %Y %H:%M:%S +0000',
filename='my.log')
logging.info('this is a info')
可以看到,隻要在logging.basicConfig中加入filename參數,就不會再在控制台中輸出日志,而是會将所有日志存入my.log檔案中。
需要注意的一點是:上面日志存儲方式是追加的,也就是說,上面這個代碼連續運作兩次,檔案中是會有兩行日志的。
如果需要改變輸出形式,需要調整參數如下
import logging
logging.basicConfig(level=logging.INFO,
format='%(asctime)s%(message)s',
datefmt='%a,%d%b %Y %H:%M:%S +0000',
filename='my.log', filemode='w')
logging.info('this is a info')
其他讀寫格式可以參考檔案讀寫格式。
指定日志輸出也可以不在logging.basicConfig中配置,而是單獨設定
import logging
logger = logging.getLogger(__name__)
logger.setLevel(level=logging.INFO)
handler = logging.FileHandler('my.log')
formatter = logging.Formatter('%(asctime)s%(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.critical('Critical 50')
logger.error('Error 40')
logger.warning('Warning 30')
logger.info('Info 20')
logger.debug('Debug 10')
這可以達到和剛才相同的效果。我們可以看到,不僅輸出方式可以單獨配置,而且format也可以單獨配置。
配置它們的流程是配置好的format設定到handler中
配置好的handler添加到logger中
添加多個輸出端,且不同輸出端輸出内容不一樣(level不同,format不同)
import logging
import sys
logger = logging.getLogger(__name__)
logger.setLevel(level=logging.DEBUG) # 設定基本level
# 輸出到控制台,不設定format
handler_stream = logging.StreamHandler(sys.stdout)
handler_stream.setLevel(level=logging.WARN) # 更改level
logger.addHandler(handler_stream)
# 輸出到檔案,繼承基礎level
handler_file = logging.FileHandler('my.log', 'w')
formatter = logging.Formatter('%(asctime)s%(message)s')
handler_file.setFormatter(formatter) # 設定format
logger.addHandler(handler_file)
logger.critical('Critical 50')
logger.error('Error 40')
logger.warning('Warning 30')
logger.info('Info 20')
logger.debug('Debug 10')
控制台輸出結果為
Critical 50
Error 40
Warning 30
my.log檔案輸出結果為
2018-07-01 21:03:27,164 Critical 50
2018-07-01 21:03:27,165 Error 40
2018-07-01 21:03:27,167 Warning 30
2018-07-01 21:03:27,171 Info 20
2018-07-01 21:03:27,172 Debug 10
這裡需要注意一點,基礎level要設定比較低一些,後面handler設定的level隻有比基礎高才有效。
logging子產品還提供了許多其他的日志輸出形式,詳情可以見官網
捕獲異常
我們希望将程式的報錯資訊記錄到log檔案中
import logging
logging.basicConfig(level=logging.INFO,
format='%(asctime)s%(message)s',
datefmt='%a,%d%b %Y %H:%M:%S +0000',
filename='my.log')
logging.info('this is a info')
try:
do
except Exception:
logging.error('There are something wrong', exc_info=True)
logging.info('continue')
輸出到檔案的結果如下
Sun, 01 Jul 2018 21:10:43 +0000 this is a info
Sun, 01 Jul 2018 21:10:53 +0000 this is a info
Sun, 01 Jul 2018 21:10:53 +0000 There are something wrong
Traceback (most recent call last):
File "learn.py", line 9, in
do
NameError: name 'do' is not defined
Sun, 01 Jul 2018 21:10:53 +0000 continue
其中exc_info的作用就是在日志中包含具體的報錯資訊,預設是False不包含。
配置共享
在寫一個項目時,需要編寫多個檔案,每個檔案内都要設定相同格式的日志輸出,如果每個檔案都重新配置一遍就太麻煩了,logging子產品為我們提供了一個簡單的方法。
比如在main.py檔案中編寫如下代碼
import logging
import a
logging.basicConfig(level=logging.INFO,
format='%(asctime)s%(message)s',
datefmt='%a,%d%b %Y %H:%M:%S +0000')
logger = logging.getLogger('mainlogger')
logger.info('main file log')
a.run()
在a.py檔案中編寫如下代碼
import logging
logger = logging.getLogger('mainlogger.a')
def run():
logger.info('a file log')
運作main.py檔案,輸出結果如下
Sun, 01 Jul 2018 21:19:57 +0000 main file log
Sun, 01 Jul 2018 21:19:57 +0000 a file log
我們可以看到,在a.py檔案中隻是将logger名稱設定為以main.py檔案中的logger名稱開頭,就可以繼承main.py檔案中的配置了,甚至不需要在a.py檔案中導入main.py檔案。
配置到檔案
我們不僅可以通過python代碼進行logging配置,而且可以通過寫一個yaml檔案進行配置,每次需要用logging時隻要調用這個檔案就配置完成。
config.yaml檔案内容如下
version: 1
formatters:
simple:
format: "%(message)s"
more:
format: "%(asctime)s - %(levelname)s - %(message)s"
handlers:
console:
class : logging.StreamHandler
formatter: simple
level: INFO
stream: ext://sys.stdout
file:
class: logging.FileHandler
formatter: more
level: DEBUG
filename: debug.log
loggers:
mainlogger:
level: DEBUG
handlers: [console, file]
root:
level: DEBUG
handlers: [console]
main.py檔案中編寫代碼如下
import logging
import logging.config
import yaml
import a
with open('config.yaml', 'r', encoding='utf-8') as f:
config = yaml.load(f)
logging.config.dictConfig(config)
logging.info('main file log')
a.run()
a.py檔案中編寫代碼如下
import logging
logger = logging.getLogger('mainlogger')
def run():
logger.info('a file log')
運作main.py結果在控制台中輸出如下
a file log
INFO:mainlogger:a file log
在debug.log檔案中輸出如下
2018-07-01 21:45:37,673 - INFO - a file log
對于這樣的輸出結果,我們回到yaml檔案理一下思路,對這個檔案從下往上看首先對于不同的logger名稱,如果名稱是mainlogger,則使用[console, file]這兩個handler,如果未指定(使用logging.info進行輸出)則對應root隻按照console輸出
對于不同的handler,名稱為console的handler使用logging.StreamHandler輸出到控制台,調用simple的format,而file則輸出到檔案,使用more的format
對于不同的format格式則在最上面的formatters中定義
通過檔案配置的更多内容可以參考官網
使用規範
1.輸出字元串的規範
import logging
logging.basicConfig(level=logging.INFO,
format='%(asctime)s%(levelname)s%(message)s')
info = 'apple'
# bad
logging.info('this is a{}'.format(info))
# good
logging.info('this is a%s', info)
二者輸出皆為
2018-07-01 21:56:12,969 INFO this is a apple
2.異常處理規範
import logging
logging.basicConfig(level=logging.INFO,
format='%(asctime)s%(message)s',
datefmt='%a,%d%b %Y %H:%M:%S +0000')
logging.info('this is a info')
try:
do
except Exception as e:
# bad
logging.error('There are something wrong:%s', e)
# good
logging.error('There are something wrong', exc_info=True)
# good
logging.exception('There are something wrong')
logging.info('continue')
第一種輸出異常結果會是
Sun, 01 Jul 2018 21:58:52 +0000 There are something wrong: name 'do' is not defined
後兩種輸出異常結果會是
Sun, 01 Jul 2018 21:58:52 +0000 There are something wrong
Traceback (most recent call last):
File "learn.py", line 8, in
do
NameError: name 'do' is not defined
3.日志級别設定
這裡列出這篇文章中認為的日志級别使用方法最細微的資訊記錄到debug中,這個級别就是用來debug的,看程式在哪一次疊代中發生了錯誤,比如每次循環都輸出一些東西用debug級别
info級别用于routines,也就是輸出start finish 狀态改變等資訊
warn輸出一些相對重要,但是不是程式bug的資訊,比如輸入了錯誤的密碼,或者連接配接較慢
error輸出程式bug,列印異常資訊
critical用于處理一些非常糟糕的事情,比如記憶體溢出、磁盤已滿,這個一般較少使用
4.建議使用RotatingFileHandler而不是FileHandler
因為日志檔案寫入是不斷增加的過程,如果用FileHandler,久了日志檔案會非常大,不利于讀寫和管理。
用RotatingFileHandler設定日志檔案大小,比如10M。則如果日志檔案達到10M,就會自動将這個檔案重命名,然後建立一個新的日志檔案繼續寫入
也可以使用TimedRotatingFileHandler,這是根據時間,每隔一段時間會自動建立新的日志檔案
5.get_logger位置
在一個被import的檔案中get_logger的代碼要放在函數裡面,因為如果放在函數外面,在主檔案import這個檔案時,就會運作這個初始化。而在import的時候,主檔案一般還沒有讀入yaml檔案,是以事先初始化的log就不會正常工作。
參考資料
專欄資訊
專欄目錄:目錄