前言
- 上篇博客:日志体系(一)JCL + JUL + LOG4J大概介绍了一下
apache
公司开源的jcl
这么一款抽象的日志框架,我们可以借助它来随意更换框架中使用的日志框架。 - 由于
jcl
的自我抛弃,不再进行维护了。但是框架总归是要记录日志的。所以对于spring 5.0.x
框架而言,它自己提供了一个jcl
框架spring-jcl。下面我们开始认识下它
一、spring 5.0.x版本spring-jcl
1.1 jar包结构
- Log.java。此接口抽象了日志的一些行为,比如记录info级别的日志、warn级别的日志等等
public interface Log { boolean isFatalEnabled(); boolean isErrorEnabled(); boolean isWarnEnabled(); boolean isInfoEnabled(); boolean isDebugEnabled(); boolean isTraceEnabled(); void fatal(Object message); void fatal(Object message, Throwable t); void error(Object message); void error(Object message, Throwable t); void warn(Object message); void warn(Object message, Throwable t); void info(Object message); void info(Object message, Throwable t); void debug(Object message); void debug(Object message, Throwable t); void trace(Object message); void trace(Object message, Throwable t); }
- LogFactory.java 创建Log类型的日志实例的工厂,里面大致做的事情就是根据当前项目中存在哪些日志的依赖来决定初始化哪种日志. 初始化顺序如下:
// Log4j 2.x API 1. org.apache.logging.log4j.spi.ExtendedLogger // SLF4J 1.7 API 2. org.slf4j.spi.LocationAwareLogger // Try SLF4J 1.7 API 3. org.slf4j.Logger // Default jul 4. java.util.logging.Logger
- NoOpLog.java是Log接口的一个实现类,在spring-jcl中只有里面的SimpleLog.java类使用了它,但是SimpleLog.java类被spring使用
@Deprecated
注解标识了,这代表着,spring不使用它了,于是我们直接忽略NoOpLog.java和SimpleLog.java类。所以整个spring-jcl中,我们唯一要看的就是一个类LogFactory.java
1.2 spring-jcl之LogFactory类
- 本次我们不总结日志的具体打印原理,只总结spring-jcl是如何根据依赖的jar包来生产对应的log对象的。主要看LogFactory的两个地方:静态代码块和getLog方法
- LogFactory.java静态代码块
由源码可知,spring-jcl加载日志的方式和private static LogApi logApi = LogApi.JUL; static { ClassLoader cl = LogFactory.class.getClassLoader(); try { // Try Log4j 2.x API cl.loadClass("org.apache.logging.log4j.spi.ExtendedLogger"); logApi = LogApi.LOG4J; } catch (ClassNotFoundException ex1) { try { // Try SLF4J 1.7 SPI cl.loadClass("org.slf4j.spi.LocationAwareLogger"); logApi = LogApi.SLF4J_LAL; } catch (ClassNotFoundException ex2) { try { // Try SLF4J 1.7 API cl.loadClass("org.slf4j.Logger"); logApi = LogApi.SLF4J; } catch (ClassNotFoundException ex3) { // Keep java.util.logging as default } } } }
apache
开源出来的jcl
加载日志的方式思想基本一致(这就是抽象的魅力)。spring-jcl LogFactory使用当前类的类加载器根据当前classpath中存在哪种jar包最终获取到对应的Class类赋值给logApi
这个静态枚举对象(这个枚举可取:LOG4J, SLF4J_LAL, SLF4J, JUL
四个值)。若在学习spring源码时,只引入了spring-context
模块,那么此时logApi
一定等于JUL
。所以打印的是JUL的日志(上篇博客中特意提过,jul的日志的字体是红色的),如下图所示:(这要一定要注册一个bean进去,否则的话spring不会执行refresh方法,进而不会打印出日志!)
刚刚看了,spring-jcl的加载顺序为Log4j 2.x
,Try SLF4J 1.7 SPI
,SLF4J 1.7 API
。那我们现在按照源码中的提示添加Log4j 2.x
试试
1.3 切换日志源为LOG4J
- 因为在源码中有提示,需要加载的Log4j是2.x之后的版本,源码中有提供一个类:org.apache.logging.log4j.spi.ExtendedLogger。按照java包名的开发规范,这应该是
apache
开发的一个项目,于是我们去maven搜索下log4j, 找到2.12.1
版本(主要要选择groupId为****),并添加依赖
maven添加依赖如下:
于是我们启动项目,纳尼,日志不打印了, 反而打印了这么一句话:<dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.12.1</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.12.1</version> </dependency>
大致的意思就是需要一个配置文件。可以给jvm添加参数ERROR StatusLogger No Log4j 2 configuration file found. Using default configuration (logging only errors to the console), or user programmatically provided configurations. Set system property 'log4j2.debug' to show Log4j 2 internal initialization logging. See https://logging.apache.org/log4j/2.x/manual/configuration.html for instructions on how to configure Log4j 2
log4j2.debug
来显示log4j 2.x内部初始化日志,可以按照如下图所示方式配置, 这里就不演示了。
我们言归正传,添加一个名叫log4j.xml
的配置文件,并配置如下内容(这个配置的由来就是上面抛出异常指定的url, 只不过我做了修改,修改了打印日志的级别为dubug及以上):
最终的项目结构和运行结果如下:<?xml version="1.0" encoding="UTF-8"?> <Configuration status="debug"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </Console> </Appenders> <Loggers> <Root level="info"> <AppenderRef ref="Console"/> </Root> </Loggers> </Configuration>
,可能这个理由还不够足以证明打印日志的是log4j
对象, 这样,我们查看打印spring日志的方法处打断点查看(在AbstractApplicationContext
类的prepareRefresh方法下), 如下图:
综上所述,我们已经将spring5.0.x自带的jul日志切换成log4j了。切换其他的日志源也同理。
二、总结
- 所有日志的对象都是从依赖的jar中生成的,所以关于日志的一些配置,比如支持打印的日志登记、打印的格式都是由对应的日志来决定的。比如上说的log4j日志,使用的就是自己的log4j.xml配置文件来指定要打印的日志级别
- spring为了集成日志框架,自己开发了一个spring版本的jcl
- I am a slow walker, but I never walk backwards