系统复习Java日志体系

news2025/1/13 6:29:33

一,我们采用硬编码体验一下几个使用比较多的日志

分别导入几种日志的 jar 包

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

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.fll</groupId>
  <artifactId>log-system-study</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>log-system-study</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <dependencies>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>

    <!--  ===  log4j1 相关jar包  ====  -->

    <!-- 单独导入 log4j1 -->
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>

    <!--  ===  log4j2 相关jar包  ===  -->

    <!-- 单独导入 log4j2 -->
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-api</artifactId>
      <version>2.13.3</version>
    </dependency>

    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
      <version>2.13.3</version>
    </dependency>

    <!--  === logback 相关jar包  ====  -->

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

    <!--  
        只导入logback-core logback使用不了,创建logback日志对象的
        类和方法都不对外开放,只能通过logback-classic中的相关api
        创建 logback 的相关日志对象
    -->
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-core</artifactId>
      <version>1.2.4</version>
    </dependency>

  </dependencies>

  <build>

  </build>

</project>
package com.fll;


import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.util.ContextInitializer;
import ch.qos.logback.core.joran.spi.JoranException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.spi.ExtendedLogger;
import org.junit.Test;

import java.net.URL;
import java.util.logging.Logger;


public class LoggerTest {

    /**
     * java自带的日志工具 
     * java.util.logging.Logger
     */
    @Test
    public void julLogger() {
        Logger logger = Logger.getLogger("julLogger");
        //System.setProperty("-Djava.util.logging.config.file", "classpath:/logging.properties");
        //logger.setLevel(Level.FINE);
        logger.info("julLogger Hello World!");
//运行结果  显示红色
//七月 30, 2024 10:51:38 上午 com.fll.LoggerTest julLogger
//信息: julLogger Hello World!
    }

    /**
     * log4j1
     */
    @Test
    public void log4JLogger(){
        org.apache.log4j.Logger log4JLogger = org.apache.log4j.Logger.getLogger("log4JLogger");
        log4JLogger.info("log4J_1_Logger Hello World!");
//运行结果
//[log4j_1] 2024-07-30 10:55:29,017 INFO [log4J_1_Logger] - log4J_1_Logger Hello World!
    }

    /**
     * log4j2
     */
    @Test
    public void log4J_2_Logger(){
        ExtendedLogger logger= LogManager.getContext().getLogger("log4J_2_Logger");
        logger.info("log4J_2_Logger Hello World!");
//运行结果
//[log4j_2] line=41 10:55:50.050 [main]INFO  - log4J_2_Logger Hello World!
    }

    /**
     * logback
     */
    @Test
    public void logBackLogger(){

        //LoggerContext 和 ContextInitializer 都是logback-classic中的类
        // Here we create context
        LoggerContext loggerContext = new LoggerContext();

        //创建一个LoggerContext的初始化器,该初始化器可以通过configureByResource()方法,
        //使用指定的配置(xml,或者 properties)对LoggerContext实例进行初始化
        // Initializer is used to enrich context with details
        ContextInitializer contextInitializer = new ContextInitializer(loggerContext);
        try {
            //获取我们的配置文件
            // Get a configuration file from classpath
            URL configurationUrl = Thread.currentThread()
                        .getContextClassLoader().getResource("logback.xml");

            if (configurationUrl == null) {
                throw new IllegalStateException
                          ("Unable to find custom logback configuration file");
            }

            // 解析配置文件,将解析到的配置设置给 LoggerContext
            // Ask context initializer to load configuration into context
            contextInitializer.configureByResource(configurationUrl);

            // Here we get logger from context
            ch.qos.logback.classic.Logger logger = 
                          loggerContext.getLogger("logBackLogger");

            logger.info("logBackLogger Hello World!");
//运行结果
//[logback] 2024-07-30 10:57:03 [main] INFO  logBackLogger - logBackLogger Hello World!

        } catch (JoranException e) {
            throw new RuntimeException("Unable to configure logger", e);
        }


    }

