Log series 1 -- slf4j log framework principle

Keywords: Java log4j xml JDK

catalog

1. Preface

When it comes to logging tools, you must have heard these terms in your daily work or study: log4j, logback, jdk-logging, slf4j, commons-logging, etc. What is the relationship between them and what is their role in the whole logging system?
Log framework is divided into three categories, including log Facade, log adapter and log library. The Facade design pattern is used to decouple and make log usage easier.

2. Log face

The facade design pattern is one of the object-oriented design patterns, which is adopted by the log framework, similar to the JDBC design concept. It only provides a set of interface specifications, and it is not responsible for the realization of the log function. The purpose is to make users not need to pay attention to which log library is responsible for the specific use details of the log printer. At present, there are two most widely used log portals: slf4j and Commons logging

3. Log Library

It has implemented the related functions of logs. There are three main log libraries, namely log4j, log JDK and logback. The first time Java wanted to record logs, it could only be done through System.out or System.err, which was very inconvenient. Log4j is proposed to solve this problem. It is the earliest log library. Then JDK also introduced a log library java.util.logging.Logger (log JDK for short) in version 1.4. In this way, there are two kinds of log functions on the market. Developers need to pay attention to the specific details of the log library they use. Logback is the latest one. It comes from the same author as log4j. It is an upgraded version of log4j and implements the slf4j interface.

4. Log adapter

The log adapter is divided into two scenarios:

  1. Because the slf4j specification was proposed later, the previous log library did not implement the slf4j interface, such as log4j. Therefore, in order to use the slf4j+log4j mode in the project, an additional adapter (slf4j+log4j12) is needed to solve the problem of interface incompatibility

  2. In some old projects, the logbase adapter directly uses the logbase API to print logs for the sake of simple development at the beginning. Over time, it wants to change the original mode of directly calling the logbase to the industry standard facade mode (such as slf4j+logback combination), but there are too many places to print logs in the old project code, so an adapter is needed In this way, slf4j can be used to manage the logs uniformly without changing the original code, and it is not a problem to replace the specific log library freely in the future.

5. Selection of log base

If it is a new project, slf4j+logback mode is recommended. Because logback implements the interface of slf4j, no additional adapter is needed. In addition, logback is an upgraded version of log4j, which has more advantages than log4j. It can be integrated through the following configuration:

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>${slf4j-api.version}</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-core</artifactId>
    <version>${logback-core.version}</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>${logback-classic.version}</version>
</dependency>

If it is an old project, the door adapter needs to be determined according to the log library used. Generally, log4j is used in the old project, so for example, log4j log library can be integrated through the following configuration:

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>${slf4j-api.version}</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>${slf4j-log4j12.version}</version>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>${log4j.version}</version>
</dependency>

If the old code directly uses the interface provided by log4j log library to print logs, the log library adapter needs to be introduced. The configuration example is as follows:

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>log4j-over-slf4j</artifactId>
    <version>${log4j-over-slf4j.version}</version>
</dependency>

6.logback.xml configuration file

<?xml version="1.0" encoding="UTF-8"?>

<configuration scan="true">
	<property name="application_name" value="web" />
    <property name="LOG_PATH" value="c:" />
	 <!-- console output  -->
        <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
            <encoder charset="UTF-8">
                <pattern>%date %-5level %logger{80} [%X{trans_id}] - %msg%n</pattern>
                <!-- <pattern>%date [%thread] %-5level %logger{80} - %msg%n</pattern> -->
            </encoder>
        </appender>
        <!-- Time rolling output file log -->
        <appender name="file—debug" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                <level>debug</level>
            </filter>
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <FileNamePattern>${LOG_PATH}/logs/${application_name}/${application_name}_debug.%d{yyyy-MM-dd}_%i.log</FileNamePattern>
                <MaxHistory>100</MaxHistory>
                <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                    <maxFileSize>10mb</maxFileSize>
                </timeBasedFileNamingAndTriggeringPolicy>
            </rollingPolicy>
            <encoder charset="UTF-8">
                <pattern>%date [%thread] %-5level %logger{80} [%X{trans_id}] - %msg%n</pattern>
            </encoder>
        </appender>
    <!-- Time rolling output file log -->
    <appender name="file—info" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>info</level>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${LOG_PATH}/logs/${application_name}/${application_name}_info.%d{yyyy-MM-dd}_%i.log</FileNamePattern>
            <MaxHistory>100</MaxHistory>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>10mb</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder charset="UTF-8">
            <pattern>%date [%thread] %-5level %logger{80} [%X{trans_id}] - %msg%n</pattern>
        </encoder>
    </appender>
        <!-- Time scrolling output level by ERROR journal -->
        <appender name="file—error" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>ERROR</level>
                <onMatch>ACCEPT</onMatch>
                <onMismatch>DENY</onMismatch>
            </filter>
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <FileNamePattern>${LOG_PATH}/logs/${application_name}/${application_name}_error.%d{yyyy-MM-dd}_%i.log</FileNamePattern>
                <MaxHistory>100</MaxHistory>
                <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                    <maxFileSize>10mb</maxFileSize>
                </timeBasedFileNamingAndTriggeringPolicy>
            </rollingPolicy>
            <encoder charset="UTF-8">
                <pattern>%date [%thread] %-5level %logger{80} [%X{trans_id}] - %msg%n</pattern>
            </encoder>
        </appender>


    <Logger name="org.apache.http.impl.conn.PoolingHttpClientConnectionManager" level="DEBUG" additivity="false">
        <appender-ref ref="file—debug" />
    </Logger>

    <appender name="ASYNC-ERROR" class="ch.qos.logback.classic.AsyncAppender">
        <!-- Do not lose logs.default,If 80 of the queue%already expired,Will be discarded TRACT,DEBUG,INFO Level log -->
        <discardingThreshold>0</discardingThreshold>
        <!-- Change the default queue depth,This value affects performance.The default value is 256 -->
        <queueSize>256</queueSize>
        <!-- Add additional appender,At most one can be added -->
        <appender-ref ref="file—error"/>
    </appender>

        <root level="info">
            <appender-ref ref="stdout" />
            <appender-ref ref="file—info" />
            <appender-ref ref="file—error" />
        </root>
  
</configuration>

The example code is as follows:

private static  final Logger logger =LoggerFactory.getLogger(ConfigureQuartz.class);

Note that the logger object is defined as a static variable. This is because the logger is bound to the current class to avoid a new object every time, resulting in a waste of resources and even causing the OutOfMemoryError problem.

When using slf4j + specific log library mode, because slf4j is equivalent to acting as api abstract interface, our log printing is also interface oriented programming. When we need to replace the specific log library, we only need to introduce specific maven dependency, and remove the original log library dependency without changing the code. So far, the explanation of slf4j's architecture principle has been completed, and then the specific logback configuration file of the log library will be explained. This chapter simply gives the basic template of logback.xml configuration file. Please look forward to the next chapter log series 2 - logback configuration file details.

Posted by dk4210 on Wed, 13 May 2020 19:21:52 -0700