How does SpringBoot | SpringBoot implement logging?

  log4j, log4j2, logback, slf4j, springboot

WeChat Public Number: An Outstanding Disabled Person. If you have any questions, please leave a message backstage. I won’t listen anyway.

Preface

On the rest day, I watched the log implementation in SpringBoot and explained my understanding to everyone.

Facade mode

Speaking of the log framework, we have to say facade mode. Facade mode, whose core is that external communication with a subsystem must be conducted through a unified appearance object, making the subsystem easier to use. The structure of the facade pattern represented by a graph is as follows:

门面模式

In simple terms, this mode is to encapsulate some complicated processes into an interface for easier use by external users. In this mode, 3 roles are designed.

1). Facade role: the core of appearance mode. It is called by the customer role and is familiar with the functions of the subsystem. The combination (module) of several functions is reserved internally according to the requirements of the customer’s role.

2). Subsystem (Module) Role: Implements the functions of the subsystem. It is unknown to the customer role and Facade. It can have interactions within the system or interfaces for external calls.

3). Customer Role: Complete the function to be realized by calling Facede.

Log Frames on the Market

Log facade Log implementation
JCL(Jakarta Commons Logging)、SLF4j(Simple Logging Facade for Java)、 jboss-logging Log4j 、JUL(java.util.logging) 、Log4j2 、 Logback

In brief, the log facade of the above table corresponds to the Facede objects in the facade mode. They are only an interface layer and do not provide log implementation. The log implementation corresponds to each subsystem or module, and the specific logical implementation of log recording is written in these right frames. Then our application is equivalent to a client.

Why use facade mode?

Imagine that we need many packages to develop the system, and these packages have their own log framework, so this situation will occur: our own system uses Logback, our system uses Hibernate, the log system used in Hibernate is jboss-logging, our system uses Spring, and the log system used in Spring is commons-logging.

In this way, our system has to support and maintain Logback, jboss-logging, commons-logging three log frameworks at the same time, which is very inconvenient. The way to solve this problem is to introduce an interface layer, which decides which logging system to use. The only thing the caller needs to do is to print the logs without paying attention to how to print the logs. The log facade of the upper table is this interface layer.

In view of this, when we select logs, we must select a framework from the log facade on the left and the log implementation on the right of the above table, while SLF4j and Logback are selected by default at the bottom of SpringBoot to implement log output.

SLF4j Use

Official documents give such an example:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HelloWorld {
  public static void main(String[] args) {
    // HelloWorld.class 就是你要打印的指定类的日志,
    // 如果你想在其它类中打印,那就把 HelloWorld.class 替换成目标类名.class 即可。
    Logger logger = LoggerFactory.getLogger(HelloWorld.class); 
    logger.info("Hello World");
  }
}

In order to understand the working principle of slf4j, I went through its official documents and saw such a picture:

slf4j

To explain briefly, slf4j in the above figure has six usages and a total of five roles. Needless to say, application is our system. SLF4J API is the log interface layer (facade); Blue and the lowest gray are the specific log implementations (subsystems); Adaptation is the adaptation layer.

Explain the second and third usage in the above figure. The second is the default usage of SpringBoot. And why is there a third? Because Log4J appeared earlier, it had no idea there would be SLF4J behind it. Log4J cannot be directly implemented as a log of SLF4J, so an adaptation layer appears in the middle. The fourth is the same.

Here remind, each log implementation framework has its own configuration file. After using slf4j, the * * configuration file is still the log implementation framework’s own configuration file. For example, Logback uses logback.xml, Log4j uses Log4j.xml files.

How to make all logs in the system unified to slf4j?

I went on browsing the website and saw a picture like this:

legacy

As can be seen from the above figure, the way to make all logs in the system unified to slf4j is:

1、Excluding other log frames in the system first

2、Replace the original log frame with a tundish

3、We import other implementations of slf4j

Log relationships in SpringBoot

SpringBoot uses the following dependencies to implement the logging function:

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-logging</artifactId>
      <version>2.1.3.RELEASE</version>
      <scope>compile</scope>
