4、Python日志处理基础:Python logging模块、日志配置、日志格式化、日志文件管理
各位同学,咱们今天聊聊Python的日志处理。说实话,日志这东西,平时你可能觉得它不起眼,但一旦线上出问题,它就是你的救命稻草。我在交易系统运维这行干了快十年,见过太多因为日志没打好,排查问题查到崩溃的案例。所以这一章,咱们把Python的logging模块彻底吃透。
4.1 为什么非要用logging模块?
你可能会说:「我直接用print不就行了?」嗯,我刚开始也是这么干的。但后来发现,print有几个硬伤:
- 没法分级:你没法区分「调试信息」和「错误信息」
- 没法控制输出:生产环境你总不能把调试信息也打出来吧?
- 没法持久化:程序一关,print的内容就没了
logging模块就是来解决这些问题的。它提供了日志分级、灵活的输出控制、以及文件管理能力。说白了,它就是print的Pro Max版。
核心概念:logging模块有四个核心组件——Logger(记录器)、Handler(处理器)、Formatter(格式化器)、Filter(过滤器)。它们各司其职,组合起来就能搭建一套完整的日志系统。
4.2 快速上手:最简单的日志配置
咱们先看一个最基础的例子。我个人习惯,学任何新东西都先跑通最小示例:
import logging
# 基本配置
logging.basicConfig(level=logging.INFO)
logging.debug('这条不会输出')
logging.info('系统启动成功')
logging.warning('磁盘空间不足')
logging.error('数据库连接失败')
你看,这里我只设了level=logging.INFO,所以debug级别的日志就被过滤掉了。这就是分级的好处——你可以在开发时设成DEBUG,上线后改成INFO或WARNING。
小技巧:我在项目中遇到过,有人把日志级别设成DEBUG就上线了,结果一天下来日志文件涨到几个G。所以上线前一定要检查日志级别!
4.3 日志配置的三种方式
配置logging,说白了就三种路子。我按推荐程度排个序:
4.3.1 代码直接配置(适合小项目)
import logging
logger = logging.getLogger('trade_system')
logger.setLevel(logging.DEBUG)
# 控制台输出
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
# 文件输出
file_handler = logging.FileHandler('trade.log')
file_handler.setLevel(logging.DEBUG)
# 格式化
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)
logger.addHandler(console_handler)
logger.addHandler(file_handler)
这种方式最直观,但缺点也很明显——配置和业务代码混在一起,不好维护。
4.3.2 配置文件方式(推荐)
我个人最喜欢这种方式。把配置写在logging.conf里,代码里一行就搞定:
[loggers]
keys=root,trade
[handlers]
keys=console_handler,file_handler
[formatters]
keys=simple_formatter,detail_formatter
[logger_root]
level=WARNING
handlers=console_handler
[logger_trade]
level=DEBUG
handlers=console_handler,file_handler
qualname=trade
propagate=0
[handler_console_handler]
class=StreamHandler
level=INFO
formatter=simple_formatter
args=(sys.stdout,)
[handler_file_handler]
class=handlers.RotatingFileHandler
level=DEBUG
formatter=detail_formatter
args=('trade.log', 'a', 10*1024*1024, 5)
[formatter_simple_formatter]
format=%(levelname)s - %(message)s
[formatter_detail_formatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
代码里这样调用:
import logging.config
logging.config.fileConfig('logging.conf')
logger = logging.getLogger('trade')
logger.info('交易系统初始化完成')
你看,配置和代码完全分离了。改日志格式、调级别,改配置文件就行,不用动代码。
4.3.3 字典配置方式(最灵活)
这种方式适合需要动态生成配置的场景。比如根据环境变量决定日志级别:
import logging.config
config = {
'version': 1,
'formatters': {
'detail': {
'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
}
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'level': 'INFO',
'formatter': 'detail'
},
'file': {
'class': 'logging.handlers.RotatingFileHandler',
'level': 'DEBUG',
'formatter': 'detail',
'filename': 'trade.log',
'maxBytes': 10485760,
'backupCount': 5
}
},
'loggers': {
'trade': {
'level': 'DEBUG',
'handlers': ['console', 'file']
}
}
}
logging.config.dictConfig(config)
4.4 日志格式化:别让信息白打了
日志格式这事,看着简单,其实坑不少。我见过有人打日志就一句话「系统出错」,连时间都没有。你想想看,排查问题的时候,你连什么时候出的错都不知道,怎么查?
常用的格式化字段:
| 字段 | 含义 | 示例 |
|---|---|---|
| %(asctime)s | 时间戳 | 2024-01-15 14:30:22,123 |
| %(name)s | Logger名称 | trade.order |
| %(levelname)s | 日志级别 | INFO, ERROR |
| %(message)s | 日志内容 | 订单提交成功 |
| %(filename)s | 文件名 | order_service.py |
| %(lineno)d | 行号 | 42 |
| %(funcName)s | 函数名 | submit_order |
我建议交易系统的日志格式至少包含:时间、Logger名、级别、文件名、行号、消息。这样定位问题最快。
formatter = logging.Formatter(
'%(asctime)s | %(name)s | %(levelname)s | %(filename)s:%(lineno)d | %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
4.5 日志文件管理:别让日志撑爆磁盘
这是个大问题。我曾经遇到过,一个交易系统的日志文件一天涨了50G,直接把磁盘撑爆了,导致系统崩溃。嗯,从那以后,我对日志文件管理就特别上心。
4.5.1 RotatingFileHandler:按大小轮转
from logging.handlers import RotatingFileHandler
handler = RotatingFileHandler(
'trade.log', # 文件名
maxBytes=10485760, # 10MB
backupCount=5 # 保留5个备份
)
当trade.log达到10MB时,会自动重命名为trade.log.1,然后新建一个trade.log。最多保留trade.log.1到trade.log.5。
4.5.2 TimedRotatingFileHandler:按时间轮转
from logging.handlers import TimedRotatingFileHandler
handler = TimedRotatingFileHandler(
'trade.log',
when='midnight', # 每天午夜轮转
backupCount=30 # 保留30天
)
这个适合交易系统——每天生成一个日志文件,保留30天。既方便按天排查,又不会占用太多空间。
注意:日志轮转时,如果程序还在写文件,可能会丢失少量日志。建议在轮转时设置delay=True,或者使用ConcurrentRotatingFileHandler(第三方库)来处理并发写入。
4.6 实战:交易系统的日志配置
最后,我分享一个我在交易系统中实际使用的配置。它结合了控制台输出和文件输出,文件按天轮转,保留30天:
import logging
import logging.handlers
import sys
def setup_logging(log_file='trade_system.log'):
# 创建Logger
logger = logging.getLogger('trade')
logger.setLevel(logging.DEBUG)
# 控制台Handler
console = logging.StreamHandler(sys.stdout)
console.setLevel(logging.INFO)
console_fmt = logging.Formatter(
'%(levelname)s | %(message)s'
)
console.setFormatter(console_fmt)
# 文件Handler(按天轮转)
file_handler = logging.handlers.TimedRotatingFileHandler(
log_file,
when='midnight',
backupCount=30,
encoding='utf-8'
)
file_handler.setLevel(logging.DEBUG)
file_fmt = logging.Formatter(
'%(asctime)s | %(name)s | %(levelname)s | '
'%(filename)s:%(lineno)d | %(message)s'
)
file_handler.setFormatter(file_fmt)
# 添加Handler
logger.addHandler(console)
logger.addHandler(file_handler)
return logger
# 使用
logger = setup_logging()
logger.info('交易系统启动')
logger.error('订单处理异常', exc_info=True)
避坑指南:我曾经在日志里打了敏感信息(比如用户密码),结果日志文件被运维同事看到了。所以,日志里千万别打密码、身份证号、银行卡号等敏感数据。如果一定要打,记得脱敏处理。
4.7 本章小结
这一章咱们把Python logging模块的核心内容过了一遍。从基础配置到文件管理,从格式化到实战配置。说实话,日志这东西,平时觉得简单,但真正用好、用对,还是需要花点心思的。
记住几个关键点:
- 用配置文件分离配置和代码
- 日志格式要包含时间、级别、位置信息
- 生产环境一定要用轮转Handler,防止磁盘爆满
- 别打敏感信息
下一章,咱们会深入日志分析的实际场景,看看怎么从海量日志里快速定位问题。到时候,你会感谢今天认真学logging的自己。
公众号:蓝海资料掘金营,微信deep3321