Java常用日志框架介绍(上)

2019 年 9 月 24 日

Java常用日志框架介绍(上)

Java 常用日志框架介绍


1 概述


对于一个应用程序来说日志记录是必不可少的一部分。线上问题追踪,基于日志的业务逻辑统计分析等都离不日志。java 领域存在多种日志框架,目前常用的日志框架包括 Log4j 1,Log4j 2,Commons Logging,Slf4j,Logback,Jul。


2 常用类别介绍


2.1 Log4j


Apache Log4j 是一个基于 Java 的日志记录工具。它是由 Ceki Gülcü首创的,现在则是 Apache 软件基金会的一个项目。 Log4j 是几种 Java 日志框架之一。


2.2 Log4j 2


Apache Log4j 2 是 apache 开发的一款 Log4j 的升级产品。


2.3 Commons Logging


Apache 基金会所属的项目,是一套 Java 日志接口,之前叫 Jakarta Commons Logging,后更名为 Commons Logging。


2.4 Slf4j


类似于 Commons Logging,是一套简易 Java 日志门面,本身并无日志的实现。(Simple Logging Facade for Java,缩写 Slf4j)。


2.5 Logback


一套日志组件的实现(Slf4j 阵营)。


2.6 Jul (Java Util Logging)


自 Java1.4 以来的官方日志实现。


看了上面的介绍是否会觉得比较混乱,这些日志框架之间有什么异同,都是由谁在维护,在项目中应该如何选择日志框架,应该如何使用? 下文会逐一介绍。


3 历史


1996 年早期,欧洲安全电子市场项目组决定编写它自己的程序跟踪 API(Tracing API)。经过不断的完善,这个 API 终于成为一个十分受欢迎的 Java 日志软件包,即 Log4j。后来 Log4j 成为 Apache 基金会项目中的一员。


期间 Log4j 近乎成了 Java 社区的日志标准。据说 Apache 基金会还曾经建议 Sun 引入 Log4j 到 java 的标准库中,但 Sun 拒绝了。


2002 年 Java1.4 发布,Sun 推出了自己的日志库 JUL(Java Util Logging),其实现基本模仿了 Log4j 的实现。在 JUL 出来以前,Log4j 就已经成为一项成熟的技术,使得 Log4j 在选择上占据了一定的优势。


接着,Apache 推出了 Jakarta Commons Logging,JCL 只是定义了一套日志接口(其内部也提供一个 Simple Log 的简单实现),支持运行时动态加载日志组件的实现,也就是说,在你应用代码里,只需调用 Commons Logging 的接口,底层实现可以是 Log4j,也可以是 Java Util Logging。


后来(2006 年),Ceki Gülcü不适应 Apache 的工作方式,离开了 Apache。然后先后创建了 Slf4j(日志门面接口,类似于 Commons Logging)和 Logback(Slf4j 的实现)两个项目,并回瑞典创建了 QOS 公司,QOS 官网上是这样描述 Logback 的:The Generic,Reliable Fast&Flexible Logging Framework(一个通用,可靠,快速且灵活的日志框架)。


现今,Java 日志领域被划分为两大阵营:Commons Logging 阵营和 Slf4j 阵营。


Commons Logging 在 Apache 大树的笼罩下,有很大的用户基数。但有证据表明,形式正在发生变化。2013 年底有人分析了 GitHub 上 30000 个项目,统计出了最流行的 100 个 Libraries,可以看出 Slf4j 的发展趋势更好:



Apache 眼看有被 Logback 反超的势头,于 2012-07 重写了 Log4j 1.x,成立了新的项目 Log4j 2, Log4j 2 具有 Logback 的所有特性。


4 关系


Log4j 2 与 Log4j 1 发生了很大的变化,Log4j 2 不兼容 Log4j 1。


Commons Logging 和 Slf4j 是日志门面(门面模式是软件工程中常用的一种软件设计模式,也被称为正面模式、外观模式。它为子系统中的一组接口提供一个统一的高层接口,使得子系统更容易使用)。Log4j 和 Logback 则是具体的日志实现方案。可以简单的理解为接口与接口的实现,调用者只需要关注接口而无需关注具体的实现,做到解耦。


比较常用的组合使用方式是 Slf4j 与 Logback 组合使用,Commons Logging 与 Log4j 组合使用。


