From 7c820a3e3edd4ac8c772ca91c395629a0314e747 Mon Sep 17 00:00:00 2001 From: Matthew Li Date: Wed, 6 May 2026 14:50:27 -0700 Subject: [PATCH 1/3] init --- ...veImageGeneratorRunnerInstrumentation.java | 1 + .../trace/api/telemetry/OtelSpiCollector.java | 75 +++++++++++++++++++ .../api/telemetry/OtelSpiCollectorTest.java | 69 +++++++++++++++++ .../datadog/telemetry/TelemetrySystem.java | 2 + .../metric/OtelSpiMetricPeriodicAction.java | 13 ++++ 5 files changed, 160 insertions(+) create mode 100644 internal-api/src/main/java/datadog/trace/api/telemetry/OtelSpiCollector.java create mode 100644 internal-api/src/test/java/datadog/trace/api/telemetry/OtelSpiCollectorTest.java create mode 100644 telemetry/src/main/java/datadog/telemetry/metric/OtelSpiMetricPeriodicAction.java diff --git a/dd-java-agent/instrumentation/graal/graal-native-image-20.0/src/main/java/datadog/trace/instrumentation/graal/nativeimage/NativeImageGeneratorRunnerInstrumentation.java b/dd-java-agent/instrumentation/graal/graal-native-image-20.0/src/main/java/datadog/trace/instrumentation/graal/nativeimage/NativeImageGeneratorRunnerInstrumentation.java index cf13266a41e..405d3f35bc2 100644 --- a/dd-java-agent/instrumentation/graal/graal-native-image-20.0/src/main/java/datadog/trace/instrumentation/graal/nativeimage/NativeImageGeneratorRunnerInstrumentation.java +++ b/dd-java-agent/instrumentation/graal/graal-native-image-20.0/src/main/java/datadog/trace/instrumentation/graal/nativeimage/NativeImageGeneratorRunnerInstrumentation.java @@ -108,6 +108,7 @@ public static void onEnter(@Advice.Argument(value = 0, readOnly = false) String[ + "datadog.trace.api.telemetry.ConfigInversionMetricCollectorImpl$ConfigInversionMetric:build_time," + "datadog.trace.api.telemetry.NoOpConfigInversionMetricCollector:build_time," + "datadog.trace.api.telemetry.OtelEnvMetricCollectorImpl:build_time," + + "datadog.trace.api.telemetry.OtelSpiCollector:build_time," + "datadog.trace.api.profiling.ProfilingEnablement:build_time," + "datadog.trace.bootstrap.config.provider.ConfigConverter:build_time," + "datadog.trace.bootstrap.config.provider.ConfigConverter$ValueOfLookup:build_time," diff --git a/internal-api/src/main/java/datadog/trace/api/telemetry/OtelSpiCollector.java b/internal-api/src/main/java/datadog/trace/api/telemetry/OtelSpiCollector.java new file mode 100644 index 00000000000..375b17b6893 --- /dev/null +++ b/internal-api/src/main/java/datadog/trace/api/telemetry/OtelSpiCollector.java @@ -0,0 +1,75 @@ +package datadog.trace.api.telemetry; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** Collects telemetry about OpenTelemetry SPIs detected in the customer environment. */ +public class OtelSpiCollector implements MetricCollector { + private static final Logger log = LoggerFactory.getLogger(OtelSpiCollector.class); + private static final String OTEL_SPI_DETECTED_METRIC_NAME = "otel.spi.detected"; + private static final String SPI_CLASS_TAG = "spi_class:"; + private static final String SOURCE_TAG = "source:"; + private static final String NAMESPACE = "tracers"; + private static final OtelSpiCollector INSTANCE = new OtelSpiCollector(); + + private final BlockingQueue metricsQueue; + + private OtelSpiCollector() { + this.metricsQueue = new ArrayBlockingQueue<>(RAW_QUEUE_SIZE); + } + + public static OtelSpiCollector getInstance() { + return INSTANCE; + } + + public void recordSpiDetected(String spiFqn, String source) { + if (!metricsQueue.offer( + new OtelSpiMetric( + NAMESPACE, + true, + OTEL_SPI_DETECTED_METRIC_NAME, + "count", + 1, + SPI_CLASS_TAG + spiFqn, + SOURCE_TAG + source))) { + log.debug( + "Unable to add telemetry metric {} for spi_class={} source={}", + OTEL_SPI_DETECTED_METRIC_NAME, + spiFqn, + source); + } + } + + @Override + public void prepareMetrics() { + // Nothing to do here + } + + @Override + public Collection drain() { + if (this.metricsQueue.isEmpty()) { + return Collections.emptyList(); + } + List drained = new ArrayList<>(this.metricsQueue.size()); + this.metricsQueue.drainTo(drained); + return drained; + } + + public static class OtelSpiMetric extends MetricCollector.Metric { + public OtelSpiMetric( + String namespace, + boolean common, + String metricName, + String type, + Number value, + final String... tags) { + super(namespace, common, metricName, type, value, tags); + } + } +} diff --git a/internal-api/src/test/java/datadog/trace/api/telemetry/OtelSpiCollectorTest.java b/internal-api/src/test/java/datadog/trace/api/telemetry/OtelSpiCollectorTest.java new file mode 100644 index 00000000000..f1ddf2f2dea --- /dev/null +++ b/internal-api/src/test/java/datadog/trace/api/telemetry/OtelSpiCollectorTest.java @@ -0,0 +1,69 @@ +package datadog.trace.api.telemetry; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Iterator; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class OtelSpiCollectorTest { + + private final OtelSpiCollector collector = OtelSpiCollector.getInstance(); + + @BeforeEach + public void clearQueue() { + collector.drain(); + } + + @Test + public void singletonReturnsSameInstance() { + assertSame(OtelSpiCollector.getInstance(), OtelSpiCollector.getInstance()); + } + + @Test + public void recordedMetricSurfacesOnDrain() { + collector.recordSpiDetected( + "io.opentelemetry.sdk.autoconfigure.spi.ConfigurablePropagatorProvider", "extensions_path"); + + Iterator drained = collector.drain().iterator(); + assertTrue(drained.hasNext()); + OtelSpiCollector.OtelSpiMetric metric = drained.next(); + assertEquals("tracers", metric.namespace); + assertEquals("otel.spi.detected", metric.metricName); + assertEquals("count", metric.type); + assertTrue(metric.common); + assertEquals(1, metric.value); + assertTrue( + metric.tags.contains( + "spi_class:io.opentelemetry.sdk.autoconfigure.spi.ConfigurablePropagatorProvider")); + assertTrue(metric.tags.contains("source:extensions_path")); + } + + @Test + public void multipleRecordsDrainAsDistinctMetrics() { + collector.recordSpiDetected( + "io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider", "app_classpath"); + collector.recordSpiDetected( + "io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider", "app_classpath"); + collector.recordSpiDetected( + "io.opentelemetry.sdk.autoconfigure.spi.ConfigurableSamplerProvider", "extensions_path"); + + assertEquals(3, collector.drain().size()); + } + + @Test + public void drainWithoutRecordsReturnsEmpty() { + assertEquals(0, collector.drain().size()); + } + + @Test + public void drainClearsQueue() { + collector.recordSpiDetected( + "io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider", + "extensions_path"); + assertEquals(1, collector.drain().size()); + assertEquals(0, collector.drain().size()); + } +} diff --git a/telemetry/src/main/java/datadog/telemetry/TelemetrySystem.java b/telemetry/src/main/java/datadog/telemetry/TelemetrySystem.java index 4c66f5349f2..1899083d3df 100644 --- a/telemetry/src/main/java/datadog/telemetry/TelemetrySystem.java +++ b/telemetry/src/main/java/datadog/telemetry/TelemetrySystem.java @@ -15,6 +15,7 @@ import datadog.telemetry.metric.IastMetricPeriodicAction; import datadog.telemetry.metric.LLMObsMetricPeriodicAction; import datadog.telemetry.metric.OtelEnvMetricPeriodicAction; +import datadog.telemetry.metric.OtelSpiMetricPeriodicAction; import datadog.telemetry.metric.WafMetricPeriodicAction; import datadog.telemetry.products.ProductChangeAction; import datadog.telemetry.rum.RumPeriodicAction; @@ -57,6 +58,7 @@ static Thread createTelemetryRunnable( if (telemetryMetricsEnabled) { actions.add(new CoreMetricsPeriodicAction()); actions.add(new OtelEnvMetricPeriodicAction()); + actions.add(new OtelSpiMetricPeriodicAction()); actions.add(new ConfigInversionMetricPeriodicAction()); actions.add(new IntegrationPeriodicAction()); actions.add(new WafMetricPeriodicAction()); diff --git a/telemetry/src/main/java/datadog/telemetry/metric/OtelSpiMetricPeriodicAction.java b/telemetry/src/main/java/datadog/telemetry/metric/OtelSpiMetricPeriodicAction.java new file mode 100644 index 00000000000..bbdd9bfc89c --- /dev/null +++ b/telemetry/src/main/java/datadog/telemetry/metric/OtelSpiMetricPeriodicAction.java @@ -0,0 +1,13 @@ +package datadog.telemetry.metric; + +import datadog.trace.api.telemetry.MetricCollector; +import datadog.trace.api.telemetry.OtelSpiCollector; +import edu.umd.cs.findbugs.annotations.NonNull; + +public class OtelSpiMetricPeriodicAction extends MetricPeriodicAction { + @Override + @NonNull + public MetricCollector collector() { + return OtelSpiCollector.getInstance(); + } +} From 0c7c551a156303b891b4f953c8dfec25938da1fc Mon Sep 17 00:00:00 2001 From: Matthew Li Date: Wed, 6 May 2026 16:19:20 -0700 Subject: [PATCH 2/3] init --- .../trace/agent/tooling/ExtensionFinder.java | 26 +++ .../agent/tooling/ExtensionFinderTest.java | 193 ++++++++++++++++++ 2 files changed, 219 insertions(+) create mode 100644 dd-java-agent/agent-installer/src/test/java/datadog/trace/agent/tooling/ExtensionFinderTest.java diff --git a/dd-java-agent/agent-installer/src/main/java/datadog/trace/agent/tooling/ExtensionFinder.java b/dd-java-agent/agent-installer/src/main/java/datadog/trace/agent/tooling/ExtensionFinder.java index 1a19535d218..e44af2805e5 100644 --- a/dd-java-agent/agent-installer/src/main/java/datadog/trace/agent/tooling/ExtensionFinder.java +++ b/dd-java-agent/agent-installer/src/main/java/datadog/trace/agent/tooling/ExtensionFinder.java @@ -3,6 +3,7 @@ import static datadog.opentelemetry.tooling.OtelExtensionHandler.OPENTELEMETRY; import static datadog.trace.agent.tooling.ExtensionHandler.DATADOG; +import datadog.trace.api.telemetry.OtelSpiCollector; import de.thetaphi.forbiddenapis.SuppressForbidden; import java.io.File; import java.io.FileNotFoundException; @@ -13,6 +14,7 @@ import java.net.URLConnection; import java.net.URLStreamHandler; import java.util.ArrayList; +import java.util.Enumeration; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -26,6 +28,11 @@ public final class ExtensionFinder { private static final ExtensionHandler[] handlers = {OPENTELEMETRY, DATADOG}; + private static final String EXTENSIONS_PATH_SOURCE = "extensions_path"; + + private static final String SERVICES_PREFIX = "META-INF/services/"; + private static final String OTEL_NAMESPACE = "io.opentelemetry."; + /** * Discovers extensions on the configured path and creates a classloader for each extension. * Registers the combined classloader with {@link Utils#setExtendedClassLoader(ClassLoader)}. @@ -40,6 +47,7 @@ public static boolean findExtensions(String extensionsPath, Class... extensio String[] descriptors = descriptors(extensionTypes); for (JarFile jar : findExtensionJars(extensionsPath)) { + recordOtelSpiTelemetry(jar); URL extensionURL = findExtensionURL(jar, descriptors); if (null != extensionURL) { log.debug("Found extension jar {}", jar.getName()); @@ -60,6 +68,24 @@ public static boolean findExtensions(String extensionsPath, Class... extensio return !classLoaders.isEmpty(); } + /** + * Reports telemetry for any OpenTelemetry SPI service descriptors present in the jar — any entry + * under {@code META-INF/services/} whose name lives in the {@code io.opentelemetry.*} namespace. + * The jar's existing handle is reused; no new file resources are opened or held. + */ + static void recordOtelSpiTelemetry(JarFile jar) { + Enumeration entries = jar.entries(); + while (entries.hasMoreElements()) { + String name = entries.nextElement().getName(); + if (name.startsWith(SERVICES_PREFIX)) { + String fqn = name.substring(SERVICES_PREFIX.length()); + if (fqn.startsWith(OTEL_NAMESPACE)) { + OtelSpiCollector.getInstance().recordSpiDetected(fqn, EXTENSIONS_PATH_SOURCE); + } + } + } + } + /** Closes jar resources from the extension path which did not contain any extensions. */ private static void close(List unusedJars) { for (JarFile jar : unusedJars) { diff --git a/dd-java-agent/agent-installer/src/test/java/datadog/trace/agent/tooling/ExtensionFinderTest.java b/dd-java-agent/agent-installer/src/test/java/datadog/trace/agent/tooling/ExtensionFinderTest.java new file mode 100644 index 00000000000..a865a998d4c --- /dev/null +++ b/dd-java-agent/agent-installer/src/test/java/datadog/trace/agent/tooling/ExtensionFinderTest.java @@ -0,0 +1,193 @@ +package datadog.trace.agent.tooling; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import datadog.trace.api.telemetry.OtelSpiCollector; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +public class ExtensionFinderTest { + + private static final String AUTOCONFIGURE_PROPAGATOR = + "io.opentelemetry.sdk.autoconfigure.spi.ConfigurablePropagatorProvider"; + private static final String AUTOCONFIGURE_RESOURCE = + "io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider"; + private static final String AUTOCONFIGURE_SAMPLER = + "io.opentelemetry.sdk.autoconfigure.spi.ConfigurableSamplerProvider"; + private static final String AUTOCONFIGURE_EXPORTER = + "io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider"; + private static final String JAVAAGENT_INSTRUMENTATION_MODULE = + "io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule"; + private static final String JAVAAGENT_AGENT_LISTENER = + "io.opentelemetry.javaagent.extension.AgentListener"; + private static final String SHADED_AUTOCONFIGURE_SAMPLER = + "io.opentelemetry.javaagent.shaded.io.opentelemetry.sdk.autoconfigure.spi.ConfigurableSamplerProvider"; + + private final OtelSpiCollector collector = OtelSpiCollector.getInstance(); + + @BeforeEach + public void clearCollector() { + collector.drain(); + } + + @Test + public void singleOtelSpiIsReported(@TempDir Path tempDir) throws IOException { + Path jarPath = buildJar(tempDir, "ext.jar", AUTOCONFIGURE_PROPAGATOR); + + try (JarFile jar = new JarFile(jarPath.toFile(), false)) { + ExtensionFinder.recordOtelSpiTelemetry(jar); + } + + Collection drained = collector.drain(); + assertEquals(1, drained.size()); + OtelSpiCollector.OtelSpiMetric metric = drained.iterator().next(); + assertEquals("otel.spi.detected", metric.metricName); + assertTrue(metric.tags.contains("spi_class:" + AUTOCONFIGURE_PROPAGATOR)); + assertTrue(metric.tags.contains("source:extensions_path")); + } + + @Test + public void allFourAutoconfigureSpisAreReported(@TempDir Path tempDir) throws IOException { + Path jarPath = + buildJar( + tempDir, + "ext.jar", + AUTOCONFIGURE_PROPAGATOR, + AUTOCONFIGURE_RESOURCE, + AUTOCONFIGURE_SAMPLER, + AUTOCONFIGURE_EXPORTER); + + try (JarFile jar = new JarFile(jarPath.toFile(), false)) { + ExtensionFinder.recordOtelSpiTelemetry(jar); + } + + assertEquals( + new HashSet<>( + java.util.Arrays.asList( + AUTOCONFIGURE_PROPAGATOR, + AUTOCONFIGURE_RESOURCE, + AUTOCONFIGURE_SAMPLER, + AUTOCONFIGURE_EXPORTER)), + reportedFqns(collector.drain())); + } + + @Test + public void javaagentExtensionSpisAreReported(@TempDir Path tempDir) throws IOException { + Path jarPath = + buildJar(tempDir, "ext.jar", JAVAAGENT_INSTRUMENTATION_MODULE, JAVAAGENT_AGENT_LISTENER); + + try (JarFile jar = new JarFile(jarPath.toFile(), false)) { + ExtensionFinder.recordOtelSpiTelemetry(jar); + } + + assertEquals( + new HashSet<>( + java.util.Arrays.asList(JAVAAGENT_INSTRUMENTATION_MODULE, JAVAAGENT_AGENT_LISTENER)), + reportedFqns(collector.drain())); + } + + @Test + public void shadedJavaagentSpiIsReported(@TempDir Path tempDir) throws IOException { + Path jarPath = buildJar(tempDir, "ext.jar", SHADED_AUTOCONFIGURE_SAMPLER); + + try (JarFile jar = new JarFile(jarPath.toFile(), false)) { + ExtensionFinder.recordOtelSpiTelemetry(jar); + } + + Collection drained = collector.drain(); + assertEquals(1, drained.size()); + assertTrue( + drained.iterator().next().tags.contains("spi_class:" + SHADED_AUTOCONFIGURE_SAMPLER)); + } + + @Test + public void nonOtelSpiIsIgnored(@TempDir Path tempDir) throws IOException { + Path jarPath = + buildJar( + tempDir, + "ext.jar", + "com.example.MyService", + "org.springframework.context.ApplicationContextInitializer", + "java.sql.Driver"); + + try (JarFile jar = new JarFile(jarPath.toFile(), false)) { + ExtensionFinder.recordOtelSpiTelemetry(jar); + } + + assertEquals(0, collector.drain().size()); + } + + @Test + public void jarWithoutAnyServiceDescriptorsEmitsNothing(@TempDir Path tempDir) + throws IOException { + Path jarPath = tempDir.resolve("empty.jar"); + try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream(jarPath))) { + jos.putNextEntry(new JarEntry("README.txt")); + jos.write("not an extension".getBytes()); + jos.closeEntry(); + } + + try (JarFile jar = new JarFile(jarPath.toFile(), false)) { + ExtensionFinder.recordOtelSpiTelemetry(jar); + } + + assertEquals(0, collector.drain().size()); + } + + @Test + public void mixedOtelAndNonOtelReportsOnlyOtel(@TempDir Path tempDir) throws IOException { + Path jarPath = + buildJar( + tempDir, + "ext.jar", + AUTOCONFIGURE_PROPAGATOR, + "com.example.MyService", + JAVAAGENT_AGENT_LISTENER, + "java.sql.Driver"); + + try (JarFile jar = new JarFile(jarPath.toFile(), false)) { + ExtensionFinder.recordOtelSpiTelemetry(jar); + } + + assertEquals( + new HashSet<>(java.util.Arrays.asList(AUTOCONFIGURE_PROPAGATOR, JAVAAGENT_AGENT_LISTENER)), + reportedFqns(collector.drain())); + } + + private static Set reportedFqns(Collection drained) { + Set fqns = new HashSet<>(); + for (OtelSpiCollector.OtelSpiMetric metric : drained) { + for (String tag : metric.tags) { + if (tag.startsWith("spi_class:")) { + fqns.add(tag.substring("spi_class:".length())); + } + } + } + return fqns; + } + + /** Builds a jar with empty {@code META-INF/services/} entries for each given FQN. */ + private static Path buildJar(Path dir, String name, String... serviceFqns) throws IOException { + Path jarPath = dir.resolve(name); + try (OutputStream out = Files.newOutputStream(jarPath); + JarOutputStream jos = new JarOutputStream(out)) { + for (String fqn : serviceFqns) { + jos.putNextEntry(new JarEntry("META-INF/services/" + fqn)); + jos.closeEntry(); + } + } + return jarPath; + } +} From 837fbf26135e237d76f73ada6fef2ba750062ca2 Mon Sep 17 00:00:00 2001 From: Matthew Li Date: Thu, 7 May 2026 11:50:17 -0700 Subject: [PATCH 3/3] remove iterating jar files w/ static list of SPIs --- .../trace/agent/tooling/ExtensionFinder.java | 44 ++++++++++++------- .../agent/tooling/ExtensionFinderTest.java | 16 +------ 2 files changed, 29 insertions(+), 31 deletions(-) diff --git a/dd-java-agent/agent-installer/src/main/java/datadog/trace/agent/tooling/ExtensionFinder.java b/dd-java-agent/agent-installer/src/main/java/datadog/trace/agent/tooling/ExtensionFinder.java index e44af2805e5..f61b477b32b 100644 --- a/dd-java-agent/agent-installer/src/main/java/datadog/trace/agent/tooling/ExtensionFinder.java +++ b/dd-java-agent/agent-installer/src/main/java/datadog/trace/agent/tooling/ExtensionFinder.java @@ -14,7 +14,6 @@ import java.net.URLConnection; import java.net.URLStreamHandler; import java.util.ArrayList; -import java.util.Enumeration; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -29,9 +28,31 @@ public final class ExtensionFinder { private static final ExtensionHandler[] handlers = {OPENTELEMETRY, DATADOG}; private static final String EXTENSIONS_PATH_SOURCE = "extensions_path"; - private static final String SERVICES_PREFIX = "META-INF/services/"; - private static final String OTEL_NAMESPACE = "io.opentelemetry."; + + private static final String[] OTEL_SPI_FQNS = { + "io.opentelemetry.context.ContextStorageProvider", + "io.opentelemetry.exporter.internal.compression.CompressorProvider", + "io.opentelemetry.exporter.internal.grpc.GrpcSenderProvider", + "io.opentelemetry.exporter.internal.http.HttpSenderProvider", + "io.opentelemetry.javaagent.extension.AgentListener", + "io.opentelemetry.javaagent.extension.ignore.IgnoredTypesConfigurer", + "io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule", + "io.opentelemetry.javaagent.tooling.BeforeAgentListener", + "io.opentelemetry.javaagent.tooling.LoggingCustomizer", + "io.opentelemetry.javaagent.tooling.bootstrap.BootstrapPackagesConfigurer", + "io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider", + "io.opentelemetry.sdk.autoconfigure.spi.AutoConfigureListener", + "io.opentelemetry.sdk.autoconfigure.spi.ConditionalResourceProvider", + "io.opentelemetry.sdk.autoconfigure.spi.ConfigurablePropagatorProvider", + "io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider", + "io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider", + "io.opentelemetry.sdk.autoconfigure.spi.logs.ConfigurableLogRecordExporterProvider", + "io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider", + "io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider", + "io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider", + "io.opentelemetry.sdk.autoconfigure.spi.traces.SpanExporterCustomizer", + }; /** * Discovers extensions on the configured path and creates a classloader for each extension. @@ -68,20 +89,11 @@ public static boolean findExtensions(String extensionsPath, Class... extensio return !classLoaders.isEmpty(); } - /** - * Reports telemetry for any OpenTelemetry SPI service descriptors present in the jar — any entry - * under {@code META-INF/services/} whose name lives in the {@code io.opentelemetry.*} namespace. - * The jar's existing handle is reused; no new file resources are opened or held. - */ + /** Reports telemetry for any tracked OpenTelemetry SPI service descriptors present in the jar. */ static void recordOtelSpiTelemetry(JarFile jar) { - Enumeration entries = jar.entries(); - while (entries.hasMoreElements()) { - String name = entries.nextElement().getName(); - if (name.startsWith(SERVICES_PREFIX)) { - String fqn = name.substring(SERVICES_PREFIX.length()); - if (fqn.startsWith(OTEL_NAMESPACE)) { - OtelSpiCollector.getInstance().recordSpiDetected(fqn, EXTENSIONS_PATH_SOURCE); - } + for (String fqn : OTEL_SPI_FQNS) { + if (null != jar.getJarEntry(SERVICES_PREFIX + fqn)) { + OtelSpiCollector.getInstance().recordSpiDetected(fqn, EXTENSIONS_PATH_SOURCE); } } } diff --git a/dd-java-agent/agent-installer/src/test/java/datadog/trace/agent/tooling/ExtensionFinderTest.java b/dd-java-agent/agent-installer/src/test/java/datadog/trace/agent/tooling/ExtensionFinderTest.java index a865a998d4c..624d73ef0c4 100644 --- a/dd-java-agent/agent-installer/src/test/java/datadog/trace/agent/tooling/ExtensionFinderTest.java +++ b/dd-java-agent/agent-installer/src/test/java/datadog/trace/agent/tooling/ExtensionFinderTest.java @@ -25,7 +25,7 @@ public class ExtensionFinderTest { private static final String AUTOCONFIGURE_RESOURCE = "io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider"; private static final String AUTOCONFIGURE_SAMPLER = - "io.opentelemetry.sdk.autoconfigure.spi.ConfigurableSamplerProvider"; + "io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider"; private static final String AUTOCONFIGURE_EXPORTER = "io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider"; private static final String JAVAAGENT_INSTRUMENTATION_MODULE = @@ -98,20 +98,6 @@ public void javaagentExtensionSpisAreReported(@TempDir Path tempDir) throws IOEx reportedFqns(collector.drain())); } - @Test - public void shadedJavaagentSpiIsReported(@TempDir Path tempDir) throws IOException { - Path jarPath = buildJar(tempDir, "ext.jar", SHADED_AUTOCONFIGURE_SAMPLER); - - try (JarFile jar = new JarFile(jarPath.toFile(), false)) { - ExtensionFinder.recordOtelSpiTelemetry(jar); - } - - Collection drained = collector.drain(); - assertEquals(1, drained.size()); - assertTrue( - drained.iterator().next().tags.contains("spi_class:" + SHADED_AUTOCONFIGURE_SAMPLER)); - } - @Test public void nonOtelSpiIsIgnored(@TempDir Path tempDir) throws IOException { Path jarPath =