From c9490d463a8c7a5651cda0bb3596f1f76e75c9c1 Mon Sep 17 00:00:00 2001 From: Lauri Tulmin Date: Thu, 4 Dec 2025 15:37:04 +0200 Subject: [PATCH 1/2] Fix using InstrumenterCustomizerProvider SPI in extensions --- .../smoketest/SpringBootIntegrationTest.java | 1 + ...ry.instrumentation.muzzle-check.gradle.kts | 80 ++++++++++--------- .../tooling/ExtensionClassLoader.java | 47 +++++++++++ 3 files changed, 89 insertions(+), 39 deletions(-) diff --git a/examples/extension/src/test/java/com/example/javaagent/smoketest/SpringBootIntegrationTest.java b/examples/extension/src/test/java/com/example/javaagent/smoketest/SpringBootIntegrationTest.java index 7b773705b9f7..920cd621cc66 100644 --- a/examples/extension/src/test/java/com/example/javaagent/smoketest/SpringBootIntegrationTest.java +++ b/examples/extension/src/test/java/com/example/javaagent/smoketest/SpringBootIntegrationTest.java @@ -87,5 +87,6 @@ private void testAndVerify() throws IOException, InterruptedException { Assertions.assertNotEquals( 0, countResourcesByValue(traces, "telemetry.distro.version", currentAgentVersion)); Assertions.assertNotEquals(0, countResourcesByValue(traces, "custom.resource", "demo")); + Assertions.assertEquals(1, countSpansByAttributeValue(traces, "demo.custom", "demo-extension")); } } diff --git a/gradle-plugins/src/main/kotlin/io.opentelemetry.instrumentation.muzzle-check.gradle.kts b/gradle-plugins/src/main/kotlin/io.opentelemetry.instrumentation.muzzle-check.gradle.kts index 9f8936ab7e8b..036774457b75 100644 --- a/gradle-plugins/src/main/kotlin/io.opentelemetry.instrumentation.muzzle-check.gradle.kts +++ b/gradle-plugins/src/main/kotlin/io.opentelemetry.instrumentation.muzzle-check.gradle.kts @@ -73,52 +73,54 @@ val shadowMuzzleBootstrap by tasks.registering(ShadowJar::class) { // this is a copied from io.opentelemetry.instrumentation.javaagent-shadowing for now at least to // avoid publishing io.opentelemetry.instrumentation.javaagent-shadowing publicly -tasks.withType().configureEach { - mergeServiceFiles() - // mergeServiceFiles requires that duplicate strategy is set to include - filesMatching("META-INF/services/**") { - duplicatesStrategy = DuplicatesStrategy.INCLUDE - } - // Merge any AWS SDK service files that may be present (too bad they didn't just use normal - // service loader...) - mergeServiceFiles("software/amazon/awssdk/global/handlers") - // mergeServiceFiles requires that duplicate strategy is set to include - filesMatching("software/amazon/awssdk/global/handlers/**") { - duplicatesStrategy = DuplicatesStrategy.INCLUDE - } +listOf(shadowModule, shadowMuzzleTooling, shadowMuzzleBootstrap).forEach { task -> + task.configure { + mergeServiceFiles() + // mergeServiceFiles requires that duplicate strategy is set to include + filesMatching("META-INF/services/**") { + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } + // Merge any AWS SDK service files that may be present (too bad they didn't just use normal + // service loader...) + mergeServiceFiles("software/amazon/awssdk/global/handlers") + // mergeServiceFiles requires that duplicate strategy is set to include + filesMatching("software/amazon/awssdk/global/handlers/**") { + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } - exclude("**/module-info.class") + exclude("**/module-info.class") - // rewrite dependencies calling Logger.getLogger - relocate("java.util.logging.Logger", "io.opentelemetry.javaagent.bootstrap.PatchLogger") + // rewrite dependencies calling Logger.getLogger + relocate("java.util.logging.Logger", "io.opentelemetry.javaagent.bootstrap.PatchLogger") - if (project.findProperty("disableShadowRelocate") != "true") { - // prevents conflict with library instrumentation, since these classes live in the bootstrap class loader - relocate("io.opentelemetry.instrumentation", "io.opentelemetry.javaagent.shaded.instrumentation") { - // Exclude resource providers since they live in the agent class loader - exclude("io.opentelemetry.instrumentation.resources.*") - exclude("io.opentelemetry.instrumentation.spring.resources.*") - } + if (project.findProperty("disableShadowRelocate") != "true") { + // prevents conflict with library instrumentation, since these classes live in the bootstrap class loader + relocate("io.opentelemetry.instrumentation", "io.opentelemetry.javaagent.shaded.instrumentation") { + // Exclude resource providers since they live in the agent class loader + exclude("io.opentelemetry.instrumentation.resources.*") + exclude("io.opentelemetry.instrumentation.spring.resources.*") + } - // relocate(OpenTelemetry API) since these classes live in the bootstrap class loader - relocate("io.opentelemetry.api", "io.opentelemetry.javaagent.shaded.io.opentelemetry.api") - relocate("io.opentelemetry.semconv", "io.opentelemetry.javaagent.shaded.io.opentelemetry.semconv") - relocate("io.opentelemetry.context", "io.opentelemetry.javaagent.shaded.io.opentelemetry.context") - relocate("io.opentelemetry.common", "io.opentelemetry.javaagent.shaded.io.opentelemetry.common") - } + // relocate(OpenTelemetry API) since these classes live in the bootstrap class loader + relocate("io.opentelemetry.api", "io.opentelemetry.javaagent.shaded.io.opentelemetry.api") + relocate("io.opentelemetry.semconv", "io.opentelemetry.javaagent.shaded.io.opentelemetry.semconv") + relocate("io.opentelemetry.context", "io.opentelemetry.javaagent.shaded.io.opentelemetry.context") + relocate("io.opentelemetry.common", "io.opentelemetry.javaagent.shaded.io.opentelemetry.common") + } - // relocate(the OpenTelemetry extensions that are used by instrumentation modules) - // these extensions live in the AgentClassLoader, and are injected into the user's class loader - // by the instrumentation modules that use them - relocate("io.opentelemetry.contrib.awsxray", "io.opentelemetry.javaagent.shaded.io.opentelemetry.contrib.awsxray") - relocate("io.opentelemetry.extension.kotlin", "io.opentelemetry.javaagent.shaded.io.opentelemetry.extension.kotlin") + // relocate(the OpenTelemetry extensions that are used by instrumentation modules) + // these extensions live in the AgentClassLoader, and are injected into the user's class loader + // by the instrumentation modules that use them + relocate("io.opentelemetry.contrib.awsxray", "io.opentelemetry.javaagent.shaded.io.opentelemetry.contrib.awsxray") + relocate("io.opentelemetry.extension.kotlin", "io.opentelemetry.javaagent.shaded.io.opentelemetry.extension.kotlin") - // this is for instrumentation of opentelemetry-api and opentelemetry-instrumentation-api - relocate("application.io.opentelemetry", "io.opentelemetry") - relocate("application.io.opentelemetry.instrumentation.api", "io.opentelemetry.instrumentation.api") + // this is for instrumentation of opentelemetry-api and opentelemetry-instrumentation-api + relocate("application.io.opentelemetry", "io.opentelemetry") + relocate("application.io.opentelemetry.instrumentation.api", "io.opentelemetry.instrumentation.api") - // this is for instrumentation on java.util.logging (since java.util.logging itself is shaded above) - relocate("application.java.util.logging", "java.util.logging") + // this is for instrumentation on java.util.logging (since java.util.logging itself is shaded above) + relocate("application.java.util.logging", "java.util.logging") + } } val compileMuzzle by tasks.registering { diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ExtensionClassLoader.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ExtensionClassLoader.java index 7437fa84da69..25b6562148a4 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ExtensionClassLoader.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ExtensionClassLoader.java @@ -25,6 +25,7 @@ import java.util.Collections; import java.util.Enumeration; import java.util.List; +import java.util.NoSuchElementException; import java.util.jar.JarEntry; import java.util.jar.JarFile; import javax.annotation.Nullable; @@ -201,4 +202,50 @@ private ExtensionClassLoader( super(new URL[] {url}, parent); this.isSecurityManagerSupportEnabled = isSecurityManagerSupportEnabled; } + + @Override + public Enumeration findResources(String name) throws IOException { + Enumeration result = super.findResources(name); + // Agent shades instrumentation-api-incubator, in extensions references to these classes are + // remapped at load time. Here we handle looking up the service files for the classes that + // were renamed using the original name. + if (name.startsWith( + "META-INF/services/io.opentelemetry.javaagent.shaded.instrumentation.api.incubator")) { + String originalName = + name.replace( + "opentelemetry.javaagent.shaded.instrumentation", "opentelemetry.instrumentation"); + return new CompoundEnumeration<>(result, super.findResources(originalName)); + } + return result; + } + + private static class CompoundEnumeration implements Enumeration { + private final Enumeration[] enumerations; + private int index = 0; + + @SafeVarargs + @SuppressWarnings("varargs") + CompoundEnumeration(Enumeration... enumerations) { + this.enumerations = enumerations; + } + + @Override + public boolean hasMoreElements() { + while (index < enumerations.length) { + if (enumerations[index].hasMoreElements()) { + return true; + } + index++; + } + return false; + } + + @Override + public E nextElement() { + if (!hasMoreElements()) { + throw new NoSuchElementException(); + } + return enumerations[index].nextElement(); + } + } } From 7d518e049d82cef652ecc2cddc79a6e452c01c98 Mon Sep 17 00:00:00 2001 From: Lauri Tulmin Date: Thu, 4 Dec 2025 16:41:07 +0200 Subject: [PATCH 2/2] fix testing agent --- examples/distro/gradle/instrumentation.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/distro/gradle/instrumentation.gradle b/examples/distro/gradle/instrumentation.gradle index 76d07101261d..83875baa74e3 100644 --- a/examples/distro/gradle/instrumentation.gradle +++ b/examples/distro/gradle/instrumentation.gradle @@ -38,7 +38,7 @@ shadowJar { mergeServiceFiles() // mergeServiceFiles requires that duplicate strategy is set to include - filesMatching("inst/META-INF/services/**") { + filesMatching("META-INF/services/**") { duplicatesStrategy = DuplicatesStrategy.INCLUDE }