872 lines
32 KiB
Java
872 lines
32 KiB
Java
/*
|
|
* 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.valves;
|
|
|
|
import java.io.CharArrayWriter;
|
|
import java.io.IOException;
|
|
import java.io.StringReader;
|
|
import java.io.UnsupportedEncodingException;
|
|
import java.net.InetAddress;
|
|
import java.net.URLEncoder;
|
|
import java.text.SimpleDateFormat;
|
|
import java.util.ArrayList;
|
|
import java.util.Date;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.TimeZone;
|
|
|
|
import javax.servlet.http.Cookie;
|
|
import javax.servlet.http.HttpSession;
|
|
|
|
import org.apache.catalina.connector.Request;
|
|
import org.apache.catalina.connector.Response;
|
|
import org.apache.catalina.util.ServerInfo;
|
|
import org.apache.juli.logging.Log;
|
|
import org.apache.juli.logging.LogFactory;
|
|
import org.apache.tomcat.util.ExceptionUtils;
|
|
|
|
/**
|
|
* An implementation of the W3c Extended Log File Format. See
|
|
* http://www.w3.org/TR/WD-logfile.html for more information about the format.
|
|
*
|
|
* The following fields are supported:
|
|
* <ul>
|
|
* <li><code>c-dns</code>: Client hostname (or ip address if
|
|
* <code>enableLookups</code> for the connector is false)</li>
|
|
* <li><code>c-ip</code>: Client ip address</li>
|
|
* <li><code>bytes</code>: bytes served</li>
|
|
* <li><code>cs-method</code>: request method</li>
|
|
* <li><code>cs-uri</code>: The full uri requested</li>
|
|
* <li><code>cs-uri-query</code>: The query string</li>
|
|
* <li><code>cs-uri-stem</code>: The uri without query string</li>
|
|
* <li><code>date</code>: The date in yyyy-mm-dd format for GMT</li>
|
|
* <li><code>s-dns</code>: The server dns entry </li>
|
|
* <li><code>s-ip</code>: The server ip address</li>
|
|
* <li><code>cs(XXX)</code>: The value of header XXX from client to server</li>
|
|
* <li><code>sc(XXX)</code>: The value of header XXX from server to client </li>
|
|
* <li><code>sc-status</code>: The status code</li>
|
|
* <li><code>time</code>: Time the request was served</li>
|
|
* <li><code>time-taken</code>: Time (in seconds) taken to serve the request</li>
|
|
* <li><code>x-threadname</code>: Current request thread name (can compare later with stacktraces)</li>
|
|
* <li><code>x-A(XXX)</code>: Pull XXX attribute from the servlet context </li>
|
|
* <li><code>x-C(XXX)</code>: Pull the first cookie of the name XXX </li>
|
|
* <li><code>x-O(XXX)</code>: Pull the all response header values XXX </li>
|
|
* <li><code>x-R(XXX)</code>: Pull XXX attribute from the servlet request </li>
|
|
* <li><code>x-S(XXX)</code>: Pull XXX attribute from the session </li>
|
|
* <li><code>x-P(...)</code>: Call request.getParameter(...)
|
|
* and URLencode it. Helpful to capture
|
|
* certain POST parameters.
|
|
* </li>
|
|
* <li>For any of the x-H(...) the following method will be called from the
|
|
* HttpServletRequest object </li>
|
|
* <li><code>x-H(authType)</code>: getAuthType </li>
|
|
* <li><code>x-H(characterEncoding)</code>: getCharacterEncoding </li>
|
|
* <li><code>x-H(contentLength)</code>: getContentLength </li>
|
|
* <li><code>x-H(locale)</code>: getLocale</li>
|
|
* <li><code>x-H(protocol)</code>: getProtocol </li>
|
|
* <li><code>x-H(remoteUser)</code>: getRemoteUser</li>
|
|
* <li><code>x-H(requestedSessionId)</code>: getRequestedSessionId</li>
|
|
* <li><code>x-H(requestedSessionIdFromCookie)</code>:
|
|
* isRequestedSessionIdFromCookie </li>
|
|
* <li><code>x-H(requestedSessionIdValid)</code>:
|
|
* isRequestedSessionIdValid</li>
|
|
* <li><code>x-H(scheme)</code>: getScheme</li>
|
|
* <li><code>x-H(secure)</code>: isSecure</li>
|
|
* </ul>
|
|
*
|
|
*
|
|
*
|
|
* <p>
|
|
* Log rotation can be on or off. This is dictated by the
|
|
* <code>rotatable</code> property.
|
|
* </p>
|
|
*
|
|
* <p>
|
|
* For UNIX users, another field called <code>checkExists</code> is also
|
|
* available. If set to true, the log file's existence will be checked before
|
|
* each logging. This way an external log rotator can move the file
|
|
* somewhere and Tomcat will start with a new file.
|
|
* </p>
|
|
*
|
|
* <p>
|
|
* For JMX junkies, a public method called <code>rotate</code> has
|
|
* been made available to allow you to tell this instance to move
|
|
* the existing log file to somewhere else and start writing a new log file.
|
|
* </p>
|
|
*
|
|
* <p>
|
|
* Conditional logging is also supported. This can be done with the
|
|
* <code>condition</code> property.
|
|
* If the value returned from ServletRequest.getAttribute(condition)
|
|
* yields a non-null value, the logging will be skipped.
|
|
* </p>
|
|
*
|
|
* <p>
|
|
* For extended attributes coming from a getAttribute() call,
|
|
* it is you responsibility to ensure there are no newline or
|
|
* control characters.
|
|
* </p>
|
|
*
|
|
* @author Peter Rossbach
|
|
*/
|
|
public class ExtendedAccessLogValve extends AccessLogValve {
|
|
|
|
private static final Log log = LogFactory.getLog(ExtendedAccessLogValve.class);
|
|
|
|
// ----------------------------------------------------- Instance Variables
|
|
|
|
|
|
/**
|
|
* The descriptive information about this implementation.
|
|
*/
|
|
protected static final String extendedAccessLogInfo =
|
|
"org.apache.catalina.valves.ExtendedAccessLogValve/2.1";
|
|
|
|
|
|
// -------------------------------------------------------- Private Methods
|
|
|
|
/**
|
|
* Wrap the incoming value with double quotes (") and escape any double
|
|
* quotes appearing in the value using two double quotes ("").
|
|
*
|
|
* @param value - The value to wrap
|
|
* @return '-' if null. Otherwise, toString() will
|
|
* be called on the object and the value will be wrapped
|
|
* in quotes and any quotes will be escaped with 2
|
|
* sets of quotes.
|
|
*/
|
|
static String wrap(Object value) {
|
|
String svalue;
|
|
// Does the value contain a " ? If so must encode it
|
|
if (value == null || "-".equals(value)) {
|
|
return "-";
|
|
}
|
|
|
|
try {
|
|
svalue = value.toString();
|
|
} catch (Throwable e) {
|
|
ExceptionUtils.handleThrowable(e);
|
|
/* Log error */
|
|
return "-";
|
|
}
|
|
|
|
/* Wrap all values in double quotes. */
|
|
StringBuilder buffer = new StringBuilder(svalue.length() + 2);
|
|
buffer.append('\"');
|
|
int i = 0;
|
|
while (i < svalue.length()) {
|
|
int j = svalue.indexOf('\"', i);
|
|
if (j == -1) {
|
|
buffer.append(svalue.substring(i));
|
|
i = svalue.length();
|
|
} else {
|
|
buffer.append(svalue.substring(i, j + 1));
|
|
buffer.append('"');
|
|
i = j + 1;
|
|
}
|
|
}
|
|
|
|
buffer.append('\"');
|
|
return buffer.toString();
|
|
}
|
|
|
|
/**
|
|
* Open the new log file for the date specified by <code>dateStamp</code>.
|
|
*/
|
|
@Override
|
|
protected synchronized void open() {
|
|
super.open();
|
|
if (currentLogFile.length()==0) {
|
|
writer.println("#Fields: " + pattern);
|
|
writer.println("#Version: 2.0");
|
|
writer.println("#Software: " + ServerInfo.getServerInfo());
|
|
}
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------ Lifecycle Methods
|
|
|
|
|
|
protected static class DateElement implements AccessLogElement {
|
|
// Milli-seconds in 24 hours
|
|
private static final long INTERVAL = (1000 * 60 * 60 * 24);
|
|
|
|
private static final ThreadLocal<ElementTimestampStruct> currentDate =
|
|
new ThreadLocal<ElementTimestampStruct>() {
|
|
@Override
|
|
protected ElementTimestampStruct initialValue() {
|
|
return new ElementTimestampStruct("yyyy-MM-dd");
|
|
}
|
|
};
|
|
|
|
@Override
|
|
public void addElement(CharArrayWriter buf, Date date, Request request,
|
|
Response response, long time) {
|
|
ElementTimestampStruct eds = currentDate.get();
|
|
long millis = eds.currentTimestamp.getTime();
|
|
if (date.getTime() > (millis + INTERVAL -1) ||
|
|
date.getTime() < millis) {
|
|
eds.currentTimestamp.setTime(
|
|
date.getTime() - (date.getTime() % INTERVAL));
|
|
eds.currentTimestampString =
|
|
eds.currentTimestampFormat.format(eds.currentTimestamp);
|
|
}
|
|
buf.append(eds.currentTimestampString);
|
|
}
|
|
}
|
|
|
|
protected static class TimeElement implements AccessLogElement {
|
|
// Milli-seconds in a second
|
|
private static final long INTERVAL = 1000;
|
|
|
|
private static final ThreadLocal<ElementTimestampStruct> currentTime =
|
|
new ThreadLocal<ElementTimestampStruct>() {
|
|
@Override
|
|
protected ElementTimestampStruct initialValue() {
|
|
return new ElementTimestampStruct("HH:mm:ss");
|
|
}
|
|
};
|
|
|
|
@Override
|
|
public void addElement(CharArrayWriter buf, Date date, Request request,
|
|
Response response, long time) {
|
|
ElementTimestampStruct eds = currentTime.get();
|
|
long millis = eds.currentTimestamp.getTime();
|
|
if (date.getTime() > (millis + INTERVAL -1) ||
|
|
date.getTime() < millis) {
|
|
eds.currentTimestamp.setTime(
|
|
date.getTime() - (date.getTime() % INTERVAL));
|
|
eds.currentTimestampString =
|
|
eds.currentTimestampFormat.format(eds.currentTimestamp);
|
|
}
|
|
buf.append(eds.currentTimestampString);
|
|
}
|
|
}
|
|
|
|
protected static class RequestHeaderElement implements AccessLogElement {
|
|
private final String header;
|
|
|
|
public RequestHeaderElement(String header) {
|
|
this.header = header;
|
|
}
|
|
@Override
|
|
public void addElement(CharArrayWriter buf, Date date, Request request,
|
|
Response response, long time) {
|
|
buf.append(wrap(request.getHeader(header)));
|
|
}
|
|
}
|
|
|
|
protected static class ResponseHeaderElement implements AccessLogElement {
|
|
private final String header;
|
|
|
|
public ResponseHeaderElement(String header) {
|
|
this.header = header;
|
|
}
|
|
|
|
@Override
|
|
public void addElement(CharArrayWriter buf, Date date, Request request,
|
|
Response response, long time) {
|
|
buf.append(wrap(response.getHeader(header)));
|
|
}
|
|
}
|
|
|
|
protected static class ServletContextElement implements AccessLogElement {
|
|
private final String attribute;
|
|
|
|
public ServletContextElement(String attribute) {
|
|
this.attribute = attribute;
|
|
}
|
|
@Override
|
|
public void addElement(CharArrayWriter buf, Date date, Request request,
|
|
Response response, long time) {
|
|
buf.append(wrap(request.getContext().getServletContext()
|
|
.getAttribute(attribute)));
|
|
}
|
|
}
|
|
|
|
protected static class CookieElement implements AccessLogElement {
|
|
private final String name;
|
|
|
|
public CookieElement(String name) {
|
|
this.name = name;
|
|
}
|
|
@Override
|
|
public void addElement(CharArrayWriter buf, Date date, Request request,
|
|
Response response, long time) {
|
|
Cookie[] c = request.getCookies();
|
|
for (int i = 0; c != null && i < c.length; i++) {
|
|
if (name.equals(c[i].getName())) {
|
|
buf.append(wrap(c[i].getValue()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* write a specific response header - x-O(xxx)
|
|
*/
|
|
protected static class ResponseAllHeaderElement implements AccessLogElement {
|
|
private final String header;
|
|
|
|
public ResponseAllHeaderElement(String header) {
|
|
this.header = header;
|
|
}
|
|
|
|
@Override
|
|
public void addElement(CharArrayWriter buf, Date date, Request request,
|
|
Response response, long time) {
|
|
if (null != response) {
|
|
Iterator<String> iter = response.getHeaders(header).iterator();
|
|
if (iter.hasNext()) {
|
|
StringBuilder buffer = new StringBuilder();
|
|
boolean first = true;
|
|
while (iter.hasNext()) {
|
|
if (first) {
|
|
first = false;
|
|
} else {
|
|
buffer.append(",");
|
|
}
|
|
buffer.append(iter.next());
|
|
}
|
|
buf.append(wrap(buffer.toString()));
|
|
}
|
|
return ;
|
|
}
|
|
buf.append("-");
|
|
}
|
|
}
|
|
|
|
protected static class RequestAttributeElement implements AccessLogElement {
|
|
private final String attribute;
|
|
|
|
public RequestAttributeElement(String attribute) {
|
|
this.attribute = attribute;
|
|
}
|
|
|
|
@Override
|
|
public void addElement(CharArrayWriter buf, Date date, Request request,
|
|
Response response, long time) {
|
|
buf.append(wrap(request.getAttribute(attribute)));
|
|
}
|
|
}
|
|
|
|
protected static class SessionAttributeElement implements AccessLogElement {
|
|
private final String attribute;
|
|
|
|
public SessionAttributeElement(String attribute) {
|
|
this.attribute = attribute;
|
|
}
|
|
@Override
|
|
public void addElement(CharArrayWriter buf, Date date, Request request,
|
|
Response response, long time) {
|
|
HttpSession session = null;
|
|
if (request != null) {
|
|
session = request.getSession(false);
|
|
if (session != null) {
|
|
buf.append(wrap(session.getAttribute(attribute)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected static class RequestParameterElement implements AccessLogElement {
|
|
private final String parameter;
|
|
|
|
public RequestParameterElement(String parameter) {
|
|
this.parameter = parameter;
|
|
}
|
|
/**
|
|
* urlEncode the given string. If null or empty, return null.
|
|
*/
|
|
private String urlEncode(String value) {
|
|
if (null==value || value.length()==0) {
|
|
return null;
|
|
}
|
|
try {
|
|
return URLEncoder.encode(value, "UTF-8");
|
|
} catch (UnsupportedEncodingException e) {
|
|
// Should never happen - all JVMs are required to support UTF-8
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void addElement(CharArrayWriter buf, Date date, Request request,
|
|
Response response, long time) {
|
|
buf.append(wrap(urlEncode(request.getParameter(parameter))));
|
|
}
|
|
}
|
|
|
|
protected static class PatternTokenizer {
|
|
private final StringReader sr;
|
|
private StringBuilder buf = new StringBuilder();
|
|
private boolean ended = false;
|
|
private boolean subToken;
|
|
private boolean parameter;
|
|
|
|
public PatternTokenizer(String str) {
|
|
sr = new StringReader(str);
|
|
}
|
|
|
|
public boolean hasSubToken() {
|
|
return subToken;
|
|
}
|
|
|
|
public boolean hasParameter() {
|
|
return parameter;
|
|
}
|
|
|
|
public String getToken() throws IOException {
|
|
if(ended) {
|
|
return null ;
|
|
}
|
|
|
|
String result = null;
|
|
subToken = false;
|
|
parameter = false;
|
|
|
|
int c = sr.read();
|
|
while (c != -1) {
|
|
switch (c) {
|
|
case ' ':
|
|
result = buf.toString();
|
|
buf = new StringBuilder();
|
|
buf.append((char) c);
|
|
return result;
|
|
case '-':
|
|
result = buf.toString();
|
|
buf = new StringBuilder();
|
|
subToken = true;
|
|
return result;
|
|
case '(':
|
|
result = buf.toString();
|
|
buf = new StringBuilder();
|
|
parameter = true;
|
|
return result;
|
|
case ')':
|
|
result = buf.toString();
|
|
buf = new StringBuilder();
|
|
break;
|
|
default:
|
|
buf.append((char) c);
|
|
}
|
|
c = sr.read();
|
|
}
|
|
ended = true;
|
|
if (buf.length() != 0) {
|
|
return buf.toString();
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public String getParameter()throws IOException {
|
|
String result;
|
|
if (!parameter) {
|
|
return null;
|
|
}
|
|
parameter = false;
|
|
int c = sr.read();
|
|
while (c != -1) {
|
|
if (c == ')') {
|
|
result = buf.toString();
|
|
buf = new StringBuilder();
|
|
return result;
|
|
}
|
|
buf.append((char) c);
|
|
c = sr.read();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public String getWhiteSpaces() throws IOException {
|
|
if(isEnded()) {
|
|
return "" ;
|
|
}
|
|
StringBuilder whiteSpaces = new StringBuilder();
|
|
if (buf.length() > 0) {
|
|
whiteSpaces.append(buf);
|
|
buf = new StringBuilder();
|
|
}
|
|
int c = sr.read();
|
|
while (Character.isWhitespace((char) c)) {
|
|
whiteSpaces.append((char) c);
|
|
c = sr.read();
|
|
}
|
|
if (c == -1) {
|
|
ended = true;
|
|
} else {
|
|
buf.append((char) c);
|
|
}
|
|
return whiteSpaces.toString();
|
|
}
|
|
|
|
public boolean isEnded() {
|
|
return ended;
|
|
}
|
|
|
|
public String getRemains() throws IOException {
|
|
StringBuilder remains = new StringBuilder();
|
|
for(int c = sr.read(); c != -1; c = sr.read()) {
|
|
remains.append((char) c);
|
|
}
|
|
return remains.toString();
|
|
}
|
|
|
|
}
|
|
|
|
@Override
|
|
protected AccessLogElement[] createLogElements() {
|
|
if (log.isDebugEnabled()) {
|
|
log.debug("decodePattern, pattern =" + pattern);
|
|
}
|
|
List<AccessLogElement> list = new ArrayList<>();
|
|
|
|
PatternTokenizer tokenizer = new PatternTokenizer(pattern);
|
|
try {
|
|
|
|
// Ignore leading whitespace.
|
|
tokenizer.getWhiteSpaces();
|
|
|
|
if (tokenizer.isEnded()) {
|
|
log.info(sm.getString("extendedAccessLogValve.emptyPattern"));
|
|
return null;
|
|
}
|
|
|
|
String token = tokenizer.getToken();
|
|
while (token != null) {
|
|
if (log.isDebugEnabled()) {
|
|
log.debug("token = " + token);
|
|
}
|
|
AccessLogElement element = getLogElement(token, tokenizer);
|
|
if (element == null) {
|
|
break;
|
|
}
|
|
list.add(element);
|
|
String whiteSpaces = tokenizer.getWhiteSpaces();
|
|
if (whiteSpaces.length() > 0) {
|
|
list.add(new StringElement(whiteSpaces));
|
|
}
|
|
if (tokenizer.isEnded()) {
|
|
break;
|
|
}
|
|
token = tokenizer.getToken();
|
|
}
|
|
if (log.isDebugEnabled()) {
|
|
log.debug("finished decoding with element size of: " + list.size());
|
|
}
|
|
return list.toArray(new AccessLogElement[0]);
|
|
} catch (IOException e) {
|
|
log.error(sm.getString("extendedAccessLogValve.patternParseError", pattern), e);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
protected AccessLogElement getLogElement(String token, PatternTokenizer tokenizer) throws IOException {
|
|
if ("date".equals(token)) {
|
|
return new DateElement();
|
|
} else if ("time".equals(token)) {
|
|
if (tokenizer.hasSubToken()) {
|
|
String nextToken = tokenizer.getToken();
|
|
if ("taken".equals(nextToken)) {
|
|
return new ElapsedTimeElement(false);
|
|
}
|
|
} else {
|
|
return new TimeElement();
|
|
}
|
|
} else if ("bytes".equals(token)) {
|
|
return new ByteSentElement(true);
|
|
} else if ("cached".equals(token)) {
|
|
/* I don't know how to evaluate this! */
|
|
return new StringElement("-");
|
|
} else if ("c".equals(token)) {
|
|
String nextToken = tokenizer.getToken();
|
|
if ("ip".equals(nextToken)) {
|
|
return new RemoteAddrElement();
|
|
} else if ("dns".equals(nextToken)) {
|
|
return new HostElement();
|
|
}
|
|
} else if ("s".equals(token)) {
|
|
String nextToken = tokenizer.getToken();
|
|
if ("ip".equals(nextToken)) {
|
|
return new LocalAddrElement(getIpv6Canonical());
|
|
} else if ("dns".equals(nextToken)) {
|
|
return new AccessLogElement() {
|
|
@Override
|
|
public void addElement(CharArrayWriter buf, Date date,
|
|
Request request, Response response, long time) {
|
|
String value;
|
|
try {
|
|
value = InetAddress.getLocalHost().getHostName();
|
|
} catch (Throwable e) {
|
|
ExceptionUtils.handleThrowable(e);
|
|
value = "localhost";
|
|
}
|
|
buf.append(value);
|
|
}
|
|
};
|
|
}
|
|
} else if ("cs".equals(token)) {
|
|
return getClientToServerElement(tokenizer);
|
|
} else if ("sc".equals(token)) {
|
|
return getServerToClientElement(tokenizer);
|
|
} else if ("sr".equals(token) || "rs".equals(token)) {
|
|
return getProxyElement(tokenizer);
|
|
} else if ("x".equals(token)) {
|
|
return getXParameterElement(tokenizer);
|
|
}
|
|
log.error(sm.getString("extendedAccessLogValve.decodeError", token));
|
|
return null;
|
|
}
|
|
|
|
protected AccessLogElement getClientToServerElement(
|
|
PatternTokenizer tokenizer) throws IOException {
|
|
if (tokenizer.hasSubToken()) {
|
|
String token = tokenizer.getToken();
|
|
if ("method".equals(token)) {
|
|
return new MethodElement();
|
|
} else if ("uri".equals(token)) {
|
|
if (tokenizer.hasSubToken()) {
|
|
token = tokenizer.getToken();
|
|
if ("stem".equals(token)) {
|
|
return new RequestURIElement();
|
|
} else if ("query".equals(token)) {
|
|
return new AccessLogElement() {
|
|
@Override
|
|
public void addElement(CharArrayWriter buf,
|
|
Date date, Request request,
|
|
Response response, long time) {
|
|
String query = request.getQueryString();
|
|
if (query != null) {
|
|
buf.append(query);
|
|
} else {
|
|
buf.append('-');
|
|
}
|
|
}
|
|
};
|
|
}
|
|
} else {
|
|
return new AccessLogElement() {
|
|
@Override
|
|
public void addElement(CharArrayWriter buf, Date date,
|
|
Request request, Response response, long time) {
|
|
String query = request.getQueryString();
|
|
if (query == null) {
|
|
buf.append(request.getRequestURI());
|
|
} else {
|
|
buf.append(request.getRequestURI());
|
|
buf.append('?');
|
|
buf.append(request.getQueryString());
|
|
}
|
|
}
|
|
};
|
|
}
|
|
}
|
|
} else if (tokenizer.hasParameter()) {
|
|
String parameter = tokenizer.getParameter();
|
|
if (parameter == null) {
|
|
log.error(sm.getString("extendedAccessLogValve.noClosing"));
|
|
return null;
|
|
}
|
|
return new RequestHeaderElement(parameter);
|
|
}
|
|
log.error(sm.getString("extendedAccessLogValve.decodeError", tokenizer.getRemains()));
|
|
return null;
|
|
}
|
|
|
|
protected AccessLogElement getServerToClientElement(
|
|
PatternTokenizer tokenizer) throws IOException {
|
|
if (tokenizer.hasSubToken()) {
|
|
String token = tokenizer.getToken();
|
|
if ("status".equals(token)) {
|
|
return new HttpStatusCodeElement();
|
|
} else if ("comment".equals(token)) {
|
|
return new StringElement("?");
|
|
}
|
|
} else if (tokenizer.hasParameter()) {
|
|
String parameter = tokenizer.getParameter();
|
|
if (parameter == null) {
|
|
log.error(sm.getString("extendedAccessLogValve.noClosing"));
|
|
return null;
|
|
}
|
|
return new ResponseHeaderElement(parameter);
|
|
}
|
|
log.error(sm.getString("extendedAccessLogValve.decodeError", tokenizer.getRemains()));
|
|
return null;
|
|
}
|
|
|
|
protected AccessLogElement getProxyElement(PatternTokenizer tokenizer)
|
|
throws IOException {
|
|
String token = null;
|
|
if (tokenizer.hasSubToken()) {
|
|
tokenizer.getToken();
|
|
return new StringElement("-");
|
|
} else if (tokenizer.hasParameter()) {
|
|
tokenizer.getParameter();
|
|
return new StringElement("-");
|
|
}
|
|
log.error(sm.getString("extendedAccessLogValve.decodeError", token));
|
|
return null;
|
|
}
|
|
|
|
protected AccessLogElement getXParameterElement(PatternTokenizer tokenizer)
|
|
throws IOException {
|
|
if (!tokenizer.hasSubToken()) {
|
|
log.error(sm.getString("extendedAccessLogValve.badXParam"));
|
|
return null;
|
|
}
|
|
String token = tokenizer.getToken();
|
|
if ("threadname".equals(token)) {
|
|
return new ThreadNameElement();
|
|
}
|
|
|
|
if (!tokenizer.hasParameter()) {
|
|
log.error(sm.getString("extendedAccessLogValve.badXParam"));
|
|
return null;
|
|
}
|
|
String parameter = tokenizer.getParameter();
|
|
if (parameter == null) {
|
|
log.error(sm.getString("extendedAccessLogValve.noClosing"));
|
|
return null;
|
|
}
|
|
if ("A".equals(token)) {
|
|
return new ServletContextElement(parameter);
|
|
} else if ("C".equals(token)) {
|
|
return new CookieElement(parameter);
|
|
} else if ("R".equals(token)) {
|
|
return new RequestAttributeElement(parameter);
|
|
} else if ("S".equals(token)) {
|
|
return new SessionAttributeElement(parameter);
|
|
} else if ("H".equals(token)) {
|
|
return getServletRequestElement(parameter);
|
|
} else if ("P".equals(token)) {
|
|
return new RequestParameterElement(parameter);
|
|
} else if ("O".equals(token)) {
|
|
return new ResponseAllHeaderElement(parameter);
|
|
}
|
|
log.error(sm.getString("extendedAccessLogValve.badXParamValue", token));
|
|
return null;
|
|
}
|
|
|
|
protected AccessLogElement getServletRequestElement(String parameter) {
|
|
if ("authType".equals(parameter)) {
|
|
return new AccessLogElement() {
|
|
@Override
|
|
public void addElement(CharArrayWriter buf, Date date,
|
|
Request request, Response response, long time) {
|
|
buf.append(wrap(request.getAuthType()));
|
|
}
|
|
};
|
|
} else if ("remoteUser".equals(parameter)) {
|
|
return new AccessLogElement() {
|
|
@Override
|
|
public void addElement(CharArrayWriter buf, Date date,
|
|
Request request, Response response, long time) {
|
|
buf.append(wrap(request.getRemoteUser()));
|
|
}
|
|
};
|
|
} else if ("requestedSessionId".equals(parameter)) {
|
|
return new AccessLogElement() {
|
|
@Override
|
|
public void addElement(CharArrayWriter buf, Date date,
|
|
Request request, Response response, long time) {
|
|
buf.append(wrap(request.getRequestedSessionId()));
|
|
}
|
|
};
|
|
} else if ("requestedSessionIdFromCookie".equals(parameter)) {
|
|
return new AccessLogElement() {
|
|
@Override
|
|
public void addElement(CharArrayWriter buf, Date date,
|
|
Request request, Response response, long time) {
|
|
buf.append(wrap(""
|
|
+ request.isRequestedSessionIdFromCookie()));
|
|
}
|
|
};
|
|
} else if ("requestedSessionIdValid".equals(parameter)) {
|
|
return new AccessLogElement() {
|
|
@Override
|
|
public void addElement(CharArrayWriter buf, Date date,
|
|
Request request, Response response, long time) {
|
|
buf.append(wrap("" + request.isRequestedSessionIdValid()));
|
|
}
|
|
};
|
|
} else if ("contentLength".equals(parameter)) {
|
|
return new AccessLogElement() {
|
|
@Override
|
|
public void addElement(CharArrayWriter buf, Date date,
|
|
Request request, Response response, long time) {
|
|
buf.append(wrap("" + request.getContentLengthLong()));
|
|
}
|
|
};
|
|
} else if ("characterEncoding".equals(parameter)) {
|
|
return new AccessLogElement() {
|
|
@Override
|
|
public void addElement(CharArrayWriter buf, Date date,
|
|
Request request, Response response, long time) {
|
|
buf.append(wrap(request.getCharacterEncoding()));
|
|
}
|
|
};
|
|
} else if ("locale".equals(parameter)) {
|
|
return new AccessLogElement() {
|
|
@Override
|
|
public void addElement(CharArrayWriter buf, Date date,
|
|
Request request, Response response, long time) {
|
|
buf.append(wrap(request.getLocale()));
|
|
}
|
|
};
|
|
} else if ("protocol".equals(parameter)) {
|
|
return new AccessLogElement() {
|
|
@Override
|
|
public void addElement(CharArrayWriter buf, Date date,
|
|
Request request, Response response, long time) {
|
|
buf.append(wrap(request.getProtocol()));
|
|
}
|
|
};
|
|
} else if ("scheme".equals(parameter)) {
|
|
return new AccessLogElement() {
|
|
@Override
|
|
public void addElement(CharArrayWriter buf, Date date,
|
|
Request request, Response response, long time) {
|
|
buf.append(request.getScheme());
|
|
}
|
|
};
|
|
} else if ("secure".equals(parameter)) {
|
|
return new AccessLogElement() {
|
|
@Override
|
|
public void addElement(CharArrayWriter buf, Date date,
|
|
Request request, Response response, long time) {
|
|
buf.append(wrap("" + request.isSecure()));
|
|
}
|
|
};
|
|
}
|
|
log.error(sm.getString("extendedAccessLogValve.badXParamValue", parameter));
|
|
return null;
|
|
}
|
|
|
|
private static class ElementTimestampStruct {
|
|
private final Date currentTimestamp = new Date(0);
|
|
private final SimpleDateFormat currentTimestampFormat;
|
|
private String currentTimestampString;
|
|
|
|
ElementTimestampStruct(String format) {
|
|
currentTimestampFormat = new SimpleDateFormat(format, Locale.US);
|
|
currentTimestampFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
|
|
}
|
|
}
|
|
}
|