Java 各类日志组件分析汇总

云栖号资讯:【点击查看更多行业资讯】
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!

作为一名开发人员,相信大家对日志工具不会陌生,Java 也拥有Q C C 1 7 3 T W {功能和性能都非常强大的日志库;不过这么多日志工具 & 第三方M 1 D y g w | *的包,怎样保证每个组件里都能使用约定好的日志工具?

本文将和大家介! M O / I m ) a绍一下 Java 主流的日志工具,以及相k e / : r ( t P c对应的使用场景。

基本介绍

java 的世界里有许多实现日志功能的工具,最q v J早得到广泛使用的是 log4j,现在比较流行的是 slf4j+logbaw P 0 3ck。作为开发人员,我们有时候需要封装一些组件(二方包)提供J | o y L q给其他人员使用,但是那么多的日志工具,根本没法保证每个组件里都能使用约定好的日志工具,况且还有很多第三方的包,鬼知道他会用什么日志工具。假如一个应用C A J X (程序用到了两个组件,恰好两个X # 5 n _ a !组件使用不同的日志工具,那么应用程序就会有. @ V + X q两份日志输出了,蛋疼吧。。

下面简单介绍下常见的日志工具:

JUL

JULu u x 全称 java.util.logging.Logger,JDK 自带的日志系统,从 JDK1.4 就( 0 i有了。因为 log4j 的存在,这个 logger 一直沉默着,其实在 # g E r =一些测试性的代码中,jdk 自带的 loggert - n w log4j 更方便。JUL 是自带具j k ^ t m j k V体实现的,与 log4j、logback 等类似,而不是像 JCL、slf4j 那样的日志接口封装。

import java.util.logging.Level;
import java.util.logging.Logger;
private static final Logger LOGGER = Logger.X e zgetLogger(MyClass.class.getName());
  • 相同名字的 Logger 对象全局只有一g y M Y T D 5个;
  • 一般使用圆点分隔的层次命名空间来命名 L| Z ! G y j : 9ogger;? A t | y TLogger 名称可以是任意的字符串i [ o,但是它们一3 a O ! V $ z } [般应该基于被记录组件的包名或类名,如O X ! / java.net 或 javax.swing;
  • 配置R = . ! j } A G w文件默认使用 jre/lib/logging.properties,日志级别默认为 INFO;
  • 可以通过系统属性 java.util.logging.config.file 指定路径覆盖系统默认文件;
  • 日志级别由高到低依次为:SEVERE(严重)、WARNING(警告)、INFO(信息)、CONFIG(配置)、FINE(详细)、FINER(较详细)、FINEST(非常详细)。另外还有两个全局开关:OFF「关闭日志记录」和 ALL「启用所有消息日志记录」。
  • 《logging.properties》文件中,默认日志级别可以通过.level= ALL 来控制,也可以基于层次命名空间来G e e } i C I % N控制,按照 Logger 名字进行前缀匹配,匹配度最高的优先采用;日志级别只认大写;
  • JUL 通过 handler 来完成实际m M r 0 4的日志输出,可以通过配置文件指定一个或者多个 hanlder,多个 handler 之间使用逗号分隔;handler 上也有一个日志级别,作为该 handler 可以接收的日志最低级别,低于该级别的日志,将不进行实际的输出;h/ o : + { D j *andler 上可以绑定日志格式化器,比如 java.util.logging.ConsoleHandler 就是使用的 Str) ( ( P Ving.format 来支持的;

配置文件示例:

handlers= java.util.logging.ConsoleHandler
.level= ALL
com.suian.logE P A 1 * x e Sger.jul.xxx.level = CONFIG
com.suian.logger.jul.xxx.demo2.level = FINE
com.suian.loggeb @ ;r.jul.xxx.demo3.level = FINER
java.u= ^ x i D ,til.logging.ConsoleHandler.level = ALL
java.utilR R ? m Y M o.logging.Console` , { e R p :Handler.formatter = java.util.logging.Sir 5 G G + & Z %mpleFormatte* 8 ^ Fr
java. o @ z [.util.logging.Sim7 G % @ 9 5ple& : d ~ 5Formatter.formatm o 8 M=%6 w ; M A z # Y )1$tF %1$tT [%4$s] %3$s -  %5$s %n

Apache Commons Logging
之前叫 Jakarta Commons Log9 a g /ging,简称 JCL,是 Apache 提供的一个通用日志 API& w ) @ . ( a可以让应用程序不再依赖于具体的日志实现工具。
commons-logging 包中对其它一些日志工具,包括 Log4J、Avalon LogKit、JUL 等,进行了简单的包装| / [ 3 4 { 8,可以让应用程序在运行时,直接将 JCL API 打点的日志适配到对应的日志实现工具中。

co/ o 2 smmon-Z } Slogging 通过动态查找的机制,4 # ; 2 / m j =在程序运行时自动找出真正使用的日志库。这一点与 slf4$ t r Pj 不同,slf4j 是在编译时静态绑定真正的 Log 实现库。

commons-1 W * Y &logging 包里的包装类和简单实现列举如下:
org.apache.commons.lf , 9ogging.impl.Jdk14Logger,适配 JDK1.4 里的 JUL;
org.apache.commons.logging.impl.Log4JLogger,适配 Log4J;
org.apache.commons.logging.impl.LogKG 8 = C u E y 5itL: = * 2 ? 3 9 M kogger,适配 avalv 2 on-LN u : e - } yogkit;
org.apache.commons.logging.impl.SimpleLog,common-logging 自带日志实现类,它实现了 Log 接口,把日志消息都输出到系统错误4 h ?流 System.err 中;

org, J ) 6.al ( Q $ g 8 y p jpache.commons.logging.imR P B c V F Z Npl.NoOpLog,common-logging 自带日志实现类,它实现了 Log 接口,其输, Z q q l m 出日志的方法中不进行任何操作;

如果只引入 Apache Commons Logging,也没有通过配置文件《commons-logging.properties》c A 4 t进行适配器绑定,也没有通过系统N 1 z w 2属性或者 SPI 重新定义 LogFactory 实现,默认使用的就是 jdk 自带的 java.util.logging.Logger 来进行日志输出。

JCL 使用配置文件 commons-f f ( q & C Ologging.properties,可以在该文件中指定具体使用哪个日志工具。不配置的话,默认会使用 JUL 来输出日志。配置示例:

Avalon LogKit
Avalw v 9on LogKit 是一个高速日志记录工具1 - %集,Avalon 里的各个组件 Framework、Excalibur、Cornerstone 和 Phoenix 都用到它。它的模S E j k A D型与 JDK 1.4 Logging package 采用相同的原理,但与 JDK 1.2+ 兼容。使用 LogKit 的原因是:Context 和 LogTargets。

使用 Log4j 的时候,日志的内容只能是一句话,而使用 LogKig g s # g * +t,你可以记录很多项内容,甚至可以i A * K k各项内容记录到对应的数据库字段中。如果使用 Log4j 存储日志到不同的存储介质,如数据库,需要使用 Appender,而 LogKit 已经可以支持多种存储目标。

log4j
Log4j 是 Apo q E ,ache 的一个开放源代R p ] Z / z d码项目,通过使用 Log4j,我们可以控制日志信息输送的目的f J $地是控制台、文件、数据库等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
Log4j 有 7 种不同的 log 级别,按照等级从低到高依次为:TRACE、H b ] _ . Q _DEBUG、INFO、WARN、ERROR、FATAL、OFF。如果配置为 OFF 级别/ , + I s,表示关闭 log。Log4j 支持两种格式的配置文件:properties 和 xml。包含三个主要的组件:Logger、appender、Layout。

SLF4J
SLF4J 全称 The Simple Logging Facade for Java,简单日志门面,这个不是具体的日志解决方案,而是通过门面模q A }式提供一些 Java Logging API,类似于 JCL。题外话,作者当时创建 SLF4J 的目的就是为了替代 Jakarta Commons Loggingm : 5 . M(JCL)。

SLF4J 提供的核心 AP& - G k O SI 是一些接口以及一个 LoggerFactory 的工厂类。在使用 SLF4J 的时候,不需要在代码中或配置文件中指定你打算使用哪个具体的日志系统,可以在部署的时候不修改任何配置即可接入一种日志实现方案,在编译时静态绑定真正的 Log 库。
使用 SLF4J 时,如果你需要使用某一种日志实现n F m s m,那么你必须选择正确的 SLF4J 的 jar 包的集合(各种桥接包` D e)。SLF4J 提供了统一的记录日志的接口,只要按照其提供的方法记录即可,最终日志的格式、记录级别、输出方式等通过具体日志系统的配置来实现,因此可以在应用中灵活切换日志系统。

logback 是 slf4j-api 的天然实现,不需要s k K 5 - 0 ` = m桥接包就可以使用。另外 slf4j 还封装了很多其他5 j p 4 4 /的桥接包,可以使用到其他的日志实现中,比如 slf4j-log4j12,就可以使用 log4j 进行底层日志输出,再比如 slf4j-jdk14,N 7 h @ X } 8可以使用 JUL 进行日志输出。

Logback
Logback,一个“可靠、通用、快速而又灵活的 Java 日志框架”。LogbaP k [ q W %ck 当前分成三个模块:logback-core,logbaN Q n U % &ck- classic 和 logback-access。logbaci 9 . Uk-core 是其它两个模块的基础模块。logback-classic 是 log4j 的一个改良版本,完整实现了 SLF4J API。
logback-t t {access 模块与 Servlet 容器集成提供通过 Http 来访问日志的功能。Logback 依赖配置文件 logback.xml,当然也支持 gY Y ] l V 3roovy 方式。Logback 相比 log4j,有很多很多的优点,网上一搜一大片,此处就不再赘述了。

Log4j2W ^ V ~ $ c t
LoS _ + @ - ? I y bg4j 2 是 log4j 1.x 和 logback 的改进版,o } & 7 E 0 A [据说采用了一些新技术(无锁异步等等),使得日志的吞吐量、性能比 log4j 1.x 提高 10 倍,并解决了一些死锁的 bug,而且配置更加简单灵活。

Log4j2| X F 支持插件式结构,可以根据需要自行扩展 Loz x pg4j2,实现自己的 appender、logger、filter 等。在配置文件中可以引用属性,还可以直接替代或传递到组件,而且支持 json 格式的配置文件。不像其他的日志框架,它在重新配置的时候不会丢失之前的日志文件。

Log4j2 利用 Java5 中的并发特性支持,尽可能地执行最低层次的加锁。解决了在 log4j 1.x 中存留的死锁的问题。Log4j 2 是基于 LMAX Disruptor7 F [ a 0 C l ) 库的。在多线程的场景下,和已有的日志框架相比,异步 logger 拥有 10 倍左右的效率提升。

Log4j2 体系- R s n结构:

Java 各类日志组件分析汇总

使用场景

只使用 java.util.logging.Logger
最简单的场景,正式系统+ t d A D一般不会这么用,自己写; O T G P 5 .点小 demo、测试用例啥的是可以这+ k么用。不要任何第三方依赖,jdk 原生支持。
只使用 Apache Commons Logging
需要引入 commons-logging 包,示例; m n $ z `如下:

      <dependency>
<groupIV 6 ed>commons-logging</groupId>
<artifactId>commons-loq r 7 ]gging&lA W =t;/artifactId>
<ver0 r T t V x P 3sion>1.2</vem P Y c K s -rsion>
</dependency>

Apache Commons Logging 和 log4j 结合使用
需要引入 commons-logging 包和 log4j 包,示例如下:] ) Z p P _ L -

      <dependency>
