本文是一个系列,欢迎关注,支持越多,更新越快
想必你们都有过使用System.out
来进行输出调试,开发开发环境下这样作固然很方便,可是线上这样作就有麻烦了:html
显然System.out
解决不了咱们的问题,可是咱们遇到的问题必定会有前人遇到过,日志也不例外,其中就有一个大牛 Ceki,整个Java的日志体系几乎都有Ceki参与或者受到了Ceki的深度影响。固然Java日志体系的复杂度也有一部分缘由是拜这位大牛所赐。java
让咱们来瞻仰一下大神,哈哈:spring
其实在Ceki设计的体系下,日志如同Java的JDBC、Servelt等同样,定义好标准后实现能够互相切换,问题在于定标准的人各自为政搞出来好多标准,JCL、SLF4j等等,官方(Sun公司)又晚又不给力,发展到如今终于被SLF4j以一种巧妙的方式(桥接、绑定,见下文)统一了,标准使用方式以下图:apache
这个图截取自slf4j手册,简化了多余部分,很清晰的表示了使用方式:编程
应用引用SLF4j-API(编码时使用SLF4j的接口org.slf4j.Logger
,而非logback或log4j的实现)
安全
具体依赖以下框架
logbackmaven
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>复制代码
log4j2spring-boot
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.12.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.12.1</version>
</dependency>复制代码
得益于maven的依赖传递机制,咱们不须要显示声明依赖SLF4j-API.jar。工具
能够看到,log4j 多依赖了 log4j-slf4j-impl.jar,其实就是上图所示的适配器层,读者可能好奇,为何使用 log4j 会有适配器层?其缘由在于,slf4j 并非官方规范,因此没人遵照(也就是本身的日志框架中没有原生实现org.slf4j.Logger
接口,如 log4j ),而绑定层( log4j-slf4j-impl.jar)的做用就是经过静态查找的方式将使用log4j做为实现(具体原理请关注后续文章),这样就是实现了不依赖log4j而使用log4j输出日志(面向接口编程的最佳实践,Ceki 大神就是用这套思想将 slf4j 作成了 Java 日志的标准,烂牌翻盘的典范)。
上面这一段讲解了绑定(concrete-bindings)思想,是本文的精髓,读者必定要理解这里,后面还有桥接思想与之相似,请继续阅读。
至此咱们已经完成了日志的整合,可是事情真的这么简单吗?
先梳理一下,如此混乱的日志体系下(slf4j,jul,jcl,logback,log4j)会不会会产生什么问题?答案是必定的,各类第三方库使用了不一样的日志框架,若是咱们依赖 Spring ,Spring(非boot)的默认日志实现是JCL、又或者咱们已有项目已经使用了Log4j,想使用logback的话,难道要逐个类改代码吗(官方有迁移工具)?咱们能不能只用一种框架来处理JUL(java.util.logging)、JCL(Jakarta Commons Logging)、Log4j一、Log4j2 呢?
答案是确定的,Ceki 的 Slf4j 给出了解决方案,就是上文所说的桥接( Bridging legacy),简单来讲就是劫持以上因此第三方日志输出并重定向至 SLF4j,最终实现统一日志上层 API(编码) 与下层实现(输出日志位置、格式统一)。咱们来看一下图示:
上图左侧就是前一张图的 logback 日志实现,为了兼容其余日志,咱们须要引用右侧的桥接包:xxx-over/to-slf4j.jar ,xxx对应日志框架,使用 logback 的状况下,除了上文的 logback 依赖,还须要引入如下依赖才能保证全部日志都被桥接至slf4j。
如何桥接?
logback 以下
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
</dependency>
<!-- log4j 桥接包,slf4j官方实现,另有log4j官方实现,二选一便可 log4j-to-slf4j-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
</dependency>复制代码
log4j2 以下
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.12.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.12.1</version>
</dependency>
<!-- 如下是桥接包,使用了log4j做为底层实现, 不能再桥接log4j,不然会出现无限递归的状况(具体缘由请关注后续文章) -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency><dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
</dependency>复制代码
SpringBoot 项目引用了一部分依赖,因此使用起来略微有些不一样:
logback 以下
<!-- logback做为内置实现,使用相对简单 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency><!-- 引入缺乏的桥接包 --><dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>复制代码
log4j2 以下
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<!-- 使用log4j2要排除logback依赖 -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Spring已经写好了一个log4j2-starter但缺乏桥接包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!-- 引入缺乏的桥接包 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>复制代码
以上两种才是项目中的最佳使用方式,其余笔者不推荐使用。
最后来看一下 slf4j 如何使用:
static final org.slf4j.Logger logger = LoggerFactory.getLogger(TestLog.class);
logger.trace("A TRACE Message");
logger.debug("A DEBUG Message");
logger.info("An INFO Message");
logger.warn("A WARN Message");
logger.error("An ERROR Message");复制代码
这样使用咱们就能够随意切换日志实现而无需改动代码,操做起来也简单,只须要按照上文切换依赖便可。至于其余使用细节本文不在赘述,关注后续文章(最佳实践、配置文件、原理、扩展等)。
若是以为写的不错,求关注、求点赞、求转发,若是有问题或者文中有错误,欢迎留言讨论。
转载请注明出处。
扫描关注公众号,第一时间得到更新
参考:
Java-日志的江湖
http://www.slf4j.org/manual.html
http://www.slf4j.org/legacy.html
www.baeldung.com/spring-boot…