or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

error-handling.mdfilter-handling.mdindex.mdlisteners.mdmonitoring.mdservlet-context.mdservlet-handling.mdtesting.md

monitoring.mddocs/

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

```