A first look at Logback: Learn to understand Logback configuration files

Preface

In today's applications, logs have become a very important tool. Through the logs printed by the system, you can monitor the operation of the system and troubleshoot the causes of system errors. From the earliest logging to today's various mature frameworks, log printing has become more standardized and clear. In particular, the emergence of SLF4J defines a common FACADE interface and capabilities for the logging framework. By simply introducing the SLF4J package and the log package that specifically implements the FACADE into the application, the upper-layer application can only program for the SLF4J interface without caring about the specific underlying log framework, thus achieving the decoupling of the upper-layer application and the underlying log framework. Logback, as a framework that supports SLF4J's general capabilities, has become one of the hottest logging frameworks. Today, let’s learn a little about some basic capabilities and configuration files of Logback logs. System.out.print

Get started quickly with Logback

Introducing MAVEN dependencies

logback is mainly composed of three modules, namely logback-core, logback-classic and logback-access. Among them, logback-core is the core module of the entire Logback, logback-classic supports SLF4J FACADE, and logback-access integrates Servlet Rongqi to provide HTTP logging function, suitable for web applications. The following is mainly introduced based on logback-classic.

The packages that introduce logback-classic are as follows:

<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.3.0-alpha5</version>
</dependency>

The Maven package pulled above will automatically pull logback-classic, logback-core and slf4j-api.jar based on transitive distance, so there is no need to declare additional dependencies on SLF4J and logback-core in the project.

Use Logback

Becauselogback-classic implements SLF4J FACADE, the upper-layer application only needs to face the calling syntax of SLF4J. The following code shows how to obtain the Logger object to print logs.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.core.util.StatusPrinter;

public class HelloWorld2 {

  public static void main(String[] args) {
    //这里的Logger和LoggerFactory均为SLF4J的类,真正调用时会使用Logback的日志能力
    //getLogger方法中传入的是Logger的名称,这个名称在后面讲解配置文件中的<logger>时会继续提到
    Logger logger = LoggerFactory.getLogger("chapters.introduction.HelloWorld2");
    
    //打印一条Debug级别的日志
    logger.debug("Hello world.");

    //获取根Logger,使用场景比较少
    Logger rootLogger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
  }
}

Log level

Each Logger in Logback has a corresponding log level. The log level can be defined by the Logger itself or inherited from the parent Logger. Logback supports a total of 5 log levels, from high to low, they are ERROR, WARN, INFO, DEBUG, and TRACE. The log level of the Logger determines which levels of logs can be output. Only logs with a level greater than or equal to the Logger level will be printed. For example, assuming that the log level of the Logger named "chapters.introduction.HelloWorld2" obtained above is INFO, calling logger.debug("xxx") will not output the log content because the DEBUG log level is lower than the INFO log level.

The log level can help us control the granularity of log printing. For example, in the development environment, the log level can be set to DEBUG to help troubleshoot problems, while in the production environment, the log level can be set to INFO, thereby reducing the performance caused by unnecessary printing logs. Influence.

Parametric output

Sometimes we don’t just print out a complete log, but want to include some running parameters in the log, as follows:

Logger logger = LoggerFactory.getLogger("chapters.introduction.HelloWorld2");
logger.debug("Hello World To " + username);

In addition to printing some structured statements, the log above also splices the name of the user who executed this logic during runtime. A problem will arise here, that is, the problem of string concatenation. Although the JVM has optimized the splicing of String strings, if the current log level is INFO, then the string splicing operation performed by this code is completely unnecessary. Therefore, it is recommended to add a line of log level judgment to the code for optimization, as follows:

//非debug级别不会执行字符串拼接操作,但是debug级别会执行两次isDebugEnabled操作,性能影响不大
if(logger.isDebugEnabled()) { 
    logger.debug("Hello World To " + username);
}

However, logback does not recommend using string concatenation to output logs in the system. Instead, it advocates using parameter passing, and logback itself performs log serialization. as follows:

//logger方法会判断是否为debug级别,再决定将entry序列化拼接如字符串
logger.debug("The entry is {}.", entry);

This log output method does not need to include an additional layer of log level judgment, because the logger.debug method will judge the log level once and then perform the log content transcoding operation. Note that the incoming parameters must implement the toString method, otherwise when the log transcodes the object, it will only print out the memory address of the object, not the specific content of the object

Overall structure

The previous article has briefly introduced the three main modules included in logback, and how to freely use the log framework in the code based on SLF4J FACADE. Let's start with understanding how to configure Logback from the perspective of configuration files.
Logback mainly supports XML and groovy structure configuration files. The following will be introduced based on the XML structure.


