blob: 5c3298a7aff4669ce5adf5b06a11f68a40765555 (
plain) (
blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.jdisc.http.server.jetty;
import com.google.common.base.Objects;
import com.yahoo.container.logging.AccessLog;
import com.yahoo.container.logging.AccessLogEntry;
import com.yahoo.jdisc.http.servlet.ServletRequest;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.RequestLog;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import javax.servlet.http.HttpServletRequest;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* This class is a bridge between Jetty's {@link org.eclipse.jetty.server.handler.RequestLogHandler}
* and our own configurable access logging in different formats provided by {@link AccessLog}.
*
* @author Oyvind Bakksjo
* @author bjorncs
*/
public class AccessLogRequestLog extends AbstractLifeCycle implements RequestLog {
private static final Logger logger = Logger.getLogger(AccessLogRequestLog.class.getName());
// TODO These hardcoded headers should be provided by config instead
private static final String HEADER_NAME_X_FORWARDED_FOR = "x-forwarded-for";
private static final String HEADER_NAME_Y_RA = "y-ra";
private static final String HEADER_NAME_Y_RP = "y-rp";
private static final String HEADER_NAME_YAHOOREMOTEIP = "yahooremoteip";
private static final String HEADER_NAME_CLIENT_IP = "client-ip";
// HTTP headers that are logged as extra key-value-pairs in access log entries
private static final List<String> LOGGED_REQUEST_HEADERS = List.of("Vespa-Client-Version");
private final AccessLog accessLog;
public AccessLogRequestLog(final AccessLog accessLog) {
this.accessLog = accessLog;
}
@Override
public void log(final Request request, final Response response) {
try {
AccessLogEntry accessLogEntry = Optional.ofNullable(request.getAttribute(JDiscHttpServlet.ATTRIBUTE_NAME_ACCESS_LOG_ENTRY))
.map(AccessLogEntry.class::cast)
.orElseGet(AccessLogEntry::new);
accessLogEntry.setRawPath(request.getRequestURI());
String queryString = request.getQueryString();
if (queryString != null) {
accessLogEntry.setRawQuery(queryString);
}
final String remoteAddress = getRemoteAddress(request);
final int remotePort = getRemotePort(request);
final String peerAddress = request.getRemoteAddr();
final int peerPort = request.getRemotePort();
accessLogEntry.setUserAgent(request.getHeader("User-Agent"));
accessLogEntry.setHttpMethod(request.getMethod());
accessLogEntry.setHostString(request.getHeader("Host"));
accessLogEntry.setReferer(request.getHeader("Referer"));
accessLogEntry.setIpV4Address(peerAddress);
accessLogEntry.setRemoteAddress(remoteAddress);
accessLogEntry.setRemotePort(remotePort);
if (!Objects.equal(remoteAddress, peerAddress)) {
accessLogEntry.setPeerAddress(peerAddress);
}
if (remotePort != peerPort) {
accessLogEntry.setPeerPort(peerPort);
}
accessLogEntry.setHttpVersion(request.getProtocol());
accessLogEntry.setScheme(request.getScheme());
accessLogEntry.setLocalPort(request.getLocalPort());
Principal principal = (Principal) request.getAttribute(ServletRequest.JDISC_REQUEST_PRINCIPAL);
if (principal != null) {
accessLogEntry.setUserPrincipal(principal);
}
X509Certificate[] clientCert = (X509Certificate[]) request.getAttribute(ServletRequest.SERVLET_REQUEST_X509CERT);
if (clientCert != null && clientCert.length > 0) {
accessLogEntry.setSslPrincipal(clientCert[0].getSubjectX500Principal());
}
final long startTime = request.getTimeStamp();
final long endTime = System.currentTimeMillis();
accessLogEntry.setTimeStamp(startTime);
accessLogEntry.setDurationBetweenRequestResponse(endTime - startTime);
accessLogEntry.setReturnedContentSize(response.getHttpChannel().getBytesWritten());
accessLogEntry.setStatusCode(response.getCommittedMetaData().getStatus());
LOGGED_REQUEST_HEADERS.forEach(header -> {
String value = request.getHeader(header);
if (value != null) {
accessLogEntry.addKeyValue(header, value);
}
});
accessLog.log(accessLogEntry);
} catch (Exception e) {
// Catching any exceptions here as it is unclear how Jetty handles exceptions from a RequestLog.
logger.log(Level.SEVERE, "Failed to log access log entry: " + e.getMessage(), e);
}
}
private static String getRemoteAddress(final HttpServletRequest request) {
return Alternative.preferred(request.getHeader(HEADER_NAME_X_FORWARDED_FOR))
.alternatively(() -> request.getHeader(HEADER_NAME_Y_RA))
.alternatively(() -> request.getHeader(HEADER_NAME_YAHOOREMOTEIP))
.alternatively(() -> request.getHeader(HEADER_NAME_CLIENT_IP))
.orElseGet(request::getRemoteAddr);
}
private static int getRemotePort(final HttpServletRequest request) {
return Optional.ofNullable(request.getHeader(HEADER_NAME_Y_RP))
.map(Integer::valueOf)
.orElseGet(request::getRemotePort);
}
}
|