SpringBoot 日志配置

1. Logback
1. 引用

由于 Logback 为 spring-boot 默认日志框架,所以无需再引用,但对于非spring - boot 项目,可以做如下引用

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.12</version>
</dependency>

Logback 的核心模块为 logback-classic,它提供了一个 SLF4J 的实现,兼容 Log4j API,可以无缝地替换 Log4j。它自身已经包含了 logback-core 模块,而 logback-core,顾名思义就是 logback 的核心功能,包括日志记录器、Appender、Layout 等。其他 logback 模块都依赖于该模块

2. 配置

logback 可以通过 XML 或者 Groovy 配置。下面以 XML 配置为例。logback 的 XML 配置文件名称通常为 logback.xml 或者 logback-spring.xml(在 Spring Boot 中),需要放置在 classpath 的根目录下,

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!--定义日志文件的存储地址,使用Spring的属性文件配置方式-->
    <springProperty scope="context" name="log.home" source="log.home" defaultValue="logs"/>

    <!--定义日志文件的路径-->
    <property name="LOG_PATH" value="${log.home}"/>

    <!--定义控制台输出-->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%-5relative [%thread] %-5level %logger{35} - %msg%n</pattern>
        </encoder>
    </appender>

    <!--定义 INFO 及以上级别信息输出到控制台-->
    <root level="INFO">
        <appender-ref ref="console"/>
    </root>

    <!--定义所有组件的日志级别,如所有 DEBUG-->
    <logger name="com.example" level="DEBUG"/>

    <!-- date 格式定义 -->
    <property name="LOG_DATEFORMAT" value="yyyy-MM-dd"/>

    <!-- 定义日志归档文件名称格式,每天生成一个日志文件 -->
    <property name="ARCHIVE_PATTERN" value="${LOG_PATH}/%d{${LOG_DATEFORMAT}}/app-%d{${LOG_DATEFORMAT}}-%i.log.gz"/>

    <!--定义文件输出,会根据定义的阈值进行切割,支持自动归档压缩过期日志-->
    <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/app.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!--定义日志文件切割的阈值,本例是 50MB-->
            <maxFileSize>50MB</maxFileSize>
            <!--定义日志文件保留时间,本例是每天生成一个日志文件-->
            <fileNamePattern>${ARCHIVE_PATTERN}</fileNamePattern>
            <maxHistory>30</maxHistory>
            <!-- zip 压缩生成的归档文件 -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>50MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!-- 删除过期文件 -->
            <cleanHistoryOnStart>true</cleanHistoryOnStart>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{35} - %msg%n</pattern>
        </encoder>
    </appender>

    <!--定义 ERROR 级别以上信息输出到文件-->
    <logger name="com.example.demo" level="ERROR" additivity="false">
        <appender-ref ref="file"/>
    </logger>

    <!--异步输出日志信息-->
    <appender name="asyncFile" class="ch.qos.logback.classic.AsyncAppender">
        <discardingThreshold>0</discardingThreshold>
        <queueSize>256</queueSize>
        <appender-ref ref="file"/>
    </appender>

    <!--定义INFO及以上级别信息异步输出到文件-->
    <logger name="com.example" level="INFO" additivity="false">
        <appender-ref ref="asyncFile"/>
    </logger>

</configuration>

其中,主要包括以下配置:

  • springProperty 定义了 log 文件的存储路径,可以通过 Spring 的属性文件配置方式进行设置,如果没有配置则默认存储在 logs 目录下。

  • appender 定义了日志输出的目标,这里包括了控制台输出和文件输出两种,具体可以根据需求进行配置。

  • root 定义了默认的日志级别和输出目标,默认情况下,INFO 级别以上的日志信息会输出到控制台,可以根据实际需求进行修改。

  • logger 定义了不同组件的日志级别和输出目标,例如,这里定义了 com.example 这个组件的日志级别为 DEBUG,而 com.example.demo 这个组件的日志级别为 ERROR,并将其输出到文件中。

  • rollingPolicy 定义了日志文件的切割规则和归档策略,此处定义了日志文件每个 50MB 进行切割,每天生成一个日志文件,并且压缩和删除过期文件,最多保留 30 天的日志文件。

  • encoder 定义了日志信息的输出格式,具体的格式可以自行定义。

  • asyncAppender 定义了异步输出日志的方式,对于高并发时,可以使用异步输出来提高系统的性能。

  • discardingThreshold 定义了异步输出队列的阈值,当队列中的数据量超过此值时,会丢弃最早放入的数据,此处设置为 0 表示队列不会丢弃任何数据。

  • queueSize 定义了异步输出队列的大小,当队列满时,会等待队列中的数据被消费后再将数据放入队列中,此处设置为 256。

