前言: 本文所指的「Python 學習」涉及的範圍大致是「平時工作業餘,學習or測試一些Python特性,或者庫程式碼邏輯時寫的 toy program」
Python 的 logging 標準庫
標準庫的實現,已經很完備了,總的來說,幾個基本目標已經達到(一個合格的日誌庫必須做到的):
- 執行緒安全
- 單例 + 工廠
而且,對 Logger/ Hanlder/ Formatter 等概念也封裝得很完備,我們一般只需要少許工作便能得到一個能用的 logger。
我的初衷
為啥我還要來封裝呢?仍然是為了方便。
不管是:
- 直接使用 logging.info
- 還是使用 fileConfig (官方已經不推薦)
- 還是 dictConfig (Django專案中就是用這個)
都還是需要做不少工作的(深入使用過的人都知道),況且2和3都是針對正式專案的做法,我們大可不必如何“繁文縟節”。
另外我們平時寫 toy program,免不了要debug/log,我的初衷便是針對這種場景。
另外,我個人還有如下習慣:
- 拒絕在任何語言中使用任何形式的print,來作為除錯工具,其功能性太過於孱弱,僅止步於“夠用”是不夠的
- 極少使用 debugger 進行單步除錯(不管是IDE的debugger,亦或是pdb,或者各種加強版的xxdb)
- log 一般是我的首選除錯手段
- 我一般期望利用 log 看到更多資訊
實現
import logging
from logging import StreamHandler
from logging.handlers import RotatingFileHandler
class LogUtils(object):
VERBOSE_FMT = ('%(levelname)s %(asctime)s %(name)s %(module)s %(process)d %(thread)d '
'%(filename)s_%(lineno)s_%(funcName)s %(message)s')
@classmethod
def get_rotating_file_logger(cls, logger_name, filename, max_bytes=100 * 1024 * 1024, backup_count=10):
"""生成一個基於輪轉檔案的logger
:param logger_name:
:param filename:
:param max_bytes:
:param backup_count:
:return:
"""
logger = logging.getLogger(logger_name)
fmtter = logging.Formatter(fmt=cls.VERBOSE_FMT)
handler = RotatingFileHandler(filename, mode='a', maxBytes=max_bytes,
backupCount=backup_count, encoding='utf-8')
return cls._make_logger(fmtter, handler, logger)
@classmethod
def get_stream_logger(cls, logger_name='CONSOLE_DEBUGGER'):
"""生成一個定向到標準輸出的logger, 可作為除錯使用
:param logger_name:
:return:
"""
logger = logging.getLogger(logger_name)
formatter = logging.Formatter(fmt=cls.VERBOSE_FMT)
handler = StreamHandler()
return cls._make_logger(formatter, handler, logger)
@classmethod
def _make_logger(cls, formatter, handler, logger):
handler.setFormatter(formatter)
handler.setLevel(logging.INFO)
logger.addHandler(handler)
logger.setLevel(logging.INFO)
return logger
複製程式碼
使用
# 我一般放在專案的 utils/log_util.py 中 :)
from log_util import LogUtils
LOGGER = LogUtils.get_stream_logger()
def func(a, b, c):
LOGGER.info('====func start. %s ====' % [a, b, c])
# 複雜的程式碼邏輯
LOGGER.info('==== func end. ====')
return something
複製程式碼
主要應用
- 除錯包含非同步邏輯的程式碼,典型如回撥,async,協程程式
- 多執行緒程式碼,學習多執行緒的使用
- 除錯多執行緒死鎖和資源競爭問題