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

core-metrics.mddocs/

0

# Core Metrics Framework

1

2

The core metrics framework provides the fundamental building blocks for application monitoring: the central registry system and the five primary metric types. These components form the foundation for all metrics collection and are designed for high-performance, thread-safe operation in production environments.

3

4

## MetricRegistry

5

6

The `MetricRegistry` is the central repository for all metrics in an application. It provides factory methods for creating metrics, manages metric lifecycles, and serves as the primary integration point for reporting systems.

7

8

```java { .api }

9

public class MetricRegistry implements MetricSet {

10

// Constructor

11

public MetricRegistry();

12

13

// Metric factory methods - primary API for creating metrics

14

public Counter counter(String name);

15

public Counter counter(String name, MetricSupplier<Counter> supplier);

16

public Histogram histogram(String name);

17

public Histogram histogram(String name, MetricSupplier<Histogram> supplier);

18

public Meter meter(String name);

19

public Meter meter(String name, MetricSupplier<Meter> supplier);

20

public Timer timer(String name);

21

public Timer timer(String name, MetricSupplier<Timer> supplier);

22

public <T extends Gauge> T gauge(String name);

23

public <T extends Gauge> T gauge(String name, MetricSupplier<T> supplier);

24

25

// Generic registration and management

26

public <T extends Metric> T register(String name, T metric) throws IllegalArgumentException;

27

public <T> Gauge<T> registerGauge(String name, Gauge<T> metric) throws IllegalArgumentException;

28

public void registerAll(MetricSet metrics) throws IllegalArgumentException;

29

public void registerAll(String prefix, MetricSet metrics) throws IllegalArgumentException;

30

public boolean remove(String name);

31

public void removeMatching(MetricFilter filter);

32

33

// Registry inspection

34

public SortedSet<String> getNames();

35

36

// Metric retrieval by type

37

public SortedMap<String, Counter> getCounters();

38

public SortedMap<String, Counter> getCounters(MetricFilter filter);

39

public SortedMap<String, Gauge> getGauges();

40

public SortedMap<String, Gauge> getGauges(MetricFilter filter);

41

public SortedMap<String, Histogram> getHistograms();

42

public SortedMap<String, Histogram> getHistograms(MetricFilter filter);

43

public SortedMap<String, Meter> getMeters();

44

public SortedMap<String, Meter> getMeters(MetricFilter filter);

45

public SortedMap<String, Timer> getTimers();

46

public SortedMap<String, Timer> getTimers(MetricFilter filter);

47

public Map<String, Metric> getMetrics();

48

49

// Listener management

50

public void addListener(MetricRegistryListener listener);

51

public void removeListener(MetricRegistryListener listener);

52

53

// Utility methods for metric naming

54

public static String name(String name, String... names);

55

public static String name(Class<?> klass, String... names);

56

57

// MetricSet implementation

58

public Map<String, Metric> getMetrics();

59

}

60

```

61

62

### Usage Examples

63

64

**Basic Registry Setup:**

65

```java

66

MetricRegistry registry = new MetricRegistry();

67

68

// Create metrics using factory methods

69

Counter requestCount = registry.counter("requests");

70

Timer responseTime = registry.timer("response.time");

71

Histogram responseSizes = registry.histogram("response.sizes");

72

```

73

74

**Hierarchical Metric Naming:**

75

```java

76

// Using static name() method for dot-separated hierarchical names

77

String timerName = MetricRegistry.name("http", "requests", "GET", "/api/users");

78

Timer timer = registry.timer(timerName); // Creates "http.requests.GET./api/users"

79

80

// Using class-based naming

81

Timer classTimer = registry.timer(MetricRegistry.name(UserService.class, "createUser"));

82

// Creates "com.example.UserService.createUser"

83

```

84

85

**Registry Filtering:**

86

```java

87

// Get only counters with names starting with "http"

88

SortedMap<String, Counter> httpCounters = registry.getCounters(

89

MetricFilter.startsWith("http"));

90

91

// Remove all metrics matching a pattern

92

registry.removeMatching(MetricFilter.contains("temp"));

93

```

94

95

## Counter

96

97

Counters are the simplest metric type, providing thread-safe increment and decrement operations. They maintain a running total that can only be changed by specified amounts.

98

99

```java { .api }

100

public class Counter implements Metric, Counting {

101

// Constructor

102

public Counter();

103

104

// Increment operations

105

public void inc();

106

public void inc(long n);

107

108

// Decrement operations

109

public void dec();

110

public void dec(long n);

111

112

// Value access

113

public long getCount();

114

}

115

```

116

117

### Usage Examples

118

119

```java

120

Counter requestCounter = registry.counter("requests.total");

121

Counter errorCounter = registry.counter("errors.total");

122

123

// Basic incrementing

124

requestCounter.inc(); // Increment by 1

125

requestCounter.inc(5); // Increment by 5

126

127

// Error tracking

128

if (errorOccurred) {

129

errorCounter.inc();

130

}

131

132

// Batch processing

133

int batchSize = processedItems.size();

134

requestCounter.inc(batchSize);

135

136

// Current value

137

long totalRequests = requestCounter.getCount();

138

```

