第三讲:日志基础——从概念到实战
说实话,我刚开始写代码那会儿,根本不知道日志是什么。
出了问题怎么办?print()大法走天下。后来项目越来越大,print出来的东西满天飞,找问题像大海捞针。直到有一次线上事故,我花了整整一个通宵才定位到bug……嗯,从那以后,我老老实实学了日志。
什么是日志?
日志,说白了就是程序运行时的“黑匣子”。
它记录着系统里发生的每一件重要的事:谁在什么时候干了什么,结果如何,有没有出错。你想想看,没有日志的系统就像一架没有飞行记录仪的飞机——出了事你根本不知道原因。
核心观点:日志不是写给机器看的,是写给人看的。尤其是未来的你——那个凌晨三点被电话叫醒的你。
日志级别——你的“报警分级系统”
我见过不少新手把所有信息都打在一个级别上。结果呢?正常信息和致命错误混在一起,根本没法看。
Python的logging模块内置了五个级别,从低到高分别是:
| 级别 | 数值 | 什么时候用 |
|---|---|---|
| DEBUG | 10 | 调试细节,比如变量值、函数入口出口。生产环境一般不开启。 |
| INFO | 20 | 正常操作的信息,比如“用户登录成功”、“订单已创建”。 |
| WARNING | 30 | 虽然能跑,但有点不对劲。比如“磁盘剩余空间不足10%”。 |
| ERROR | 40 | 出错了,但程序还能继续跑。比如“数据库连接失败,重试中”。 |
| CRITICAL | 50 | 系统要挂了!比如“内存耗尽,无法分配新对象”。 |
我的习惯:开发时把日志级别设成DEBUG,上线后调到INFO或WARNING。CRITICAL级别的日志,我会额外发短信或打电话报警——这玩意儿不能等。
Python logging模块——五分钟上手
Python自带的logging模块,功能很强大。但别被它的配置吓到,我们先从最简单的开始。
最简示例:直接打印日志
import logging
logging.debug("这是调试信息")
logging.info("这是普通信息")
logging.warning("这是警告")
logging.error("这是错误")
logging.critical("这是致命错误")
运行一下,你会发现只有WARNING及以上的日志被打印出来了。为什么?因为默认的日志级别是WARNING。
注意:默认的日志输出格式很简陋,只有消息本身,没有时间、没有文件名、没有行号。这在生产环境里基本没法用。
配置日志格式——让日志“说人话”
我个人习惯用basicConfig快速配置,像这样:
import logging
logging.basicConfig(
level=logging.DEBUG,
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 - 系统启动成功
你看,时间、模块名、级别、消息,一目了然。
记录到文件——别让日志飘散在风中
我曾经犯过一个错:日志只打印到控制台,结果程序崩溃后,日志也跟着没了。后来我学乖了,一定要写文件。
import logging
logging.basicConfig(
level=logging.INFO,
filename='app.log',
filemode='a', # 追加模式
format='%(asctime)s - %(levelname)s - %(message)s'
)
logging.info("这条日志会写入文件")
避坑指南:我曾经在生产环境用filemode='w',结果每次重启程序,之前的日志全被清空了。嗯,血的教训。生产环境一定要用'a'(追加模式)。
日志架构的“最小可行模型”
下面这张图,是我做小项目时常用的日志架构。别看简单,够用。
流程很简单:你的代码调用logging.info(),Logger对象收到消息,然后交给Handler去处理——可以同时输出到控制台和文件。
一个完整的实战例子
把上面学到的串起来,写一个能用的日志工具:
import logging
def setup_logger():
"""配置日志系统"""
logger = logging.getLogger("my_app")
logger.setLevel(logging.DEBUG)
# 控制台 Handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
# 文件 Handler
file_handler = logging.FileHandler("app.log", mode='a')
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)
return logger
# 使用
log = setup_logger()
log.info("应用启动")
log.debug("调试信息只在文件里出现")
log.error("出错了,但控制台和文件都会记录")
我的建议:控制台只显示INFO及以上,文件记录所有DEBUG信息。这样开发时看控制台不烦,查问题时翻文件有细节。
小结
日志这东西,说白了就是给未来的自己留条后路。
- 五个级别:DEBUG、INFO、WARNING、ERROR、CRITICAL
- 用
basicConfig快速上手,用getLogger做精细控制 - 控制台+文件双输出,生产环境必备
- 格式里一定要带时间、级别、模块名
下一讲,我们会深入logging模块的架构,聊聊Logger、Handler、Formatter、Filter这四大组件是怎么配合的。到时候你会发现,原来日志系统也可以设计得很优雅。