The strongest and most common development library - log class library in detail

Introduction to Log Library

  • The most important thing is to distinguish the log system from the log facade.
  • Secondly, the use of log libraries, including configuration and API usage; configuration focuses on the configuration of the log system, API usage focuses on the log facet;
  • Finally, selection, transformation, best practices, etc.

Log System for Log Library

java.util.logging (JUL)

Beginning with JDK1.4, logging was provided through java.util.logging.Although it is the official log lib, JUL is not widely used.Main cause:

  • JUL joined JDK1.4 (2002) when various third-party log LIBS were widely used
  • JUL had early performance problems and made good progress with JDK1.5, but it is still not as good as Logback/Log4j2
  • JUL is not as good as Logback/Log4j2. Output Handler, for example, does not have the richness of Logback/Log4j2. It sometimes needs to inherit customizations by itself, or it does not load configuration files from ClassPath by default.

Log4j

Log4j is an open source project for apache, founded by Ceki Gulcu.Log4j is arguably the oldest and most widely used logging tool in Java.Log4j is highly configurable and can be configured through external files at runtime.Depending on the priority of the record, it provides a mechanism to indicate where the recorded information is going, such as databases, files, consoles, UNIX system logs, and so on.

Log4j has three main components:

  • loggers - Responsible for capturing record information.
  • appenders - Responsible for publishing log information to different preferred destinations.
  • layouts - Responsible for formatting different styles of log information.

Official address: http://logging.apache.org/log4j/2.x/

Log4j's short board is performance, and after Logback and Log4j2 came out, Log4j usage was reduced.

Logback

Logback is another open source journal component designed by Ceki Gulcu, the founder of log4j, and is developed as a successor to Log4j, providing better performance implementations, asynchronous logger, Filter, and more.

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

  • logback-core - is the basic module of the other two modules.
  • Logback-classic - is an improved version of log4j.In addition, the full implementation of the SLF4J API by logback-classic allows you to easily change to other journal systems such as log4j or JDK14 Logging.
  • logback-access - The access module integrates with the Servlet container to provide access to the journal via Http.

Official address: http://logback.qos.ch/

Log4j2

People who maintain Log4j have created Log4j2 for performance.

Log4j2 and Log4j1.x are incompatible. They are designed to mimic SLF4J/Logback to a large extent and have greatly improved performance.

Log4j2 also has a Facade/Implementation separation design, which is divided into log4j-api and log4j-core.

Official address: http://logging.apache.org/log4j/2.x/

Log4j vs Logback vs Log4j2

Log4J2 is stronger in performance but ecologically Logback+SLF4J takes precedence.

Preliminary comparison

Both logback and log4j2 claim to be descendants of log4j, one from the same author and the other from the same name.

Regardless of descent, compare log4j2 with logback:

  • Log4j2 is newer than logback: The GA version of log4j2 was released at the end of 2014, several years later than logback, during which time log4j2 did absorb some of the advantages of slf4j and logback (such as log templates) and applied a number of new technologies
  • log4j2 outperforms logback due to more advanced locking mechanisms and the LMAX Disruptor library, especially in multithreaded and asynchronous logging environments
  • Both support Filter (which should be said log4j2 borrows from the filter of logback) and enable flexible logging rules (such as debug-level logging for only a few users)
  • Both support dynamic updates to configuration files
  • Both can be adapted to slf4j, and it should be better for logback to be adapted to slf4j, since one layer of adapter libraries is omitted
  • logback can automatically compress/delete old logs
  • logback provides HTTP access to logs
  • Log4j2 implements the "no garbage" and "low garbage" modes.Simply put, log4j2 can reuse objects (such as String) when logging, avoid instantiating new temporary objects whenever possible, reduce garbage objects generated by logging, and reduce performance degradation caused by garbage collection
  • Log4j2 and logback have their own advantages. Overall, log4j2 is a better choice if performance requirements are high.

Performance comparison

