SLF4J + Logback 是当前最稳妥的Java日志组合,推荐使用 slf4j-api 2.0.13 和 logback-classic 1.4.14,配置文件须置于 classpath 根目录,通过系统属性动态控制日志级别,并注意 MDC 在异步场景下的手动传递。

Java日志框架选型:SLF4J + Logback 是当前最稳妥的组合
Java 本身有 java.util.logging(JUL),但实际项目中几乎没人直接用它——功能弱、配置僵硬、桥接麻烦。主流选择是 SLF4J 作为门面(facade),底层绑定 Logback(原生支持 SLF4J,性能好、配置灵活)。避免用 log4j2 除非有明确需求(如异步日志吞吐压测),否则会多一层 slf4j-log4j2 桥接,还容易因版本不匹配触发 NoClassDefFoundError: org.apache.logging.log4j.spi.LoggerContextFactory 这类错误。
- 确认 Maven 依赖只保留一组:移除所有
log4j、commons-logging、java.util.logging直接引用 - 只引入:
org.slf4j slf4j-api 2.0.13 ch.qos.logback logback-classic 1.4.14 -
logback-classic已包含slf4j-api的实现,无需额外加slf4j-simple或slf4j-jdk14
logback.xml 配置必须放在 classpath 根目录
Logback 启动时会按固定顺序查找配置文件:logback-test.xml → logback.xml → logback.groovy。开发阶段优先用 logback-test.xml(放在 src/test/resources),生产用 logback.xml(放在 src/main/resources)。放错位置(比如写成 config/logback.xml)会导致 Logback 回退到默认控制台输出,且控制台会打印警告:WARN in ch.qos.logback.classic.LoggerContext[default] - No appenders present...
- 检查编译后
target/classes/(Maven)或out/production/(IntelliJ)下是否存在logback.xml - 配置中路径使用相对 classpath 路径,例如:
表示在 JVM 启动目录下创建logs/app.log logs/app.log,不是 classpath 里找 - 避免在
中漏写,否则日志内容为空白行
区分开发与生产环境的日志级别和输出目标
开发时需要看到 DEBUG 级别日志快速定位问题;生产必须关掉 DEBUG,防止磁盘打满或敏感信息泄露。不能靠改代码里的 logger.debug("..."),而应通过配置动态控制。
- 在
logback.xml中用(Spring Boot)或(需引入logback-core的 Janino 支持)做环境分支 - 更轻量的做法:用系统属性区分,例如启动时加
-Dlog.level=DEBUG,然后配置: - 生产环境禁用
CONSOLEappender,只留滚动文件;开发环境可加AsyncAppender包裹FILE,减少 I/O 阻塞
避免日志中出现线程安全或格式化异常
SLF4J 的占位符语法 logger.info("User {} logged in at {}", userId, LocalDateTime.now()) 是线程安全的,且只有当日志级别允许输出时才真正执行参数计算。但若手写字符串拼接:logger.info("User " + userId + " logged in at " + LocalDateTime.now()),每次都会构造对象、触发 toString(),浪费 CPU 和 GC 压力。
立即学习“Java免费学习笔记(深入)”;
- 禁止在日志参数中调用可能抛异常的方法,例如:
logger.error("Failed to parse JSON", jsonNode.toString())—— 若jsonNode为 null,直接 NPE,日志都打不出来 - 敏感字段(密码、token、身份证号)必须脱敏后再传入日志,不能依赖日志框架过滤(Logback 的
Filter是事后扫描,已进内存) - 异步日志(
AsyncAppender)下,MDC(Mapped Diagnostic Context)不会自动继承,需显式调用MDC.copyInto或改用LoggingEventAsyncDisruptor等替代方案
Logback 的配置自由度高,但也意味着出错时排查路径长:从类路径、XML 语法、appender 初始化顺序,到 MDC 传递、异步缓冲区大小,每一步都可能静默失败。最常被忽略的是 RollingFileAppender 的 里没配 maxHistory,导致日志文件无限累积。










