From 6bc2b0e91fff06b3c092f71cd02b5997751e85e6 Mon Sep 17 00:00:00 2001 From: Matteo Mazzola Date: Fri, 24 Jul 2026 16:44:14 +0100 Subject: [PATCH] Not throw if endpoint is not set correctly, log and noop --- .../otelsdk/OtelSdkExportMeterSupplier.java | 24 ++++++++------- .../otelsdk/OtelSdkExportTracerSupplier.java | 20 +++++++++---- .../OtelSdkExportMeterSupplierTests.java | 21 ++++--------- .../OtelSdkExportTracerSupplierTests.java | 30 ++++++++----------- 4 files changed, 47 insertions(+), 48 deletions(-) diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/export/otelsdk/OtelSdkExportMeterSupplier.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/export/otelsdk/OtelSdkExportMeterSupplier.java index 4c79931736f4d..8bf1f57378658 100644 --- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/export/otelsdk/OtelSdkExportMeterSupplier.java +++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/export/otelsdk/OtelSdkExportMeterSupplier.java @@ -27,6 +27,8 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.ssl.TrustEverythingConfig; import org.elasticsearch.core.Booleans; +import org.elasticsearch.logging.LogManager; +import org.elasticsearch.logging.Logger; import org.elasticsearch.telemetry.apm.internal.APMAgentSettings; import org.elasticsearch.telemetry.apm.internal.export.MeterSupplier; @@ -58,6 +60,8 @@ public class OtelSdkExportMeterSupplier implements MeterSupplier { // Per-instrument-stream cardinality limit private static final int METRIC_CARDINALITY_LIMIT = 1000; + private static final Logger logger = LogManager.getLogger(OtelSdkExportMeterSupplier.class); + private final Settings settings; private final Path diskBufferPath; private volatile OTelMetricsResources resources; @@ -77,12 +81,7 @@ public OtelSdkExportMeterSupplier(Settings settings, Path diskBufferPath) { @Override public Meter get() { - synchronized (mutex) { - if (resources == null) { - resources = createMeteringResources(); - } - return resources.meterProvider().get("elasticsearch"); - } + return getMeterProvider().get("elasticsearch"); } private OTelMetricsResources createMeteringResources() { @@ -147,11 +146,6 @@ private SdkMeterProvider sdkMeterProvider(PeriodicMetricReader reader) { private OtlpGrpcMetricExporter createOTLPExporter(Supplier meterProviderSupplier) { String endpoint = OtelSdkSettings.TELEMETRY_EXPORT_ENDPOINT.get(settings); - if (endpoint == null || endpoint.isEmpty()) { - throw new IllegalStateException( - OTEL_METRICS_ENABLED_SYSTEM_PROPERTY + "=true requires telemetry.export.endpoint to be configured" - ); - } OtlpGrpcMetricExporterBuilder builder = OtlpGrpcMetricExporter.builder() .setEndpoint(endpoint) .setMeterProvider(meterProviderSupplier) @@ -215,6 +209,14 @@ public CompletableResultCode attemptFlushMetrics() { public MeterProvider getMeterProvider() { synchronized (mutex) { if (resources == null) { + String endpoint = OtelSdkSettings.TELEMETRY_EXPORT_ENDPOINT.get(settings); + if (endpoint == null || endpoint.isEmpty()) { + logger.warn( + "{}=true but [telemetry.export.endpoint] is not configured; OTel SDK metrics export is disabled", + OTEL_METRICS_ENABLED_SYSTEM_PROPERTY + ); + return MeterProvider.noop(); + } resources = createMeteringResources(); } return resources.meterProvider(); diff --git a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/export/otelsdk/OtelSdkExportTracerSupplier.java b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/export/otelsdk/OtelSdkExportTracerSupplier.java index 3c3da875276e1..0b0e760c63d5e 100644 --- a/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/export/otelsdk/OtelSdkExportTracerSupplier.java +++ b/modules/apm/src/main/java/org/elasticsearch/telemetry/apm/internal/export/otelsdk/OtelSdkExportTracerSupplier.java @@ -24,6 +24,8 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.TimeValue; +import org.elasticsearch.logging.LogManager; +import org.elasticsearch.logging.Logger; import org.elasticsearch.telemetry.apm.internal.export.TraceSupplier; import java.util.concurrent.TimeUnit; @@ -37,15 +39,21 @@ */ public class OtelSdkExportTracerSupplier implements TraceSupplier { + private static final Logger logger = LogManager.getLogger(OtelSdkExportTracerSupplier.class); + private final SdkTracerProvider tracerProvider; private final OpenTelemetrySdk openTelemetrySdk; public OtelSdkExportTracerSupplier(Settings settings, Supplier meterProvider) { String endpoint = OtelSdkSettings.TELEMETRY_EXPORT_ENDPOINT.get(settings); if (endpoint == null || endpoint.isEmpty()) { - throw new IllegalStateException( - OTEL_TRACES_ENABLED_SYSTEM_PROPERTY + "=true requires telemetry.export.endpoint to be configured" + logger.warn( + "{}=true but [telemetry.export.endpoint] is not configured; OTel SDK trace export is disabled", + OTEL_TRACES_ENABLED_SYSTEM_PROPERTY ); + this.tracerProvider = null; + this.openTelemetrySdk = null; + return; } TimeValue interval = OtelSdkSettings.TELEMETRY_EXPORT_INTERVAL.get(settings); @@ -94,16 +102,18 @@ public OtelSdkExportTracerSupplier(Settings settings, Supplier me @Override public OpenTelemetry get() { - return openTelemetrySdk; + return openTelemetrySdk != null ? openTelemetrySdk : OpenTelemetry.noop(); } @Override public CompletableResultCode attemptFlushTraces() { - return tracerProvider.forceFlush(); + return tracerProvider == null ? CompletableResultCode.ofSuccess() : tracerProvider.forceFlush(); } @Override public void close() { - tracerProvider.close(); + if (tracerProvider != null) { + tracerProvider.close(); + } } } diff --git a/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/export/otelsdk/OtelSdkExportMeterSupplierTests.java b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/export/otelsdk/OtelSdkExportMeterSupplierTests.java index e1275b6edb080..4259540232996 100644 --- a/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/export/otelsdk/OtelSdkExportMeterSupplierTests.java +++ b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/export/otelsdk/OtelSdkExportMeterSupplierTests.java @@ -9,6 +9,7 @@ package org.elasticsearch.telemetry.apm.internal.export.otelsdk; +import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.common.InternalTelemetryVersion; import io.opentelemetry.sdk.metrics.SdkMeterProvider; @@ -25,27 +26,21 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.test.ESTestCase; -import static org.elasticsearch.telemetry.TelemetryProvider.OTEL_METRICS_ENABLED_SYSTEM_PROPERTY; -import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; @ThreadLeakFilters(filters = { OkHttpThreadsFilter.class }) public class OtelSdkExportMeterSupplierTests extends ESTestCase { - public void testGetWithoutEndpointThrows() { - IllegalStateException e = expectThrows( - IllegalStateException.class, - () -> new OtelSdkExportMeterSupplier(Settings.EMPTY, null).get() - ); - assertThat(e.getMessage(), containsString(OTEL_METRICS_ENABLED_SYSTEM_PROPERTY)); - assertThat(e.getMessage(), containsString("telemetry.export.endpoint")); + public void testMissingEndpointReturnsNoopInsteadOfThrowing() { + assertThat(new OtelSdkExportMeterSupplier(Settings.EMPTY, null).getMeterProvider(), is(MeterProvider.noop())); } - public void testGetWithEmptyEndpointThrows() { + public void testEmptyEndpointReturnsNoopInsteadOfThrowing() { Settings settings = Settings.builder().put(OtelSdkSettings.TELEMETRY_EXPORT_ENDPOINT.getKey(), "").build(); - expectThrows(IllegalStateException.class, () -> new OtelSdkExportMeterSupplier(settings, null).get()); + assertThat(new OtelSdkExportMeterSupplier(settings, null).getMeterProvider(), is(MeterProvider.noop())); } public void testBuildOtlpAuthorizationHeaderWithNeitherCredential() { @@ -74,10 +69,6 @@ public void testBuildOtlpAuthorizationHeaderWithApiKeyOnly() { assertThat(OtelSdkExportMeterSupplier.buildOtlpAuthorizationHeader(settings), equalTo("ApiKey xyz")); } - public void testGetMeterProviderWithoutEndpointThrows() { - expectThrows(IllegalStateException.class, () -> new OtelSdkExportMeterSupplier(Settings.EMPTY, null).getMeterProvider()); - } - public void testGetMeterProviderAfterGetReturnsSdkProvider() { String bogusUrl = "http://127.0.0.1:9"; Settings settings = Settings.builder().put(OtelSdkSettings.TELEMETRY_EXPORT_ENDPOINT.getKey(), bogusUrl).build(); diff --git a/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/export/otelsdk/OtelSdkExportTracerSupplierTests.java b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/export/otelsdk/OtelSdkExportTracerSupplierTests.java index 620ecee2c38d5..3f79df692b1a1 100644 --- a/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/export/otelsdk/OtelSdkExportTracerSupplierTests.java +++ b/modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/export/otelsdk/OtelSdkExportTracerSupplierTests.java @@ -9,6 +9,7 @@ package org.elasticsearch.telemetry.apm.internal.export.otelsdk; +import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.sdk.metrics.SdkMeterProvider; import io.opentelemetry.sdk.metrics.data.MetricData; @@ -19,30 +20,25 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.test.ESTestCase; -import static org.elasticsearch.telemetry.TelemetryProvider.OTEL_TRACES_ENABLED_SYSTEM_PROPERTY; -import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.is; @ThreadLeakFilters(filters = { OkHttpThreadsFilter.class }) public class OtelSdkExportTracerSupplierTests extends ESTestCase { - public void testConstructorWithoutEndpointThrows() { - IllegalStateException e = expectThrows( - IllegalStateException.class, - () -> new OtelSdkExportTracerSupplier(Settings.EMPTY, MeterProvider::noop) - ); - assertThat(e.getMessage(), containsString(OTEL_TRACES_ENABLED_SYSTEM_PROPERTY)); - assertThat(e.getMessage(), containsString("telemetry.export.endpoint")); + public void testMissingEndpointReturnsNoopInsteadOfThrowing() { + assertDegradesToNoop(Settings.EMPTY); } - public void testConstructorWithEmptyEndpointThrows() { - Settings settings = Settings.builder().put(OtelSdkSettings.TELEMETRY_EXPORT_ENDPOINT.getKey(), "").build(); - IllegalStateException e = expectThrows( - IllegalStateException.class, - () -> new OtelSdkExportTracerSupplier(settings, MeterProvider::noop) - ); - assertThat(e.getMessage(), containsString(OTEL_TRACES_ENABLED_SYSTEM_PROPERTY)); - assertThat(e.getMessage(), containsString("telemetry.export.endpoint")); + public void testEmptyEndpointReturnsNoopInsteadOfThrowing() { + assertDegradesToNoop(Settings.builder().put(OtelSdkSettings.TELEMETRY_EXPORT_ENDPOINT.getKey(), "").build()); + } + + private void assertDegradesToNoop(Settings settings) { + try (var supplier = new OtelSdkExportTracerSupplier(settings, MeterProvider::noop)) { + assertThat(supplier.get(), is(OpenTelemetry.noop())); + assertThat(supplier.attemptFlushTraces().isSuccess(), is(true)); + } } public void testConstructorWithNoopMeterProviderDoesNotThrow() {