首页 > 编程语言 >spring-jcl 模块源码分析

spring-jcl 模块源码分析

时间:2023-12-23 14:44:23浏览次数:46  
标签:name spring slf4j private SLF4J 源码 static jcl 日志

目录

简介

spring-jcl是spring用于处理日志打印的模块,被spring-core所依赖:
image

jcl全称是Jakarta Commons Logging,是apache提供的日志门面(功能同slf4j),日志门面利用设计模式中的门面模式提供统一的日志接口,实际的日志实现可以任意更换。

不过jcl支持的日志实现有限,已经被淘汰了,目前日志门面一般使用slf4j

源码分析

spring-jcl是从jcl改造而来,使用它提供的工厂方法时会调用它提供的LogAdapter.creatLog()

public abstract class LogFactory {
	//外部接口 获取Log实例 这个Log接口跟commons-loggings的一样
	public static Log getLog(Class<?> clazz) {
		return getLog(clazz.getName());
	}

	public static Log getLog(String name) {
		//调用提供的适配器方法
		return LogAdapter.createLog(name);
	}

LogAdapter根据logApi属性分别使用各种实现创建Log,再后面会加载实际的实现类,通过实际的实现类进行打日志。因此,走到某个分支的时候,其实现类必须在类路径上,否则肯定就ClassNotFound了

package org.apache.commons.logging;

import java.io.Serializable;
//注意 这些类jul的
import java.util.logging.LogRecord;

//注意 这些类是log4j的
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.spi.ExtendedLogger;
import org.apache.logging.log4j.spi.LoggerContext;
//注意 这些类是slf4j的
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.spi.LocationAwareLogger;

//检测日志优先级:log4j2 -> sl4j,都没有则使用jul
/**
 * Spring's common JCL adapter behind {@link LogFactory} and {@link LogFactoryService}.
 * Detects the presence of Log4j 2.x / SLF4J, falling back to {@code java.util.logging}.
 *
 * @author Juergen Hoeller
 * @since 5.1
 */
final class LogAdapter {
	private static final String LOG4J_SPI = "org.apache.logging.log4j.spi.ExtendedLogger";
	private static final String LOG4J_SLF4J_PROVIDER = "org.apache.logging.slf4j.SLF4JProvider";
	private static final String SLF4J_SPI = "org.slf4j.spi.LocationAwareLogger";
	private static final String SLF4J_API = "org.slf4j.Logger";

	private static final LogApi logApi;

	static {
		//先判断是否有log4j2实现
		if (isPresent(LOG4J_SPI)) {
			//如果有log4j2 同时 又有log4j到slf4j的桥接器和slf4j的绑定实现 那么日志就使用slf4j
			if (isPresent(LOG4J_SLF4J_PROVIDER) && isPresent(SLF4J_SPI)) {
				//使用slf4j
				// 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 { //否则就直接使用llog4j2
				// Use Log4j 2.x directly, including location awareness support
				logApi = LogApi.LOG4J;
			}
		}
		else if (isPresent(SLF4J_SPI)) { //如果没有log4j2 有slf4j的绑定实现则使用slf4j的绑定实现
			// Full SLF4J SPI including location awareness support
			logApi = LogApi.SLF4J_LAL;
		}
		else if (isPresent(SLF4J_API)) { //如果只有slf4j的api那么使用slf4j
			// Minimal SLF4J API without location awareness support
			logApi = LogApi.SLF4J;
		}
		else { //默认情况下 使用jdk的日志实现
			// java.util.logging as default
			logApi = LogApi.JUL;
		}
	}

	private LogAdapter() {
	}


	/**
	 * Create an actual {@link Log} instance for the selected API.
	 * @param name the logger name
	 */
	public static Log createLog(String name) {
		//根据类路径上的日志门面及日志实现情况 判断使用哪种日志 这里的XxxAdapter会加载它们对应的实现类创建具体的log实例 在使用这里的Log打印日志时 实际调用实际的实现 如log4j的 下面分析下Log4jAdapter.createLog()
		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);
		}
	}

	//判断类是否在类路径上的工具方法
	private static boolean isPresent(String className) {
		try {
			Class.forName(className, false, LogAdapter.class.getClassLoader());
			return true;
		}
		catch (ClassNotFoundException ex) {
			return false;
		}
	}

	//不同日志实现的枚举
	private enum LogApi {LOG4J, SLF4J_LAL, SLF4J, JUL}

	//xxxAdapter分别创建对应的内部类实例 内部类实例分别调用对应实现的包内的api获取对应logger进行日志打印
	private static class Log4jAdapter {
		public static Log createLog(String name) {
			return new Log4jLog(name); //直接创建这个内部类的一个实例
		}
	}

	private static class Slf4jAdapter {
		//...
	}
	private static class JavaUtilAdapter {
		//...
	}

	@SuppressWarnings("serial")
	private static class Log4jLog implements Log, Serializable {
		private static final String FQCN = Log4jLog.class.getName();
		private static final LoggerContext loggerContext =
				LogManager.getContext(Log4jLog.class.getClassLoader(), false);
		private final ExtendedLogger logger;

		public Log4jLog(String name) {
			LoggerContext context = loggerContext;
			if (context == null) {
				// Circular call in early-init scenario -> static field not initialized yet
				context = LogManager.getContext(Log4jLog.class.getClassLoader(), false);
			}
			this.logger = context.getLogger(name);
		}
		//使用logger打印日志 这个logger就是log4j2的logger
		//...
	}

	@SuppressWarnings("serial")
	private static class Slf4jLog<T extends Logger> implements Log, Serializable {
		protected final String name;
		protected transient T logger;
		public Slf4jLog(T logger) {
			this.name = logger.getName();
			this.logger = logger;
		}
		// 使用slf4j打印日志
		//...
		
		protected Object readResolve() {
			return Slf4jAdapter.createLog(this.name);
		}
	}

	// 这个Slf4jLocationAwareLog继承了Slf4jLog
	@SuppressWarnings("serial")
	private static class Slf4jLocationAwareLog extends Slf4jLog<LocationAwareLogger> implements Serializable {
	//...
	}

	@SuppressWarnings("serial")
	private static class JavaUtilLog implements Log, Serializable {
		private String name;
		private transient java.util.logging.Logger logger;
		public JavaUtilLog(String name) {
			this.name = name;
			this.logger = java.util.logging.Logger.getLogger(name);
		}
		//...
	}
	//...
}

