优先用 logback-spring.xml;Spring Boot 项目支持 springProfile 动态配置,可按环境切换日志级别与输出方式,而 log4j2.xml 更适用于纯 Java SE 或老 Spring MVC 项目。

用 log4j2.xml 还是 logback-spring.xml?看依赖选对日志框架
Java 项目默认不带日志实现,必须显式引入一个日志门面(如 slf4j-api)+ 一个日志实现(如 log4j-core 或 logback-classic)。Spring Boot 2.0+ 默认用 logback,但如果你手动加了 log4j2 依赖,又没排除掉 spring-boot-starter-logging 里的 logback,就会报 SLF4J: Class path contains multiple SLF4J bindings。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- Spring Boot 项目优先用
logback-spring.xml(支持springProfile标签,可按dev/test/prod切换配置) - 纯 Java SE 或老 Spring MVC 项目,
log4j2.xml更常见,注意要引入log4j-slf4j-impl(不是log4j-slf4j-binding,后者已废弃) - Maven 排除冲突依赖时,检查
mvn dependency:tree -Dincludes=org.slf4j确认只有一套 binding
logback-spring.xml 中如何区分开发和生产日志级别与输出位置
关键不是写死 level="DEBUG",而是用 Spring 的 profile 动态控制。开发环境输出到控制台并开 DEBUG,生产环境关 DEBUG、只写文件、自动压缩归档。
实操建议:
立即学习“Java免费学习笔记(深入)”;
-
块里配+level="DEBUG" -
块里用,fileNamePattern设为logs/app.%d{yyyy-MM-dd}.%i.gz实现日切+压缩 - 避免在
外层直接设level,否则 profile 块里的 level 不生效
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
logs/app.log logs/app.%d{yyyy-MM-dd}.%i.gz 10MB %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n
为什么加了 @Slf4j 却报 Cannot resolve symbol 'log'
这是 Lombok 配置缺失或 IDE 未启用注解处理器导致的,和日志框架本身无关。Lombok 的 @Slf4j 只是自动生成一个名为 log 的静态 Logger 字段,底层仍调用 SLF4J API。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- Maven 加
lombok依赖后,IntelliJ 必须打开Settings → Build → Compiler → Annotation Processors → Enable annotation processing - IDEA 中安装 Lombok 插件(Plugin Marketplace 搜 “Lombok”),否则代码补全和编译都会失败
- 确认类上写了
@Slf4j(不是@Log或@Log4j),且类不在default包下(Lombok 对 default 包支持不稳定)
日志异步输出真的更快?AsyncAppender 和 AsyncLogger 别混用
Logback 的 AsyncAppender 是包装已有 appender 的装饰器;Log4j2 的 AsyncLogger 是从 Logger 层面异步。两者机制不同,不能跨框架混搭 —— 在 Logback 里配 AsyncLogger 会静默失效。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- Logback 项目统一用
AsyncAppender:把ConsoleAppender或RollingFileAppender套一层,注意设置queueSize(默认 256,高并发写日志可能丢日志) - Log4j2 项目用
AsyncLogger,需在log4j2.xml顶部加status="WARN" shutdownHook="disable"并确保 JVM 关闭前调用LogManager.shutdown() - 异步不等于零成本,如果日志内容本身要拼接大量字符串(如
log.debug("user=" + user.getName() + ", id=" + user.getId())),先拼再判断级别,反而浪费 CPU —— 改用占位符:log.debug("user={}, id={}", user.getName(), user.getId())
真正容易被忽略的是日志上下文传递:微服务中一次请求跨多个线程(比如用了 CompletableFuture 或线程池),MDC 数据不会自动继承。这需要手动做 MDC.copyIntoContextMap() 或用封装好的工具类,否则排查链路时日志就断了。










