Core server component of Eclipse Jetty web server providing HTTP server functionality, request handling, and connection management
—
Request logging provides comprehensive HTTP request and response logging with customizable formats, file rotation, asynchronous writing, and integration with SLF4J logging frameworks.
The core interface for logging HTTP requests and responses.
public interface RequestLog {
// Log a request/response pair
void log(Request request, Response response);
// Nested interface for log writers
interface Writer {
void write(String requestEntry) throws IOException;
}
}Flexible request logger with customizable log formats and output destinations.
public class CustomRequestLog extends ContainerLifeCycle implements RequestLog {
// Constructors
public CustomRequestLog();
public CustomRequestLog(RequestLog.Writer writer);
public CustomRequestLog(RequestLog.Writer writer, String format);
public CustomRequestLog(String filename);
public CustomRequestLog(String filename, String format);
// Format configuration
public void setLogFormat(String format);
public String getLogFormat();
public void setLogTimeZone(String timeZone);
public String getLogTimeZone();
// Writer configuration
public RequestLog.Writer getWriter();
public void setWriter(RequestLog.Writer writer);
// Cookie logging
public String[] getLogCookies();
public void setLogCookies(String[] logCookies);
// Latency logging
public boolean isLogLatency();
public void setLogLatency(boolean logLatency);
// Server information
public boolean isLogServer();
public void setLogServer(boolean logServer);
// Request logging
public void log(Request request, Response response);
}public class BasicRequestLogging {
public void setupRequestLogging(Server server) {
// Create request log writer to file
RequestLogWriter logWriter = new RequestLogWriter("logs/access.log");
// Configure log rotation
logWriter.setRetainDays(30); // Keep 30 days of logs
logWriter.setAppend(true); // Append to existing log
// Create custom request log with NCSA format
CustomRequestLog requestLog = new CustomRequestLog(logWriter);
requestLog.setLogFormat(CustomRequestLog.NCSA_FORMAT);
// Set timezone for timestamps
requestLog.setLogTimeZone("UTC");
// Configure what to log
requestLog.setLogLatency(true); // Include response time
requestLog.setLogServer(true); // Include server info
requestLog.setLogCookies(new String[]{"JSESSIONID"}); // Log specific cookies
// Assign to server
server.setRequestLog(requestLog);
}
}public class LogFormatExamples {
public void demonstrateLogFormats() {
// NCSA Common Log Format
String ncsaFormat = CustomRequestLog.NCSA_FORMAT;
// %{client}a - %u %t "%r" %>s %O
// Extended NCSA format
String extendedFormat = CustomRequestLog.EXTENDED_NCSA_FORMAT;
// %{client}a - %u %t "%r" %>s %O "%{Referer}i" "%{User-Agent}i"
// Custom format with response time and additional fields
String customFormat = "%{client}a - %u %t \"%r\" %>s %O %D \"%{Referer}i\" \"%{User-Agent}i\"";
CustomRequestLog requestLog = new CustomRequestLog();
requestLog.setLogFormat(customFormat);
}
public void explainFormatTokens() {
/*
* Format tokens:
* %a - Remote IP address
* %{client}a - Client IP address (real client behind proxy)
* %A - Local IP address
* %b - Bytes sent (CLF format)
* %B - Bytes sent
* %D - Time taken to process request (microseconds)
* %h - Remote host name
* %H - Request protocol
* %l - Remote logical username (identd)
* %m - Request method
* %O - Bytes sent including headers
* %q - Query string (prepended with ?)
* %r - First line of request
* %s - Response status
* %>s - Final response status
* %t - Time (CLF format)
* %{format}t - Time with custom format
* %T - Time taken (seconds)
* %u - Remote user
* %U - Requested URL path
* %v - Server name
* %{header}i - Request header
* %{header}o - Response header
* %{cookie}C - Request cookie
* %{cookie}c - Response cookie
*/
}
}// Detailed access log with timing and security info
String detailedFormat = "%{client}a %l %u %t \"%r\" %>s %b %D " +
"\"%{Referer}i\" \"%{User-Agent}i\" " +
"\"%{X-Forwarded-For}i\" \"%{Authorization}i\"";
// JSON-style log format
String jsonFormat = "{\"timestamp\":\"%t\", \"client\":\"%{client}a\", " +
"\"method\":\"%m\", \"uri\":\"%U\", \"query\":\"%q\", " +
"\"status\":%>s, \"bytes\":%b, \"duration\":%D, " +
"\"referer\":\"%{Referer}i\", \"userAgent\":\"%{User-Agent}i\"}";
// Performance monitoring format
String performanceFormat = "%t %{client}a %m %U %>s %b %D %T";
// Security audit format
String securityFormat = "%t %{client}a \"%r\" %>s \"%{Authorization}i\" " +
"\"%{X-Forwarded-For}i\" \"%{X-Real-IP}i\"";File-based request log writer with rotation support.
public class RequestLogWriter extends AbstractLifeCycle implements RequestLog.Writer {
// Constructors
public RequestLogWriter();
public RequestLogWriter(String filename);
// File configuration
public void setFilename(String filename);
public String getFilename();
public void setAppend(boolean append);
public boolean isAppend();
// Rotation configuration
public void setRetainDays(int retainDays);
public int getRetainDays();
public void setFilenameDateFormat(String filenameDateFormat);
public String getFilenameDateFormat();
// Buffering
public void setBufferedLogs(boolean bufferedLogs);
public boolean isBufferedLogs();
// Writing
public void write(String requestEntry) throws IOException;
public void closeWriter() throws IOException;
}Asynchronous request log writer for high-performance logging.
public class AsyncRequestLogWriter extends RequestLogWriter {
// Queue configuration
public void setQueueSize(int queueSize);
public int getQueueSize();
// Async behavior
public boolean dispatch(String entry);
public void flush() throws IOException;
}SLF4J-based request log writer for integration with logging frameworks.
public class Slf4jRequestLogWriter extends AbstractLifeCycle implements RequestLog.Writer {
// Constructors
public Slf4jRequestLogWriter();
public Slf4jRequestLogWriter(Logger logger);
// Logger configuration
public void setLoggerName(String loggerName);
public String getLoggerName();
public Logger getLogger();
// Log level
public void setLogLevel(Level level);
public Level getLogLevel();
// Writing
public void write(String requestEntry) throws IOException;
}public class AdvancedRequestLogging {
public void setupMultipleLoggers(Server server) {
// Main access log
RequestLogWriter mainLogWriter = new RequestLogWriter("logs/access.log");
mainLogWriter.setRetainDays(90);
mainLogWriter.setFilenameDateFormat("yyyy-MM-dd");
CustomRequestLog mainLog = new CustomRequestLog(mainLogWriter,
CustomRequestLog.EXTENDED_NCSA_FORMAT);
// Error log (4xx and 5xx responses only)
RequestLogWriter errorLogWriter = new RequestLogWriter("logs/error.log");
CustomRequestLog errorLog = new FilteredRequestLog(errorLogWriter) {
@Override
protected boolean shouldLog(Request request, Response response) {
return response.getStatus() >= 400;
}
};
// Performance log (slow requests only)
AsyncRequestLogWriter perfLogWriter = new AsyncRequestLogWriter();
perfLogWriter.setFilename("logs/performance.log");
perfLogWriter.setQueueSize(10000);
CustomRequestLog perfLog = new TimingRequestLog(perfLogWriter) {
@Override
protected boolean shouldLog(Request request, Response response) {
return getResponseTime() > 1000; // Log requests > 1 second
}
};
// Combine multiple loggers
CompositeRequestLog compositeLog = new CompositeRequestLog(
mainLog, errorLog, perfLog);
server.setRequestLog(compositeLog);
}
public void setupSLF4JLogging() {
// Use SLF4J for request logging
Slf4jRequestLogWriter slf4jWriter = new Slf4jRequestLogWriter();
slf4jWriter.setLoggerName("jetty.access");
slf4jWriter.setLogLevel(Level.INFO);
CustomRequestLog requestLog = new CustomRequestLog(slf4jWriter);
requestLog.setLogFormat("%t %{client}a \"%r\" %>s %b %D");
server.setRequestLog(requestLog);
}
}public abstract class FilteredRequestLog extends CustomRequestLog {
public FilteredRequestLog(RequestLog.Writer writer) {
super(writer);
}
public FilteredRequestLog(RequestLog.Writer writer, String format) {
super(writer, format);
}
@Override
public void log(Request request, Response response) {
if (shouldLog(request, response)) {
super.log(request, response);
}
}
protected abstract boolean shouldLog(Request request, Response response);
}
// Example usage
public class APIRequestLog extends FilteredRequestLog {
public APIRequestLog(RequestLog.Writer writer) {
super(writer, "%t %{client}a \"%r\" %>s %b %D \"%{Authorization}i\"");
}
@Override
protected boolean shouldLog(Request request, Response response) {
// Only log API requests
return request.getHttpURI().getPath().startsWith("/api/");
}
}public class DatabaseRequestLog implements RequestLog {
private final DataSource dataSource;
private final String insertSQL =
"INSERT INTO access_log (timestamp, client_ip, method, uri, status, " +
"bytes_sent, response_time, user_agent, referer) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
public DatabaseRequestLog(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public void log(Request request, Response response) {
try (Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement(insertSQL)) {
stmt.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
stmt.setString(2, request.getConnectionMetaData().getRemoteSocketAddress().toString());
stmt.setString(3, request.getMethod());
stmt.setString(4, request.getHttpURI().toString());
stmt.setInt(5, response.getStatus());
stmt.setLong(6, response.getBytesWritten());
stmt.setLong(7, getResponseTime(request));
stmt.setString(8, request.getHeaders().get("User-Agent"));
stmt.setString(9, request.getHeaders().get("Referer"));
stmt.executeUpdate();
} catch (SQLException e) {
System.err.println("Failed to log request to database: " + e.getMessage());
}
}
private long getResponseTime(Request request) {
Long startTime = (Long) request.getAttribute("startTime");
return startTime != null ? System.currentTimeMillis() - startTime : 0;
}
}public class CompositeRequestLog implements RequestLog {
private final List<RequestLog> requestLogs;
public CompositeRequestLog(RequestLog... requestLogs) {
this.requestLogs = Arrays.asList(requestLogs);
}
@Override
public void log(Request request, Response response) {
for (RequestLog requestLog : requestLogs) {
try {
requestLog.log(request, response);
} catch (Exception e) {
System.err.println("Error in composite request log: " + e.getMessage());
}
}
}
}public class TimingHandler extends Handler.Wrapper {
@Override
public boolean handle(Request request, Response response, Callback callback)
throws Exception {
long startTime = System.nanoTime();
request.setAttribute("startTime", startTime);
// Wrap callback to capture end time
Callback timingCallback = new Callback() {
@Override
public void succeeded() {
long endTime = System.nanoTime();
long duration = endTime - startTime;
request.setAttribute("responseTime", duration / 1_000_000); // Convert to milliseconds
callback.succeeded();
}
@Override
public void failed(Throwable x) {
long endTime = System.nanoTime();
long duration = endTime - startTime;
request.setAttribute("responseTime", duration / 1_000_000);
callback.failed(x);
}
};
return super.handle(request, response, timingCallback);
}
}
// Custom request log that includes timing
public class TimingRequestLog extends CustomRequestLog {
public TimingRequestLog(RequestLog.Writer writer) {
super(writer, "%t %{client}a \"%r\" %>s %b %{responseTime}ra ms");
}
protected long getResponseTime() {
// Access timing information from request attributes
return 0; // Implementation depends on how timing is stored
}
}public class LogMonitoringHandler extends Handler.Wrapper {
private final AtomicLong requestCount = new AtomicLong();
private final AtomicLong errorCount = new AtomicLong();
private final Map<String, AtomicLong> statusCounts = new ConcurrentHashMap<>();
@Override
public boolean handle(Request request, Response response, Callback callback)
throws Exception {
requestCount.incrementAndGet();
Callback monitoringCallback = new Callback() {
@Override
public void succeeded() {
recordResponse(response.getStatus());
callback.succeeded();
}
@Override
public void failed(Throwable x) {
errorCount.incrementAndGet();
callback.failed(x);
}
};
return super.handle(request, response, monitoringCallback);
}
private void recordResponse(int status) {
String statusCategory = getStatusCategory(status);
statusCounts.computeIfAbsent(statusCategory, k -> new AtomicLong()).incrementAndGet();
if (status >= 400) {
errorCount.incrementAndGet();
}
}
private String getStatusCategory(int status) {
if (status < 300) return "2xx";
if (status < 400) return "3xx";
if (status < 500) return "4xx";
return "5xx";
}
public long getRequestCount() { return requestCount.get(); }
public long getErrorCount() { return errorCount.get(); }
public Map<String, Long> getStatusCounts() {
return statusCounts.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
e -> e.getValue().get()
));
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-eclipse-jetty--jetty-server