or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-gauges.mdcore-metrics.mdindex.mdreporting.mdreservoirs-sampling.mdutilities.md

reporting.mddocs/

0

# Reporting Framework

1

2

The reporting framework provides flexible mechanisms for outputting metrics data to various destinations on configurable schedules. It includes built-in reporters for console output, CSV files, and SLF4J logging, along with an extensible base class for custom reporter implementations.

3

4

## Reporter Interfaces

5

6

### Base Reporter Interface

7

8

```java { .api }

9

public interface Reporter extends Closeable {

10

// Tag interface - no methods

11

// Closeable provides: void close() throws IOException;

12

}

13

```

14

15

### ScheduledReporter Base Class

16

17

`ScheduledReporter` is the abstract base class for all scheduled reporters, providing common functionality for periodic metric reporting.

18

19

```java { .api }

20

public abstract class ScheduledReporter implements Closeable, Reporter {

21

// Lifecycle management

22

public void start(long period, TimeUnit unit);

23

public void start(long initialDelay, long period, TimeUnit unit);

24

public void stop();

25

public void close();

26

27

// Manual reporting

28

public void report();

29

30

// Abstract method for concrete implementations

31

protected abstract void report(

32

SortedMap<String, Gauge> gauges,

33

SortedMap<String, Counter> counters,

34

SortedMap<String, Histogram> histograms,

35

SortedMap<String, Meter> meters,

36

SortedMap<String, Timer> timers);

37

38

// Protected utility methods for subclasses

39

protected String getRateUnit();

40

protected String getDurationUnit();

41

protected double convertDuration(double duration);

42

protected double convertRate(double rate);

43

}

44

```

45

46

## ConsoleReporter

47

48

`ConsoleReporter` outputs formatted metrics to a `PrintStream` (typically `System.out`), making it ideal for development, debugging, and simple production monitoring.

49

50

```java { .api }

51

public class ConsoleReporter extends ScheduledReporter {

52

// Factory method

53

public static Builder forRegistry(MetricRegistry registry);

54

55

// Builder class for configuration

56

public static class Builder {

57

// Output configuration

58

public Builder outputTo(PrintStream output);

59

public Builder formattedFor(Locale locale);

60

public Builder formattedFor(TimeZone timeZone);

61

public Builder withClock(Clock clock);

62

63

// Unit conversion

64

public Builder convertRatesTo(TimeUnit rateUnit);

65

public Builder convertDurationsTo(TimeUnit durationUnit);

66

67

// Filtering and scheduling

68

public Builder filter(MetricFilter filter);

69

public Builder scheduleOn(ScheduledExecutorService executor);

70

public Builder shutdownExecutorOnStop(boolean shutdownExecutorOnStop);

71

72

// Attribute selection

73

public Builder disabledMetricAttributes(Set<MetricAttribute> disabledMetricAttributes);

74

75

// Build final reporter

76

public ConsoleReporter build();

77

}

78

}

79

```

80

81

### Usage Examples

82

83

**Basic Console Reporting:**

84

```java

85

MetricRegistry registry = new MetricRegistry();

86

87

// Simple console reporter with default settings

88

ConsoleReporter reporter = ConsoleReporter.forRegistry(registry)

89

.convertRatesTo(TimeUnit.SECONDS)

90

.convertDurationsTo(TimeUnit.MILLISECONDS)

91

.build();

92

93

// Start reporting every 30 seconds

94

reporter.start(30, TimeUnit.SECONDS);

95

96

// Report once immediately

97

reporter.report();

98

```

99

100

**Advanced Console Configuration:**

101

```java

102

// Customized console reporter

103

ConsoleReporter customReporter = ConsoleReporter.forRegistry(registry)

104

.outputTo(System.err) // Output to stderr instead of stdout

105

.convertRatesTo(TimeUnit.MINUTES) // Show rates per minute

106

.convertDurationsTo(TimeUnit.MICROSECONDS) // Show durations in microseconds

107

.filter(MetricFilter.startsWith("http")) // Only report HTTP metrics

108

.formattedFor(Locale.FRANCE) // French number formatting

109

.formattedFor(TimeZone.getTimeZone("UTC")) // UTC timestamps

110

.disabledMetricAttributes(EnumSet.of( // Exclude certain attributes

111

MetricAttribute.P95,

112

MetricAttribute.P98))

113

.build();

114

115

// Start with initial delay

116

customReporter.start(10, 60, TimeUnit.SECONDS); // Wait 10s, then every 60s

117

```

