Spring Boot中的日志记录

2023/05/12

1. 概述

在这个简短的教程中,我们将探讨Spring Boot中可用的主要日志记录选项。

有关Logback的更深入信息可在Logback指南中找到,而在Log4j2介绍–Appenders、Layouts和Filters中介绍了Log4j2。

2. 初始设置

首先我们创建一个Spring Boot模块,推荐的方法是使用Spring Initializr,我们在Spring Boot教程对此进行了介绍。

现在让我们创建我们唯一的类文件LoggingController:

@RestController
public class LoggingController {

    Logger logger = LoggerFactory.getLogger(LoggingController.class);

    @RequestMapping("/")
    public String index() {
        logger.trace("A TRACE Message");
        logger.debug("A DEBUG Message");
        logger.info("An INFO Message");
        logger.warn("A WARN Message");
        logger.error("An ERROR Message");

        return "Howdy! Check out the Logs to see the output...";
    }
}

一旦我们加载了Web应用程序,我们就可以通过简单地访问http://localhost:8080/来触发这些日志记录行

3. 零配置日志记录

Spring Boot是一个非常有用的框架,它允许我们忘记大多数配置设置,其中许多设置都是自以为是地自动调整的。

在日志记录的情况下,唯一的强制性依赖项是Apache Commons Logging。

我们只有在使用Spring 4.x(Spring Boot 1.x)时才需要导入它,因为它是由Spring Framework的spring-jcl模块在Spring 5(Spring Boot 2.x)中提供的。

如果我们使用的是Spring Boot Starter(我们几乎总是这样),我们根本不应该担心导入spring-jcl,这是因为每个Starter,比如我们的spring-boot-starter-web,都依赖于spring-boot-starter-logging,它已经为我们引入了spring-jcl。

3.1 默认Logback日志记录

当使用Starter时,默认情况下使用Logback进行日志记录

Spring Boot使用模式和ANSI颜色对其进行预配置,以使标准输出更具可读性。

现在让我们运行应用程序并访问http://localhost:8080/页面,看看控制台中发生了什么:

正如我们所见,Logger的默认日志记录级别预设为INFO,这意味着TRACE和DEBUG消息是不可见的

为了在不更改配置的情况下激活它们,我们可以在命令行上传递–debug或–trace参数

java -jar target/spring-boot-logging-1.0.0.jar --trace

3.2 日志级别

Spring Boot还允许我们通过环境变量访问更细粒度的日志级别设置,我们可以通过多种方式实现这一目标。

首先,我们可以在VM选项中设置日志记录级别:

-Dlogging.level.org.springframework=TRACE 
-Dlogging.level.cn.tuyucheng.taketoday=TRACE

或者,如果我们使用Maven,我们可以通过命令行定义我们的日志设置:

mvn spring-boot:run 
  -Dspring-boot.run.arguments=--logging.level.org.springframework=TRACE,--logging.level.cn.tuyucheng.taketoday=TRACE

使用Gradle时,我们可以通过命令行传递日志设置,这将需要设置bootRun任务

完成后,我们运行应用程序:

./gradlew bootRun -Pargs=--logging.level.org.springframework=TRACE,--logging.level.cn.tuyucheng.taketoday=TRACE

如果我们想永久更改详细程度,我们可以在application.properties文件中执行此操作,如下所述

logging.level.root=WARN
logging.level.cn.tuyucheng.taketoday=TRACE

最后,我们可以使用我们的日志框架配置文件永久更改日志记录级别

我们提到Spring Boot Starter默认使用Logback,让我们看看如何定义Logback配置文件的片段,其中我们为两个单独的包设置级别:

<logger name="org.springframework" level="INFO" />
<logger name="cn.tuyucheng.taketoday" level="INFO" />

请记住,如果使用上述不同选项多次定义包的日志级别,但使用不同的日志级别,则将使用最低级别

因此,如果我们同时使用Logback、Spring Boot和环境变量设置日志记录级别,则日志级别将为TRACE,因为它是请求级别中最低的。

4. Logback配置日志记录

即使默认配置很有用(例如,在POC或快速实验期间零时间上手),但它很有可能不足以满足我们的日常需求。

让我们看看如何包含一个具有不同颜色和日志记录模式的Logback配置,具有单独的控制台和文件输出规范,以及一个合适的滚动策略来避免生成巨大的日志文件。

首先,我们应该找到一个解决方案,允许单独处理我们的日志记录设置,而不是污染application.properties,application.properties通常用于许多其他应用程序设置。

当类路径中的文件具有以下名称之一时,Spring Boot将自动加载它并覆盖默认配置

  • logback-spring.xml
  • logback.xml
  • logback-spring.groovy
  • logback.groovy

