[Log] Log framework logback and log4j2 in Java

[Log] Log framework in Java (I) JUL, Log4j
[Log] Log framework in Java (middle) JCL and SLF
[Log] Log framework in Java (Part 2): logback and log4j2

1. logback learning

1.1 logback introduction

logback is another open source log component designed by the founder of log4j, and its performance is better than log4j

logback is mainly divided into three modules:

  • Logback core: the basic module of the other two modules
  • Logback classic: it is an improved version of log4j, and it fully implements the slf4j API
  • Logback access: the access module is integrated with the Servlet container and provides the function of accessing logs through http

1.2 quick start

Create a Maven project and introduce dependencies. POM file:

<!--slf Log facade-->
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.26</version>
</dependency>
<!--logback-->
<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.2.3</version>
</dependency>

1. Introduction case:

public class LogbackTest {

    public static final Logger LOGGER = LoggerFactory.getLogger(LogbackTest.class);

    @Test
    public void testQuick() {
        LOGGER.error("error");
        LOGGER.warn("warn");
        LOGGER.info("info");
        LOGGER.debug("debug");
        LOGGER.trace("trace");
    }

}

The operation results are as follows:

Indicates that the default level of logback is debug.

1.3 configuration file

logback reads the following types of configuration files in turn:

  • logback.groovy
  • logback-test.xml
  • logback.xml

If none exists, the default configuration is used

1. Relationship between logback components

  1. Logger: logger. After it is associated with the corresponding context of the application, it is mainly used to store log objects, and you can also define log types and levels
  2. Appender: used to specify the destination of log output. The destination can be console, file, database, etc
  3. Layout: it is responsible for converting events into strings and formatting log information output. In logback, the layout object is encapsulated in the encoder

2. Console Appender

Console Appender: outputs log information to the console, and some log information can be set.

Create a new logback.xml configuration file under resources

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <!--Configure centralized management properties-->
    <property name="pattern" value="[%-5level] %d{yyyy-mm-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n"></property>

    <!--Console log output Appender-->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <!--Controls the output flow object. The default is System.out,Can be modified to System.err-->
        <target>System.err</target>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <!--root logger to configure-->
    <root level="ALL">
        <appender-ref ref="console"></appender-ref>
    </root>
</configuration>

%-5level: log level. Align left
%D {yyyy MM DD HH: mm: SS. SSS}: date (formatted)
%c: Method name
%M: Class name
%Thread: thread name
%m: Log information
%n: Line feed

After configuring the configuration file, the operation results are as follows:

3. File Appender

File Appender: stores the log information in the log file

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <!--Configure centralized management properties-->
    <property name="pattern" value="[%-5level] %d{yyyy-mm-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n"></property>
    <property name="log_dir" value="E:/temp/logs"></property>

    <!--Console log output Appender-->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <!--Controls the output flow object. The default is System.out,Can be modified to System.err-->
        <target>System.err</target>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <!--File log output Appender-->
    <appender name="file" class="ch.qos.logback.core.FileAppender">
        <!--Log save path-->
        <file>${log_dir}/logback.log</file>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <!--root logger to configure-->
    <root level="ALL">
        <appender-ref ref="console"></appender-ref>
        <appender-ref ref="file" />
    </root>
</configuration>

Of course, if you want to see the log more intuitively, you can set the log file to html format:

<!--html file format-->
<appender name="htmlFile" class="ch.qos.logback.core.FileAppender">
    <!--Log save path-->
    <file>${log_dir}/logback.html</file>
    <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
        <layout class="ch.qos.logback.classic.html.HTMLLayout">
            <pattern>%level%d{yyyy-mm-dd HH:mm:ss.SSS}%c%M%L%thread%m</pattern>
        </layout>
    </encoder>
</appender>

4. Appender for log splitting and archive compression

When the log file is large, we cannot store all the log information in the same log file, so we need to split the log information.