118

119

**Console Output Example:**

120

```

121

2023-05-15 14:30:15 ===============================================================

122

123

-- Counters --------------------------------------------------------------------

124

http.requests.count 1247

125

http.errors.count 23

126

127

-- Gauges ----------------------------------------------------------------------

128

memory.heap.usage 67108864

129

queue.size 142

130

131

-- Histograms ------------------------------------------------------------------

132

response.sizes

133

count = 1247

134

min = 128

135

max = 65536

136

mean = 2048.32

137

stddev = 512.18

138

median = 1024.00

139

75% <= 2560.00

140

95% <= 4096.00

141

98% <= 8192.00

142

99% <= 16384.00

143

99.9% <= 32768.00

144

145

-- Meters ----------------------------------------------------------------------

146

request.rate

147

count = 1247

148

mean rate = 4.17 events/second

149

1-minute rate = 5.23 events/second

150

5-minute rate = 4.89 events/second

151

15-minute rate = 4.76 events/second

152

153

-- Timers ----------------------------------------------------------------------

154

request.duration

155

count = 1247

156

mean rate = 4.17 calls/second

157

1-minute rate = 5.23 calls/second

158

5-minute rate = 4.89 calls/second

159

15-minute rate = 4.76 calls/second

160

min = 12.34 milliseconds

161

max = 987.65 milliseconds

162

mean = 145.67 milliseconds

163

stddev = 89.23 milliseconds

164

median = 123.45 milliseconds

165

75% <= 189.12 milliseconds

166

95% <= 345.67 milliseconds

167

98% <= 456.78 milliseconds

168

99% <= 567.89 milliseconds

169

99.9% <= 789.01 milliseconds

170

```

171

172

**Filtered Console Reporting:**

173

```java

174

// Report only error-related metrics

175

ConsoleReporter errorReporter = ConsoleReporter.forRegistry(registry)

176

.filter(MetricFilter.contains("error"))

177

.build();

178

179

// Report only specific metric types

180

MetricFilter timerAndHistogramFilter = new MetricFilter() {

181

@Override

182

public boolean matches(String name, Metric metric) {

183

return metric instanceof Timer || metric instanceof Histogram;

184

}

185

};

186

187

ConsoleReporter timingReporter = ConsoleReporter.forRegistry(registry)

188

.filter(timerAndHistogramFilter)

189

.build();

190

```

191

192

## CsvReporter

193

194

`CsvReporter` writes metrics to CSV files in a specified directory, creating separate files for each metric. This format is ideal for data analysis, visualization tools, and long-term trend analysis.

195

196

```java { .api }

197

public class CsvReporter extends ScheduledReporter {

198

// Factory method

199

public static Builder forRegistry(MetricRegistry registry);

200

201

// Builder class for configuration

202

public static class Builder {

203

// Unit conversion

204

public Builder convertRatesTo(TimeUnit rateUnit);

205

public Builder convertDurationsTo(TimeUnit durationUnit);

206

207

// Filtering and scheduling

208

public Builder filter(MetricFilter filter);

209

public Builder scheduleOn(ScheduledExecutorService executor);

210

public Builder shutdownExecutorOnStop(boolean shutdownExecutorOnStop);

211

212

// CSV-specific configuration

213

public Builder withClock(Clock clock);

214

public Builder formatFor(Locale locale);

215

216

// Build final reporter - requires output directory

217

public CsvReporter build(File directory);

218

}

219

}

220

```

221

222

### CSV File Provider Interface

223

224

```java { .api }

225

public interface CsvFileProvider {

226

File getFile(File directory, String metricName);

227

}

228

229

public class FixedNameCsvFileProvider implements CsvFileProvider {

230

public File getFile(File directory, String metricName);

231

}

232

```

233

234

### Usage Examples

235

236

**Basic CSV Reporting:**

237

```java

238

File csvDirectory = new File("/var/metrics/csv");

239

csvDirectory.mkdirs();

240

241

CsvReporter csvReporter = CsvReporter.forRegistry(registry)

242

.convertRatesTo(TimeUnit.SECONDS)

243

.convertDurationsTo(TimeUnit.MILLISECONDS)

244

.build(csvDirectory);

245

246

// Write metrics to CSV files every 5 minutes

247

csvReporter.start(5, TimeUnit.MINUTES);

248

```

