python 日誌庫logging

日誌級別

NOTSET	(0)
DEBUG (10)
INFO (20)
WARNING (30)
ERROR (40)
CRITICAL (50)

logging 輸入日誌時,會輸出>= 設置的日誌級別的日誌,例如設置爲DEBUG,則會輸出DEBUG、INFO、WARNING、ERROR、CRITICAL級別的日誌。默認的日誌級別爲WARNING。

Logging流程

Logger:日誌
LogRecord:日誌記錄器
Handler:處理器(將日誌發送到哪裏)
Filter:過濾器,決定輸入哪些日誌
Formatter:格式化器,日誌佈局

logging工作流程

1、判斷 Logger 對象對於設置的級別是否可用,如果可用,則往下執行,否則,流程結束。
2、創建 LogRecord 對象,如果註冊到 Logger 對象中的 Filter 對象過濾後返回 False,則不記錄日誌,流程結束,否則,則向下執行。
3、LogRecord 對象將 Handler 對象傳入當前的 Logger 對象,(圖中的子流程)如果 Handler 對象的日誌級別大於設置的日誌級別,再判斷註冊到 Handler 對象中的 Filter 對象過濾後是否返回 True 而放行輸出日誌信息,否則不放行,流程結束。
4、如果傳入的 Handler 大於 Logger 中設置的級別,也即 Handler 有效,則往下執行,否則,流程結束。
5、判斷這個 Logger 對象是否還有父 Logger 對象,如果沒有(代表當前 Logger 對象是最頂層的 Logger 對象 root Logger),流程結束。否則將 Logger 對象設置爲它的父 Logger 對象,重複上面的 3、4 兩步,輸出父類 Logger 對象中的日誌輸出,直到是 root Logger 爲止。

組件詳細介紹

1.Logger

Logger是一個樹形層級結構,輸出信息之前都要獲得一個Logger(如果沒有顯示的獲取則自動創建並使用root Logger,如第一個例子所示)。
logger = logging.getLogger() 返回一個默認的Logger也即root Logger,並應用默認的日誌級別、Handler和Formatter設置。
當然也可以通過Logger.setLevel(lel)指定最低的日誌級別,可用的日誌級別有:
logging.DEBUG、logging.INFO、logging.WARNING、logging.ERROR、logging.CRITICAL。
Logger.debug()、Logger.info()、Logger.warning()、Logger.error()、Logger.critical()
輸出不同級別的日誌,只有日誌等級大於或等於設置的日誌級別的日誌纔會被輸出。

logger1 = logging.getLogger('mylogger')  
logger1.setLevel(logging.DEBUG)  
logger2 = logging.getLogger('mylogger')  
logger2.setLevel(logging.INFO)  
logger3 = logging.getLogger('mylogger.child1') #創建了(root.)mylogger.child1
logger4 = logging.getLogger('mylogger.child1.child2') #創建了(root.)mylogger.child1.child2
logger5 = logging.getLogger('mylogger.child1.child2.child3') #創建了(root.)mylogger.child1.child2.child3

2.Handler

Handler對象負責發送相關的信息到指定目的地,有幾個常用的Handler方法:
Handler.setLevel(lel): 指定日誌級別,低於lel級別的日誌將被忽略
Handler.setFormatter(): 給這個handler選擇一個Formatter
Handler.addFilter(filt)、Handler.removeFilter(filt):新增或刪除一個filter對象

可以通過addHandler()方法爲Logger添加多個Handler:
有多中可用的Handler:

logging.StreamHandler 可以向類似與sys.stdout或者sys.stderr的任何文件對象(file object)輸出信息
logging.FileHandler 用於向一個文件輸出日誌信息
logging.handlers.RotatingFileHandler 類似於上面的FileHandler,但是它可以管理文件大小。
當文件達到一定大小之後,它會自動將當前日誌文件改名,然後創建一個新的同名日誌文件繼續輸出
logging.handlers.TimedRotatingFileHandler 和RotatingFileHandler類似,不過,它沒有通過判斷文件大小來決定何時重新創建日誌文件,而是間隔一定時間就自動創建新的日誌文件
logging.handlers.SocketHandler 使用TCP協議,將日誌信息發送到網絡。
logging.handlers.DatagramHandler 使用UDP協議,將日誌信息發送到網絡。
logging.handlers.SysLogHandler 日誌輸出到syslog
logging.handlers.NTEventLogHandler 遠程輸出日誌到Windows NT/2000/XP的事件日誌
logging.handlers.SMTPHandler 遠程輸出日誌到郵件地址
logging.handlers.MemoryHandler 日誌輸出到內存中的制定buffer
logging.handlers.HTTPHandler 通過"GET"或"POST"遠程輸出到HTTP服務器

fh = logging.FileHandler('/tmp/test.log')   
ch = logging.StreamHandler()  

logger.addHandler(fh)  
logger.addHandler(ch)  

3.Formatter

Formatter對象設置日誌信息最後的規則、結構和內容,默認的時間格式爲%Y-%m-%d %H:%M:%S。

#定義Formatter  
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')  
#爲Handler添加Formatter  
fh.setFormatter(formatter)  

