日志级别详解: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 日志长期开着,磁盘和性能都扛不住。
日志级别决策流程
下面这张图,是我平时判断该用哪个级别的参考流程。你写代码时也可以照着来:
常见误区与避坑
最后,聊聊我这些年见过的坑。你写日志时注意避开:
- 所有异常都打 ERROR:有些异常是业务预期内的,比如用户输入校验失败。这种打 WARN 就够了,别动不动就 ERROR。
- 日志里不打印异常堆栈:光记一句「发生异常」,排查时根本不知道哪行代码出的问题。一定要记 e.printStackTrace() 或 e 对象。
- 生产环境开 DEBUG:除非你在排查特定问题,否则别开。日志量会暴涨,磁盘 IO 和 CPU 都扛不住。
- FATAL 后不退出:既然打了 FATAL,说明系统已经没法正常工作了。该退出就退出,别硬撑着继续跑,反而造成更多问题。
我曾经踩过的坑:有一次线上出问题,我为了快速定位,把整个应用的日志级别调成了 DEBUG。结果日志量太大,直接把磁盘写满了。系统挂了,日志也没了。从那以后,我只针对具体模块调级别,绝不全量开 DEBUG。
日志级别这事儿,看似简单,但用好了能救命,用不好就是灾难。你写代码时多想想:这条日志是给谁看的?他需要什么信息?用哪个级别最合适?想清楚了再写,比事后补日志强一百倍。