logging模塊

logging模塊是Python內置的標準模塊,主要用於輸出運行日誌,能夠設置輸出日誌的等級、日誌保存路徑、日誌文件回滾等;相比print,具有以下優勢:html

  1. 多線程支持
  2. 能夠經過設置不一樣的日誌等級,在release版本中只輸出重要信息,而沒必要顯示大量的調試信息;
  3. print將全部信息都輸出到標準輸出中,嚴重影響開發者從標準輸出中查看其它數據;logging則能夠由開發者決定將信息輸出到什麼地方,以及怎麼輸出;
  4. logging中能夠選擇不少消息級別,如debug、info、warning、error以及critical。經過賦予logger或者handler不一樣的級別,開發者就能夠只輸出錯誤信息到特定的記錄文件,或者在調試時只記錄調試信息。

logging模塊的導入

import logging

最簡單的logging日誌

import logging
logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')  

# 輸出爲:
# WARNING:root:warning message
# ERROR:root:error message
# CRITICAL:root:critical message

從這個例子中,咱們能夠看到默認狀況下python

  1. Python的logging模塊將日誌打印到了標準輸出中
  2. 只顯示大於等於WARNING級別的日誌,這說明默認的日誌級別設置爲WARNING(日誌級別等級CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET)
  3. 默認的日誌格式爲日誌級別:日誌器名稱:日誌內容。

關於logging的內容遠不止這麼簡單,且慢慢看下去。程序員

在後面分別會說明日誌級別,模塊級函數記錄日誌,logging模塊日誌流處理流程服務器

日誌級別

日誌級別分類網絡

  • NOTSET:沒有設置
  • DEBUG:最詳細的日誌信息,典型應用場景是在開發過程當中打印一些運行信息。
  • INFO:重要信息,信息詳細程度僅次於DEBUG,一般只記錄關鍵節點信息,用於確認一切都是按照咱們預期的那樣進行工做
  • WARNING:警告信息,代表會出現潛在錯誤的情形,當某些不指望的事情發生時記錄的信息(如,磁盤可用空間較低),可是此時應用程序仍是正常運行的,有些信息不是錯誤信息,可是也要給程序員的一些提示。
  • ERROR:錯誤信息,因爲一個更嚴重的問題致使某些功能不能正常運行時記錄的信息,雖然發生錯誤事件,但仍然不影響系統的繼續運行。打印錯誤和異常信息,若是不想輸出太多的日誌,可使用這個級別。
  • CRITICAL:嚴重錯誤信息,當發生嚴重錯誤,致使應用程序不能繼續運行時記錄的信息

說明多線程

  • 後面的日誌等級是從大到小依次下降的,即:CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET,而日誌的信息量是依次增多的;
  • 開發應用程序或部署開發環境時,可使用DEBUG或INFO級別的日誌獲取儘量詳細的日誌信息來進行開發或部署調試;應用上線或部署生產環境時,應該使用WARNING或ERROR或CRITICAL級別的日誌來下降機器的I/O壓力和提升獲取錯誤日誌信息的效率。日誌級別的指定一般都是在應用程序的配置文件中進行指定的。
  • 當爲某個應用程序指定一個日誌級別後,應用程序會記錄全部日誌級別大於或等於指定日誌級別的日誌信息,而不是僅僅記錄指定級別的日誌信息。

logging中記錄日誌

logging模塊中提供了兩種記錄日誌的方式:ide

  • 第一種方式是使用logging提供的模塊級別的函數
  • 第二種方式是使用Logging日誌系統的四大組件

說明: logging模塊提供的模塊級別的那些函數實際上也是經過這幾個組件的相關實現類來記錄日誌的,只是在建立這些類的實例時設置了一些默認值。函數

1、模塊級函數記錄日誌

經常使用的記錄日誌的模塊級函數

上面說到了logging的basicConfig函數進行參數的配置。工具

