Java 日志体系(二)jcl 和 slf4j

Java 日志体系(二)jcl 和 slf4j

  1. 《java 日志体系(一)统一日志》:https://www.cnblogs.com/binarylei/p/9828166.html
  2. 《Java 日志体系(二)jcl 和 slf4j》:https://www.cnblogs.com/binarylei/p/10781582.html

前面介绍了 jdk 自带的 logging、log4j1、log4j2、logback 等实际的日志框架。对于开发者而言,每种日志都有不同的写法。如果我们以实际的日志框架来进行编写,代码就限制死了,之后就很难再更换日志系统,很难做到无缝切换。

所以我们应该是按照一套统一的 API 来进行日志编程,实际的日志框架来实现这套 API,这样的话,即使更换日志框架,也可以做到无缝切换。这就是 commons-logging 与 slf4j 的初衷。

下面就来介绍下 commons-logging 与 slf4j 这两个门面如何与上述四个实际的日志框架进行集成的呢。

一、SLF4J 和 Commons-Logging 如何绑定具体的日志实现

《SLF4J 和 Commons-Logging 日志工具的区别》:http://ifeve.com/simplifying-distinction-between-sl4j/

编译时绑定和运行时绑定

当我第一次阅读关于编译时绑定时,感觉很模糊:一个 java 库如何能用不同的依赖编译时绑定的框架来记录日志?答案是“编译时”绑定只适用于这样的情况-对 SLF4J logger 的实现时,SLF4J “被编译”。然而,你仍可以在运行时使用不同的绑定。

SLF4J 不使用类加载器,而是,很简单:它加载 org.slf4j.impl.StaticLoggerBinder。每一个 SLF4J 的实现(例如 slf4j-log4j 绑定)提供一个有确切名称的类。所以这里没有疑惑,在运行时,相同的情况发生了:类被从类路径里直接取出,没有任何魔术运行。如果在类路劲下没有 slf4j 实现方法会怎么样? 怎样…然后会没有任何日志。

二、commons-logging

《jcl 与 jul、log4j1、log4j2、logback 集成》:https://jybzjf.iteye.com/blog/2238792
《Commons-Logging 存在的 ClassLoader 问题详解》:https://yq.aliyun.com/articles/46888

2.1 简单的使用

引入 maven 依赖,以 log4j 为例:

<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

使用如下,日志将以 log4j 输出:

@Test
public void test() {
    Log log = LogFactory.getLog(JclTest.class);
    log.info("jcl log");
}

2.2 commons-logging 绑定日志实现

LogFactory.getLog(JclTest.class) 的源码如下:

public static Log getLog(Class clazz) throws LogConfigurationException {
    return getFactory().getInstance(clazz);
}

上述获取 Log 的过程大致分成 2 个阶段

  • 获取 LogFactory 的过程 (从字面上理解就是生产 Log 的工厂)。commons-logging 默认提供的 LogFactory 实现:LogFactoryImpl
  • 根据 LogFactory 获取 Log 的过程。commons-logging 默认提供的 Log 实现:Jdk14Logger、Log4JLogger、SimpleLog。

来看下 commons-logging 包中的大概内容:

下面来详细说明:

2.2.1 获取 LogFactory 的过程

从下面几种途径来获取 LogFactory

(1) 系统属性中获取

System.getProperty("org.apache.commons.logging.LogFactory")

(2) 使用 java 的 SPI 机制

对于 java 的 SPI 机制,详细内容可以自行搜索,这里不再说明。搜寻路径如下:

META-INF/services/org.apache.commons.logging.LogFactory

简单来说就是搜寻哪些 jar 包中含有搜寻含有上述文件,该文件中指明了对应的 LogFactory 实现

(3) 从 commons-logging 的配置文件

commons-logging 也是可以拥有自己的配置文件的,名字为 commons-logging.properties,只不过目前大多数情况下,我们都没有去使用它。如果使用了该配置文件,尝试从配置文件中读取属性 "org.apache.commons.logging.LogFactory" 对应的值

(4) 默认的 org.apache.commons.logging.impl.LogFactoryImpl

LogFactoryImpl 是 commons-logging 提供的默认实现

2.2.2 根据 LogFactory 获取 Log 的过程

这时候就需要寻找底层是选用哪种类型的日志

就以 commons-logging 提供的默认实现为例,来详细看下这个过程:

(1) 从 commons-logging 的配置文件中寻找 Log 实现类的类名

从commons-logging.properties 配置文件中寻找属性为 "org.apache.commons.logging.Log" 对应的 Log 类名

