Author: markt Date: Tue Aug 18 11:49:26 2009 New Revision: 805375 URL: http://svn.apache.org/viewvc?rev=805375&view=rev Log: Replace the RequestDumperValve with a RequestDumperFilter. Merge the RequestDumperFilter from the examples with this new filter. Adds: - thread name to start of output line to make analysing output easier - request timings GSOC 2009 Based on a patch by Xie Xiaodong
Added: tomcat/trunk/java/org/apache/catalina/filters/RequestDumperFilter.java (with props) Removed: tomcat/trunk/java/org/apache/catalina/valves/RequestDumperValve.java tomcat/trunk/webapps/examples/WEB-INF/classes/filters/RequestDumperFilter.java Modified: tomcat/trunk/conf/server.xml tomcat/trunk/java/org/apache/catalina/mbeans/MBeanFactory.java tomcat/trunk/webapps/docs/config/filter.xml tomcat/trunk/webapps/docs/config/valve.xml tomcat/trunk/webapps/examples/WEB-INF/web.xml Modified: tomcat/trunk/conf/server.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/conf/server.xml?rev=805375&r1=805374&r2=805375&view=diff ============================================================================== --- tomcat/trunk/conf/server.xml (original) +++ tomcat/trunk/conf/server.xml Tue Aug 18 11:49:26 2009 @@ -106,13 +106,6 @@ <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/> --> - <!-- The request dumper valve dumps useful debugging information about - the request and response data received and sent by Tomcat. - Documentation at: /docs/config/valve.html --> - <!-- - <Valve className="org.apache.catalina.valves.RequestDumperValve"/> - --> - <!-- This Realm uses the UserDatabase configured in the global JNDI resources under the key "UserDatabase". Any edits that are performed against this UserDatabase are immediately Added: tomcat/trunk/java/org/apache/catalina/filters/RequestDumperFilter.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/filters/RequestDumperFilter.java?rev=805375&view=auto ============================================================================== --- tomcat/trunk/java/org/apache/catalina/filters/RequestDumperFilter.java (added) +++ tomcat/trunk/java/org/apache/catalina/filters/RequestDumperFilter.java Tue Aug 18 11:49:26 2009 @@ -0,0 +1,283 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.catalina.filters; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Enumeration; + +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.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; + + +/** + * <p>Implementation of a Filter that logs interesting contents from the + * specified Request (before processing) and the corresponding Response + * (after processing). It is especially useful in debugging problems + * related to headers and cookies.</p> + * + * <p>When using this Filter, it is strongly recommended that the + * <code>org.apache.catalina.filter.RequestDumperFilter</code> logger is + * directed to a dedicated file and that the + * <code>org.apache.juli.VerbatimFormmater</code> is used.</p> + * + * @author Craig R. McClanahan + */ + +public class RequestDumperFilter implements Filter { + + private static final String NON_HTTP_REQ_MSG = + "Not available. Non-http request."; + private static final String NON_HTTP_RES_MSG = + "Not available. Non-http response."; + + private static final ThreadLocal<Timestamp> timestamp = + new ThreadLocal<Timestamp>() { + protected Timestamp initialValue() { + return new Timestamp(); + } + }; + + /** + * The logger for this class. + */ + private static Log log = LogFactory.getLog(RequestDumperFilter.class); + + + /** + * Log the interesting request parameters, invoke the next Filter in the + * sequence, and log the interesting response parameters. + * + * @param request The servlet request to be processed + * @param response The servlet response to be created + * @param chain The filter chain being processed + * + * @exception IOException if an input/output error occurs + * @exception ServletException if a servlet error occurs + */ + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) + throws IOException, ServletException { + + HttpServletRequest hRequest = null; + HttpServletResponse hResponse = null; + + if (request instanceof HttpServletRequest) { + hRequest = (HttpServletRequest) request; + } + if (response instanceof HttpServletResponse) { + hResponse = (HttpServletResponse) response; + } + + // Log pre-service information + doLog("START TIME ", getTimestamp()); + + if (hRequest == null) { + doLog(" requestURI", NON_HTTP_REQ_MSG); + doLog(" authType", NON_HTTP_REQ_MSG); + } else { + doLog(" requestURI", hRequest.getRequestURI()); + doLog(" authType", hRequest.getAuthType()); + } + + doLog(" characterEncoding", request.getCharacterEncoding()); + doLog(" contentLength", + Integer.valueOf(request.getContentLength()).toString()); + doLog(" contentType", request.getContentType()); + + if (hRequest == null) { + doLog(" contextPath", NON_HTTP_REQ_MSG); + doLog(" cookie", NON_HTTP_REQ_MSG); + doLog(" header", NON_HTTP_REQ_MSG); + } else { + doLog(" contextPath", hRequest.getContextPath()); + Cookie cookies[] = hRequest.getCookies(); + if (cookies != null) { + for (int i = 0; i < cookies.length; i++) + doLog(" cookie", cookies[i].getName() + + "=" + cookies[i].getValue()); + } + Enumeration<String> hnames = hRequest.getHeaderNames(); + while (hnames.hasMoreElements()) { + String hname = hnames.nextElement(); + Enumeration<String> hvalues = hRequest.getHeaders(hname); + while (hvalues.hasMoreElements()) { + String hvalue = hvalues.nextElement(); + doLog(" header", hname + "=" + hvalue); + } + } + } + + doLog(" locale", request.getLocale().toString()); + + if (hRequest == null) { + doLog(" method", NON_HTTP_REQ_MSG); + } else { + doLog(" method", hRequest.getMethod()); + } + + Enumeration<String> pnames = request.getParameterNames(); + while (pnames.hasMoreElements()) { + String pname = pnames.nextElement(); + String pvalues[] = request.getParameterValues(pname); + StringBuffer result = new StringBuffer(pname); + result.append('='); + for (int i = 0; i < pvalues.length; i++) { + if (i > 0) + result.append(", "); + result.append(pvalues[i]); + } + doLog(" parameter", result.toString()); + } + + if (hRequest == null) { + doLog(" pathInfo", NON_HTTP_REQ_MSG); + } else { + doLog(" pathInfo", hRequest.getPathInfo()); + } + + doLog(" protocol", request.getProtocol()); + + if (hRequest == null) { + doLog(" queryString", NON_HTTP_REQ_MSG); + } else { + doLog(" queryString", hRequest.getQueryString()); + } + + doLog(" remoteAddr", request.getRemoteAddr()); + doLog(" remoteHost", request.getRemoteHost()); + + if (hRequest == null) { + doLog(" remoteUser", NON_HTTP_REQ_MSG); + doLog("requestedSessionId", NON_HTTP_REQ_MSG); + } else { + doLog(" remoteUser", hRequest.getRemoteUser()); + doLog("requestedSessionId", hRequest.getRequestedSessionId()); + } + + doLog(" scheme", request.getScheme()); + doLog(" serverName", request.getServerName()); + doLog(" serverPort", + Integer.valueOf(request.getServerPort()).toString()); + + if (hRequest == null) { + doLog(" servletPath", NON_HTTP_REQ_MSG); + } else { + doLog(" servletPath", hRequest.getServletPath()); + } + + doLog(" isSecure", + Boolean.valueOf(request.isSecure()).toString()); + doLog("------------------", + "--------------------------------------------"); + + // Perform the request + chain.doFilter(request, response); + + // Log post-service information + doLog("------------------", + "--------------------------------------------"); + if (hRequest == null) { + doLog(" authType", NON_HTTP_REQ_MSG); + } else { + doLog(" authType", hRequest.getAuthType()); + } + + doLog(" contentType", response.getContentType()); + + if (hResponse == null) { + doLog(" header", NON_HTTP_RES_MSG); + } else { + Iterable<String> rhnames = hResponse.getHeaderNames(); + for (String rhname : rhnames) { + Iterable<String> rhvalues = hResponse.getHeaders(rhname); + for (String rhvalue : rhvalues) + doLog(" header", rhname + "=" + rhvalue); + } + } + + if (hRequest == null) { + doLog(" remoteUser", NON_HTTP_REQ_MSG); + } else { + doLog(" remoteUser", hRequest.getRemoteUser()); + } + + if (hResponse == null) { + doLog(" remoteUser", NON_HTTP_RES_MSG); + } else { + doLog(" status", + Integer.valueOf(hResponse.getStatus()).toString()); + } + + doLog("END TIME ", getTimestamp()); + doLog("==================", + "============================================"); + } + + private void doLog(String attribute, String value) { + StringBuilder sb = new StringBuilder(80); + sb.append(Thread.currentThread().getName()); + sb.append(' '); + sb.append(attribute); + sb.append('='); + sb.append(value); + log.info(sb.toString()); + } + + private String getTimestamp() { + Timestamp ts = timestamp.get(); + long currentTime = System.currentTimeMillis(); + + if ((ts.date.getTime() + 999) < currentTime) { + ts.date.setTime(currentTime - (currentTime % 1000)); + ts.update(); + } + return ts.dateString; + } + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + // NOOP + } + + @Override + public void destroy() { + // NOOP + } + + private static final class Timestamp { + private Date date = new Date(0); + private SimpleDateFormat format = + new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss"); + private String dateString = format.format(date); + private void update() { + dateString = format.format(date); + } + } +} Propchange: tomcat/trunk/java/org/apache/catalina/filters/RequestDumperFilter.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: tomcat/trunk/java/org/apache/catalina/filters/RequestDumperFilter.java ------------------------------------------------------------------------------ svn:keywords = Date Author Id Revision Modified: tomcat/trunk/java/org/apache/catalina/mbeans/MBeanFactory.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/mbeans/MBeanFactory.java?rev=805375&r1=805374&r2=805375&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/mbeans/MBeanFactory.java (original) +++ tomcat/trunk/java/org/apache/catalina/mbeans/MBeanFactory.java Tue Aug 18 11:49:26 2009 @@ -49,7 +49,6 @@ import org.apache.catalina.valves.AccessLogValve; import org.apache.catalina.valves.RemoteAddrValve; import org.apache.catalina.valves.RemoteHostValve; -import org.apache.catalina.valves.RequestDumperValve; import org.apache.catalina.valves.ValveBase; import org.apache.tomcat.util.modeler.BaseModelMBean; @@ -513,29 +512,6 @@ /** - * Create a new Request Dumper Valve. - * - * @param parent MBean Name of the associated parent component - * - * @exception Exception if an MBean cannot be created or registered - */ - public String createRequestDumperValve(String parent) - throws Exception { - - // Create a new RequestDumperValve instance - RequestDumperValve valve = new RequestDumperValve(); - - // Add the new instance to its parent component - ObjectName pname = new ObjectName(parent); - ContainerBase containerBase = getParentContainerFromParent(pname); - containerBase.addValve(valve); - ObjectName oname = valve.getObjectName(); - return (oname.toString()); - - } - - - /** * Create a new Single Sign On Valve. * * @param parent MBean Name of the associated parent component Modified: tomcat/trunk/webapps/docs/config/filter.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/filter.xml?rev=805375&r1=805374&r2=805375&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/config/filter.xml (original) +++ tomcat/trunk/webapps/docs/config/filter.xml Tue Aug 18 11:49:26 2009 @@ -194,6 +194,75 @@ </section> +<section name="Request Dumper Filter"> + + <subsection name="Introduction"> + + <p>The Request Dumper Filter logs information from the request and response + objects and is intended to be used for debugging purposes. When using this + Filter, it is recommended that the + <code>org.apache.catalina.filter.RequestDumperFilter</code> logger is + directed to a dedicated file and that the + <code>org.apache.juli.VerbatimFormmater</code> is used.</p> + + <p><strong>WARNING: Using this filter has side-effects.</strong> The + output from this filter includes any parameters included with the request. + The parameters will be decoded using the default platform encoding. Any + subsequent calls to <code>request.setCharacterEncoding()</code> within + the web application will have no effect.</p> + + </subsection> + + <subsection name="Filter Class Name"> + + <p>The filter class name for the Request Dumper Filter is + <strong><code>org.apache.catalina.filters.RequestDumperFilter</code> + </strong>.</p> + + </subsection> + + <subsection name="Initialisation parameters"> + + <p>The Request Dumper Filter does not support any initialization + parameters.</p> + + </subsection> + + <subsection name="Sample Configuration"> + + <p>The following entries in a web application's web.xml would enable the + Request Dumper filter for all requests for that web application. If the + entries were added to <code>CATALINA_BASE/conf/web.xml</code>, the Request + Dumper Filter would be enabled for all web applications.</p> + <source> +<filter> + <filter-name>requestdumper</filter-name> + <filter-class> + org.apache.catalina.filters.RequestDumperFilter + </filter-class> +</filter> +<filter-mapping> + <filter-name>requestdumper</filter-name> + <url-pattern>*</url-pattern> +</filter-mapping> + </source> + + <p>The following entries in CATALINA_BASE/conf/logging.properties would + create a separate log file for the Request Dumper Filter output.</p> + <source> +# To this configuration below, 1request-dumper.org.apache.juli.FileHandler +# also needs to be added to the handlers property near the top of the file +1request-dumper.org.apache.juli.FileHandler.level = INFO +1request-dumper.org.apache.juli.FileHandler.directory = ${catalina.base}/logs +1request-dumper.org.apache.juli.FileHandler.prefix = request-dumper. +1request-dumper.org.apache.juli.FileHandler.formatter = org.apache.juli.VerbatimFormatter +org.apache.catalina.filters.RequestDumperFilter.level = INFO +org.apache.catalina.filters.RequestDumperFilter.handlers = 1request-dumper.org.apache.juli.FileHandler + </source> + </subsection> +</section> + + <section name="WebDAV Fix Filter"> <subsection name="Introduction"> Modified: tomcat/trunk/webapps/docs/config/valve.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/valve.xml?rev=805375&r1=805374&r2=805375&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/config/valve.xml (original) +++ tomcat/trunk/webapps/docs/config/valve.xml Tue Aug 18 11:49:26 2009 @@ -320,47 +320,6 @@ </section> -<section name="Request Dumper Valve"> - - - <subsection name="Introduction"> - - <p>The <em>Request Dumper Valve</em> is a useful tool in debugging - interactions with a client application (or browser) that is sending - HTTP requests to your Tomcat-based server. When configured, it causes - details about each request processed by its associated <code>Engine</code>, - <code>Host</code>, or <code>Context</code> to be logged according to - the logging configuration for that container.</p> - - <p><strong>WARNING: Using this valve has side-effects.</strong> The - output from this valve includes any parameters included with the request. - The parameters will be decoded using the default platform encoding. Any - subsequent calls to <code>request.setCharacterEncoding()</code> within - the web application will have no effect.</p> - - </subsection> - - - <subsection name="Attributes"> - - <p>The <strong>Request Dumper Valve</strong> supports the following - configuration attributes:</p> - - <attributes> - - <attribute name="className" required="true"> - <p>Java class name of the implementation to use. This MUST be set to - <strong>org.apache.catalina.valves.RequestDumperValve</strong>.</p> - </attribute> - - </attributes> - - </subsection> - - -</section> - - <section name="Single Sign On Valve"> <subsection name="Introduction"> Modified: tomcat/trunk/webapps/examples/WEB-INF/web.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/examples/WEB-INF/web.xml?rev=805375&r1=805374&r2=805375&view=diff ============================================================================== --- tomcat/trunk/webapps/examples/WEB-INF/web.xml (original) +++ tomcat/trunk/webapps/examples/WEB-INF/web.xml Tue Aug 18 11:49:26 2009 @@ -39,7 +39,7 @@ <filter> <filter-name>Request Dumper Filter</filter-name> - <filter-class>filters.RequestDumperFilter</filter-class> + <filter-class>org.apache.catalina.filters.RequestDumperFilter</filter-class> </filter> <!-- Example filter to set character encoding on each request --> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org