tomcat-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Xie Xiaodong <xxd82...@gmail.com>
Subject Re: Access Log /Filter/?
Date Tue, 02 Mar 2010 20:58:30 GMT
/*
 * 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.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.TimeZone;

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 javax.servlet.http.HttpSession;

import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.res.StringManager;


/**
 * <p>Implementation of the <b>Filter</b> interface that generates a web
server
 * access log with the detailed line contents matching a configurable
pattern.
 * The syntax of the available patterns is similar to that supported by the
 * Apache <code>mod_log_config</code> module.  As an additional feature,
 * automatic rollover of log files when the date changes is also
supported.</p>
 *
 * <p>Patterns for the logged message may include constant text or any of
the
 * following replacement strings, for which the corresponding information
 * from the specified Response is substituted:</p>
 * <ul>
 * <li><b>%a</b> - Remote IP address
 * <li><b>%A</b> - Local IP address
 * <li><b>%b</b> - Bytes sent, excluding HTTP headers, or '-' if no bytes
 *     were sent
 * <li><b>%B</b> - Bytes sent, excluding HTTP headers
 * <li><b>%h</b> - Remote host name
 * <li><b>%H</b> - Request protocol
 * <li><b>%l</b> - Remote logical username from identd (always returns '-')
 * <li><b>%m</b> - Request method
 * <li><b>%p</b> - Local port
 * <li><b>%q</b> - Query string (prepended with a '?' if it exists,
otherwise
 *     an empty string
 * <li><b>%r</b> - First line of the request
 * <li><b>%s</b> - HTTP status code of the response
 * <li><b>%S</b> - User session ID
 * <li><b>%t</b> - Date and time, in Common Log Format format
 * <li><b>%u</b> - Remote user that was authenticated
 * <li><b>%U</b> - Requested URL path
 * <li><b>%v</b> - Local server name
 * <li><b>%D</b> - Time taken to process the request, in millis
 * <li><b>%T</b> - Time taken to process the request, in seconds
 * <li><b>%I</b> - current Request thread name (can compare later with
stacktraces)
 * </ul>
 * <p>In addition, the caller can specify one of the following aliases for
 * commonly utilized patterns:</p>
 * <ul>
 * <li><b>common</b> - <code>%h %l %u %t "%r" %s %b</code>
 * <li><b>combined</b> -
 *   <code>%h %l %u %t "%r" %s %b "%{Referer}i" "%{User-Agent}i"</code>
 * </ul>
 *
 * <p>
 * There is also support to write information from the cookie, incoming
 * header, the Session or something else in the ServletRequest.<br>
 * It is modeled after the apache syntax:
 * <ul>
 * <li><code>%{xxx}i</code> for incoming headers
 * <li><code>%{xxx}o</code> for outgoing response headers
 * <li><code>%{xxx}c</code> for a specific cookie
 * <li><code>%{xxx}r</code> xxx is an attribute in the ServletRequest
 * <li><code>%{xxx}s</code> xxx is an attribute in the HttpSession
 * </ul>
 * </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>
 *
 * @author Craig R. McClanahan
 * @author Jason Brittain
 * @author Remy Maucherat
 * @author Takayuki Kaneko
 * @author Peter Rossbach
 *
 */