</dependency>

Spring-boot-starter-logging has such a diagram:

日志底层依赖

Obviously,
1. The SpringBoot2.x bottom layer also uses slf4j+logback or log4j for logging. ​
2. SpringBoot introduces an intermediate replacement package to replace all other logs with slf4j;;
3. If we want to introduce other frameworks, we can remove the default log dependency of this framework.

For example, Spring uses the commons-logging framework, which we can remove.

<dependency>
    <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                        <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
</dependency>

SpringBoot can automatically adapt all logs, and the bottom layer uses slf4j+logback to record logs. When introducing other frameworks, you only need to exclude the log framework that this framework depends on.

Log usage

1. Default configuration (taking Log4j framework as an example). SpringBoot has configured logs for us by default:

    //记录器
    Logger logger = LoggerFactory.getLogger(getClass());
    @Test
    public void contextLoads() {
        //日志的级别;
        //由低到高   trace<debug<info<warn<error
        //可以调整输出的日志级别;日志就只会在这个级别以以后的高级别生效
        logger.trace("这是trace日志...");
        logger.debug("这是debug日志...");
        // SpringBoot 默认给我们使用的是 info 级别的,没有指定级别的就用SpringBoot 默认规定的级别;root 级别
        logger.info("这是info日志...");
        logger.warn("这是warn日志...");
        logger.error("这是error日志...");
    }

2. log4j.properties Modify Log Default Configuration

logging.level.com.nasus=debug

#logging.path=
# 不指定路径在当前项目下生成 springboot.log 日志
# 可以指定完整的路径;
#logging.file=Z:/springboot.log

# 在当前磁盘的根路径下创建 spring 文件夹和里面的 log 文件夹;使用 spring.log 作为默认文件
logging.path=/spring/log

#  在控制台输出的日志的格式
logging.pattern.console=%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n
# 指定文件中日志输出的格式
logging.pattern.file=%d{yyyy-MM-dd} === [%thread] === %-5level === %logger{50} ==== %msg%n

配置解释

3. Specify the configuration

SpringBoot will automatically load the configuration files of the corresponding frames under the class path, so we just need to send the configuration files of each log frame to the class path, and SpringBoot will not use the default configuration.

