In project development, logging is often used to record information in order to track how the code is doing.

In the Java world, there are a number of logging tool libraries to implement the logging functionality without having to reinvent the wheel.

Let’s start by taking a look at the major logging tools.

: Notebook: This article is filed under “blog”

The logging framework

java.util.logging (JUL)

Starting with JDK1.4, logging is provided through java.util.logging.

It meets basic logging needs, but is not as powerful or widely used as Log4j.

Log4j

Log4j is an open source project of Apache by Ceki Gulcu.

Log4j is arguably the oldest and most widely used logging tool in the Java world. From the day of its birth to now has been widely welcomed by the industry.

Log4j is highly configurable and can be configured through external files at run time. It is based on the priority level of logging and provides mechanisms to direct logging to a number of destinations, such as databases, files, consoles, UNIX system logs, etc.

There are three main components in Log4j:

  • Loggers – Responsible for capturing record information.
  • Appenders – Responsible for publishing log information to different preferred destinations.
  • Layouts – Layouts for different types of log information.

The website address

Logback

Logback is another open source diary component designed by Log4J founder Ceki Gulcu to replace Log4j.

Logback is currently divided into three modules: logback-core, logback-classic, and logback-access.

  • logback-core– is the base module of the other two modules.
  • logback-classic– is an improved version of Log4j. In additionlogback-classicFully implementing the SLF4J API makes it easy to switch to other Logging systems such as Log4j or JDK14 Logging.
  • logback-access– The access module integrates with the Servlet container to provide access to the diary over Http.

The website address

Log4j2

The website address

Log4j2 is officially a replacement for Log4j and Logback.

Log4j2 architecture:



Log4j vs Logback vs Log4j2

Officially, Log4j2 is vastly superior to Log4j and Logback.

So what are the advantages of Log4j2 over its predecessors Log4j and Logback?

  1. Log4j2 is intended to be used as an audit logging framework. Both Log4j 1.x and Logback lose events during reconfiguration. Log4j 2 does not. In Logback, exceptions in an Appender are never visible to the application. In Log4j, appenders can be configured to allow exceptions to infiltrate applications.
  2. Log4j2 In multithreaded scenarios, asynchronous Loggers have a throughput 10 times higher than Log4j 1.x and Logback, with latency several orders of magnitude lower.
  3. Log4j2 is garbage free for standalone applications and low garbage for Web applications during steady state logging. This reduces the stress on the garbage collector and can provide better response time performance.
  4. Log4j2 uses a plug-in system that makes it easy to extend the framework without making any changes to Log4j by adding new appenders, filters, Layouts, Lookup, and Pattern Converter.
  5. The plug-in system is simpler to configure. Entries in the configuration do not need to specify class names.
  6. Supports custom log levels.
  7. Lambda expressions are supported.
  8. Support for message objects.
  9. Log4j and Logback’s Layout returns a string, while Log4j2 returns a binary array, making it usable by a variety of appenders.
  10. Syslog Appender supports TCP and UDP and BSD system logs.
  11. Log4j2 uses the Java5 concurrency feature to minimize the granularity of locks and reduce the overhead of locks.

Logging facade

What is log facade?

Log facade is an API encapsulation provided by different logging frameworks. It allows you to access a logging implementation solution without modifying any configuration during deployment.

common-logging

Common-logging is an open source project of Apache. Also called Jakarta Commons Logging, abbreviated JCL.

Common-logging is an API that provides logging functions. It does not provide specific logging implementation (of course, there is a Simple logger implementation inside common-logging, but it is weak and ignored). Instead, it works dynamically at run time by binding logging implementation components (such as log4j, java.util.loggin).

The website address

slf4j

Short for Simple Logging Facade for Java.

What? It’s by Ceki Gulcu again! The creator of Log4j, Logback, and SLF4J, who has been developing logging components for 500 years, has been outdoing himself.

