Python日誌logging模組使用詳解
- 2019 年 10 月 4 日
- 筆記
前言
每個語言都會有自己的日誌模組,Python也不例外。通常情況下當需要使用到日誌的時候, 一般都是匆匆查找下資料,按照步驟進行下配置就是完事了,不太會去總結日誌模組的使用方式。 經常過一段時間新項目需要用的時候,還是需要去網上搜索下配置方式。所以今天就為了日後的使用方便而進行的內容整理。
使用默認配置記錄日誌
Python的日誌模組是logging,屬於Python的標準發行庫。如果你只是用於調試程式,又不想用print
函數的情況下。 倒是可以直接使用logging的默認配置來進行調試資訊的列印。比如:
import logging logging.debug('debug') logging.info('info') logging.warning('warning')
這段程式碼的運行結果如下:(日誌格式為:日誌等級:Logger名稱:用戶資訊)
WARNING:root:warning
可以看出,默認情況下logging的日誌級別為warning
,並不是info
。當然你也可以通過如下設置來更改日誌級別。
import logging logging.basicConfig(level=logging.INFO, format="[%(asctime)s][%(name)s][%(levelname)s] => %(message)s", datefmt='%Y-%m-%d %H:%M:%S %a' ) logging.info('info')
這段程式碼不僅設置了默認的日誌等級,同時還設置了日誌的輸出格式和日期格式。該段程式碼的運行結果如下:
[2019-03-23 17:57:31 Sat][root][INFO] => info
關於更多的日誌格式屬性說明,請參見官方說明文檔
寫入到日誌文件
前面都是直接在命令行輸出日誌資訊,有時候你可能希望輸入到日誌文件中。那麼你也可以通過basicConfig方法來進行設置。比如:
import logging logging.basicConfig(level=logging.INFO, format="[%(asctime)s][%(name)s][%(levelname)s] => %(message)s", datefmt='%Y-%m-%d %H:%M:%S %a', filename='my.log', filemode='a' ) logging.info('info')
該段程式碼執行後,不會在命令行有任何資訊的輸出,但是會在程式碼執行目錄生成一個mylog
的日誌文件。裡面的內容為:
[2019-03-23 17:58:27 Sat][root][INFO] => info
另外,這個設置中還規定了文件日誌的寫入模式為a
,即追加寫入模式。重新再次執行該程式碼時會在原日誌內容後追加新日誌。 而如果你希望每次執行都是覆蓋原來的日誌內容,則可以把寫入模式該為w
,即寫入模式。
日誌資訊格式化
前面作為介紹方便,列印的資訊都是比較簡單的內容;而有時候我們還是會列印一些比較複雜的、需要拼接的字元內容。 此時就需要使用格式化的功能來完成了,除了我們提前把日誌資訊格式化好,Logging的日誌方法也提供了格式化的調用。
import logging logging.warning('this is %s', 'warning')
需要注意的是,
logging.basicConfig
方法只能配置一次,配置多次時也只有第一次會生效。
通過程式碼配置日誌
上面是直接使用logging模組的方法來記錄日誌資訊的,這時用到的是Logger是頂級的Logger,名字為root
,是個單例對象。 其實除了這個頂級的Logger之外,還可以生成其它的Logger對象。下面就來做個介紹。
首先,想要獲取Logger對象,可以通過logging.getLogger方法來實現。比如:
import logging logging.basicConfig(level=logging.INFO, format="[%(asctime)s][%(name)s][%(levelname)s] => %(message)s", datefmt='%Y-%m-%d %H:%M:%S %a' ) Logger = logging.getLogger() # 獲取的是名為root的Logger Logger.warning('this is warning') Logger = logging.getLogger(__name__) # 獲取的是名為__main__的Logger Logger.warning('this is warning')
這段程式碼在命令行列印的資訊如下:
[2019-03-23 18:52:39 Sat][root][WARNING] => this is warning [2019-03-23 18:52:39 Sat][__main__][WARNING] => this is warning
可以看到%(name)s
佔位符的內容分別是root和main。當然你也可以獲取指定名字的Logger。 比如:logging.getLogger('test')
則會獲取一個名為test的Logger。
不同Logger進行不同設置
通過上面的程式碼,我們已經可以獲取多個Logger對象了,那麼就有需求對不同的Logger進行日誌設置。不同Logger分別不同設置的操作如下:
import logging formatter = logging.Formatter( fmt="[%(asctime)s][%(name)s][%(levelname)s] => %(message)s", datefmt="%Y-%m-%d %H:%M:%S %a" ) Logger = logging.getLogger('test') Logger.setLevel(logging.INFO) FH = logging.FileHandler("my.log", encoding="utf-8") # 文件處理器,日誌會記錄到文件 FH.setFormatter(formatter) Logger.addHandler(FH) Logger.info('this is info') Logger2 = logging.getLogger(__name__) Logger2.setLevel(logging.WARNING) CH = logging.StreamHandler() # 串流處理器,日誌默認顯示在命令行 CH.setFormatter(formatter) Logger2.addHandler(CH) Logger2.warning('this is warning')
這段程式碼執行後,info的日誌會寫入到my.log文件,而warning的日誌則會在命令行執行列印出來。 此外,還可以對日誌文件按照指定規則進行分割,這時就需要使用特定的Handler來完成這個任務了。 比如:RotatingFileHandler可以根據文件大小來分割,TimedRotatingFileHandler可以按照指定的時間間隔來分割日誌。
import logging.handlers formatter = logging.Formatter( fmt="[%(asctime)s][%(name)s][%(levelname)s] => %(message)s", datefmt="%Y-%m-%d %H:%M:%S %a" ) Logger = logging.getLogger('test') Logger.setLevel(logging.INFO) RFH = logging.handlers.RotatingFileHandler("test.log", maxBytes=1024 * 1024 * 5, backupCount=5) RFH.setFormatter(formatter) Logger.addHandler(RFH) Logger.info('this is info') Logger2 = logging.getLogger('test2') Logger2.setLevel(logging.WARNING) TRFH = logging.handlers.TimedRotatingFileHandler("test2.log", when='D', interval=2, backupCount=3) TRFH.setFormatter(formatter) Logger2.addHandler(TRFH) Logger2.warning('this is warning')
這段程式碼中RotatingFileHandler
的配置是日誌文件按照5M大小來分割,最多最多保留5個日誌文件。 而TimedRotatingFileHandler
的配置是按照每2天分割一次日誌文件,保留3個日誌文件。
關於更多的Handler類,可以查看官方文檔
通過文件配置日誌
上面介紹的日誌配置都是通過程式碼的方式配置的,其實還可以通過日誌配置文件來配置。下面就來看看配置文件的格式,首先是字典配置的格式:
import logging.config LOGGING = { "version": 1, "disable_existing_loggers": False, "formatters": { "simple": { 'format': '%(asctime)s [%(name)s:%(lineno)d] [%(levelname)s]- %(message)s' }, 'standard': { 'format': '%(asctime)s [%(threadName)s:%(thread)d] [%(name)s:%(lineno)d] [%(levelname)s]- %(message)s' }, }, "handlers": { "console": { "class": "logging.StreamHandler", "level": "DEBUG", "formatter": "simple", "stream": "ext://sys.stdout" }, "default": { "class": "logging.handlers.RotatingFileHandler", "level": "INFO", "formatter": "simple", "filename": 'test.log', 'mode': 'a', "maxBytes": 1024*1024*5, # 5 MB "backupCount": 5, "encoding": "utf8" }, }, "loggers": { "test": { "level": "WARNING", "handlers": ["console"], "propagate": "no" } }, "root": { 'handlers': ['default'], 'level': "INFO", 'propagate': False } } logging.config.dictConfig(LOGGING) Logger = logging.getLogger('test') Logger.warning('this is warning')
接著是文件配置的格式,先看日誌配置文件:
[loggers] keys=root,logger01 [logger_root] level=DEBUG handlers=hand01 [logger_logger01] handlers=hand02,hand03 qualname=logger01 propagate=0 [handlers] keys=hand01,hand02,hand03 [handler_hand01] class=StreamHandler level=INFO formatter=form02 args=(sys.stderr,) [handler_hand02] class=FileHandler level=DEBUG formatter=form01 args=('test.log', 'a') [handler_hand03] class=handlers.RotatingFileHandler level=INFO formatter=form02 args=('test.log', 'a', 10*1024*1024, 5) [formatters] keys=form01,form02 [formatter_form01] format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s datefmt=%a, %d %b %Y %H:%M:%S [formatter_form02] format=%(name)-12s: %(levelname)-8s %(message)s
python程式碼就更簡單了。
import logging.config logging.config.fileConfig('logging.conf') Logger = logging.getLogger() # root Logger Logger.warning('this is warning') Logger2 = logging.getLogger('logger01') # logger01 Logger Logger2.info('this is info')
總結
到這裡關於Python的logging模組的介紹就結束了。現在回過頭來再總結下,logging模組其實有很多的子模組, 不同的子模組有不同的作用,具體而言可以通過一張圖來理解。

logging
從圖中可以看出logging模組的主要子模組有:Logger,Handler,Filter, Formatter等。 其中Logger下面調用Handler,Handler下面調用Filter和Formatter來進行日誌處理。