python的logging模塊提供了通用的日志系統,可以方便第三方模塊或應用使用。
簡單使用
import logging # logging.config.fileConfig("./logging.conf") logging.basicConfig(filename='logger.log', level=logging.INFO) # create logger logger_name1 = "example01" logger = logging.getLogger(logger_name1) logger.debug('debug message') logger.info('info message') logger.warn('warn message') logger.error('error message') logger.critical('critical message')
控制台無打印信息,發現當前工作目錄下生成了logger.log,內容為:
INFO:example01:info message
WARNING:example01:warn message
ERROR:example01:error message
CRITICAL:example01:critical message
該模塊可以配置哪些信息:
詳解:
Logging模塊提供Logger,handler,filter,formatter。
- Logger:記錄器
- 應用程序代碼能直接調用日志接口。
- Logger最常用的操作有兩類:配置和發送日志消息。
- 初始化 logger = logging.getLogger("endlesscode"),獲取logger對象,getLogger()方法后面最好加上所要日志記錄的模塊名字,配置文件和打印日志格式中的%(name)s 對應的是這里的模塊名字,如果不指定name則返回root對象。
- logger.setLevel(logging.DEBUG),Logging中有NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL這幾種級別,日志會記錄設置級別以上的日志
-
級別 何時使用 DEBUG 詳細信息,典型地調試問題時會感興趣。 INFO 證明事情按預期工作。 WARNING 表明發生了一些意外,或者不久的將來會發生問題(如‘磁盤滿了’)。軟件還是在正常工作。 ERROR 由於更嚴重的問題,軟件已不能執行一些功能了。 CRITICAL 嚴重錯誤,表明軟件已不能繼續運行了。 - 多次使用相同的name調用getLogger方法返回同一個looger對象;
- handler:處理器
- 將(記錄器產生的)日志記錄(log record)發送至合適的目的地(destination),比如文件,socket等。
- Handler常用的是StreamHandler和FileHandler,可以簡單理解為一個是console和文件日志,一個打印在調試窗口上,一個記錄在一個文件上
- 一個logger對象可以通過addHandler方法添加0到多個handler,每個handler又可以定義不同日志級別,以實現日志分級過濾顯示。
- filter:
- 過濾器,提供了更好的粒度控制,它可以決定輸出哪些日志記錄。。
- formatter:
- 格式化器,指明了最終輸出中日志記錄的布局。
- 指定日志記錄輸出的具體格式。
- formatter的構造方法需要兩個參數:消息的格式字符串和日期字符串,這兩個參數都是可選的。
- 喜歡用這樣的格式 '[%(asctime)s] [%(levelname)s] %(message)s', '%Y-%m-%d %H:%M:%S'
Logger 記錄器
Logger是一個樹形層級結構,在使用接口debug,info,warn,error,critical之前必須創建Logger實例
創建方法:
logger = logging.getLogger(logger_name)
Handler 處理器
Handler處理器類型有很多種,比較常用的有三個,StreamHandler,FileHandler,NullHandler,詳情可以訪問Python logging.handlers
創建StreamHandler之后,可以通過使用以下方法設置日志級別,設置格式化器Formatter,增加或刪除過濾器Filter。
創建方法: sh = logging.StreamHandler(stream=None)
FileHandler
創建方法:
fh = logging.FileHandler(filename, mode='a', encoding=None, delay=False)
Formatter 格式化器
使用Formatter對象設置日志信息最后的規則、結構和內容,默認的時間格式為%Y-%m-%d %H:%M:%S。
創建方法:
formatter = logging.Formatter(fmt=None, datefmt=None)
其中,fmt是消息的格式化字符串,datefmt是日期字符串。如果不指明fmt,將使用'%(message)s'。如果不指明datefmt,將使用ISO8601日期格式。
有用的format格式
格式 | 描述 |
---|---|
%(levelno)s | 打印日志級別的數值 |
%(levelname)s | 打印日志級別名稱 |
%(pathname)s | 打印當前執行程序的路徑 |
%(filename)s | 打印當前執行程序名稱 |
%(funcName)s | 打印日志的當前函數 |
%(lineno)d | 打印日志的當前行號 |
%(asctime)s | 打印日志的時間 |
%(thread)d | 打印線程id |
%(threadName)s | 打印線程名稱 |
%(process)d | 打印進程ID |
%(message)s | 打印日志信息 |
Filter 過濾器
Handlers和Loggers可以使用Filters來完成比級別更復雜的過濾。Filter基類只允許特定Logger層次以下的事件。例如用‘A.B’初始化的Filter允許Logger ‘A.B’, ‘A.B.C’, ‘A.B.C.D’, ‘A.B.D’等記錄的事件,logger‘A.BB’, ‘B.A.B’ 等就不行。 如果用空字符串來初始化,所有的事件都接受。
創建方法:
filter = logging.Filter(name='')
配置文件格式說明
%(name)s Logger的名字 %(levelname)s 文本形式的日志級別 %(message)s 用戶輸出的消息 %(asctime)s 字符串形式的當前時間。默認格式是 “2003-07-08 16:49:45,896”。逗號后面的是毫秒 %(levelno)s 數字形式的日志級別 %(pathname)s 調用日志輸出函數的模塊的完整路徑名,可能沒有 %(filename)s 調用日志輸出函數的模塊的文件名 %(module)s 調用日志輸出函數的模塊名 %(funcName)s 調用日志輸出函數的函數名 %(lineno)d 調用日志輸出函數的語句所在的代碼行 %(created)f 當前時間,用UNIX標准的表示時間的浮 點數表示 %(relativeCreated)d 輸出日志信息時來自Logger創建的毫秒數 %(thread)d 線程ID。可能沒有 %(threadName)s 線程名。可能沒有 %(process)d 進程ID。可能沒有
實例代碼:
1、在調試窗口上只打出error以上級別的日志,但是在日志中打出debug以上的信息
1 import logging 2 3 logger = logging.getLogger("simple_example") 4 logger.setLevel(logging.DEBUG) 5 6 # 建立一個filehandler來把日志記錄在文件里,級別為debug以上 7 fh = logging.FileHandler("spam.log") 8 fh.setLevel(logging.DEBUG) 9 10 # 建立一個streamhandler來把日志打在CMD窗口上,級別為error以上 11 ch = logging.StreamHandler() 12 ch.setLevel(logging.ERROR) 13 14 # 設置日志格式 15 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") 16 ch.setFormatter(formatter) 17 fh.setFormatter(formatter) 18 19 # 將相應的handler添加在logger對象中 20 logger.addHandler(ch) 21 logger.addHandler(fh) 22 23 # 開始打日志 24 logger.debug("debug message") 25 logger.info("info message") 26 logger.warn("warn message") 27 logger.error("error message") 28 logger.critical("critical message")
將Logging封裝成一個類
這樣每次使用的時候,只要實例化一個對象就可以了
1 #! /usr/bin/python3 2 3 import logging 4 5 6 class Logger: 7 def __init__(self, path, clevel=logging.DEBUG, Flevel=logging.DEBUG): 8 self.logger = logging.getLogger(path) 9 self.logger.setLevel(logging.DEBUG) 10 fmt = logging.Formatter('[%(asctime)s] [%(levelname)s] %(message)s', '%Y-%m-%d %H:%M:%S') 11 12 # 設置CMD日志 13 sh = logging.StreamHandler() 14 sh.setFormatter(fmt) 15 sh.setLevel(clevel) 16 17 # 設置文件日志 18 fh = logging.FileHandler(path) 19 fh.setFormatter(fmt) 20 fh.setLevel(Flevel) 21 self.logger.addHandler(sh) 22 self.logger.addHandler(fh) 23 24 def debug(self, message): 25 self.logger.debug(message) 26 27 def info(self, message): 28 self.logger.info(message) 29 30 def war(self, message): 31 self.logger.warn(message) 32 33 def error(self, message): 34 self.logger.error(message) 35 36 def cri(self, message): 37 self.logger.critical(message) 38 39 40 if __name__ == '__main__': 41 logyyx = Logger('yyx.log', logging.ERROR, logging.DEBUG) 42 logyyx.debug('一個debug信息') 43 logyyx.info('一個info信息') 44 logyyx.war('一個warning信息') 45 logyyx.error('一個error信息') 46 logyyx.cri('一個致命critical信息')
調試運行結果:
/usr/bin/python3.5 /home/rxf/python3_1000/1000/python3_server/logging封裝/封裝為一個類.py [2017-11-07 10:25:50] [ERROR] 一個error信息 [2017-11-07 10:25:50] [CRITICAL] 一個致命critical信息
日志文件內的內容:
[2017-11-07 10:25:50] [DEBUG] 一個debug信息 [2017-11-07 10:25:50] [INFO] 一個info信息 [2017-11-07 10:25:50] [WARNING] 一個warning信息 [2017-11-07 10:25:50] [ERROR] 一個error信息 [2017-11-07 10:25:50] [CRITICAL] 一個致命critical信息
多模塊使用logging
logging模塊保證在同一個python解釋器內,多次調用logging.getLogger('log_name')都會返回同一個logger實例,即使是在多個模塊的情況下。所以典型的多模塊場景下使用logging的方式是在main模塊中配置logging,這個配置會作用於多個的子模塊,然后在其他模塊中直接通過getLogger獲取Logger對象即可。
logging.conf:
[loggers] keys=root,main [handlers] keys=consoleHandler,fileHandler [formatters] keys=fmt [logger_root] level=DEBUG handlers=consoleHandler [logger_main] level=DEBUG qualname=main handlers=fileHandler [handler_consoleHandler] class=StreamHandler level=DEBUG formatter=fmt args=(sys.stdout,) [handler_fileHandler] class=logging.handlers.RotatingFileHandler level=DEBUG formatter=fmt args=('tst.log','a',20000,5,) [formatter_fmt] format=%(asctime)s - %(name)s - %(levelname)s - %(message)s datefmt=
主模塊main.py
1 import logging 2 import logging.config 3 import mod 4 5 logging.config.fileConfig('logging.conf') 6 root_logger = logging.getLogger('root') 7 root_logger.debug('test root logger...') 8 9 logger = logging.getLogger('main') 10 logger.info('test main logger') 11 logger.info('start import module \'mod\'...') 12 13 14 logger.debug('let\'s test mod.testLogger()') 15 mod.testLogger() 16 17 root_logger.info('finish test...')
子模塊mod.py
import logging import submod logger = logging.getLogger('main.mod') logger.info('logger of mod say something...') def testLogger(): logger.debug('this is mod.testLogger...') submod.tst()
子子模塊submod.py:
import logging logger = logging.getLogger('main.mod.submod') logger.info('logger of submod say something...') def tst(): logger.info('this is submod.tst()...')
然后運行python main.py,控制台輸出:
/usr/bin/python3.5 /home/rxf/python3_1000/1000/python3_server/logging封裝/main.py 2017-11-07 10:53:49,228 - root - DEBUG - test root logger... 2017-11-07 10:53:49,228 - main - INFO - test main logger 2017-11-07 10:53:49,228 - main - INFO - start import module 'mod'... 2017-11-07 10:53:49,229 - main - DEBUG - let's test mod.testLogger() 2017-11-07 10:53:49,229 - main.mod - DEBUG - this is mod.testLogger... 2017-11-07 10:53:49,229 - main.mod.submod - INFO - this is submod.tst()... 2017-11-07 10:53:49,229 - root - INFO - finish test...
再看一下tst.log,logger配置中的輸出的目的地:
2017-11-07 10:56:54,189 - main - INFO - test main logger 2017-11-07 10:56:54,189 - main - INFO - start import module 'mod'... 2017-11-07 10:56:54,189 - main - DEBUG - let's test mod.testLogger() 2017-11-07 10:56:54,189 - main.mod - DEBUG - this is mod.testLogger... 2017-11-07 10:56:54,190 - main.mod.submod - INFO - this is submod.tst()...
tst.log中沒有root logger輸出的信息,因為logging.conf中配置了只有main logger及其子logger使用RotatingFileHandler,而root logger是輸出到標准輸出。
學習思考思路參考:
Logging工作流程
logging模塊使用過程
- 第一次導入logging模塊或使用reload函數重新導入logging模塊,logging模塊中的代碼將被執行,這個過程中將產生logging日志系統的默認配置。
- 自定義配置(可選)。logging標准模塊支持三種配置方式: dictConfig,fileConfig,listen。其中,dictConfig是通過一個字典進行配置Logger,Handler,Filter,Formatter;fileConfig則是通過一個文件進行配置;而listen則監聽一個網絡端口,通過接收網絡數據來進行配置。當然,除了以上集體化配置外,也可以直接調用Logger,Handler等對象中的方法在代碼中來顯式配置。
- 使用logging模塊的全局作用域中的getLogger函數來得到一個Logger對象實例(其參數即是一個字符串,表示Logger對象實例的名字,即通過該名字來得到相應的Logger對象實例)。
- 使用Logger對象中的debug,info,error,warn,critical等方法記錄日志信息。
logging模塊處理流程