logging.basicConfig函數各個參數測試

  • filename:指定日誌文件名
  • filemode:指定日誌文件的打開模式,默認爲'a'。須要注意的是,該選項要在filename指定時纔有效
  • format:指定日誌格式字符串,即指定日誌輸出時所包含的字段信息以及它們的順序。logging模塊定義的格式字段下面會列出。
    • 經常使用參數及做用
      • %(asctime)s:日誌事件發生的時間--人類可讀時間,如:2003-07-08 16:49:45,896
        %(created)f:日誌事件發生的時間--時間戳,就是當時調用time.time()函數返回的值
        %(relativeCreated)d:日誌事件發生的時間相對於logging模塊加載時間的相對毫秒數(目前還不知道幹嗎用的)
        %(msecs)d:日誌事件發生事件的毫秒部分
        %(levelname)s:該日誌記錄的文字形式的日誌級別('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL')
        %(levelno)s:該日誌記錄的數字形式的日誌級別(10, 20, 30, 40, 50)
        %(name)s:所使用的日誌器名稱,默認是'root',由於默認使用的是 rootLogger
        %(message)s:日誌記錄的文本內容,經過 msg % args計算獲得的
        %(pathname)s:調用日誌記錄函數的源碼文件的全路徑
        %(filename)s:pathname的文件名部分,包含文件後綴
        %(module)s:filename的名稱部分,不包含後綴
        %(lineno)d:調用日誌記錄函數的源代碼所在的行號
        %(funcName)s:調用日誌記錄函數的函數名
        %(process)d:進程ID
        %(processName)s:進程名稱,Python 3.1新增
        %(thread)d:線程ID
        %(thread)s:線程名稱

  • datefmt:指定日期/時間格式。須要注意的是,該選項要在format中包含時間字段%(asctime)s時纔有效
  • level:設置日誌級別,默認爲logging.WARNNING;
  • stream:指定將日誌的輸出流,能夠指定輸出到sys.stderr,sys.stdout或者文件,默認輸出到sys.stderr,stream和filename不能同時提供,不然會引起 ValueError異常;
  • style:Python 3.2中新添加的配置項。指定format格式字符串的風格,可取值爲'%'、'{'和'$',默認爲'%'
  • handlers:Python 3.3中新添加的配置項。該選項若是被指定,它應該是一個建立了多個Handler的可迭代對象,這些handler將會被添加到root logger。須要說明的是:filename、stream和handlers這三個配置項只能有一個存在,不能同時出現2個或3個,不然會引起ValueError異常。

 簡單的日誌輸出

在一開始寫的那個簡單的例子其實就是用模塊級函數寫的日誌。除了上面的那種寫法,還有另外一種寫法。

import logging
logging.log(logging.DEBUG, "This is a debug log.")
logging.log(logging.INFO, "This is a info log.")
logging.log(logging.WARNING, "This is a warning log.")
logging.log(logging.ERROR, "This is a error log.")
logging.log(logging.CRITICAL, "This is a critical log.")

# 輸出爲:
# WARNING:root:This is a warning log.
# ERROR:root:This is a error log.
# CRITICAL:root:This is a critical log.
View Code

 正如前面一開始說的那樣,日誌的的默認日誌級別爲WARNING,只有它和它以上的日誌記錄被輸出。而默認的輸出格式是:日誌級別:日誌器名稱:日誌內容。而這樣輸出也是由於

logging模塊提供的日誌記錄函數所使用的日誌器設置的日誌格式默認是BASIC_FORMAT,其值爲:

"%(levelname)s:%(name)s:%(message)s"

 詳細的默認值能夠看源碼,當咱們沒有提供任何配置信息的時候,這些函數都會去調用logging.basicConfig(**kwargs)方法,且不會向該方法傳遞任何參數。繼續查看basicConfig()方法的代碼就能夠找到上面這些問題的答案了。

 稍做配置後的模塊級函很多天志輸出

import logging
LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
DATE_FORMAT = "%m/%d/%Y %H:%M:%S %p"

logging.basicConfig(filename='my.log', level=logging.DEBUG, format=LOG_FORMAT, datefmt=DATE_FORMAT)

logging.debug("This is a debug log.")
logging.info("This is a info log.")
logging.warning("This is a warning log.")
logging.error("This is a error log.")
logging.critical("This is a critical log.")

