目录
简介
spring-jcl是spring用于处理日志打印的模块,被spring-core所依赖:
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