format參數中可能用到的格式化串:
%(name)s Logger的名字
%(levelno)s 數字形式的日誌級別
%(levelname)s 文本形式的日誌級別
%(pathname)s 調用日誌輸出函數的模塊的完整路徑名,可能沒有
%(filename)s 調用日誌輸出函數的模塊的文件名
%(module)s 調用日誌輸出函數的模塊名
%(funcName)s 調用日誌輸出函數的函數名
%(lineno)d 調用日誌輸出函數的語句所在的代碼行
%(created)f 當前時間,用UNIX標準的表示時間的浮 點數表示
%(relativeCreated)d 輸出日誌信息時的,自Logger創建以 來的毫秒數
%(asctime)s 字符串形式的當前時間。默認格式是 “2003-07-08 16:49:45,896”。逗號後面的是毫秒
%(thread)d 線程ID。可能沒有
%(threadName)s 線程名。可能沒有
%(process)d 進程ID。可能沒有
%(message)s 用戶輸出的消息

4.Filter

限制只有滿足過濾規則的日誌纔會輸出。
比如我們定義了filter = logging.Filter(‘a.b.c’),並將這個Filter添加到了一個Handler上,
則使用該Handler的Logger中只有名字帶a.b.c前綴的Logger才能輸出其日誌。

# 只輸出mylogger.child1.child2的日誌
filter = logging.Filter('mylogger.child1.child2')  
fh.addFilter(filter)  

日誌輸出格式

級別:Logger實例名稱:日誌內容

定義Log兩種方法

第一種:就是實例化logger = logging.logger 然後手動給logger添加addHandler, addFilter, handler.setFormatter 添加格式,這樣的形式來獲取logger
第二種:就是使用 logging.config.dictConfig 來從配置文件生成logger

# 獲取root logger 
root_logger = logging.getLogger() 
print 'root logger', root_logger, id(root_logger) 
root_logger = logging.root 
print 'root logger', root_logger, id(root_logger)

#結果
root logger <logging.RootLogger object at 0x7f7aa0fdd6d0> 140164663727824
root logger <logging.RootLogger object at 0x7f7aa0fdd6d0> 140164663727824
# 他們是同一個root logger
import logging
# 沒有創建logger, 默認是root logger, 直接打印在屏幕
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s:%(name)s:%(levelname)s:%(message)s')
# logging.warning('this is warning')
# logging.info('this is info')

                                                                                                       
logger = logging.getLogger('apps')
apps_handler = logging.FileHandler(filename="apps.log")
logger.addHandler(apps_handler)
logger.setLevel(logging.DEBUG)
logger.info('shis')
print logger.handlers
print logger

上面流程講解:

首先 logging.basicConfig 配置的是root logger 的StreamHandler 的格式,即打印在終端的內容
然後新的apps logger 都是root logger的子,輸出的時候,同時會輸出到root logger, 除非 logger.propagate = False。
所以如果沒有 logging.basicConfig這個對root logger的配置,app logger就會只發送內容到自己的handlers

其他參考代碼

import logging
# 沒有創建logger, 默認是root logger, 直接打印在屏幕
# root logger 默認的level 是warning,所以這裏設置成debug,才能打印info的日誌
# 設置了root logger的format,包括時間,logger名字,levelname等
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s:%(name)s:%(levelname)s:%(message)s')
logging.warning('this is warning')
logging.info('this is info')

"""
#輸出:
2018-01-30 15:50:28,761:root:WARNING:this is warning
2018-01-30 15:50:28,761:root:INFO:this is info
"""

# 創建一個新的apps 的logger, 如果logger不設置,就會用root logger那套(打印到屏幕和上面的格式)
# 因爲它是會默認傳播到祖先logger
logger = logging.getLogger('apps')
logger.setLevel(logging.DEBUG)
# 是否傳播這個日誌到祖先logger, 如果設置了False 就不會傳到root logger(祖先Logger)的
# 默認StreamHandler那裏, 也就是不會打印在頁面上
logger.propagate = False
# 添加handler, 決定日誌落地到哪裏,可以多個
# 這個是記錄在文件的Handler
apps_handler = logging.FileHandler(filename="apps.log")
# 設置這個handler的處理格式, 實例化一個Formatter對象
apps_formatter = logging.Formatter('%(asctime)s:%(name)s:%(levelname)s:%(message)s')
apps_handler.setFormatter(apps_formatter)
logger.addHandler(apps_handler)
# 日誌會打印到apps.log, 並且不會輸出到屏幕(如果logger.propagate=True就會)
logger.debug('shit')

# 定義一個新的logger
child_logger = logging.getLogger('apps.owan')
# 因爲這個child_logger 是apps.owan, 它是繼承了apps這個logger
# 這個child_logger.propagate 默認是True
# 所以還是會傳到它的祖先logger 也就是apps
child_logger.info('haha')
# 所以這個info 是會傳播到apps 所以apps.log會出現這個日誌。
# 這裏充分說明logger的繼承關係
tail apps.log
# apps ,apps.owan 的logger都把日誌寫到apps.log了, 而且格式一樣
# 因爲child_logger沒有設置handler 和 formatter, 默認傳播到祖先那裏apps 的logger
2018-01-30 16:00:10,258:apps:DEBUG:apps logger coming
2018-01-30 16:00:10,258:apps.owan:INFO:i am apps child_logger
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章