Issue #113 - Added Request Log Writers

Introduced the RequestLog.Writer where a RequestLog takes a writer
which manages what to do with the log strings produced by the RequestLog

deprecated the NCSA and SLF4J RequestLogs in favor of CustomRequestLog

Implemented more format codes in CustomRequestLog

Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
This commit is contained in:
Lachlan Roberts 2018-11-19 13:58:53 +01:00
parent 7dd3cfffe6
commit e4d30b367c
9 changed files with 623 additions and 395 deletions

View File

@ -20,7 +20,6 @@ package org.eclipse.jetty.server;
import java.io.IOException;
import java.util.Locale;
import javax.servlet.http.Cookie;
import org.eclipse.jetty.http.HttpHeader;
@ -28,7 +27,7 @@ import org.eclipse.jetty.http.pathmap.PathMappings;
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.component.ContainerLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -37,20 +36,16 @@ import org.eclipse.jetty.util.log.Logger;
* 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.
* @deprecated use {@link CustomRequestLog} given format string {@link CustomRequestLog#NCSA_FORMAT} with a {@link RequestLog.Writer}
*/
public abstract class AbstractNCSARequestLog extends AbstractLifeCycle implements RequestLog
@Deprecated
public abstract class AbstractNCSARequestLog extends ContainerLifeCycle 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 static ThreadLocal<StringBuilder> _buffers = ThreadLocal.withInitial(() -> new StringBuilder(256));
protected final RequestLog.Writer _requestLogWriter;
private String[] _ignorePaths;
private boolean _extended;
@ -64,7 +59,11 @@ public abstract class AbstractNCSARequestLog extends AbstractLifeCycle implement
private Locale _logLocale = Locale.getDefault();
private String _logTimeZone = "GMT";
/* ------------------------------------------------------------ */
protected AbstractNCSARequestLog(RequestLog.Writer requestLogWriter)
{
this._requestLogWriter = requestLogWriter;
addBean(_requestLogWriter);
}
/**
* Is logging enabled
@ -72,16 +71,15 @@ public abstract class AbstractNCSARequestLog extends AbstractLifeCycle implement
*/
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;
/* ------------------------------------------------------------ */
public void write(String requestEntry) throws IOException
{
_requestLogWriter.write(requestEntry);
}
private void append(StringBuilder buf,String s)
{

View File

@ -18,112 +18,22 @@
package org.eclipse.jetty.server;
import java.io.IOException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.util.BlockingArrayQueue;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/* ------------------------------------------------------------ */
/**
* An asynchronously writing NCSA Request Log
* @deprecated use {@link CustomRequestLog} given format string {@link CustomRequestLog#NCSA_FORMAT} with an {@link AsyncRequestLogWriter}
*/
@Deprecated
public class AsyncNCSARequestLog extends NCSARequestLog
{
private static final Logger LOG = Log.getLogger(AsyncNCSARequestLog.class);
private final BlockingQueue<String> _queue;
private transient WriterThread _thread;
private boolean _warnedFull;
public AsyncNCSARequestLog()
{
this(null,null);
}
public AsyncNCSARequestLog(BlockingQueue<String> queue)
public AsyncNCSARequestLog(String filename, BlockingQueue<String> queue)
{
this(null,queue);
super(new AsyncRequestLogWriter(filename, queue));
}
public AsyncNCSARequestLog(String filename)
{
this(filename,null);
}
public AsyncNCSARequestLog(String filename,BlockingQueue<String> queue)
{
super(filename);
if (queue==null)
queue=new BlockingArrayQueue<>(1024);
_queue=queue;
}
private class WriterThread extends Thread
{
WriterThread()
{
setName("AsyncNCSARequestLog@"+Integer.toString(AsyncNCSARequestLog.this.hashCode(),16));
}
@Override
public void run()
{
while (isRunning())
{
try
{
String log = _queue.poll(10,TimeUnit.SECONDS);
if (log!=null)
AsyncNCSARequestLog.super.write(log);
while(!_queue.isEmpty())
{
log=_queue.poll();
if (log!=null)
AsyncNCSARequestLog.super.write(log);
}
}
catch (IOException e)
{
LOG.warn(e);
}
catch (InterruptedException e)
{
LOG.ignore(e);
}
}
}
}
@Override
protected synchronized void doStart() throws Exception
{
super.doStart();
_thread = new WriterThread();
_thread.start();
}
@Override
protected void doStop() throws Exception
{
_thread.interrupt();
_thread.join();
super.doStop();
_thread=null;
}
@Override
public void write(String log) throws IOException
{
if (!_queue.offer(log))
{
if (_warnedFull)
LOG.warn("Log Queue overflow");
_warnedFull=true;
}
}
}

View File

@ -0,0 +1,95 @@
package org.eclipse.jetty.server;
import java.io.IOException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.util.BlockingArrayQueue;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
public class AsyncRequestLogWriter extends RequestLogWriter
{
private static final Logger LOG = Log.getLogger(AsyncRequestLogWriter.class);
private final BlockingQueue<String> _queue;
private transient AsyncRequestLogWriter.WriterThread _thread;
private boolean _warnedFull;
public AsyncRequestLogWriter()
{
this(null, null);
}
public AsyncRequestLogWriter(String filename,BlockingQueue<String> queue)
{
super(filename);
if (queue==null)
queue=new BlockingArrayQueue<>(1024);
_queue=queue;
}
private class WriterThread extends Thread
{
WriterThread()
{
setName("AsyncRequestLogWriter@"+Integer.toString(AsyncRequestLogWriter.this.hashCode(),16));
}
@Override
public void run()
{
while (isRunning())
{
try
{
String log = _queue.poll(10, TimeUnit.SECONDS);
if (log!=null)
AsyncRequestLogWriter.super.write(log);
while(!_queue.isEmpty())
{
log=_queue.poll();
if (log!=null)
AsyncRequestLogWriter.super.write(log);
}
}
catch (IOException e)
{
LOG.warn(e);
}
catch (InterruptedException e)
{
LOG.ignore(e);
}
}
}
}
@Override
protected synchronized void doStart() throws Exception
{
super.doStart();
_thread = new AsyncRequestLogWriter.WriterThread();
_thread.start();
}
@Override
protected void doStop() throws Exception
{
_thread.interrupt();
_thread.join();
super.doStop();
_thread=null;
}
@Override
public void write(String log) throws IOException
{
if (!_queue.offer(log))
{
if (_warnedFull)
LOG.warn("Log Queue overflow");
_warnedFull=true;
}
}
}

View File

@ -18,7 +18,6 @@
package org.eclipse.jetty.server;
import java.io.IOException;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
@ -27,16 +26,17 @@ import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.Cookie;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.QuotedCSV;
import org.eclipse.jetty.http.pathmap.PathMappings;
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.component.ContainerLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -83,7 +83,10 @@ import static java.lang.invoke.MethodType.methodType;
<tr>
<td valign="top">%{VARNAME}C</td>
<td>The contents of cookie VARNAME in the request sent to the server. Only version 0 cookies are fully supported.</td>
<td>
The contents of cookie VARNAME in the request sent to the server. Only version 0 cookies are fully supported.
Optional VARNAME parameter, without this parameter %C will log all cookies from the request.
</td>
</tr>
<tr>
@ -201,8 +204,11 @@ import static java.lang.invoke.MethodType.methodType;
</tr>
<tr>
<td valign="top">%u</td>
<td>Remote user if the request was authenticated. May be bogus if return status (%s) is 401 (unauthorized).</td>
<td valign="top">%{d}u</td>
<td>
Remote user if the request was authenticated. May be bogus if return status (%s) is 401 (unauthorized).
Optional parameter d, with this parameter deferred authentication will also be checked.
</td>
</tr>
<tr>
@ -228,17 +234,17 @@ import static java.lang.invoke.MethodType.methodType;
<tr>
<td valign="top">%I</td>
<td>Bytes received, including request and headers. Cannot be zero.</td>
<td>Bytes received.</td>
</tr>
<tr>
<td valign="top">%O</td>
<td>Bytes sent, including headers. May be zero in rare cases such as when a request is aborted before a response is sent.</td>
<td>Bytes sent.</td>
</tr>
<tr>
<td valign="top">%S</td>
<td>Bytes transferred (received and sent), including request and headers, cannot be zero. This is the combination of %I and %O.</td>
<td>Bytes transferred (received and sent). This is the combination of %I and %O.</td>
</tr>
<tr>
@ -253,25 +259,29 @@ import static java.lang.invoke.MethodType.methodType;
</table>
*/
public class CustomRequestLog extends AbstractLifeCycle implements RequestLog
public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
{
protected static final Logger LOG = Log.getLogger(CustomRequestLog.class);
//TODO previous NCSA format includes "" in the append, so %C would print out "cookies" if cookies exist and - without the "" if they do not
public static final String NCSA_FORMAT = "%a - %u %t \"%r\" %s %B \"%{Referer}i\" \"%{User-Agent}i\" \"%C\"";
private static ThreadLocal<StringBuilder> _buffers = ThreadLocal.withInitial(() -> new StringBuilder(256));
private String[] _ignorePaths;
private boolean _extended;
private transient PathMappings<String> _ignorePathMap;
private boolean _preferProxiedForAddress;
private transient DateCache _logDateCache;
private String _logDateFormat = "dd/MMM/yyyy:HH:mm:ss ZZZ";
private final static String DEFAULT_DATE_FORMAT = "dd/MMM/yyyy:HH:mm:ss ZZZ";
private Locale _logLocale = Locale.getDefault();
private String _logTimeZone = "GMT";
private RequestLog.Writer _requestLogWriter;
private final MethodHandle _logHandle;
public CustomRequestLog(String formatString)
public CustomRequestLog(RequestLog.Writer writer, String formatString)
{
_requestLogWriter = writer;
addBean(_requestLogWriter);
try
{
_logHandle = getLogHandle(formatString);
@ -286,31 +296,6 @@ public class CustomRequestLog extends AbstractLifeCycle implements RequestLog
}
}
/* ------------------------------------------------------------ */
/**
* Is logging enabled
* @return true if logging is enabled
*/
protected boolean isEnabled()
{
return true;
}
/* ------------------------------------------------------------ */
/**
* Write requestEntry out. (to disk or slf4j log)
* @param requestEntry the request entry
* @throws IOException if unable to write the entry
*/
protected void write(String requestEntry) throws IOException
{
}
/* ------------------------------------------------------------ */
/**
* Writes the request and response information to the output stream.
*
@ -324,16 +309,13 @@ public class CustomRequestLog extends AbstractLifeCycle implements RequestLog
if (_ignorePathMap != null && _ignorePathMap.getMatch(request.getRequestURI()) != null)
return;
if (!isEnabled())
return;
StringBuilder sb = _buffers.get();
sb.setLength(0);
_logHandle.invoke(sb, request, response);
String log = sb.toString();
write(log);
_requestLogWriter.write(log);
}
catch (Throwable e)
{
@ -346,16 +328,23 @@ public class CustomRequestLog extends AbstractLifeCycle implements RequestLog
* @param request The request to extract from
* @return The string to log for authenticated user.
*/
protected String getAuthentication(Request request)
protected static String getAuthentication(Request request, boolean checkDeferred)
{
Authentication authentication = request.getAuthentication();
String name = null;
boolean deferred = false;
if (checkDeferred && authentication instanceof Authentication.Deferred)
{
authentication = ((Authentication.Deferred)authentication).authenticate(request);
deferred = true;
}
if (authentication instanceof Authentication.User)
return ((Authentication.User)authentication).getUserIdentity().getUserPrincipal().getName();
name = ((Authentication.User)authentication).getUserIdentity().getUserPrincipal().getName();
// TODO extract the user name if it is Authentication.Deferred and return as '?username'
return null;
return (name==null) ? null : (deferred ? ("?"+name):name);
}
/**
@ -378,49 +367,6 @@ public class CustomRequestLog extends AbstractLifeCycle implements RequestLog
return _ignorePaths;
}
/**
* 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.
*
@ -429,11 +375,6 @@ public class CustomRequestLog extends AbstractLifeCycle implements RequestLog
@Override
protected synchronized void doStart() throws Exception
{
if (_logDateFormat != null)
{
_logDateCache = new DateCache(_logDateFormat, _logLocale ,_logTimeZone);
}
if (_ignorePaths != null && _ignorePaths.length > 0)
{
_ignorePathMap = new PathMappings<>();
@ -446,34 +387,6 @@ public class CustomRequestLog extends AbstractLifeCycle implements RequestLog
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.
*
@ -529,7 +442,6 @@ public class CustomRequestLog extends AbstractLifeCycle implements RequestLog
append(buf, s);
}
private MethodHandle getLogHandle(String formatString) throws NoSuchMethodException, IllegalAccessException
{
MethodHandle append = MethodHandles.lookup().findStatic(CustomRequestLog.class, "append", methodType(Void.TYPE, String.class, StringBuilder.class));
@ -549,7 +461,6 @@ public class CustomRequestLog extends AbstractLifeCycle implements RequestLog
return logHandle;
}
private static List<Token> getTokens(String formatString)
{
final Pattern PATTERN = Pattern.compile("^(?:%(?<MOD>!?[0-9,]+)?(?:\\{(?<ARG>[^}]+)})?(?<CODE>(?:(?:ti)|(?:to)|[a-zA-Z%]))|(?<LITERAL>[^%]+))(?<REMAINING>.*)");
@ -604,14 +515,7 @@ public class CustomRequestLog extends AbstractLifeCycle implements RequestLog
private static class Token
{
public boolean isLiteralString()
{
return(literal != null);
}
public boolean isPercentCode()
{
return(code != null);
}
//todo make final
public String code = null;
public String arg = null;
public List<String> modifiers = null;
@ -630,6 +534,15 @@ public class CustomRequestLog extends AbstractLifeCycle implements RequestLog
{
this.literal = literal;
}
public boolean isLiteralString()
{
return(literal != null);
}
public boolean isPercentCode()
{
return(code != null);
}
}
@ -665,6 +578,7 @@ public class CustomRequestLog extends AbstractLifeCycle implements RequestLog
{
case "%":
{
//todo use literal
specificHandle = dropArguments(dropArguments(append.bindTo("%"), 1, Request.class), 2, Response.class);
break;
}
@ -713,11 +627,16 @@ public class CustomRequestLog extends AbstractLifeCycle implements RequestLog
case "C":
{
if (arg == null || arg.isEmpty())
throw new IllegalArgumentException("No arg for %C");
String method = "logRequestCookie";
specificHandle = MethodHandles.lookup().findStatic(CustomRequestLog.class, method, logTypeArg);
specificHandle = specificHandle.bindTo(arg);
{
String method = "logRequestCookies";
specificHandle = MethodHandles.lookup().findStatic(CustomRequestLog.class, method, logType);
}
else
{
String method = "logRequestCookie";
specificHandle = MethodHandles.lookup().findStatic(CustomRequestLog.class, method, logTypeArg);
specificHandle = specificHandle.bindTo(arg);
}
break;
}
@ -857,7 +776,7 @@ public class CustomRequestLog extends AbstractLifeCycle implements RequestLog
//todo is this correctly supporting the right formats
DateCache logDateCache;
if (arg == null || arg.isEmpty())
logDateCache = new DateCache(_logDateFormat, _logLocale , _logTimeZone);
logDateCache = new DateCache(DEFAULT_DATE_FORMAT, _logLocale , _logTimeZone);
else
logDateCache = new DateCache(arg, _logLocale , _logTimeZone);
@ -896,7 +815,12 @@ public class CustomRequestLog extends AbstractLifeCycle implements RequestLog
case "u":
{
String method = "logRequestAuthentication";
String method;
if (arg == null || arg.isEmpty())
method = "logRequestAuthenticationWithDeferred";
else
method = "logRequestAuthentication";
specificHandle = MethodHandles.lookup().findStatic(CustomRequestLog.class, method, logType);
break;
}
@ -949,25 +873,23 @@ public class CustomRequestLog extends AbstractLifeCycle implements RequestLog
if (arg == null || arg.isEmpty())
throw new IllegalArgumentException("No arg for %ti");
String method = "logRequestTrailerLines";
String method = "logRequestTrailer";
specificHandle = MethodHandles.lookup().findStatic(CustomRequestLog.class, method, logTypeArg);
specificHandle = specificHandle.bindTo(arg);
break;
}
case "to":
{
if (arg == null || arg.isEmpty())
throw new IllegalArgumentException("No arg for %to");
String method = "logResponseTrailerLines";
String method = "logResponseTrailer";
specificHandle = MethodHandles.lookup().findStatic(CustomRequestLog.class, method, logTypeArg);
specificHandle = specificHandle.bindTo(arg);
break;
}
default:
throw new IllegalArgumentException("Unsupported code %" + code);
}
@ -1000,8 +922,7 @@ public class CustomRequestLog extends AbstractLifeCycle implements RequestLog
private static void logConnectionIP(StringBuilder b, Request request, Response response)
{
//todo don't think this is correct
append(b, request.getHeader(HttpHeader.X_FORWARDED_FOR.toString()));
append(b, request.getHttpChannel().getEndPoint().getRemoteAddress().getAddress().getHostAddress());
}
private static void logLocalIP(StringBuilder b, Request request, Response response)
@ -1012,18 +933,20 @@ public class CustomRequestLog extends AbstractLifeCycle implements RequestLog
private static void logResponseSize(StringBuilder b, Request request, Response response)
{
long written = response.getHttpChannel().getBytesWritten();
b.append(Long.toString(written));
b.append(written);
}
private static void logResponseSizeCLF(StringBuilder b, Request request, Response response)
{
long written = response.getHttpChannel().getBytesWritten();
b.append((written==0) ? "-" : Long.toString(written));
if (written==0)
b.append('-');
else
b.append(written);
}
private static void logRequestCookie(String arg, StringBuilder b, Request request, Response response)
{
//todo should this be logging full cookie or its value, can you log multiple cookies, can multiple cookies have the same name
for (Cookie c : request.getCookies())
{
if (arg.equals(c.getName()))
@ -1033,7 +956,25 @@ public class CustomRequestLog extends AbstractLifeCycle implements RequestLog
}
}
b.append("-");
b.append('-');
}
private static void logRequestCookies(StringBuilder b, Request request, Response response)
{
Cookie[] cookies = request.getCookies();
if (cookies == null || cookies.length == 0)
b.append("-");
else
{
for (int i = 0; i < cookies.length; i++)
{
if (i != 0)
b.append(';');
b.append(cookies[i].getName());
b.append('=');
b.append(cookies[i].getValue());
}
}
}
private static void logEnvironmentVar(String arg, StringBuilder b, Request request, Response response)
@ -1043,8 +984,8 @@ public class CustomRequestLog extends AbstractLifeCycle implements RequestLog
private static void logFilename(StringBuilder b, Request request, Response response)
{
//TODO probably wrong, getRealPath?
append(b, request.getContextPath());
//TODO verify
append(b, request.getServletContext().getRealPath(request.getPathInfo()));
}
private static void logRemoteHostName(StringBuilder b, Request request, Response response)
@ -1059,14 +1000,16 @@ public class CustomRequestLog extends AbstractLifeCycle implements RequestLog
private static void logRequestHeader(String arg, StringBuilder b, Request request, Response response)
{
//TODO does this need to do multiple headers 'request.getHeaders(arg)'
append(b, request.getHeader(arg));
}
private static void logKeepAliveRequests(StringBuilder b, Request request, Response response)
{
//todo verify this "number of requests on this connection? what about http2?"
b.append(request.getHttpChannel().getRequests());
long requests = request.getHttpChannel().getConnection().getMessagesIn();
if (requests >= 0)
b.append(requests);
else
b.append('-');
}
private static void logRequestMethod(StringBuilder b, Request request, Response response)
@ -1076,27 +1019,22 @@ public class CustomRequestLog extends AbstractLifeCycle implements RequestLog
private static void logResponseHeader(String arg, StringBuilder b, Request request, Response response)
{
//TODO does this need to do multiple headers 'Response.getHeaders(arg)'
append(b, response.getHeader(arg));
}
private static void logCanonicalPort(StringBuilder b, Request request, Response response)
{
//todo implement
append(b, "?");
b.append(request.getServerPort());
}
private static void logLocalPort(StringBuilder b, Request request, Response response)
{
//todo implement
append(b, "?");
b.append(request.getLocalPort());
}
private static void logRemotePort(StringBuilder b, Request request, Response response)
{
//todo implement
append(b, "?");
b.append(request.getRemotePort());
}
private static void logQueryString(StringBuilder b, Request request, Response response)
@ -1106,8 +1044,11 @@ public class CustomRequestLog extends AbstractLifeCycle implements RequestLog
private static void logRequestFirstLine(StringBuilder b, Request request, Response response)
{
//todo is there a better way to do this
append(b, request.getMethod() + " " + request.getOriginalURI() + " " + request.getProtocol());
append(b, request.getMethod());
b.append(" ");
append(b, request.getOriginalURI());
b.append(" ");
append(b, request.getProtocol());
}
private static void logRequestHandler(StringBuilder b, Request request, Response response)
@ -1118,16 +1059,20 @@ public class CustomRequestLog extends AbstractLifeCycle implements RequestLog
private static void logResponseStatus(StringBuilder b, Request request, Response response)
{
b.append(response.getStatus());
//todo can getCommittedMetaData be null? check what happens when its aborted
b.append(response.getCommittedMetaData().getStatus());
}
private static void logRequestTime(DateCache dateCache, StringBuilder b, Request request, Response response)
{
b.append(dateCache.format(request.getTimeStamp()));
b.append('[');
append(b, dateCache.format(request.getTimeStamp()));
b.append(']');
}
private static void logLatencyMicroseconds(StringBuilder b, Request request, Response response)
{
//todo can we use nanotime?
long latency = System.currentTimeMillis() - request.getTimeStamp();
b.append(TimeUnit.MILLISECONDS.toMicros(latency));
}
@ -1140,20 +1085,22 @@ public class CustomRequestLog extends AbstractLifeCycle implements RequestLog
private static void logLatencySeconds(StringBuilder b, Request request, Response response)
{
//todo should this give decimal places
long latency = System.currentTimeMillis() - request.getTimeStamp();
b.append(TimeUnit.MILLISECONDS.toSeconds(latency));
}
private static void logRequestAuthentication(StringBuilder b, Request request, Response response)
{
//todo implement
append(b, "?");
append(b, getAuthentication(request, false));
}
private static void logRequestAuthenticationWithDeferred(StringBuilder b, Request request, Response response)
{
append(b, getAuthentication(request, true));
}
private static void logUrlRequestPath(StringBuilder b, Request request, Response response)
{
//todo verify if this contains the query string
append(b, request.getRequestURI());
}
@ -1164,19 +1111,18 @@ public class CustomRequestLog extends AbstractLifeCycle implements RequestLog
private static void logConnectionStatus(StringBuilder b, Request request, Response response)
{
//todo implement
append(b, "?");
b.append(request.getHttpChannel().isResponseCompleted() ? (request.getHttpChannel().isPersistent() ? '+' : '-') : 'X');
}
private static void logBytesReceived(StringBuilder b, Request request, Response response)
{
//todo implement
append(b, "?");
//todo should this be content received rather than consumed
b.append(request.getHttpInput().getContentConsumed());
}
private static void logBytesSent(StringBuilder b, Request request, Response response)
{
//todo implement
//todo redirect to logResponseSize
append(b, "?");
}
@ -1186,15 +1132,28 @@ public class CustomRequestLog extends AbstractLifeCycle implements RequestLog
append(b, "?");
}
private static void logRequestTrailerLines(String arg, StringBuilder b, Request request, Response response)
private static void logRequestTrailer(String arg, StringBuilder b, Request request, Response response)
{
//todo implement
append(b, "?");
HttpFields trailers = request.getTrailers();
if (trailers != null)
append(b, trailers.get(arg));
else
b.append('-');
}
private static void logResponseTrailerLines(String arg, StringBuilder b, Request request, Response response)
private static void logResponseTrailer(String arg, StringBuilder b, Request request, Response response)
{
//todo implement
append(b, "?");
Supplier<HttpFields> supplier = response.getTrailers();
if (supplier != null)
{
HttpFields trailers = supplier.get();
if (trailers != null)
append(b, trailers.get(arg));
else
b.append('-');
}
else
b.append("-");
}
}

View File

@ -19,13 +19,9 @@
package org.eclipse.jetty.server;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.TimeZone;
import org.eclipse.jetty.util.RolloverFileOutputStream;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
@ -36,18 +32,13 @@ import org.eclipse.jetty.util.annotation.ManagedObject;
* 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.
* @deprecated use {@link CustomRequestLog} given format string {@link CustomRequestLog#NCSA_FORMAT} with a {@link RequestLogWriter}
*/
@Deprecated
@ManagedObject("NCSA standard format request log")
public class NCSARequestLog extends AbstractNCSARequestLog
{
private String _filename;
private boolean _append;
private int _retainDays;
private boolean _closeOut;
private String _filenameDateFormat = null;
private transient OutputStream _out;
private transient OutputStream _fileOut;
private transient Writer _writer;
private final RequestLogWriter _requestLogWriter;
/* ------------------------------------------------------------ */
/**
@ -55,9 +46,7 @@ public class NCSARequestLog extends AbstractNCSARequestLog
*/
public NCSARequestLog()
{
setExtended(true);
_append = true;
_retainDays = 31;
this((String)null);
}
/* ------------------------------------------------------------ */
@ -70,10 +59,18 @@ public class NCSARequestLog extends AbstractNCSARequestLog
*/
public NCSARequestLog(String filename)
{
this(new RequestLogWriter(filename));
}
/* ------------------------------------------------------------ */
/**
* Create request log object with specified output file name.
*/
public NCSARequestLog(RequestLogWriter writer)
{
super(writer);
_requestLogWriter = writer;
setExtended(true);
_append = true;
_retainDays = 31;
setFilename(filename);
}
/* ------------------------------------------------------------ */
@ -87,13 +84,15 @@ public class NCSARequestLog extends AbstractNCSARequestLog
*/
public void setFilename(String filename)
{
if (filename != null)
{
filename = filename.trim();
if (filename.length() == 0)
filename = null;
}
_filename = filename;
_requestLogWriter.setFilename(filename);
}
/* ------------------------------------------------------------ */
@Override
public void setLogTimeZone(String tz)
{
super.setLogTimeZone(tz);
_requestLogWriter.setTimeZone(tz);
}
/* ------------------------------------------------------------ */
@ -105,7 +104,7 @@ public class NCSARequestLog extends AbstractNCSARequestLog
@ManagedAttribute("file of log")
public String getFilename()
{
return _filename;
return _requestLogWriter.getFileName();
}
/* ------------------------------------------------------------ */
@ -118,16 +117,14 @@ public class NCSARequestLog extends AbstractNCSARequestLog
*/
public String getDatedFilename()
{
if (_fileOut instanceof RolloverFileOutputStream)
return ((RolloverFileOutputStream)_fileOut).getDatedFilename();
return null;
return _requestLogWriter.getDatedFilename();
}
/* ------------------------------------------------------------ */
@Override
protected boolean isEnabled()
{
return (_fileOut != null);
return _requestLogWriter.isEnabled();
}
/* ------------------------------------------------------------ */
@ -138,7 +135,7 @@ public class NCSARequestLog extends AbstractNCSARequestLog
*/
public void setRetainDays(int retainDays)
{
_retainDays = retainDays;
_requestLogWriter.setRetainDays(retainDays);
}
/* ------------------------------------------------------------ */
@ -150,7 +147,7 @@ public class NCSARequestLog extends AbstractNCSARequestLog
@ManagedAttribute("number of days that log files are kept")
public int getRetainDays()
{
return _retainDays;
return _requestLogWriter.getRetainDays();
}
/* ------------------------------------------------------------ */
@ -162,7 +159,7 @@ public class NCSARequestLog extends AbstractNCSARequestLog
*/
public void setAppend(boolean append)
{
_append = append;
_requestLogWriter.setAppend(append);
}
/* ------------------------------------------------------------ */
@ -174,7 +171,7 @@ public class NCSARequestLog extends AbstractNCSARequestLog
@ManagedAttribute("existing log files are appends to the new one")
public boolean isAppend()
{
return _append;
return _requestLogWriter.isAppend();
}
/* ------------------------------------------------------------ */
@ -186,7 +183,7 @@ public class NCSARequestLog extends AbstractNCSARequestLog
*/
public void setFilenameDateFormat(String logFileDateFormat)
{
_filenameDateFormat = logFileDateFormat;
_requestLogWriter.setFilenameDateFormat(logFileDateFormat);
}
/* ------------------------------------------------------------ */
@ -197,21 +194,14 @@ public class NCSARequestLog extends AbstractNCSARequestLog
*/
public String getFilenameDateFormat()
{
return _filenameDateFormat;
return _requestLogWriter.getFilenameDateFormat();
}
/* ------------------------------------------------------------ */
@Override
public void write(String requestEntry) throws IOException
{
synchronized(this)
{
if (_writer==null)
return;
_writer.write(requestEntry);
_writer.write(StringUtil.__LINE_SEPARATOR);
_writer.flush();
}
_requestLogWriter.write(requestEntry);
}
/* ------------------------------------------------------------ */
@ -223,21 +213,6 @@ public class NCSARequestLog extends AbstractNCSARequestLog
@Override
protected synchronized void doStart() throws Exception
{
if (_filename != null)
{
_fileOut = new RolloverFileOutputStream(_filename,_append,_retainDays,TimeZone.getTimeZone(getLogTimeZone()),_filenameDateFormat,null);
_closeOut = true;
LOG.info("Opened " + getDatedFilename());
}
else
_fileOut = System.err;
_out = _fileOut;
synchronized(this)
{
_writer = new OutputStreamWriter(_out);
}
super.doStart();
}
@ -253,29 +228,6 @@ public class NCSARequestLog extends AbstractNCSARequestLog
synchronized (this)
{
super.doStop();
try
{
if (_writer != null)
_writer.flush();
}
catch (IOException e)
{
LOG.ignore(e);
}
if (_out != null && _closeOut)
try
{
_out.close();
}
catch (IOException e)
{
LOG.ignore(e);
}
_out = null;
_fileOut = null;
_closeOut = false;
_writer = null;
}
}
}

View File

@ -18,6 +18,8 @@
package org.eclipse.jetty.server;
import java.io.IOException;
import org.eclipse.jetty.server.handler.RequestLogHandler;
/**
@ -28,7 +30,6 @@ import org.eclipse.jetty.server.handler.RequestLogHandler;
*/
public interface RequestLog
{
/* ------------------------------------------------------------ */
/**
* @param request The request to log.
* @param response The response to log. Note that for some requests
@ -37,5 +38,29 @@ public interface RequestLog
* log information it is best to consult {@link Response#getCommittedMetaData()}
* and {@link Response#getHttpChannel()} directly.
*/
public void log(Request request, Response response);
void log(Request request, Response response);
interface Writer
{
void write(String requestEntry) throws IOException;
}
class Collection implements RequestLog
{
private final RequestLog[] _logs;
public Collection(RequestLog... logs)
{
_logs = logs;
}
@Override
public void log(Request request, Response response)
{
for (RequestLog log : _logs)
{
log.log(request, response);
}
}
}
}

View File

@ -0,0 +1,245 @@
package org.eclipse.jetty.server;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.TimeZone;
import org.eclipse.jetty.util.RolloverFileOutputStream;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
public class RequestLogWriter extends AbstractLifeCycle implements RequestLog.Writer
{
private static final Logger LOG = Log.getLogger(RequestLogWriter.class);
private String _filename;
private boolean _append;
private int _retainDays;
private boolean _closeOut;
private String _timeZone = "GMT";
private String _filenameDateFormat = null;
private transient OutputStream _out;
private transient OutputStream _fileOut;
private transient Writer _writer;
public RequestLogWriter()
{
this(null);
}
public RequestLogWriter(String filename)
{
setAppend(true);
setRetainDays(31);
if(filename != null)
setFilename(filename);
}
/**
* Set the output file name of the request log.
* The file name may be in the format expected by
* {@link RolloverFileOutputStream}.
*
* @param filename file name of the request log
*
*/
public void setFilename(String filename)
{
if (filename != null)
{
filename = filename.trim();
if (filename.length() == 0)
filename = null;
}
_filename = filename;
}
/**
* Retrieve the output file name of the request log.
*
* @return file name of the request log
*/
public String getFileName()
{
return _filename;
}
/**
* Retrieve the file name of the request log with the expanded
* date wildcard if the output is written to the disk using
* {@link RolloverFileOutputStream}.
*
* @return file name of the request log, or null if not applicable
*/
public String getDatedFilename()
{
if (_fileOut instanceof RolloverFileOutputStream)
return ((RolloverFileOutputStream)_fileOut).getDatedFilename();
return null;
}
protected boolean isEnabled()
{
return (_fileOut != null);
}
/**
* Set the number of days before rotated log files are deleted.
*
* @param retainDays number of days to keep a log file
*/
public void setRetainDays(int retainDays)
{
_retainDays = retainDays;
}
/**
* Retrieve the number of days before rotated log files are deleted.
*
* @return number of days to keep a log file
*/
public int getRetainDays()
{
return _retainDays;
}
/**
* Set append to log flag.
*
* @param append true - request log file will be appended after restart,
* false - request log file will be overwritten after restart
*/
public void setAppend(boolean append)
{
_append = append;
}
/**
* Retrieve append to log flag.
*
* @return value of the flag
*/
public boolean isAppend()
{
return _append;
}
/**
* Set the log file name date format.
* @see RolloverFileOutputStream#RolloverFileOutputStream(String, boolean, int, TimeZone, String, String)
*
* @param logFileDateFormat format string that is passed to {@link RolloverFileOutputStream}
*/
public void setFilenameDateFormat(String logFileDateFormat)
{
_filenameDateFormat = logFileDateFormat;
}
/* ------------------------------------------------------------ */
/**
* Retrieve the file name date format string.
*
* @return the log File Date Format
*/
public String getFilenameDateFormat()
{
return _filenameDateFormat;
}
/* ------------------------------------------------------------ */
@Override
public void write(String requestEntry) throws IOException
{
synchronized(this)
{
if (_writer==null)
return;
_writer.write(requestEntry);
_writer.write(StringUtil.__LINE_SEPARATOR);
_writer.flush();
}
}
/* ------------------------------------------------------------ */
/**
* Set up request logging and open log file.
*
* @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
*/
@Override
protected synchronized void doStart() throws Exception
{
if (_filename != null)
{
_fileOut = new RolloverFileOutputStream(_filename,_append,_retainDays,TimeZone.getTimeZone(getTimeZone()),_filenameDateFormat,null);
_closeOut = true;
LOG.info("Opened " + getDatedFilename());
}
else
_fileOut = System.err;
_out = _fileOut;
synchronized(this)
{
_writer = new OutputStreamWriter(_out);
}
super.doStart();
}
public void setTimeZone(String timeZone)
{
_timeZone = timeZone;
}
public String getTimeZone()
{
return _timeZone;
}
/* ------------------------------------------------------------ */
/**
* Close the log file and perform cleanup.
*
* @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop()
*/
@Override
protected void doStop() throws Exception
{
synchronized (this)
{
super.doStop();
try
{
if (_writer != null)
_writer.flush();
}
catch (IOException e)
{
LOG.ignore(e);
}
if (_out != null && _closeOut)
try
{
_out.close();
}
catch (IOException e)
{
LOG.ignore(e);
}
_out = null;
_fileOut = null;
_closeOut = false;
_writer = null;
}
}
}

View File

@ -21,49 +21,47 @@ package org.eclipse.jetty.server;
import java.io.IOException;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.log.Slf4jLog;
/**
* Implementation of NCSARequestLog where output is sent as a SLF4J INFO Log message on the named logger "org.eclipse.jetty.server.RequestLog"
* @deprecated use {@link CustomRequestLog} given format string {@link CustomRequestLog#NCSA_FORMAT} with an {@link Slf4jRequestLogWriter}
*/
@Deprecated
@ManagedObject("NCSA standard format request log to slf4j bridge")
public class Slf4jRequestLog extends AbstractNCSARequestLog
{
private Slf4jLog logger;
private String loggerName;
private final Slf4jRequestLogWriter _requestLogWriter;
public Slf4jRequestLog()
{
// Default logger name (can be set)
this.loggerName = "org.eclipse.jetty.server.RequestLog";
this(new Slf4jRequestLogWriter());
}
public Slf4jRequestLog(Slf4jRequestLogWriter writer)
{
super(writer);
_requestLogWriter = writer;
}
public void setLoggerName(String loggerName)
{
this.loggerName = loggerName;
_requestLogWriter.setLoggerName(loggerName);
}
public String getLoggerName()
{
return loggerName;
return _requestLogWriter.getLoggerName();
}
@Override
protected boolean isEnabled()
{
return logger != null;
return _requestLogWriter.isEnabled();
}
@Override
public void write(String requestEntry) throws IOException
{
logger.info(requestEntry);
}
@Override
protected synchronized void doStart() throws Exception
{
logger = new Slf4jLog(loggerName);
super.doStart();
_requestLogWriter.write(requestEntry);
}
}

View File

@ -0,0 +1,46 @@
package org.eclipse.jetty.server;
import java.io.IOException;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.log.Slf4jLog;
public class Slf4jRequestLogWriter extends AbstractLifeCycle implements RequestLog.Writer
{
private Slf4jLog logger;
private String loggerName;
public Slf4jRequestLogWriter()
{
// Default logger name (can be set)
this.loggerName = "org.eclipse.jetty.server.RequestLog";
}
public void setLoggerName(String loggerName)
{
this.loggerName = loggerName;
}
public String getLoggerName()
{
return loggerName;
}
protected boolean isEnabled()
{
return logger != null;
}
@Override
public void write(String requestEntry) throws IOException
{
logger.info(requestEntry);
}
@Override
protected synchronized void doStart() throws Exception
{
logger = new Slf4jLog(loggerName);
super.doStart();
}
}