Skip to content

Commit 508d4df

Browse files
authored
Improve OtlpMetricsSender API (#5994)
Remove the possible inconsistency where the sender could be given an instance of OtlpConfig that differs from the one passed to OtlpMeterRegistry. It is not clear which would be used or why. Now it is clear that the config on the registry will be used and the same sender can be used with different registry instances, potentially configured with different addresses (OtlpConfig#url).
1 parent 2bc1a70 commit 508d4df

File tree

7 files changed

+50
-36
lines changed

7 files changed

+50
-36
lines changed

Diff for: docs/modules/ROOT/pages/implementations/otlp.adoc

+2-2
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ management:
4040
key1: value1
4141
----
4242

43-
1. `url` - The URL to which data is reported. Environment variables `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` and `OTEL_EXPORTER_OTLP_ENDPOINT` are also supported in the default implementation. If a value is not provided, it defaults to `http://localhost:4318/v1/metrics`
43+
1. `url` - The address to which metrics are published. Environment variables `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` and `OTEL_EXPORTER_OTLP_ENDPOINT` are also supported in the default implementation. If a value is not provided, it defaults to `http://localhost:4318/v1/metrics`
4444
2. `batchSize` - number of ``Meter``s to include in a single payload sent to the backend. The default is 10,000.
4545
3. `aggregationTemporality` - https://opentelemetry.io/docs/specs/otel/metrics/data-model/#temporality[Aggregation temporality, window=_blank] determines how the additive quantities are expressed, in relation to time. The environment variable `OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE` is supported by the default implementation. The supported values are `cumulative` or `delta`. Defaults to `cumulative`.
4646
4. `headers` - Additional headers to send with exported metrics. This can be used for authorization headers. By default, headers are loaded from the config. If that is not set, they can be taken from the environment variables `OTEL_EXPORTER_OTLP_HEADERS` and `OTEL_EXPORTER_OTLP_METRICS_HEADERS`. If a header is set in both the environmental variables, the header in the latter overrides the former.
@@ -78,7 +78,7 @@ You may use a different `HttpSender` implementation by creating and configuring
7878
include::{include-java}/metrics/OtlpMeterRegistryCustomizationTest.java[tags=customizeHttpSender, indent=0]
7979
-----
8080

81-
You can also provide a custom implementation of `OtlpMetricsSender` that does not use HTTP at all.
81+
You can also provide a custom implementation of `OtlpMetricsSender` that does not use `HttpSender` at all. `OtlpConfig#url` will be used as the address when the sender is called in the `OtlpMeterRegistry` `publish` method.
8282
For instance, if you made a gRPC implementation, you could configure it in the following way.
8383
Micrometer does not currently provide a gRPC implementation of `OtlpMetricsSender`.
8484

Diff for: docs/src/test/java/io/micrometer/docs/metrics/OtlpMeterRegistryCustomizationTest.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class OtlpMeterRegistryCustomizationTest {
3131
void customizeHttpSender() {
3232
// tag::customizeHttpSender[]
3333
OtlpConfig config = OtlpConfig.DEFAULT;
34-
OtlpHttpMetricsSender httpMetricsSender = new OtlpHttpMetricsSender(new OkHttpSender(), config);
34+
OtlpHttpMetricsSender httpMetricsSender = new OtlpHttpMetricsSender(new OkHttpSender());
3535
OtlpMeterRegistry meterRegistry = OtlpMeterRegistry.builder(config).metricsSender(httpMetricsSender).build();
3636
// end::customizeHttpSender[]
3737
}
@@ -49,7 +49,7 @@ void customizeOtlpSender() {
4949
private static class OtlpGrpcMetricsSender implements OtlpMetricsSender {
5050

5151
@Override
52-
public void send(byte[] metricsData, Map<String, String> headers) {
52+
public void send(String address, byte[] metricsData, Map<String, String> headers) {
5353
}
5454

5555
}

Diff for: implementations/micrometer-registry-otlp/src/main/java/io/micrometer/registry/otlp/MetricsSender.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,12 @@ interface MetricsSender {
2424
/**
2525
* Send encoded metrics data from a {@link io.micrometer.core.instrument.MeterRegistry
2626
* MeterRegistry}.
27+
* @param address where to send the metrics
2728
* @param metricsData encoded batch of metrics
2829
* @param headers metadata to send as headers with the metrics data
30+
* @throws Throwable when there is an exception in sending the metrics; the caller
31+
* should handle this in some way such as logging the exception
2932
*/
30-
void send(byte[] metricsData, Map<String, String> headers);
33+
void send(String address, byte[] metricsData, Map<String, String> headers) throws Throwable;
3134

3235
}

Diff for: implementations/micrometer-registry-otlp/src/main/java/io/micrometer/registry/otlp/OtlpHttpMetricsSender.java

+21-24
Original file line numberDiff line numberDiff line change
@@ -32,32 +32,31 @@ public class OtlpHttpMetricsSender implements OtlpMetricsSender {
3232

3333
private final HttpSender httpSender;
3434

35-
private final OtlpConfig config;
36-
3735
private final String userAgentHeader;
3836

39-
public OtlpHttpMetricsSender(HttpSender httpSender, OtlpConfig config) {
37+
public OtlpHttpMetricsSender(HttpSender httpSender) {
4038
this.httpSender = httpSender;
41-
this.config = config;
4239
this.userAgentHeader = getUserAgentHeader();
4340
}
4441

42+
/**
43+
* Send a batch of OTLP Protobuf format metrics to an OTLP HTTP receiver.
44+
* @param address address of the OTLP HTTP receiver to which metrics will be sent
45+
* @param metricsData OTLP protobuf encoded batch of metrics
46+
* @param headers metadata to send as headers with the metrics data
47+
* @throws Throwable when there is an exception in sending the metrics; the caller
48+
* should handle this in some way such as logging the exception
49+
*/
4550
@Override
46-
public void send(byte[] metricsData, Map<String, String> headers) {
47-
HttpSender.Request.Builder httpRequest = this.httpSender.post(config.url())
51+
public void send(String address, byte[] metricsData, Map<String, String> headers) throws Throwable {
52+
HttpSender.Request.Builder httpRequest = this.httpSender.post(address)
4853
.withHeader("User-Agent", userAgentHeader)
4954
.withContent("application/x-protobuf", metricsData);
5055
headers.forEach(httpRequest::withHeader);
51-
try {
52-
HttpSender.Response response = httpRequest.send();
53-
if (!response.isSuccessful()) {
54-
logger.warn(
55-
"Failed to publish metrics (context: {}). Server responded with HTTP status code {} and body {}",
56-
getConfigurationContext(), response.code(), response.body());
57-
}
58-
}
59-
catch (Throwable e) {
60-
logger.warn("Failed to publish metrics (context: {}) ", getConfigurationContext(), e);
56+
HttpSender.Response response = httpRequest.send();
57+
if (!response.isSuccessful()) {
58+
throw new OtlpHttpMetricsSendUnsuccessfulException(String
59+
.format("Server responded with HTTP status code %d and body %s", response.code(), response.body()));
6160
}
6261
}
6362

@@ -70,14 +69,12 @@ private String getUserAgentHeader() {
7069
return userAgent;
7170
}
7271

73-
/**
74-
* Get the configuration context.
75-
* @return A message containing enough information for the log reader to figure out
76-
* what configuration details may have contributed to the failure.
77-
*/
78-
private String getConfigurationContext() {
79-
// While other values may contribute to failures, these two are most common
80-
return "url=" + config.url() + ", resource-attributes=" + config.resourceAttributes();
72+
private static class OtlpHttpMetricsSendUnsuccessfulException extends RuntimeException {
73+
74+
public OtlpHttpMetricsSendUnsuccessfulException(String message) {
75+
super(message);
76+
}
77+
8178
}
8279

8380
}

Diff for: implementations/micrometer-registry-otlp/src/main/java/io/micrometer/registry/otlp/OtlpMeterRegistry.java

+15-4
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ public OtlpMeterRegistry(OtlpConfig config, Clock clock) {
111111
* @since 1.14.0
112112
*/
113113
public OtlpMeterRegistry(OtlpConfig config, Clock clock, ThreadFactory threadFactory) {
114-
this(config, clock, threadFactory, new OtlpHttpMetricsSender(new HttpUrlConnectionSender(), config));
114+
this(config, clock, threadFactory, new OtlpHttpMetricsSender(new HttpUrlConnectionSender()));
115115
}
116116

117117
private OtlpMeterRegistry(OtlpConfig config, Clock clock, ThreadFactory threadFactory,
@@ -181,14 +181,25 @@ protected void publish() {
181181
.build())
182182
.build();
183183

184-
metricsSender.send(request.toByteArray(), this.config.headers());
184+
metricsSender.send(config.url(), request.toByteArray(), config.headers());
185185
}
186186
catch (Throwable e) {
187-
logger.warn("Failed to publish metrics to OTLP receiver", e);
187+
logger.warn(String.format("Failed to publish metrics to OTLP receiver (context: %s)",
188+
getConfigurationContext()), e);
188189
}
189190
}
190191
}
191192

193+
/**
194+
* Get the configuration context.
195+
* @return A message containing enough information for the log reader to figure out
196+
* what configuration details may have contributed to the failure.
197+
*/
198+
private String getConfigurationContext() {
199+
// While other values may contribute to failures, these two are most common
200+
return "url=" + config.url() + ", resource-attributes=" + config.resourceAttributes();
201+
}
202+
192203
@Override
193204
protected <T> Gauge newGauge(Meter.Id id, @Nullable T obj, ToDoubleFunction<T> valueFunction) {
194205
return new DefaultGauge<>(id, obj, valueFunction);
@@ -500,7 +511,7 @@ public static class Builder {
500511

501512
private Builder(OtlpConfig otlpConfig) {
502513
this.otlpConfig = otlpConfig;
503-
this.metricsSender = new OtlpHttpMetricsSender(new HttpUrlConnectionSender(), otlpConfig);
514+
this.metricsSender = new OtlpHttpMetricsSender(new HttpUrlConnectionSender());
504515
}
505516

506517
/** Override the default clock. */

Diff for: implementations/micrometer-registry-otlp/src/main/java/io/micrometer/registry/otlp/OtlpMetricsSender.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,14 @@
2626
public interface OtlpMetricsSender extends MetricsSender {
2727

2828
/**
29-
* Send a batch of OTLP Protobuf format metrics to a receiver.
29+
* Send a batch of OTLP Protobuf format metrics to an OTLP receiver.
30+
* @param address address of the OTLP receiver to which metrics will be sent
3031
* @param metricsData OTLP protobuf encoded batch of metrics
3132
* @param headers metadata to send as headers with the metrics data
33+
* @throws Throwable when there is an exception in sending the metrics; the caller
34+
* should handle this in some way such as logging the exception
3235
*/
3336
@Override
34-
void send(byte[] metricsData, Map<String, String> headers);
37+
void send(String address, byte[] metricsData, Map<String, String> headers) throws Throwable;
3538

3639
}

Diff for: implementations/micrometer-registry-otlp/src/test/java/io/micrometer/registry/otlp/OtlpMeterRegistryTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ void setUp() {
6969
this.clock = new MockClock();
7070
OtlpConfig config = otlpConfig();
7171
this.mockHttpSender = mock(HttpSender.class);
72-
OtlpMetricsSender metricsSender = new OtlpHttpMetricsSender(mockHttpSender, config);
72+
OtlpMetricsSender metricsSender = new OtlpHttpMetricsSender(mockHttpSender);
7373
this.registry = OtlpMeterRegistry.builder(config).clock(clock).metricsSender(metricsSender).build();
7474
this.registryWithExponentialHistogram = new OtlpMeterRegistry(exponentialHistogramOtlpConfig(), clock);
7575
}

0 commit comments

Comments
 (0)