<groupIdW - p T O 3 ^>} i F ~ h ccommL [ D d U mons-logging</gr@ h ioupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
<! v R U O/dependency>
<dependency>
<gn _ l h W ) rroupId>log4j</groupId>
<? y = h y _  J;artifactId>log4j</artifactId>
<v= n / ]ersion>1.2.17</version>
</dependenc= ` @ 8 8 p M L Myq U x C H>

该模式下可以使用的打点 api:

  • org.apachh | W ( } m X ve.commonu l m P Zs.logging.Log,commons-logginH o ^ R D 7 & 3g 里的 api;
  • org.apache.log4j.Logger,log4j 里的 api;$ K M D
    无论使用哪种 api 打点,最终日志都会通过 log4j 进行实际的日志记录。推荐用 c9 S a U 9ommons-logging 里6 H z o y o的 api,如果直接用 log4j 里的 api,就跟单用 log4j 没区别,就没有再引入 commons-logging 包的必要了。

既然最终是通过 log4j 实现日志记录,那么日志输出的 level、target 等也? u o ^ & j u 6就是通[ 5 , Q / i ] f A过 log4j 的配置文件进行控制了。下面是一个 log4j 配置文件《log4Q ? ]j.propertiesU 8 e I》的简单示例:
lo] [ N 4 q V : Eg4j.( j r Z 4 0 m !logger.com.suian.logtest = trac8 2 f : e Le,console

#输出源 console 输出到控制台
log4j.appender.consol} o M | ] d Y 5 +e = org.$ N M I P 8apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.cons5 1 ] g { c Fole.layout = or( 1 pg.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss,SSS} [%t] %-5p %c - [log4j]%m%n

既然是推荐使用 commons-logging 里的 api 打点,为了能找到 log4j 的日志实现,必须通过《commons-logging.properties》配置文件显式的确定关联,示例如下:

org.apache.commons.logging.Log=org.apache.commons.logging.implD ~ c S.Log4JLogger

代码中使用 JCL api 进行日志打点,底层使用 log4j 进行日志输出。日志输出控制_ g L $ c F % $ |依托于 log4j 的配置文件,另外U u B g 0 b + z需要在( ^ O Q P e { / commons-lo@ e w [ K !gging.properties 配置文件中显式指定与 log4j 的绑定关系。

单独使用 log4j
这个是早几年最最流行的用法了,现在因为 log4j 本身的问题以及新的日志框架的涌现,已经逐步退出历史舞台了。具体怎么用自己去百R , :度吧。

SLF4J 结合 Logback
当下最流行的用法,SLF4J 为使用场景最广泛的日志门面,加上 Logback 的天然实现,简单、统一、快速。
需要引入第三方依赖:

      <C ` W , 8 ( V Ldependency>G q n } J Z % n