<!--Log splitting and archive compression Appender-->
<appender name="rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!--Log save path-->
    <file>${log_dir}/roll_logback.log</file>
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
        <pattern>${pattern}</pattern>
    </encoder>
    <!--Specify split rules-->
    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
        <!--Declare the split file name in time and compression format-->
        <fileNamePattern>${log_dir}/rolling.%d{yyyy-MM-dd-HH-mm-ss}.log%i.gz</fileNamePattern>
        <!--Split by file size-->
        <maxFileSize>1MB</maxFileSize>
    </rollingPolicy>
</appender>

Test code: run 10000 times

@Test
public void testQuick() {
    for (int i = 0; i < 10000; i++) {
        LOGGER.error("error");
        LOGGER.warn("warn");
        LOGGER.info("info");
        LOGGER.debug("debug");
        LOGGER.trace("trace");
    }
}

The operation results are as follows:

Log file splitting rules: log compressed files are split in seconds (accurate to seconds). When the log file exceeds 1 MB, the log file will be split again, and I in. log%i.gz in the log file name will be added automatically.

5. Log level filter

<!--Log splitting and archive compression Appender-->
<appender name="rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!--Log save path-->
    <file>${log_dir}/roll_logback.log</file>
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
        <pattern>${pattern}</pattern>
    </encoder>
    <!--Specify split rules-->
    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
        <!--Declare the split file name in time and compression format-->
        <fileNamePattern>${log_dir}/rolling.%d{yyyy-MM-dd-HH-mm-ss}.log%i.gz</fileNamePattern>
        <!--Split by file size-->
        <maxFileSize>1MB</maxFileSize>
    </rollingPolicy>

    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>ERROR</level>
        <onMatch>ACCEPT</onMatch>
        <onMismatch>DENY</onMismatch>
    </filter>
</appender>

Among them,

<filter class="ch.qos.logback.classic.filter.LevelFilter">
    <level>ERROR</level>
    <onMatch>ACCEPT</onMatch>
    <onMismatch>DENY</onMismatch>
</filter>

Indicates that only log information of ERROR level is recorded

6. Asynchronous log

By default, when the program runs, the program and logging are synchronized. With asynchronous logging, you can make the program and logging not synchronized.

<!--Asynchronous log-->
<appender name="asyc" class="ch.qos.logback.classic.AsyncAppender">
    <!--Specify a specific appender-->
    <appender-ref ref="rollFile"></appender-ref>
</appender>

<!--root logger to configure-->
<root level="ALL">
    <appender-ref ref="console"></appender-ref>
    <appender-ref ref="asyc" />
</root>

7. User defined logger

<!--root logger to configure-->
<root level="ALL">
    <appender-ref ref="console" />
    <appender-ref ref="async" />
</root>

<!--custom Logger object-->
<logger name="com.zzc.log.logback" level="info" additivity="false">
    <appender-ref ref="console" />
</logger>

Meaning of the above label: the custom Logger object does not inherit the rootLogger object, but it uses the console appender and only outputs log information above info level.

Attribute description of logger tag:

  • Name: the class name of the test class LogbackTest
  • Level: controls the log level
  • additivity: whether the user-defined logger object inherits the functions of the rootLogger object. Is false, which means that it does not inherit the function of root logger

2. log4j2 learning

2.1 log4j2 introduction

log4j2 is an upgraded version of log4j. It refers to the excellent design of logback and fixes some problems. Therefore, it has brought some significant improvements

2.2 quick start

At present, the mainstream log facade on the market is slf4j. Although log4j2 is also a log facade, its implementation function is very powerful and its performance is superior. Therefore, log4j2 is generally regarded as the implementation of log. slf4j + log4j2 is the future trend.

Create a Maven project and introduce dependencies. POM file:

<!--log4j2 Log facade-->
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-api</artifactId>
  <version>2.11.1</version>
</dependency>
<!--log4j2 Log implementation of-->
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-core</artifactId>
  <version>2.10.0</version>
</dependency>

Introductory case:

public class Log4j2Test {

    public static final Logger LOGGER = LogManager.getLogger(Log4j2Test.class);

