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