如何合理配置Oracle JDBC驱动日志以记录所有增删改查SQL语句
我希望记录应用程序发起的所有SQL语句,以便精准掌握其运行行为。我已参照相关方案为Oracle配置了Java Util Logging,尝试从最底层捕获相关日志。
我的应用使用Hibernate(当前版本为3.2.3.ga)。
当我在logging.properties中配置oracle.jdbc.level=CONFIG或oracle.jdbc.level=FINE时,日志量级比较合理,但完全看不到应用发起的update语句,只能看到select和delete语句,示例如下:
CONFIG oracle.jdbc: 73FCEAC9 SQL: select <REDACTED FOR STACK OVERFLOW>
当我在logging.properties中配置oracle.jdbc.level=FINEST时,可以看到update和insert语句,但日志量级过大完全不具备实用性。看起来这些语句是被FINEST级别下「记录内部方法调用」的逻辑捕获的,单条update语句会在日志中出现数十次,示例如下:
FINEST oracle.jdbc: 212E0078 Enter: "update <REDACTED FOR STACK OVERFLOW>" FINEST oracle.jdbc: 212E0078 Enter: "update <REDACTED FOR STACK OVERFLOW>", -1, -1 FINEST oracle.jdbc: 212E0078 Enter: "update <REDACTED FOR STACK OVERFLOW>", "[ {result_set_type=-1} {use_long_fetch=false} {result_set_concurrency=-1} ]" FINEST oracle.jdbc: 212E0078 Enter: "update <REDACTED FOR STACK OVERFLOW>", "[ {result_set_type=-1} {use_long_fetch=false} {result_set_concurrency=-1} ]" FINEST oracle.jdbc: 212E0078 Enter: void FINEST oracle.jdbc: 212E0078 Return: void FINEST oracle.jdbc: 212E0078 Exit: [0.012976 ms] FINEST oracle.jdbc: 2C5FB90F Enter: oracle.jdbc.driver.T4CConnection@212e0078, "update <REDACTED FOR STACK OVERFLOW>", "[ {result_set_type=-1} {use_long_fetch=false} {result_set_concurrency=-1} ]" FINEST oracle.jdbc: Enter: oracle.jdbc.driver.T4CConnection@212e0078 FINEST oracle.jdbc: Return: void FINEST oracle.jdbc: Exit: [0.012198 ms] FINEST oracle.jdbc: Enter: oracle.jdbc.driver.T4CConnection@212e0078, "[ {result_set_type=-1} {use_long_fetch=false} {result_set_concurrency=-1} ]" FINEST oracle.jdbc: Enter: oracle.jdbc.driver.T4CPreparedStatement@9887002 FINEST oracle.jdbc: Return: void FINEST oracle.jdbc: Exit: [0.016657 ms]
我原本以为oracle.jdbc.level=FINE配置可以捕获update语句,因为官方文档说明该级别会「记录所有公共方法的出入栈,提供JDBC操作的详细追踪」,但实际该级别记录的内容和oracle.jdbc.level=CONFIG几乎完全一致。
我已尝试以下版本的Oracle驱动(Gradle依赖格式):
compile group: 'com.oracle.database.jdbc.debug', name: 'ojdbc8_g', version: '12.2.0.1' compile group: 'com.oracle.database.jdbc.debug', name: 'ojdbc8_g', version: '21.3.0.0'
有没有办法配置Oracle驱动,使其只记录所有select、update、insert、delete语句,不产生多余日志?我还尝试过配置oracle.sql.level=FINE和oracle.level=FINER,但在这些级别及更粗的日志级别下,都没有输出和update相关的有效日志。
更新
我安装了p6spy,但它同样无法记录insert和update语句。不过有相关方案通过启用批处理日志功能解决了这个问题。本次遇到的Oracle驱动日志问题是否是同样的原因,即CONFIG级别不会记录批量更新语句?
解决方案
你的猜测是正确的,该问题的核心原因是Oracle JDBC驱动对批量执行的DML语句和单条执行语句的日志分级策略不同:单条执行的select、delete语句默认会在CONFIG/FINE级别输出,而批量执行的insert、update语句默认仅在FINEST级别输出,同时FINEST级别会默认打印所有内部方法的出入栈日志,导致日志冗余。
可通过以下两种方案实现仅输出所有SQL语句、无冗余日志的需求:
方案1:调整Oracle JDBC驱动日志配置,添加日志过滤
无需修改应用代码,仅调整logging.properties配置即可:
# 基础日志级别,保证常规单条SQL正常输出 oracle.jdbc.level=FINE # 仅针对PreparedStatement类开启FINEST级别,避免全局开启产生大量冗余日志 oracle.jdbc.driver.OraclePreparedStatement.level=FINEST # 配置日志过滤器,仅保留包含SQL关键字的日志,过滤掉方法出入栈的Enter/Return/Exit日志 oracle.jdbc.driver.OraclePreparedStatement.filter=java.util.logging.Filter \ public boolean isLoggable(LogRecord record) { \ String msg = record.getMessage(); \ return msg != null && (msg.contains("insert") || msg.contains("update") || msg.contains("delete") || msg.contains("select")); \ } # 开启批量执行日志输出 oracle.jdbc.batchLog=true
如果你的项目使用SLF4J桥接JUL,也可以直接在日志框架(Logback/Log4j)的配置中添加过滤规则,实现相同效果,配置更灵活。
方案2:调整p6spy配置输出批量SQL
你之前使用p6spy无法捕获insert/update就是因为没有开启批量日志,修改spy.properties配置即可:
# 移除batch类别的日志排除 excludecategories=info,debug,result,resultset # 显式包含批量执行日志 includecategories=batch # 可选:配置为单行输出,日志可读性更高 logMessageFormat=com.p6spy.engine.spy.appender.SingleLineFormat
该方案无需调整Oracle驱动本身的日志配置,输出的SQL格式更统一,还可以直接打印SQL的执行耗时、参数替换后的完整语句,实用性更高。
内容的提问来源于stack exchange,提问作者Kaypro II

