详解Log4j组件:工业级Java日志框架

Hello,大家好!我是唐叔,今天给大家带来的是有关Log4j的使用介绍,希望读完本文章能加深你对Log4j的理解。

一、Log4j介绍

Log4j 是一款功能强大的工业级 Java 日志框架,能够高效处理各类日志记录任务,使开发者可以更专注于业务逻辑的实现。

其主要功能包括:

  • 丰富日志内容:自动附加时间戳、文件名、类与方法名称、行号、主机信息、日志级别等上下文信息;

  • 灵活格式化消息:支持通过预定义的布局(如 CSV、JSON 等)对日志消息进行结构化格式化;

  • 多目标输出:可以将日志写入多种终端,包括控制台、文件、套接字、数据库及消息队列等;

  • 精细化过滤:可根据严重级别、内容关键字等条件过滤待输出的日志,提升有效信息密度。

二、Log4j基本使用

使用log4j,就三步骤:引入组件、撰写配置文件、使用组件。

2.1 引入组件

xml 复制代码
<dependencies>
    <!-- Log4j核心依赖 -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.17.1</version> <!-- 建议使用最新稳定版本 -->
    </dependency>

    <!-- Log4j API -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>2.17.1</version>
    </dependency>
</dependencies>

PS:使用log4j,需要同时引入API和对应的实现模块。

2.2 编写配置文件

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Properties>
        <Property name="LOG_PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n</Property>
    </Properties>

    <Appenders>
        <!-- 控制台输出 -->
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="${LOG_PATTERN}"/>
        </Console>
    </Appenders>

    <Loggers>
        <Root level="info">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>

PS:配置文件的命名必须是以log4j2为前缀,如log4j2.xml、log4j2-test.xml等,且需要放在classpath根目录下(通常是src/main/resources/

2.3 使用组件

java 复制代码
package cn.uil.demo;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class MyClass {
    // 获取Logger实例
    private static final Logger logger = LogManager.getLogger(MyClass.class);

    public void myMethod() {
        // 记录不同级别的日志
        logger.trace("这是一条TRACE级别的日志");
        logger.debug("这是一条DEBUG级别的日志");
        logger.info("这是一条INFO级别的日志");
        logger.warn("这是一条WARN级别的日志");
        logger.error("这是一条ERROR级别的日志");
        logger.fatal("这是一条FATAL级别的日志");

        // 带参数的日志
        String name = "张三";
        int age = 25;
        logger.info("用户信息:姓名={}, 年龄={}", name, age);

        // 异常日志记录
        try {
            // 可能抛出异常的代码
            int result = 10 / 0;
        } catch (Exception e) {
            logger.error("计算过程中发生错误", e);
        }
    }

    public static void main(String[] args) {
        MyClass myClass = new MyClass();
        myClass.myMethod();
    }
}

日志输出如下:

properties 复制代码
2025-08-26 08:37:16.322 [main] INFO  cn.uil.demo.MyClass - 这是一条INFO级别的日志
2025-08-26 08:37:16.324 [main] WARN  cn.uil.demo.MyClass - 这是一条WARN级别的日志
2025-08-26 08:37:16.324 [main] ERROR cn.uil.demo.MyClass - 这是一条ERROR级别的日志
2025-08-26 08:37:16.324 [main] FATAL cn.uil.demo.MyClass - 这是一条FATAL级别的日志
2025-08-26 08:37:16.325 [main] INFO  cn.uil.demo.MyClass - 用户信息:姓名=张三, 年龄=25
2025-08-26 08:37:16.325 [main] ERROR cn.uil.demo.MyClass - 计算过程中发生错误
java.lang.ArithmeticException: / by zero
    at cn.uil.demo.MyClass.myMethod(MyClass.java:27) [classes/:?]
    at cn.uil.demo.MyClass.main(MyClass.java:35) [classes/:?]

三、Log4j进阶使用

如果你只是想对 log4j 有基本的了解,上述内容足以。如果想要进一步,可以继续往下阅读。

3.1 Log4j日志信息增强

和使用 System.out.print("log data") 相比,使用 Log4j 比较直接的差别莫过于能获取到更多信息,比如日志打印时间记录、日志分级等。

1)日志级别

