导读:本期聚焦于日本程序员创作的《Spring使用Log4j记录日志有哪些常见误区?如何正确配置?》,敬请观看详情。在Spring项目里配置Log4j时,有一个特别容易踩的坑:把log4j.properties放在src目录下,启动后却提示log4j:WARN No appenders could be found for logger,或者日志文件始终不生成。出现这类问题,往往不是Log4j本身坏了,而是Spring与Log4j的桥接、配置文件加载优先级、依赖冲突等细节没理清。本文从配置文件写法、Appender与Logger的关系、Spring Boot下Log4j2的接入方式三个角度,把Log4j日志记录的用途和常见误区一次性讲明白。你会看到为什么不能把Log4j和SLF4J的绑定搞混,为什么log4j.properties的位置会影响加载,以及如何通过合理的日志级别和输出格式避免线上排查时抓瞎。读完本文,你可以直接对照自己的项目检查配置,少走弯路。

在Spring项目里引入Log4j记录日志,通常是为了统一输出格式、按级别过滤、把日志持久化到文件,方便线上问题追踪。但实际配置时,经常碰到日志不输出、输出两次、启动时报找不到Appender等情况。这些问题的根源往往不在Log4j本身,而在依赖选择、配置文件命名和日志继承关系上。本文从基础配置讲起,重点分析几个高频误区,并给出可直接落地的排查思路。

Spring使用Log4j记录日志有哪些常见误区?如何正确配置?

Log4j在Spring中的角色与基础配置

Log4j是Apache开源的日志框架,分为1.x和2.x两个主要版本,二者配置格式和核心包完全不一样。Spring本身只依赖SLF4J日志门面,并不绑定具体实现。想要用Log4j输出日志,需要额外引入对应的桥接包。如果是传统Spring项目使用Log4j 1.x,一般加入slf4j-log4j12;如果使用Log4j 2.x,则加入log4j-slf4j-impl和log4j-core。这个步骤如果少了,SLF4J会在启动时给出警告,提示找不到日志实现。对于Spring Boot项目,默认使用的是Logback,要切换到Log4j2必须显式排除spring-boot-starter-logging并引入spring-boot-starter-log4j2,否则同时存在两个实现会让SLF4J选择不可预期。

先看一个Log4j 1.x的基础配置,通常放在classpath根目录下的log4j.properties文件中。

log4j.rootLogger=INFO, console, file

log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=logs/app.log
log4j.appender.file.MaxFileSize=10MB
log4j.appender.file.MaxBackupIndex=10
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

log4j.logger.com.example=DEBUG

配置中rootLogger定义了全局级别和挂载的Appender,console输出到控制台,file使用RollingFileAppender做文件滚动。PatternLayout里的%d表示时间,%-5p表示日志级别左对齐占5位,%c{1}输出类名最后一段,%L输出行号,%m是日志内容,%n换行。这类配置适用于传统Servlet或Spring MVC项目。如果使用Log4j 2.x,配置文件名必须是log4j2.xml,且同样要放在资源根目录。

对于Spring Boot项目,切换Log4j2的典型依赖配置如下。

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <exclusions>
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-logging</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-log4j2</artifactId>
    </dependency>
</dependencies>

这里通过exclusion排除了默认的Logback,再引入Log4j2 starter。启动成功后,在resources目录下放置log4j2.xml即可生效。如果缺少排除步骤,classpath中会同时存在Logback和Log4j2,SLF4J会绑定其中一个,具体绑定结果取决于依赖顺序,经常导致日志配置不按预期工作。

Spring中使用Log4j的常见误区

第一个误区是依赖不全或者冲突。很多人只在pom.xml中加了log4j-core,没有加log4j-slf4j-impl或log4j-api。这样SLF4J在初始化时找不到对应实现,会打印类似SLF4J: Failed to load class org.slf4j.impl.StaticLoggerBinder的警告,然后回退到无操作实现,所有日志全部丢失。反过来,如果同时保留Logback和Log4j2,SLF4J会警告Class path contains multiple SLF4J bindings,并选择其中一个。排查这类问题最直接的办法是执行mvn dependency:tree查看日志相关的依赖树,确认只存在一个slf4j绑定包。

