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