(2) 从系统属性中寻找 Log 实现类的类名

System.getProperty("org.apache.commons.logging.Log")

(3) 如果上述方式没找到,则从 classesToDiscover 属性中寻找

classesToDiscover 属性值如下:

private static final String[] classesToDiscover = {
    "org.apache.commons.logging.impl.Log4JLogger",
    "org.apache.commons.logging.impl.Jdk14Logger",
    "org.apache.commons.logging.impl.Jdk13LumberjackLogger",
    "org.apache.commons.logging.impl.SimpleLog"
};

它会尝试根据上述类名,依次进行创建,如果能创建成功,则使用该 Log,然后返回给用户。

三、slf4j

相比于 commons-logging 采用 classloader 这种复杂的绑定方式,SLF4J 提供了更简单、更明确和同样动态的方法。

SLF4J 不使用类加载器,而是,很简单:它加载 org.slf4j.impl.StaticLoggerBinder。每一个 SLF4J 的实现(例如slf4j-log4j 绑定)提供一个有确切名称的类。

@Test
public void test() {
    Logger logger = LoggerFactory.getLogger(Slf4jTest.class);
    logger.info("slf4j log");
}

我们来看一下 LoggerFactory.getLogger(Slf4jTest.class) 到底做了什么事情呢?slf4j-api-1.7.25.jar 下 LoggerFactory#getLogger 方法如下:

public static Logger getLogger(String name) {
    ILoggerFactory iLoggerFactory = getILoggerFactory();
    return iLoggerFactory.getLogger(name);
}

LoggerFactory#getLogger 调用链如下,最终调用 StaticLoggerBinder#getSingleton 方法,这个类由具体的桥接包来实现。

// getLogger -> getILoggerFactory -> performInitialization -> bind -> StaticLoggerBinder.getSingleton()
public static ILoggerFactory getILoggerFactory() {
    // 1. 初始化日志系统,调用 performInitialization -> bind -> StaticLoggerBinder.getSingleton()
    if (INITIALIZATION_STATE == UNINITIALIZED) {
        synchronized (LoggerFactory.class) {
            if (INITIALIZATION_STATE == UNINITIALIZED) {
                INITIALIZATION_STATE = ONGOING_INITIALIZATION;
                performInitialization();
            }
        }
    }
    // 2. 日志系统初始化成功,则返回 StaticLoggerBinder.getSingleton().getLoggerFactory()
    switch (INITIALIZATION_STATE) {
    case SUCCESSFUL_INITIALIZATION:
        return StaticLoggerBinder.getSingleton().getLoggerFactory();
    // 3. 没有 StaticLoggerBinder 则不输出任何日志
    case NOP_FALLBACK_INITIALIZATION:
        return NOP_FALLBACK_FACTORY;
    case FAILED_INITIALIZATION:
        throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
    case ONGOING_INITIALIZATION:
        // support re-entrant behavior.
        // See also http://jira.qos.ch/browse/SLF4J-97
        return SUBST_FACTORY;
    }
    throw new IllegalStateException("Unreachable code");
}

以 logback-classic-1.2.3.jar 为例,其包下也有 org.slf4j.impl.StaticLoggerBinder 类。 StaticLoggerBinder#getLoggerFactory 返回了 LoggerContext 类。LoggerContext 也实现了 ILoggerFactory 接口,即 getLogger(String name) 方法。

public ILoggerFactory getLoggerFactory() {
    if (!initialized) {
        return defaultLoggerContext;
    }

    if (contextSelectorBinder.getContextSelector() == null) {
        throw new IllegalStateException("contextSelector cannot be null. See also " + NULL_CS_URL);
    }
    return contextSelectorBinder.getContextSelector().getLoggerContext();
}

参考:

  1. 《jcl 与 jul、log4j1、log4j2、logback 集成》:https://jybzjf.iteye.com/blog/2238792
  2. 《Commons-Logging 存在的 ClassLoader 问题详解》:https://yq.aliyun.com/articles/46888
  3. 《SLF4J 和 Commons-Logging 日志工具的区别》:http://ifeve.com/simplifying-distinction-between-sl4j/


每天用心记录一点点。内容也许不重要,但习惯很重要!

原文地址:https://www.cnblogs.com/binarylei/p/10781582.html

时间: 2024-08-05 07:25:57

Java 日志体系(二)jcl 和 slf4j的相关文章

混乱的 Java 日志体系