249

250

**CSV File Structure:**

251

```

252

/var/metrics/csv/

253

├── http.requests.count.csv

254

├── http.response.time.csv

255

├── memory.heap.usage.csv

256

└── queue.size.csv

257

```

258

259

**Sample CSV Content (http.response.time.csv):**

260

```csv

261

t,count,max,mean,min,stddev,p50,p75,p95,p98,p99,p999,mean_rate,m1_rate,m5_rate,m15_rate,rate_unit,duration_unit

262

1684155615000,1247,987.65,145.67,12.34,89.23,123.45,189.12,345.67,456.78,567.89,789.01,4.17,5.23,4.89,4.76,calls/second,milliseconds

263

1684155915000,1389,1023.45,148.92,11.23,91.56,125.67,192.34,352.11,463.22,574.33,795.44,4.23,5.31,4.91,4.78,calls/second,milliseconds

264

```

265

266

**Filtered CSV Reporting:**

267

```java

268

// Report only performance metrics to CSV

269

CsvReporter performanceReporter = CsvReporter.forRegistry(registry)

270

.filter(MetricFilter.endsWith(".time"))

271

.convertDurationsTo(TimeUnit.MICROSECONDS)

272

.build(new File("/var/metrics/performance"));

273

274

// Report only business metrics to CSV

275

CsvReporter businessReporter = CsvReporter.forRegistry(registry)

276

.filter(MetricFilter.startsWith("business"))

277

.build(new File("/var/metrics/business"));

278

```

279

280

## Slf4jReporter

281

282

`Slf4jReporter` outputs metrics through the SLF4J logging framework, integrating metrics reporting with your application's existing logging infrastructure.

283

284

```java { .api }

285

public class Slf4jReporter extends ScheduledReporter {

286

// Factory method

287

public static Builder forRegistry(MetricRegistry registry);

288

289

// Logging levels

290

public enum LoggingLevel {

291

TRACE, DEBUG, INFO, WARN, ERROR

292

}

293

294

// Builder class for configuration

295

public static class Builder {

296

// Logging configuration

297

public Builder outputTo(Logger logger);

298

public Builder outputTo(String loggerName);

299

public Builder markWith(Marker marker);

300

public Builder withLoggingLevel(LoggingLevel loggingLevel);

301

302

// Unit conversion

303

public Builder convertRatesTo(TimeUnit rateUnit);

304

public Builder convertDurationsTo(TimeUnit durationUnit);

305

306

// Filtering and scheduling

307

public Builder filter(MetricFilter filter);

308

public Builder scheduleOn(ScheduledExecutorService executor);

309

public Builder shutdownExecutorOnStop(boolean shutdownExecutorOnStop);

310

311

// Build final reporter

312

public Slf4jReporter build();

313

}

314

}

315

```

316

317

### Usage Examples

318

319

**Basic SLF4J Reporting:**

320

```java

321

import org.slf4j.LoggerFactory;

322

323

Slf4jReporter slf4jReporter = Slf4jReporter.forRegistry(registry)

324

.outputTo(LoggerFactory.getLogger("metrics"))

325

.convertRatesTo(TimeUnit.SECONDS)

326

.convertDurationsTo(TimeUnit.MILLISECONDS)

327

.build();

328

329

// Log metrics every 1 minute at INFO level

330

slf4jReporter.start(1, TimeUnit.MINUTES);

331

```

332

333

**Advanced SLF4J Configuration:**

334

```java

335

import org.slf4j.MarkerFactory;

336

337

Slf4jReporter advancedSlf4jReporter = Slf4jReporter.forRegistry(registry)

338

.outputTo("com.example.metrics.performance") // Logger name

339

.markWith(MarkerFactory.getMarker("METRICS")) // Log marker

340

.withLoggingLevel(Slf4jReporter.LoggingLevel.DEBUG) // Debug level

341

.filter(MetricFilter.startsWith("critical")) // Only critical metrics

342

.convertRatesTo(TimeUnit.MINUTES) // Rates per minute

343

.build();

344

```

345

346

**Sample Log Output:**

347