日志级别是预定义的枚举值,用于表示一条日志信息的重要性或严重性。它们有两个关键特性:

  1. 有序性:级别有高低之分,顺序是固定的。
  2. 门槛性 :Logger 只会输出不低于其当前设置级别的日志信息。这条规则是理解日志级别的重中之重。

Log4j 2 提供了以下标准级别,其顺序和典型用途如下:

级别 数值 含义与用途 示例 生产环境建议
ALL Integer.MIN_VALUE 最低级别,打开所有日志记录。主要用于配置,而不是在代码中使用。 关闭
TRACE 600 追踪信息 。比 DEBUG 更细致、更冗长,用于追踪复杂的程序流,如每个循环内的状态。 TRACE - Entering method calculate(), loop i=1, value=5 关闭
DEBUG 500 调试信息。详细的系统运行信息,对调试应用程序非常有帮助,如输入参数、中间结果、变量值。 DEBUG - Parameters: userId=123, action=login 通常关闭
INFO 400 信息性消息 。记录应用程序正常运行的关键业务节点和状态。用于回答"系统正在做什么"。 INFO - User [admin] logged in successfully. INFO - Order [1001] has been shipped. 打开
WARN 300 警告 。表明发生了不常见非错误的情况。应用程序仍在正常运行,但未来可能引发错误,需要关注。 WARN - Cache size is approaching the limit (90%). WARN - API response was slower than expected (2000ms). 打开
ERROR 200 错误 。表明发生了错误事件 ,影响了当前操作或请求(如保存失败、连接断开),但应用程序整体可能仍在运行。需要尽快调查修复。 ERROR - Failed to connect to database. Retrying... ERROR - Payment processing failed for order [1001]. 打开
FATAL 100 致命错误 。表明发生了非常严重的错误 ,可能导致应用程序完全崩溃或无法继续运行。 FATAL - Critical configuration file is missing. Shutting down. FATAL - JVM is running out of memory. 打开
OFF Integer.MAX_VALUE 最高级别,关闭所有日志记录。主要用于配置。 关闭

级别高低顺序ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF

使用示例说明:

假设我们在配置文件中将 Root Logger 的级别设置为 WARN

xml 复制代码
<Root level="WARN">
  <AppenderRef ref="Console"/>
</Root>

那么在代码中调用不同方法时,会发生以下情况:

java 复制代码
logger.trace("This is a trace message."); // TRACE(600) < WARN(300) -> **不输出**
logger.debug("This is a debug message."); // DEBUG(500) < WARN(300) -> **不输出**
logger.info("This is an info message.");  // INFO(400) < WARN(300) -> **不输出**
logger.warn("This is a warning!");        // WARN(300) == WARN(300) -> **输出**
logger.error("This is an error!");        // ERROR(200) > WARN(300) -> **输出**
logger.fatal("This is a fatal error!");   // FATAL(100) > WARN(300) -> **输出**

环境配置建议:

环境 推荐级别 原因
本地开发 DEBUG 需要最详细的信息来调试和开发功能。
测试环境 DEBUG / INFO 需要详细信息来排查测试中发现的问题。
生产环境 INFOWARN 平衡可观察性和性能。INFO 记录关键业务流程;WARN 只记录警告和错误,日志量最小,性能最好。
线上排查问题 动态调整为 DEBUG 当生产环境出现疑难问题时,可以通过管理工具(如Spring Boot Actuator)临时 将特定Logger的级别调为DEBUG来抓取详细信息,排查完后改回。

2)日志格式

Log4j 的核心功能是将日志信息按照用户指定的格式输出到指定的目的地(控制台、文件等)。这个格式就是通过 布局(Layout) 来定义的,其中最常用、最灵活的是 PatternLayout

