2、日志级别详解(上):DEBUG、INFO、WARNING 级别的定义、使用场景与最佳实践
日志级别这东西,说白了就是给日志信息贴个标签。告诉系统:「这条消息有多重要?」。我刚开始写代码那会儿,所有日志全用 print() 搞定。结果线上出问题,几千行输出翻得我眼冒金星。后来才明白,级别分得好,排查问题能省一半时间。
今天咱们先聊三个最常用的级别:DEBUG、INFO、WARNING。它们覆盖了从「细到变态」到「有点不对劲」的整个区间。
2.1 DEBUG 级别:开发者的放大镜
定义:DEBUG 是最低级别,记录最详细的信息。说白了,就是给开发者自己看的。
使用场景:
- 变量值的变化过程
- 函数入口和出口的调用参数
- 循环中的中间计算结果
- 条件分支的走向判断
我个人习惯,DEBUG 日志只在开发环境和测试环境开启。线上环境默认关闭。为什么?你想想看,如果线上每秒处理一万个请求,每条请求输出 5 条 DEBUG 日志,那磁盘 IO 直接爆炸。
核心原则:DEBUG 日志是写给「人」看的,不是写给「机器」看的。它的价值在于帮你理解程序运行时发生了什么。
import logging
logging.basicConfig(level=logging.DEBUG)
def calculate_discount(price, user_level):
logging.debug(f"进入 calculate_discount,参数:price={price}, user_level={user_level}")
if user_level == 'vip':
discount = 0.8
logging.debug(f"VIP 用户,折扣系数:{discount}")
else:
discount = 1.0
logging.debug(f"普通用户,无折扣")
result = price * discount
logging.debug(f"计算结果:{result}")
return result
# 调用
calculate_discount(100, 'vip')
最佳实践:
- 不要用 DEBUG 记录业务数据(比如用户密码、身份证号)
- 每条 DEBUG 日志要有明确的目的,别为了打日志而打日志
- 我建议在关键算法或复杂逻辑处加 DEBUG,简单赋值语句就算了
避坑指南:我曾经在一个项目里看到同事把整个数据库查询结果都打成了 DEBUG 日志。结果一张表 10 万行数据,每次查询日志文件暴涨 200MB。记住:DEBUG 日志要「精」不要「多」。
2.2 INFO 级别:系统的运行轨迹
定义:INFO 记录系统正常运行时的关键节点信息。它不像 DEBUG 那么琐碎,但比 WARNING 更乐观——「一切正常,我只是告诉你一声」。
使用场景:
- 服务启动和关闭
- 用户登录、登出
- 订单创建、支付成功
- 定时任务开始和结束
- 配置加载完成
嗯,这里要注意:INFO 日志是运维人员和业务方最常看的。所以信息要清晰、可读性强。别写一堆技术内部术语。
import logging
logging.basicConfig(level=logging.INFO)
def create_order(user_id, product_id, amount):
logging.info(f"开始创建订单,用户:{user_id},商品:{product_id}")
# 模拟业务逻辑
order_id = "ORD" + str(hash(user_id + product_id))[:8]
logging.info(f"订单创建成功,订单号:{order_id},金额:{amount}")
return order_id
def cancel_order(order_id):
logging.info(f"收到取消订单请求,订单号:{order_id}")
# 执行取消逻辑...
logging.info(f"订单 {order_id} 已取消")
最佳实践:
- INFO 日志要包含「谁、什么时间、做了什么、结果如何」
- 每条 INFO 日志最好能对应一个业务动作
- 我习惯在 INFO 里加上请求 ID 或用户 ID,方便后续串联
小技巧:INFO 日志的格式最好统一。比如「[动作] 主体 + 结果」。这样 grep 的时候一搜一个准。我在团队里推行过一套模板,后来排查问题效率提升了不少。
2.3 WARNING 级别:温柔的警报
定义:WARNING 表示「有问题,但系统还能继续运行」。它不像 ERROR 那么严重,但也不能忽视。说白了,就是系统在跟你说:「嘿,注意一下,这事儿不太对劲」。
使用场景:
- 配置使用了默认值(用户没配置,系统用了兜底方案)
- API 调用返回了非预期但可接受的状态码
- 磁盘使用率超过 80%
- 重试机制触发了第一次重试
- 使用了即将废弃的接口
import logging
logging.basicConfig(level=logging.WARNING)
def load_config(config_path):
try:
with open(config_path, 'r') as f:
config = json.load(f)
logging.info(f"配置文件加载成功:{config_path}")
return config
except FileNotFoundError:
logging.warning(f"配置文件 {config_path} 不存在,使用默认配置")
return DEFAULT_CONFIG
except json.JSONDecodeError as e:
logging.error(f"配置文件格式错误:{e}")
raise
def connect_to_database(host, port, timeout=5):
try:
# 尝试连接...
pass
except TimeoutError:
logging.warning(f"数据库连接超时({timeout}s),尝试重试...")
# 重试逻辑...
最佳实践:
- WARNING 一定要有「补救措施」或「后续动作」
- 别把 WARNING 当 ERROR 用——如果系统已经挂了,请用 ERROR
- 我建议给 WARNING 设置告警阈值。比如 5 分钟内出现 100 次 WARNING,就该人工介入了
避坑指南:我曾经见过一个系统,WARNING 日志一天能打几十万条。结果真正出 ERROR 的时候,运维人员已经对 WARNING 麻木了,直接忽略。这叫「狼来了效应」。所以 WARNING 要少而精,别滥用。
2.4 三个级别的对比
| 级别 | 数值 | 谁在看 | 典型场景 | 线上是否开启 |
|---|---|---|---|---|
| DEBUG | 10 | 开发者 | 变量追踪、函数调用 | 否 |
| INFO | 20 | 运维、业务方 | 业务节点、系统状态 | 是 |
| WARNING | 30 | 运维、开发者 | 异常但可恢复 | 是 |
你想想看,这三个级别就像交通信号灯:DEBUG 是路边的小摄像头,记录每一辆车经过;INFO 是路口的电子屏,显示「当前车流量正常」;WARNING 是黄灯闪烁——「注意,前方可能有状况」。
2.5 我的个人建议
做了这么多年日志系统,我总结了几条铁律:
- DEBUG 日志要「舍得删」。功能稳定后,把那些没用的 DEBUG 日志清理掉。别留着占地方。
- INFO 日志要「能看懂」。写 INFO 的时候,假设看日志的人完全不懂你的代码。他能不能通过 INFO 日志还原出业务全貌?
- WARNING 日志要「有行动」。每条 WARNING 都应该对应一个处理动作。要么自动恢复,要么触发告警。
- 级别设置要「灵活」。我习惯在配置文件里控制日志级别,而不是写死在代码里。这样线上出问题时,可以动态调低级别获取更多信息。
最后说一句:日志级别没有绝对的对错。同一个场景,有人用 INFO,有人用 DEBUG,这都正常。关键是团队内部要统一标准。我们团队就有一份《日志规范文档》,新人来了先看这个。效果还不错。
下一节咱们聊 ERROR 和 CRITICAL 级别,那才是真正让人头疼的东西。到时候我会分享几个线上事故的排查经历,保证让你印象深刻。