4、日志级别管理:DEBUG、INFO、WARNING、ERROR、CRITICAL级别定义与使用场景

日志级别这东西,说白了就是给日志信息贴个标签。你想想看,系统跑起来每天产生几万条日志,要是没个级别区分,排查问题就像大海捞针。我刚开始做运维那会儿,就吃过这个亏——日志全打在一个文件里,出问题的时候翻得眼都快瞎了。

Python 的 logging 模块内置了五个标准级别。嗯,这里要注意,它们是有优先级的。从低到高分别是:DEBUG、INFO、WARNING、ERROR、CRITICAL。每个级别代表的信息严重程度不一样,使用场景也完全不同。

核心原则:生产环境不要开 DEBUG,开发环境不要关 ERROR。这个原则我踩过坑才真正理解。

4.1 五个级别的定义与数值

每个级别其实对应一个数字,Python 内部就是靠这个数字来比较优先级。数值越小,级别越低。

级别 数值 什么时候用
DEBUG 10 开发调试时,打印变量值、函数入口出口
INFO 20 记录系统正常运行的关键节点信息
WARNING 30 系统还能跑,但有点不对劲,需要关注
ERROR 40 某个功能出错了,但系统整体没挂
CRITICAL 50 系统要挂了,必须立刻处理

我个人习惯把 DEBUG 和 INFO 归为「开发级」,WARNING 及以上归为「运维级」。这样分的好处是,线上只看 WARNING 以上的日志,开发环境才看全部。

4.2 DEBUG 级别:开发者的放大镜

DEBUG 级别的日志,说白了就是给开发者自己看的。我在项目中遇到过好几次,线上出了问题,结果发现 DEBUG 日志全开着,磁盘 IO 直接被打满。所以生产环境一定要关掉 DEBUG。

什么时候用 DEBUG?

  • 函数调用时的入参和返回值
  • 循环中的中间变量
  • 条件分支的走向判断
  • 第三方接口的原始返回数据
import logging

logger = logging.getLogger(__name__)

def calculate_discount(price, user_level):
    logger.debug(f"入参: price={price}, user_level={user_level}")
    
    if user_level == 'vip':
        discount = 0.8
        logger.debug(f"VIP用户,折扣={discount}")
    else:
        discount = 1.0
    
    result = price * discount
    logger.debug(f"计算结果: {result}")
    return result

我的小技巧:DEBUG 日志里不要打印密码、token 等敏感信息。我曾经见过同事把数据库密码打到了 DEBUG 日志里,结果日志文件被误传到 GitHub...嗯,那场面很尴尬。

4.3 INFO 级别:系统的脉搏

INFO 日志记录的是「系统正常运行时,我想知道的信息」。比如服务启动成功、用户登录、订单创建成功这些。它不表示有问题,只是告诉你系统在正常工作。

我个人习惯把 INFO 日志当作系统的「关键节点标记」。比如一个支付流程,我会在「收到请求」「校验通过」「扣款成功」「返回结果」这四个节点各打一条 INFO 日志。这样出了问题,一看日志就知道卡在哪一步。

def process_payment(user_id, amount):
    logger.info(f"收到支付请求: user={user_id}, amount={amount}")
    
    if not validate_user(user_id):
        logger.error(f"用户校验失败: {user_id}")
        return False
    
    logger.info(f"用户校验通过: {user_id}")
    
    result = deduct_balance(user_id, amount)
    if result:
        logger.info(f"扣款成功: user={user_id}, amount={amount}")
    else:
        logger.error(f"扣款失败: user={user_id}, amount={amount}")
    
    return result

注意:INFO 日志不要打得太频繁。比如循环里每处理一条记录就打一条 INFO,那日志量会爆炸。循环里的细节用 DEBUG,循环外的统计用 INFO。

4.4 WARNING 级别:预警信号

WARNING 表示「系统还能跑,但有点不对劲」。它不像 ERROR 那样需要立即处理,但也不能忽视。我建议运维同学每天花 10 分钟扫一遍 WARNING 日志,很多大问题其实是从小警告开始的。

常见的使用场景:

  • 磁盘使用率超过 80%
  • 接口响应时间超过阈值但还没超时
  • 重试机制被触发
  • 使用了即将废弃的 API
  • 配置项使用了默认值
def check_disk_usage():
    usage = get_disk_usage('/data')
    if usage > 90:
        logger.critical(f"磁盘使用率超过90%: {usage}%")
    elif usage > 80:
        logger.warning(f"磁盘使用率超过80%: {usage}%")
    else:
        logger.info(f"磁盘使用率正常: {usage}%")

