Background
The instrumentation documentation system generates docs/instrumentation-list.yaml by running test suites with -PcollectMetadata=true. Each test suite is annotated with a metadataConfig system property whose value becomes the when: key in the generated YAML, allowing readers to understand what telemetry is emitted under which configuration. For example:
systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database")
produces entries like:
telemetry:
- when: otel.semconv-stability.opt-in=database
spans: ...
The ideal model is: one test suite tagged default (no metadataConfig), plus one additional suite per non-default configuration. This lets a reader see both what they get out of the box and what each opt-in adds.
The controller-telemetry problem
Controller-span instrumentations require otel.instrumentation.common.experimental.controller-telemetry.enabled=true to emit anything; by default they are a no-op. Because there is nothing to observe without the flag, most of these modules run their tests with the flag unconditionally via jvmArgs, and they set metadataConfig to the controller-telemetry option so all telemetry is attributed to that condition.
This is largely correct for pure controller-span modules (e.g. struts-2.3, tapestry-5.4, spring-ws-2.0), but it creates three categories of problems across the ~36 affected modules.
Problem 1: Missing =true suffix in the when condition (spring-webflux-5.0)
spring-webflux-5.0/javaagent/build.gradle.kts sets:
systemProperty("metadataConfig", "otel.instrumentation.common.experimental.controller-telemetry.enabled")
// note: no =true
All other modules use the full form:
systemProperty("metadataConfig", "otel.instrumentation.common.experimental.controller-telemetry.enabled=true")
This causes the generated YAML to produce a non-standard when key:
# spring-webflux-5.0 (inconsistent)
- when: otel.instrumentation.common.experimental.controller-telemetry.enabled
# all other modules (correct)
- when: otel.instrumentation.common.experimental.controller-telemetry.enabled=true
Fix: Add =true to the metadataConfig value in spring-webflux-5.0/javaagent/build.gradle.kts and re-collect telemetry.
Problem 2: Default telemetry buried under the controller-telemetry condition (spring-webflux-5.0)
spring-webflux-5.0 instruments both the Spring WebFlux WebClient (HTTP client spans and http.client.request.duration metric) and controller spans. The WebClient telemetry is emitted by default regardless of whether controller-telemetry is enabled. However, the current test setup runs all tests with jvmArgs("-Dotel.instrumentation.common.experimental.controller-telemetry.enabled=true") and no default test suite, which causes the generated YAML to look like:
telemetry:
- when: otel.instrumentation.common.experimental.controller-telemetry.enabled # ← all telemetry is here
metrics:
- name: http.client.request.duration # ← actually emitted by default!
spans:
- span_kind: CLIENT # ← actually emitted by default!
- span_kind: INTERNAL # ← only emitted with the flag
A reader of this documentation would incorrectly conclude that http.client.request.duration and the CLIENT span require the controller-telemetry flag, when in fact they do not.
The same structure appears in the otel.semconv-stability.opt-in=service.peer combined condition entry.
Fix: Add a separate default test suite (no controller-telemetry jvmArg, no metadataConfig) to capture the WebClient telemetry on its own, then a dedicated controller-telemetry suite for the INTERNAL spans only. This separates the two concerns and gives an accurate picture of the default state.
Problem 3: Controller spans incorrectly attributed as default behavior (spring-webmvc-3.1, spring-webmvc-6.0)
spring-webmvc-3.1 and spring-webmvc-6.0 always set the controller-telemetry flag via jvmArgs on every test:
jvmArgs("-Dotel.instrumentation.common.experimental.controller-telemetry.enabled=true")
but they do not set a corresponding metadataConfig for that flag. The only named test suite is testExperimental, which tags telemetry under otel.instrumentation.spring-webmvc.experimental-span-attributes=true. This means the controller INTERNAL spans — which require the flag — land in the untagged (default) bucket:
telemetry:
- when: default # ← controller spans, which actually require the flag!
spans:
- span_kind: INTERNAL
attributes:
- name: code.function
- name: code.namespace
The description even says "controller spans are disabled by default", yet the telemetry shows them as default. This is a direct contradiction.
Fix: Add a dedicated testControllerTelemetry test suite with metadataConfig set to otel.instrumentation.common.experimental.controller-telemetry.enabled=true, and run the default suite without the controller-telemetry jvmArg so the default bucket accurately reflects out-of-the-box behavior (no controller spans).
Scope
Approximately 36 modules set otel.instrumentation.common.experimental.controller-telemetry.enabled=true as a jvmArg. They break down into three categories that need different treatment:
| Category |
Example modules |
Required action |
| Pure controller-span modules (no other default telemetry) |
struts-*, jaxrs-*, jsf-*, tapestry-5.4, spring-ws-2.0, jaxws-*, finatra-2.9 |
Verify metadataConfig includes =true; no default suite needed since there is nothing to emit by default |
| Mixed modules with independent default telemetry |
spring-webflux-5.0, ratpack-1.4 |
Add a default suite (no controller-telemetry) to capture non-controller telemetry; keep a controller-telemetry suite for the INTERNAL spans |
| Modules with controller spans leaking into default |
spring-webmvc-3.1, spring-webmvc-6.0 |
Split into a true default suite (no flag) and a named controller-telemetry suite |
Work to do
- Audit the
when values in instrumentation-list.yaml for all controller-telemetry entries — find any that are missing =true beyond spring-webflux-5.0.
- Cross-reference each affected module's
build.gradle.kts to determine which category above it falls into (pure controller-only vs. mixed vs. false-default).
- For mixed modules: identify what telemetry the non-controller instrumentation in the same module emits (HTTP client spans, metrics, etc.) and verify it belongs in the
when: default bucket.
- Fix test suites module by module
Background
The instrumentation documentation system generates
docs/instrumentation-list.yamlby running test suites with-PcollectMetadata=true. Each test suite is annotated with ametadataConfigsystem property whose value becomes thewhen:key in the generated YAML, allowing readers to understand what telemetry is emitted under which configuration. For example:produces entries like:
The ideal model is: one test suite tagged
default(nometadataConfig), plus one additional suite per non-default configuration. This lets a reader see both what they get out of the box and what each opt-in adds.The controller-telemetry problem
Controller-span instrumentations require
otel.instrumentation.common.experimental.controller-telemetry.enabled=trueto emit anything; by default they are a no-op. Because there is nothing to observe without the flag, most of these modules run their tests with the flag unconditionally viajvmArgs, and they setmetadataConfigto the controller-telemetry option so all telemetry is attributed to that condition.This is largely correct for pure controller-span modules (e.g.
struts-2.3,tapestry-5.4,spring-ws-2.0), but it creates three categories of problems across the ~36 affected modules.Problem 1: Missing
=truesuffix in thewhencondition (spring-webflux-5.0)spring-webflux-5.0/javaagent/build.gradle.ktssets:All other modules use the full form:
This causes the generated YAML to produce a non-standard
whenkey:Fix: Add
=trueto themetadataConfigvalue inspring-webflux-5.0/javaagent/build.gradle.ktsand re-collect telemetry.Problem 2: Default telemetry buried under the controller-telemetry condition (spring-webflux-5.0)
spring-webflux-5.0instruments both the Spring WebFlux WebClient (HTTP client spans andhttp.client.request.durationmetric) and controller spans. The WebClient telemetry is emitted by default regardless of whether controller-telemetry is enabled. However, the current test setup runs all tests withjvmArgs("-Dotel.instrumentation.common.experimental.controller-telemetry.enabled=true")and no default test suite, which causes the generated YAML to look like:A reader of this documentation would incorrectly conclude that
http.client.request.durationand the CLIENT span require the controller-telemetry flag, when in fact they do not.The same structure appears in the
otel.semconv-stability.opt-in=service.peercombined condition entry.Fix: Add a separate default test suite (no controller-telemetry jvmArg, no
metadataConfig) to capture the WebClient telemetry on its own, then a dedicated controller-telemetry suite for the INTERNAL spans only. This separates the two concerns and gives an accurate picture of the default state.Problem 3: Controller spans incorrectly attributed as default behavior (spring-webmvc-3.1, spring-webmvc-6.0)
spring-webmvc-3.1andspring-webmvc-6.0always set the controller-telemetry flag viajvmArgson every test:jvmArgs("-Dotel.instrumentation.common.experimental.controller-telemetry.enabled=true")but they do not set a corresponding
metadataConfigfor that flag. The only named test suite istestExperimental, which tags telemetry underotel.instrumentation.spring-webmvc.experimental-span-attributes=true. This means the controller INTERNAL spans — which require the flag — land in the untagged (default) bucket:The description even says "controller spans are disabled by default", yet the telemetry shows them as default. This is a direct contradiction.
Fix: Add a dedicated
testControllerTelemetrytest suite withmetadataConfigset tootel.instrumentation.common.experimental.controller-telemetry.enabled=true, and run the default suite without the controller-telemetry jvmArg so the default bucket accurately reflects out-of-the-box behavior (no controller spans).Scope
Approximately 36 modules set
otel.instrumentation.common.experimental.controller-telemetry.enabled=trueas a jvmArg. They break down into three categories that need different treatment:struts-*,jaxrs-*,jsf-*,tapestry-5.4,spring-ws-2.0,jaxws-*,finatra-2.9metadataConfigincludes=true; no default suite needed since there is nothing to emit by defaultspring-webflux-5.0,ratpack-1.4spring-webmvc-3.1,spring-webmvc-6.0Work to do
whenvalues ininstrumentation-list.yamlfor all controller-telemetry entries — find any that are missing=truebeyond spring-webflux-5.0.build.gradle.ktsto determine which category above it falls into (pure controller-only vs. mixed vs. false-default).when: defaultbucket.