public class AccessLogFilter
    extends FilterBase {

    private static Log log = LogFactory.getLog(AccessLogFilter.class);

    // ----------------------------------------------------- Instance
Variables


    /**
     * The as-of date for the currently open log file, or a zero-length
     * string if there is no open log file.
     */
    private volatile String dateStamp = "";


    /**
     * The directory in which log files are created.
     */
    private String directory = "logs";


    /**
     * The set of month abbreviations for log messages.
     */
    protected static final String months[] =
    { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };


    /**
     * enabled this component
     */
    protected boolean enabled = true;

    /**
     * The pattern used to format our access log lines.
     */
    protected String pattern = null;


    /**
     * The prefix that is added to log file filenames.
     */
    protected String prefix = "access_log.";


    /**
     * Should we rotate our log file? Default is true (like old behavior)
     */
    protected boolean rotatable = true;


    /**
     * Buffered logging.
     */
    private boolean buffered = true;


    /**
     * The string manager for this package.
     */
    protected StringManager sm =
        StringManager.getManager(Constants.Package);


    /**
     * Has this component been started yet?
     */
    protected boolean started = false;


    /**
     * The suffix that is added to log file filenames.
     */
    protected String suffix = "";


    /**
     * The PrintWriter to which we are currently logging, if any.
     */
    protected PrintWriter writer = null;


    /**
     * A date formatter to format a Date into a date in the format
     * "yyyy-MM-dd".
     */
    protected SimpleDateFormat fileDateFormatter = null;


    /**
     * The system timezone.
     */
    private TimeZone timezone = null;


    /**
     * The time zone offset relative to GMT in text form when daylight
saving
     * is not in operation.
     */
    private String timeZoneNoDST = null;


    /**
     * The time zone offset relative to GMT in text form when daylight
saving
     * is in operation.
     */
    private String timeZoneDST = null;


    /**
     * The current log file we are writing to. Helpful when checkExists
     * is true.
     */
    protected File currentLogFile = null;
    private static class AccessDateStruct {
        private Date currentDate = new Date();
        private String currentDateString = null;
        private SimpleDateFormat dayFormatter = new SimpleDateFormat("dd");
        private SimpleDateFormat monthFormatter = new
SimpleDateFormat("MM");
        private SimpleDateFormat yearFormatter = new
SimpleDateFormat("yyyy");
        private SimpleDateFormat timeFormatter = new
SimpleDateFormat("HH:mm:ss");
        public AccessDateStruct() {
            TimeZone tz = TimeZone.getDefault();
            dayFormatter.setTimeZone(tz);
            monthFormatter.setTimeZone(tz);
            yearFormatter.setTimeZone(tz);
            timeFormatter.setTimeZone(tz);
        }
    }

    /**
     * The system time when we last updated the Date that this filter
     * uses for log lines.
     */
    private static final ThreadLocal<AccessDateStruct> currentDateStruct =
        new ThreadLocal<AccessDateStruct>() {
        protected AccessDateStruct initialValue() {
            return new AccessDateStruct();
        }
    };
    /**
     * Resolve hosts.
     */
    private boolean resolveHosts = false;


    /**
     * Instant when the log daily rotation was last checked.
     */
    private volatile 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.
     */
    protected String condition = null;


    /**
     * Date format to place in log file name. Use at your own risk!
     */
    protected String fileDateFormat = null;

    /**
     * Array of AccessLogElement, they will be used to make log message.
     */
    protected AccessLogElement[] logElements = null;

    // -------------------------------------------------------------
Properties

    /**
     * @return Returns the enabled.
     */
    public boolean getEnabled() {
        return enabled;
    }

    /**
     * @param enabled
     *            The enabled to set.
     */
    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    /**
     * 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 the format pattern.
     */
    public String getPattern() {
        return (this.pattern);
    }


    /**
     * Set the format pattern, first translating any recognized alias.
     *
     * @param pattern The new pattern
     */
    public void setPattern(String pattern) {
        if (pattern == null)
            pattern = "";
        if (pattern.equals(Constants.AccessLog.COMMON_ALIAS))
            pattern = Constants.AccessLog.COMMON_PATTERN;
        if (pattern.equals(Constants.AccessLog.COMBINED_ALIAS))
            pattern = Constants.AccessLog.COMBINED_PATTERN;
        this.pattern = pattern;
        logElements = createLogElements();
    }


    /**
     * 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() {
        return (prefix);
    }


    /**
     * Set the log file prefix.
     *
     * @param prefix The new log file prefix
     */
    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }


    /**
     * Should we rotate the logs
     */
    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;
    }


    /**
     * Is the logging buffered
     */
    public boolean isBuffered() {
        return buffered;
    }


    /**
     * Set the value if the logging should be buffered
     *
     * @param buffered true if buffered.
     */
    public void setBuffered(boolean buffered) {
        this.buffered = buffered;
    }


    /**
     * 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;
    }


    /**
     * Set the resolve hosts flag.
     *
     * @param resolveHosts The new resolve hosts value
     */
    public void setResolveHosts(boolean resolveHosts) {
        this.resolveHosts = resolveHosts;
    }


    /**
     * Get the value of the resolve hosts flag.
     */
    public boolean isResolveHosts() {
        return resolveHosts;
    }


    /**
     * 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;
    }

    /**
     *  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

    /**
     * Execute a periodic task, such as reloading, etc. This method will be
     * invoked inside the classloading context of this container. Unexpected
     * throwables will be caught and logged.
     */
    public void backgroundProcess() {
        if (started && getEnabled() && writer != null && buffered)
{
            writer.flush();
        }
    }