4.5 ERROR 级别:出事了,但别慌

ERROR 表示某个功能出错了,但系统整体还在运行。比如用户请求超时、数据库连接失败、第三方接口返回错误码。这时候需要记录详细的错误信息,方便排查。

我曾经犯过一个错误:在 except 块里只打了 ERROR 日志,没打印异常堆栈。结果线上出了问题,日志里只有一句「数据库连接失败」,完全不知道是哪行代码、什么上下文导致的。从那以后,我所有的 ERROR 日志都会带上异常信息。

try:
    result = db.query(sql)
except Exception as e:
    logger.error(f"数据库查询失败: {sql}", exc_info=True)
    # exc_info=True 会打印完整的异常堆栈
    return None

关键点:ERROR 日志一定要包含足够的信息来复现问题。至少要有:错误描述、异常堆栈、关键上下文(如用户 ID、请求参数)。

4.6 CRITICAL 级别:系统要挂了

CRITICAL 是最高级别,表示系统已经无法正常工作了。比如数据库完全不可用、内存溢出、关键服务宕机。这时候不光要记录日志,通常还要触发告警——发短信、打电话、钉钉群艾特所有人。

我个人习惯把 CRITICAL 和告警系统绑定。一旦出现 CRITICAL 日志,自动触发电话告警。因为这种级别的问题,晚处理一分钟可能就损失惨重。

def health_check():
    if not check_database():
        logger.critical("数据库完全不可用,系统无法提供服务")
        alert_ops_team("数据库宕机", level="critical")
        return False
    
    if not check_cache():
        logger.warning("缓存服务不可用,系统降级运行")
        return True
    
    logger.info("系统健康检查通过")
    return True

4.7 级别过滤与动态调整

实际项目中,我们不会把所有级别的日志都输出到同一个地方。常见的做法是:

  • 文件日志:记录 INFO 及以上级别
  • 控制台日志:开发时用 DEBUG,线上用 WARNING
  • 告警系统:只接收 ERROR 和 CRITICAL

而且,日志级别应该是可以动态调整的。我参与过的一个项目,线上突然出现诡异问题,我们通过运维后台把某个模块的日志级别临时调成了 DEBUG,等定位到问题后再改回去。整个过程不需要重启服务,非常方便。

import logging

# 设置不同 handler 的不同级别
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.INFO)

console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)

# 告警 handler 只处理 ERROR 及以上
alert_handler = AlertHandler()
alert_handler.setLevel(logging.ERROR)

logger = logging.getLogger('my_app')
logger.addHandler(file_handler)
logger.addHandler(console_handler)
logger.addHandler(alert_handler)
logger.setLevel(logging.DEBUG)  # 全局最低级别

4.8 知识体系图

下面这张图展示了日志级别的核心逻辑和流转关系。你可以看到不同级别对应不同的处理方式和输出目标。

日志级别管理体系 DEBUG INFO WARNING ERROR CRITICAL 开发调试 变量追踪 函数调用 中间结果 运行记录 服务启动 关键节点 业务统计 预警信号 资源告警 性能下降 重试触发 功能故障 请求失败 连接异常 业务错误 文件日志 INFO 及以上 控制台 开发: DEBUG / 线上: WARNING 告警系统 ERROR 及以上 级别越高 → 严重程度越高 → 处理优先级越高 💡 日志级别可动态调整,无需重启服务

4.9 避坑指南

最后,分享几个我踩过的坑:

  • 不要用 print 代替 logging:print 没法控制级别,也没法输出到文件。我曾经接手过一个项目,全是用 print 打日志,线上排查问题的时候,日志全在控制台,早就被冲走了。
  • ERROR 日志一定要带堆栈:光打一句「出错了」等于没打。加上 exc_info=True 或者 traceback.format_exc(),能省下你半天排查时间。
  • 别把敏感信息打进去:密码、身份证、银行卡号这些,打死也别往日志里写。我见过有人把用户密码明文打到了 DEBUG 日志里,结果日志文件被运维同学拿去分析,隐私全泄露了。
  • 日志级别要能动态调整:线上出了问题,能临时把某个模块的日志级别调成 DEBUG,定位完再改回去。这个能力在微服务架构下尤其重要。

我的建议:刚开始写日志的时候,多花 10 秒钟想想「这条日志该用哪个级别」。养成习惯后,你会发现排查问题的效率提升不止一倍。

专注资料整理