栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java - log4jdbc 使用与原理介绍(SQL/日志监控)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java - log4jdbc 使用与原理介绍(SQL/日志监控)

Java - log4jdbc(SQL/日志监控) 前言

之前老板让实现一个日志服务器,然而我们都不知道已经有现成的日志监控包直接使用(没有站在巨人肩膀上干活,害)

本篇主要介绍 log4jdbc,在进行系统开发时,我们一般会查看执行的SQL/了解SQL执行时间,这个时候其实可以代码在sql执行前后计算时间,框架执行sql的时候进行sql输出,其实也是可以实现的,但是有更简单的干嘛不用呢~,学习了 log4jdbc 然后来记录一下

log4jdbc介绍 1、简介

log4jdbc 是一个 Java JDBC Driver,可以记录 SQL 日志和 SQL 执行时间等信息,使用 SLF4J作为日志系统

日志系统相关内容:(一个日志门面 一个日志实现 构成日志系统)

2、特性
  • 同时支持 JDBC3 和 JDBC4
  • 配置简单
  • 可将 SQL 中的 ?更换成实际的参数
  • 能够 显示 SQL 的 执行时间
  • 显示 SQL Connection 数量
  • 可以与JDK1.4+和SLF4J1.X等大多数常见的JDBC驱动协同工作
  • 开放源码
3、驱动支持

log4jdbc 可以会加载以下驱动:

源码可看:net.sf.log4jdbc.sql.jdbcapi

Driver ClassDatabase Type
oracle.jdbc.driver.OracleDriverOlder Oracle Driver
oracle.jdbc.OracleDriverNewer Oracle Driver
com.sybase.jdbc2.jdbc.SybDriverSybase
net.sourceforge.jtds.jdbc.DriverjTDS SQL Server & Sybase driver
com.microsoft.jdbc.sqlserver.SQLServerDriverMicrosoft SQL Server 2000 driver
com.microsoft.sqlserver.jdbc.SQLServerDriverMicrosoft SQL Server 2005 driver
weblogic.jdbc.sqlserver.SQLServerDriverWeblogic SQL Server driver
com.informix.jdbc.IfxDriverInformix
org.apache.derby.jdbc.ClientDriverApache Derby client/server driver, aka the Java DB
org.apache.derby.jdbc.EmbeddedDriverApache Derby embedded driver, aka the Java DB
com.mysql.jdbc.DriverMySQL
org.postgresql.DriverPostgresSQL
org.hsqldb.jdbcDriverHSQLDB pure Java database
org.h2.DriverH2 pure Java database
4、logger 介绍

log4jdbc 中使用了以下 7 种logger,可以进行相应的配置来实现需求

logger描述
jdbc.sqlonly仅仅记录 SQL 语句,会将占位符?替换为实际的参数
jdbc.sqltiming记录 SQL 语句实际的执行时间
jdbc.audit除了 ResultSet 之外的所有JDBC调用信息,篇幅较长
jdbc.resultset包含 ResultSet 的信息,输出篇幅较长
jdbc.connection输出了 Connection 的 open、close 等信息
log4jdbc.debug内部调试使用,输出 log4jdbc spy 加载驱动时的信息
jdbc.resultsettable显示前滚结果集的记录器

一般常用的是这5个:jdbc.audit jdbc.resultset jdbc.sqlonly jdbc.sqltiming jdbc.connection

log4jdbc 使用

以下 基于 SpringBoot 框架的项目进行使用介绍

1、导包

使用首先导包


      org.bgee.log4jdbc-log4j2
      log4jdbc-log4j2-jdbc4.1
      1.16

2、简单使用 2.1 配置

在使用 log4jdbc 时,需要进行三个配置:

  • 更改数据库连接信息
  • 编写 log4jdbc.log4j2.properties 文件
  • 配置 logger

(1)更改数据库连接信息

主要修改 driverClassName 与 url,以yaml 配置为例:

  • url 加上前缀 jdbc:log4
  • driverClassName 改为 net.sf.log4jdbc.sql.jdbcapi.DriverSpy
# 修改前
driverClassName: com.mysql.jdbc.Driver
url: mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=UTF-8

# 修改后
driverClassName: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
url: jdbc:log4jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=UTF-8

(2)编写 log4jdbc.log4j2.properties 文件

在 resources 目录下新建 log4jdbc.log4j2.properties 文件,可配置信息如下:

