1.jdk自带的java util logging,即jul
日志输出格式:
从源码可以看到jul的Logger是个具体的类
2.apache的commons-logging,即jcl
输出结果,没有任何配置
从表面看起来是使用的jul,实际上是什么样的,查看源码Log是一个接口,而不是具体的实现类
看LogFactory.getLog源码,实际上是有一个数组,里面放了4种日志打印方式
然后通过反射获取数组里的对象
private Log createLogFromClass(String logAdapterClassName, String logCategory, boolean affectState) throws LogConfigurationException {
if (isDiagnosticsEnabled()) {
this.logDiagnostic("Attempting to instantiate '" + logAdapterClassName + "'");
}
Object[] params = new Object[]{logCategory};
Log logAdapter = null;
Constructor constructor = null;
Class logAdapterClass = null;
ClassLoader currentCL = this.getBaseClassLoader();
while(true) {
this.logDiagnostic("Trying to load '" + logAdapterClassName + "' from classloader " + LogFactory.objectId(currentCL));
String resourceName;
try {
if (isDiagnosticsEnabled()) {
resourceName = logAdapterClassName.replace('.', '/') + ".class";
URL url;
if (currentCL != null) {
url = currentCL.getResource(resourceName);
} else {
url = ClassLoader.getSystemResource(resourceName + ".class");
}
if (url == null) {
this.logDiagnostic("Class '" + logAdapterClassName + "' [" + resourceName + "] cannot be found.");
} else {
this.logDiagnostic("Class '" + logAdapterClassName + "' was found at '" + url + "'");
}
}
Class c;
try {
c = Class.forName(logAdapterClassName, true, currentCL);
} catch (ClassNotFoundException var15) {
String msg = var15.getMessage();
this.logDiagnostic("The log adapter '" + logAdapterClassName + "' is not available via classloader " + LogFactory.objectId(currentCL) + ": " + msg.trim());
try {
c = Class.forName(logAdapterClassName);
} catch (ClassNotFoundException var14) {
msg = var14.getMessage();
this.logDiagnostic("The log adapter '" + logAdapterClassName + "' is not available via the LogFactoryImpl class classloader: " + msg.trim());
break;
}
}
constructor = c.getConstructor(this.logConstructorSignature);
Object o = constructor.newInstance(params);
if (o instanceof Log) {
logAdapterClass = c;
logAdapter = (Log)o;
break;
}
this.handleFlawedHierarchy(currentCL, c);
} catch (NoClassDefFoundError var16) {
resourceName = var16.getMessage();
this.logDiagnostic("The log adapter '" + logAdapterClassName + "' is missing dependencies when loaded via classloader " + LogFactory.objectId(currentCL) + ": " + resourceName.trim());
break;
} catch (ExceptionInInitializerError var17) {
resourceName = var17.getMessage();
this.logDiagnostic("The log adapter '" + logAdapterClassName + "' is unable to initialize itself when loaded via classloader " + LogFactory.objectId(currentCL) + ": " + resourceName.trim());
break;
} catch (LogConfigurationException var18) {
throw var18;
} catch (Throwable var19) {
LogFactory.handleThrowable(var19);
this.handleFlawedDiscovery(logAdapterClassName, currentCL, var19);
}
if (currentCL == null) {
break;
}
currentCL = this.getParentClassLoader(currentCL);
}
if (logAdapterClass != null && affectState) {
this.logClassName = logAdapterClassName;
this.logConstructor = constructor;
try {
this.logMethod = logAdapterClass.getMethod("setLogFactory", this.logMethodSignature);
this.logDiagnostic("Found method setLogFactory(LogFactory) in '" + logAdapterClassName + "'");
} catch (Throwable var13) {
LogFactory.handleThrowable(var13);
this.logMethod = null;
this.logDiagnostic("[INFO] '" + logAdapterClassName + "' from classloader " + LogFactory.objectId(currentCL) + " does not declare optional method " + "setLogFactory(LogFactory)");
}
this.logDiagnostic("Log adapter '" + logAdapterClassName + "' from classloader " + LogFactory.objectId(logAdapterClass.getClassLoader()) + " has been selected for use.");
}
return logAdapter;
}
当能找到log4j的情况,则使用log4j打印,加入log4j的jar包和log4j.properties
log4j.rootLogger=INFO,stdout,logfile
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=D:/test.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n
再输出结果,则使用的时loj4j打印
根据源码分析出:使用jcl打印日志的时,当项目中有log4j的jar包和配置的时候,优先使用log4j,没有log4j的时候使用jul(jdk14或jdk13),jdk13目前也没用了,最后是Simple Log(目前基本已不使用)
jcl的缺陷,扩展性差,只有数组里提供的四种日志类型,spring5.0已下则依赖的是apache的commons-logging
而spring5.0以上在不再使用apache原生的commons-logging,而是自己扩展了jcl,apache原生的commons-logging已停止更新不建议使用
3.spring的jcl
来源于spring的jcl的jar包
spring5以上支持的日志顺序log4j2->slf4j_lal(1.3版本及以后)->slf4j(1.3版本之前)->默认jul,当匹配不到时使用默认的jul
可以发现spring5是不支持log4j,支持log4j2的,那为什么在我们的项目中会看到可以使用log4j呢?一种情况使用的是spring5已下的版本,如果是spring5是因为我们使用的slf4j然后通过绑定器实现使用log4j打印日志。
增加slf4j的jar包slf4j-api和slf4j与log4j的的绑定器slf4j-log4j12(log4j 1.x的版本)
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.28</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.28</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
发现可以正常使用log4j的方式打印
去掉绑定器slf4j-log4j12,则报错
以上分析可知:从日志扩展角度和移植行,建议使用slf4j接口,它为每个日志的实现类都对应了一个绑定器,换日志打印时比较方便,扩展性好,不建议在代码中直接使日志具体实现类。如log4j,jul,也不建议使用apache原生jcl,已停止更新且扩展性差
还有一个问题是slf4j log4j死循环的问题:
项目加入slf4j-log4j12的jar包,又加入log4j-over-slf4j的jar包,会导致死循环
究其本质的原因是: log4j代理给slf4j --> slf4j绑定到log4j,所以循环于此,所以会报错