第二个误区是配置文件没被加载。Log4j 1.x默认寻找classpath根目录下的log4j.properties,Log4j 2.x默认寻找log4j2.xml。如果文件放在src/main/java目录而非src/main/resources目录,编译后不会出现在classpath根下,自然无法加载。此时启动会看到log4j:WARN No appenders could be found for logger这样的提示。Spring Boot还支持通过logging.config属性指定配置文件位置,但指定时要注意文件后缀必须对应实现类型,否则依旧不生效。确认是否加载成功,可以在启动日志中观察是否存在Log4j2的初始化信息,或者故意写错一个Appender名称看启动是否报错。

第三个误区是additivity属性理解错误导致日志重复输出。Log4j中的Logger具有继承关系,子Logger默认会把日志传递给父Logger的Appender。例如根Logger挂载了console,com.example包下的Logger如果也挂载console,并且没有关闭additivity,那么com.example下的日志会在两个Appender中各输出一次,有时甚至出现三份。解决方式是在子Logger上设置additivity=false。Log4j 1.x的写法是log4j.additivity.com.example=false;Log4j 2.x则在Logger元素上直接写additivity="false"。下面是一个Log4j 2.x的修复片段。

<Logger name="com.example" level="DEBUG" additivity="false">
    <AppenderRef ref="Console"/>
</Logger>

第四个误区是日志级别设置不合理。开发阶段为了调试方便把根Logger设为DEBUG,上线时忘记改回INFO或WARN,导致大量调试信息写入磁盘,不仅拖慢性能,还可能覆盖掉关键错误。反过来,把根Logger设为OFF会直接关闭所有日志,线上出问题后只能靠猜。比较合理的做法是生产环境根级别设INFO,针对频繁调试的包单独设DEBUG,并在发布前检查配置文件是否与当前环境一致。此外,避免在循环中打印DEBUG日志,尽量使用参数化消息而不是字符串拼接,减少运行开销。

实用配置与排查技巧

生产环境推荐使用滚动文件Appender,防止单个日志文件无限增长。下面展示一个Log4j 2.x的RollingFile配置,基于文件大小和时间两个维度滚动,并自动压缩旧文件,保留最近20个归档。

<RollingFile name="RollingFile" fileName="logs/app.log"
             filePattern="logs/app-%d{yyyy-MM-dd}-%i.log.gz">
    <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n"/>
    <Policies>
        <SizeBasedTriggeringPolicy size="10 MB"/>
        <TimeBasedTriggeringPolicy interval="1"/>
    </Policies>
    <DefaultRolloverStrategy max="20"/>
</RollingFile>

这个配置会按天滚动,单个文件超过10MB也会触发滚动。旧文件压缩为.gz格式,最多保留20个。注意filePattern中的%d和%i是Log4j2的占位符,日期格式和归档序号要与Policies配合使用,否则可能产生归档文件名冲突。对于Log4j 1.x,可以使用DailyRollingFileAppender或RollingFileAppender,但2.x的配置更灵活,建议新项目直接使用2.x。

如果需要在分布式或高并发场景下追踪同一个请求的完整日志链路,可以使用MDC。MDC是SLF4J提供的上下文容器,能在一个线程内保存键值对。在Spring Web应用中,通过Filter把请求ID放入MDC,日志输出时用%X{requestId}打印,就能把同一请求的所有日志串起来。下面是一个简单的Filter实现。

import org.slf4j.MDC;
import javax.servlet.*;
import java.io.IOException;

public class RequestIdFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        String requestId = java.util.UUID.randomUUID().toString();
        MDC.put("requestId", requestId);
        try {
            chain.doFilter(request, response);
        } finally {
            MDC.remove("requestId");
        }
    }
}

在PatternLayout中加上%X{requestId}后,每一行日志都会带上本次请求的唯一标识。注意在finally块中必须调用MDC.remove,否则线程池复用线程时会导致requestId串掉。这个技巧在排查线上并发问题时非常有用。

最后补充几个排查方向。如果日志文件一直不出现,除了检查配置文件和依赖,还要看进程是否有目标目录的写权限,以及日志文件路径是否使用了相对路径导致写到了启动目录而非预期目录。启动时加上-Dlog4j.debug=true可以查看Log4j 1.x的配置加载过程,Log4j 2.x对应-Dorg.apache.logging.log4j.simplelog.StatusLogger.level=TRACE。通过这些内部状态输出,可以快速定位是配置文件没找到、Appender初始化失败,还是依赖绑定问题。总之,Spring项目使用Log4j记录日志并不复杂,但依赖、命名、继承、级别这四个环节必须逐一核对,才能避开常见坑,让日志真正成为排障的利器。

SpringLog4j日志记录修改时间:2026-09-29 08:38:38

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0929/63330.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。