preface

For a Web project, the logging framework is essential, the log record can help us in the development and maintenance process quickly locate errors. Many people have heard of slF4J, log4J,logback,JDK Logging and other Logging frameworks.

First published on personal blog: [www.xiongfrblog.cn]

Relationship between

First, SLF4J can be understood as a rule maker, an abstraction layer that defines logging related interfaces. Log4j, Logback, and JDK Logging are all slF4J implementation layers, but they come from different sources and are used in different ways.

As you can see, Logback is a direct implementation of SLF4J, while the others have an adaptation layer in between, for the simple reason that the author of Logback and SLF4J is the same person. For detailed information about these frameworks, find an easy-to-understand article on the Internet, interested friends can understand [Portal]

Why slf4J + LogBack

I use this framework because I used it at the beginning of my contact. Later, I learned that SLF4J + LogBack is also the most popular logging framework on the Internet, and IT is really easy to use myself, so I have been using it. About the advantages of this framework compared with other logging frameworks, because I have not used other frameworks, Here is not to do that mistake the children of the matter, but also on the Internet to do understanding, here is a more detailed introduction of the blog [portal]

Use slF4J + Logback logging framework in Spring Boot

Adding a Configuration File

Slf4j + Logback is the default logging framework supported by Spring Boot. If you want to customize the logging policy, you only need to add a configuration file under SRC /main/resources. By default, configuration files must be named according to the following rules:

  • logback.xml
  • logback-spring.xml

Logback-spring. XML is officially recommended, and only using this naming convention can you configure different logging policies for different environments.

Configuration file details

Here are the key nodes of the configuration file:

The framework is introduced


: the root node has three properties:

  1. scan: Specifies whether to reload the configuration file when it is modified. Two options are availabletrue or falseBy default,true.
  2. scanPeriod: Indicates the time period for checking whether the configuration file is modified. If no time unit is given, the default unit is millisecond. The default value is 1 minutescanAttribute values fortrueWill take effect.
  3. debug: Print or notlobackInternal log information. Two optional valuestrue or falseBy default,false.

The root node < Configuration > has three important child nodes, and it is the different combinations of these three child nodes that form the basic framework of the configuration file and make the logback.xml configuration file very flexible:

  • The < appender > : A configuration file can have zero or more of these nodes. If a configuration file does not have at least one

    , the program will not generate any error, but will not have any log information output and will lose meaning. This node has two necessary attributes:

    1. name: Specifies the name of the node for later reference.
    2. class: Specifies the fully qualified name of the node. The fully qualified name defines what type of logging policy the node is, for example, when we need to output logs to the consoleclassThe value ofch.qos.logback.core.ConsoleAppender; Logs need to be exported to a fileclassThe value ofch.qos.logback.core.FileAppenderEtc., want to know all aboutappenderType, you can check the official documentation【 Portal 】

  • : Sets the logging level for a package or class and can use

    to bind logging policies. It has three properties:

    1. name: is used to specify this<logger>The package or class of the constraint.
    2. level: Optional property that specifies the output level of logs. If not set, the current<logger>Inherits the rank of a superior.
    3. additivity: Indicates whether to send output information to the upper level. Two optional values are availabletrue or falseBy default,true.

To this node you can add a child

, which has a mandatory attribute ref with the value of the name attribute of the

node we defined.

  • <root>Root:<logger>A special<logger>, that is, the defaultnameProperties forrootthe<logger>Because it’s a root<logger>, so there is no upward pass, so there is noadditivityProperty, so there is only one nodelevelProperties.

Having introduced the three main children of the root node, here are two less important but understandable children:

  • <contextName>: Sets the context name, each<logger>Are associated with<logger>Context, the default context name isdefault, but can be used to set to other names, used to distinguish between different applications of the record, once set, cannot be modified, can pass%contextNameTo print the log context name, generally we do not use this property, optional.
  • <property>: node used to define variables. After variables are defined, you can enableThe ${}To use variables, two attributes, when defining multiple<appender>“Is still useful:
    1. name: the variable name
    2. value: a variable’s value

Ok, now that we have introduced the above nodes we are ready to build a simple configuration file framework as follows:

<?xml version="1.0" encoding="UTF-8"? >
<! The root node does not need to write attributes, use the default.
<configuration>

    <contextName>demo</contextName>
    
    <! -- this variable represents the directory where the log files are stored -->
    <property name="log.dir" value="logs"/>
    <! -- This variable represents the log name -->
	<property name="log.appname" value="eran"/>
    
    <! An appender that outputs logs to the console named STDOUT -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <! -- Content to be determined -->
    </appender>
    
    <! Define an appender for exporting logs to files named FILE_LOG -->
    <appender name="FILE_LOG" class="ch.qos.logback.core.FileAppender">
        <! -- Content to be determined -->
    </appender>
  
    <! -- Set the log level of the com.demo package to INFO, but because there is no appender, the logger will not print logs, and logs will be passed up.
    <logger name="com.demo" level="INFO"/>
  
    <! An appender named STDOUT can be appended to output logs to the console.
    <root level="debug">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>
