本文共 6633 字,大约阅读时间需要 22 分钟。
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,logfilelog4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.Target=System.outlog4j.appender.stdout.layout=org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%nlog4j.appender.logfile=org.apache.log4j.FileAppenderlog4j.appender.logfile.File=D:/test.loglog4j.appender.logfile.layout=org.apache.log4j.PatternLayoutlog4j.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的版本)org.slf4j slf4j-log4j12 1.7.28 org.slf4j slf4j-api 1.7.28 log4j log4j 1.2.17
发现可以正常使用log4j的方式打印
去掉绑定器slf4j-log4j12,则报错以上分析可知:从日志扩展角度和移植行,建议使用slf4j接口,它为每个日志的实现类都对应了一个绑定器,换日志打印时比较方便,扩展性好,不建议在代码中直接使日志具体实现类。如log4j,jul,也不建议使用apache原生jcl,已停止更新且扩展性差 还有一个问题是slf4j log4j死循环的问题: 项目加入slf4j-log4j12的jar包,又加入log4j-over-slf4j的jar包,会导致死循环究其本质的原因是: log4j代理给slf4j --> slf4j绑定到log4j,所以循环于此,所以会报错
转载地址:http://yswmf.baihongyu.com/