Spring建议尽可能使用-spring变体而不是普通变体,如此处所述。

让我们写一个简单的logback-spring.xml:

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

    <property name="LOGS" value="./logs"/>

    <appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>
                %black(%d{ISO8601}) %highlight(%-5level) [%blue(%t)] %yellow(%C{1.}): %msg%n%throwable
            </Pattern>
        </layout>
    </appender>

    <appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOGS}/spring-boot-logger.log</file>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <Pattern>%d %p %C{1.} [%t] %m%n</Pattern>
        </encoder>

        <rollingPolicy
                class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- rollover daily and when the file reaches 10 MegaBytes -->
            <fileNamePattern>${LOGS}/archived/spring-boot-logger-%d{yyyy-MM-dd}.%i.log
            </fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
    </appender>

    <!-- LOG everything at INFO level -->
    <root level="info">
        <appender-ref ref="RollingFile"/>
        <appender-ref ref="Console"/>
    </root>

    <!-- LOG "cn.tuyucheng.taketoday*" at TRACE level -->
    <logger name="cn.tuyucheng.taketoday" level="trace" additivity="false">
        <appender-ref ref="RollingFile"/>
        <appender-ref ref="Console"/>
    </logger>

</configuration>

当我们运行应用程序时,输出如下:

正如我们所见,它现在记录了TRACE和DEBUG消息,并且整个控制台模式在文本和色彩上都与以前不同。

它还会在当前路径下创建的/logs文件夹中记录一个文件,并通过滚动策略将其存档。

5. Log4j2配置日志记录

虽然Apache Commons Logging是核心,而Logback是提供的参考实现,但已经包含了到其他日志库的所有路由,以便轻松切换到它们。

但是,为了使用除Logback之外的任何日志记录库,我们需要将其从我们的依赖项中排除

对于像这样的每个Starter(它是我们示例中唯一的一个,但我们可以有很多):

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

我们需要将它变成一个精简版,并且(仅一次)添加我们的替代库,这里通过Starter本身:

<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>

此时,我们需要在类路径中放置一个名为以下之一的文件:

  • log4j2-spring.xml
  • log4j2.xml

我们将通过Log4j2(通过SLF4J)打印,无需进一步修改。

让我们创建一个简单的log4j2-spring.xml:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
              <PatternLayout
                    pattern="%style{%d{ISO8601}}{black} %highlight{%-5level }[%style{%t}{bright,blue}] %style{%C{1.}}{bright,yellow}: %msg%n%throwable"/> 
        </Console>

        <RollingFile name="RollingFile"
                     fileName="./logs/spring-boot-logger-log4j2.log"
                     filePattern="./logs/$${date:yyyy-MM}/spring-boot-logger-log4j2-%d{-dd-MMMM-yyyy}-%i.log.gz">
            <PatternLayout>
                <pattern>%d %p %C{1.} [%t] %m%n</pattern>
            </PatternLayout>
            <Policies>
                <!-- rollover on startup, daily and when the file reaches 
                    10 MegaBytes -->
                <OnStartupTriggeringPolicy/>
                <SizeBasedTriggeringPolicy
                        size="10 MB"/>
                <TimeBasedTriggeringPolicy/>
            </Policies>
        </RollingFile>
    </Appenders>

    <Loggers>
        <!-- LOG everything at INFO level -->
        <Root level="info">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="RollingFile"/>
        </Root>

        <!-- LOG "cn.tuyucheng.taketoday*" at TRACE level -->
        <Logger name="cn.tuyucheng.taketoday" level="trace"></Logger>
    </Loggers>
</Configuration>

当我们运行应用程序时,输出如下:

正如我们所看到的,输出与Logback的输出有很大不同,这证明我们现在使用的确实是Log4j2。

除了XML配置之外,Log4j2还允许我们使用YAML或JSON配置,如下所述

6. 没有SLF4J的Log4j2

我们也可以在本机使用Log4j2,而无需通过SLF4J。

为了做到这一点,我们只需使用原生类:

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
// [...]
Logger logger = LogManager.getLogger(LoggingController.class);

我们不需要对标准Log4j2 Spring Boot配置执行任何其他修改。

我们现在可以利用Log4j2的全新功能,而不会受困于旧的SLF4J接口。但是我们也依赖于这个实现,当切换到另一个日志记录框架时我们需要重写我们的代码。

7. 使用Lombok记录

在我们目前看到的示例中,我们必须从我们的日志记录框架中声明一个Logger实例。这个样板代码可能很烦人,我们可以使用Lombok引入的各种注解来避免它。

