程式語言:Python
Package:logging
官方文件
功能:顯示不同層級的訊息以供 debug
以相同的名稱多次調用 getLogger() 將永遠返回相同 Logger 對象
name 可以是層次結構的值,比如 foo.bar.baz(也可以只是普通的 foo)
層次列表下游的 loggers 是上游 loggers 的子孫
例如,對於名稱為 foo 的 logger,名稱為 foo.bar、foo.bar.baz 和 foo.bam 的 logger 都是 foo 的後代。
建議的構造方式 logging.getLogger(__name__) ,__name__ 是 module 的名字。
Logger.info(msg, *args, **kwargs)
Logger.warning(msg, *args, **kwargs)
Logger.error(msg, *args, **kwargs)
Logger.critical(msg, *args, **kwargs)
Logger.log(lvl, msg, *args, **kwargs)
Logger.exception(msg, *args, **kwargs)
Logger.addFilter(filt)
Logger.removeFilter(filt)
Logger.filters
Logger.filter(record)
Logger.addHandler(hdlr)
Logger.removeHandler(hdlr)
Logger.handlers
Logger.hasHandlers()
Logger.findCaller(stack_info=False)
Logger.handle(record)
Logger.makeRecord(name, lvl, fn, lno, msg, args, exc_info, func=None, extra=None, sinfo=None)
屬性
Python模塊學習——logging
Package:logging
官方文件
功能:顯示不同層級的訊息以供 debug
import logging # 基礎設定 logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s', datefmt='%m-%d %H:%M', handlers = [logging.FileHandler('my.log', 'w', 'utf-8'),]) # 定義 handler 輸出 sys.stderr console = logging.StreamHandler() console.setLevel(logging.INFO) # 設定輸出格式 formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s') # handler 設定輸出格式 console.setFormatter(formatter) # 加入 hander 到 root logger logging.getLogger('').addHandler(console) # root 輸出 logging.info('道可道非常道') # 定義另兩個 logger logger1 = logging.getLogger('myapp.area1') logger2 = logging.getLogger('myapp.area2') logger1.debug('天高地遠') logger1.info('天龍地虎') logger2.warning('天發殺機') logger2.error('地動天搖')終端結果
root : INFO 道可道非常道 myapp.area1 : INFO 天龍地虎 myapp.area2 : WARNING 天發殺機 myapp.area2 : ERROR 地動天搖日誌文件結果
08-20 11:25 root INFO 道可道非常道 08-20 11:25 myapp.area1 DEBUG 天高地遠 08-20 11:25 myapp.area1 INFO 天龍地虎 08-20 11:25 myapp.area2 WARNING 天發殺機 08-20 11:25 myapp.area2 ERROR 地動天搖
Logger Objects
永遠不要直接初始化 Logger Object,應該通過 logging.getLogger(name) 來得到以相同的名稱多次調用 getLogger() 將永遠返回相同 Logger 對象
name 可以是層次結構的值,比如 foo.bar.baz(也可以只是普通的 foo)
層次列表下游的 loggers 是上游 loggers 的子孫
例如,對於名稱為 foo 的 logger,名稱為 foo.bar、foo.bar.baz 和 foo.bam 的 logger 都是 foo 的後代。
建議的構造方式 logging.getLogger(__name__) ,__name__ 是 module 的名字。
- class logging.Logger
- Logger.propagate
- True 表示會再將消息往上傳,但消息將直接傳遞給父代的 handler,不會考慮父代 logger 的級別和 filter
- Logger.setLevel(lvl)
- 設置 logger 的級別為 lvl
- 嚴重程度低於 lvl 的日誌消息將被忽略
- 根 logger 預設為 WARNING,普通 logger 預設為 NOTSET,則會往上尋找,直到父輩非 NOTSET 或是到達根 logger
- lvl
- Logger.isEnabledFor(lvl)
- 是否 lvl 級別的訊息會被處理
- Logger.getEffectiveLevel()
- 得到有效的 lvl
- Logger.getChild(suffix)
- 返回子代
- 兩者相同
- logging.getLogger('abc').getChild('def.ghi')
- logging.getLogger('abc.def.ghi')
- Logger.debug(msg, *args, **kwargs)
- 記錄級別為 DEBUG 的消息
- msg
- 消息格式字符串
- *args
- 通過字符串格式操作符合併到 msg 的參數
- **kwargs
- exc_info
- True:Exception 異常會被添加到消息
- extra
- 傳遞 dict 附加到消息
- extra 的 dict key 不應該與內建的衝突
Level | Numeric value |
---|---|
CRITICAL |
50 |
ERROR |
40 |
WARNING |
30 |
INFO |
20 |
DEBUG |
10 |
NOTSET |
0 |
import logging FORMAT = '%(asctime)-15s %(clientip)s %(user)-8s %(message)s' logging.basicConfig(format=FORMAT) d = {'clientip': '192.168.0.1', 'user': 'fbloggs'} logger = logging.getLogger('tcpserver') logger.warning('Protocol problem: %s', 'connection reset', extra=d) # 2006-02-08 22:20:02,165 192.168.0.1 fbloggs Protocol problem: connection reset
- 記錄級別為 INFO 的消息
- 其參數的解釋與 debug() 相同
- 記錄級別為 WARNING 的消息
- 其參數的解釋與 debug() 相同
- 記錄級別為 ERROR 的消息
- 其參數的解釋與 debug() 相同
- 記錄級別為 CRITICAL 的消息
- 其參數的解釋與 debug() 相同
- 記錄級別為 lvl (int) 的消息
- 其參數的解釋與 debug() 相同
- 記錄以 ERROR 為級別的異常消息,應該只在 excpet 中調用
- 其參數的解釋與 debug() 相同
- 同 exc_info 設為 True
- 加入 filter
import logging class NoParsingFilter(logging.Filter): def filter(self, record): return not record.getMessage().startswith('parsing') logger = logging.getLogger('test') logger.addFilter(NoParsingFilter()) print(logger.filters) # [<__main__.NoParsingFilter object at 0x0000000002AE7C88>]
- 移除 filter
import logging class NoParsingFilter(logging.Filter): def filter(self, record): return not record.getMessage().startswith('parsing') logger = logging.getLogger('test') logger.addFilter(NoParsingFilter()) print(logger.filters) # [<__main__.NoParsingFilter object at 0x0000000002AE7C88>] logger.removeFilter(logger.filters[0]) print(logger.filters) # []
- 此 logger 擁有的 filters
- 對 logRecord 應用 filters
如果符合返回 True,並傳遞給 handlers
任一個 filter 返回 False,將不會做任何處理
- 加入 handler
- 刪除 handler
- 此 logger 擁有的 handlers
- 是否擁有 handlers
- 回傳 4 元素 tuple,檔名, 行數, 函數名 和 stack 訊息,stack_info 需設為 True 才會有 stack 訊息,不然皆為 None
- 處理一個 logRecord,將之傳給該 logger 及其父輩的所有 handler,直到propagate 為 False
- 這是一個工廠方法,可以在子類中覆蓋它來創建特定的 logRecord 實例
Handler Objects
請勿直接初始化 Handler- class Handler(level=NOTSET)
- 可用 handler
- createLock()
- 初始化線程鎖,用以序列化訪問底層的可能非線程安全的I/O機制
- acquire()
- 獲取 createLock() 創建的線程鎖
- release()
- 釋放由 acquire() 獲取的線程鎖
- setLevel(lvl)
- 設置 handler 級別為 lvl
- setFormatter(form)
- 設置 formatter
- addFilter(filt)
- 加入 filter
- removeFilter(filt)
- 移除 filter
- filter(record)
- 將 handler 的 filter 應用到 logRecord
- flush()
- 確保所有的日誌輸出被刷新。base 不做任何事情,需實現
- close()
- 清除 handler 所使用的資源
- handle(record)
- 處理一個 logRecord,符合 filters 則記錄。包含對 I/O 線程鎖的獲取/釋放過程
- handleError(record)
- 在 handler 中調用 emit() 發生異常時,應調用該方法
- format(record)
- 格式化 logRecord
- emit(record)
- 做記錄日誌 logRecord 真正需要的動作。base 只拋出 NotImplementedError,需實現
Filter Objects
可用來過濾訊息- class logging.Filter(name='')
- 返回 Filter 類實例
- name
- 若是一個 logger 的名字,該 logger 及其子代皆通過該過濾器
若是空字符串,所有的事件都允許 - filter(record)
- 特定的記錄是否要記下來? 0 為否,非 0 為是
- 可在此修改字串或添加變數
import logging from random import choice class ContextFilter(logging.Filter): """ This is a filter which injects contextual information into the log. Rather than use actual contextual information, we just use random data in this demo. """ USERS = ['jim', 'fred', 'sheila'] IPS = ['123.231.231.123', '127.0.0.1', '192.168.0.1'] def filter(self, record): record.ip = choice(ContextFilter.IPS) record.user = choice(ContextFilter.USERS) return True if __name__ == '__main__': levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL) logging.basicConfig(level=logging.DEBUG, format='%(asctime)-15s %(name)-5s %(levelname)-8s IP: %(ip)-15s User: %(user)-8s %(message)s') a1 = logging.getLogger('a.b.c') a2 = logging.getLogger('d.e.f') f = ContextFilter() a1.addFilter(f) a2.addFilter(f) a1.debug('A debug message') a1.info('An info message with %s', 'some parameters') for x in range(10): lvl = choice(levels) lvlname = logging.getLevelName(lvl) a2.log(lvl, 'A message at %s level with %d %s', lvlname, 2, 'parameters') # 2010-09-06 22:38:15,292 a.b.c DEBUG IP: 123.231.231.123 User: fred A debug message # 2010-09-06 22:38:15,300 a.b.c INFO IP: 192.168.0.1 User: sheila An info message with some parameters # 2010-09-06 22:38:15,300 d.e.f CRITICAL IP: 127.0.0.1 User: sheila A message at CRITICAL level with 2 parameters # 2010-09-06 22:38:15,300 d.e.f ERROR IP: 127.0.0.1 User: jim A message at ERROR level with 2 parameters # 2010-09-06 22:38:15,300 d.e.f DEBUG IP: 127.0.0.1 User: sheila A message at DEBUG level with 2 parameters # 2010-09-06 22:38:15,300 d.e.f ERROR IP: 123.231.231.123 User: fred A message at ERROR level with 2 parameters # 2010-09-06 22:38:15,300 d.e.f CRITICAL IP: 192.168.0.1 User: jim A message at CRITICAL level with 2 parameters # 2010-09-06 22:38:15,300 d.e.f CRITICAL IP: 127.0.0.1 User: sheila A message at CRITICAL level with 2 parameters # 2010-09-06 22:38:15,300 d.e.f DEBUG IP: 192.168.0.1 User: jim A message at DEBUG level with 2 parameters # 2010-09-06 22:38:15,301 d.e.f ERROR IP: 127.0.0.1 User: sheila A message at ERROR level with 2 parameters # 2010-09-06 22:38:15,301 d.e.f DEBUG IP: 123.231.231.123 User: fred A message at DEBUG level with 2 parameters # 2010-09-06 22:38:15,301 d.e.f INFO IP: 123.231.231.123 User: fred A message at INFO level with 2 parameters
Formatter Objects
設定字串的表示方法- class logging.Formatter(fmt=None, datefmt=None, style='%')
- 回傳一個設定好的 Formatter instance
- formatter = logging.Formatter('{asctime:s}', datefmt='%a, %d %b %Y %H:%M:%S', style='{')
- Sat, 20 Aug 2016 13:46:51
- 參數
- fmt
- 字串格式
- datefmt
- 日期時間格式
- style
- %
- 最原始的字串方法
- {
- string.format
- $
- string.Template
- format(record)
- 格式化 logRecord,並返回字串結果
- formatTime(record, datefmt=None)
- 被 format() 調用,以格式化時間
- formatException(exc_info)
- 格式化 Exception,並返回字串結果
- formatStack(stack_info)
- 格式化 stack 訊息,並返回字串結果
LogRecord Objects
Logger 每次記錄日誌時都會建立 LogRecord 實例,也可以調用 makeLogRecord() 來手動創建 LogRecord 實例(例如從網絡上收到序列化過的事件)- class logging.LogRecord(name, level, pathname, lineno, msg, args, exc_info, func=None, sinfo=None)
- 參數
- name
- 該 LogRecord 的 logger 的名字
- level
- 記錄事件的數字級別(DEBUG、INFO等)
- 被轉成 LogRecord 的兩個屬性
- levelno 表示數字值
- levelname 表示對應的級別名稱
- pathname
- 文件的完整路徑名
- lineno
- 文件的行號
- msg
- 事件的消息,可為格式化字符串
- args
- msg 的參數
- exc_info
- 帶當前異常消息的異常 tuple,如果沒有異常為 None
- func
- 所在的函數/方法名
- sinfo
- stack 資訊
- 方法
- getMessage()
- 返回格式化後的消息
- 屬性
Attribute name | Format | Description |
---|---|---|
args | 不應該被格式化 | 參數 tuple |
exc_info | 不應該被格式化 | 異常 tuple |
msg | 不應該被格式化 | 原始的格式化字符串 |
stack_info | 不應該被格式化 | Stack 訊息 |
asctime | %(asctime)s |
時間日期,默認形式為『2003-07-08 16:49:45,896』(逗號後面的數字表示時間的毫秒部分) |
created | %(created)f |
LogRecord 創建的時間 |
filename | %(filename)s |
module 的文件名 |
funcName | %(funcName)s |
函數名 |
levelname | %(levelname)s |
消息級別 |
levelno | %(levelno)s |
消息級別數字 |
lineno | %(lineno)d |
行數 |
module | %(module)s |
module 名 |
msecs | %(msecs)d |
LogRecord 創建時間中的毫秒部分 |
message | %(message)s |
用戶輸出的消息 |
name | %(name)s |
logger 名字 |
pathname | %(pathname)s |
文件的完整路徑名(如果可用) |
process | %(process)d |
進程 ID (如果可用) |
processName | %(processName)s |
進程名(如果可用) |
relativeCreated | %(relativeCreated)d |
LogRecord 創建離 logging loaded 的毫秒數 |
thread | %(thread)d |
線程 ID (如果可用) |
threadName | %(threadName)s |
線程名字(如果可用) |
LoggerAdapter Objects
給 logger 調用以供重新修改消息- class logging.LoggerAdapter(logger, extra)
- 以底層的 Logger 物件和一個類字典對象來初始化
- process(msg, kwargs)
- 修改傳給日誌調用的消息和關鍵字參數,以插入上下文消息,返回值是(msg, kwargs)
import logging class CustomAdapter(logging.LoggerAdapter): """ This example adapter expects the passed in dict-like object to have a 'connid' key, whose value in brackets is prepended to the log message. """ def process(self, msg, kwargs): return '[%s] %s' % (self.extra['connid'], msg), kwargs logger = logging.getLogger(__name__) adapter = CustomAdapter(logger, {'connid': 123}) # adapter.process('456', {'1':'2'}) # ('[123] 456', {'1': '2'})
Module logging
- 方法
- logging.getLogger(name=None)
- 得到 logger,無名字表示回傳 root
- logging.getLoggerClass()
- 返回標準的 Logger class,或最後一次傳給 setLoggerClass() 的 class
class MyLogger(logging.getLoggerClass()): # ... 覆寫行為
- logging.setLoggerClass(klass)
- 指定 logger 的 class 為 klass,需在建立任何 logger 前調用
- logging.getLogRecordFactory()
- 返回標準建立 LogRecord 的 callable,或最後一次傳給 setLogRecordFactory() 的 callable
- logging.setLogRecordFactory(factory)
- 指定建立 logRecord 的 callable
- factory(name, level, fn, lno, msg, args, exc_info, func=None, sinfo=None, **kwargs)
- name
- logger 名字
- level
- logging level (numeric)
- fn
- file 的完整路徑
- lno
- 行數
- msg
- 事件的消息,可為格式化字符串
- args
- msg 的參數
- exc_info
- An exception tuple or None.
- func
- 函數名字
- sinfo
- stack 資訊
- kwargs
- 額外的 dict
- logging.debug(msg, *args, **kwargs)
- 根 logger 記錄級別為 DEBUG 的消息
- msg
- 消息格式字符串
- *args
- 通過字符串格式操作符合併到 msg 的參數
- **kwargs
- exc_info
- True:Exception 異常會被添加到消息
- extra
- 傳遞 dict 附加到消息
- extra 的 dict key 不應該與內建的衝突
import logging FORMAT = '%(asctime)-15s %(clientip)s %(user)-8s %(message)s' logging.basicConfig(format=FORMAT) d = {'clientip': '192.168.0.1', 'user': 'fbloggs'} logging.warning('Protocol problem: %s', 'connection reset', extra=d) # 2006-02-08 22:20:02,165 192.168.0.1 fbloggs Protocol problem: connection reset
- logging.info(msg, *args, **kwargs)
- 根 logger 記錄級別為 INFO 的消息
- 參數同 debug()
- logging.warning(msg, *args, **kwargs)
- 根 logger 記錄級別為 INFO 的消息
- 參數同 debug()
- logging.error(msg, *args, **kwargs)
- 根 logger 記錄級別為 ERROR 的消息
- 參數同 debug()
- logging.critical(msg, *args, **kwargs)
- 根 logger 記錄級別為 CRITICAL 的消息
- 參數同 debug()
- logging.exception(msg, *args, **kwargs)
- 根 logger 記錄級別為 ERROR 的 Exception 異常消息
- 參數同 debug()
- logging.log(level, msg, *args, **kwargs)
- 根 logger 記錄級別為 level 的消息
- 參數同 debug()
- logging.disable(lvl)
- 對所有 logger 禁用級別 lvl 以下的訊息,包括 lvl
如果 logging.disable(logging.NOTSET) 被調用,所有 logger 回復原本 lvl - logging.addLevelName(lvl, levelName)
- 自定級別,但最好不要這麼做
- logging.getLevelName(lvl)
- 得到級別名字
- logging.makeLogRecord(attrdict)
- 創建並返回 LogRecord 實例,attrdict 定義了其屬性
可以在網絡上傳輸序列化過的 LogRecord 屬性字典,然後在接受端重建LogRecord 實例 - logging.basicConfig(**kwargs)
- 配置基本設定
- 若根 logger 已經配置了 handler 則該函數什麼都不做,故若已初始化,則無法再次初始化
- 參數
- filename
- 存入檔名
- filemode
- 寫入的方式 (w, w+, a+)
- format
- 指定表示的方式
- datefmt
- 時間的表示
- style
- formatter 使用 % or {} or $
- level
- 級別
- stream
- 指定輸出的 stream,與 filename 不相容,無法共用
- handlers
- 指定 handlers,與 filename & stream 不相容,無法共用
- logging.shutdown()
- 關閉所有 handlers,調用後不應該再繼續使用此日誌系統
- logging.lastResort
- debug logging 用
參考
15.7. logging — Python的日誌工具¶Python模塊學習——logging
留言
張貼留言