improve spring-mvc stat support

This commit is contained in:
wenshao
2013-08-10 18:13:01 +08:00
parent b0f87d132d
commit 8e7af7308a
6 changed files with 474 additions and 241 deletions
@@ -0,0 +1,256 @@
/*
* 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.support.http;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import com.alibaba.druid.filter.stat.StatFilterContextListenerAdapter;
import com.alibaba.druid.support.http.stat.WebAppStat;
import com.alibaba.druid.support.http.stat.WebRequestStat;
import com.alibaba.druid.support.http.stat.WebSessionStat;
import com.alibaba.druid.util.DruidWebUtils;
public class AbstractWebStatImpl {
public final static int DEFAULT_MAX_STAT_SESSION_COUNT = 1000 * 1;
protected WebAppStat webAppStat = null;
protected boolean sessionStatEnable = true;
protected int sessionStatMaxCount = DEFAULT_MAX_STAT_SESSION_COUNT;
protected boolean createSession = false;
protected boolean profileEnable = false;
protected String contextPath;
protected String principalSessionName;
protected String principalCookieName;
protected String realIpHeader;
protected WebStatFilterContextListener statFilterContextListener = new WebStatFilterContextListener();
public boolean isSessionStatEnable() {
return sessionStatEnable;
}
public void setSessionStatEnable(boolean sessionStatEnable) {
this.sessionStatEnable = sessionStatEnable;
}
public boolean isProfileEnable() {
return profileEnable;
}
public void setProfileEnable(boolean profileEnable) {
this.profileEnable = profileEnable;
}
public String getContextPath() {
return contextPath;
}
public int getSessionStatMaxCount() {
return sessionStatMaxCount;
}
public String getRequestURI(HttpServletRequest request) {
return request.getRequestURI();
}
public String getPrincipalSessionName() {
return principalSessionName;
}
public String getPrincipalCookieName() {
return principalCookieName;
}
public WebSessionStat getSessionStat(HttpServletRequest request) {
if (!isSessionStatEnable()) {
return null;
}
WebSessionStat sessionStat = null;
String sessionId = getSessionId(request);
if (sessionId != null) {
sessionStat = webAppStat.getSessionStat(sessionId, true);
}
if (sessionStat != null) {
long currentMillis = System.currentTimeMillis();
String userAgent = request.getHeader("user-agent");
if (sessionStat.getCreateTimeMillis() == -1L) {
HttpSession session = request.getSession(false);
if (session != null) {
sessionStat.setCreateTimeMillis(session.getCreationTime());
} else {
sessionStat.setCreateTimeMillis(currentMillis);
}
webAppStat.computeUserAgent(userAgent);
webAppStat.incrementSessionCount();
}
sessionStat.setUserAgent(userAgent);
String ip = getRemoteAddress(request);
sessionStat.addRemoteAddress(ip);
}
return sessionStat;
}
protected String getRemoteAddress(HttpServletRequest request) {
String ip = null;
if (this.realIpHeader != null && this.realIpHeader.length() != 0) {
ip = request.getHeader(realIpHeader);
}
if (ip == null || ip.length() == 0) {
ip = DruidWebUtils.getRemoteAddr(request);
}
return ip;
}
public String getSessionId(HttpServletRequest httpRequest) {
String sessionId = null;
HttpSession session = httpRequest.getSession(createSession);
if (session != null) {
sessionId = session.getId();
}
return sessionId;
}
public String getPrincipal(HttpServletRequest httpRequest) {
if (principalSessionName != null) {
HttpSession session = httpRequest.getSession(createSession);
if (session == null) {
return null;
}
Object sessionValue = session.getAttribute(principalSessionName);
if (sessionValue == null) {
return null;
}
return sessionValue.toString();
}
if (principalCookieName != null && httpRequest.getCookies() != null) {
for (Cookie cookie : httpRequest.getCookies()) {
if (principalCookieName.equals(cookie.getName())) {
return cookie.getValue();
}
}
}
return null;
}
public class WebStatFilterContextListener extends StatFilterContextListenerAdapter {
@Override
public void addUpdateCount(int updateCount) {
WebRequestStat reqStat = WebRequestStat.current();
if (reqStat != null) {
reqStat.addJdbcUpdateCount(updateCount);
}
}
@Override
public void addFetchRowCount(int fetchRowCount) {
WebRequestStat reqStat = WebRequestStat.current();
if (reqStat != null) {
reqStat.addJdbcFetchRowCount(fetchRowCount);
}
}
@Override
public void executeBefore(String sql, boolean inTransaction) {
WebRequestStat reqStat = WebRequestStat.current();
if (reqStat != null) {
reqStat.incrementJdbcExecuteCount();
}
}
@Override
public void executeAfter(String sql, long nanos, Throwable error) {
WebRequestStat reqStat = WebRequestStat.current();
if (reqStat != null) {
reqStat.addJdbcExecuteTimeNano(nanos);
if (error != null) {
reqStat.incrementJdbcExecuteErrorCount();
}
}
}
@Override
public void commit() {
WebRequestStat reqStat = WebRequestStat.current();
if (reqStat != null) {
reqStat.incrementJdbcCommitCount();
}
}
@Override
public void rollback() {
WebRequestStat reqStat = WebRequestStat.current();
if (reqStat != null) {
reqStat.incrementJdbcRollbackCount();
}
}
@Override
public void pool_connect() {
WebRequestStat reqStat = WebRequestStat.current();
if (reqStat != null) {
reqStat.incrementJdbcPoolConnectCount();
}
}
@Override
public void pool_close(long nanos) {
WebRequestStat reqStat = WebRequestStat.current();
if (reqStat != null) {
reqStat.incrementJdbcPoolCloseCount();
}
}
@Override
public void resultSet_open() {
WebRequestStat reqStat = WebRequestStat.current();
if (reqStat != null) {
reqStat.incrementJdbcResultSetOpenCount();
}
}
@Override
public void resultSet_close(long nanos) {
WebRequestStat reqStat = WebRequestStat.current();
if (reqStat != null) {
reqStat.incrementJdbcResultSetCloseCount();
}
}
}
}
@@ -27,14 +27,11 @@ import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import javax.servlet.http.HttpSession;
import com.alibaba.druid.filter.stat.StatFilterContext;
import com.alibaba.druid.filter.stat.StatFilterContextListenerAdapter;
import com.alibaba.druid.support.http.stat.WebAppStat;
import com.alibaba.druid.support.http.stat.WebAppStatManager;
import com.alibaba.druid.support.http.stat.WebRequestStat;
@@ -55,39 +52,24 @@ import com.alibaba.druid.util.ServletPathMatcher;
* @author wenshao <szujobs@htomail.com>
* @author Zhangming Qi <qizhanming@gmail.com>
*/
public class WebStatFilter implements Filter {
public class WebStatFilter extends AbstractWebStatImpl implements Filter {
private final static Log LOG = LogFactory.getLog(WebStatFilter.class);
private final static Log LOG = LogFactory.getLog(WebStatFilter.class);
public final static String PARAM_NAME_PORFILE_ENABLE = "profileEnable";
public final static String PARAM_NAME_SESSION_STAT_ENABLE = "sessionStatEnable";
public final static String PARAM_NAME_SESSION_STAT_MAX_COUNT = "sessionStatMaxCount";
public static final String PARAM_NAME_EXCLUSIONS = "exclusions";
public static final String PARAM_NAME_PRINCIPAL_SESSION_NAME = "principalSessionName";
public static final String PARAM_NAME_PRINCIPAL_COOKIE_NAME = "principalCookieName";
public static final String PARAM_NAME_REAL_IP_HEADER = "realIpHeader";
public final static String PARAM_NAME_PORFILE_ENABLE = "profileEnable";
public final static String PARAM_NAME_SESSION_STAT_ENABLE = "sessionStatEnable";
public final static String PARAM_NAME_SESSION_STAT_MAX_COUNT = "sessionStatMaxCount";
public static final String PARAM_NAME_EXCLUSIONS = "exclusions";
public static final String PARAM_NAME_PRINCIPAL_SESSION_NAME = "principalSessionName";
public static final String PARAM_NAME_PRINCIPAL_COOKIE_NAME = "principalCookieName";
public static final String PARAM_NAME_REAL_IP_HEADER = "realIpHeader";
public final static int DEFAULT_MAX_STAT_SESSION_COUNT = 1000 * 100;
private WebAppStat webAppStat = null;
private WebStatFilterContextListener statFilterContextListener = new WebStatFilterContextListener();
/**
* PatternMatcher used in determining which paths to react to for a given request.
*/
protected PatternMatcher pathMatcher = new ServletPathMatcher();
protected PatternMatcher pathMatcher = new ServletPathMatcher();
private Set<String> excludesPattern;
private boolean sessionStatEnable = true;
private int sessionStatMaxCount = DEFAULT_MAX_STAT_SESSION_COUNT;
private boolean createSession = false;
private boolean profileEnable = false;
private String contextPath;
private String principalSessionName;
private String principalCookieName;
private String realIpHeader;
private Set<String> excludesPattern;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
@@ -203,94 +185,6 @@ public class WebStatFilter implements Filter {
}
}
public WebSessionStat getSessionStat(HttpServletRequest request) {
if (!isSessionStatEnable()) {
return null;
}
WebSessionStat sessionStat = null;
String sessionId = getSessionId(request);
if (sessionId != null) {
sessionStat = webAppStat.getSessionStat(sessionId, true);
}
if (sessionStat != null) {
long currentMillis = System.currentTimeMillis();
String userAgent = request.getHeader("user-agent");
if (sessionStat.getCreateTimeMillis() == -1L) {
HttpSession session = request.getSession(false);
if (session != null) {
sessionStat.setCreateTimeMillis(session.getCreationTime());
} else {
sessionStat.setCreateTimeMillis(currentMillis);
}
webAppStat.computeUserAgent(userAgent);
webAppStat.incrementSessionCount();
}
sessionStat.setUserAgent(userAgent);
String ip = getRemoteAddress(request);
sessionStat.addRemoteAddress(ip);
}
return sessionStat;
}
protected String getRemoteAddress(HttpServletRequest request) {
String ip = null;
if (this.realIpHeader != null && this.realIpHeader.length() != 0) {
ip = request.getHeader(realIpHeader);
}
if (ip == null || ip.length() == 0) {
ip = DruidWebUtils.getRemoteAddr(request);
}
return ip;
}
public String getSessionId(HttpServletRequest httpRequest) {
String sessionId = null;
HttpSession session = httpRequest.getSession(createSession);
if (session != null) {
sessionId = session.getId();
}
return sessionId;
}
public String getPrincipal(HttpServletRequest httpRequest) {
if (principalSessionName != null) {
HttpSession session = httpRequest.getSession(createSession);
if (session == null) {
return null;
}
Object sessionValue = session.getAttribute(principalSessionName);
if (sessionValue == null) {
return null;
}
return sessionValue.toString();
}
if (principalCookieName != null && httpRequest.getCookies() != null) {
for (Cookie cookie : httpRequest.getCookies()) {
if (principalCookieName.equals(cookie.getName())) {
return cookie.getValue();
}
}
}
return null;
}
public boolean isExclusion(String requestURI) {
if (excludesPattern == null) {
return false;
@@ -312,18 +206,6 @@ public class WebStatFilter implements Filter {
return false;
}
public String getRequestURI(HttpServletRequest request) {
return request.getRequestURI();
}
public String getPrincipalSessionName() {
return principalSessionName;
}
public String getPrincipalCookieName() {
return principalCookieName;
}
@Override
public void init(FilterConfig config) throws ServletException {
{
@@ -421,22 +303,6 @@ public class WebStatFilter implements Filter {
}
}
public boolean isSessionStatEnable() {
return sessionStatEnable;
}
public void setSessionStatEnable(boolean sessionStatEnable) {
this.sessionStatEnable = sessionStatEnable;
}
public boolean isProfileEnable() {
return profileEnable;
}
public void setProfileEnable(boolean profileEnable) {
this.profileEnable = profileEnable;
}
public void setWebAppStat(WebAppStat webAppStat) {
this.webAppStat = webAppStat;
}
@@ -445,104 +311,10 @@ public class WebStatFilter implements Filter {
return webAppStat;
}
public String getContextPath() {
return contextPath;
}
public int getSessionStatMaxCount() {
return sessionStatMaxCount;
}
public WebStatFilterContextListener getStatFilterContextListener() {
return statFilterContextListener;
}
class WebStatFilterContextListener extends StatFilterContextListenerAdapter {
@Override
public void addUpdateCount(int updateCount) {
WebRequestStat reqStat = WebRequestStat.current();
if (reqStat != null) {
reqStat.addJdbcUpdateCount(updateCount);
}
}
@Override
public void addFetchRowCount(int fetchRowCount) {
WebRequestStat reqStat = WebRequestStat.current();
if (reqStat != null) {
reqStat.addJdbcFetchRowCount(fetchRowCount);
}
}
@Override
public void executeBefore(String sql, boolean inTransaction) {
WebRequestStat reqStat = WebRequestStat.current();
if (reqStat != null) {
reqStat.incrementJdbcExecuteCount();
}
}
@Override
public void executeAfter(String sql, long nanos, Throwable error) {
WebRequestStat reqStat = WebRequestStat.current();
if (reqStat != null) {
reqStat.addJdbcExecuteTimeNano(nanos);
if (error != null) {
reqStat.incrementJdbcExecuteErrorCount();
}
}
}
@Override
public void commit() {
WebRequestStat reqStat = WebRequestStat.current();
if (reqStat != null) {
reqStat.incrementJdbcCommitCount();
}
}
@Override
public void rollback() {
WebRequestStat reqStat = WebRequestStat.current();
if (reqStat != null) {
reqStat.incrementJdbcRollbackCount();
}
}
@Override
public void pool_connect() {
WebRequestStat reqStat = WebRequestStat.current();
if (reqStat != null) {
reqStat.incrementJdbcPoolConnectCount();
}
}
@Override
public void pool_close(long nanos) {
WebRequestStat reqStat = WebRequestStat.current();
if (reqStat != null) {
reqStat.incrementJdbcPoolCloseCount();
}
}
@Override
public void resultSet_open() {
WebRequestStat reqStat = WebRequestStat.current();
if (reqStat != null) {
reqStat.incrementJdbcResultSetOpenCount();
}
}
@Override
public void resultSet_close(long nanos) {
WebRequestStat reqStat = WebRequestStat.current();
if (reqStat != null) {
reqStat.incrementJdbcResultSetCloseCount();
}
}
}
public final static class StatHttpServletResponseWrapper extends HttpServletResponseWrapper implements HttpServletResponse {
private int status;
@@ -23,6 +23,7 @@ import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import com.alibaba.druid.stat.DruidDataSourceStatManager;
import com.alibaba.druid.util.StringUtils;
public class WebAppStatManager {
@@ -35,6 +36,22 @@ public class WebAppStatManager {
public static WebAppStatManager getInstance() {
return instance;
}
public synchronized WebAppStat getWebAppStat(String contextPath) {
Set<Object> stats = getWebAppStatSet();
for (Object item : stats) {
if (item instanceof WebAppStat) {
WebAppStat stat = (WebAppStat) item;
if (StringUtils.equals(stat.getContextPath(), contextPath)) {
return stat;
}
}
}
WebAppStat stat = new WebAppStat(contextPath);
this.addWebAppStatSet(stat);
return stat;
}
public Set<Object> getWebAppStatSet() {
if (webAppStatSet == null) {
@@ -0,0 +1,162 @@
/*
* 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.support.spring.mvc;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.druid.filter.stat.StatFilterContext;
import com.alibaba.druid.support.http.AbstractWebStatImpl;
import com.alibaba.druid.support.http.stat.WebAppStat;
import com.alibaba.druid.support.http.stat.WebAppStatManager;
import com.alibaba.druid.support.http.stat.WebRequestStat;
import com.alibaba.druid.support.http.stat.WebSessionStat;
import com.alibaba.druid.support.http.stat.WebURIStat;
import com.alibaba.druid.support.profile.ProfileEntryKey;
import com.alibaba.druid.support.profile.ProfileEntryReqStat;
import com.alibaba.druid.support.profile.Profiler;
import com.alibaba.druid.util.DruidWebUtils;
public class StatHandlerInterceptor extends AbstractWebStatImpl implements HandlerInterceptor, InitializingBean, DisposableBean {
public StatHandlerInterceptor(){
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
final WebAppStat webAppStat = getWebAppStat(request);
String requestURI = getRequestURI(request);
long startNano = System.nanoTime();
long startMillis = System.currentTimeMillis();
WebRequestStat requestStat = new WebRequestStat(startNano, startMillis);
WebRequestStat.set(requestStat);
WebSessionStat sessionStat = getSessionStat(request);
webAppStat.beforeInvoke();
WebURIStat uriStat = webAppStat.getURIStat(requestURI, false);
if (uriStat == null) {
int index = requestURI.indexOf(";jsessionid=");
if (index != -1) {
requestURI = requestURI.substring(0, index);
uriStat = webAppStat.getURIStat(requestURI, false);
}
}
if (isProfileEnable()) {
Profiler.initLocal();
Profiler.enter(requestURI, Profiler.PROFILE_TYPE_WEB);
}
// 第一次访问时,uriStat这里为null,是为了防止404攻击。
if (uriStat != null) {
uriStat.beforeInvoke();
}
// 第一次访问时,sessionId为null,如果缺省sessionCreate=false,sessionStat就为null。
if (sessionStat != null) {
sessionStat.beforeInvoke();
}
return true;
}
public WebAppStat getWebAppStat(HttpServletRequest request) {
if (webAppStat != null) {
return webAppStat;
}
ServletContext context = request.getSession().getServletContext();
String contextPath = DruidWebUtils.getContextPath(context);
webAppStat = WebAppStatManager.getInstance().getWebAppStat(contextPath);
return webAppStat;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception error) throws Exception {
WebRequestStat requestStat = WebRequestStat.current();
long endNano = System.nanoTime();
requestStat.setEndNano(endNano);
WebSessionStat sessionStat = getSessionStat(request);
WebURIStat uriStat = WebURIStat.current();
long nanos = endNano - requestStat.getStartNano();
webAppStat.afterInvoke(null, nanos);
if (sessionStat == null) {
sessionStat = getSessionStat(request);
if (sessionStat != null) {
sessionStat.beforeInvoke(); // 补偿
}
}
if (sessionStat != null) {
sessionStat.afterInvoke(error, nanos);
sessionStat.setPrincipal(getPrincipal(request));
}
if (uriStat != null) {
uriStat.afterInvoke(error, nanos);
}
WebRequestStat.set(null);
if (isProfileEnable()) {
Profiler.release(nanos);
Map<ProfileEntryKey, ProfileEntryReqStat> requestStatsMap = Profiler.getStatsMap();
if (uriStat != null) {
uriStat.getProfiletat().record(requestStatsMap);
}
Profiler.removeLocal();
}
}
public String getRequestURI(HttpServletRequest request) {
return (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
}
@Override
public void afterPropertiesSet() throws Exception {
StatFilterContext.getInstance().addContextListener(statFilterContextListener);
}
@Override
public void destroy() throws Exception {
StatFilterContext.getInstance().removeContextListener(statFilterContextListener);
}
}
@@ -34,7 +34,7 @@ public class DruidWebUtils {
return ip;
}
private static String getContextPath_2_5(ServletContext context) {
String contextPath = context.getContextPath();
@@ -56,4 +56,5 @@ public class DruidWebUtils {
return null;
}
}
}