/** * Analytica - beta version - Systems Monitoring Tool * * Copyright (C) 2013, KleeGroup, direction.technique@kleegroup.com (http://www.kleegroup.com) * KleeGroup, Centre d'affaire la Boursidi�re - BP 159 - 92357 Le Plessis Robinson Cedex - France * * This program is free software; you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Foundation; * either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; * if not, see <http://www.gnu.org/licenses> */ package io.analytica.spies.impl.httprequest; import io.analytica.agent.AgentManager; import io.vertigo.core.Home; import io.vertigo.lang.VUserException; import java.io.IOException; import java.lang.management.ManagementFactory; import java.lang.management.ThreadMXBean; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; /** * Filtre de servlet (javax.servlet.Filter). * Il permet d'instrumenter les pages avec appels, dur�es, taille des flux d'entr�e et de sortie. * * @author pchretien, npiedeloup * @version $Id: UIFilter.java,v 1.7 2010/11/16 10:36:54 pchretien Exp $ */ public final class HttpRequestSpyFilter implements Filter { private static final String PT_REQUEST = "REQUEST"; private static final String ME_RESPONSE_LENGTH = "RESPONSE_LENGTH"; private static final String ME_USER_ERROR_PCT = "USER_ERROR_PCT"; private static final String ME_OTHER_ERROR_PCT = "OTHER_ERROR_PCT"; private static final String ME_CPU_TIME = "CPU_TIME"; /** * M�canisme de log racine */ private final Logger generalLog = Logger.getRootLogger(); /** {@inheritDoc} */ @Override public void init(final FilterConfig filterConfig) { //rien } /** {@inheritDoc} */ @Override public void destroy() { // Rien de sp�cial } /** * La m�thode doFilter est appel�e par le container chaque fois qu'une paire * requ�te/r�ponse passe � travers la cha�ne suite � une requ�te d'un client * pour une ressource au bout de la cha�ne. L'instance de FilterChain pass�e * dans cette m�thode permet au filtre de passer la requ�te et la r�ponse � * l'entit� suivante dans la cha�ne. * * Cette impl�mentation encapsule les flux d'entr�e et de sortie (compress�s * ou non) pour compter les octets lus ou �crits. Puis elle logue le * r�sultat avec le nom du filtre (pour distinguer si c'est avant ou apr�s * compression). Le r�sultat est �galement enregistr� dans les statistiques. * * @param request javax.servlet.ServletRequest * @param response javax.servlet.ServletResponse * @param chain javax.servlet.FilterChain * @throws java.io.IOException Si une erreur d'entr�e/sortie survient * @throws javax.servlet.ServletException Si une erreur de servlet survient */ @Override public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException { if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) { filter((HttpServletRequest) request, (HttpServletResponse) response, chain); } else { chain.doFilter(request, response); } } private void filter(final HttpServletRequest httpRequest, final HttpServletResponse httpResponse, final FilterChain chain) throws IOException, ServletException { final AgentManager agentManager = getAgentManager(); final long begin = System.currentTimeMillis(); boolean ok = false; //D�marrage de la requ�te. final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); final long startCpuTime = threadBean.getThreadCpuTime(Thread.currentThread().getId()); final String processName = getProcessName(httpRequest); agentManager.startProcess(PT_REQUEST, processName); logRequestStart(httpRequest); final HttpRequestSpyServletResponseWrapper wrappedResponse = new HttpRequestSpyServletResponseWrapper(httpResponse); try { chain.doFilter(httpRequest, httpResponse); agentManager.setMeasure(ME_RESPONSE_LENGTH, wrappedResponse.getDataLength()); ok = true; } catch (final ServletException servletException) { //Permet de compter les erreurs par type. if (isUserException(servletException)) { agentManager.setMeasure(ME_USER_ERROR_PCT, 100); } else { agentManager.setMeasure(ME_OTHER_ERROR_PCT, 100); } logRequestException(servletException); throw servletException; } finally { final long endCpuTime = threadBean.getThreadCpuTime(Thread.currentThread().getId()); if (startCpuTime != -1 && endCpuTime != -1) { //On compte le temps CPU de ce thread agentManager.setMeasure(ME_CPU_TIME, endCpuTime / 1000000 - startCpuTime / 1000000); } agentManager.stopProcess(); logRequestFinish(httpRequest, System.currentTimeMillis() - begin, ok); } } //------------------------------------------------------------------------- private static boolean isUserException(final Throwable throwable) { Throwable e = throwable; while (e != null) { if (e instanceof VUserException) { return true; } Throwable t = e.getCause(); if (t == null && e instanceof ServletException) { t = ((ServletException) e).getRootCause(); } if (t == null && e instanceof java.sql.SQLException) { t = ((java.sql.SQLException) e).getNextException(); } e = t; } return false; } /** * Retourne le nom du type de la requ�te. * * @param request Requ�te HTTP * @return Nom du dossier dans lequel sont class�s les r�sultats */ private String getProcessName(final HttpServletRequest request) { final StringBuilder requestName = new StringBuilder(); final String pathInfo = request.getPathInfo(); if (pathInfo != null) { requestName.append(pathInfo); } else { final String servletPath = request.getServletPath(); requestName.append(servletPath); } requestName.append(" (").append(request.getMethod()).append(")"); return requestName.toString(); } private static AgentManager getAgentManager() { //Le Filter �tant un filtre de servlet, il ne supporte pas l'injection de d�pendance via le contructeur return Home.getComponentSpace().resolve(AgentManager.class); } //------------------------------------------------------------------------- private void logRequestStart(final HttpServletRequest request) { if (generalLog.isTraceEnabled()) { generalLog.trace("[Start] " + getRequestNameFull(request)); } } private void logRequestFinish(final HttpServletRequest httpRequest, final long requestTime, final boolean success) { final String requestName = getProcessName(httpRequest); if (success) { if (generalLog.isTraceEnabled()) { generalLog.trace("[Finish] " + requestName + " (" + getRequestNameFull(httpRequest) + ") reussie en ( " + requestTime + ") ms"); } else if (generalLog.isInfoEnabled()) { generalLog.info("[Finish] " + requestName + " reussie en ( " + requestTime + ") ms"); } } else { //Echec if (requestName == null) { generalLog.warn("[Finish] -NoRequestName- interrompue apr�s ( " + requestTime + ") ms"); } else { generalLog.warn("[Finish] " + requestName + " interrompue apr�s ( " + requestTime + ") ms"); } } } private void logRequestException(final Exception exception) { // e.toString()) affiche la classe du message et le message lui m�me. // Une KUser non proprement g�r� est volontairement logg�e en erreur. generalLog.error(exception.toString(), exception); } private static String getRequestNameFull(final HttpServletRequest request) { final StringBuilder requestName = new StringBuilder(request.getMethod()); requestName.append(" from "); requestName.append(request.getRemoteAddr()); requestName.append(" ("); requestName.append(request.getRequestURL().toString()); final String queryString = request.getQueryString(); if (queryString != null) { requestName.append('?'); requestName.append(request.getQueryString()); } requestName.append(')'); return requestName.toString(); } }