【Java】日志框架

一、概述

Java日志框架是开发中记录和管理日志的重要工具,合理选择和使用日志组件能提升项目的可维护性和灵活性。

1、核心概念

  • 日志门面(Logging Facade)
    • 作用:提供统一的API接口,解耦业务代码与具体日志实现。
    • 优势:允许灵活切换底层日志库,无需修改代码。
    • 常见门面:SLF4J、JCL(Apache Commons Logging)。
  • 日志实现(Logging Implementation)
    • 作用:实际处理日志输出的具体库。
    • 常见实现:Log4j 2.x、Logback、JUL(java.util.logging)。
  • 桥接器(Bridging)
    • 作用:将其他日志API调用重定向到门面或指定实现。
    • 示例:jcl-over-slf4j(将JCL调用转到SLF4J)、log4j-to-slf4j(将Log4j 2.x调用转到SLF4J)。

2、主流技术栈对比

(1)日志门面

  • SLF4J(Simple Logging Facade for Java)
    • 通过LoggerFactory加载日志具体的实现对象,在绑定具体实现的时候,通过类加载器,加载org/slf4j/impl/StaticLoggerBinder.class
    • 绑定实现:需配合具体实现(如Logback、Log4j 2.x)使用。
    • 特点:社区活跃,支持参数化日志(logger.debug(“Value: {}”, value)),避免字符串拼接开销。
  • JCL(Jakarta Commons Logging)
    • 通过LogFactory动态加载Log实现类(运行时决定)
    • 已逐渐被SLF4J取代。

(2)日志实现

  • Log4j 2.x
    • 优势:异步日志、高性能插件架构、支持多种配置格式(XML/JSON/YAML)。
    • 功能:异步Logger、自定义日志级别、过滤机制。
  • Logback
    • 优势:SLF4J原生实现,性能优于Log4j 1.x,支持自动重载配置。
    • 功能:条件化配置、SiftingAppender(按上下文分离日志)。
  • JUL(Java Util Logging)
    • 特点:JDK内置,无需额外依赖,但功能较少,配置不够灵活。

3、典型组合方案

3.1 SLF4J + Logback


<dependency>
    <groupId>org.slf4jgroupId>
    <artifactId>slf4j-apiartifactId>
    <version>2.0.12version>
dependency>

<dependency>
    <groupId>ch.qos.logbackgroupId>
    <artifactId>logback-classicartifactId>
    <version>1.5.3version>
dependency>

3.2 SLF4J + Log4j 2.x


<dependency>
    <groupId>org.slf4jgroupId>
    <artifactId>slf4j-apiartifactId>
    <version>2.0.12version>
dependency>

<dependency>
    <groupId>org.apache.logging.log4jgroupId>
    <artifactId>log4j-slf4j2-implartifactId>
    <version>2.23.1version>
dependency>

<dependency>
    <groupId>org.apache.logging.log4jgroupId>
    <artifactId>log4j-coreartifactId>
    <version>2.23.1version>
dependency>

4、桥接旧项目示例

若旧项目使用Log4j 1.x或JCL,可通过桥接器统一到SLF4J:

  • Log4j 1.x → SLF4J
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>log4j-over-slf4j</artifactId>
        <version>2.0.12</version>
    </dependency>
    <!-- 排除原Log4j 1.x依赖 -->
    
  • JCL → SLF4J
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>jcl-over-slf4j</artifactId>
        <version>2.0.12</version>
    </dependency>
    

二、Log4j2

Apache Log4j 2 是 Java 社区中广泛使用的高性能日志框架,作为 Log4j 1.x 的升级版本,它在架构设计、性能和功能上进行了全面优化。

1、Log4j 2 的核心优势

  • 高性能
    • 异步日志(Asynchronous Logging):通过 AsyncLogger 实现非阻塞日志写入,支持 LMAX Disruptor 库,大幅提升吞吐量(尤其在多线程场景)。
    • 低垃圾生成(Low Garbage Collection):通过对象重用和缓冲区设计减少内存分配,降低 GC 压力。
  • 灵活的插件架构
    • 模块化设计:支持自定义 Appender、Filter、Layout 等组件,通过注解 @Plugin 轻松扩展功能。
    • 丰富的内置插件:如 KafkaAppender、CassandraAppender、NoSQLAppender 等,适配多种日志存储场景。
  • 多配置格式支持
    • XML/JSON/YAML/Properties:支持多种配置文件格式,推荐使用 XML 或 JSON 实现复杂配置。
    • 动态重载配置:修改配置文件后无需重启应用,自动检测并应用新配置。
  • 强大的过滤机制
    • 条件化日志输出:通过 ThresholdFilter、RegexFilter 等按日志级别、内容动态过滤。
    • 复合过滤:使用 CompositeFilter 组合多个过滤规则。
  • 安全增强
    • 漏洞修复:Log4j 2.17+ 修复了 Log4Shell(CVE-2021-44228)等安全漏洞,默认禁用 JNDI 查找。
    • 严格模式:通过配置 log4j2.formatMsgNoLookups=true 进一步限制动态解析。