    @Test
    public void logBackLogger1(){

        //LoggerContext 和 ContextInitializer 都是logback-classic中的类
        // Here we create context
        LoggerContext loggerContext = new LoggerContext();
        // Initializer is used to enrich context with details
        ContextInitializer contextInitializer = new ContextInitializer(loggerContext);
        try {

            //autoConfig 方法中 会先调用 findURLOfDefaultConfigurationFile()
            //获取一个默认的配置文件,

            // 然后调用configureByResource 对 loggerContext进行初始化
            // findURLOfDefaultConfigurationFile 逻辑是先获取环境变量 
            //logback.configurationFile

            // 配置的文件,找不到再找 logback-test.xml ,logback.groovy , logback.xml
            contextInitializer.autoConfig();

            // Here we get logger from context
            ch.qos.logback.classic.Logger logger =     
                         loggerContext.getLogger("logBackLogger");
            logger.info("logBackLogger Hello World!");

//运行结果
//[logback] 2024-07-30 10:57:51 [main] INFO  logBackLogger - logBackLogger Hello World!
        } catch (JoranException e) {
            throw new RuntimeException("Unable to configure logger", e);
        }


    }

}

 log4j的配置文件  log4j.properties

log4j.rootLogger=info, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[log4j_1] %d %p [%c] - %m%n

 log4j2的配置文件  log4j2.xml

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

	<appenders>
	
		<Console name="Console" target="SYSTEM_OUT">
			<PatternLayout pattern="[log4j_2] line=%L %d{HH:mm:ss.sss} [%t]%highlight{%-5level} - %msg%n"/>
		</Console>
		
		<!-- <Console name="Console" target="SYSTEM_0UT"> -->
		<!-- <PatternLayout pattern="file=%c line=%L %dHH:mm:ss.sss}[%t]%highlight{%-5level} %logger{36}-%msg%n"/> -->
		<!-- </Console> -->
	</appenders>
	
	<loggers>
		<root level="debug">
			<appender-ref ref="Console"/>
		</root>
	</loggers>
	
</configuration>

java.util.logging.Logger 配置文件 logging.properties

.level=WARNING

handlers=java.util.logging.ConsoleHandler

java.util.logging.ConsoleHandler.level=WARNING
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter

logback的配置文件 logback.xml

<configuration>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>[logback] %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="debug">
        <appender-ref ref="STDOUT" />
    </root>

</configuration>

二、采用 commons-logging 动态选用日志

官网commons-logging文档  Apache Commons Logging – Overview

<!--  ===  commons-logging 相关jar包  ==  -->
<dependency>
  <groupId>commons-logging</groupId>
  <artifactId>commons-logging</artifactId>
  <version>1.2</version>
</dependency>

先只导入commons-logging.jar不导入其他的日志包

package com.fll;

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

public class CommonLoggingTest {

    @Test
    public void commonLogging(){
        Log logger = LogFactory.getLog("commonLogging");
        logger.info("commonLogging Hello World!");
    }

}

 可以看到运行结果:日志打印采用的是 
class org.apache.commons.logging.impl.Jdk14Logger
内部封装的是 java.util.logging.Logger

除了commons-logging之外再导入 log4j1的 jar 包

<!--  ===  commons-logging 相关jar包  ==  -->
<dependency>
  <groupId>commons-logging</groupId>
  <artifactId>commons-logging</artifactId>
  <version>1.2</version>
</dependency>

<!-- 单独导入 log4j1 -->
<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>

  可以看到运行结果:日志打印采用的是 
class org.apache.commons.logging.impl.Log4JLogger

public static Log getLog(String name) throws LogConfigurationException {
	return getFactory().getInstance(name);
}

 可以看到 LogFactory 只有一个实现类 
org.apache.commons.logging.impl.LogFactoryImpl

org.apache.commons.logging.impl.LogFactoryImpl#getInstance(java.lang.String)

public Log getInstance(String name) throws LogConfigurationException {
	Log instance = (Log) instances.get(name);
	if (instance == null) {
		instance = newInstance(name);
		instances.put(name, instance);
	}
	return instance;
}