Similar to common-logging, SLF4J is an API package provided by different Logging frameworks that allows access to a Logging implementation at deployment time without changing any configuration. However, SLF4J statically binds the real Log library at compile time. When using SLF4J, if you need to use a particular logging implementation, you must select the correct set of SLF4J JARS (various bridging packages).

The website address



common-logging vs slf4j

The SLF4J library is similar to Apache Common-logging. However, it statically binds the real log library at compile time. This may seem cumbersome, but it’s really just importing the bridge JAR package.

One of the highlights of SLF4J is that it provides a more convenient way to log:

There is no need to use Logger.isDebugenabled () to solve log performance problems due to character concatenation. Slf4j uses {} as a string substitution, of the following form:

logger.debug("id: {}, name: {} ", id, name);
Copy the code

conclusion

In summary, using SLF4J + Logback is the most ideal logging solution today.

Next, is how to implement in the project.

Implement the logging solution

There are basically three steps to using the logging solution:

  1. The introduction of the jar package
  2. configuration
  3. Use the API

Step 2 and Step 3 of the various common logging solutions are basically the same, with the implementation differences mainly in step 1, which is to use different libraries.

The introduction of the jar package

A combination of SLF4J + Logback is preferred here.

If you are used to common-logging, you can choose common-logging+log4j.

It is strongly recommended not to use logging implementation components (LogBack, log4J, java.util.logging) directly because, as mentioned earlier, there is no flexibility to replace logging libraries.

Alternatively, your old project uses common-logging or implements the logging component directly. If you modify the old code, too much work, need to be compatible processing. In the following sections, you’ll see a variety of solutions.

Note: As far as I know, there is currently no way to bridge SLF4J to common-logging. If I am ignorant, please enlighten me.

Slf4j binds the logging component directly

slf4j + logback

Add the dependency to pom.xml.

Logback-classic-1.0.13.jar will automatically add slf4J-APi-1.7.21.jar and logback-core-1.0.13.jar to your project as well.

<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.0.13</version>
</dependency>
Copy the code

slf4j + log4j

Add the dependency to pom.xml.

Slf4j-log4j12-1.7.21.jar will automatically add slf4J-APi-1.7.21.jar and log4j-1.2.17.jar to your project as well.

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>1.7.21</version>
</dependency>
Copy the code

slf4j + java.util.logging

Add the dependency to pom.xml.

Slf4j-jdk14-1.7.21.jar will automatically add slf4J-APi-1.7.21.jar to your project as well.

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-jdk14</artifactId>
  <version>1.7.21</version>
</dependency>
Copy the code

Slf4j is compatible with non-SLF4J logging components

Before introducing the solution, let me introduce a concept — bridging

What is bridging

If you are developing an application that calls components that already use common-logging, you need jCL-over-slf4j. jar to redirect the log output to slf4J-API, which in turn calls the logging component that SLF4J actually relies on. This process is called bridging. The official SLF4J bridging policy is shown below:



As you can see from the figure, whether you are using common-logging in your old project or using log4j and java.util.logging directly, you can use the corresponding bridge JAR packages to resolve compatibility issues.

Slf4j compatible with common – logging

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>jcl-over-slf4j</artifactId>
  <version>1.7.12</version>
</dependency>
Copy the code

Slf4j compatible log4j

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>log4j-over-slf4j</artifactId>
    <version>1.7.12</version>
</dependency>
Copy the code

Slf4j compatible with Java. Util. Logging

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jul-to-slf4j</artifactId>
    <version>1.7.12</version>
</dependency>
Copy the code

Spring integration slf4j

Do Java Web development, basic can’t do without spring framework. Unfortunately, the logging solution used by Spring is common-logging + Log4j.

So, you need a bridge JAR package: logback-ext-Spring.

<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.1.3</version>
</dependency>
<dependency>
  <groupId>org.logback-extensions</groupId>
  <artifactId>logback-ext-spring</artifactId>
  <version>0.1.2</version>
</dependency>
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>jcl-over-slf4j</artifactId>
  <version>1.7.12</version>
</dependency>
Copy the code

Common-logging binds the logging component

common-logging + log4j

Add the dependency to pom.xml.

