All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jetty.server.CustomRequestLog Maven / Gradle / Ivy

There is a newer version: 12.1.0.alpha0
Show newest version
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.server;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import java.util.function.BiPredicate;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.eclipse.jetty.http.HttpCookie;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.QuotedCSV;
import org.eclipse.jetty.http.pathmap.PathMappings;
import org.eclipse.jetty.util.DateCache;
import org.eclipse.jetty.util.NanoTime;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.resource.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static java.lang.invoke.MethodHandles.dropArguments;
import static java.lang.invoke.MethodHandles.foldArguments;
import static java.lang.invoke.MethodType.methodType;

/**
 * 

A flexible RequestLog, which produces log strings in a customizable format.

*

The Logger takes a format string where request characteristics can be added using "%" format codes which are * replaced by the corresponding value in the log output.

*

The terms server, client, local and remote are used to refer to the different addresses and ports * which can be logged. Server and client refer to the logical addresses which can be modified in the request * headers. Where local and remote refer to the physical addresses which may be a proxy between the * end-user and the server.

* * *

Format codes are specified with the syntax %MODIFIERS{PARAM}CODE as follows:

*
*
MODIFIERS
*
Optional list of comma separated HTTP status codes which may be preceded by a single "!" to indicate * negation. If the status code is not in the list the literal string "-" will be logged instead of * the resulting value from the percent code.
*
{PARAM}
*
Parameter string which may be optional depending on the percent code used.
*
CODE
*
A one or two character code specified by the table of format codes below.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Format Codes
Format StringDescription
X *

The X character.

*
%% *

The percent character.

*
%{format}a *

The address or host name.

*

Valid format values are: "server", "client", "local", "remote". * The format parameter is optional and defaults to "server".

*

Values "server" and "client" are the logical addresses which can be modified in the request headers, * while "local" and "remote" are the physical addresses so may be the addresses of a proxy between * the end-user and the server.

*
%{format}p *

The port.

*

Valid format values are: "server", "client", "local", "remote". * The format parameter is optional and defaults to "server".

*

Values "server" and "client" are the logical ports which can be modified in the request headers, * while "local" and "remote" are the physical ports so may be the ports of a proxy between * the end-user and the server.

*
%{CLF}I *

The size of request in bytes, excluding HTTP headers.

*

The parameter is optional. * When the parameter value is "CLF" the Common Log Format is used, i.e. a {@code -} rather than a {@code 0} * when no bytes are present.

*
%{CLF}O *

The size of response in bytes, excluding HTTP headers.

*

The parameter is optional. * When the parameter value is "CLF" the Common Log Format is used, i.e. a {@code -} rather than a {@code 0} * when no bytes are present.

*
%{CLF}S *

The bytes transferred (received and sent). This is the combination of {@code %I} and {@code %O}.

*

The parameter is optional. * When the parameter value is "CLF" the Common Log Format is used, i.e. a {@code -} rather than a {@code 0} * when no bytes are present.

*
%{VARNAME}C *

The value of the request cookie VARNAME.

*

The parameter is optional. * Only version 0 cookies are fully supported. * When the parameter is missing, all request cookies will be logged.

*
%D *

The time taken to serve the request, in microseconds.

*
%{VARNAME}e *

The value of the environment variable VARNAME.

*
%f *

The file system path of the requested resource.

*
%H *

The name and version of the request protocol, such as "HTTP/1.1".

*
%{VARNAME}i *

The value of the VARNAME request header.

*
%k *

The number of requests handled on a connection.

*

The initial request on a connection yields a value 0, the first request after the initial on the same connection * yields the value 1, the second request on the same connection yields the value 2, etc.

*
%m *

The HTTP request method.

*
%{VARNAME}o *

The value of the VARNAME response header.

*
%q *

The query string, prepended with a ? if a query string exists, otherwise an empty string.

*
%r *

First line of an HTTP/1.1 request (or equivalent information for HTTP/2 or later).

*
%R *

The name of the Handler or Servlet generating the response (if any).

*
%s *

The HTTP response status code.

*
%{format|timeZone|locale}t *

The time at which the request was received.

*

The parameter is optional and may have the following values: {format}, {format|timeZone} or {format|timeZone|locale}.

*
*
format
*
Default is e.g. [18/Sep/2011:19:18:28 -0400] where the last number indicates the timezone offset from GMT. * Must be in a format supported by the {@code java.time} package.
*
timeZone
*
Default is GMT. * Must be in a format supported by the {@code java.time} package.
*
locale
*
Default is the JVM default locale. * Must be in a format supported by {@code java.util.Locale.forLanguageTag()}.
*
*
%{UNIT}T *

The time taken to serve the request.

*

The parameter UNIT is optional and defaults to "s". * The parameter UNIT indicates the unit of time: "s" for seconds, "ms" for milliseconds, "us" for microseconds. * %{us}T is identical to {@code %D}.

*
%{d}u *

The remote user if the request was authenticated with servlet authentication.

*

May be an invalid value if response status code ({@code %s}) is 401 (unauthorized).

*

The parameter is optional. * When the parameter value is "d", deferred authentication will also be checked.

*
%U *

The URL path requested, not including any query string.

*
%X *

The connection status when response is completed:

*
*
X
*
The connection is aborted before the response completed.
*
+
*
The connection may be kept alive after the response is sent.
*
-
*
The connection will be closed after the response is sent.
*
*
%{VARNAME}ti *

The value of the VARNAME request trailer.

*
%{VARNAME}to *

The value of the VARNAME response trailer.

*
%{OPTION}uri *

The request URI.

*

The parameter is optional and may have the be one of the following options:

*
*
%uri
*
The entire request URI.
*
%{-query}uri
*
The entire request URI without the query.
*
%{-path,-query}uri
*
The request URI without path or query (so just `scheme://authority`).
*
%{scheme}uri
*
The scheme of the request URI.
*
%{authority}uri
*
The authority of the request URI.
*
%{path}uri
*
The path of the request URI.
*
%{query}uri
*
The query of the request URI.
*
%{host}uri
*
The host of the request URI.
*
%{port}uri
*
The port of the request URI.
*
*
%{attributeName}attr *

The value of the request attribute with the given name.

*
* */ @ManagedObject("Custom format request log") public class CustomRequestLog extends ContainerLifeCycle implements RequestLog { /** * Record holding extra detail for logging * @param handlerName The name of the entity that handled the request * @param realPath The real path on the filesystem represented by the request */ public record LogDetail(String handlerName, String realPath) { } public static final String DEFAULT_DATE_FORMAT = "dd/MMM/yyyy:HH:mm:ss ZZZ"; public static final String NCSA_FORMAT = "%{client}a - %u %t \"%r\" %s %O"; public static final String EXTENDED_NCSA_FORMAT = NCSA_FORMAT + " \"%{Referer}i\" \"%{User-Agent}i\""; public static final String LOG_DETAIL = CustomRequestLog.class.getName() + ".logDetail"; private static final Logger LOG = LoggerFactory.getLogger(CustomRequestLog.class); private static final ThreadLocal _buffers = ThreadLocal.withInitial(() -> new StringBuilder(256)); private static final Pattern PATTERN = Pattern.compile("^(?:%(?!?[0-9,]+)?(?:\\{(?[^}]+)})?(?(?:(?:ti)|(?:to)|(?:uri)|(?:attr)|[a-zA-Z%]))|(?[^%]+))(?.*)", Pattern.DOTALL | Pattern.MULTILINE); private final RequestLog.Writer _requestLogWriter; private final MethodHandle _logHandle; private final String _formatString; private transient PathMappings _ignorePathMap; private String[] _ignorePaths; private BiPredicate _filter; public CustomRequestLog() { this(new Slf4jRequestLogWriter(), EXTENDED_NCSA_FORMAT); } public CustomRequestLog(String file) { this(file, EXTENDED_NCSA_FORMAT); } public CustomRequestLog(String file, String format) { this(new RequestLogWriter(file), format); } public CustomRequestLog(RequestLog.Writer writer, String formatString) { _formatString = formatString; _requestLogWriter = writer; installBean(_requestLogWriter); try { _logHandle = getLogHandle(formatString); } catch (NoSuchMethodException | IllegalAccessException e) { throw new IllegalStateException(e); } } /** * This allows you to set a custom filter to decide whether to log a request or omit it from the request log. * This filter is evaluated after path filtering is applied from {@link #setIgnorePaths(String[])}. * @param filter - a BiPredicate which returns true if this request should be logged. */ public void setFilter(BiPredicate filter) { _filter = filter; } @ManagedAttribute("The RequestLogWriter") public RequestLog.Writer getWriter() { return _requestLogWriter; } @Override public void log(Request request, Response response) { try { if (_ignorePathMap != null && _ignorePathMap.getMatched(request.getHttpURI().getCanonicalPath()) != null) return; if (_filter != null && !_filter.test(request, response)) return; StringBuilder sb = _buffers.get(); sb.setLength(0); _logHandle.invoke(sb, request, response); String log = sb.toString(); _requestLogWriter.write(log); } catch (Throwable e) { LOG.warn("Unable to log request", e); } } /** * 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; } /** * Retrieve the format string. * * @return the format string */ @ManagedAttribute("format string") public String getFormatString() { return _formatString; } /** * Set up request logging and open log file. * * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart() */ @Override protected void doStart() throws Exception { if (_ignorePaths != null && _ignorePaths.length > 0) { _ignorePathMap = new PathMappings<>(); for (String ignorePath : _ignorePaths) { _ignorePathMap.put(ignorePath, ignorePath); } } else _ignorePathMap = null; super.doStart(); } private static void append(StringBuilder buf, String s) { if (s == null || s.isEmpty()) buf.append('-'); else buf.append(s); } private static void append(String s, StringBuilder buf) { append(buf, s); } private MethodHandle getLogHandle(String formatString) throws NoSuchMethodException, IllegalAccessException { MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodHandle append = lookup.findStatic(CustomRequestLog.class, "append", methodType(void.class, String.class, StringBuilder.class)); MethodHandle logHandle = lookup.findStatic(CustomRequestLog.class, "logNothing", methodType(void.class, StringBuilder.class, Request.class, Response.class)); List tokens = getTokens(formatString); Collections.reverse(tokens); for (Token t : tokens) { if (t.isLiteralString()) logHandle = updateLogHandle(logHandle, append, t.literal); else if (t.isPercentCode()) logHandle = updateLogHandle(logHandle, append, lookup, t.code, t.arg, t.modifiers, t.negated); else throw new IllegalStateException("bad token " + t); } return logHandle; } private static List getTokens(String formatString) { /* Extracts literal strings and percent codes out of the format string. We will either match a percent code of the format %MODIFIERS{PARAM}CODE, or a literal string until the next percent code or the end of the formatString is reached. where MODIFIERS is an optional comma separated list of numbers. {PARAM} is an optional string parameter to the percent code. CODE is a 1 to 2 character string corresponding to a format code. */ List tokens = new ArrayList<>(); String remaining = formatString; while (remaining.length() > 0) { Matcher m = PATTERN.matcher(remaining); if (m.matches()) { if (m.group("CODE") != null) { String code = m.group("CODE"); String arg = m.group("ARG"); String modifierString = m.group("MOD"); List modifiers = null; boolean negated = false; if (modifierString != null) { if (modifierString.startsWith("!")) { modifierString = modifierString.substring(1); negated = true; } modifiers = new QuotedCSV(modifierString) .getValues() .stream() .map(Integer::parseInt) .collect(Collectors.toList()); } tokens.add(new Token(code, arg, modifiers, negated)); } else if (m.group("LITERAL") != null) { String literal = m.group("LITERAL"); tokens.add(new Token(literal)); } else { throw new IllegalStateException("formatString parsing error: " + formatString); } remaining = m.group("REMAINING"); } else { throw new IllegalArgumentException("Invalid format string: " + formatString); } } return tokens; } private static class Token { public final String code; public final String arg; public final List modifiers; public final boolean negated; public final String literal; public Token(String code, String arg, List modifiers, boolean negated) { this.code = code; this.arg = arg; this.modifiers = modifiers; this.negated = negated; this.literal = null; } public Token(String literal) { this.code = null; this.arg = null; this.modifiers = null; this.negated = false; this.literal = literal; } public boolean isLiteralString() { return (literal != null); } public boolean isPercentCode() { return (code != null); } } @SuppressWarnings("unused") private static boolean modify(List modifiers, Boolean negated, StringBuilder b, Request request, Response response) { if (negated) return !modifiers.contains(response.getStatus()); else return modifiers.contains(response.getStatus()); } private MethodHandle updateLogHandle(MethodHandle logHandle, MethodHandle append, String literal) { return foldArguments(logHandle, dropArguments(dropArguments(append.bindTo(literal), 1, Request.class), 2, Response.class)); } private MethodHandle updateLogHandle(MethodHandle logHandle, MethodHandle append, MethodHandles.Lookup lookup, String code, String arg, List modifiers, boolean negated) throws NoSuchMethodException, IllegalAccessException { MethodType logType = methodType(void.class, StringBuilder.class, Request.class, Response.class); MethodType logTypeArg = methodType(void.class, String.class, StringBuilder.class, Request.class, Response.class); //TODO should we throw IllegalArgumentExceptions when given arguments for codes which do not take them MethodHandle specificHandle = switch (code) { case "%" -> dropArguments(dropArguments(append.bindTo("%"), 1, Request.class), 2, Response.class); case "a" -> { if (StringUtil.isEmpty(arg)) arg = "server"; String method = switch (arg) { case "server" -> "logServerHost"; case "client" -> "logClientHost"; case "local" -> "logLocalHost"; case "remote" -> "logRemoteHost"; default -> throw new IllegalArgumentException("Invalid arg for %a"); }; yield lookup.findStatic(CustomRequestLog.class, method, logType); } case "p" -> { if (StringUtil.isEmpty(arg)) arg = "server"; String method = switch (arg) { case "server" -> "logServerPort"; case "client" -> "logClientPort"; case "local" -> "logLocalPort"; case "remote" -> "logRemotePort"; default -> throw new IllegalArgumentException("Invalid arg for %p"); }; yield lookup.findStatic(CustomRequestLog.class, method, logType); } case "I" -> { String method; if (StringUtil.isEmpty(arg)) method = "logBytesReceived"; else if (arg.equalsIgnoreCase("clf")) method = "logBytesReceivedCLF"; else throw new IllegalArgumentException("Invalid argument for %I"); yield lookup.findStatic(CustomRequestLog.class, method, logType); } case "O" -> { String method; if (StringUtil.isEmpty(arg)) method = "logBytesSent"; else if (arg.equalsIgnoreCase("clf")) method = "logBytesSentCLF"; else throw new IllegalArgumentException("Invalid argument for %O"); yield lookup.findStatic(CustomRequestLog.class, method, logType); } case "S" -> { String method; if (StringUtil.isEmpty(arg)) method = "logBytesTransferred"; else if (arg.equalsIgnoreCase("clf")) method = "logBytesTransferredCLF"; else throw new IllegalArgumentException("Invalid argument for %S"); yield lookup.findStatic(CustomRequestLog.class, method, logType); } case "C" -> { if (StringUtil.isEmpty(arg)) { yield lookup.findStatic(CustomRequestLog.class, "logRequestCookies", logType); } else { yield lookup.findStatic(CustomRequestLog.class, "logRequestCookie", logTypeArg).bindTo(arg); } } case "D" -> lookup.findStatic(CustomRequestLog.class, "logLatencyMicroseconds", logType); case "e" -> { if (StringUtil.isEmpty(arg)) throw new IllegalArgumentException("No arg for %e"); yield lookup.findStatic(CustomRequestLog.class, "logEnvironmentVar", logTypeArg).bindTo(arg); } case "f" -> lookup.findStatic(CustomRequestLog.class, "logFilename", logType); case "H" -> lookup.findStatic(CustomRequestLog.class, "logRequestProtocol", logType); case "i" -> { if (StringUtil.isEmpty(arg)) throw new IllegalArgumentException("No arg for %i"); yield lookup.findStatic(CustomRequestLog.class, "logRequestHeader", logTypeArg).bindTo(arg); } case "k" -> lookup.findStatic(CustomRequestLog.class, "logKeepAliveRequests", logType); case "m" -> lookup.findStatic(CustomRequestLog.class, "logRequestMethod", logType); case "o" -> { if (StringUtil.isEmpty(arg)) throw new IllegalArgumentException("No arg for %o"); yield lookup.findStatic(CustomRequestLog.class, "logResponseHeader", logTypeArg).bindTo(arg); } case "q" -> lookup.findStatic(CustomRequestLog.class, "logQueryString", logType); case "r" -> lookup.findStatic(CustomRequestLog.class, "logRequestFirstLine", logType); case "R" -> lookup.findStatic(CustomRequestLog.class, "logRequestHandler", logType); case "s" -> lookup.findStatic(CustomRequestLog.class, "logResponseStatus", logType); case "t" -> { String format = DEFAULT_DATE_FORMAT; TimeZone timeZone = TimeZone.getTimeZone("GMT"); Locale locale = Locale.getDefault(); if (arg != null && !arg.isEmpty()) { String[] args = arg.split("\\|"); switch (args.length) { case 1 -> format = args[0]; case 2 -> { format = args[0]; timeZone = TimeZone.getTimeZone(args[1]); } case 3 -> { format = args[0]; timeZone = TimeZone.getTimeZone(args[1]); locale = Locale.forLanguageTag(args[2]); } default -> throw new IllegalArgumentException("Too many \"|\" characters in %t"); } } DateCache logDateCache = new DateCache(format, locale, timeZone); MethodType logTypeDateCache = methodType(void.class, DateCache.class, StringBuilder.class, Request.class, Response.class); yield lookup.findStatic(CustomRequestLog.class, "logRequestTime", logTypeDateCache).bindTo(logDateCache); } case "T" -> { if (arg == null) arg = "s"; String method = switch (arg) { case "s" -> "logLatencySeconds"; case "us" -> "logLatencyMicroseconds"; case "ms" -> "logLatencyMilliseconds"; default -> throw new IllegalArgumentException("Invalid arg for %T"); }; yield lookup.findStatic(CustomRequestLog.class, method, logType); } case "u" -> { String method; if (StringUtil.isEmpty(arg)) method = "logRequestAuthentication"; else if ("d".equals(arg)) method = "logRequestAuthenticationWithDeferred"; else throw new IllegalArgumentException("Invalid arg for %u: " + arg); yield lookup.findStatic(CustomRequestLog.class, method, logType); } case "U" -> lookup.findStatic(CustomRequestLog.class, "logUrlRequestPath", logType); case "X" -> lookup.findStatic(CustomRequestLog.class, "logConnectionStatus", logType); case "ti" -> { if (StringUtil.isEmpty(arg)) throw new IllegalArgumentException("No arg for %ti"); yield lookup.findStatic(CustomRequestLog.class, "logRequestTrailer", logTypeArg).bindTo(arg); } case "to" -> { if (StringUtil.isEmpty(arg)) throw new IllegalArgumentException("No arg for %to"); yield lookup.findStatic(CustomRequestLog.class, "logResponseTrailer", logTypeArg).bindTo(arg); } case "uri" -> { if (arg == null) arg = ""; String method = switch (arg) { case "" -> "logRequestHttpUri"; case "-query" -> "logRequestHttpUriWithoutQuery"; case "-path,-query" -> "logRequestHttpUriWithoutPathQuery"; case "scheme" -> "logRequestScheme"; case "authority" -> "logRequestAuthority"; case "path" -> "logUrlRequestPath"; case "query" -> "logQueryString"; case "host" -> "logRequestHttpUriHost"; case "port" -> "logRequestHttpUriPort"; default -> throw new IllegalArgumentException("Invalid arg for %uri"); }; yield lookup.findStatic(CustomRequestLog.class, method, logType); } case "attr" -> { MethodType logRequestAttribute = methodType(void.class, String.class, StringBuilder.class, Request.class, Response.class); yield lookup.findStatic(CustomRequestLog.class, "logRequestAttribute", logRequestAttribute).bindTo(arg); } default -> throw new IllegalArgumentException("Unsupported code %" + code); }; if (modifiers != null && !modifiers.isEmpty()) { MethodHandle dash = updateLogHandle(logHandle, append, "-"); MethodHandle log = foldArguments(logHandle, specificHandle); MethodHandle modifierTest = lookup.findStatic(CustomRequestLog.class, "modify", methodType(Boolean.TYPE, List.class, Boolean.class, StringBuilder.class, Request.class, Response.class)); modifierTest = modifierTest.bindTo(modifiers).bindTo(negated); return MethodHandles.guardWithTest(modifierTest, log, dash); } return foldArguments(logHandle, specificHandle); } //-----------------------------------------------------------------------------------// @SuppressWarnings("unused") private static void logNothing(StringBuilder b, Request request, Response response) { } @SuppressWarnings("unused") private static void logServerHost(StringBuilder b, Request request, Response response) { append(b, Request.getServerName(request)); } @SuppressWarnings("unused") private static void logClientHost(StringBuilder b, Request request, Response response) { append(b, Request.getRemoteAddr(request)); } @SuppressWarnings("unused") private static void logLocalHost(StringBuilder b, Request request, Response response) { // Unwrap to bypass any customizers append(b, Request.getLocalAddr(Request.unWrap(request))); } @SuppressWarnings("unused") private static void logRemoteHost(StringBuilder b, Request request, Response response) { // Unwrap to bypass any customizers append(b, Request.getRemoteAddr(Request.unWrap(request))); } @SuppressWarnings("unused") private static void logServerPort(StringBuilder b, Request request, Response response) { b.append(Request.getServerPort(request)); } @SuppressWarnings("unused") private static void logClientPort(StringBuilder b, Request request, Response response) { b.append(Request.getRemotePort(request)); } @SuppressWarnings("unused") private static void logLocalPort(StringBuilder b, Request request, Response response) { // Unwrap to bypass any customizers b.append(Request.getLocalPort(Request.unWrap(request))); } @SuppressWarnings("unused") private static void logRemotePort(StringBuilder b, Request request, Response response) { // Unwrap to bypass any customizers b.append(Request.getRemotePort(Request.unWrap(request))); } @SuppressWarnings("unused") private static void logResponseSize(StringBuilder b, Request request, Response response) { b.append(Response.getContentBytesWritten(response)); } @SuppressWarnings("unused") private static void logResponseSizeCLF(StringBuilder b, Request request, Response response) { long written = Response.getContentBytesWritten(response); if (written == 0) b.append('-'); else b.append(written); } @SuppressWarnings("unused") private static void logBytesSent(StringBuilder b, Request request, Response response) { b.append(Response.getContentBytesWritten(response)); } @SuppressWarnings("unused") private static void logBytesSentCLF(StringBuilder b, Request request, Response response) { long sent = Response.getContentBytesWritten(response); if (sent == 0) b.append('-'); else b.append(sent); } @SuppressWarnings("unused") private static void logBytesReceived(StringBuilder b, Request request, Response response) { b.append(Request.getContentBytesRead(request)); } @SuppressWarnings("unused") private static void logBytesReceivedCLF(StringBuilder b, Request request, Response response) { long received = Request.getContentBytesRead(request); if (received == 0) b.append('-'); else b.append(received); } @SuppressWarnings("unused") private static void logBytesTransferred(StringBuilder b, Request request, Response response) { b.append(Request.getContentBytesRead(request) + Response.getContentBytesWritten(response)); } @SuppressWarnings("unused") private static void logBytesTransferredCLF(StringBuilder b, Request request, Response response) { long transferred = Request.getContentBytesRead(request) + Response.getContentBytesWritten(response); if (transferred == 0) b.append('-'); else b.append(transferred); } @SuppressWarnings("unused") private static void logRequestCookie(String arg, StringBuilder b, Request request, Response response) { List cookies = Request.getCookies(request); if (cookies != null) { for (HttpCookie c : cookies) { if (arg.equals(c.getName())) { b.append(c.getValue()); return; } } } b.append('-'); } @SuppressWarnings("unused") private static void logRequestCookies(StringBuilder b, Request request, Response response) { List cookies = Request.getCookies(request); if (cookies == null || cookies.size() == 0) { b.append('-'); } else { for (int i = 0; i < cookies.size(); i++) { if (i != 0) b.append(';'); b.append(cookies.get(i).getName()); b.append('='); b.append(cookies.get(i).getValue()); } } } @SuppressWarnings("unused") private static void logEnvironmentVar(String arg, StringBuilder b, Request request, Response response) { append(b, System.getenv(arg)); } @SuppressWarnings("unused") private static void logFilename(StringBuilder b, Request request, Response response) { LogDetail logDetail = (LogDetail)request.getAttribute(LOG_DETAIL); if (logDetail == null || logDetail.realPath == null) { Context context = request.getContext(); Resource baseResource = context.getBaseResource(); if (baseResource != null) { String fileName = baseResource.resolve(Request.getPathInContext(request)).getName(); append(b, fileName); } else { b.append("-"); } } else { b.append(logDetail.realPath); } } @SuppressWarnings("unused") private static void logRequestProtocol(StringBuilder b, Request request, Response response) { append(b, request.getConnectionMetaData().getProtocol()); } @SuppressWarnings("unused") private static void logRequestHeader(String arg, StringBuilder b, Request request, Response response) { append(b, request.getHeaders().get(arg)); } @SuppressWarnings("unused") private static void logKeepAliveRequests(StringBuilder b, Request request, Response response) { long requests = request.getConnectionMetaData().getConnection().getMessagesIn(); if (requests >= 0) b.append(requests); else b.append('-'); } @SuppressWarnings("unused") private static void logRequestMethod(StringBuilder b, Request request, Response response) { append(b, request.getMethod()); } @SuppressWarnings("unused") private static void logResponseHeader(String arg, StringBuilder b, Request request, Response response) { append(b, response.getHeaders().get(arg)); } @SuppressWarnings("unused") private static void logQueryString(StringBuilder b, Request request, Response response) { append(b, "?" + request.getHttpURI().getQuery()); } @SuppressWarnings("unused") private static void logRequestFirstLine(StringBuilder b, Request request, Response response) { append(b, request.getMethod()); b.append(" "); append(b, request.getHttpURI().getPathQuery()); b.append(" "); append(b, request.getConnectionMetaData().getProtocol()); } @SuppressWarnings("unused") private static void logRequestHandler(StringBuilder b, Request request, Response response) { LogDetail logDetail = (LogDetail)request.getAttribute(LOG_DETAIL); append(b, logDetail == null ? null : logDetail.handlerName); } @SuppressWarnings("unused") private static void logResponseStatus(StringBuilder b, Request request, Response response) { b.append(response.getStatus()); } @SuppressWarnings("unused") private static void logRequestTime(DateCache dateCache, StringBuilder b, Request request, Response response) { b.append('['); append(b, dateCache.format(Request.getTimeStamp(request))); b.append(']'); } @SuppressWarnings("unused") private static void logLatencyMicroseconds(StringBuilder b, Request request, Response response) { logLatency(b, request, TimeUnit.MICROSECONDS); } @SuppressWarnings("unused") private static void logLatencyMilliseconds(StringBuilder b, Request request, Response response) { logLatency(b, request, TimeUnit.MILLISECONDS); } @SuppressWarnings("unused") private static void logLatencySeconds(StringBuilder b, Request request, Response response) { logLatency(b, request, TimeUnit.SECONDS); } private static void logLatency(StringBuilder b, Request request, TimeUnit unit) { b.append(unit.convert(NanoTime.since(request.getBeginNanoTime()), TimeUnit.NANOSECONDS)); } @SuppressWarnings("unused") private static void logRequestAuthentication(StringBuilder b, Request request, Response response) { Request.AuthenticationState authenticationState = Request.getAuthenticationState(request); Principal userPrincipal = authenticationState == null ? null : authenticationState.getUserPrincipal(); append(b, userPrincipal == null ? null : userPrincipal.getName()); } @SuppressWarnings("unused") private static void logRequestAuthenticationWithDeferred(StringBuilder b, Request request, Response response) { // TODO: deferred to be implemented. logRequestAuthentication(b, request, response); } @SuppressWarnings("unused") private static void logUrlRequestPath(StringBuilder b, Request request, Response response) { append(b, request.getHttpURI().getPath()); } @SuppressWarnings("unused") private static void logConnectionStatus(StringBuilder b, Request request, Response response) { b.append(response.isCompletedSuccessfully() ? (request.getConnectionMetaData().isPersistent() ? '+' : '-') : 'X'); } @SuppressWarnings("unused") private static void logRequestTrailer(String arg, StringBuilder b, Request request, Response response) { HttpFields trailers = request.getTrailers(); if (trailers != null) append(b, trailers.get(arg)); else b.append('-'); } @SuppressWarnings("unused") private static void logResponseTrailer(String arg, StringBuilder b, Request request, Response response) { Supplier supplier = response.getTrailersSupplier(); HttpFields trailers = supplier == null ? null : supplier.get(); if (trailers != null) append(b, trailers.get(arg)); else b.append('-'); } @SuppressWarnings("unused") private static void logRequestAuthority(StringBuilder b, Request request, Response response) { HttpURI httpURI = request.getHttpURI(); if (httpURI.hasAuthority()) append(b, httpURI.getAuthority()); else b.append('-'); } @SuppressWarnings("unused") private static void logRequestScheme(StringBuilder b, Request request, Response response) { append(b, request.getHttpURI().getScheme()); } @SuppressWarnings("unused") private static void logRequestHttpUri(StringBuilder b, Request request, Response response) { append(b, request.getHttpURI().toString()); } @SuppressWarnings("unused") private static void logRequestHttpUriWithoutQuery(StringBuilder b, Request request, Response response) { HttpURI.Mutable uri = HttpURI.build(request.getHttpURI()).query(null); append(b, uri.toString()); } @SuppressWarnings("unused") private static void logRequestHttpUriWithoutPathQuery(StringBuilder b, Request request, Response response) { // HttpURI doesn't support null path so we do this manually. HttpURI httpURI = request.getHttpURI(); if (httpURI.getScheme() != null) b.append(httpURI.getScheme()).append(':'); if (httpURI.getHost() != null) { b.append("//"); if (httpURI.getUser() != null) b.append(httpURI.getUser()).append('@'); b.append(httpURI.getHost()); } int normalizedPort = URIUtil.normalizePortForScheme(httpURI.getScheme(), httpURI.getPort()); if (normalizedPort > 0) b.append(':').append(normalizedPort); } @SuppressWarnings("unused") private static void logRequestHttpUriHost(StringBuilder b, Request request, Response response) { append(b, request.getHttpURI().getHost()); } @SuppressWarnings("unused") private static void logRequestHttpUriPort(StringBuilder b, Request request, Response response) { b.append(request.getHttpURI().getPort()); } @SuppressWarnings("unused") private static void logRequestAttribute(String arg, StringBuilder b, Request request, Response response) { Object attribute = request.getAttribute(arg); if (attribute != null) append(b, attribute.toString()); else b.append('-'); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy