log4jdbc-log4j2浅析(sqlfx客户端采集日志的来源)-java 输出sql执行时间

前言

我们公司现在强制要求每个项目必须集成sqlfx客户端,然后把项目输出的jdbc日志收集到sqlfx服务器端进行分析,根据配置将分析报告以邮件的形式发给相关人员。
但是大家有没有思考过logback为何可以把sql执行时间输出到日志文件中呢?或者有没有遇到过明明把公司的logback.xml(logback-spring.xml)模板复制到项目中了,为啥别的日志都输出好好的,偏偏就没有输出sql相关日志呢?日志文件也生成了,为啥里面没内容呢?

主体

其实logback本身是没有格式化sql输出日志功能的,我们项目能输出sqlfx可以识别的jdbc日志,完全是xxx-log4jdbc-log4j2的功劳。下面我们就基于目前的xxx-log4jdbc-log4j21.17.1-SNAPSHOT版本一起看一下他的真实面目吧(后续代码均基于xxx-log4jdbc-log4j21.17.1-SNAPSHOT)。

1、组件集成

使用公司的logback模板我就不说了(springboot推荐使用logback-spring.xml,并且集成xxx-client-spring-boot-starter3.1.3版本)
同时还需要我们做两件事儿:把连接池驱动改为net.sf.log4jdbc.sql.jdbcapi.DriverSpy(必须),url改为jdbc:log4jdbc:DatabaseTypeName://IP:PORT/DBName?Charset=utf8¤tSchema=SchemaName&AppName=AppName;引入log4jdbc.log4j2.properties配置(springboot项目可以在yml中配置,引入sqlfxClient-3.1.3版本即可),把实际驱动类配置一下。
做完上面这些事儿我们再去跑项目,你就会发现大功告成,sql信息被输出了,那么他咋实现的呢?

2、问题及答案探索

问题1:为啥驱动类要改成net.sf.log4jdbc.sql.jdbcapi.DriverSpy

log4jdbc-log4j2浅析(sqlfx客户端采集日志的来源)-java 输出sql执行时间_第1张图片


log4jdbc-log4j2浅析(sqlfx客户端采集日志的来源)-java 输出sql执行时间_第2张图片
log4jdbc的驱动类DriverSpy和DatabaseTypeName的驱动类Driver均是集成java.sql.Driver,其实可以把前者理解为统一的一个驱动代理,实际执行sql命令的还是各个数据库的驱动,只不过由log4jdbc在上层代理了一下,这时候他就可以做一些小动作,其中拼接sql占位及sql执行时间就是他做的小动作。

问题2:连接串要改成啥样?

log4jdbc-log4j2浅析(sqlfx客户端采集日志的来源)-java 输出sql执行时间_第3张图片
在原连接串的中间加上log4jdbc即可,jdbc:log4jdbc:实际驱动类,例如原串jdbc:DatabaseTypeName://IP:PORT...改成jdbc:**log4jdbc**:DatabaseTypeName://IP:PORT...即可。
DriverSpy类中有个log4jdbcUrlPrefix属性,就是配置前缀的,后面他会截取咱们配置urljdbc:log4的后面部分,你会发现,咦,不就变成原始连接串了吗?写这个工具的人还真是聪明呀!

问题3:驱动类在什么时候在哪儿加载的?

log4jdbc-log4j2浅析(sqlfx客户端采集日志的来源)-java 输出sql执行时间_第4张图片
上图可看到DriverSpy类里面有一个静态代码块,首次调用该类时就会触发整个驱动初始化的逻辑
log4jdbc-log4j2浅析(sqlfx客户端采集日志的来源)-java 输出sql执行时间_第5张图片
主要还是看这里,我们在log4jdbc.log4j2.properties中配置的驱动会在这里被读取出来并加载,并且会先加载自己本身。
log4jdbc-log4j2浅析(sqlfx客户端采集日志的来源)-java 输出sql执行时间_第6张图片
配置项为log4jdbc.drivers,而且看到没有?他支持用英文逗号分隔配置多个
log4jdbc-log4j2浅析(sqlfx客户端采集日志的来源)-java 输出sql执行时间_第7张图片
配置文件中直接配置即可