protected Log newInstance(String name) throws LogConfigurationException {
	Log instance;
	try {
		if (logConstructor == null) {
            // 关键的方法,根据环境和配置获取要实例化的日志对象
			instance = discoverLogImplementation(name);
		}
		else {
			Object params[] = { name };
			instance = (Log) logConstructor.newInstance(params);
		}

		if (logMethod != null) {
			Object params[] = { this };
			logMethod.invoke(instance, params);
		}

		return instance;

	//省略不重要代码
}


/**
 * The names of classes that will be tried (in order) as logging
 * adapters. Each class is expected to implement the Log interface,
 * and to throw NoClassDefFound or ExceptionInInitializerError when
 * loaded if the underlying logging library is not available. Any
 * other error indicates that the underlying logging library is available
 * but broken/unusable for some reason.
 */
private static final String[] classesToDiscover = {
		LOGGING_IMPL_LOG4J_LOGGER,
		"org.apache.commons.logging.impl.Jdk14Logger",
		"org.apache.commons.logging.impl.Jdk13LumberjackLogger",
		"org.apache.commons.logging.impl.SimpleLog"
};


/**
* 根据环境和配置获取要实例化的日志对象
*
*/
private Log discoverLogImplementation(String logCategory)
	throws LogConfigurationException {
	
	if (isDiagnosticsEnabled()) {
		logDiagnostic("Discovering a Log implementation...");
	}

	initConfiguration();

	Log result = null;

//先看用户有没有指定的 Log 的实现
//LOG_PROPERTY = "org.apache.commons.logging.Log";
//LOG_PROPERTY_OLD = "org.apache.commons.logging.log";
//从attributes中获取 
//先从 String specifiedClass = (String) getAttribute(LOG_PROPERTY);
//再从 specifiedClass = (String) getAttribute(LOG_PROPERTY_OLD);

//从环境变量中获取
//先找 specifiedClass = getSystemProperty(LOG_PROPERTY, null); 
//再找 specifiedClass = getSystemProperty(LOG_PROPERTY_OLD, null);

	// See if the user specified the Log implementation to use
	String specifiedLogClassName = findUserSpecifiedLogClassName();

    //如果找到用户配置的的 日志实现
	if (specifiedLogClassName != null) {
		if (isDiagnosticsEnabled()) {
			logDiagnostic("Attempting to load user-specified log 
                       class '" + specifiedLogClassName + "'...");
		}

        //使用用户 配置的日志实现
		result = createLogFromClass(specifiedLogClassName,
									logCategory,true);

        //如果按照用户配置的创建日志对象失败
		if (result == null) {
			StringBuffer messageBuffer =  new StringBuffer
                                    ("User-specified log class '");

			messageBuffer.append(specifiedLogClassName);
			messageBuffer.append("' cannot be found or is not useable.");

			// Mistyping or misspelling names is a common fault.
			// Construct a good error message, if we can
			informUponSimilarName(messageBuffer, 
                specifiedLogClassName, LOGGING_IMPL_LOG4J_LOGGER);
			informUponSimilarName(messageBuffer, 
                specifiedLogClassName, LOGGING_IMPL_JDK14_LOGGER);
			informUponSimilarName(messageBuffer, 
                specifiedLogClassName, LOGGING_IMPL_LUMBERJACK_LOGGER);
			informUponSimilarName(messageBuffer, 
                specifiedLogClassName, LOGGING_IMPL_SIMPLE_LOGGER);

			throw new LogConfigurationException(messageBuffer.toString());
		}

        //如果按照用户配置的创建日志对象成功,直接返回创建的对象
		return result;
	}

	
	if (isDiagnosticsEnabled()) {
		logDiagnostic(
			"No user-specified Log implementation; performing discovery" +
            " using the standard supported logging implementations...");
	}
	
    // 如果用户没有配置任何日志实现
    // 依次遍历 classesToDiscover 并尝试在classpath中加载每一个日志实现
    // 如果哪个加载成功就返回哪一个,后面的不在尝试
    // 所以按照顺序是
    // 先加载 = "org.apache.commons.logging.impl.Log4JLogger";
    // 再加载 org.apache.commons.logging.impl.Jdk14Logger
	for(int i=0; i<classesToDiscover.length && result == null; ++i) {
		result = createLogFromClass(classesToDiscover[i], logCategory, true);
	}

    //如果从classpath没有加载到任何日志实现 就报错
	if (result == null) {
		throw new LogConfigurationException
					("No suitable Log implementation");
	}

	return result;
}
private static final String[] classesToDiscover = {
		"org.apache.commons.logging.impl.Log4JLogger",
		"org.apache.commons.logging.impl.Jdk14Logger",
		"org.apache.commons.logging.impl.Jdk13LumberjackLogger",
		"org.apache.commons.logging.impl.SimpleLog"
};


这里是写死的,
优先加载 org.apache.commons.logging.impl.Log4JLogger
然后加载 org.apache.commons.logging.impl.Jdk14Logger 

 

org.apache.commons.logging.impl.Log4JLogger是对org.apache.log4j.Logger简单装饰
org.apache.commons.logging.impl.Jdk14Logger是对java.util.logging.Logger简单装饰

可以看到 commons-logging 中默认实现的日志我们常用的只有 org.apache.log4j.Logger
和 java.util.logging.Logger 如果想用其他的一些流行的日志还得自己去实现该日志的装饰
类,并且还得通过 attributes 或者 环境变量进行 配置才能使用,要不就得下载源码,自己在
数组中加入要使用的日志实现。这样就比较麻烦。所以使用commons-logging的人越来越少了,
还有一个原因是 自从 1.2 版本之后,commons-logging 就不再更新了

三、slf4j 通过动态绑定灵活调整要使用的日志

这里是官网SLF4J的文档       SLF4J Manual

package com.fll;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Slf4jLogBinderTest {

    @Test
    public void slf4jLogBinder(){
        Logger logger = LoggerFactory.getLogger( "a");
        logger.info("slf4jLogBinder Hello World!");
    }

}

1.绑定 java.util.logging.Logger

    <!--  =====  java.util.logging.Logger 绑定器 =======  -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-jdk14</artifactId>
      <version>2.0.13</version>
    </dependency>

运行结果:

 说明绑定 java.util.logging.Logger 成功了

这里需要注意,导入绑定包 slf4j-jdk14 的时候,它会通过maven依赖的传递性,把相关的jar

都下载好,可以看到这里把需要的 slf4j-api 的包也下载好了

 这个时候我们如果自己再单独导入 slf4j-api 的包,就有可能会因为版本问题出现冲突,导致报错

2.绑定 log4j1 ,代码还是上面的,这里改一下依赖

    <!--  =======  单独导入 slf4j 相关jar包  =====  -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.32</version>
    </dependency>

    <!--  === log4j1 相关jar包  ======  -->

    <!-- 单独导入 log4j1 -->
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>


    <!--  === log4j1 绑定包  ======  -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.30</version>
    </dependency>

 这里绑定包也自动把需要的 slf4j-api 和 log4j 的jar都下载好了,和我们自己单独导入的包版本一样

所以没有报错正常执行

[log4j_1] 2024-07-31 11:23:14,001 INFO [a] - slf4jLogBinder Hello World!

说明绑定 log4j1 成功了

我们最好也别自己再单独导入slf4j-api 和 log4j 的 jar 包了,也有可能版本冲突

 把自己单独导入的包删除以后,正常执行了

3.绑定 log4j2 

    <!--  log4j2  绑定器   -->
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-slf4j-impl</artifactId>
      <version>2.13.3</version>
    </dependency>

 执行结果:

[log4j_2] line=12 13:17:39.039 [main]INFO  - slf4jLogBinder Hello World!

绑定成功了,和之前一样,自己单独导入jar包的话也要注意版本冲突问题

4.绑定 logback

<!-- logback 绑定包 -->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.4</version>
</dependency>

运行结果:

[logback] 2024-07-31 13:29:29 [main] INFO  a - slf4jLogBinder Hello World!

5.绑定 commons-logging

这里和之前的绑定稍微有点区别,之前是 slf4j 直接绑定具体的日志实现,比如直接绑定 
java.util.logging.Logger , log4j1 , log4j2 , logback. 这里是 slf4j 绑定到 commons-logging,然后
commons-logging 根据类路径下具体情况是选择java.util.logging.Logger 还是 log4j1

先试一下类路径下没有 log4j1 的 jar 包

    <!-- commons-logging 绑定包 -->
    <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-jcl -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-jcl</artifactId>
      <version>1.7.36</version>
    </dependency>

 运行结果:

 再加上 log4j1 的 jar 包
 

<!-- commons-logging 绑定包 -->
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-jcl -->
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-jcl</artifactId>
  <version>1.7.36</version>
</dependency>

<!-- 单独导入 log4j1 -->
<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>

运行结果:

[log4j_1] 2024-07-31 14:17:09,116 INFO [a] - slf4jLogBinder Hello World!

 结果确实是  commons-logging 选择了 log4j1 的日志实现

6.多个绑定器出现警告

如果类路径下出现一个以上的绑定器,并且使用的 slf4j 版本低于 2.0 版本,那么就会出现一个警告

 可以看到虽然成功打印日志,但是有个红色的警告

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/D:/local_repository/org/slf4j/slf4j-log4j12/1.7.30/slf4j-log4j12-1.7.30.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/D:/local_repository/org/apache/logging/log4j/log4j-slf4j-impl/2.13.3/log4j-slf4j-impl-2.13.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]

[log4j_1] 2024-07-31 14:46:47,308 INFO [a] - slf4jLogBinder Hello World!

 要解决这种问题有几种办法
        1.使用高版本 slf4j 不建议,治标不治本,掩耳盗铃
        2.删掉不适用的那个绑定器
        3.如果代码中使用的多种日志硬编码,可以将其他的日志实现桥接到 slf4j ,将 slf4j 绑定到一种我们选定的日志实现

四、将具体日志实现桥接到 slf4j ,统一日志

官网文档   Log4j Bridge

将除了 slf4j 绑定的 日志实现以外,其他的日志实现桥接到 slf4j ,这种情况适用于对旧系统的日志进行改造。我们可以选定一个要使用的日志实现,将 slf4j 绑定到要使用的日志实现。然后把之前使用的日志桥接到 slf4j ,这样就实现了整个系统都统一为 slf4j 绑定的那个日志实现。

举个例子,旧系统之前由于人员复杂,同一个系统使用了不同的日志实现,而且还是采用硬编码的方式使用 log4j1 和 log4j2 和 jul  和 jcl 。现在需要将系统所有的日志统一采用 logback。我们就可以将 slf4j 绑定 logback。然后把之前使用的 log4j1 和 log4j2 和 jul  和 jcl 桥接到 slf4j 上,这样所有的日志就都统一为 logback 了,而且之前的代码也不用改变,只需要新写的日志使用 slf4j 就行了。

1. 以下是测试桥接时需要用到的依赖

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

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.fll</groupId>
  <artifactId>log-system-study</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>log-system-study</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <dependencies>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
	
	<!--  =====  java.util.logging.Logger 绑定器 =======  -->
	<dependency> 
	  <groupId>org.slf4j</groupId>
	  <artifactId>slf4j-jdk14</artifactId>
	  <version>2.0.13</version>
	</dependency>
	
	<!--  =====  java.util.logging.Logger 桥接器 =======  -->
	<!-- https://mvnrepository.com/artifact/org.slf4j/jul-to-slf4j -->
	<dependency>
		<groupId>org.slf4j</groupId>
		<artifactId>jul-to-slf4j</artifactId>
		<version>1.7.36</version>
	</dependency>

	

    <!--  ====  log4j 相关jar包  =====  -->

    <!-- 单独导入 log4j1 -->
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>


    <!-- log4j1 2.0.13 绑定器 -->
<!--    <dependency>-->
<!--      <groupId>org.slf4j</groupId>-->
<!--      <artifactId>slf4j-log4j12</artifactId>-->
<!--      <version>2.0.13</version>-->
<!--    </dependency>-->

    <!-- log4j1 1.7.30 绑定器 -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.30</version>
    </dependency>


    <!-- log4j-over-slf4j桥按器 -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>log4j-over-slf4j</artifactId>
      <version>1.7.36</version>
    </dependency>




    <!--  ====  log4j2 相关jar包  ======  -->

    <!-- 单独导入 log4j2 -->
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
      <version>2.13.3</version>
    </dependency>


    <!--  log4j2  绑定器 2.23.1  -->
    <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl -->
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-slf4j-impl</artifactId>
      <version>2.23.1</version>
    </dependency>

    <!-- log4j2桥按器 2.20.0 -->
    <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-to-slf4j -->
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-to-slf4j</artifactId>
      <version>2.20.0</version>
    </dependency>


    <!--  ====  logback 相关jar包  ====  -->

    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-core</artifactId>
      <version>1.2.4</version>
    </dependency>

    <!--  logback 绑定器  -->
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.2.4</version>
    </dependency>



    <!--  =====  commons-logging 相关jar包  =======  -->
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.2</version>
    </dependency>
	
	    <!-- commons-logging 绑定包 -->
    <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-jcl -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-jcl</artifactId>
      <version>1.7.36</version>
    </dependency>

    <!-- commons-logging 桥接器 -->
    <!-- https://mvnrepository.com/artifact/org.slf4j/jcl-over-slf4j -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>jcl-over-slf4j</artifactId>
      <version>1.7.36</version>
    </dependency>



    <!--  =======  单独导入 slf4j 相关jar包  ======  -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.36</version>
    </dependency>



  </dependencies>

  <build>

  </build>

</project>

2.以下时测试桥接所用的代码

@Test
    public void allBridgeLog(){

        //java.util.logging.Logger  日志打印
        SLF4JBridgeHandler.removeHandlersForRootLogger();
        SLF4JBridgeHandler.install();
        Logger julLogger = Logger.getLogger("julLogger");
        //System.setProperty("-Djava.util.logging.config.file", "classpath:/logging.properties");
        //logger.setLevel(Level.FINE);
        julLogger.info("julLogger Hello World!");

        // log4j1 日志打印
        org.apache.log4j.Logger log4J_1_Logger = org.apache.log4j.Logger.getLogger("log4J_1_Logger");
        log4J_1_Logger.info("log4J_1_Logger Hello World!");

        // log4j2 日志打印
        ExtendedLogger log4J_2_Logger= LogManager.getContext().getLogger("log4J_2_Logger");
        log4J_2_Logger.info("log4J_2_Logger Hello World!");

        // commons-logging 日志打印
        Log commonLogging = LogFactory.getLog("commonLogging");
        //System.out.println(logger.getClass());
        commonLogging.info("commonLogging Hello World!");

        // slf4j 日志打印
        org.slf4j.Logger slf4jLogBinder = LoggerFactory.getLogger( "slf4jLogBinder");
        slf4jLogBinder.info("slf4jLogBinder Hello World!");
    }

3.需要注意的地方

        1.如果需要将 java.util.logging.Logger桥接到 slf4j 需要在项目启动的时候执行以下代码 
具体说明在 官网 Log4j Bridge
 SLF4JBridgeHandler 的介绍官网文档 SLF4JBridgeHandler (SLF4J javadoc)

文档关键说明:

        SLF4JBridgeHandler.removeHandlersForRootLogger();
        SLF4JBridgeHandler.install();
        2.如果需要将 log4j1 桥接到 slf4j 需要把类路径下 log4j1 其他 jar 包删除掉,因为 log4j1 的桥接包内对 log4j1 的api进行了实现,把其他的 log4j1 jar删除,就会使用 桥接包内的,这样才可以

         3.如果需要将 commons-logging 桥接到 slf4j 需要把类路径下 commons-logging 其他 jar 包删除掉

         4.同一种日志是实现的 绑定包 和 桥接包 不能同时使用
         5.具体的桥接方案测试
                 1. slf4j 绑定 java.util.logging.Logger , 其他的日志实现桥接到 slf4j ,这样运行结果应该是所有的日志都按照绑定那个日志打印(也就是所有的都按照 jul 打印),具体的依赖如下:

 运行上面的桥接测试代码

          2. slf4j 绑定 log4j1 , 其他的日志实现桥接到 slf4j ,这样运行结果应该是所有的日志都按照绑定那个日志打印(也就是所有的都按照 log4j1 打印),具体的依赖如下:

 

 

          3. slf4j 绑定 log4j2 , 其他的日志实现桥接到 slf4j ,这样运行结果应该是所有的日志都按照绑定那个日志打印(也就是所有的都按照 log4j2 打印),具体的依赖如下:

         

 

          4. slf4j 绑定 jcl , 其他的日志实现桥接到 slf4j ,这样运行结果应该是所有的日志都按照绑定那个日志打印(也就是所有的都按照 jcl 打印),这里有两种情况

        第一种是 当前类路径 不存在 log4j1,这样 jcl 加载的是 java.util.logging.Logger, 按照 jcl 打印其实就是按照 java.util.logging.Logger 打印。

        

@Test
    public void allBridgeLog(){

        //java.util.logging.Logger  日志打印
        //SLF4JBridgeHandler.removeHandlersForRootLogger();
        //SLF4JBridgeHandler.install();
        Logger julLogger = Logger.getLogger("julLogger");
        //System.setProperty("-Djava.util.logging.config.file", "classpath:/logging.properties");
        //logger.setLevel(Level.FINE);
        julLogger.info("julLogger Hello World!");

        // log4j1 日志打印  没有导入log4j1的jar包,这里先注释
//        org.apache.log4j.Logger log4J_1_Logger = org.apache.log4j.Logger.getLogger("log4J_1_Logger");
//        log4J_1_Logger.info("log4J_1_Logger Hello World!");

        // log4j2 日志打印
        ExtendedLogger log4J_2_Logger= LogManager.getContext().getLogger("log4J_2_Logger");
        log4J_2_Logger.info("log4J_2_Logger Hello World!");

        // commons-logging 日志打印
        Log commonLogging = LogFactory.getLog("commonLogging");
        //System.out.println(logger.getClass());
        commonLogging.info("commonLogging Hello World!");

        // slf4j 日志打印
        org.slf4j.Logger slf4jLogBinder = LoggerFactory.getLogger( "slf4jLogBinder");
        slf4jLogBinder.info("slf4jLogBinder Hello World!");
    }

          第一种是 当前类路径存在 log4j1,这样 jcl 加载的是 log4j1, 按照 jcl 打印其实就是按照 log4j1 打印

 

         我在测试的过程中发现一个问题,就是当导入 jcl 的 绑定包 slf4j-jcl 之后不能再导入 jul 的桥接包 jul-to-slf4j , 和 log4j1 的桥接包 log4j-over-slf4j ,否则就会内存溢出。我的猜想是 slf4j 绑定到 jcl ,jcl 又加载 log4j1,log4j1 又桥接到 slf4j。形成了循环依赖,导致内存溢出。所以 slf4j-jcl 不能和 jul-to-slf4j , log4j-over-slf4j 同时使用。这里只是我的猜想

        5. slf4j 绑定 logback , 其他的日志实现桥接到 slf4j ,这样运行结果应该是所有的日志都按照绑定那个日志打印(也就是所有的都按照 logback 打印) 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1968606.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【已解决】YOLOv8加载模型报错:super().__init__(torch._C.PyTorchFileReader(name_or_buffer))

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

C#——Json数据存储

本文使用的软件为VS2022&#xff0c;不同的软件使用上有些许差异。 C#数据存储 关于数据存储&#xff0c;一般在退出控制台之后&#xff0c;暂存的数据都会释放掉&#xff0c;有没有什么方法能够在下一次进入的时候还能加载上一次的数据呢&#xff1f;答案是有的&#xff0c;关…

利用Arcgis设置分式标注(分子分母标注)

因工作需要&#xff0c;需要设置分式标注&#xff0c;下面详细介绍下如何利用arcgis 设置分式标注&#xff0c;以下操作以供参考&#xff0c;如有疑义可提出。 一、准备工作 软件&#xff1a;arcmap 示例数据&#xff1a;行政区shp矢量图 二、操作步骤 1.添加数据 将行政区sh…

Golang | Leetcode Golang题解之第307题区域和检索-数组可修改

题目&#xff1a; 题解&#xff1a; type NumArray struct {nums, tree []int }func Constructor(nums []int) NumArray {tree : make([]int, len(nums)1)na : NumArray{nums, tree}for i, num : range nums {na.add(i1, num)}return na }func (na *NumArray) add(index, val …

MinIO安装(思路、方法、步骤(以centos8安装为例))

MinIO安装&#xff08;思路、方法、步骤&#xff08;以centos8安装为例&#xff09;&#xff09; 简介 MinIO 是一个高性能、开源的对象存储系统&#xff0c;旨在提供简单、可扩展的存储解决方案&#xff0c;特别适用于大数据、AI 和 ML 应用。 步骤记录 首先我们进入官网看…

前端使用 Konva 实现可视化设计器(19)- 连接线 - 直线、折线

本章响应小伙伴的反馈&#xff0c;除了算法自动画连接线&#xff08;仍需优化完善&#xff09;&#xff0c;实现了可以手动绘制直线、折线连接线功能。 请大家动动小手&#xff0c;给我一个免费的 Star 吧~ 大家如果发现了 Bug&#xff0c;欢迎来提 Issue 哟~ github源码 gitee…

最全个人笔记【Makefile】

1. 基本概念 1.1 make是什么 当一个项目中要编译的文件很多时&#xff0c;手工使用编译器一个个进行编译&#xff0c;很明显不具有可操作性&#xff0c;此时必须借助某些软件&#xff0c;协助我们有序地、正确地自动编译整个工程的所有该编译的文件。这样的软件被称为 工程管…

Simulink|基于粒子群算法的永磁同步电机多参数辨识

目录 主要内容 模型研究 结果一览 下载链接 主要内容 仿真程序参考文献《改进粒子群算法的永磁同步电机多参数辨识》&#xff0c;采用粒子群算法与simulink模型结合的方式&#xff0c;对永磁同步电机进行多参数辨识。程序以定子绕组电阻、d轴电感、q轴电感和永磁…

ai写作免费版工具上哪找?一文详解5大ai写作神器

面对写作难题&#xff0c;你是否曾感到力不从心&#xff1f;从创意枯竭到语法错误&#xff0c;每个写作环节都可能成为挑战。但如今&#xff0c;有了ai写作工具的帮助&#xff0c;这些难题都能迎刃而解。今天&#xff0c;就让我们一起来看看ai写作免费网页版应该怎么选吧&#…

Win11系统文件资源管理器鼠标右键卡顿解决方法

引用链接&#xff1a; Windows 11文件资源管理器崩溃怎么解决&#xff1f;看看这7个解决办法&#xff01;

订单搜索分页查询业务

文章目录 概要整体架构流程技术细节小结 概要 订单搜索分页查询是电商、物流、零售等众多行业中的常见需求&#xff0c;主要用于管理和分析大量订单数据. 需求分析以及接口设计 技术细节 1.Controller层: 根据接口设计来写 ApiOperation("订单搜索")GetMapping(…

使用plink和git进行数据处理

首先使用git进行plink环境配置&#xff0c;显示环境安装成功&#xff0c;在此环境下可以使用plink 在基因型数据处理过程中&#xff0c;看到vcf文件后首要做的就是将vcf文件转成二进制文件&#xff0c;输入命令 plink --vcf genotype.vcf --allow-extra-chr --recode --out tes…

滑动窗口代码实现

public int minSubArrayLen(int target, int[] nums) {int len nums.length;int res len 1;//最大是len&#xff0c;如果最后res结果还是n1说明没有答案返回0int sum0;int left0;for(int right0; right<len; right){sumnums[right];while(sum>target){//不需要判断rig…

算法 —— 递推

目录 递推 数楼梯 斐波那契数列 一维数组递推 P1002 过河卒 二维数组递推 P1044 栈 卡特兰数 递推 将一个很大的任务分解成规模小一些的子任务&#xff0c;子任务分成更小的子任务&#xff0c;直到遇到初始条件&#xff0c;最后整理归纳解决大任务的思想就是递推与递…

Kafka基本概念,工作流程介绍

1、消息队列与Kafka 1.1、Kafka简介 Kafka使用scala开发&#xff0c;支持多语言客户端&#xff08;c、java、python、go等&#xff09; Kafka最先由LinkedIn公司开发&#xff0c;之后成为Apache的顶级项目。 Kafka是一个分布式的、分区化、可复制提交的日志服务 LinkedIn使…

麒麟系统查看和修改ip

查看ip ifconfig ifconfig enp0s3 192.168.1.110

ROS中自定义头文件和源文件

今天分享一下如何在ROS中进行头文件和源文件的调用。案例是实现输出"Hello World" 本文的头文件(即.h文件)放在include/${project_name}路径下, 源文件和可执行文件则在src文件夹下 第一步&#xff1a;创建工作空间&#xff0c;创建包&#xff0c;使用vscode打开工作…

mysql高级语句的查询语句

一、排序语法&#xff0c;关键字排序 升序和降序 默认的排序方式就是升序 升序&#xff1a;ASC 降序&#xff1a;DESC 配合语法&#xff1a;order by 语法 1、升序 select * from info order by name; 根据名字升序排序&#xff0c;不需要加ASC select * from info order…

kubernetes prometheus 系列| helm 部署prometheus+grafana

一、环境准备 部署k8s集群 k8sv1.26直通车搭建 安装存储类 nfs动态供给直通车 安装helm工具 https://github.com/helm/helm/releases tar -zxvf helm-v3.5.4-linux-amd64.tar.gz sudo mv linux-amd64/helm /usr/local/bin/helm chmod ox /usr/local/bin/helm helm version二、…

day05 1.多线程基本概念 2.线程支持函数(多线程编程)

1、使用两个线程完成两个文件的拷贝&#xff0c;分支线程1拷贝前一半&#xff0c;分支线程2拷贝后一半&#xff0c;主线程回收两个分支线程的资源 #include <myhead.h>int copy_file(const char *srcfile,const char *destfile,int start,int len);struct Buff {const c…