日志组件相关历史

Java 界里有许多实现日志功能的工具,最早得到广泛使用的是 log4j,许多应用程序的日志部分都交给了 log4j,不过作为组件开发者,他们希望自己的组件不要紧紧依赖某一个工具,毕竟在同一个时候还有很多其他很多日志工具,假如一个应用程序用到了两个组 件,恰好两个组件使用不同的日志工具,那么应用程序就会有两份日志输出了。

为了解决这个问题,Apache Commons Logging (之前叫 Jakarta Commons Logging,JCL)粉墨登场,JCL 只提供 log 接口,具体的实现则在运行时动态寻找。这样一来组件开发者只需要针对 JCL 接口开发,而调用组件的应用程序则可以在运行时搭配自己喜好的日志实践工具。

所以即使到现在你仍会看到很多程序应用 JCL + log4j 这种搭配,不过当程序规模越来越庞大时,JCL的动态绑定并不是总能成功,具体原因大家可以 Google 一下,这里就不再赘述了。解决方法之一就是在程序部署时静态绑定指定的日志工具,这就是 SLF4J 产生的原因。

跟 JCL 一样,SLF4J 也是只提供 log 接口,具体的实现是在打包应用程序时所放入的绑定器(名字为 slf4j-XXX-version.jar)来决定,XXX 可以是 log4j12, jdk14, jcl, nop 等,他们实现了跟具体日志工具(比如 log4j)的绑定及代理工作。举个例子:如果一个程序希望用 log4j 日志工具,那么程序只需针对 slf4j-api 接口编程,然后在打包时再放入 slf4j-log4j12-version.jar 和 log4j.jar 就可以了。

现 在还有一个问题,假如你正在开发应用程序所调用的组件当中已经使用了 JCL 的,还有一些组建可能直接调用了 java.util.logging,这时你需要一个桥接器(名字为 XXX-over-slf4j.jar)把他们的日志输出重定向到 SLF4J,所谓的桥接器就是一个假的日志实现工具,比如当你把 jcl-over-slf4j.jar 放到 CLASS_PATH 时,即使某个组件原本是通过 JCL 输出日志的,现在却会被 jcl-over-slf4j “骗到”SLF4J 里,然后 SLF4J 又会根据绑定器把日志交给具体的日志实现工具。

Component
|
| log to Apache Commons Logging
V
jcl-over-slf4j.jar --- (redirect) ---> SLF4j ---> slf4j-log4j12-version.jar ---> log4j.jar ---> 输出日志

看到上面的流程图可能会发现一个有趣的问题,假如在 CLASS_PATH 里同时放置 log4j-over-slf4j.jar 和 slf4j-log4j12-version.jar 会发生什么情况呢?没错,日志会被踢来踢去,最终进入死循环

所以使用 SLF4J 的比较典型搭配就是把 slf4j-api、JCL 桥接器、java.util.logging(JUL)桥接器、log4j 绑定器、log4j 这5个 jar 放置在 CLASS_PATH 里。

=============

在项目开发中常见的日志:

commons-logging、log4j、slf4j、logback及java.util.logging

以上日志之间到底啥关系,项目里经常会见到好几个都出现,到底咋回事。

其实这些都可以分为两大类:api接口、api实现

常见的接口:slf4j-api、commons-logging-api、jboss-logging等

常见实现:log4j、logback、commons-logging-impl、java.util.logging

注意:slf比较比较特殊点,他有适配器这一概念:

旧日志到slf的适配器:jcl-over-slf4j、jul-to-slf4j、log4j-over-slf4j等

slf到实现的适配器:slf4j-jdk14、slf4j-jcl、slf4j-log4j12等

slf4J与旧日志框架的关系 :
slf4j等于commons-logging,是各种日志实现的通用入口,会根据classpath中存在下面哪一个Jar来决定具体的日志实现库。 
logback-classic(默认的logback实现) 
slf4j-jcl.jar(apache commons logging) 
slf4j-logj12.jar(log4j 1.2.4) 
slf4j-jdk14(java.util.logging)


下面简单捋一捋:

