系统复习Java日志体系

测试学习所需要的所有可能用到的依赖 

目录

测试学习所需要的所有可能用到的依赖 

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

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

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

1.绑定 java.util.logging.Logger

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

3.绑定 log4j2 

4.绑定 logback

5.绑定 commons-logging

6.多个绑定器出现警告

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

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

3.需要注意的地方

        1.如果需要将 java.util.logging.Logger桥接到 slf4j 需要在项目启动的时候执行以下代码 

具体说明在 官网 Log4j Bridge

​编辑 SLF4JBridgeHandler 的介绍官网文档 SLF4JBridgeHandler (SLF4J javadoc)

        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 打印),这里有两种情况

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


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

分别导入几种日志的 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 打印) 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值