前言
我們公司現在強制要求每個項目必須集成sqlfx客戶端,然後把項目輸出的jdbc日誌收集到sqlfx服務器端進行分析,根據配置將分析報告以郵件的形式發給相關人員。
但是大家有沒有思考過logback爲何可以把sql執行時間輸出到日誌文件中呢?或者有沒有遇到過明明把公司的logback.xml(logback-spring.xml)模板複製到項目中了,爲啥別的日誌都輸出好好的,偏偏就沒有輸出sql相關日誌呢?日誌文件也生成了,爲啥裏面沒內容呢?
主體
其實logback本身是沒有格式化sql輸出日誌功能的,我們項目能輸出sqlfx可以識別的jdbc日誌,完全是xxx-log4jdbc-log4j2的功勞。下面我們就基於目前的xxx-log4jdbc-log4j2
1.17.1-SNAPSHOT版本一起看一下他的真實面目吧(後續代碼均基於xxx-log4jdbc-log4j2
1.17.1-SNAPSHOT)。
1、組件集成
使用公司的logback模板我就不說了(springboot推薦使用logback-spring.xml,並且集成xxx-client-spring-boot-starter
3.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的驅動類DriverSpy和DatabaseTypeName
的驅動類Driver均是集成java.sql.Driver,其實可以把前者理解爲統一的一個驅動代理,實際執行sql命令的還是各個數據庫的驅動,只不過由log4jdbc在上層代理了一下,這時候他就可以做一些小動作,其中拼接sql佔位及sql執行時間就是他做的小動作。
問題2:連接串要改成啥樣?
在原連接串的中間加上log4jdbc即可,jdbc:log4jdbc:實際驅動類,例如原串jdbc:DatabaseTypeName://IP:PORT...
改成jdbc:**log4jdbc**:DatabaseTypeName://IP:PORT...
即可。
DriverSpy類中有個log4jdbcUrlPrefix
屬性,就是配置前綴的,後面他會截取咱們配置urljdbc:log4
的後面部分,你會發現,咦,不就變成原始連接串了嗎?寫這個工具的人還真是聰明呀!
問題3:驅動類在什麼時候在哪兒加載的?
上圖可看到DriverSpy類裏面有一個靜態代碼塊,首次調用該類時就會觸發整個驅動初始化的邏輯
主要還是看這裏,我們在log4jdbc.log4j2.properties
中配置的驅動會在這裏被讀取出來並加載,並且會先加載自己本身。
配置項爲log4jdbc.drivers
,而且看到沒有?他支持用英文逗號分隔配置多個
配置文件中直接配置即可
有點兒注意事項
這裏針對mysql及oracle增加了特殊配置類
這幾個類都繼承自net.sf.log4jdbc.sql.rdbmsspecifics.RdbmsSpecifics
,裏面只有一個主要的formatParameterObject
,是用於拼接參數時格式化成對應數據庫腳本用的
爲啥咱們的sql日誌裏面帶了##^^##這個符號知道了不?奧祕就在這裏
問題4:sql執行時間是如何輸出的?
其實吧,雖然我不想這樣說,但實際上他的底層也是用方法執行結束時的System.currentTimeMillis()
減去方法執行開始時的System.currentTimeMillis()
來實現的,並且做了格式化,就是這麼簡單
看看這裏的說明,如果啓用了日誌的開關,則獲取連接時返回的是經過封裝的連接,若未開啓日誌開關,則返回真實的連接實例
看到沒?紅框裏面,就是那兩個代碼起到了輸出sql執行時間的關鍵作用!!!
上圖是他的實例化代碼
可以看到內部還是做了很多事兒的,並且也記錄下來了實際的db連接對象(跟數據庫連接池的理念一樣,都是緩存下來,以供實際使用的時候代理操作)
裏面方法也是挺全的,跟普通的Connection對象沒啥區別,只不過內部都是做了適配的
只要連接是經過代理的,那麼裏面所有的東西就都用代理了
跟ConnectionSpy基本一樣,只不過加了用於處理特殊參數的對象
也是做了一層封裝,畢竟實現了內一套接口,大家都一樣
重中之重就在這裏啦 看到紅框沒?他就是這樣幫我們計算腳本執行耗時的
通知logger對象,起牀尿尿啦
這裏是具體打印步驟,第一個紅框裏面就是咱們常說的“超過多少毫秒會自動升級爲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