Skip to content

Commit c4791e9

Browse files
jkwatsonanuraaga
andauthored
Very basic Aggregation-configuration API in the SDK. (#2037)
CHANGELOG: SDK : Enhancement: A basic aggregation configuration API has been added to the SDK's meter provider implementation. * Create a very basic view API in the SDK. * fix formatting * move the ViewRegistry up one package, and clean up the visibility of other classes * Support matching by instrument name * Update sdk/src/main/java/io/opentelemetry/sdk/metrics/ViewRegistry.java Co-authored-by: Anuraag Agrawal <[email protected]> * Update sdk/src/main/java/io/opentelemetry/sdk/metrics/view/ViewSpecification.java Co-authored-by: Anuraag Agrawal <[email protected]> * Update sdk/src/main/java/io/opentelemetry/sdk/metrics/view/ViewSpecification.java Co-authored-by: Anuraag Agrawal <[email protected]> * Update sdk/src/main/java/io/opentelemetry/sdk/metrics/view/ViewSpecification.java Co-authored-by: Anuraag Agrawal <[email protected]> * Update sdk/src/main/java/io/opentelemetry/sdk/metrics/view/ViewSpecification.java Co-authored-by: Anuraag Agrawal <[email protected]> * fix formatting issues from GH * small renaming to a big name * small renaming to a big name * re-order matching check and fix a merge issue * Update from upstream changes. * Update from upstream changes. * Adjust defaults based on the latest behavior * refactor before writing tests * tests for the AggregationChooser and a bugfix they uncovered * tests for the ViewRegistry * Javadoc for the AggregationConfiguration * Add more javadoc. * Update sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/Batcher.java Co-authored-by: Anuraag Agrawal <[email protected]> * Update sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/view/AggregationConfiguration.java Co-authored-by: Anuraag Agrawal <[email protected]> * Update sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/view/AggregationConfiguration.java Co-authored-by: Anuraag Agrawal <[email protected]> * Update sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/view/InstrumentSelector.java Co-authored-by: Anuraag Agrawal <[email protected]> * Update sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/view/InstrumentSelector.java Co-authored-by: Anuraag Agrawal <[email protected]> * Update sdk/src/main/java/io/opentelemetry/sdk/metrics/view/AggregationConfiguration.java Co-authored-by: Anuraag Agrawal <[email protected]> * Update sdk/src/main/java/io/opentelemetry/sdk/metrics/view/AggregationConfiguration.java Co-authored-by: Anuraag Agrawal <[email protected]> * Update sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/view/AggregationConfiguration.java Co-authored-by: Anuraag Agrawal <[email protected]> * Update sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/view/InstrumentSelector.java Co-authored-by: Anuraag Agrawal <[email protected]> * Update sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/view/InstrumentSelector.java Co-authored-by: Anuraag Agrawal <[email protected]> * Update sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/view/InstrumentSelector.java Co-authored-by: Anuraag Agrawal <[email protected]> * Update sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/view/InstrumentSelector.java Co-authored-by: Anuraag Agrawal <[email protected]> * Update sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/view/InstrumentSelector.java Co-authored-by: Anuraag Agrawal <[email protected]> * Update sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/view/InstrumentSelector.java Co-authored-by: Anuraag Agrawal <[email protected]> * fix formatting issues * Update sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/view/InstrumentSelector.java Co-authored-by: Anuraag Agrawal <[email protected]>
1 parent 5528fe8 commit c4791e9

File tree

14 files changed

+1026
-38
lines changed

14 files changed

+1026
-38
lines changed

sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/ActiveBatcher.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,9 @@ public void batch(Labels labelSet, Aggregator aggregator, boolean mappedAggregat
4141
public List<MetricData> completeCollectionCycle() {
4242
return batcher.completeCollectionCycle();
4343
}
44+
45+
@Override
46+
public boolean generatesDeltas() {
47+
return batcher.generatesDeltas();
48+
}
4449
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.sdk.metrics;
7+
8+
import io.opentelemetry.sdk.metrics.view.AggregationConfiguration;
9+
import io.opentelemetry.sdk.metrics.view.Aggregations;
10+
import io.opentelemetry.sdk.metrics.view.InstrumentSelector;
11+
import java.util.ArrayList;
12+
import java.util.List;
13+
import java.util.Map;
14+
import java.util.concurrent.ConcurrentHashMap;
15+
import java.util.regex.Pattern;
16+
17+
class AggregationChooser {
18+
private static final AggregationConfiguration CUMULATIVE_SUM =
19+
AggregationConfiguration.create(
20+
Aggregations.sum(), AggregationConfiguration.Temporality.CUMULATIVE);
21+
private static final AggregationConfiguration DELTA_SUMMARY =
22+
AggregationConfiguration.create(
23+
Aggregations.minMaxSumCount(), AggregationConfiguration.Temporality.DELTA);
24+
private static final AggregationConfiguration CUMULATIVE_LAST_VALUE =
25+
AggregationConfiguration.create(
26+
Aggregations.lastValue(), AggregationConfiguration.Temporality.CUMULATIVE);
27+
private static final AggregationConfiguration DELTA_LAST_VALUE =
28+
AggregationConfiguration.create(
29+
Aggregations.lastValue(), AggregationConfiguration.Temporality.DELTA);
30+
31+
private final Map<InstrumentSelector, AggregationConfiguration> configuration =
32+
new ConcurrentHashMap<>();
33+
34+
AggregationConfiguration chooseAggregation(InstrumentDescriptor descriptor) {
35+
List<Map.Entry<InstrumentSelector, AggregationConfiguration>> possibleMatches =
36+
new ArrayList<>();
37+
for (Map.Entry<InstrumentSelector, AggregationConfiguration> entry : configuration.entrySet()) {
38+
InstrumentSelector registeredSelector = entry.getKey();
39+
// if it matches everything, return it right away...
40+
if (matchesOnType(descriptor, registeredSelector)
41+
&& matchesOnName(descriptor, registeredSelector)) {
42+
return entry.getValue();
43+
}
44+
// otherwise throw it into a bucket of possible matches if it matches one of the criteria
45+
if (matchesOne(descriptor, registeredSelector)) {
46+
possibleMatches.add(entry);
47+
}
48+
}
49+
50+
if (possibleMatches.isEmpty()) {
51+
return getDefaultSpecification(descriptor);
52+
}
53+
54+
// If no exact matches found, pick the first one that matches something:
55+
return possibleMatches.get(0).getValue();
56+
}
57+
58+
private static boolean matchesOne(InstrumentDescriptor descriptor, InstrumentSelector selector) {
59+
if (selector.hasInstrumentNameRegex() && !matchesOnName(descriptor, selector)) {
60+
return false;
61+
}
62+
if (selector.hasInstrumentType() && !matchesOnType(descriptor, selector)) {
63+
return false;
64+
}
65+
return true;
66+
}
67+
68+
private static boolean matchesOnType(
69+
InstrumentDescriptor descriptor, InstrumentSelector selector) {
70+
if (selector.instrumentType() == null) {
71+
return false;
72+
}
73+
return selector.instrumentType().equals(descriptor.getType());
74+
}
75+
76+
private static boolean matchesOnName(
77+
InstrumentDescriptor descriptor, InstrumentSelector registeredSelector) {
78+
Pattern pattern = registeredSelector.instrumentNamePattern();
79+
if (pattern == null) {
80+
return false;
81+
}
82+
return pattern.matcher(descriptor.getName()).matches();
83+
}
84+
85+
private static AggregationConfiguration getDefaultSpecification(InstrumentDescriptor descriptor) {
86+
switch (descriptor.getType()) {
87+
case COUNTER:
88+
case UP_DOWN_COUNTER:
89+
return CUMULATIVE_SUM;
90+
case VALUE_RECORDER:
91+
return DELTA_SUMMARY;
92+
case VALUE_OBSERVER:
93+
return DELTA_LAST_VALUE;
94+
case SUM_OBSERVER:
95+
case UP_DOWN_SUM_OBSERVER:
96+
return CUMULATIVE_LAST_VALUE;
97+
}
98+
throw new IllegalArgumentException("Unknown descriptor type: " + descriptor.getType());
99+
}
100+
101+
void addView(InstrumentSelector selector, AggregationConfiguration specification) {
102+
configuration.put(selector, specification);
103+
}
104+
}

sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/Batcher.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,9 @@ interface Batcher {
5151
* @return the list of metrics batched in this Batcher.
5252
*/
5353
List<MetricData> completeCollectionCycle();
54+
55+
/**
56+
* Returns whether this batcher generate "delta" style metrics. The alternative is "cumulative".
57+
*/
58+
boolean generatesDeltas();
5459
}

sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/Batchers.java

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ public void batch(Labels labelSet, Aggregator aggregator, boolean mappedAggregat
8080
public List<MetricData> completeCollectionCycle() {
8181
return Collections.emptyList();
8282
}
83+
84+
@Override
85+
public boolean generatesDeltas() {
86+
return false;
87+
}
8388
}
8489

8590
private static final class AllLabels implements Batcher {
@@ -155,6 +160,75 @@ public final List<MetricData> completeCollectionCycle() {
155160
aggregation.getDescriptorType(descriptor.getType(), descriptor.getValueType()),
156161
points));
157162
}
163+
164+
@Override
165+
public boolean generatesDeltas() {
166+
return delta;
167+
}
168+
169+
@Override
170+
public boolean equals(Object o) {
171+
if (this == o) {
172+
return true;
173+
}
174+
if (o == null || getClass() != o.getClass()) {
175+
return false;
176+
}
177+
178+
AllLabels allLabels = (AllLabels) o;
179+
180+
if (startEpochNanos != allLabels.startEpochNanos) {
181+
return false;
182+
}
183+
if (delta != allLabels.delta) {
184+
return false;
185+
}
186+
if (descriptor != null
187+
? !descriptor.equals(allLabels.descriptor)
188+
: allLabels.descriptor != null) {
189+
return false;
190+
}
191+
if (aggregation != null
192+
? !aggregation.equals(allLabels.aggregation)
193+
: allLabels.aggregation != null) {
194+
return false;
195+
}
196+
if (resource != null ? !resource.equals(allLabels.resource) : allLabels.resource != null) {
197+
return false;
198+
}
199+
if (instrumentationLibraryInfo != null
200+
? !instrumentationLibraryInfo.equals(allLabels.instrumentationLibraryInfo)
201+
: allLabels.instrumentationLibraryInfo != null) {
202+
return false;
203+
}
204+
if (clock != null ? !clock.equals(allLabels.clock) : allLabels.clock != null) {
205+
return false;
206+
}
207+
if (aggregatorFactory != null
208+
? !aggregatorFactory.equals(allLabels.aggregatorFactory)
209+
: allLabels.aggregatorFactory != null) {
210+
return false;
211+
}
212+
return aggregatorMap != null
213+
? aggregatorMap.equals(allLabels.aggregatorMap)
214+
: allLabels.aggregatorMap == null;
215+
}
216+
217+
@Override
218+
public int hashCode() {
219+
int result = descriptor != null ? descriptor.hashCode() : 0;
220+
result = 31 * result + (aggregation != null ? aggregation.hashCode() : 0);
221+
result = 31 * result + (resource != null ? resource.hashCode() : 0);
222+
result =
223+
31 * result
224+
+ (instrumentationLibraryInfo != null ? instrumentationLibraryInfo.hashCode() : 0);
225+
result = 31 * result + (clock != null ? clock.hashCode() : 0);
226+
result = 31 * result + (aggregatorFactory != null ? aggregatorFactory.hashCode() : 0);
227+
result = 31 * result + (aggregatorMap != null ? aggregatorMap.hashCode() : 0);
228+
result = 31 * result + (int) (startEpochNanos ^ (startEpochNanos >>> 32));
229+
result = 31 * result + (delta ? 1 : 0);
230+
return result;
231+
}
158232
}
159233

