From: remm Date: Mon, 19 Feb 2007 12:19:08 +0000 (+0000) Subject: - Refactoring of ExtendedAccessLogValve, which now extends AccessLogValve. X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=f3cc13f1f3d786a4ea1b10f5fc2d823fd11a630d;p=tomcat7.0 - Refactoring of ExtendedAccessLogValve, which now extends AccessLogValve. - Submitted by Takayuki Kaneko. git-svn-id: https://svn.apache.org/repos/asf/tomcat/tc6.0.x/trunk@509179 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/java/org/apache/catalina/valves/AccessLogValve.java b/java/org/apache/catalina/valves/AccessLogValve.java index 1c4484aec..c2a90096f 100644 --- a/java/org/apache/catalina/valves/AccessLogValve.java +++ b/java/org/apache/catalina/valves/AccessLogValve.java @@ -43,6 +43,8 @@ import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; import org.apache.catalina.util.LifecycleSupport; import org.apache.catalina.util.StringManager; +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; /** @@ -116,17 +118,7 @@ public class AccessLogValve extends ValveBase implements Lifecycle { - - // ----------------------------------------------------------- Constructors - - - /** - * Construct a new instance of this class with default property values. - */ - public AccessLogValve() { - setPattern("common"); - } - + private static Log log = LogFactory.getLog(AccessLogValve.class); // ----------------------------------------------------- Instance Variables @@ -168,19 +160,19 @@ public class AccessLogValve /** * The pattern used to format our access log lines. */ - private String pattern = null; + protected String pattern = null; /** * The prefix that is added to log file filenames. */ - private String prefix = "access_log."; + protected String prefix = "access_log."; /** * Should we rotate our log file? Default is true (like old behavior) */ - private boolean rotatable = true; + protected boolean rotatable = true; /** @@ -192,33 +184,33 @@ public class AccessLogValve /** * The string manager for this package. */ - private StringManager sm = + protected StringManager sm = StringManager.getManager(Constants.Package); /** * Has this component been started yet? */ - private boolean started = false; + protected boolean started = false; /** * The suffix that is added to log file filenames. */ - private String suffix = ""; + protected String suffix = ""; /** * The PrintWriter to which we are currently logging, if any. */ - private PrintWriter writer = null; + protected PrintWriter writer = null; /** * A date formatter to format a Date into a date in the format * "yyyy-MM-dd". */ - private SimpleDateFormat dateFormatter = null; + protected SimpleDateFormat fileDateFormatter = null; /** @@ -270,6 +262,12 @@ public class AccessLogValve /** + * The current log file we are writing to. Helpful when checkExists + * is true. + */ + protected File currentLogFile = null; + + /** * The system time when we last updated the Date that this valve * uses for log lines. */ @@ -289,22 +287,28 @@ public class AccessLogValve */ private long rotationLastChecked = 0L; - + /** + * Do we check for log file existence? Helpful if an external + * agent renames the log file so we can automagically recreate it. + */ + private boolean checkExists = false; + + /** * Are we doing conditional logging. default false. */ - private String condition = null; + protected String condition = null; /** * Date format to place in log file name. Use at your own risk! */ - private String fileDateFormat = null; + protected String fileDateFormat = null; /** * Array of AccessLogElement, they will be used to make log message. */ - private AccessLogElement[] logElements = null; + protected AccessLogElement[] logElements = null; // ------------------------------------------------------------- Properties @@ -361,6 +365,28 @@ public class AccessLogValve /** + * Check for file existence before logging. + */ + public boolean isCheckExists() { + + return checkExists; + + } + + + /** + * Set whether to check for log file existence before logging. + * + * @param checkExists true meaning to check for file existence. + */ + public void setCheckExists(boolean checkExists) { + + this.checkExists = checkExists; + + } + + + /** * Return the log file prefix. */ public String getPrefix() { @@ -519,7 +545,7 @@ public class AccessLogValve long t2 = System.currentTimeMillis(); long time = t2 - t1; - if (condition != null + if (logElements == null || condition != null && null != request.getRequest().getAttribute(condition)) { return; } @@ -534,6 +560,38 @@ public class AccessLogValve log(result.toString()); } + + /** + * Rename the existing log file to something else. Then open the + * old log file name up once again. Intended to be called by a JMX + * agent. + * + * + * @param newFileName The file name to move the log file entry to + * @return true if a file was rotated with no error + */ + public synchronized boolean rotate(String newFileName) { + + if (currentLogFile != null) { + File holder = currentLogFile; + close(); + try { + holder.renameTo(new File(newFileName)); + } catch (Throwable e) { + log.error("rotate failed", e); + } + + /* Make sure date is correct */ + currentDate = new Date(System.currentTimeMillis()); + dateStamp = fileDateFormatter.format(currentDate); + + open(); + return true; + } else { + return false; + } + + } // -------------------------------------------------------- Private Methods @@ -549,6 +607,7 @@ public class AccessLogValve writer.close(); writer = null; dateStamp = ""; + currentLogFile = null; } @@ -569,7 +628,7 @@ public class AccessLogValve rotationLastChecked = systime; // Check for a change of date - String tsDate = dateFormatter.format(currentDate); + String tsDate = fileDateFormatter.format(currentDate); // If the date has changed, switch log files if (!dateStamp.equals(tsDate)) { @@ -583,6 +642,25 @@ public class AccessLogValve } } } + + /* In case something external rotated the file instead */ + if (checkExists) { + synchronized (this) { + if (currentLogFile != null && !currentLogFile.exists()) { + try { + close(); + } catch (Throwable e) { + log.info("at least this wasn't swallowed", e); + } + + /* Make sure date is correct */ + currentDate = new Date(System.currentTimeMillis()); + dateStamp = fileDateFormatter.format(currentDate); + + open(); + } + } + } // Log this message if (writer != null) { @@ -615,7 +693,7 @@ public class AccessLogValve /** * Open the new log file for the date specified by dateStamp. */ - private synchronized void open() { + protected synchronized void open() { // Create the directory if necessary File dir = new File(directory); if (!dir.isAbsolute()) @@ -635,8 +713,11 @@ public class AccessLogValve } writer = new PrintWriter(new BufferedWriter(new FileWriter( pathname, true), 128000), false); + + currentLogFile = new File(pathname); } catch (IOException e) { writer = null; + currentLogFile = null; } } @@ -755,8 +836,8 @@ public class AccessLogValve if (fileDateFormat == null || fileDateFormat.length() == 0) fileDateFormat = "yyyy-MM-dd"; - dateFormatter = new SimpleDateFormat(fileDateFormat); - dateFormatter.setTimeZone(timezone); + fileDateFormatter = new SimpleDateFormat(fileDateFormat); + fileDateFormatter.setTimeZone(timezone); dayFormatter = new SimpleDateFormat("dd"); dayFormatter.setTimeZone(timezone); monthFormatter = new SimpleDateFormat("MM"); @@ -766,7 +847,7 @@ public class AccessLogValve timeFormatter = new SimpleDateFormat("HH:mm:ss"); timeFormatter.setTimeZone(timezone); currentDate = new Date(); - dateStamp = dateFormatter.format(currentDate); + dateStamp = fileDateFormatter.format(currentDate); open(); } @@ -793,7 +874,7 @@ public class AccessLogValve /** * AccessLogElement writes the partial message into the buffer. */ - private interface AccessLogElement { + protected interface AccessLogElement { public void addElement(StringBuffer buf, Date date, Request request, Response response, long time); @@ -802,14 +883,20 @@ public class AccessLogValve /** * write local IP address - %A */ - private class LocalAddrElement implements AccessLogElement { + protected class LocalAddrElement implements AccessLogElement { + + private String value = null; + public void addElement(StringBuffer buf, Date date, Request request, Response response, long time) { - String value; - try { - value = InetAddress.getLocalHost().getHostAddress(); - } catch (Throwable e) { - value = "127.0.0.1"; + if (value == null) { + synchronized (this) { + try { + value = InetAddress.getLocalHost().getHostAddress(); + } catch (Throwable e) { + value = "127.0.0.1"; + } + } } buf.append(value); } @@ -818,7 +905,7 @@ public class AccessLogValve /** * write remote IP address - %a */ - private class RemoteAddrElement implements AccessLogElement { + protected class RemoteAddrElement implements AccessLogElement { public void addElement(StringBuffer buf, Date date, Request request, Response response, long time) { buf.append(request.getRemoteAddr()); @@ -828,7 +915,7 @@ public class AccessLogValve /** * write remote host name - %h */ - private class HostElement implements AccessLogElement { + protected class HostElement implements AccessLogElement { public void addElement(StringBuffer buf, Date date, Request request, Response response, long time) { buf.append(request.getRemoteHost()); @@ -838,7 +925,7 @@ public class AccessLogValve /** * write remote logical username from identd (always returns '-') - %l */ - private class LogicalUserNameElement implements AccessLogElement { + protected class LogicalUserNameElement implements AccessLogElement { public void addElement(StringBuffer buf, Date date, Request request, Response response, long time) { buf.append('-'); @@ -848,7 +935,7 @@ public class AccessLogValve /** * write request protocol - %H */ - private class ProtocolElement implements AccessLogElement { + protected class ProtocolElement implements AccessLogElement { public void addElement(StringBuffer buf, Date date, Request request, Response response, long time) { buf.append(request.getProtocol()); @@ -858,7 +945,7 @@ public class AccessLogValve /** * write remote user that was authenticated (if any), else '-' - %u */ - private class UserElement implements AccessLogElement { + protected class UserElement implements AccessLogElement { public void addElement(StringBuffer buf, Date date, Request request, Response response, long time) { if (request != null) { @@ -877,7 +964,7 @@ public class AccessLogValve /** * write date and time, in Common Log Format - %t */ - private class DateAndTimeElement implements AccessLogElement { + protected class DateAndTimeElement implements AccessLogElement { private Date currentDate = new Date(0); private String currentDateString = null; @@ -911,7 +998,7 @@ public class AccessLogValve /** * write first line of the request (method and request URI) - %r */ - private class RequestElement implements AccessLogElement { + protected class RequestElement implements AccessLogElement { public void addElement(StringBuffer buf, Date date, Request request, Response response, long time) { if (request != null) { @@ -934,7 +1021,7 @@ public class AccessLogValve /** * write HTTP status code of the response - %s */ - private class HttpStatusCodeElement implements AccessLogElement { + protected class HttpStatusCodeElement implements AccessLogElement { public void addElement(StringBuffer buf, Date date, Request request, Response response, long time) { if (response != null) { @@ -948,7 +1035,7 @@ public class AccessLogValve /** * write local port on which this request was received - %p */ - private class LocalPortElement implements AccessLogElement { + protected class LocalPortElement implements AccessLogElement { public void addElement(StringBuffer buf, Date date, Request request, Response response, long time) { buf.append(request.getServerPort()); @@ -958,7 +1045,7 @@ public class AccessLogValve /** * write bytes sent, excluding HTTP headers - %b, %B */ - private class ByteSentElement implements AccessLogElement { + protected class ByteSentElement implements AccessLogElement { private boolean conversion; /** @@ -982,7 +1069,7 @@ public class AccessLogValve /** * write request method (GET, POST, etc.) - %m */ - private class MethodElement implements AccessLogElement { + protected class MethodElement implements AccessLogElement { public void addElement(StringBuffer buf, Date date, Request request, Response response, long time) { if (request != null) { @@ -994,7 +1081,7 @@ public class AccessLogValve /** * write time taken to process the request - %D, %T */ - private class ElapsedTimeElement implements AccessLogElement { + protected class ElapsedTimeElement implements AccessLogElement { private boolean millis; /** @@ -1025,7 +1112,7 @@ public class AccessLogValve /** * write Query string (prepended with a '?' if it exists) - %q */ - private class QueryElement implements AccessLogElement { + protected class QueryElement implements AccessLogElement { public void addElement(StringBuffer buf, Date date, Request request, Response response, long time) { String query = null; @@ -1041,7 +1128,7 @@ public class AccessLogValve /** * write user session ID - %S */ - private class SessionIdElement implements AccessLogElement { + protected class SessionIdElement implements AccessLogElement { public void addElement(StringBuffer buf, Date date, Request request, Response response, long time) { if (request != null) { @@ -1060,7 +1147,7 @@ public class AccessLogValve /** * write requested URL path - %U */ - private class RequestURIElement implements AccessLogElement { + protected class RequestURIElement implements AccessLogElement { public void addElement(StringBuffer buf, Date date, Request request, Response response, long time) { if (request != null) { @@ -1074,7 +1161,7 @@ public class AccessLogValve /** * write local server name - %v */ - private class LocalServerNameElement implements AccessLogElement { + protected class LocalServerNameElement implements AccessLogElement { public void addElement(StringBuffer buf, Date date, Request request, Response response, long time) { buf.append(request.getServerName()); @@ -1084,7 +1171,7 @@ public class AccessLogValve /** * write any string */ - private class StringElement implements AccessLogElement { + protected class StringElement implements AccessLogElement { private String str; public StringElement(String str) { @@ -1100,7 +1187,7 @@ public class AccessLogValve /** * write incoming headers - %{xxx}i */ - private class HeaderElement implements AccessLogElement { + protected class HeaderElement implements AccessLogElement { private String header; public HeaderElement(String header) { @@ -1116,7 +1203,7 @@ public class AccessLogValve /** * write a specific cookie - %{xxx}c */ - private class CookieElement implements AccessLogElement { + protected class CookieElement implements AccessLogElement { private String header; public CookieElement(String header) { @@ -1142,7 +1229,7 @@ public class AccessLogValve /** * write an attribute in the ServletRequest - %{xxx}r */ - private class RequestAttributeElement implements AccessLogElement { + protected class RequestAttributeElement implements AccessLogElement { private String header; public RequestAttributeElement(String header) { @@ -1172,7 +1259,7 @@ public class AccessLogValve /** * write an attribute in the HttpSession - %{xxx}s */ - private class SessionAttributeElement implements AccessLogElement { + protected class SessionAttributeElement implements AccessLogElement { private String header; public SessionAttributeElement(String header) { @@ -1207,8 +1294,8 @@ public class AccessLogValve /** * parse pattern string and create the array of AccessLogElement */ - private AccessLogElement[] createLogElements() { - List list = new ArrayList(); + protected AccessLogElement[] createLogElements() { + List list = new ArrayList(); boolean replace = false; StringBuffer buf = new StringBuffer(); for (int i = 0; i < pattern.length(); i++) { diff --git a/java/org/apache/catalina/valves/ExtendedAccessLogValve.java b/java/org/apache/catalina/valves/ExtendedAccessLogValve.java index 252164f4a..4fde98165 100644 --- a/java/org/apache/catalina/valves/ExtendedAccessLogValve.java +++ b/java/org/apache/catalina/valves/ExtendedAccessLogValve.java @@ -19,31 +19,23 @@ package org.apache.catalina.valves; -import java.io.File; -import java.io.FileWriter; import java.io.IOException; -import java.io.PrintWriter; +import java.io.StringReader; import java.net.InetAddress; import java.net.URLEncoder; -import java.text.DecimalFormat; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; -import java.util.Iterator; -import java.util.LinkedList; +import java.util.List; import java.util.TimeZone; -import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpSession; import org.apache.catalina.Lifecycle; -import org.apache.catalina.LifecycleException; -import org.apache.catalina.LifecycleListener; import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; -import org.apache.catalina.util.LifecycleSupport; import org.apache.catalina.util.ServerInfo; -import org.apache.catalina.util.StringManager; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; @@ -103,7 +95,7 @@ import org.apache.juli.logging.LogFactory; *