/**
 * Log a message summarizing the specified request and response, according
to the
 * format specified by the <code>pattern</code> 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
 */
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {

log.info("In AccessLogFilter doFilter. ");
 if (!isHttpServlet(request, response)) {
            chain.doFilter(request, response);
            return;
        }
 HttpServletRequest httpRequest = ((HttpServletRequest) request);

HttpServletResponse httpResponse = ((HttpServletResponse) response);

if (started && getEnabled()) {
// Pass this request on to the next filter in the filterChain
long t1 = System.currentTimeMillis();

chain.doFilter(request, response);

long t2 = System.currentTimeMillis();
long time = t2 - t1;

if (logElements == null
|| condition != null
&& null != httpRequest.getAttribute(
condition)) {
return;
}

log.debug("logElements: " + logElements.length + " ; "
+ Arrays.toString(logElements));

Date date = getDate();
StringBuffer result = new StringBuffer();

for (int i = 0; i < logElements.length; i++) {
logElements[i].addElement(result, date, httpRequest,
httpResponse, time);
}

log(result.toString());
} else
chain.doFilter(request, response);
}

@Override
public void init(FilterConfig filterConfig) throws ServletException {
super.init(filterConfig);
start();
}

@Override
public void destroy() {
try {
stop();
} catch (ServletException e) {
log.error(e);
throw new RuntimeException(e);
}
}


    /**
     * 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 */
            dateStamp = fileDateFormatter.format(
                    new Date(System.currentTimeMillis()));

            open();
            return true;
        } else {
            return false;
        }

    }

    protected Log getLogger() {
        return log;
    }

    // -------------------------------------------------------- Private