PatternLayout 允许通过一个"转换模式"字符串(即 pattern)来定义日志输出的格式。

转换说明符 :以 % 开头,用于插入特定的日志事件数据,如日期、日志级别、类名等。格式通常为 %{格式}{参数},例如 %d{HH:mm:ss.SSS}, %logger{36}

以下是一些最常用和重要的转换说明符:

说明符 含义与作用 示例输出
%d{pattern} 日期/时间{pattern} 指定日期格式,遵循 Java SimpleDateFormat 规则。 %d{yyyy-MM-dd HH:mm:ss.SSS} -> 2023-10-27 14:35:21.123
%p / %level 日志级别。输出日志事件的级别。 DEBUG, INFO, WARN, ERROR
%t 线程名。输出生成日志事件的线程名称。 main, http-nio-8080-exec-1
%c / %logger Logger 名称 。通常是发出日志请求的类名。{precision} 可简化包名。 %c{1} 输出类名 (Main),%c 输出全限定类名 (com.example.Main)
%M 方法名 。输出发出日志请求的方法名。注意:获取方法名有性能开销,生产环境慎用。 doGet, main
%L 行号 。输出发出日志请求的代码行号。同样有性能开销,生产环境慎用。 123
%m / %msg / %message 应用程序消息。输出应用程序提供的日志内容。 User login successfully.
%n 平台相关的换行符 。在 Windows 上是 \r\n,在 Unix/Linux 上是 \n
%X 输出和当前线程关联的 MDC (Mapped Diagnostic Context) 内容%X{key} 输出指定 key 的值。 %X{requestId} -> rId-12345
%throwable 输出与日志事件关联的异常堆栈跟踪。如果不存在异常,则不输出任何内容。 java.io.FileNotFoundException: ...
%highlight 高亮显示。通常与其他模式组合,根据日志级别为内容着色。需要支持 ANSI 颜色的终端。 %highlight{%p} 会让 ERROR 显示为红色
%r 相对时间。输出从应用程序启动到日志事件创建所经过的毫秒数。 15234 (毫秒)
%F 文件名。输出发出日志请求的源代码文件名。 Main.java

常用格式示例

  1. 标准开发/调试格式:这种格式信息非常全,便于调试,但因为包含了方法名(%M)和行号(%L),性能有损耗,不建议在生产环境使用。
properties 复制代码
%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36}.%M(%L) - %msg%n

说明: %-5level: -5 表示左对齐并固定宽度为5个字符,这样各级别(INFO, DEBUG, ERROR)就能对齐,更美观。

  1. 生产环境格式:生产环境通常更关心上下文(时间、级别、线程、Logger名)和业务消息,避免使用高开销的%M%L
properties 复制代码
%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n
  1. 高亮模式:在 IntelliJ IDEA 或 Terminal 等支持 ANSI 颜色的控制台中,使用 %highlight 可以让日志级别更加醒目。
properties 复制代码
%highlight{%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n}{FATAL=red blink, ERROR=red, WARN=yellow, INFO=green, DEBUG=cyan, TRACE=white}

这里以测试环境模式为示例,高亮模式貌似社区版IDEA不支持...

补充内容:环境差异化配置

核心机制: Log4j 2 在启动时,会按照一个明确的顺序查找配置文件。它允许在文件名中包含环境变量系统属性 ,从而实现环境的自动切换。常见的环境差异化配置有:log4j2-dev.xml (开发环境) 、log4j2-test.xml (测试环境)等。

  • 通过 JVM 参数指定环境