2、核心组件与概念

2.1 组成

  • 日志信息的优先级:TRACE < DEBUG < INFO < WARN < ERROR < FATAL
  • 日志信息的输出目的地:日志信息的输出目的地指定了日志将打印到控制台还是文件中;
  • 日志信息的输出格式:而输出格式则控制了日志信息的显示内容。

2.2 组件层级

组件 作用
Logger 日志记录器,通过名称层次化继承配置(如 com.example.service)。
Appender 定义日志输出目的地(控制台、文件、数据库、消息队列等)。
Layout 指定日志格式(如时间、级别、类名、自定义字段)。
Filter 过滤日志事件,决定是否输出或传递给下级组件。

2.3 日志级别(Level)

内置级别(从低到高):TRACE < DEBUG < INFO < WARN < ERROR < FATAL。

  • TRACE:追踪,是最低的日志级别,相当于追踪程序的执行
  • DEBUG:调试,一般在开发中,都将其设置为最低的日志级别
  • INFO:信息,输出重要的信息,使用较多
  • WARN:警告,输出警告的信息
  • ERROR:错误,输出错误信息
  • FATAL:严重错误

支持自定义级别,但需谨慎使用以避免兼容性问题。

2.4 异步日志模式

  • 全局异步(AsyncLogger):所有日志异步处理,需引入 disruptor 依赖。
  • 混合异步(AsyncAppender):将同步日志缓冲后异步写入,适用于部分异步场景。

3、配置文件详解(以 XML 为例)

Log4j 2 默认加载类路径下的 log4j2.xml,也可通过 -Dlog4j.configurationFile 指定路径。

3.1 最小化配置示例


<Configuration status="WARN">
    <Appenders>
        
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/>
        Console>
        
        <RollingFile name="File" fileName="logs/app.log"
                     filePattern="logs/app-%d{yyyy-MM-dd}-%i.log.gz">
            <PatternLayout pattern="%d{ISO8601} %-5level [%t] %c{1.} - %msg%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            Policies>
            <DefaultRolloverStrategy max="10"/>
        RollingFile>
    Appenders>
    <Loggers>
        
        <Logger name="com.example.service" level="DEBUG" additivity="false">
            <AppenderRef ref="File"/>
        Logger>
        
        <Root level="INFO">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="File"/>
        Root>
    Loggers>
Configuration>

配置参数说明:

  • 日志级别:如果一条日志信息的级别大于等于配置文件的级别就记录。

    • trace:追踪,就是程序推进一下,可以写个trace输出
    • debug:调试,一般作为最低级别,trace基本不用。
    • info:输出重要的信息,使用较多
    • warn:警告,有些信息不是错误信息,但也要给程序员一些提示。
    • error:错误信息。用的也很多。
    • fatal:致命错误。
  • 输出源

    • CONSOLE(输出到控制台)
    • FILE(输出到文件)
  • 格式

    • SimpleLayout:以简单的形式显示
    • HTMLLayout:以HTML表格显示
    • PatternLayout:自定义形式显示
      • %d{yyyy-MM-dd HH:mm:ss, SSS} : 日志生产时间,输出到毫秒的时间
      • %-5level : 输出日志级别,-5表示左对齐并且固定输出5个字符,如果不足在右边补0
      • %c : logger的名称(%logger)
      • %t : 输出当前线程名称
      • %p : 日志输出格式
      • %m : 日志内容,即 logger.info(“message”)
      • %n : 换行符
      • %C : Java类名(%F)
      • %L : 行号
      • %M : 方法名
      • %l : 输出语句所在的行数, 包括类名、方法名、文件名、行数
      • hostName : 本地机器名
      • hostAddress : 本地ip地址
  • :,如 Console、File、RollingFile、Kafka 等。

  • :定义日志记录规则,可继承父 Logger 配置,additivity=“false” 阻止传递给父 Logger。

  • :在 Logger 或 Appender 中定义过滤条件(如 ThresholdFilter)。