3. 演示

我们新建一个普通工程(非spring工程),引用Logback后,把上述配置文件复制进logback.xml,然后将工程结构设置成如下模式

图片

其中两个类的代码如下:

public class Main {
    private static final Logger log = LoggerFactory.getLogger(Main.class);
    public static void main(String[] args) {
        log.trace("This is a Main trace message.");
        log.debug("This is a Main debug message.");
        log.info("This is a Main info message.");
        log.warn("This is a Main warn message.");
        log.error("This is a Main error message.");
        Slave.main(args);
    }
}

public class Slave {
    private static final Logger log = LoggerFactory.getLogger(Slave.class);
    public static void main(String[] args) {
        log.trace("This is a Slave trace message.");
        log.debug("This is a Slave debug message.");
        log.info("This is a Slave info message.");
        log.warn("This is a Slave warn message.");
        log.error("This is a Slave error message.");
    }
}

我们想实现这样的效果,首先日志要同时 输出到控制台 及 日志文件,且不同层级的代码,输出的日志层级也不同。那么我们可以对上述的xml做出一些调整:

因为是非Spring项目,所以 springProperty 这样的标签就不要用了,我们直接写死一个日志文件地址即可。

<!--定义日志文件的路径-->
<property name="LOG_PATH" value="D://logs/log"/>

去掉原有的那些root、logger标签,我们自己新建两个logger,用于两个不同的层级。我们想里层输出 debug 级别,外层输出info 级别,我们可以这么设置。并且同时输出到控制台及日志文件

<logger name="com.zhanfu" level="INFO">
    <appender-ref ref="file" />
    <appender-ref ref="console"/>
</logger>
<logger name="com.zhanfu.child" level="DEBUG" additivity="false">
    <appender-ref ref="file" />
    <appender-ref ref="console"/>
</logger>

当我们运行Main.main的时候,就可以得到以下日志,slave 能输出debug级别,Main 只能输出 info及以上级别

图片

4. 细节点

其实我们上面的演示,有两个细节点,需要注意一下。一个就是我们的

 <logger name="com.zhanfu.child" level="DEBUG" additivity="false">

使用了一个 additivity="false" 的属性,这其实是因为 logger 这个标签在锁定某个目录时,可能会发生层级关系。比如我们的两个 logger, 一个针对的目录是 com.zhanfu 另一个是 com.zhanfu.child ,后者就会被前者包含。

当我们的 com.zhanfu.child.Slave 打印日志时,当然会使用后者(更精确)的设置,但前者的设置还使用吗?就依赖于 additivity=“false”,此处如果我们把 additivity="false" (该属性默认值为true)去掉,再来打印日志

图片

就会发现,Slave 的日志打了两遍,而且连 debug 级别的都打了两遍,我们可以把这种逻辑理解为继承,子类执行一遍,父类还能在执行一遍,但 leve 属性还是会采用子类而非父类的。

另一点就是我们把 root 标签删除了,root 其实是一个顶级的 logger , 其他的logger都可以视为它的子类,如果那些logger存在没涵盖的地方,或其没有指定 additivity="false" ,那最后root的设置就会被使用。比如我们将设置改为如下:

<logger name="com.zhanfu" level="INFO">
    <appender-ref ref="file" />
    <appender-ref ref="console"/>
</logger>
<logger name="com.zhanfu.child" level="DEBUG">
    <appender-ref ref="file" />
    <appender-ref ref="console"/>
</logger>
<root level="WARN">
    <appender-ref ref="console"/>
</root>

结果控制台的输出日志,Main会重复两次,Slave 会重复三次,如下

但是因为我们的 root 只配置了控制台输出,所以日志文件里还是不会变的

图片

2. Log4j 2
1. 引用

对于spring-boot项目,除了引用 Log4j 2 我们还需要先剔除 Logback 的引用,对于普通项目,我们只需直接引用即可。但注意我们的原则,通过 SLF4J 来使用 Log4j2,所以引用下面这个包

<dependency>
     <groupId>org.apache.logging.log4j</groupId>
     <artifactId>log4j-slf4j-impl</artifactId>
     <version>2.13.3</version>
</dependency>

