Package com.alibaba.druid.filter.stat

Source Code of com.alibaba.druid.filter.stat.StatFilter

/*
* Copyright 1999-2011 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.druid.filter.stat;

import java.io.InputStream;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.NClob;
import java.sql.SQLException;
import java.sql.Savepoint;
import java.util.Date;
import java.util.Properties;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;

import javax.management.JMException;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.TabularData;

import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.filter.FilterChain;
import com.alibaba.druid.filter.FilterEventAdapter;
import com.alibaba.druid.logging.Log;
import com.alibaba.druid.logging.LogFactory;
import com.alibaba.druid.proxy.jdbc.CallableStatementProxy;
import com.alibaba.druid.proxy.jdbc.ConnectionProxy;
import com.alibaba.druid.proxy.jdbc.DataSourceProxy;
import com.alibaba.druid.proxy.jdbc.JdbcParameter;
import com.alibaba.druid.proxy.jdbc.PreparedStatementProxy;
import com.alibaba.druid.proxy.jdbc.ResultSetProxy;
import com.alibaba.druid.proxy.jdbc.StatementExecuteType;
import com.alibaba.druid.proxy.jdbc.StatementProxy;
import com.alibaba.druid.sql.visitor.ParameterizedOutputVisitorUtils;
import com.alibaba.druid.stat.JdbcConnectionStat;
import com.alibaba.druid.stat.JdbcDataSourceStat;
import com.alibaba.druid.stat.JdbcResultSetStat;
import com.alibaba.druid.stat.JdbcSqlStat;
import com.alibaba.druid.stat.JdbcStatContext;
import com.alibaba.druid.stat.JdbcStatManager;
import com.alibaba.druid.stat.JdbcStatementStat;

/**
* @author wenshao<szujobs@hotmail.com>
*/
public class StatFilter extends FilterEventAdapter implements StatFilterMBean {

    private final static Log          LOG                        = LogFactory.getLog(StatFilter.class);

    public final static String        ATTR_UPDATE_COUNT          = "stat.updteCount";
    public final static String        ATTR_TRANSACTION           = "stat.tx";

    protected JdbcDataSourceStat      dataSourceStat;

    @Deprecated
    protected final JdbcStatementStat statementStat              = JdbcStatManager.getInstance().getStatementStat();

    @Deprecated
    protected final JdbcResultSetStat resultSetStat              = JdbcStatManager.getInstance().getResultSetStat();

    private boolean                   connectionStackTraceEnable = false;

    protected DataSourceProxy         dataSource;

    protected final AtomicLong        resetCount                 = new AtomicLong();

    // 3 seconds is slow sql
    protected long                    slowSqlMillis              = 3 * 1000;

    private String                    dbType;

    private boolean                   mergeSql                   = false;

    public StatFilter(){
    }

    public String getDbType() {
        return dbType;
    }

    public void setDbType(String dbType) {
        this.dbType = dbType;
    }

    public boolean isConnectionStackTraceEnable() {
        return connectionStackTraceEnable;
    }

    public void setConnectionStackTraceEnable(boolean connectionStackTraceEnable) {
        this.connectionStackTraceEnable = connectionStackTraceEnable;
    }

    public JdbcDataSourceStat getDataSourceStat() {
        return this.dataSourceStat;
    }

    public void reset() {
        dataSourceStat.reset();

        resetCount.incrementAndGet();
    }

    public long getResetCount() {
        return resetCount.get();
    }

    public boolean isMergeSql() {
        return mergeSql;
    }

    public void setMergeSql(boolean mergeSql) {
        this.mergeSql = mergeSql;
    }

    public String mergeSql(String sql) {
        if (!mergeSql) {
            return sql;
        }

        try {
            sql = ParameterizedOutputVisitorUtils.parameterize(sql, dbType);
        } catch (Exception e) {
            LOG.error("merge sql error", e);
        }

        return sql;
    }

