Core instrumentation library for the Prometheus Java client, providing fundamental metric types for application monitoring
—
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.
Create exemplars with trace information to link metrics to distributed traces.
/**
* Create an exemplar without timestamp
* @param value The observed metric value
* @param labels Name/value pairs for trace information (even number required)
*/
public Exemplar(double value, String... labels);
/**
* Create an exemplar with timestamp
* @param value The observed metric value
* @param timestampMs Timestamp in milliseconds (System.currentTimeMillis())
* @param labels Name/value pairs for trace information (even number required)
*/
public Exemplar(double value, Long timestampMs, String... labels);
/**
* Create an exemplar with Map labels
* @param value The observed metric value
* @param labels Map of label names to values
*/
public Exemplar(double value, Map<String, String> labels);Usage Examples:
import io.prometheus.client.exemplars.Exemplar;
// Basic exemplar with trace ID
Exemplar exemplar1 = new Exemplar(0.123, "trace_id", "abc123");
// Exemplar with timestamp and multiple labels
Exemplar exemplar2 = new Exemplar(
0.456,
System.currentTimeMillis(),
"trace_id", "def456",
"span_id", "span789",
"user_id", "user123"
);
// Exemplar using Map
Map<String, String> traceLabels = new HashMap<>();
traceLabels.put("trace_id", "xyz789");
traceLabels.put("operation", "database_query");
Exemplar exemplar3 = new Exemplar(0.089, traceLabels);Retrieve exemplar information for trace correlation and debugging.
/**
* Get the observed metric value
* @return The metric value this exemplar represents
*/
public double getValue();
/**
* Get the timestamp in milliseconds
* @return Timestamp or null if not set
*/
public Long getTimestampMs();
/**
* Get the trace labels as array
* @return Array of alternating label names and values
*/
public String[] getLabels();
/**
* Convert Map labels to array format
* @param labels Map of label names to values
* @return Array of alternating names and values
*/
public static String[] mapToArray(Map<String, String> labels);Usage Examples:
// Access exemplar data
Exemplar exemplar = new Exemplar(0.234, "trace_id", "trace123", "span_id", "span456");
double value = exemplar.getValue(); // 0.234
Long timestamp = exemplar.getTimestampMs(); // null (not set)
String[] labels = exemplar.getLabels(); // ["trace_id", "trace123", "span_id", "span456"]
// Utility method for label conversion
Map<String, String> labelMap = Map.of("trace_id", "abc", "span_id", "def");
String[] labelArray = Exemplar.mapToArray(labelMap);
// Result: ["trace_id", "abc", "span_id", "def"] (order may vary)Configure exemplar sampling behavior globally across all metrics.
/**
* Check if exemplars are globally enabled
* @return true if exemplars should be collected
*/
public static boolean isExemplarsEnabled();
/**
* Enable or disable exemplars globally
* @param enabled Whether to enable exemplar collection
*/
public static void setExemplarsEnabled(boolean enabled);
/**
* Get the global counter exemplar sampler
* @return Current counter sampler or null if not set
*/
public static CounterExemplarSampler getCounterExemplarSampler();
/**
* Set global counter exemplar sampler
* @param sampler Sampler implementation for counter metrics
*/
public static void setCounterExemplarSampler(CounterExemplarSampler sampler);
/**
* Get the global histogram exemplar sampler
* @return Current histogram sampler or null if not set
*/
public static HistogramExemplarSampler getHistogramExemplarSampler();
/**
* Set global histogram exemplar sampler
* @param sampler Sampler implementation for histogram metrics
*/
public static void setHistogramExemplarSampler(HistogramExemplarSampler sampler);Implement custom sampling strategies for different metric types.
/**
* Base interface for exemplar sampling
*/
public interface ExemplarSampler {
// Marker interface for exemplar sampling implementations
}
/**
* Exemplar sampler for counter metrics
*/
public interface CounterExemplarSampler extends ExemplarSampler {
/**
* Sample an exemplar for counter increment
* @param increment The increment amount
* @param previous Previous exemplar (may be null)
* @return New exemplar or null to skip sampling
*/
Exemplar sample(double increment, Exemplar previous);
}
/**
* Exemplar sampler for histogram metrics
*/
public interface HistogramExemplarSampler extends ExemplarSampler {
/**
* Sample an exemplar for histogram observation
* @param value The observed value
* @param bucketFrom Lower bound of the bucket
* @param bucketTo Upper bound of the bucket
* @param previous Previous exemplar for this bucket
* @return New exemplar or null to skip sampling
*/
Exemplar sample(double value, double bucketFrom, double bucketTo, Exemplar previous);
}Built-in sampling implementation with reasonable defaults.
/**
* Default exemplar sampler implementation
*/
public class DefaultExemplarSampler implements CounterExemplarSampler, HistogramExemplarSampler {
// Implementation details handled internally
}Usage Examples:
import io.prometheus.client.exemplars.*;
// Enable exemplars globally
ExemplarConfig.setExemplarsEnabled(true);
// Set up default samplers
ExemplarConfig.setCounterExemplarSampler(new DefaultExemplarSampler());
ExemplarConfig.setHistogramExemplarSampler(new DefaultExemplarSampler());
// Custom counter sampler
CounterExemplarSampler customCounterSampler = new CounterExemplarSampler() {
@Override
public Exemplar sample(double increment, Exemplar previous) {
// Sample every 100th increment
if (Math.random() < 0.01) {
String traceId = getCurrentTraceId(); // Your trace context
return new Exemplar(increment, "trace_id", traceId);
}
return null; // Skip sampling
}
};
// Custom histogram sampler
HistogramExemplarSampler customHistogramSampler = new HistogramExemplarSampler() {
@Override
public Exemplar sample(double value, double bucketFrom, double bucketTo, Exemplar previous) {
// Sample slow requests (>1 second)
if (value > 1.0) {
String traceId = getCurrentTraceId();
String spanId = getCurrentSpanId();
return new Exemplar(value, "trace_id", traceId, "span_id", spanId);
}
return null;
}
};
ExemplarConfig.setCounterExemplarSampler(customCounterSampler);
ExemplarConfig.setHistogramExemplarSampler(customHistogramSampler);Interface for integrating with distributed tracing systems.
/**
* Interface for trace context integration
*/
public interface Tracer {
/**
* Get current span context for exemplar labels
* @return Map of trace labels or null if no active span
*/
Map<String, String> getCurrentSpanContext();
}Exemplar labels must follow specific rules:
[a-zA-Z_][a-zA-Z_0-9]*// Valid exemplar labels
Exemplar valid = new Exemplar(1.0, "trace_id", "abc123");
// Invalid - odd number of strings
try {
Exemplar invalid = new Exemplar(1.0, "trace_id", "abc123", "span_id"); // Missing value
} catch (IllegalArgumentException e) {
// Handle invalid label format
}
// Invalid - label name format
try {
Exemplar invalid = new Exemplar(1.0, "trace-id", "abc123"); // Hyphen not allowed
} catch (IllegalArgumentException e) {
// Handle invalid label name
}Exemplars work with Counter and Histogram metrics:
// Counter with exemplars
Counter requests = Counter.build()
.name("requests_total")
.help("Total requests")
.withExemplars() // Enable exemplar support
.register();
// Manual exemplar
requests.incWithExemplar(1.0, "trace_id", "abc123");
// Histogram with exemplars
Histogram latency = Histogram.build()
.name("request_duration_seconds")
.help("Request duration")
.withExemplarSampler(customHistogramSampler)
.register();
// Automatic exemplar via sampler
latency.observe(0.123); // May create exemplar based on sampler logic// OpenTelemetry integration
public class OpenTelemetryTracer implements Tracer {
@Override
public Map<String, String> getCurrentSpanContext() {
Span currentSpan = Span.current();
if (currentSpan.getSpanContext().isValid()) {
Map<String, String> context = new HashMap<>();
context.put("trace_id", currentSpan.getSpanContext().getTraceId());
context.put("span_id", currentSpan.getSpanContext().getSpanId());
return context;
}
return null;
}
}
// Jaeger integration example
public class JaegerIntegration {
public static void configureExemplars() {
CounterExemplarSampler counterSampler = (increment, previous) -> {
io.jaegertracing.internal.JaegerSpan span = getActiveSpan();
if (span != null) {
return new Exemplar(increment,
"trace_id", span.context().getTraceId(),
"span_id", span.context().getSpanId());
}
return null;
};
ExemplarConfig.setCounterExemplarSampler(counterSampler);
}
}
// Application startup configuration
public class MetricsConfiguration {
@PostConstruct
public void configureExemplars() {
// Enable exemplars if tracing is available
boolean tracingEnabled = isTracingConfigured();
ExemplarConfig.setExemplarsEnabled(tracingEnabled);
if (tracingEnabled) {
// Configure samplers based on your tracing system
ExemplarConfig.setCounterExemplarSampler(new DefaultExemplarSampler());
ExemplarConfig.setHistogramExemplarSampler(new DefaultExemplarSampler());
}
}
}
// Manual exemplar with current trace context
public void processRequestWithExemplar() {
String traceId = getCurrentTraceId();
String spanId = getCurrentSpanId();
if (traceId != null) {
requestCounter.incWithExemplar(1.0, "trace_id", traceId, "span_id", spanId);
double duration = requestLatency.time(() -> {
return handleRequest();
});
// Histogram automatically uses configured sampler for exemplars
}
}// Check exemplar configuration
public void debugExemplarConfig() {
System.out.println("Exemplars enabled: " + ExemplarConfig.isExemplarsEnabled());
System.out.println("Counter sampler: " + ExemplarConfig.getCounterExemplarSampler());
System.out.println("Histogram sampler: " + ExemplarConfig.getHistogramExemplarSampler());
}
// Validate exemplar creation
public Exemplar createSafeExemplar(double value, Map<String, String> traceContext) {
try {
return new Exemplar(value, traceContext);
} catch (IllegalArgumentException e) {
// Log warning and return null instead of failing
logger.warn("Failed to create exemplar: " + e.getMessage());
return null;
}
}Install with Tessl CLI
npx tessl i tessl/maven-io-prometheus--simpleclient