Framework Naming method
Logback logback-spring.xml,logback-spring.groovy,logback.xmlorlogback.groovy
Log4j2 log4j2-spring.xmlorlog4j2.xml
JDK (Java Util Logging) `logging.properties

Xml: recognized directly by the log framework.

logback-spring.xml: The log framework does not directly load the configuration items of the log. SpringBoot parses the log configuration and can use SpringBoot’s advanced Profile function.

<springProfile name="staging">
    <!-- configuration to be enabled when the "staging" profile is active -->
      可以指定某段配置只在某个环境下生效
</springProfile>

Example (Take Logback Framework for Example):

<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <!--
        日志输出格式:
            %d表示日期时间,
            %thread表示线程名,
            %-5level:级别从左显示5个字符宽度
            %logger{50} 表示logger名字最长50个字符,否则按照句点分割。 
            %msg:日志消息,
            %n是换行符
        -->
        <layout class="ch.qos.logback.classic.PatternLayout">
                 <!--指定在 dev 环境下,控制台使用该格式输出日志-->
                 <springProfile name="dev">
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ----> [%thread] ---> %-5level %logger{50} - %msg%n</pattern>
            </springProfile>
            <!--指定在非 dev 环境下,控制台使用该格式输出日志-->
            <springProfile name="!dev">
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ==== [%thread] ==== %-5level %logger{50} - %msg%n</pattern>
            </springProfile>
        </layout>
    </appender>

If logback.xml is used as the log configuration file instead of logback-spring.xml and profile function is also used, the following error will occur:

no applicable action for [springProfile]

Toggle log frame

Knowing the underlying log dependency of SpringBoot, we can switch according to the log adaptation diagram of slf4j.

For example, switching to slf4j+log4j can be done

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
  <exclusions>
    <exclusion>
      <artifactId>logback-classic</artifactId>
      <groupId>ch.qos.logback</groupId>
    </exclusion>
  </exclusions>
</dependency>

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
</dependency>

Switch to log4j2 and you can do this.

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

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

Finally, the detailed configuration of logback-spring.xml is put on, and you can refer to the configuration in your own project.

<?xml version="1.0" encoding="UTF-8"?>
<!--
scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
-->
<configuration scan="false" scanPeriod="60 seconds" debug="false">
    <!-- 定义日志的根目录 -->
    <property name="LOG_HOME" value="/app/log" />
    <!-- 定义日志文件名称 -->
    <property name="appName" value="nasus-springboot"></property>
    <!-- ch.qos.logback.core.ConsoleAppender 表示控制台输出 -->
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <!--
        日志输出格式:
            %d表示日期时间,
            %thread表示线程名,
            %-5level:级别从左显示5个字符宽度
            %logger{50} 表示logger名字最长50个字符,否则按照句点分割。 
            %msg:日志消息,
            %n是换行符
        -->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <springProfile name="dev">
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ----> [%thread] ---> %-5level %logger{50} - %msg%n</pattern>
            </springProfile>
            <springProfile name="!dev">
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ==== [%thread] ==== %-5level %logger{50} - %msg%n</pattern>
            </springProfile>
        </layout>
    </appender>

    <!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 -->  
    <appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 指定日志文件的名称 -->
        <file>${LOG_HOME}/${appName}.log</file>
        <!--
        当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名
        TimeBasedRollingPolicy: 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动。
        -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--
            滚动时产生的文件的存放位置及文件名称 %d{yyyy-MM-dd}:按天进行日志滚动 
            %i:当文件大小超过maxFileSize时,按照i进行文件滚动
            -->
            <fileNamePattern>${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
            <!-- 
            可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假设设置每天滚动,
            且maxHistory是365,则只保存最近365天的文件,删除之前的旧文件。注意,删除旧文件是,
            那些为了归档而创建的目录也会被删除。
            -->
            <MaxHistory>365</MaxHistory>
            <!-- 
            当日志文件超过maxFileSize指定的大小是,根据上面提到的%i进行日志文件滚动 注意此处配置SizeBasedTriggeringPolicy是无法实现按文件大小进行滚动的,必须配置timeBasedFileNamingAndTriggeringPolicy
            -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <!-- 日志输出格式: -->     
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n</pattern>
        </layout>
    </appender>

    <!-- 
        logger主要用于存放日志对象,也可以定义日志类型、级别
        name:表示匹配的logger类型前缀,也就是包的前半部分
        level:要记录的日志级别,包括 TRACE < DEBUG < INFO < WARN < ERROR
        additivity:作用在于children-logger是否使用 rootLogger配置的appender进行输出,
        false:表示只用当前logger的appender-ref,true:
        表示当前logger的appender-ref和rootLogger的appender-ref都有效
    -->
    <!-- hibernate logger -->
    <logger name="com.nasus" level="debug" />
    <!-- Spring framework logger -->
    <logger name="org.springframework" level="debug" additivity="false"></logger>



    <!-- 
    root 与 logger 是父子关系,没有特别定义则默认为root,任何一个类只会和一个logger对应,
    要么是定义的logger,要么是root,判断的关键在于找到这个logger,然后判断这个logger的appender和level。 
    -->
    <root level="info">
        <appender-ref ref="stdout" />
        <appender-ref ref="appLogAppender" />
    </root>
</configuration> 

References

http://www.importnew.com/2849 …

https://www.cnblogs.com/lthIU …

Postscript

If this article is of any help to you, please help me look good. Your good looks are my motivation to persist in writing.

In addition, attention is sent after1024Free study materials are available.

For details, please refer to this old article:Python, C++, Java, Linux, Go, Front End, Algorithm Data Sharing

一个优秀的废人,给你讲几斤技术。