Attached is a benchmark comparing log4j2 and logback performance. This benchmark is from Apache Logging. How much water is unknown. For reference only

benchmark for synchronous file log writing:

benchmark for asynchronous log writing:

Of course, these benchmark s are measured when the log Pattern s do not contain Location information (such as the log code line number, caller information, Class name/source file name, and so on), and performance cannot be saved if Location information is output:

Log Face of Log Library

common-logging

common-logging is an open source project for apache.Also known as Jakarta Commons Logging, short for JCL.

The common-logging functionality is an API interface that provides logging functionality, and it does not provide a specific implementation of logging itself (there is, of course, a simple implementation of Simple logger within common-logging, but the functionality is weak and ignored directly), but rather dynamically binds the log implementation components to work at runtime (such as log4j, java.util.loggin).

Official address: http://commons.apache.org/proper/commons-logging/

slf4j

Fully known as Simple Logging Facade for Java, the java simple logging facade.

What, the author is Ceki Gulcu!The God wrote Log4j, Logback, and slf4j, and focused on log component development for five hundred years, always surpassing himself.

Similar to Common-Logging, slf4j is an API encapsulation provided by different log frameworks that allows access to a log implementation without modifying any configuration at deployment time.However, slf4j statically binds the real Log library at compile time.When using SLF4J, if you need to use one of the log implementations, you must select the correct set of jar packages (various bridging packages) for SLF4J.

Official address: http://www.slf4j.org/

common-logging vs slf4j

The slf4j library is similar to Apache Common-Logging.However, at compile time, he statically binds the real log libraries.This may seem cumbersome, but it's all about importing bridged jar packages.

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 performance problems caused by character splicing in logs.slf4j uses {} as a string substitute in the following form:

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

Log Library Usage Schemes

There are three basic steps to using a log solution:

  • Introducing jar packages
  • To configure
  • Use API

The second and third steps of common log solutions are basically the same, and the implementation differences are mainly in the first step, which is to use different libraries.

Log library jar package

The combination of slf4j + logback is preferred here.

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

It is strongly recommended that components (logback, log4j, java.util.logging) not be implemented directly using logs, for the reason that, as mentioned earlier, there is no flexibility in replacing log libraries.

Another scenario is when your old project uses common-logging, or you implement components directly using logs.If you modify the old code, it is too heavy and requires compatible processing.You will see a variety of approaches below.

Note: As far as I know, there is still no way to bridge slf4j to common-logging.If I'm ignorant, don't stint.

slf4j directly binds log components

  • slf4j + logback

Add a dependency to pom.xml.

logback-classic-1.0.13.jar automatically adds 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>
  • slf4j + log4j

Add a dependency to pom.xml.

slf4j-log4j12-1.7.21.jar automatically adds 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>
  • slf4j + java.util.logging

Add a dependency to pom.xml.

slf4j-jdk14-1.7.21.jar automatically adds 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>

Slf4j compatible non-slf4j log components

Before introducing the solution, 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 information output to slf4j-api, and slf4j-api to call the log components that slf4j actually depends on.This process is called bridging.The following is the official slf4j bridging strategy diagram:

As you can see from the diagram, whether you use common-logging in your old project or use log4j, java.util.logging directly, you can use the corresponding bridging jar package to solve compatibility problems.

  • slf4j is compatible with common-logging
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>jcl-over-slf4j</artifactId>
  <version>1.7.12</version>
</dependency>
  • slf4j compatible log4j
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>log4j-over-slf4j</artifactId>
    <version>1.7.12</version>
</dependency>
  • slf4j is compatible with java.util.logging
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jul-to-slf4j</artifactId>
    <version>1.7.12</version>
</dependency>
  • spring integration slf4j

The spring framework is essential for java web development.Unfortunately, the log solution Spring uses is common-logging + log4j.

So you need a bridging 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>

common-logging binding log component

  • common-logging + log4j

Add a 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>

Log Library Configuration - For Log Framework

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>

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>

logback configuration

