使用 JDBC 时如何为 SQL 语句启用日志记录

Posted

技术标签:

【中文标题】使用 JDBC 时如何为 SQL 语句启用日志记录【英文标题】:How to enable logging for SQL statements when using JDBC 【发布时间】:2015-01-19 13:14:39 【问题描述】:

我正在尝试通过连接到 Eclipse IDE 中的 Oracle 数据库来使用我的 JDBC 程序启用日志。

我已经浏览了这篇 SO 帖子 JDBC logging to file 然后我创建了下面的 java 程序并从我的 eclipse IDE 运行它,但我无法看到 JDBC 驱动程序类生成的任何日志。

import java.io.File;
import java.io.FileInputStream;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
import java.util.logging.LogManager;
import java.util.logging.Logger;

public class Logging 

    static Logger log = Logger.getLogger(Logging.class.toString());
    static Connection con = null;

    public static void main(String[] args) throws SQLException,
            ClassNotFoundException 
        System.setProperty("oracle.jdbc.Trace", Boolean.TRUE.toString());
        System.setProperty("java.util.logging.config.file",
                "OracleLog.properties");
        log.info("Test Message");
        enableLogging(false);
        getConnection();
        closeConnection();
    

    static private void enableLogging(boolean logDriver) 
        try 
            oracle.jdbc.driver.OracleLog.setTrace(true);

            // compute the ObjectName
            String loader = Thread.currentThread().getContextClassLoader()
                    .toString().replaceAll("[,=:\"]+", "");
            javax.management.ObjectName name = new javax.management.ObjectName(
                    "com.oracle.jdbc:type=diagnosability,name=" + loader);

            // get the MBean server
            javax.management.MBeanServer mbs = java.lang.management.ManagementFactory
                    .getPlatformMBeanServer();

            // find out if logging is enabled or not
            System.out.println("LoggingEnabled = "
                    + mbs.getAttribute(name, "LoggingEnabled"));

            // enable logging
            mbs.setAttribute(name, new javax.management.Attribute(
                    "LoggingEnabled", true));

            File propFile = new File("path/to/properties");
            LogManager logManager = LogManager.getLogManager();
            logManager.readConfiguration(new FileInputStream(propFile));

            if (logDriver) 
                DriverManager.setLogWriter(new PrintWriter(System.err));
            
         catch (Exception e) 
            e.printStackTrace();
        
    

    public static Connection getConnection() throws SQLException,
            ClassNotFoundException 
        Properties connectionProps = new Properties();
        connectionProps.put("user", "test_app");
        connectionProps.put("password", "test");

        Class.forName("oracle.jdbc.driver.OracleDriver");
        con = DriverManager.getConnection(
                "jdbc:oracle:thin:@"+HOST_IP+":1521:"+SID,
                connectionProps);
        System.out.println("Connected to database");
        return con;
    

    public static void closeConnection() throws SQLException 
        if (con != null) 
            con.close();
        
    


我的 OracleLog.properties 文件中有以下内容:

.level=SEVERE
oracle.jdbc.level=INFO
oracle.jdbc.handlers=java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.level=INFO
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter

但是当我通过在类路径中放置 ojdbc6-11.2.0.3.jar 来运行我的程序时,我得到了异常:

INFO: Test Message
javax.management.InstanceNotFoundException: com.oracle.jdbc:type=diagnosability,name=sun.misc.Launcher$AppClassLoader@73d16e93
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.getMBean(DefaultMBeanServerInterceptor.java:1095)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.getAttribute(DefaultMBeanServerInterceptor.java:643)
    at com.sun.jmx.mbeanserver.JmxMBeanServer.getAttribute(JmxMBeanServer.java:678)
    at myjdbc.Logging.enableLogging(Logging.java:45)
    at myjdbc.Logging.main(Logging.java:24)
Connected to database

如果我在类路径中有ojdbc6_g.jar,那么我也会遇到同样的异常。

请告诉我如何为我的 JDBC 程序启用日志记录?基本上我希望看​​到内部 JDBC 代码生成的日志。

更新: 现在我将ojdbc6dms.jar 文件放在类路径中,我的程序给出了以下异常:

Nov 28, 2014 9:09:02 PM jdbc.chap2.Logging main
INFO: Test Message
javax.management.InstanceNotFoundException: com.oracle.jdbc:type=diagnosability,name=sun.misc.Launcher$AppClassLoader@73d16e93
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.getMBean(DefaultMBeanServerInterceptor.java:1095)
    at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.getAttribute(DefaultMBeanServerInterceptor.java:643)
    at com.sun.jmx.mbeanserver.JmxMBeanServer.getAttribute(JmxMBeanServer.java:678)
    at jdbc.chap2.Logging.enableLogging(Logging.java:45)
    at jdbc.chap2.Logging.main(Logging.java:24)
