探析Java日志框架

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介:         目前,几乎所有的应用程序中,都会用到日志框架来记录程序的运行信息。日志虽然不影响应用程序的运行结果,但是没有日志的应用程序是不健全,不完整的。良好的日志系统可以帮助我们快速的定位到程序问题,包括近几年火起来的日志分析系统,比如ELK,日志在我们系统中被重视起来,也起到了举足轻重的作用

        几乎所有的应用程序中,都会用到日志框架来记录程序的运行信息。日志虽然不影响应用程序的运行结果,但是没有日志的应用程序是不健全,不完整的。良好的日志系统可以帮助我们快速的定位到程序问题,还可以帮助我们分析系统的缺陷,比如近几年火起来的ELK。目前主流的日志框架也挺多,然而回顾以往,在开发过程中也没有深入了解,只是简单的引入一个依赖包,编码时log打印就完事了,不禁感慨,工具做的越来越便捷好用的同时,我们也不要忘了去探究更深层次的实现。

        本文以探究java日志框架体系为出发点,回顾一下日志系统的发展历程,了解一下目前主流的几种日志框架的区别及特性,最后从使用的角度分析日志框架在系统中的应用。

参考链接

意义

  • 开发调试: 在合适代码位置打印日志,并将程序运行中的一些参数信息也打印出来,可以更快的定位问题,也能在解决时提供必要的上下文信息。
  • 系统维护: 记录大部分的异常信息,通过收集日志信息可以对系统的运行状态进行实时监控预警。
  • 数据分析: 配合日渐成熟的大数据技术,对海量日志进行分析,可以获取有助于战略角色的信息,比如最火的ELK框架。

发展历程

        其实Java的日志框架体系是很混乱的。早期 Java 日志框架没有制定统一的标准,开发时经常会引入第三方的库,这第三方库中又可能使用了不同的日志框架,那么这个应用程序中其实包含了多种日志框架。

        比如经常会遇到,在项目启动的时候会出现以下红色字体的日志,提示有多个日志框架绑定到了slf4j,就是因为找到了多个日志框架的实现。关于这个问题如何解决会在后文中提到。

image.png

接下来看一下日志框架都有哪些,以及发展过程中经历了哪些阶段

image.png

        在 jdk1.3 之前,还没有内置的日志功能,只能使用原始的 System.out.println(), System.err.println()或者 e.printStackTrace()。通过把 debug 日志写到 StdOut 流,错误日志写到 ErrOut 流,以此记录应用程序的运行状态。这种原始的日志记录方式缺陷明显。后来慢慢出现了各种日志框架。整个发展历程也经历了如下五个阶段。

(一)Log4j
        Apache基金会的一个项目。1999年由Ceki Gülcü 创建,Log4j几乎成为了Java日志框架的实际标准。

(二)JUL
        Log4j 作为 Apache 基金会的一员,Apache 希望将 Log4j 引入 jdk,不过被 sun 公司拒绝了。随后,sun 模仿 Log4j,在 jdk1.4 中引入了 JUL(java.util.logging)。

(三)Commons Logging
        如果有更换日志组件的想法,如 log4j 换成 JUL,因为 API 完全不同,就需要改动代码。为了将日志接口与实现解耦,2002 年 Apache 推出了 JCL(Jakarta Commons Logging)。Commons Logging 定义了一套日志接口,具体实现则由 Log4j 或 JUL 来完成。Commons Logging 基于动态绑定来实现日志的记录,在使用时只需要用它定义的接口编码即可,程序运行时会使用 ClassLoader 寻找和载入底层的日志库,因此可以自由选择由 log4j 或 JUL 来实现日志功能。

