深入理解 Logback.xml文件及运用

在Java开发中,日志记录是不可或缺的一部分,它帮助开发者监控应用程序的运行状态和调试问题。Logback作为一种强大的日志框架,与SLF4J(Simple Logging Facade for Java)紧密结合,提供了一种灵活的日志解决方案。SLF4J是一个简单的日志门面,允许开发者使用统一的API,而不必依赖具体的日志实现。Logback是SLF4J的默认实现,它不仅提供了丰富的功能,还具备高性能和低延迟的特点。

通过使用SLF4J,开发者可以在不同的环境中灵活地切换日志实现,例如Logback、Log4j或Java Util Logging,而无需修改代码。这种解耦设计使得应用程序更具可维护性和可扩展性。

1. 启动自动扫描

<configuration scan="true" scanPeriod="60 seconds" debug="false">
  • scan:启用自动扫描配置文件的更改,主要是针对 logback.xml 文件本身的更改,当 logback.xml 文件有任何修改时,Logback 会自动加载新的配置,而不需要重启应用程序。

2. 引入默认配置

<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
  • 引入默认配置后,只需在 logback.xml 中覆盖需要更改的部分。例如,如果您想修改某个日志级别或输出格式,只需重新定义相应的 appenderlogger,就会覆盖 Spring Boot 提供的默认配置。

3. 自定义转换规则

<conversionRule conversionWord="ip" converterClass="com.longfor.gaia.gfs.core.utils.IPAddressConverter" />
<conversionRule conversionWord="m" converterClass="com.property.commons.log.sensitive.SensitiveMessageConverter"/>
  • 这两个规则表示当日志中出现 ipm 这两个字符时,Logback 将调用相应的转换器 IPAddressConverterSensitiveMessageConverter 进行处理。具体来说,IPAddressConverter 可以将 IP 地址格式化或隐匿,而 SensitiveMessageConverter 可以处理敏感信息,例如替换或掩码。

4. Spring 属性

<springProperty scope="context" name="spring_application_name" source="spring.application.name" />
  • 这个定义的属性表示从 application.propertiesapplication.yml 中获取配置,例如 spring.application.name=myApplication,将取代 spring_application_name。这样在日志输出中可以动态使用应用名称。

  • 使用示例

    <property name="CONSOLE_LOG_PATTERN" value="%clr(${spring_application_name}){cyan}||%clr(%d{ISO8601}){faint}|%clr(%p)|%X{requestId}|%X{X-B3-TraceId:-}|%X{requestIp}|%X{userIp}|%ip|${server_port}|${PID}|%clr(%t){faint}|%clr(%.40logger{39}){cyan}.%clr(%method){cyan}:%L|%m|%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
    
    • 在日志输出中,${spring_application_name} 会被替换为 myApplication。经过转换后的日志示例可能是:
      myApplication||2024-10-28 10:00:00|INFO|requestId|X-B3-TraceId|192.168.0.1|192.168.0.1|0.0.0.0|8080|12345|main|com.example.MyClass.myMethod:42|Log message here
      
  • CONSOLE_LOG_PATTERN 的运用

    • CONSOLE_LOG_PATTERN 会在配置的 appender 中使用,尤其是在控制台输出时,确保日志格式统一和清晰。

5. 定义appender

在 Logback 中,Appender 是日志记录的输出目的地。您可以将日志输出到控制台、文件、数据库、消息队列等。通过配置不同的 Appender,您可以实现灵活的日志管理。

