前言: 本文所指的「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,协程程序
- 多线程代码,学习多线程的使用
- 调试多线程死锁和资源竞争问题