Zacard's Notes

重构总结之升级log4j为logback

背景

随着业务的复杂度上升,流量增大,各个系统的日志量也急剧上升。个别基础服务日志量达到了2G/天。使用的log4j(1)出现了大量丢日志的情况,且写日志的性能也出现下降。由于使用了slf4j作为日志门面,所以考虑换到业界使用更为广泛的logback。

现状

目前系统都以log4j作为日志实现,且有很多第三方包同样也依赖了log4j作为日志输出。如果要使用logback,总不能一个一个的去排除对log4j的依赖吧,不仅工作量大而且容易漏掉。

只能先看下slf4j选择具体日志实现的原理了。。。

slf4j选择日志实现的原理

slf4j官网:传送门

SLF4J API is designed to bind with one and only one underlying logging framework at a time. If more than one binding is present on the class path, SLF4J will emit a warning, listing the location of those bindings.

When multiple bindings are available on the class path, select one and only one binding you wish to use, and remove the other bindings. For example, if you have both slf4j-simple-1.8.0-alpha2.jar and slf4j-nop-1.8.0-alpha2.jar on the class path and you wish to use the nop (no-operation) binding, then remove slf4j-simple-1.8.0-alpha2.jar from the class path.

The list of locations that SLF4J provides in this warning usually provides sufficient information to identify the dependency transitively pulling in an unwanted SLF4J binding into your project. In your project’s pom.xml file, exclude this SLF4J binding when declaring the unscrupulous dependency. For example, cassandra-all version 0.8.1 declares both log4j and slf4j-log4j12 as compile-time dependencies. Thus, when you include cassandra-all as a dependency in your project, the cassandra-all declaration will cause both slf4j-log4j12.jar and log4j.jar to be pulled in as dependencies. In case you do not wish to use log4j as the the SLF4J backend, you can instruct Maven to exclude these two artifacts as shown next:



org.apache.cassandra
cassandra-all
0.8.1

<exclusions>
  <exclusion> 
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
  </exclusion>
  <exclusion> 
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
  </exclusion>
</exclusions> 


Note The warning emitted by SLF4J is just that, a warning. Even when multiple bindings are present, SLF4J will pick one logging framework/implementation and bind with it. The way SLF4J picks a binding is determined by the JVM and for all practical purposes should be considered random. As of version 1.6.6, SLF4J will name the framework/implementation class it is actually bound to.

Embedded components such as libraries or frameworks should not declare a dependency on any SLF4J binding but only depend on slf4j-api. When a library declares a compile-time dependency on a SLF4J binding, it imposes that binding on the end-user, thus negating SLF4J’s purpose. When you come across an embedded component declaring a compile-time dependency on any SLF4J binding, please take the time to contact the authors of said component/library and kindly ask them to mend their ways.

意思就是说如果有多个日志组件并存的时候,slf4j的选择方式是随机的…

黑魔法方式

最终google发现一种巧妙的方式。请看以下maven pom.xml配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!-- 强制去除log4j的依赖,全部 delegate 到 log4j-over-slf4j 上-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>99.0-does-not-exist</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>99.0-does-not-exist</version>
</dependency>
<dependency>
<groupId>apache-log4j</groupId>
<artifactId>log4j</artifactId>
<version>99.0-not-exist</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.25</version>
</dependency>
<!-- 明确指定只用logback -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.11</version>
</dependency>
<dependency>
<groupId>org.logback-extensions</groupId>
<artifactId>logback-ext-spring</artifactId>
<version>0.1.4</version>
</dependency>

原理就是maven对于冲突包的选择方式是就近原则,当项目主动定义了一个并不存在的log4j版本(当然你得打个空jar到你的maven私服或者本地),maven优先会选择这个不存在的版本,从而使log4j的依赖全部被排除。

坚持原创技术分享,您的支持将鼓励我继续创作!

热评文章