Appender 的具体运用
  1. 定义日志文件大小、保存时间和归档策略

    <!--定义日志文件大小 超过这个大小会压缩归档 -->
    <property name="DEBUG_MAX_FILE_SIZE" value="100MB"/>
    <property name="INFO_MAX_FILE_SIZE" value="100MB"/>
    <property name="ERROR_MAX_FILE_SIZE" value="100MB"/>
    <property name="TRACE_MAX_FILE_SIZE" value="100MB"/>
    <property name="WARN_MAX_FILE_SIZE" value="100MB"/>
    
    <!--定义日志文件最长保存时间 -->
    <property name="DEBUG_MAX_HISTORY" value="9"/>
    <property name="INFO_MAX_HISTORY" value="9"/>
    <property name="ERROR_MAX_HISTORY" value="9"/>
    <property name="TRACE_MAX_HISTORY" value="9"/>
    <property name="WARN_MAX_HISTORY" value="9"/>
    
    <!--定义归档日志文件最大保存大小 -->
    <property name="DEBUG_TOTAL_SIZE_CAP" value="5GB"/>
    <property name="INFO_TOTAL_SIZE_CAP" value="5GB"/>
    <property name="ERROR_TOTAL_SIZE_CAP" value="5GB"/>
    <property name="TRACE_TOTAL_SIZE_CAP" value="5GB"/>
    <property name="WARN_TOTAL_SIZE_CAP" value="5GB"/>
    
    • 最大文件大小:这些属性定义了每个日志文件的最大大小(例如 100MB)。当日志文件超过这个大小时,Logback 会进行压缩归档。
    • 最大保存时间:这些属性定义了每个日志文件的保存历史(例如 9 个备份)。这意味着系统会保留最近的 9 个日志文件,超出部分会被删除。
    • 总大小限制:这些属性限制了所有归档日志的总大小(例如 5GB)。如果总大小超过此限制,Logback 将删除旧的日志文件以腾出空间。
  2. RollingFileAppender 的配置

    例如,下面是一个 DEBUG_FILE 的配置:

    <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_HOME}/debug.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/backup/debug/debug.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
            <maxHistory>${DEBUG_MAX_HISTORY}</maxHistory>
            <maxFileSize>${DEBUG_MAX_FILE_SIZE}</maxFileSize>
            <totalSizeCap>${DEBUG_TOTAL_SIZE_CAP}</totalSizeCap>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>DEBUG</level>
        </filter>
    </appender>
    
    • Appender 类型RollingFileAppender 允许您根据大小和时间对日志文件进行滚动管理。
    • 当前日志文件<file>${LOG_HOME}/debug.log</file> 定义了当前的日志文件名。
    • Rolling PolicySizeAndTimeBasedRollingPolicy 指定了日志文件滚动的策略,这里是根据大小和时间进行滚动。
    • 文件名模式<fileNamePattern> 定义了归档文件的命名模式,其中 %d{yyyy-MM-dd} 表示日期,%i 表示索引(以防当天有多个文件)。
    • Encoder<encoder> 定义了日志输出的格式,使用 PatternLayoutEncoder 进行格式化。
    • Filter<filter> 用于过滤日志消息,只有 DEBUG 级别的日志会被记录。
  3. 其他类型的 Appender

    • INFO_FILEWARN_FILEERROR_FILE 的配置与 DEBUG_FILE 类似,只是它们的过滤级别不同(分别是 INFOWARNERROR)。
    • 这些配置确保不同级别的日志被输出到不同的文件中,方便进行管理和查看。
    • 有些Appender在<filter>加上了<onMatch><onMismatch>标签,若是没有加则会记录级别大于当前定义<level>的日志级别。若是加伤了则只会记录当前定义的<level>的日志级别。
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    
  4. Kafka Appender

    <appender name="KAFKA" class="com.github.danielwegener.logback.kafka.KafkaAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>
        <topic>${kafka_env}applog_${spring_application_name}</topic>
        <keyingStrategy class="com.github.danielwegener.logback.kafka.keying.NoKeyKeyingStrategy" />
        <deliveryStrategy="com.github.danielwegener.logback.kafka.delivery.AsynchronousDeliveryStrategy" />
        <producerConfig>bootstrap.servers=${kafka_broker}</producerConfig>
        <producerConfig>acks=0</producerConfig>
        <producerConfig>linger.ms=1000</producerConfig>
        <producerConfig>max.block.ms=0</producerConfig>
    </appender>
    
    • 这个 Appender 将日志发送到 Kafka 消息队列,以便进行异步处理或集中管理。
    • 配置了主题、键策略、交付策略和生产者配置,以确保日志消息能被有效地发送到 Kafka。
  5. AsyncAppender

    <!-- 异步传递策略,建议选择异步,不然连接kafka失败,会阻挡服务启动 -->
    <appender name="KAFKA_ASYNC" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="KAFKA" />
    </appender>
    
    • AsyncAppender 用于将日志记录异步化,以提高应用程序的性能。通过将日志消息发送到异步处理队列,应用程序可以更快地响应请求而不被日志记录阻塞。

6、根日志配置

<root level="INFO">
    <appender-ref ref="CONSOLE"/>
    <appender-ref ref="INFO_FILE"/>
    <appender-ref ref="WARN_FILE"/>
    <appender-ref ref="ERROR_FILE"/>
    <if condition='"true".equals(property("kafka_enabled"))'>
        <then>
            <appender-ref ref="KAFKA_ASYNC"/>
        </then>
    </if>