<dependency>
  <groupId>commons-logging</groupId>
  <artifactId>commons-logging</artifactId>
  <version>1.2</version>
</dependency>
<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>
Copy the code

Use the API

Slf4j usage

Using the SLF4J API is simple. Initialize a Logger instance with LoggerFactory and call Logger’s print-level function.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class App {
    private static final Logger log = LoggerFactory.getLogger(App.class);
    public static void main(String[] args) {
        String msg = "print log, current level: {}";
        log.trace(msg, "trace");
        log.debug(msg, "debug");
        log.info(msg, "info");
        log.warn(msg, "warn");
        log.error(msg, "error"); }}Copy the code

Common usage – logging

Common-logging uses almost the same as SLF4J, but supports a higher level of printing: fatal.

In addition, common-Logging does not support {} substitution arguments, so you have to concatenate strings.

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class JclTest {
    private static final Log log = LogFactory.getLog(JclTest.class);

    public static void main(String[] args) {
        String msg = "print log, current level: ";
        log.trace(msg + "trace");
        log.debug(msg + "debug");
        log.info(msg + "info");
        log.warn(msg + "warn");
        log.error(msg + "error");
        log.fatal(msg + "fatal"); }}Copy the code

Log4j2 configuration

The basic configuration of log4j2 is as follows:

<?xml version="1.0" encoding="UTF-8"? >;
<Configuration>
  <Properties>
    <Property name="name1">value</property>
    <Property name="name2" value="value2"/>
  </Properties>
  <Filter type="type" . />
  <Appenders>
    <Appender type="type" name="name">
      <Filter type="type" . />
    </Appender>.</Appenders>
  <Loggers>
    <Logger name="name1">
      <Filter type="type" . />
    </Logger>.<Root level="level">
      <AppenderRef ref="name"/>
    </Root>
  </Loggers>
</Configuration>
Copy the code

Configuration example:

<?xml version="1.0" encoding="UTF-8"? >
<Configuration status="debug" strict="true" name="XMLConfigTest"
               packages="org.apache.logging.log4j.test">
  <Properties>
    <Property name="filename">target/test.log</Property>
  </Properties>
  <Filter type="ThresholdFilter" level="trace"/>

  <Appenders>
    <Appender type="Console" name="STDOUT">
      <Layout type="PatternLayout" pattern="%m MDC%X%n"/>
      <Filters>
        <Filter type="MarkerFilter" marker="FLOW" onMatch="DENY" onMismatch="NEUTRAL"/>
        <Filter type="MarkerFilter" marker="EXCEPTION" onMatch="DENY" onMismatch="ACCEPT"/>
      </Filters>
    </Appender>
    <Appender type="Console" name="FLOW">
      <Layout type="PatternLayout" pattern="%C{1}.%M %m %ex%n"/><! -- class and line number -->
      <Filters>
        <Filter type="MarkerFilter" marker="FLOW" onMatch="ACCEPT" onMismatch="NEUTRAL"/>
        <Filter type="MarkerFilter" marker="EXCEPTION" onMatch="ACCEPT" onMismatch="DENY"/>
      </Filters>
    </Appender>
    <Appender type="File" name="File" fileName="${filename}">
      <Layout type="PatternLayout">
        <Pattern>%d %p %C{1.} [%t] %m%n</Pattern>
      </Layout>
    </Appender>
  </Appenders>

  <Loggers>
    <Logger name="org.apache.logging.log4j.test1" level="debug" additivity="false">
      <Filter type="ThreadContextMapFilter">
        <KeyValuePair key="test" value="123"/>
      </Filter>
      <AppenderRef ref="STDOUT"/>
    </Logger>

    <Logger name="org.apache.logging.log4j.test2" level="debug" additivity="false">
      <AppenderRef ref="File"/>
    </Logger>

    <Root level="trace">
      <AppenderRef ref="STDOUT"/>
    </Root>
  </Loggers>

</Configuration>
Copy the code

Logback configuration

