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      * @param request the HTTP request to log
226      * @param b the build to write logs into
227      * @throws IOException for no reason
228      * @deprecated override {@link #logExtended(StringBuilder, Request, Response)} instead
229      */
230     @Deprecated
231     protected void logExtended(Request request, StringBuilder b) throws IOException
232     {
233         String referer = request.getHeader(HttpHeader.REFERER.toString());
234         if (referer == null)
235             b.append("\"-\" ");
236         else
237         {
238             b.append('"');
239             b.append(referer);
240             b.append("\" ");
241         }
242 
243         String agent = request.getHeader(HttpHeader.USER_AGENT.toString());
244         if (agent == null)
245             b.append("\"-\"");
246         else
247         {
248             b.append('"');
249             b.append(agent);
250             b.append('"');
251         }
252     }
253 
254     /**
255      * Writes extended request and response information to the output stream.
256      *
257      * @param b        StringBuilder to write to
258      * @param request  request object
259      * @param response response object
260      * @throws IOException if unable to log the extended information
261      */
262     protected void logExtended(StringBuilder b, Request request, Response response) throws IOException
263     {
264         logExtended(request, b);
265     }
266 
267     /**
268      * Set request paths that will not be logged.
269      *
270      * @param ignorePaths array of request paths
271      */
272     public void setIgnorePaths(String[] ignorePaths)
273     {
274         _ignorePaths = ignorePaths;
275     }
276 
277     /**
278      * Retrieve the request paths that will not be logged.
279      *
280      * @return array of request paths
281      */
282     public String[] getIgnorePaths()
283     {
284         return _ignorePaths;
285     }
286 
287     /**
288      * Controls logging of the request cookies.
289      *
290      * @param logCookies true - values of request cookies will be logged, false - values of request cookies will not be
291      *                   logged
292      */
293     public void setLogCookies(boolean logCookies)
294     {
295         _logCookies = logCookies;
296     }
297 
298     /**
299      * Retrieve log cookies flag
300      *
301      * @return value of the flag
302      */
303     public boolean getLogCookies()
304     {
305         return _logCookies;
306     }
307 
308     /**
309      * Controls logging of the request hostname.
310      *
311      * @param logServer true - request hostname will be logged, false - request hostname will not be logged
312      */
313     public void setLogServer(boolean logServer)
314     {
315         _logServer = logServer;
316     }
317 
318     /**
319      * Retrieve log hostname flag.
320      *
321      * @return value of the flag
322      */
323     public boolean getLogServer()
324     {
325         return _logServer;
326     }
327 
328     /**
329      * Controls logging of request processing time.
330      *
331      * @param logLatency true - request processing time will be logged false - request processing time will not be
332      *                   logged
333      */
334     public void setLogLatency(boolean logLatency)
335     {
336         _logLatency = logLatency;
337     }
338 
339     /**
340      * Retrieve log request processing time flag.
341      *
342      * @return value of the flag
343      */
344     public boolean getLogLatency()
345     {
346         return _logLatency;
347     }
348 
349     /**
350      * @param value true to log dispatch
351      * @deprecated use {@link StatisticsHandler}
352      */
353     @Deprecated
354     public void setLogDispatch(boolean value)
355     {
356     }
357 
358     /**
359      * @return true if logging dispatches
360      * @deprecated use {@link StatisticsHandler}
361      */
362     @Deprecated
363     public boolean isLogDispatch()
364     {
365         return false;
366     }
367 
368     /**
369      * Controls whether the actual IP address of the connection or the IP address from the X-Forwarded-For header will
370      * be logged.
371      *
372      * @param preferProxiedForAddress true - IP address from header will be logged, false - IP address from the
373      *                                connection will be logged
374      */
375     public void setPreferProxiedForAddress(boolean preferProxiedForAddress)
376     {
377         _preferProxiedForAddress = preferProxiedForAddress;
378     }
379 
380     /**
381      * Retrieved log X-Forwarded-For IP address flag.
382      *
383      * @return value of the flag
384      */
385     public boolean getPreferProxiedForAddress()
386     {
387         return _preferProxiedForAddress;
388     }
389 
390     /**
391      * Set the extended request log format flag.
392      *
393      * @param extended true - log the extended request information, false - do not log the extended request information
394      */
395     public void setExtended(boolean extended)
396     {
397         _extended = extended;
398     }
399 
400     /**
401      * Retrieve the extended request log format flag.
402      *
403      * @return value of the flag
404      */
405     @ManagedAttribute("use extended NCSA format")
406     public boolean isExtended()
407     {
408         return _extended;
409     }
410 
411     /**
412      * Set up request logging and open log file.
413      *
414      * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
415      */
416     @Override
417     protected synchronized void doStart() throws Exception
418     {
419         if (_logDateFormat != null)
420         {
421             _logDateCache = new DateCache(_logDateFormat, _logLocale ,_logTimeZone);
422         }
423 
424         if (_ignorePaths != null && _ignorePaths.length > 0)
425         {
426             _ignorePathMap = new PathMap<>();
427             for (int i = 0; i < _ignorePaths.length; i++)
428                 _ignorePathMap.put(_ignorePaths[i], _ignorePaths[i]);
429         }
430         else
431             _ignorePathMap = null;
432 
433         super.doStart();
434     }
435 
436     @Override
437     protected void doStop() throws Exception
438     {
439         _logDateCache = null;
440         super.doStop();
441     }
442 
443     /**
444      * Set the timestamp format for request log entries in the file. If this is not set, the pre-formated request
445      * timestamp is used.
446      *
447      * @param format timestamp format string
448      */
449     public void setLogDateFormat(String format)
450     {
451         _logDateFormat = format;
452     }
453 
454     /**
455      * Retrieve the timestamp format string for request log entries.
456      *
457      * @return timestamp format string.
458      */
459     public String getLogDateFormat()
460     {
461         return _logDateFormat;
462     }
463 
464     /**
465      * Set the locale of the request log.
466      *
467      * @param logLocale locale object
468      */
469     public void setLogLocale(Locale logLocale)
470     {
471         _logLocale = logLocale;
472     }
473 
474     /**
475      * Retrieve the locale of the request log.
476      *
477      * @return locale object
478      */
479     public Locale getLogLocale()
480     {
481         return _logLocale;
482     }
483 
484     /**
485      * Set the timezone of the request log.
486      *
487      * @param tz timezone string
488      */
489     public void setLogTimeZone(String tz)
490     {
491         _logTimeZone = tz;
492     }
493 
494     /**
495      * Retrieve the timezone of the request log.
496      *
497      * @return timezone string
498      */
499     @ManagedAttribute("the timezone")
500     public String getLogTimeZone()
501     {
502         return _logTimeZone;
503     }
504 }