Skip to content
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 histogram (and histogram part of timer) Prometheus output #2912

Merged
merged 27 commits into from
Apr 13, 2021
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
551f742
Infrastructure for exemplar support
tjquinno Apr 2, 2021
78a9180
Some refactoring in Prometheus output logic
tjquinno Apr 5, 2021
51762aa
Add metrics exemplar tracing module
tjquinno Apr 5, 2021
2439409
Fix style issues
tjquinno Apr 5, 2021
057204b
Copyright issues
tjquinno Apr 5, 2021
c2057d1
Fix calculation of which sample to report for mean()
tjquinno Apr 5, 2021
aa58489
Make retrieval of tracing context more resilient
tjquinno Apr 5, 2021
3000934
Improve search for near-matching value among samples; add exemplar su…
tjquinno Apr 5, 2021
96a0a80
Straggler
tjquinno Apr 5, 2021
31c0863
Revise doc; update SimpleTimer support; rework labeled and derived sa…
tjquinno Apr 6, 2021
ea227a8
Filter empty labels from ExemplarServices
tjquinno Apr 6, 2021
a1161a1
Add exemplar tracing example - not yet working in SE
tjquinno Apr 6, 2021
861c17d
Use SpanContext instead of TracingContext to retrieve the trace ID
tjquinno Apr 7, 2021
1ee0594
Fix the test checking the exemplar output
tjquinno Apr 7, 2021
29e17dc
Fix style violations
tjquinno Apr 7, 2021
4859299
Remove unused import
tjquinno Apr 7, 2021
c3b0181
Copyright errors
tjquinno Apr 7, 2021
ff98767
Expand and correct README.md
tjquinno Apr 7, 2021
de05d5f
Move old SE doc content to shared file; create new SE and MP pages in…
tjquinno Apr 7, 2021
602a778
Minor doc revisions
tjquinno Apr 7, 2021
2703d6c
Review comments: clarify some doc; fix missing newlines at ends of so…
tjquinno Apr 7, 2021
8baeb1b
Review comment: Remove now-unneeded export
tjquinno Apr 9, 2021
a1e511f
Review comment: remove unneeded no-op reflect-config.json
tjquinno Apr 9, 2021
d8bc178
Review comment: add provides for service
tjquinno Apr 9, 2021
a177b2b
Review comments: use common service loader with priority ordering
tjquinno Apr 12, 2021
d40f83f
Review comment: use common service loader with priority ordering
tjquinno Apr 12, 2021
5036ad7
Fix style violation
tjquinno Apr 12, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,11 @@
<artifactId>helidon-metrics</artifactId>
<version>${helidon.version}</version>
</dependency>
<dependency>
<groupId>io.helidon.metrics</groupId>
<artifactId>helidon-metrics-trace-exemplar</artifactId>
<version>${helidon.version}</version>
</dependency>

<!-- health checks -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright (c) 2021 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package io.helidon.metrics;

import java.util.function.Supplier;

