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

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