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