class DefaultExemplarService implements ExemplarService {

@Override
public Supplier<String> labelSupplier() {
return () -> "";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2021 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package io.helidon.metrics;

import io.helidon.metrics.LabeledSample.Derived;
import io.helidon.metrics.WeightedSnapshot.WeightedSample;

/**
* Internal interface prescribing minimum behavior of a snapshot needed to produce output.
*/
interface DisplayableLabeledSnapshot {

Derived value(double quantile);

Derived median();

WeightedSample max();

WeightedSample min();

Derived mean();

Derived stdDev();

Derived sample75thPercentile();

Derived sample95thPercentile();

Derived sample98thPercentile();

Derived sample99thPercentile();

Derived sample999thPercentile();



}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2021 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package io.helidon.metrics;

import java.util.function.Supplier;

/**
* Behavior for supporting exemplars in metrics histograms.
*/
public interface ExemplarService {

/**
* Defayult priority for an {@code ExemplarService} found by the service loader without an explicit {@code @Priority}
tjquinno marked this conversation as resolved.
Show resolved Hide resolved
* annotation.
*/
int DEFAULT_PRIORITY = 1000;

/**
* Returns a label using whatever current context is available.
*
* @return the label
*/
Supplier<String> labelSupplier();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright (c) 2021 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package io.helidon.metrics;

import java.util.Comparator;
import java.util.ServiceLoader;
import java.util.StringJoiner;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.annotation.Priority;

import io.helidon.common.LazyValue;

/**
* Loads the {@link ExemplarService} instance (if any) with the most urgent priority.
*/
class ExemplarServiceManager {

private static final Logger LOGGER = Logger.getLogger(ExemplarServiceManager.class.getName());

private static final LazyValue<Supplier<String>> EXEMPLAR_SUPPLIER =
LazyValue.create(ExemplarServiceManager::chooseExemplarSupplier);

private ExemplarServiceManager() {
}

/**
* Returns the current exemplar string (e.g., trace ID).
*
* @return exemplar string provided by the highest-priority service instance
*/
static String exemplar() {
return EXEMPLAR_SUPPLIER.get().get();
}

private static Supplier<String> chooseExemplarSupplier() {
StringJoiner joiner = LOGGER.isLoggable(Level.FINE) ? new StringJoiner(",", "[", "]") : null;
ExemplarService exemplarService =
ServiceLoader.load(ExemplarService.class)
.stream()
.peek(provider -> {
if (joiner != null) {
joiner.add(String.format("%s(priority %d)", provider.type().getName(), priorityValue(provider)));
}
})
.min(Comparator.comparing(ExemplarServiceManager::priorityValue))
.map(ServiceLoader.Provider::get)
.orElse(new DefaultExemplarService());
if (joiner != null) {
LOGGER.log(Level.FINE, String.format("Candidate ExemplarSupports: %s, using %s", joiner.toString(),
exemplarService.getClass().getName()));
}

return exemplarService.labelSupplier();
}

private static int priorityValue(ServiceLoader.Provider<ExemplarService> exemplarSupportProvider) {
Priority p = exemplarSupportProvider.type().getAnnotation(Priority.class);
return p == null ? ExemplarService.DEFAULT_PRIORITY : p.value();
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.eclipse.microprofile.metrics.Snapshot;

import static java.lang.Math.exp;
import static java.lang.Math.min;

Expand Down Expand Up @@ -91,22 +89,23 @@ public int size() {
return (int) min(size, count.get());
}

public void update(long value) {
update(value, currentTimeInSeconds());
public void update(long value, String label) {
update(value, currentTimeInSeconds(), label);
}

/**
* Adds an old value with a fixed timestamp to the reservoir.
*
* @param value the value to be added
* @param timestamp the epoch timestamp of {@code value} in seconds
* @param label the optional label associated with the sample
*/
public void update(long value, long timestamp) {
public void update(long value, long timestamp, String label) {
rescaleIfNeeded();
lockForRegularUsage();
try {
final double itemWeight = weight(timestamp - startTime);
final WeightedSnapshot.WeightedSample sample = new WeightedSnapshot.WeightedSample(value, itemWeight);
final WeightedSnapshot.WeightedSample sample = new WeightedSnapshot.WeightedSample(value, itemWeight, label);
final double priority = itemWeight / ThreadLocalRandom.current().nextDouble();

final long newCount = count.incrementAndGet();
Expand Down Expand Up @@ -134,7 +133,7 @@ private void rescaleIfNeeded() {
}
}

public Snapshot getSnapshot() {
public WeightedSnapshot getSnapshot() {
rescaleIfNeeded();
lockForRegularUsage();
try {
Expand Down Expand Up @@ -184,7 +183,8 @@ private void rescale(long now, long next) {
for (Double key : keys) {
final WeightedSnapshot.WeightedSample sample = values.remove(key);
final WeightedSnapshot.WeightedSample newSample = new WeightedSnapshot.WeightedSample(sample.getValue(),
sample.getWeight() * scalingFactor);
sample.getWeight() * scalingFactor,
sample.label());
if (Double.compare(newSample.getWeight(), 0) == 0) {
continue;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2021 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -66,6 +66,26 @@ public String prometheusNameWithUnits(MetricID metricID) {
return metricName.endsWith("total") ? metricName : metricName + "_total";
}

@Override
public void prometheusData(StringBuilder sb, MetricID metricID, boolean withHelpType) {
String nameWithUnits = prometheusNameWithUnits(metricID);
if (withHelpType) {
prometheusType(sb, nameWithUnits, metadata().getType());
prometheusHelp(sb, nameWithUnits);
}
sb.append(nameWithUnits)
.append(prometheusTags(metricID.getTags()))
.append(" ")
.append(prometheusValue());
if (delegate instanceof CounterImpl) {
CounterSample sample = ((CounterImpl) delegate).counterSample;
if (sample != null) {
sb.append(prometheusExemplar(sample.label(), sample.value(), sample.timestamp()));
}
}
sb.append('\n');
}

@Override
public String prometheusValue() {
return Long.toString(getCount());
Expand All @@ -79,14 +99,17 @@ public void jsonData(JsonObjectBuilder builder, MetricID metricID) {
private static class CounterImpl implements Counter {
private final LongAdder adder = new LongAdder();

private CounterSample counterSample = null;

@Override
public void inc() {
adder.increment();
inc(1);
}

@Override
public void inc(long n) {
adder.add(n);
counterSample = new CounterSample(n);
}

@Override
Expand Down Expand Up @@ -136,4 +159,11 @@ public String toString() {
+ "metadata=" + metadata()
+ '}';
}

private static class CounterSample extends LabeledSample<Long> {

CounterSample(long value) {
super(value, ExemplarServiceManager.exemplar());
}
}
}
Loading