</root>
  1. <root level="INFO">

    • 设置根日志记录器的级别为 INFO。这意味着所有 INFO 及以上级别的日志(如 WARNERROR 等)才会被记录。
  2. <appender-ref ref="CONSOLE"/>

    • 指定输出到控制台的 appender,所有匹配级别的日志将会输出到控制台。
  3. <appender-ref ref="INFO_FILE"/>

    • 指定输出到 INFO_FILE appender,这个 appender 会记录 INFO 及以上级别的日志到指定文件中。
  4. <appender-ref ref="WARN_FILE"/>

    • 指定输出到 WARN_FILE appender,这个 appender 会记录 WARN 及以上级别的日志到指定文件中。
  5. <appender-ref ref="ERROR_FILE"/>

    • 指定输出到 ERROR_FILE appender,这个 appender 会记录 ERROR 级别的日志到指定文件中。
  6. 条件判断

    <if condition='"true".equals(property("kafka_enabled"))'>
        <then>
            <appender-ref ref="KAFKA_ASYNC"/>
        </then>
    </if>
    
    • 这段代码检查一个名为 kafka_enabled 的属性。如果这个属性的值为 "true",那么就会引用 KAFKA_ASYNC appender。
    • 这意味着,如果你希望将日志信息发送到 Kafka,需要在配置中将 kafka_enabled 属性设置为 true

所以只有日志级别大于或等于 中指定的级别的日志(即 INFO、WARN 和 ERROR)才会被记录。同时,这些日志必须通过在 标签中引用的 appender(如 CONSOLE、INFO_FILE、WARN_FILE 和 ERROR_FILE)进行输出。

因此,只有符合这两个条件的日志才会被实际记录和输出。这样的配置确保了日志的精确管理和灵活输出。

7、完整logback.xml文件

<?xml version="1.0" encoding="UTF-8"?>

