0
# Request Logging
1
2
Request logging provides comprehensive HTTP request and response logging with customizable formats, file rotation, asynchronous writing, and integration with SLF4J logging frameworks.
3
4
## RequestLog Interface
5
6
The core interface for logging HTTP requests and responses.
7
8
```java { .api }
9
public interface RequestLog {
10
// Log a request/response pair
11
void log(Request request, Response response);
12
13
// Nested interface for log writers
14
interface Writer {
15
void write(String requestEntry) throws IOException;
16
}
17
}
18
```
19
20
## CustomRequestLog
21
22
Flexible request logger with customizable log formats and output destinations.
23
24
```java { .api }
25
public class CustomRequestLog extends ContainerLifeCycle implements RequestLog {
26
// Constructors
27
public CustomRequestLog();
28
public CustomRequestLog(RequestLog.Writer writer);
29
public CustomRequestLog(RequestLog.Writer writer, String format);
30
public CustomRequestLog(String filename);
31
public CustomRequestLog(String filename, String format);
32
33
// Format configuration
34
public void setLogFormat(String format);
35
public String getLogFormat();
36
public void setLogTimeZone(String timeZone);
37
public String getLogTimeZone();
38
39
// Writer configuration
40
public RequestLog.Writer getWriter();
41
public void setWriter(RequestLog.Writer writer);
42
43
// Cookie logging
44
public String[] getLogCookies();
45
public void setLogCookies(String[] logCookies);
46
47
// Latency logging
48
public boolean isLogLatency();
49
public void setLogLatency(boolean logLatency);
50
51
// Server information
52
public boolean isLogServer();
53
public void setLogServer(boolean logServer);
54
55
// Request logging
56
public void log(Request request, Response response);
57
}
58
```
59
60
## Basic Request Logging Setup
61
62
```java
63
public class BasicRequestLogging {
64
65
public void setupRequestLogging(Server server) {
66
// Create request log writer to file
67
RequestLogWriter logWriter = new RequestLogWriter("logs/access.log");
68
69
// Configure log rotation
70
logWriter.setRetainDays(30); // Keep 30 days of logs
71
logWriter.setAppend(true); // Append to existing log
72
73
// Create custom request log with NCSA format
74
CustomRequestLog requestLog = new CustomRequestLog(logWriter);
75
requestLog.setLogFormat(CustomRequestLog.NCSA_FORMAT);
76
77
// Set timezone for timestamps
78
requestLog.setLogTimeZone("UTC");
79
80
// Configure what to log
81
requestLog.setLogLatency(true); // Include response time
82
requestLog.setLogServer(true); // Include server info
83
requestLog.setLogCookies(new String[]{"JSESSIONID"}); // Log specific cookies
84
85
// Assign to server
86
server.setRequestLog(requestLog);
87
}
88
}
89
```
90
91
## Log Formats
92
93
### Standard Log Formats
94
95
```java
96
public class LogFormatExamples {
97
98
public void demonstrateLogFormats() {
99
// NCSA Common Log Format
100
String ncsaFormat = CustomRequestLog.NCSA_FORMAT;
101
// %{client}a - %u %t "%r" %>s %O
102
103
// Extended NCSA format
104
String extendedFormat = CustomRequestLog.EXTENDED_NCSA_FORMAT;
105
// %{client}a - %u %t "%r" %>s %O "%{Referer}i" "%{User-Agent}i"
106
107
// Custom format with response time and additional fields
108
String customFormat = "%{client}a - %u %t \"%r\" %>s %O %D \"%{Referer}i\" \"%{User-Agent}i\"";
109
110
CustomRequestLog requestLog = new CustomRequestLog();
111
requestLog.setLogFormat(customFormat);
112
}
113
114
public void explainFormatTokens() {
115
/*
116
* Format tokens:
117
* %a - Remote IP address
118
* %{client}a - Client IP address (real client behind proxy)
119
* %A - Local IP address
120
* %b - Bytes sent (CLF format)
121
* %B - Bytes sent
122
* %D - Time taken to process request (microseconds)
123
* %h - Remote host name
124
* %H - Request protocol
125
* %l - Remote logical username (identd)
126
* %m - Request method
127
* %O - Bytes sent including headers
128
* %q - Query string (prepended with ?)
129
* %r - First line of request
130
* %s - Response status
131
* %>s - Final response status
132
* %t - Time (CLF format)
133
* %{format}t - Time with custom format
134
* %T - Time taken (seconds)
135
* %u - Remote user
136
* %U - Requested URL path
137
* %v - Server name
138
* %{header}i - Request header
139
* %{header}o - Response header
140
* %{cookie}C - Request cookie
141
* %{cookie}c - Response cookie
142
*/
143
}
144
}
145
```
146
147
### Custom Log Format Examples
148
149
```java
150
// Detailed access log with timing and security info
151
String detailedFormat = "%{client}a %l %u %t \"%r\" %>s %b %D " +
152
"\"%{Referer}i\" \"%{User-Agent}i\" " +
153
"\"%{X-Forwarded-For}i\" \"%{Authorization}i\"";
154
155
// JSON-style log format
156
String jsonFormat = "{\"timestamp\":\"%t\", \"client\":\"%{client}a\", " +
157
"\"method\":\"%m\", \"uri\":\"%U\", \"query\":\"%q\", " +
158
"\"status\":%>s, \"bytes\":%b, \"duration\":%D, " +
159
"\"referer\":\"%{Referer}i\", \"userAgent\":\"%{User-Agent}i\"}";
160
161
// Performance monitoring format
162
String performanceFormat = "%t %{client}a %m %U %>s %b %D %T";
163
164
// Security audit format
165
String securityFormat = "%t %{client}a \"%r\" %>s \"%{Authorization}i\" " +
166
"\"%{X-Forwarded-For}i\" \"%{X-Real-IP}i\"";
167
```
168
169
## Request Log Writers
170
171
### RequestLogWriter
172
173
File-based request log writer with rotation support.
174
175
```java { .api }
176
public class RequestLogWriter extends AbstractLifeCycle implements RequestLog.Writer {
177
// Constructors
178
public RequestLogWriter();
179
public RequestLogWriter(String filename);
180
181
// File configuration
182
public void setFilename(String filename);
183
public String getFilename();
184
public void setAppend(boolean append);
185
public boolean isAppend();
186
187
// Rotation configuration
188
public void setRetainDays(int retainDays);
189
public int getRetainDays();
190
public void setFilenameDateFormat(String filenameDateFormat);
191
public String getFilenameDateFormat();
192
193
// Buffering
194
public void setBufferedLogs(boolean bufferedLogs);
195
public boolean isBufferedLogs();
196
197
// Writing
198
public void write(String requestEntry) throws IOException;
199
public void closeWriter() throws IOException;
200
}
201
```
202
203
### AsyncRequestLogWriter
204
205
Asynchronous request log writer for high-performance logging.
206
207
```java { .api }
208
public class AsyncRequestLogWriter extends RequestLogWriter {
209
// Queue configuration
210
public void setQueueSize(int queueSize);
211
public int getQueueSize();
212
213
// Async behavior
214
public boolean dispatch(String entry);
215
public void flush() throws IOException;
216
}
217
```
218
219
### Slf4jRequestLogWriter
220
221
SLF4J-based request log writer for integration with logging frameworks.
222
223
```java { .api }
224
public class Slf4jRequestLogWriter extends AbstractLifeCycle implements RequestLog.Writer {
225
// Constructors
226
public Slf4jRequestLogWriter();
227
public Slf4jRequestLogWriter(Logger logger);
228
229
// Logger configuration
230
public void setLoggerName(String loggerName);
231
public String getLoggerName();
232
public Logger getLogger();
233
234
// Log level
235
public void setLogLevel(Level level);
236
public Level getLogLevel();
237
238
// Writing
239
public void write(String requestEntry) throws IOException;
240
}
241
```
242
243
## Advanced Request Logging Configuration
244
245
```java
246
public class AdvancedRequestLogging {
247
248
public void setupMultipleLoggers(Server server) {
249
// Main access log
250
RequestLogWriter mainLogWriter = new RequestLogWriter("logs/access.log");
251
mainLogWriter.setRetainDays(90);
252
mainLogWriter.setFilenameDateFormat("yyyy-MM-dd");
253
254
CustomRequestLog mainLog = new CustomRequestLog(mainLogWriter,
255
CustomRequestLog.EXTENDED_NCSA_FORMAT);
256
257
// Error log (4xx and 5xx responses only)
258
RequestLogWriter errorLogWriter = new RequestLogWriter("logs/error.log");
259
CustomRequestLog errorLog = new FilteredRequestLog(errorLogWriter) {
260
@Override
261
protected boolean shouldLog(Request request, Response response) {
262
return response.getStatus() >= 400;
263
}
264
};
265
266
// Performance log (slow requests only)
267
AsyncRequestLogWriter perfLogWriter = new AsyncRequestLogWriter();
268
perfLogWriter.setFilename("logs/performance.log");
269
perfLogWriter.setQueueSize(10000);
270
271
CustomRequestLog perfLog = new TimingRequestLog(perfLogWriter) {
272
@Override
273
protected boolean shouldLog(Request request, Response response) {
274
return getResponseTime() > 1000; // Log requests > 1 second
275
}
276
};
277
278
// Combine multiple loggers
279
CompositeRequestLog compositeLog = new CompositeRequestLog(
280
mainLog, errorLog, perfLog);
281
282
server.setRequestLog(compositeLog);
283
}
284
285
public void setupSLF4JLogging() {
286
// Use SLF4J for request logging
287
Slf4jRequestLogWriter slf4jWriter = new Slf4jRequestLogWriter();
288
slf4jWriter.setLoggerName("jetty.access");
289
slf4jWriter.setLogLevel(Level.INFO);
290
291
CustomRequestLog requestLog = new CustomRequestLog(slf4jWriter);
292
requestLog.setLogFormat("%t %{client}a \"%r\" %>s %b %D");
293
294
server.setRequestLog(requestLog);
295
}
296
}
297
```
298
299
## Custom Request Log Implementations
300
301
### Filtered Request Log
302
303
```java
304
public abstract class FilteredRequestLog extends CustomRequestLog {
305
306
public FilteredRequestLog(RequestLog.Writer writer) {
307
super(writer);
308
}
309
310
public FilteredRequestLog(RequestLog.Writer writer, String format) {
311
super(writer, format);
312
}
313
314
@Override
315
public void log(Request request, Response response) {
316
if (shouldLog(request, response)) {
317
super.log(request, response);
318
}
319
}
320
321
protected abstract boolean shouldLog(Request request, Response response);
322
}
323
324
// Example usage
325
public class APIRequestLog extends FilteredRequestLog {
326
327
public APIRequestLog(RequestLog.Writer writer) {
328
super(writer, "%t %{client}a \"%r\" %>s %b %D \"%{Authorization}i\"");
329
}
330
331
@Override
332
protected boolean shouldLog(Request request, Response response) {
333
// Only log API requests
334
return request.getHttpURI().getPath().startsWith("/api/");
335
}
336
}
337
```
338
339
### Database Request Log
340
341
```java
342
public class DatabaseRequestLog implements RequestLog {
343
private final DataSource dataSource;
344
private final String insertSQL =
345
"INSERT INTO access_log (timestamp, client_ip, method, uri, status, " +
346
"bytes_sent, response_time, user_agent, referer) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
347
348
public DatabaseRequestLog(DataSource dataSource) {
349
this.dataSource = dataSource;
350
}
351
352
@Override
353
public void log(Request request, Response response) {
354
try (Connection conn = dataSource.getConnection();
355
PreparedStatement stmt = conn.prepareStatement(insertSQL)) {
356
357
stmt.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
358
stmt.setString(2, request.getConnectionMetaData().getRemoteSocketAddress().toString());
359
stmt.setString(3, request.getMethod());
360
stmt.setString(4, request.getHttpURI().toString());
361
stmt.setInt(5, response.getStatus());
362
stmt.setLong(6, response.getBytesWritten());
363
stmt.setLong(7, getResponseTime(request));
364
stmt.setString(8, request.getHeaders().get("User-Agent"));
365
stmt.setString(9, request.getHeaders().get("Referer"));
366
367
stmt.executeUpdate();
368
369
} catch (SQLException e) {
370
System.err.println("Failed to log request to database: " + e.getMessage());
371
}
372
}
373
374
private long getResponseTime(Request request) {
375
Long startTime = (Long) request.getAttribute("startTime");
376
return startTime != null ? System.currentTimeMillis() - startTime : 0;
377
}
378
}
379
```
380
381
### Composite Request Log
382
383
```java
384
public class CompositeRequestLog implements RequestLog {
385
private final List<RequestLog> requestLogs;
386
387
public CompositeRequestLog(RequestLog... requestLogs) {
388
this.requestLogs = Arrays.asList(requestLogs);
389
}
390
391
@Override
392
public void log(Request request, Response response) {
393
for (RequestLog requestLog : requestLogs) {
394
try {
395
requestLog.log(request, response);
396
} catch (Exception e) {
397
System.err.println("Error in composite request log: " + e.getMessage());
398
}
399
}
400
}
401
}
402
```
403
404
## Request Timing Integration
405
406
### Timing Handler with Request Logging
407
408
```java
409
public class TimingHandler extends Handler.Wrapper {
410
411
@Override
412
public boolean handle(Request request, Response response, Callback callback)
413
throws Exception {
414
415
long startTime = System.nanoTime();
416
request.setAttribute("startTime", startTime);
417
418
// Wrap callback to capture end time
419
Callback timingCallback = new Callback() {
420
@Override
421
public void succeeded() {
422
long endTime = System.nanoTime();
423
long duration = endTime - startTime;
424
request.setAttribute("responseTime", duration / 1_000_000); // Convert to milliseconds
425
callback.succeeded();
426
}
427
428
@Override
429
public void failed(Throwable x) {
430
long endTime = System.nanoTime();
431
long duration = endTime - startTime;
432
request.setAttribute("responseTime", duration / 1_000_000);
433
callback.failed(x);
434
}
435
};
436
437
return super.handle(request, response, timingCallback);
438
}
439
}
440
441
// Custom request log that includes timing
442
public class TimingRequestLog extends CustomRequestLog {
443
444
public TimingRequestLog(RequestLog.Writer writer) {
445
super(writer, "%t %{client}a \"%r\" %>s %b %{responseTime}ra ms");
446
}
447
448
protected long getResponseTime() {
449
// Access timing information from request attributes
450
return 0; // Implementation depends on how timing is stored
451
}
452
}
453
```
454
455
## Log Analysis and Monitoring
456
457
### Real-time Log Monitoring
458
459
```java
460
public class LogMonitoringHandler extends Handler.Wrapper {
461
private final AtomicLong requestCount = new AtomicLong();
462
private final AtomicLong errorCount = new AtomicLong();
463
private final Map<String, AtomicLong> statusCounts = new ConcurrentHashMap<>();
464
465
@Override
466
public boolean handle(Request request, Response response, Callback callback)
467
throws Exception {
468
469
requestCount.incrementAndGet();
470
471
Callback monitoringCallback = new Callback() {
472
@Override
473
public void succeeded() {
474
recordResponse(response.getStatus());
475
callback.succeeded();
476
}
477
478
@Override
479
public void failed(Throwable x) {
480
errorCount.incrementAndGet();
481
callback.failed(x);
482
}
483
};
484
485
return super.handle(request, response, monitoringCallback);
486
}
487
488
private void recordResponse(int status) {
489
String statusCategory = getStatusCategory(status);
490
statusCounts.computeIfAbsent(statusCategory, k -> new AtomicLong()).incrementAndGet();
491
492
if (status >= 400) {
493
errorCount.incrementAndGet();
494
}
495
}
496
497
private String getStatusCategory(int status) {
498
if (status < 300) return "2xx";
499
if (status < 400) return "3xx";
500
if (status < 500) return "4xx";
501
return "5xx";
502
}
503
504
public long getRequestCount() { return requestCount.get(); }
505
public long getErrorCount() { return errorCount.get(); }
506
public Map<String, Long> getStatusCounts() {
507
return statusCounts.entrySet().stream()
508
.collect(Collectors.toMap(
509
Map.Entry::getKey,
510
e -> e.getValue().get()
511
));
512
}
513
}
514
```