@@ -40,13 +40,14 @@ public class ExemplarSampler {
4040 // to be overwritten by automatic exemplar sampling. exemplars.length == customExemplars.length
4141 private final AtomicBoolean acceptingNewExemplars = new AtomicBoolean (true );
4242 private final AtomicBoolean acceptingNewCustomExemplars = new AtomicBoolean (true );
43+ private final LongSupplier currentTimeMillis ;
4344
4445 @ Nullable
4546 private final SpanContext
4647 spanContext ; // may be null, in that case SpanContextSupplier.getSpanContext() is used.
4748
4849 public ExemplarSampler (ExemplarSamplerConfig config ) {
49- this (config , null );
50+ this (config , null , System :: currentTimeMillis );
5051 }
5152
5253 /**
@@ -58,15 +59,32 @@ public ExemplarSampler(ExemplarSamplerConfig config) {
5859 * SpanContextSupplier.getSpanContext()} is called to find a span context.
5960 */
6061 public ExemplarSampler (ExemplarSamplerConfig config , @ Nullable SpanContext spanContext ) {
62+ this (config , spanContext , System ::currentTimeMillis );
63+ }
64+
65+ /**
66+ * Constructor with an additional {code currentTimeMillis} argument for testing. This allows
67+ * injecting a custom time source to make tests deterministic and avoid flaky tests caused by
68+ * timing issues.
69+ *
70+ * @param config the exemplar sampler configuration
71+ * @param spanContext the span context, may be null
72+ * @param currentTimeMillis time source function that returns current time in milliseconds
73+ */
74+ public ExemplarSampler (
75+ ExemplarSamplerConfig config ,
76+ @ Nullable SpanContext spanContext ,
77+ LongSupplier currentTimeMillis ) {
6178 this .config = config ;
6279 this .exemplars = new Exemplar [config .getNumberOfExemplars ()];
6380 this .customExemplars = new Exemplar [exemplars .length ];
6481 this .spanContext = spanContext ;
82+ this .currentTimeMillis = currentTimeMillis ;
6583 }
6684
6785 public Exemplars collect () {
6886 // this may run in parallel with observe()
69- long now = System . currentTimeMillis ();
87+ long now = currentTimeMillis . getAsLong ();
7088 List <Exemplar > result = new ArrayList <>(exemplars .length );
7189 for (int i = 0 ; i < customExemplars .length ; i ++) {
7290 Exemplar exemplar = customExemplars [i ];
@@ -129,7 +147,7 @@ private long doObserve(double value) {
129147 }
130148
131149 private long doObserveSingleExemplar (double value ) {
132- long now = System . currentTimeMillis ();
150+ long now = currentTimeMillis . getAsLong ();
133151 Exemplar current = exemplars [0 ];
134152 if (current == null
135153 || now - current .getTimestampMillis () > config .getMinRetentionPeriodMillis ()) {
@@ -139,7 +157,7 @@ private long doObserveSingleExemplar(double value) {
139157 }
140158
141159 private long doObserveSingleExemplar (double amount , Labels labels ) {
142- long now = System . currentTimeMillis ();
160+ long now = currentTimeMillis . getAsLong ();
143161 Exemplar current = customExemplars [0 ];
144162 if (current == null
145163 || now - current .getTimestampMillis () > config .getMinRetentionPeriodMillis ()) {
@@ -149,7 +167,7 @@ private long doObserveSingleExemplar(double amount, Labels labels) {
149167 }
150168
151169 private long doObserveWithUpperBounds (double value , double [] classicUpperBounds ) {
152- long now = System . currentTimeMillis ();
170+ long now = currentTimeMillis . getAsLong ();
153171 for (int i = 0 ; i < classicUpperBounds .length ; i ++) {
154172 if (value <= classicUpperBounds [i ]) {
155173 Exemplar previous = exemplars [i ];
@@ -165,7 +183,7 @@ private long doObserveWithUpperBounds(double value, double[] classicUpperBounds)
165183 }
166184
167185 private long doObserveWithoutUpperBounds (double value ) {
168- final long now = System . currentTimeMillis ();
186+ final long now = currentTimeMillis . getAsLong ();
169187 Exemplar smallest = null ;
170188 int smallestIndex = -1 ;
171189 Exemplar largest = null ;
@@ -234,7 +252,7 @@ private long doObserveWithExemplar(double amount, Labels labels) {
234252
235253 private long doObserveWithExemplarWithUpperBounds (
236254 double value , Labels labels , double [] classicUpperBounds ) {
237- long now = System . currentTimeMillis ();
255+ long now = currentTimeMillis . getAsLong ();
238256 for (int i = 0 ; i < classicUpperBounds .length ; i ++) {
239257 if (value <= classicUpperBounds [i ]) {
240258 Exemplar previous = customExemplars [i ];
@@ -250,7 +268,7 @@ private long doObserveWithExemplarWithUpperBounds(
250268 }
251269
252270 private long doObserveWithExemplarWithoutUpperBounds (double amount , Labels labels ) {
253- final long now = System . currentTimeMillis ();
271+ final long now = currentTimeMillis . getAsLong ();
254272 int nullPos = -1 ;
255273 int oldestPos = -1 ;
256274 Exemplar oldest = null ;
0 commit comments