View Javadoc

1   //========================================================================
2   //Copyright 1997-2006 Mort Bay Consulting Pty. Ltd.
3   //------------------------------------------------------------------------
4   //Licensed under the Apache License, Version 2.0 (the "License");
5   //you may not use this file except in compliance with the License.
6   //You may obtain a copy of the License at
7   //http://www.apache.org/licenses/LICENSE-2.0
8   //Unless required by applicable law or agreed to in writing, software
9   //distributed under the License is distributed on an "AS IS" BASIS,
10  //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  //See the License for the specific language governing permissions and
12  //limitations under the License.
13  //========================================================================
14  
15  package org.mortbay.jetty; 
16  
17  import java.io.IOException;
18  import java.io.OutputStream;
19  import java.io.OutputStreamWriter;
20  import java.io.Writer;
21  import java.util.Locale;
22  import java.util.TimeZone;
23  
24  import javax.servlet.http.Cookie;
25  
26  import org.mortbay.component.AbstractLifeCycle;
27  import org.mortbay.jetty.servlet.PathMap;
28  import org.mortbay.log.Log;
29  import org.mortbay.util.DateCache;
30  import org.mortbay.util.RolloverFileOutputStream;
31  import org.mortbay.util.StringUtil;
32  import org.mortbay.util.TypeUtil;
33  
34  /** 
35   * This {@link RequestLog} implementation 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)
37   * and the Combined Log Format (single log format).
38   * This log format can be output by most web servers, and almost all web log analysis software can understand
39   *  these formats.
40   * @author Greg Wilkins
41   * @author Nigel Canonizado
42   * 
43   * @org.apache.xbean.XBean element="ncsaLog"
44   */
45  public class NCSARequestLog extends AbstractLifeCycle implements RequestLog
46  {
47      private String _filename;
48      private boolean _extended;
49      private boolean _append;
50      private int _retainDays;
51      private boolean _closeOut;
52      private boolean _preferProxiedForAddress;
53      private String _logDateFormat="dd/MMM/yyyy:HH:mm:ss Z";
54      private String _filenameDateFormat = null;
55      private Locale _logLocale = Locale.getDefault();
56      private String _logTimeZone = "GMT";
57      private String[] _ignorePaths;
58      private boolean _logLatency = false;
59      private boolean _logCookies = false;
60      private boolean _logServer = false;
61      
62      private transient OutputStream _out;
63      private transient OutputStream _fileOut;
64      private transient DateCache _logDateCache;
65      private transient PathMap _ignorePathMap;
66      private transient Writer _writer;
67  
68      
69      public NCSARequestLog()
70      {
71          _extended = true;
72          _append = true;
73          _retainDays = 31;
74      }
75      
76      /* ------------------------------------------------------------ */
77      /**
78       * @param filename The filename for the request log. This may be in the format expected by {@link RolloverFileOutputStream}
79       */
80      public NCSARequestLog(String filename)
81      {
82          _extended = true;
83          _append = true;
84          _retainDays = 31;
85          setFilename(filename);
86      }
87      
88      /* ------------------------------------------------------------ */
89      /**
90       * @param filename The filename for the request log. This may be in the format expected by {@link RolloverFileOutputStream}
91       */
92      public void setFilename(String filename)
93      {
94          if (filename != null) 
95          {
96              filename = filename.trim();
97              if (filename.length() == 0)
98                  filename = null;
99          }    
100         _filename = filename;
101     }
102     
103     public String getFilename() 
104     {
105         return _filename;
106     }
107     
108     public String getDatedFilename()
109     {
110         if (_fileOut instanceof RolloverFileOutputStream)
111             return ((RolloverFileOutputStream)_fileOut).getDatedFilename();
112         return null;
113     }
114     
115     /* ------------------------------------------------------------ */
116     /**
117      * @param format Format for the timestamps in the log file.  If not set,
118      * the pre-formated request timestamp is used.
119      */
120     public void setLogDateFormat(String format)
121     {
122         _logDateFormat = format;
123     }
124     
125     public String getLogDateFormat() 
126     {
127         return _logDateFormat;
128     }
129     
130     
131     public void setLogTimeZone(String tz) 
132     {
133         _logTimeZone = tz;
134     }
135     
136     public String getLogTimeZone()
137     {
138         return _logTimeZone;
139     }
140     
141     public void setRetainDays(int retainDays)
142     {
143         _retainDays = retainDays;
144     }
145     
146     public int getRetainDays()
147     {
148         return _retainDays;
149     }
150     
151     public void setExtended(boolean extended)
152     {
153         _extended = extended;
154     }
155     
156     public boolean isExtended() 
157     {
158         return _extended;
159     }
160     
161     public void setAppend(boolean append)
162     {
163         _append = append;
164     }
165     
166     public boolean isAppend()
167     {
168         return _append;
169     }
170     
171     public void setIgnorePaths(String[] ignorePaths) 
172     {
173         _ignorePaths = ignorePaths;
174     }
175     
176     public String[] getIgnorePaths()
177     {
178         return _ignorePaths;
179     }
180     
181     public void setLogCookies(boolean logCookies) 
182     {
183         _logCookies = logCookies;
184     }
185     
186     public boolean getLogCookies()
187     {
188         return _logCookies;
189     }
190 
191     public boolean getLogServer()
192     {
193         return _logServer;
194     }
195 
196     public void setLogServer(boolean logServer)
197     {
198         _logServer=logServer;
199     }
200     
201     public void setLogLatency(boolean logLatency) 
202     {
203         _logLatency = logLatency;
204     }
205     
206     public boolean getLogLatency()
207     {
208         return _logLatency;
209     }
210     
211     public void setPreferProxiedForAddress(boolean preferProxiedForAddress)
212     {
213         _preferProxiedForAddress = preferProxiedForAddress;
214     }
215 
216     public void log(Request request, Response response)
217     {
218         if (!isStarted()) 
219             return;
220         
221         try 
222         {
223             if (_ignorePathMap != null && _ignorePathMap.getMatch(request.getRequestURI()) != null)
224                 return;
225             
226             if (_fileOut == null)
227                 return;
228 
229             StringBuffer buf = new StringBuffer(160);
230             String log =null;
231             synchronized(buf) // for efficiency until we can use StringBuilder
232             {
233                 if (_logServer)
234                 {
235                     buf.append(request.getServerName());
236                     buf.append(' ');
237                 }
238 
239                 String addr = null;
240                 if (_preferProxiedForAddress) 
241                 {
242                     addr = request.getHeader(HttpHeaders.X_FORWARDED_FOR);
243                 }
244 
245                 if (addr == null) 
246                     addr = request.getRemoteAddr();
247 
248                 buf.append(addr);
249                 buf.append(" - ");
250                 String user = request.getRemoteUser();
251                 buf.append((user == null)? " - " : user);
252                 buf.append(" [");
253                 if (_logDateCache!=null)
254                     buf.append(_logDateCache.format(request.getTimeStamp()));
255                 else
256                     buf.append(request.getTimeStampBuffer().toString());
257                     
258                 buf.append("] \"");
259                 buf.append(request.getMethod());
260                 buf.append(' ');
261                 buf.append(request.getUri());
262                 buf.append(' ');
263                 buf.append(request.getProtocol());
264                 buf.append("\" ");
265                 int status = response.getStatus();
266                 if (status<=0)
267                     status=404;
268                 buf.append((char)('0'+((status/100)%10)));
269                 buf.append((char)('0'+((status/10)%10)));
270                 buf.append((char)('0'+(status%10)));
271 
272 
273                 long responseLength=response.getContentCount();
274                 if (responseLength >=0)
275                 {
276                     buf.append(' ');
277                     if (responseLength > 99999)
278                         buf.append(Long.toString(responseLength));
279                     else 
280                     {
281                         if (responseLength > 9999)
282                             buf.append((char)('0' + ((responseLength / 10000)%10)));
283                         if (responseLength > 999)
284                             buf.append((char)('0' + ((responseLength /1000)%10)));
285                         if (responseLength > 99)
286                             buf.append((char)('0' + ((responseLength / 100)%10)));
287                         if (responseLength > 9)
288                             buf.append((char)('0' + ((responseLength / 10)%10)));
289                         buf.append((char)('0' + (responseLength)%10));
290                     }
291                     buf.append(' ');
292                 }
293                 else 
294                     buf.append(" - ");
295 
296                 log = buf.toString();
297             }
298             
299             synchronized(_writer)
300             {
301                 _writer.write(log);
302                 if (_extended)
303                     logExtended(request, response, _writer);
304                 
305                 if (_logCookies)
306                 {
307                     Cookie[] cookies = request.getCookies(); 
308                     if (cookies == null || cookies.length == 0)
309                         _writer.write(" -");
310                     else
311                     {
312                         _writer.write(" \"");
313                         for (int i = 0; i < cookies.length; i++) 
314                         {
315                             if (i != 0)
316                                 _writer.write(';');
317                             _writer.write(cookies[i].getName());
318                             _writer.write('=');
319                             _writer.write(cookies[i].getValue());
320                         }
321                         _writer.write("\"");
322                     }
323                 }
324                 
325                 if (_logLatency)
326                 {
327                     _writer.write(" ");
328                     _writer.write(TypeUtil.toString(System.currentTimeMillis() - request.getTimeStamp()));
329                 }
330                 
331                 _writer.write(StringUtil.__LINE_SEPARATOR);
332                 _writer.flush();
333                 
334             }
335         } 
336         catch (IOException e) 
337         {
338             Log.warn(e);
339         }
340         
341     }
342     
343     protected void logExtended(Request request, 
344                                Response response, 
345                                Writer writer) throws IOException 
346     {
347         String referer = request.getHeader(HttpHeaders.REFERER);
348         if (referer == null) 
349             writer.write("\"-\" ");
350         else 
351         {
352             writer.write('"');
353             writer.write(referer);
354             writer.write("\" ");
355         }
356         
357         String agent = request.getHeader(HttpHeaders.USER_AGENT);
358         if (agent == null)
359             writer.write("\"-\" ");
360         else
361         {
362             writer.write('"');
363             writer.write(agent);
364             writer.write('"');
365         }          
366     }
367 
368     protected void doStart() throws Exception
369     {
370         if (_logDateFormat!=null)
371         {       
372             _logDateCache = new DateCache(_logDateFormat, _logLocale);
373             _logDateCache.setTimeZoneID(_logTimeZone);
374         }
375         
376         if (_filename != null) 
377         {
378             _fileOut = new RolloverFileOutputStream(_filename,_append,_retainDays,TimeZone.getTimeZone(_logTimeZone),_filenameDateFormat,null);
379             _closeOut = true;
380             Log.info("Opened "+getDatedFilename());
381         }
382         else 
383             _fileOut = System.err;
384         
385         _out = _fileOut;
386         
387         if (_ignorePaths != null && _ignorePaths.length > 0)
388         {
389             _ignorePathMap = new PathMap();
390             for (int i = 0; i < _ignorePaths.length; i++) 
391                 _ignorePathMap.put(_ignorePaths[i], _ignorePaths[i]);
392         }
393         else 
394             _ignorePathMap = null;
395         
396         _writer = new OutputStreamWriter(_out);
397         super.doStart();
398     }
399 
400     protected void doStop() throws Exception
401     {
402         super.doStop();
403         try {if (_writer != null) _writer.flush();} catch (IOException e) {Log.ignore(e);}
404         if (_out != null && _closeOut) 
405             try {_out.close();} catch (IOException e) {Log.ignore(e);}
406             
407         _out = null;
408         _fileOut = null;
409         _closeOut = false;
410         _logDateCache = null;
411         _writer = null;
412     }
413 
414     /* ------------------------------------------------------------ */
415     /**
416      * @return the log File Date Format
417      */
418     public String getFilenameDateFormat()
419     {
420         return _filenameDateFormat;
421     }
422 
423     /* ------------------------------------------------------------ */
424     /** Set the log file date format.
425      * @see {@link RolloverFileOutputStream#RolloverFileOutputStream(String, boolean, int, TimeZone, String, String)}
426      * @param logFileDateFormat the logFileDateFormat to pass to {@link RolloverFileOutputStream}
427      */
428     public void setFilenameDateFormat(String logFileDateFormat)
429     {
430         _filenameDateFormat=logFileDateFormat;
431     }
432 
433 }