用 Python logging 写可落盘的日志:三段式一次讲清
很多新手调试都靠 print,程序一跑满屏都是输出,问题却不好找。print 只能看到当时发生了什么,程序关掉就没了;上线前想删掉它,又怕删错一行影响逻辑;更麻烦的是,它没有级别,调试信息和真正的错误混在一起,出了问题只能从头翻起。logging 就是为了解决这些问题而存在的:它把“记录”变成一件可以配置的事,而不是临时插进代码的语句。
三段式:Formatter、Handler 与 Logger
理解 logging,只要抓住三段式。Logger 是我们直接调用的入口,负责发出一条日志;Handler 决定这条日志往哪里去,是控制台还是文件;Formatter 决定它长什么样,比如时间、级别、模块名和具体内容。三者分工清楚,改输出位置不用改业务代码,改格式也不用动调用处。一个 Logger 可以挂多个 Handler,这正是分流的基础。
按级别分流到控制台与文件
级别是分流的第一步。DEBUG 记录排查细节,INFO 记录正常流程,WARNING 提醒值得注意但还能继续的情况,ERROR 表示某一步失败了,CRITICAL 则意味着程序可能无法继续。给 Logger 设一个总级别,再给每个 Handler 各设一个级别,就能实现分流:控制台只看 INFO 以上,文件里则从 DEBUG 开始全量保存。这样开发时看屏幕不吵,事后查文件又有据可查。Formatter 里的时间格式建议固定下来,包含年月日时分秒,事后按时间定位问题会快很多。
一个可以直接跑的小例子
import logging
def get_logger():
logger = logging.getLogger("demo")
logger.setLevel(logging.DEBUG)
if logger.handlers:
return logger
fmt = logging.Formatter("%(asctime)s %(levelname)s %(name)s: %(message)s")
console = logging.StreamHandler()
console.setLevel(logging.INFO)
console.setFormatter(fmt)
file_handler = logging.FileHandler("demo.log", encoding="utf-8")
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(fmt)
logger.addHandler(console)
logger.addHandler(file_handler)
return logger
log = get_logger()
log.debug("这是调试细节,只写进文件")
log.info("程序正常启动")
log.error("某一步失败,需要人工处理")
运行后,控制台会看到 INFO 与 ERROR,文件 demo.log 里则能看到 DEBUG、INFO 与 ERROR 三条。注意例子里没有用 print,日志去向全部由 Handler 控制,改一行级别就能改变输出范围。
怎样避免重复输出
新手最容易踩的坑是重复输出:同一个 Logger 被反复添加 Handler,或者同时用了 basicConfig 与手工添加 Handler,同一条日志就会出现两遍。办法也很简单:把配置集中到一个函数里,只在程序入口调用一次;添加 Handler 前先检查 logger.handlers 是否为空;模块里只用 getLogger(name),不要在每个函数里重新配置。日志一旦重复,排查成本反而比 print 更高,所以“只配一次”是必须守住的底线。
小结
从 print 换到 logging,并不是换一个输出函数,而是换一种态度:让每一次运行都留下可查、可分级、可落盘的痕迹。先搭好三段式,再按级别分流,最后守住只配一次的规则,你的程序就有了自己的“黑匣子”。下次出问题时,不用再凭记忆猜测,而是直接翻日志找答案。
- 点赞
- 收藏
- 关注作者
评论(0)