# 此時會在my.log日誌文件中看到下面的輸出結果
# 08/12/2018 21:54:39 PM - DEBUG - This is a debug log.
# 08/12/2018 21:54:39 PM - INFO - This is a info log.
# 08/12/2018 21:54:39 PM - WARNING - This is a warning log.
# 08/12/2018 21:54:39 PM - ERROR - This is a error log.
# 08/12/2018 21:54:39 PM - CRITICAL - This is a critical log.
View Code

會上面這些基本的日誌記錄就算是掌握了。 

說明:

  • logging.basicConfig()函數是一個一次性的簡單配置工具使,也就是說只有在第一次調用該函數時會起做用,後續再次調用該函數時徹底不會產生任何操做的,屢次調用的設置並非累加操做。
  • 日誌器(Logger)是有層級關係的,上面調用的logging模塊級別的函數所使用的日誌器是RootLogger類的實例,其名稱爲'root',它是處於日誌器層級關係最頂層的日誌器,且該實例是以單例模式存在的。
  • 若是要記錄的日誌中包含變量數據,可以使用一個格式字符串做爲這個事件的描述消息(logging.debug、logging.info等函數的第一個參數),而後將變量數據做爲第二個參數*args的值進行傳遞,如:logging.warning('%s is %d years old.', 'Tom', 10),輸出內容爲WARNING:root:Tom is 10 years old.
  • logging.debug(), logging.info()等方法的定義中,除了msg和args參數外,還有一個**kwargs參數。它們支持3個關鍵字參數: exc_info, stack_info, extra,下面對這幾個關鍵字參數做個說明。
關於exc_info, stack_info, extra關鍵詞參數的說明:
  • exc_info: 其值爲布爾值,若是該參數的值設置爲True,則會將異常異常信息添加到日誌消息中。若是沒有異常信息則添加None到日誌信息中。
  • stack_info: 其值也爲布爾值,默認值爲False。若是該參數的值設置爲True,棧信息將會被添加到日誌信息中。
  • extra: 這是一個字典(dict)參數,它能夠用來自定義消息格式中所包含的字段,可是它的key不能與logging模塊定義的字段衝突。

一個例子:

import logging
LOG_FORMAT = "%(asctime)s - %(levelname)s - %(user)s[%(ip)s] - %(message)s"
DATE_FORMAT = "%m/%d/%Y %H:%M:%S %p"

logging.basicConfig(format=LOG_FORMAT, datefmt=DATE_FORMAT)
logging.warning("Some one delete the log file.", exc_info=True, stack_info=True, extra={'user': 'Tom', 'ip':'192.168.199.22'})

# 打印結果爲
# 08/12/2018 22:01:43 PM - WARNING - Tom[192.168.199.22] - Some one delete the log file.
# NoneType: None
# Stack (most recent call last):
#   File "D:/workspace/modue/bin.py", line 6, in <module>
#     logging.warning("Some one delete the log file.", exc_info=True, stack_info=True, extra={'user': 'Tom', 'ip':'192.168.199.22'})
View Code

logging模塊日誌流處理流程

在說明用logging模塊的四大組件記錄日誌以前, 頗有必要對logging模塊所包含的重要組件以及其工做流程作個全面、簡要的介紹,這有助於咱們更好的理解咱們所寫的代碼(將會觸發什麼樣的操做)。

 logging模塊的四大組件

logging模塊就是經過這些組件來完成日誌處理的,上面所使用的logging模塊級別的函數也是經過這些組件對應的類來實現的。

這些組件之間的關係描述:

  • 日誌器(logger)須要經過處理器(handler)將日誌信息輸出到目標位置,如:文件、sys.stdout、網絡等;
  • 不一樣的處理器(handler)能夠將日誌輸出到不一樣的位置;
  • 日誌器(logger)能夠設置多個處理器(handler)將同一條日誌記錄輸出到不一樣的位置;
  • 每一個處理器(handler)均可以設置本身的過濾器(filter)實現日誌過濾,從而只保留感興趣的日誌;
  • 每一個處理器(handler)均可以設置本身的格式器(formatter)實現同一條日誌以不一樣的格式輸出到不一樣的地方。

