Save This Page
Home » apache-tomcat-6.0.26-src » org.apache » catalina » valves » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    *
   11    * Unless required by applicable law or agreed to in writing, software
   12    * distributed under the License is distributed on an "AS IS" BASIS,
   13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    * See the License for the specific language governing permissions and
   15    * limitations under the License.
   16    */
   17   
   18   
   19   package org.apache.catalina.valves;
   20   
   21   
   22   import java.io.BufferedWriter;
   23   import java.io.File;
   24   import java.io.FileWriter;
   25   import java.io.IOException;
   26   import java.io.PrintWriter;
   27   import java.net.InetAddress;
   28   import java.text.SimpleDateFormat;
   29   import java.util.ArrayList;
   30   import java.util.Date;
   31   import java.util.List;
   32   import java.util.TimeZone;
   33   
   34   import javax.servlet.ServletException;
   35   import javax.servlet.http.Cookie;
   36   import javax.servlet.http.HttpSession;
   37   
   38   import org.apache.catalina.Lifecycle;
   39   import org.apache.catalina.LifecycleException;
   40   import org.apache.catalina.LifecycleListener;
   41   import org.apache.catalina.connector.Request;
   42   import org.apache.catalina.connector.Response;
   43   import org.apache.catalina.util.LifecycleSupport;
   44   import org.apache.catalina.util.StringManager;
   45   import org.apache.coyote.RequestInfo;
   46   import org.apache.juli.logging.Log;
   47   import org.apache.juli.logging.LogFactory;
   48   
   49   
   50   /**
   51    * <p>Implementation of the <b>Valve</b> interface that generates a web server
   52    * access log with the detailed line contents matching a configurable pattern.
   53    * The syntax of the available patterns is similar to that supported by the
   54    * Apache <code>mod_log_config</code> module.  As an additional feature,
   55    * automatic rollover of log files when the date changes is also supported.</p>
   56    *
   57    * <p>Patterns for the logged message may include constant text or any of the
   58    * following replacement strings, for which the corresponding information
   59    * from the specified Response is substituted:</p>
   60    * <ul>
   61    * <li><b>%a</b> - Remote IP address
   62    * <li><b>%A</b> - Local IP address
   63    * <li><b>%b</b> - Bytes sent, excluding HTTP headers, or '-' if no bytes
   64    *     were sent
   65    * <li><b>%B</b> - Bytes sent, excluding HTTP headers
   66    * <li><b>%h</b> - Remote host name
   67    * <li><b>%H</b> - Request protocol
   68    * <li><b>%l</b> - Remote logical username from identd (always returns '-')
   69    * <li><b>%m</b> - Request method
   70    * <li><b>%p</b> - Local port
   71    * <li><b>%q</b> - Query string (prepended with a '?' if it exists, otherwise
   72    *     an empty string
   73    * <li><b>%r</b> - First line of the request
   74    * <li><b>%s</b> - HTTP status code of the response
   75    * <li><b>%S</b> - User session ID
   76    * <li><b>%t</b> - Date and time, in Common Log Format format
   77    * <li><b>%u</b> - Remote user that was authenticated
   78    * <li><b>%U</b> - Requested URL path
   79    * <li><b>%v</b> - Local server name
   80    * <li><b>%D</b> - Time taken to process the request, in millis
   81    * <li><b>%T</b> - Time taken to process the request, in seconds
   82    * <li><b>%I</b> - current Request thread name (can compare later with stacktraces)
   83    * </ul>
   84    * <p>In addition, the caller can specify one of the following aliases for
   85    * commonly utilized patterns:</p>
   86    * <ul>
   87    * <li><b>common</b> - <code>%h %l %u %t "%r" %s %b</code>
   88    * <li><b>combined</b> -
   89    *   <code>%h %l %u %t "%r" %s %b "%{Referer}i" "%{User-Agent}i"</code>
   90    * </ul>
   91    *
   92    * <p>
   93    * There is also support to write information from the cookie, incoming
   94    * header, the Session or something else in the ServletRequest.<br>
   95    * It is modeled after the apache syntax:
   96    * <ul>
   97    * <li><code>%{xxx}i</code> for incoming headers
   98    * <li><code>%{xxx}o</code> for outgoing response headers
   99    * <li><code>%{xxx}c</code> for a specific cookie
  100    * <li><code>%{xxx}r</code> xxx is an attribute in the ServletRequest
  101    * <li><code>%{xxx}s</code> xxx is an attribute in the HttpSession
  102    * </ul>
  103    * </p>
  104    *
  105    * <p>
  106    * Conditional logging is also supported. This can be done with the
  107    * <code>condition</code> property.
  108    * If the value returned from ServletRequest.getAttribute(condition)
  109    * yields a non-null value. The logging will be skipped.
  110    * </p>
  111    *
  112    * @author Craig R. McClanahan
  113    * @author Jason Brittain
  114    * @author Remy Maucherat
  115    * @author Takayuki Kaneko
  116    * @author Peter Rossbach
  117    * 
  118    * @version $Revision: 832123 $ $Date: 2009-11-02 22:57:21 +0100 (Mon, 02 Nov 2009) $
  119    */
  120   
  121   public class AccessLogValve
  122       extends ValveBase
  123       implements Lifecycle {
  124   
  125       private static Log log = LogFactory.getLog(AccessLogValve.class);
  126   
  127       // ----------------------------------------------------- Instance Variables
  128   
  129   
  130       /**
  131        * The as-of date for the currently open log file, or a zero-length
  132        * string if there is no open log file.
  133        */
  134       private volatile String dateStamp = "";
  135   
  136   
  137       /**
  138        * The directory in which log files are created.
  139        */
  140       private String directory = "logs";
  141   
  142   
  143       /**
  144        * The descriptive information about this implementation.
  145        */
  146       protected static final String info =
  147           "org.apache.catalina.valves.AccessLogValve/2.1";
  148   
  149   
  150       /**
  151        * The lifecycle event support for this component.
  152        */
  153       protected LifecycleSupport lifecycle = new LifecycleSupport(this);
  154   
  155   
  156       /**
  157        * The set of month abbreviations for log messages.
  158        */
  159       protected static final String months[] =
  160       { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  161         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
  162   
  163   
  164       /**
  165        * enabled this component
  166        */
  167       protected boolean enabled = true;
  168   
  169       /**
  170        * The pattern used to format our access log lines.
  171        */
  172       protected String pattern = null;
  173   
  174   
  175       /**
  176        * The prefix that is added to log file filenames.
  177        */
  178       protected String prefix = "access_log.";
  179   
  180   
  181       /**
  182        * Should we rotate our log file? Default is true (like old behavior)
  183        */
  184       protected boolean rotatable = true;
  185   
  186   
  187       /**
  188        * Buffered logging.
  189        */
  190       private boolean buffered = true;
  191   
  192   
  193       /**
  194        * The string manager for this package.
  195        */
  196       protected StringManager sm =
  197           StringManager.getManager(Constants.Package);
  198   
  199   
  200       /**
  201        * Has this component been started yet?
  202        */
  203       protected boolean started = false;
  204   
  205   
  206       /**
  207        * The suffix that is added to log file filenames.
  208        */
  209       protected String suffix = "";
  210   
  211   
  212       /**
  213        * The PrintWriter to which we are currently logging, if any.
  214        */
  215       protected PrintWriter writer = null;
  216   
  217   
  218       /**
  219        * A date formatter to format a Date into a date in the format
  220        * "yyyy-MM-dd".
  221        */
  222       protected SimpleDateFormat fileDateFormatter = null;
  223   
  224   
  225       /**
  226        * The system timezone.
  227        */
  228       private TimeZone timezone = null;
  229   
  230       
  231       /**
  232        * The time zone offset relative to GMT in text form when daylight saving
  233        * is not in operation.
  234        */
  235       private String timeZoneNoDST = null;
  236   
  237   
  238       /**
  239        * The time zone offset relative to GMT in text form when daylight saving
  240        * is in operation.
  241        */
  242       private String timeZoneDST = null;
  243       
  244       
  245       /**
  246        * The current log file we are writing to. Helpful when checkExists
  247        * is true.
  248        */
  249       protected File currentLogFile = null;
  250       private static class AccessDateStruct {
  251           private Date currentDate = new Date();
  252           private String currentDateString = null;
  253           private SimpleDateFormat dayFormatter = new SimpleDateFormat("dd");
  254           private SimpleDateFormat monthFormatter = new SimpleDateFormat("MM");
  255           private SimpleDateFormat yearFormatter = new SimpleDateFormat("yyyy");
  256           private SimpleDateFormat timeFormatter = new SimpleDateFormat("HH:mm:ss");
  257           public AccessDateStruct() {
  258               TimeZone tz = TimeZone.getDefault();
  259               dayFormatter.setTimeZone(tz);
  260               monthFormatter.setTimeZone(tz);
  261               yearFormatter.setTimeZone(tz);
  262               timeFormatter.setTimeZone(tz);
  263           }
  264       }
  265       
  266       /**
  267        * The system time when we last updated the Date that this valve
  268        * uses for log lines.
  269        */
  270       private static final ThreadLocal<AccessDateStruct> currentDateStruct =
  271               new ThreadLocal<AccessDateStruct>() {
  272           protected AccessDateStruct initialValue() {
  273               return new AccessDateStruct();
  274           }
  275       };
  276       /**
  277        * Resolve hosts.
  278        */
  279       private boolean resolveHosts = false;
  280   
  281   
  282       /**
  283        * Instant when the log daily rotation was last checked.
  284        */
  285       private volatile long rotationLastChecked = 0L;
  286   
  287       /**
  288        * Do we check for log file existence? Helpful if an external
  289        * agent renames the log file so we can automagically recreate it.
  290        */
  291       private boolean checkExists = false;
  292       
  293       
  294       /**
  295        * Are we doing conditional logging. default false.
  296        */
  297       protected String condition = null;
  298   
  299   
  300       /**
  301        * Date format to place in log file name. Use at your own risk!
  302        */
  303       protected String fileDateFormat = null;
  304       
  305       /**
  306        * Array of AccessLogElement, they will be used to make log message.
  307        */
  308       protected AccessLogElement[] logElements = null;
  309   
  310       // ------------------------------------------------------------- Properties
  311   
  312       /**
  313        * @return Returns the enabled.
  314        */
  315       public boolean getEnabled() {
  316           return enabled;
  317       }
  318   
  319       /**
  320        * @param enabled
  321        *            The enabled to set.
  322        */
  323       public void setEnabled(boolean enabled) {
  324           this.enabled = enabled;
  325       }
  326   
  327       /**
  328        * Return the directory in which we create log files.
  329        */
  330       public String getDirectory() {
  331           return (directory);
  332       }
  333   
  334   
  335       /**
  336        * Set the directory in which we create log files.
  337        *
  338        * @param directory The new log file directory
  339        */
  340       public void setDirectory(String directory) {
  341           this.directory = directory;
  342       }
  343   
  344   
  345       /**
  346        * Return descriptive information about this implementation.
  347        */
  348       public String getInfo() {
  349           return (info);
  350       }
  351   
  352   
  353       /**
  354        * Return the format pattern.
  355        */
  356       public String getPattern() {
  357           return (this.pattern);
  358       }
  359   
  360   
  361       /**
  362        * Set the format pattern, first translating any recognized alias.
  363        *
  364        * @param pattern The new pattern
  365        */
  366       public void setPattern(String pattern) {
  367           if (pattern == null)
  368               pattern = "";
  369           if (pattern.equals(Constants.AccessLog.COMMON_ALIAS))
  370               pattern = Constants.AccessLog.COMMON_PATTERN;
  371           if (pattern.equals(Constants.AccessLog.COMBINED_ALIAS))
  372               pattern = Constants.AccessLog.COMBINED_PATTERN;
  373           this.pattern = pattern;
  374           logElements = createLogElements();
  375       }
  376   
  377   
  378       /**
  379        * Check for file existence before logging.
  380        */
  381       public boolean isCheckExists() {
  382   
  383           return checkExists;
  384   
  385       }
  386   
  387   
  388       /**
  389        * Set whether to check for log file existence before logging.
  390        *
  391        * @param checkExists true meaning to check for file existence.
  392        */
  393       public void setCheckExists(boolean checkExists) {
  394   
  395           this.checkExists = checkExists;
  396   
  397       }
  398       
  399       
  400       /**
  401        * Return the log file prefix.
  402        */
  403       public String getPrefix() {
  404           return (prefix);
  405       }
  406   
  407   
  408       /**
  409        * Set the log file prefix.
  410        *
  411        * @param prefix The new log file prefix
  412        */
  413       public void setPrefix(String prefix) {
  414           this.prefix = prefix;
  415       }
  416   
  417   
  418       /**
  419        * Should we rotate the logs
  420        */
  421       public boolean isRotatable() {
  422           return rotatable;
  423       }
  424   
  425   
  426       /**
  427        * Set the value is we should we rotate the logs
  428        *
  429        * @param rotatable true is we should rotate.
  430        */
  431       public void setRotatable(boolean rotatable) {
  432           this.rotatable = rotatable;
  433       }
  434   
  435   
  436       /**
  437        * Is the logging buffered
  438        */
  439       public boolean isBuffered() {
  440           return buffered;
  441       }
  442   
  443   
  444       /**
  445        * Set the value if the logging should be buffered
  446        *
  447        * @param buffered true if buffered.
  448        */
  449       public void setBuffered(boolean buffered) {
  450           this.buffered = buffered;
  451       }
  452   
  453   
  454       /**
  455        * Return the log file suffix.
  456        */
  457       public String getSuffix() {
  458           return (suffix);
  459       }
  460   
  461   
  462       /**
  463        * Set the log file suffix.
  464        *
  465        * @param suffix The new log file suffix
  466        */
  467       public void setSuffix(String suffix) {
  468           this.suffix = suffix;
  469       }
  470   
  471   
  472       /**
  473        * Set the resolve hosts flag.
  474        *
  475        * @param resolveHosts The new resolve hosts value
  476        */
  477       public void setResolveHosts(boolean resolveHosts) {
  478           this.resolveHosts = resolveHosts;
  479       }
  480   
  481   
  482       /**
  483        * Get the value of the resolve hosts flag.
  484        */
  485       public boolean isResolveHosts() {
  486           return resolveHosts;
  487       }
  488   
  489   
  490       /**
  491        * Return whether the attribute name to look for when
  492        * performing conditional loggging. If null, every
  493        * request is logged.
  494        */
  495       public String getCondition() {
  496           return condition;
  497       }
  498   
  499   
  500       /**
  501        * Set the ServletRequest.attribute to look for to perform
  502        * conditional logging. Set to null to log everything.
  503        *
  504        * @param condition Set to null to log everything
  505        */
  506       public void setCondition(String condition) {
  507           this.condition = condition;
  508       }
  509   
  510       /**
  511        *  Return the date format date based log rotation.
  512        */
  513       public String getFileDateFormat() {
  514           return fileDateFormat;
  515       }
  516   
  517   
  518       /**
  519        *  Set the date format date based log rotation.
  520        */
  521       public void setFileDateFormat(String fileDateFormat) {
  522           this.fileDateFormat =  fileDateFormat;
  523       }
  524   
  525       // --------------------------------------------------------- Public Methods
  526   
  527       /**
  528        * Execute a periodic task, such as reloading, etc. This method will be
  529        * invoked inside the classloading context of this container. Unexpected
  530        * throwables will be caught and logged.
  531        */
  532       public void backgroundProcess() {
  533           if (started && getEnabled() && writer != null && buffered) {
  534               writer.flush();
  535           }
  536       }    
  537   
  538       /**
  539        * Log a message summarizing the specified request and response, according
  540        * to the format specified by the <code>pattern</code> property.
  541        *
  542        * @param request Request being processed
  543        * @param response Response being processed
  544        *
  545        * @exception IOException if an input/output error has occurred
  546        * @exception ServletException if a servlet error has occurred
  547        */
  548       public void invoke(Request request, Response response) throws IOException,
  549               ServletException {
  550   
  551           if (started && getEnabled()) {                
  552               // Pass this request on to the next valve in our pipeline
  553               long t1 = System.currentTimeMillis();
  554       
  555               getNext().invoke(request, response);
  556       
  557               long t2 = System.currentTimeMillis();
  558               long time = t2 - t1;
  559       
  560               if (logElements == null || condition != null
  561                       && null != request.getRequest().getAttribute(condition)) {
  562                   return;
  563               }
  564       
  565               Date date = getDate();
  566               StringBuffer result = new StringBuffer(128);
  567       
  568               for (int i = 0; i < logElements.length; i++) {
  569                   logElements[i].addElement(result, date, request, response, time);
  570               }
  571       
  572               log(result.toString());
  573           } else
  574               getNext().invoke(request, response);       
  575       }
  576   
  577       
  578       /**
  579        * Rename the existing log file to something else. Then open the
  580        * old log file name up once again. Intended to be called by a JMX
  581        * agent.
  582        *
  583        *
  584        * @param newFileName The file name to move the log file entry to
  585        * @return true if a file was rotated with no error
  586        */
  587       public synchronized boolean rotate(String newFileName) {
  588   
  589           if (currentLogFile != null) {
  590               File holder = currentLogFile;
  591               close();
  592               try {
  593                   holder.renameTo(new File(newFileName));
  594               } catch (Throwable e) {
  595                   log.error("rotate failed", e);
  596               }
  597   
  598               /* Make sure date is correct */
  599               dateStamp = fileDateFormatter.format(
  600                       new Date(System.currentTimeMillis()));
  601   
  602               open();
  603               return true;
  604           } else {
  605               return false;
  606           }
  607   
  608       }
  609   
  610       // -------------------------------------------------------- Private Methods
  611   
  612   
  613       /**
  614        * Close the currently open log file (if any)
  615        */
  616       private synchronized void close() {
  617           if (writer == null) {
  618               return;
  619           }
  620           writer.flush();
  621           writer.close();
  622           writer = null;
  623           dateStamp = "";
  624           currentLogFile = null;
  625       }
  626   
  627   
  628       /**
  629        * Log the specified message to the log file, switching files if the date
  630        * has changed since the previous log call.
  631        *
  632        * @param message Message to be logged
  633        */
  634       public void log(String message) {
  635           if (rotatable) {
  636               // Only do a logfile switch check once a second, max.
  637               long systime = System.currentTimeMillis();
  638               if ((systime - rotationLastChecked) > 1000) {
  639                   synchronized(this) {
  640                       if ((systime - rotationLastChecked) > 1000) {
  641                           rotationLastChecked = systime;
  642       
  643                           String tsDate;
  644                           // Check for a change of date
  645                           tsDate = fileDateFormatter.format(new Date(systime));
  646       
  647                           // If the date has changed, switch log files
  648                           if (!dateStamp.equals(tsDate)) {
  649                               close();
  650                               dateStamp = tsDate;
  651                               open();
  652                           }
  653                       }
  654                   }
  655               }
  656           }
  657           
  658           /* In case something external rotated the file instead */
  659           if (checkExists) {
  660               synchronized (this) {
  661                   if (currentLogFile != null && !currentLogFile.exists()) {
  662                       try {
  663                           close();
  664                       } catch (Throwable e) {
  665                           log.info("at least this wasn't swallowed", e);
  666                       }
  667   
  668                       /* Make sure date is correct */
  669                       dateStamp = fileDateFormatter.format(
  670                               new Date(System.currentTimeMillis()));
  671   
  672                       open();
  673                   }
  674               }
  675           }
  676   
  677           // Log this message
  678           synchronized(this) {
  679               if (writer != null) {
  680                   writer.println(message);
  681                   if (!buffered) {
  682                       writer.flush();
  683                   }
  684               }
  685           }
  686   
  687       }
  688   
  689   
  690       /**
  691        * Return the month abbreviation for the specified month, which must
  692        * be a two-digit String.
  693        *
  694        * @param month Month number ("01" .. "12").
  695        */
  696       private String lookup(String month) {
  697           int index;
  698           try {
  699               index = Integer.parseInt(month) - 1;
  700           } catch (Throwable t) {
  701               index = 0;  // Can not happen, in theory
  702           }
  703           return (months[index]);
  704       }
  705   
  706   
  707       /**
  708        * Open the new log file for the date specified by <code>dateStamp</code>.
  709        */
  710       protected synchronized void open() {
  711           // Create the directory if necessary
  712           File dir = new File(directory);
  713           if (!dir.isAbsolute())
  714               dir = new File(System.getProperty("catalina.base"), directory);
  715           dir.mkdirs();
  716   
  717           // Open the current log file
  718           try {
  719               String pathname;
  720               // If no rotate - no need for dateStamp in fileName
  721               if (rotatable) {
  722                   pathname = dir.getAbsolutePath() + File.separator + prefix
  723                           + dateStamp + suffix;
  724               } else {
  725                   pathname = dir.getAbsolutePath() + File.separator + prefix
  726                           + suffix;
  727               }
  728               writer = new PrintWriter(new BufferedWriter(new FileWriter(
  729                       pathname, true), 128000), false);
  730               
  731               currentLogFile = new File(pathname);
  732           } catch (IOException e) {
  733               writer = null;
  734               currentLogFile = null;
  735           }
  736       }
  737    
  738       /**
  739        * This method returns a Date object that is accurate to within one second.
  740        * If a thread calls this method to get a Date and it's been less than 1
  741        * second since a new Date was created, this method simply gives out the
  742        * same Date again so that the system doesn't spend time creating Date
  743        * objects unnecessarily.
  744        * 
  745        * @return Date
  746        */
  747       private Date getDate() {
  748           // Only create a new Date once per second, max.
  749           long systime = System.currentTimeMillis();
  750           AccessDateStruct struct = currentDateStruct.get(); 
  751           if ((systime - struct.currentDate.getTime()) > 1000) {
  752               struct.currentDate.setTime(systime);
  753               struct.currentDateString = null;
  754           }
  755           return struct.currentDate;
  756       }
  757   
  758   
  759       private String getTimeZone(Date date) {
  760           if (timezone.inDaylightTime(date)) {
  761               return timeZoneDST;
  762           } else {
  763               return timeZoneNoDST;
  764           }
  765       }
  766       
  767       
  768       private String calculateTimeZoneOffset(long offset) {
  769           StringBuffer tz = new StringBuffer();
  770           if ((offset < 0)) {
  771               tz.append("-");
  772               offset = -offset;
  773           } else {
  774               tz.append("+");
  775           }
  776   
  777           long hourOffset = offset / (1000 * 60 * 60);
  778           long minuteOffset = (offset / (1000 * 60)) % 60;
  779   
  780           if (hourOffset < 10)
  781               tz.append("0");
  782           tz.append(hourOffset);
  783   
  784           if (minuteOffset < 10)
  785               tz.append("0");
  786           tz.append(minuteOffset);
  787   
  788           return tz.toString();
  789       }
  790   
  791   
  792       // ------------------------------------------------------ Lifecycle Methods
  793   
  794   
  795       /**
  796        * Add a lifecycle event listener to this component.
  797        *
  798        * @param listener The listener to add
  799        */
  800       public void addLifecycleListener(LifecycleListener listener) {
  801           lifecycle.addLifecycleListener(listener);
  802       }
  803   
  804   
  805       /**
  806        * Get the lifecycle listeners associated with this lifecycle. If this
  807        * Lifecycle has no listeners registered, a zero-length array is returned.
  808        */
  809       public LifecycleListener[] findLifecycleListeners() {
  810           return lifecycle.findLifecycleListeners();
  811       }
  812   
  813   
  814       /**
  815        * Remove a lifecycle event listener from this component.
  816        *
  817        * @param listener The listener to add
  818        */
  819       public void removeLifecycleListener(LifecycleListener listener) {
  820           lifecycle.removeLifecycleListener(listener);
  821       }
  822   
  823   
  824       /**
  825        * Prepare for the beginning of active use of the public methods of this
  826        * component.  This method should be called after <code>configure()</code>,
  827        * and before any of the public methods of the component are utilized.
  828        *
  829        * @exception LifecycleException if this component detects a fatal error
  830        *  that prevents this component from being used
  831        */
  832       public void start() throws LifecycleException {
  833   
  834           // Validate and update our current component state
  835           if (started)
  836               throw new LifecycleException(sm
  837                       .getString("accessLogValve.alreadyStarted"));
  838           lifecycle.fireLifecycleEvent(START_EVENT, null);
  839           started = true;
  840   
  841           // Initialize the timeZone, Date formatters, and currentDate
  842           timezone = TimeZone.getDefault();
  843           timeZoneNoDST = calculateTimeZoneOffset(timezone.getRawOffset());
  844           int offset = timezone.getDSTSavings();
  845           timeZoneDST = calculateTimeZoneOffset(timezone.getRawOffset() + offset);
  846   
  847           if (fileDateFormat == null || fileDateFormat.length() == 0)
  848               fileDateFormat = "yyyy-MM-dd";
  849           fileDateFormatter = new SimpleDateFormat(fileDateFormat);
  850           fileDateFormatter.setTimeZone(timezone);
  851           dateStamp = fileDateFormatter.format(currentDateStruct.get().currentDate);
  852           open();
  853       }
  854   
  855   
  856       /**
  857        * Gracefully terminate the active use of the public methods of this
  858        * component.  This method should be the last one called on a given
  859        * instance of this component.
  860        *
  861        * @exception LifecycleException if this component detects a fatal error
  862        *  that needs to be reported
  863        */
  864       public void stop() throws LifecycleException {
  865   
  866           // Validate and update our current component state
  867           if (!started)
  868               throw new LifecycleException(sm
  869                       .getString("accessLogValve.notStarted"));
  870           lifecycle.fireLifecycleEvent(STOP_EVENT, null);
  871           started = false;
  872           
  873           close();
  874       }
  875       
  876       /**
  877        * AccessLogElement writes the partial message into the buffer.
  878        */
  879       protected interface AccessLogElement {
  880           public void addElement(StringBuffer buf, Date date, Request request,
  881                   Response response, long time);
  882   
  883       }
  884       
  885       /**
  886        * write thread name - %I
  887        */
  888       protected class ThreadNameElement implements AccessLogElement {
  889           public void addElement(StringBuffer buf, Date date, Request request,
  890                   Response response, long time) {
  891               RequestInfo info = request.getCoyoteRequest().getRequestProcessor();
  892               if(info != null) {
  893                   buf.append(info.getWorkerThreadName());
  894               } else {
  895                   buf.append("-");
  896               }
  897           }
  898       }
  899       
  900       /**
  901        * write local IP address - %A
  902        */
  903       protected static class LocalAddrElement implements AccessLogElement {
  904           
  905           private static final String LOCAL_ADDR_VALUE;
  906   
  907           static {
  908               String init;
  909               try {
  910                   init = InetAddress.getLocalHost().getHostAddress();
  911               } catch (Throwable e) {
  912                   init = "127.0.0.1";
  913               }
  914               LOCAL_ADDR_VALUE = init;
  915           }
  916           
  917           public void addElement(StringBuffer buf, Date date, Request request,
  918                   Response response, long time) {
  919               buf.append(LOCAL_ADDR_VALUE);
  920           }
  921       }
  922       
  923       /**
  924        * write remote IP address - %a
  925        */
  926       protected class RemoteAddrElement implements AccessLogElement {
  927           public void addElement(StringBuffer buf, Date date, Request request,
  928                   Response response, long time) {
  929               buf.append(request.getRemoteAddr());
  930           }
  931       }
  932       
  933       /**
  934        * write remote host name - %h
  935        */
  936       protected class HostElement implements AccessLogElement {
  937           public void addElement(StringBuffer buf, Date date, Request request,
  938                   Response response, long time) {
  939               buf.append(request.getRemoteHost());
  940           }
  941       }
  942       
  943       /**
  944        * write remote logical username from identd (always returns '-') - %l
  945        */
  946       protected class LogicalUserNameElement implements AccessLogElement {
  947           public void addElement(StringBuffer buf, Date date, Request request,
  948                   Response response, long time) {
  949               buf.append('-');
  950           }
  951       }
  952       
  953       /**
  954        * write request protocol - %H
  955        */
  956       protected class ProtocolElement implements AccessLogElement {
  957           public void addElement(StringBuffer buf, Date date, Request request,
  958                   Response response, long time) {
  959               buf.append(request.getProtocol());
  960           }
  961       }
  962   
  963       /**
  964        * write remote user that was authenticated (if any), else '-' - %u
  965        */
  966       protected class UserElement implements AccessLogElement {
  967           public void addElement(StringBuffer buf, Date date, Request request,
  968                   Response response, long time) {
  969               if (request != null) {
  970                   String value = request.getRemoteUser();
  971                   if (value != null) {
  972                       buf.append(value);
  973                   } else {
  974                       buf.append('-');
  975                   }
  976               } else {
  977                   buf.append('-');
  978               }
  979           }
  980       }
  981   
  982       /**
  983        * write date and time, in Common Log Format - %t
  984        */
  985       protected class DateAndTimeElement implements AccessLogElement {
  986           
  987           
  988   
  989   
  990           public void addElement(StringBuffer buf, Date date, Request request,
  991                   Response response, long time) {
  992               AccessDateStruct struct = currentDateStruct.get();
  993               if (struct.currentDateString == null) {
  994                   StringBuffer current = new StringBuffer(32);
  995                   current.append('[');
  996                   current.append(struct.dayFormatter.format(date));
  997                   current.append('/');
  998                   current.append(lookup(struct.monthFormatter.format(date)));
  999                   current.append('/');
 1000                   current.append(struct.yearFormatter.format(date));
 1001                   current.append(':');
 1002                   current.append(struct.timeFormatter.format(date));
 1003                   current.append(' ');
 1004                   current.append(getTimeZone(date));
 1005                   current.append(']');
 1006                   struct.currentDateString = current.toString();
 1007               }
 1008               buf.append(struct.currentDateString);
 1009           }
 1010       }
 1011   
 1012       /**
 1013        * write first line of the request (method and request URI) - %r
 1014        */
 1015       protected class RequestElement implements AccessLogElement {
 1016           public void addElement(StringBuffer buf, Date date, Request request,
 1017                   Response response, long time) {
 1018               if (request != null) {
 1019                   buf.append(request.getMethod());
 1020                   buf.append(' ');
 1021                   buf.append(request.getRequestURI());
 1022                   if (request.getQueryString() != null) {
 1023                       buf.append('?');
 1024                       buf.append(request.getQueryString());
 1025                   }
 1026                   buf.append(' ');
 1027                   buf.append(request.getProtocol());
 1028               } else {
 1029                   buf.append("- - ");
 1030               }
 1031           }
 1032       }
 1033   
 1034       /**
 1035        * write HTTP status code of the response - %s
 1036        */
 1037       protected class HttpStatusCodeElement implements AccessLogElement {
 1038           public void addElement(StringBuffer buf, Date date, Request request,
 1039                   Response response, long time) {
 1040               if (response != null) {
 1041                   buf.append(response.getStatus());
 1042               } else {
 1043                   buf.append('-');
 1044               }
 1045           }
 1046       }
 1047   
 1048       /**
 1049        * write local port on which this request was received - %p
 1050        */
 1051       protected class LocalPortElement implements AccessLogElement {
 1052           public void addElement(StringBuffer buf, Date date, Request request,
 1053                   Response response, long time) {
 1054               buf.append(request.getServerPort());
 1055           }
 1056       }
 1057   
 1058       /**
 1059        * write bytes sent, excluding HTTP headers - %b, %B
 1060        */
 1061       protected class ByteSentElement implements AccessLogElement {
 1062           private boolean conversion;
 1063   
 1064           /**
 1065            * if conversion is true, write '-' instead of 0 - %b
 1066            */
 1067           public ByteSentElement(boolean conversion) {
 1068               this.conversion = conversion;
 1069           }
 1070   
 1071           public void addElement(StringBuffer buf, Date date, Request request,
 1072                   Response response, long time) {
 1073               long length = response.getContentCountLong() ;
 1074               if (length <= 0 && conversion) {
 1075                   buf.append('-');
 1076               } else {
 1077                   buf.append(length);
 1078               }
 1079           }
 1080       }
 1081   
 1082       /**
 1083        * write request method (GET, POST, etc.) - %m
 1084        */
 1085       protected class MethodElement implements AccessLogElement {
 1086           public void addElement(StringBuffer buf, Date date, Request request,
 1087                   Response response, long time) {
 1088               if (request != null) {
 1089                   buf.append(request.getMethod());
 1090               }
 1091           }
 1092       }
 1093   
 1094       /**
 1095        * write time taken to process the request - %D, %T
 1096        */
 1097       protected class ElapsedTimeElement implements AccessLogElement {
 1098           private boolean millis;
 1099   
 1100           /**
 1101            * if millis is true, write time in millis - %D
 1102            * if millis is false, write time in seconds - %T
 1103            */
 1104           public ElapsedTimeElement(boolean millis) {
 1105               this.millis = millis;
 1106           }
 1107   
 1108           public void addElement(StringBuffer buf, Date date, Request request,
 1109                   Response response, long time) {
 1110               if (millis) {
 1111                   buf.append(time);
 1112               } else {
 1113                   // second
 1114                   buf.append(time / 1000);
 1115                   buf.append('.');
 1116                   int remains = (int) (time % 1000);
 1117                   buf.append(remains / 100);
 1118                   remains = remains % 100;
 1119                   buf.append(remains / 10);
 1120                   buf.append(remains % 10);
 1121               }
 1122           }
 1123       }
 1124       
 1125       /**
 1126        * write Query string (prepended with a '?' if it exists) - %q
 1127        */
 1128       protected class QueryElement implements AccessLogElement {
 1129           public void addElement(StringBuffer buf, Date date, Request request,
 1130                   Response response, long time) {
 1131               String query = null;
 1132               if (request != null)
 1133                   query = request.getQueryString();
 1134               if (query != null) {
 1135                   buf.append('?');
 1136                   buf.append(query);
 1137               }
 1138           }
 1139       }
 1140   
 1141       /**
 1142        * write user session ID - %S
 1143        */
 1144       protected class SessionIdElement implements AccessLogElement {
 1145           public void addElement(StringBuffer buf, Date date, Request request,
 1146                   Response response, long time) {
 1147               if (request != null) {
 1148                   if (request.getSession(false) != null) {
 1149                       buf.append(request.getSessionInternal(false)
 1150                               .getIdInternal());
 1151                   } else {
 1152                       buf.append('-');
 1153                   }
 1154               } else {
 1155                   buf.append('-');
 1156               }
 1157           }
 1158       }
 1159   
 1160       /**
 1161        * write requested URL path - %U
 1162        */
 1163       protected class RequestURIElement implements AccessLogElement {
 1164           public void addElement(StringBuffer buf, Date date, Request request,
 1165                   Response response, long time) {
 1166               if (request != null) {
 1167                   buf.append(request.getRequestURI());
 1168               } else {
 1169                   buf.append('-');
 1170               }
 1171           }
 1172       }
 1173   
 1174       /**
 1175        * write local server name - %v
 1176        */
 1177       protected class LocalServerNameElement implements AccessLogElement {
 1178           public void addElement(StringBuffer buf, Date date, Request request,
 1179                   Response response, long time) {
 1180               buf.append(request.getServerName());
 1181           }
 1182       }
 1183       
 1184       /**
 1185        * write any string
 1186        */
 1187       protected class StringElement implements AccessLogElement {
 1188           private String str;
 1189   
 1190           public StringElement(String str) {
 1191               this.str = str;
 1192           }
 1193   
 1194           public void addElement(StringBuffer buf, Date date, Request request,
 1195                   Response response, long time) {
 1196               buf.append(str);
 1197           }
 1198       }
 1199   
 1200       /**
 1201        * write incoming headers - %{xxx}i
 1202        */
 1203       protected class HeaderElement implements AccessLogElement {
 1204           private String header;
 1205   
 1206           public HeaderElement(String header) {
 1207               this.header = header;
 1208           }
 1209   
 1210           public void addElement(StringBuffer buf, Date date, Request request,
 1211                   Response response, long time) {
 1212               String value = request.getHeader(header);
 1213               if (value == null) {
 1214                   buf.append('-');
 1215               } else {
 1216                   buf.append(value);
 1217               }
 1218           }
 1219       }
 1220   
 1221       /**
 1222        * write a specific cookie - %{xxx}c
 1223        */
 1224       protected class CookieElement implements AccessLogElement {
 1225           private String header;
 1226   
 1227           public CookieElement(String header) {
 1228               this.header = header;
 1229           }
 1230   
 1231           public void addElement(StringBuffer buf, Date date, Request request,
 1232                   Response response, long time) {
 1233               String value = "-";
 1234               Cookie[] c = request.getCookies();
 1235               if (c != null) {
 1236                   for (int i = 0; i < c.length; i++) {
 1237                       if (header.equals(c[i].getName())) {
 1238                           value = c[i].getValue();
 1239                           break;
 1240                       }
 1241                   }
 1242               }
 1243               buf.append(value);
 1244           }
 1245       }
 1246   
 1247       /**
 1248        * write a specific response header - %{xxx}o
 1249        */
 1250       protected class ResponseHeaderElement implements AccessLogElement {
 1251           private String header;
 1252   
 1253           public ResponseHeaderElement(String header) {
 1254               this.header = header;
 1255           }
 1256           
 1257           public void addElement(StringBuffer buf, Date date, Request request,
 1258                   Response response, long time) {
 1259              if (null != response) {
 1260                   String[] values = response.getHeaderValues(header);
 1261                   if(values.length > 0) {
 1262                       for (int i = 0; i < values.length; i++) {
 1263                           String string = values[i];
 1264                           buf.append(string) ;
 1265                           if(i+1<values.length)
 1266                               buf.append(",");
 1267                       }
 1268                       return ;
 1269                   }
 1270               }
 1271               buf.append("-");
 1272           }
 1273       }
 1274       
 1275       /**
 1276        * write an attribute in the ServletRequest - %{xxx}r
 1277        */
 1278       protected class RequestAttributeElement implements AccessLogElement {
 1279           private String header;
 1280   
 1281           public RequestAttributeElement(String header) {
 1282               this.header = header;
 1283           }
 1284   
 1285           public void addElement(StringBuffer buf, Date date, Request request,
 1286                   Response response, long time) {
 1287               Object value = null;
 1288               if (request != null) {
 1289                   value = request.getAttribute(header);
 1290               } else {
 1291                   value = "??";
 1292               }
 1293               if (value != null) {
 1294                   if (value instanceof String) {
 1295                       buf.append((String) value);
 1296                   } else {
 1297                       buf.append(value.toString());
 1298                   }
 1299               } else {
 1300                   buf.append('-');
 1301               }
 1302           }
 1303       }
 1304   
 1305       /**
 1306        * write an attribute in the HttpSession - %{xxx}s
 1307        */
 1308       protected class SessionAttributeElement implements AccessLogElement {
 1309           private String header;
 1310   
 1311           public SessionAttributeElement(String header) {
 1312               this.header = header;
 1313           }
 1314   
 1315           public void addElement(StringBuffer buf, Date date, Request request,
 1316                   Response response, long time) {
 1317               Object value = null;
 1318               if (null != request) {
 1319                   HttpSession sess = request.getSession(false);
 1320                   if (null != sess)
 1321                       value = sess.getAttribute(header);
 1322               } else {
 1323                   value = "??";
 1324               }
 1325               if (value != null) {
 1326                   if (value instanceof String) {
 1327                       buf.append((String) value);
 1328                   } else {
 1329                       buf.append(value.toString());
 1330                   }
 1331               } else {
 1332                   buf.append('-');
 1333               }
 1334           }
 1335       }
 1336   
 1337   
 1338   
 1339   
 1340       /**
 1341        * parse pattern string and create the array of AccessLogElement
 1342        */
 1343       protected AccessLogElement[] createLogElements() {
 1344           List<AccessLogElement> list = new ArrayList<AccessLogElement>();
 1345           boolean replace = false;
 1346           StringBuffer buf = new StringBuffer();
 1347           for (int i = 0; i < pattern.length(); i++) {
 1348               char ch = pattern.charAt(i);
 1349               if (replace) {
 1350                   /*
 1351                    * For code that processes {, the behavior will be ... if I do
 1352                    * not enounter a closing } - then I ignore the {
 1353                    */
 1354                   if ('{' == ch) {
 1355                       StringBuffer name = new StringBuffer();
 1356                       int j = i + 1;
 1357                       for (; j < pattern.length() && '}' != pattern.charAt(j); j++) {
 1358                           name.append(pattern.charAt(j));
 1359                       }
 1360                       if (j + 1 < pattern.length()) {
 1361                           /* the +1 was to account for } which we increment now */
 1362                           j++;
 1363                           list.add(createAccessLogElement(name.toString(),
 1364                                   pattern.charAt(j)));
 1365                           i = j; /* Since we walked more than one character */
 1366                       } else {
 1367                           // D'oh - end of string - pretend we never did this
 1368                           // and do processing the "old way"
 1369                           list.add(createAccessLogElement(ch));
 1370                       }
 1371                   } else {
 1372                       list.add(createAccessLogElement(ch));
 1373                   }
 1374                   replace = false;
 1375               } else if (ch == '%') {
 1376                   replace = true;
 1377                   list.add(new StringElement(buf.toString()));
 1378                   buf = new StringBuffer();
 1379               } else {
 1380                   buf.append(ch);
 1381               }
 1382           }
 1383           if (buf.length() > 0) {
 1384               list.add(new StringElement(buf.toString()));
 1385           }
 1386           return list.toArray(new AccessLogElement[0]);
 1387       }
 1388   
 1389       /**
 1390        * create an AccessLogElement implementation which needs header string
 1391        */
 1392       private AccessLogElement createAccessLogElement(String header, char pattern) {
 1393           switch (pattern) {
 1394           case 'i':
 1395               return new HeaderElement(header);
 1396           case 'c':
 1397               return new CookieElement(header);
 1398           case 'o':
 1399               return new ResponseHeaderElement(header);
 1400           case 'r':
 1401               return new RequestAttributeElement(header);
 1402           case 's':
 1403               return new SessionAttributeElement(header);            
 1404           default:
 1405               return new StringElement("???");
 1406           }
 1407       }
 1408   
 1409       /**
 1410        * create an AccessLogElement implementation
 1411        */
 1412       private AccessLogElement createAccessLogElement(char pattern) {
 1413           switch (pattern) {
 1414           case 'a':
 1415               return new RemoteAddrElement();
 1416           case 'A':
 1417               return new LocalAddrElement();
 1418           case 'b':
 1419               return new ByteSentElement(true);
 1420           case 'B':
 1421               return new ByteSentElement(false);
 1422           case 'D':
 1423               return new ElapsedTimeElement(true);
 1424           case 'h':
 1425               return new HostElement();
 1426           case 'H':
 1427               return new ProtocolElement();
 1428           case 'l':
 1429               return new LogicalUserNameElement();
 1430           case 'm':
 1431               return new MethodElement();
 1432           case 'p':
 1433               return new LocalPortElement();
 1434           case 'q':
 1435               return new QueryElement();
 1436           case 'r':
 1437               return new RequestElement();
 1438           case 's':
 1439               return new HttpStatusCodeElement();
 1440           case 'S':
 1441               return new SessionIdElement();
 1442           case 't':
 1443               return new DateAndTimeElement();
 1444           case 'T':
 1445               return new ElapsedTimeElement(false);
 1446           case 'u':
 1447               return new UserElement();
 1448           case 'U':
 1449               return new RequestURIElement();
 1450           case 'v':
 1451               return new LocalServerNameElement();
 1452           case 'I':
 1453               return new ThreadNameElement();
 1454           default:
 1455               return new StringElement("???" + pattern + "???");
 1456           }
 1457       }
 1458   }

Save This Page
Home » apache-tomcat-6.0.26-src » org.apache » catalina » valves » [javadoc | source]