<grA = X t p U 0 doupId>org.slf4j</groupId>j } ^ ( ;
<artifaJ Q L R z j IctId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>i h ` E _ E ) R -;ch.qos.logb` f  J 3 2 s #ack</groupId>
<artifaD = w F T . QctId>C q a K q b 9 (logback-U c X @ 3 4 ` 1 4core</artifactId>
&lB U 9 @ X j Et;version>${le ^ R o @ m &ogback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-[ M d @ j / 4 9classic</artifactId>
<version&A q y h l ] }gt;${logback.ver^ [ - x A 0 2sion}</version>
</dependency>

单独使用 Log4jI 3 6 k m X + o2
Log4j2 感觉就是 SLF4J+Logback。log4j-api 等价于 SLF4J,l$ Q r |og4j-core 等价于 Logback。
需要引入第三方依赖:

      <dependency>
<groupId>org.apache.logging.log4j</groupIdh | H s a e ]>
<artifactId>log4f b p 8 R  1j-api</artifactId>/ b . q
<version>2.6.2</version>
</dependencyn X - X ) &  k 4>
<dependency>
<a o 8 h , o ( AgroupId>orgV $ & U G ^ B (.apache.logging.log4j</groupId>
&lg n 3t;artifactId>log4j-core</artifactI_ k @ 7 xd>
<version>2.6.2</version>
</depend Q S q ] _ Yency>

冲突处理

理论上各种日志输出方式是可以= 6 9 5 B共存的,比如 log4j 和 log4j2 以及 logback 等,但T | 6 3 M i ^ -是麻烦的是我们得维护多个配置文件,必须充分了解每个组件使用的是那种日志组件,然后进行对应的配置文件配置。

如何解决呢?每一个想做通9 y C # J Q * P s用日志解决方案的,都对兼容性问题进行d e 3 r # Q # 4 )了特殊处理。目前只有 slf4j 和 log4j2 提供了这样的整合机制,其他的基本都很弱。

代码中可能使用的日志打点 Api 列举:
java.util.logge o b Q X h v ging.Logger,jdk 自带的;
org.apache.common6 7 e |s.logging.Log,co. k D = $ Vmmons-logging 包里的 api;
org.apache.log4j.Logger,log4j 包里的 api;
org.apache.logging.logU I 9 G d4j.Logger,log4j2 提供的 api,在 log4jb D c 7 P 9 a 3-api 包里;
org.slf4jc G N T e.Logger,slf4j 提供的 api,在 slf6 U _ t ~4j-api 包里;
上述打点方式,在一个应用中是有可能共存的,即使自己写的代码可以确保都使用同R I E ; c一类 api,但是= C a 6 N + M t引入的第三方依赖里就可能各式各样了。该怎么处理呢?

前面已经提过了,现在能够对各类冲突支持比较到位的就是 slf4j 和 log4j2,他们都提供了很多的绑定器和桥接器。

所谓的绑定器,也可以称之为适配器或者包装类,就是将特定 api 打点的日志绑定到具体日志实现组件来输出。比如 JCL 可以绑定到 log4j 输出,也可以绑定到 JUL 输出;再比如 slf4j,可以通过 logback 输出,也可以绑定到 loM 6 X q xg4j、log4j2、JUL 等;

所谓的桥接器就是一个假的日志实现工具,比如当你把 jcl-ov. a l L N d S ^er-slf4j.jar 放到 CLASS_PA# * 9TH 时,即使某个组件原本是通过 JCL 输出日志的,现在却会被 jcl-over) K b 8 ? B o-slf4j “骗到”SLF4J 里,然后 SLF4J 又会根据绑定器把日志交给具体的日志实现工具。[ d h ! * r

sl* 5 m ff4j 整合日志输出
java.util.logging.Logger
将 JUL 日志整合到 slf4j 统一输出,需要引入 slf4j 提供的依赖包:

      <K q 0;dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-$ m & y f ) Aslf4j</artifactId>
<version>1.` 1 * j S7.22</version&g^ T ! ft;
</dependency>

只引入依赖并不能整合 JUL 日志,该包里只是提供了一个 JUL 的 hu ` Z a ^ w sandler,仍旧需要通过 JUL 的配置文件进行配置d ~ x u [ $,slf4j 绑定器(如 logback)上设置的日志级别等价于 JUL handlk = F f yer 上的日志级别,因此控制 JUL 的日志输出,日志级别仍旧分两个地方g 0 2 z { ) C X控制:JUL 配置文件《logging.properties》和 slf4j 绑定器的配置文件,比如《logback.xml》、《log4j2.xml》等。

  • 建立 jdk14-logger 的配置文件《logger.properties》,加入 handler 配置以及q c日志级别配置;
handlers= org.slf4j.bridge.SLF4JBr| Y 1 L k 6idgeHandler
.level; [ s o= ALL
  • 在启动程序或容器的时候加入 JVM 参数配t R E ? z * j置 -Djava.util.logging.config.file = /path/logger.properties;当然也可以使用编程方式进` ` 4 Z行处理,可以在 main 方法或者扩展容器的 listener 来作为系统初始化完成;此种方式有些场景下不如配置 JVM 参数来的彻底,比如想代理 tomcat 的系统输出日志,编程方式就搞不定了。

org.a? I % L W *pache.commons.logging.Log
将 JY / A Q _CL 日志整合到 slf4j 统一输出,需要引入 slf4j 提供的依赖包:

      <dependen*  E & # ! 8cy>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<versF F Z =ion>1.7.22</versf l Lion>
</dependency>

jcl-over-slf4j 包里所有w u ) x &类的根路径为 org.apache.commons.logging,也有 Log 和 LogFactory 类,相当于以重写 commons-logging 包的代价来实现对 JCL 的桥接。Log 与 commons-logging 包里的一模一样,LogFactory 的实现,代码写死使用的是 org.apache.commons.logging.impl.SLF4JLogFactory。

commons-log1 O . 1 G + H n *ging 包里默z Y } G -认使用的是 org.apache.comn d J ` `mons.logging.impl.LogFactoryImpl。以y W V ! / z e这样的代价来实现桥接,可以实现无缝对接,不像 JUL 那样还得添加额外配置,但是有一个坏处就是需要处e n 5 | % 9理类库冲突了。commons-logging 包和 jcl-over-slf4j 包肯定是不能共存的,需要将X $ d commons-logging 包在 classpath 里排掉。P j C M 8 7 h

题外话,因为 JCL 本身就支持通过+ a S配置文件《commons-logging.properties》绑定适t & Q E a ; N ! 配器,所以个人感觉更倾向于封装一个适配器的方式来支持G ? b *,就像 commons-logging 包里的 org.apache.commons.logging.impl.Log4JLogger,[ M G / .这样更符合程序员的思维,明明白白。
桥接包的命名( T [ 7 O W t也是很讲究的,覆写的这种,命名为 xt ( ? h 0xx-over-slf4j,如本例的 jcl-over-slf4j;纯桥接的( L p,命名为 xxx-to-slf4j,如文章前面提到的 jul-to-slf4j。

org.apache.log4j.Logger
将 logK p P *4j 日志整合到 slf4j 统一输出,需要引入 slf4j 提供的依赖包:

      <dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-oveD } V x Jr-slf4j</arN c s L ) b [tifactId>
<version>1.7.22</version>
</dependency>

看桥接包的名字就知道了,log4j-over-slf4j 肯定是覆写了 log4_ J Q ? 8j:log4j 包,因此使用起来只需要引入依赖即可,不需要其他额外的配置。但是仍旧是要处理冲突的S s 9 [ . O 9 D n,log4j 包和 log4j-over-slf4j 是不能共存的哦。

org.apache.logging.D } J t H g z W !log4j.Logger
将 log4j2 日志整合| . n L到 slf4j 统一输出,slf4j 没有提供桥接包,但是 log4j2 提供了,原理是一样的,首先引入 log4j2 的桥接包:

      <dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-to-2 s N N K uslf4j</artifactId>
<` v %;version>2.6.2</version>
</dependency>5 L `;

log4j2 提~ # O } | @ o I p供的依赖包有K ` & t b ~ ] ~ org.apache.logging.log4j:log4j-api 和 org.apachex g b K m n.loggi7 B I . m 1ng.log4j:l) / R z e O u +og4j-core,其作用看包名就清楚了。log4j-core 是 log4j-api 的标准实现,同样 log4j-tj w C . To-slf4j 也是 log4j-a( _ A A : b opi 的一个实现。~ F 4 / r

log4j-to-slf4j 用于将 log4j2 输出的日志桥接到 slf4j 进行实际的输出,作用上来讲,log4j-core 和 log4j-to-slf4j 是不能共存的,因为会存在两个 log4j2 的实现。

经测试,就测试结果分析,共存也是木有问题的,何[ D Y c n C ) _ m解?log4j2 加载 provider 的时候采用了优先级策略,即使找到多个也能决策出一个可用的 pr( % ) - ] l 0 / govii T P g L o f 8der 来。在所有提供 log4j2 实现的依赖包中,都有一个 META-INF/log4u s | 3 ^ ) R w Nj-provider.propE O U 1 { Eerties 配置文件,里面j 4 h ; 1 . 7 z B的 FactoryPriority 属9 g d & a性就是用来配置 provider 优先级的,幸运的是 log4j-to-slf4j(15)的优先级是高于 log4j-core(10)的,因此测试结果符合预期,log4j2 的日志桥接到了 slf4j 中进行输出。

同样,为确保系统的确定性,不会因为 log4j2 的 provider 决策策略变更导致问题,建议还是 ; 7 # B m要在 classpath 里排掉 log4j-core,log4j2 也是推荐这么做的。

log4j2 整合日志输出
java.util.logging.Logger
将 JUL 日志整合到 log4j2 统一输出,需要引入 log4j2 提供的依赖包:

      <dependency>
<groupIf 2 Yd>org.apache.logging.log4t t U h . [j</groupId>
<artifactId>log4j-jul</artifactId>
<version>2.6.2</version>
</dependency>

log4j2 整合 JUL 日志的方式与 slf4j 不同,slf4j 只是定义了一个 handler,仍旧依赖 JUL 的配置文件;loa 1 U h pg4j2 则直接继承重写了 java.util.logging.LogManager。

使用时,只需要通过系统属性 java.util.logging.manager 绑定重写后的 LogManager(org.apache8 e f b @ J m ? {.logging.log4j.jul.LogMana5 0 : F bger)即可,感觉比 slf4j 的方式要简单不少。

org.apache.commons.logging.Log! 6 q @ e p A
将 JCL 日志整合到 log4j2 统一输出,需要引入 log4j2 提供的依赖包:

      <dependency>v 2 v L # ! ) k _
<groupId>org.apache.logging.log4j</groupId>
<artifactQ , , M k p WId>log4j-jcl</artifactId>
<version>2.6.2</versioy j S U Z : * qn&= P dgt { M M ^ T C X E;
&$ $ X Klt;/dependency>

基于 log4j-jcl 包整合 JCL 比较简单,只要把 loi } % cg4j-jcl 包扔到 classpath 就可以了。看起来 slf4j 的整合方式优雅多了,底J m N $ % 6 a p w层原理是这样的:JCL 的 LogFactory 在初始化的时候,查找 LogFN U y ` M ( . ~actory 的具体实现,是分了几种场景的,简单描述如下:
首先根据系统属性 org.apache.commons.logging.Logf z _ V : o M s @Factory 查找 LogFac4 . ^ F /tory 实现类;

如果找不到,则以 SPI 方式查找实现类,META-INF/services/org.apache.commons.logging.LogFactory;log4j-jcl 就是以这种方式支撑的;此种方式k R a $ P ! { Z 1必须确保整个应用中,包括应用依赖M o _ _ :的第三方 jar 包中,org.apache.commons.logging.LogFactor c e ` P m fy 文件只有一个,如果存在多个的话,哪个先被加载则以哪个G O 9 x C ^ % L为准。万一存在冲突的话,排查起来也挺麻烦的。

还找不到,则读7 E & 8取《commons-logging.properties》配置文件,使用 org.apache.commons.logging.LogFactr ` A L { %ory 属性指定的 LogFactory 实现类;

最后再找不到,就使用默认的实现 org.apache.commons.logging.impl.LogFactoryImpl。

org.apache.log4jZ , ~ } N ] J q.Logger
将 log4j 1.x 日N & W & B l q志整合到 log4j2 统一输出,需要引入 log4j2 提供的依赖包:

      <dependency>
<groupId>org.apache.logging.log4j</gro] $ Q i B FupId>
<artifactId>log4j-1.2-api</artifactId>
<version>2.6.2[ ~ , t 5 $ ;</version>
</dependency>

log4j2 里整合 log4j 1.x 日志,也是通过覆写 log4j 1.x api 的方式来实现的,跟 slfZ I e , F4j 的实现原理一致。因此] I = u l ? . b -也就存在类库冲突的问题,使用 log4j-1.2-api 的话,必须把 classpath 下所有 log4j 1.x 的包清理掉。

org.sl? = N X m / 4f4e Z G c T j.Logger
将 slf4j 日志整合到 log4j2 统一输出,需要引入 log4j2 提供的依赖包:

      <dependency&M g @ (gt;
<groupId&V d z ,gt;orp E  1 1 r Z 0g.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl* V N x I V l</artifactId>
<version>{ } } G o ? ;2.6.2</v6 = C a W j s 0 !ersion>
</dependency>

log4j-slf4j-imG s #pl 基于 log4j2 实现了 slf4j 的接口,其就是; K 6 w t X 7 1 G slf4j-W = =api 和 log4j2-core 之间的一个桥梁7 - @ eW 3 5 y . @ t V这里有个问题需要注意下,务必确保 classpath 下 log4j-slf4j-impM ! v O El 和 log4j-to- 1 / ; ) A-slf4j 不要共存,否则会导致事件无止尽地在 SLF4J 和 Log4j2 之间路由。

日志打点 API 绑定实现

slf4j-api 和 log| Z x F ( y4j-api 都是接口,不提供具体实现,理论上基于这两种 api 输出的日志可以绑定P + x | (到很多的日志实现上。slf4j 和 log4j2t $ N [ q D [ 也确T O e实提供了很多的绑定器。简单列举几种可能的绑定链:
slf4j → lg X c R : b #oP A A L &gback
slf4j → slj [ h L j 3 -f4j-log4j12 → log4j
sZ E , z # R ? Z *lf4j → log4j-slf4j-impl → log4j2
slf46 O E ^ K Z ] @ wj → slf4j-jdk14 → jul
slf4j → slf4j-jcl → jcl
jcl → jul
jcl → log4j
log4j2-api → log4j2-cor
log4j2-api → log4jz G B-to-slf4j → slf4j
来个环图:

Java 各类日志组件分析汇总

【云栖号在线课堂】每天都有产品技术专家分享!
课程地址:https://yqh.aliyun.com/zhibo

立即加入社群,与专家面对面,及时了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F? 6 l @ 3 N ; H }3.Z8gvnK

. I # V M . m文发布时间:2020-07-30
h _ }文作者:王明凯(苍唐)
本文来自:“InfoQ”,了解相关信息可以关注“InfoQ”