log4j2配置节点说明:

  • Configuration:根节点
    • 属性:
      • status:用来指定log4j本身的打印日志的级别.
      • monitorinterval:用于指定log4j自动重新配置的监测间隔时间,单位是s,最小是5s.
    • 子节点:Appenders + Loggers
      • Appenders:定义输出目标
        • 子节点:Console + File + RollingFile
          • Console 节点:用来定义输出到控制台的Appender。
            • 属性:
              • name:指定Appender的名字.
              • target:SYSTEM_OUT 或 SYSTEM_ERR,一般只设置默认:SYSTEM_OUT.
            • 子节点:
              • PatternLayout:输出格式,不设置默认为:%m%n.
          • File 节点:用来定义输出到指定位置的文件的Appender.
            • 属性:
              • name:指定Appender的名字.
              • fileName:指定输出日志的目的文件带全路径的文件名.
              • PatternLayout:输出格式,不设置默认为:%m%n.
          • RollingFile节点:用来定义超过指定条件自动删除旧的创建新的Appender.
            • 属性:
              • name:指定Appender的名字.
              • fileName:指定输出日志的目的文件带全路径的文件名.
              • filePattern:指定当发生Rolling时,文件的转移和重命名规则.
            • 子节点:
              • PatternLayout:输出格式,不设置默认为:%m%n.
              • Policies:指定滚动日志的策略,就是什么时候进行新建日志文件输出日志.
                • TimeBasedTriggeringPolicy:Policies子节点,基于时间的滚动策略,interval属性用来指定多久滚动一次,默认是1 hour。modulate=true用来调整时间:比如现在是早上3am,interval是4,那么第一次滚动是在4am,接着是8am,12am…而不是7am.
                • SizeBasedTriggeringPolicy:Policies子节点,基于指定文件大小的滚动策略,size属性用来定义每个日志文件的大小.
              • DefaultRolloverStrategy:用来指定同一个文件夹下最多有几个日志文件时开始删除最旧的,创建新的(通过max属性)。
      • Loggers节点:Root + Logger.
        • Root节点:用来指定项目的根日志,如果没有单独指定Logger,那么就会默认使用该Root日志输出
          • 属性:
            • level:日志输出级别,共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF.
            • 子节点:
              • AppenderRef:Root的子节点,用来指定该日志输出到哪个Appender.
        • Logger 节点:用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。
          • 属性:
            • name:用来指定该Logger所适用的类或者类所在的包全路径,继承自Root节点.
            • level:日志输出级别,共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF。
          • 子节点:
            • AppenderRef:Logger的子节点,用来指定该日志输出到哪个Appender,如果没有指定,就会默认继承自Root.如果指定了,那么会在指定的这个Appender和Root的Appender中都会输出,此时我们可以设置Logger的additivity="false"只在自定义的Appender中进行输出。

4、高级特性

4.1 异步日志配置

全局异步(需 disruptor 依赖)

<Configuration>
    <AsyncLogger name="com.example" level="DEBUG" includeLocation="true"/>
    <AsyncRoot level="INFO">
        <AppenderRef ref="Console"/>
    </AsyncRoot>
</Configuration>

引入 Maven 依赖:

<dependency>
    <groupId>com.lmaxgroupId>
    <artifactId>disruptorartifactId>
    <version>3.4.4version>
dependency>

4.2 自定义日志字段

使用 ThreadContext(即 MDC)添加上下文信息:

ThreadContext.put("requestId", UUID.randomUUID().toString());
logger.info("Processing request");
ThreadContext.clear();

在 Layout 中引用:

<PatternLayout pattern="%d{ISO8601} [%X{requestId}] %msg%n"/>

4.3 日志分离(RoutingAppender)

按条件动态路由日志到不同 Appender:

<Routing name="RoutingAppender">
    <Routes pattern="$${ctx:logType}">
        <Route key="AUDIT">
            <File name="AuditLog" fileName="logs/audit.log"/>
        </Route>
        <Route key="DEBUG">
            <Console name="Console"/>
        </Route>
    </Routes>
</Routing>

5、 集成日志门面

5.1 使用log4j-api做门面

虽然Log4j2也是日志门面,因为它的日志实现功能非常强大,性能优越。所以大家一般还是将Log4j2看作是日志的实现。

<!-- Log4j2 门面API-->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.14.1</version>
</dependency>
<!-- Log4j2 日志实现 -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.14.1</version>
</dependency>

5.2 使用slf4j做门面

 
 <dependency>
     <groupId>org.apache.logging.log4jgroupId>
     <artifactId>log4j-coreartifactId>
     <version>2.14.1version>
 dependency>
 <dependency>
     <groupId>org.apache.logging.log4jgroupId>
     <artifactId>log4j-slf4j-implartifactId>
     <version>2.14.1version> 
 dependency>