首先我们需要在我们的构建脚本中添加Lombok依赖项才能使用它:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.20</version>
    <scope>provided</scope>
</dependency>

7.1 @Slf4j和@CommonsLog

SLF4J和Apache Commons Logging APIs允许我们灵活地改变我们的日志框架而不影响我们的代码。

我们可以使用Lombok的@Slf4j和@CommonsLog注解将正确的记录器实例添加到我们的类中:用于SLF4J的org.slf4j.Logger和用于Apache Commons Logging的org.apache.commons.logging.log。

要查看这些注解的实际效果,让我们创建一个类似于LoggingController但没有记录器实例的类,我们将其命名为LombokLoggingController并使用@Slf4j对其进行标注:

@RestController
@Slf4j
public class LombokLoggingController {

    @RequestMapping("/lombok")
    public String index() {
        log.trace("A TRACE Message");
        log.debug("A DEBUG Message");
        log.info("An INFO Message");
        log.warn("A WARN Message");
        log.error("An ERROR Message");

        return "Howdy! Check out the Logs to see the output...";
    }
}

请注意,我们使用log作为记录器实例稍微调整了代码片段,这是因为添加注解@Slf4j会自动添加一个名为log的字段。

使用零配置日志记录,应用程序将使用底层日志记录实现Logback进行日志记录。同样,Log4j2实现用于使用Log4j2-Configuration Logging进行日志记录。

当我们用@CommonsLog替换注解@Slf4j时,我们得到相同的行为。

7.2 @Log4j2

我们可以使用注解@Log4j2来直接使用Log4j2,因此,我们对LombokLoggingController进行了简单的更改,以使用@Log4j2而不是@Slf4j或@CommonsLog:

@RestController
@Log4j2
public class LombokLoggingController {

    @RequestMapping("/lombok")
    public String index() {
        log.trace("A TRACE Message");
        log.debug("A DEBUG Message");
        log.info("An INFO Message");
        log.warn("A WARN Message");
        log.error("An ERROR Message");

        return "Howdy! Check out the Logs to see the output...";
    }
}

除了日志记录之外,还有来自Lombok的其他注解有助于保持我们的代码干净整洁。有关它们的更多信息,请参阅Project Lombok介绍,我们还有一个关于使用Eclipse和IntelliJ设置Lombok的教程。

8. 谨防Java Util Logging

Spring Boot还通过logging.properties配置文件支持JDK日志记录。

不过,在有些情况下使用它并不是一个好主意,从文档中可知:

Java Util Logging存在已知的类加载问题,这些问题在从“可执行jar”运行时会导致问题,我们建议你在从“可执行jar”运行时尽可能避免使用它。

当使用Spring 4手动排除pom.xml中的commons-logging时,这也是一种很好的做法,以避免日志库之间的潜在冲突。Spring 5会自动处理它,因此我们在使用Spring Boot 2时不需要做任何事情。

9. Windows上的JANSI

虽然Linux和Mac OS X等基于Unix的操作系统默认支持ANSI颜色代码,但在Windows控制台上,一切都将是单色的,Windows可以通过一个名为JANSI的库获取ANSI颜色

不过,我们应该注意可能的类加载缺点

我们必须在配置中导入并显式激活它,如下所示:

Logback

<configuration debug="true">
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <withJansi>true</withJansi>
        <encoder>
            <pattern>[%thread] %highlight(%-5level) %cyan(%logger{15}) - %msg %n</pattern>
        </encoder>
    </appender>
    <!-- more stuff -->
</configuration>

Log4j2

许多平台原生支持ANSI转义序列,但默认情况下Windows不支持。要启用ANSI支持,请将Jansi jar添加到我们的应用程序并将属性log4j.skipJansi设置为false,这允许Log4j在写入控制台时使用Jansi添加ANSI转义码。 注意:在Log4j 2.10之前,默认情况下启用Jansi,Jansi需要本机代码这一事实意味着Jansi只能由单个类加载器加载。对于Web应用程序,这意味着Jansi jar必须位于Web容器的类路径中。为了避免给Web应用程序带来问题,Log4j不再自动尝试在没有显式配置的情况下从Log4j 2.10开始加载Jansi。

还值得注意的是:

  • 布局文档页面highlight{pattern}{style}部分包含有用的Log4j2 JANSI信息。
  • 虽然JANSI可以为输出着色,但Spring Boot的横幅(原生的或通过banner.txt文件自定义的)将保持单色。

10. 总结

我们了解了在Spring Boot项目中与主要日志记录框架交互的主要方法,并探讨了每种解决方案的主要优点和缺陷。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章