io.undertow.server.handlers.accesslog.AccessLogHandler Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source.
* Copyright 2014 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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 io.undertow.server.handlers.accesslog;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import io.undertow.attribute.ExchangeAttribute;
import io.undertow.attribute.ExchangeAttributes;
import io.undertow.attribute.SubstituteEmptyWrapper;
import io.undertow.predicate.Predicate;
import io.undertow.predicate.Predicates;
import io.undertow.server.ExchangeCompletionListener;
import io.undertow.server.HandlerWrapper;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.handlers.builder.HandlerBuilder;
/**
* Access log handler. This handler will generate access log messages based on the provided format string,
* and pass these messages into the provided {@link AccessLogReceiver}.
*
* This handler can log any attribute that is provides via the {@link io.undertow.attribute.ExchangeAttribute}
* mechanism. A general guide to the most common attribute is provided before, however this mechanism is extensible.
*
*
*
This factory produces token handlers for the following patterns
*
* - %a - Remote IP address
*
- %A - Local IP address
*
- %b - Bytes sent, excluding HTTP headers, or '-' if no bytes
* were sent
*
- %B - Bytes sent, excluding HTTP headers
*
- %h - Remote host name
*
- %H - Request protocol
*
- %l - Remote logical username from identd (always returns '-')
*
- %m - Request method
*
- %o - Obfuscated remote IP address (IPv4: last byte removed,
* IPv6: cut off after second colon, ie. '1.2.3.' or 'fe08:44:')
*
- %p - Local port
*
- %q - Query string (excluding the '?' character)
*
- %r - First line of the request
*
- %s - HTTP status code of the response
*
- %t - Date and time, in Common Log Format format
*
- %u - Remote user that was authenticated
*
- %U - Requested URL path
*
- %v - Local server name
*
- %D - Time taken to process the request, in millis
*
- %T - Time taken to process the request, in seconds
*
- %I - current Request thread name (can compare later with stacktraces)
*
* In addition, the caller can specify one of the following aliases for
* commonly utilized patterns:
*
* - common -
%h %l %u %t "%r" %s %b
* - combined -
*
%h %l %u %t "%r" %s %b "%{i,Referer}" "%{i,User-Agent}"
* - commonobf -
%o %l %u %t "%r" %s %b
* - combinedobf -
*
%o %l %u %t "%r" %s %b "%{i,Referer}" "%{i,User-Agent}"
*
*
*
* There is also support to write information from the cookie, incoming
* header, or the session
* It is modeled after the apache syntax:
*
* %{i,xxx}
for incoming headers
* %{o,xxx}
for outgoing response headers
* %{c,xxx}
for a specific cookie
* %{r,xxx}
xxx is an attribute in the ServletRequest
* %{s,xxx}
xxx is an attribute in the HttpSession
*
*
* @author Stuart Douglas
*/
public class AccessLogHandler implements HttpHandler {
private final HttpHandler next;
private final AccessLogReceiver accessLogReceiver;
private final String formatString;
private final ExchangeAttribute tokens;
private final ExchangeCompletionListener exchangeCompletionListener = new AccessLogCompletionListener();
private final Predicate predicate;
public AccessLogHandler(final HttpHandler next, final AccessLogReceiver accessLogReceiver, final String formatString, ClassLoader classLoader) {
this(next, accessLogReceiver, formatString, classLoader, Predicates.truePredicate());
}
public AccessLogHandler(final HttpHandler next, final AccessLogReceiver accessLogReceiver, final String formatString, ClassLoader classLoader, Predicate predicate) {
this.next = next;
this.accessLogReceiver = accessLogReceiver;
this.predicate = predicate;
this.formatString = handleCommonNames(formatString);
this.tokens = ExchangeAttributes.parser(classLoader, new SubstituteEmptyWrapper("-")).parse(this.formatString);
}
public AccessLogHandler(final HttpHandler next, final AccessLogReceiver accessLogReceiver, String formatString, final ExchangeAttribute attribute) {
this(next, accessLogReceiver, formatString, attribute, Predicates.truePredicate());
}
public AccessLogHandler(final HttpHandler next, final AccessLogReceiver accessLogReceiver, String formatString, final ExchangeAttribute attribute, Predicate predicate) {
this.next = next;
this.accessLogReceiver = accessLogReceiver;
this.predicate = predicate;
this.formatString = handleCommonNames(formatString);
this.tokens = attribute;
}
private static String handleCommonNames(String formatString) {
if(formatString.equals("common")) {
return "%h %l %u %t \"%r\" %s %b";
} else if (formatString.equals("combined")) {
return "%h %l %u %t \"%r\" %s %b \"%{i,Referer}\" \"%{i,User-Agent}\"";
} else if(formatString.equals("commonobf")) {
return "%o %l %u %t \"%r\" %s %b";
} else if (formatString.equals("combinedobf")) {
return "%o %l %u %t \"%r\" %s %b \"%{i,Referer}\" \"%{i,User-Agent}\"";
}
return formatString;
}
@Override
public void handleRequest(final HttpServerExchange exchange) throws Exception {
exchange.addExchangeCompleteListener(exchangeCompletionListener);
next.handleRequest(exchange);
}
private class AccessLogCompletionListener implements ExchangeCompletionListener {
@Override
public void exchangeEvent(final HttpServerExchange exchange, final NextListener nextListener) {
try {
if(predicate == null || predicate.resolve(exchange)) {
accessLogReceiver.logMessage(tokens.readAttribute(exchange));
}
} finally {
nextListener.proceed();
}
}
}
@Override
public String toString() {
return "AccessLogHandler{" +
"formatString='" + formatString + '\'' +
'}';
}
public static class Builder implements HandlerBuilder {
@Override
public String name() {
return "access-log";
}
@Override
public Map> parameters() {
Map> params = new HashMap<>();
params.put("format", String.class);
params.put("category", String.class);
return params;
}
@Override
public Set requiredParameters() {
return Collections.singleton("format");
}
@Override
public String defaultParameter() {
return "format";
}
@Override
public HandlerWrapper build(Map config) {
return new Wrapper((String) config.get("format"), (String) config.get("category"));
}
}
private static class Wrapper implements HandlerWrapper {
private final String format;
private final String category;
private Wrapper(String format, String category) {
this.format = format;
this.category = category;
}
@Override
public HttpHandler wrap(HttpHandler handler) {
if (category == null || category.trim().isEmpty()) {
return new AccessLogHandler(handler, new JBossLoggingAccessLogReceiver(), format, Wrapper.class.getClassLoader());
} else {
return new AccessLogHandler(handler, new JBossLoggingAccessLogReceiver(category), format, Wrapper.class.getClassLoader());
}
}
}
}