From 0e067771955c978b39caa04b8dcbcb7820fc1995 Mon Sep 17 00:00:00 2001 From: Lauri Tulmin Date: Tue, 5 May 2026 11:22:38 +0300 Subject: [PATCH 1/7] Generate class with instrumetnation version --- ...ntelemetry.instrumentation.base.gradle.kts | 43 +++++++++++++++++++ .../api/instrumenter/InstrumenterBuilder.java | 6 ++- .../EmbeddedInstrumentationProperties.java | 31 +++++++++++++ instrumentation/jdbc/library/build.gradle.kts | 1 + .../build.gradle.kts | 1 + javaagent/build.gradle.kts | 3 ++ 6 files changed, 83 insertions(+), 2 deletions(-) diff --git a/conventions/src/main/kotlin/io.opentelemetry.instrumentation.base.gradle.kts b/conventions/src/main/kotlin/io.opentelemetry.instrumentation.base.gradle.kts index 1da32a0f93d4..4edca6640fcf 100644 --- a/conventions/src/main/kotlin/io.opentelemetry.instrumentation.base.gradle.kts +++ b/conventions/src/main/kotlin/io.opentelemetry.instrumentation.base.gradle.kts @@ -2,6 +2,7 @@ import io.opentelemetry.instrumentation.gradle.OtelPropsExtension import io.opentelemetry.javaagent.muzzle.AcceptableVersions +import kotlin.text.replace plugins { `java-library` @@ -245,6 +246,45 @@ tasks { File(propertiesDir.get().asFile, "$name.properties").writeText("version=$version") } } + + val generateInstrumentationVersionClass by registering { + val name = computeInstrumentationName() + val version = project.version as String + inputs.property("instrumentation.name", name) + inputs.property("instrumentation.version", version) + + val moduleName = name.replace("(-[0-9.]*)$".toRegex(), "").replace("-", "").replace("([0-9]+)\\.([0-9]+)".toRegex(), "$1_$2") + val baseVersion = name.replace(".*?([0-9.]*)$".toRegex(), "$1").replace(".", "_") + val packageName = moduleName + if (baseVersion.isNotEmpty()) "_v$baseVersion" else "" + + val classDir = layout.buildDirectory.dir("generated/instrumentationVersionClass/${packageName.replace('.', '/')}/internal") + outputs.dir(classDir) + + doLast { + File(classDir.get().asFile, "InstrumentationVersion.java").writeText(""" + package $packageName.internal; + + // import javax.annotation.Generated; + + /** Autogenerated class do not edit. */ + // @Generated("io.opentelemetry.instrumentation.base") + public final class InstrumentationVersion { + public static final String VERSION = "$version"; + + public InstrumentationVersion() {} + + @Override + public String toString() { + return VERSION; + } + } + """.trimIndent()) + } + } + + project.tasks.matching { it.name == "compileJava" || it.name == "sourcesJar" || it.name == "compileKotlin" }.configureEach { + dependsOn(generateInstrumentationVersionFile, generateInstrumentationVersionClass) + } } fun computeInstrumentationName(): String { @@ -258,5 +298,8 @@ fun computeInstrumentationName(): String { sourceSets { main { output.dir("build/generated/instrumentationVersion", "builtBy" to "generateInstrumentationVersionFile") + java { + srcDir(layout.buildDirectory.dir("generated/instrumentationVersionClass")) + } } } diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterBuilder.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterBuilder.java index 6b6a2914bf83..305ae1a33db0 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterBuilder.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterBuilder.java @@ -89,8 +89,6 @@ public final class InstrumenterBuilder { this.openTelemetry = openTelemetry; this.instrumentationName = instrumentationName; this.spanNameExtractor = spanNameExtractor; - this.instrumentationVersion = - EmbeddedInstrumentationProperties.findVersion(instrumentationName); } /** @@ -302,6 +300,10 @@ private Instrumenter buildInstrumenter( } Tracer buildTracer() { + if (instrumentationVersion == null) { + instrumentationVersion = EmbeddedInstrumentationProperties.findVersion(instrumentationName); + } + TracerBuilder tracerBuilder = openTelemetry.getTracerProvider().tracerBuilder(instrumentationName); if (instrumentationVersion != null) { diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/EmbeddedInstrumentationProperties.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/EmbeddedInstrumentationProperties.java index 70002f426220..a2817c84fa7d 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/EmbeddedInstrumentationProperties.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/EmbeddedInstrumentationProperties.java @@ -13,6 +13,7 @@ import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; +import java.util.regex.Pattern; import javax.annotation.Nullable; /** @@ -54,6 +55,36 @@ public static String findVersion(String instrumentationName) { @Nullable private static String loadVersion(String instrumentationName) { + String version = loadVersionFromClass(instrumentationName); + if (version == null) { + version = loadVersionFromProperties(instrumentationName); + } + return version; + } + + private static final Pattern stripVersionSuffix = Pattern.compile("(-[0-9.]*)$"); + private static final Pattern normalizeVersion = Pattern.compile("([0-9]+)\\.([0-9]+)"); + private static final Pattern extractVersion = Pattern.compile(".*?([0-9.]*)$"); + + @Nullable + private static String loadVersionFromClass(String instrumentationName) { + String moduleName = + stripVersionSuffix.matcher(instrumentationName).replaceAll("").replace("-", ""); + moduleName = normalizeVersion.matcher(moduleName).replaceAll("$1_$2"); + String baseVersion = + extractVersion.matcher(instrumentationName).replaceAll("$1").replace(".", "_"); + String packageName = moduleName + (baseVersion.isEmpty() ? "" : "_v" + baseVersion); + + try { + Class clazz = Class.forName(packageName + ".internal.InstrumentationVersion"); + return clazz.getConstructor().newInstance().toString(); + } catch (Exception e) { + return null; + } + } + + @Nullable + private static String loadVersionFromProperties(String instrumentationName) { String path = "META-INF/io/opentelemetry/instrumentation/" + instrumentationName + ".properties"; try (InputStream in = loader.getResourceAsStream(path)) { diff --git a/instrumentation/jdbc/library/build.gradle.kts b/instrumentation/jdbc/library/build.gradle.kts index 4531a2f11823..3fa56c603271 100644 --- a/instrumentation/jdbc/library/build.gradle.kts +++ b/instrumentation/jdbc/library/build.gradle.kts @@ -53,6 +53,7 @@ tasks { into("build/extracted/shadow-javaagent") exclude("META-INF/**") exclude("io/opentelemetry/javaagent/bootstrap/**") + exclude("**/InstrumentationVersion.class") } // this will be included in bootstrap module diff --git a/instrumentation/r2dbc-1.0/library-instrumentation-shaded/build.gradle.kts b/instrumentation/r2dbc-1.0/library-instrumentation-shaded/build.gradle.kts index c750072d17ab..c7a08949bfdd 100644 --- a/instrumentation/r2dbc-1.0/library-instrumentation-shaded/build.gradle.kts +++ b/instrumentation/r2dbc-1.0/library-instrumentation-shaded/build.gradle.kts @@ -34,6 +34,7 @@ tasks { dependsOn(shadowJar) from(zipTree(shadowJar.get().archiveFile)) exclude("META-INF/**") + exclude("**/InstrumentationVersion.class") into("build/extracted/shadow") } diff --git a/javaagent/build.gradle.kts b/javaagent/build.gradle.kts index 435ca4ebcd88..9b078eb5c312 100644 --- a/javaagent/build.gradle.kts +++ b/javaagent/build.gradle.kts @@ -185,6 +185,9 @@ tasks { filesMatching("META-INF/io/opentelemetry/instrumentation/**") { duplicatesStrategy = DuplicatesStrategy.EXCLUDE } + filesMatching("**/InstrumentationVersion.class") { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } exclude("META-INF/LICENSE") exclude("META-INF/NOTICE") exclude("META-INF/maven/**") From ca30c0dd6e9baf3c2136ba477cacd580dc629e9f Mon Sep 17 00:00:00 2001 From: Lauri Tulmin Date: Tue, 5 May 2026 14:50:55 +0300 Subject: [PATCH 2/7] fix version separator --- .../kotlin/io.opentelemetry.instrumentation.base.gradle.kts | 2 +- .../api/internal/EmbeddedInstrumentationProperties.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/conventions/src/main/kotlin/io.opentelemetry.instrumentation.base.gradle.kts b/conventions/src/main/kotlin/io.opentelemetry.instrumentation.base.gradle.kts index 4edca6640fcf..6f0bbac77726 100644 --- a/conventions/src/main/kotlin/io.opentelemetry.instrumentation.base.gradle.kts +++ b/conventions/src/main/kotlin/io.opentelemetry.instrumentation.base.gradle.kts @@ -255,7 +255,7 @@ tasks { val moduleName = name.replace("(-[0-9.]*)$".toRegex(), "").replace("-", "").replace("([0-9]+)\\.([0-9]+)".toRegex(), "$1_$2") val baseVersion = name.replace(".*?([0-9.]*)$".toRegex(), "$1").replace(".", "_") - val packageName = moduleName + if (baseVersion.isNotEmpty()) "_v$baseVersion" else "" + val packageName = moduleName + if (baseVersion.isNotEmpty()) ".v$baseVersion" else "" val classDir = layout.buildDirectory.dir("generated/instrumentationVersionClass/${packageName.replace('.', '/')}/internal") outputs.dir(classDir) diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/EmbeddedInstrumentationProperties.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/EmbeddedInstrumentationProperties.java index a2817c84fa7d..0f6e0f63786c 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/EmbeddedInstrumentationProperties.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/EmbeddedInstrumentationProperties.java @@ -73,7 +73,7 @@ private static String loadVersionFromClass(String instrumentationName) { moduleName = normalizeVersion.matcher(moduleName).replaceAll("$1_$2"); String baseVersion = extractVersion.matcher(instrumentationName).replaceAll("$1").replace(".", "_"); - String packageName = moduleName + (baseVersion.isEmpty() ? "" : "_v" + baseVersion); + String packageName = moduleName + (baseVersion.isEmpty() ? "" : ".v" + baseVersion); try { Class clazz = Class.forName(packageName + ".internal.InstrumentationVersion"); From 35aaac0bf70be3ffcf7554b1c2fa6dd72d328b19 Mon Sep 17 00:00:00 2001 From: Lauri Tulmin Date: Thu, 7 May 2026 17:07:13 +0300 Subject: [PATCH 3/7] fix --- ...ntelemetry.instrumentation.base.gradle.kts | 73 ----------------- .../otel.instrumentation-version.gradle.kts | 78 +++++++++++++++++++ .../otel.javaagent-instrumentation.gradle.kts | 5 +- .../otel.library-instrumentation.gradle.kts | 1 + 4 files changed, 82 insertions(+), 75 deletions(-) create mode 100644 conventions/src/main/kotlin/otel.instrumentation-version.gradle.kts diff --git a/conventions/src/main/kotlin/io.opentelemetry.instrumentation.base.gradle.kts b/conventions/src/main/kotlin/io.opentelemetry.instrumentation.base.gradle.kts index 6f0bbac77726..5161f842fef1 100644 --- a/conventions/src/main/kotlin/io.opentelemetry.instrumentation.base.gradle.kts +++ b/conventions/src/main/kotlin/io.opentelemetry.instrumentation.base.gradle.kts @@ -2,7 +2,6 @@ import io.opentelemetry.instrumentation.gradle.OtelPropsExtension import io.opentelemetry.javaagent.muzzle.AcceptableVersions -import kotlin.text.replace plugins { `java-library` @@ -231,75 +230,3 @@ if (otelProps.testLatestDeps) { } } } - -tasks { - val generateInstrumentationVersionFile by registering { - val name = computeInstrumentationName() - val version = project.version as String - inputs.property("instrumentation.name", name) - inputs.property("instrumentation.version", version) - - val propertiesDir = layout.buildDirectory.dir("generated/instrumentationVersion/META-INF/io/opentelemetry/instrumentation/") - outputs.dir(propertiesDir) - - doLast { - File(propertiesDir.get().asFile, "$name.properties").writeText("version=$version") - } - } - - val generateInstrumentationVersionClass by registering { - val name = computeInstrumentationName() - val version = project.version as String - inputs.property("instrumentation.name", name) - inputs.property("instrumentation.version", version) - - val moduleName = name.replace("(-[0-9.]*)$".toRegex(), "").replace("-", "").replace("([0-9]+)\\.([0-9]+)".toRegex(), "$1_$2") - val baseVersion = name.replace(".*?([0-9.]*)$".toRegex(), "$1").replace(".", "_") - val packageName = moduleName + if (baseVersion.isNotEmpty()) ".v$baseVersion" else "" - - val classDir = layout.buildDirectory.dir("generated/instrumentationVersionClass/${packageName.replace('.', '/')}/internal") - outputs.dir(classDir) - - doLast { - File(classDir.get().asFile, "InstrumentationVersion.java").writeText(""" - package $packageName.internal; - - // import javax.annotation.Generated; - - /** Autogenerated class do not edit. */ - // @Generated("io.opentelemetry.instrumentation.base") - public final class InstrumentationVersion { - public static final String VERSION = "$version"; - - public InstrumentationVersion() {} - - @Override - public String toString() { - return VERSION; - } - } - """.trimIndent()) - } - } - - project.tasks.matching { it.name == "compileJava" || it.name == "sourcesJar" || it.name == "compileKotlin" }.configureEach { - dependsOn(generateInstrumentationVersionFile, generateInstrumentationVersionClass) - } -} - -fun computeInstrumentationName(): String { - val name = when (projectDir.name) { - "javaagent", "library", "library-autoconfigure" -> projectDir.parentFile.name - else -> project.name - } - return "io.opentelemetry.$name" -} - -sourceSets { - main { - output.dir("build/generated/instrumentationVersion", "builtBy" to "generateInstrumentationVersionFile") - java { - srcDir(layout.buildDirectory.dir("generated/instrumentationVersionClass")) - } - } -} diff --git a/conventions/src/main/kotlin/otel.instrumentation-version.gradle.kts b/conventions/src/main/kotlin/otel.instrumentation-version.gradle.kts new file mode 100644 index 000000000000..58d77e6a10b0 --- /dev/null +++ b/conventions/src/main/kotlin/otel.instrumentation-version.gradle.kts @@ -0,0 +1,78 @@ +import kotlin.text.replace + +plugins { + `java-library` +} + +tasks { + val generateInstrumentationVersionFile by registering { + val name = computeInstrumentationName() + val version = project.version as String + inputs.property("instrumentation.name", name) + inputs.property("instrumentation.version", version) + + val propertiesDir = layout.buildDirectory.dir("generated/instrumentationVersion/META-INF/io/opentelemetry/instrumentation/") + outputs.dir(propertiesDir) + + doLast { + File(propertiesDir.get().asFile, "$name.properties").writeText("version=$version") + } + } + + val generateInstrumentationVersionClass by registering { + val name = computeInstrumentationName() + val version = project.version as String + inputs.property("instrumentation.name", name) + inputs.property("instrumentation.version", version) + + val moduleName = name.replace("(-[0-9.]*)$".toRegex(), "").replace("-", "").replace("([0-9]+)\\.([0-9]+)".toRegex(), "$1_$2") + val baseVersion = name.replace(".*?([0-9.]*)$".toRegex(), "$1").replace(".", "_") + val packageName = moduleName + if (baseVersion.isNotEmpty()) ".v$baseVersion" else "" + + val classDir = layout.buildDirectory.dir("generated/instrumentationVersionClass/${packageName.replace('.', '/')}/internal") + outputs.dir(classDir) + + doLast { + File(classDir.get().asFile, "InstrumentationVersion.java").writeText(""" + package $packageName.internal; + + // import javax.annotation.Generated; + + /** Autogenerated class do not edit. */ + // @Generated("otel.instrumentation-version") + public final class InstrumentationVersion { + public static final String VERSION = "$version"; + + public InstrumentationVersion() {} + + @Override + public String toString() { + return VERSION; + } + } + """.trimIndent()) + } + } + + project.tasks.matching { it.name == "compileJava" || it.name == "sourcesJar" + || it.name == "compileKotlin" || it.name == "kotlinSourcesJar" }.configureEach { + dependsOn(generateInstrumentationVersionFile, generateInstrumentationVersionClass) + } +} + +fun computeInstrumentationName(): String { + val name = when (projectDir.name) { + "javaagent", "library", "library-autoconfigure" -> projectDir.parentFile.name + else -> project.name + } + return "io.opentelemetry.$name" +} + +sourceSets { + main { + output.dir("build/generated/instrumentationVersion", "builtBy" to "generateInstrumentationVersionFile") + java { + srcDir(layout.buildDirectory.dir("generated/instrumentationVersionClass")) + } + } +} diff --git a/conventions/src/main/kotlin/otel.javaagent-instrumentation.gradle.kts b/conventions/src/main/kotlin/otel.javaagent-instrumentation.gradle.kts index 2fe5adb596bf..20e248b55116 100644 --- a/conventions/src/main/kotlin/otel.javaagent-instrumentation.gradle.kts +++ b/conventions/src/main/kotlin/otel.javaagent-instrumentation.gradle.kts @@ -1,8 +1,9 @@ plugins { + id("io.opentelemetry.instrumentation.javaagent-instrumentation") + id("otel.javaagent-testing") id("otel.publish-conventions") - - id("io.opentelemetry.instrumentation.javaagent-instrumentation") + id("otel.instrumentation-version") } extra["mavenGroupId"] = "io.opentelemetry.javaagent.instrumentation" diff --git a/conventions/src/main/kotlin/otel.library-instrumentation.gradle.kts b/conventions/src/main/kotlin/otel.library-instrumentation.gradle.kts index 92e1cbfc5c96..68a5eb969b05 100644 --- a/conventions/src/main/kotlin/otel.library-instrumentation.gradle.kts +++ b/conventions/src/main/kotlin/otel.library-instrumentation.gradle.kts @@ -4,6 +4,7 @@ plugins { id("otel.jacoco-conventions") id("otel.java-conventions") id("otel.publish-conventions") + id("otel.instrumentation-version") } extra["mavenGroupId"] = "io.opentelemetry.instrumentation" From 0af2d5b2e1329b8832f4c82c9ca0be637a4abeab Mon Sep 17 00:00:00 2001 From: Lauri Tulmin Date: Thu, 7 May 2026 17:49:12 +0300 Subject: [PATCH 4/7] disable checkstyle --- .../src/main/kotlin/otel.instrumentation-version.gradle.kts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/conventions/src/main/kotlin/otel.instrumentation-version.gradle.kts b/conventions/src/main/kotlin/otel.instrumentation-version.gradle.kts index 58d77e6a10b0..33a4794f96eb 100644 --- a/conventions/src/main/kotlin/otel.instrumentation-version.gradle.kts +++ b/conventions/src/main/kotlin/otel.instrumentation-version.gradle.kts @@ -58,6 +58,10 @@ tasks { || it.name == "compileKotlin" || it.name == "kotlinSourcesJar" }.configureEach { dependsOn(generateInstrumentationVersionFile, generateInstrumentationVersionClass) } + + named("checkstyleMain") { + exclude("**/InstrumentationVersion.java") + } } fun computeInstrumentationName(): String { From 3b49453e89dda879fca8589cd06d627b043bd02e Mon Sep 17 00:00:00 2001 From: Lauri Tulmin Date: Fri, 8 May 2026 10:35:11 +0300 Subject: [PATCH 5/7] address review comments --- .../otel.instrumentation-version.gradle.kts | 31 ++++++++++++------- .../EmbeddedInstrumentationProperties.java | 23 +++++++++----- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/conventions/src/main/kotlin/otel.instrumentation-version.gradle.kts b/conventions/src/main/kotlin/otel.instrumentation-version.gradle.kts index 33a4794f96eb..27a9ce49ef36 100644 --- a/conventions/src/main/kotlin/otel.instrumentation-version.gradle.kts +++ b/conventions/src/main/kotlin/otel.instrumentation-version.gradle.kts @@ -1,9 +1,11 @@ -import kotlin.text.replace - plugins { `java-library` } +dependencies { + compileOnly("javax.annotation:javax.annotation-api:1.3.2") +} + tasks { val generateInstrumentationVersionFile by registering { val name = computeInstrumentationName() @@ -25,8 +27,18 @@ tasks { inputs.property("instrumentation.name", name) inputs.property("instrumentation.version", version) - val moduleName = name.replace("(-[0-9.]*)$".toRegex(), "").replace("-", "").replace("([0-9]+)\\.([0-9]+)".toRegex(), "$1_$2") - val baseVersion = name.replace(".*?([0-9.]*)$".toRegex(), "$1").replace(".", "_") + // The same logic is duplicated in EmbeddedInstrumentationProperties + // Strip trailing version suffix and remove dashes. + // If the module name contains a non-trailing version number e.g. jaxrs-3.0-jersey-3.0 replace + // the dot with underscore. This is needed to turn the module name into valid package name, java + // package name segments cannot start with a number. + val moduleName = name.replace("(-[0-9.]*)$".toRegex(), "") + .replace("-", "") + .replace("([0-9]+)\\.([0-9]+)".toRegex(), "$1_$2") + // Extract trailing version number and replace dots with underscores so it could be used as a + // package name segment. + val baseVersion = name.replace(".*?([0-9.]*)$".toRegex(), "$1") + .replace(".", "_") val packageName = moduleName + if (baseVersion.isNotEmpty()) ".v$baseVersion" else "" val classDir = layout.buildDirectory.dir("generated/instrumentationVersionClass/${packageName.replace('.', '/')}/internal") @@ -36,19 +48,14 @@ tasks { File(classDir.get().asFile, "InstrumentationVersion.java").writeText(""" package $packageName.internal; - // import javax.annotation.Generated; + import javax.annotation.Generated; /** Autogenerated class do not edit. */ - // @Generated("otel.instrumentation-version") + @Generated("otel.instrumentation-version") public final class InstrumentationVersion { public static final String VERSION = "$version"; - public InstrumentationVersion() {} - - @Override - public String toString() { - return VERSION; - } + private InstrumentationVersion() {} } """.trimIndent()) } diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/EmbeddedInstrumentationProperties.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/EmbeddedInstrumentationProperties.java index 0f6e0f63786c..e3256be395d3 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/EmbeddedInstrumentationProperties.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/EmbeddedInstrumentationProperties.java @@ -62,23 +62,30 @@ private static String loadVersion(String instrumentationName) { return version; } - private static final Pattern stripVersionSuffix = Pattern.compile("(-[0-9.]*)$"); - private static final Pattern normalizeVersion = Pattern.compile("([0-9]+)\\.([0-9]+)"); - private static final Pattern extractVersion = Pattern.compile(".*?([0-9.]*)$"); + private static final Pattern STRIP_VERSION_SUFFIX = Pattern.compile("(-[0-9.]*)$"); + private static final Pattern NORMALIZE_VERSION = Pattern.compile("([0-9]+)\\.([0-9]+)"); + private static final Pattern EXTRACT_VERSION = Pattern.compile(".*?([0-9.]*)$"); @Nullable private static String loadVersionFromClass(String instrumentationName) { + // The same logic is duplicated in otel.instrumentation-version + // Strip trailing version suffix and remove dashes String moduleName = - stripVersionSuffix.matcher(instrumentationName).replaceAll("").replace("-", ""); - moduleName = normalizeVersion.matcher(moduleName).replaceAll("$1_$2"); + STRIP_VERSION_SUFFIX.matcher(instrumentationName).replaceAll("").replace("-", ""); + // If the module name contains a non-trailing version number e.g. jaxrs-3.0-jersey-3.0 replace + // the dot with underscore. This is needed to turn the module name into valid package name, java + // package name segments cannot start with a number. + moduleName = NORMALIZE_VERSION.matcher(moduleName).replaceAll("$1_$2"); + // Extract trailing version number and replace dots with underscores so it could be used as a + // package name segment. String baseVersion = - extractVersion.matcher(instrumentationName).replaceAll("$1").replace(".", "_"); + EXTRACT_VERSION.matcher(instrumentationName).replaceAll("$1").replace(".", "_"); String packageName = moduleName + (baseVersion.isEmpty() ? "" : ".v" + baseVersion); try { Class clazz = Class.forName(packageName + ".internal.InstrumentationVersion"); - return clazz.getConstructor().newInstance().toString(); - } catch (Exception e) { + return clazz.getField("VERSION").get(null).toString(); + } catch (ReflectiveOperationException e) { return null; } } From a79c71f2dd5b874d0b8996c14762902d3399d180 Mon Sep 17 00:00:00 2001 From: Lauri Tulmin Date: Wed, 13 May 2026 13:02:46 +0300 Subject: [PATCH 6/7] add test --- instrumentation-api/build.gradle.kts | 1 + .../EmbeddedInstrumentationProperties.java | 6 ++-- ...EmbeddedInstrumentationPropertiesTest.java | 31 +++++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/internal/EmbeddedInstrumentationPropertiesTest.java diff --git a/instrumentation-api/build.gradle.kts b/instrumentation-api/build.gradle.kts index f22e94e0a33e..8c628eae27ed 100644 --- a/instrumentation-api/build.gradle.kts +++ b/instrumentation-api/build.gradle.kts @@ -22,6 +22,7 @@ dependencies { testImplementation("io.opentelemetry.javaagent:opentelemetry-testing-common") testImplementation("io.opentelemetry:opentelemetry-sdk-testing") testImplementation("io.opentelemetry:opentelemetry-exporter-common") + testImplementation(project(":instrumentation:okhttp:okhttp-3.0:library")) jmhImplementation(project(":instrumentation-api-incubator")) } diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/EmbeddedInstrumentationProperties.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/EmbeddedInstrumentationProperties.java index e3256be395d3..f23e8ea0b925 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/EmbeddedInstrumentationProperties.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/EmbeddedInstrumentationProperties.java @@ -66,8 +66,9 @@ private static String loadVersion(String instrumentationName) { private static final Pattern NORMALIZE_VERSION = Pattern.compile("([0-9]+)\\.([0-9]+)"); private static final Pattern EXTRACT_VERSION = Pattern.compile(".*?([0-9.]*)$"); + // visible for testing @Nullable - private static String loadVersionFromClass(String instrumentationName) { + static String loadVersionFromClass(String instrumentationName) { // The same logic is duplicated in otel.instrumentation-version // Strip trailing version suffix and remove dashes String moduleName = @@ -90,8 +91,9 @@ private static String loadVersionFromClass(String instrumentationName) { } } + // visible for testing @Nullable - private static String loadVersionFromProperties(String instrumentationName) { + static String loadVersionFromProperties(String instrumentationName) { String path = "META-INF/io/opentelemetry/instrumentation/" + instrumentationName + ".properties"; try (InputStream in = loader.getResourceAsStream(path)) { diff --git a/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/internal/EmbeddedInstrumentationPropertiesTest.java b/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/internal/EmbeddedInstrumentationPropertiesTest.java new file mode 100644 index 000000000000..67d075d421cf --- /dev/null +++ b/instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/internal/EmbeddedInstrumentationPropertiesTest.java @@ -0,0 +1,31 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.api.internal; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class EmbeddedInstrumentationPropertiesTest { + + @Test + void loadVersionFromClass() { + assertThat( + EmbeddedInstrumentationProperties.loadVersionFromClass("io.opentelemetry.okhttp-3.0")) + .isNotEmpty(); + assertThat(EmbeddedInstrumentationProperties.loadVersionFromClass("does-not-exist")).isNull(); + } + + @Test + void loadVersionFromProperties() { + assertThat( + EmbeddedInstrumentationProperties.loadVersionFromProperties( + "io.opentelemetry.okhttp-3.0")) + .isNotEmpty(); + assertThat(EmbeddedInstrumentationProperties.loadVersionFromProperties("does-not-exist")) + .isNull(); + } +} From 824508829f9aa24f8140d5f93a0c5cd58309a010 Mon Sep 17 00:00:00 2001 From: Lauri Tulmin Date: Wed, 13 May 2026 13:40:37 +0300 Subject: [PATCH 7/7] address review comments --- .../main/kotlin/otel.instrumentation-version.gradle.kts | 8 ++++++-- instrumentation-api/build.gradle.kts | 1 + .../api/internal/EmbeddedInstrumentationProperties.java | 3 ++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/conventions/src/main/kotlin/otel.instrumentation-version.gradle.kts b/conventions/src/main/kotlin/otel.instrumentation-version.gradle.kts index 27a9ce49ef36..4b79fc4593d7 100644 --- a/conventions/src/main/kotlin/otel.instrumentation-version.gradle.kts +++ b/conventions/src/main/kotlin/otel.instrumentation-version.gradle.kts @@ -17,7 +17,9 @@ tasks { outputs.dir(propertiesDir) doLast { - File(propertiesDir.get().asFile, "$name.properties").writeText("version=$version") + val outputDir = propertiesDir.get().asFile + outputDir.mkdirs() + File(outputDir, "$name.properties").writeText("version=$version") } } @@ -45,7 +47,9 @@ tasks { outputs.dir(classDir) doLast { - File(classDir.get().asFile, "InstrumentationVersion.java").writeText(""" + val outputDir = classDir.get().asFile + outputDir.mkdirs() + File(outputDir, "InstrumentationVersion.java").writeText(""" package $packageName.internal; import javax.annotation.Generated; diff --git a/instrumentation-api/build.gradle.kts b/instrumentation-api/build.gradle.kts index 8c628eae27ed..6149a3fcc0e1 100644 --- a/instrumentation-api/build.gradle.kts +++ b/instrumentation-api/build.gradle.kts @@ -22,6 +22,7 @@ dependencies { testImplementation("io.opentelemetry.javaagent:opentelemetry-testing-common") testImplementation("io.opentelemetry:opentelemetry-sdk-testing") testImplementation("io.opentelemetry:opentelemetry-exporter-common") + // used for testing embedded instrumentation properties testImplementation(project(":instrumentation:okhttp:okhttp-3.0:library")) jmhImplementation(project(":instrumentation-api-incubator")) diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/EmbeddedInstrumentationProperties.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/EmbeddedInstrumentationProperties.java index f23e8ea0b925..fc9612cd664f 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/EmbeddedInstrumentationProperties.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/EmbeddedInstrumentationProperties.java @@ -84,7 +84,8 @@ static String loadVersionFromClass(String instrumentationName) { String packageName = moduleName + (baseVersion.isEmpty() ? "" : ".v" + baseVersion); try { - Class clazz = Class.forName(packageName + ".internal.InstrumentationVersion"); + Class clazz = + Class.forName(packageName + ".internal.InstrumentationVersion", false, loader); return clazz.getField("VERSION").get(null).toString(); } catch (ReflectiveOperationException e) { return null;