Exception in thread "main" java.lang.NoClassDefFoundError: oracle/dms/console/DMSConsole
    at oracle.jdbc.driver.DMSFactory.<clinit>(DMSFactory.java:48)
    at oracle.jdbc.driver.PhysicalConnection.createDMSSensors(PhysicalConnection.java:2121)
    at oracle.jdbc.driver.PhysicalConnection.<init>(PhysicalConnection.java:730)
    at oracle.jdbc.driver.T4CConnection.<init>(T4CConnection.java:433)
    at oracle.jdbc.driver.T4CDriverExtension.getConnection(T4CDriverExtension.java:32)
    at oracle.jdbc.driver.OracleDriver.connect(OracleDriver.java:608)
    at java.sql.DriverManager.getConnection(DriverManager.java:664)
    at java.sql.DriverManager.getConnection(DriverManager.java:208)
    at jdbc.chap2.Logging.getConnection(Logging.java:70)
    at jdbc.chap2.Logging.main(Logging.java:25)
Caused by: java.lang.ClassNotFoundException: oracle.dms.console.DMSConsole
    at java.net.URLClassLoader$1.run(URLClassLoader.java:372)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 10 more

【问题讨论】:

类路径中有合适的驱动程序吗? '确保调试 JAR 文件,例如 ojdbc5_g.jar 或 ojdbc6_g.jar,是 CLASSPATH 环境变量中唯一的 Oracle JDBC JAR 文件。和'要获取日志输出,必须使用调试JAR文件,文件名中用“_g”表示,' @KonstantinV.Salikhov,现在我尝试在类路径中使用ojdbc6_g.jar,但收到相同的错误消息,我已经用错误详细信息更新了我的问题。 mbean 使用不同的名称注册,尝试使用 jconsole 并查看 mbean 名称并使用正确的名称。 @SajanChandran,我在 Eclipse 中创建了这个程序并将其作为独立程序运行。我是 Jconsole 的新手,我已经在命令提示符下运行了命令,你能告诉我如何获取 mbean 名称吗? 你见过this question,特别是它的代码吗? 【参考方案1】:

2019 年更新:自 2015 年以来未维护 log4jdbc。p6spy 似乎仍在积极维护中。

原答案

有很多 Spy 框架可用于此目的,请查看log4jdbc,我认为这就是您要找的。​​p>

特点

完全支持 JDBC 3 和 JDBC 4! 在大多数情况下易于配置 您需要做的就是将驱动程序类名称更改为 net.sf.log4jdbc.DriverSpy 并将“jdbc:log4”添加到您现有的 jdbc url,设置您的日志记录类别,您就可以开始了! 在记录的输出中,对于准备好的语句,绑定参数是 自动插入到 SQL 输出中。这大大提高了 许多情况下的可读性和调试。 可以生成 SQL 计时信息,以帮助确定 SQL 语句运行所需的时间, 帮助识别运行速度过慢的语句,这 可以使用附带的工具对数据进行后处理以生成分析 报告数据以快速识别应用程序中的慢速 SQL。 生成SQL连接号信息,帮助识别 连接池或线程问题。适用于任何底层证券 JDBC驱动,JDK 1.4及以上,SLF4J 1.x。 开源软件,根据商业友好的 Apache 2.0 许可证获得许可

用法

将 log4jdbc jar(基于 JDK 版本)放入应用程序的类路径中。 选择使用的日志系统,支持log4j、logback、commons logging..等 在应用程序的配置中将 JDBC 驱动程序类设置为 net.sf.log4jdbc.DriverSpy。 在许多情况下被监视的底层驱动程序将自动加载,无需任何额外配置。

将 jdbc:log4 添加到您正在使用的普通 jdbc url。

例如,如果您的正常 jdbc url 是 jdbc:derby://localhost:1527//db-derby-10.2.2.0-bin/databases/MyDatabase 那么您将其更改为: jdbc:log4jdbc:derby://localhost:1527//db-derby-10.2.2.0-bin/databases/MyDatabase

设置您的记录器。

jdbc.sqlonly:仅记录 SQL。在准备好的语句中执行的 SQL 会自动显示,其绑定参数替换为绑定在该位置的数据,从而大大提高了可读性。 1.0

jdbc.sqltiming:记录 SQL,执行后,包括有关 SQL 执行时间的计时统计信息。 1.0

jdbc.audit:记录除 ResultSets 之外的所有 JDBC 调用。这是一个非常庞大的输出,除非跟踪特定的 JDBC 问题,否则通常不需要。 1.0

jdbc.resultset:更多,因为所有对 ResultSet 对象的调用都被记录下来。 1.0

jdbc.connection:记录连接打开和关闭事件以及转储所有打开的连接号。这对于查找连接泄漏问题非常有用。

【讨论】:

【参考方案2】:

我知道这是一个非常老的话题,但尚未提及的是,对于 Oracle,存在一个不需要对应用程序代码进行任何更改的解决方案,只需使用所需的启用跟踪的 Oracle JDBC 驱动程序并启用日志记录通过启动时的 JVM 属性。

Oracle 自己已经描述了这个here,经过一些试验和错误后,我得到了它的工作:

    将启用跟踪的 ojdbc jar 文件放在您的类路径中。从链接的 Oracle 页面引用:“要获取日志输出,您必须使用调试 JAR 文件,这些文件在文件名中以“_g”表示,例如 ojdbc5_g.jar 或 ojdbc6_g.jar。”我的 Oracle 11g 安装包含

    按照链接的 Oracle 页面上的说明创建一个 logging.properties 文件,并根据您的需要调整日志记录级别。示例:

    .level=SEVERE oracle.jdbc.level=FINEST oracle.jdbc.handlers=java.util.logging.FileHandler java.util.logging.FileHandler.level=FINEST java.util.logging.FileHandler.pattern=jdbc.log java.util.logging.FileHandler.count=1 java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter

    将 JVM 属性“-Doracle.jdbc.Trace=true -Djava.util.logging.config.file=logging.properties”添加到 JDBC 应用程序的 java 启动命令中。

    李>

JDBC 应用程序现在应该生成一个名为 jdbc.log 的文件,其中应该包含所需的信息。在某些情况下,可能需要指定 logging.properties 文件的完整路径。

【讨论】:

这个答案真的很震撼,因为它允许检查应用程序的 JDBC / Oracle 行为而无需更改其代码。 这是什么.level=.SEVERE?它给了我一个糟糕的级别值错误。 打错字,应该是.level=SEVERE 如果您只想查看 SQL 语句,请改用oracle.jdbc.level = CONFIGFINEST 会收到大量消息。 oracle.jdbc.level=CONFIG 似乎省略了更新和插入语句。当我使用它时,我只看到选择和删除语句。我问了一个问题,是否有办法让它们出现:***.com/questions/69533236【参考方案3】:

如果你使用的是Spring框架,那么datasource-proxy框架非常方便。您基本上可以环绕任何 DataSource 并添加日志记录行为。

如果您使用的是 Java EE,那么 P6spy 是一个不错的选择:

在幕后,p6spy 提供了Driver 级别的语句拦截器,这对于 Java EE 应用程序来说更加方便,因为DataSource 是由应用程序服务器提供的。

【讨论】:

嗨,弗拉德,你能看看我在 Hibernate 上的帖子吗 - ***.com/questions/37088759/… @Vlad:你用什么工具来生成图表? 对于其他想要与图表相关的答案的人,我在推特上得到了他的答案。该工具是yEd Graph Editor。检查推特推文 - twitter.com/vlad_mihalcea/status/1209366642062512128?s=20【参考方案4】:

假设您使用的是 Log4j,请执行以下操作:

使用 ojdbc_g,如 Oracle JDBC Developer's Guide: Diagnosability in JDBC 中所述 请注意,19.3 驱动程序现在在 Maven 中,或者只需手动下载。

        <dependency>
            <groupId>com.oracle.ojdbc</groupId>
            <artifactId>ojdbc8_g</artifactId>
            <version>19.3.0.0</version>
        </dependency>

您还需要Log4j JDK Logging Adapter

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-jul</artifactId>
            <version>2.13.0</version>
        </dependency>

设置这些系统属性:-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager-Doracle.jdbc.Trace=true

log4j2.xml

中配置您的记录器
        <Logger name="oracle.jdbc" level="TRACE">
            <AppenderRef ref="Console"/>
        </Logger>
        <Logger name="oracle.sql" level="TRACE">
            <AppenderRef ref="Console"/>
        </Logger>

请注意,level="ALL" 将提供***别的日志记录

【讨论】:

【参考方案5】:

您是否尝试过设置相应的系统属性?例如。追踪:

System.setProperty( "oracle.jdbc.Trace", Boolean.TRUE.toString() );

(来自https://docs.oracle.com/cd/B28359_01/java.111/b31224/diagnose.htm)

【讨论】:

嗨乔,我现在添加了这一行,但得到了同样的错误。

以上是关于使用 JDBC 时如何为 SQL 语句启用日志记录的主要内容,如果未能解决你的问题,请参考以下文章

如何为 Apache Hadoop NameNodes 启用 GC 日志记录,同时防止日志文件覆盖和限制磁盘空间使用

如何为 Azure 上的 Node.js Api 应用启用 BLOB 日志记录?

向导中断时如何为 ambari 重新安装 Kerberos 客户端?

如何为一张表启用 sequelize 日志记录

如何为 Spring Security 启用日志记录?

如何为 Spring Security 启用日志记录?