0
# Monitoring and Statistics
1
2
Eclipse Jetty provides comprehensive monitoring and statistics capabilities through the StatisticsServlet for runtime metrics and JMX support for management and monitoring integration with enterprise management systems.
3
4
## StatisticsServlet
5
6
The `StatisticsServlet` provides HTTP access to server runtime statistics and performance metrics.
7
8
```java { .api }
9
public class StatisticsServlet extends HttpServlet {
10
// Constructor
11
public StatisticsServlet();
12
13
// Servlet lifecycle
14
public void init(ServletConfig config);
15
16
// Request handling
17
protected void doGet(HttpServletRequest req, HttpServletResponse resp);
18
protected void doPost(HttpServletRequest req, HttpServletResponse resp);
19
}
20
```
21
22
## JMX Support Classes
23
24
Eclipse Jetty provides JMX management beans for comprehensive monitoring and management.
25
26
### HolderMBean
27
28
```java { .api }
29
// JMX management bean for holders (ServletHolder, FilterHolder, ListenerHolder)
30
public class HolderMBean extends ObjectMBean {
31
// Provides JMX access to holder properties and lifecycle
32
}
33
```
34
35
### FilterMappingMBean
36
37
```java { .api }
38
// JMX management bean for filter mappings
39
public class FilterMappingMBean extends ObjectMBean {
40
// Provides JMX access to filter mapping configuration
41
}
42
```
43
44
### ServletMappingMBean
45
46
```java { .api }
47
// JMX management bean for servlet mappings
48
public class ServletMappingMBean extends ObjectMBean {
49
// Provides JMX access to servlet mapping configuration
50
}
51
```
52
53
## DefaultServlet Monitoring
54
55
The `DefaultServlet` provides resource serving capabilities with built-in monitoring support.
56
57
```java { .api }
58
public class DefaultServlet extends HttpServlet implements ResourceFactory, WelcomeFactory {
59
// Initialization and configuration
60
public void init();
61
public String getInitParameter(String name);
62
63
// Resource access with monitoring
64
public Resource getResource(String pathInContext);
65
public String getWelcomeFile(String pathInContext);
66
67
// HTTP method handlers with performance tracking
68
protected void doGet(HttpServletRequest request, HttpServletResponse response);
69
protected void doPost(HttpServletRequest request, HttpServletResponse response);
70
protected void doPut(HttpServletRequest request, HttpServletResponse response);
71
protected void doDelete(HttpServletRequest request, HttpServletResponse response);
72
protected void doOptions(HttpServletRequest request, HttpServletResponse response);
73
protected void doTrace(HttpServletRequest request, HttpServletResponse response);
74
}
75
```
76
77
## Usage Examples
78
79
### Basic StatisticsServlet Configuration
80
81
```java
82
import org.eclipse.jetty.servlet.StatisticsServlet;
83
import org.eclipse.jetty.servlet.ServletContextHandler;
84
import org.eclipse.jetty.servlet.ServletHolder;
85
import org.eclipse.jetty.server.Server;
86
87
// Configure statistics servlet
88
ServletContextHandler context = new ServletContextHandler("/app");
89
90
// Add statistics servlet
91
ServletHolder statsServlet = new ServletHolder("statistics", StatisticsServlet.class);
92
context.addServlet(statsServlet, "/stats");
93
94
// Optional: Restrict access to statistics
95
FilterHolder securityFilter = new FilterHolder(AdminSecurityFilter.class);
96
context.addFilter(securityFilter, "/stats", EnumSet.of(DispatcherType.REQUEST));
97
98
// Add to server
99
Server server = new Server(8080);
100
server.setHandler(context);
101
server.start();
102
103
// Access statistics at: http://localhost:8080/app/stats
104
```
105
106
### Custom Statistics Collection
107
108
```java
109
import org.eclipse.jetty.server.handler.StatisticsHandler;
110
import org.eclipse.jetty.servlet.ServletContextHandler;
111
112
// Add statistics handler to collect metrics
113
StatisticsHandler statsHandler = new StatisticsHandler();
114
ServletContextHandler context = new ServletContextHandler("/app");
115
116
// Wrap context with statistics handler
117
statsHandler.setHandler(context);
118
119
// Add application servlets
120
context.addServlet(MyApplicationServlet.class, "/api/*");
121
context.addServlet(StatisticsServlet.class, "/admin/stats");
122
123
// Configure server with statistics collection
124
Server server = new Server(8080);
125
server.setHandler(statsHandler);
126
server.start();
127
128
// Custom statistics servlet that exposes StatisticsHandler metrics
129
public class CustomStatisticsServlet extends HttpServlet {
130
131
@Override
132
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
133
throws IOException {
134
135
// Find statistics handler in the handler hierarchy
136
StatisticsHandler statsHandler = findStatisticsHandler();
137
138
if (statsHandler == null) {
139
resp.sendError(503, "Statistics not available");
140
return;
141
}
142
143
// Generate statistics response
144
resp.setContentType("application/json");
145
PrintWriter out = resp.getWriter();
146
147
out.println("{");
148
out.println(" \"requests\": " + statsHandler.getRequests() + ",");
149
out.println(" \"requestsActive\": " + statsHandler.getRequestsActive() + ",");
150
out.println(" \"requestTimeMax\": " + statsHandler.getRequestTimeMax() + ",");
151
out.println(" \"requestTimeMean\": " + statsHandler.getRequestTimeMean() + ",");
152
out.println(" \"requestTimeStdDev\": " + statsHandler.getRequestTimeStdDev() + ",");
153
out.println(" \"responses1xx\": " + statsHandler.getResponses1xx() + ",");
154
out.println(" \"responses2xx\": " + statsHandler.getResponses2xx() + ",");
155
out.println(" \"responses3xx\": " + statsHandler.getResponses3xx() + ",");
156
out.println(" \"responses4xx\": " + statsHandler.getResponses4xx() + ",");
157
out.println(" \"responses5xx\": " + statsHandler.getResponses5xx() + ",");
158
out.println(" \"bytesReceived\": " + statsHandler.getBytesReceived() + ",");
159
out.println(" \"bytesSent\": " + statsHandler.getBytesSent() + ",");
160
out.println(" \"statsOnMs\": " + statsHandler.getStatsOnMs());
161
out.println("}");
162
}
163
164
private StatisticsHandler findStatisticsHandler() {
165
// Navigate handler hierarchy to find StatisticsHandler
166
ServletContext context = getServletContext();
167
ServletContextHandler contextHandler =
168
ServletContextHandler.getServletContextHandler(context);
169
170
Handler handler = contextHandler;
171
while (handler != null) {
172
if (handler instanceof StatisticsHandler) {
173
return (StatisticsHandler) handler;
174
}
175
if (handler instanceof HandlerWrapper) {
176
handler = ((HandlerWrapper) handler).getHandler();
177
} else {
178
break;
179
}
180
}
181
return null;
182
}
183
}
184
```
185
186
### JMX Integration
187
188
```java
189
import org.eclipse.jetty.jmx.MBeanContainer;
190
import org.eclipse.jetty.server.Server;
191
import javax.management.MBeanServer;
192
import java.lang.management.ManagementFactory;
193
194
// Enable JMX monitoring
195
public class JMXMonitoringSetup {
196
197
public static Server createServerWithJMX() throws Exception {
198
// Get platform MBean server
199
MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
200
201
// Create MBean container
202
MBeanContainer mbeanContainer = new MBeanContainer(mbeanServer);
203
204
// Create server
205
Server server = new Server(8080);
206
207
// Add MBean container to server
208
server.addBean(mbeanContainer);
209
210
// Configure servlet context with JMX
211
ServletContextHandler context = new ServletContextHandler("/app");
212
213
// Add servlets and filters - they will be automatically exposed via JMX
214
ServletHolder dataServlet = new ServletHolder("dataServlet", DataServlet.class);
215
dataServlet.setDisplayName("Data Processing Servlet");
216
context.addServlet(dataServlet, "/data/*");
217
218
FilterHolder loggingFilter = new FilterHolder("loggingFilter", LoggingFilter.class);
219
loggingFilter.setDisplayName("Request Logging Filter");
220
context.addFilter(loggingFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
221
222
// Add statistics servlet for HTTP access to metrics
223
context.addServlet(StatisticsServlet.class, "/admin/stats");
224
225
server.setHandler(context);
226
return server;
227
}
228
}
229
230
// Custom MBean for application-specific metrics
231
public interface ApplicationMetricsMBean {
232
long getActiveUserCount();
233
long getTotalTransactions();
234
double getAverageResponseTime();
235
String getHealthStatus();
236
void resetCounters();
237
}
238
239
public class ApplicationMetrics implements ApplicationMetricsMBean {
240
private final AtomicLong activeUsers = new AtomicLong();
241
private final AtomicLong totalTransactions = new AtomicLong();
242
private final AtomicLong totalResponseTime = new AtomicLong();
243
private volatile String healthStatus = "HEALTHY";
244
245
@Override
246
public long getActiveUserCount() {
247
return activeUsers.get();
248
}
249
250
@Override
251
public long getTotalTransactions() {
252
return totalTransactions.get();
253
}
254
255
@Override
256
public double getAverageResponseTime() {
257
long transactions = totalTransactions.get();
258
return transactions > 0 ? (double) totalResponseTime.get() / transactions : 0.0;
259
}
260
261
@Override
262
public String getHealthStatus() {
263
return healthStatus;
264
}
265
266
@Override
267
public void resetCounters() {
268
totalTransactions.set(0);
269
totalResponseTime.set(0);
270
// Don't reset active users as it's current state
271
}
272
273
// Methods for internal use
274
public void incrementActiveUsers() {
275
activeUsers.incrementAndGet();
276
}
277
278
public void decrementActiveUsers() {
279
activeUsers.decrementAndGet();
280
}
281
282
public void recordTransaction(long responseTimeMs) {
283
totalTransactions.incrementAndGet();
284
totalResponseTime.addAndGet(responseTimeMs);
285
}
286
287
public void setHealthStatus(String status) {
288
this.healthStatus = status;
289
}
290
}
291
292
// Register custom MBean
293
public class ApplicationMetricsSetup {
294
295
public static void registerApplicationMetrics(Server server, ApplicationMetrics metrics)
296
throws Exception {
297
MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
298
ObjectName objectName = new ObjectName("org.eclipse.jetty.servlet:type=ApplicationMetrics");
299
mbeanServer.registerMBean(metrics, objectName);
300
301
// Make metrics available to servlets via servlet context
302
ServletContextHandler context = (ServletContextHandler) server.getHandler();
303
context.getServletContext().setAttribute("applicationMetrics", metrics);
304
}
305
}
306
```
307
308
### Performance Monitoring Filter
309
310
```java
311
import jakarta.servlet.Filter;
312
import jakarta.servlet.FilterChain;
313
import jakarta.servlet.FilterConfig;
314
import jakarta.servlet.ServletRequest;
315
import jakarta.servlet.ServletResponse;
316
import jakarta.servlet.http.HttpServletRequest;
317
import jakarta.servlet.http.HttpServletResponse;
318
319
public class PerformanceMonitoringFilter implements Filter {
320
private ApplicationMetrics metrics;
321
322
@Override
323
public void init(FilterConfig filterConfig) {
324
ServletContext context = filterConfig.getServletContext();
325
metrics = (ApplicationMetrics) context.getAttribute("applicationMetrics");
326
}
327
328
@Override
329
public void doFilter(ServletRequest request, ServletResponse response,
330
FilterChain chain) throws IOException, ServletException {
331
332
HttpServletRequest httpReq = (HttpServletRequest) request;
333
HttpServletResponse httpResp = (HttpServletResponse) response;
334
335
long startTime = System.currentTimeMillis();
336
337
// Track active users (sessions)
338
HttpSession session = httpReq.getSession(false);
339
boolean newSession = false;
340
if (session == null) {
341
session = httpReq.getSession(true);
342
newSession = true;
343
if (metrics != null) {
344
metrics.incrementActiveUsers();
345
}
346
}
347
348
try {
349
chain.doFilter(request, response);
350
} finally {
351
long responseTime = System.currentTimeMillis() - startTime;
352
353
// Record performance metrics
354
if (metrics != null) {
355
metrics.recordTransaction(responseTime);
356
357
// Check for error conditions
358
int status = httpResp.getStatus();
359
if (status >= 500) {
360
metrics.setHealthStatus("DEGRADED");
361
}
362
}
363
364
// Log slow requests
365
if (responseTime > 5000) { // 5 seconds
366
System.err.println("SLOW REQUEST: " + httpReq.getRequestURI() +
367
" took " + responseTime + "ms");
368
}
369
}
370
}
371
372
@Override
373
public void destroy() {
374
// Cleanup
375
}
376
}
377
378
// Session listener to track user sessions
379
public class UserSessionListener implements HttpSessionListener {
380
private ApplicationMetrics metrics;
381
382
public UserSessionListener(ApplicationMetrics metrics) {
383
this.metrics = metrics;
384
}
385
386
@Override
387
public void sessionCreated(HttpSessionEvent se) {
388
// Already tracked in filter when session is created
389
}
390
391
@Override
392
public void sessionDestroyed(HttpSessionEvent se) {
393
if (metrics != null) {
394
metrics.decrementActiveUsers();
395
}
396
}
397
}
398
```
399
400
### Health Check Servlet
401
402
```java
403
public class HealthCheckServlet extends HttpServlet {
404
405
@Override
406
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
407
throws IOException {
408
409
ServletContext context = getServletContext();
410
ApplicationMetrics metrics = (ApplicationMetrics) context.getAttribute("applicationMetrics");
411
412
// Perform health checks
413
HealthStatus status = performHealthChecks();
414
415
// Set response status based on health
416
if (status.isHealthy()) {
417
resp.setStatus(200);
418
} else {
419
resp.setStatus(503); // Service Unavailable
420
}
421
422
// Generate health report
423
resp.setContentType("application/json");
424
PrintWriter out = resp.getWriter();
425
426
out.println("{");
427
out.println(" \"status\": \"" + status.getOverallStatus() + "\",");
428
out.println(" \"timestamp\": " + System.currentTimeMillis() + ",");
429
430
if (metrics != null) {
431
out.println(" \"activeUsers\": " + metrics.getActiveUserCount() + ",");
432
out.println(" \"totalTransactions\": " + metrics.getTotalTransactions() + ",");
433
out.println(" \"averageResponseTime\": " + metrics.getAverageResponseTime() + ",");
434
out.println(" \"applicationStatus\": \"" + metrics.getHealthStatus() + "\",");
435
}
436
437
out.println(" \"checks\": {");
438
439
Map<String, Boolean> checks = status.getChecks();
440
boolean first = true;
441
for (Map.Entry<String, Boolean> check : checks.entrySet()) {
442
if (!first) out.println(",");
443
out.println(" \"" + check.getKey() + "\": " + check.getValue());
444
first = false;
445
}
446
447
out.println(" }");
448
out.println("}");
449
}
450
451
private HealthStatus performHealthChecks() {
452
Map<String, Boolean> checks = new HashMap<>();
453
454
// Database connectivity check
455
checks.put("database", checkDatabaseConnection());
456
457
// External service checks
458
checks.put("externalAPI", checkExternalService());
459
460
// File system checks
461
checks.put("fileSystem", checkFileSystemAccess());
462
463
// Memory check
464
checks.put("memory", checkMemoryUsage());
465
466
// Determine overall status
467
boolean allHealthy = checks.values().stream().allMatch(Boolean::booleanValue);
468
String overallStatus = allHealthy ? "HEALTHY" : "UNHEALTHY";
469
470
return new HealthStatus(overallStatus, checks);
471
}
472
473
private boolean checkDatabaseConnection() {
474
try {
475
// Attempt database connection
476
// Return true if successful, false otherwise
477
return true; // Placeholder
478
} catch (Exception e) {
479
return false;
480
}
481
}
482
483
private boolean checkExternalService() {
484
try {
485
// Check external service availability
486
// Return true if reachable, false otherwise
487
return true; // Placeholder
488
} catch (Exception e) {
489
return false;
490
}
491
}
492
493
private boolean checkFileSystemAccess() {
494
try {
495
// Check file system read/write access
496
File tempFile = File.createTempFile("health", ".check");
497
boolean canWrite = tempFile.exists();
498
tempFile.delete();
499
return canWrite;
500
} catch (Exception e) {
501
return false;
502
}
503
}
504
505
private boolean checkMemoryUsage() {
506
Runtime runtime = Runtime.getRuntime();
507
long maxMemory = runtime.maxMemory();
508
long totalMemory = runtime.totalMemory();
509
long freeMemory = runtime.freeMemory();
510
long usedMemory = totalMemory - freeMemory;
511
512
// Consider unhealthy if using more than 90% of max memory
513
double memoryUsage = (double) usedMemory / maxMemory;
514
return memoryUsage < 0.90;
515
}
516
517
// Helper class for health status
518
private static class HealthStatus {
519
private final String overallStatus;
520
private final Map<String, Boolean> checks;
521
522
public HealthStatus(String overallStatus, Map<String, Boolean> checks) {
523
this.overallStatus = overallStatus;
524
this.checks = new HashMap<>(checks);
525
}
526
527
public boolean isHealthy() {
528
return "HEALTHY".equals(overallStatus);
529
}
530
531
public String getOverallStatus() {
532
return overallStatus;
533
}
534
535
public Map<String, Boolean> getChecks() {
536
return checks;
537
}
538
}
539
}
540
```
541
542
### Complete Monitoring Setup
543
544
```java
545
// Complete monitoring and statistics setup
546
public class MonitoringConfiguration {
547
548
public static Server createMonitoredServer() throws Exception {
549
// Enable JMX
550
MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
551
MBeanContainer mbeanContainer = new MBeanContainer(mbeanServer);
552
553
// Create server with statistics
554
Server server = new Server(8080);
555
server.addBean(mbeanContainer);
556
557
// Add statistics handler
558
StatisticsHandler statsHandler = new StatisticsHandler();
559
560
// Create application context
561
ServletContextHandler context = new ServletContextHandler("/app");
562
563
// Create and register application metrics
564
ApplicationMetrics appMetrics = new ApplicationMetrics();
565
registerApplicationMetrics(server, appMetrics);
566
context.getServletContext().setAttribute("applicationMetrics", appMetrics);
567
568
// Add performance monitoring filter
569
FilterHolder perfFilter = new FilterHolder(PerformanceMonitoringFilter.class);
570
context.addFilter(perfFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
571
572
// Add session listener
573
context.addEventListener(new UserSessionListener(appMetrics));
574
575
// Add application servlets
576
context.addServlet(MyApplicationServlet.class, "/api/*");
577
578
// Add monitoring endpoints
579
addMonitoringEndpoints(context);
580
581
// Wire handlers together
582
statsHandler.setHandler(context);
583
server.setHandler(statsHandler);
584
585
return server;
586
}
587
588
private static void addMonitoringEndpoints(ServletContextHandler context) {
589
// Statistics servlet (built-in Jetty statistics)
590
context.addServlet(StatisticsServlet.class, "/admin/stats");
591
592
// Custom statistics servlet (application + server metrics)
593
context.addServlet(CustomStatisticsServlet.class, "/admin/metrics");
594
595
// Health check endpoint
596
context.addServlet(HealthCheckServlet.class, "/admin/health");
597
598
// JMX servlet (if needed for HTTP access to JMX)
599
context.addServlet(JMXServlet.class, "/admin/jmx");
600
601
// Restrict access to admin endpoints
602
FilterHolder adminFilter = new FilterHolder(AdminSecurityFilter.class);
603
context.addFilter(adminFilter, "/admin/*", EnumSet.of(DispatcherType.REQUEST));
604
}
605
606
private static void registerApplicationMetrics(Server server, ApplicationMetrics metrics)
607
throws Exception {
608
MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
609
ObjectName objectName = new ObjectName("org.eclipse.jetty.servlet:type=ApplicationMetrics");
610
mbeanServer.registerMBean(metrics, objectName);
611
}
612
}
613
614
// Usage
615
public class MonitoredApplication {
616
public static void main(String[] args) throws Exception {
617
Server server = MonitoringConfiguration.createMonitoredServer();
618
server.start();
619
620
System.out.println("Server started with monitoring:");
621
System.out.println(" Application: http://localhost:8080/app/");
622
System.out.println(" Statistics: http://localhost:8080/app/admin/stats");
623
System.out.println(" Metrics: http://localhost:8080/app/admin/metrics");
624
System.out.println(" Health: http://localhost:8080/app/admin/health");
625
System.out.println(" JMX: Available via JConsole or similar tools");
626
627
server.join();
628
}
629
}
630
```