<?xml version="1.0" encoding="UTF-8" ?>
 
<!-- logback There are five levels of validity, one for each TRACE,DEBUG,INFO,WARN,ERROR,Priorities range from low to high -->
<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">
    <!-- Develop rolling strategy based on 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>
 
    <!-- Formulate scrolling strategy based on 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">
    <!-- Develop rolling strategy based on 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>
 
    <!-- Formulate scrolling strategy based on 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">
    <!-- Develop rolling strategy based on 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>
 
    <!-- Formulate scrolling strategy based on 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">
    <!-- Develop rolling strategy based on 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>
 
    <!-- Formulate scrolling strategy based on 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">
    <!-- Develop rolling strategy based on 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>
 
    <!-- Formulate scrolling strategy based on 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">
    <!-- Develop rolling strategy based on 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>
 
    <!-- Formulate scrolling strategy based on 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">
    <!-- Develop rolling strategy based on 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>
 
    <!-- Formulate scrolling strategy based on 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 -->
  <!-- Logging of this project, graded 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 Frame 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>

log4j configuration

Complete log4j.xml reference example

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
 
<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 sets the level of output-->
    <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"/>
    <!-- Regenerate log files daily -->
    <param name="DatePattern" value="'-'yyyy-MM-dd'.log'"/>
    <!-- Regenerate log files hourly -->
    <!--<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>
 
  <!-- Appoint logger The settings of additivity Indicates 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 for-->
  <root>
    <level value="warn"/>
    <appender-ref ref="STDOUT"/>
  </root>
</log4j:configuration>

Log Library API - For Log Face

Usage of slf4j

The API for using slf4j is simple.Initialize a Logger instance using LoggerFactory and call the corresponding print level function for Logger.

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");
    }
}

common-logging usage

The common-logging usage is almost the same as slf4j, but there is a higher level of support for printing: fatal.

In addition, common-logging does not support {} substitution parameters, you can only stitch strings this way.

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");
    }
}

Selection and transformation of log Library

Recommendations for Java Log Component Selection

Slf4j has become the star player of Java Log Components, replacing JCL perfectly, and using the JCL Bridge Library is also perfectly compatible with all the class libraries that use JCL as the log facade. There is no reason why the new system no longer uses slf4j as the log API.
For logging services, log4j loses functionality to logback and log4j2, while log4j2 surpasses log4j and logback in performance.So the new system should choose between logback and log4j2. For systems with high performance requirements, log4j2 should be a priority

Better practice with log architecture

Always use Log Facade instead of specific Log Implementation

As mentioned earlier, using Log Facade can easily switch specific log implementations.Furthermore, if you rely on multiple projects and use different Log Facades, you can easily transfer to the same implementation through the Adapter.Dependent projects can be more cumbersome if they use multiple different log implementations.

Specifically, Log4j-API or SLF4j are now recommended, and JCL is not recommended.

Add only one Log Implementation dependency

There is no doubt that only one specific Log Implementation should be used in the project, and Logback or Log4j2 are recommended.If the Log Facade used in a dependent project does not support direct use of the current Log Implementation, add the appropriate bridge dependency.Specific bridging relationships are illustrated in the previous article.

Specific log implementation dependencies should be set to optional and use runtime scope

In the project, Log Implementation's dependency is strongly recommended to be set to runtime scope and optional.For example, if a project uses SLF4J as a Log Facade and then wants to use Log4j2 as an Implementation, set this when using maven to add dependencies:

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>${log4j.version}</version>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>${log4j.version}</version>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>

Set optional, dependencies will not be passed, so if you are a lib project and another project uses your lib, you will not be introduced into an unwanted Log Implementation dependency;

Scope is set to runtime to prevent developers from using classes in Log Implementation directly in their projects, not in Log Facade.

Exclude Log Impementation dependencies from dependent third-party libraries if necessary

