日志级别详解:DEBUG、INFO、WARN、ERROR、FATAL

日志级别这东西,说白了就是给日志信息贴个标签。告诉系统:这条消息有多重要?该不该记?记到哪里?

我刚带团队那会儿,见过太多人把日志当流水账。所有信息全往一个文件里塞,结果线上出问题,翻日志翻到手抽筋。嗯,今天我们就把这个事儿彻底聊透。

五个级别的含义

标准日志体系里,一般分五个级别。从低到高,严重程度递增:

级别 含义 典型场景
DEBUG 调试信息,开发阶段用 变量值、函数入口出口、循环次数
INFO 常规运行信息 服务启动、请求到达、任务完成
WARN 警告,不影响运行但值得关注 配置降级、重试次数超限、磁盘接近满
ERROR 错误,功能受影响但系统未崩溃 数据库连接失败、请求超时、业务异常
FATAL 致命错误,系统即将或已经崩溃 内存溢出、关键服务不可用、启动失败

核心原则:生产环境只开 INFO 及以上。DEBUG 日志量太大,会拖垮磁盘 IO。我见过有人生产环境开 DEBUG,结果一天写了几十 G 日志,直接把磁盘撑爆了。

每个级别的使用场景

DEBUG:开发者的放大镜

DEBUG 级别的日志,是给你自己看的。排查问题时打开,定位完就关掉。

我个人习惯,在关键逻辑的入口出口各打一条 DEBUG。比如:

logger.debug("用户 {} 开始执行订单创建,参数:{}", userId, orderParams);
// ... 业务逻辑 ...
logger.debug("用户 {} 订单创建完成,订单号:{}", userId, orderId);

这样一旦出问题,我能快速知道代码走到哪一步了。哪个环节没执行,一目了然。

小技巧:DEBUG 日志里别打印密码、token 等敏感信息。我曾经在排查问题时,不小心把用户密码打进了日志,结果被安全团队约谈了一下午。

INFO:系统的脉搏

INFO 级别的日志,记录系统正常运行的关键节点。运维和监控系统主要看这个。

哪些该打 INFO?我总结了几条:

  • 服务启动/停止
  • 关键业务流程完成(订单创建、支付成功)
  • 定时任务执行结果
  • 外部接口调用结果
  • 配置加载完成
logger.info("订单服务启动完成,监听端口:{}", port);
logger.info("用户 {} 支付成功,金额:{}", userId, amount);

INFO 日志要让人一眼看懂系统在干什么。别打太细,也别太粗。太细了像 DEBUG,太粗了等于没打。

WARN:预警信号

WARN 级别,说明系统遇到了非预期情况,但还能继续跑。它是个缓冲带,告诉你「这里有问题,但还没到报警的程度」。

我在项目中遇到过,某个第三方接口偶尔超时。直接报 ERROR 吧,显得太紧张;不记吧,又怕问题恶化。这时候 WARN 就派上用场了:

logger.warn("第三方支付接口超时,第 {} 次重试", retryCount);
logger.warn("磁盘使用率已达 {}%,请关注", diskUsage);

WARN 日志要配合监控告警阈值使用。比如连续出现 10 次 WARN,就该升级成 ERROR 并触发告警。

ERROR:需要立即处理

ERROR 级别的日志,意味着某个功能挂了。用户操作失败、数据写入异常、依赖服务不可用——这些都应该记 ERROR。

注意,ERROR 日志一定要包含足够的上下文信息。光记一句「操作失败」,排查时你根本不知道是哪个操作、哪个用户、哪个参数。

logger.error("用户 {} 订单 {} 退款失败,原因:{}", userId, orderId, e.getMessage(), e);

避坑指南:我曾经见过有人把 ERROR 日志写在 catch 块里,然后继续抛异常。结果同一个错误打了两次日志,排查时被重复信息搞晕了。记住:要么在 catch 里打日志,要么抛异常让上层处理,别两个都干。

FATAL:最后的遗言

FATAL 级别,是系统崩溃前的最后一条日志。它意味着进程即将退出,或者已经无法提供服务。

实际项目中,FATAL 用得很少。一般只在以下场景出现:

  • 数据库连接池初始化失败
  • 核心配置文件加载失败
  • 内存溢出导致进程被 Kill
  • 关键依赖服务完全不可用
logger.fatal("数据库连接池初始化失败,系统无法启动,退出进程");
System.exit(1);

FATAL 日志一旦出现,监控系统应该立刻告警,值班人员必须马上响应。

如何合理设置日志级别

日志级别不是一成不变的。不同环境、不同模块,应该用不同的策略。

按环境设置

环境 推荐级别 说明
开发环境 DEBUG 方便调试,所有信息都看得到
测试环境 INFO 验证功能是否正常,偶尔开 DEBUG 排查
预发布环境 INFO 模拟生产,但可以临时调低级别
生产环境 INFO 或 WARN 稳定运行,只记录关键信息

按模块设置

有些模块天生就需要更详细的日志。比如支付模块、用户认证模块,出问题影响大,可以单独调低级别。

在 logback 或 log4j 配置里,可以针对特定包设置级别:

<logger name="com.yourcompany.payment" level="DEBUG"/>
<logger name="com.yourcompany.auth" level="DEBUG"/>
<root level="INFO"/>

这样支付和认证模块打 DEBUG,其他模块只打 INFO。既不影响性能,又能拿到关键模块的详细信息。

我的习惯:生产环境默认开 INFO。如果某个模块出问题,通过动态配置临时把该模块降到 DEBUG。问题排查完再改回来。别让 DEBUG 日志长期开着,磁盘和性能都扛不住。

日志级别决策流程

下面这张图,是我平时判断该用哪个级别的参考流程。你写代码时也可以照着来:

日志级别选择决策流程 遇到一条日志 系统还能继续运行吗? 否 FATAL 是 功能受影响吗? 是 ERROR 否 是否非预期情况? 是 WARN 否 关键业务流程? 是 INFO 否 DEBUG 生产环境建议:INFO 起步,DEBUG 仅临时开启

常见误区与避坑

最后,聊聊我这些年见过的坑。你写日志时注意避开:

  • 所有异常都打 ERROR:有些异常是业务预期内的,比如用户输入校验失败。这种打 WARN 就够了,别动不动就 ERROR。
  • 日志里不打印异常堆栈:光记一句「发生异常」,排查时根本不知道哪行代码出的问题。一定要记 e.printStackTrace() 或 e 对象。
  • 生产环境开 DEBUG:除非你在排查特定问题,否则别开。日志量会暴涨,磁盘 IO 和 CPU 都扛不住。
  • FATAL 后不退出:既然打了 FATAL,说明系统已经没法正常工作了。该退出就退出,别硬撑着继续跑,反而造成更多问题。

我曾经踩过的坑:有一次线上出问题,我为了快速定位,把整个应用的日志级别调成了 DEBUG。结果日志量太大,直接把磁盘写满了。系统挂了,日志也没了。从那以后,我只针对具体模块调级别,绝不全量开 DEBUG。

日志级别这事儿,看似简单,但用好了能救命,用不好就是灾难。你写代码时多想想:这条日志是给谁看的?他需要什么信息?用哪个级别最合适?想清楚了再写,比事后补日志强一百倍。