diff --git a/pjcloud-common/pjcloud-common-data/src/main/java/com/bsd/pjcloud/common/data/mybatis/DruidSqlLogFilter.java b/pjcloud-common/pjcloud-common-data/src/main/java/com/bsd/pjcloud/common/data/mybatis/DruidSqlLogFilter.java index 46b3e23ac..b0500a259 100644 --- a/pjcloud-common/pjcloud-common-data/src/main/java/com/bsd/pjcloud/common/data/mybatis/DruidSqlLogFilter.java +++ b/pjcloud-common/pjcloud-common-data/src/main/java/com/bsd/pjcloud/common/data/mybatis/DruidSqlLogFilter.java @@ -53,159 +53,179 @@ import java.util.Map; @RequiredArgsConstructor public class DruidSqlLogFilter extends FilterEventAdapter { - private static final SQLUtils.FormatOption FORMAT_OPTION = new SQLUtils.FormatOption(false, false); + private static final SQLUtils.FormatOption FORMAT_OPTION = new SQLUtils.FormatOption(false, false); - private final PjcloudMybatisProperties properties; + private final PjcloudMybatisProperties properties; - @Override - protected void statementExecuteBefore(StatementProxy statement, String sql) { - statement.setLastExecuteStartNano(); - } + @Override + protected void statementExecuteBefore(StatementProxy statement, String sql) { + statement.setLastExecuteStartNano(); + } - @Override - protected void statementExecuteBatchBefore(StatementProxy statement) { - statement.setLastExecuteStartNano(); - } + @Override + protected void statementExecuteBatchBefore(StatementProxy statement) { + statement.setLastExecuteStartNano(); + } - @Override - protected void statementExecuteUpdateBefore(StatementProxy statement, String sql) { - statement.setLastExecuteStartNano(); - } + @Override + protected void statementExecuteUpdateBefore(StatementProxy statement, String sql) { + statement.setLastExecuteStartNano(); + } - @Override - protected void statementExecuteQueryBefore(StatementProxy statement, String sql) { - statement.setLastExecuteStartNano(); - } + @Override + protected void statementExecuteQueryBefore(StatementProxy statement, String sql) { + statement.setLastExecuteStartNano(); + } - @Override - protected void statementExecuteAfter(StatementProxy statement, String sql, boolean firstResult) { - statement.setLastExecuteTimeNano(); - } + @Override + protected void statementExecuteAfter(StatementProxy statement, String sql, boolean firstResult) { + statement.setLastExecuteTimeNano(); + } - @Override - protected void statementExecuteBatchAfter(StatementProxy statement, int[] result) { - statement.setLastExecuteTimeNano(); - } + @Override + protected void statementExecuteBatchAfter(StatementProxy statement, int[] result) { + statement.setLastExecuteTimeNano(); + } - @Override - protected void statementExecuteQueryAfter(StatementProxy statement, String sql, ResultSetProxy resultSet) { - statement.setLastExecuteTimeNano(); - } + @Override + protected void statementExecuteQueryAfter(StatementProxy statement, String sql, ResultSetProxy resultSet) { + statement.setLastExecuteTimeNano(); + } - @Override - protected void statementExecuteUpdateAfter(StatementProxy statement, String sql, int updateCount) { - statement.setLastExecuteTimeNano(); - } + @Override + protected void statementExecuteUpdateAfter(StatementProxy statement, String sql, int updateCount) { + statement.setLastExecuteTimeNano(); + } - @Override - public void statement_close(FilterChain chain, StatementProxy statement) throws SQLException { - // 先调用父类关闭 statement - super.statement_close(chain, statement); - // 支持动态开启 - if (!properties.isShowSql()) { - return; - } + @Override + public void statement_close(FilterChain chain, StatementProxy statement) throws SQLException { + // 先调用父类关闭 statement + super.statement_close(chain, statement); + // 支持动态开启 + if (!properties.isShowSql()) { + return; + } - // 是否开启调试 - if (!log.isInfoEnabled()) { - return; - } + // 是否开启调试 + if (!log.isInfoEnabled()) { + return; + } - // 打印可执行的 sql - String sql = statement.getBatchSql(); - // sql 为空直接返回 - if (StringUtils.isEmpty(sql)) { - return; - } + // 打印可执行的 sql + String sql = statement.getBatchSql(); + // sql 为空直接返回 + if (StringUtils.isEmpty(sql)) { + return; + } - String dbType = statement.getConnectionProxy().getDirectDataSource().getDbType(); + String dbType = statement.getConnectionProxy().getDirectDataSource().getDbType(); - // 判断表名是配置了匹配过滤 - if (CollUtil.isNotEmpty(properties.getSkipTable())) { - List skipTableList = properties.getSkipTable(); + // 判断表名是配置了匹配过滤 + if (CollUtil.isNotEmpty(properties.getSkipTable())) { + List skipTableList = properties.getSkipTable(); - List tableNameList = getTablesBydruid(sql, dbType); - if (tableNameList.stream() - .anyMatch(tableName -> StrUtil.containsAnyIgnoreCase(tableName, - ArrayUtil.toArray(skipTableList, String.class)))) { - return; - } - } + List tableNameList = getTablesBydruid(sql, dbType); + if (tableNameList.stream() + .anyMatch(tableName -> StrUtil.containsAnyIgnoreCase(tableName, + ArrayUtil.toArray(skipTableList, String.class)))) { + return; + } + } - int parametersSize = statement.getParametersSize(); - List parameters = new ArrayList<>(parametersSize); - for (int i = 0; i < parametersSize; ++i) { - // 转换参数,处理 java8 时间 - parameters.add(getJdbcParameter(statement.getParameter(i))); - } - String formattedSql = SQLUtils.format(sql, DbType.of(dbType), parameters, FORMAT_OPTION); - printSql(formattedSql, statement); - } + int parametersSize = statement.getParametersSize(); + List parameters = new ArrayList<>(parametersSize); + for (int i = 0; i < parametersSize; ++i) { + // 转换参数,处理 java8 时间 + parameters.add(getJdbcParameter(statement.getParameter(i))); + } + String formattedSql = SQLUtils.format(sql, DbType.of(dbType), parameters, FORMAT_OPTION); + printSqlV2(formattedSql, statement); + } - private static Object getJdbcParameter(JdbcParameter jdbcParam) { - if (jdbcParam == null) { - return null; - } - Object value = jdbcParam.getValue(); - // 处理 java8 时间 - if (value instanceof TemporalAccessor) { - return value.toString(); - } - return value; - } + private static Object getJdbcParameter(JdbcParameter jdbcParam) { + if (jdbcParam == null) { + return null; + } + Object value = jdbcParam.getValue(); + // 处理 java8 时间 + if (value instanceof TemporalAccessor) { + return value.toString(); + } + return value; + } - private static void printSql(String sql, StatementProxy statement) { - // 打印 sql - String sqlLogger = "\n\n======= Sql Logger ======================" + "\n{}" - + "\n======= Sql Execute Time: {} =======\n"; - log.info(sqlLogger, sql.trim(), format(statement.getLastExecuteTimeNano())); - } + private static void printSql(String sql, StatementProxy statement) { + // 打印 sql + String sqlLogger = "\n\n======= Sql Logger ======================" + "\n{}" + + "\n======= Sql Execute Time: {} =======\n"; + log.info(sqlLogger, sql.trim(), format(statement.getLastExecuteTimeNano())); + } - /** - * 格式化执行时间,单位为 ms 和 s,保留三位小数 - * @param nanos 纳秒 - * @return 格式化后的时间 - */ - private static String format(long nanos) { - if (nanos < 1) { - return "0ms"; - } - double millis = (double) nanos / (1000 * 1000); - // 不够 1 ms,最小单位为 ms - if (millis > 1000) { - return String.format("%.3fs", millis / 1000); - } - else { - return String.format("%.3fms", millis); - } - } + private static void printSqlV2(String sql, StatementProxy statement) { + // 打印 sql 和调用堆栈 + StringBuilder sqlLogger = new StringBuilder(); + sqlLogger.append("\n\n======= Sql Logger V2 ======================\n"); + sqlLogger.append("{}\n"); // SQL语句占位符 + sqlLogger.append("======= Sql Execute Time: {} =======\n"); // 执行时间占位符 + sqlLogger.append("======= Call Stack ======================\n"); - /** - * 从SQL中提取表名(sql中出现的所有表) - * @param sql sql语句 - * @param dbType dbType - * @return List - */ - public static List getTablesBydruid(String sql, String dbType) { - List result = new ArrayList(); - List stmtList = SQLUtils.parseStatements(sql, dbType); - for (SQLStatement stmt : stmtList) { - // 也可以用更精确的解析器,如MySqlSchemaStatVisitor - SchemaStatVisitor visitor; - // SQL server 数据库特殊处理 - if (DbType.sqlserver.name().equalsIgnoreCase(dbType)) { - visitor = new SQLServerSchemaStatVisitor(); - } - else { - visitor = new SchemaStatVisitor(); - } - stmt.accept(visitor); - Map tables = visitor.getTables(); - for (TableStat.Name name : tables.keySet()) { - result.add(name.getName()); - } - } - return result; - } + // 获取调用堆栈信息 + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + for (int i = 3; i < stackTrace.length; i++) { // 跳过前几个内部调用 + if (stackTrace[i].getClassName().contains("com.bsd.pjcloud")) { // 只记录项目相关的调用 + sqlLogger.append(" at ").append(stackTrace[i].toString()).append("\n"); + } + } + sqlLogger.append("=========================================\n"); + + log.info(sqlLogger.toString(), sql.trim(), format(statement.getLastExecuteTimeNano())); + } + + /** + * 格式化执行时间,单位为 ms 和 s,保留三位小数 + * + * @param nanos 纳秒 + * @return 格式化后的时间 + */ + private static String format(long nanos) { + if (nanos < 1) { + return "0ms"; + } + double millis = (double) nanos / (1000 * 1000); + // 不够 1 ms,最小单位为 ms + if (millis > 1000) { + return String.format("%.3fs", millis / 1000); + } else { + return String.format("%.3fms", millis); + } + } + + /** + * 从SQL中提取表名(sql中出现的所有表) + * + * @param sql sql语句 + * @param dbType dbType + * @return List + */ + public static List getTablesBydruid(String sql, String dbType) { + List result = new ArrayList(); + List stmtList = SQLUtils.parseStatements(sql, dbType); + for (SQLStatement stmt : stmtList) { + // 也可以用更精确的解析器,如MySqlSchemaStatVisitor + SchemaStatVisitor visitor; + // SQL server 数据库特殊处理 + if (DbType.sqlserver.name().equalsIgnoreCase(dbType)) { + visitor = new SQLServerSchemaStatVisitor(); + } else { + visitor = new SchemaStatVisitor(); + } + stmt.accept(visitor); + Map tables = visitor.getTables(); + for (TableStat.Name name : tables.keySet()) { + result.add(name.getName()); + } + } + return result; + } }