前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >slf4j 原理及使用原则

slf4j 原理及使用原则

作者头像
用户3147702
发布2022-12-21 17:56:37
9850
发布2022-12-21 17:56:37
举报

1. 引言

对于服务端运行的程序来说,日志是极为重要的,无论是日常运行状态的监控还是问题发生后的定位排查,都离不开日志。但在 java 语言中,有着许许多多不同的日志框架供我们使用。

通常,我们会习惯于使用其中的一两种,比如时下最流行的 log4j2 或是 java 原生的 java.util.logging。但在实际的开发中,我们经常会去依赖大量别人提供的 jar 包,我们不能去限制和要求给我们提供 jar 包的人会使用什么日志框架,这样一来,我们就必须学习每一种日志框架的配置方法,并且一一进行配置,否则我们就无法控制项目实际依赖的外部 jar 包中代码的日志行为了,这无疑是过于艰巨的任务,那么,有什么办法可以解决这个难题吗?slf4j 项目就是为此而生的。

2. slf4j 介绍

slf4j 是 simple logging facade for java 的缩写,可以翻译为 java 的简单日志外观。如上所述,slf4j 这个开源项目的目的就是为我们提供一个一致的 API 来使用不同的日志框架,通过将不同的日志框架桥接到我们熟悉的日志框架上,我们就可以实现用一套配置适配所有日志框架的目的了。

从 slf4j 的名称,以及上图,我们可以看出来,这是一个 facade 设计模式的实现,它通过提供一个日志抽象层,来实现对所有日志框架用法的桥接。

在实际使用中,我们只要依赖 slf4j 的相关依赖,然后通过 LoggerFactory 获取 logger 对象,即可用这个 logger 对象进行相关的日志打印,例如:

public class Demo { public static void main(String[] args) { LoggerFactory.getLogger(Demo.class).error("+++++++"); } }

代码语言:javascript
复制

2. slf4j 原理

slf4j 的体系结构如图所示:

图中,我们可以看到,整个体系分为 5 层:

  • 遗留层 -- 在没有 slf4j 时,我们使用每一个日志框架时所依赖的对应框架的组件。
  • 桥接层 -- 通过实现对应日志框架的入口类,实现对相应框架的桥接,从而让原本依赖对应框架组件的程序变成实际对 slf4j-api 的依赖。
  • 入口层 -- sfl4j 的真实入口。
  • 适配器层 -- sfl4j 用于将日志输出适配到指定日志框架实现的适配器模式实现。
  • 实现层 -- 用于将日志以指定日志框架输出的具体实现。

得益于 slf4j 体系结构分层的清晰,了解了上述五层,我想不用再做过多讲解,slf4j 的工作原理已经十分清楚了。

3. slf4j 使用三原则

在实际的使用中,我们常常会遇到一些问题,例如虽然配置了 slf4j 却没有按照预期以同一个日志框架的方式输出日志,日志仍然是出现在了多个地方,或者因为一系列包冲突导致项目无法启动,等等问题。这往往是对 slf4j 体系内的 jar 包依赖使用不当导致的。

根据 slf4j 的上述原理,桥接层的各个 jar 包实现了 java 各个日志框架的入口类,因此,我们要使用 slf4j 来桥接所有日志框架的话,就没有必要再保留遗留层的 jar 包了。

同时,由于我们最终要输出到某个我们熟悉并且配置好的目标日志框架上,所以适配器层和实现层应该只保留这个目标日志框架对应的那一套 jar 包,而绝不能依赖两套及以上,否则 slf4j 无法找到日志的输出方式,必然造成包冲突等问题。

既然我们要用目标日志框架来输出日志,我们自然要排除对应框架的桥接包,否则一边适配,又一边桥接,这就陷入到死循环中了。

总结起来,slf4j 使用的三个原则就是:

  1. 遗留层的包必须全部去除,这样才能保证遗留日志都会经过桥接包进入 slf4j-api
  2. 桥接层与适配器层的同一列的包不能共存,否则会死循环导致StackOverFlow
  3. 适配器层(包括logback-classic也算适配器层)最多只能选1个包存在,否则slf4j的日志有可能输出到非预料位置

4. jul 桥接 slf4j 失败问题

上述 slf4j 使用原则中有一个问题,那就是图中 inner-java 对应的 java.util.logging 包名下的日志框架是定义在 rt.jar 中的,我们不能排除这个框架包依赖,而由于双亲委派原则,我们也不能通过类加载机制覆盖这个包中的任何类,这样一来,slf4j 就无法实现用 jul-to-slf4j 实现对 jul 日志框架的桥接了。

这是 slf4j 使用中的一个常见的问题,你会发现虽然配置好了 slf4j 的依赖并且正常启动,但基于 jul 的日志仍然输出到了默认的位置,那么,如何来解决这个问题呢?

slf4j 的桥接组件 jul-to-slf4j 与其他桥接组件是完全不同的,它并没有实现它所桥接的 jul 的任何类,而是基于 jul 的框架机制,实现了一套日志输出 handler 来实现日志输出时调用 slf4j-api 的目的。这在官方文档中有详细的介绍:

http://www.slf4j.org/legacy.html

有两种方法可以实现对 jul 的桥接:

1. 配置文件

在配置文件 ${JDK_HOME}/jre/lib/logging.properties 中配置:

代码语言:javascript
复制
 handlers = org.slf4j.bridge.SLF4JBridgeHandler

2. 启动代码

第二种方法,在程序启动时执行下面的代码,先删除所有的 handler,再添加 slf4j 的 handler:

SLF4JBridgeHandler.removeHandlersForRootLogger(); SLF4JBridgeHandler.install();

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-10-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 小脑斧科技博客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 引言
  • 2. slf4j 介绍
  • 2. slf4j 原理
  • 3. slf4j 使用三原则
  • 4. jul 桥接 slf4j 失败问题
    • 1. 配置文件
      • 2. 启动代码
      相关产品与服务
      日志服务
      日志服务(Cloud Log Service,CLS)是腾讯云提供的一站式日志服务平台,提供了从日志采集、日志存储到日志检索,图表分析、监控告警、日志投递等多项服务,协助用户通过日志来解决业务运维、服务监控、日志审计等场景问题。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档