5.3 SpringBoot + slf4j + log4j2

  • 导入依赖
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starterartifactId>
        <exclusions>
            <exclusion>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-loggingartifactId>
            exclusion>
        exclusions>
    dependency>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-log4j2artifactId>
        <version>3.2.0version>
    dependency>
    
    如果用到 Mybatis-Plus 也需要排除默认日志依赖。
    SLF4J: Class path contains multiple SLF4J providers.
    SLF4J: Found provider [ch.qos.logback.classic.spi.LogbackServiceProvider@e874448]
    SLF4J: Found provider [org.apache.logging.slf4j.SLF4JServiceProvider@29b5cd00]
    SLF4J: See https://www.slf4j.org/codes.html#multiple_bindings for an explanation.
    SLF4J: Actual provider is of type [ch.qos.logback.classic.spi.LogbackServiceProvider@e874448]
    
  • 配置文件:
    • 在项目的 resource 目录下新建文件 log4j2-spring.xml,如果自定义其他名称需在 application.yml中配置:
      logging:
        config: classpath:log4j2.xml
      
    • 配置文件 log4j2-spring.xml 模版:
      
      
      
      <configuration monitorInterval="5">
        
      
        
        <Properties>
          
          
          <property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" />
          
          <property name="FILE_PATH" value="更换为你的日志路径" />
          <property name="FILE_NAME" value="更换为你的项目名" />
        Properties>
      
        <appenders>
      
          <console name="Console" target="SYSTEM_OUT">
            
            <PatternLayout pattern="${LOG_PATTERN}"/>
            
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
          console>
      
          
          <File name="Filelog" fileName="${FILE_PATH}/test.log" append="false">
            <PatternLayout pattern="${LOG_PATTERN}"/>
          File>
      
          
          <RollingFile name="RollingFileInfo" fileName="${FILE_PATH}/info.log" filePattern="${FILE_PATH}/${FILE_NAME}-INFO-%d{yyyy-MM-dd}_%i.log.gz">
            
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
              
              <TimeBasedTriggeringPolicy interval="1"/>
              <SizeBasedTriggeringPolicy size="10MB"/>
            Policies>
            
            <DefaultRolloverStrategy max="15"/>
          RollingFile>
      
          
          <RollingFile name="RollingFileWarn" fileName="${FILE_PATH}/warn.log" filePattern="${FILE_PATH}/${FILE_NAME}-WARN-%d{yyyy-MM-dd}_%i.log.gz">
            
            <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
              
              <TimeBasedTriggeringPolicy interval="1"/>
              <SizeBasedTriggeringPolicy size="10MB"/>
            Policies>
            
            <DefaultRolloverStrategy max="15"/>
          RollingFile>
      
          
          <RollingFile name="RollingFileError" fileName="${FILE_PATH}/error.log" filePattern="${FILE_PATH}/${FILE_NAME}-ERROR-%d{yyyy-MM-dd}_%i.log.gz">
            
            <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
              
              <TimeBasedTriggeringPolicy interval="1"/>
              <SizeBasedTriggeringPolicy size="10MB"/>
            Policies>
            
            <DefaultRolloverStrategy max="15"/>
          RollingFile>
      
        appenders>
      
        
        
        <loggers>
      
          
          <logger name="org.mybatis" level="info" additivity="false">
            <AppenderRef ref="Console"/>
          logger>
          
          
          <Logger name="org.springframework" level="info" additivity="false">
            <AppenderRef ref="Console"/>
          Logger>
      
          <root level="info">
            <appender-ref ref="Console"/>
            <appender-ref ref="Filelog"/>
            <appender-ref ref="RollingFileInfo"/>
            <appender-ref ref="RollingFileWarn"/>
            <appender-ref ref="RollingFileError"/>
          root>
        loggers>
      
      configuration>
      
  • 在代码中使用
    • 直接使用 slf4j
      public class Demo {
        private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExampleOther.class);
        
        public static void main(String... args) {
          log.error("Something else is wrong here");
        }
      }
      
    • 搭配 Lombok
      
      <dependency>
          <groupId>org.projectlombokgroupId>
          <artifactId>lombokartifactId>
          <version>1.18.26version>
      dependency>
      
      import lombok.extern.log4j.Log4j2;
      
      @Log4j2
      public class LogTest {
          public static void main(String[] args) {
              log.info("this is info log");
              log.error("this is error log");
              log.debug("this is debug log");
              log.warn("this is warn log");
              log.trace("this is trace log");
              log.fatal("this is fatal log");
          }
      }
      

6、最佳实践

  • 避免日志性能问题
    • 使用异步日志时,设置合理的缓冲区大小(bufferSize)。
    • 避免在高频代码中频繁记录 DEBUG 级别日志,可通过 logger.isDebugEnabled() 预先判断。
  • 配置分离与动态化
    • 使用 ${env:VARIABLE} 引用环境变量,实现环境差异化配置。
    • 将日志配置拆分为多个文件,通过 引入。
  • 日志归档与清理
    • 使用 RollingFile 结合时间和大小策略,避免单个文件过大。
    • 配置 DefaultRolloverStrategy 的 max 参数限制历史文件数量。
  • 监控与告警
    • 集成 PrometheusAppender 或 HTTPAppender 将日志发送到监控系统。
    • 使用 SMTPAppender 在发生 ERROR 日志时触发邮件告警。

十、资料

  • springboot整合log4j2日志全解

你可能感兴趣的:(java,python,开发语言)