1.slf4j的概念
近期学习springboot的时候,其中的日志模块 spring-boot-logging-starter,springboot 底层的日志实现默认是 slf4j + logback。我一直很好奇springboot 集成了 怎么多的框架,而每个框架的自身实现的日志又不一样如(spring 采用的是 common-logging ,hibernate 采用的是jboss-logging等),那他又是怎么实现日志统一的呢?然后就细细看了一下springboot的文档,springboot大概的意思就是 用slf4j 做抽象层,然后用logback 做实现层,在引入第三方依赖的时候,将原有的日志依赖排除。。。 顿时感觉脑袋有点晕晕,然后硬着头皮的读了一下slf4j文档。
首先我们的了解一下什么是slf4j? 度娘的说法是
slf4j百度百科
SLF4J,即简单日志门面(Simple Logging Facade for Java),不是具体的日志解决方案,它只服务于各种各样的日志系统。按照官方的说法,SLF4J是一个用于日志系统的简单Facade,允许最终用户在部署其应用时使用其所希望的日志System
实际上,SLF4J所提供的核心API是一些接口以及一个LoggerFactory的工厂类。从某种程度上,SLF4J有点类似JDBC,不过比JDBC更简单,在JDBC中,你需要指定驱动程序,而在使用SLF4J的时候,不需要在代码中或配置文件中指定你打算使用那个具体的日志系统。如同使用JDBC基本不用考虑具体数据库一样,SLF4J提供了统一的记录日志的接口,只要按照其提供的方法记录即可,最终日志的格式、记录级别、输出方式等通过具体日志系统的配置来实现,因此可以在应用中灵活切换日志系统。
看到Jdbc 大家有点感悟了,原来 slf4j是干这活的。顿时想起在导jar包到lib目录下搭建项目环境, 导入slf4j-api-jar 还要引起他的依赖如log4j等的血泪史了。
2.slf4j底层实现日志记录的几种方式
上文说道 slf4j 主要是充当日志门面的角色,日志具体实现还是开发者自己决定的。那现在主流的日志框架有哪些呢?
2.1 主流的日志框架
- JUL(java.util.logging) java内置的日志模块
- JCL(Jakarta Commons Logging) 也叫Apache Commons Logging其中spring就是用的这个实现日志记录的
- log4j (Log4j是Apache的一个开源项目) 是一个日志功能比较强大的日志记录器
- logback是log4j的升级版,由三个模块组成:logback-core, logback-classic and logback-access。 其中logback-core为另外两个模块提供了基础,logback-classic是 log4j的改进版和实现slf4j。
- log4j2 是现在市面上功能最强大的日志记录器,和log4j是两个框架并不是log4j的升级版。只是借用了一下log4j的名称而已。
- Jboss-logging(用得很少忽略)
2.2 这几种主流的日志框架的实现
我们了解了常用的几种日志实现框架。但是在项目中我们该怎么用呢?上面我们提到过 slf4j是简单日志门面是抽象层,针对开发者在代码中编写的,那我们在代码中应该怎么写呢?其实我们实现起来很简单 在slf4j的用户手册中有一个hello world的入门程序
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");
}
}
我们只把当前类.class 作为参数传给LoggerFactory,由日志工厂给我们生产出一个日志记录器 Logger ,然后就可以通过logger.info(),
logger.error()来实现日志记录功能。但是它底层是怎么实现的呢?
在slf4j官方文档中有一个图片就阐述了这一过程
在这个图片中我们可以大概的了解到通过slf4j日志记录大分不同日志实现有不同日志实现架构(api —slf4j直接实现/ 适配器层— 第三方日志记录器)。其中slf4j直接实现 和适配层是最为关键的一步:
我们可以从两个方面来阐述他的重要性:如 loggback为例
a.从代码层面:
从上图中我们可以看出logback.claassic里的LoggerContext直接实现了slf4j的getLogger 。这就是slf4j的本地实现。
b.从架构方面(导入依赖)
其中logback-classic这层就依赖 slf4j-api 以及 logback-core ,我们可以认为slf4j的直接实现层 把 slf4j-api 层以及 日志实现做了处理,所以我们导入依赖的时候 由依赖的传递性,我们只需要导入 这个logback-classic 依赖就可以了。
适配层也可以这样分析:
代码层面:
public class Log4jLoggerFactory implements ILoggerFactory {
private static final String LOG4J_DELEGATION_LOOP_URL = "http://www.slf4j.org/codes.html#log4jDelegationLoop";
ConcurrentMap<String, Logger> loggerMap = new ConcurrentHashMap();
public Log4jLoggerFactory() {
LogManager.getRootLogger();
}
public Logger getLogger(String name) {
Logger slf4jLogger = (Logger)this.loggerMap.get(name);
if(slf4jLogger != null) {
return slf4jLogger;
} else {
//这里底层就采用的log4j来实现的
org.apache.log4j.Logger log4jLogger;
if(name.equalsIgnoreCase("ROOT")) {
log4jLogger = LogManager.getRootLogger();
} else {
log4jLogger = LogManager.getLogger(name);
}
Logger newInstance = new Log4jLoggerAdapter(log4jLogger);
Logger oldInstance = (Logger)this.loggerMap.putIfAbsent(name, newInstance);
return (Logger)(oldInstance == null?newInstance:oldInstance);
}
}
架构层次:
1.如果只有slf4j,而没有其他日志实现框架,会报错
throwing a NoClassDefFoundError because the org.slf4j.impl.StaticLoggerBinder class is missing
2.如果你想用 logback 做为日志实现,我们只要导入slf4j直接实现层依赖就可以
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.13</version>
</dependency>
3.如果你想用 log4j的话 ,我们只要导入适配层依赖就可以使用:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.8.0-beta2</version>
</dependency>
4.如果要想用jdk自带的日志模块,同理我们只要导入中间适配层的依赖
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.8.0-beta2</version>
</dependency>
5.同理如果你想用其他实现框架,同理我们只要导入slf4j直接实现层或者适配层依赖就可以。
3. 导入依赖总结
从上文中对slf4j的直接实现层与适配层的分析,我们可以得出一个结论,我们想要用哪个实现框架就 参照slf4j的官方文档 导入对应得slf4j直接实现层依赖或者适配层依赖就可以。当然这只是针对Maven 等构建工具;如果还是导jar的方式,那就。。。
这篇博客只是让我们了解了一下slf4j在项目中的使用以及不同具体日志实现框架的项目环境搭建。我们在下章我将深入的了解一下logback 的功能以及配置。