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來進行日誌處理。