Logback 必须配合 Slf4j 使用。由于 Logback 和 Slf4j 是同一个作者,其兼容性不言而喻。


5 实现机制对比


5.1 Commons Logging 实现机制


Commons Logging 是通过动态查找机制,在程序运行时,使用自己的 ClassLoader 寻找和载入本地具体的实现。详细策略可以查看 commons-logging-*.jar 包中的 org.apache.commons.logging.impl.LogFactoryImpl.java 文件。由于 Osgi 不同的插件使用独立的 ClassLoader,Osgi 的这种机制保证了插件互相独立, 其机制限制了 Commons Logging 在 Osgi 中的正常使用。


5.2 Slf4j 实现机制


Slf4j 在编译期间,静态绑定本地的 Log 库,因此可以在 Osgi 中正常使用。它是通过查找类路径下 org.slf4j.impl.StaticLoggerBinder,然后在 StaticLoggerBinder 中进行绑定。


6 项目中的选择


如果是在一个新的项目中建议使用 Slf4j 与 Logback 组合,这样有如下的几个优点。


1)Slf4j 实现机制决定 Slf4j 限制较少,使用范围更广。由于 Slf4j 在编译期间,静态绑定本地的 LOG 库使得通用性要比 Commons Logging 要好。


2)Logback 拥有更好的性能。Logback 声称:某些关键操作,比如判定是否记录一条日志语句的操作,其性能得到了显著的提高。这个操作在 Logback 中需要 3 纳秒,而在 Log4J 中则需要 30 纳秒。LogBack 创建记录器(logger)的速度也更快:13 毫秒,而在 Log4J 中需要 23 毫秒。更重要的是,它获取已存在的记录器只需 94 纳秒,而 Log4J 需要 2234 纳秒,时间减少到了 1/23。跟 JUL 相比的性能提高也是显著的。


3)Commons Logging 开销更高


 1# 在使Commons Logging时为了减少构建日志信息的开销,通常的做法是 2if(log.isDebugEnabled()){ 3  log.debug("User name: " + 4    user.getName() + " buy goods id :" + good.getId()); 5} 6 7# 在Slf4j阵营,你只需这么做: 8log.debug("User name:{} ,buy goods id :{}", user.getName(),good.getId()); 910# 也就是说,Slf4j把构建日志的开销放在了它确认需要显示这条日志之后,减少内存和Cup的开销,使用占位符号,代码也更为简洁
复制代码


4)Logback 文档免费。Logback 的所有文档是全面免费提供的,不像 Log4J 那样只提供部分免费文档而需要用户去购买付费文档。


7Slf4j 的使用


7.1 Slf4j 与其它日志组件的关系说明


1)Slf4j 的设计思想比较简洁,使用了 Facade 设计模式,Slf4j 本身只提供了一个 slf4j-api-version.jar 包,这个 jar 中主要是日志的抽象接口,jar 中本身并没有对抽象出来的接口做实现。


2)对于不同的日志实现方案(例如 Logback,Log4j…),封装出不同的桥接组件(例如 logback-classic-version.jar,slf4j-log4j12-version.jar),这样使用过程中可以灵活的选取自己项目里的日志实现。


7.2 Slf4j 与其它日志组件调用关系图



7.3 Slf4j 与其他各种日志组件的桥接说明



具体的接入方式参见下图:



8Slf4j 源码分析


8.1 slf4j-api-version.jar 中几个核心类与接口



8.2 Slf4j 调用过程源码分析,只加入 slf4j-api-version.jar,不加入任何实现包


8.2.1 示例代码


https://github.com/chlsmile/slf4j-demo


8.2.2 pom 核心配置如下


1  <dependencies>2    <!--只有slf4j-api依赖-->3    <dependency>4      <groupId>org.slf4j</groupId>5      <artifactId>slf4j-api</artifactId>6      <version>1.7.13</version>7    </dependency>8  </dependencies>
复制代码


8.2.3 程序入口类如下



8.2.4 源码追踪分析


1)调用 LoggerFactory 的 getLogger()方法创建 Logger



2)调用 LoggerFactory 的 getILoggerFactory 方法来创建 ILoggerFactory



3)调用 LoggerFactory 的


performInitialization 方法来进行初始化



4)调用 LoggerFactory 的 bind()方法



5)调用 LoggerFactory 的