139

140

## Gauge

141

142

Gauges provide instantaneous readings of arbitrary values. They are functional interfaces that you implement to return current values, making them ideal for measuring things like queue sizes, memory usage, or any other fluctuating values.

143

144

```java { .api }

145

@FunctionalInterface

146

public interface Gauge<T> extends Metric {

147

T getValue();

148

}

149

```

150

151

### Usage Examples

152

153

```java

154

import java.util.concurrent.ConcurrentLinkedQueue;

155

156

Queue<String> processingQueue = new ConcurrentLinkedQueue<>();

157

158

// Lambda gauge for queue size

159

Gauge<Integer> queueSize = processingQueue::size;

160

registry.gauge("queue.size", queueSize);

161

162

// Anonymous class gauge

163

registry.gauge("memory.usage", new Gauge<Long>() {

164

@Override

165

public Long getValue() {

166

Runtime runtime = Runtime.getRuntime();

167

return runtime.totalMemory() - runtime.freeMemory();

168

}

169

});

170

171

// Method reference gauge

172

registry.gauge("active.threads", Thread::activeCount);

173

174

// Custom calculation gauge

175

registry.gauge("cpu.utilization", () -> {

176

return managementFactory.getOperatingSystemMXBean().getProcessCpuLoad();

177

});

178

```

179

180

## Meter

181

182

Meters measure the rate of events over time, providing mean throughput and exponentially-weighted moving averages over 1, 5, and 15-minute windows. They're similar to Unix load averages but for arbitrary events.

183

184

```java { .api }

185

public class Meter implements Metered {

186

// Constructors

187

public Meter();

188

public Meter(MovingAverages movingAverages);

189

public Meter(Clock clock);

190

public Meter(MovingAverages movingAverages, Clock clock);

191

192

// Event marking

193

public void mark();

194

public void mark(long n);

195

196

// Rate access (from Metered interface)

197

public long getCount();

198

public double getMeanRate();

199

public double getOneMinuteRate();

200

public double getFiveMinuteRate();

201

public double getFifteenMinuteRate();

202

}

203

```

204

205

### Usage Examples

206

207

```java

208

Meter requestMeter = registry.meter("requests.rate");

209

Meter errorMeter = registry.meter("errors.rate");

210

211

// Mark single events

212

requestMeter.mark();

213

214

// Mark multiple events

215

int batchSize = 10;

216

requestMeter.mark(batchSize);

217

218

// Error rate tracking

219

try {

220

processRequest();

221

requestMeter.mark();

222

} catch (Exception e) {

223

errorMeter.mark();

224

throw e;

225

}

226

227

// Accessing rates

228

double avgRequestsPerSecond = requestMeter.getMeanRate();

229

double recentRequestsPerSecond = requestMeter.getOneMinuteRate();

230

231

System.out.printf("Requests: %.2f/sec (1min: %.2f/sec)%n",

232

avgRequestsPerSecond, recentRequestsPerSecond);

233

```

234

235

## Histogram

236

237

Histograms measure the statistical distribution of values in a stream of data. They provide statistical summaries including minimum, maximum, mean, standard deviation, and various percentiles, while using configurable sampling strategies to manage memory usage.

238

239

```java { .api }

240

public class Histogram implements Metric, Sampling, Counting {

241

// Constructor

242

public Histogram(Reservoir reservoir);

243

244

// Value recording

245

public void update(int value);

246

public void update(long value);

247

248

// Statistical access

249

public long getCount();

250

public Snapshot getSnapshot();

251

}

252

```

253

254

### Usage Examples

255

256

```java

257

// Using default exponentially decaying reservoir

258

Histogram responseSizes = registry.histogram("response.sizes");

259

260

// Using custom reservoir

261

Histogram customHistogram = new Histogram(new UniformReservoir(1000));

262

registry.register("custom.distribution", customHistogram);

263

264

// Recording values

265

responseSizes.update(1024); // Response size in bytes

266

responseSizes.update(2048);

267

responseSizes.update(512);

268

269

// Getting statistics

270

Snapshot snapshot = responseSizes.getSnapshot();

271

System.out.printf("Response sizes - Count: %d, Mean: %.2f, Median: %.2f, 95th: %.2f%n",

272

responseSizes.getCount(),

273

snapshot.getMean(),

274

snapshot.getMedian(),

275

snapshot.get95thPercentile());

276

277

// Batch processing statistics

278

List<Integer> responseTimes = getResponseTimes();

279

for (int time : responseTimes) {

280

responseSizes.update(time);

281

}

282

```

283

284

## Timer

285

286

Timers are essentially histograms of duration measurements that also track the rate of events. They provide comprehensive timing statistics including duration distributions and throughput metrics, making them ideal for measuring method execution times, request processing times, and other time-based operations.

287

288

