0
# Exemplar Support
1
2
Exemplars link individual metric observations to distributed trace data, enabling correlation between metrics and traces. They provide sample trace information alongside metric values, making it easier to investigate performance issues and understand system behavior.
3
4
## Capabilities
5
6
### Exemplar Creation
7
8
Create exemplars with trace information to link metrics to distributed traces.
9
10
```java { .api }
11
/**
12
* Create an exemplar without timestamp
13
* @param value The observed metric value
14
* @param labels Name/value pairs for trace information (even number required)
15
*/
16
public Exemplar(double value, String... labels);
17
18
/**
19
* Create an exemplar with timestamp
20
* @param value The observed metric value
21
* @param timestampMs Timestamp in milliseconds (System.currentTimeMillis())
22
* @param labels Name/value pairs for trace information (even number required)
23
*/
24
public Exemplar(double value, Long timestampMs, String... labels);
25
26
/**
27
* Create an exemplar with Map labels
28
* @param value The observed metric value
29
* @param labels Map of label names to values
30
*/
31
public Exemplar(double value, Map<String, String> labels);
32
```
33
34
**Usage Examples:**
35
36
```java
37
import io.prometheus.client.exemplars.Exemplar;
38
39
// Basic exemplar with trace ID
40
Exemplar exemplar1 = new Exemplar(0.123, "trace_id", "abc123");
41
42
// Exemplar with timestamp and multiple labels
43
Exemplar exemplar2 = new Exemplar(
44
0.456,
45
System.currentTimeMillis(),
46
"trace_id", "def456",
47
"span_id", "span789",
48
"user_id", "user123"
49
);
50
51
// Exemplar using Map
52
Map<String, String> traceLabels = new HashMap<>();
53
traceLabels.put("trace_id", "xyz789");
54
traceLabels.put("operation", "database_query");
55
Exemplar exemplar3 = new Exemplar(0.089, traceLabels);
56
```
57
58
### Exemplar Data Access
59
60
Retrieve exemplar information for trace correlation and debugging.
61
62
```java { .api }
63
/**
64
* Get the observed metric value
65
* @return The metric value this exemplar represents
66
*/
67
public double getValue();
68
69
/**
70
* Get the timestamp in milliseconds
71
* @return Timestamp or null if not set
72
*/
73
public Long getTimestampMs();
74
75
/**
76
* Get the trace labels as array
77
* @return Array of alternating label names and values
78
*/
79
public String[] getLabels();
80
81
/**
82
* Convert Map labels to array format
83
* @param labels Map of label names to values
84
* @return Array of alternating names and values
85
*/
86
public static String[] mapToArray(Map<String, String> labels);
87
```
88
89
**Usage Examples:**
90
91
```java
92
// Access exemplar data
93
Exemplar exemplar = new Exemplar(0.234, "trace_id", "trace123", "span_id", "span456");
94
95
double value = exemplar.getValue(); // 0.234
96
Long timestamp = exemplar.getTimestampMs(); // null (not set)
97
String[] labels = exemplar.getLabels(); // ["trace_id", "trace123", "span_id", "span456"]
98
99
// Utility method for label conversion
100
Map<String, String> labelMap = Map.of("trace_id", "abc", "span_id", "def");
101
String[] labelArray = Exemplar.mapToArray(labelMap);
102
// Result: ["trace_id", "abc", "span_id", "def"] (order may vary)
103
```
104
105
### Global Exemplar Configuration
106
107
Configure exemplar sampling behavior globally across all metrics.
108
109
```java { .api }
110
/**
111
* Check if exemplars are globally enabled
112
* @return true if exemplars should be collected
113
*/
114
public static boolean isExemplarsEnabled();
115
116
/**
117
* Enable or disable exemplars globally
118
* @param enabled Whether to enable exemplar collection
119
*/
120
public static void setExemplarsEnabled(boolean enabled);
121
122
/**
123
* Get the global counter exemplar sampler
124
* @return Current counter sampler or null if not set
125
*/
126
public static CounterExemplarSampler getCounterExemplarSampler();
127
128
/**
129
* Set global counter exemplar sampler
130
* @param sampler Sampler implementation for counter metrics
131
*/
132
public static void setCounterExemplarSampler(CounterExemplarSampler sampler);
133
134
/**
135
* Get the global histogram exemplar sampler
136
* @return Current histogram sampler or null if not set
137
*/
138
public static HistogramExemplarSampler getHistogramExemplarSampler();
139
140
/**
141
* Set global histogram exemplar sampler
142
* @param sampler Sampler implementation for histogram metrics
143
*/
144
public static void setHistogramExemplarSampler(HistogramExemplarSampler sampler);
145
```
146
147
### Exemplar Sampling Interfaces
148
149
Implement custom sampling strategies for different metric types.
150
151
```java { .api }
152
/**
153
* Base interface for exemplar sampling
154
*/
155
public interface ExemplarSampler {
156
// Marker interface for exemplar sampling implementations
157
}
158
159
/**
160
* Exemplar sampler for counter metrics
161
*/
162
public interface CounterExemplarSampler extends ExemplarSampler {
163
/**
164
* Sample an exemplar for counter increment
165
* @param increment The increment amount
166
* @param previous Previous exemplar (may be null)
167
* @return New exemplar or null to skip sampling
168
*/
169
Exemplar sample(double increment, Exemplar previous);
170
}
171
172
/**
173
* Exemplar sampler for histogram metrics
174
*/
175
public interface HistogramExemplarSampler extends ExemplarSampler {
176
/**
177
* Sample an exemplar for histogram observation
178
* @param value The observed value
179
* @param bucketFrom Lower bound of the bucket
180
* @param bucketTo Upper bound of the bucket
181
* @param previous Previous exemplar for this bucket
182
* @return New exemplar or null to skip sampling
183
*/
184
Exemplar sample(double value, double bucketFrom, double bucketTo, Exemplar previous);
185
}
186
```
187
188
### Default Exemplar Sampler
189
190
Built-in sampling implementation with reasonable defaults.
191
192
```java { .api }
193
/**
194
* Default exemplar sampler implementation
195
*/
196
public class DefaultExemplarSampler implements CounterExemplarSampler, HistogramExemplarSampler {
197
// Implementation details handled internally
198
}
199
```
200
201
**Usage Examples:**
202
203
```java
204
import io.prometheus.client.exemplars.*;
205
206
// Enable exemplars globally
207
ExemplarConfig.setExemplarsEnabled(true);
208
209
// Set up default samplers
210
ExemplarConfig.setCounterExemplarSampler(new DefaultExemplarSampler());
211
ExemplarConfig.setHistogramExemplarSampler(new DefaultExemplarSampler());
212
213
// Custom counter sampler
214
CounterExemplarSampler customCounterSampler = new CounterExemplarSampler() {
215
@Override
216
public Exemplar sample(double increment, Exemplar previous) {
217
// Sample every 100th increment
218
if (Math.random() < 0.01) {
219
String traceId = getCurrentTraceId(); // Your trace context
220
return new Exemplar(increment, "trace_id", traceId);
221
}
222
return null; // Skip sampling
223
}
224
};
225
226
// Custom histogram sampler
227
HistogramExemplarSampler customHistogramSampler = new HistogramExemplarSampler() {
228
@Override
229
public Exemplar sample(double value, double bucketFrom, double bucketTo, Exemplar previous) {
230
// Sample slow requests (>1 second)
231
if (value > 1.0) {
232
String traceId = getCurrentTraceId();
233
String spanId = getCurrentSpanId();
234
return new Exemplar(value, "trace_id", traceId, "span_id", spanId);
235
}
236
return null;
237
}
238
};
239
240
ExemplarConfig.setCounterExemplarSampler(customCounterSampler);
241
ExemplarConfig.setHistogramExemplarSampler(customHistogramSampler);
242
```
243
244
### Trace Integration Interface
245
246
Interface for integrating with distributed tracing systems.
247
248
```java { .api }
249
/**
250
* Interface for trace context integration
251
*/
252
public interface Tracer {
253
/**
254
* Get current span context for exemplar labels
255
* @return Map of trace labels or null if no active span
256
*/
257
Map<String, String> getCurrentSpanContext();
258
}
259
```
260
261
## Important Notes
262
263
### Exemplar Label Requirements
264
265
Exemplar labels must follow specific rules:
266
- Total label length (names + values) cannot exceed 128 UTF-8 characters
267
- Label names must match regex pattern: `[a-zA-Z_][a-zA-Z_0-9]*`
268
- Must provide even number of strings (alternating names and values)
269
- Neither label names nor values can be null
270
271
```java
272
// Valid exemplar labels
273
Exemplar valid = new Exemplar(1.0, "trace_id", "abc123");
274
275
// Invalid - odd number of strings
276
try {
277
Exemplar invalid = new Exemplar(1.0, "trace_id", "abc123", "span_id"); // Missing value
278
} catch (IllegalArgumentException e) {
279
// Handle invalid label format
280
}
281
282
// Invalid - label name format
283
try {
284
Exemplar invalid = new Exemplar(1.0, "trace-id", "abc123"); // Hyphen not allowed
285
} catch (IllegalArgumentException e) {
286
// Handle invalid label name
287
}
288
```
289
290
### Performance Considerations
291
292
- Exemplars add overhead to metric operations
293
- Sampling reduces performance impact while preserving useful traces
294
- Global configuration affects all metrics - use carefully in high-throughput systems
295
- Consider sampling rates based on your tracing system capacity
296
297
### Integration with Metrics
298
299
Exemplars work with Counter and Histogram metrics:
300
301
```java
302
// Counter with exemplars
303
Counter requests = Counter.build()
304
.name("requests_total")
305
.help("Total requests")
306
.withExemplars() // Enable exemplar support
307
.register();
308
309
// Manual exemplar
310
requests.incWithExemplar(1.0, "trace_id", "abc123");
311
312
// Histogram with exemplars
313
Histogram latency = Histogram.build()
314
.name("request_duration_seconds")
315
.help("Request duration")
316
.withExemplarSampler(customHistogramSampler)
317
.register();
318
319
// Automatic exemplar via sampler
320
latency.observe(0.123); // May create exemplar based on sampler logic
321
```
322
323
### Common Integration Patterns
324
325
```java
326
// OpenTelemetry integration
327
public class OpenTelemetryTracer implements Tracer {
328
@Override
329
public Map<String, String> getCurrentSpanContext() {
330
Span currentSpan = Span.current();
331
if (currentSpan.getSpanContext().isValid()) {
332
Map<String, String> context = new HashMap<>();
333
context.put("trace_id", currentSpan.getSpanContext().getTraceId());
334
context.put("span_id", currentSpan.getSpanContext().getSpanId());
335
return context;
336
}
337
return null;
338
}
339
}
340
341
// Jaeger integration example
342
public class JaegerIntegration {
343
public static void configureExemplars() {
344
CounterExemplarSampler counterSampler = (increment, previous) -> {
345
io.jaegertracing.internal.JaegerSpan span = getActiveSpan();
346
if (span != null) {
347
return new Exemplar(increment,
348
"trace_id", span.context().getTraceId(),
349
"span_id", span.context().getSpanId());
350
}
351
return null;
352
};
353
354
ExemplarConfig.setCounterExemplarSampler(counterSampler);
355
}
356
}
357
358
// Application startup configuration
359
public class MetricsConfiguration {
360
@PostConstruct
361
public void configureExemplars() {
362
// Enable exemplars if tracing is available
363
boolean tracingEnabled = isTracingConfigured();
364
ExemplarConfig.setExemplarsEnabled(tracingEnabled);
365
366
if (tracingEnabled) {
367
// Configure samplers based on your tracing system
368
ExemplarConfig.setCounterExemplarSampler(new DefaultExemplarSampler());
369
ExemplarConfig.setHistogramExemplarSampler(new DefaultExemplarSampler());
370
}
371
}
372
}
373
374
// Manual exemplar with current trace context
375
public void processRequestWithExemplar() {
376
String traceId = getCurrentTraceId();
377
String spanId = getCurrentSpanId();
378
379
if (traceId != null) {
380
requestCounter.incWithExemplar(1.0, "trace_id", traceId, "span_id", spanId);
381
382
double duration = requestLatency.time(() -> {
383
return handleRequest();
384
});
385
386
// Histogram automatically uses configured sampler for exemplars
387
}
388
}
389
```
390
391
### Debugging and Monitoring
392
393
```java
394
// Check exemplar configuration
395
public void debugExemplarConfig() {
396
System.out.println("Exemplars enabled: " + ExemplarConfig.isExemplarsEnabled());
397
System.out.println("Counter sampler: " + ExemplarConfig.getCounterExemplarSampler());
398
System.out.println("Histogram sampler: " + ExemplarConfig.getHistogramExemplarSampler());
399
}
400
401
// Validate exemplar creation
402
public Exemplar createSafeExemplar(double value, Map<String, String> traceContext) {
403
try {
404
return new Exemplar(value, traceContext);
405
} catch (IllegalArgumentException e) {
406
// Log warning and return null instead of failing
407
logger.warn("Failed to create exemplar: " + e.getMessage());
408
return null;
409
}
410
}
411
```