这种方式通过在启动应用时传递一个 JVM 参数来明确指定要使用哪个环境的配置。

  1. 创建不同环境的配置文件 在你的项目资源目录(如 src/main/resources)下创建多个配置文件:

    • log4j2-dev.xml - 开发环境配置(级别为 DEBUG, 输出到控制台)[代码略]
    • log4j2-pro.xml - 生产环境配置(级别为 INFO, 输出到文件和时间滚动归档)[代码略]
  2. 在启动命令中指定环境 通过 JVM 参数 -Dlog4j.configurationFile 来指定激活哪个文件。参数的值就是配置文件的完整名称

    • 在 IDEA 中启动(开发):

      • 编辑运行配置(Edit Configurations...)

      • VM options 中添加:-Dlog4j.configurationFile=log4j2-dev.xml

    • 在服务器上启动java -Dlog4j.configurationFile=log4j2-pro.xml -jar myapplication.jar

在 IDEA 中没显示 VM options,可以点击 Modify options,勾选 Add VM options 即可。

3.2 Log4j日志结构化

Log4j日志结构化,主要依赖于 JSON Template Layout。它允许你使用 Jackson JSON 处理器和 JSON 模板,将日志事件(LogEvent)高度定制化地序列化为 JSON 对象 ,而不是一行行的文本。与传统 PatternLayout 相比,结构化数据 (Structured Data),日志被输出为键值对(JSON),便于日志收集系统(如ELK)直接摄取和索引,无需复杂的解析(Grok)规则。

使用方式

  • 添加依赖
xml 复制代码
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-layout-template-json</artifactId>
    <version>2.17.1</version> <!-- 请使用与log4j-core相同的版本 -->
</dependency>
  • 配置 (log4j2.xml)
xml 复制代码
<Appenders>
    <Console name="Console" target="SYSTEM_OUT">
      <JsonTemplateLayout eventTemplateUri="classpath:LogstashJsonEventLayoutV1.json"/>
    </Console>
</Appenders>

LogstashJsonEventLayoutV1.json 是内置的json格式模板,也可以配置自定义json模板,然后这里进行替换即可。

  • 使用效果演示

3.3 Log4j日志归档

Log4j 通过 Appender 组件模型来实现将日志输出到控制台、文件、套接字、数据库、队列等各种位置。

以将日志同时输出到控制台和文件为例,调整 log4j2.xml 文件,增加RollingFile配置。

xml 复制代码
<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.SSS} [%t] %-5level %logger{36} - %msg%n"/>

    <Policies>
        <!-- 基于时间的触发策略:每天轮转一次 -->
        <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
        <!-- 基于大小的触发策略:如果文件超过10MB,也触发轮转 -->
        <SizeBasedTriggeringPolicy size="10 MB"/>
    </Policies>

    <!-- 保留最近7天的日志,最多15个文件,超过的自动删除 -->
    <DefaultRolloverStrategy max="15">
        <Delete basePath="logs" maxDepth="1">
            <IfFileName glob="app-*.log.gz"/>
            <IfLastModified age="7D"/>
        </Delete>
    </DefaultRolloverStrategy>
</RollingFile>

效果演示

日志也可以写入队列等,额外引入日志扩展组件即可,这里就不作展开了。

补充内容:日志异步

Log4j 使用 Async 组件实现日志异步打印。

以日志异步输出到文件为例,调整 log4j2.xml 文件,在 Appenders 添加 Async 组件。

xml 复制代码
<Async name="AsyncFile" bufferSize="8192">
    <!-- 引用同步的文件Appender -->
    <AppenderRef ref="RollingFile"/>
    <!-- 可选:如果异步队列满了,可以指定一个备用的同步Appender(如控制台)来接收日志,避免丢失 -->
    <!-- <AppenderRef ref="Console"/> -->
</Async>

效果演示

3.4 Log4j日志过滤

Log4j 的 过滤器(Filter) 组件可以在日志事件进入 Appender 或 Logger 之前对其进行拦截和判断,决定是接受、拒绝还是忽略该日志事件。例如可以使用 RegexFilter 用于过滤敏感信息,不做日志打印。

xml 复制代码
<!-- 过滤掉包含"password"一词的日志(忽略大小写) -->
<RegexFilter regex=".*password.*" onMatch="DENY" onMismatch="NEUTRAL" useRawMsg="true"/>