    @Override
    public synchronized void init(DataSourceProxy dataSource) {
        this.dataSource = dataSource;

        this.dataSourceStat = dataSource.getDataSourceStat();

        if (this.dbType == null || this.dbType.trim().length() == 0) {
            this.dbType = dataSource.getDbType();
        }

        initFromProperties(dataSource.getConnectProperties());
        initFromProperties(System.getProperties());
    }

    private void initFromProperties(Properties properties) {
        if (properties == null) {
            return;
        }

        {
            String property = properties.getProperty("druid.stat.mergeSql");
            if ("true".equals(property)) {
                this.mergeSql = true;
            } else if ("false".equals(property)) {
                this.mergeSql = false;
            }
        }

        {
            String property = properties.getProperty("druid.stat.slowSqlMillis");
            if (property != null && property.trim().length() > 0) {
                property = property.trim();
                try {
                    this.slowSqlMillis = Long.parseLong(property);
                } catch (Exception e) {
                    LOG.error("property 'druid.stat.slowSqlMillis' format error");
                }
            }
        }
    }

    @Override
    public synchronized void destory() {
        if (dataSource == null) {
            return;
        }

        dataSource = null;
    }

    public ConnectionProxy connection_connect(FilterChain chain, Properties info) throws SQLException {
        ConnectionProxy connection = null;
        {
            long startNano = System.nanoTime();
            long startTime = System.currentTimeMillis();

            long nanoSpan;
            long nowTime = System.currentTimeMillis();

            dataSourceStat.getConnectionStat().beforeConnect();
            try {
                connection = chain.connection_connect(info);
                nanoSpan = System.nanoTime() - startNano;
            } catch (SQLException ex) {
                dataSourceStat.getConnectionStat().connectError(ex);
                throw ex;
            }
            dataSourceStat.getConnectionStat().afterConnected(nanoSpan);

            if (connection != null) {
                JdbcConnectionStat.Entry statEntry = getConnectionInfo(connection);

                dataSourceStat.getConnections().put(connection.getId(), statEntry);

                statEntry.setConnectTime(new Date(startTime));
                statEntry.setConnectTimespanNano(nanoSpan);
                statEntry.setEstablishNano(System.nanoTime());
                statEntry.setEstablishTime(nowTime);
                statEntry.setConnectStackTrace(new Exception());

                dataSourceStat.getConnectionStat().setActiveCount(dataSourceStat.getConnections().size());
            }
        }
        return connection;
    }

    @Override
    public void connection_close(FilterChain chain, ConnectionProxy connection) throws SQLException {
        long nowNano = System.nanoTime();

        dataSourceStat.getConnectionStat().incrementConnectionCloseCount();

        JdbcConnectionStat.Entry connectionInfo = getConnectionInfo(connection);

        long aliveNanoSpan = nowNano - connectionInfo.getEstablishNano();

        JdbcConnectionStat.Entry existsConnection = dataSourceStat.getConnections().remove(connection.getId());
        if (existsConnection != null) {
            dataSourceStat.getConnectionStat().afterClose(aliveNanoSpan);
        }

        chain.connection_close(connection);
        // duplicate close, C3P0等连接池,在某些情况下会关闭连接多次。
    }

    @Override
    public void connection_commit(FilterChain chain, ConnectionProxy connection) throws SQLException {
        chain.connection_commit(connection);

        dataSourceStat.getConnectionStat().incrementConnectionCommitCount();
    }

    @Override
    public void connection_rollback(FilterChain chain, ConnectionProxy connection) throws SQLException {
        chain.connection_rollback(connection);

        dataSourceStat.getConnectionStat().incrementConnectionRollbackCount();

        dataSourceStat.getConnectionStat().incrementConnectionRollbackCount();
    }

    @Override
    public void connection_rollback(FilterChain chain, ConnectionProxy connection, Savepoint savepoint)
                                                                                                       throws SQLException {
        chain.connection_rollback(connection, savepoint);

        dataSourceStat.getConnectionStat().incrementConnectionRollbackCount();
    }

    @Override
    public void statementCreateAfter(StatementProxy statement) {
        dataSourceStat.getStatementStat().incrementCreateCounter();

        super.statementCreateAfter(statement);
    }

