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:
scan
: Specifies whether to reload the configuration file when it is modified. Two options are availabletrue
orfalse
By default,true
.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 minutescan
Attribute values fortrue
Will take effect.debug
: Print or notloback
Internal log information. Two optional valuestrue
orfalse
By 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:
name
: Specifies the name of the node for later reference.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 consoleclass
The value ofch.qos.logback.core.ConsoleAppender
; Logs need to be exported to a fileclass
The value ofch.qos.logback.core.FileAppender
Etc., want to know all aboutappender
Type, 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:
name
: is used to specify this<logger>
The package or class of the constraint.level
: Optional property that specifies the output level of logs. If not set, the current<logger>
Inherits the rank of a superior.additivity
: Indicates whether to send output information to the upper level. Two optional values are availabletrue
orfalse
By 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 defaultname
Properties forroot
the<logger>
Because it’s a root<logger>
, so there is no upward pass, so there is noadditivity
Property, so there is only one nodelevel
Properties.
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%contextName
To 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:name
: the variable namevalue
: 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 consoleConsoleAppender
Introduction:
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
%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.SimpleDateFormat
Once 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:- No input indicates complete output
<logger>
The name of the - The input
0
Output only<logger>
The string after the dot on the far right - Input other numbers represent the number of characters before the last dot of the output decimal point
- No input indicates complete output
%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 valuestrue
andfalse
By default,true
, indicates that each log output to the file is appended to the end of the original file.false
Empty the existing files<encoder>
And:ConsoleAppender
The same
Obviously, our logging policy in the sample indicates that we append log information to the D:/test.log file each time.
Scrolling file policyRollingFileAppender
introduce
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.
<fileNamePattern>
: Specifies the log path and the naming rule of the log file nameLog file +%d{}.log
The default format of the date isyyyy-MM-dd
Indicates that a file is generated every day, that is, scroll by dayyyyy-MM
, indicates that a file is generated every month<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<encoder>
: same as above<totalSizeCap>
: this object indicates the maximum memory size for all log files. When the value is exceeded,logback
The 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 filterNEUTRAL
: Logs are filtered through the next filterACCEPT
: 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, whenBlockingQueue
There are20%
The capacity that he will discardTRACE
,DEBUG
andINFO
Logs of this level are reserved onlyWARN
andERROR
Level of logs. To keep all logs, set this value to0
.<queueSize>
:BlockingQueue
By 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
- perform
jar
Add parameters to the package:
java -jar xxx.jar --spring.profiles.active=prod
Copy the code
- In the project
application.properties
Add:
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.