findPossibleStaticLoggerBinderPathSet()方法获取 StaticLoggerBinderPath 集合



6)调用 LoggerFactory 的


reportMultipleBindingAmbiguity()方法,记录绑定的 StaticLoggerBinder 信息



7)LoggerFactory 的


reportMultipleBindingAmbiguity()方法



8)LoggerFactory 的 bind()方法找不到 StaticLoggerBinder,


抛出 NoClassDefFoundError 异常



9)LoggerFactory 的 bind()方法捕获 NoClassDefFoundError 异常,匹配到 StaticLoggerBinder 关键词记录信息到控制台



10)LoggerFactory 的 performInitialization()方法内部调用 bind()方法结束



11)LoggerFactory 的 getLogger()方法内部 getILoggerFactory()方法调用完成,创建出 NOPLoggerFactory,然后由 NOPLoggerFactory 调用内部的 getLogger()方法,创建出 NOPLogger






12)App 类内部的 logger 实际为 NOPLogger,调用logger.info()方法实际调用的是 NOPLogger 的 info 方法




2019 年 9 月 24 日 18:19702

评论

发布
暂无评论
发现更多内容

玩转Redis高可用 - 哨兵(Sentinel)模式

Man

高可用 redis高可用 中间件

给 Spring Boot 项目减减肥!18.18M 到 0.18M 是如何做到的?

给你买橘子

Java 程序员 Spring Cloud 编码 SpringBoot 2

redis里的数据结构

流沙

redis

java 后端博客系统文章系统——No3

猿灯塔

计算机操作系统基础(十七)---进程同步之Unix域套接字

书旅

php laravel 线程 操作系统 进程

DOM 树的构建

法正

html DOM 前端进阶训练营

ARTS 打卡 第2周

Scotty

RESTful 架构及实践

pingan8787

Java 前端 RESTf

一个爱不释手的Apifox,让我扔掉 Postman的想法

给你买橘子

Java 编程 程序员 开发 Postman

啃碎并发(九):内存模型之基础概述

猿灯塔

Java 猿灯塔

图说前端-内存管理(1/3)

梦见君笑

前端 内存 前端进阶训练营

《精益思想》读后感分享

zhongzhq

高效工作 精益 精益思想 精益生产方式

基于Kubernetes实现的大数据采集与存储实践总结

岿然独存5

Docker Kubernetes S3 EFK Fluentd

分布式系统的一些基础理论

俊俊哥

分布式事务 CAP Base

Git 常用操作汇总-cheat sheet

多选参数

git GitHub gitlab gitee

游戏夜读 | 如何分析游戏体验?

game1night

如何搭建一个HBase集群

Rayjun

HBase

那些让程序员目瞪口呆的Bug

Java小咖秀

程序员 程序员人生 bug

Java 线程的生老病死

武培轩

Java 线程 多线程 并发 线程状态

使用 Dockerfile 创建镜像 | Docker 系列

AlwaysBeta

Docker 容器 镜像 Dockerfile 容器技术

刘华:上云还是不上云,这是一个问题

刘华Kenneth

架构 敏捷

架构师必须知道的架构知识

Chank

架构 架构师 Architecture Architect

如何基于 BitMap 进行海量数据分析

GrowingIO技术专栏

互联网 数据分析 科技互联网 数据化

图说前端-ArrayBuffers 和 SharedArrayBuffers(2/3)

梦见君笑

前端 内存管理 前端进阶训练营

图说前端-使用Atomics避免SharedArrayBuffers中的race conditions(3/3)

梦见君笑

前端 内存管理 前端进阶训练营

图解:深度优先搜索与广度优先搜索

淡蓝色

Java 数据结构 算法

redis系列之——Redis为什么这么快?

诸葛小猿

Java redis 程序员

如果你想写自己的Benchmark框架

程序那些事

JVM 性能调优 GC benchmark

猿灯塔:spring Boot Starter开发及源码刨析(三)

猿灯塔

Java 猿灯塔

无价值人生记录.0:浪费1000%时间去做一个用来节省1%时间的“轮子玩具”(上:因缘)

八苦-瞿昙

C# 程序员人生 随笔 随笔杂谈 aop

创业使人成长系列 (2)- 散伙协议

石云升

创业 股权 合伙人 散伙协议

Java常用日志框架介绍(上)-InfoQ