SpringBoot | How does SpringBoot implement logging?

SpringBoot | How does SpringBoot implement logging?

WeChat public account: an excellent cripple. If you have any questions, please leave a message in the background, I won't listen anyway.

Preface

I was bored on the rest day and looked at the log implementation in SpringBoot, and I would like to share my understanding with you.

Facade pattern

Speaking of the log framework, I have to talk about the facade mode. Facade mode, the core of which is that the communication between the outside and a subsystem must be carried out through a unified appearance object, making the subsystem easier to use. Use a picture to represent the structure of the facade model:

Simply put, this mode is to encapsulate some complex processes into an interface for easier use by external users. In this mode, 3 roles are designed.

1). Facade role: the core of the appearance model. It is called by the customer role, and it is familiar with the functions of the subsystem. Several combinations of functions (modules) are reserved internally according to the needs of the customer's role.

2). The role of the subsystem (module): the function of the subsystem is realized. It is unknown to the customer role and facade. It can have mutual interaction within the system, and it can also be called by the outside world.

3). Customer role: by calling Facede to complete the function to be realized.

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

Simply put, the log facade in the above table corresponds to the Facede object in the facade mode. They are only an interface layer and do not provide log implementation; while the log implementation corresponds to each subsystem or module, and the specific logic implementation of log records is Written in these frames on the right; then our application is equivalent to the client.

Why use facade mode?

Imagine the scenario of our development system, we need to use many packages, and these packages have their own logging framework, so there will be such a situation: our own system uses Logback this logging system, our system uses Hibernate , The logging system used in Hibernate is jboss-logging, our system uses Spring again, and the logging system used in Spring is commons-logging.

In this way, our system has to support and maintain three log frameworks: Logback, jboss-logging, and commons-logging at the same time, which is very inconvenient. The way to solve this problem is to introduce an interface layer, and the interface layer decides which logging system to use, and the only thing the caller needs to do is to print the log without worrying about how to print the log, and the log facade in the table above is like this Interface layer.

In view of this, when we choose the log, we must choose a framework from the log facade on the left of the above table and the log implementation on the right. The default choice for the bottom layer of SpringBoot is SLF4j and Logback to achieve log output.

SLF4j use

The official document gives 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 checked its official document and saw this picture:

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

Explain, the second and third usage of the above figure. The second one is the default usage of SpringBoot; and why does the third one appear? Because Log4J appeared earlier, it has no idea that SLF4J will follow. Log4J cannot be directly implemented as the log of SLF4J, so an adaptation layer appears in the middle. The fourth is the same.

Here is a reminder that each log implementation framework has its own configuration file. After using slf4j, the ** configuration file is still made into the configuration file of the log implementation framework itself. For example, Logback uses logback.xml, Log4j uses Log4j.xml file.

How to unify all logs in the system to slf4j?

I continued to browse the official website and saw this picture:

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

1. Exclude other log frameworks in the system first

2. Replace the original log framework with an intermediate package

3. We import other implementations of slf4j

Log relationship in SpringBoot

SpringBoot uses the following dependencies to implement logging functions:

<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 relationship diagram:

It can be seen that 1. The bottom layer of SpringBoot 2.x 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 use The default log dependency of this framework is removed.

For example, Spring uses the commons-logging framework, we can remove it like this.

<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 to 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. The default configuration (take the Log4j framework as an example), SpringBoot configures the log 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 the 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 file of the corresponding framework under the classpath, so we only need to put each log framework's own configuration file under the classpath, and SpringBoot will not use the default configuration.

frame naming method
Logback logback-spring.xml, logback-spring.groovy, logback.xmlOrlogback.groovy
Log4j2 log4j2-spring.xml or log4j2.xml
JDK (Java Util Logging) `logging.properties

logback.xml: It is directly recognized by the log framework.

logback-spring.xml : The logging framework does not directly load the configuration items of the log. The log configuration is parsed by SpringBoot, and the advanced profile function of SpringBoot can be used.

<springProfile name="staging">
    <!-- configuration to be enabled when the "staging" profile is active -->
  	 
</springProfile>
 

Example (take the Logback framework as an 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 you use logback.xml as the log configuration file instead of logback-spring.xml, but also use the profile function, the following error will occur:

no applicable action for [springProfile]
 

Switch log frame

Knowing the underlying log dependencies of SpringBoot, we can perform related switching according to the log adaptation diagram of slf4j.

For example, to switch to slf4j+log4j, you can do this

<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, put the detailed configuration of logback-spring.xml, 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

www.importnew.com/28494.html

www.cnblogs.com/lthIU/p/586...

Afterword

If this article is even a little helpful to you, please help me to look good. Your good looks is the motivation for me to insist on writing.

In addition, after paying attention, you can receive free learning materials after sending 1024 .

For details, please see this old article: Python, C++, Java, Linux, Go, front-end, algorithm data sharing