Skip to content

Generate class with instrumentation version#18600

Open
laurit wants to merge 8 commits into
open-telemetry:mainfrom
laurit:instrumentation-version-class
Open

Generate class with instrumentation version#18600
laurit wants to merge 8 commits into
open-telemetry:mainfrom
laurit:instrumentation-version-class

Conversation

@laurit

@laurit laurit commented May 5, 2026

Copy link
Copy Markdown
Contributor

Resolves #18495
Instrumenter sets instrumentation version based on the content of a properties files such as META-INF/io/opentelemetry/instrumentation/io.opentelemetry.okhttp-3.0.properties. Apparently on android reading the resource file is undesirable. This PR introduces generated InstrumentationVersion class files that also contain the instrumentation version. InstrumentationVersion class name is created based on the instrumentation name. We can do this since we verify that gradle module name matches instrumentation name, the existing version properties files also rely on this. To transform the instrumentation name into package name we apply some transformations, since the instrumentation name isn't necessarily a valid java package name. These transformations aren't nice. Initially I thought that perhaps we could replace the properties files with the class files but I think that for anybody who needs to add the version to their instrumentations properties files are easier to use.
Not completely convinced that this is worth it.

@laurit laurit changed the title Generate class with instrumetnation version Generate class with instrumentation version May 5, 2026
@trask trask mentioned this pull request May 5, 2026
@laurit laurit marked this pull request as ready for review May 7, 2026 18:01
@laurit laurit requested a review from a team as a code owner May 7, 2026 18:01

@trask trask left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not completely convinced that this is worth it.

I'm ok with it, I don't have a perspective on how important the issue is to solve, maybe @open-telemetry/android-approvers can chime in?

AI findings below...

Comment thread conventions/src/main/kotlin/otel.instrumentation-version.gradle.kts Outdated
Comment thread conventions/src/main/kotlin/otel.instrumentation-version.gradle.kts Outdated
@fractalwrench

Copy link
Copy Markdown
Member

@trask from an Android perspective it's certainly preferable to generate POJOs with constants at buildtime. Reading a META-INF file at runtime results in disk access which has the potential to block the UI thread, and therefore freeze the screen.

.replace(".", "_")
val packageName = moduleName + if (baseVersion.isNotEmpty()) ".v$baseVersion" else ""

val classDir = layout.buildDirectory.dir("generated/instrumentationVersionClass/${packageName.replace('.', '/')}/internal")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note that the package name will be something like io.opentelemetry.okhttp.v3_0 which doesn't get shaded

@laurit

laurit commented May 8, 2026

Copy link
Copy Markdown
Contributor Author

@trask have a look at https://medium.com/@NimbleDroid/why-is-classloader-getresourceasstream-so-slow-in-android-5f7c8583b63 it is an old article so idk whether anything has changed.

@LikeTheSalad LikeTheSalad left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I only added a recommendation for using a Gradle plugin to avoid doing all the Gradle task setup ourselves, which should help keep our code simpler.

outputs.dir(classDir)