<configuration scan="true" scanPeriod="60 seconds" debug="false">

    <!-- Override CONSOLE_LOG_PATTERN from defaults.xml -->
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>

    <conversionRule conversionWord="ip" converterClass="com.longfor.gaia.gfs.core.utils.IPAddressConverter" />
    <conversionRule conversionWord="m" converterClass="com.property.commons.log.sensitive.SensitiveMessageConverter"/>
    <springProperty scope="context" name="spring_application_name" source="spring.application.name" />
    <springProperty scope="context" name="server_port" source="server.port" />
    <springProperty scope="context" name="LOG_HOME" source="logging.path" defalutValue="../logs"/>
    <springProperty scope="context" name="env" source="env" defalutValue="sit"/>
    <springProperty scope="context" name="console_switch" source="log.console.switch" defalutValue="false"/>

    <property name="CONSOLE_LOG_PATTERN" value="%clr(${spring_application_name}){cyan}||%clr(%d{ISO8601}){faint}|%clr(%p)|%X{requestId}|%X{X-B3-TraceId:-}|%X{requestIp}|%X{userIp}|%ip|${server_port}|${PID}|%clr(%t){faint}|%clr(%.40logger{39}){cyan}.%clr(%method){cyan}:%L|%m|%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
    <property name="FILE_LOG_PATTERN" value="${spring_application_name}||%d{ISO8601}|%p|%X{requestId}|%X{X-B3-TraceId:-}|%X{requestIp}|%X{userIp}|%ip|${server_port}|${PID}|%t|%.40logger{39}.%method:%L|%m|%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
    <include resource="org/springframework/boot/logging/logback/console-appender.xml"/>

    <springProperty scope="context" name="spring_application_name" source="spring.application.name" />
    <springProperty scope="context" name="kafka_enabled" source="longfor.web.logging.kafka.enabled"/>
    <springProperty scope="context" name="kafka_broker" source="longfor.web.logging.kafka.broker"/>
    <springProperty scope="context" name="kafka_env" source="longfor.web.logging.kafka.env"/>

    <!--定义日志文件大小 超过这个大小会压缩归档 -->
    <property name="DEBUG_MAX_FILE_SIZE" value="100MB"/>
    <property name="INFO_MAX_FILE_SIZE" value="100MB"/>
    <property name="ERROR_MAX_FILE_SIZE" value="100MB"/>
    <property name="TRACE_MAX_FILE_SIZE" value="100MB"/>
    <property name="WARN_MAX_FILE_SIZE" value="100MB"/>

    <!--定义日志文件最长保存时间 -->
    <property name="DEBUG_MAX_HISTORY" value="9"/>
    <property name="INFO_MAX_HISTORY" value="9"/>
    <property name="ERROR_MAX_HISTORY" value="9"/>
    <property name="TRACE_MAX_HISTORY" value="9"/>
    <property name="WARN_MAX_HISTORY" value="9"/>

    <!--定义归档日志文件最大保存大小,当所有归档日志大小超出定义时,会触发删除  -->
    <property name="DEBUG_TOTAL_SIZE_CAP" value="5GB"/>
    <property name="INFO_TOTAL_SIZE_CAP" value="5GB"/>
    <property name="ERROR_TOTAL_SIZE_CAP" value="5GB"/>
    <property name="TRACE_TOTAL_SIZE_CAP" value="5GB"/>
    <property name="WARN_TOTAL_SIZE_CAP" value="5GB"/>

    <!-- 按照每天生成日志文件 -->
    <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 当前Log文件名 -->
        <file>${LOG_HOME}/debug.log</file>
        <!-- 压缩备份设置 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/backup/debug/debug.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
            <maxHistory>${DEBUG_MAX_HISTORY}</maxHistory>
            <maxFileSize>${DEBUG_MAX_FILE_SIZE}</maxFileSize>
            <totalSizeCap>${DEBUG_TOTAL_SIZE_CAP}</totalSizeCap>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>DEBUG</level>
        </filter>
    </appender>
    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 当前Log文件名 -->
        <file>${LOG_HOME}/info.log</file>
        <!-- 压缩备份设置 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/backup/info/info.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
            <maxHistory>${INFO_MAX_HISTORY}</maxHistory>
            <maxFileSize>${INFO_MAX_FILE_SIZE}</maxFileSize>
            <totalSizeCap>${INFO_TOTAL_SIZE_CAP}</totalSizeCap>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
    </appender>
    <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 当前Log文件名 -->
        <file>${LOG_HOME}/warn.log</file>
        <!-- 压缩备份设置 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/backup/warn/warn.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
            <maxHistory>${WARN_MAX_HISTORY}</maxHistory>
            <maxFileSize>${WARN_MAX_FILE_SIZE}</maxFileSize>
            <totalSizeCap>${WARN_TOTAL_SIZE_CAP}</totalSizeCap>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 当前Log文件名 -->
        <file>${LOG_HOME}/error.log</file>
        <!-- 压缩备份设置 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/backup/error/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
            <maxHistory>${ERROR_MAX_HISTORY}</maxHistory>
            <maxFileSize>${ERROR_MAX_FILE_SIZE}</maxFileSize>
            <totalSizeCap>${ERROR_TOTAL_SIZE_CAP}</totalSizeCap>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    <appender name="KAFKA" class="com.github.danielwegener.logback.kafka.KafkaAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>
        <topic>${kafka_env}applog_${spring_application_name}</topic>
        <keyingStrategy class="com.github.danielwegener.logback.kafka.keying.NoKeyKeyingStrategy" />
        <deliveryStrategy class="com.github.danielwegener.logback.kafka.delivery.AsynchronousDeliveryStrategy" />
        <producerConfig>bootstrap.servers=${kafka_broker}</producerConfig>
        <!-- don't wait for a broker to ack the reception of a batch.  -->
        <producerConfig>acks=0</producerConfig>
        <!-- wait up to 1000ms and collect log messages before sending them as a batch -->
        <producerConfig>linger.ms=1000</producerConfig>
        <!-- even if the producer buffer runs full, do not block the application but start to drop messages -->
        <producerConfig>max.block.ms=0</producerConfig>
        <!-- Optional parameter to use a fixed partition -->
        <!--        <partition>8</partition>-->
    </appender>

    <appender name="KAFKA_ASYNC" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="KAFKA" />
    </appender>

    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="INFO_FILE"/>
        <appender-ref ref="WARN_FILE"/>
        <appender-ref ref="ERROR_FILE"/>
        <if condition='"true".equals(property("kafka_enabled"))'>
            <then>
                <appender-ref ref="KAFKA_ASYNC"/>
            </then>
        </if>
    </root>

</configuration>

8、如何使用@SLF4j注解进行日志记录

在使用@Slf4j注解之前,请确保项目中已经添加了SLF4J和Logback的相关依赖。使用@Slf4j注解可以简化日志记录的过程,自动为类生成一个名为log的Logger实例,开发者无需手动创建Logger对象。

下面是一个简单的示例:

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class MyService {
    
    

    public void execute() {
    
    
        log.info("执行方法开始");
        try {
    
    
            // 执行一些操作
        } catch (Exception e) {
    
    
            log.error("发生错误", e);
        }
        log.info("执行方法结束");
    }
}

在这个示例中,我们通过@Slf4j注解为MyService类添加了一个Logger实例。然后,在execute方法中,我们使用log.info和log.error记录了不同级别的日志信息。这种方式不仅简化了代码,还提高了代码的可读性。

猜你喜欢

转载自blog.csdn.net/weixin_54574094/article/details/143328593