Spring Boot整合Logback日志
in JavaDevelop with 0 comment

Spring Boot整合Logback日志

in JavaDevelop with 0 comment

介绍

作为一名开发人员应该了解日志在日常开发程序的调试和监控过程中的重要性。本章节通过介绍常用的 Logback 日志框架再来具体讲述我们在一个 Spring Boot 的项目中应该怎样来集成这样的一个日志框架。实验的最终效果记录了程序运行过程中的日志,同时生成了控制台日志和文件日志记录。

知识点

本课程内容中涉及 Spring Boot 项目源代码来自 xkcoding/spring-boot-demo,该项目使用 MIT License 开源协议。

日志的重要性及常用的日志框架

日志的重要性

日志记录了系统行为的时间、地点等很多细节的具体信息,可以帮助开发人员了解并且监控系统的状态,在发生错误或者接近某种危险状态时能够及时提醒开发人员处理,往往在系统产生问题时承担问题定位与诊断和解决的重要角色。一般我们的项目出现问题的时候首先会想到 debug,但是很多线上的问题是只能通过我们进行日志分析才可以解决的,所以作为一名开发人员需要明确日志在日常开发环节中的重要性。

常用的日志框架

下面列举一些常用的日志框架:Commons Logging、Slf4j、Log4j,Log4j2,Logback。

上述的这些常用日志框架中,Log4j2 和 Log4j 完全不兼容,Commons Logging 和 Slf4j 是日志门面,这里需要介绍一下日志门面的概念,所谓日志门面就是门面模式的一个典型的应用,门面模式也称为外观模式,而日志门面框架就是一套提供了日志相关功能的接口而无具体实现的框架,日志记录是通过调用具体的实现框架来进行的。所以日志门面框架是兼容具体日志实现框架的。典型的日志门面就是 Commons Logging、SLF4J。比较常用的组合方式是 Slf4j 和 Logback 的组合使用,Commons Logging 与 Log4j 的组合使用,并且 Logback 必须得配合 Slf4j 使用。

以下是官方列举的一些 Logback 的优势:

Logback 的工作原理

Logback 是由 log4j 的创始人编写的,但是性能上的话会比 log4j 更好一些,他主要分为有三个模块:

slf4j 是标准,对用户提供统一的 API,而下方对接各个日志框架。这有点类似 JVM,我们 Java 开发者使用统一的 API,而 JVM 对接各个操作系统。严格意义上说 slf4j 自身并不提供日志具体实现。在 Logback 的模块里面,logback-classic 就是实现了 slf4j 具体接口的核心模块。它可以自动重新加载配置文件,如果开发过程中配置文件修改了,logback-classic 能自动重新加载配置文件,扫描过程快且安全,它并不需要另外创建一个扫描线程。

更多的关于 Logback 具体的工作使用流程可以通过具体的配置文件信息来进行相应的配置。所以接下来我们在具体的一个实验中来看看 Logback 日志框架在 Spring Boot 中是怎样整合使用的。

Spring Boot 整合 Logback 日志框架

首先我们需要新建一个 Maven 工程,打开我们的 WebIDE 环境,在终端输入以下内容:

mvn archetype:generate \
  -DgroupId=com.example.logback \
  -DartifactId=spring-boot-log-logback \
  -DarchetypeArtifactId=maven-archetype-quickstart \
  -DinteractiveMode=false

在环境中构建出来的项目的结构的效果如下,大家需要注意一下,通过上面的方式我们构建出来的项目启动文件是 App.java ,但是为了命名上更贴近本章节课程,我们修改成了 SpringBootDemologbackApplication.java 文件,后面大家需要手动修改一下,当然也可以保留,但是大家一定要注意项目启动类书写的规范不要出错,下面贴的有修改过后的项目启动类的代码。

image-1655351402442

上面需要注意的是图中的 logs 用于存放日志的文件夹不需要自己手动来创建的,target 文件夹是编译过后生成的文件,因为我们在配置文件中进行了相应的配置之后是可以自动在项目路径下生成的。所以我们需要创建的项目结构是 src/main/java/com/example/logbacksrc/main/resources

