日志是进行软件开发必不可少的一项功能,目前流行着很多开源日志库,比如log4j、log4j2、logback、JDK Logging、commons-logging、slf4j等。

  几种日志产品的介绍

  JDK Logging:Java标准库内置的日志包 java.util.logging,以下简称jul。

  log4j:一种非常流行的日志框架,最新版本是2.x。

  commons-logging:简称jcl,它是一个第三方的日志库,由Apache创建的日志模块。特点是可以挂接不同的日志系统,可以根据配置文件指定挂接的日志系统。默认情况下,jcl自动搜索并使用log4j,如果过没找到log4j,再使用JDK Logging。

  前面介绍了Commons Logging和Log4j,它们一个负责充当日志API,一个负责实现日志底层,搭配使用非常便于开发。还有SLF4J和Logback,其实SLF4J类似于Commons Logging,也是一个日志接口,而Logback类似于Log4j,是一个日志的实现。

  为什么有了Commons Logging和Log4j,又会蹦出来SLF4J和Logback?这是因为Java有着非常悠久的开源历史,不但OpenJDK本身是开源的,而且我们用到的第三方库,几乎全部都是开源的。开源生态丰富的一个特定就是,同一个功能,可以找到若干种互相竞争的开源库。因为对Commons Logging的接口不满意,有人就搞了SLF4J。因为对Log4j的性能不满意,有人就搞了Logback。

  总结起来,几个日志产品的关系如图所示

  

  spring日志测试

  spring 4.x 及以前版本基本采用jcl,扩展机制根据用户手动依赖的日志产品进行挂接,改变spring默认日志框架,有兴趣的童鞋可以看下spring 4.x版本的源码。本文基于spring 5.x 版本。

  书写了一个小demo进行spring的默认日志测试,pom文件中只添加了spring上下文环境依赖

 <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.4.RELEASE</version>
</dependency>

  运行以下spring上下文初始化代码,

 public class SpringLogTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(BeanConfig.class);
}
}

  console打印以下日志

  十一月 27, 2019 4:38:09 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
  信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@51081592: startup date [Wed Nov 27 16:37:30 CST 2019]; root of context hierarchy

  spring日志源码分析

  当然,以上测试中,日志的输出内容并不重要,重要的是spring是如何选择logging类型,并进行打印的。

  打印上述日志的地方,是在AbstractApplicationContext类(AnnotationConfigApplicationContext继承自它)的prepareRefresh方法,该方法在AnnotationConfigApplicationContext初始化的时候执行。

 protected void prepareRefresh() {
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true); if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
}

  这段代码里面有个logger是关键,该logger是在AbstractApplicationContext中的定义的一个属性:

 protected final Log logger = LogFactory.getLog(getClass());

   那我们继续看LogFactory类,会发现该类是在org.apache.commons.logging包下面,但是仔细查看spring包结构,发现该包位于spring-jcl目录下(这是与4.x不同的地方,5.x系列spring开发团队改写了commons-logging的内部源码)

  在LogFactory类中,getLog方法是一个门面方法,调用了重载的getFactory方法。

 public abstract class LogFactory {
private static LogFactory.LogApi logApi; //默认的 public LogFactory() {
} public static Log getLog(Class<?> clazz) {
return getLog(clazz.getName());
}
  //根据logApi的值选择logger类型
public static Log getLog(String name) {
switch(logApi) {
case LOG4J:
return LogFactory.Log4jDelegate.createLog(name);
case SLF4J_LAL:
return LogFactory.Slf4jDelegate.createLocationAwareLog(name);
case SLF4J:
return LogFactory.Slf4jDelegate.createLog(name);
default:
return LogFactory.JavaUtilDelegate.createLog(name);
}
}
23   ......部分不太重要的代码,此处忽略
static {
       //logApi的默认值
logApi = LogFactory.LogApi.JUL;
ClassLoader cl = LogFactory.class.getClassLoader();
     //根据load的情况,动态更改logApi的值
try {
cl.loadClass("org.apache.logging.log4j.spi.ExtendedLogger");
logApi = LogFactory.LogApi.LOG4J;
} catch (ClassNotFoundException var6) {
try {
cl.loadClass("org.slf4j.spi.LocationAwareLogger");
logApi = LogFactory.LogApi.SLF4J_LAL;
} catch (ClassNotFoundException var5) {
try {
cl.loadClass("org.slf4j.Logger");
logApi = LogFactory.LogApi.SLF4J;
} catch (ClassNotFoundException var4) {
;
}
}
} }

  在重载的getLog方法中,会根据一个变量logApi的值返回不同的日志对象,logApi的值初始化为jul;

  在目前的场景下,代码会返回一个JDK Logging logger进行打印信息,也就是以上的getLog方法中的default情况。

  那么,现在问题来了,如果想改变spring的输出日志类型,如何修改?

  从上述的代码看,返回logger对象是根据logApi的值的,但是spring在此处并未提供可供用户修改logApi值的方法或者接口。细看LogFactory中有一个static的静态代码块,在该代码块执行的时候会去load相关的日志包产品,根据加载到的情况去动态的修改logApi的值,从而可以达到用户自选择logger类型的目的。

  那么我如果要改变spring的输出是log4j打印的消息,那么可以考虑采用slf4j的桥接方式,在pom中加入如下依赖

 <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.28</version>
