日志级别:什么时候记什么
日志级别是日志系统的核心概念。选对了级别,信息不冗余又不遗漏;选错了,要么告警满天飞,要么出了问题找不到原因。
五个级别
从低到高:
| 级别 | 数字 | 含义 | 什么时候用 |
|---|---|---|---|
| TRACE | 0 | 最详细 | 极度详细的执行路径,链路追踪 |
| DEBUG | 1 | 调试信息 | 开发调试,生产环境通常关闭 |
| INFO | 2 | 一般信息 | 正常业务流程的关键节点 |
| WARN | 3 | 警告 | 潜在问题,但不影响功能 |
| ERROR | 4 | 错误 | 影响功能,已发生的异常 |
低级别包含高级别:设为 INFO 后,DEBUG 和 TRACE 不输出;设为 ERROR 后,只有 ERROR 输出。
每个级别该记什么
ERROR:程序出问题了
java
// ✅ 记录业务异常
try {
userRepository.save(user);
} catch (Exception e) {
log.error("保存用户失败: userId={}", user.getId(), e);
throw e; // 不要吞掉异常
}
// ✅ 记录无法恢复的错误
log.error("无法连接到数据库,程序即将退出", e);WARN:值得关注但不紧急
java
// ✅ 重试次数多
if (retryCount > 3) {
log.warn("重试 {} 次后成功,可能存在网络问题", retryCount);
}
// ✅ 资源接近上限
if (connectionPool.getActiveCount() > 80) {
log.warn("连接池活跃连接数: {},接近上限: {}",
connectionPool.getActiveCount(),
connectionPool.getMaxSize());
}
// ✅ 配置使用了默认值
if (StringUtils.isBlank(config.getTimeout())) {
log.warn("未配置超时时间,使用默认值: 3000ms");
}INFO:业务里程碑
java
// ✅ 系统启动
log.info("Application started in {}ms", stopwatch.elapsedMillis());
// ✅ 业务流程关键节点
log.info("用户注册成功 | userId={} | email={}", user.getId(), email);
// ✅ 定时任务执行
log.info("数据同步任务开始 | records={}", records.size());DEBUG:开发时用
java
// ✅ 方法入参(DEBUG 级别,生产不输出)
log.debug("调用外部API | url={} | params={}", url, params);
// ✅ 返回值
log.debug("外部API返回 | status={} | body={}", status, body);
// ✅ 循环中的状态(用采样减少日志量)
if (log.isDebugEnabled() && loopCount % 1000 == 0) {
log.debug("处理进度 | count={}", loopCount);
}生产环境建议
| 环境 | 建议级别 | 说明 |
|---|---|---|
| 开发 | DEBUG | 看到所有信息 |
| 测试 | INFO | 看到正常流程 |
| 生产 | INFO/WARN | 只看到问题和关键节点 |
生产环境开 DEBUG 级别的代价:日志量暴增,磁盘飞快写满,查询变慢。所以除非排查问题临时开启,DEBUG 在生产通常是关闭的。
动态调整日志级别
不需要重启应用,通过 JMX 或配置中心动态调整:
java
// JMX 方式
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
Logger logger = context.getLogger("com.example");
logger.setLevel(Level.DEBUG);总结
写日志时先问自己:这条日志是给谁看的?给运维看 ERROR,给开发者看 DEBUG,给自己排查问题时临时打开看。
级别选对了,告警才有效;日志才有用。