(四)Slf4j & Logback
        Ceki Gülcü 与 Apache 基金会关于 Commons-Logging 制定的标准存在分歧,后来, Ceki Gülcü 离开 Apache 并先后创建了 Slf4j 和 Logback 两个项目。Slf4j 是一个日志门面,只提供接口,可以支持 Logback、JUL、log4j 等日志实现,Logback 提供具体的实现,它相较于 log4j 有更快的执行速度和更完善的功能。当前分为三个目标模块:

  • logback-core:核心模块,是其它两个模块的基础模块
  • logback-classic:是log4j的一个改良版本,同时完整实现 SLF4J API,可以很方便地更换成其它日记系统如log4j 或 JDK Logging
  • logback-access:访问模块与Servlet容器集成提供通过Http来访问日志的功能,是logback不可或缺的组成部分

(五)Log4j2
        为了维护在 Java 日志江湖的地位,防止 JCL、Log4j 被 Slf4j、Logback 组合取代 ,2014 年 Apache 推出了 Log4j 2。Log4j 2 与 log4j 不兼容,经过大量深度优化,其性能也有显著提升。

分类

        上文中提到的日志框架有 Log4j,Log4j2,Commons Logging,Slf4j,Logback,JUL。可以分为两种类型:门面日志日志系统

  • 日志门面: 只提供日志相关的接口定义,即相应的 API,而不提供具体的接口实现。日志门面在使用时,可以动态或者静态地指定具体的日志框架实现,解除了接口和实现的耦合,使用者可以灵活地选择日志的具体实现框架。
  • 日志系统: 与日志门面相对,它提供了具体的日志接口实现,应用程序通过它执行日志打印的功能。

image.png

框架

        在整个日志框架中主要包括日志门面、日志适配器、日志库三个部分,它们之间的关系如下图所示:

image.png

日志门面

        使用日志门面的目的就是将接口和实现解耦,使用的就是设计模式中的门面模式,而门面模式的核心思想就是: 外部客户端与一个子系统的通信,必须通过一个统一的外观对象进行,使得子系统更易于使用,其本质就是为子系统中的一组接口提供一个统一的高层接口, 在这种日志门面下,使用者可以随意切换底层使用的日志系统了。

        比如有这样的使用场景:系统开发时使用的是 Logback,引入了第三方A依赖,A依赖中使用的日志系统为 Log4j,又引入了 B.jar,而 B.jar 中使用的日志系统为 JUL。

        在这种场景下,如果每使用到一种日志系统,我们的系统都需要同时支持和维护的话,其繁琐程度不言而喻。为了解决这个问题,可以引入一个适配层,由适配层决定具体使用哪一种日志系统,应用程序中的调用者只管打印日志,而不必关心日志是如何被打印出来的,这样就避免了需要维护复杂日志系统的问题。Slf4j 和 Commons-Logging 就是适配层,而 JUL、Log4j 和 Logback 等就是打印日志的具体实现。

日志适配器

        Slf4j 的作者 Ceki Gülcü 当年因为觉得Commons-Logging的API设计以及性能都不够好,因而设计了 Slf4j。为了 Slf4j 能够兼容各种类型的日志系统实现,还设计了相当多的 adapter 和 bridge 来连接:

image.png

日志库

        日志库就是对日志门面中接口的具体实现,市面上的日志库有很多,比如log4j2,logback等等。比较常用的组合使用方式是:

  • Slf4j 与 Logback 组合使用,Logback 也必须配合 Slf4j 使用。由于 Logback 和 Slf4j 是同一个作者,兼容性非常好。
  • Commons Logging 与 Log4j 组合使用。

        看上面的组合方式也看得出来,一个门面搭配一个日志库使用,那为什么会出现两个门面呢?其实曾经Apache 试图说服 Log4j 以及其它的日志来按照 Commons-Logging 的标准编写,但是由于 Commons-Logging 的类加载机制在实际应用中存在问题(它使用 ClassLoader 寻找和载入底层的日志库),实现起来也不友好,因此 Log4j 的作者便开发了 Slf4j,与 Commons-Logging 两分天下。

