// ======================================================================== // $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 net.lightbody.bmp.proxy.jetty.http; import net.lightbody.bmp.proxy.jetty.log.LogFactory; import net.lightbody.bmp.proxy.jetty.util.DateCache; import net.lightbody.bmp.proxy.jetty.util.LogSupport; import net.lightbody.bmp.proxy.jetty.util.RolloverFileOutputStream; import net.lightbody.bmp.proxy.jetty.util.StringUtil; import org.apache.commons.logging.Log; import javax.servlet.http.Cookie; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.util.Locale; import java.util.TimeZone; /* ------------------------------------------------------------ */ /** 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 net.lightbody.bmp.proxy.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('"'); } } }