Python logging模块入门:基本配置、输出到控制台、日志格式设置

日志这东西,说白了就是程序的「黑匣子」。

我刚入行那会儿,排查问题全靠 print(),满屏飘着「这里执行了」「那里报错了」。后来线上出了个诡异 bug,print 语句被同事顺手注释掉了,我愣是找了三个小时才定位到问题。从那以后,我养成了一个习惯——任何项目第一件事,先把日志框架搭好。

今天咱们就从 Python 自带的 logging 模块开始,把最基础的东西吃透。

为什么不用 print?

你想想看,print() 输出到控制台,程序一关就没了。线上环境你总不能一直盯着终端吧?而且 print 没有级别区分,调试信息、警告、错误全混在一起,看着就头疼。

logging 模块就不一样了:

  • 可以控制输出到文件、控制台、网络等不同地方
  • 有 DEBUG、INFO、WARNING、ERROR、CRITICAL 五个级别
  • 可以自定义格式,带上时间、文件名、行号
  • 生产环境可以动态调整日志级别,不用改代码

核心观点:print 是调试工具,logging 才是运维工具。别混着用。

最简单的配置:输出到控制台

先来个最基础的例子,看看 logging 长什么样:

import logging

logging.basicConfig(level=logging.INFO)
logging.debug('这条不会显示')
logging.info('程序启动了')
logging.warning('磁盘空间不足')
logging.error('数据库连接失败')

运行一下,控制台会输出:

INFO:root:程序启动了
WARNING:root:磁盘空间不足
ERROR:root:数据库连接失败

看到没?DEBUG 级别的消息被过滤掉了。因为咱们设置了 level=logging.INFO,只有 INFO 及以上的日志才会输出。

小技巧:开发阶段我习惯设成 DEBUG,上线后改成 WARNING 或 ERROR。这样既能看到调试信息,又不会刷爆日志文件。

日志格式设置:让日志更「可读」

刚才的输出太简陋了,连时间都没有。线上排查问题,时间戳是命根子。咱们来格式化一下:

import logging

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

logging.info('用户登录成功')

输出变成:

2025-01-15 14:30:22 - root - INFO - 用户登录成功

嗯,这下清楚多了。格式字符串里常用的占位符有这些:

占位符 含义 示例
%(asctime)s 时间戳 2025-01-15 14:30:22
%(name)s 日志器名称 root
%(levelname)s 日志级别 INFO
%(message)s 日志内容 用户登录成功
%(filename)s 文件名 app.py
%(lineno)d 行号 42

注意:datefmt 参数只对 %(asctime)s 生效。如果不指定,默认格式是 2025-01-15 14:30:22,123(带毫秒)。我个人习惯去掉毫秒,看着清爽。

实战:一个完整的入门配置

把上面这些串起来,写一个生产环境可用的基础配置:

import logging

logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s | %(levelname)-8s | %(filename)s:%(lineno)-4d | %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

logger = logging.getLogger(__name__)

logger.debug('开始处理请求')
logger.info('请求参数: %s', {'user_id': 123})
logger.warning('接口响应时间超过阈值: 2.3s')
logger.error('数据库查询失败: timeout')

输出效果:

2025-01-15 14:35:10 | DEBUG    | app.py:10   | 开始处理请求
2025-01-15 14:35:10 | INFO     | app.py:11   | 请求参数: {'user_id': 123}
2025-01-15 14:35:10 | WARNING  | app.py:12   | 接口响应时间超过阈值: 2.3s
2025-01-15 14:35:10 | ERROR    | app.py:13   | 数据库查询失败: timeout

这里我用了 %-8s%-4d 做对齐,日志看起来整整齐齐的。线上排查时,一眼就能扫到 ERROR 级别的日志。

避坑指南:我曾经在配置里忘了加 level 参数,结果 DEBUG 日志全打出来了,把磁盘撑爆了。嗯,线上环境一定要记得设日志级别,最好配合日志轮转一起用。

关于日志器名称

刚才代码里用了 getLogger(__name__),这玩意儿有啥用?

说白了,__name__ 就是当前模块的名字。如果你的文件叫 user_service.py,那日志器名字就是 user_service。这样在多模块项目里,一看日志就知道是哪个模块出的问题。

我习惯在项目入口文件用 root 日志器,其他模块各自创建自己的日志器。这样既能统一配置,又能区分来源。

小结

今天咱们干了三件事:

  • basicConfig 快速配置日志
  • 把日志输出到控制台
  • 自定义了日志格式,带上时间、级别、位置信息

这些基础操作,够你应付大部分日常开发了。下一节咱们聊聊怎么把日志写到文件里,以及如何处理日志文件轮转——毕竟线上日志可不能一直往一个文件里怼。

课后练习:试着写一个脚本,模拟用户登录流程,分别用 DEBUG、INFO、WARNING、ERROR 记录不同阶段的日志。格式要求:时间精确到秒,显示文件名和行号。