对于上面两种组合方式,logback对比log4j有如下的几个优点:

  1. Slf4j 实现机制决定 Slf4j 限制较少,使用范围更广。相较于 Commons-Logging,Slf4j 在编译期间便静态绑定本地的 Log 库,其通用性要好得多。
  2. Logback 拥有更好的性能。Logback 声称:某些关键操作,比如判定是否记录一条日志语句的操作,其性能得到了显著的提高,这个操作在 Logback 中只需 3 纳秒,而在 Log4j 则需要 30 纳秒。
  3. Slf4j 支持参数化,可以使用{}占位符
  4. Logback 的所有文档是免费提供的,Log4j 只提供部分免费文档而需要用户去购买付费文档。
  5. MDC (Mapped Diagnostic Contexts) 用 Filter,将当前用户名等业务信息放入MDC 中,在日志 format 定义中即可使用该变量。具体而言,在诊断问题时,通常需要打出日志。如果使用 Log4j,则只能降低日志级别,但是这样会打出大量的日志,影响应用性能;如果使用 Logback,通过配置log的pattern即可将MDC中的业务信息跟随日志打印在同一行。

应用

如果是新项目使用日志框架,建议使用 Slf4j 与 Logback 组合,可通过如下配置进行集成:

<dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>2.0.0-alpha1</version>
</dependency>

<dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-core</artifactId>
        <version>1.2.10</version>
</dependency>
            
<dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.10</version>
</dependency>

        对于已有工程,需要根据所使用的日志库来确定门面适配器从而使用 Slf4j。Slf4j 的设计思想比较简洁,使用了 Facade 设计模式,Slf4j 本身只提供了一个 slf4j-api.jar 包,主要是日志的抽象接口,jar 包中本身并没有对抽象出来的接口做实现。对于不同的日志实现方案(例如 Logback,Log4j 等),封装出不同的桥接组件(例如 logback-classic-version.jar,slf4j-log4j12-version.jar),这样使用过程中可以灵活地选取自己项目里的日志实现。

举例说明,如果已有工程中使用了 Log4j 日志库,可通过如下配置进行集成:

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>2.0.0-alpha1</version>
</dependency>

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.32</version>
</dependency>

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

Slf4j 与其它日志组件调用关系图如下:

image.png

具体的接入方式如下:

image.png

        如果在同一项目中使用不同的组件时,会出现不同组件依赖的日志组件不一致的情况,这就需要统一日志方案,统一使用 Slf4j,把他们的日志输出重定向到 Slf4j,然后 Slf4j 又会根据绑定器把日志交给具体的日志实现工具。Slf4j 带有几个桥接模块,可以重定向 Log4j,JCL 和 java.util.logging 中的 Api 到 Slf4j。

举例说明:如果老代码中直接使用了 Log4j 日志库接口打印日志,需引入如下配置:

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>log4j-over-slf4j</artifactId>
    <version>1.7.32</version>
</dependency>

桥接方式如下:

image.png

常见问题

日志组件冲突

image.png

        如第二章提到的那个问题,Spring 本身的日志实现使用了 Commons Logging,如果想使用 Slf4j+Logback 组合,这时候需要在项目中将 Commons Logging 排除掉,通常会用到以下 3 种方案,各有利弊,可以根据项目的实际情况选择最适合自己项目的解决方案:

  • 方案一:采用 maven 的 exclusion 方案,如果有多个组件都依赖了 commons-logging,则需要在很多处增加 exclusion,比较繁琐。
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>5.3.14</version>
    <exclusions>
        <exclusion>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>
  • 在 maven 声明 commons-logging 的 scope 为 provided,在调试代码时有可能导致 IDE 将 commons-logging 放置在 classpath 下,从而导致程序运行时出现异常。
<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
    <scope>provided</scope>