混乱的 Java 日志体系 2016/09/10 | 分类: 基础技术 | 0 条评论 | 标签: LOG 分享到: 原文出处: xirong 一.困扰的疑惑 目前的日志框架有 jdk 自带的 logging,log4j1.log4j2.logback ,这些框架都自己定制了日志 API ,并且有相应的实现:目前用于实现日志统一的框架 Apache commons-logging.slf4j ,遵循「面向接口编程」的原则,这两大框架可以让用户在程序运行期间去选择具体的日志实现系统(log4j1\

Java日志体系居然这么复杂?——架构篇

摘自:https://www.cnblogs.com/xuningfans/p/12151851.html 本文是一个系列,欢迎关注 日志到底是何方神圣?为什么要使用日志框架? 想必大家都有过使用System.out来进行输出调试,开发开发环境下这样做当然很方便,但是线上这样做就有麻烦了: 系统一直运行,输出越来越多,磁盘空间逐渐被写满 不同的业务想要把日志输出在不同的位置 有些场合为了更高性能,尽量控制减少日志输出,需要动态调整日志输出量 自动输出日志相关信息,比如:日期.线程.方法名称等等

Java 日志体系

参考资料: 混乱的 Java 日志体系 Java常用日志框架介绍 Java常用的日志框架对比和深入分析

Java日志体系 —— slf4j

1.1 简介 与commons-logging相同,slf4j也是一个通用的日志接口,在程序中与其他日志框架结合使用,并对外提供服务. Simple Logging Facade for Java简称 slf4j,Java简单日志门面系统.在我们的代码中,不需要显式指定具体日志框架(例如:java.util.logging.logback.log4j),而是使用slf4j的API来记录日志便可,最终日志的格式.记录级别.输出方式等通过具体日志框架的配置来实现,因此可以在应用中灵活切换日志系统.

架构师必备,带你弄清混乱的JAVA日志体系

引言还在为弄不清 commons-logging-xx.jar . log4j-xx.jar . sl4j-api-xx.jar 等日志框架之间复杂的关系而感到烦恼吗? 还在为如何统一系统的日志输出而感到不知所措嘛? 您是否依然存在这样的烦恼.比如,要更改spring的日志输出为log4j 2,却不知该引哪些jar包,只知道去百度一下所谓的博客,照着人家复制,却无法弄懂其中的原理? 不要急,不要方!本文带你们弄懂其中的原理,只要你静下心看本文,你就能随心所欲更改你系统里的日志框架,统一日志输出!

Java日志体系 —— logback

1.1 简介 师出同门,与log4j一样,logback也是由Ceki Gülcü开发的开源日志组件,可以说是log4j的改进版:在现如今的项目中,logback的出现次数越来越多,是目前主流首选的日志记录工具. 1.2 logback结构 logback分成三个模块:logback-core,logback- classic,logback-access. logback-core提供了logBack的核心功能,是另外两个组件的基础: logback-classic模块实现了SLF4J API

Java日志体系 —— 日志框架切换

通过 SLF4J 统一日志 在实际的日志转换过程中,SLF4J其实是充当了一个中介的角色.例如当我们一个项目原来是使用LOG4J进行日志记录,但是我们要换成LogBack进行日志记录. 此时我们需要先将LOG4J转换成SLF4J日志系统,再从SLF4J日志系统转成LogBack日志系统. 从日志框架转向SLF4J jul-to-slf4j:jdk-logging到slf4j的桥梁 log4j-over-slf4j:log4j1到slf4j的桥梁 jcl-over-slf4j:commons-lo

java日志系列(logback common-loging slf4j)—— log4j

java日志是开发过程中一个绕不过的坎,优雅的日志是开发一个优秀系统必不可少的.java日志组件有很多种,比如经常用到的log4j. 却一直没有对java日志系列进行一个系统的学习与整理.本文将详细整理java日志系列组件. 1.从零开始 日志记录是应用程序运行中必不可少的一部分.具有良好格式和完备信息的日志记录可以在程序出现问题时帮助开发人员迅速地定位错误的根源. 就像我们在简单代码里加入System.out.println();是一样的.我们需要看到一些输出来让我们看到这个程序正在跑着.当然

Java日志体系(八)最佳实践

java常用日志框架关系 Log4j 2与Log4j 1发生了很大的变化,Log4j 2不兼容Log4j 1. Logback必须配合Slf4j使用.由于Logback和Slf4j是同一个作者,其兼容性不言而喻. 比较常用的组合使用方式是Slf4j与Logback组合使用,Commons Logging与Log4j组合使用. 项目中选择日志框架选择 如果是在一个新的项目中建议使用Slf4j与Logback组合,这样有如下的几个优点. Slf4j实现机制决定Slf4j限制较少,使用范围更广.由于S