```

348

2023-05-15 14:30:15.123 INFO metrics - type=COUNTER, name=http.requests.count, count=1247

349

2023-05-15 14:30:15.124 INFO metrics - type=GAUGE, name=memory.heap.usage, value=67108864

350

2023-05-15 14:30:15.125 INFO metrics - type=HISTOGRAM, name=response.sizes, count=1247, min=128, max=65536, mean=2048.32, stddev=512.18, p50=1024.00, p75=2560.00, p95=4096.00, p98=8192.00, p99=16384.00, p999=32768.00

351

2023-05-15 14:30:15.126 INFO metrics - type=METER, name=request.rate, count=1247, mean_rate=4.17, m1_rate=5.23, m5_rate=4.89, m15_rate=4.76, rate_unit=events/second

352

2023-05-15 14:30:15.127 INFO metrics - type=TIMER, name=request.duration, count=1247, min=12.34, max=987.65, mean=145.67, stddev=89.23, p50=123.45, p75=189.12, p95=345.67, p98=456.78, p99=567.89, p999=789.01, mean_rate=4.17, m1_rate=5.23, m5_rate=4.89, m15_rate=4.76, rate_unit=calls/second, duration_unit=milliseconds

353

```

354

355

**Multiple Logger Configuration:**

356

```java

357

// Performance metrics to performance logger

358

Slf4jReporter performanceReporter = Slf4jReporter.forRegistry(registry)

359

.outputTo("performance")

360

.filter(MetricFilter.contains("time"))

361

.withLoggingLevel(Slf4jReporter.LoggingLevel.INFO)

362

.build();

363

364

// Error metrics to error logger

365

Slf4jReporter errorReporter = Slf4jReporter.forRegistry(registry)

366

.outputTo("errors")

367

.filter(MetricFilter.contains("error"))

368

.withLoggingLevel(Slf4jReporter.LoggingLevel.WARN)

369

.build();

370

```

371

372

## Multiple Reporter Setup

373

374

You can run multiple reporters simultaneously to output metrics to different destinations:

375

376

```java

377

MetricRegistry registry = new MetricRegistry();

378

379

// Console reporter for development

380

ConsoleReporter consoleReporter = ConsoleReporter.forRegistry(registry)

381

.convertRatesTo(TimeUnit.SECONDS)

382

.convertDurationsTo(TimeUnit.MILLISECONDS)

383

.build();

384

385

// CSV reporter for analysis

386

CsvReporter csvReporter = CsvReporter.forRegistry(registry)

387

.build(new File("/var/metrics"));

388

389

// SLF4J reporter for production logging

390

Slf4jReporter slf4jReporter = Slf4jReporter.forRegistry(registry)

391

.outputTo("metrics")

392

.build();

393

394

// Start all reporters with different schedules

395

consoleReporter.start(30, TimeUnit.SECONDS); // Console every 30s

396

csvReporter.start(5, TimeUnit.MINUTES); // CSV every 5 minutes

397

slf4jReporter.start(1, TimeUnit.MINUTES); // Logs every 1 minute

398

399

// Proper shutdown

400

Runtime.getRuntime().addShutdownHook(new Thread(() -> {

401

consoleReporter.stop();

402

csvReporter.stop();

403

slf4jReporter.stop();

404

}));

405

```

406

407

## Custom Reporter Implementation

408

409

You can create custom reporters by extending `ScheduledReporter`:

410

411