簡單點說就是:日誌器(logger)是入口,真正幹活兒的是處理器(handler),處理器(handler)還能夠經過過濾器(filter)和格式器(formatter)對要輸出的日誌內容作過濾和格式化等處理操做。

 logging日誌模塊相關類及經常使用的方法介紹

下面介紹下與logging四大組件相關的類:Logger,Handler,Filter,Formatter。

  Logger類  

Logger對象有3個任務要作

  1. 嚮應用程序代碼暴露幾個方法,使應用程序能夠在運行時記錄日誌消息;
  2. 基於日誌嚴重等級(默認的過濾設施)或filter對象來決定要對哪些日誌進行後續處理;
  3. 將日誌消息傳送給全部感興趣的日誌handlers。

logger對象最經常使用的方法分爲兩類:配置方法和消息發送。

建立一個Logger對象

  1. 方法一:建立Logger類的實例
    • logger = logging.Logger
  2. 方法二:經過logging.getLogger([name])方法(經常使用)
    • logger = logging.getLogger() 
    • logging.getLogger()方法有一個可選參數name,該參數表示將要返回的日誌器的名稱標識,若是不提供該參數,則其值爲'root'。若以相同的name參數值屢次調用getLogger()方法,將會返回指向同一個logger對象的引用,由於在建立日誌器的時候是用的單例模式。

當咱們建立了一個Logger對象,其實它並沒什麼功能,須要咱們給它配置添加。

經常使用的配置方法

(下面寫的logger都是Logger類的實例化對象):

  • logger.setLevel():設置日誌器將會處理的日誌消息的最低嚴重級別,好比:logger.setLevel("DEBUG")
  • logger.addHandler()和logger.removeHandler():爲該Logger對象添加 和 移除一個Handler對象
    • import logging
      
      logger = logging.getLogger()    # 得到一個Logger實例化對象
      fh = logging.FileHandler('my_log')  # 建立一個FileHandler對象,用於寫入日誌文件,參數爲日誌寫到哪一個文件中
      ch = logging.StreamHandler()  # 建立一個StreamHandler對象,用於輸出到控制檯
      
      logger.addHandler(fh)       # 爲Logger對象添加FileHandler對象
      logger.addHandler(ch)       # 爲Logger對象添加StreamHandler對象
      logger.removeHandler(fh)    # 爲Logger對象刪除FileHandler對象
      logger.removeHandler(ch)    # 爲Logger對象刪除StreamHandler對象
  • logger.addFilter()和logger.removeFilter():
    • 使用參照上面例子,可是它只接收FileHandler對象

建立日誌記錄

  • Logger.debug(), Logger.info(), Logger.warning(), Logger.error(), Logger.critical():建立一個與它們的方法名對應等級的日誌記錄
  • Logger.exception():建立一個相似於Logger.error()的日誌消息
  • Logger.log():須要獲取一個明確的日誌level參數來建立一個日誌記錄

例如:

logger = logging.getLogger()    # 得到一個Logger實例化對象
logger.debug('logger debug message')
logger.info('logger info message')
logger.warning('logger warning message')
logger.error('logger error message')
logger.critical('logger critical message')

說明:

  • Logger.exception()與Logger.error()的區別在於:Logger.exception()將會輸出堆棧追蹤信息,另外一般只是在一個exception handler中調用該方法。
  • Logger.log()與Logger.debug()、Logger.info()等方法相比,雖然須要多傳一個level參數,顯得不是那麼方便,可是當須要記錄自定義level的日誌時仍是須要該方法來完成。

關於logger的層級結構與有效等級的說明:

  • logger的名稱是一個以'.'分割的層級結構,每一個'.'後面的logger都是'.'前面的logger的children,例如,有一個名稱爲 foo 的logger,其它名稱分別爲 foo.bar, foo.bar.baz 和 foo.bam都是 foo 的後代。
  • logger有一個"有效等級(effective level)"的概念。若是一個logger上沒有被明確設置一個level,那麼該logger就是使用它parent的level;若是它的parent也沒有明確設置level則繼續向上查找parent的parent的有效level,依次類推,直到找到個一個明確設置了level的祖先爲止。須要說明的是,root logger老是會有一個明確的level設置(默認爲 WARNING)。當決定是否去處理一個已發生的事件時,logger的有效等級將會被用來決定是否將該事件傳遞給該logger的handlers進行處理。
  • child loggers在完成對日誌消息的處理後,默認會將日誌消息傳遞給與它們的祖先loggers相關的handlers。所以,咱們沒必要爲一個應用程序中所使用的全部loggers定義和配置handlers,只須要爲一個頂層的logger配置handlers,而後按照須要建立child loggers就可足夠了。咱們也能夠經過將一個logger的propagate屬性設置爲False來關閉這種傳遞機制。

 

  Handler類 

 Handler對象的做用是(基於日誌消息的level)將消息分發到handler指定的位置(文件、網絡、郵件等)。在上面的Logger類中,咱們使用了Handler類的一些知識。因此對Handler類應該有了必定的瞭解,這裏會詳細的介紹這個類。