    @Test
    public void testQuick() {
        LOGGER.fatal("fatal");
        LOGGER.error("error");
        LOGGER.warn("warn");
        LOGGER.info("info");
        LOGGER.debug("debug");
        LOGGER.trace("trace");
    }

}

After running results:

Indicates that the default log level of log4j2 is error.

slf4j + log4j2:

<!--slf Log facade-->
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.26</version>
</dependency>
<!--use log4j2 Bind to your adapter-->
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-slf4j-impl</artifactId>
  <version>2.10.0</version>
</dependency>

<!--log4j2 Log facade-->
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-api</artifactId>
  <version>2.11.1</version>
</dependency>
<!--log4j2 Log implementation of-->
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-core</artifactId>
  <version>2.10.0</version>
</dependency>

Test code:

public class SlfTest {

    public static final Logger LOGGER = LoggerFactory.getLogger(SlfTest.class);

    @Test
    public void testQuick() {
        LOGGER.error("error");
        LOGGER.warn("warn");
        LOGGER.info("info");
        LOGGER.debug("debug");
        LOGGER.trace("trace");
    }

}

2.3 configuration file

Log4j2 loads the configuration in the log4j2.xml file under the classpath by default

<?xml version="1.0" encoding="utf-8" ?>
<!--
    status=warn: The output log level of the log framework itself
    monitorInterval="5": The interval between automatic loading of configuration files shall not be less than 5 seconds
-->
<Configuration status="warn" monitorInterval="5">
    <!--Configure centralized management properties-->
    <properties>
        <property name="LOG_HOME" value="E:/temp/logs"></property>
    </properties>

    <Appenders>
        <Console name="console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n"></PatternLayout>
        </Console>

        <File name="file" fileName="${LOG_HOME}/myfile.log">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n"></PatternLayout>
        </File>
        <!--Use random read / write stream-->
        <RandomAccessFile name="accessFile" fileName="${LOG_HOME}/myAcclog.log">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n"></PatternLayout>
        </RandomAccessFile>
        <!--Split according to certain rules-->
        <RollingFile name="rollingFile" fileName="${LOG_HOME}/myRolllog.log"
                     filePattern="${LOG_HOME}/$${date:yyyy-MM-dd}/myRolllog-%d{yyyy-MM-dd-HH-mm}-%i.log">
            <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY" />
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n"></PatternLayout>
            <!--Split rule-->
            <Policies>
                <!--When the system starts, trigger and start the split rule to generate a new log file-->
                <OnStartupTriggeringPolicy />
                <!--Split by file size-->
                <SizeBasedTriggeringPolicy size="10 MB" />
                <!--Split by time node. The rules are determined by filePattern definition-->
                <TimeBasedTriggeringPolicy />
            </Policies>
            <!--In the same directory, the number of files is limited to 30, and the old log information is overwritten after exceeding the limit-->
            <DefaultRolloverStrategy max="30" />
        </RollingFile>
    </Appenders>

    <Loggers>
        <Root level="info">
            <AppenderRef ref="console" />
        </Root>
    </Loggers>

</Configuration>

2.4 asynchronous log

The biggest feature of log4j2 is asynchronous logging, and its performance improvement mainly benefits from asynchronous logging.

log4j2 provides two asynchronous modes: AsyncAppender and asyncloger (common)

[note]: to configure asynchronous logs, you need to introduce dependencies:

<dependency>
  <groupId>com.lmax</groupId>
  <artifactId>disruptor</artifactId>
  <version>3.3.4</version>
</dependency>

1. AsyncAppender

<Configuration>
	...
	<File name="file" fileName="${LOG_HOME}/myfile.log">
	    <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n"></PatternLayout>
	</File>
	
	<!--Asynchronous log configuration-->
	<Async name="Async">
	    <AppenderRef ref="file" />
	</Async>

	<Loggers>
        <Root level="info">
            <AppenderRef ref="console" />
            <AppenderRef ref="Async" />
        </Root>
    </Loggers>
</Configuration>

After running the code, the results are as follows:

2. AsyncAppender
AsyncAppender is the highlight of log4j2 and the official recommended way. There are two ways: Global asynchrony and hybrid asynchrony.

