View Javadoc

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