Logger對象能夠經過addHandler()方法爲本身添加0個或者更多個handler對象。好比,一個應用程序可能想要實現如下幾個日誌需求:

  1. 把全部日誌都發送到一個日誌文件中;
  2. 把全部嚴重級別大於等於error的日誌發送到stdout(標準輸出);
  3. 把全部嚴重級別爲critical的日誌發送到一個email郵件地址。

這種場景就須要3個不一樣的handlers,每一個handler複雜發送一個特定嚴重級別的日誌到一個特定的位置。

經常使用的Handler

  • logging.StreamHandler:將日誌消息發送到輸出到Stream,如std.out, std.err或任何file-like對象。
  • logging.FileHandler:將日誌消息發送到磁盤文件,默認狀況下文件大小會無限增加
  • logging.handlers.RotatingFileHandler:將日誌消息發送到磁盤文件,並支持日誌文件按大小切割
  • logging.hanlders.TimedRotatingFileHandler:將日誌消息發送到磁盤文件,並支持日誌文件按時間切割
  • logging.handlers.HTTPHandler:將日誌消息以GET或POST的方式發送給一個HTTP服務器
  • logging.handlers.SMTPHandler:將日誌消息發送給一個指定的email地址
  • logging.NullHandler:該Handler實例會忽略error messages,一般被想使用logging的library開發者使用來避免'No handlers could be found for logger XXX'信息的出現。

handle對象經常使用配置方法

  • Handler.setLevel():設置handler將會處理的日誌消息的最低嚴重級別
  • Handler.setFormatter():爲handler設置一個格式器對象
  • Handler.addFilter() 和 Handler.removeFilter():爲handler添加 和 刪除一個過濾器對象

  filter類  

Filter能夠被Handler和Logger用來作比level更細粒度的、更復雜的過濾功能。Filter是一個過濾器基類,它只容許某個logger層級下的日誌事件經過過濾。該類定義以下:

class logging.Filter(name='') filter(record)

好比,一個filter實例化時傳遞的name參數值爲'A.B',那麼該filter實例將只容許名稱爲相似以下規則的loggers產生的日誌記錄經過過濾:'A.B','A.B,C','A.B.C.D','A.B.D',而名稱爲'A.BB', 'B.A.B'的loggers產生的日誌則會被過濾掉。若是name的值爲空字符串,則容許全部的日誌事件經過過濾。

filter方法用於具體控制傳遞的record記錄是否能經過過濾,若是該方法返回值爲0表示不能經過過濾,返回值爲非0表示能夠經過過濾。

  formatter類  

Formater對象用於配置日誌信息的最終順序、結構和內容。與logging.Handler基類不一樣的是,應用代碼能夠直接實例化Formatter類。另外,若是你的應用程序須要一些特殊的處理行爲,也能夠實現一個Formatter的子類來完成。

Formatter類的構造方法定義以下:

logging.Formatter.__init__(fmt=None, datefmt=None, style='%')

可見,該構造方法接收3個可選參數:

  • fmt:指定消息格式化字符串,若是不指定該參數則默認使用message的原始值
  • datefmt:指定日期格式字符串,若是不指定該參數則默認使用"%Y-%m-%d %H:%M:%S"
  • style:Python 3.2新增的參數,可取值爲 '%', '{'和 '$',若是不指定該參數則默認使用'%'

logging日誌處理流程

下圖大體表述了日誌流的處理流程

