Spring Boot 手摸手系列-日志框架

/ 2020-08-01 / 1307人浏览 / 0人评论

参与项目的同学大多都知道,日志是多么的重要。日志能帮我们定位问题,也可以通过日志去统计分析数据。而我们手动去搭建日志框架稍显繁琐,所以springboot 都为我们准备好了当下成熟的日志框架。

1、当下日志框架的介绍

日志-抽象层 日志-实现层
JCL、SLF4J、jboss-logging jul、log4j、log4j2、logback

目前常用的日志框架组合就是 SLF4J(抽象层) + logback (实现层);而springboot 默认使用的日志框的也就是 SLF4J + logback;
使用 SLF4J + logback 的组合其实是当下的最优解, SLF4J , log4j , logback 它们其实同一个人的杰作,相对兼容性会好很多;目前像 log4j 是存在性能问题, jul 、 log4j2 对于 SLF4J 兼容相对弱一些。

2、SLF4J 介绍
slf4j 已知是抽象层 ,它是日志实现层的适配器,通过slf4j你是可以调用所有日志实现层的,slf4j 官网出图,如下:

我们可很清晰的看到 slf4j 是如何实现对于实现层的调用的。

  • 对于 logback 可以直接调用
  • 对于log4j,由于 log4j 出土时间太久远,只能通过适配调用
  • 对于jul 也是同样的,通过适配调用
  • 对于log4j2,官网都未直接提供

对于 slf4j 如何使用,官网也给了一个HelloWorld demo,如下:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// 咋样,是不是有熟悉感,是不是经常用
public class HelloWorld {
  public static void main(String[] args) {
    Logger logger = LoggerFactory.getLogger(HelloWorld.class);
    logger.info("Hello World");
  }
}

3、 SLF4J 如何在项目中使用同一的实现层
因为java日志历史遗留问题,导致日志的实现层是没有一个统一的标准的,所以大家使用的spring、hibernate、mybatis 等框架使用的不是一套日志实现层,如 spring 使用的commons-logging(新版本的spring使用的spring-jcl,原理还是改了 commons-logging 的实现,改成 SLF4J )、 hibernate 使用的是jboss-logging。那么我们怎么使用SLF4J把整个项目使用一个实现层呢?
SLF4J 给我们出了一个狸猫换太子的方案,看下图:

SLF4J 很鸡贼啊,他偷偷的将 jul、log4j、log4j2 这些实现层源码都改掉了,出了对应的包,当项目中框架使用到 commons-logging 时,commons-logging 会被排除掉,增加 jcl-over-slf4j.jar 的依赖,大事就成了, jcl-over-slf4j.jar 其实就是涵盖了 commons-logging 的源码,只是在调用日志是,用了 SLF4J 的方法;鸡贼不。springboot默认的日志框架也是基于这一套理论。
说明:spring 新版本中自己偷梁换柱 ,整了个 spring-jcl ;这个东西和 jcl-over-slf4j.jar 原理是一样的

我们可以看下 spring-jcl.jar 的实现;看下包下 LogFactory类

/**
	 * Convenience method to return a named logger.
	 * @param name logical name of the <code>Log</code> instance to be returned
	 */
	public static Log getLog(String name) {
		return LogAdapter.createLog(name);
	}

LogAdapter.createLog(name) 点过去看到的代码是

public static Log createLog(String name) {
		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);
		}
	}

哈哈,最后还是到了SLF4J的去适配最终的实现层。

4、springboot 的日志框架介绍

目前我用的springboot 2.2.2.RELEASE版本;基于这个版本,做一下介绍。
当前这个版本spring已经更新到5.2.2.RELEASE 版本; spring 自己底层 commons-logging.jar 的应用已经被替换成了spring-jcl。

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jcl</artifactId>
    <version>5.2.2.RELEASE</version>
    <scope>compile</scope>
</dependency>

spring-jcl 的实现原理上面也提到过,基于 commons-logging.jar 的源码结合slf4j实现的封装。
springboot 默认的日志框架还是 slf4j + logback;
slf4j 的替换方案除了少了 jcl-over-slf4j.jar ,其他依旧健在。

springboot 日志框架变成了

全部评论