有点儿注意事项
log4jdbc-log4j2浅析(sqlfx客户端采集日志的来源)-java 输出sql执行时间_第8张图片
这里针对mysql及oracle增加了特殊配置类

log4jdbc-log4j2浅析(sqlfx客户端采集日志的来源)-java 输出sql执行时间_第9张图片
这几个类都继承自net.sf.log4jdbc.sql.rdbmsspecifics.RdbmsSpecifics,里面只有一个主要的formatParameterObject,是用于拼接参数时格式化成对应数据库脚本用的

log4jdbc-log4j2浅析(sqlfx客户端采集日志的来源)-java 输出sql执行时间_第10张图片
为啥咱们的sql日志里面带了##^^##这个符号知道了不?奥秘就在这里

问题4:sql执行时间是如何输出的?

其实吧,虽然我不想这样说,但实际上他的底层也是用方法执行结束时的System.currentTimeMillis()减去方法执行开始时的System.currentTimeMillis()来实现的,并且做了格式化,就是这么简单

log4jdbc-log4j2浅析(sqlfx客户端采集日志的来源)-java 输出sql执行时间_第11张图片
看看这里的说明,如果启用了日志的开关,则获取连接时返回的是经过封装的连接,若未开启日志开关,则返回真实的连接实例

log4jdbc-log4j2浅析(sqlfx客户端采集日志的来源)-java 输出sql执行时间_第12张图片
看到没?红框里面,就是那两个代码起到了输出sql执行时间的关键作用!!!

log4jdbc-log4j2浅析(sqlfx客户端采集日志的来源)-java 输出sql执行时间_第13张图片
上图是他的实例化代码

log4jdbc-log4j2浅析(sqlfx客户端采集日志的来源)-java 输出sql执行时间_第14张图片
可以看到内部还是做了很多事儿的,并且也记录下来了实际的db连接对象(跟数据库连接池的理念一样,都是缓存下来,以供实际使用的时候代理操作)

log4jdbc-log4j2浅析(sqlfx客户端采集日志的来源)-java 输出sql执行时间_第15张图片
里面方法也是挺全的,跟普通的Connection对象没啥区别,只不过内部都是做了适配的

log4jdbc-log4j2浅析(sqlfx客户端采集日志的来源)-java 输出sql执行时间_第16张图片
只要连接是经过代理的,那么里面所有的东西就都用代理了

log4jdbc-log4j2浅析(sqlfx客户端采集日志的来源)-java 输出sql执行时间_第17张图片
跟ConnectionSpy基本一样,只不过加了用于处理特殊参数的对象

log4jdbc-log4j2浅析(sqlfx客户端采集日志的来源)-java 输出sql执行时间_第18张图片
也是做了一层封装,毕竟实现了内一套接口,大家都一样

log4jdbc-log4j2浅析(sqlfx客户端采集日志的来源)-java 输出sql执行时间_第19张图片
重中之重就在这里啦 看到红框没?他就是这样帮我们计算脚本执行耗时的

log4jdbc-log4j2浅析(sqlfx客户端采集日志的来源)-java 输出sql执行时间_第20张图片
通知logger对象,起床尿尿啦

log4jdbc-log4j2浅析(sqlfx客户端采集日志的来源)-java 输出sql执行时间_第21张图片
这里是具体打印步骤,第一个红框里面就是咱们常说的“超过多少毫秒会自动升级为error级别日志”

结语

具体项目怎么配置log4jdbc请参考我的另一篇文章 (内部资料,已隐藏。其实就是一个日志客户端组件,没啥特殊的)
网上关于log4jdbc的一些资料
http://log4jdbc.brunorozendo.com/
https://zacard.net/2015/09/24/log4jdbc20150924/
http://blog.oneapm.com/apm-tech/178.html
https://blog.csdn.net/blueheart20/article/details/26471019

 

你可能感兴趣的:(经验分享,数据库,技术管理)