默认文件名称为log4jdbc.log4j2.properties ,如果想自定义配置,需要在System properties文件中指定 log4jdbc.log4j2.properties.file ,value对应文件名称

log4jdbc.log4j2.properties.file=xxxx.properties

源码:

  • net.sf.log4jdbc.Properties

可配置字段:

logger描述
log4jdbc.spylogdelegator.namespy日志处理类,默认为 net.sf.log4jdbc.log4j2.Log4j2SpyLogDelegator
log4jdbc.debug.stack.prefix项目应用程序的包的部分或全部的前缀
log4jdbc.sqltiming.warn.threshold执行SQL的时间长度(毫秒),在该时间段内,SQL需要至少运行这么长时间才能生成警告消息
log4jdbc.sqltiming.error.threshold执行SQL的时间长度(毫秒),在该时间段内,SQL需要至少运行这么长时间才能生成错误消息
log4jdbc.dump.booleanastruefalseboolean 值显示为 0 和 1 ,为 true 时 boolean 值显示为 true 和 false,默认false
log4jdbc.dump.sql.maxlinelengthSQL显示的最大长度,默认90
log4jdbc.dump.fulldebugstacktrace在调试模式下是否转储完整堆栈跟踪,默认false
log4jdbc.statement.warn使用statement(不是PreparedStatement)时,在日志中是否与SQL一起显示警告,默认false
log4jdbc.dump.sql.selectselect语句是否输出,默认true
log4jdbc.dump.sql.insertinsert语句是否输出,默认true
log4jdbc.dump.sql.updateupdate语句是否输出,默认true
log4jdbc.dump.sql.deletedelete语句是否输出,默认true
log4jdbc.dump.sql.createcreate语句是否输出,默认true
log4jdbc.dump.sql.addsemicolon是否在 SQL 的行末添加一个分号,默认false
log4jdbc.auto.load.popular.drivers是否自动选择最佳jdbc驱动,默认true
log4jdbc.driversjdbc驱动,String类型
log4jdbc.trim.sql默认true
log4jdbc.trim.sql.extrablanklines默认true
log4jdbc.suppress.generated.keys.exception默认false

简单常用配置**(直接拿去用)**:

# 以下三个都需配置
# spy日志处理类
log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator
# jdbc驱动(这里对应的mysql驱动版本为8.x)
log4jdbc.drivers=com.mysql.cj.jdbc.Driver
# 自动选择最佳jdbc驱动
log4jdbc.auto.load.popular.drivers=false


# 以下三个配置看自己需求了
# 设置的值为项目应用程序的包的部分或全部的前缀
log4jdbc.debug.stack.prefix=blog
# 当该值为 false 时,boolean 值显示为 0 和 1 ,为 true 时 boolean 值显示为 true 和 false
log4jdbc.dump.booleanastruefalse=true
# 是否在 SQL 的行末添加一个分号
log4jdbc.dump.sql.addsemicolon=true

(3)配置 logger

在 resources 目录下新建 logback.xml 文件(默认文件名)

如果需要自定义,可以在yaml中指定:

logging:
  config: classpath:xxxx.xml

配置上述的常用的 5大logger:(看自己实际需求配置了)



    BLOG
    
    
    
    
    
    
        
            ${log.pattern}
            ${log.charset}
        
    

    
    
        
            ${LOG_HOME}/server.%d{yyyy-MM-dd-HH}.log
            60
        
        
            ${log.pattern}
            ${log.charset}
        
    

    
    
    
        
    
    
    
        
    
    
    
        
    
    
    
        
    
    
    
        
    
    
    
        
    

    
    
        
        
    

2.2、示例

上面配置好了,无需其他额外的配置,运行项目就可以直接看到相应的SQL执行情况,在控制台输出,输出的情况根据配置的情况有所不同

log4jdbc 原理

总结:其实底层还是调用logger(org.apache.logging.log4j.Logger / org.slf4j.Logger )来实现日志记录,它的工作只是在执行sql的前后做了一些操作,将相应的内容进行输出

代码总览:

以下面的数据源与驱动配置来介绍一下我理解的原理:

db-type: com.alibaba.druid.pool.DruidDataSource
driverClassName: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
url: jdbc:log4jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:eladmin}?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false

以下为 debug 查看的相应执行流程:

