feat:sql打印带堆栈
This commit is contained in:
1 parent
2f847923ef
commit
96c058ca9a
1 file changed
+154
-134
+154
-134
@@ -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<String> skipTableList = properties.getSkipTable();
|
||||
// 判断表名是配置了匹配过滤
|
||||
if (CollUtil.isNotEmpty(properties.getSkipTable())) {
|
||||
List<String> skipTableList = properties.getSkipTable();
|
||||
|
||||
List<String> tableNameList = getTablesBydruid(sql, dbType);
|
||||
if (tableNameList.stream()
|
||||
.anyMatch(tableName -> StrUtil.containsAnyIgnoreCase(tableName,
|
||||
ArrayUtil.toArray(skipTableList, String.class)))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
List<String> tableNameList = getTablesBydruid(sql, dbType);
|
||||
if (tableNameList.stream()
|
||||
.anyMatch(tableName -> StrUtil.containsAnyIgnoreCase(tableName,
|
||||
ArrayUtil.toArray(skipTableList, String.class)))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int parametersSize = statement.getParametersSize();
|
||||
List<Object> 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<Object> 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<String>
|
||||
*/
|
||||
public static List<String> getTablesBydruid(String sql, String dbType) {
|
||||
List<String> result = new ArrayList<String>();
|
||||
List<SQLStatement> 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<TableStat.Name, TableStat> 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<String>
|
||||
*/
|
||||
public static List<String> getTablesBydruid(String sql, String dbType) {
|
||||
List<String> result = new ArrayList<String>();
|
||||
List<SQLStatement> 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<TableStat.Name, TableStat> tables = visitor.getTables();
|
||||
for (TableStat.Name name : tables.keySet()) {
|
||||
result.add(name.getName());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user