持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情
一、日志的使用方式
随着日志的的发展,目前用的较多有JDK自带的jul,log4j(已逐步淘汰)、log4j2、logback等。较大的系统,会拆分为各个微服务,最后再进行互相调用,如果各个微服务项目经理之间未达成共识,可能每个服务用的日志还不一样,比如有的小组用的jul,有的小组用的log4j,这样给后期维护运维就带来了一些难题,下面逐步来进行叙述日志的使用过程。
二、多日志使用
2-1、使用JUL、log4j记录日志
2-1-1、使用jul日志
如上可以看大使用JUL日志,因为是JDK内置的,因此输出日志为红色字体。
2-1-2、使用log4j2日志
2-1-2-1、引入pom依赖
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.17.2</version>
</dependency>
2-1-2-2、添加log4j配置文件
log4j.properties
#trace<debug<info<warn<error<fatal(日志级别,设置trace,则后面的日志级别都会输出)
log4j.rootLogger=trace, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] ====== %m%n
2-1-2-3、测试log4j
可以看到引用的jar包为log4j,同时日志输出也为黑色字体。
2-2、多日志的问题
通过以上的方式可以完成日志的使用,但是万一如果项目合并使用的时候,因为多日志就会有些影响了,这样就不得不修改其中一端,造成了一些不必要的工作量,在阿里开发规范中,就有这样一条:
强制:应用中不可直接使用日志系统(log4j、logback)中的 API ,而应依赖使用日志框架 SLF4J 中的 API 。使用门面模式的日志框架,有利于维护和各个类的日志处理方式的统一。
2-2-1、日志门面桥接器
因此我们在使用日志的时候需要去加一层日志门面也就是slf4j,slf4j和具体日志对接的时候,需要配置桥接器,就可以使用了,各个日志插件的桥接器如下:
具体说明如下:
| jar包名 | 说明 |
|---|---|
| slf4j-log4j12-1.7.13.jar | Log4j1.2版本的桥接器,你需要将Log4j.jar加入Classpath。 |
| log4j-slf4j-impl.jar | Log4j2版本的桥接器,还需要log4j-api.jar log4j-core.jar |
| slf4j-jdk14-1.7.13.jar | java.util.logging的桥接器,Jdk原生日志框架。 |
| slf4j-nop-1.7.13.jar | NOP桥接器,默默丢弃一切日志。 |
| slf4j-simple-1.7.13.jar | 一个简单实现的桥接器,该实现输出所有事件到System.err. 只有Info以及高于该级别的消息被打印,在小型应用中它也许是有用的。 |
| slf4j-jcl-1.7.13.jar | Jakarta Commons Logging 的桥接器. 这个桥接器将Slf4j所有日志委派给Jcl。 |
| logback-classic-1.0.13.jar(requires logback-core-1.0.13.jar) | Slf4j的原生实现,Logback直接实现了Slf4j的接口,因此使用Slf4j与Logback的结合使用也意味更小的内存与计算开销 |
2-2-2、slf4j日志门面的实现
2-2-2-1、使用log4j日志添加相关依赖
2-2-2-1-1、引入slf4j核心包
<!--slf4j核心包-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
2-2-2-1-2、引入log4j-slf4j门面桥接器
如果使用其他日志,引入那个桥接器可以看上面
<!--log4j-slf4j桥接器-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
2-2-2-1-3、使用slf4j声明Logger并测试
需要注意的是,导入的包都需要导入slf4j门面相关package,可以看到输出的日志class为log4j
2-2-2-2、使用JUL桥接器JCL
2-2-2-2-1、添加相关依赖
<!--引入JCL门面依赖-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
2-2-2-2-2、测试
如下可以发现,虽然我们使用了JCL但是实际使用的还是log4j,这是为什么呢?
因为JCL使用的ClassLoader的机制进行加载,执行顺序为: commons-logging.properties->系统环境变量->log4j->jul->simplelog->nooplog
项目中,没有使用commons-logging.properties,环境变量也没有配置,而此时在前面测试时,引入了log4j,这样JCL就使用log4j了。如果要使用jdk的自带可以使用配置文件来处理。
2-2-2-2-3、使用commons-logging.properties加载jdk log
配置日志使用JDK log
2-2-2-2-4、再次测试如下
再次测试可以看到日志颜色变为红色,并且日志类型已改为Jdk14Logger
2-3、多日志使用适配器
通过以上的方式,JUL和log4j都实现了门面模式,符合了我们开发规范,但是项目中有两个日志处理器,后期处理还是麻烦,该如何合并呢?先看下图
左一图中,分别举例了JCL/Log4j/JUL和slf4j的适配器,上面我们使用了JCL,因此我们就需要使用jcl-over-slf4j.jar即可
2-3-1、引入jcl-slf4j适配器依赖
<!--为了日志统一实现,将JCL转化到SLF4J
添加JCL-SLF4J的适配器
-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.30</version>
</dependency>
2-3-2、再次测试
如下可以看到,在没有改动代码的情况下,只是添加了适配器就可以将jcl转到slf4j,并且输出的日志颜色不是红色了。
2-4、根据以上的实验,我们可以看一个案例
2-4-1、案例
一个项目,一个模块用log4j,另一个模块用slf4j+log4j2,如何统一输出? 其实在某些中小型公司,这种情况很常见。我曾经见过某公司的项目,因为研发不懂底层的日志原理,日志文件里头既有log4j.properties,又有log4j2.xml,各种API混用,惨不忍睹!
还有人用着jul的API,然后拿着log4j.properties,跑来问我,为什么配置不生效!简直是一言难尽!
OK,回到我们的问题,如何统一输出!OK,这里就要用上slf4j的适配器,slf4j提供了各种各样的适配器,用来将某种日志框架委托给slf4j。其最明显的集成工作方式有如下:
进行选择填空,将我们的案例里的条件填入,根据题意应该选log4j-over-slf4j适配器,于是就变成下面这张图
就可以实现日志统一为log4j2来输出!
ps:根据适配器工作原理的不同,被适配的日志框架并不是一定要删除!以上图为例,log4j这个日志框架删不删都可以,你只要能保证log4j的加载顺序在log4j-over-slf4j后即可。因为log4j-over-slf4j这个适配器的工作原理是,内部提供了和log4j一模一样的api接口,因此你在程序中调用log4j的api的时候,你必须想办法让其走适配器的api。如果你删了log4j这个框架,那你程序里肯定是走log4j-over-slf4j这个组件里的api。如果不删log4j,只要保证其在classpth里的顺序比log4j前即可!