</dependency>
相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
1月前
|
XML 安全 Java
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
本文介绍了Java日志框架的基本概念和使用方法,重点讨论了SLF4J、Log4j、Logback和Log4j2之间的关系及其性能对比。SLF4J作为一个日志抽象层,允许开发者使用统一的日志接口,而Log4j、Logback和Log4j2则是具体的日志实现框架。Log4j2在性能上优于Logback,推荐在新项目中使用。文章还详细说明了如何在Spring Boot项目中配置Log4j2和Logback,以及如何使用Lombok简化日志记录。最后,提供了一些日志配置的最佳实践,包括滚动日志、统一日志格式和提高日志性能的方法。
271 30
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
|
23天前
|
Java 数据库
在Java中使用Seata框架实现分布式事务的详细步骤
通过以上步骤,利用 Seata 框架可以实现较为简单的分布式事务处理。在实际应用中,还需要根据具体业务需求进行更详细的配置和处理。同时,要注意处理各种异常情况,以确保分布式事务的正确执行。
|
23天前
|
消息中间件 Java Kafka
在Java中实现分布式事务的常用框架和方法
总之,选择合适的分布式事务框架和方法需要综合考虑业务需求、性能、复杂度等因素。不同的框架和方法都有其特点和适用场景,需要根据具体情况进行评估和选择。同时,随着技术的不断发展,分布式事务的解决方案也在不断更新和完善,以更好地满足业务的需求。你还可以进一步深入研究和了解这些框架和方法,以便在实际应用中更好地实现分布式事务管理。
|
1月前
|
JSON Java Apache
非常实用的Http应用框架,杜绝Java Http 接口对接繁琐编程
UniHttp 是一个声明式的 HTTP 接口对接框架,帮助开发者快速对接第三方 HTTP 接口。通过 @HttpApi 注解定义接口,使用 @GetHttpInterface 和 @PostHttpInterface 等注解配置请求方法和参数。支持自定义代理逻辑、全局请求参数、错误处理和连接池配置,提高代码的内聚性和可读性。
126 3
|
2月前
|
存储 Java
深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。
【10月更文挑战第16天】本文深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。HashSet基于哈希表实现,添加元素时根据哈希值分布,遍历时顺序不可预测;而TreeSet利用红黑树结构,按自然顺序或自定义顺序存储元素,确保遍历时有序输出。文章还提供了示例代码,帮助读者更好地理解这两种集合类型的使用场景和内部机制。
46 3
|
2月前
|
存储 Java 数据处理
Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位
【10月更文挑战第16天】Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位。本文通过快速去重和高效查找两个案例,展示了Set如何简化数据处理流程,提升代码效率。使用HashSet可轻松实现数据去重,而contains方法则提供了快速查找的功能,彰显了Set在处理大量数据时的优势。
34 2
|
19天前
|
存储 缓存 安全
Java 集合框架优化:从基础到高级应用
《Java集合框架优化:从基础到高级应用》深入解析Java集合框架的核心原理与优化技巧,涵盖列表、集合、映射等常用数据结构,结合实际案例,指导开发者高效使用和优化Java集合。
29 4
|
1月前
|
人工智能 前端开发 Java
基于开源框架Spring AI Alibaba快速构建Java应用
本文旨在帮助开发者快速掌握并应用 Spring AI Alibaba,提升基于 Java 的大模型应用开发效率和安全性。
基于开源框架Spring AI Alibaba快速构建Java应用
|
1月前
|
消息中间件 Java 数据库连接
Java 反射最全详解 ,框架设计必掌握!
本文详细解析Java反射机制,包括反射的概念、用途、实现原理及应用场景。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
Java 反射最全详解 ,框架设计必掌握!
|
28天前
|
开发框架 Java 关系型数据库
Java哪个框架适合开发API接口?
在快速发展的软件开发领域,API接口连接了不同的系统和服务。Java作为成熟的编程语言,其生态系统中出现了许多API开发框架。Magic-API因其独特优势和强大功能,成为Java开发者优选的API开发框架。本文将从核心优势、实际应用价值及未来展望等方面,深入探讨Magic-API为何值得选择。
39 2