160234
private Batchers() {}

sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/MeterSdkProvider.java

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import io.opentelemetry.sdk.internal.MillisClock;
1414
import io.opentelemetry.sdk.metrics.data.MetricData;
1515
import io.opentelemetry.sdk.metrics.export.MetricProducer;
16+
import io.opentelemetry.sdk.metrics.view.AggregationConfiguration;
17+
import io.opentelemetry.sdk.metrics.view.InstrumentSelector;
1618
import io.opentelemetry.sdk.resources.Resource;
1719
import java.util.ArrayList;
1820
import java.util.Collection;
@@ -35,11 +37,12 @@ public final class MeterSdkProvider implements MeterProvider {
3537
static final String DEFAULT_METER_NAME = "unknown";
3638
private final MeterSdkComponentRegistry registry;
3739
private final MetricProducer metricProducer;
40+
private final ViewRegistry viewRegistry = new ViewRegistry();
3841

3942
private MeterSdkProvider(Clock clock, Resource resource) {
4043
this.registry =
4144
new MeterSdkComponentRegistry(
42-
MeterProviderSharedState.create(clock, resource), new ViewRegistry());
45+
MeterProviderSharedState.create(clock, resource), viewRegistry);
4346
this.metricProducer = new MetricProducerSdk(this.registry);
4447
}
4548