总结

spring-jcl从jcl改造而来,使用了同样的Log接口,重写了LogFactory,这里面通过根据类路径上的日志实现情况加载对应的日志实现,从而实现在使用spring框架的时候,可以动态调整应用程序中使用的日志实现。

如使用log4j2,添加log4j2的依赖即可

如果使用logback,添加logback依赖即可(slf4j的直接实现)

日志详细配置参考:https://www.cnblogs.com/bingmous/p/15786789.html

标签:name,spring,slf4j,private,SLF4J,源码,static,jcl,日志
From: https://www.cnblogs.com/bingmous/p/17923036.html

相关文章

  • 基于SpringBoot+Vue的文理医院预约挂号系统设计实现(源码+lw+部署文档+讲解等)
    (文章目录)前言:heartpulse:博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌:heartpulse:......
  • 源码安装pgsql16.1
    源码安装pgsql16.1进入postgresql官网下载源码包postgresql源码包下载地址这里以目前最新的pgsqlv16.1为例创建组:postgresgroupaddpostgres创建用户postgres并加入组postgres中useradd-gpostgrespostgres解压源码包tar-xfpostgresql-16.1.tar.gz进入解压后的目......
  • Spring的BeanDefinitionRegistryPostProcessor接口详解
    BeanDefinitionRegistryPostProcessor介绍BeanDefinitionRegistryPostProcessor它是Spring框架的一个扩展点,用于对Bean定义的注册过程进行干预和定制,例如添加,修改或删除Bean定义等。BeanDefinitionRegistryPostProcessor它继承BeanFactoryPostProcessor接口,并在其基础上扩展了......
  • Spring的Bean后置处理器之AnnotationAwareAspectJAutoProxyCreator
    本文能帮你回答以下几个问题;AnnotationAwareAspectJAutoProxyCreator后置器的作用是什么?SpringAOP自动增强bean是如何实现的。如何在spring上下文添加AnnotationAwareAspectJAutoProxyCreator?如何利用ProxyFactory硬编码实现一个bean的增强?AnnotationAwareAspectJAutoProx......
  • 短视频app源码,实现幂等设计的重要方式
    短视频app源码,实现幂等设计的重要方式一、取消重试取消重试有两种方法,第一是设置重试次数为零,第二是选择不重试的集群容错策略。<!--设置重试次数为零--><dubbo:referenceid="helloService"interface="com.java.front.dubbo.demo.provider.HelloService"retries="......
  • Spring Boot之@Autowired注解使用区别,实战演示?
    ......
  • Java Spring Boot 配置读取进阶篇-@ConfigurationProperties && @Value
    之前我们学习了在SpringBoot如何读取application.properties/application.yaml配置文件的配置信息,在上文中我们主要是简单地实践了些简单的设置,这次我们带着同样的问题,如果配置更加复杂,我们的配置读取又应该怎么处理呢。本文的学习主要基于SpringBoot自带的库来解析配置,......
  • java云HIS源码:云端部署,支持多医院、多门诊、多机构、实现医疗数据共享与交换
    系统概述云HIS是针对中小医疗机构推出的一套基于云端的云HIS服务平台,借助云his,将医院业务流程化,大大提高医院的服务效率和服务质量,为客户提供医院一体化的信息解决方案。云his系统是用计算机网络将医院内各个环节(门诊计价收费、门诊药房、住院信息、临床科室、医技、财务等)全部连......
  • Spring编程式事务控制
    目录Spring编程式事务控制代码实现测试Spring编程式事务控制实际中很少使用代码实现pom.xml<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance&......
  • Spring基于XML的事务管理器DataSourceTransactionManager
    Spring基于XML的事务管理器DataSourceTransactionManager源码代码测试pom.xml<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instan......