Spring5.x之spring-jcl

本文目的

1、Spring5.x单纯使用spring-jcl打印日志是什么输出格式(JUL)

2、Spring5.x如何让spring-jcl用log4j2输出,直接加入log4j2依赖[调皮]

3、Spring5.x加入log4j2又加了slf4j+logback,都spring-jcl的api打印日志,为什么都是log4j2输出

4、Spring5.x加入log4j2又加了slf4j+logback,分别用jcl和slf4j的api打印日志,为什么日志输出格式不统一

5、如何做到和SpringBoot一样统一日志输出格式(slf4j+桥接器)

准备

Spring版本号:5.2.19.RELEASE

知识储备:1、需要了解slf4jlog4j2logback;2、什么是slf4j的绑定器;3、什么是slf4j的桥接器

说明

该模块是对输出的日志框架进行适配,是从Apache的commons-logging改造而来

Spring更喜爱log4j(log4j2),用spring-jcl这个来表明

Spring5.x开始自己实现了日志框架的适配器在spring-jcl模块下 

开始

spring5.x现象:

1、自定义工程,加入spring-context依赖,使用spring-jcl下的LogFactory

package com.csh.spring.log;

import org.apache.commons.logging.LogFactory;
//import org.slf4j.LoggerFactory;

public class Test {

    public static void main(String[] args) {
        //spring-jcl api
        LogFactory.getLog(Test.class).info("log4j2 say hello world!!");
//        //slf4j api
//        LoggerFactory.getLogger(Test.class).info("logback say hello world!!");
    }

}

2、没有log4j2依赖,执行代码,打印如下 

3、加入log4j2依赖

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

 4、添加logback依赖和在resources中logback.xml配置

    我在logback设置了时间-线程高亮,日志级别绿色 ,log4j2是黑底白字

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

a、都是用jcl的api打印日志

package com.csh.spring.log;

import org.apache.commons.logging.LogFactory;
import org.slf4j.LoggerFactory;

public class Test {

    public static void main(String[] args) {
        //spring-jcl api
        LogFactory.getLog(Test.class).info("log4j2 say hello world!!");
        LogFactory.getLog(Test.class).error("log4j2 2 say hello world!!");
//        //slf4j api
//        LoggerFactory.getLogger(Test.class).info("logback say hello world!!");
    }

}

b、分别使用slf4和spring-jcl的japi打印日志

package com.csh.spring.log;

import org.apache.commons.logging.LogFactory;
import org.slf4j.LoggerFactory;

public class Test {

    public static void main(String[] args) {
        //spring-jcl api
        LogFactory.getLog(Test.class).info("log4j2 say hello world!!");
//        //slf4j api
        LoggerFactory.getLogger(Test.class).info("logback say hello world!!");
    }

}

 刷新依赖重新执行,发现两种日志输出格式,spring-jcl使用的是log4j2而slf4j是logback

得出问题

1、spring-jcl,单纯自己默认输出框架是JUL

2、加入logback绑定器,都用spring-jcl的api打印,为什么是用log4j2输出

3、加入logback绑定器,新加入slf4j的api打印日志输出是用logback而原来用spring-jcl还是会用log4j2输出,日志不统一,怎么解决

源码分析

问题1

spring-jcl,单纯自己默认输出框架是JUL

org.apache.commons.logging.LogFactory#getLog(java.lang.String)

public static Log getLog(String name) {
		return LogAdapter.createLog(name);
	}

org.apache.commons.logging.LogAdapter#createLog