```java { .api }

289

public class Timer implements Metered, Sampling {

290

// Constructors

291

public Timer();

292

public Timer(Reservoir reservoir);

293

public Timer(Reservoir reservoir, Clock clock);

294

295

// Duration recording

296

public void update(long duration, TimeUnit unit);

297

public void update(Duration duration);

298

299

// Timing convenience methods

300

public <T> T time(Callable<T> event) throws Exception;

301

public void time(Runnable event);

302

public <T> T timeSupplier(Supplier<T> event);

303

public Context time();

304

305

// Statistical access (from Sampling)

306

public Snapshot getSnapshot();

307

308

// Rate access (from Metered)

309

public long getCount();

310

public double getMeanRate();

311

public double getOneMinuteRate();

312

public double getFiveMinuteRate();

313

public double getFifteenMinuteRate();

314

315

// Nested Context class for manual timing

316

public static class Context implements AutoCloseable {

317

public long stop();

318

public void close();

319

}

320

}

321

```

322

323

### Usage Examples

324

325

**Manual Timing with Context:**

326

```java

327

Timer requestTimer = registry.timer("request.duration");

328

329

// Manual timing with try-with-resources

330

try (Timer.Context context = requestTimer.time()) {

331

processRequest();

332

// Timing stops automatically when context closes

333

}

334

335

// Manual timing with explicit stop

336

Timer.Context context = requestTimer.time();

337

try {

338

processRequest();

339

} finally {

340

long elapsed = context.stop(); // Returns elapsed time in nanoseconds

341

System.out.println("Request took " + elapsed + " nanoseconds");

342

}

343

```

344

345

**Timing Callables and Runnables:**

346

```java

347

Timer dbTimer = registry.timer("database.query.time");

348

349

// Time a Callable (with return value)

350

String result = dbTimer.time(() -> {

351

return database.executeQuery("SELECT * FROM users");

352

});

353

354

// Time a Runnable (no return value)

355

dbTimer.time(() -> {

356

database.executeUpdate("UPDATE users SET last_login = NOW()");

357

});

358

359

// Time a Supplier

360

List<User> users = dbTimer.timeSupplier(() -> userService.getAllUsers());

361

```

362

363

**Direct Duration Recording:**

364

```java

365

Timer responseTimer = registry.timer("response.time");

366

367

long startTime = System.nanoTime();

368

processRequest();

369

long endTime = System.nanoTime();

370

371

// Record duration directly

372

responseTimer.update(endTime - startTime, TimeUnit.NANOSECONDS);

373

374

// Record using Duration (Java 8+)

375

Instant start = Instant.now();

376

processRequest();

377

Duration elapsed = Duration.between(start, Instant.now());

378

responseTimer.update(elapsed);

379

```

380

381

**Accessing Timer Statistics:**

382

```java

383

Timer timer = registry.timer("api.request.time");

384

385

// Get duration statistics

386

Snapshot snapshot = timer.getSnapshot();

387

System.out.printf("Request timing - Count: %d, Mean: %.2fms, 99th: %.2fms%n",

388

timer.getCount(),

389

snapshot.getMean() / 1_000_000.0, // Convert nanoseconds to milliseconds

390

snapshot.get99thPercentile() / 1_000_000.0);

391

392

// Get rate statistics

393

System.out.printf("Request rate - Mean: %.2f/sec, 1min: %.2f/sec%n",

394

timer.getMeanRate(),

395

timer.getOneMinuteRate());

396

```

397

398

## Core Interfaces

399

400

The core metrics implement several key interfaces that provide common functionality:

401

402

### Counting Interface

403

```java { .api }

404

public interface Counting {

405

long getCount();

406

}

407

```

408

409

Implemented by: `Counter`, `Histogram`, `Meter`, `Timer`

410

411

### Metered Interface

412

```java { .api }

413

public interface Metered extends Metric, Counting {

414

long getCount();

415

double getFifteenMinuteRate();

416

double getFiveMinuteRate();

417

double getMeanRate();

418

double getOneMinuteRate();

419

}

420

```

421

422

Implemented by: `Meter`, `Timer`

423

424

### Sampling Interface

425

```java { .api }

426

public interface Sampling {

427

Snapshot getSnapshot();

428

}

429

```

430

431

Implemented by: `Histogram`, `Timer`

432

433

### Metric Interface

434

```java { .api }

435

public interface Metric {

436

// Tag interface - no methods

437

}

438

```

439

440

Base interface implemented by all metric types.

441

442

## Best Practices

443

444

### Metric Naming

445

- Use hierarchical dot-separated names: `"http.requests.GET.api.users"`

446

- Be consistent with naming conventions across your application

447

- Use `MetricRegistry.name()` utility methods for consistent naming

448

- Consider using class names as prefixes for method-level metrics

449

450

### Registry Management

451

- Create one `MetricRegistry` per application (use `SharedMetricRegistries` for global access)

452

- Register metrics early in application lifecycle, ideally during initialization

453

- Use meaningful metric names that clearly indicate what is being measured

454

455

### Performance Considerations

456

- Metric operations are designed to be very fast (nanoseconds) and thread-safe

457

- Prefer instance variables for frequently-accessed metrics rather than registry lookups

458

- Consider the overhead of string concatenation in hot paths when building metric names

459

460

### Error Handling

461

- Metric operations should never throw exceptions in normal operation

462

- Metrics are designed to degrade gracefully under memory pressure

463

- Failed metric registrations throw `IllegalArgumentException` for duplicate names