* *

- * For UNIX users, another field called checkExistsis also + * For UvNIX users, another field called checkExistsis 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. @@ -134,650 +126,37 @@ import org.apache.juli.logging.LogFactory; */ public class ExtendedAccessLogValve - extends ValveBase + extends AccessLogValve implements Lifecycle { - - // ----------------------------------------------------------- Constructors - - - /** - * Construct a new instance of this class with default property values. - */ - public ExtendedAccessLogValve() { - - super(); - - - } - - - // ----------------------------------------------------- Instance Variables private static Log log = LogFactory.getLog(ExtendedAccessLogValve.class); - - /** - * The descriptive information about this implementation. - */ - protected static final String info = - "org.apache.catalina.valves.ExtendedAccessLogValve/1.0"; - - - /** - * The lifecycle event support for this component. - */ - protected LifecycleSupport lifecycle = new LifecycleSupport(this); - - - - /** - * The string manager for this package. - */ - private StringManager sm = - StringManager.getManager(Constants.Package); - - - /** - * Has this component been started yet? - */ - private boolean started = false; - - - /** - * The as-of date for the currently open log file, or a zero-length - * string if there is no open log file. - */ - private String dateStamp = ""; - - - /** - * The PrintWriter to which we are currently logging, if any. - */ - private PrintWriter writer = null; - - - /** - * The formatter for the date contained in the file name. - */ - private SimpleDateFormat fileDateFormatter = null; - - - /** - * A date formatter to format a Date into a date in the format - * "yyyy-MM-dd". - */ - private SimpleDateFormat dateFormatter = null; - - - /** - * A date formatter to format a Date into a time in the format - * "kk:mm:ss" (kk is a 24-hour representation of the hour). - */ - private SimpleDateFormat timeFormatter = null; - - - /** - * Time taken formatter for 3 decimal places. - */ - private DecimalFormat timeTakenFormatter = null; - - - /** - * My ip address. Look it up once and remember it. Dump this if we can - * determine another reliable way to get server ip address since this - * server may have many ip's. - */ - private String myIpAddress = null; - - - /** - * My dns name. Look it up once and remember it. Dump this if we can - * determine another reliable way to get server name address since this - * server may have many ip's. - */ - private String myDNSName = null; - - - /** - * Holder for all of the fields to log after the pattern is decoded. - */ - private FieldInfo[] fieldInfos; - - - /** - * The current log file we are writing to. Helpful when checkExists - * is true. - */ - private File currentLogFile = null; - - - - /** - * The system time when we last updated the Date that this valve - * uses for log lines. - */ - private Date currentDate = null; - - - /** - * Instant when the log daily rotation was last checked. - */ - private long rotationLastChecked = 0L; - - - /** - * The directory in which log files are created. - */ - private String directory = "logs"; - - - /** - * The pattern used to format our access log lines. - */ - private String pattern = null; - - - /** - * The prefix that is added to log file filenames. - */ - private String prefix = "access_log."; - - - /** - * Should we rotate our log file? Default is true (like old behavior) - */ - private boolean rotatable = true; - - - /** - * The suffix that is added to log file filenames. - */ - private String suffix = ""; - - - /** - * Are we doing conditional logging. default false. - */ - private String condition = null; - - - /** - * Do we check for log file existence? Helpful if an external - * agent renames the log file so we can automagically recreate it. - */ - private boolean checkExists = false; - - - /** - * Date format to place in log file name. Use at your own risk! - */ - private String fileDateFormat = null; - - - // ------------------------------------------------------------- Properties - - - /** - * Return the directory in which we create log files. - */ - public String getDirectory() { - - return (directory); - - } - - - /** - * Set the directory in which we create log files. - * - * @param directory The new log file directory - */ - public void setDirectory(String directory) { - - this.directory = directory; - - } - - - /** - * Return descriptive information about this implementation. - */ - public String getInfo() { - - return (info); - - } - - - /** - * Return the format pattern. - */ - public String getPattern() { - - return (this.pattern); - - } - - - /** - * Set the format pattern, first translating any recognized alias. - * - * @param pattern The new pattern pattern - */ - public void setPattern(String pattern) { - - FieldInfo[] f= decodePattern(pattern); - if (f!=null) { - this.pattern = pattern; - this.fieldInfos = f; - } - } - - - /** - * Return the log file prefix. - */ - public String getPrefix() { - - return (prefix); - - } - - - /** - * Set the log file prefix. - * - * @param prefix The new log file prefix - */ - public void setPrefix(String prefix) { - - this.prefix = prefix; - - } - - - /** - * Return true if logs are automatically rotated. - */ - public boolean isRotatable() { - - return rotatable; - - } - - - /** - * Set the value is we should we rotate the logs - * - * @param rotatable true is we should rotate. - */ - public void setRotatable(boolean rotatable) { - - this.rotatable = rotatable; - - } - - - /** - * Return the log file suffix. - */ - public String getSuffix() { - - return (suffix); - - } - - - /** - * Set the log file suffix. - * - * @param suffix The new log file suffix - */ - public void setSuffix(String suffix) { - - this.suffix = suffix; - - } - - - - /** - * Return whether the attribute name to look for when - * performing conditional loggging. If null, every - * request is logged. - */ - public String getCondition() { - - return condition; - - } - - - /** - * Set the ServletRequest.attribute to look for to perform - * conditional logging. Set to null to log everything. - * - * @param condition Set to null to log everything - */ - public void setCondition(String condition) { - - this.condition = condition; - - } - - - - /** - * Check for file existence before logging. - */ - public boolean isCheckExists() { - - return checkExists; - - } - - - /** - * Set whether to check for log file existence before logging. - * - * @param checkExists true meaning to check for file existence. - */ - public void setCheckExists(boolean checkExists) { - - this.checkExists = checkExists; - - } - - - /** - * Return the date format date based log rotation. - */ - public String getFileDateFormat() { - return fileDateFormat; - } - - - /** - * Set the date format date based log rotation. - */ - public void setFileDateFormat(String fileDateFormat) { - this.fileDateFormat = fileDateFormat; - } - - - // --------------------------------------------------------- Public Methods - - - /** - * Log a message summarizing the specified request and response, according - * to the format specified by the pattern property. - * - * @param request Request being processed - * @param response Response being processed - * - * @exception IOException if an input/output error has occurred - * @exception ServletException if a servlet error has occurred - */ - public void invoke(Request request, Response response) - throws IOException, ServletException { - - // Pass this request on to the next valve in our pipeline - long endTime; - long runTime; - long startTime=System.currentTimeMillis(); - - getNext().invoke(request, response); - - endTime = System.currentTimeMillis(); - runTime = endTime-startTime; - - if (fieldInfos==null || condition!=null && - null!=request.getRequest().getAttribute(condition)) { - return; - } - - - Date date = getDate(endTime); - StringBuffer result = new StringBuffer(); - - for (int i=0; fieldInfos!=null && i 0) - result.append(length); - else - result.append("-"); - } else if (FieldInfo.SPECIAL_CACHED==fieldInfos[i].location) - result.append('-'); /* I don't know how to evaluate this! */ - else - result.append("?WTF?"); /* This should never happen! */ - break; - default: - result.append("?WTF?"); /* This should never happen! */ - } - - if (fieldInfos[i].postWhiteSpace!=null) { - result.append(fieldInfos[i].postWhiteSpace); - } - } - log(result.toString(), date); - - } - - - /** - * Rename the existing log file to something else. Then open the - * old log file name up once again. Intended to be called by a JMX - * agent. - * - * - * @param newFileName The file name to move the log file entry to - * @return true if a file was rotated with no error - */ - public synchronized boolean rotate(String newFileName) { - - if (currentLogFile!=null) { - File holder = currentLogFile; - close(); - try { - holder.renameTo(new File(newFileName)); - } catch(Throwable e){ - log.error("rotate failed", e); - } - - /* Make sure date is correct */ - currentDate = new Date(System.currentTimeMillis()); - dateStamp = fileDateFormatter.format(currentDate); - - open(); - return true; - } else { - return false; - } - - } - - // -------------------------------------------------------- Private Methods - - - /** - * Return the client to server data. - * @param fieldInfo The field to decode. - * @param request The object we pull data from. - * @return The appropriate value. - */ - private String getClientToServer(FieldInfo fieldInfo, Request request) { - - switch(fieldInfo.location) { - case FieldInfo.FIELD_METHOD: - return request.getMethod(); - case FieldInfo.FIELD_URI: - if (null==request.getQueryString()) - return request.getRequestURI(); - else - return request.getRequestURI() + "?" + request.getQueryString(); - case FieldInfo.FIELD_URI_STEM: - return request.getRequestURI(); - case FieldInfo.FIELD_URI_QUERY: - if (null==request.getQueryString()) - return "-"; - return request.getQueryString(); - case FieldInfo.FIELD_HEADER: - return wrap(request.getHeader(fieldInfo.value)); - default: - ; - } - - return "-"; - - } + // ----------------------------------------------------- Instance Variables /** - * Return the server to client data. - * @param fieldInfo The field to decode. - * @param response The object we pull data from. - * @return The appropriate value. + * The descriptive information about this implementation. */ - private String getServerToClient(FieldInfo fieldInfo, Response response) { - switch(fieldInfo.location) { - case FieldInfo.FIELD_STATUS: - return "" + response.getStatus(); - case FieldInfo.FIELD_COMMENT: - return "?"; /* Not coded yet*/ - case FieldInfo.FIELD_HEADER: - return wrap(response.getHeader(fieldInfo.value)); - default: - ; - } + protected static final String extendedAccessLogInfo = + "org.apache.catalina.valves.ExtendedAccessLogValve/1.0"; - return "-"; - } + // ------------------------------------------------------------- Properties /** - * Get app specific data. - * @param fieldInfo The field to decode - * @param request Where we will pull the data from. - * @return The appropriate value + * Return descriptive information about this implementation. */ - private String getAppSpecific(FieldInfo fieldInfo, Request request) { - - switch(fieldInfo.xType) { - case FieldInfo.X_PARAMETER: - return wrap(urlEncode(request.getParameter(fieldInfo.value))); - case FieldInfo.X_REQUEST: - return wrap(request.getAttribute(fieldInfo.value)); - case FieldInfo.X_SESSION: - HttpSession session = null; - if (request!=null){ - session = request.getSession(false); - if (session!=null) - return wrap(session.getAttribute(fieldInfo.value)); - } - break; - case FieldInfo.X_COOKIE: - Cookie[] c = request.getCookies(); - for (int i=0; c != null && i < c.length; i++){ - if (fieldInfo.value.equals(c[i].getName())){ - return wrap(c[i].getValue()); - } - } - case FieldInfo.X_APP: - return wrap(request.getContext().getServletContext() - .getAttribute(fieldInfo.value)); - case FieldInfo.X_SERVLET_REQUEST: - if (fieldInfo.location==FieldInfo.X_LOC_AUTHTYPE) { - return wrap(request.getAuthType()); - } else if (fieldInfo.location==FieldInfo.X_LOC_REMOTEUSER) { - return wrap(request.getRemoteUser()); - } else if (fieldInfo.location== - FieldInfo.X_LOC_REQUESTEDSESSIONID) { - return wrap(request.getRequestedSessionId()); - } else if (fieldInfo.location== - FieldInfo.X_LOC_REQUESTEDSESSIONIDFROMCOOKIE) { - return wrap(""+request.isRequestedSessionIdFromCookie()); - } else if (fieldInfo.location== - FieldInfo.X_LOC_REQUESTEDSESSIONIDVALID) { - return wrap(""+request.isRequestedSessionIdValid()); - } else if (fieldInfo.location==FieldInfo.X_LOC_CONTENTLENGTH) { - return wrap(""+request.getContentLength()); - } else if (fieldInfo.location== - FieldInfo.X_LOC_CHARACTERENCODING) { - return wrap(request.getCharacterEncoding()); - } else if (fieldInfo.location==FieldInfo.X_LOC_LOCALE) { - return wrap(request.getLocale()); - } else if (fieldInfo.location==FieldInfo.X_LOC_PROTOCOL) { - return wrap(request.getProtocol()); - } else if (fieldInfo.location==FieldInfo.X_LOC_SCHEME) { - return wrap(request.getScheme()); - } else if (fieldInfo.location==FieldInfo.X_LOC_SECURE) { - return wrap(""+request.isSecure()); - } - break; - default: - ; - } - - return "-"; - + public String getInfo() { + return (extendedAccessLogInfo); } - /** - * urlEncode the given string. If null or empty, return null. - */ - private String urlEncode(String value) { - if (null==value || value.length()==0) { - return null; - } - return URLEncoder.encode(value); - } + // --------------------------------------------------------- Public Methods + // -------------------------------------------------------- Private Methods + /** * Wrap the incoming value into quotes and escape any inner * quotes with double quotes. @@ -789,642 +168,640 @@ public class ExtendedAccessLogValve * sets of quotes. */ private String wrap(Object value) { - String svalue; // Does the value contain a " ? If so must encode it - if (value==null || "-".equals(value)) + if (value == null || "-".equals(value)) return "-"; - try { svalue = value.toString(); if ("".equals(svalue)) return "-"; - } catch(Throwable e){ + } catch (Throwable e) { /* Log error */ return "-"; } /* Wrap all quotes in double quotes. */ - StringBuffer buffer = new StringBuffer(svalue.length()+2); - buffer.append('"'); - int i=0; - while (idateStamp. */ - private synchronized void close() { - - if (writer == null) - return; - writer.flush(); - writer.close(); - writer = null; - currentLogFile = null; - + protected synchronized void open() { + super.open(); + if (currentLogFile.length()==0) { + writer.println("#Fields: " + pattern); + writer.println("#Version: 1.0"); + writer.println("#Software: " + ServerInfo.getServerInfo()); + } } - /** - * Log the specified message to the log file, switching files if the date - * has changed since the previous log call. - * - * @param message Message to be logged - * @param date the current Date object (so this method doesn't need to - * create a new one) - */ - private void log(String message, Date date) { - - if (rotatable){ - // Only do a logfile switch check once a second, max. - long systime = System.currentTimeMillis(); - if ((systime - rotationLastChecked) > 1000) { - - // We need a new currentDate - currentDate = new Date(systime); - rotationLastChecked = systime; + // ------------------------------------------------------ Lifecycle Methods - // Check for a change of date - String tsDate = fileDateFormatter.format(currentDate); - // If the date has changed, switch log files - if (!dateStamp.equals(tsDate)) { - synchronized (this) { - if (!dateStamp.equals(tsDate)) { - close(); - dateStamp = tsDate; - open(); - } + protected class DateElement implements AccessLogElement { + private Date currentDate = new Date(0); + + private String currentDateString = null; + + /** + * A date formatter to format a Date into a date in the format + * "yyyy-MM-dd". + */ + private SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd"); + + public DateElement() { + dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT")); + } + + public void addElement(StringBuffer buf, Date date, Request request, + Response response, long time) { + if (currentDate != date) { + synchronized (this) { + if (currentDate != date) { + currentDateString = dateFormatter.format(date); + currentDate = date; } } } + buf.append(currentDateString); } - - /* In case something external rotated the file instead */ - if (checkExists){ - synchronized (this) { - if (currentLogFile!=null && !currentLogFile.exists()) { - try { - close(); - } catch (Throwable e){ - log.info("at least this wasn't swallowed", e); + } + + protected class TimeElement implements AccessLogElement { + private Date currentDate = new Date(0); + + private String currentTimeString = null; + + /** + * A date formatter to format a Date into a time in the format + * "kk:mm:ss" (kk is a 24-hour representation of the hour). + */ + private SimpleDateFormat timeFormatter = new SimpleDateFormat("HH:mm:ss"); + + public TimeElement() { + timeFormatter.setTimeZone(TimeZone.getTimeZone("GMT")); + } + + public void addElement(StringBuffer buf, Date date, Request request, + Response response, long time) { + if (currentDate != date) { + synchronized (this) { + if (currentDate != date) { + currentTimeString = timeFormatter.format(date); + currentDate = date; } - - /* Make sure date is correct */ - currentDate = new Date(System.currentTimeMillis()); - dateStamp = fileDateFormatter.format(currentDate); - - open(); } } + buf.append(currentTimeString); } - - // Log this message - if (writer != null) { - writer.println(message); - } - } - - - /** - * Open the new log file for the date specified by dateStamp. - */ - private synchronized void open() { - - // Create the directory if necessary - File dir = new File(directory); - if (!dir.isAbsolute()) - dir = new File(System.getProperty("catalina.base"), directory); - dir.mkdirs(); - - // Open the current log file - try { - String pathname; - - // If no rotate - no need for dateStamp in fileName - if (rotatable){ - pathname = dir.getAbsolutePath() + File.separator + - prefix + dateStamp + suffix; - } else { - pathname = dir.getAbsolutePath() + File.separator + - prefix + suffix; - } - - currentLogFile = new File(pathname); - writer = new PrintWriter(new FileWriter(pathname, true), true); - if (currentLogFile.length()==0) { - writer.println("#Fields: " + pattern); - writer.println("#Version: 1.0"); - writer.println("#Software: " + ServerInfo.getServerInfo()); - } - - - } catch (IOException e) { - writer = null; - currentLogFile = null; + + protected class RequestHeaderElement implements AccessLogElement { + private String header; + + public RequestHeaderElement(String header) { + this.header = header; + } + public void addElement(StringBuffer buf, Date date, Request request, + Response response, long time) { + buf.append(wrap(request.getHeader(header))); } - } - - - /** - * This method returns a Date object that is accurate to within one - * second. If a thread calls this method to get a Date and it's been - * less than 1 second since a new Date was created, this method - * simply gives out the same Date again so that the system doesn't - * spend time creating Date objects unnecessarily. - */ - private Date getDate(long systime) { - /* Avoid extra call to System.currentTimeMillis(); */ - if (0==systime) { - systime = System.currentTimeMillis(); + + protected class ResponseHeaderElement implements AccessLogElement { + private String header; + + public ResponseHeaderElement(String header) { + this.header = header; } - - // Only create a new Date once per second, max. - if ((systime - currentDate.getTime()) > 1000) { - currentDate.setTime(systime); + + public void addElement(StringBuffer buf, Date date, Request request, + Response response, long time) { + buf.append(wrap(response.getHeader(header))); } - - return currentDate; - } - - - // ------------------------------------------------------ Lifecycle Methods - - - /** - * Add a lifecycle event listener to this component. - * - * @param listener The listener to add - */ - public void addLifecycleListener(LifecycleListener listener) { - - lifecycle.addLifecycleListener(listener); - + + protected class ServletContextElement implements AccessLogElement { + private String attribute; + + public ServletContextElement(String attribute) { + this.attribute = attribute; + } + public void addElement(StringBuffer buf, Date date, Request request, + Response response, long time) { + buf.append(wrap(request.getContext().getServletContext() + .getAttribute(attribute))); + } } - - - /** - * Get the lifecycle listeners associated with this lifecycle. If this - * Lifecycle has no listeners registered, a zero-length array is returned. - */ - public LifecycleListener[] findLifecycleListeners() { - - return lifecycle.findLifecycleListeners(); - + + protected class CookieElement implements AccessLogElement { + private String name; + + public CookieElement(String name) { + this.name = name; + } + public void addElement(StringBuffer 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())); + } + } + } } - - - /** - * Remove a lifecycle event listener from this component. - * - * @param listener The listener to add - */ - public void removeLifecycleListener(LifecycleListener listener) { - - lifecycle.removeLifecycleListener(listener); - + + protected class RequestAttributeElement implements AccessLogElement { + private String attribute; + + public RequestAttributeElement(String attribute) { + this.attribute = attribute; + } + + public void addElement(StringBuffer buf, Date date, Request request, + Response response, long time) { + buf.append(wrap(request.getAttribute(attribute))); + } + } + + protected class SessionAttributeElement implements AccessLogElement { + private String attribute; + + public SessionAttributeElement(String attribute) { + this.attribute = attribute; + } + public void addElement(StringBuffer 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))); + } + } } - - - /** - * Prepare for the beginning of active use of the public methods of this - * component. This method should be called after configure(), - * and before any of the public methods of the component are utilized. - * - * @exception LifecycleException if this component detects a fatal error - * that prevents this component from being used - */ - public void start() throws LifecycleException { - - // Validate and update our current component state - if (started) - throw new LifecycleException - (sm.getString("extendedAccessLogValve.alreadyStarted")); - lifecycle.fireLifecycleEvent(START_EVENT, null); - started = true; - - // Initialize the timeZone, Date formatters, and currentDate - TimeZone tz = TimeZone.getTimeZone("GMT"); - dateFormatter = new SimpleDateFormat("yyyy-MM-dd"); - dateFormatter.setTimeZone(tz); - timeFormatter = new SimpleDateFormat("HH:mm:ss"); - timeFormatter.setTimeZone(tz); - currentDate = new Date(System.currentTimeMillis()); - if (fileDateFormat==null || fileDateFormat.length()==0) - fileDateFormat = "yyyy-MM-dd"; - fileDateFormatter = new SimpleDateFormat(fileDateFormat); - dateStamp = fileDateFormatter.format(currentDate); - timeTakenFormatter = new DecimalFormat("0.000"); - - /* Everybody say ick ... ick */ - try { - InetAddress inetAddress = InetAddress.getLocalHost(); - myIpAddress = inetAddress.getHostAddress(); - myDNSName = inetAddress.getHostName(); - } catch(Throwable e){ - myIpAddress="127.0.0.1"; - myDNSName="localhost"; + + protected class RequestParameterElement implements AccessLogElement { + private 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; + } + return URLEncoder.encode(value); + } + + public void addElement(StringBuffer buf, Date date, Request request, + Response response, long time) { + buf.append(wrap(urlEncode(request.getParameter(parameter)))); } - - open(); - } - - - /** - * Gracefully terminate the active use of the public methods of this - * component. This method should be the last one called on a given - * instance of this component. - * - * @exception LifecycleException if this component detects a fatal error - * that needs to be reported - */ - public void stop() throws LifecycleException { - - // Validate and update our current component state - if (!started) - throw new LifecycleException - (sm.getString("extendedAccessLogValve.notStarted")); - lifecycle.fireLifecycleEvent(STOP_EVENT, null); - started = false; - - close(); - + + protected class PatternTokenizer { + private StringReader sr = null; + private StringBuffer buf = new StringBuffer(); + 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 { + String result = null; + subToken = false; + parameter = false; + + int c = sr.read(); + while (c != -1) { + switch (c) { + case ' ': + result = buf.toString(); + buf = new StringBuffer(); + buf.append((char) c); + return result; + case '-': + result = buf.toString(); + buf = new StringBuffer(); + subToken = true; + return result; + case '(': + result = buf.toString(); + buf = new StringBuffer(); + parameter = true; + return result; + case ')': + result = buf.toString(); + buf = new StringBuffer(); + 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 StringBuffer(); + return result; + } + buf.append((char) c); + c = sr.read(); + } + return null; + } + + public String getWhiteSpaces() throws IOException { + StringBuffer whiteSpaces = new StringBuffer(); + if (buf.length() > 0) { + whiteSpaces.append(buf); + buf = new StringBuffer(); + } + 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 { + StringBuffer remains = new StringBuffer(); + for(int c = sr.read(); c != -1; c = sr.read()) { + remains.append((char) c); + } + return remains.toString(); + } + } + + protected AccessLogElement[] createLogElements() { + if (log.isDebugEnabled()) { + log.debug("decodePattern, pattern =" + pattern); + } + List list = new ArrayList(); + PatternTokenizer tokenizer = new PatternTokenizer(pattern); + try { - /** - * Decode the given pattern. Is public so a pattern may - * allows to be validated. - * @param fields The pattern to decode - * @return null on error. Otherwise array of decoded fields - */ - public FieldInfo[] decodePattern(String fields) { - - if (log.isDebugEnabled()) - log.debug("decodePattern, fields=" + fields); - - LinkedList list = new LinkedList(); + // Ignore leading whitespace. + tokenizer.getWhiteSpaces(); - //Ignore leading whitespace. - int i=0; - for (;i=fields.length()) { - log.info("fields was just empty or whitespace"); + 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 (AccessLogElement[]) list.toArray(new AccessLogElement[0]); + } catch (IOException e) { + log.error("parse error", e); return null; } - - int j; - while(i=fields.length()) { - if (j==i) { - // Special case - end of string - currentFieldInfo.postWhiteSpace = ""; + } 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(); + } else if ("dns".equals(nextToken)) { + return new AccessLogElement() { + public void addElement(StringBuffer buf, Date date, + Request request, Response response, long time) { + String value; + try { + value = InetAddress.getLocalHost().getHostName(); + } catch (Throwable 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("unable to decode with rest of chars starting: " + 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() { + public void addElement(StringBuffer buf, Date date, + Request request, Response response, + long time) { + String query = request.getQueryString(); + if (query != null) { + buf.append(query); + } else { + buf.append('-'); + } + } + }; + } } else { - currentFieldInfo.postWhiteSpace = fields.substring(i); - i=j; + return new AccessLogElement() { + public void addElement(StringBuffer 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 { - currentFieldInfo.postWhiteSpace = fields.substring(i,j); - i=j; } - - list.add(currentFieldInfo); + } else if (tokenizer.hasParameter()) { + String parameter = tokenizer.getParameter(); + if (parameter == null) { + log.error("No closing ) found for in decode"); + return null; + } + return new RequestHeaderElement(parameter); } - - i=0; - FieldInfo[] f = new FieldInfo[list.size()]; - for (Iterator k = list.iterator(); k.hasNext();) - f[i++] = (FieldInfo)k.next(); - - if (log.isDebugEnabled()) - log.debug("finished decoding with length of: " + i); - - return f; - } - - /** - * Decode the cs or sc fields. - * Returns negative on error. - * - * @param fields The pattern to decode - * @param i The string index where we are decoding. - * @param fieldInfo Where to store the results - * @param type The type we are decoding. - * @return -1 on error. Otherwise the new String index. - */ - private int decode(String fields, int i, FieldInfo fieldInfo, short type) { - - if (fields.startsWith("-status",i)) { - fieldInfo.location = FieldInfo.FIELD_STATUS; - i+="-status".length(); - } else if (fields.startsWith("-comment",i)) { - fieldInfo.location = FieldInfo.FIELD_COMMENT; - i+="-comment".length(); - } else if (fields.startsWith("-uri-query",i)) { - fieldInfo.location = FieldInfo.FIELD_URI_QUERY; - i+="-uri-query".length(); - } else if (fields.startsWith("-uri-stem",i)) { - fieldInfo.location = FieldInfo.FIELD_URI_STEM; - i+="-uri-stem".length(); - } else if (fields.startsWith("-uri",i)) { - fieldInfo.location = FieldInfo.FIELD_URI; - i+="-uri".length(); - } else if (fields.startsWith("-method",i)) { - fieldInfo.location = FieldInfo.FIELD_METHOD; - i+="-method".length(); - } else if (fields.startsWith("(",i)) { - fieldInfo.location = FieldInfo.FIELD_HEADER; - i++; /* Move past the ( */ - int j = fields.indexOf(')', i); - if (j==-1) { /* Not found */ + log.error("The next characters couldn't be decoded: " + + 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("No closing ) found for in decode"); - return -1; + return null; } - fieldInfo.value = fields.substring(i,j); - i=j+1; // Move pointer past ) */ - } else { - log.error("The next characters couldn't be decoded: " + fields.substring(i)); - return -1; + return new ResponseHeaderElement(parameter); } - - fieldInfo.type = type; - return i; - - } - - - /** - * Decode app specific log entry. - * - * Special fields are of the form: - * x-C(...) - For cookie - * x-A(...) - Value in servletContext - * x-S(...) - Value in session - * x-R(...) - Value in servletRequest - * @param fields The pattern to decode - * @param i The string index where we are decoding. - * @param fieldInfo Where to store the results - * @return -1 on error. Otherwise the new String index. - */ - private int decodeAppSpecific(String fields, int i, FieldInfo fieldInfo) { - - fieldInfo.type = FieldInfo.DATA_APP_SPECIFIC; - /* Move past 'x-' */ - i+=2; - - if (i>=fields.length()) { - log.error("End of line reached before decoding x- param"); - return -1; + log.error("The next characters couldn't be decoded: " + + tokenizer.getRemains()); + return null; + } + + protected AccessLogElement getProxyElement(PatternTokenizer tokenizer) + throws IOException { + String token = null; + if (tokenizer.hasSubToken()) { + token = tokenizer.getToken(); + return new StringElement("-"); + } else if (tokenizer.hasParameter()) { + tokenizer.getParameter(); + return new StringElement("-"); } - - switch(fields.charAt(i)) { - case 'A': - fieldInfo.xType = FieldInfo.X_APP; - break; - case 'C': - fieldInfo.xType = FieldInfo.X_COOKIE; - break; - case 'R': - fieldInfo.xType = FieldInfo.X_REQUEST; - break; - case 'S': - fieldInfo.xType = FieldInfo.X_SESSION; - break; - case 'H': - fieldInfo.xType = FieldInfo.X_SERVLET_REQUEST; - break; - case 'P': - fieldInfo.xType = FieldInfo.X_PARAMETER; - break; - default: - return -1; + log.error("The next characters couldn't be decoded: " + token); + return null; + } + + protected AccessLogElement getXParameterElement(PatternTokenizer tokenizer) + throws IOException { + if (!tokenizer.hasSubToken()) { + log.error("x param in wrong format. Needs to be 'x-#(...)' read the docs!"); + return null; } - - /* test that next char is a ( */ - if (i+1!=fields.indexOf('(',i)) { + String token = tokenizer.getToken(); + if (!tokenizer.hasParameter()) { log.error("x param in wrong format. Needs to be 'x-#(...)' read the docs!"); - return -1; + return null; } - i+=2; /* Move inside of the () */ - - /* Look for ending ) and return error if not found. */ - int j = fields.indexOf(')',i); - if (j==-1) { - log.error("x param in wrong format. No closing ')'!"); - return -1; + String parameter = tokenizer.getParameter(); + if (parameter == null) { + log.error("No closing ) found for in decode"); + return null; } - - fieldInfo.value = fields.substring(i,j); - - if (fieldInfo.xType == FieldInfo.X_SERVLET_REQUEST) { - if ("authType".equals(fieldInfo.value)){ - fieldInfo.location = FieldInfo.X_LOC_AUTHTYPE; - } else if ("remoteUser".equals(fieldInfo.value)){ - fieldInfo.location = FieldInfo.X_LOC_REMOTEUSER; - } else if ("requestedSessionId".equals(fieldInfo.value)){ - fieldInfo.location = FieldInfo.X_LOC_REQUESTEDSESSIONID; - } else if ("requestedSessionIdFromCookie".equals(fieldInfo.value)){ - fieldInfo.location = FieldInfo.X_LOC_REQUESTEDSESSIONIDFROMCOOKIE; - } else if ("requestedSessionIdValid".equals(fieldInfo.value)){ - fieldInfo.location = FieldInfo.X_LOC_REQUESTEDSESSIONIDVALID; - } else if ("contentLength".equals(fieldInfo.value)){ - fieldInfo.location = FieldInfo.X_LOC_CONTENTLENGTH; - } else if ("characterEncoding".equals(fieldInfo.value)){ - fieldInfo.location = FieldInfo.X_LOC_CHARACTERENCODING; - } else if ("locale".equals(fieldInfo.value)){ - fieldInfo.location = FieldInfo.X_LOC_LOCALE; - } else if ("protocol".equals(fieldInfo.value)){ - fieldInfo.location = FieldInfo.X_LOC_PROTOCOL; - } else if ("scheme".equals(fieldInfo.value)){ - fieldInfo.location = FieldInfo.X_LOC_SCHEME; - } else if ("secure".equals(fieldInfo.value)){ - fieldInfo.location = FieldInfo.X_LOC_SECURE; - } else { - log.error("x param for servlet request, couldn't decode value: " + - fieldInfo.location); - return -1; - } + 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); } - - return j+1; - + log.error("x param for servlet request, couldn't decode value: " + + token); + return null; + } + + protected AccessLogElement getServletRequestElement(String parameter) { + if ("authType".equals(parameter)) { + return new AccessLogElement() { + public void addElement(StringBuffer buf, Date date, + Request request, Response response, long time) { + buf.append(wrap(request.getAuthType())); + } + }; + } else if ("remoteUser".equals(parameter)) { + return new AccessLogElement() { + public void addElement(StringBuffer buf, Date date, + Request request, Response response, long time) { + buf.append(wrap(request.getRemoteUser())); + } + }; + } else if ("requestedSessionId".equals(parameter)) { + return new AccessLogElement() { + public void addElement(StringBuffer buf, Date date, + Request request, Response response, long time) { + buf.append(wrap(request.getRequestedSessionId())); + } + }; + } else if ("requestedSessionIdFromCookie".equals(parameter)) { + return new AccessLogElement() { + public void addElement(StringBuffer buf, Date date, + Request request, Response response, long time) { + buf.append(wrap("" + + request.isRequestedSessionIdFromCookie())); + } + }; + } else if ("requestedSessionIdValid".equals(parameter)) { + return new AccessLogElement() { + public void addElement(StringBuffer buf, Date date, + Request request, Response response, long time) { + buf.append(wrap("" + request.isRequestedSessionIdValid())); + } + }; + } else if ("contentLength".equals(parameter)) { + return new AccessLogElement() { + public void addElement(StringBuffer buf, Date date, + Request request, Response response, long time) { + buf.append(wrap("" + request.getContentLength())); + } + }; + } else if ("characterEncoding".equals(parameter)) { + return new AccessLogElement() { + public void addElement(StringBuffer buf, Date date, + Request request, Response response, long time) { + buf.append(wrap(request.getCharacterEncoding())); + } + }; + } else if ("locale".equals(parameter)) { + return new AccessLogElement() { + public void addElement(StringBuffer buf, Date date, + Request request, Response response, long time) { + buf.append(wrap(request.getLocale())); + } + }; + } else if ("protocol".equals(parameter)) { + return new AccessLogElement() { + public void addElement(StringBuffer buf, Date date, + Request request, Response response, long time) { + buf.append(wrap(request.getProtocol())); + } + }; + } else if ("scheme".equals(parameter)) { + return new AccessLogElement() { + public void addElement(StringBuffer buf, Date date, + Request request, Response response, long time) { + buf.append(request.getScheme()); + } + }; + } else if ("secure".equals(parameter)) { + return new AccessLogElement() { + public void addElement(StringBuffer buf, Date date, + Request request, Response response, long time) { + buf.append(wrap("" + request.isSecure())); + } + }; + } + log.error("x param for servlet request, couldn't decode value: " + + parameter); + return null; } - - -} - -/** - * A simple helper for decoding the pattern. - */ -class FieldInfo { - /* - The goal of the constants listed below is to make the construction of the log - entry as quick as possible via numerci decodings of the methods to call instead - of performing many String comparisons on each logging request. - */ - - /* Where the data is located. */ - static final short DATA_CLIENT = 0; - static final short DATA_SERVER = 1; - static final short DATA_REMOTE = 2; - static final short DATA_CLIENT_TO_SERVER = 3; - static final short DATA_SERVER_TO_CLIENT = 4; - static final short DATA_SERVER_TO_RSERVER = 5; /* Here to honor the spec. */ - static final short DATA_RSERVER_TO_SERVER = 6; /* Here to honor the spec. */ - static final short DATA_APP_SPECIFIC = 7; - static final short DATA_SPECIAL = 8; - - /* The type of special fields. */ - static final short SPECIAL_DATE = 1; - static final short SPECIAL_TIME_TAKEN = 2; - static final short SPECIAL_TIME = 3; - static final short SPECIAL_BYTES = 4; - static final short SPECIAL_CACHED = 5; - - /* Where to pull the data for prefixed values */ - static final short FIELD_IP = 1; - static final short FIELD_DNS = 2; - static final short FIELD_STATUS = 3; - static final short FIELD_COMMENT = 4; - static final short FIELD_METHOD = 5; - static final short FIELD_URI = 6; - static final short FIELD_URI_STEM = 7; - static final short FIELD_URI_QUERY = 8; - static final short FIELD_HEADER = 9; - - - /* Application Specific parameters */ - static final short X_REQUEST = 1; /* For x app specific */ - static final short X_SESSION = 2; /* For x app specific */ - static final short X_COOKIE = 3; /* For x app specific */ - static final short X_APP = 4; /* For x app specific */ - static final short X_SERVLET_REQUEST = 5; /* For x app specific */ - static final short X_PARAMETER = 6; /* For x app specific */ - - static final short X_LOC_AUTHTYPE = 1; - static final short X_LOC_REMOTEUSER = 2; - static final short X_LOC_REQUESTEDSESSIONID = 3; - static final short X_LOC_REQUESTEDSESSIONIDFROMCOOKIE = 4; - static final short X_LOC_REQUESTEDSESSIONIDVALID = 5; - static final short X_LOC_CONTENTLENGTH = 6; - static final short X_LOC_CHARACTERENCODING = 7; - static final short X_LOC_LOCALE = 8; - static final short X_LOC_PROTOCOL = 9; - static final short X_LOC_SCHEME = 10; - static final short X_LOC_SECURE = 11; - - - - /** The field type */ - short type; - - /** Where to pull the data from? Icky variable name. */ - short location; - - /** The x- specific place to pull the data from. */ - short xType; - - /** The field value if needed. Needed for headers and app specific. */ - String value; - - /** Any white space after this field? Put it here. */ - String postWhiteSpace = null; - + }