標籤:spring aop sql日誌
對資料庫連接池Proxool比較熟悉的讀者,都知道Proxool可以記錄SQL執行內容和時間等資訊日誌。我們可以將該日誌記錄專門的SQL記錄檔,對於尋找執行特別耗時的SQL起了不小的作用。對於一些其他串連池,沒有該特性時,本文介紹Spring AOP切面方法來記錄SQL日誌。
當然也可以通過資料庫提供的特性來查詢執行效率較低的SQL,本文不做探討。
本文介紹使用SpringJdbcTemplate執行SQL,使用其他方法或者ORM思路類似(Hibernate提供了日誌記錄功能)。
使用AOP,可以使用Around通知,在JdbcTemplate執行方法前,記錄目前時間,在方法執行完後,計算SQL耗時,並記錄日誌。思路很簡單,不過多介紹,代碼如下。
package org.enyes.sql.util;import org.apache.log4j.Logger;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;/** * 對Spring JdbcTemplate進行切面,記錄每個sql執行時間。 */@Aspectpublic class SqlExecutionTimeAspect {/** * logger */private static final Logger LOG = Logger.getLogger(SqlExecutionTimeAspect.class);/** * 當Sql執行時間超過該值時,則進行log warn層級題型,否則記錄INFO日誌。 */private long warnWhenOverTime = 2 * 60 * 1000L;@Around("execution(* org.springframework.jdbc.core.JdbcTemplate.*(..))")public Object logSqlExecutionTime(ProceedingJoinPoint joinPoint)throws Throwable {long startTime = System.currentTimeMillis();Object result = joinPoint.proceed();long costTime = System.currentTimeMillis() - startTime;if (costTime > warnWhenOverTime) {StringBuilder sb = new StringBuilder();sb.append("execute method :").append(joinPoint.getSignature());sb.append("args: ").append(arrayToString(joinPoint.getArgs()));sb.append(" cost time[").append(costTime).append("]ms");LOG.warn(sb);} else if (LOG.isInfoEnabled()) {StringBuilder sb = new StringBuilder();sb.append("execute method :").append(joinPoint.getSignature());sb.append("args: ").append(arrayToString(joinPoint.getArgs()));sb.append(" cost time[").append(costTime).append("]ms");LOG.info(sb);}return result;}private static String arrayToString(Object[] a) {if (a == null)return "null";int iMax = a.length - 1;if (iMax == -1)return "[]";StringBuilder b = new StringBuilder();b.append('[');for (int i = 0;; i++) {if (a[i] instanceof Object[]) {b.append(arrayToString((Object[]) a[i]));} else {b.append(String.valueOf(a[i]));}if (i == iMax)return b.append(']').toString();b.append(", ");}}}
Springxml配置如下:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.1.xsd "><beans><aop:aspectj-autoproxy proxy-target-class="true"/><bean class="org.enyes.sql.util.SqlExecutionTimeAspect"/></beans>
可以使用Log4J將SqlExecutionTimeAspect類的日誌列印到專門的日誌中,並且warnWhenOverTime提供setter方法,可以通過Spring xml來具體配置。
完畢。
Spring AOP監控SQL執行