<configuration>

  • Function:<configuration>Is the root element of the Logback configuration file.
  • The main points of
    • It has a<appender>,<logger>,<root>Three child elements.



<appender>

  • What it does: Delegates the logging task to a component named appender.
  • The main points of
    • Zero or more can be configured.
    • It has a<file>,<filter>,<layout>,<encoder>Four child elements.
  • attribute
    • Name: Sets the appender name.
    • Class: Sets the specific instantiated class.

<file>

  • Effect: Sets the log file path.

<filter>

  • Effect: Sets a filter.
  • The main points of
    • Zero or more can be configured.

<layout>

  • Effect: Sets appender.
  • The main points of
    • Zero or one can be configured.
  • attribute
    • Class: Sets the specific instantiated class.

<encoder>

  • Function: Sets the encoding.
  • The main points of
    • Zero or more can be configured.
  • attribute
    • Class: Sets the specific instantiated class.



<logger>

  • Purpose: Set logger.
  • The main points of
    • Zero or more can be configured.
  • attribute
    • name
    • Level: Sets the log level. Case insensitive. Optional values: TRACE, DEBUG, INFO, WARN, ERROR, ALL, and OFF.
    • Additivity: Optional value: true or false.

<appender-ref>

  • Function: appender reference.
  • The main points of
    • Zero or more can be configured.

<root>

  • Action: Sets the root Logger.
  • The main points of
    • Only one can be configured.
    • No attributes other than level are supported. Level properties and<logger>Is the same as.
    • I have one child element<appender-ref>, and<logger>Is the same as.

Complete logback.xml reference example

In the configuration file below, I set up five levels for my project code (root directory: org.zp.notes.spring) :

TRACE, DEBUG, INFO, WARN, and ERROR are in descending order of priority.

Because I was concerned about some of the information in the Spring framework itself, I added logs that specifically print Spring WARN and above.

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

<! There are five valid levels in logback: TRACE, DEBUG, INFO, WARN, and ERROR, in descending order of priority.
<configuration scan="true" scanPeriod="60 seconds" debug="false">

  <property name="DIR_NAME" value="spring-helloworld"/>

  <! Print log to console -->
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n</pattern>
    </encoder>
  </appender>

  <! -- RollingFileAppender begin -->
  <appender name="ALL" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <! -- Set scrolling strategy according to time -->
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>${user.dir}/logs/${DIR_NAME}/all.%d{yyyy-MM-dd}.log</fileNamePattern>
      <maxHistory>30</maxHistory>
    </rollingPolicy>

    <! -- Set scrolling strategy according to file size -->
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>30MB</maxFileSize>
    </triggeringPolicy>

    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n</pattern>
    </encoder>
  </appender>

  <appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <! -- Set scrolling strategy according to time -->
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>${user.dir}/logs/${DIR_NAME}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
      <maxHistory>30</maxHistory>
    </rollingPolicy>

    <! -- Set scrolling strategy according to file size -->
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>10MB</maxFileSize>
    </triggeringPolicy>

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

    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n</pattern>
    </encoder>
  </appender>

  <appender name="WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <! -- Set scrolling strategy according to time -->
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>${user.dir}/logs/${DIR_NAME}/warn.%d{yyyy-MM-dd}.log</fileNamePattern>
      <maxHistory>30</maxHistory>
    </rollingPolicy>

    <! -- Set scrolling strategy according to file size -->
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>10MB</maxFileSize>
    </triggeringPolicy>

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

    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n</pattern>
    </encoder>
  </appender>

  <appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <! -- Set scrolling strategy according to time -->
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>${user.dir}/logs/${DIR_NAME}/info.%d{yyyy-MM-dd}.log</fileNamePattern>
      <maxHistory>30</maxHistory>
    </rollingPolicy>

    <! -- Set scrolling strategy according to file size -->
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>10MB</maxFileSize>
    </triggeringPolicy>

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

    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n</pattern>
    </encoder>
  </appender>

  <appender name="DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <! -- Set scrolling strategy according to time -->
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>${user.dir}/logs/${DIR_NAME}/debug.%d{yyyy-MM-dd}.log</fileNamePattern>
      <maxHistory>30</maxHistory>
    </rollingPolicy>

    <! -- Set scrolling strategy according to file size -->
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>10MB</maxFileSize>
    </triggeringPolicy>

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

    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n</pattern>
    </encoder>
  </appender>

  <appender name="TRACE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <! -- Set scrolling strategy according to time -->
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>${user.dir}/logs/${DIR_NAME}/trace.%d{yyyy-MM-dd}.log</fileNamePattern>
      <maxHistory>30</maxHistory>
    </rollingPolicy>

    <! -- Set scrolling strategy according to file size -->
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>10MB</maxFileSize>
    </triggeringPolicy>

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

    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n</pattern>
    </encoder>
  </appender>

  <appender name="SPRING" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <! -- Set scrolling strategy according to time -->
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>${user.dir}/logs/${DIR_NAME}/springframework.%d{yyyy-MM-dd}.log
      </fileNamePattern>
      <maxHistory>30</maxHistory>
    </rollingPolicy>

    <! -- Set scrolling strategy according to file size -->
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>10MB</maxFileSize>
    </triggeringPolicy>

    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n</pattern>
    </encoder>
  </appender>
  <! -- RollingFileAppender end -->

  <! -- logger begin -->
  <! -- Log recording of this item, hierarchical printing -->
  <logger name="org.zp.notes.spring" level="TRACE" additivity="false">
    <appender-ref ref="STDOUT"/>
    <appender-ref ref="ERROR"/>
    <appender-ref ref="WARN"/>
    <appender-ref ref="INFO"/>
    <appender-ref ref="DEBUG"/>
    <appender-ref ref="TRACE"/>
  </logger>

  <! -- SPRING Framework log -->
  <logger name="org.springframework" level="WARN" additivity="false">
    <appender-ref ref="SPRING"/>
  </logger>

  <root level="TRACE">
    <appender-ref ref="ALL"/>
  </root>
  <! -- logger end -->

