View Javadoc

    1   //========================================================================
    2   //Copyright 1997-2006 Mort Bay Consulting Pty. Ltd.
    3   //------------------------------------------------------------------------
    4   //Licensed under the Apache License, Version 2.0 (the "License");
    5   //you may not use this file except in compliance with the License.
    6   //You may obtain a copy of the License at
    7   //http://www.apache.org/licenses/LICENSE-2.0
    8   //Unless required by applicable law or agreed to in writing, software
    9   //distributed under the License is distributed on an "AS IS" BASIS,
    10  //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  //See the License for the specific language governing permissions and
    12  //limitations under the License.
    13  //========================================================================
    14
    15  package org.mortbay.jetty;
    16
    17  import java.io.IOException;
    18  import java.io.OutputStream;
    19  import java.io.OutputStreamWriter;
    20  import java.io.Writer;
    21  import java.util.ArrayList;
    22  import java.util.Locale;
    23  import java.util.TimeZone;
    24
    25  import javax.servlet.http.Cookie;
    26
    27  import org.mortbay.component.AbstractLifeCycle;
    28  import org.mortbay.jetty.servlet.PathMap;
    29  import org.mortbay.log.Log;
    30  import org.mortbay.util.DateCache;
    31  import org.mortbay.util.RolloverFileOutputStream;
    32  import org.mortbay.util.StringUtil;
    33  import org.mortbay.util.TypeUtil;
    34  import org.mortbay.util.Utf8StringBuffer;
    35
    36  /**
    37   * This {@link RequestLog} implementation outputs logs in the pseudo-standard NCSA common log format.
    38   * Configuration options allow a choice between the standard Common Log Format (as used in the 3 log format)
    39   * and the Combined Log Format (single log format).
    40   * This log format can be output by most web servers, and almost all web log analysis software can understand
    41   *  these formats.
    42   * @author Greg Wilkins
    43   * @author Nigel Canonizado
    44   *
    45   * @org.apache.xbean.XBean element="ncsaLog"
    46   */
    47  public class NCSARequestLog extends AbstractLifeCycle implements RequestLog
    48  {
    49      private String _filename;
    50      private boolean _extended;
    51      private boolean _append;
    52      private int _retainDays;
    53      private boolean _closeOut;
    54      private boolean _preferProxiedForAddress;
    55      private String _logDateFormat="dd/MMM/yyyy:HH:mm:ss Z";
    56      private String _filenameDateFormat = null;
    57      private Locale _logLocale = Locale.getDefault();
    58      private String _logTimeZone = "GMT";
    59      private String[] _ignorePaths;
    60      private boolean _logLatency = false;
    61      private boolean _logCookies = false;
    62      private boolean _logServer = false;
    63
    64      private transient OutputStream _out;
    65      private transient OutputStream _fileOut;
    66      private transient DateCache _logDateCache;
    67      private transient PathMap _ignorePathMap;
    68      private transient Writer _writer;
    69      private transient ArrayList _buffers;
    70      private transient char[] _copy;
    71
    72
    73      public NCSARequestLog()
    74      {
    75          _extended = true;
    76          _append = true;
    77          _retainDays = 31;
    78      }
    79
    80      /* ------------------------------------------------------------ */
    81      /**
    82       * @param filename The filename for the request log. This may be in the format expected by {@link RolloverFileOutputStream}
    83       */
    84      public NCSARequestLog(String filename)
    85      {
    86          _extended = true;
    87          _append = true;
    88          _retainDays = 31;
    89          setFilename(filename);
    90      }
    91
    92      /* ------------------------------------------------------------ */
    93      /**
    94       * @param filename The filename for the request log. This may be in the format expected by {@link RolloverFileOutputStream}
    95       */
    96      public void setFilename(String filename)
    97      {
    98          if (filename != null)
    99          {
    100             filename = filename.trim();
    101             if (filename.length() == 0)
    102                 filename = null;
    103         }
    104         _filename = filename;
    105     }
    106
    107     public String getFilename()
    108     {
    109         return _filename;
    110     }
    111
    112     public String getDatedFilename()
    113     {
    114         if (_fileOut instanceof RolloverFileOutputStream)
    115             return ((RolloverFileOutputStream)_fileOut).getDatedFilename();
    116         return null;
    117     }
    118
    119     /* ------------------------------------------------------------ */
    120     /**
    121      * @param format Format for the timestamps in the log file.  If not set,
    122      * the pre-formated request timestamp is used.
    123      */
    124     public void setLogDateFormat(String format)
    125     {
    126         _logDateFormat = format;
    127     }
    128
    129     public String getLogDateFormat()
    130     {
    131         return _logDateFormat;
    132     }
    133
    134
    135     public void setLogTimeZone(String tz)
    136     {
    137         _logTimeZone = tz;
    138     }
    139
    140     public String getLogTimeZone()
    141     {
    142         return _logTimeZone;
    143     }
    144
    145     public void setRetainDays(int retainDays)
    146     {
    147         _retainDays = retainDays;
    148     }
    149
    150     public int getRetainDays()
    151     {
    152         return _retainDays;
    153     }
    154
    155     public void setExtended(boolean extended)
    156     {
    157         _extended = extended;
    158     }
    159
    160     public boolean isExtended()
    161     {
    162         return _extended;
    163     }
    164
    165     public void setAppend(boolean append)
    166     {
    167         _append = append;
    168     }
    169
    170     public boolean isAppend()
    171     {
    172         return _append;
    173     }
    174
    175     public void setIgnorePaths(String[] ignorePaths)
    176     {
    177         _ignorePaths = ignorePaths;
    178     }
    179
    180     public String[] getIgnorePaths()
    181     {
    182         return _ignorePaths;
    183     }
    184
    185     public void setLogCookies(boolean logCookies)
    186     {
    187         _logCookies = logCookies;
    188     }
    189
    190     public boolean getLogCookies()
    191     {
    192         return _logCookies;
    193     }
    194
    195     public boolean getLogServer()
    196     {
    197         return _logServer;
    198     }
    199
    200     public void setLogServer(boolean logServer)
    201     {
    202         _logServer=logServer;
    203     }
    204
    205     public void setLogLatency(boolean logLatency)
    206     {
    207         _logLatency = logLatency;
    208     }
    209
    210     public boolean getLogLatency()
    211     {
    212         return _logLatency;
    213     }
    214
    215     public void setPreferProxiedForAddress(boolean preferProxiedForAddress)
    216     {
    217         _preferProxiedForAddress = preferProxiedForAddress;
    218     }
    219
    220     /* ------------------------------------------------------------ */
    221     public void log(Request request, Response response)
    222     {
    223         if (!isStarted())
    224             return;
    225
    226         try
    227         {
    228             if (_ignorePathMap != null && _ignorePathMap.getMatch(request.getRequestURI()) != null)
    229                 return;
    230
    231             if (_fileOut == null)
    232                 return;
    233
    234             Utf8StringBuffer u8buf;
    235             StringBuffer buf;
    236             synchronized(_writer)
    237             {
    238                 int size=_buffers.size();
    239                 u8buf = size==0?new Utf8StringBuffer(160):(Utf8StringBuffer)_buffers.remove(size-1);
    240                 buf = u8buf.getStringBuffer();
    241             }
    242
    243             synchronized(buf) // for efficiency until we can use StringBuilder
    244             {
    245                 if (_logServer)
    246                 {
    247                     buf.append(request.getServerName());
    248                     buf.append(' ');
    249                 }
    250
    251                 String addr = null;
    252                 if (_preferProxiedForAddress)
    253                 {
    254                     addr = request.getHeader(HttpHeaders.X_FORWARDED_FOR);
    255                 }
    256
    257                 if (addr == null)
    258                     addr = request.getRemoteAddr();
    259
    260                 buf.append(addr);
    261                 buf.append(" - ");
    262                 String user = request.getRemoteUser();
    263                 buf.append((user == null)? " - " : user);
    264                 buf.append(" [");
    265                 if (_logDateCache!=null)
    266                     buf.append(_logDateCache.format(request.getTimeStamp()));
    267                 else
    268                     buf.append(request.getTimeStampBuffer().toString());
    269
    270                 buf.append("] \"");
    271                 buf.append(request.getMethod());
    272                 buf.append(' ');
    273
    274                 request.getUri().writeTo(u8buf);
    275
    276                 buf.append(' ');
    277                 buf.append(request.getProtocol());
    278                 buf.append("\" ");
    279                 int status = response.getStatus();
    280                 if (status<=0)
    281                     status=404;
    282                 buf.append((char)('0'+((status/100)%10)));
    283                 buf.append((char)('0'+((status/10)%10)));
    284                 buf.append((char)('0'+(status%10)));
    285
    286
    287                 long responseLength=response.getContentCount();
    288                 if (responseLength >=0)
    289                 {
    290                     buf.append(' ');
    291                     if (responseLength > 99999)
    292                         buf.append(Long.toString(responseLength));
    293                     else
    294                     {
    295                         if (responseLength > 9999)
    296                             buf.append((char)('0' + ((responseLength / 10000)%10)));
    297                         if (responseLength > 999)
    298                             buf.append((char)('0' + ((responseLength /1000)%10)));
    299                         if (responseLength > 99)
    300                             buf.append((char)('0' + ((responseLength / 100)%10)));
    301                         if (responseLength > 9)
    302                             buf.append((char)('0' + ((responseLength / 10)%10)));
    303                         buf.append((char)('0' + (responseLength)%10));
    304                     }
    305                     buf.append(' ');
    306                 }
    307                 else
    308                     buf.append(" - ");
    309
    310             }
    311
    312             if (!_extended && !_logCookies && !_logLatency)
    313             {
    314                 synchronized(_writer)
    315                 {
    316                     buf.append(StringUtil.__LINE_SEPARATOR);
    317                     int l=buf.length();
    318                     if (l>_copy.length)
    319                         l=_copy.length;
    320                     buf.getChars(0,l,_copy,0);
    321                     _writer.write(_copy,0,l);
    322                     _writer.flush();
    323                     u8buf.reset();
    324                     _buffers.add(u8buf);
    325                 }
    326             }
    327             else
    328             {
    329                 synchronized(_writer)
    330                 {
    331                     int l=buf.length();
    332                     if (l>_copy.length)
    333                         l=_copy.length;
    334                     buf.getChars(0,l,_copy,0);
    335                     _writer.write(_copy,0,l);
    336                     u8buf.reset();
    337                     _buffers.add(u8buf);
    338
    339                     // TODO do outside synchronized scope
    340                     if (_extended)
    341                         logExtended(request, response, _writer);
    342
    343                     // TODO do outside synchronized scope
    344                     if (_logCookies)
    345                     {
    346                         Cookie[] cookies = request.getCookies();
    347                         if (cookies == null || cookies.length == 0)
    348                             _writer.write(" -");
    349                         else
    350                         {
    351                             _writer.write(" \"");
    352                             for (int i = 0; i < cookies.length; i++)
    353                             {
    354                                 if (i != 0)
    355                                     _writer.write(';');
    356                                 _writer.write(cookies[i].getName());
    357                                 _writer.write('=');
    358                                 _writer.write(cookies[i].getValue());
    359                             }
    360                             _writer.write('\"');
    361                         }
    362                     }
    363
    364                     if (_logLatency)
    365                     {
    366                         _writer.write(' ');
    367                         _writer.write(TypeUtil.toString(System.currentTimeMillis() - request.getTimeStamp()));
    368                     }
    369
    370                     _writer.write(StringUtil.__LINE_SEPARATOR);
    371                     _writer.flush();
    372                 }
    373             }
    374         }
    375         catch (IOException e)
    376         {
    377             Log.warn(e);
    378         }
    379
    380     }
    381
    382     /* ------------------------------------------------------------ */
    383     protected void logExtended(Request request,
    384                                Response response,
    385                                Writer writer) throws IOException
    386     {
    387         String referer = request.getHeader(HttpHeaders.REFERER);
    388         if (referer == null)
    389             writer.write("\"-\" ");
    390         else
    391         {
    392             writer.write('"');
    393             writer.write(referer);
    394             writer.write("\" ");
    395         }
    396
    397         String agent = request.getHeader(HttpHeaders.USER_AGENT);
    398         if (agent == null)
    399             writer.write("\"-\" ");
    400         else
    401         {
    402             writer.write('"');
    403             writer.write(agent);
    404             writer.write('"');
    405         }
    406     }
    407
    408     /* ------------------------------------------------------------ */
    409     protected void doStart() throws Exception
    410     {
    411         if (_logDateFormat!=null)
    412         {
    413             _logDateCache = new DateCache(_logDateFormat, _logLocale);
    414             _logDateCache.setTimeZoneID(_logTimeZone);
    415         }
    416
    417         if (_filename != null)
    418         {
    419             _fileOut = new RolloverFileOutputStream(_filename,_append,_retainDays,TimeZone.getTimeZone(_logTimeZone),_filenameDateFormat,null);
    420             _closeOut = true;
    421             Log.info("Opened "+getDatedFilename());
    422         }
    423         else
    424             _fileOut = System.err;
    425
    426         _out = _fileOut;
    427
    428         if (_ignorePaths != null && _ignorePaths.length > 0)
    429         {
    430             _ignorePathMap = new PathMap();
    431             for (int i = 0; i < _ignorePaths.length; i++)
    432                 _ignorePathMap.put(_ignorePaths[i], _ignorePaths[i]);
    433         }
    434         else
    435             _ignorePathMap = null;
    436
    437         _writer = new OutputStreamWriter(_out);
    438         _buffers = new ArrayList();
    439         _copy = new char[1024];
    440         super.doStart();
    441     }
    442
    443     /* ------------------------------------------------------------ */
    444     protected void doStop() throws Exception
    445     {
    446         super.doStop();
    447         try {if (_writer != null) _writer.flush();} catch (IOException e) {Log.ignore(e);}
    448         if (_out != null && _closeOut)
    449             try {_out.close();} catch (IOException e) {Log.ignore(e);}
    450
    451         _out = null;
    452         _fileOut = null;
    453         _closeOut = false;
    454         _logDateCache = null;
    455         _writer = null;
    456         _buffers = null;
    457         _copy = null;
    458     }
    459
    460     /* ------------------------------------------------------------ */
    461     /**
    462      * @return the log File Date Format
    463      */
    464     public String getFilenameDateFormat()
    465     {
    466         return _filenameDateFormat;
    467     }
    468
    469     /* ------------------------------------------------------------ */
    470     /** Set the log file date format.
    471      * @see {@link RolloverFileOutputStream#RolloverFileOutputStream(String, boolean, int, TimeZone, String, String)}
    472      * @param logFileDateFormat the logFileDateFormat to pass to {@link RolloverFileOutputStream}
    473      */
    474     public void setFilenameDateFormat(String logFileDateFormat)
    475     {
    476         _filenameDateFormat=logFileDateFormat;
    477     }
    478
    479 }