Methods


    /**
     * Close the currently open log file (if any)
     */
    private synchronized void close() {
        if (writer == null) {
            return;
        }
        writer.flush();
        writer.close();
        writer = null;
        dateStamp = "";
        currentLogFile = null;
    }


    /**
     * 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
     */
    public void log(String message) {
        if (rotatable) {
            // Only do a logfile switch check once a second, max.
            long systime = System.currentTimeMillis();
            if ((systime - rotationLastChecked) > 1000) {
                synchronized(this) {
                    if ((systime - rotationLastChecked) > 1000) {
                        rotationLastChecked = systime;

                        String tsDate;
                        // Check for a change of date
                        tsDate = fileDateFormatter.format(new
Date(systime));

                        // If the date has changed, switch log files
                        if (!dateStamp.equals(tsDate)) {
                            close();
                            dateStamp = tsDate;
                            open();
                        }
                    }
                }
            }
        }

        /* 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 */
                    dateStamp = fileDateFormatter.format(
                            new Date(System.currentTimeMillis()));

                    open();
                }
            }
        }

        // Log this message
        synchronized(this) {
            if (writer != null) {
                writer.println(message);
                if (!buffered) {
                    writer.flush();
                }
            }
        }

    }


    /**
     * Return the month abbreviation for the specified month, which must
     * be a two-digit String.
     *
     * @param month Month number ("01" .. "12").
     */
    private static String lookup(String month) {
        int index;
        try {
            index = Integer.parseInt(month) - 1;
        } catch (Throwable t) {
            index = 0;  // Can not happen, in theory
        }
        return (months[index]);
    }


    /**
     * Open the new log file for the date specified by
<code>dateStamp</code>.
     */
    protected 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;
            }
            writer = new PrintWriter(new BufferedWriter(new FileWriter(
                    pathname, true), 128000), false);

            currentLogFile = new File(pathname);
        } catch (IOException e) {
            writer = null;
            currentLogFile = null;
        }
    }

    /**
     * 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.
     *
     * @return Date
     */
    private Date getDate() {
        // Only create a new Date once per second, max.
        long systime = System.currentTimeMillis();
        AccessDateStruct struct = currentDateStruct.get();
        if ((systime - struct.currentDate.getTime()) > 1000) {
            struct.currentDate.setTime(systime);
            struct.currentDateString = null;
        }
        return struct.currentDate;
    }


    private String getTimeZone(Date date) {
        if (timezone.inDaylightTime(date)) {
            return timeZoneDST;
        } else {
            return timeZoneNoDST;
        }
    }


    private String calculateTimeZoneOffset(long offset) {
        StringBuffer tz = new StringBuffer();
        if ((offset < 0)) {
            tz.append("-");
            offset = -offset;
        } else {
            tz.append("+");
        }

        long hourOffset = offset / (1000 * 60 * 60);
        long minuteOffset = (offset / (1000 * 60)) % 60;

        if (hourOffset < 10)
            tz.append("0");
        tz.append(hourOffset);

        if (minuteOffset < 10)
            tz.append("0");
        tz.append(minuteOffset);

        return tz.toString();
    }

    /**
     * Prepare for the beginning of active use of the public methods of this
     * component.  This method should be called after
<code>configure()</code>,
     * and before any of the public methods of the component are utilized.
     *
     * @exception ServletException if this component detects a fatal error
     *  that prevents this component from being used
     */
    public void start() throws ServletException {

        // Validate and update our current component state
        if (started)
            throw new ServletException(sm
                    .getString("accessLogFilter.alreadyStarted"));
        started = true;

        // Initialize the timeZone, Date formatters, and currentDate
        timezone = TimeZone.getDefault();
        timeZoneNoDST = calculateTimeZoneOffset(timezone.getRawOffset());
        int offset = timezone.getDSTSavings();
        timeZoneDST = calculateTimeZoneOffset(timezone.getRawOffset() +
offset);

        if (fileDateFormat == null || fileDateFormat.length() == 0)
            fileDateFormat = "yyyy-MM-dd";
        fileDateFormatter = new SimpleDateFormat(fileDateFormat);
        fileDateFormatter.setTimeZone(timezone);
        dateStamp =
fileDateFormatter.format(currentDateStruct.get().currentDate);
        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 ServletException if this component detects a fatal error
     *  that needs to be reported
     */
    public void stop() throws ServletException {

        // Validate and update our current component state
        if (!started)
            throw new ServletException(sm
                    .getString("accessLogFilter.notStarted"));
        started = false;

        close();
    }

    /**
     * AccessLogElement writes the partial message into the buffer.
     */
    protected interface AccessLogElement {
        public void addElement(StringBuffer buf, Date date,
HttpServletRequest request,
         HttpServletResponse response, long time);

    }

    /**
     * write thread name - %I
     */
    protected static class ThreadNameElement implements AccessLogElement {
        public void addElement(StringBuffer buf, Date date,
HttpServletRequest request, HttpServletResponse response,
long time) {
            // I found that the workerThreadName is set using current
thread's
            // name, and I think that the thread executing event() or
service() should be
            // the same thread as this one, so I modified this method like
this.
            buf.append(Thread.currentThread().getName());
        }
    }

    /**
     * write local IP address - %A
     */
    protected static class LocalAddrElement implements AccessLogElement {

        private static final String LOCAL_ADDR_VALUE;

        static {
            String init;
            try {
                init = InetAddress.getLocalHost().getHostAddress();
            } catch (Throwable e) {
                init = "127.0.0.1";
            }
            LOCAL_ADDR_VALUE = init;
        }

        public void addElement(StringBuffer buf, Date date,
HttpServletRequest request, HttpServletResponse response,
long time) {
            buf.append(LOCAL_ADDR_VALUE);
        }
    }

    /**
     * write remote IP address - %a
     */
    protected static class RemoteAddrElement implements AccessLogElement {
        public void addElement(StringBuffer buf, Date date,
HttpServletRequest request, HttpServletResponse response,
long time) {
            buf.append(request.getRemoteAddr());
        }
    }

    /**
     * write remote host name - %h
     */
    protected static class HostElement implements AccessLogElement {
        public void addElement(StringBuffer buf, Date date,
HttpServletRequest request, HttpServletResponse response,
long time) {
            buf.append(request.getRemoteHost());
        }
    }

    /**
     * write remote logical username from identd (always returns '-') - %l
     */
    protected static class LogicalUserNameElement implements
AccessLogElement {
        public void addElement(StringBuffer buf, Date date,
HttpServletRequest request, HttpServletResponse response,
long time) {
            buf.append('-');
        }
    }

    /**
     * write request protocol - %H
     */
    protected static class ProtocolElement implements AccessLogElement {
        public void addElement(StringBuffer buf, Date date,
HttpServletRequest request, HttpServletResponse response,
long time) {
            buf.append(request.getProtocol());
        }
    }

    /**
     * write remote user that was authenticated (if any), else '-' - %u
     */
    protected static class UserElement implements AccessLogElement {
        public void addElement(StringBuffer buf, Date date,
HttpServletRequest request, HttpServletResponse response,
long time) {
            if (request != null) {
                String value = request.getRemoteUser();
                if (value != null) {
                    buf.append(value);
                } else {
                    buf.append('-');
                }
            } else {
                buf.append('-');
            }
        }
    }

    /**
     * write date and time, in Common Log Format - %t
     */
    protected class DateAndTimeElement implements AccessLogElement {

        public void addElement(StringBuffer buf, Date date,
HttpServletRequest request, HttpServletResponse response,
long time) {
            AccessDateStruct struct = currentDateStruct.get();
            if (struct.currentDateString == null) {
                StringBuffer current = new StringBuffer(32);
                current.append('[');
                current.append(struct.dayFormatter.format(date));
                current.append('/');
                current.append(lookup(struct.monthFormatter.format(date)));
                current.append('/');
                current.append(struct.yearFormatter.format(date));
                current.append(':');
                current.append(struct.timeFormatter.format(date));
                current.append(' ');
                current.append(getTimeZone(date));
                current.append(']');
                struct.currentDateString = current.toString();
            }
            buf.append(struct.currentDateString);
        }
    }

    /**
     * write first line of the request (method and request URI) - %r
     */
    protected static class RequestElement implements AccessLogElement {
        public void addElement(StringBuffer buf, Date date,
HttpServletRequest request, HttpServletResponse response,
long time) {
            if (request != null) {
                buf.append(request.getMethod());
                buf.append(' ');
                buf.append(request.getRequestURI());
                if (request.getQueryString() != null) {
                    buf.append('?');
                    buf.append(request.getQueryString());
                }
                buf.append(' ');
                buf.append(request.getProtocol());
            } else {
                buf.append("- - ");
            }
        }
    }

    /**
     * write HTTP status code of the response - %s
     */
    protected static class HttpStatusCodeElement implements AccessLogElement
{
        public void addElement(StringBuffer buf, Date date,
HttpServletRequest request, HttpServletResponse response,
long time) {
            if (response != null) {
                buf.append(response.getStatus());
            } else {
                buf.append('-');
            }
        }
    }

    /**
     * write local port on which this request was received - %p
     */
    protected static class LocalPortElement implements AccessLogElement {
        public void addElement(StringBuffer buf, Date date,
HttpServletRequest request, HttpServletResponse response,
long time) {
            buf.append(request.getServerPort());
        }
    }

    /**
     * write bytes sent, excluding HTTP headers - %b, %B
     */
    protected static class ByteSentElement implements AccessLogElement {
        private boolean conversion;

        /**
         * if conversion is true, write '-' instead of 0 - %b
         */
        public ByteSentElement(boolean conversion) {
            this.conversion = conversion;
        }

        public void addElement(StringBuffer buf, Date date,
HttpServletRequest request, HttpServletResponse response,
long time) {
            long length = 0;
            if (response.getHeader("Content-Length") != null
                    && !"".equals(response.getHeader("Content-Length"))) {
                length =
Long.parseLong(response.getHeader("Content-Length"));
            }
            if (length <= 0 && response.getHeader("content-length") != null
                    && !"".equals(response.getHeader("content-length"))) {
                length =
Long.parseLong(response.getHeader("content-length"));
            }
if (length <= 0 && conversion) {
buf.append('-');
} else {
buf.append(length);
}
        }
    }

    /**
     * write request method (GET, POST, etc.) - %m
     */
    protected static class MethodElement implements AccessLogElement {
        public void addElement(StringBuffer buf, Date date,
HttpServletRequest request, HttpServletResponse response,
long time) {
            if (request != null) {
                buf.append(request.getMethod());
            }
        }
    }

    /**
     * write time taken to process the request - %D, %T
     */
    protected static class ElapsedTimeElement implements AccessLogElement {
        private boolean millis;

        /**
         * if millis is true, write time in millis - %D
         * if millis is false, write time in seconds - %T
         */
        public ElapsedTimeElement(boolean millis) {
            this.millis = millis;
        }

        public void addElement(StringBuffer buf, Date date,
HttpServletRequest request, HttpServletResponse response,
long time) {
            if (millis) {
                buf.append(time);
            } else {
                // second
                buf.append(time / 1000);
                buf.append('.');
                int remains = (int) (time % 1000);
                buf.append(remains / 100);
                remains = remains % 100;
                buf.append(remains / 10);
                buf.append(remains % 10);
            }
        }
    }

    /**
     * write Query string (prepended with a '?' if it exists) - %q
     */
    protected static class QueryElement implements AccessLogElement {
        public void addElement(StringBuffer buf, Date date,
HttpServletRequest request, HttpServletResponse response,
long time) {
            String query = null;
            if (request != null)
                query = request.getQueryString();
            if (query != null) {
                buf.append('?');
                buf.append(query);
            }
        }
    }

    /**
     * write user session ID - %S
     */
    protected static class SessionIdElement implements AccessLogElement {
        public void addElement(StringBuffer buf, Date date,
HttpServletRequest request, HttpServletResponse response,
long time) {
            if (request != null) {
                if (request.getSession(false) != null) {
                    buf.append(request.getSession(false).getId());
                } else {
                    buf.append('-');
                }
            } else {
                buf.append('-');
            }
        }
    }

    /**
     * write requested URL path - %U
     */
    protected static class RequestURIElement implements AccessLogElement {
        public void addElement(StringBuffer buf, Date date,
HttpServletRequest request, HttpServletResponse response,
long time) {
            if (request != null) {
                buf.append(request.getRequestURI());
            } else {
                buf.append('-');
            }
        }
    }

    /**
     * write local server name - %v
     */
    protected static class LocalServerNameElement implements
AccessLogElement {
        public void addElement(StringBuffer buf, Date date,
HttpServletRequest request, HttpServletResponse response,
long time) {
            buf.append(request.getServerName());
        }
    }

    /**
     * write any string
     */
    protected static class StringElement implements AccessLogElement {
        private String str;

        public StringElement(String str) {
            this.str = str;
        }

        public void addElement(StringBuffer buf, Date date,
HttpServletRequest request, HttpServletResponse response,
long time) {
            buf.append(str);
        }
    }

    /**
     * write incoming headers - %{xxx}i
     */
    protected static class HeaderElement implements AccessLogElement {
        private String header;

        public HeaderElement(String header) {
            this.header = header;
        }

        public void addElement(StringBuffer buf, Date date,
HttpServletRequest request, HttpServletResponse response,
long time) {
            String value = request.getHeader(header);
            if (value == null) {
                buf.append('-');
            } else {
                buf.append(value);
            }
        }
    }

    /**
     * write a specific cookie - %{xxx}c
     */
    protected static class CookieElement implements AccessLogElement {
        private String header;

        public CookieElement(String header) {
            this.header = header;
        }

        public void addElement(StringBuffer buf, Date date,
HttpServletRequest request, HttpServletResponse response,
long time) {
            String value = "-";
            Cookie[] c = request.getCookies();
            if (c != null) {
                for (int i = 0; i < c.length; i++) {
                    if (header.equals(c[i].getName())) {
                        value = c[i].getValue();
                        break;
                    }
                }
            }
            buf.append(value);
        }
    }

    /**
     * write a specific response header - %{xxx}o
     */
    protected static class ResponseHeaderElement implements AccessLogElement
{
        private String header;

        public ResponseHeaderElement(String header) {
            this.header = header;
        }

        public void addElement(StringBuffer buf, Date date,
HttpServletRequest request, HttpServletResponse response,
long time) {
           if (null != response) {
                Iterator<String> iter =
response.getHeaders(header).iterator();
                boolean first = true;
                while (iter.hasNext()) {
                    if (!first) {
                        buf.append(",");
                    }
                    buf.append(iter.next());
                }
                return ;
            }
            buf.append("-");
        }
    }

    /**
     * write an attribute in the ServletRequest - %{xxx}r
     */
    protected static class RequestAttributeElement implements
AccessLogElement {
        private String header;

        public RequestAttributeElement(String header) {
            this.header = header;
        }

        public void addElement(StringBuffer buf, Date date,
HttpServletRequest request, HttpServletResponse response,
long time) {
            Object value = null;
            if (request != null) {
                value = request.getAttribute(header);
            } else {
                value = "??";
            }
            if (value != null) {
                if (value instanceof String) {
                    buf.append((String) value);
                } else {
                    buf.append(value.toString());
                }
            } else {
                buf.append('-');
            }
        }
    }

    /**
     * write an attribute in the HttpSession - %{xxx}s
     */
    protected static class SessionAttributeElement implements
AccessLogElement {
        private String header;

        public SessionAttributeElement(String header) {
            this.header = header;
        }

        public void addElement(StringBuffer buf, Date date,
HttpServletRequest request, HttpServletResponse response,
long time) {
            Object value = null;
            if (null != request) {
                HttpSession sess = request.getSession(false);
                if (null != sess)
                    value = sess.getAttribute(header);
            } else {
                value = "??";
            }
            if (value != null) {
                if (value instanceof String) {
                    buf.append((String) value);
                } else {
                    buf.append(value.toString());
                }
            } else {
                buf.append('-');
            }
        }
    }




    /**
     * parse pattern string and create the array of AccessLogElement
     */
    protected AccessLogElement[] createLogElements() {
        List<AccessLogElement> list = new ArrayList<AccessLogElement>();
        boolean replace = false;
        StringBuffer buf = new StringBuffer();
        for (int i = 0; i < pattern.length(); i++) {
            char ch = pattern.charAt(i);
            if (replace) {
                /*
                 * For code that processes {, the behavior will be ... if I
do
                 * not enounter a closing } - then I ignore the {
                 */
                if ('{' == ch) {
                    StringBuffer name = new StringBuffer();
                    int j = i + 1;
                    for (; j < pattern.length() && '}' != pattern.charAt(j);
j++) {
                        name.append(pattern.charAt(j));
                    }
                    if (j + 1 < pattern.length()) {
                        /* the +1 was to account for } which we increment
now */
                        j++;
                        list.add(createAccessLogElement(name.toString(),
                                pattern.charAt(j)));
                        i = j; /* Since we walked more than one character */
                    } else {
                        // D'oh - end of string - pretend we never did this
                        // and do processing the "old way"
                        list.add(createAccessLogElement(ch));
                    }
                } else {
                    list.add(createAccessLogElement(ch));
                }
                replace = false;
            } else if (ch == '%') {
                replace = true;
                list.add(new StringElement(buf.toString()));
                buf = new StringBuffer();
            } else {
                buf.append(ch);
            }
        }
        if (buf.length() > 0) {
            list.add(new StringElement(buf.toString()));
        }
        return list.toArray(new AccessLogElement[0]);
    }

    /**
     * create an AccessLogElement implementation which needs header string
     */
    private AccessLogElement createAccessLogElement(String header, char
pattern) {
        switch (pattern) {
        case 'i':
            return new HeaderElement(header);
        case 'c':
            return new CookieElement(header);
        case 'o':
            return new ResponseHeaderElement(header);
        case 'r':
            return new RequestAttributeElement(header);
        case 's':
            return new SessionAttributeElement(header);
        default:
            return new StringElement("???");
        }
    }

    /**
     * create an AccessLogElement implementation
     */
    private AccessLogElement createAccessLogElement(char pattern) {
        switch (pattern) {
        case 'a':
            return new RemoteAddrElement();
        case 'A':
            return new LocalAddrElement();
        case 'b':
            return new ByteSentElement(true);
        case 'B':
            return new ByteSentElement(false);
        case 'D':
            return new ElapsedTimeElement(true);
        case 'h':
            return new HostElement();
        case 'H':
            return new ProtocolElement();
        case 'l':
            return new LogicalUserNameElement();
        case 'm':
            return new MethodElement();
        case 'p':
            return new LocalPortElement();
        case 'q':
            return new QueryElement();
        case 'r':
            return new RequestElement();
        case 's':
            return new HttpStatusCodeElement();
        case 'S':
            return new SessionIdElement();
        case 't':
            return new DateAndTimeElement();
        case 'T':
            return new ElapsedTimeElement(false);
        case 'u':
            return new UserElement();
        case 'U':
            return new RequestURIElement();
        case 'v':
            return new LocalServerNameElement();
        case 'I':
            return new ThreadNameElement();
        default:
            return new StringElement("???" + pattern + "???");
        }
    }
}


On Tue, Mar 2, 2010 at 9:45 PM, Christopher Schultz <
chris@christopherschultz.net> wrote:

> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> Xie (or is it Xiaodong?),
>
> On 3/2/2010 2:57 PM, Xie Xiaodong wrote:
> > I submitted some code but not including the AccessLogFilter since I had
> > something question about the implementation of it. Here I give you the
> > version I wrote last summer. Hope you could check it and submit a patch.
>
> Your attachment must have been stripped from the list. Please consider
> re-posting inline.
>
> - -chris
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.10 (MingW32)
> Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
>
> iEYEARECAAYFAkuNeOkACgkQ9CaO5/Lv0PBqXQCfUCrW3JCxOfU7tL1mnuJjwumC
> oRQAnRKH+Xizgh5iimVvQjq8rGZ1wTte
> =tCKO
> -----END PGP SIGNATURE-----
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
> For additional commands, e-mail: users-help@tomcat.apache.org
>
>


-- 
Sincerely yours and Best Regards,
Xie Xiaodong

Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message