likes
comments
collection
share

外观模式助力 | 从 JCL 到 SLF4J

作者站长头像
站长
· 阅读数 10

前言

运行好好的老系统突然报了个生产问题

同事忙不过来,so,摸鱼人加入战场

查日志

诶?这日志能打出 Service 层的咋没打出 DAO 层的?🙉

所幸,一顿协同后生产问题解决了

排查日志不全问题

该老项目基于 SpingMVC + JPA 开发,而且是个父子 Maven 项目

因为 Service 能打出 log 而 DAO 打不出来,且考虑到开发不规范的历史遗留问题,所以最开始想到的就是看看代码

Service 使用的 log 是平时开发常用的SLF4J

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
...
private static final Logger logger = LoggerFactory.getLogger(A.class);
...
logger.error("xxx");

DAO 使用 log 的方式却和 Service 不一样,它继承了JpaDaoSupportJpaDaoSupport又继承了DaoSupport,最终使用的是DaoSupport里的logger

package org.springframework.dao.support;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
...
public abstract class DaoSupport implements InitializingBean {
	protected final Log logger = LogFactory.getLog(getClass());
	...
}	

好嘛 SLF4J、Commons-logging,两套日志接口

猜测问题基本出在这了

最后发现还真是

SLF4J 与 Commons.logging(JCL)

它俩都是日志门面接口👇

SLF4J(Simple Logging Facade for Java)是简单日志门面 Jakarta Commons-logging(JCL)是 Apache 最早提供的日志门面

刚开始还反应了下啥是“门面”

一看到“Facade”,艾玛,设计模式里的外观模式

(所以,日常积累时最好能关注下关键名词的英文名,有个印象指不定哪天就碰到了)

外观模式回顾

外观模式助力 | 从 JCL 到 SLF4J

外观模式通过一个高层接口(Facade)来封装 subsystem,按需暴露 subsystem 中的方法,从而降低 subsystem 使用的复杂度

SLF4J 和 JCL 遵循外观模式的理念,使 Client 只关心打印日志而不用了解具体是怎么实现的日志打印

SLF4J 的具体实现有 logback、slf4j-simple 等 JCL 的具体实现有 log4j、java.util.logging 等

那 SLF4J 和 JCL 是怎么绑定实现的呢?

SLF4J 与 JCL 绑定具体实现

SLF4J 绑定 logback👇

图出自SLF4J官网

外观模式助力 | 从 JCL 到 SLF4J

JCL 选择日志系统流程👇

按照顺序,找到第一个时终止

  1. classpath 下的 commons-logging.properties 文件:org.apache.commons.logging.Log 配置属性的值
  2. 系统中属性中名叫 org.apache.commons.logging.Log 的值
  3. 若 classpath 中有 log4j 包,则使用 Log4j 作为日志实现类
  4. 若 jdk 中有 java.util.Logging,则使用(jdk1.4 及之后有 java.util.Logging)
  5. 使用 commons-logging 自己提供的一个简单的日志实现类 SimpleLog

露出马脚

项目日志使用情况分析:

  • 有 logback-classic.jar,可以确定 SLF4J 的实现为logback
  • 没有 commons-logging.properties 但有 log4j.jar,可以确定 JCL 的实现为log4j,而且也有 log4j.properties

虽然存在两套日志不太好,但也不是不行,为啥 DAO 层日志没打出来?

本地跑项目的时候发现控制台有关于 log4j 的 Warn:

外观模式助力 | 从 JCL 到 SLF4J

一查,原来是 log4j 的配置文件没生效(因为位置不对),所以 DAO 层日志打不出来

解决日志不全问题

问题明确了就好解决

有2种方案:

  1. 把 log4j.properties 放到 classpath 下使它生效(亲测可用)
  2. 把 JCL 的日志实现转为 SLF4J

用 Maven 构建项目时候 resources 目录就是默认的 classpath

第一种方案会使系统里存在两套日志实现,不太好

优先选择方案二

由上面 SLF4J 绑定 logback 的图可知,通过jcl-over-slf4j.jar可以把 JCL 转为 SLF4J 的实现👇

JCL 转为 logback 实现

首先在 pom.xml 中排除 commons-logging 的依赖:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-orm</artifactId>
    <exclusions>
        <exclusion>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>

然后在 pom.xml 中加入 jcl-over-slf4j 依赖:

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
    <version>???</version>
</dependency>

jcl-over-slf4j.jar有很多版本,该选哪一个呢?

Mvn Repository查询适配版本

既然引入jcl-over-slf4j.jar是为了把 JCL 转为 SLF4J 的实现,那肯定得和slf4j-api.jar版本适配(项目里使用的是1.7.30版本),不能随便找一个

可以通过 Mvn Repository 查询适配版本:

首先打开mvnrepository.com/ ,查询对应的jar(jcl-over-slf4j)

外观模式助力 | 从 JCL 到 SLF4J

因为slf4j-api.jar是1.7.30版本,所以也找jcl-over-slf4j.jar的1.7.30版本,碰碰运气

外观模式助力 | 从 JCL 到 SLF4J

点进去,向下翻

看到compile dependencies

外观模式助力 | 从 JCL 到 SLF4J

jcl-over-slf4j.jar 1.7.30 确实依赖的是slf4j-api.jar是1.7.30

完美


然后就OK了,启动后没有 log4j 的 Warn,DAO 层日志也可正常打印:

外观模式助力 | 从 JCL 到 SLF4J

总结

其实挺有感触的,没成想学到的设计模式这样用上了

因为了解外观模式的理念,所以在看 SLF4J 和 JCL 那一堆概念的时候没有畏难心理,而且很快就能理解它俩的核心实现,最终快速且简单的解决了日志问题

这不又挤出摸鱼时间了嘛哈哈


感谢阅读~不喜勿喷。欢迎讨论、指正