效果演示

补充内容:敏感字打印替换

对于复杂的数据脱敏(如过滤JSON字符串中的特定字段),RewriteAppender 允许你在将日志事件传递给最终Appender之前,先对其进行修改。

  • 自定义敏感信息替换类
java 复制代码
// 使用 @Plugin 注解注册插件
@Plugin(name = "RegexReplaceRewritePolicy",
        category = Core.CATEGORY_NAME,
        elementType = "rewritePolicy",
        printObject = true)
public final class RegexReplaceRewritePolicy implements RewritePolicy {

    private final Pattern regexPattern;
    private final String replacement;

    // 构造函数
    private RegexReplaceRewritePolicy(String regex, String replacement) {
        this.regexPattern = Pattern.compile(regex);
        this.replacement = replacement;
    }

    // 重写 rewrite 方法,实现正则替换逻辑
    @Override
    public LogEvent rewrite(LogEvent event) {
        // 获取原始日志消息
        String originalMessage = event.getMessage().getFormattedMessage();

        // 执行正则替换
        String modifiedMessage = regexPattern.matcher(originalMessage).replaceAll(replacement);

        // 如果消息未被修改,直接返回原事件
        if (originalMessage.equals(modifiedMessage)) {
            return event;
        }

        // 创建并返回一个新的 LogEvent,包含修改后的消息
        return new Log4jLogEvent.Builder(event)
                .setMessage(new SimpleMessage(modifiedMessage))
                .build();
    }

    // 使用 @PluginFactory 注解标记工厂方法,用于从配置创建策略实例
    @PluginFactory
    public static RegexReplaceRewritePolicy createPolicy(
            @PluginAttribute("regex") String regex,
            @PluginAttribute("replacement") String replacement) {
        return new RegexReplaceRewritePolicy(regex, replacement);
    }
}
  • 配置log4j2.xml 文件:在Appenders中声明
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <!-- 1. 定义最终的输出目标(控制台) -->
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level - %msg%n"/>
        </Console>

        <!-- 2. 定义重写器,进行敏感信息脱敏 -->
        <Rewrite name="RewriteSensitiveData">
            <!-- 指定脱敏后的日志输出到哪个Appender -->
            <AppenderRef ref="Console"/>
            <!-- 使用RegexReplace重写策略 -->
            <RegexReplaceRewritePolicy regex="(?i)(password|pwd)=[^&amp;\s]+" replacement="$1=***"/>
        </Rewrite>
    </Appenders>

    <Loggers>
        <Root level="INFO">
            <!-- 关键:将Root Logger指向重写器,而不是直接的Console -->
            <AppenderRef ref="RewriteSensitiveData"/>
        </Root>
    </Loggers>
</Configuration>

效果演示

四、Log4j深入理解

下面的内容是唐叔对 Log4j 的一些理解,如果你只是掌握 Log4j 的使用,下述内容就可以直接略过啦。

4.1 Log4j2 架构&配置文件解读

通过上述的配置,其实大家可以明确地体会到 Log4j.xml 文件在 Log4j 的使用中十分重要。

在理解 Log4j.xml 配置文件前,我们先了解下 Log4j 的代码架构。下图是 Log4j 官网的架构图。

4.1.1 Log4j 整体架构概述

Log4j 的架构主要由以下几个核心部分组成:

  1. LoggerContext:日志系统的入口和上下文环境。

  2. Configuration:配置信息的容器,包括 Appender、LoggerConfig、Filter 等。

  3. Logger:用户直接使用的日志记录接口。

  4. LoggerConfig:配置每个 Logger 的行为(如级别、Appender、Filter)。

  5. Appender:定义日志输出的目的地(如文件、控制台、网络)。

  6. Layout:定义日志输出的格式。

  7. Filter:在不同级别上过滤日志事件。

  8. StrSubstitutor / Interpolator / StrLookup:处理配置中的变量替换(如 ${env:USER})。

