Skip to content

Commit 129a318

Browse files
authored
Upgrade to OpenTelemetry 1.32.0, add support for bucket boundaries metrics API (#3447)
1 parent 08bc842 commit 129a318

File tree

14 files changed

+272
-53
lines changed

14 files changed

+272
-53
lines changed

CHANGELOG.asciidoc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ Use subheadings with the "=====" level for adding notes for unreleased changes:
3838
* Added support for Spring 6.1 / Spring-Boot 3.2 - {pull}3440[#3440]
3939
* Add support for Apache HTTP client 5.x - {pull}3419[#3419]
4040
41+
[float]
42+
===== Potentially breaking changes
43+
* Added support for OpenTelemetry `1.32.0`. As a result, `custom_metrics_histogram_boundaries` will not
44+
work when you bring your own `MeterProvider` from an SDK with version `1.32.0` or newer. As a workaround,
45+
you should manually register a corresponding View in your `MeterProvider`. Note that this change will not
46+
affect you, if you are using the OpenTelemetry API only and not the SDK. - {pull}3447[#3447]
47+
4148
[[release-notes-1.x]]
4249
=== Java Agent version 1.x
4350

apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/MetricsConfiguration.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ public class MetricsConfiguration extends ConfigurationOptionProvider implements
4848
private final ConfigurationOption<List<Double>> customMetricsHistogramBoundaries = ConfigurationOption.builder(new ListValueConverter<>(DoubleValueConverter.INSTANCE), List.class)
4949
.key("custom_metrics_histogram_boundaries")
5050
.configurationCategory(METRICS_CATEGORY)
51-
.description("Defines the default bucket boundaries to use for OpenTelemetry histograms.")
51+
.description("Defines the default bucket boundaries to use for OpenTelemetry histograms.\n" +
52+
"\n" +
53+
"Note that for OpenTelemetry 1.32.0 or newer this setting will only work when using API only. " +
54+
"The default buckets will not be applied when bringing your own SDK.")
5255
.dynamic(false)
5356
.tags("added[1.37.0]", "experimental")
5457
.addValidator(new ConfigurationOption.Validator<List<Double>>() {

apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-embedded-metrics-sdk/src/main/java/co/elastic/apm/agent/embeddedotel/proxy/ProxyDoubleHistogramBuilder.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,21 @@
1818
*/
1919
package co.elastic.apm.agent.embeddedotel.proxy;
2020

21-
import io.opentelemetry.api.metrics.DoubleHistogram;
21+
import co.elastic.apm.agent.configuration.MetricsConfiguration;
22+
import co.elastic.apm.agent.tracer.GlobalTracer;
2223
import io.opentelemetry.api.metrics.DoubleHistogramBuilder;
23-
import io.opentelemetry.api.metrics.LongHistogramBuilder;
24+
25+
import java.util.List;
2426

2527
public class ProxyDoubleHistogramBuilder {
2628

2729
private final DoubleHistogramBuilder delegate;
2830

2931
public ProxyDoubleHistogramBuilder(DoubleHistogramBuilder delegate) {
3032
this.delegate = delegate;
33+
//apply default bucket boundaries
34+
List<Double> boundaries = GlobalTracer.get().getConfig(MetricsConfiguration.class).getCustomMetricsHistogramBoundaries();
35+
delegate.setExplicitBucketBoundariesAdvice(boundaries);
3136
}
3237

3338
public DoubleHistogramBuilder getDelegate() {
@@ -52,4 +57,8 @@ public ProxyDoubleHistogramBuilder setDescription(String arg0) {
5257
return this;
5358
}
5459

55-
}
60+
public ProxyDoubleHistogramBuilder setExplicitBucketBoundariesAdvice(List<Double> bucketBoundaries) {
61+
delegate.setExplicitBucketBoundariesAdvice(bucketBoundaries);
62+
return this;
63+
}
64+
}

apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-embedded-metrics-sdk/src/main/java/co/elastic/apm/agent/embeddedotel/proxy/ProxyLongHistogramBuilder.java

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,34 @@
1818
*/
1919
package co.elastic.apm.agent.embeddedotel.proxy;
2020

21-
import io.opentelemetry.api.metrics.LongHistogram;
21+
import co.elastic.apm.agent.configuration.MetricsConfiguration;
22+
import co.elastic.apm.agent.tracer.GlobalTracer;
2223
import io.opentelemetry.api.metrics.LongHistogramBuilder;
2324

25+
import java.util.ArrayList;
26+
import java.util.List;
27+
2428
public class ProxyLongHistogramBuilder {
2529

2630
private final LongHistogramBuilder delegate;
2731

2832
public ProxyLongHistogramBuilder(LongHistogramBuilder delegate) {
2933
this.delegate = delegate;
34+
//apply default bucket boundaries, they are guaranteed to be ordered
35+
List<Double> boundaries = GlobalTracer.get().getConfig(MetricsConfiguration.class).getCustomMetricsHistogramBoundaries();
36+
delegate.setExplicitBucketBoundariesAdvice(convertToLongBoundaries(boundaries));
37+
}
38+
39+
private List<Long> convertToLongBoundaries(List<Double> boundaries) {
40+
List<Long> result = new ArrayList<>();
41+
for(double val : boundaries) {
42+
long rounded = Math.round(val);
43+
//Do not add the same boundary twice
44+
if(rounded > 0 && (result.isEmpty() || result.get(result.size() - 1) != rounded)) {
45+
result.add(rounded);
46+
}
47+
}
48+
return result;
3049
}
3150

3251
public LongHistogramBuilder getDelegate() {
@@ -47,4 +66,8 @@ public ProxyLongHistogramBuilder setDescription(String arg0) {
4766
return this;
4867
}
4968

50-
}
69+
public ProxyLongHistogramBuilder setExplicitBucketBoundariesAdvice(List<Long> bucketBoundaries) {
70+
delegate.setExplicitBucketBoundariesAdvice(bucketBoundaries);
71+
return this;
72+
}
73+
}

apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-metrics-bridge-parent/apm-opentelemetry-metrics-bridge-latest/src/main/java/co/elastic/apm/agent/opentelemetry/metrics/bridge/BridgeFactoryLatest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,25 +21,31 @@
2121
import co.elastic.apm.agent.embeddedotel.proxy.ProxyBatchCallback;
2222
import co.elastic.apm.agent.embeddedotel.proxy.ProxyDoubleCounterBuilder;
2323
import co.elastic.apm.agent.embeddedotel.proxy.ProxyDoubleGaugeBuilder;
24+
import co.elastic.apm.agent.embeddedotel.proxy.ProxyDoubleHistogramBuilder;
2425
import co.elastic.apm.agent.embeddedotel.proxy.ProxyDoubleUpDownCounterBuilder;
2526
import co.elastic.apm.agent.embeddedotel.proxy.ProxyLongCounterBuilder;
2627
import co.elastic.apm.agent.embeddedotel.proxy.ProxyLongGaugeBuilder;
28+
import co.elastic.apm.agent.embeddedotel.proxy.ProxyLongHistogramBuilder;
2729
import co.elastic.apm.agent.embeddedotel.proxy.ProxyLongUpDownCounterBuilder;
2830
import co.elastic.apm.agent.embeddedotel.proxy.ProxyMeter;
2931
import co.elastic.apm.agent.opentelemetry.metrics.bridge.latest.BridgeBatchCallback;
3032
import co.elastic.apm.agent.opentelemetry.metrics.bridge.latest.BridgeDoubleCounterBuilder;
3133
import co.elastic.apm.agent.opentelemetry.metrics.bridge.latest.BridgeDoubleGaugeBuilder;
34+
import co.elastic.apm.agent.opentelemetry.metrics.bridge.latest.BridgeDoubleHistogramBuilder;
3235
import co.elastic.apm.agent.opentelemetry.metrics.bridge.latest.BridgeDoubleUpDownCounterBuilder;
3336
import co.elastic.apm.agent.opentelemetry.metrics.bridge.latest.BridgeLongCounterBuilder;
3437
import co.elastic.apm.agent.opentelemetry.metrics.bridge.latest.BridgeLongGaugeBuilder;
38+
import co.elastic.apm.agent.opentelemetry.metrics.bridge.latest.BridgeLongHistogramBuilder;
3539
import co.elastic.apm.agent.opentelemetry.metrics.bridge.latest.BridgeLongUpDownCounterBuilder;
3640
import co.elastic.apm.agent.opentelemetry.metrics.bridge.latest.BridgeMeter;
3741
import io.opentelemetry.api.metrics.BatchCallback;
3842
import io.opentelemetry.api.metrics.DoubleCounterBuilder;
3943
import io.opentelemetry.api.metrics.DoubleGaugeBuilder;
44+
import io.opentelemetry.api.metrics.DoubleHistogramBuilder;
4045
import io.opentelemetry.api.metrics.DoubleUpDownCounterBuilder;
4146
import io.opentelemetry.api.metrics.LongCounterBuilder;
4247
import io.opentelemetry.api.metrics.LongGaugeBuilder;
48+
import io.opentelemetry.api.metrics.LongHistogramBuilder;
4349
import io.opentelemetry.api.metrics.LongUpDownCounterBuilder;
4450
import io.opentelemetry.api.metrics.Meter;
4551

@@ -99,4 +105,14 @@ public DoubleGaugeBuilder bridgeDoubleGaugeBuilder(ProxyDoubleGaugeBuilder deleg
99105
public BatchCallback bridgeBatchCallback(ProxyBatchCallback delegate) {
100106
return new BridgeBatchCallback(delegate);
101107
}
108+
109+
@Override
110+
public DoubleHistogramBuilder bridgeDoubleHistogramBuilder(ProxyDoubleHistogramBuilder delegate) {
111+
return new BridgeDoubleHistogramBuilder(delegate);
112+
}
113+
114+
@Override
115+
public LongHistogramBuilder bridgeLongHistogramBuilder(ProxyLongHistogramBuilder delegate) {
116+
return new BridgeLongHistogramBuilder(delegate);
117+
}
102118
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package co.elastic.apm.agent.opentelemetry.metrics.bridge.latest;
20+
21+
import co.elastic.apm.agent.embeddedotel.proxy.ProxyDoubleHistogramBuilder;
22+
import io.opentelemetry.api.metrics.DoubleHistogramBuilder;
23+
24+
import java.util.List;
25+
26+
public class BridgeDoubleHistogramBuilder extends co.elastic.apm.agent.opentelemetry.metrics.bridge.v1_14.BridgeDoubleHistogramBuilder {
27+
public BridgeDoubleHistogramBuilder(ProxyDoubleHistogramBuilder delegate) {
28+
super(delegate);
29+
}
30+
31+
/**
32+
* This method was added in 1.32.0, but is safe to have even if the provided API is older.
33+
* This is safe because it doesn't reference any newly added API types.
34+
*/
35+
@Override
36+
public DoubleHistogramBuilder setExplicitBucketBoundariesAdvice(List<Double> bucketBoundaries) {
37+
delegate.setExplicitBucketBoundariesAdvice(bucketBoundaries);
38+
return this;
39+
}
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package co.elastic.apm.agent.opentelemetry.metrics.bridge.latest;
20+
21+
import co.elastic.apm.agent.embeddedotel.proxy.ProxyDoubleHistogramBuilder;
22+
import co.elastic.apm.agent.embeddedotel.proxy.ProxyLongHistogramBuilder;
23+
import io.opentelemetry.api.metrics.DoubleHistogramBuilder;
24+
import io.opentelemetry.api.metrics.LongHistogramBuilder;
25+
26+
import java.util.List;
27+
28+
public class BridgeLongHistogramBuilder extends co.elastic.apm.agent.opentelemetry.metrics.bridge.v1_14.BridgeLongHistogramBuilder {
29+
public BridgeLongHistogramBuilder(ProxyLongHistogramBuilder delegate) {
30+
super(delegate);
31+
}
32+
33+
/**
34+
* This method was added in 1.32.0, but is safe to have even if the provided API is older.
35+
* This is safe because it doesn't reference any newly added API types.
36+
*/
37+
@Override
38+
public LongHistogramBuilder setExplicitBucketBoundariesAdvice(List<Long> bucketBoundaries) {
39+
delegate.setExplicitBucketBoundariesAdvice(bucketBoundaries);
40+
return this;
41+
}
42+
}

apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-metricsdk-plugin/src/main/java/co/elastic/apm/agent/otelmetricsdk/ElasticOtelMetricsExporter.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import co.elastic.apm.agent.sdk.internal.util.ExecutorUtils;
2626
import co.elastic.apm.agent.tracer.configuration.MetricsConfiguration;
2727
import co.elastic.apm.agent.tracer.configuration.ReporterConfiguration;
28+
import io.opentelemetry.api.metrics.DoubleHistogramBuilder;
2829
import io.opentelemetry.sdk.common.CompletableResultCode;
2930
import io.opentelemetry.sdk.metrics.Aggregation;
3031
import io.opentelemetry.sdk.metrics.InstrumentType;
@@ -37,13 +38,16 @@
3738

3839
import java.time.Duration;
3940
import java.util.Collection;
41+
import java.util.List;
4042

4143
public class ElasticOtelMetricsExporter implements MetricExporter {
4244

4345
private static final Logger logger = LoggerFactory.getLogger(ElasticOtelMetricsExporter.class);
4446

4547
private static final AggregationTemporalitySelector TEMPORALITY_SELECTOR = AggregationTemporalitySelector.deltaPreferred();
4648

49+
private static final boolean API_SUPPORTS_BUCKET_ADVICE = checkOtelApiSupportsHistogramBucketAdvice();
50+
4751
private final Aggregation defaultHistogramAggregation;
4852

4953
private final OtelMetricSerializer serializer;
@@ -103,10 +107,22 @@ public AggregationTemporality getAggregationTemporality(InstrumentType instrumen
103107

104108
@Override
105109
public Aggregation getDefaultAggregation(InstrumentType instrumentType) {
106-
if (instrumentType == InstrumentType.HISTOGRAM) {
110+
// Unfortunately advices are not applied when a non-default aggregation is returned here
111+
// When instrumenting API-usages, we now apply the default histogram boundaries via the
112+
// ProxyLongHistogramBuilder and ProxyDoubleHistogramBuilder
113+
if (instrumentType == InstrumentType.HISTOGRAM && !API_SUPPORTS_BUCKET_ADVICE) {
107114
return defaultHistogramAggregation;
108115
} else {
109116
return Aggregation.defaultAggregation();
110117
}
111118
}
119+
120+
private static boolean checkOtelApiSupportsHistogramBucketAdvice() {
121+
try {
122+
DoubleHistogramBuilder.class.getMethod("setExplicitBucketBoundariesAdvice", List.class);
123+
return true;
124+
} catch (NoSuchMethodException | SecurityException e) {
125+
return false;
126+
}
127+
}
112128
}

0 commit comments

Comments
 (0)