001package io.prometheus.client.exemplars; 002 003import io.prometheus.client.exemplars.tracer.common.SpanContextSupplier; 004 005/** 006 * Default Exemplar sampler. 007 * <p> 008 * Keeps each Exemplar for a minimum of ~7 seconds, then samples a new one. 009 */ 010public class DefaultExemplarSampler implements ExemplarSampler { 011 012 private static final String SPAN_ID = "span_id"; 013 private static final String TRACE_ID = "trace_id"; 014 015 private final SpanContextSupplier spanContextSupplier; 016 // Choosing a prime number for the retention interval makes behavior more predictable, 017 // because it is unlikely that retention happens at the exact same time as a Prometheus scrape. 018 private final long minRetentionIntervalMs = 7109; 019 private final Clock clock; 020 021 public DefaultExemplarSampler(SpanContextSupplier spanContextSupplier) { 022 this.spanContextSupplier = spanContextSupplier; 023 this.clock = new SystemClock(); 024 } 025 026 // for unit tests only 027 DefaultExemplarSampler(SpanContextSupplier spanContextSupplier, Clock clock) { 028 this.spanContextSupplier = spanContextSupplier; 029 this.clock = clock; 030 } 031 032 @Override 033 public Exemplar sample(double increment, Exemplar previous) { 034 return doSample(increment, previous); 035 } 036 037 @Override 038 public Exemplar sample(double value, double bucketFrom, double bucketTo, Exemplar previous) { 039 return doSample(value, previous); 040 } 041 042 private Exemplar doSample(double value, Exemplar previous) { 043 long timestampMs = clock.currentTimeMillis(); 044 if (previous == null || previous.getTimestampMs() == null 045 || timestampMs - previous.getTimestampMs() > minRetentionIntervalMs) { 046 String spanId = spanContextSupplier.getSpanId(); 047 String traceId = spanContextSupplier.getTraceId(); 048 if (traceId != null && spanId != null) { 049 return new Exemplar(value, timestampMs, SPAN_ID, spanId, TRACE_ID, traceId); 050 } 051 } 052 return null; 053 } 054 055 interface Clock { 056 long currentTimeMillis(); 057 } 058 059 static class SystemClock implements Clock { 060 @Override 061 public long currentTimeMillis() { 062 return System.currentTimeMillis(); 063 } 064 } 065}