Add opt-in OSGi bundle metadata for selected instrumentation, API, and SDK extension artifacts#18995
Add opt-in OSGi bundle metadata for selected instrumentation, API, and SDK extension artifacts#18995royteeuwen wants to merge 11 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds OSGi bundle metadata generation for published library artifacts and introduces a dedicated :osgi-test module to verify that the produced bundles resolve and run in an Apache Felix OSGi container (including SPI Fly ServiceLoader mediation).
Changes:
- Introduce
otel.osgi-conventions+ newotelJavaextension knobs (osgiEnabled,osgiOptionalPackages) to generate OSGi manifest metadata via Bnd. - Add
:osgi-testintegration test module with multiple OSGi suites (api, apacheHttpClient, logbackAppender, resources). - Adjust specific instrumentation metadata (e.g., logback appender optional imports) and centralize OSGi test dependency versions.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| settings.gradle.kts | Registers the new :osgi-test module in the build. |
| osgi-test/build.gradle.kts | Adds Gradle+Bnd wiring to generate/resolve/run OSGi test suites under Felix/SPI Fly. |
| osgi-test/src/testApi/.../ApiOsgiTest.java | Verifies core API/annotations are usable inside OSGi. |
| osgi-test/src/testApacheHttpClient/.../ApacheHttpClientOsgiTest.java | Verifies Apache HttpClient telemetry bundle works in OSGi. |
| osgi-test/src/testLogbackAppender/.../LogbackAppenderOsgiTest.java | Verifies logback appender resolves without optional Logstash packages. |
| osgi-test/src/testResources/.../ResourcesOsgiTest.java | Verifies AutoService/SPI metadata is bridged to OSGi services via SPI Fly. |
| conventions/src/main/kotlin/otel.osgi-conventions.gradle.kts | New convention plugin applying Bnd bundle generation to published libraries. |
| conventions/src/main/kotlin/io/.../OtelJavaExtension.kt | Adds OSGi-related extension properties. |
| conventions/build.gradle.kts | Adds the Bnd Gradle plugin dependency used by the new convention. |
| dependencyManagement/build.gradle.kts | Pins versions for OSGi test runtime + JUnit/Bnd tester. |
| conventions/src/main/kotlin/otel.library-instrumentation.gradle.kts | Applies otel.osgi-conventions to library instrumentation modules. |
| conventions/src/main/kotlin/otel.sdk-extension.gradle.kts | Applies otel.osgi-conventions to SDK extension modules. |
| instrumentation-api/build.gradle.kts | Applies otel.osgi-conventions to instrumentation API. |
| instrumentation-api-incubator/build.gradle.kts | Applies otel.osgi-conventions to incubator API. |
| instrumentation-annotations/build.gradle.kts | Applies otel.osgi-conventions to annotations module. |
| instrumentation-annotations-support/build.gradle.kts | Applies otel.osgi-conventions to annotations support module. |
| instrumentation/logback/.../library/build.gradle.kts | Marks Logstash packages as optional OSGi imports. |
Pull request dashboard statusStatus last refreshed: 2026-07-23 23:33:51 UTC.
This automated status or its linked feedback items may be incorrect. If something looks wrong, please report it with the result you expected. |
|
|
||
| init { | ||
| minJavaVersionSupported.convention(JavaVersion.VERSION_1_8) | ||
| osgiEnabled.convention(true) |
There was a problem hiding this comment.
Some library instrumentations add classes to the same package as instrumented library. These probably won't work even with the osgi manifests. Should we disable osgi manifest for these instrumentations? Maybe it isn't worth the effort.
There was a problem hiding this comment.
Good catch. Five library instrumentations put classes in the instrumented library's own package to reach package-private members or class-loader hooks, and those can't work across separate OSGi bundle class loaders:
grpc-1.6—io.grpc.override.ContextStorageOverride(gRPC loads it by name from its own class loader)lettuce-5.1—io.lettuce.core.protocol.OtelCommandArgsUtil(package-private field access)elasticsearch-rest-7.0—org.elasticsearch.client.RestClientPackageAccess(package-privateLookupfor the proxy)nats-2.17—io.nats.client.impl.OpenTelemetryDispatcherFactory(extends package-privateDispatcherFactory)rxjava-1.0—rx.OpenTelemetryTracingUtil(package-privateonSubscribefield)
I've set osgiEnabled = false for all five so they publish a plain manifest rather than a bundle that resolves but then fails at runtime. lettuce and rxjava could be reworked to use reflection and re-enabled later; grpc/nats/elasticsearch would need upstream changes to the instrumented libraries. Done in 3d89b3d.
(Also fixed the plugin so osgiEnabled = false actually removes bnd's buildBundle action from the jar task — previously it only skipped adding the bnd config.)
|
@laurit anything else I can do to make this mergeable? |
|
hi @royteeuwen! how many of the modules are you (or people you have heard from) using in osgi? It might be better to make it opt-in instead of opt-out, and only opt-in to the ones actually tested via the smoke test, currently I think these? :instrumentation-api |
Probably too much of a hassle. It is likely that many of the library instrumentations will work and even if the don't it is not a big deal. I guess the biggest argument for going opt in would be all libraries don't have osgi metadata so us adding it to the corresponding instrumentation library won't help much. |
Generate OSGi bundle metadata (via the bnd gradle plugin) for every published io.opentelemetry.instrumentation library: the instrumentation-api/annotations modules, all library instrumentations, and SDK extensions. This lets them be consumed directly in OSGi runtimes, replacing external wrapper bundles. - conventions: add bnd dependency, osgiEnabled/osgiOptionalPackages on OtelJavaExtension, and a new otel.osgi-conventions plugin applied via otel.library-instrumentation, otel.sdk-extension and the four API modules. Javaagent/shadowed artifacts are intentionally left untouched. - SPI capabilities are auto-generated from META-INF/services (-metainf-services: auto). - osgi-test: new module that boots Apache Felix + Aries SPI Fly and runs JUnit 5 tests inside the container (api, logbackAppender, resources suites).
…che OSGi bundles The apache-httpclient-4.3 suite now boots Felix and runs ApacheHttpClientTelemetry against the stock org.apache.httpcomponents.httpclient/httpcore OSGi bundles - the same bundles AEM/Sling provide. They are pulled non-transitively because their POMs also drag in the plain (non-OSGi) httpclient/httpcore jars, which otherwise shadow the bundles in the resolver and leave org.apache.http unexported. All four suites (api, apacheHttpClient, logbackAppender, resources) now pass fully in-container.
…assert concrete class resource in logback test
… for split-package modules Rename the :osgi-test module to :smoke-tests-osgi to match the existing smoke-tests and smoke-tests-otel-starter naming, and move it under the smoke tests section in settings.gradle.kts. Disable OSGi bundle metadata (osgiEnabled = false) for the five library instrumentations that place classes in the instrumented library's own package (grpc-1.6, lettuce-5.1, elasticsearch-rest-7.0, nats-2.17, rxjava-1.0). These rely on package-private access or class-loader hooks that cannot work across separate OSGi bundle class loaders, so advertising OSGi support would produce bundles that resolve but fail at runtime. Make the osgi-conventions plugin remove the bnd buildBundle action when osgiEnabled is false, so opted-out modules get a plain manifest.
Instead of generating OSGi bundle metadata for every published library, API and SDK extension artifact (opt-out), apply otel.osgi-conventions explicitly only to modules verified to work as OSGi bundles by the :smoke-tests-osgi suites: - instrumentation-api - instrumentation-api-incubator - instrumentation-annotations - instrumentation-annotations-support - apache-httpclient-4.3:library - logback-appender-1.0:library - resources:library Plugin application is now the opt-in switch, so the osgiEnabled toggle and the per-module osgiEnabled=false overrides for split-package libraries (grpc, lettuce, elasticsearch-rest, nats, rxjava) are removed; those modules simply don't apply the convention.
|
@trask @laurit thanks both. I went with the opt-in approach. OSGi is now opt-in per module: a module is only published as a bundle if it explicitly applies
This also covers the real-world usage I have (my AEM/Sling deployment consumes the api, api-incubator and apache-httpclient bundles), so the tested set and the actually-used set line up. Because plugin application is now the opt-in switch, the Verified locally: the 7 modules above build with Also rebased onto |
- Root instrumentation-api-incubator and instrumentation-annotations-support in the api OSGi suite's -runrequires so they are actually resolved in the container (previously available to the resolver but omitted, since no test imports their packages). Also add annotations-support to the suite so its bundle is exercised. - Fix the smoke-test -runsystempackages/-runpath matching to match the artifact name exactly instead of by prefix: 'opentelemetry-semconv' was also matching 'opentelemetry-semconv-incubating' (pulled in by annotations-support), which emitted a duplicate io.opentelemetry.semconv system package and an ambiguous -runpath entry that broke the framework launch. - Close the CloseableHttpClient in ApacheHttpClientOsgiTest with try-with-resources. - Remove the hand-written CHANGELOG entry: per CONTRIBUTING.md the changelog is generated from merged PRs and entries are reserved for deprecations and breaking changes.
- Broaden the logback-appender OSGi import ranges to match the versions the library actually supports at runtime: the bundle is compiled against Logback 1.3 / SLF4J 2.0 but works on Logback 1.0 / SLF4J 1.6.4 (newer APIs guarded via reflection). Widen ch.qos.logback.* to [1.0,2) and org.slf4j to [1.6,3), and make org.slf4j.event (SLF4J 2.0+ only) optional so its absence on older SLF4J does not block resolution. Adds a new osgiImportPackages extension knob for explicit per-module Import-Package clauses. - Add a resolve-only logbackAppenderLegacy smoke suite proving the appender bundle resolves against Logback 1.2.13 / SLF4J 1.7.36 (where org.slf4j.event 2.0 is absent) - would fail if that import were still mandatory. - Skip the bnd Resolve/TestOSGi tasks under -PskipTests=true. The root build only disables Test-typed tasks, so previously the CI build-without-tests phase still booted all OSGi containers.
ResourceComponentProvider references io.opentelemetry.api.incubator.config (DeclarativeConfigProperties), but opentelemetry-api-incubator is a compileOnly optional dependency. bnd emitted a mandatory import for that package, so the entire resources bundle - including its plain ResourceProviders - failed to resolve in an OSGi runtime without the optional declarative-config stack. Mark io.opentelemetry.api.incubator optional, mirroring how the core SDK handles the same optional dependency. Add a resolve-only resourcesNoIncubator smoke suite that excludes opentelemetry-api-incubator and proves the bundle still resolves - it would fail if that import were still mandatory. The existing resources suite (with the incubator present) is unchanged.
trask
left a comment
There was a problem hiding this comment.
Produced from a local AI review...
…SGi bundle InternalInstrumenterCustomizerUtil looks up an instrumentation-api-incubator class via Class.forName and tolerates its absence, but bnd emitted a mandatory import for io.opentelemetry.instrumentation.api.incubator.instrumenter.internal (it fell through to the * clause). The stable instrumentation-api bundle could therefore not resolve without instrumentation-api-incubator. Mark that package optional. The upstream io.opentelemetry.api.incubator.* imports stay mandatory - instrumentation-api genuinely requires opentelemetry-api-incubator. Add a resolve-only apiNoIncubator smoke suite that resolves instrumentation-api with instrumentation-api-incubator absent - it would fail if that import were still mandatory. The api suite always roots the incubator bundle and masked it.
|
Thanks for the detailed review @trask! Went through all three: 1. 2. semconv system-package masks a deployment prerequisite — correct, and this is the real gate. 3. |
Resolves #18884.
With open-telemetry/opentelemetry-java#8417 the core modules gained OSGi support. This adds the same, opt-in, for a selected set of artifacts published by this repo.
What this does
Generates OSGi bundle metadata (via the bnd Gradle plugin) so the artifacts can be consumed directly in OSGi runtimes (e.g. Apache Felix / Eclipse Equinox, AEM/Sling) without external wrapper bundles.
OSGi support is opt-in per module: applying the new
otel.osgi-conventionsplugin is the switch, and it is applied only to modules that are verified to work as bundles by the:smoke-tests-osgisuites:instrumentation-apiinstrumentation-api-incubatorinstrumentation-annotationsinstrumentation-annotations-supportinstrumentation:apache-httpclient:apache-httpclient-4.3:libraryinstrumentation:logback:logback-appender-1.0:libraryinstrumentation:resources:libraryOther library instrumentations and SDK extensions are intentionally left as plain jars for now — most of the value is in the API/SDK artifacts, and library bundles are only useful when the instrumented library itself ships OSGi metadata. Library instrumentations that add classes to the instrumented library's own package (e.g. grpc, lettuce, elasticsearch-rest, nats, rxjava) can't work across separate OSGi bundle class loaders and so are deliberately excluded. Javaagent and shadowed artifacts are untouched.
How
biz.aQute.bnd:biz.aQute.bnd.gradle:7.3.0) to the convention build, mirroringopentelemetry-java.otel.osgi-conventionsconvention plugin configures thejartask's bndbundleblock:-exportcontents: io.opentelemetry.*, anImport-Packagethat makesjavax.annotation(and per-moduleosgiOptionalPackages) optional, and-metainf-services: autoso SPI providers inMETA-INF/services(including AutoService-generated ones like theresourcesResourceProviders) become OSGiProvide-Capabilityheaders.OtelJavaExtension.osgiOptionalPackageslets a module mark extra imports optional. Because plugin application is the opt-in switch, there is noosgiEnabledflag and no per-module opt-out.Tests
New
smoke-tests-osgimodule that boots a real OSGi container (Apache Felix + Aries SPI Fly) and runs JUnit 5 tests inside it, via bnd'sBundle→Resolve→TestOSGitasks (modeled onopentelemetry-java'sintegration-tests/osgi). Suites:instrumentation-api+-api-incubator+-annotations+-annotations-support: context propagation, builds anInstrumenter, references@WithSpan. The incubator and annotations-support bundles are rooted in-runrequiresso their generated wiring is resolved even though no test class imports them.ApacheHttpClientTelemetryresolved against the stock Apachehttpclient-osgi/httpcore-osgibundles.osgiOptionalPackagestuning).ResourceProviders are mediated by SPI Fly (validating-metainf-services: auto).All four suites pass in-container, and each opted-in module is verified to produce a valid bundle (
Bundle-SymbolicName+Export-Package).Notes
osgiOptionalPackagescurrently tunes the logback appender's optional Logstash import; a future opted-in module may need similar per-module tuning, which surfaces as a clear OSGi resolution error (never silent).httpclient-osgi/httpcore-osginon-transitively because their POMs also drag in the plain (non-OSGi)httpclient/httpcorejars, which otherwise shadow the bundles in the resolver.