自定义filter的起因是希望在跟踪resultset的获取行数,同时slow sql输出的时候可以输出thread stack,以帮助快速定位问题。

先看关键类:
public abstract class LogFilter

首先定义了4个log 对象

protected String          dataSourceLoggerName                 = "druid.sql.DataSource";
    protected String          connectionLoggerName                 = "druid.sql.Connection";
    protected String          statementLoggerName                  = "druid.sql.Statement";
    protected String          resultSetLoggerName                  = "druid.sql.ResultSet";

这些对应了log配置,以log4j2为例:

  <!--记录druid-sql的记录-->
        <logger name="druid.sql.Statement" level="info" additivity="false">
            <appender-ref ref="druidSqlRollingFile"/>
                        <appender-ref ref="Console"/>
            
        </logger>
         
 
        <!--log4j2 自带过滤日志-->
         <Logger name="druid.sql.ResultSet" level="debug" additivity="false">
          <appender-ref ref="druidSqlRollingFile"/>
                        <appender-ref ref="Console"/>
        </Logger>

及大量开关参数:

private boolean           connectionConnectBeforeLogEnable     = true;
    private boolean           connectionConnectAfterLogEnable      = true;
    private boolean           connectionCommitAfterLogEnable       = true;
    private boolean           connectionRollbackAfterLogEnable     = true;
    private boolean           connectionCloseAfterLogEnable        = true;

这些在spring boot 模式下,对应的properties 如下:

spring.datasource.druid.filter.slf4j.enabled=true

spring.datasource.druid.filter.slf4j.statement-create-after-log-enabled=false

spring.datasource.druid.filter.slf4j.statement-close-after-log-enabled=false
spring.datasource.druid.filter.slf4j.statement-execute-update-after-log-enabled=false
#spring.datasource.druid.filter.log4j2.statement-execute-after-log-enabled=false

spring.datasource.druid.filter.stat.log-slow-sql=true

spring.datasource.druid.filter.stat.slow-sql-millis=1

但有一些也可以通过environment 来定义,这些定义在如下代码中:

public void configFromProperties(Properties properties) {
        {
            String prop = properties.getProperty("druid.log.conn");
            if ("false".equals(prop)) {
                connectionLogEnabled = false;
            } else if ("true".equals(prop)) {
                connectionLogEnabled = true;
            }
        }
        {
            String prop = properties.getProperty("druid.log.stmt");
            if ("false".equals(prop)) {
                statementLogEnabled = false;
            } else if ("true".equals(prop)) {
                statementLogEnabled = true;
            }
        }
        {
            String prop = properties.getProperty("druid.log.rs");
            if ("false".equals(prop)) {
                resultSetLogEnabled = false;
            } else if ("true".equals(prop)) {
                resultSetLogEnabled = true;
            }
        }
        {
            String prop = properties.getProperty("druid.log.stmt.executableSql");
            if ("true".equals(prop)) {
                statementExecutableSqlLogEnable = true;
            } else if ("false".equals(prop)) {
                statementExecutableSqlLogEnable = false;
            }
        }
    }

同时发现如下,可以发现必须是debug模式才输出rs信息

@Override
    public boolean isStatementLogEnabled() {
        return statementLogger.isDebugEnabled() && super.isStatementLogEnabled();
    }

    @Override
    public boolean isResultSetLogEnabled() {
        return resultSetLogger.isDebugEnabled() && super.isResultSetLogEnabled();
    }

通过以上代码,可以输出resultset的结果如下:

[10:52:55.803] DEBUG com.alibaba.druid.filter.logging.Log4j2Filter 142 resultSetLog - {conn-10001, pstmt-20001, rs-50130} open
[10:52:55.803] DEBUG com.alibaba.druid.filter.logging.Log4j2Filter 142 resultSetLog - {conn-10001, pstmt-20001, rs-50130} Header: [NEXTVAL]
[10:52:55.803] DEBUG com.alibaba.druid.filter.logging.Log4j2Filter 142 resultSetLog - {conn-10001, pstmt-20001, rs-50130} Result: [7728]
[10:52:55.803] DEBUG com.alibaba.druid.filter.logging.Slf4jLogFilter 139 resultSetLog - {conn-10001, pstmt-20001, rs-50130} Result: [7728]
[10:52:55.803] DEBUG com.alibaba.druid.filter.logging.Log4j2Filter 142 resultSetLog - {conn-10001, pstmt-20001, rs-50130} closed
[10:52:55.803] DEBUG com.alibaba.druid.filter.logging.Slf4jLogFilter 139 resultSetLog - {conn-10001, pstmt-20001, rs-50130} closed

还是不能满足要求,于是开始看是否可以自定义。阅读代码,发现关键代码如下:

 private static void loadFilterConfig(Properties filterProperties, ClassLoader classLoader) throws IOException {
        if (classLoader == null) {
            return;
        }
        
        for (Enumeration<URL> e = classLoader.getResources("META-INF/druid-filter.properties"); e.hasMoreElements();) {
            URL url = e.nextElement();

            Properties property = new Properties();

            InputStream is = null;
            try {
                is = url.openStream();
                property.load(is);
            } finally {
                JdbcUtils.close(is);
            }

            filterProperties.putAll(property);
        }
    }


public static Properties loadFilterConfig() throws IOException {
        Properties filterProperties = new Properties();

        loadFilterConfig(filterProperties, ClassLoader.getSystemClassLoader());
        loadFilterConfig(filterProperties, FilterManager.class.getClassLoader());
        loadFilterConfig(filterProperties, Thread.currentThread().getContextClassLoader());
        loadFilterConfig(filterProperties, FilterManager.class.getClassLoader());

        return filterProperties;
    }

最关键代码:
classLoader.getResources(“META-INF/druid-filter.properties”)
即从classpath的多个druid-filter.properties文件读取filter,因此自己在应用中定义个META-INF/druid-filter.properties,内容如下:
druid.filters.log4j24=extend.druid.Log4j2Filter

修改application.properties
spring.datasource.druid.filters= stat,wall,log4j24

使用自定义filter 代替默认的log4j2输出,增加如下代码

 private String stmtId(ResultSetProxy resultSet) {
        return stmtId(resultSet.getStatementProxy());
    }
    
    private String stmtId(StatementProxy statement) {
        StringBuffer buf = new StringBuffer();
        if (statement instanceof CallableStatementProxy) {
            buf.append("cstmt-");
        } else if (statement instanceof PreparedStatementProxy) {
            buf.append("pstmt-");
        } else {
            buf.append("stmt-");
        }
        buf.append(statement.getId());

        return buf.toString();
    }

    
    @Override
    public void resultSet_close(FilterChain chain, ResultSetProxy resultSet) throws SQLException {
        chain.resultSet_close(resultSet);

        StringBuffer buf = new StringBuffer();
        buf.append("{custom-conn-");
        buf.append(resultSet.getStatementProxy().getConnectionProxy().getId());
        buf.append(", ");
        buf.append(stmtId(resultSet));
        buf.append(", rs-");
        buf.append(resultSet.getId());
        buf.append(", FetchSize-");
        buf.append(resultSet.getFetchSize());
        buf.append(", FetchRowCount-");
        buf.append(resultSet.getFetchRowCount());
        buf.append("} closed");

        if (isResultSetCloseAfterLogEnabled()) {
            resultSetLog(buf.toString());
        }
    }

顺利达到输出fetchsize的目的,同理stat及wall filter也是可以自定义的。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