This is a common problem. Developers of third-party libraries may not necessarily set specific log implementations or bridge dependencies to optional, and then your project inherits these dependencies - specific log implementations may not be what you want to use, for example, if they depend on Log4j and you want to use Logback, which is embarrassing.In addition, rings can easily be formed if different third-party dependencies use different bridges and Log implementations.

In this case, the recommended approach is to use exclude to exclude all of these Log implementations and bridge dependencies, leaving only the Log Facade dependencies in third-party libraries.

For example, Ali's JStorm doesn't handle this problem very well. Depending on jstorm can introduce dependencies on Logback and log4j-over-slf4j. If you want to use Log4j or other Log implementations in your own projects, you need to add excludes:

<dependency>
    <groupId>com.alibaba.jstorm</groupId>
    <artifactId>jstorm-core</artifactId>
    <version>2.1.1</version>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>log4j-over-slf4j</artifactId>
        </exclusion>
        <exclusion>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
        </exclusion>
    </exclusions>
</dependency>

Avoid paying for log s that don't output

Log libraries are flexible in setting output boundaries, so it is possible that logs in each program will not be output.Be careful not to pay extra at this time.

First look at two problematic writings:

logger.debug("start process request, url: " + url);
logger.debug("receive request: {}", toJson(request));

The first one does string splicing directly, so it does a string join even if the log level is higher than debug; the second one avoids unnecessary string splicing overhead by using lazy evaluation in SLF4J/Log4j2, but the toJson() function is called and overhead.

The recommended wording is as follows:

logger.debug("start process request, url:{}", url); // SLF4J/LOG4J2
logger.debug("receive request: {}", () -> toJson(request)); // LOG4J2
logger.debug(() -> "receive request: " + toJson(request)); // LOG4J2
if (logger.isDebugEnabled()) { // SLF4J/LOG4J2
    logger.debug("receive request: " + toJson(request)); 
}

Do not use fields such as line number, function name, etc. in log format

The reason is that in order to get the function name or line number where the statement is located, the implementation of the log library is to get the current stacktrace, then analyze and extract this information, which is expensive to obtain stacktrace.If there is a lot of log output, it will consume a lot of CPU.It is recommended that these fields not be output in the log without special needs.

Finally, don't output strange characters in the log!

Some developers prefix their log statements with striking prefixes for easy viewing, such as:

logger.debug("========================start process request=============");

Although it's convenient for you, if everyone does this, you won't be able to see the log output!The right way to do this is to use grep to view only the logs that you care about.

Suggestions for improving the existing system log architecture

If the existing system uses JCL as the log facade and is indeed facing the problems caused by JCL's lassLoader mechanism, it is entirely possible to introduce slf4j and bridge the log output from JCL api to slf4j through the bridging library, then adapt to the existing log output service (such as log4j) through the adapter library, as shown below:

This does not require any code-level modifications to solve problems with JCL's lassLoader, but it is not possible to enjoy the benefits of slf4j's api, such as log templates.However, the new functions developed on the current system can use the API of slf4j, and the old code can be modified in batches.

If the existing system uses JCL as the log facade, it is also a headache that JCL does not support new log services such as logback and log4j2, or can replace JCL with slf4j through the bridging library, but it also cannot directly enjoy the advantages of the slf4j api.

If you want to use the api of slf4j, you will have to make code modifications, which can also be done step by step in the way mentioned in 1.

If your system is facing log4j performance issues, you can bridge log4j to log4j2 using the log4j-1.2-api bridge library provided by Apache Logging.This allows you to use the advanced performance of log4j2 as quickly as possible, but the absence of slf4j in the component can have an impact on the flexibility of subsequent log architecture modifications.Another option is to bridge log4j to slf4j, then use the slf4j to log4j2 adapter library.This is a bit cumbersome, but you can gradually standardize the log output in your system to use the slf4j API to lay the foundation for later work.

 

Nineteen original articles were published. 6. Visits 6566
Private letter follow

Tags: log4j Apache Java Spring

Posted on Mon, 13 Jan 2020 20:38:51 -0500 by Basdub