其内包含 Log4j2 的实现,和 SLF4J 的 API,如下:

图片

2. 配置

Log4j2 的配置逻辑和 logback 是类似的,只有些细节不同,比如Logger 的首字母大写等等,最后我们写下这样一个 log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO" monitorInterval="30">
  <Properties>
    <Property name="logPath">logs</Property>
  </Properties>
  <Appenders>
    <Console name="Console" target="SYSTEM_OUT">
      <PatternLayout pattern="%d{ISO8601} [%t] %-5level %logger{36} - %msg%n" />
    </Console>
    <RollingFile name="File" fileName="${logPath}/example.log"
                 filePattern="${logPath}/example-%d{yyyy-MM-dd}-%i.log">
      <PatternLayout pattern="%d{ISO8601} [%t] %-5level %logger{36} - %msg%n"/>
      <Policies>
        <SizeBasedTriggeringPolicy size="10 MB"/>
      </Policies>
      <DefaultRolloverStrategy max="4"/>
    </RollingFile>
  </Appenders>
  <Loggers>
    <Logger name="com.zhanfu.child" level="DEBUG">
      <AppenderRef ref="File"/>
      <AppenderRef ref="Console"/>
    </Logger>
    <Logger name="com.zhanfu" level="INFO">
      <AppenderRef ref="File"/>
      <AppenderRef ref="Console"/>
    </Logger>
    <Root level="WARN">
      <AppenderRef ref="Console" />
    </Root>
  </Loggers>
</Configuration>
  • Properties

部分定义了一个 logPath 属性,方便在其他地方引用。

  • Appenders

定义了两个 Appender:Console 和 RollingFile,分别将日志输出到控制台和文件中。RollingFile 使用了 RollingFileAppender,并设置了日志滚动策略和默认的备份文件数量。

  • Loggers

定义了三个 Logger:com.zhanfu.child 的日志级别为 DEBUG,com.zhanfu 的日志级别为INFO,Root Logger 的日志级别为 WARN。并指定了两个 Appender:Console 和 File。

3. 演示

由于我们的配置逻辑没变,所以日志的结果还是一样的:

图片

图片

3. 对比

Log4j2和Logback都是Java应用程序中最流行的日志框架之一。它们均具备高度的可配置性和使用灵活性,并提供了一系列有用的功能,例如异步日志记录和日志过滤等。下面从配置遍历性、功能性、性能等方面进行比较和总结。

配置遍历性

Logback的配置文件格式相对简单,易于阅读和修改。它支持符号来引用变量、属性和环境变量等。此外,它还支持条件日志记录(根据日志级别、日志记录器名称或时间等),以及滚动文件的大小或日期等。

Log4j2的配置文件格式较复杂,但它在配置文件中提供了大量的选项来控制日志记录。它支持在配置文件中直接声明上下文参数、过滤器、输出器和Appender等,这使得它的配置更加灵活。此外,Log4j2还支持异步日志记录、日志事件序列化和性能优化等。

总体来说,两者都很好地支持了配置遍历性,但Log4j2提供了更多的选项和更高的灵活性。

功能性

Logback提供了一系列基本的日志记录功能,例如异步Appender、滚动文件和GZIP压缩等。它还支持与SLF4J一起使用,可以很容易地与其他日志框架集成。

Log4j2提供了更多的高级功能,例如异步日志记录、性能优化和日志事件序列化等。它还支持Lambda表达式,可以使日志记录器更加简洁和易读。此外,Log4j2还支持Flume和Kafka等大数据处理框架,可以方便地将日志记录发送到这些框架中。

总体来说,Log4j2提供了更多的高级功能,并且可以更好地与大数据处理框架集成。

性能

Logback的性能很好,可以处理高吞吐量的日志记录。它采用了异步记录器,利用了多线程来提高性能。

Log4j2在性能方面更加强大。它使用了异步记录器和多线程,还引入了RingBuffer数据结构和Disruptor库来加速日志事件的传递和处理。这使得它比Logback具有更高的吞吐量和更低的延迟。

综上所述,Log4j2在配置灵活性、功能性和性能方面都比Logback更为强大。但如果需要轻量级的日志框架或者只需要基本的日志记录功能,Logback也是一个不错的选择

但如果我们同时引用了这两者,会报错吗?还是会使用其中的某一个?

 <dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.12</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>2.13.3</version>
</dependency>

可以看到,SLF4J 发现了系统中同时存在两个插件框架,并最终选择了使用 Logback

图片

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值