Global asynchrony

Global asynchrony: all logs are recorded asynchronously without any modification on the configuration file. Just add a log4j2.component.properties configuration file.

Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector

Hybrid asynchronous

Hybrid asynchrony: synchronous logs and asynchronous logs can be used in applications at the same time, which makes the log configuration more flexible

Modify log4j2.xml:

<Configuration>
	...
	<Loggers>
        <!--custom Logger object includeLocation="false" Close line number information-->
        <AsyncLogger name="com.zzc.log.log4j2" level="info" includeLocation="false" additivity="false">
            <AppenderRef ref="console" />
        </AsyncLogger>

        <Root level="info">
            <AppenderRef ref="console" />
            <AppenderRef ref="Async" />
        </Root>
    </Loggers>
</Configuration>

Run the code and the results are as follows:

Indicates that the line number is not displayed.

3. Log in springboot

SpringBoot logging is also a common logging system in development. By default, SpringBoot uses SLF4J as the log facade and logback as the log implementation to record logs.

3.1 log design in springboot

Logs in SpringBoot:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-logging</artifactId>
</dependency>

Dependency structure diagram:

Summary:

  1. The bottom layer of SpringBoot uses logback as the log implementation
  2. SLF4J is used as the log facade
  3. Also convert JUL to SLF4J
  4. Log4j2 can also be used as the log facade, but logback is finally called through SLF4J

3.2 using logs in springboot

1. Introduction case:

@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTests {

	private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationTests.class);

	@Test
	public void contextLoads() {
		LOGGER.error("error");
		LOGGER.warn("warn");
		LOGGER.info("info");
		LOGGER.debug("debug");
		LOGGER.trace("trace");
	}

}

Operation results:

Default log level in SpringBoot: info

2. Configuration file:

application.properties:

# Specifies the logging level of the custom Logger object
logging.level.com.zzc.sbexp=debug
# Specifies the format of console output information
logging.pattern.console=[%-5level] %d{yyyy-MM-dd HH:mm:ss} %c [%thread] === %m%n
# Specify the directory where the log files are stored. The default file name is spring.log
logging.file=E://temp/springboot.log
# Specifies the log file message format
logging.pattern.file=[%-5level] %d{yyyy-MM-dd HH:mm:ss} %c [%thread] === %m%n

The operation results are as follows:

3. Specify log profile:

Assign each log framework's own configuration file to the classpath; SpringBoot does not use the default configuration.

Log frameconfiguration file
logbacklogback.xml,logback-spring.xml
log4j2log4j2.xml,log4j2-spring.xml
jullogging.properties

logback.xml is directly recognized by SpringBoot.

logback.xml:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <!--Configure centralized management properties-->
    <property name="pattern" value="[%-5level] %d{yyyy-mm-dd HH:mm:ss.SSS} %c %M %L [%thread] --- %m%n"></property>
    <property name="log_dir" value="E:/temp/logs"></property>

    <!--Console log output Appender-->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <!--Controls the output flow object. The default is System.out,Can be modified to System.err-->
        <target>System.err</target>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <!--custom Logger object-->
    <logger name="com.zzc.sbexp" level="info" additivity="false">
        <appender-ref ref="console" />
    </logger>
</configuration>

After running the code:

4. Switch log profile:

Switch the log frame to log4j2

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
	<exclusions>
		<!--exclude logback-->
		<exclusion>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-logging</artifactId>
		</exclusion>
	</exclusions>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

log4j2.xml:

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

<Configuration status="warn" monitorInterval="5">
    <!--Configure centralized management properties-->
    <properties>
        <property name="LOG_HOME" value="E:/temp/logs"></property>
    </properties>

    <Appenders>
        <Console name="console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n"></PatternLayout>
        </Console>
    </Appenders>

    <Loggers>
        <Root level="info">
            <AppenderRef ref="console" />
        </Root>
    </Loggers>

</Configuration>

Tags: Java Back-end

Posted on Mon, 06 Dec 2021 14:10:51 -0500 by Tsukiyomi