外观模式助力 | 从 JCL 到 SLF4J
前言
运行好好的老系统突然报了个生产问题
同事忙不过来,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 不一样,它继承了JpaDaoSupport
,JpaDaoSupport
又继承了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”,艾玛,设计模式里的外观模式呀
(所以,日常积累时最好能关注下关键名词的英文名
,有个印象指不定哪天就碰到了)
外观模式回顾
外观模式通过一个高层接口(Facade)来封装 subsystem,按需暴露 subsystem 中的方法,从而降低 subsystem 使用的复杂度
SLF4J 和 JCL 遵循外观模式的理念,使 Client 只关心打印日志而不用了解具体是怎么实现的日志打印
SLF4J 的具体实现有 logback、slf4j-simple 等 JCL 的具体实现有 log4j、java.util.logging 等
那 SLF4J 和 JCL 是怎么绑定实现的呢?
SLF4J 与 JCL 绑定具体实现
SLF4J 绑定 logback👇
图出自SLF4J官网
JCL 选择日志系统流程👇
按照顺序,找到第一个时终止
- classpath 下的 commons-logging.properties 文件:org.apache.commons.logging.Log 配置属性的值
- 系统中属性中名叫 org.apache.commons.logging.Log 的值
- 若 classpath 中有 log4j 包,则使用 Log4j 作为日志实现类
- 若 jdk 中有 java.util.Logging,则使用(jdk1.4 及之后有 java.util.Logging)
- 使用 commons-logging 自己提供的一个简单的日志实现类 SimpleLog
露出马脚
项目日志使用情况分析:
- 有 logback-classic.jar,可以确定 SLF4J 的实现为
logback
- 没有 commons-logging.properties 但有 log4j.jar,可以确定 JCL 的实现为
log4j
,而且也有 log4j.properties
虽然存在两套日志不太好,但也不是不行,为啥 DAO 层日志没打出来?
本地跑项目的时候发现控制台有关于 log4j 的 Warn:
一查,原来是 log4j 的配置文件没生效(因为位置不对),所以 DAO 层日志打不出来
解决日志不全问题
问题明确了就好解决
有2种方案:
- 把 log4j.properties 放到 classpath 下使它生效(亲测可用)
- 把 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)
因为slf4j-api.jar
是1.7.30版本,所以也找jcl-over-slf4j.jar
的1.7.30版本,碰碰运气
点进去,向下翻
看到compile dependencies
jcl-over-slf4j.jar
1.7.30 确实依赖的是slf4j-api.jar
是1.7.30
完美
然后就OK了,启动后没有 log4j 的 Warn,DAO 层日志也可正常打印:
总结
其实挺有感触的,没成想学到的设计模式这样用上了
因为了解外观模式的理念,所以在看 SLF4J 和 JCL 那一堆概念的时候没有畏难心理,而且很快就能理解它俩的核心实现,最终快速且简单的解决了日志问题
这不又挤出摸鱼时间了嘛哈哈
感谢阅读~不喜勿喷。欢迎讨论、指正
转载自:https://juejin.cn/post/7224903881728786492