如何记录 Tomcat 7 JDBC 连接池、连接创建

Posted

技术标签:

【中文标题】如何记录 Tomcat 7 JDBC 连接池、连接创建【英文标题】:How to log Tomcat 7 JDBC connection pool, connection creation 【发布时间】:2014-11-13 02:17:30 【问题描述】:

尽管我们使用的是 Tomcat 7 JDBC 连接池,但我正在尝试调试似乎打开和关闭的数据库连接数量过多的问题。当在数据源上调用 getConnection() 导致打开新连接而不是从池中借用现有连接时,如何记录?

【问题讨论】:

哇哦,我从来不知道Tomcat有它自己的CP。无论如何,你为什么要在客户端做呢?在大多数情况下,做数据库方面要容易得多 您是在寻找 tomcat 配置还是普通日志记录? 我更喜欢客户端。有许多不同的客户端可以使用相同的凭据连接到数据库。我正在尝试诊断客户端问题,可能与池有关。任何有助于理解何时打开新连接以及为什么打开的东西。谢谢! 【参考方案1】:

我知道查看 Tomcat 数据库连接池信息的两种方法。

1. 使用 JMX 监控

默认情况下,Tomcat 连接池会将自己注册为 MBean(JMX Bean)。可以使用 tomcat-jdbc-pool 上的 jmxEnabled 属性打开/关闭此功能。见The Tomcat JDBC Connection Pool。

您可以使用各种外部 JMX 工具来监控 DB 连接池和其他 JMX 资源。我建议从 Java 附带的 JConsole 开始。启动 JConsole,连接到您的 Tomcat (Catalina) JVM,选择 MBeans 标头,打开 Catalina/DataSource/... 见下图。

阅读更多Monitoring Tomcat。

2. 编写 JdbcInterceptor 类,记录 DB Connection Pool 信息

Tomcat 连接池允许您为 JDBC 连接注册拦截器。下面,我将展示如何编写一个记录连接使用情况的JdbcInterceptor 类。该示例适用于 Tomcat 8,但它也可能适用于 Tomcat 7。

tomcat-jdbc.jar 作为提供的依赖项添加到您的项目中。

    <dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>tomcat-jdbc</artifactId>
        <version>8.0.8</version>
        <scope>provided</scope>
    </dependency>

创建一个 JdbcInterceptor 类

这个类使用公共日志,你可能想使用别的东西。

package com.acme.rest.config;

import java.lang.reflect.Method;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tomcat.jdbc.pool.ConnectionPool;
import org.apache.tomcat.jdbc.pool.JdbcInterceptor;
import org.apache.tomcat.jdbc.pool.PooledConnection;

public class MyConnectionPoolLogger extends JdbcInterceptor 

    private static final Log log = LogFactory.getLog(MyConnectionPoolLogger.class);

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
        if (log.isDebugEnabled()) 
            String name = method.getName();
            if (CLOSE_VAL.equals(name)) 
                log.debug(String.format("Returning Connection to Pool [%s]", proxy));
            
        
        return super.invoke(proxy, method, args);
    

    @Override
    public void reset(ConnectionPool connPool, PooledConnection conn) 
        if (connPool != null && conn != null) 
            if (log.isDebugEnabled()) 
                log.debug(String.format("Getting Connection [%s], Pool active=[%s], idle=[%s]", conn.toString(),
                        connPool.getActive(), connPool.getIdle()));
            
        
    

    @Override
    public void disconnected(ConnectionPool connPool, PooledConnection conn, boolean finalizing) 
        if (connPool != null && conn != null) 
            if (log.isDebugEnabled()) 
                log.debug(String.format("Closing Connection [%s], Pool active=[%s], idle=[%s]", conn.toString(),
                        connPool.getActive(), connPool.getIdle()));
            
        
    

在 Context.xml 中注册这个拦截器类

<Context>
  <Resource 
    name="jdbc/acmedb"
    auth="Container"
    type="javax.sql.DataSource"
    factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"

    jdbcInterceptors="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;
      org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer; 
      com.acme.rest.config.MyConnectionPoolLogger"

  />
</Context>

JdbcInterceptor 类可以注册为Resource,如上所示,也可以注册为POJO。

日志样本

以下是访问连接池时来自 Tomcat 的一些示例日志

2017-11-04 00:15:19,389 DEBUG Getting Connection [PooledConnection[com.mysql.jdbc.JDBC4Connection@6dea96f]], Pool active=[1], idle=[0]
2017-11-04 00:15:19,393 DEBUG Returning Connection to Pool [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@6dea96f]]]
2017-11-04 00:16:19,249 DEBUG Closing Connection [PooledConnection[com.mysql.jdbc.JDBC4Connection@6dea96f]], Pool active=[0], idle=[1]

【讨论】:

这是一个非常有用的sn-p。感谢您抽出宝贵时间发布此内容。【参考方案2】:

我今天遇到了类似的情况。我通过slf4j登录,我的问题是hibernate引起的。

我所做的是在日志配置中设置调用 JDBC 连接 getter 的空间。幸运的是,对于休眠,那里有调试级别的日志。

<logger name="org.hibernate.engine.jdbc">
    <level value="debug"/>
</logger>

我的猜测是,对于您的情况,您可以尝试对调用 JDBC 池 getter 的命名空间执行等效操作。可能是这样的:

<logger name="org.apache.tomcat.jdbc.pool">
    <level value="debug"/>
</logger>

我希望这会有所帮助。使用休眠我得到这样的结果:

DEBUG 02.07.2015 16:36:50,571 http-bio-8080-exec-33 (org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection():212) [] - Obtaining JDBC connection
DEBUG 02.07.2015 16:36:50,601 http-bio-8080-exec-6 (org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection():218) [] - Obtained JDBC connection
DEBUG 02.07.2015 16:36:50,627 http-bio-8080-exec-10 (org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection():218) [] - Obtained JDBC connection
DEBUG 02.07.2015 16:36:50,643 http-bio-8080-exec-32 (org.hibernate.engine.jdbc.spi.SqlExceptionHelper.logExceptions():139) [] - Could not open connection [n/a]
org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot get a connection, pool error Timeout waiting for idle object
    at org.apache.tomcat.dbcp.dbcp.PoolingDataSource.getConnection(PoolingDataSource.java:114)
    at org.apache.tomcat.dbcp.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044)

...

【讨论】:

以上是关于如何记录 Tomcat 7 JDBC 连接池、连接创建的主要内容,如果未能解决你的问题,请参考以下文章

tomcat 7 jdbc连接池和jdbc4验证

使用 Tomcat 7 新的连接池 —— Tomcat jdbc pool

Tomcat 7 和 Oracle 连接池

在 Tomcat 7 上设置 SQL Server JDBC 连接池和资源

Tomcat7 新的数据库连接池Tomcat jdbc pool介绍和配置

如何在 Spring/Tomcat 中完全禁用 JDBC 连接池?