(1)加载 DruidDataSource 数据源加载相应的驱动 net.sf.log4jdbc.sql.jdbcapi.DriverSpy,获取数据库连接

  • net.sf.log4jdbc.sql.jdbcapi.DriverSpy

根据前缀,找到真实url的驱动:

static final private String log4jdbcUrlPrefix = "jdbc:log4";
// net.sf.log4jdbc.sql.jdbcapi.DriverSpy#getUnderlyingDriver
private Driver getUnderlyingDriver(String url) throws SQLException{
   if (url.startsWith(log4jdbcUrlPrefix)) {
      url = this.getRealUrl(url); // substring 前缀后的真实url
      Enumeration e = DriverManager.getDrivers();
      Driver d;
      while (e.hasMoreElements()) {
         d = e.nextElement();
         if (d.acceptsURL(url)) {
            return d;
         }
      }
   }
   return null;
}

获取到 Connection / ConnectionSpy(包装JDBC连接),主要是根据5大logger是否有一个配置Error级别来决定返回类型

// net.sf.log4jdbc.sql.jdbcapi.DriverSpy#connect
@Override
public Connection connect(String url, java.util.Properties info) throws SQLException
{
   Driver d = getUnderlyingDriver(url); //获取到驱动
   if (d == null) {
      return null;
   }
   // 真实url,substring前缀后的url
   url = this.getRealUrl(url);

   lastUnderlyingDriverRequested = d;
   long tstart = System.currentTimeMillis();
   // 根据log4jdbc.log4j2.properties 中指定的 log4jdbc.drivers 进行connect (com.mysql.cj.jdbc.Driver)
   Connection c = d.connect(url, info); 

   if (c == null) {
      throw new SQLException("invalid or unknown driver url: " + url);
   }
   if (log.isJdbcLoggingEnabled()) { // 如果5大logger有配置leverl为Error级别的就为true
      ConnectionSpy cspy = new ConnectionSpy(c, System.currentTimeMillis() - tstart, log); // 包装JDBC连接并报告方法调用、返回和异常。
      RdbmsSpecifics r = null;
      String dclass = d.getClass().getName(); // 指定的 log4jdbc.drivers name
      if (dclass != null && dclass.length() > 0){
         r = rdbmsSpecifics.get(dclass);
      }
       // defaultRdbmsSpecifics为 net.sf.log4jdbc.sql.rdbmsspecifics.RdbmsSpecifics
      if (r == null){
         r = defaultRdbmsSpecifics; // 主要是封装特定关系数据库管理系统的sql格式细节,以便为该RDMBS编写准确、可用的sql。
      }
      cspy.setRdbmsSpecifics(r);
      return cspy;
   }
   return c;
}

(2)执行SQL 日志处理流程

真正执行SQL的时候,会在 包装Statement并报告方法调用、返回和异常 的xxxSpy来进行相应的SQL执行逻辑

  • net.sf.log4jdbc.sql.jdbcapi.StatementSpy

以执行一条查询语句为例:

// net.sf.log4jdbc.sql.jdbcapi.StatementSpy 
@Override
public ResultSet executeQuery(String sql) throws SQLException
{
   String methodCall = "executeQuery(" + sql + ")";
   this.sql = sql;
   reportStatementSql(sql, methodCall); // SqlOnlyLogger记录
   long tstart = System.currentTimeMillis();
   try
   {
      ResultSet result = realStatement.executeQuery(sql); // 真正执行SQL
      reportStatementSqlTiming(System.currentTimeMillis() - tstart, sql, methodCall); // SqlTimingLogger记录
      ResultSetSpy r = new ResultSetSpy(this, result, this.log); 
      return (ResultSet) reportReturn(methodCall, r); // resultsetLogger记录
   }
   catch (SQLException s)
   {
      // 如果Logger配置了Error,即记录
      reportException(methodCall, s, sql, System.currentTimeMillis() - tstart);
      throw s;
   }
}

说明:

  • 本质是执行sql的前后做了一些操作,利用对应的Logger将相应的内容进行输出
  • 底层调用会根据指定的 log4jdbc.spylogdelegator.name 的相应 xxxDelegator 中的5大logger来进行记录
  • 5大logger也是调用日志实现的相应slf4j或者log4j来实现日志打印/输出
相关链接

github log4jdbc

log4jdbc 官网

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/842666.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号