    @Override
    public void statementPrepareCallAfter(CallableStatementProxy statement) {
        dataSourceStat.getStatementStat().incrementPrepareCallCount();

        JdbcSqlStat sqlStat = createSqlStat(statement, statement.getSql());
        statement.setSqlStat(sqlStat);
    }

    @Override
    public void statementPrepareAfter(PreparedStatementProxy statement) {
        dataSourceStat.getStatementStat().incrementPrepareCounter();
        JdbcSqlStat sqlStat = createSqlStat(statement, statement.getSql());
        statement.setSqlStat(sqlStat);
    }

    @Override
    public void statement_close(FilterChain chain, StatementProxy statement) throws SQLException {
        chain.statement_close(statement);

        dataSourceStat.getStatementStat().incrementStatementCloseCounter();
        JdbcStatContext context = JdbcStatManager.getInstance().getStatContext();
        if (context != null) {
            context.setName(null);
            context.setFile(null);
            context.setSql(null);
        }
    }

    @Override
    protected void statementExecuteUpdateBefore(StatementProxy statement, String sql) {
        internalBeforeStatementExecute(statement, sql);
    }

    @Override
    protected void statementExecuteUpdateAfter(StatementProxy statement, String sql, int updateCount) {
        internalAfterStatementExecute(statement, updateCount);
    }

    @Override
    protected void statementExecuteQueryBefore(StatementProxy statement, String sql) {
        internalBeforeStatementExecute(statement, sql);
    }

    @Override
    protected void statementExecuteQueryAfter(StatementProxy statement, String sql, ResultSetProxy resultSet) {
        internalAfterStatementExecute(statement);
    }

    @Override
    protected void statementExecuteBefore(StatementProxy statement, String sql) {
        internalBeforeStatementExecute(statement, sql);
    }

    @Override
    protected void statementExecuteAfter(StatementProxy statement, String sql, boolean result) {
        internalAfterStatementExecute(statement);
    }

    @Override
    protected void statementExecuteBatchBefore(StatementProxy statement) {
        final String sql = statement.getBatchSql();

        final int batchSize = statement.getBatchSqlList().size();
        JdbcSqlStat sqlStat = statement.getSqlStat();
        if (sqlStat == null) {
            sqlStat = createSqlStat(statement, sql);
            statement.setSqlStat(sqlStat);
        }

        if (sqlStat != null) {
            sqlStat.addExecuteBatchCount(batchSize);
        }

        internalBeforeStatementExecute(statement, sql);

    }

    @Override
    protected void statementExecuteBatchAfter(StatementProxy statement, int[] result) {
        internalAfterStatementExecute(statement, result);

    }

    private final void internalBeforeStatementExecute(StatementProxy statement, String sql) {

        dataSourceStat.getStatementStat().beforeExecute();

        final JdbcStatementStat.Entry statementStat = getStatementInfo(statement);
        final ConnectionProxy connection = statement.getConnectionProxy();
        final JdbcConnectionStat.Entry connectionCounter = getConnectionInfo(connection);

        statementStat.setLastExecuteStartNano(System.nanoTime());
        statementStat.setLastExecuteSql(sql);

        connectionCounter.setLastSql(sql);

        if (connectionStackTraceEnable) {
            connectionCounter.setLastStatementStatckTrace(new Exception());
        }

        // //////////SQL

        JdbcSqlStat sqlStat = statement.getSqlStat();
        if (sqlStat == null) {
            sqlStat = createSqlStat(statement, sql);
            statement.setSqlStat(sqlStat);
        }

        JdbcStatContext statContext = JdbcStatManager.getInstance().getStatContext();
        if (statContext != null) {
            sqlStat.setName(statContext.getName());
            sqlStat.setFile(statContext.getFile());
        }

        if (sqlStat != null) {
            sqlStat.setExecuteLastStartTime(System.currentTimeMillis());
            sqlStat.incrementRunningCount();

            try {
                boolean inTransaction = !statement.getConnectionProxy().getAutoCommit();
                if (inTransaction) {
                    sqlStat.incrementInTransactionCount();
                }
            } catch (SQLException e) {
                LOG.error("getAutoCommit error", e);
            }
        }
    }