The above picture is a description of the overall structure of the Logback configuration file on the official website. The configuration file uses <configuration> as the root element, and contains 1 <root> element below it to define the configuration information of the root log, and there are 0 to more <logger> elements and zero to more <appender> elements. The <logger> element corresponds to the logging tool obtained through LoggerFactory.getLogger() in the application, and the <appender> element defines the output destination of the log, a <logger> can be associated with multiple <appender>, which allows the same line of logs to be output to multiple destinations.

A simple Logback configuration file is as follows:

<configuration> 
   <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 
   <!-- encoders are  by default assigned the type
         ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>
  <root level="debug">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

This configuration file declares an appender whose output name is STDOUT to the console, declares the log level of the root logger to be debug, and specifies that the log be output to the STDOUT stream.

Logback allows multiple configuration files, and the order in which configuration files are read when loading is as follows:

  1. Find logback-test.xml on the classpath (generally the classpath is src/test/resources)
  2. If the file does not exist, logback tries to find logback.groovy
  3. If the file does not exist, logback tries to find logback.xml
  4. If the file does not exist, logback will look for it under META-INF[com.qos.logback.classic.spi.Configurator](http://logback.qos.ch/xref/ch/qos/logback/classic/spi/Configurator.html)接口的实现
  5. If it is still not found, the default BasicConfigurator will be used, causing the log to be printed directly to the console. The log level is DEBUG and the log format is _%d{HH:mm:ss.SSS} [%thread] %-5level % logger{36} - %msg%n_

Configuration file syntax

After a brief understanding of the basic structure of the logback configuration file, this chapter introduces in detail some of the more commonly used tags in logback and their respective meanings.

configuration tag

As the root tag of the configuration file, configuration defines the mode for reading the entire Logback configuration. The attributes that can be defined in the configuration tag are as follows:

  1. debug: The default debug value is false. If debug is set to true, the status of the log frame will be printed regardless of whether the configuration is read successfully. If it is false, the status log will be printed only when there is an error in reading the configuration.
  2. scan: The default is false. If scan is set to true, logback will automatically scan the configuration file regularly. If the configuration file changes, logback can quickly identify and reconfigure it. The default scan interval can be overridden via scanPeriod. It is recommended not to enable this function in the production environment, because basically the configuration of the log framework in the production environment is stable. Only when the development environment needs to debug the behavior of the logging framework, this function can be turned on to reduce the trouble of restarting the application because of modifying the configuration for debugging.

logger tag

logger is the basic unit of log stream isolation. Each logger will be bound to a LoggerContext. There is a tree-like hierarchical relationship between Loggers, that is, Logger A can be the parent Logger of Logger B. The hierarchical relationship between them is determined based on the name of the logger. If the name of logger A is com.moduleA, and the name of logger B is com.moduleA.packageA, then A can be said to be B's parent logger. The purpose of this tree structure is that if B does not define its own log level, it will inherit A's log level. Others such as appenders will also be calculated based on the inheritance relationship.

Logger has only one name attribute that is required. Generally speaking, except for several logger names that need to be specially defined, the others are basically defined in the module dimension, thereby ensuring that each class under the module is defined in its own class. When the Logger is obtained by name, the corresponding Logger can be found upwards.

For example, if a logger named com.rale.service is defined, it is located in the com.rale.service.HelloService.java class and used LoggerFactory.getLogger(HelloService.class)Although the obtained Logger is not declared in the configuration file, the full path of the class will be used as the name of the logger, and the nearest parent Logger will be found upwards according to the Logger level, and the name will finally be returned as < a logger for i=4>. com.rale.service

The logger also has a label called level, which can assign a corresponding log level to the logger. Only logs higher than this level will be output. If no level value is explicitly defined, its log level is inherited from the nearest parent node that explicitly declares a log level.

A basic logger configuration is as follows:

<logger name="integration" level="INFO" additivity="false">
    <appender-ref ref="integration"/>
    <appender-ref ref="common-error"/>
</logger>

A logger can contain multiple appender-ref tags, which declare that the logger's logs will be printed to these output streams. There is also a special attribute additivity, which is used to constrain appender inheritance behavior. By default, the value of aditicity is true, that is, the logger will print to all appenders inherited from the parent Logger in addition to the currently explicitly declared appender-ref. For example, assuming that <appender-ref ref="common"> is declared in root, integration will print logs to these three output streams at the same time. If the same appender exists in the parent logger and child logger, the log will also be printed twice to the appender. Therefore, by setting additivity to false, excessive log output caused by unexpected appender inheritance can be reduced.

appender tag

An appender corresponds to a log output stream. The same appender can be bound to multiple loggers, that is, multiple loggers can output logs to the appender. Therefore, the appender implementation implements concurrency control internally to prevent log garbled characters.

Appender supports many output terminals, including console, file, remote Socket server, MySQL, PostgreSQL and other databases, remote UNIX log process, JMS, etc.

<appender> has two mandatory attributes name and class (the full path of the Appender class), including 0 to more <layout class=""> tags, and 0 to more <encoder class="" ;> tag, 0 to multiple <filter> tags. It can also contain any number of member variable attribute values ​​of the Appender Bean class.

The layout and encoder tags are used to format the logs in the appender, and the filter tag supports filtering the log information from the appender to determine which logs are printed and which are not. Therefore, the logs in the appender dimension can be defined through filter. level.

A typical appender is as follows:

    <appender name="common-error"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/sls-common-error.log</file>
        <encoder>
            <pattern>${LOCAL_FILE_LOG_PATTERN}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
    </appender>

A file output stream is declared here, and the file tag is used to define the location of the output file, and the encoder is used to define the log printing format. It is defined here by referencing variables. Variables will be introduced in detail in the property tag later. Then a filter is bound, and the appender is defined using this filter to only print logs with a log level greater than or equal to the ERROR level.

root tag

The root tag must be declared once in the configuration. The root tag actually defines the configuration information of the root logger. Its default log level is debug. The final parent logger of all loggers must be the root logger.

property tag

The property tag supports declaring variables in the configuration file. There are three sources of configuration file variables, namely through JVM COMMAND, JAVA COMMAND, Classpth and the current configuration file. For example, the format of the variables passed in by the JAVA command is as followsjava -DUSER_HOME="/home/sebastien" MyApp2. The <property> tag supports the declaration of member variables in the configuration file. It supports three types: KV, file relative path, and files under Classpth.

  <!--键值型声明-->
  <property name="USER_HOME" value="/home/sebastien" />

  <!--配置文件声明-->
  <property file="src/main/java/chapters/configuration/variables1.properties" />

  <!--Classpath资源-->
  <property resource="resource1.properties"/>

The standard Linux variable reference method is used to reference these variables. The value of the variable can be referenced through ${variable name}. It also supports declaring default values ​​for these variables through the ${变量名称:-默认值} syntax structure.

A simple example of declaring configuration and using it is as follows:

<configuration>
  <property name="USER_HOME" value="/home/sebastien" />
  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>${USER_HOME}/myApp.log</file>
    <encoder>
      <pattern>%msg%n</pattern>
    </encoder>
  </appender>

  <root level="debug">
    <appender-ref ref="FILE" />
  </root>
</configuration>

define tag

The define tag is also used to declare variables, but the difference from the property above is that define declares dynamic variables, that is, the values ​​of these variables can only be obtained after the program is run. For example, the ${HOSTNAME} variable that exists by default in the configuration file is implemented through the define tag. It will dynamically obtain the host name of the current container after the program is run, and assign it to the HOSTNAME variable.

A typical usage of the define tag is as follows. It is required that the class filled in the define class must be an implementation of the PropertyDefiner interface.

<configuration>

  <define name="rootLevel" class="a.class.implementing.PropertyDefiner">
    <shape>round</shape>
    <color>brown</color>
    <size>24</size>
  </define>
 
  <root level="${rootLevel}"/>
</configuration>

Logback provides several basic Definer implementations, such asFileExistsPropertyDefiner, which is a definer used to determine whether the file declared in the path exists. .

include tag

The include tag allows the introduction of logback configuration stored in another path. An example is as follows:

<configuration>
  <include file="src/main/java/chapters/configuration/includedConfig.xml"/>

  <root level="DEBUG">
    <appender-ref ref="includedConsole" />
  </root>

</configuration>

src/main/java/chapters/configuration/includedConfig.xmlThe contents of the file are as follows:

<included>
  <appender name="includedConsole" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>"%d - %m%n"</pattern>
    </encoder>
  </appender>
</included>

The content of the included file must be included in the included tag, and the syntax must meet the syntax of the logback configuration file. Here is the introduction of an appender declared in includeConfig.xml.

Guess you like

Origin blog.csdn.net/risen16/article/details/127687943