View Javadoc

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