2. 日志模块(上)

news2024/11/26 4:34:55

日志需求分析

无论对于业务系统还是中间件来说,日志都是必不可少的基础功能。完善、清晰地日志可以帮助我们观测系统运行的状态,并且快速定位问题。现在让我们站在 MyBatis 框架开发者的角度,来简单做一下日志功能的需求分析:

  1. 作为一个成熟的中间件,日志功能是必不可少的。那么,MyBatis 是要自己实现日志功能,还是集成现有的日志呢?MyBatis 没有选择重复造轮子,而是直接集成了第三方日志框架。
  2. 第三方的日志框架种类繁多,常用的如 slf4j、log4j2、logback 等等,而且每种框架的日志级别定义、打印方式、配置格式都不尽相同。MyBatis 作为底层的中间件,每个依赖 MyBatis 的业务系统都可能使用不同的日志组件,那 MyBatis 如何进行兼容呢?如果业务方引入了多个日志框架,MyBatis 按照什么优先级进行选择?
  3. 在 MyBatis 的核心处理流程中,包括 SQL 拼接、SQL 执行、结果集映射等关键步骤,都是需要打印日志的,如果在各处都显式地进行 log.info(“xxx”) 打印肯定不太合适,那么如何将日志打印优雅地织入到核心流程中?

Adapter Pattern 适配器模式

