Python 的 logging:别再用 print 调试生产问题
Python 的 logging:别再用 print 调试生产问题
写 Python 脚本时,最顺手的调试办法就是到处加 print。本地跑一次能看到输出,问题就解决了——可一旦脚本跑到服务器上、跑进定时任务里,print 的输出就不知道去哪了:有的被重定向丢了,有的混在几千行输出里找不到,有的根本没留下任何记录。等线上出问题再想复现,往往连第一现场都找不回来。print 适合临时看一眼,不适合当作程序的记录方式,logging 才是为这件事准备的。
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
logger.info("开始处理订单 %s", order_id)
logger.warning("库存不足,仅剩 %s 件", stock)
第一点好处是分级。DEBUG、INFO、WARNING、ERROR、CRITICAL 五个级别,先把“这条信息有多重要”定下来:平时只看 INFO 以上,排查问题时再把级别调到 DEBUG,不用改代码、不用删 print,改一行配置就行。临时加的 print 没有级别,重要和不重要的输出全混在一起,找一条关键记录得翻半天。
第二个要点是输出去向可以分开管。print 只能去标准输出,logging 可以同时写控制台和文件,文件还能按大小或按天轮转,不怕日志把磁盘写满:
handler = logging.FileHandler("app.log", encoding="utf-8")
handler.setLevel(logging.WARNING)
logger.addHandler(handler)
控制台看实时动向,文件留长期记录;线上只记 WARNING 以上,本地调试再开 DEBUG,两边互不干扰。轮转用 RotatingFileHandler 或 TimedRotatingFileHandler,旧文件自动归档,不用自己写清理逻辑。
第三个容易踩的坑是格式。print 打出来只有值本身,出了问题不知道是哪一行、什么时候打的。logging 的格式里把时间、级别、模块名都带上,查问题时先看时间戳定位,再看模块名缩小范围:
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s %(name)s: %(message)s",
)
还有两个细节值得先讲清。一是不要用 f-string 或 % 先拼好再传进去,写成 logger.info("订单 %s", order_id) 这种“格式串 + 参数”的形式:这条日志因为级别不够被过滤掉时,字符串根本不会拼接,省下的开销在循环里很可观。二是异常不要只记一句话,logger.exception("处理订单失败") 会在 except 块里自动把完整的堆栈打出来,比只记 str(e) 有用得多——很多线上问题,缺的就是当时那几行堆栈。
最后说一个常见误区:logging 不是只能在“大项目”里用。哪怕只是一个定时跑的脚本,第一次上线就把 basicConfig 配好、把关键步骤记成 INFO、把失败记成带堆栈的 ERROR,以后排查问题就不用再靠猜。print 是写给当时正在看屏幕的人,logging 是写给以后来查问题的人——很多时候,那个人就是几天后的自己。
- 点赞
- 收藏
- 关注作者
评论(0)