    private final void internalAfterStatementExecute(StatementProxy statement, int... updateCountArray) {

        final JdbcStatementStat.Entry entry = getStatementInfo(statement);

        long nowNano = System.nanoTime();

        long nanoSpan = nowNano - entry.getLastExecuteStartNano();

        dataSourceStat.getStatementStat().afterExecute(nanoSpan);

        // // SQL
        final JdbcSqlStat sqlStat = statement.getSqlStat();

        if (sqlStat != null) {
            sqlStat.incrementExecuteSuccessCount();
            for (int updateCount : updateCountArray) {
                sqlStat.addUpdateCount(updateCount);
            }

            sqlStat.decrementRunningCount();
            sqlStat.addExecuteTime(statement.getLastExecuteType(), nanoSpan);
            statement.setLastExecuteTimeNano(nanoSpan);
            if ((!statement.isFirstResultSet()) && statement.getLastExecuteType() == StatementExecuteType.Execute) {
              try {
          int updateCount = statement.getUpdateCount();
          sqlStat.addUpdateCount(updateCount);
        } catch (SQLException e) {
          LOG.error("getUpdateCount error", e);
        }
            }

            long millis = nanoSpan / (1000 * 1000);
            if (millis >= slowSqlMillis) {
                StringBuilder buf = new StringBuilder();
                buf.append('[');
                int index = 0;
                for (JdbcParameter parameter : statement.getParameters().values()) {
                    if (index != 0) {
                        buf.append(',');
                    }
                    Object value = parameter.getValue();
                    if (value == null) {
                        buf.append("null");
                    } else if (value instanceof String) {
                        buf.append('"');
                        String text = (String) value;
                        if (text.length() > 100) {
                            for (int i = 0; i < 97; ++i) {
                                char ch = text.charAt(i);
                                if (ch == '\'') {
                                    buf.append('\\');
                                    buf.append(ch);
                                } else {
                                    buf.append(ch);
                                }
                            }
                            buf.append("...");
                        } else {
                            for (int i = 0; i < text.length(); ++i) {
                                char ch = text.charAt(i);
                                if (ch == '\'') {
                                    buf.append('\\');
                                    buf.append(ch);
                                } else {
                                    buf.append(ch);
                                }
                            }
                        }
                        buf.append('"');
                    } else if (value instanceof Number) {
                        buf.append(value.toString());
                    } else if (value instanceof java.util.Date) {
                        java.util.Date date = (java.util.Date) value;
                        buf.append(date.getClass().getSimpleName());
                        buf.append('(');
                        buf.append(date.getTime());
                        buf.append(')');
                    } else if (value instanceof Boolean) {
                        buf.append(value.toString());
                    } else if (value instanceof InputStream) {
                        buf.append("<InputStream>");
                    } else if (value instanceof Clob) {
                        buf.append("<Clob>");
                    } else if (value instanceof NClob) {
                        buf.append("<NClob>");
                    } else if (value instanceof Blob) {
                        buf.append("<Blob>");
                    } else {
                        buf.append('<');
                        buf.append(value.getClass().getName());
                        buf.append('>');
                    }
                    index++;
                }
                buf.append(']');
                sqlStat.setLastSlowParameters(buf.toString());
            }
        }
    }

    @Override
    protected void statement_executeErrorAfter(StatementProxy statement, String sql, Throwable error) {

        JdbcStatementStat.Entry counter = getStatementInfo(statement);
        ConnectionProxy connection = statement.getConnectionProxy();
        JdbcConnectionStat.Entry connectionCounter = getConnectionInfo(connection);

        long nanoSpan = System.nanoTime() - counter.getLastExecuteStartNano();

        dataSourceStat.getStatementStat().error(error);
        dataSourceStat.getStatementStat().afterExecute(nanoSpan);

        connectionCounter.error(error);

        // SQL
        JdbcSqlStat sqlStat = statement.getSqlStat();

        if (sqlStat != null) {
            sqlStat.error(error);
            sqlStat.addExecuteTime(statement.getLastExecuteType(), nanoSpan);
            statement.setLastExecuteTimeNano(nanoSpan);
        }

        super.statement_executeErrorAfter(statement, sql, error);
    }

