add spring stat support

This commit is contained in:
wenshao
2012-08-13 14:52:39 +08:00
parent efb066f508
commit 428141bd39
22 changed files with 850 additions and 50 deletions
@@ -7,6 +7,7 @@ import java.util.Map;
import com.alibaba.druid.stat.DruidStatManagerFacade;
import com.alibaba.druid.support.http.stat.WebAppStatManager;
import com.alibaba.druid.support.spring.stat.SpringStatManager;
import com.alibaba.druid.util.MapComparator;
import com.alibaba.druid.util.StringUtils;
import com.alibaba.fastjson.JSON;
@@ -90,10 +91,19 @@ public class JSONDruidStatService {
if (url.startsWith("/websession.json")) {
return returnJSONResult(RESULT_CODE_SUCCESS, getWebSessionStatDataList(parameters));
}
if (url.startsWith("/spring.json")) {
return returnJSONResult(RESULT_CODE_SUCCESS, getSpringStatDataList(parameters));
}
return returnJSONResult(RESULT_CODE_ERROR, "Do not support this request, please contact with administrator.");
}
private List<Map<String, Object>> getSpringStatDataList(Map<String, String> parameters) {
List<Map<String, Object>> array = SpringStatManager.getInstance().getMethodStatData();
return comparatorOrderBy(array, parameters);
}
private List<Map<String, Object>> getWebURIStatDataList(Map<String, String> parameters) {
List<Map<String, Object>> array = WebAppStatManager.getInstance().getURIStatData();
return comparatorOrderBy(array, parameters);
@@ -22,6 +22,7 @@ import com.alibaba.druid.support.http.stat.WebRequestStat;
import com.alibaba.druid.support.http.stat.WebSessionStat;
import com.alibaba.druid.support.logging.Log;
import com.alibaba.druid.support.logging.LogFactory;
import com.alibaba.druid.support.spring.stat.SpringMethodStat;
import com.alibaba.druid.util.DruidWebUtils;
import com.alibaba.druid.util.PatternMatcher;
import com.alibaba.druid.util.ServletPathMatcher;
@@ -264,49 +265,79 @@ public class WebStatFilter implements Filter {
@Override
public void addUpdateCount(int updateCount) {
WebRequestStat localStat = WebRequestStat.current();
if (localStat != null) {
localStat.addJdbcUpdateCount(updateCount);
WebRequestStat reqStat = WebRequestStat.current();
if (reqStat != null) {
reqStat.addJdbcUpdateCount(updateCount);
}
SpringMethodStat springMethodStat = SpringMethodStat.current();
if (springMethodStat != null) {
springMethodStat.addJdbcUpdateCount(updateCount);
}
}
@Override
public void addFetchRowCount(int fetchRowCount) {
WebRequestStat localStat = WebRequestStat.current();
if (localStat != null) {
localStat.addJdbcFetchRowCount(fetchRowCount);
WebRequestStat reqStat = WebRequestStat.current();
if (reqStat != null) {
reqStat.addJdbcFetchRowCount(fetchRowCount);
}
SpringMethodStat springMethodStat = SpringMethodStat.current();
if (springMethodStat != null) {
springMethodStat.addJdbcFetchRowCount(fetchRowCount);
}
}
@Override
public void executeBefore(String sql, boolean inTransaction) {
WebRequestStat localStat = WebRequestStat.current();
if (localStat != null) {
localStat.incrementJdbcExecuteCount();
WebRequestStat reqStat = WebRequestStat.current();
if (reqStat != null) {
reqStat.incrementJdbcExecuteCount();
}
SpringMethodStat springMethodStat = SpringMethodStat.current();
if (springMethodStat != null) {
springMethodStat.incrementJdbcExecuteCount();
}
}
@Override
public void executeAfter(String sql, long nanoSpan, Throwable error) {
WebRequestStat localStat = WebRequestStat.current();
if (localStat != null) {
localStat.addJdbcExecuteNano(nanoSpan);
public void executeAfter(String sql, long nanos, Throwable error) {
WebRequestStat reqStat = WebRequestStat.current();
if (reqStat != null) {
reqStat.addJdbcExecuteTimeNano(nanos);
}
SpringMethodStat springMethodStat = SpringMethodStat.current();
if (springMethodStat != null) {
springMethodStat.addJdbcExecuteTimeNano(nanos);
}
}
@Override
public void commit() {
WebRequestStat localStat = WebRequestStat.current();
if (localStat != null) {
localStat.incrementJdbcCommitCount();
WebRequestStat reqStat = WebRequestStat.current();
if (reqStat != null) {
reqStat.incrementJdbcCommitCount();
}
SpringMethodStat springMethodStat = SpringMethodStat.current();
if (springMethodStat != null) {
springMethodStat.incrementJdbcCommitCount();
}
}
@Override
public void rollback() {
WebRequestStat localStat = WebRequestStat.current();
if (localStat != null) {
localStat.incrementJdbcRollbackCount();
WebRequestStat reqStat = WebRequestStat.current();
if (reqStat != null) {
reqStat.incrementJdbcRollbackCount();
}
SpringMethodStat springMethodStat = SpringMethodStat.current();
if (springMethodStat != null) {
springMethodStat.incrementJdbcRollbackCount();
}
}
}
@@ -61,11 +61,11 @@ public class WebRequestStat {
return jdbcExecuteCount;
}
public long getJdbcExecuteNano() {
public long getJdbcExecuteTimeNano() {
return jdbcExecuteNano;
}
public void addJdbcExecuteNano(long nano) {
public void addJdbcExecuteTimeNano(long nano) {
jdbcExecuteNano += nano;
}
@@ -135,7 +135,7 @@ public class WebSessionStat {
this.addJdbcUpdateCount(requestStat.getJdbcUpdateCount());
this.addJdbcCommitCount(requestStat.getJdbcCommitCount());
this.addJdbcRollbackCount(requestStat.getJdbcRollbackCount());
this.addJdbcExecuteTimeNano(requestStat.getJdbcExecuteNano());
this.addJdbcExecuteTimeNano(requestStat.getJdbcExecuteTimeNano());
}
}
@@ -0,0 +1,87 @@
package com.alibaba.druid.support.spring.stat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.Assert;
import org.springframework.util.PatternMatchUtils;
/**
* 类BeanTypeAutoProxyCreator.java的实现描述:使用配置类型代替Springframework中配置名称的实现
*
* @author hualiang.lihl 2011-12-31 上午10:48:20
*/
public class BeanTypeAutoProxyCreator extends AbstractAutoProxyCreator implements InitializingBean, ApplicationContextAware {
private static final long serialVersionUID = -9094985530794052264L;
private Class<?> targetBeanType;
private ApplicationContext context;
private List<String> beanNames = new ArrayList<String>();
/**
* @param targetClass the targetClass to set
*/
public void setTargetBeanType(Class<?> targetClass) {
this.targetBeanType = targetClass;
}
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.context = context;
}
/**
* Identify as bean to proxy if the bean name is in the configured list of names.
*/
@SuppressWarnings("rawtypes")
protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName, TargetSource targetSource) {
for (Iterator<String> it = this.beanNames.iterator(); it.hasNext();) {
String mappedName = (String) it.next();
if (FactoryBean.class.isAssignableFrom(beanClass)) {
if (!mappedName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
continue;
}
mappedName = mappedName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
}
if (isMatch(beanName, mappedName)) {
return PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS;
}
}
return DO_NOT_PROXY;
}
/**
* Return if the given bean name matches the mapped name.
* <p>
* The default implementation checks for "xxx*", "*xxx" and "*xxx*" matches, as well as direct equality. Can be
* overridden in subclasses.
*
* @param beanName the bean name to check
* @param mappedName the name in the configured list of names
* @return if the names match
* @see org.springframework.util.PatternMatchUtils#simpleMatch(String, String)
*/
protected boolean isMatch(String beanName, String mappedName) {
return PatternMatchUtils.simpleMatch(mappedName, beanName);
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(targetBeanType, "targetType cannot be null");
String[] beanNames = context.getBeanNamesForType(targetBeanType);
for (String name : beanNames) {
this.beanNames.add(name);
}
}
}
@@ -0,0 +1,128 @@
package com.alibaba.druid.support.spring.stat;
import java.lang.reflect.Method;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.TargetSource;
import org.springframework.beans.factory.InitializingBean;
import com.alibaba.druid.support.logging.Log;
import com.alibaba.druid.support.logging.LogFactory;
public class DruidStatInterceptor implements MethodInterceptor, InitializingBean {
private final static Log LOG = LogFactory.getLog(DruidStatInterceptor.class);
private static SpringStat springStat = new SpringStat();
public DruidStatInterceptor() {
}
@Override
public void afterPropertiesSet() throws Exception {
SpringStatManager.getInstance().addSpringStat(springStat);
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
SpringMethodStat lastMethodStat = SpringMethodStat.current();
MethodInfo methodInfo = getMethodInfo(invocation);
SpringMethodStat methodStat = springStat.getMethodStat(methodInfo, true);
if (methodStat != null) {
methodStat.beforeInvoke();
}
long startNanos = System.nanoTime();
Throwable error = null;
try {
Object returnObject = invocation.proceed();
return returnObject;
} catch (Throwable e) {
error = e;
throw e;
} finally {
long endNanos = System.nanoTime();
long nanos = endNanos - startNanos;
if (methodStat != null) {
methodStat.afterInvoke(error, nanos);
}
SpringMethodStat.setCurrent(lastMethodStat);
}
}
public MethodInfo getMethodInfo(MethodInvocation invocation) {
Object thisObject = invocation.getThis();
Method method = invocation.getMethod();
if (thisObject == null) {
return new MethodInfo(method.getDeclaringClass(), method);
}
if (method.getDeclaringClass() == thisObject.getClass()) {
return new MethodInfo(method.getDeclaringClass(), method);
}
{
Class<?> clazz = thisObject.getClass();
boolean isCglibProxy = false;
boolean isJavassistProxy = false;
for (Class<?> item : clazz.getInterfaces()) {
if (item.getName().equals("net.sf.cglib.proxy.Factory")) {
isCglibProxy = true;
break;
} else if (item.getName().equals("javassist.util.proxy.ProxyObject")) {
isJavassistProxy = true;
break;
}
}
if (isCglibProxy || isJavassistProxy) {
Class<?> superClazz = clazz.getSuperclass();
return new MethodInfo(superClazz, method);
}
}
Class<?> clazz = null;
try {
// 最多支持10层代理
for (int i = 0; i < 10; ++i) {
if (thisObject instanceof org.springframework.aop.framework.Advised) {
TargetSource targetSource = ((org.springframework.aop.framework.Advised) thisObject).getTargetSource();
if (targetSource == null) {
break;
}
Object target = targetSource.getTarget();
if (target != null) {
thisObject = target;
} else {
clazz = targetSource.getTargetClass();
break;
}
} else {
break;
}
}
} catch (Exception ex) {
LOG.error("getMethodInfo error", ex);
}
if (clazz == null) {
return new MethodInfo(method.getDeclaringClass(), method);
}
return new MethodInfo(clazz, method);
}
}
@@ -0,0 +1,83 @@
package com.alibaba.druid.support.spring.stat;
import java.lang.reflect.Method;
public class MethodInfo {
private final Class<?> instanceClass;
private final Method method;
private final String signature;
public MethodInfo(Class<?> instanceClass, Method method){
super();
this.instanceClass = instanceClass;
this.method = method;
signature = getMethodSignature(this.getMethod());
}
public Class<?> getInstanceClass() {
return instanceClass;
}
public Method getMethod() {
return method;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((instanceClass == null) ? 0 : instanceClass.hashCode());
result = prime * result + ((method == null) ? 0 : method.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
MethodInfo other = (MethodInfo) obj;
if (instanceClass != other.instanceClass) {
return false;
}
if (signature == null) {
return other.signature == null;
}
if (!signature.equals(other.signature)) {
return false;
}
return true;
}
public static String getMethodSignature(Method method) {
StringBuffer sb = new StringBuffer();
sb.append(method.getName() + "(");
Class<?>[] params = method.getParameterTypes();
for (int j = 0; j < params.length; j++) {
sb.append(params[j].getName());
if (j < (params.length - 1)) sb.append(",");
}
sb.append(")");
return sb.toString();
}
public String getSignature() {
return signature;
}
}
@@ -0,0 +1,184 @@
package com.alibaba.druid.support.spring.stat;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
public class SpringMethodStat {
private final static ThreadLocal<SpringMethodStat> currentLocal = new ThreadLocal<SpringMethodStat>();
private final MethodInfo methodInfo;
private final AtomicInteger runningCount = new AtomicInteger();
private final AtomicInteger concurrentMax = new AtomicInteger();
private final AtomicLong executeCount = new AtomicLong(0);
private final AtomicLong executeErrorCount = new AtomicLong(0);
private final AtomicLong executeTimeNano = new AtomicLong();
private final AtomicLong jdbcFetchRowCount = new AtomicLong();
private final AtomicLong jdbcUpdateCount = new AtomicLong();
private final AtomicLong jdbcExecuteCount = new AtomicLong();
private final AtomicLong jdbcExecuteTimeNano = new AtomicLong();
private final AtomicLong jdbcCommitCount = new AtomicLong();
private final AtomicLong jdbcRollbackCount = new AtomicLong();
public SpringMethodStat(MethodInfo methodInfo){
this.methodInfo = methodInfo;
}
public MethodInfo getMethodInfo() {
return methodInfo;
}
public static SpringMethodStat current() {
return currentLocal.get();
}
public static void setCurrent(SpringMethodStat current) {
currentLocal.set(current);
}
public void beforeInvoke() {
currentLocal.set(this);
int running = runningCount.incrementAndGet();
for (;;) {
int max = concurrentMax.get();
if (running > max) {
if (concurrentMax.compareAndSet(max, running)) {
break;
} else {
continue;
}
} else {
break;
}
}
executeCount.incrementAndGet();
}
public void afterInvoke(Throwable error, long nanos) {
runningCount.decrementAndGet();
executeTimeNano.addAndGet(nanos);
if (error != null) {
executeErrorCount.incrementAndGet();
}
}
public int getRunningCount() {
return this.runningCount.get();
}
public long getConcurrentMax() {
return concurrentMax.get();
}
public long getExecuteCount() {
return executeCount.get();
}
public long getExecuteErrorCount() {
return executeErrorCount.get();
}
public long getExecuteTimeNano() {
return executeTimeNano.get();
}
public long getExecuteTimeMillis() {
return getExecuteTimeNano() / (1000 * 1000);
}
public void addJdbcFetchRowCount(long delta) {
this.jdbcFetchRowCount.addAndGet(delta);
}
public long getJdbcFetchRowCount() {
return jdbcFetchRowCount.get();
}
public void addJdbcUpdateCount(long updateCount) {
this.jdbcUpdateCount.addAndGet(updateCount);
}
public long getJdbcUpdateCount() {
return jdbcUpdateCount.get();
}
public void incrementJdbcExecuteCount() {
jdbcExecuteCount.incrementAndGet();
}
public void addJdbcExecuteCount(long executeCount) {
jdbcExecuteCount.addAndGet(executeCount);
}
public long getJdbcExecuteCount() {
return jdbcExecuteCount.get();
}
public long getJdbcExecuteTimeMillis() {
return getJdbcExecuteTimeNano() / (1000 * 1000);
}
public long getJdbcExecuteTimeNano() {
return jdbcExecuteTimeNano.get();
}
public void addJdbcExecuteTimeNano(long nano) {
jdbcExecuteTimeNano.addAndGet(nano);
}
public void incrementJdbcCommitCount() {
jdbcCommitCount.incrementAndGet();
}
public long getJdbcCommitCount() {
return jdbcCommitCount.get();
}
public void addJdbcCommitCount(long commitCount) {
this.jdbcCommitCount.addAndGet(commitCount);
}
public void incrementJdbcRollbackCount() {
jdbcRollbackCount.incrementAndGet();
}
public long getJdbcRollbackCount() {
return jdbcRollbackCount.get();
}
public void addJdbcRollbackCount(long rollbackCount) {
this.jdbcRollbackCount.addAndGet(rollbackCount);
}
public Map<String, Object> getStatData() {
Map<String, Object> data = new LinkedHashMap<String, Object>();
data.put("Class", this.getMethodInfo().getInstanceClass().getName());
data.put("Method", this.getMethodInfo().getSignature());
data.put("RunningCount", this.getRunningCount());
data.put("ConcurrentMax", this.getConcurrentMax());
data.put("ExecuteCount", this.getExecuteCount());
data.put("ExecuteErrorCount", this.getExecuteErrorCount());
data.put("ExecuteTimeMillis", this.getExecuteTimeMillis());
data.put("JdbcCommitCount", this.getJdbcCommitCount());
data.put("JdbcRollbackCount", this.getJdbcRollbackCount());
data.put("JdbcExecuteCount", this.getJdbcExecuteCount());
data.put("JdbcExecuteTimeMillis", this.getJdbcExecuteTimeMillis());
data.put("JdbcFetchRowCount", this.getJdbcFetchRowCount());
data.put("JdbcUpdateCount", this.getJdbcUpdateCount());
return data;
}
}
@@ -0,0 +1,47 @@
package com.alibaba.druid.support.spring.stat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class SpringStat {
private ConcurrentMap<MethodInfo, SpringMethodStat> methodStats = new ConcurrentHashMap<MethodInfo, SpringMethodStat>();
public SpringStat(){
}
public SpringMethodStat getMethodStat(MethodInfo methodInfo, boolean create) {
SpringMethodStat methodStat = methodStats.get(methodInfo);
if (methodStat != null) {
return methodStat;
}
if (create) {
methodStats.putIfAbsent(methodInfo, new SpringMethodStat(methodInfo));
methodStat = methodStats.get(methodInfo);
}
return methodStat;
}
public List<Map<String, Object>> getMethodStatDataList() {
List<Map<String, Object>> methodStatDataList = new ArrayList<Map<String, Object>>(this.methodStats.size());
for (SpringMethodStat methodStat : this.methodStats.values()) {
Map<String, Object> methodStatData = methodStat.getStatData();
int runningCount = ((Number) methodStatData.get("RunningCount")).intValue();
long executeCount = (Long) methodStatData.get("ExecuteCount");
if (runningCount == 0 && executeCount == 0) {
continue;
}
methodStatDataList.add(methodStatData);
}
return methodStatDataList;
}
}
@@ -0,0 +1,65 @@
package com.alibaba.druid.support.spring.stat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
public class SpringStatManager {
public final static String SYS_PROP_INSTANCES = "druid.spring.springStat";
private final static SpringStatManager instance = new SpringStatManager();
private Set<Object> springStatSet = null;
public static SpringStatManager getInstance() {
return instance;
}
public Set<Object> getSpringStatSet() {
if (springStatSet == null) {
springStatSet = getSpringStatSetFromSysProperty();
}
return springStatSet;
}
public void addSpringStat(Object springStat) {
getSpringStatSet().add(springStat);
}
@SuppressWarnings("unchecked")
static Set<Object> getSpringStatSetFromSysProperty() {
Properties properties = System.getProperties();
Set<Object> webAppStats = (Set<Object>) properties.get(SYS_PROP_INSTANCES);
if (webAppStats == null) {
synchronized (properties) {
webAppStats = (Set<Object>) properties.get(SYS_PROP_INSTANCES);
if (webAppStats == null) {
webAppStats = new CopyOnWriteArraySet<Object>();
properties.put(SYS_PROP_INSTANCES, webAppStats);
}
}
}
return webAppStats;
}
public List<Map<String, Object>> getMethodStatData() {
Set<Object> stats = getSpringStatSet();
List<Map<String, Object>> allMethodStatDataList = new ArrayList<Map<String, Object>>();
for (Object stat : stats) {
List<Map<String, Object>> methodStatDataList = SpringStatUtils.getMethodStatDataList(stat);
allMethodStatDataList.addAll(methodStatDataList);
}
return allMethodStatDataList;
}
}
@@ -0,0 +1,29 @@
package com.alibaba.druid.support.spring.stat;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import com.alibaba.druid.support.logging.Log;
import com.alibaba.druid.support.logging.LogFactory;
public class SpringStatUtils {
private final static Log LOG = LogFactory.getLog(SpringStatUtils.class);
@SuppressWarnings("unchecked")
public static List<Map<String, Object>> getMethodStatDataList(Object methodStat) {
if (methodStat.getClass() == SpringStat.class) {
return ((SpringStat) methodStat).getMethodStatDataList();
}
try {
Method method = methodStat.getClass().getMethod("getMethodStatDataList");
Object obj = method.invoke(methodStat);
return (List<Map<String, Object>>) obj;
} catch (Exception e) {
LOG.error("getMethodStatDataList error", e);
return null;
}
}
}