logApi是类LogAdapter静态成员变量,类加载时静态代码块赋值,通过下面源码分析知道,首先判断是否有log4j2,若没有则判断是否有slf4j,都没有所以走了else,也就是JUL


	private static final LogApi logApi;

	static {
		//判断是否有log4j2
		if (isPresent(LOG4J_SPI)) {
			//存在log4j2判断是否引入了log4j2到slf4j的桥接器,且有Slf4j
			if (isPresent(LOG4J_SLF4J_PROVIDER) && isPresent(SLF4J_SPI)) {
				// log4j-to-slf4j bridge -> we'll rather go with the SLF4J SPI;
				// however, we still prefer Log4j over the plain SLF4J API since
				// the latter does not have location awareness support.
				logApi = LogApi.SLF4J_LAL;
			}
			else {//若是没有log4j2到slf4j的桥接器就还是使用log4j2
				// Use Log4j 2.x directly, including location awareness support
				logApi = LogApi.LOG4J;
			}
		}//使用slf4j,和下个判断都是用slf4j,但有位置感应没找到相关资料
		else if (isPresent(SLF4J_SPI)) {
			// Full SLF4J SPI including location awareness support
			logApi = LogApi.SLF4J_LAL;
		}
		else if (isPresent(SLF4J_API)) {
			// Minimal SLF4J API without location awareness support
			logApi = LogApi.SLF4J;
		}
		else {//JDK自带的
			// java.util.logging as default
			logApi = LogApi.JUL;
		}
	}

/**
	 * Create an actual {@link Log} instance for the selected API.
	 * @param name the logger name
	 */
	public static Log createLog(String name) {
		switch (logApi) {
			case LOG4J:
				return Log4jAdapter.createLog(name);
			case SLF4J_LAL:
				return Slf4jAdapter.createLocationAwareLog(name);
			case SLF4J:
				return Slf4jAdapter.createLog(name);
			default:
				// Defensively use lazy-initializing adapter class here as well since the
				// java.logging module is not present by default on JDK 9. We are requiring
				// its presence if neither Log4j nor SLF4J is available; however, in the
				// case of Log4j or SLF4J, we are trying to prevent early initialization
				// of the JavaUtilLog adapter - e.g. by a JVM in debug mode - when eagerly
				// trying to parse the bytecode for all the cases of this switch clause.
				return JavaUtilAdapter.createLog(name);
		}
	}

问题2

加入logback绑定器,都用jcl的api打印,为什么是用log4j2输出

通过问题1中分析的源码发现有了log4j2还会进入判断,isPresent(LOG4J_SLF4J_PROVIDER)必须为true才会判断是否引入了slf4j,这个要成立就需要用到log4j2到slf4j的桥接器【(开头提到的知识储备),所以加入对应桥接器即可,你会发现slf4j官网只有提到log4j而没有log4j2桥接器,这需要到log4j2官网才有提及到Log4j to SLF4J Adapter也提到绑定器和桥接不能同时存在否则会有问题】,但没有引入xxx到slf4j的桥接器所以该条件不成立,所以还是使用log4j2的实现。也就是为什么只用log4j2,由此看出来spring更偏向log4j

问题3

加入logback绑定器,新加入slf4j的api打印日志输出是用logback而原来用spring-jcl还是会用log4j2输出,日志不统一

上面问题2解答提及到的,isPresent(LOG4J_SLF4J_PROVIDER)为true时才会改变spring-jcl的日志输出框架,所以加入log4j2到slf4j的桥接器,该条件就成立了使用spring-jcl的api也能通过slf4j转到logback


        <!--log4j2 到slfj的桥接器 log4j桥接器是log4j-over-slf4j-->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-to-slf4j</artifactId>
            <version>2.17.2</version>
        </dependency>

刷新依赖重新执行,bingo!!这时两种日志输出格式统一了。

彩蛋

1、Spring4.x也一样会出现日志不统一,一样加桥接器即可。

2、桥接器不能和同一个日志框架的绑定器同时存在否则异常 log4j-slf4j-impl cannot be present with log4j-to-slf4j,绑定器会校验是否存在桥接器的某个类[org.apache.logging.slf4j.SLF4JLoggerContext] 

3、不能同时出现多个绑定器,存在多个会提示,按所有绑定器依赖的第一作为绑定器

log4j2的依赖放在前面都是log4j2输出的,logback放前面就都是logback

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值