-
Notifications
You must be signed in to change notification settings - Fork 961
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add exemplar support to _count #3996
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,6 +28,7 @@ | |
import java.time.Duration; | ||
import java.util.Arrays; | ||
import java.util.concurrent.TimeUnit; | ||
import java.util.concurrent.atomic.AtomicReference; | ||
import java.util.concurrent.atomic.AtomicReferenceArray; | ||
|
||
import static java.util.concurrent.TimeUnit.NANOSECONDS; | ||
|
@@ -41,20 +42,23 @@ | |
*/ | ||
class PrometheusHistogram extends TimeWindowFixedBoundaryHistogram { | ||
|
||
@Nullable | ||
private final double[] buckets; | ||
|
||
@Nullable | ||
private final AtomicReferenceArray<Exemplar> exemplars; | ||
|
||
@Nullable | ||
private final AtomicReference<Exemplar> lastExemplar; | ||
|
||
@Nullable | ||
private final HistogramExemplarSampler exemplarSampler; | ||
|
||
PrometheusHistogram(Clock clock, DistributionStatisticConfig config, | ||
@Nullable HistogramExemplarSampler exemplarSampler) { | ||
super(clock, DistributionStatisticConfig.builder() | ||
.expiry(Duration.ofDays(1825)) // effectively | ||
// never | ||
// roll | ||
// over | ||
// effectively never rolls over | ||
.expiry(Duration.ofDays(1825)) | ||
.bufferLength(1) | ||
.build() | ||
.merge(config), true); | ||
|
@@ -70,10 +74,12 @@ class PrometheusHistogram extends TimeWindowFixedBoundaryHistogram { | |
this.buckets = originalBuckets; | ||
} | ||
this.exemplars = new AtomicReferenceArray<>(this.buckets.length); | ||
this.lastExemplar = new AtomicReference<>(); | ||
} | ||
else { | ||
this.buckets = null; | ||
this.exemplars = null; | ||
this.lastExemplar = null; | ||
} | ||
} | ||
|
||
|
@@ -107,16 +113,20 @@ private void updateExemplar(double value, @Nullable TimeUnit sourceUnit, @Nullab | |
int index) { | ||
double bucketFrom = (index == 0) ? Double.NEGATIVE_INFINITY : buckets[index - 1]; | ||
double bucketTo = buckets[index]; | ||
jonatan-ivanov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Exemplar prev; | ||
Exemplar next; | ||
Exemplar previusBucketExemplar; | ||
Exemplar previousLastExemplar; | ||
Exemplar nextExemplar; | ||
|
||
double exemplarValue = (sourceUnit != null && destinationUnit != null) | ||
? TimeUtils.convert(value, sourceUnit, destinationUnit) : value; | ||
do { | ||
prev = exemplars.get(index); | ||
next = exemplarSampler.sample(exemplarValue, bucketFrom, bucketTo, prev); | ||
previusBucketExemplar = exemplars.get(index); | ||
previousLastExemplar = lastExemplar.get(); | ||
nextExemplar = exemplarSampler.sample(exemplarValue, bucketFrom, bucketTo, previusBucketExemplar); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please notice that here and both in |
||
} | ||
while (next != null && next != prev && !exemplars.compareAndSet(index, prev, next)); | ||
while (nextExemplar != null && nextExemplar != previusBucketExemplar | ||
&& !(exemplars.compareAndSet(index, previusBucketExemplar, nextExemplar) | ||
&& lastExemplar.compareAndSet(previousLastExemplar, nextExemplar))); | ||
} | ||
|
||
@Nullable | ||
|
@@ -134,6 +144,11 @@ Exemplar[] exemplars() { | |
} | ||
} | ||
|
||
@Nullable | ||
Exemplar lastExemplar() { | ||
return this.lastExemplar.get(); | ||
} | ||
|
||
/** | ||
* The least bucket that is less than or equal to a sample. | ||
*/ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -232,6 +232,7 @@ public DistributionSummary newDistributionSummary(Meter.Id id, | |
} | ||
} | ||
|
||
Exemplar lastExemplar = summary.lastExemplar(); | ||
Collector.Type type = Collector.Type.SUMMARY; | ||
if (histogramCounts.length > 0) { | ||
// Prometheus doesn't balk at a metric being BOTH a histogram and a | ||
|
@@ -244,7 +245,7 @@ public DistributionSummary newDistributionSummary(Meter.Id id, | |
case Prometheus: | ||
histogramKeys.add("le"); | ||
|
||
Exemplar[] exemplars = summary.exemplars(); | ||
Exemplar[] exemplars = summary.histogramExemplars(); | ||
|
||
// satisfies | ||
// https://prometheus.io/docs/concepts/metric_types/#histogram | ||
|
@@ -295,8 +296,14 @@ public DistributionSummary newDistributionSummary(Meter.Id id, | |
|
||
} | ||
|
||
samples.add( | ||
new Collector.MetricFamilySamples.Sample(conventionName + "_count", tagKeys, tagValues, count)); | ||
if (lastExemplar == null) { | ||
samples.add(new Collector.MetricFamilySamples.Sample(conventionName + "_count", tagKeys, tagValues, | ||
count)); | ||
} | ||
else { | ||
samples.add(new Collector.MetricFamilySamples.Sample(conventionName + "_count", tagKeys, tagValues, | ||
count, copyExemplarWithNewValue(1.0, lastExemplar))); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here I'm setting a new value for the |
||
} | ||
|
||
samples.add(new Collector.MetricFamilySamples.Sample(conventionName + "_sum", tagKeys, tagValues, | ||
summary.totalAmount())); | ||
|
@@ -316,7 +323,7 @@ protected io.micrometer.core.instrument.Timer newTimer(Meter.Id id, | |
PrometheusTimer timer = new PrometheusTimer(id, clock, distributionStatisticConfig, pauseDetector, | ||
prometheusConfig.histogramFlavor(), exemplarSampler); | ||
applyToCollector(id, (collector) -> addDistributionStatisticSamples(distributionStatisticConfig, collector, | ||
timer, timer::exemplars, tagValues(id), false)); | ||
timer, timer::lastExemplar, timer::histogramExemplars, tagValues(id), false)); | ||
return timer; | ||
} | ||
|
||
|
@@ -338,7 +345,7 @@ protected LongTaskTimer newLongTaskTimer(Meter.Id id, DistributionStatisticConfi | |
LongTaskTimer ltt = new CumulativeHistogramLongTaskTimer(id, clock, getBaseTimeUnit(), | ||
distributionStatisticConfig); | ||
applyToCollector(id, (collector) -> addDistributionStatisticSamples(distributionStatisticConfig, collector, ltt, | ||
() -> null, tagValues(id), true)); | ||
() -> null, () -> null, tagValues(id), true)); | ||
return ltt; | ||
} | ||
|
||
|
@@ -440,8 +447,8 @@ public CollectorRegistry getPrometheusRegistry() { | |
} | ||
|
||
private void addDistributionStatisticSamples(DistributionStatisticConfig distributionStatisticConfig, | ||
MicrometerCollector collector, HistogramSupport histogramSupport, Supplier<Exemplar[]> exemplarsSupplier, | ||
List<String> tagValues, boolean forLongTaskTimer) { | ||
MicrometerCollector collector, HistogramSupport histogramSupport, Supplier<Exemplar> lastExemplarSupplier, | ||
Supplier<Exemplar[]> histogramExemplarsSupplier, List<String> tagValues, boolean forLongTaskTimer) { | ||
collector.add(tagValues, (conventionName, tagKeys) -> { | ||
Stream.Builder<Collector.MetricFamilySamples.Sample> samples = Stream.builder(); | ||
|
||
|
@@ -463,6 +470,7 @@ private void addDistributionStatisticSamples(DistributionStatisticConfig distrib | |
} | ||
} | ||
|
||
Exemplar lastExemplar = lastExemplarSupplier.get(); | ||
Collector.Type type = distributionStatisticConfig.isPublishingHistogram() ? Collector.Type.HISTOGRAM | ||
: Collector.Type.SUMMARY; | ||
if (histogramCounts.length > 0) { | ||
|
@@ -477,7 +485,7 @@ private void addDistributionStatisticSamples(DistributionStatisticConfig distrib | |
case Prometheus: | ||
histogramKeys.add("le"); | ||
|
||
Exemplar[] exemplars = exemplarsSupplier.get(); | ||
Exemplar[] exemplars = histogramExemplarsSupplier.get(); | ||
|
||
// satisfies | ||
// https://prometheus.io/docs/concepts/metric_types/#histogram | ||
|
@@ -523,8 +531,15 @@ private void addDistributionStatisticSamples(DistributionStatisticConfig distrib | |
|
||
} | ||
|
||
samples.add(new Collector.MetricFamilySamples.Sample( | ||
conventionName + (forLongTaskTimer ? "_active_count" : "_count"), tagKeys, tagValues, count)); | ||
if (lastExemplar == null) { | ||
samples.add(new Collector.MetricFamilySamples.Sample( | ||
conventionName + (forLongTaskTimer ? "_active_count" : "_count"), tagKeys, tagValues, count)); | ||
} | ||
else { | ||
samples.add(new Collector.MetricFamilySamples.Sample( | ||
conventionName + (forLongTaskTimer ? "_active_count" : "_count"), tagKeys, tagValues, count, | ||
copyExemplarWithNewValue(1.0, lastExemplar))); | ||
} | ||
|
||
samples.add(new Collector.MetricFamilySamples.Sample( | ||
conventionName + (forLongTaskTimer ? "_duration_sum" : "_sum"), tagKeys, tagValues, | ||
|
@@ -537,6 +552,15 @@ private void addDistributionStatisticSamples(DistributionStatisticConfig distrib | |
}); | ||
} | ||
|
||
private Exemplar copyExemplarWithNewValue(double newValue, Exemplar exemplar) { | ||
String[] labels = new String[exemplar.getNumberOfLabels() * 2]; | ||
for (int i = 0; i < exemplar.getNumberOfLabels(); i++) { | ||
labels[2 * i] = exemplar.getLabelName(i); | ||
labels[2 * i + 1] = exemplar.getLabelValue(i); | ||
} | ||
return new Exemplar(newValue, exemplar.getTimestampMs(), labels); | ||
} | ||
|
||
private void onMeterRemoved(Meter meter) { | ||
MicrometerCollector collector = collectorMap.get(getConventionName(meter.getId())); | ||
if (collector != null) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both in
PrometheusDistributionSummary
and inPrometheusTimer
, the last sampled exemplars can be recorded in two ways:PrometheusHistogram
records it so that the exemplar will not be sampled twice (onesample
call inPrometheusHistogram
):Histogram
use-case (there are buckets +_count
)PrometheusDistributionSummary
/PrometheusTimer
record it directly:Summary
use-case (no buckets, just_count
)