Copy the code

A ConsoleAppender for output to the console and a FileAppender for output to files are defined. Here are two basic logging strategies and the RollingFileAppender for the most commonly used RollingFileAppender. These three types of logging policies are sufficient for our daily use.

Output to the consoleConsoleAppenderIntroduction:

Let’s start with a demo:

<! An appender that outputs logs to the console named STDOUT -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <pattern>[Eran]%date [%thread %line] %level >> %msg >> %logger{10}%n</pattern>
    </encoder>
</appender>

Copy the code

ConsoleAppender outputs logs to the console. There is a

node that specifies the format of the log output. In earlier versions, there was a

node that did the same thing, but the encoder node was recommended. So let’s just introduce the Encoder node.

<encoder>Node is introduced

This node does two things:

  • Convert log information into byte arrays
  • Writes an array of bytes to the output stream

The child of this node is used to define the format of the log, that is, to define what a log information contains, such as the current time, the number of lines in the code thread name, and so on. It is up to us to define what we need, as defined in the format of %+ converters. The common converters are listed below:

  • %date{}: Output time. The time format can be specified in curly braces, for example, –%data{yyyy-MM-dd HH:mm:ss}, format syntax andjava.text.SimpleDateFormatOnce again, we could write it as%d{}Can be omitted if the default format is used{}.
  • %logger{}: Indicates the logger name of the log%c{}.%lo{}Can be omitted if the default parameter is used{}, you can define an integer parameter to control the length of the output name in the following three cases:
    1. No input indicates complete output<logger>The name of the
    2. The input0Output only<logger>The string after the dot on the far right
    3. Input other numbers represent the number of characters before the last dot of the output decimal point
  • %thread: Specifies the name of the thread that generates logs%t
  • %line: Indicates the line number of the current log printing statement in the program%L
  • %level: Indicates the log level%le.%p
  • %message: Log print content defined by the programmer%msg.%m
  • %n: newline, that is, one log message occupies a line

Having introduced common converters, let’s look at the format we defined in the above example:

<pattern>[Eran]%date [%thread %line] %level >> %msg >> %logger{10}%n</pattern>
Copy the code

The format of the log is clear, you can see that we add the string [eran] at the front of the log, this is my personal habit, generally display the project name at the front of the log, and add a space between each conversion, which makes it easier to view the log, and use the >> string to separate % MSG. It makes it easier for us to find out what we care about in the log messages, which we can do according to our own preferences.

Output to a fileFileAppender

Let’s start with a demo:

<! Define an appender for exporting logs to files named FILE_LOG -->
<appender name="FILE_LOG" class="ch.qos.logback.core.FileAppender">  
    <file>D:/test.log</file>
    <append>true</append>  
    <encoder>  
        <pattern>[Eran]%date [%thread %line] %level >> %msg >> %logger{10}%n</pattern>
    </encoder>
</appender>
Copy the code

FileAppender means to output logs to a file. It has several children:

  • <file>: Defines the file name and path, which can be relative or absolute. If the path does not exist, it will be created automatically
  • <append>: two valuestrueandfalseBy default,true, indicates that each log output to the file is appended to the end of the original file.falseEmpty the existing files
  • <encoder>And:ConsoleAppenderThe same

Obviously, our logging policy in the sample indicates that we append log information to the D:/test.log file each time.

Scrolling file policyRollingFileAppenderintroduce

Scroll by timeTimeBasedRollingPolicy

The demo is as follows:

<appender name="ROL-FILE-LOG" class="ch.qos.logback.core.rolling.RollingFileAppender"> 
  <! TimeBasedRollingPolicy-->
  <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
  	<fileNamePattern>D:/logs/test.%d{yyyy-MM-dd}.log</fileNamePattern>
  	<! -- Keep only logs for the last seven days -->
  	<maxHistory>7</maxHistory>
  	<! -- Specifies the maximum size of the log file, at which the old log file will be deleted -->
  	<totalSizeCap>1GB</totalSizeCap>
  </rollingPolicy> 
  
  <encoder>
	<pattern>[Eran]%date [%thread %line] %level >> %msg >> %logger{10}%n</pattern>
  </encoder>
</appender>
Copy the code