</configuration>
Copy the code

Log4j configuration

A complete sample log4J.xml reference

Log4j typically has configuration files in XML format or properties format. Properties are not introduced here for the sake of comparison with logback.xml, but there is not much difference.

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

      

<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>

  <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern"
             value="%d{yyyy-MM-dd HH:mm:ss,SSS\} [%-5p] [%t] %c{36\}.%M - %m%n"/>
    </layout>

    <! Filter set output level -->
    <filter class="org.apache.log4j.varia.LevelRangeFilter">
      <param name="levelMin" value="debug"/>
      <param name="levelMax" value="fatal"/>
      <param name="AcceptOnMatch" value="true"/>
    </filter>
  </appender>


  <appender name="ALL" class="org.apache.log4j.DailyRollingFileAppender">
    <param name="File" value="${user.dir}/logs/spring-common/jcl/all"/>
    <param name="Append" value="true"/>
    <! Create log files every day -->
    <param name="DatePattern" value="'-'yyyy-MM-dd'.log'"/>
    <! Regenerating log files every hour -->
    <! --<param name="DatePattern" value="'-'yyyy-MM-dd-HH'.log'"/>-->
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern"
             value="%d{yyyy-MM-dd HH:mm:ss,SSS\} [%-5p] [%t] %c{36\}.%M - %m%n"/>
    </layout>
  </appender>

  <! -- Specify logger Settings and additivity to indicate whether the default inheritance mechanism is followed -->
  <logger name="org.zp.notes.spring" additivity="false">
    <level value="error"/>
    <appender-ref ref="STDOUT"/>
    <appender-ref ref="ALL"/>
  </logger>

  <! -- Root Logger Settings -->
  <root>
    <level value="warn"/>
    <appender-ref ref="STDOUT"/>
  </root>
</log4j:configuration>
Copy the code

reference

  • Slf4 official document
  • Logback official document
  • Log4j official documentation
  • Commons-logging official documentation
  • Blog.csdn.net/yycdaizi/ar…