- 判斷日志的等級是否大於Logger對象的等級,如果大於,則往下執行,否則,流程結束。
- 產生日志。第一步,判斷是否有異常,如果有,則添加異常信息。第二步,處理日志記錄方法(如debug,info等)中的占位符,即一般的字符串格式化處理。
- 使用注冊到Logger對象中的Filters進行過濾。如果有多個過濾器,則依次過濾;只要有一個過濾器返回假,則過濾結束,且該日志信息將丟棄,不再處理,而處理流程也至此結束。否則,處理流程往下執行。
- 在當前Logger對象中查找Handlers,如果找不到任何Handler,則往上到該Logger對象的父Logger中查找;如果找到一個或多個Handler,則依次用Handler來處理日志信息。但在每個Handler處理日志信息過程中,會首先判斷日志信息的等級是否大於該Handler的等級,如果大於,則往下執行(由Logger對象進入Handler對象中),否則,處理流程結束。
- 執行Handler對象中的filter方法,該方法會依次執行注冊到該Handler對象中的Filter。如果有一個Filter判斷該日志信息為假,則此后的所有Filter都不再執行,而直接將該日志信息丟棄,處理流程結束。
- 使用Formatter類格式化最終的輸出結果。 注:Formatter同上述第2步的字符串格式化不同,它會添加額外的信息,比如日志產生的時間,產生日志的源代碼所在的源文件的路徑等等。
- 真正地輸出日志信息(到網絡,文件,終端,郵件等)。至於輸出到哪個目的地,由Handler的種類來決定。
參考文章:
http://www.jb51.net/article/88449.htm
http://www.jianshu.com/p/feb86c06c4f4