RollingFileAppender is a very common type of logging that represents a rolling record file. The log is first logged to a specified file, and when certain conditions are met, the log is logged to another file.


  • : rollingPolicy. The class attribute specifies which rollingPolicy to use. The most commonly used rollingPolicy is TimeBasedRollingPolicy.

    1. <fileNamePattern>: Specifies the log path and the naming rule of the log file nameLog file +%d{}.logThe default format of the date isyyyy-MM-ddIndicates that a file is generated every day, that is, scroll by dayyyyy-MM, indicates that a file is generated every month
    2. <maxHistory>: Optional node to control the maximum number of log files to be saved, delete old files when the number exceeds, for example, set daily scrolling, and<maxHistory>If the value is 7, only the files generated in the last 7 days are saved and the old files are deleted
    3. <encoder>: same as above
    4. <totalSizeCap>: this object indicates the maximum memory size for all log files. When the value is exceeded,logbackThe original log file is deleted.

A RollingFileAppender can be appended in a TimeBasedRollingPolicy. The appender can be appended in TimeBasedRollingPolicy. Below to introduce a kind of commonly used type of rolling SizeAndTimeBasedRollingPolicy, namely, in accordance with the time and size to scroll.

Scroll by time and sizeSizeAndTimeBasedRollingPolicy

The demo is as follows:

<appender name="ROL-SIZE-FILE-LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
        <fileNamePattern>D:/logs/test.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
        <! -- Maximum memory for a single file -->
        <maxFileSize>100MB</maxFileSize>
        <! -- Keep only logs for the last seven days -->
        <maxHistory>7</maxHistory>
        <! -- Specifies the maximum size of the log file, at which the old log file will be deleted -->
        <totalSizeCap>1GB</totalSizeCap>
    </rollingPolicy>
    
    <encoder>
        <pattern>[Eran]%date [%thread %line] %level >> %msg >> %logger{10}%n</pattern>
    </encoder>
</appender>
Copy the code

If you look closely at the

in the demo above, you will see that there are more.% I characters than the

defined in TimeBasedRollingPolicy. In SizeAndTimeBasedRollingPolicy is indispensable.

There is an extra

node in the previous demo, which is described here. The other nodes have been explained above and will not be described here.


: indicates the maximum memory size occupied by a single file. When a file exceeds this value, a scrolling policy is triggered to generate a new log file.

Log filter

Level introduction

Before talking about level filtering, let me introduce the log level information:

  • TRACE
  • DEBUG
  • INFO
  • WARN
  • ERROR

From top to bottom, our development tests typically output logs at DEBUG level, while production configurations only output logs at INFO level or even ERROR level, which is flexible depending on the situation.

The filter node<filter>introduce

An Appender can be configured with one or more filters. If there are multiple filters, they can be configured in an order or not. In most cases, you do not need to configure the filters, but in some cases you must configure them. The LevelFilter and ThresholdFilter mechanisms are the same as the LevelFilter and ThresholdFilter mechanisms.

