From 7c8dcf2cdf61500ed1a221a40970196b35ab480e Mon Sep 17 00:00:00 2001 From: Gagan Juneja Date: Wed, 12 Jul 2023 19:20:46 +0530 Subject: [PATCH] Telemetry exporter config (#8624) * Makes exporter configurable Signed-off-by: Gagan Juneja * Makes exporter configurable Signed-off-by: Gagan Juneja * Makes exporter configurable Signed-off-by: Gagan Juneja * Makes exporter configurable Signed-off-by: Gagan Juneja * Makes exporter configurable Signed-off-by: Gagan Juneja * Add logs for successful instantiation Signed-off-by: Gagan Juneja * Move settings to OtelTelemetrySettings Signed-off-by: Gagan Juneja * Move settings to OtelTelemetrySettings Signed-off-by: Gagan Juneja * Move settings to OtelTelemetrySettings Signed-off-by: Gagan Juneja * Add test cases Signed-off-by: Gagan Juneja * Fix test cases Signed-off-by: Gagan Juneja * Empty-Commit Signed-off-by: Gagan Juneja * Empty-Commit Signed-off-by: Gagan Juneja * Fix test cases Signed-off-by: Gagan Juneja * refactor Signed-off-by: Gagan Juneja * refactor Signed-off-by: Gagan Juneja * refactor Signed-off-by: Gagan Juneja --------- Signed-off-by: Gagan Juneja Signed-off-by: Gagan Juneja Co-authored-by: Gagan Juneja Signed-off-by: sahil buddharaju --- CHANGELOG.md | 1 + .../telemetry/OTelTelemetryPlugin.java | 38 ++------- .../telemetry/OTelTelemetrySettings.java | 84 +++++++++++++++++++ .../tracing/OTelResourceProvider.java | 10 +-- .../exporter/OTelSpanExporterFactory.java | 79 +++++++++++++++++ .../tracing/exporter/package-info.java | 12 +++ .../plugin-metadata/plugin-security.policy | 2 + .../telemetry/OTelTelemetryPluginTests.java | 40 ++++++--- .../tracing/exporter/DummySpanExporter.java | 31 +++++++ .../OTelSpanExporterFactoryTests.java | 65 ++++++++++++++ 10 files changed, 315 insertions(+), 47 deletions(-) create mode 100644 plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/OTelTelemetrySettings.java create mode 100644 plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/exporter/OTelSpanExporterFactory.java create mode 100644 plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/exporter/package-info.java create mode 100644 plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/exporter/DummySpanExporter.java create mode 100644 plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/exporter/OTelSpanExporterFactoryTests.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 686ce86463fbf..ef26caadc48bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Pass localNode info to all plugins on node start ([#7919](https://github.com/opensearch-project/OpenSearch/pull/7919)) - Improved performance of parsing floating point numbers ([#7909](https://github.com/opensearch-project/OpenSearch/pull/7909)) - Move span actions to Scope ([#8411](https://github.com/opensearch-project/OpenSearch/pull/8411)) +- Make Span exporter configurable ([#8620](https://github.com/opensearch-project/OpenSearch/issues/8620)) - Add wrapper tracer implementation ### Deprecated diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/OTelTelemetryPlugin.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/OTelTelemetryPlugin.java index 1c38c9dc8d6be..a1ca3adf4d2a2 100644 --- a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/OTelTelemetryPlugin.java +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/OTelTelemetryPlugin.java @@ -10,7 +10,6 @@ import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.TimeValue; import org.opensearch.plugins.Plugin; import org.opensearch.plugins.TelemetryPlugin; import org.opensearch.telemetry.metrics.MetricsTelemetry; @@ -29,36 +28,6 @@ public class OTelTelemetryPlugin extends Plugin implements TelemetryPlugin { static final String OTEL_TRACER_NAME = "otel"; - /** - * span exporter batch size - */ - public static final Setting TRACER_EXPORTER_BATCH_SIZE_SETTING = Setting.intSetting( - "telemetry.otel.tracer.exporter.batch_size", - 512, - 1, - Setting.Property.NodeScope, - Setting.Property.Dynamic - ); - /** - * span exporter max queue size - */ - public static final Setting TRACER_EXPORTER_MAX_QUEUE_SIZE_SETTING = Setting.intSetting( - "telemetry.otel.tracer.exporter.max_queue_size", - 2048, - 1, - Setting.Property.NodeScope, - Setting.Property.Dynamic - ); - /** - * span exporter delay in seconds - */ - public static final Setting TRACER_EXPORTER_DELAY_SETTING = Setting.timeSetting( - "telemetry.otel.tracer.exporter.delay", - TimeValue.timeValueSeconds(2), - Setting.Property.NodeScope, - Setting.Property.Dynamic - ); - private final Settings settings; /** @@ -71,7 +40,12 @@ public OTelTelemetryPlugin(Settings settings) { @Override public List> getSettings() { - return Arrays.asList(TRACER_EXPORTER_BATCH_SIZE_SETTING, TRACER_EXPORTER_DELAY_SETTING, TRACER_EXPORTER_MAX_QUEUE_SIZE_SETTING); + return Arrays.asList( + OTelTelemetrySettings.TRACER_EXPORTER_BATCH_SIZE_SETTING, + OTelTelemetrySettings.TRACER_EXPORTER_DELAY_SETTING, + OTelTelemetrySettings.TRACER_EXPORTER_MAX_QUEUE_SIZE_SETTING, + OTelTelemetrySettings.OTEL_TRACER_SPAN_EXPORTER_CLASS_SETTING + ); } @Override diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/OTelTelemetrySettings.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/OTelTelemetrySettings.java new file mode 100644 index 0000000000000..2df13e2cd5612 --- /dev/null +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/OTelTelemetrySettings.java @@ -0,0 +1,84 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry; + +import io.opentelemetry.exporter.logging.LoggingSpanExporter; +import io.opentelemetry.sdk.trace.export.SpanExporter; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import org.opensearch.SpecialPermission; +import org.opensearch.common.settings.Setting; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.telemetry.tracing.exporter.OTelSpanExporterFactory; + +/** + * OTel specific telemetry settings. + */ +public final class OTelTelemetrySettings { + + /** + * Base Constructor. + */ + private OTelTelemetrySettings() {} + + /** + * span exporter batch size + */ + public static final Setting TRACER_EXPORTER_BATCH_SIZE_SETTING = Setting.intSetting( + "telemetry.otel.tracer.exporter.batch_size", + 512, + 1, + Setting.Property.NodeScope, + Setting.Property.Final + ); + /** + * span exporter max queue size + */ + public static final Setting TRACER_EXPORTER_MAX_QUEUE_SIZE_SETTING = Setting.intSetting( + "telemetry.otel.tracer.exporter.max_queue_size", + 2048, + 1, + Setting.Property.NodeScope, + Setting.Property.Final + ); + /** + * span exporter delay in seconds + */ + public static final Setting TRACER_EXPORTER_DELAY_SETTING = Setting.timeSetting( + "telemetry.otel.tracer.exporter.delay", + TimeValue.timeValueSeconds(2), + Setting.Property.NodeScope, + Setting.Property.Final + ); + + /** + * Span Exporter type setting. + */ + @SuppressWarnings("unchecked") + public static final Setting> OTEL_TRACER_SPAN_EXPORTER_CLASS_SETTING = new Setting<>( + "telemetry.otel.tracer.span.exporter.class", + LoggingSpanExporter.class.getName(), + className -> { + // Check we ourselves are not being called by unprivileged code. + SpecialPermission.check(); + + try { + return AccessController.doPrivileged((PrivilegedExceptionAction>) () -> { + final ClassLoader loader = OTelSpanExporterFactory.class.getClassLoader(); + return (Class) loader.loadClass(className); + }); + } catch (PrivilegedActionException ex) { + throw new IllegalStateException("Unable to load span exporter class:" + className, ex.getCause()); + } + }, + Setting.Property.NodeScope, + Setting.Property.Final + ); +} diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelResourceProvider.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelResourceProvider.java index 292165979c2f2..4d3605ae03993 100644 --- a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelResourceProvider.java +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelResourceProvider.java @@ -12,7 +12,6 @@ import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; import io.opentelemetry.context.propagation.ContextPropagators; -import io.opentelemetry.exporter.logging.LoggingSpanExporter; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.SdkTracerProvider; @@ -23,10 +22,11 @@ import org.opensearch.common.settings.Settings; import java.util.concurrent.TimeUnit; +import org.opensearch.telemetry.tracing.exporter.OTelSpanExporterFactory; -import static org.opensearch.telemetry.OTelTelemetryPlugin.TRACER_EXPORTER_BATCH_SIZE_SETTING; -import static org.opensearch.telemetry.OTelTelemetryPlugin.TRACER_EXPORTER_DELAY_SETTING; -import static org.opensearch.telemetry.OTelTelemetryPlugin.TRACER_EXPORTER_MAX_QUEUE_SIZE_SETTING; +import static org.opensearch.telemetry.OTelTelemetrySettings.TRACER_EXPORTER_BATCH_SIZE_SETTING; +import static org.opensearch.telemetry.OTelTelemetrySettings.TRACER_EXPORTER_DELAY_SETTING; +import static org.opensearch.telemetry.OTelTelemetrySettings.TRACER_EXPORTER_MAX_QUEUE_SIZE_SETTING; /** * This class encapsulates all OpenTelemetry related resources @@ -42,7 +42,7 @@ private OTelResourceProvider() {} public static OpenTelemetry get(Settings settings) { return get( settings, - LoggingSpanExporter.create(), + OTelSpanExporterFactory.create(settings), ContextPropagators.create(W3CTraceContextPropagator.getInstance()), Sampler.alwaysOn() ); diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/exporter/OTelSpanExporterFactory.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/exporter/OTelSpanExporterFactory.java new file mode 100644 index 0000000000000..c73de4370465f --- /dev/null +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/exporter/OTelSpanExporterFactory.java @@ -0,0 +1,79 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing.exporter; + +import io.opentelemetry.sdk.trace.export.SpanExporter; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.SpecialPermission; +import org.opensearch.common.settings.Settings; +import org.opensearch.telemetry.OTelTelemetrySettings; + +/** + * Factory class to create the {@link SpanExporter} instance. + */ +public class OTelSpanExporterFactory { + + private static final Logger logger = LogManager.getLogger(OTelSpanExporterFactory.class); + + /** + * Base constructor. + */ + private OTelSpanExporterFactory() { + + } + + /** + * Creates the {@link SpanExporter} instances based on the OTEL_TRACER_SPAN_EXPORTER_CLASS_SETTING value. + * As of now, it expects the SpanExporter implementations to have a create factory method to instantiate the + * SpanExporter. + * @param settings settings. + * @return SpanExporter instance. + */ + public static SpanExporter create(Settings settings) { + Class spanExporterProviderClass = OTelTelemetrySettings.OTEL_TRACER_SPAN_EXPORTER_CLASS_SETTING.get(settings); + SpanExporter spanExporter = instantiateSpanExporter(spanExporterProviderClass); + logger.info("Successfully instantiated the SpanExporter class {}", spanExporterProviderClass); + return spanExporter; + } + + private static SpanExporter instantiateSpanExporter(Class spanExporterProviderClass) { + try { + // Check we ourselves are not being called by unprivileged code. + SpecialPermission.check(); + return AccessController.doPrivileged((PrivilegedExceptionAction) () -> { + try { + return (SpanExporter) MethodHandles.publicLookup() + .findStatic(spanExporterProviderClass, "create", MethodType.methodType(spanExporterProviderClass)) + .asType(MethodType.methodType(SpanExporter.class)) + .invokeExact(); + } catch (Throwable e) { + if (e.getCause() instanceof NoSuchMethodException) { + throw new IllegalStateException("No create factory method exist in [" + spanExporterProviderClass.getName() + "]"); + } else { + throw new IllegalStateException( + "SpanExporter instantiation failed for class [" + spanExporterProviderClass.getName() + "]", + e.getCause() + ); + } + } + }); + } catch (PrivilegedActionException ex) { + throw new IllegalStateException( + "SpanExporter instantiation failed for class [" + spanExporterProviderClass.getName() + "]", + ex.getCause() + ); + } + } +} diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/exporter/package-info.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/exporter/package-info.java new file mode 100644 index 0000000000000..a5fffadabd75f --- /dev/null +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/exporter/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * This package contains classes needed for telemetry. + */ +package org.opensearch.telemetry.tracing.exporter; diff --git a/plugins/telemetry-otel/src/main/plugin-metadata/plugin-security.policy b/plugins/telemetry-otel/src/main/plugin-metadata/plugin-security.policy index 0f556121915bb..4480cbb2bab4b 100644 --- a/plugins/telemetry-otel/src/main/plugin-metadata/plugin-security.policy +++ b/plugins/telemetry-otel/src/main/plugin-metadata/plugin-security.policy @@ -7,6 +7,8 @@ */ grant { + permission java.lang.RuntimePermission "getClassLoader"; + permission java.lang.RuntimePermission "accessDeclaredMembers"; }; diff --git a/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/OTelTelemetryPluginTests.java b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/OTelTelemetryPluginTests.java index c6ffba04ac285..d57ae554a462d 100644 --- a/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/OTelTelemetryPluginTests.java +++ b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/OTelTelemetryPluginTests.java @@ -8,6 +8,8 @@ package org.opensearch.telemetry; +import org.junit.After; +import org.junit.Before; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; @@ -23,28 +25,46 @@ import java.util.Optional; import static org.opensearch.telemetry.OTelTelemetryPlugin.OTEL_TRACER_NAME; -import static org.opensearch.telemetry.OTelTelemetryPlugin.TRACER_EXPORTER_BATCH_SIZE_SETTING; -import static org.opensearch.telemetry.OTelTelemetryPlugin.TRACER_EXPORTER_DELAY_SETTING; -import static org.opensearch.telemetry.OTelTelemetryPlugin.TRACER_EXPORTER_MAX_QUEUE_SIZE_SETTING; +import static org.opensearch.telemetry.OTelTelemetrySettings.TRACER_EXPORTER_BATCH_SIZE_SETTING; +import static org.opensearch.telemetry.OTelTelemetrySettings.TRACER_EXPORTER_DELAY_SETTING; +import static org.opensearch.telemetry.OTelTelemetrySettings.TRACER_EXPORTER_MAX_QUEUE_SIZE_SETTING; +import static org.opensearch.telemetry.OTelTelemetrySettings.OTEL_TRACER_SPAN_EXPORTER_CLASS_SETTING; public class OTelTelemetryPluginTests extends OpenSearchTestCase { + private OTelTelemetryPlugin oTelTracerModulePlugin; + private Optional telemetry; + private TracingTelemetry tracingTelemetry; + + @Before + public void setup() { + // TRACER_EXPORTER_DELAY_SETTING should always be less than 10 seconds because + // io.opentelemetry.sdk.OpenTelemetrySdk.close waits only for 10 seconds for shutdown to complete. + Settings settings = Settings.builder().put(TRACER_EXPORTER_DELAY_SETTING.getKey(), "1s").build(); + oTelTracerModulePlugin = new OTelTelemetryPlugin(settings); + telemetry = oTelTracerModulePlugin.getTelemetry(null); + tracingTelemetry = telemetry.get().getTracingTelemetry(); + } + public void testGetTelemetry() { Set> allTracerSettings = new HashSet<>(); ClusterSettings.FEATURE_FLAGGED_CLUSTER_SETTINGS.get(List.of(FeatureFlags.TELEMETRY)).stream().forEach((allTracerSettings::add)); - Settings settings = Settings.builder().build(); - OTelTelemetryPlugin oTelTracerModulePlugin = new OTelTelemetryPlugin(settings); - Optional tracer = oTelTracerModulePlugin.getTelemetry(null); - assertEquals(OTEL_TRACER_NAME, oTelTracerModulePlugin.getName()); - TracingTelemetry tracingTelemetry = tracer.get().getTracingTelemetry(); assertTrue(tracingTelemetry instanceof OTelTracingTelemetry); assertEquals( - Arrays.asList(TRACER_EXPORTER_BATCH_SIZE_SETTING, TRACER_EXPORTER_DELAY_SETTING, TRACER_EXPORTER_MAX_QUEUE_SIZE_SETTING), + Arrays.asList( + TRACER_EXPORTER_BATCH_SIZE_SETTING, + TRACER_EXPORTER_DELAY_SETTING, + TRACER_EXPORTER_MAX_QUEUE_SIZE_SETTING, + OTEL_TRACER_SPAN_EXPORTER_CLASS_SETTING + ), oTelTracerModulePlugin.getSettings() ); - tracingTelemetry.close(); } + @After + public void cleanup() { + tracingTelemetry.close(); + } } diff --git a/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/exporter/DummySpanExporter.java b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/exporter/DummySpanExporter.java new file mode 100644 index 0000000000000..3f250b5aa481f --- /dev/null +++ b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/exporter/DummySpanExporter.java @@ -0,0 +1,31 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing.exporter; + +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.export.SpanExporter; +import java.util.Collection; + +public class DummySpanExporter implements SpanExporter { + @Override + public CompletableResultCode export(Collection spans) { + return null; + } + + @Override + public CompletableResultCode flush() { + return null; + } + + @Override + public CompletableResultCode shutdown() { + return null; + } +} diff --git a/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/exporter/OTelSpanExporterFactoryTests.java b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/exporter/OTelSpanExporterFactoryTests.java new file mode 100644 index 0000000000000..80cba425ed163 --- /dev/null +++ b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/exporter/OTelSpanExporterFactoryTests.java @@ -0,0 +1,65 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing.exporter; + +import io.opentelemetry.exporter.logging.LoggingSpanExporter; +import io.opentelemetry.sdk.trace.export.SpanExporter; +import org.opensearch.common.settings.Settings; +import org.opensearch.telemetry.OTelTelemetrySettings; +import org.opensearch.test.OpenSearchTestCase; + +public class OTelSpanExporterFactoryTests extends OpenSearchTestCase { + + public void testSpanExporterDefault() { + Settings settings = Settings.builder().build(); + SpanExporter spanExporter = OTelSpanExporterFactory.create(settings); + assertTrue(spanExporter instanceof LoggingSpanExporter); + } + + public void testSpanExporterLogging() { + Settings settings = Settings.builder() + .put( + OTelTelemetrySettings.OTEL_TRACER_SPAN_EXPORTER_CLASS_SETTING.getKey(), + "io.opentelemetry.exporter.logging.LoggingSpanExporter" + ) + .build(); + SpanExporter spanExporter = OTelSpanExporterFactory.create(settings); + assertTrue(spanExporter instanceof LoggingSpanExporter); + } + + public void testSpanExporterInvalid() { + Settings settings = Settings.builder().put(OTelTelemetrySettings.OTEL_TRACER_SPAN_EXPORTER_CLASS_SETTING.getKey(), "abc").build(); + assertThrows(IllegalArgumentException.class, () -> OTelSpanExporterFactory.create(settings)); + } + + public void testSpanExporterNoCreateFactoryMethod() { + Settings settings = Settings.builder() + .put( + OTelTelemetrySettings.OTEL_TRACER_SPAN_EXPORTER_CLASS_SETTING.getKey(), + "org.opensearch.telemetry.tracing.exporter.DummySpanExporter" + ) + .build(); + IllegalStateException exception = assertThrows(IllegalStateException.class, () -> OTelSpanExporterFactory.create(settings)); + assertEquals( + "SpanExporter instantiation failed for class [org.opensearch.telemetry.tracing.exporter.DummySpanExporter]", + exception.getMessage() + ); + } + + public void testSpanExporterNonSpanExporterClass() { + Settings settings = Settings.builder() + .put(OTelTelemetrySettings.OTEL_TRACER_SPAN_EXPORTER_CLASS_SETTING.getKey(), "java.lang.String") + .build(); + IllegalStateException exception = assertThrows(IllegalStateException.class, () -> OTelSpanExporterFactory.create(settings)); + assertEquals("SpanExporter instantiation failed for class [java.lang.String]", exception.getMessage()); + assertTrue(exception.getCause() instanceof NoSuchMethodError); + + } + +}