CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-prometheus--simpleclient

Core instrumentation library for the Prometheus Java client, providing fundamental metric types for application monitoring

Pending
Overview
Eval results
Files

exemplars.mddocs/

Exemplar Support

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.

Capabilities

Exemplar Creation

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);

Exemplar Data Access

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)

Global Exemplar Configuration

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);

Exemplar Sampling Interfaces

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);
}

Default Exemplar Sampler

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);

Trace Integration Interface

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();
}

Important Notes

Exemplar Label Requirements

Exemplar labels must follow specific rules:

  • Total label length (names + values) cannot exceed 128 UTF-8 characters
  • Label names must match regex pattern: [a-zA-Z_][a-zA-Z_0-9]*
  • Must provide even number of strings (alternating names and values)
  • Neither label names nor values can be null
// 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
}

Performance Considerations

  • Exemplars add overhead to metric operations
  • Sampling reduces performance impact while preserving useful traces
  • Global configuration affects all metrics - use carefully in high-throughput systems
  • Consider sampling rates based on your tracing system capacity

Integration with Metrics

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

Common Integration Patterns

// 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
    }
}

Debugging and Monitoring

// 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

docs

counter.md

enumeration.md

exemplars.md

gauge.md

histogram.md

index.md

info.md

registry.md

summary.md

tile.json