| // |
| // ======================================================================== |
| // Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. |
| // ------------------------------------------------------------------------ |
| // All rights reserved. This program and the accompanying materials |
| // are made available under the terms of the Eclipse Public License v1.0 |
| // and Apache License v2.0 which accompanies this distribution. |
| // |
| // The Eclipse Public License is available at |
| // http://www.eclipse.org/legal/epl-v10.html |
| // |
| // The Apache License v2.0 is available at |
| // http://www.opensource.org/licenses/apache2.0.php |
| // |
| // You may elect to redistribute this code under either of these licenses. |
| // ======================================================================== |
| // |
| |
| package org.eclipse.jetty.server; |
| |
| import java.io.IOException; |
| import java.util.Locale; |
| |
| import javax.servlet.http.Cookie; |
| |
| import org.eclipse.jetty.http.HttpHeader; |
| import org.eclipse.jetty.http.PathMap; |
| import org.eclipse.jetty.server.handler.StatisticsHandler; |
| import org.eclipse.jetty.util.DateCache; |
| import org.eclipse.jetty.util.annotation.ManagedAttribute; |
| import org.eclipse.jetty.util.component.AbstractLifeCycle; |
| import org.eclipse.jetty.util.log.Log; |
| import org.eclipse.jetty.util.log.Logger; |
| |
| /** |
| * Base implementation of the {@link RequestLog} outputs logs in the pseudo-standard NCSA common log format. |
| * Configuration options allow a choice between the standard Common Log Format (as used in the 3 log format) and the |
| * Combined Log Format (single log format). This log format can be output by most web servers, and almost all web log |
| * analysis software can understand these formats. |
| */ |
| public abstract class AbstractNCSARequestLog extends AbstractLifeCycle implements RequestLog |
| { |
| protected static final Logger LOG = Log.getLogger(AbstractNCSARequestLog.class); |
| |
| private static ThreadLocal<StringBuilder> _buffers = new ThreadLocal<StringBuilder>() |
| { |
| @Override |
| protected StringBuilder initialValue() |
| { |
| return new StringBuilder(256); |
| } |
| }; |
| |
| |
| private String[] _ignorePaths; |
| private boolean _extended; |
| private transient PathMap<String> _ignorePathMap; |
| private boolean _logLatency = false; |
| private boolean _logCookies = false; |
| private boolean _logServer = false; |
| private boolean _preferProxiedForAddress; |
| private transient DateCache _logDateCache; |
| private String _logDateFormat = "dd/MMM/yyyy:HH:mm:ss Z"; |
| private Locale _logLocale = Locale.getDefault(); |
| private String _logTimeZone = "GMT"; |
| |
| /* ------------------------------------------------------------ */ |
| |
| /** |
| * Is logging enabled |
| * @return true if logging is enabled |
| */ |
| protected abstract boolean isEnabled(); |
| |
| /* ------------------------------------------------------------ */ |
| |
| /** |
| * Write requestEntry out. (to disk or slf4j log) |
| * @param requestEntry the request entry |
| * @throws IOException if unable to write the entry |
| */ |
| public abstract void write(String requestEntry) throws IOException; |
| |
| /* ------------------------------------------------------------ */ |
| |
| private void append(StringBuilder buf,String s) |
| { |
| if (s==null || s.length()==0) |
| buf.append('-'); |
| else |
| buf.append(s); |
| } |
| |
| /** |
| * Writes the request and response information to the output stream. |
| * |
| * @see org.eclipse.jetty.server.RequestLog#log(Request, Response) |
| */ |
| @Override |
| public void log(Request request, Response response) |
| { |
| try |
| { |
| if (_ignorePathMap != null && _ignorePathMap.getMatch(request.getRequestURI()) != null) |
| return; |
| |
| if (!isEnabled()) |
| return; |
| |
| StringBuilder buf = _buffers.get(); |
| buf.setLength(0); |
| |
| if (_logServer) |
| { |
| append(buf,request.getServerName()); |
| buf.append(' '); |
| } |
| |
| String addr = null; |
| if (_preferProxiedForAddress) |
| { |
| addr = request.getHeader(HttpHeader.X_FORWARDED_FOR.toString()); |
| } |
| |
| if (addr == null) |
| addr = request.getRemoteAddr(); |
| |
| buf.append(addr); |
| buf.append(" - "); |
| Authentication authentication = request.getAuthentication(); |
| append(buf,(authentication instanceof Authentication.User)?((Authentication.User)authentication).getUserIdentity().getUserPrincipal().getName():null); |
| |
| buf.append(" ["); |
| if (_logDateCache != null) |
| buf.append(_logDateCache.format(request.getTimeStamp())); |
| else |
| buf.append(request.getTimeStamp()); |
| |
| buf.append("] \""); |
| append(buf,request.getMethod()); |
| buf.append(' '); |
| append(buf,request.getHttpURI().toString()); |
| buf.append(' '); |
| append(buf,request.getProtocol()); |
| buf.append("\" "); |
| |
| int status = response.getCommittedMetaData().getStatus(); |
| if (status >=0) |
| { |
| buf.append((char)('0' + ((status / 100) % 10))); |
| buf.append((char)('0' + ((status / 10) % 10))); |
| buf.append((char)('0' + (status % 10))); |
| } |
| else |
| buf.append(status); |
| |
| long written = response.getHttpChannel().getBytesWritten(); |
| if (written >= 0) |
| { |
| buf.append(' '); |
| if (written > 99999) |
| buf.append(written); |
| else |
| { |
| if (written > 9999) |
| buf.append((char)('0' + ((written / 10000) % 10))); |
| if (written > 999) |
| buf.append((char)('0' + ((written / 1000) % 10))); |
| if (written > 99) |
| buf.append((char)('0' + ((written / 100) % 10))); |
| if (written > 9) |
| buf.append((char)('0' + ((written / 10) % 10))); |
| buf.append((char)('0' + (written) % 10)); |
| } |
| buf.append(' '); |
| } |
| else |
| buf.append(" - "); |
| |
| |
| if (_extended) |
| logExtended(buf, request, response); |
| |
| if (_logCookies) |
| { |
| Cookie[] cookies = request.getCookies(); |
| if (cookies == null || cookies.length == 0) |
| buf.append(" -"); |
| else |
| { |
| buf.append(" \""); |
| for (int i = 0; i < cookies.length; i++) |
| { |
| if (i != 0) |
| buf.append(';'); |
| buf.append(cookies[i].getName()); |
| buf.append('='); |
| buf.append(cookies[i].getValue()); |
| } |
| buf.append('\"'); |
| } |
| } |
| |
| if (_logLatency) |
| { |
| long now = System.currentTimeMillis(); |
| |
| if (_logLatency) |
| { |
| buf.append(' '); |
| buf.append(now - request.getTimeStamp()); |
| } |
| } |
| |
| String log = buf.toString(); |
| write(log); |
| } |
| catch (IOException e) |
| { |
| LOG.warn(e); |
| } |
| } |
| |
| /** |
| * @param request the HTTP request to log |
| * @param b the build to write logs into |
| * @throws IOException for no reason |
| * @deprecated override {@link #logExtended(StringBuilder, Request, Response)} instead |
| */ |
| @Deprecated |
| protected void logExtended(Request request, StringBuilder b) throws IOException |
| { |
| String referer = request.getHeader(HttpHeader.REFERER.toString()); |
| if (referer == null) |
| b.append("\"-\" "); |
| else |
| { |
| b.append('"'); |
| b.append(referer); |
| b.append("\" "); |
| } |
| |
| String agent = request.getHeader(HttpHeader.USER_AGENT.toString()); |
| if (agent == null) |
| b.append("\"-\""); |
| else |
| { |
| b.append('"'); |
| b.append(agent); |
| b.append('"'); |
| } |
| } |
| |
| /** |
| * Writes extended request and response information to the output stream. |
| * |
| * @param b StringBuilder to write to |
| * @param request request object |
| * @param response response object |
| * @throws IOException if unable to log the extended information |
| */ |
| protected void logExtended(StringBuilder b, Request request, Response response) throws IOException |
| { |
| logExtended(request, b); |
| } |
| |
| /** |
| * Set request paths that will not be logged. |
| * |
| * @param ignorePaths array of request paths |
| */ |
| public void setIgnorePaths(String[] ignorePaths) |
| { |
| _ignorePaths = ignorePaths; |
| } |
| |
| /** |
| * Retrieve the request paths that will not be logged. |
| * |
| * @return array of request paths |
| */ |
| public String[] getIgnorePaths() |
| { |
| return _ignorePaths; |
| } |
| |
| /** |
| * Controls logging of the request cookies. |
| * |
| * @param logCookies true - values of request cookies will be logged, false - values of request cookies will not be |
| * logged |
| */ |
| public void setLogCookies(boolean logCookies) |
| { |
| _logCookies = logCookies; |
| } |
| |
| /** |
| * Retrieve log cookies flag |
| * |
| * @return value of the flag |
| */ |
| public boolean getLogCookies() |
| { |
| return _logCookies; |
| } |
| |
| /** |
| * Controls logging of the request hostname. |
| * |
| * @param logServer true - request hostname will be logged, false - request hostname will not be logged |
| */ |
| public void setLogServer(boolean logServer) |
| { |
| _logServer = logServer; |
| } |
| |
| /** |
| * Retrieve log hostname flag. |
| * |
| * @return value of the flag |
| */ |
| public boolean getLogServer() |
| { |
| return _logServer; |
| } |
| |
| /** |
| * Controls logging of request processing time. |
| * |
| * @param logLatency true - request processing time will be logged false - request processing time will not be |
| * logged |
| */ |
| public void setLogLatency(boolean logLatency) |
| { |
| _logLatency = logLatency; |
| } |
| |
| /** |
| * Retrieve log request processing time flag. |
| * |
| * @return value of the flag |
| */ |
| public boolean getLogLatency() |
| { |
| return _logLatency; |
| } |
| |
| /** |
| * @param value true to log dispatch |
| * @deprecated use {@link StatisticsHandler} |
| */ |
| @Deprecated |
| public void setLogDispatch(boolean value) |
| { |
| } |
| |
| /** |
| * @return true if logging dispatches |
| * @deprecated use {@link StatisticsHandler} |
| */ |
| @Deprecated |
| public boolean isLogDispatch() |
| { |
| return false; |
| } |
| |
| /** |
| * Controls whether the actual IP address of the connection or the IP address from the X-Forwarded-For header will |
| * be logged. |
| * |
| * @param preferProxiedForAddress true - IP address from header will be logged, false - IP address from the |
| * connection will be logged |
| */ |
| public void setPreferProxiedForAddress(boolean preferProxiedForAddress) |
| { |
| _preferProxiedForAddress = preferProxiedForAddress; |
| } |
| |
| /** |
| * Retrieved log X-Forwarded-For IP address flag. |
| * |
| * @return value of the flag |
| */ |
| public boolean getPreferProxiedForAddress() |
| { |
| return _preferProxiedForAddress; |
| } |
| |
| /** |
| * Set the extended request log format flag. |
| * |
| * @param extended true - log the extended request information, false - do not log the extended request information |
| */ |
| public void setExtended(boolean extended) |
| { |
| _extended = extended; |
| } |
| |
| /** |
| * Retrieve the extended request log format flag. |
| * |
| * @return value of the flag |
| */ |
| @ManagedAttribute("use extended NCSA format") |
| public boolean isExtended() |
| { |
| return _extended; |
| } |
| |
| /** |
| * Set up request logging and open log file. |
| * |
| * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart() |
| */ |
| @Override |
| protected synchronized void doStart() throws Exception |
| { |
| if (_logDateFormat != null) |
| { |
| _logDateCache = new DateCache(_logDateFormat, _logLocale ,_logTimeZone); |
| } |
| |
| 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; |
| |
| super.doStart(); |
| } |
| |
| @Override |
| protected void doStop() throws Exception |
| { |
| _logDateCache = null; |
| super.doStop(); |
| } |
| |
| /** |
| * Set the timestamp format for request log entries in the file. If this is not set, the pre-formated request |
| * timestamp is used. |
| * |
| * @param format timestamp format string |
| */ |
| public void setLogDateFormat(String format) |
| { |
| _logDateFormat = format; |
| } |
| |
| /** |
| * Retrieve the timestamp format string for request log entries. |
| * |
| * @return timestamp format string. |
| */ |
| public String getLogDateFormat() |
| { |
| return _logDateFormat; |
| } |
| |
| /** |
| * Set the locale of the request log. |
| * |
| * @param logLocale locale object |
| */ |
| public void setLogLocale(Locale logLocale) |
| { |
| _logLocale = logLocale; |
| } |
| |
| /** |
| * Retrieve the locale of the request log. |
| * |
| * @return locale object |
| */ |
| public Locale getLogLocale() |
| { |
| return _logLocale; |
| } |
| |
| /** |
| * Set the timezone of the request log. |
| * |
| * @param tz timezone string |
| */ |
| public void setLogTimeZone(String tz) |
| { |
| _logTimeZone = tz; |
| } |
| |
| /** |
| * Retrieve the timezone of the request log. |
| * |
| * @return timezone string |
| */ |
| @ManagedAttribute("the timezone") |
| public String getLogTimeZone() |
| { |
| return _logTimeZone; |
| } |
| } |