Under the said before the < filter > concept, first of all, a filter (filter > all of the return value has three, each filter returns only one value in the following:

  • DENY: Logs are filtered out and do not pass through the next filter
  • NEUTRAL: Logs are filtered through the next filter
  • ACCEPT: Logs are processed immediately and do not go to the next filter
Level filterLevelFilter

Filter criteria: Only INFO logs are processed in the following format:

<filter class="ch.qos.logback.classic.filter.LevelFilter">   
    <level>INFO</level>   
    <onMatch>ACCEPT</onMatch>   
    <onMismatch>DENY</onMismatch>   
</filter>
Copy the code
  • <level>: Log level
  • <onMatch>: Sets the processing mode that meets the filtering conditions
  • <onMismatch>: Sets the processing mode that does not meet the filtering conditions

As in the previous demo, if the level is set to INFO, logs that meet the conditions are accepted, which means they are processed immediately; logs that do not meet the conditions are denied, which means they are discarded. In this way, only INFO logs are printed out after passing the filter.

Threshold filterThresholdFilter

Filter criteria: Only logs at the INFO level are processed. The format is as follows:

<filter class="ch.qos.logback.classic.filter.ThresholdFilter">   
    <level>INFO</level>   
</filter>
Copy the code

When the log level is equal to or higher than the threshold, the filter returns NEUTRAL. When the log level is lower than the threshold, the filter returns DENY.

strainer<Appender>

Here is an

with a filter:

<appender name="ROL-SIZE-FILE-LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
        <fileNamePattern>D:/logs/test.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
        <! -- Maximum memory for a single file -->
        <maxFileSize>100MB</maxFileSize>
        <! -- Keep only logs for the last seven days -->
        <maxHistory>7</maxHistory>
        <! -- Specifies the maximum size of the log file, at which the old log file will be deleted -->
        <totalSizeCap>1GB</totalSizeCap>
    </rollingPolicy>
    
    <encoder>
        <pattern>[Eran]%date [%thread %line] %level >> %msg >> %logger{10}%n</pattern>
    </encoder>
    
    <! -- Only handle INFO level and above logs -->
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">   
        <level>INFO</level>   
    </filter>
    
    <! -- Only INFO level logs -->
    <filter class="ch.qos.logback.classic.filter.LevelFilter">   
        <level>INFO</level>   
        <onMatch>ACCEPT</onMatch>   
        <onMismatch>DENY</onMismatch>   
    </filter>
</appender>
Copy the code

In the demo above, we can give according to the time and size rolling SizeAndTimeBasedRollingPolicy rolling type and the filter conditions.

Asynchronous write to logAsyncAppender

All know, our log statements are embedded within the program, if the log and the execution of a program is in a state of a serial, so log records will inevitably hinder the execution, longer response time of the program, is undoubtedly a great loss of efficiency, so our logging in practical projects with asynchronous way to record, This will form a parallel state with the main program and will not affect the operation of our program, which is also a point we need to pay attention to performance tuning.

AsyncAppender does not process logs, it simply buffers logs into a BlockingQueue and creates an internal worker thread to fetch logs from the queue header and loop the logs to additional appenders without blocking the main thread. Thus AsynAppender simply acts as an event forwarder and must reference another appender to write logs.

<appender name ="ASYNC" class= "ch.qos.logback.classic.AsyncAppender">  
    <! -- Do not lose logs. By default, TRACT, DEBUG, and INFO logs are discarded if 80% of the queue is full.  
    <discardingThreshold >0</discardingThreshold>  
    <! Change the default queue depth, which affects performance. The default is 256 -->  
    <queueSize>512</queueSize>  
    <! Add additional appenders, one at most -->  
    <appender-ref ref ="FILE_LOG"/>
</appender>
Copy the code

Common nodes:

  • <discardingThreshold>: By default, whenBlockingQueueThere are20%The capacity that he will discardTRACE,DEBUGandINFOLogs of this level are reserved onlyWARNandERRORLevel of logs. To keep all logs, set this value to0.
  • <queueSize>:BlockingQueueBy default, the size is256.
  • <appender-ref>: Adds additional<appender>, a maximum of one can be added

<logger>and<root>Node is introduced

After a lengthy discussion of

, let’s take a closer look at

nodes and

nodes.


Above has simply introduced the < logger > node’s properties and child nodes, here we are, for example to illustrate the logback – spring. The XML file, exactly what role the node, and his works, see the demo below:

First, the project structure is given here:

Define two

and

:

<! -- logger1 -->
<logger name="com.example" level="ERROR">
	<appender-ref ref="STDOUT" />
</logger>

<! -- logger2 -->
<logger name="com.example.demo.controller" level="debug">
	<appender-ref ref="STDOUT" />
</logger>

<! An appender named STDOUT can be appended to output logs to the console.
<root level="INFO">
    <appender-ref ref="STDOUT" />
</root>
Copy the code

When there are multiple

, there is a concept of the parent level and the child level. The log processing process is first child level and then parent level. Of course,

is the highest level. It doesn’t matter in what order we define

.


Logger1 is the parent of logger2. Logger1 is the parent of logger2.

The flow chart is clear, so I won’t repeat it here, but in practical projects, we generally don’t allow < Logger > to output logs, and put them in the

node. Therefore, we generally don’t add

to < Logger > node. Of course, this can be flexibly configured according to actual needs.

configurationprofile

A profile uses different logging policies for different environments. Here are examples of development and production environments:

<! -- Development environment output to console -->
<springProfile  name="dev">
    <root level="INFO">
        <appender-ref ref="STDOUT" />
    </root>
</springProfile>

<! -- Production environment output to file -->
<springProfile  name="prod">
    <root level="INFO">
        <appender-ref ref="FILE_LOG" />
    </root>
</springProfile>
Copy the code























































  1. performjarAdd parameters to the package:
java -jar xxx.jar --spring.profiles.active=prod
Copy the code
  1. In the projectapplication.propertiesAdd:
spring.profiles.active=prod
Copy the code

integration

Finally all the modules are put together to form a complete configuration file:

<?xml version="1.0" encoding="UTF-8"? >
<configuration>    
    <! An appender that outputs logs to the console named STDOUT -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>[%contextName]%date [%thread %line] %level >> %msg >> %logger{10}%n</pattern>
        </encoder>
    </appender> 
    
    <! Define an appender for exporting logs to files named FILE_LOG -->
    <appender name="FILE_LOG" class="ch.qos.logback.core.FileAppender">  
        <file>D:/test.log</file>
        <append>true</append>  
        <encoder>  
            <pattern>[Eran]%date [%thread %line] %level >> %msg >> %logger{10}%n</pattern>
        </encoder>
    </appender>  
    
    <! Create log file by time -->
    <appender name="ROL-FILE-LOG" class="ch.qos.logback.core.rolling.RollingFileAppender"> 
      <! TimeBasedRollingPolicy-->
      <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      	<fileNamePattern>D:/logs/test.%d{yyyy-MM-dd}.log</fileNamePattern>
      	<! -- Keep only logs for the last seven days -->
      	<maxHistory>7</maxHistory>
      	<! -- Specifies the maximum size of the log file, at which the old log file will be deleted -->
      	<totalSizeCap>1GB</totalSizeCap>
      </rollingPolicy> 
      
      <encoder>
    	<pattern>[Eran]%date [%thread %line] %level >> %msg >> %logger{10}%n</pattern>
      </encoder>
    </appender>
    
    <! Scroll to generate log file by time and file size
    <appender name="ROL-SIZE-FILE-LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>D:/logs/test.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <! -- Maximum memory for a single file -->
            <maxFileSize>100MB</maxFileSize>
            <! -- Keep only logs for the last seven days -->
            <maxHistory>7</maxHistory>
            <! -- Specifies the maximum size of the log file, at which the old log file will be deleted -->
            <totalSizeCap>1GB</totalSizeCap>
        </rollingPolicy>
        
        <encoder>
            <pattern>[Eran]%date [%thread %line] %level >> %msg >> %logger{10}%n</pattern>
        </encoder>
        
        <! -- Only handle INFO level and above logs -->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">   
            <level>INFO</level>   
        </filter>
        
        <! -- Only INFO level logs -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">   
            <level>INFO</level>   
            <onMatch>ACCEPT</onMatch>   
            <onMismatch>DENY</onMismatch>   
        </filter>
    </appender>
    
    <! -- Asynchronous write to log -->
    <appender name ="ASYNC" class= "ch.qos.logback.classic.AsyncAppender">  
        <! -- Do not lose logs. By default, TRACT, DEBUG, and INFO logs are discarded if 80% of the queue is full.  
        <discardingThreshold >0</discardingThreshold>  
        <! Change the default queue depth, which affects performance. The default is 256 -->  
        <queueSize>512</queueSize>  
        <! Add additional appenders, one at most -->  
        <appender-ref ref ="FILE_LOG"/>
    </appender>
    
    
    <! -- Set the log level of com.demo to DEBUG, but because there is no appender, the logger will not print logs, and logs will be passed up.
    <logger name="com.example" level="DEBUG"></logger>
    <! -- The logger can be configured flexibly according to the needs of the logger. This is just a demo.
    
    <! An appender named STDOUT can be appended to output logs to the console.
    <springProfile  name="dev">
        <root level="DEBUG">
            <appender-ref ref="STDOUT" />
        </root>
    </springProfile>
    
    <! An appender named ASYNC can be appended to output logs to files asynchronously.
    <springProfile  name="prod">
        <root level="INFO">
            <appender-ref ref="ASYNC" />
        </root>
    </springProfile>
</configuration>
Copy the code

Use in code

The logback-spring. XML configuration file is now in the final step. Here is how to introduce a log object into your project and how to use it to export logs directly to the code:

package com.example.demo.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class TestLog {

	private final static Logger log = LoggerFactory.getLogger(TestLog.class);
	
	
	@RequestMapping(value="/log",method=RequestMethod.GET)
	public void testLog(a) {
		log.trace("Trace level log");
		log.debug(Debug Level logs);
		log.info("Info level logs");
		log.warn("Logs of WARN level");
		log.error("Error Level Logs"); }}Copy the code

Private final static Logger log = LoggerFactory.getLogger(xxx.class); The XXX generation refers to the current class name. If you feel this is too troublesome, you can also inject it by @slf4J annotation. However, this method requires poM dependency and lombok plug-in installation.

conclusion

Nearly a month of time not updated, rest didn’t write, during the Spring Festival holiday has to work overtime, home about ten o ‘clock every day, every day since last week spare some time to finish writing this blog, from think to write too detailed, on the one hand is own deeper impression, because this kind of configuration file will not every time in the actual project to configure, Copy and paste has been used, many concepts have long forgotten, take this opportunity to consolidate their own, share the hope to be a little help to more people.