4.1.2 运作机理概述

  1. 用户代码调用 Logger: logger.info("This is a log message");

  2. Logger 委托给 LoggerConfig:

    • Logger 本身不处理日志,而是委托给其对应的 LoggerConfig。

    • LoggerConfig 根据配置决定是否处理该日志(基于 Level 和 Filter)。

  3. LoggerConfig 调用 AppenderControl:

    • 每个 LoggerConfig 包含一个或多个 AppenderControl。

    • AppenderControl 是对 Appender 的封装,可能附加 Filter。

  4. Appender 输出日志:

    • Appender 使用 Layout 格式化日志事件。

    • 最终输出到目的地(文件、控制台、数据库等)。

  5. Filter 机制:

    • Filter 可以在四个级别上设置:Configuration、LoggerConfig、AppenderControl、Appender。

    • 每个 Filter 决定是否接受、拒绝或中立处理 LogEvent。

  6. 变量替换:

    • 使用 StrSubstitutorInterpolatorStrLookup 链解析 ${...} 表达式。

4.1.3 Log4j2.xml理解

当用户调用 Logger 时,Log4j 会读取配置文件信息,根据配置文件来判断如何处理用户的日志打印。

而每个配置文件上的每个标签都有具体的含义,下图是简要的解读:

简单说,Log4j 通过配置驱动责任链模式 实现了高度可扩展的日志系统。用户只需与 Logger 交互,而底层的 LoggerConfigAppenderFilterLayout 等组件通过配置灵活组合,满足各种日志需求。

4.2 多日志框架混用的统一策略

事实上,Java主流的日志框架,除了 log4j,其实还有 logback、Slf4j等。而大型项目是由很多组件构成,每个组件可能最终使用的日志框架不尽一样。而 Log4j 的开发者其实也考虑到了这一点,利用桥接模式巧妙的解决了多日志框架混用的日志输出混乱问题。下图是 Log4j 官网的多日志框架桥接适配方式图。

项目本身或存在多个模块或组件(Application、Library 1/2/3),使用了不同的日志框架。默认用户调用的都是各个日志框架的API层(SLF4J、JUL、JPL、Log4j API),通过桥接器的方式(SLF4J-to-Log4jJUL-to-Log4jJPL-to-Log4j),将其他日志API的调用转发到Log4j API的实现层 Log4j Core,而最终都统一采用 Log4j 的方式进行日志打印输出。

理解了上面的原理,那么日常该如何使用呢,下面是唐叔的一些建议:

  • 如果你的项目只使用 Log4j2 ,直接引入 log4j-core (API实现)和 log4j-api

  • 如果项目中有第三方库使用 SLF4J,引入:

    • log4j-core

    • log4j-api

    • log4j-slf4j-impl(即图中的 SLF4J-to-Log4j

    • 如果项目中存在 SLF4J API的实现,需要同步移除

  • 如果使用 JUL ,可引入 log4j-jul 桥接器。

  • 注意:避免同时引入多个桥接器或日志实现,防止冲突。


好啦,以上就是今天的讲解内容啦,感谢阅读。

相关推荐
一根会骑马的Banana3 分钟前
关于DTO、DO、BO、VO
java
cur1es6 分钟前
数据结构Java--8
java·数据结构·算法·散列表
简单点了1 小时前
SM4加密算法
java·开发语言
IT_陈寒1 小时前
Python开发者必知的5个高效技巧,让你的代码速度提升50%!
前端·人工智能·后端
用户3721574261351 小时前
Java 实现HTML转Word:从HTML文件与字符串到可编辑Word文档
java
yvya_2 小时前
Mybatis总结
java·spring·mybatis
姜太小白2 小时前
【VSCode】VSCode为Java C/S项目添加图形用户界面
java·c语言·vscode
谦行2 小时前
Andrej Karpathy 谈持续探索最佳大语言模型辅助编程体验之路
后端
一路向北North2 小时前
java将doc文件转pdf
java·pdf