blob: e92d97dd57583d7b102d24f730f1e39635f77b17 [file] [log] [blame]
/*
* 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.VerbatimFormatter</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>() {
@Override
protected Timestamp initialValue() {
return new Timestamp();
}
};
// Log must be non-static as loggers are created per class-loader and this
// Filter may be used in multiple class loaders
private final Log log = LogFactory.getLog(RequestDumperFilter.class); // must not be static
/**
* 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
*/
@Override
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",
Long.toString(request.getContentLengthLong()));
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);
StringBuilder result = new StringBuilder(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.toString(request.getServerPort()));
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(" status", NON_HTTP_RES_MSG);
} else {
doLog(" status",
Integer.toString(hResponse.getStatus()));
}
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 final Date date = new Date(0);
private final SimpleDateFormat format =
new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
private String dateString = format.format(date);
private void update() {
dateString = format.format(date);
}
}
}