| // ======================================================================== |
| // $Id: NCSARequestLog.java,v 1.35 2005/08/13 00:01:24 gregwilkins Exp $ |
| // Copyright 2000-2004 Mort Bay Consulting Pty. Ltd. |
| // ------------------------------------------------------------------------ |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // ======================================================================== |
| |
| package org.openqa.jetty.http; |
| |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.io.OutputStreamWriter; |
| import java.io.Writer; |
| import java.util.Locale; |
| import java.util.TimeZone; |
| |
| import javax.servlet.http.Cookie; |
| |
| import org.apache.commons.logging.Log; |
| import org.openqa.jetty.log.LogFactory; |
| import org.openqa.jetty.util.DateCache; |
| import org.openqa.jetty.util.LogSupport; |
| import org.openqa.jetty.util.RolloverFileOutputStream; |
| import org.openqa.jetty.util.StringUtil; |
| |
| |
| /* ------------------------------------------------------------ */ |
| /** NCSA HTTP Request Log. |
| * NCSA common or NCSA extended (combined) request log. |
| * @version $Id: NCSARequestLog.java,v 1.35 2005/08/13 00:01:24 gregwilkins Exp $ |
| * @author Tony Thompson |
| * @author Greg Wilkins |
| */ |
| public class NCSARequestLog implements RequestLog |
| { |
| private static Log log = LogFactory.getLog(NCSARequestLog.class); |
| |
| private String _filename; |
| private boolean _extended; |
| private boolean _append; |
| private int _retainDays; |
| private boolean _closeOut; |
| private boolean _preferProxiedForAddress; |
| private String _logDateFormat="dd/MMM/yyyy:HH:mm:ss ZZZ"; |
| private Locale _logLocale=Locale.getDefault(); |
| private String _logTimeZone=TimeZone.getDefault().getID(); |
| private String[] _ignorePaths; |
| private boolean _logLatency=false; |
| private boolean _logCookies=false; |
| |
| private transient OutputStream _out; |
| private transient OutputStream _fileOut; |
| private transient DateCache _logDateCache; |
| private transient PathMap _ignorePathMap; |
| private transient Writer _writer; |
| |
| /* ------------------------------------------------------------ */ |
| /** Constructor. |
| */ |
| public NCSARequestLog() |
| { |
| _extended=true; |
| _append=true; |
| _retainDays=31; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** Constructor. |
| * @param filename Filename, which can be in |
| * rolloverFileOutputStream format |
| * @see org.openqa.jetty.util.RolloverFileOutputStream |
| * @exception IOException |
| */ |
| public NCSARequestLog(String filename) |
| throws IOException |
| { |
| _extended=true; |
| _append=true; |
| _retainDays=31; |
| setFilename(filename); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** Set the log filename. |
| * @see NCSARequestLog#setRetainDays(int) |
| * @param filename The filename to use. If the filename contains the |
| * string "yyyy_mm_dd", then a RolloverFileOutputStream is used and the |
| * log is rolled over nightly and aged according setRetainDays. If no |
| * filename is set or a null filename |
| * passed, then requests are logged to System.err. |
| */ |
| public void setFilename(String filename) |
| { |
| if (filename!=null) |
| { |
| filename=filename.trim(); |
| if (filename.length()==0) |
| filename=null; |
| } |
| _filename=filename; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** Get the log filename. |
| * @see NCSARequestLog#getDatedFilename() |
| * @return The log filename without any date expansion. |
| */ |
| public String getFilename() |
| { |
| return _filename; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** Get the dated log filename. |
| * @see NCSARequestLog#getFilename() |
| * @return The log filename with any date encoding expanded. |
| */ |
| public String getDatedFilename() |
| { |
| if (_fileOut instanceof RolloverFileOutputStream) |
| return ((RolloverFileOutputStream)_fileOut).getDatedFilename(); |
| return null; |
| } |
| |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @param format The date format to use within the log file. |
| */ |
| public void setLogDateFormat(String format) |
| { |
| _logDateFormat=format; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @return The date format to use within the log file. |
| */ |
| public String getLogDateFormat() |
| { |
| return _logDateFormat; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @param tz The date format timezone to use within the log file. |
| */ |
| public void setLogTimeZone(String tz) |
| { |
| _logTimeZone=tz; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @return The date format timezone to use within the log file. |
| */ |
| public String getLogTimeZone() |
| { |
| return _logTimeZone; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @return The number of days to retain rollovered log files. |
| */ |
| public int getRetainDays() |
| { |
| return _retainDays; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @param retainDays The number of days to retain rollovered log files. |
| */ |
| public void setRetainDays(int retainDays) |
| { |
| _retainDays = retainDays; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @return True if NCSA extended format is to be used. |
| */ |
| public boolean isExtended() |
| { |
| return _extended; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @param e True if NCSA extended format is to be used. |
| */ |
| public void setExtended(boolean e) |
| { |
| _extended=e; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @return True if logs are appended to existing log files. |
| */ |
| public boolean isAppend() |
| { |
| return _append; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @param a True if logs are appended to existing log files. |
| */ |
| public void setAppend(boolean a) |
| { |
| _append=a; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @deprecated ignored |
| */ |
| public void setBuffered(boolean b) |
| {} |
| |
| /* ------------------------------------------------------------ */ |
| /** Set which paths to ignore. |
| * |
| * @param ignorePaths Array of path specifications to ignore |
| */ |
| public void setIgnorePaths(String[] ignorePaths) |
| { |
| // Contributed by Martin Vilcans (martin@jadestone.se) |
| _ignorePaths = ignorePaths; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| public String[] getIgnorePaths() |
| { |
| return _ignorePaths; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @return Returns the logCookies. |
| */ |
| public boolean getLogCookies() |
| { |
| return _logCookies; |
| } |
| /* ------------------------------------------------------------ */ |
| /** |
| * @param logCookies The logCookies to set. |
| */ |
| public void setLogCookies(boolean logCookies) |
| { |
| _logCookies = logCookies; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @return Returns true if logging latency |
| */ |
| public boolean getLogLatency() |
| { |
| return _logLatency; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @param logLatency If true, latency is logged at the end of the log line |
| */ |
| public void setLogLatency(boolean logLatency) |
| { |
| _logLatency = logLatency; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * Prefer to log the proxied-for IP address (if present in |
| * the request header) over the native requester IP address. |
| * Useful in reverse-proxy situations when you'd rather see |
| * the IP address of the host before the most recent proxy |
| * server, as opposed to your own proxy server(s) every time. |
| * |
| * jlrobins@socialserve.com, March 2004. |
| **/ |
| public void setPreferProxiedForAddress(boolean value) |
| { |
| _preferProxiedForAddress = value; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| public void start() |
| throws Exception |
| { |
| _logDateCache=new DateCache(_logDateFormat,_logLocale); |
| _logDateCache.setTimeZoneID(_logTimeZone); |
| |
| if (_filename != null) |
| { |
| _fileOut=new RolloverFileOutputStream(_filename,_append,_retainDays); |
| _closeOut=true; |
| } |
| else |
| _fileOut=System.err; |
| |
| _out=_fileOut; |
| |
| if (_ignorePaths!=null && _ignorePaths.length>0) |
| { |
| _ignorePathMap=new PathMap(); |
| for (int i=0;i<_ignorePaths.length;i++) |
| _ignorePathMap.put(_ignorePaths[i],_ignorePaths[i]); |
| } |
| else |
| _ignorePathMap=null; |
| |
| _writer=new OutputStreamWriter(_out); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| public boolean isStarted() |
| { |
| return _fileOut!=null; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| public void stop() |
| { |
| try{if (_writer!=null)_writer.flush();} catch (IOException e){LogSupport.ignore(log,e);} |
| if (_out!=null && _closeOut) |
| try{_out.close();}catch(IOException e){LogSupport.ignore(log,e);} |
| _out=null; |
| _fileOut=null; |
| _closeOut=false; |
| _logDateCache=null; |
| _writer=null; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** Log a request. |
| * @param request The request |
| * @param response The response to this request. |
| * @param responseLength The bytes written to the response. |
| */ |
| public void log(HttpRequest request, |
| HttpResponse response, |
| int responseLength) |
| { |
| try{ |
| // ignore ignorables |
| if (_ignorePathMap != null && |
| _ignorePathMap.getMatch(request.getPath()) != null) |
| return; |
| |
| // log the rest |
| if (_fileOut==null) |
| return; |
| |
| StringBuffer buf = new StringBuffer(160); |
| |
| String addr = null; |
| if(_preferProxiedForAddress) |
| { |
| // If header is not present, addr will remain null ... |
| addr = request.getField(HttpFields.__XForwardedFor); |
| } |
| if(addr == null) |
| addr = request.getRemoteAddr(); |
| buf.append(addr); |
| |
| buf.append(" - "); |
| String user = request.getAuthUser(); |
| buf.append((user==null)?"-":user); |
| buf.append(" ["); |
| buf.append(_logDateCache.format(request.getTimeStamp())); |
| buf.append("] \""); |
| buf.append(request.getMethod()); |
| buf.append(' '); |
| buf.append(request.getURI()); |
| buf.append(' '); |
| buf.append(request.getVersion()); |
| buf.append("\" "); |
| int status=response.getStatus(); |
| buf.append((char)('0'+((status/100)%10))); |
| buf.append((char)('0'+((status/10)%10))); |
| buf.append((char)('0'+(status%10))); |
| if (responseLength>=0) |
| { |
| buf.append(' '); |
| if (responseLength>99999) |
| buf.append(Integer.toString(responseLength)); |
| else |
| { |
| if (responseLength>9999) |
| buf.append((char)('0'+((responseLength/10000)%10))); |
| if (responseLength>999) |
| buf.append((char)('0'+((responseLength/1000)%10))); |
| if (responseLength>99) |
| buf.append((char)('0'+((responseLength/100)%10))); |
| if (responseLength>9) |
| buf.append((char)('0'+((responseLength/10)%10))); |
| buf.append((char)('0'+(responseLength%10))); |
| } |
| buf.append(' '); |
| } |
| else |
| buf.append(" - "); |
| |
| String log =buf.toString(); |
| synchronized(_writer) |
| { |
| _writer.write(log); |
| if (_extended) |
| { |
| logExtended(request,response,_writer); |
| if (!_logCookies) |
| _writer.write(" -"); |
| } |
| |
| if (_logCookies) |
| { |
| Cookie[] cookies = request.getCookies(); |
| if (cookies==null || cookies.length==0) |
| _writer.write(" -"); |
| else |
| { |
| _writer.write(" \""); |
| for (int i=0;i<cookies.length;i++) |
| { |
| if (i!=0) |
| _writer.write(';'); |
| _writer.write(cookies[i].getName()); |
| _writer.write('='); |
| _writer.write(cookies[i].getValue()); |
| } |
| _writer.write("\""); |
| } |
| } |
| |
| if (_logLatency) |
| _writer.write(" "+(System.currentTimeMillis()-request.getTimeStamp())); |
| |
| _writer.write(StringUtil.__LINE_SEPARATOR); |
| _writer.flush(); |
| } |
| } |
| catch(IOException e) |
| { |
| log.warn(LogSupport.EXCEPTION,e); |
| } |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** Log Extended fields. |
| * This method can be extended by a derived class to add extened fields to |
| * each log entry. It is called by the log method after all standard |
| * fields have been added, but before the line terminator. |
| * Derived implementations should write extra fields to the Writer |
| * provided. |
| * The default implementation writes the referer and user agent. |
| * @param request The request to log. |
| * @param response The response to log. |
| * @param log The writer to write the extra fields to. |
| * @exception IOException Problem writing log |
| */ |
| protected void logExtended(HttpRequest request, |
| HttpResponse response, |
| Writer log) |
| throws IOException |
| { |
| String referer = request.getField(HttpFields.__Referer); |
| if(referer==null) |
| log.write("\"-\" "); |
| else |
| { |
| log.write('"'); |
| log.write(referer); |
| log.write("\" "); |
| } |
| |
| String agent = request.getField(HttpFields.__UserAgent); |
| if(agent==null) |
| log.write("\"-\""); |
| else |
| { |
| log.write('"'); |
| log.write(agent); |
| log.write('"'); |
| } |
| |
| |
| } |
| } |
| |