接下来我们需要添加这个项目的依赖,大家可以将下面的代码复制粘贴到项目结构中的 pom.xml 文件中:仔细观察这个依赖你会发现 dependency 依赖组中并没有 Logback 的依赖说明。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example.logback</groupId>
    <artifactId>spring-boot-log-logback</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>spring-boot-log-logback</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

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

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

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <build>
        <finalName>spring-boot-demo-logback</finalName>
        <plugins>
            <!--Spring Boot maven插件-->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

其实因为 Spring Boot 默认已经整合了 Logback 的,所以在创建 Spring Boot 工程时引入的 spring-boot-starter 就已经包含了 spring-boot-starter-logging,而 spring-boot-starter-logging 中引用了 Logback 的依赖。如果我们点开 spring-boot-starter-parent 的包查看源码的话会发现 Logback 所需要引用的三个包已经被 Spring Boot 整合了,所以我们无需再在 pom.xml 文件中重新引入这三个包。

image-1655351414354

另外我们在 pom.xml 文件中添加进去了 Lombok 相关的依赖这样子我们就可以使用 @Slf4j 注解来直接使用 log()方法了。

image-1655351444138

上面处理完 pom.xml 文件之后,我们再在 src/main/resources 路径下新增并配置 application.yml 和 Logback 相关的配置文件,首先是 application.yml 文件。这是我们在实验中的常规配置,但是在本实验中我们不需要发起 Web 端的请求,我们只需要在控制台界面查看我们需要打印和记录的日志信息。

server:
  port: 8080
  servlet:
    context-path: /demo

Logback 相关的配置文件 logback-spring.xml

我们需要在 resources 文件夹下面新建如下的配置文件信息,来配置我们的 Logback。

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <property name="FILE_ERROR_PATTERN"
            value="${FILE_LOG_PATTERN:-%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} %file:%line: %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
  <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
        </filter>
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <appender name="FILE_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--如果只是想要 Info 级别的日志,只是过滤 info 还是会输出 Error 日志,因为 Error 的级别高, 所以我们使用下面的策略,可以避免输出 Error 的日志-->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!--过滤 Error-->
            <level>ERROR</level>
            <!--匹配到就禁止-->
            <onMatch>DENY</onMatch>
            <!--没有匹配到就允许-->
            <onMismatch>ACCEPT</onMismatch>
        </filter>
        <!--日志名称,如果没有File 属性,那么只会使用FileNamePattern的文件路径规则如果同时有<File>和<FileNamePattern>,那么当天日志是<File>,明天会自动把今天的日志改名为今天的日期。即,<File> 的日志都是当天的。-->
        <!--<File>logs/info.spring-boot-demo-logback.log</File>-->
        <!--滚动策略,按照时间滚动 TimeBasedRollingPolicy-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--文件路径,定义了日志的切分方式——把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间-->
            <FileNamePattern>logs/spring-boot-demo-logback/info.created_on_%d{yyyy-MM-dd}.part_%i.log</FileNamePattern>
            <!--只保留最近90天的日志-->
            <maxHistory>90</maxHistory>
            <!--用来指定日志文件的上限大小,那么到了这个值,就会删除旧的日志-->
            <!--<totalSizeCap>1GB</totalSizeCap>-->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!-- maxFileSize:这是活动文件的大小,默认值是10MB,本篇设置为1KB,只是为了演示 -->
                <maxFileSize>2MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <!--<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">-->
        <!--<maxFileSize>1KB</maxFileSize>-->
        <!--</triggeringPolicy>-->
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern>
            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
        </encoder>
    </appender>

    <appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--如果只是想要 Error 级别的日志,那么需要过滤一下,默认是 info 级别的,ThresholdFilter-->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>Error</level>
        </filter>
        <!--日志名称,如果没有File 属性,那么只会使用FileNamePattern的文件路径规则如果同时有<File>和<FileNamePattern>,那么当天日志是<File>,明天会自动把今天的日志改名为今天的日期。即,<File> 的日志都是当天的。-->
        <!--<File>logs/error.spring-boot-demo-logback.log</File>-->
        <!--滚动策略,按照时间滚动 TimeBasedRollingPolicy-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--文件路径,定义了日志的切分方式——把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间-->
            <FileNamePattern>logs/spring-boot-demo-logback/error.created_on_%d{yyyy-MM-dd}.part_%i.log</FileNamePattern>
            <!--只保留最近90天的日志-->
            <maxHistory>90</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!-- maxFileSize:这是活动文件的大小,默认值是10MB,本篇设置为1KB,只是为了演示 -->
                <maxFileSize>2MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <pattern>${FILE_ERROR_PATTERN}</pattern>
            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
        </encoder>
    </appender>

    <root level="info">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="FILE_INFO"/>
        <appender-ref ref="FILE_ERROR"/>
    </root>