doLast {
File(classDir.get().asFile, "InstrumentationVersion.java").writeText("""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd recommend using this plugin for creating this class: https://github.com/gmazzo/gradle-buildconfig-plugin - It's meant for these use cases.

@breedx-splk breedx-splk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this. I think I understand the desire to use a gradle plugin, but I am constantly paranoid these days about any new dependency...especially one in the build chain. 😱

I have been skeptical about these kinds of changes having any real-world impact. After all, a load is a load, and why would loading and interpreting some class bytes necessarily be better/faster than loading and interpreting a tiny property file? I have some vague theories, but I can't quite reason that out...sooooo.....

I needed to see it myself, so I build this thing to do some very low fi benchmarking, and I was pleasantly surprised. The results are considerably faster when loading classes over loading resource properties files.

https://github.com/breedx-splk/android_resource_loading_bench

@laurit

laurit commented May 13, 2026

Copy link
Copy Markdown
Contributor Author

I needed to see it myself, so I build this thing to do some very low fi benchmarking, and I was pleasantly surprised. The results are considerably faster when loading classes over loading resource properties files.

If I understand https://medium.com/@NimbleDroid/why-is-classloader-getresourceasstream-so-slow-in-android-5f7c8583b63 correctly to test it you should load the resource only once. Only the very first resource access via class loader should be slow, the rest should be significantly faster. You should make your test app large by including a ton of dependencies. The impact should be higher the larger your app is.

Copilot AI review requested due to automatic review settings May 13, 2026 10:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses Android StrictMode disk-read violations caused by loading instrumentation version from META-INF/...properties by introducing a generated InstrumentationVersion class per instrumentation and updating version resolution to prefer the class-based lookup (with a properties fallback).

Changes:

  • Add EmbeddedInstrumentationProperties.loadVersionFromClass(...) and update version resolution to prefer the generated class, falling back to the existing properties file.
  • Introduce a new Gradle convention plugin (otel.instrumentation-version) to generate both the version properties file and the InstrumentationVersion source for instrumentations.
  • Adjust javaagent/shaded packaging to avoid duplicate InstrumentationVersion.class conflicts; add a basic unit test for both class- and properties-based loading.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
javaagent/build.gradle.kts Excludes duplicate InstrumentationVersion.class entries when assembling the agent shadow jar.
instrumentation/r2dbc-1.0/library-instrumentation-shaded/build.gradle.kts Excludes InstrumentationVersion.class from extracted shaded contents to prevent duplication.
instrumentation/jdbc/library/build.gradle.kts Excludes InstrumentationVersion.class from extracted shaded contents to prevent duplication.
instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/internal/EmbeddedInstrumentationPropertiesTest.java Adds tests for loading version via generated class and via properties file.
instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/EmbeddedInstrumentationProperties.java Adds class-based version lookup (reflection) and makes version loading try class first, then properties.
instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/InstrumenterBuilder.java Defers instrumentation version lookup until tracer construction.
instrumentation-api/build.gradle.kts Adds an instrumentation module dependency for tests (to provide a real generated version class).
conventions/src/main/kotlin/otel.library-instrumentation.gradle.kts Applies the new otel.instrumentation-version generation plugin to library instrumentations.
conventions/src/main/kotlin/otel.javaagent-instrumentation.gradle.kts Applies the new otel.instrumentation-version generation plugin to javaagent instrumentations.
conventions/src/main/kotlin/otel.instrumentation-version.gradle.kts New plugin that generates the version properties file and an InstrumentationVersion Java source file.
conventions/src/main/kotlin/io.opentelemetry.instrumentation.base.gradle.kts Removes the older “generate version properties” logic from the base conventions plugin.

Comment thread conventions/src/main/kotlin/otel.instrumentation-version.gradle.kts
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"))
Comment thread conventions/src/main/kotlin/otel.instrumentation-version.gradle.kts Outdated
@breedx-splk

Copy link
Copy Markdown
Contributor

If I understand https://medium.com/@NimbleDroid/why-is-classloader-getresourceasstream-so-slow-in-android-5f7c8583b63 correctly to test it you should load the resource only once. Only the very first resource access via class loader should be slow, the rest should be significantly faster. You should make your test app large by including a ton of dependencies. The impact should be higher the larger your app is.

Yeah, old article, but I wonder if that's all still the case. I bet it is. There sure didn't seem to a huge or linear difference between first resource load and subsequent loads, so that makes sense. As the article suggests, having a huge APK should make it worse for the resource loading case. For the classloading case, are you suggesting that having a ton of dependencies would make the classpath much slower to search via Class.forName()?.... 🤔

I think that sounds reasonable. I tried the simple/small case, but it would be interesting to compare a fat classpath with a minimal resource set.

@laurit

laurit commented May 14, 2026

Copy link
Copy Markdown
Contributor Author

For the classloading case, are you suggesting that having a ton of dependencies would make the classpath much slower to search via Class.forName()?.... 🤔

No, only the first resource loading via the class loader should be slow.

@laurit laurit added this to the v2.29.0 milestone Jun 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Strict Mode Violation on version property read via getResourceAsStream from EmbeddedInstrumentationProperties in OkHttp library

6 participants