我们要在系统中集成多个第三方组件,每个组件具有相似的功能,但是接口定义各不相同,而我们自己的系统希望以统一地方式对组件进行调用。这么典型的使用场景,第一时间就可以想到 Adapter Pattern 适配器模式。
我们先来复习一下经典适配器模式的 UML 图:
Adapter Pattern
(图片来源:https://refactoring.guru/design-patterns/adapter)

适配器模式的作用:将一个接口转换成满足客户端期望的另一个接口,使得接口不兼容的那些类可以一起工作。

Adapter 模式主要包含了以下角色:

  • Client:客户端,即我们自己的业务系统;
  • Client Interface:目标接口,定义了统一的、所有第三方组件都需要遵循的规范;
  • Service:需要集成的第三方组件,它包含了我们需要的功能,但是因为接口定义不匹配,所以无法直接使用;
  • Adapter:即最核心的适配器,它实现了 Client Interface 接口,并且对于 Service 进行了包装。这样一来,Adapter 就成为了既符合业务接口规范,同时又具备了期望的功能的组件,可以直接在项目中使用

集成第三方日志框架

了解了适配器模式之后,我们来看下 MyBatis 是怎么把它灵活运用于日志模块中的。

首先,MyBatis 定义了 Log 接口,并指定了四种日志级别:

/**
 * MyBatis日志接口定义
 * 指定了trace、debug、warn和error四种日志级别
 */
public interface Log {

  boolean isDebugEnabled();

  boolean isTraceEnabled();

  void error(String s, Throwable e);

  void error(String s);

  void debug(String s);

  void trace(String s);

  void warn(String s);

}

可以看出,这其实是所有主流日志框架所支持的级别的交集。

接下来,MyBatis 为常用的日志框架都进行了 Adapter 的实现。这里以常用的 slf4j 为例:

/**
 * slf4j日志框架的Adapter实现
 * 该Adapter实现了Log接口,并且内部包装了slf4j的Logger对象以完成实际的日志打印功能
 */
class Slf4jLoggerImpl implements Log {

  private final Logger log;

  public Slf4jLoggerImpl(Logger logger) {
    log = logger;
  }

  @Override
  public boolean isDebugEnabled() {
    return log.isDebugEnabled();
  }

  @Override
  public boolean isTraceEnabled() {
    return log.isTraceEnabled();
  }

  @Override
  public void error(String s, Throwable e) {
    log.error(s, e);
  }

  @Override
  public void error(String s) {
    log.error(s);
  }

  @Override
  public void debug(String s) {
    log.debug(s);
  }

  @Override
  public void trace(String s) {
    log.trace(s);
  }

  @Override
  public void warn(String s) {
    log.warn(s);
  }

}

该 Adapter 实现了 Log 接口,并且内部包装了 slf4j 的 org.slf4j.Logger 对象以完成实际的日志打印功能,是一种经典的适配器实现。

这样一来,日志适配器的整体结构就比较清晰了,我简单画一张图类比一下:
日志适配器
这里的对应关系为:

Adapter 模式MyBatis 实现
Client InterfaceLogger 接口
Serviceorg.slf4j.Logger 组件
AdapterSlf4jLoggerImpl 适配器

有了日志适配器,就可以在 MyBatis 中实现日志打印的功能了。但是第三方的日志框架众多,如果业务方引入了多个框架,MyBatis 应该如何决策该使用哪一个呢?我们来看下 MyBatis 中 LogFactory 日志工厂的实现:

/**
 * 日志工厂,通过getLog()方法获取日志实现类
 */
public final class LogFactory {

  public static final String MARKER = "MYBATIS";

  private static Constructor<? extends Log> logConstructor;

  //按照顺序依次尝试加载Log实现类
  //优先级为:slf4j -> commons-logging -> log4j2 -> log4j -> jdk-logging -> no-logging
  static {
    tryImplementation(LogFactory::useSlf4jLogging);
    tryImplementation(LogFactory::useCommonsLogging);
    tryImplementation(LogFactory::useLog4J2Logging);
    tryImplementation(LogFactory::useLog4JLogging);
    tryImplementation(LogFactory::useJdkLogging);
    tryImplementation(LogFactory::useNoLogging);
  }

  private LogFactory() {
    // disable construction
  }

  public static Log getLog(Class<?> clazz) {
    return getLog(clazz.getName());
  }

  public static Log getLog(String logger) {
    try {
      return logConstructor.newInstance(logger);
    } catch (Throwable t) {
      throw new LogException("Error creating logger for logger " + logger + ".  Cause: " + t, t);
    }
  }
  ...省略非必要代码
}

可以看到,在 LogFactory 的静态代码块中,按照指定的顺序尝试加载 Log 实现类,具体的优先级为:slf4j -> commons-logging -> log4j2 -> log4j -> jdk-logging -> no-logging 。如果加载成功,则不再继续加载。这样就实现了主流日志框架的选择。从 MyBatis 的选择中也可以看出,slf4j 确实是日志框架的首选。

最后,可以稍微留意一下,日志适配器中有一个 no-logging,它对应的是 NoLoggingImpl 类,它是一个空的实现,里面什么都没做。这其实是一种 Null Object Pattern(空对象模式),它也实现了目标接口,但是内部实际上是 Do Noting,这样能够以统一的方式使用目标组件,并且省去了很多判空操作。

/**
 * 空日志适配器
 * Null Object模式
 */
public class NoLoggingImpl implements Log {

  public NoLoggingImpl(String clazz) {
    // Do Nothing
  }

  @Override
  public boolean isDebugEnabled() {
    return false;
  }

  @Override
  public boolean isTraceEnabled() {
    return false;
  }

  @Override
  public void error(String s, Throwable e) {
    // Do Nothing
  }

  @Override
  public void error(String s) {
    // Do Nothing
  }

  @Override
  public void debug(String s) {
    // Do Nothing
  }

  @Override
  public void trace(String s) {
    // Do Nothing
  }

  @Override
  public void warn(String s) {
    // Do Nothing
  }

}

好了,到这里 MyBatis 的日志功能已经实现了。但是作为有追求的程序员,我们不能只满足于实现业务需求,还应该考虑提升代码的可扩展性,在面对新需求的时候可以尽可能少地修改现有代码。 那么 MyBatis 是如何实现优雅地打印日志的呢?我们下节再来分析。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/720716.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

1760_C语言中选择排序的实现

全部学习汇总&#xff1a; GreyZhang/c_basic: little bits of c. (github.com) 选择排序的实现思想跟冒泡排序的思想非常相近&#xff0c;二者的差一点在于&#xff1a;冒泡排序在比较交换的过程中交换的是两个位置的数据&#xff0c;而选择排序则是在遍历比较的过程中寻找最小…

web安全php基础_echo,print,print_r,var_dump 的用处及区别

echo & print 在 PHP 中有两个基本的输出方式&#xff1a; echo 和 print。 PHP echo 语句 echo 是一个语言结构&#xff0c;使用的时候可以不用加括号&#xff0c;也可以加上括号&#xff1a; echo 或 echo()。 显示字符串 下面的实例演示了如何使用 echo 命令输出字…

QtWebApp介绍、下载和搭建http轻量级服务器Demo

一、QtWebApp介绍 QtWepApp是一个C中的HTTP服务器库&#xff0c;其灵感来自Java Servlet。适用于Linux、Windows、Mac OS和Qt Framework支持的许多其他操作系统。   QtWebApp包含以下组件&#xff1a; • HTTP(S)1.0和1.1服务器 • 模板引擎 • 缓冲记录器   这些组件可以…

暑假第二天打卡

离散&#xff1a; &#xff08;1&#xff09;联结词集及其优先级 &#xff08;2&#xff09;真值表 真值表&#xff0c;最让人迷糊的就是p的真值是0&#xff0c;q的真值是0,p→q的真值是1&#xff0c;理解话虚假的前提可以推出任意结论 构造真值表关键在于从00……0开始&…

【单例模式】—— 每天一点小知识

&#x1f4a7; 单例模式 \color{#FF1493}{单例模式} 单例模式&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x1f984; 个人主页——微风撞见云的博客&#x1f390; &#x1f433; 《数据结构与算法》专栏的文章图文并茂&#x1f995;生动形…

点大商城V2_2.4.6 全开源版 百度+支付宝+QQ+头条+小程序端+unipp开源前端系统安装教程

播播资源了解到点大商城V2是一款采用全新界面设计支持多端覆盖的小程序应用&#xff0c;支持H5、微信公众号、微信小程序、头条小程序、支付宝小程序、百度小程序&#xff0c;本次测试全套安装的是序是点大商城V2独立版&#xff0c;包含全部插件&#xff0c;代码全开源&#xf…

java项目之高校二手交易平台(ssm+jsp+mysql)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于ssm的高校二手交易平台。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 &#x1f495;&#x1f495;作者&#xff1a;风歌&…

Android中级——IPC

IPC IPC是什么&#xff1f;多进程带来的问题IPC前提SerializableParcelableBinder Android中的IPCBundle文件共享MessengerAIDLContentProviderSocket不同IPC优缺点 Binder连接池 IPC是什么&#xff1f; Inter-Process Communcation&#xff0c;含义为进程间通信或者跨进程通信…

NB-IoT学习笔记 —— NB-IoT介绍

一、简介 NB-IoT 是指窄带物联网&#xff08;Narrow Band Internet of Things&#xff09;技术&#xff0c;是一种低功耗广域&#xff08;LPWA&#xff09;网络技术标准&#xff0c;基于蜂窝技术&#xff0c;用于连接使用无线蜂窝网络的各种智能传感器和设备&#xff0c;聚焦于…

Vite+Vue3+Cesium工程搭建及初始化

现如今的前端更新太快了,我记得我一年前还在写 vue2+cesium 的工程构建方式。现在转眼都 vue3 了。而且 vite 也越来越火逐渐成为趋势。那么这篇文章我们就来介绍一下如何使用 vite 构建一个 vue3+cesium 的工程。 首先我们可以打开 vite 的官网学习如何构建 vite 项目。第一…

Nginx缓存代理服务器

Nginx缓存代理服务器 一、实验部署 二、搭建Nginx缓存代理服务器 1.nginx反向缓存代理服务配置 ###关闭和禁止防火墙开机自启功能 systemctl stop firewalld systemctl disable firewalld setenforce 0 sed -i s/enforcing/disabled/ /etc/selinux/config2.安装nginx服务 v…

基于SSM的人力资源管理系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

剑指offer12.矩阵中的路径

太难了&#xff0c;想了一会儿没有头绪就直接看了题解。 class Solution {public boolean exist(char[][] board, String word) {int clowns board.length;int rows board[0].length;boolean[][] visited new boolean[clowns][rows];for(int i 0;i<clowns;i){for(int j0…

【QT】——QWidget窗口类

1.QWidget基本概念 QWidget 类是所有窗口类的父类 (控件类是也属于窗口类),QWidget 类的父类的 QObject, 也就意味着所有的窗口类对象只要指定了父对象, 都可以实现内存资源的自动回收.可以内嵌到其他窗口的内部&#xff0c;没有边框,需要指定父类窗口可以作为独立的窗口显示&…

DBeaver(一款非常好用的数据库管理工具)大批量数据导入导出非常友好

DBeaver(一款非常好用的数据库管理工具)大批量数据导入导出非常友好 背景说明&#xff1a; 最近因工作需要进行大批量的数据导入导出&#xff0c;很多工具都很难完成千万级以上的单表数据导入导出工作。后来找到了DBeaver这款工具&#xff0c;可以将千万级数据进行分批写入或导…

安装部署mysql

1.二进制方式安装 (1)下载压缩包 (2)创建用户和组 (3)解压 &#xff08;4&#xff09;创建软链接 (5)初始化 &#xff08;6&#xff09; 2.使用yum方式安装 (1)用yum install wget unzip bash-completion tree vim lrzsz net-tools -y命令安装常用软件 (2)用yum install mysql…

【数据仓库】FineBI数据可视化使用体验

FineBI介绍 FineBI是新一代自助式BI工具,企业客户多,服务范围广.凭借finebi简单流畅的操作,强劲的大数据性能和自助式的分析体验。 1&#xff0c;对个人用户来说&#xff0c;免费的无限期试用&#xff0c;解锁所有功能&#xff0c;除了限制两个并发访问&#xff0c;个人用户可以…

vue门户系统实现顶部菜单下拉效果

门户系统实现顶部菜单下拉效果 组件封装 直接上代码&#xff0c;已经封装成组件&#xff0c;只要传输正确的数据格式即可使用。 <template> <div class"head-v3" id"index"><div class"navigation-up"><div class"…

Java SSM 重制版(二)SpringMvc

SpringMVC基础 进入之前&#xff1a;《Spring核心内容》《JavaWeb》《JDK9-17新特性篇》 在前面学习完Spring框架技术之后&#xff0c;差不多会出现两批人&#xff1a;一批是听得云里雾里&#xff0c;依然不明白这个东西是干嘛的&#xff1b;还有一批就是差不多理解了核心思想…

大的zip文件使用7-zip软件分卷压缩和还原

使用7-zip软件进行分卷压缩和还原 1、7-zip软件的安装 官网&#xff1a;https://www.7-zip.org。 2、将比较大的zip文件拆分 设置压缩格式zip,分卷最大150M 拆分后 3、还原 还原后&#xff0c;又重新生成了原zip文件