咱們來描述下上面這個圖的日誌流處理流程:

  • 1)(在用戶代碼中進行)日誌記錄函數調用,如:logger.info(...),logger.debug(...)等;
  • 2)判斷要記錄的日誌級別是否知足日誌器設置的級別要求(要記錄的日誌級別要大於或等於日誌器設置的級別纔算知足要求),若是不知足則該日誌記錄會被丟棄並終止後續的操做,若是知足則繼續下一步操做;
  • 3)根據日誌記錄函數調用時摻入的參數,建立一個日誌記錄(LogRecord類)對象;
  • 4)判斷日誌記錄器上設置的過濾器是否拒絕這條日誌記錄,若是日誌記錄器上的某個過濾器拒絕,則該日誌記錄會被丟棄並終止後續的操做,若是日誌記錄器上設置的過濾器不拒絕這條日誌記錄或者日誌記錄器上沒有設置過濾器則繼續下一步操做--將日誌記錄分別交給該日誌器上添加的各個處理器;
  • 5)判斷要記錄的日誌級別是否知足處理器設置的級別要求(要記錄的日誌級別要大於或等於該處理器設置的日誌級別纔算知足要求),若是不知足記錄將會被該處理器丟棄並終止後續的操做,若是知足則繼續下一步操做;
  • 6)判斷該處理器上設置的過濾器是否拒絕這條日誌記錄,若是該處理器上的某個過濾器拒絕,則該日誌記錄會被當前處理器丟棄並終止後續的操做,若是當前處理器上設置的過濾器不拒絕這條日誌記錄或當前處理器上沒有設置過濾器測繼續下一步操做;
  • 7)若是能到這一步,說明這條日誌記錄通過了層層關卡容許被輸出了,此時當前處理器會根據自身被設置的格式器(若是沒有設置則使用默認格式)將這條日誌記錄進行格式化,最後將格式化後的結果輸出到指定位置(文件、網絡、類文件的Stream等);
  • 8)若是日誌器被設置了多個處理器的話,上面的第5-8步會執行屢次;
  • 9)這裏纔是完整流程的最後一步:判斷該日誌器輸出的日誌消息是否須要傳遞給上一級logger(以前提到過,日誌器是有層級關係的)的處理器,若是propagate屬性值爲1則表示日誌消息將會被輸出處處理器指定的位置,同時還會被傳遞給parent日誌器的handlers進行處理直到當前日誌器的propagate屬性爲0中止,若是propagate值爲0則表示不向parent日誌器的handlers傳遞該消息,到此結束。

可見,一條日誌信息要想被最終輸出須要依次通過如下幾回過濾:

  • 日誌器等級過濾;
  • 日誌器的過濾器過濾;
  • 日誌器的處理器等級過濾;
  • 日誌器的處理器的過濾器過濾;

須要說明的是: 

1.關於上面第9個步驟,若是propagate值爲1,那麼日誌消息會直接傳遞交給上一級logger的handlers進行處理,此時上一級logger的日誌等級並不會對該日誌消息進行等級過濾。

2.使用getLogger()方法的時候,若是要返回的日誌器名字相同,返回的爲同一個日誌器,由於它的內部是用單例生成的,因此當多個對象使用同一日誌器而後進行設置時,新的設置會覆蓋舊的設置。而當輸出的時候,有幾個對象要顯示,就顯示幾遍日誌。

3.日誌器是一個樹形結構,好比:getLogger("mylogger.sontree")mylogger是root的孩子,sontree是mylogger的孩子,root的孫子。

4.當咱們使用子層的日誌器的時候,它會默認往上去找父級直到找到root,有幾層顯示幾遍日誌:

  例如:

import logging

logger = logging.getLogger()    # 得到一個Logger實例化對象,因爲沒寫參數返回的root日誌器
ch = logging.StreamHandler()  # 建立一個StreamHandler對象,用於輸出到控制檯
logger.addHandler(ch)       # 爲Logger對象添加StreamHandler對象
logger.setLevel("ERROR")

logger1 = logging.getLogger("my_log")    # 得到一個名爲Logger實例化對象
logger1.addHandler(ch)  # # 爲Logger對象添加StreamHandler對象
logger1.setLevel("DEBUG")