```java

412

public class CustomReporter extends ScheduledReporter {

413

414

protected CustomReporter(MetricRegistry registry,

415

String name,

416

MetricFilter filter,

417

TimeUnit rateUnit,

418

TimeUnit durationUnit) {

419

super(registry, name, filter, rateUnit, durationUnit);

420

}

421

422

@Override

423

protected void report(SortedMap<String, Gauge> gauges,

424

SortedMap<String, Counter> counters,

425

SortedMap<String, Histogram> histograms,

426

SortedMap<String, Meter> meters,

427

SortedMap<String, Timer> timers) {

428

429

// Custom reporting logic

430

for (Map.Entry<String, Counter> entry : counters.entrySet()) {

431

String name = entry.getKey();

432

Counter counter = entry.getValue();

433

sendToCustomDestination(name, "counter", counter.getCount());

434

}

435

436

for (Map.Entry<String, Histogram> entry : histograms.entrySet()) {

437

String name = entry.getKey();

438

Histogram histogram = entry.getValue();

439

Snapshot snapshot = histogram.getSnapshot();

440

441

sendToCustomDestination(name, "histogram", Map.of(

442

"count", histogram.getCount(),

443

"mean", snapshot.getMean(),

444

"p95", snapshot.get95thPercentile(),

445

"p99", snapshot.get99thPercentile()

446

));

447

}

448

449

// Handle other metric types...

450

}

451

452

private void sendToCustomDestination(String name, String type, Object value) {

453

// Custom implementation: send to database, web service, message queue, etc.

454

}

455

456

public static Builder forRegistry(MetricRegistry registry) {

457

return new Builder(registry);

458

}

459

460

public static class Builder {

461

private final MetricRegistry registry;

462

private MetricFilter filter = MetricFilter.ALL;

463

private TimeUnit rateUnit = TimeUnit.SECONDS;

464

private TimeUnit durationUnit = TimeUnit.MILLISECONDS;

465

466

private Builder(MetricRegistry registry) {

467

this.registry = registry;

468

}

469

470

public Builder filter(MetricFilter filter) {

471

this.filter = filter;

472

return this;

473

}

474

475

public Builder convertRatesTo(TimeUnit rateUnit) {

476

this.rateUnit = rateUnit;

477

return this;

478

}

479

480

public Builder convertDurationsTo(TimeUnit durationUnit) {

481

this.durationUnit = durationUnit;

482

return this;

483

}

484

485

public CustomReporter build() {

486

return new CustomReporter(registry, "custom-reporter", filter, rateUnit, durationUnit);

487

}

488

}

489

}

490

```

491

492

### Usage of Custom Reporter:

493

494

```java

495

CustomReporter customReporter = CustomReporter.forRegistry(registry)

496

.filter(MetricFilter.startsWith("api"))

497

.convertRatesTo(TimeUnit.MINUTES)

498

.build();

499

500

customReporter.start(2, TimeUnit.MINUTES);

501

```

502

503

## Metric Filtering

504

505

All reporters support filtering to control which metrics are reported:

506

507

```java

508

// Predefined filters

509

MetricFilter all = MetricFilter.ALL;

510

MetricFilter httpMetrics = MetricFilter.startsWith("http");

511

MetricFilter errorMetrics = MetricFilter.contains("error");

512

MetricFilter apiMetrics = MetricFilter.endsWith("api");

513

514

// Custom filter implementation

515

MetricFilter customFilter = new MetricFilter() {

516

@Override

517

public boolean matches(String name, Metric metric) {

518

// Only report timers and histograms with high activity

519

if (metric instanceof Timer) {

520

return ((Timer) metric).getCount() > 100;

521

}

522

if (metric instanceof Histogram) {

523

return ((Histogram) metric).getCount() > 50;

524

}

525

return false;

526

}

527

};

528

529

// Composite filters

530

MetricFilter compositeFilter = new MetricFilter() {

531

@Override

532

public boolean matches(String name, Metric metric) {

533

return MetricFilter.startsWith("critical").matches(name, metric) ||

534

MetricFilter.contains("error").matches(name, metric);

535

}

536

};

537

```

538

539

## Best Practices

540

541

### Reporter Configuration

542

- Use console reporters for development and debugging

543

- Use CSV reporters for detailed analysis and data visualization

544

- Use SLF4J reporters for production logging and integration with log aggregation systems

545

- Configure appropriate reporting intervals based on metric update frequency and monitoring requirements

546

547

### Performance Considerations

548

- Reporting operations can be expensive for large numbers of metrics

549

- Use filtering to limit the number of metrics reported

550

- Consider separate reporters for different metric categories with different reporting intervals

551

- Monitor reporter performance and adjust intervals if necessary

552

553

### Resource Management

554

- Always stop reporters during application shutdown

555

- Use `shutdownExecutorOnStop(true)` unless managing executors externally

556

- Monitor disk space when using CSV reporters with high-frequency reporting

557

- Be aware of log file rotation when using SLF4J reporters

558

559

### Production Deployment

560

- Start with longer reporting intervals in production and adjust based on monitoring needs

561

- Use multiple reporters to send different metrics to different destinations

562

- Implement custom reporters for integration with specific monitoring systems

563

- Monitor reporter error logs and implement fallback strategies for critical metrics

564

565

### Testing Strategies

566

- Use manual `report()` calls for testing reporter output

567

- Test filtering logic with known metric sets

568

- Verify unit conversions in reporter output

569

- Test reporter lifecycle (start, stop, restart) in integration tests