</dependency>

  运行代码会发现此处打印的日志和之前默认的不同

  INFO - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@3e57cd70: startup date [Wed Nov 27 17:26:47 CST 2019]; root of context hierarchy

  spring 5.x 日志的基本功能和核心源码基本说明完了,可能有不太准确的地方,希望各位大神给我留言讨论交流。

    

spring日志体系浅析(spring 5.x)的更多相关文章

  1. NoSql存储日志数据之Spring+Logback+Hbase深度集成

    NoSql存储日志数据之Spring+Logback+Hbase深度集成 关键词:nosql, spring logback, logback hbase appender 技术框架:spring-d ...

  2. spring 日志

    spring日志结构

  3. spring源码浅析——IOC

    =========================================== 原文链接: spring源码浅析--IOC   转载请注明出处! ======================= ...

  4. 浅析Spring事务传播行为和隔离级别

    7个传播行为.4个隔离级别. Spring事务的传播行为和隔离级别[transaction behaviorand isolatedlevel] Spring中事务的定义: Propagation(k ...

  5. Spring Cloud体系介绍

    上图只是Spring Cloud体系的一部分,Spring Cloud共集成了19个子项目,里面都包含一个或者多个第三方的组件或者框架! Spring Cloud 工具框架 1.Spring Clou ...

  6. 【spring boot】8.spring boot的日志框架logback使用

    在继续上一篇的Debug调试之后,把spring boot的日志框架使用情况逐步蚕食. 参考:http://tengj.top/2017/04/05/springbo 开篇之前,贴上完整applica ...

  7. Spring Cloud体系实现标签路由

    如果你正在使用Spring Cloud体系,在实际使用过程中正遇到以下问题,可以阅读本文章的内容作为后续你解决这些问题的参考,文章内容不保证无错,请务必仔细思考之后再进行实践. 问题: 1,本地连上开 ...

  8. Python写的微服务如何融入Spring Cloud体系?

    前言 在今天的文章中小码哥将会给大家分享一个目前工作中遇到的一个比较有趣的案例,就是如何将Python写的微服务融入到以Java技术栈为主的Spring Cloud微服务体系中?也许有朋友会有疑问,到 ...

  9. Spring框架系列(6) - Spring IOC实现原理详解之IOC体系结构设计

    在对IoC有了初步的认知后,我们开始对IOC的实现原理进行深入理解.本文将帮助你站在设计者的角度去看IOC最顶层的结构设计.@pdai Spring框架系列(6) - Spring IOC实现原理详解 ...

随机推荐

  1. Spring Boot WebFlux 2.1.7 中文翻译文档

    1. 前言 从一开始学习 Netty 到 rxjava.Rector,再到 java8 的 CompletableFuture,就深深的为响应式编程着迷,这种区别于传统的顺序式编程,没准未来能在编程世 ...

  2. css四种基本选择器

    css选择器是什么? 要使用css对HTML页面中的元素实现一对一,一对多或者多对一的控制,这就需要用到CSS选择器. HTML页面中的元素就是通过CSS选择器进行控制的. CSS选择器:就是指定CS ...

  3. python:枚举类型

    1.什么是枚举类型? 枚举类型可以看做是一系列常量的集合,通常用于表示某些有限且固定的集合,例如月份(一年有12个月).星期(一星期有七天).季节(一年四个季节)等. 2.枚举的定义 定义枚举首先要导 ...

  4. vue学习笔记(一)入门

    前言 随着前端不断的壮大,许多公司对于前端开发者的需求也越来越多了,作为一名优秀的前端工程师,如果连vue和react都不会的话,那真是out了,为什么那么说呢?这是我在招聘网站上截的一张图,十家公司 ...

  5. 永恒之蓝复现(win7/2008)

    Kali对Windows2008/7的MS17010漏洞测试(MSF自带模块) 0x01 说明 其实这个MSF自带的exp模块还是挺让人伤脑筋的,因为它支持的OS并不是很多,也就Windows Ser ...

  6. Java加密、解密Word文档

    对一些重要文档,我们为保证其文档内容不被泄露,常需要对文件进行加密,查看文件时,需要正确输入密码才能打开文件.下面介绍了一种比较简单的方法给Word文件添加密码保护以及如何给已加密的Word文件取消密 ...

  7. 掌握git命令的正确使用姿势

    前言 最近在团队内部发起了一个小的python项目(用tkinter实现一个小工具),但是发现大家对git的使用还不太熟悉,不知道怎么同步代码.解决冲突等等.因为我觉得对测试工程师来说,git应该是必 ...

  8. dianFanEditor Web在线编辑器

    个人很喜欢kodexplorer 的在线编辑器.苦于没有加载FTP目录的功能. 索性自己改造了一下,用.NET 做了几个WEB接口,用CEF3做浏览器内核,打包了在线地址做编辑器. 即可加载本地磁盘, ...

  9. Git连接GitHub仓库详解

    [Annotation]本文将从标题八开始,因为前七个标题是关于Git的基本操作,如果对Git的基本操作不了解的话,可以点击下方链接先看一下Git怎么使用. 关于Git的详细使用 八:创建SSH Ke ...

  10. PHP更新用户微信信息的方法

    PHP更新用户微信信息的方法 大家都知道 授权登录一次 获取后 再登录就会提示已经授权登录 就没办法重新获得用户信息了 这个时候根据openid来获取了请求user/info这个获取ps:必须关注过公 ...