1、apache commons-logging(http://commons.apache.org/proper/commons-logging/)

  The Logging package is an ultra-thin bridge between different logging implementations。

其实就是说commons-logging是一个通用接口,是其他日志实现框架的接口封装层,就好比是jdbc与其他的jdbc实现之间的关系。

 A library that uses the commons-logging API can be used with any logging implementation at runtime,使用了commons-logging api ,在运行时你可以选择任何log实现产品(任何?有点夸大)。

Commons-logging comes with support for a number of popular logging implementations,

and writing adapters for others is a reasonably simple task。

commons-logging does not attempt to initialise or terminate the underlying logging implementation,However many popular logging implementations do automatically initialise themselves; in this case an application may be able to avoid containing any code that is specific to the logging implementation used.

配置:

动态查找原理:Log 是一个接口声明。LogFactory 的内部会去装载具体的日志系统,并获得实现该Log 接口的实现类。LogFactory 内部装载日志系统的流程如下:

  1. 首先,寻找org.apache.commons.logging.LogFactory 属性配置。

  2. 否则,利用JDK1.3 开始提供的service 发现机制,会扫描classpah 下的META-INF/services/org.apache.commons.logging.LogFactory文件,若找到则装载里面的配置,使用里面的配置。

  3. 否则,从Classpath 里寻找commons-logging.properties ,找到则根据里面的配置加载。

  4. 否则,使用默认的配置:如果能找到Log4j 则默认使用log4j 实现,如果没有则使用JDK14Logger 实现,再没有则使用commons-logging 内部提供的SimpleLog 实现。

从上述加载流程来看,只要引入了log4j 并在classpath 配置了log4j.xml ,则commons-logging 就会使log4j 使用正常,而代码里不需要依赖任何log4j 的代码。

2、Log4J

     注意: log4j 1.X 版本在2015.8 被宣布寿终正寝了,后续就是log4j 2了。

      log4j是一个日志的框架的实现体。

  Configure Log4J using system properties and/or a properties file:           

  log4j.configuration=log4j.propertiesUse this system property to specify the name of a Log4J configuration file. If not specified, the default configuration file is log4j.properties


Configuration of Log4j 2 can be accomplished in 1 of 4 ways:

1.Through a configuration file written in XML, JSON, YAML, or properties format.

2.Programmatically, by creating a ConfigurationFactory and Configuration implementation.

3.Programmatically, by calling the APIs exposed in the Configuration interface to add components to the default configuration.

4.Programmatically, by calling methods on the internal Logger class.

This page focuses primarily on configuring Log4j through a configuration file. Information on

programmatically configuring Log4j can be found at

Extending Log4j 2andProgrammatic Log4jConfiguration

Note that unlike Log4j 1.x, the public Log4j 2 API does not expose methods to add, modify or remove

appenders and filters or manipulate the configuration in any way.

3、slf4j

全称 为Simple Logging Facade for JAVA,java简单日志门面。类似于Apache Common-Logging,是对不同日志框架提供的一个门面封装,可以在部署的时候不修改任何配置即可接入一种日志实现方案。但是,他在编译时静态绑 定真正的Log库。使用SLF4J时,如果你需要使用某一种日志实现,那么你必须选择正确的SLF4J的jar包的集合(各种桥接包)。

slf4j静态绑定原理:SLF4J 会在编译时会绑定import org.slf4j.impl.StaticLoggerBinder; 该类里面实现对具体日志方案的绑定接入。任何一种基于slf4j 的实现都要有一个这个类。如:org.slf4j.slf4j-log4j12-1.5.6: 提供对 log4j 的一种适配实现。注意:如果有任意两个实现slf4j 的包同时出现,那么就可能出现问题。

common-logging通过动态查找的机制, 在程序运行时自动找出真正使用的日志库。由于它使用了ClassLoader寻找和载入底层的日志库, 导致了象OSGI这样的框架无法正常工作,因为OSGI的不同的插件使用自己的ClassLoader。 OSGI的这种机制保证了插件互相独立,然而却使Apache Common-Logging无法工作。


slf4j编译时静态绑定真 正的Log库,因此可以再OSGI中使用。另外,SLF4J 支持参数化的log字符串,避免了之前为了减少字符串拼接的性能损耗而不得不写的if(logger.isDebugEnable()),现在你可以直接 写:logger.debug(“current user is: {}”, user)。拼装消息被推迟到了它能够确定是不是要显示这条消息的时候,但是获取参数的代价并没有幸免。


4、logback

Logback是由log4j创始人设计的又一个开源日记组件。logback 当前分成三个模块:logback-core,logback- classic和logback-access。logback-core是其它两个模块的基础模块。logback-classic是log4j的一个 改良版本。此外logback-classic完整实现SLF4J API使你可以很方便地更换成其它日记系统如log4j或JDK14 Logging。logback-access访问模块与Servlet容器集成提供通过Http来访问日记的功能。

LogBack作为一个通用可靠、快速灵活的日志框架,将作为Log4j的替代和SLF4J组成新的日志系统的完整实现。


最后引用网友的一个图:

wKioL1cZ1MbDTVYnAAQpr7TfrRs837.png