    @Override
    protected void resultSetOpenAfter(ResultSetProxy resultSet) {
        dataSourceStat.getResultSetStat().beforeOpen();

        resultSet.setConstructNano();
    }

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

        long nanoSpan = System.nanoTime() - resultSet.getConstructNano();

        int fetchCount = resultSet.getFetchRowCount();

        dataSourceStat.getResultSetStat().afterClose(nanoSpan);

        dataSourceStat.getResultSetStat().addFetchRowCount(fetchCount);

        dataSourceStat.getResultSetStat().incrementCloseCounter();

        String sql = resultSet.getSql();
        if (sql != null) {
            JdbcSqlStat sqlStat = resultSet.getSqlStat();
            if (sqlStat != null) {
                sqlStat.addFetchRowCount(fetchCount);
                long stmtExecuteNano = resultSet.getStatementProxy().getLastExecuteTimeNano();
                sqlStat.addResultSetHoldTimeNano(stmtExecuteNano, nanoSpan);
            }
        }

        chain.resultSet_close(resultSet);
    }

    public final static String ATTR_NAME_CONNECTION_STAT = "stat.conn";
    public final static String ATTR_NAME_STATEMENT_STAT  = "stat.stmt";

    public JdbcConnectionStat.Entry getConnectionInfo(ConnectionProxy connection) {
        JdbcConnectionStat.Entry counter = (JdbcConnectionStat.Entry) connection.getAttributes().get(ATTR_NAME_CONNECTION_STAT);

        if (counter == null) {
            connection.getAttributes().put(ATTR_NAME_CONNECTION_STAT,
                                           new JdbcConnectionStat.Entry(this.dataSource.getName(), connection.getId()));
            counter = (JdbcConnectionStat.Entry) connection.getAttributes().get(ATTR_NAME_CONNECTION_STAT);
        }

        return counter;
    }

    public JdbcStatementStat.Entry getStatementInfo(StatementProxy statement) {
        JdbcStatementStat.Entry counter = (JdbcStatementStat.Entry) statement.getAttributes().get(ATTR_NAME_STATEMENT_STAT);

        if (counter == null) {
            statement.getAttributes().put(ATTR_NAME_STATEMENT_STAT, new JdbcStatementStat.Entry());
            counter = (JdbcStatementStat.Entry) statement.getAttributes().get(ATTR_NAME_STATEMENT_STAT);
        }

        return counter;
    }

    @Override
    public long getConnectionActiveCount() {
        return dataSourceStat.getConnections().size();
    }

    @Override
    public long getConnectionCloseCount() {
        return dataSourceStat.getConnectionStat().getCloseCount();
    }

    @Override
    public long getConnectionCommitCount() {
        return dataSourceStat.getConnectionStat().getCommitCount();
    }

    @Override
    public long getConnectionConnectCount() {
        return dataSourceStat.getConnectionStat().getConnectCount();
    }

    @Override
    public long getConnectionConnectMillis() {
        return dataSourceStat.getConnectionStat().getConnectMillis();
    }

    @Override
    public long getConnectionConnectingMax() {
        return dataSourceStat.getConnectionStat().getConnectingMax();
    }

    @Override
    public long getConnectionRollbackCount() {
        return dataSourceStat.getConnectionStat().getConnectMillis();
    }

    @Override
    public long getConnectionConnectAliveMillis() {
        return dataSourceStat.getConnectionConnectAliveMillis();
    }

    @Override
    public long getConnectionConnectErrorCount() {
        return dataSourceStat.getConnectionStat().getConnectErrorCount();
    }

    @Override
    public Date getConnectionConnectLastTime() {
        return dataSourceStat.getConnectionStat().getConnectLastTime();
    }

    @Override
    public long getStatementCloseCount() {
        return dataSourceStat.getStatementStat().getCloseCount();
    }

    @Override
    public long getStatementCreateCount() {
        return dataSourceStat.getStatementStat().getCreateCount();
    }

    @Override
    public long getStatementExecuteMillisTotal() {
        return dataSourceStat.getStatementStat().getExecuteMillisTotal();
    }

    @Override
    public Date getStatementExecuteErrorLastTime() {
        return dataSourceStat.getStatementStat().getLastErrorTime();
    }

    @Override
    public Date getStatementExecuteLastTime() {
        return dataSourceStat.getStatementStat().getExecuteLastTime();
    }

    @Override
    public long getStatementPrepareCallCount() {
        return dataSourceStat.getStatementStat().getPrepareCallCount();
    }

    @Override
    public long getStatementPrepareCount() {
        return dataSourceStat.getStatementStat().getPrepareCount();
    }

    @Override
    public long getStatementExecuteErrorCount() {
        return dataSourceStat.getStatementStat().getErrorCount();
    }

    @Override
    public long getStatementExecuteSuccessCount() {
        return dataSourceStat.getStatementStat().getExecuteSuccessCount();
    }

    @Override
    public long getResultSetHoldMillisTotal() {
        return dataSourceStat.getResultSetStat().getHoldMillisTotal();
    }

    @Override
    public long getResultSetFetchRowCount() {
        return dataSourceStat.getResultSetStat().getFetchRowCount();
    }

    @Override
    public long getResultSetOpenCount() {
        return dataSourceStat.getResultSetStat().getOpenCount();
    }

    @Override
    public long getResultSetCloseCount() {
        return dataSourceStat.getResultSetStat().getCloseCount();
    }

    @Override
    public String getConnectionUrl() {
        return dataSource.getUrl();
    }

    public JdbcSqlStat createSqlStat(StatementProxy statement, String sql) {
        JdbcStatContext context = JdbcStatManager.getInstance().getStatContext();
      String contextSql = context != null ? context.getSql() : null;
      if (contextSql != null && contextSql.length() > 0) {
        return dataSourceStat.createSqlStat(contextSql);
      } else {
        sql = mergeSql(sql);
        return dataSourceStat.createSqlStat(sql);
      }
    }

    public JdbcSqlStat getSqlCounter(String sql) {
     
      sql = mergeSql(sql); //mergeSql
        return getSqlStat(sql);
    }

    public JdbcSqlStat getSqlStat(String sql) {
     
      sql = mergeSql(sql);
        return dataSourceStat.getSqlStat(sql);
    }

    @Override
    public TabularData getSqlList() throws JMException {
        return dataSourceStat.getSqlList();
    }

    public static StatFilter getStatFilter(DataSourceProxy dataSource) {
        for (Filter filter : dataSource.getProxyFilters()) {
            if (filter instanceof StatFilter) {
                return (StatFilter) filter;
            }
        }

        return null;
    }

    public JdbcSqlStat getSqlStat(long id) {
        return dataSourceStat.getSqlStat(id);
    }

    @Override
    public CompositeData getStatementExecuteLastError() throws JMException {
        return dataSourceStat.getStatementStat().getLastError();
    }

    public final ConcurrentMap<Long, JdbcConnectionStat.Entry> getConnections() {
        return dataSourceStat.getConnections();
    }

    @Override
    public TabularData getConnectionList() throws JMException {
        return dataSourceStat.getConnectionList();
    }

    public static enum Feature {

        ;

        private Feature(){
            mask = (1 << ordinal());
        }

        private final int mask;

        public final int getMask() {
            return mask;
        }

        public static boolean isEnabled(int features, Feature feature) {
            return (features & feature.getMask()) != 0;
        }

        public static int config(int features, Feature feature, boolean state) {
            if (state) {
                features |= feature.getMask();
            } else {
                features &= ~feature.getMask();
            }

            return features;
        }
    }
}
TOP

Related Classes of com.alibaba.druid.filter.stat.StatFilter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.