@@ -144,6 +147,34 @@ public MeterSdk newComponent(InstrumentationLibraryInfo instrumentationLibraryIn
144147
}
145148
}
146149

150+
/**
151+
* Register a view with the given {@link InstrumentSelector}.
152+
*
153+
* <p>Example on how to register a view:
154+
*
155+
* <pre>{@code
156+
* // get a handle to the MeterSdkProvider
157+
* MeterSdkProvider meterProvider = OpenTelemetrySdk.getMeterProvider();
158+
*
159+
* // create a selector to select which instruments to customize:
160+
* InstrumentSelector instrumentSelector = InstrumentSelector.newBuilder()
161+
* .instrumentType(InstrumentType.COUNTER)
162+
* .build();
163+
*
164+
* // create a specification of how you want the metrics aggregated:
165+
* AggregationConfiguration viewSpecification =
166+
* AggregationConfiguration.create(Aggregations.minMaxSumCount(), Temporality.DELTA);
167+
*
168+
* //register the view with the MeterSdkProvider
169+
* meterProvider.registerView(instrumentSelector, viewSpecification);
170+
* }</pre>
171+
*
172+
* @see AggregationConfiguration
173+
*/
174+
public void registerView(InstrumentSelector selector, AggregationConfiguration specification) {
175+
viewRegistry.registerView(selector, specification);
176+
}
177+
147178
private static final class MetricProducerSdk implements MetricProducer {
148179
private final MeterSdkComponentRegistry registry;
149180

sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/ViewRegistry.java

Lines changed: 32 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,44 @@
66
package io.opentelemetry.sdk.metrics;
77

88
import io.opentelemetry.sdk.metrics.view.Aggregation;
9-
import io.opentelemetry.sdk.metrics.view.Aggregations;
9+
import io.opentelemetry.sdk.metrics.view.AggregationConfiguration;
10+
import io.opentelemetry.sdk.metrics.view.AggregationConfiguration.Temporality;
11+
import io.opentelemetry.sdk.metrics.view.InstrumentSelector;
1012

1113
// notes:
1214
// specify by pieces of the descriptor.
13-
// instrument type
14-
// instrument value type
15-
// instrument name (wildcards allowed?)
15+
// instrument type
16+
// instrument name (regex) √
17+
// instrument value type (?)
1618
// constant labels (?)
1719
// units (?)
1820

1921
// what you can choose:
20-
// aggregation
22+
// aggregation √
23+
// delta vs. cumulative √
2124
// all labels vs. a list of labels
22-
// delta vs. cumulative
2325

2426
/**
2527
* Central location for Views to be registered. Registration of a view should eventually be done via
2628
* the {@link io.opentelemetry.sdk.metrics.MeterSdkProvider}.
2729
*/
2830
class ViewRegistry {
2931

32+
private final AggregationChooser aggregationChooser;
33+
34+
ViewRegistry() {
35+
this(new AggregationChooser());
36+
}
37+
38+
// VisibleForTesting
39+
ViewRegistry(AggregationChooser aggregationChooser) {
40+
this.aggregationChooser = aggregationChooser;
41+
}
42+
43+
void registerView(InstrumentSelector selector, AggregationConfiguration specification) {
44+
aggregationChooser.addView(selector, specification);
45+
}
46+
3047
/**
3148
* Create a new {@link io.opentelemetry.sdk.metrics.Batcher} for use in metric recording
3249
* aggregation.
@@ -36,39 +53,17 @@ Batcher createBatcher(
3653
MeterSharedState meterSharedState,
3754
InstrumentDescriptor descriptor) {
3855

39-
Aggregation aggregation = getRegisteredAggregation(descriptor);
56+
AggregationConfiguration specification = aggregationChooser.chooseAggregation(descriptor);
4057

41-
// todo: don't just use the defaults!
42-
switch (descriptor.getType()) {
43-
case COUNTER:
44-
case UP_DOWN_COUNTER:
45-
case SUM_OBSERVER:
46-
case UP_DOWN_SUM_OBSERVER:
47-
return Batchers.getCumulativeAllLabels(
48-
descriptor, meterProviderSharedState, meterSharedState, aggregation);
49-
case VALUE_RECORDER:
50-
// TODO: Revisit the batcher used here for value observers,
51-
// currently this does not remove duplicate records in the same cycle.
52-
case VALUE_OBSERVER:
53-
return Batchers.getDeltaAllLabels(
54-
descriptor, meterProviderSharedState, meterSharedState, aggregation);
55-
}
56-
throw new IllegalArgumentException("Unknown descriptor type: " + descriptor.getType());
57-
}
58+
Aggregation aggregation = specification.aggregation();
5859

59-
private static Aggregation getRegisteredAggregation(InstrumentDescriptor descriptor) {
60-
// todo look up based on fields of the descriptor.
61-
switch (descriptor.getType()) {
62-
case COUNTER:
63-
case UP_DOWN_COUNTER:
64-
return Aggregations.sum();
65-
case VALUE_RECORDER:
66-
return Aggregations.minMaxSumCount();
67-
case VALUE_OBSERVER:
68-
case SUM_OBSERVER:
69-
case UP_DOWN_SUM_OBSERVER:
70-
return Aggregations.lastValue();
60+
if (Temporality.CUMULATIVE == specification.temporality()) {
61+
return Batchers.getCumulativeAllLabels(
62+
descriptor, meterProviderSharedState, meterSharedState, aggregation);
63+
} else if (Temporality.DELTA == specification.temporality()) {
64+
return Batchers.getDeltaAllLabels(
65+
descriptor, meterProviderSharedState, meterSharedState, aggregation);
7166
}
72-
throw new IllegalArgumentException("Unknown descriptor type: " + descriptor.getType());
67+
throw new IllegalStateException("unsupported Temporality: " + specification.temporality());
7368
}
7469
}

0 commit comments

Comments
 (0)