logger.debug('logger debug message')
logger.info('logger info message')
logger.warning('logger warning message')
logger.error('logger error message')
logger.critical('logger critical message')

logger1.debug('logger1 debug message')
logger1.info('logger1 info message')
logger1.warning('logger1 warning message')
logger1.error('logger1 error message')
logger1.critical('logger1 critical message')

# 輸出結果爲:
# logger error message
# logger critical message
# logger1 debug message
# logger1 debug message
# logger1 info message
# logger1 info message
# logger1 warning message
# logger1 warning message
# logger1 error message
# logger1 error message
# logger1 critical message
# logger1 critical message
View Code

例子中,logger不寫參數獲取的日誌器爲root,它是正常顯示的,而logger1獲取的則是名爲my_log的日誌器,它除了打印本身的,還向上去找,找到了它的父級就是頂級的root,因此也打印了一遍,這樣就看到了後面打印了兩遍的日誌。你能夠再按上面說到建立一個my_log日誌器的子日誌器進行測試。

5.上面提到了會重複打印的問題,那麼我不想這樣怎麼辦。那就是把例子中的logger.addHandler(ch)  註釋了,不讓父日誌器的handler工做就好了。因此咱們須要注意的就是每當一個日誌器的父日誌器有輸出的時候,它就會重複輸出一次。

 用logging模塊的四大組件記錄日誌

如今,咱們對logging模塊的重要組件及整個日誌流處理流程都應該有了一個比較全面的瞭解,下面咱們來看一個例子。

需求

如今有如下幾個日誌記錄的需求:

  • 1)要求將全部級別的全部日誌都寫入磁盤文件中
  • 2)all.log文件中記錄全部的日誌信息,日誌格式爲:日期和時間 - 日誌級別 - 日誌信息
  • 3)error.log文件中單獨記錄error及以上級別的日誌信息,日誌格式爲:日期和時間 - 日誌級別 - 文件名[:行號] - 日誌信息
  • 4)要求all.log在天天凌晨進行日誌切割

分析

  • 1)要記錄全部級別的日誌,所以日誌器的有效level須要設置爲最低級別--DEBUG;
  • 2)日誌須要被髮送到兩個不一樣的目的地,所以須要爲日誌器設置兩個handler;另外,兩個目的地都是磁盤文件,所以這兩個handler都是與FileHandler相關的;
  • 3)all.log要求按照時間進行日誌切割,所以他須要用logging.handlers.TimedRotatingFileHandler; 而error.log沒有要求日誌切割,所以可使用FileHandler;
  • 4)兩個日誌文件的格式不一樣,所以須要對這兩個handler分別設置格式器;

代碼實現

import logging
import logging.handlers
import datetime

logger = logging.getLogger('mylogger')
logger.setLevel(logging.DEBUG)

rf_handler = logging.handlers.TimedRotatingFileHandler('all.log', when='midnight', interval=1, backupCount=7, atTime=datetime.time(0, 0, 0, 0))
rf_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))

f_handler = logging.FileHandler('error.log')
f_handler.setLevel(logging.ERROR)
f_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(filename)s[:%(lineno)d] - %(message)s"))

logger.addHandler(rf_handler)
logger.addHandler(f_handler)

logger.debug('debug message')
logger.info('info message')
logger.warning('warning message')
logger.error('error message')
logger.critical('critical message')

all.log文件輸出

2018-08-13 11:00:58,246 - DEBUG - debug message
2018-08-13 11:00:58,247 - INFO - info message
2018-08-13 11:00:58,247 - WARNING - warning message
2018-08-13 11:00:58,247 - ERROR - error message
2018-08-13 11:00:58,247 - CRITICAL - critical message

error.log文件輸出

2018-08-13 11:00:58,247 - ERROR - bin.py[:21] - error message
2018-08-13 11:00:58,247 - CRITICAL - bin.py[:22] - critical message

 

【轉】 向日志輸出中添加上下文信息:http://www.cnblogs.com/yyds/p/6897964.html

參考文檔:

http://www.javashuo.com/article/p-wqlkttny-y.html

相關文章
相關標籤/搜索