View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.server;
20  
21  import java.io.IOException;
22  import java.util.Locale;
23  
24  import javax.servlet.http.Cookie;
25  
26  import org.eclipse.jetty.http.HttpHeader;
27  import org.eclipse.jetty.http.PathMap;
28  import org.eclipse.jetty.server.handler.StatisticsHandler;
29  import org.eclipse.jetty.util.DateCache;
30  import org.eclipse.jetty.util.annotation.ManagedAttribute;
31  import org.eclipse.jetty.util.component.AbstractLifeCycle;
32  import org.eclipse.jetty.util.log.Log;
33  import org.eclipse.jetty.util.log.Logger;
34  
35  /**
36   * Base implementation of the {@link RequestLog} outputs logs in the pseudo-standard
37   * NCSA common log format. Configuration options allow a choice between the
38   * standard Common Log Format (as used in the 3 log format) and the Combined Log
39   * Format (single log format). This log format can be output by most web
40   * servers, and almost all web log analysis software can understand these
41   * formats.
42   */
43  public abstract class AbstractNCSARequestLog extends AbstractLifeCycle implements RequestLog
44  {
45      protected static final Logger LOG = Log.getLogger(AbstractNCSARequestLog.class);
46  
47      private static ThreadLocal<StringBuilder> _buffers = new ThreadLocal<StringBuilder>()
48              {
49                  @Override
50                  protected StringBuilder initialValue()
51                  {
52                      return new StringBuilder(256);
53                  }
54              };
55  
56      
57      private String[] _ignorePaths;
58      private boolean _extended;
59      private transient PathMap<String> _ignorePathMap;
60      private boolean _logLatency = false;
61      private boolean _logCookies = false;
62      private boolean _logServer = false;
63      private boolean _preferProxiedForAddress;
64      private transient DateCache _logDateCache;
65      private String _logDateFormat = "dd/MMM/yyyy:HH:mm:ss Z";
66      private Locale _logLocale = Locale.getDefault();
67      private String _logTimeZone = "GMT";
68  
69      /* ------------------------------------------------------------ */
70      /**
71       * Is logging enabled
72       */
73      protected abstract boolean isEnabled();
74      
75      /* ------------------------------------------------------------ */
76      /**
77       * Write requestEntry out. (to disk or slf4j log)
78       */
79      public abstract void write(String requestEntry) throws IOException;
80  
81      /* ------------------------------------------------------------ */
82      /**
83       * Writes the request and response information to the output stream.
84       *
85       * @see org.eclipse.jetty.server.RequestLog#log(org.eclipse.jetty.server.Request, org.eclipse.jetty.server.Response)
86       */
87      @Override
88      public void log(Request request, Response response)
89      {
90          try
91          {
92              if (_ignorePathMap != null && _ignorePathMap.getMatch(request.getRequestURI()) != null)
93                  return;
94  
95              if (!isEnabled())
96                  return;
97  
98              StringBuilder buf= _buffers.get();
99              buf.setLength(0);
100 
101             if (_logServer)
102             {
103                 buf.append(request.getServerName());
104                 buf.append(' ');
105             }
106 
107             String addr = null;
108             if (_preferProxiedForAddress)
109             {
110                 addr = request.getHeader(HttpHeader.X_FORWARDED_FOR.toString());
111             }
112 
113             if (addr == null)
114                 addr = request.getRemoteAddr();
115 
116             buf.append(addr);
117             buf.append(" - ");
118             Authentication authentication=request.getAuthentication();
119             if (authentication instanceof Authentication.User)
120                 buf.append(((Authentication.User)authentication).getUserIdentity().getUserPrincipal().getName());
121             else
122                 buf.append(" - ");
123 
124             buf.append(" [");
125             if (_logDateCache != null)
126                 buf.append(_logDateCache.format(request.getTimeStamp()));
127             else
128                 buf.append(request.getTimeStamp());
129 
130             buf.append("] \"");
131             buf.append(request.getMethod());
132             buf.append(' ');
133             buf.append(request.getUri().toString());
134             buf.append(' ');
135             buf.append(request.getProtocol());
136             buf.append("\" ");
137             if (request.getHttpChannelState().isInitial())
138             {
139                 int status = response.getStatus();
140                 if (status <= 0)
141                     status = 404;
142                 buf.append((char)('0' + ((status / 100) % 10)));
143                 buf.append((char)('0' + ((status / 10) % 10)));
144                 buf.append((char)('0' + (status % 10)));
145             }
146             else
147                 buf.append("Async");
148 
149             long responseLength = response.getLongContentLength();
150             if (responseLength >= 0)
151             {
152                 buf.append(' ');
153                 if (responseLength > 99999)
154                     buf.append(responseLength);
155                 else
156                 {
157                     if (responseLength > 9999)
158                         buf.append((char)('0' + ((responseLength / 10000) % 10)));
159                     if (responseLength > 999)
160                         buf.append((char)('0' + ((responseLength / 1000) % 10)));
161                     if (responseLength > 99)
162                         buf.append((char)('0' + ((responseLength / 100) % 10)));
163                     if (responseLength > 9)
164                         buf.append((char)('0' + ((responseLength / 10) % 10)));
165                     buf.append((char)('0' + (responseLength) % 10));
166                 }
167                 buf.append(' ');
168             }
169             else
170                 buf.append(" - ");
171 
172 
173             if (_extended)
174                 logExtended(request, response, buf);
175 
176             if (_logCookies)
177             {
178                 Cookie[] cookies = request.getCookies();
179                 if (cookies == null || cookies.length == 0)
180                     buf.append(" -");
181                 else
182                 {
183                     buf.append(" \"");
184                     for (int i = 0; i < cookies.length; i++)
185                     {
186                         if (i != 0)
187                             buf.append(';');
188                         buf.append(cookies[i].getName());
189                         buf.append('=');
190                         buf.append(cookies[i].getValue());
191                     }
192                     buf.append('\"');
193                 }
194             }
195 
196             if (_logLatency)
197             {
198                 long now = System.currentTimeMillis();
199 
200                 if (_logLatency)
201                 {
202                     buf.append(' ');
203                     buf.append(now - request.getTimeStamp());
204                 }
205             }
206 
207             String log = buf.toString();
208             write(log);
209         }
210         catch (IOException e)
211         {
212             LOG.warn(e);
213         }
214     }
215     
216     /* ------------------------------------------------------------ */
217     /**
218      * Writes extended request and response information to the output stream.
219      *
220      * @param request request object
221      * @param response response object
222      * @param b StringBuilder to write to
223      * @throws IOException
224      */
225     protected void logExtended(Request request,
226                                Response response,
227                                StringBuilder b) throws IOException
228     {
229         String referer = request.getHeader(HttpHeader.REFERER.toString());
230         if (referer == null)
231             b.append("\"-\" ");
232         else
233         {
234             b.append('"');
235             b.append(referer);
236             b.append("\" ");
237         }
238 
239         String agent = request.getHeader(HttpHeader.USER_AGENT.toString());
240         if (agent == null)
241             b.append("\"-\" ");
242         else
243         {
244             b.append('"');
245             b.append(agent);
246             b.append('"');
247         }
248     }
249 
250 
251 
252     /**
253      * Set request paths that will not be logged.
254      *
255      * @param ignorePaths array of request paths
256      */
257     public void setIgnorePaths(String[] ignorePaths)
258     {
259         _ignorePaths = ignorePaths;
260     }
261 
262     /**
263      * Retrieve the request paths that will not be logged.
264      *
265      * @return array of request paths
266      */
267     public String[] getIgnorePaths()
268     {
269         return _ignorePaths;
270     }
271 
272     /**
273      * Controls logging of the request cookies.
274      *
275      * @param logCookies true - values of request cookies will be logged,
276      *                   false - values of request cookies will not be logged
277      */
278     public void setLogCookies(boolean logCookies)
279     {
280         _logCookies = logCookies;
281     }
282 
283     /**
284      * Retrieve log cookies flag
285      *
286      * @return value of the flag
287      */
288     public boolean getLogCookies()
289     {
290         return _logCookies;
291     }
292 
293     /**
294      * Controls logging of the request hostname.
295      *
296      * @param logServer true - request hostname will be logged,
297      *                  false - request hostname will not be logged
298      */
299     public void setLogServer(boolean logServer)
300     {
301         _logServer = logServer;
302     }
303 
304     /**
305      * Retrieve log hostname flag.
306      *
307      * @return value of the flag
308      */
309     public boolean getLogServer()
310     {
311         return _logServer;
312     }
313 
314     /**
315      * Controls logging of request processing time.
316      *
317      * @param logLatency true - request processing time will be logged
318      *                   false - request processing time will not be logged
319      */
320     public void setLogLatency(boolean logLatency)
321     {
322         _logLatency = logLatency;
323     }
324 
325     /**
326      * Retrieve log request processing time flag.
327      *
328      * @return value of the flag
329      */
330     public boolean getLogLatency()
331     {
332         return _logLatency;
333     }
334 
335     /**
336      * @deprecated use {@link StatisticsHandler}
337      */
338     public void setLogDispatch(boolean value)
339     {
340     }
341 
342     /**
343      * @deprecated use {@link StatisticsHandler}
344      */
345     public boolean isLogDispatch()
346     {
347         return false;
348     }
349 
350     /**
351      * Controls whether the actual IP address of the connection or
352      * the IP address from the X-Forwarded-For header will be logged.
353      *
354      * @param preferProxiedForAddress true - IP address from header will be logged,
355      *                                false - IP address from the connection will be logged
356      */
357     public void setPreferProxiedForAddress(boolean preferProxiedForAddress)
358     {
359         _preferProxiedForAddress = preferProxiedForAddress;
360     }
361 
362     /**
363      * Retrieved log X-Forwarded-For IP address flag.
364      *
365      * @return value of the flag
366      */
367     public boolean getPreferProxiedForAddress()
368     {
369         return _preferProxiedForAddress;
370     }
371 
372     /**
373      * Set the extended request log format flag.
374      *
375      * @param extended true - log the extended request information,
376      *                 false - do not log the extended request information
377      */
378     public void setExtended(boolean extended)
379     {
380         _extended = extended;
381     }
382 
383     /**
384      * Retrieve the extended request log format flag.
385      *
386      * @return value of the flag
387      */
388     @ManagedAttribute("use extended NCSA format")
389     public boolean isExtended()
390     {
391         return _extended;
392     }
393     
394     /**
395      * Set up request logging and open log file.
396      *
397      * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
398      */
399     @Override
400     protected synchronized void doStart() throws Exception
401     {
402         if (_logDateFormat != null)
403         {
404             _logDateCache = new DateCache(_logDateFormat,_logLocale);
405             _logDateCache.setTimeZoneID(_logTimeZone);
406         }
407 
408         if (_ignorePaths != null && _ignorePaths.length > 0)
409         {
410             _ignorePathMap = new PathMap<>();
411             for (int i = 0; i < _ignorePaths.length; i++)
412                 _ignorePathMap.put(_ignorePaths[i],_ignorePaths[i]);
413         }
414         else
415             _ignorePathMap = null;
416 
417         super.doStart();
418     }
419     
420     @Override
421     protected void doStop() throws Exception
422     {
423         _logDateCache = null;
424         super.doStop();
425     }
426 
427     /**
428      * Set the timestamp format for request log entries in the file.
429      * If this is not set, the pre-formated request timestamp is used.
430      *
431      * @param format timestamp format string
432      */
433     public void setLogDateFormat(String format)
434     {
435         _logDateFormat = format;
436     }
437 
438     /**
439      * Retrieve the timestamp format string for request log entries.
440      *
441      * @return timestamp format string.
442      */
443     public String getLogDateFormat()
444     {
445         return _logDateFormat;
446     }
447 
448     /**
449      * Set the locale of the request log.
450      *
451      * @param logLocale locale object
452      */
453     public void setLogLocale(Locale logLocale)
454     {
455         _logLocale = logLocale;
456     }
457 
458     /**
459      * Retrieve the locale of the request log.
460      *
461      * @return locale object
462      */
463     public Locale getLogLocale()
464     {
465         return _logLocale;
466     }
467 
468     /**
469      * Set the timezone of the request log.
470      *
471      * @param tz timezone string
472      */
473     public void setLogTimeZone(String tz)
474     {
475         _logTimeZone = tz;
476     }
477 
478     /**
479      * Retrieve the timezone of the request log.
480      *
481      * @return timezone string
482      */
483     @ManagedAttribute("the timezone")
484     public String getLogTimeZone()
485     {
486         return _logTimeZone;
487     }
488 }