在一個軟件中,日誌是能夠說必不可少的一個組成部分,一般會在定位客戶問題或者記錄軟件使用狀況等場景中會用到。logging模板塊是Python的一個內置標準庫,用於實現對日誌的控制輸出,對於日常的日誌輸出,甚至是系統級的日誌輸出,也均可以使用logging模塊來進行實現。html
1、使用basicConfig進行簡單的一次性配置服務器
basicConfig一次性配置,簡單示例:函數
# -*- coding:utf-8 -*- import logging import datetime # filename:設置日誌輸出文件,以天爲單位輸出到不一樣的日誌文件,以避免單個日誌文件日誌信息過多, # 日誌文件若是不存在則會自動建立,但前面的路徑如log文件夾必須存在,不然會報錯 log_file = 'log/sys_%s.log' % datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d') # level:設置日誌輸出的最低級別,即低於此級別的日誌都不會輸出 # 在平時開發測試的時候能夠設置成logging.debug以便定位問題,但正式上線後建議設置爲logging.WARNING,既能夠下降系統I/O的負荷,也能夠避免輸出過多的無用日誌信息 log_level = logging.WARNING # format:設置日誌的字符串輸出格式 log_format = '%(asctime)s[%(levelname)s]: %(message)s' logging.basicConfig(filename=log_file, level=logging.WARNING, format=log_format) logger = logging.getLogger() # 如下日誌輸出因爲level被設置爲了logging.WARNING,因此debug和info的日誌不會被輸出 logger.debug('This is a debug message!') logger.info('This is a info message!') logger.warning('This is a warning message!') logger.error('This is a error message!') logger.critical('This is a critical message!')
運行後,日誌文件sys_2019-04-14.log的內容以下:測試
2019-04-14 23:34:42,444[WARNING]: This is a warning message! 2019-04-14 23:34:42,444[ERROR]: This is a error message! 2019-04-14 23:34:42,444[CRITICAL]: This is a critical message!
logging.basicConfig:用於對logging模塊整個日誌輸出的一次性配置,也就是說屢次配置時以第一次配置的爲準,以後再使用basicConfig進行配置則無效。spa
logging.basicConfig的參數:線程
filename:設置日誌輸出的文件,默認輸出到控制檯。
filemode:設置打開日誌文件的方式,默認爲「a」,即追加。
format:設置日誌輸出的字符串格式,具體的格式有以下幾種:debug
datefmt:設置日誌時間字符串的輸出格式,默認時間字符串格式爲%Y-%m-%d %H:%M:%S。
style:設置format字符串格式化的風格,能夠是「%」,「{」或「$」,默認是「%」。
level:設置日誌的級別,具體有如下幾種(由高到低):調試
stream:指定日誌的輸出Stream,能夠是sys.stderr,sys.stdout或者是文件(即便用open函數打開的文件流,可是這個文件流logging模塊不會主動關閉它),默認是sys.stderr,若是同時指定了filename參數和stream參數,那stream參數就會被忽略。日誌
2、使用Handler將日誌同時輸出到文件和控制檯code
添加Handler打印日誌,簡單示例:
# -*- coding:utf-8 -*- import logging import datetime logger = logging.getLogger() # 設置此logger的最低日誌級別,以後添加的Handler級別若是低於這個設置,則以這個設置爲最低限制 logger.setLevel(logging.INFO) # 建立一個FileHandler,將日誌輸出到文件 log_file = 'log/sys_%s.log' % datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d') file_handler = logging.FileHandler(log_file) # 設置此Handler的最低日誌級別 file_handler.setLevel(logging.WARNING) # 設置此Handler的日誌輸出字符串格式 log_formatter = logging.Formatter('%(asctime)s[%(levelname)s]: %(message)s') file_handler.setFormatter(log_formatter) # 建立一個StreamHandler,將日誌輸出到Stream,默認輸出到sys.stderr stream_handler = logging.StreamHandler() stream_handler.setLevel(logging.INFO) # 將不一樣的Handler添加到logger中,日誌就會同時輸出到不一樣的Handler控制的輸出中 # 注意若是此logger在以前使用basicConfig進行基礎配置,由於basicConfig會自動建立一個Handler,因此此logger將會有3個Handler # 會將日誌同時輸出到3個Handler控制的輸出中 logger.addHandler(file_handler) logger.addHandler(stream_handler) # 文件中將會輸出WARNING及以上級別的日誌 # 控制檯將會輸出INFO及以上級別的日誌 logger.debug('This is a debug message!') logger.info('This is a info message!') logger.warning('This is a warning message!') logger.error('This is a error message!') logger.critical('This is a critical message!')
運行後,日誌文件sys_2019-04-15.log的內容以下:
2019-04-15 21:50:40,292[WARNING]: This is a warning message! 2019-04-15 21:50:40,292[ERROR]: This is a error message! 2019-04-15 21:50:40,293[CRITICAL]: This is a critical message!
控制檯打印的內容以下:
This is a info message! This is a warning message! This is a error message! This is a critical message!
經過給logger添加不一樣的Handler,能夠將日誌同時輸出到不一樣的地方,須要注意的是使用basicConfig進行一次性基礎配置時,會根據配置內容自動建立一個Handler,因此若是以後再添加了N個Handler,實際上總的Handler數量有N+1個。
通常在logging或者logging.handlers下就能夠你想要的Handler,不一樣的Handler會以不一樣的方式輸出到不一樣的地方,如下是幾種經常使用的Handler:
from logging import FileHandler: 以「a」(追加)的方式將日誌輸出到文件,若是文件不存在,則自動建立該文件。
from logging import StreamHandler: 將日誌輸出到Stream,好比sys.stderr、sys.stdour、文件流等。
from logging.handlers import RotatingFileHandler: 將日誌輸出到文件,能夠經過設置文件大小,文件達到上限後自動建立一個新的文件來繼續輸出文件。
from logging.handlers import TimedRotatingFileHandler: 將日誌輸出到文件,能夠經過設置時間,使日誌根據不一樣的時間自動建立並輸出到不一樣的文件中。
from logging.handlers import HTTPHandler: 將日誌發送到一個HTTP服務器。
from logging.handlers import SMTPHandler: 將日誌發送到一個指定的郵件地址。
3、使用不一樣的日誌級別輸出日誌
最低級別:logger.setLevel爲設置logger的最低日誌級別,若是handler中也設置了級別,則不能低於這個級別,低於這個級別的設置是無效的。
不一樣級別的意義:在開發或者部署應用程序時,須要儘量詳盡的信息來進行開發和調試,這時候用的比較多的是來自DEBUG和INFO級別的日誌信息,可是到了正式上線或者生產環境時,應該使用WARNING和CRITICAL級別的日誌信息以下降機器的I/O壓力和提升獲取到錯誤信息的效率。
日誌量:日誌的信息量應該是與日誌級別成反比的:DEBUG>INFO>WARNING>ERROR>CRITICAL。
不一樣級別的設置:只有日誌級別大於或等於指定日誌級別的日誌纔會被輸出,因此在程序中能夠設置一個比較高的日誌級別,好比WARNING,開發和調試的時候修改成DEBUG或INFO以便獲得更多的日誌信息,正式上線的時候這些日誌仍是保持WARNING的級別。
4、輸出traceback.format_exc異常信息
能夠在打印指定信息的同時在下一行打印出traceback.format_exc異常信息,如:logger.error(msg, exc_info=True)。若是指定了exc_info爲True來打印錯誤消息,可是沒有發生錯誤的話,就會在消息的下一行打印「NoneType: None」。
注:對於參數exc_info,其實info,debug等函數也有這個關鍵字參數,可是不推薦使用這些低級別的打印函數來打印錯誤消息。
5、配置日誌:配置文件和字典配置
參考文章:http://www.cnblogs.com/yyds/p/6885182.html
配置文件方式:將配置文件使用特定規則配置好對應section和option,而後經過logging.config.fileConfig函數加載並使用文件中的配置信息。
配置文件配置logging,簡單示例:
以下爲配置文件log_cfg.ini的配置內容
[loggers] keys=root, console [handlers] keys=consolehandler, filehandler [formatters] keys=consoleformatter, fileformatter [logger_root] level=DEBUG handlers=filehandler [logger_console] level=WARNING handlers=consolehandler qualname=console propagate=0 [handler_filehandler] class=FileHandler args=('log/sys.log', ) level=WARNING formatter=fileformatter [handler_consolehandler] class=logging.StreamHandler args=(sys.stdout, ) level=DEBUG formatter=consoleformatter [formatter_fileformatter] format=%(asctime)s[%(name)s][%(levelname)s]: %(message)s [formatter_consoleformatter] format=%(asctime)s[%(levelname)s]: %(message)s
以下爲py代碼
# -*- coding:utf-8 -*- import logging.config # 加載配置文件 logging.config.fileConfig('log_cfg.ini') # 獲取配置好的logger logger = logging.getLogger('console') logger.debug('This is a debug message!') logger.info('This is a info message!') logger.warning('This is a warning message!') logger.error('This is a error message!') logger.critical('This is a critical message!') # 獲取沒有配置的logger時,會自動使用root中配置的Handler any_logger = logging.getLogger('any') any_logger.critical('This is a test message!')
運行結果:
以下爲sys.log文件內容
2019-04-21 21:39:43,029[any][CRITICAL]: This is a test message!
以下爲控制檯輸出
2019-04-21 21:39:43,029[WARNING]: This is a warning message! 2019-04-21 21:39:43,029[ERROR]: This is a error message! 2019-04-21 21:39:43,029[CRITICAL]: This is a critical message!
配置規則:
字典配置方式:將字典或者類字典的配置文件(如JSON格式的配置文件或者YAML格式的配置文件)使用特定規則配置好對應key和value,經過logging.config.dictConfig函數加載並使用裏面的配置信息。這裏須要說明的是,dictConfig只須要一個能夠解析成字典的對象便可,能夠是Python的字典或者其餘配置文件,只要能構建出這個字典就行。
以YAML格式的文件來配置字典方式配置logging,簡單示例:
以下爲配置文件log_cfg.yml的配置內容
version: 1 root: level: DEBUG handlers: [filehandler, ] loggers: console: level: WARNING handlers: [consolehandler, ] propagate: no handlers: filehandler: class: logging.FileHandler filename: log/sys.log level: WARNING formatter: fileformatter consolehandler: class: logging.StreamHandler stream: ext://sys.stdout level: DEBUG formatter: consoleformatter formatters: fileformatter: format: '%(asctime)s[%(name)s][%(levelname)s]: %(message)s' consoleformatter: format: '%(asctime)s[%(levelname)s]: %(message)s'
以下爲py代碼
# -*- coding:utf-8 -*- import logging.config import yaml # 加載配置文件 with open('log_cfg.yml') as log_cfg_file: log_dictcfg = yaml.safe_load(log_cfg_file) logging.config.dictConfig(log_dictcfg) # 獲取配置好的logger logger = logging.getLogger('console') logger.debug('This is a debug message!') logger.info('This is a info message!') logger.warning('This is a warning message!') logger.error('This is a error message!') logger.critical('This is a critical message!') # 獲取沒有配置的logger時,會自動使用root中配置的Handler,若是沒有配置root,則會使用默認的root any_logger = logging.getLogger('any') any_logger.critical('This is a test message!')
輸出結果:同上一個配置文件方式的示例的輸出是相同的。
配置規則:
6、日誌中輸出額外信息:日誌傳參、LoggerAdapter
參考文章:http://www.cnblogs.com/yyds/p/6897964.html
直接傳參,簡單示例:
ip = '127.0.0.1' username = 'Jason' # 第一個參數爲格式化消息字符串,第二個及以後的參數爲對應的變量 logger.warning('[%s][%s]This is a warning message!', ip, username)
使用extra參數,簡單示例:
logger = logging.getLogger() logger.setLevel(logging.DEBUG) console_handler = logging.StreamHandler() console_handler.setLevel(logging.WARNING) # 在formatter中定義好須要額外傳遞參數,且在以後的日誌輸出必須給出這些額外參數的字典值 console_formatter = logging.Formatter('%(asctime)s[%(ip)s][%(username)s]: %(message)s') console_handler.setFormatter(console_formatter) logger.addHandler(console_handler) # 將對應的字典信息傳給extra extra = {'ip': '127.0.0.1', 'username': 'Jason'} logger.warning('This is a warning message!', extra=extra) # 若是不傳入extra參數,則會報錯 # logger.warning('This is a warning message!')
使用LoggerAdapter設置默認的extra參數,簡單示例:
# -*- coding:utf-8 -*- import logging class IpUserLoggerAdapter(logging.LoggerAdapter): # 原process方法就兩行代碼,即便用默認的extra值 # 重寫process方法,若是沒有傳入extra,才使用默認值 def process(self, msg, kwargs): if 'extra' not in kwargs: kwargs['extra'] = self.extra return msg, kwargs def get_ipuser_logger(): logger = logging.getLogger() logger.setLevel(logging.DEBUG) console_handler = logging.StreamHandler() console_handler.setLevel(logging.WARNING) console_formatter = logging.Formatter('%(asctime)s[%(ip)s][%(user)s][%(levelname)s]: %(message)s') console_handler.setFormatter(console_formatter) logger.addHandler(console_handler) # 設置默認ip和user,這裏的key要和formatter中的key對應 local_extra = { 'ip': '127.0.0.1', 'user': 'Jason' } return IpUserLoggerAdapter(logger, local_extra) if __name__ == '__main__': ipuser_logger = get_ipuser_logger() ipuser_logger.debug('This is a debug message!') ipuser_logger.info('This is a info message!') ipuser_logger.warning('This is a warning message!') # 打印一個臨時的ip和user ipuser_logger.error('This is a error message!', extra={'ip': '0.0.0.0', 'user': 'anyone'}) ipuser_logger.critical('This is a critical message!')
運行後,控制檯輸出爲:
2019-04-24 21:46:44,433[127.0.0.1][Jason][WARNING]: This is a warning message! 2019-04-24 21:46:44,433[0.0.0.0][anyone][ERROR]: This is a error message! 2019-04-24 21:46:44,433[127.0.0.1][Jason][CRITICAL]: This is a critical message!
7、子logger和共享配置
子logger:在logging模塊中,logger是相似於樹的層級結構,父logger和子logger之間使用點號「.」鏈接和識別,如:logging.getLogger('root'),logging.getLogger('root.child'),logging.getLogger('root.child.grand')……
配置共享:子logger擁有父logger相同的配置,若是在子logger中又另外增長了一些配置,那麼子logger不只擁有父logger的配置,也有本身新增的配置,不過須要注意的是就算子logger新增的配置信息與父logger相同(好比文件),也會執行爲它單獨執行一次,即父logger和子logger的配置互不影響,而且子logger輸出一條日誌時會先執行子logger的配置,再執行父logger的配置。
隔離父looger:logger中有一個屬性propagate,默認爲True,即子logger會共享父logger的配置,能夠手動設置爲False,這樣子logger執行完後就不會再去執行父logger的配置了。
全局使用:在程序運行過程當中,若是想要使用以前使用的logger,不須要每次都運行一次對應的配置,直接使用logging.getLogger(name)獲得對應名稱的logger便可。
注:在使用logging.getLogger(name=None)時若是沒有傳入logger的名稱name,那麼會自動返回名爲root的logger。
簡單示例:
# -*- coding:utf-8 -*- import logging import datetime # 建立一個父logger logger = logging.getLogger('main') logger.setLevel(logging.INFO) # 給父logger添加一個文件輸出的Handler log_file = 'log/sys_%s.log' % datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d') file_handler = logging.FileHandler(log_file) file_handler.setLevel(logging.WARNING) log_formatter = logging.Formatter('%(asctime)s[%(name)s][%(levelname)s]: %(message)s') file_handler.setFormatter(log_formatter) logger.addHandler(file_handler) # 向文件輸出一條日誌 logger.error('This is a error message!') # 建立一個子logger child_logger = logging.getLogger('main.child') # 給子logger添加一個輸出到控制檯的Handler stream_handler = logging.StreamHandler() stream_handler.setLevel(logging.WARNING) child_logger.addHandler(stream_handler)
# 設置propagate屬性爲False能夠隔離父logger的配置
# child_logger.propagate = False # 同時向文件和控制檯輸出一條日誌 child_logger.warning('This is a warning message!')
運行後,日誌文件sys_2019-04-16.log文件內容以下:
2019-04-16 00:09:53,154[main][ERROR]: This is a error message! 2019-04-16 00:09:53,154[main.child][WARNING]: This is a warning message!
控制檯打印的內容以下:
This is a warning message!