</configuration>

logback-spring.xml 配置文件中,configuration 标签表示了日志配置文件的配置信息,其包含有一些子节点包括 property,用于设置一些变量,另外比较重要的就是 appender 标签了,appender 是负责日志写的组件。

主要标签节点可以归纳为如下图所示:

image-1655351462648

配置文件最后的 root 标签的相关内容就是说明的默认的日志配置信息,比如日志默认的级别是 info 级别的。

另外我们多次谈到了日志的级别,这里我们对日志的级别再进行一个集中的总结:

关于日志的级别,Logback 有 5 种级别,分别是 TRACE < DEBUG < INFO < WARN < ERROR,定义于 ch.qos.logback.classic.Level 类中。<符号代表了这些日志的等级顺序。其中:

编写日志测试启动类 SpringBootDemologbackApplication

我们需要修改我们的启动类如上所示,因为原来 mvn 构建项目的命令过后是生成的一个 App.java 的启动类,我们将它改变成下面代码所示:构建好后的 SpringBootDemologbackApplication.java 的路径是在 src/main/java/com/example/logback/SpringBootDemologbackApplication.java 下面的。

package com.example.logback;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
@Slf4j
public class SpringBootDemologbackApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringBootDemologbackApplication.class, args);
        int length = context.getBeanDefinitionNames().length;
        log.trace("Spring boot启动初始化了 {} 个 Bean", length);
        log.debug("Spring boot启动初始化了 {} 个 Bean", length);
        log.info("Spring boot启动初始化了 {} 个 Bean", length);
        log.warn("Spring boot启动初始化了 {} 个 Bean", length);
        log.error("Spring boot启动初始化了 {} 个 Bean", length);
        try {
            int i = 0;
            int j = 1 / i;
        } catch (Exception e) {
            log.error("【SpringBootDemologbackApplication】启动异常:", e);
        }
    }
}

我们除了 Spring Boot 项目启动添加的常用注解 @SpringBootApplication 以外需要注意的是,我们使用了 @Slf4j2 这个注解,我们之前在 pom.xml 文件中是引进了 Lombok 插件的,回想如果我们之前每次使用日志 logger 的时候,都是要写上这样的一句话:

private final Logger logger = LoggerFactory.getLogger(当前类名.class);

但是其实我们加上了 @Slf4j2 这个注解之后就可省略掉这句话的描述,这样是很方便的,大家平时在自己的环境里面使用 lombok 的时候记得要在自己的 IDE(如果大家使用的是 idea 的话)里面安装 lombok 插件使用。这样子我们可以使用 log 来打印日志了。

另外在功能模块测试上我们分为了两个方面来设计,首先包含了 log.trace、log.debug、log.info、log.warn、log.error 这几个日志打印的函数,这是我们指定的打印的日志的级别,我们通过 context.getBeanDefinitionNames()来获取 bean 的名称,日志打印它的长度,另外我们在 try-catch 的循环体中利用 i=0 不能作为除数来捕获这个异常,并通过 log.error 打印这样的一场信息。

我们在终端命令中输入如下的命令,但是需要注意的是我们已经将环境的工作空间切换到了项目目录包含了 pom.xml 文件下面,即将此时的工作空间切换到 /home/project/spring-boot-log-logback 下,我们可以在终端中输入 cd spring-boot-demo 并且看终端中是否显示当前的工作目录在 spring-boot-demo 下面。

mvn spring-boot:run

来运行程序,运行的效果如下:

image-1655351482627

可以看出最后我们是抛出并且捕获了这个算术异常的:java.lang.ArithmeticException: / by zero

总结

通过初始化一个 Spring Boot 和 Logback 日志框架整合的项目 demo 了解并学习了 Spring Boot 支持的高性能的日志框架 Logback,通过类比其他常用的日志框架对比分析 Logback 的优势,并通过实际操作熟悉了 Logback 框架下面的常用的配置文件,日志在项目的开发环节中起着非常重要的作用,这是每一个开发人员都要掌握的内容。