2、日志级别详解(上):DEBUG、INFO、WARNING 级别的定义、使用场景与最佳实践

日志级别这东西,说白了就是给日志信息贴个标签。告诉系统:「这条消息有多重要?」。我刚开始写代码那会儿,所有日志全用 print() 搞定。结果线上出问题,几千行输出翻得我眼冒金星。后来才明白,级别分得好,排查问题能省一半时间。

今天咱们先聊三个最常用的级别:DEBUGINFOWARNING。它们覆盖了从「细到变态」到「有点不对劲」的整个区间。

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 我的个人建议

做了这么多年日志系统,我总结了几条铁律:

  1. DEBUG 日志要「舍得删」。功能稳定后,把那些没用的 DEBUG 日志清理掉。别留着占地方。
  2. INFO 日志要「能看懂」。写 INFO 的时候,假设看日志的人完全不懂你的代码。他能不能通过 INFO 日志还原出业务全貌?
  3. WARNING 日志要「有行动」。每条 WARNING 都应该对应一个处理动作。要么自动恢复,要么触发告警。
  4. 级别设置要「灵活」。我习惯在配置文件里控制日志级别,而不是写死在代码里。这样线上出问题时,可以动态调低级别获取更多信息。

最后说一句:日志级别没有绝对的对错。同一个场景,有人用 INFO,有人用 DEBUG,这都正常。关键是团队内部要统一标准。我们团队就有一份《日志规范文档》,新人来了先看这个。效果还不错。

下一节咱们聊 ERROR 和 CRITICAL 级别,那才是真正让人头疼的东西。到时候我会分享几个线上事故的排查经历,保证让你印象深刻。