使用druid自定义拦截器

发布时间 2023-11-24 16:36:26作者: Doyourself!

         使用druid自定义的拦截器StatFilter,是可以通过日志进行慢sql打印的。但是如果想要把慢sql放入DB,或者通过钉钉告警的方式进行实时打印,则需要实现自定义的拦截器。

         第一步:重新自定义拦截器

      

package com.example.demo.filter;

import com.alibaba.druid.filter.FilterEventAdapter;
import com.alibaba.druid.filter.stat.StatFilterMBean;
import com.alibaba.druid.proxy.jdbc.JdbcParameter;
import com.alibaba.druid.proxy.jdbc.ResultSetProxy;
import com.alibaba.druid.proxy.jdbc.StatementProxy;
import com.alibaba.druid.support.json.JSONWriter;
import com.example.demo.entity.BbsDruidSql;
import com.example.demo.entity.SlowSqlConfig;
import com.example.demo.entity.SpringUtil;
import com.example.demo.mapper.BbsDruidSqlDao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.InputStream;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.NClob;
import java.util.Date;

/**
 * @Author:hongtaofan
 * @Version:1.0
 * @Description:
 * @Date: 2023/11/22 11:45
 */
@Component
public class SlowSqlTestFilter extends FilterEventAdapter implements StatFilterMBean {

    private static final String IGNORE_SQL = "SELECT 1";

    private static final String INSERT= "insert";
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    private BbsDruidSqlDao bbsDruidSqlDao;
    @Resource
    private SlowSqlConfig slowSqlConfig;

    @Override
    protected void statementExecuteBefore(StatementProxy statement, String sql) {
        super.statementExecuteBefore(statement, sql);
        //sql开始执行的时间
        statement.setLastExecuteStartNano();
    }

    @Override
    protected void statementExecuteBatchBefore(StatementProxy statement) {
        super.statementExecuteBatchBefore(statement);
        //sql开始执行的时间
        statement.setLastExecuteTimeNano();
    }

    @Override
    protected void statementExecuteAfter(StatementProxy statement, String sql, boolean result) {
        if (IGNORE_SQL.equals(sql) || sql.contains(INSERT)) {
            return;
        }
        if (slowSqlConfig == null) {
            slowSqlConfig = SpringUtil.getBean(SlowSqlConfig.class);
        }
        final long nonNano = System.nanoTime();
        final long lastTime = nonNano - statement.getLastExecuteStartNano();
        long millis = lastTime / (1000 * 1000);
        if (millis >= slowSqlConfig.getSlowSqlMillis()) {
            slowSqlToMysql(statement, sql,millis);
        }

    }


    @Override
    protected void statementExecuteUpdateBefore(StatementProxy statement, String sql) {
        super.statementExecuteUpdateBefore(statement, sql);
        statement.setLastExecuteTimeNano();
    }

    @Override
    protected void statementExecuteUpdateAfter(StatementProxy statement, String sql, int updateCount) {
        final long nonNano = System.nanoTime();
        final long lastTime = nonNano - statement.getLastExecuteStartNano();
        long millis = lastTime / (1000 * 1000);
        if (millis >= slowSqlConfig.getSlowSqlMillis()) {
            slowSqlToMysql(statement, sql,millis);
        }
    }

    @Override
    protected void statementExecuteQueryBefore(StatementProxy statement, String sql) {
        super.statementExecuteQueryBefore(statement, sql);
        statement.setLastExecuteStartNano();
    }

    @Override
    protected void statementExecuteQueryAfter(StatementProxy statement, String sql, ResultSetProxy resultSet) {
        if (IGNORE_SQL.equals(sql)) {
            return;
        }
        final long nonNano = System.nanoTime();
        final long lastTime = nonNano - statement.getLastExecuteStartNano();
        long millis = lastTime / (1000 * 1000);
        if (millis >= slowSqlConfig.getSlowSqlMillis()) {
            slowSqlToMysql(statement, sql,millis);
        }
    }


    @Override
    protected void statement_executeErrorAfter(StatementProxy statement, String sql, Throwable error) {
        super.statement_executeErrorAfter(statement, sql, error);
    }

    private void slowSqlToMysql(StatementProxy statement, String sql,Long millis) {
        if (bbsDruidSqlDao == null) {
            bbsDruidSqlDao = SpringUtil.getBean(BbsDruidSqlDao.class);
        }
        String slowParamters = buildSlowParameters(statement);
        logger.info("慢sql语句{},入参:{}", sql, slowParamters);
        BbsDruidSql bbsDruidSql = BbsDruidSql.builder().slowSql(sql).slowSqlParam(slowParamters)
                .createTime(new Date()).updateTime(new Date()).slowSqlTraceid(millis.toString()).build();
        bbsDruidSqlDao.insertSelective(bbsDruidSql);
    }

    protected String buildSlowParameters(StatementProxy statement) {
        JSONWriter out = new JSONWriter();

        out.writeArrayStart();
        for (int i = 0, parametersSize = statement.getParametersSize(); i < parametersSize; ++i) {
            JdbcParameter parameter = statement.getParameter(i);
            if (i != 0) {
                out.writeComma();
            }
            if (parameter == null) {
                continue;
            }

            Object value = parameter.getValue();
            if (value == null) {
                out.writeNull();
            } else if (value instanceof String) {
                String text = (String) value;
                if (text.length() > 100) {
                    out.writeString(text.substring(0, 97) + "...");
                } else {
                    out.writeString(text);
                }
            } else if (value instanceof Number) {
                out.writeObject(value);
            } else if (value instanceof java.util.Date) {
                out.writeObject(value);
            } else if (value instanceof Boolean) {
                out.writeObject(value);
            } else if (value instanceof InputStream) {
                out.writeString("<InputStream>");
            } else if (value instanceof NClob) {
                out.writeString("<NClob>");
            } else if (value instanceof Clob) {
                out.writeString("<Clob>");
            } else if (value instanceof Blob) {
                out.writeString("<Blob>");
            } else {
                out.writeString('<' + value.getClass().getName() + '>');
            }
        }
        out.writeArrayEnd();

        return out.toString();
    }

    @Override
    public boolean isMergeSql() {
        return false;
    }

    @Override
    public void setMergeSql(boolean mergeSql) {

    }

    @Override
    public boolean isLogSlowSql() {
        return false;
    }

    @Override
    public void setLogSlowSql(boolean logSlowSql) {

    }

    @Override
    public String mergeSql(String sql, String dbType) {
        return null;
    }

    @Override
    public long getSlowSqlMillis() {
        return 0;
    }

    @Override
    public void setSlowSqlMillis(long slowSqlMillis) {

    }
}

第二步:把自定义的拦截器放入druid的datasource中

@ConfigurationProperties(prefix = "spring.datasource")
@Bean(initMethod = "init",destroyMethod = "close")
public DruidDataSource dataSource(){
    DruidDataSource dataSource = new DruidDataSource();
    //使用自定义拦截器 注意 不能new
    dataSource.setProxyFilters(Collections.singletonList(new SlowSqlTestFilter()));
    //dataSource.setProxyFilters(Collections.singletonList(statGwmFilter()));
    //设置druid的重置间隔
    dataSource.setTimeBetweenLogStatsMillis(60000);
    return dataSource;
}