Repository navigation
[improve][build] Upgrade OpenTelemetry to 1.65.0, instrumentation to 2.30.0, semconv to 1.43.0 - #26334
Merged
Conversation
…2.30.0, semconv to 1.43.0 - opentelemetry-java: 1.62.0 -> 1.65.0 - opentelemetry-java-instrumentation: 2.28.1 -> 2.30.0 - opentelemetry-semconv: 1.41.1 -> 1.43.0 - opentelemetry-gcp-resources (contrib): 1.57.0-alpha -> 1.59.0-alpha Also updates the binary LICENSE files for the server and shell distributions, including the transitive Prometheus client bump (1.5.1 -> 1.8.0) pulled in by opentelemetry-exporter-prometheus. Assisted-by: Claude Code (claude-opus-5)
nodece
approved these changes
Aug 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
Keep the OpenTelemetry stack current. Pulsar is several releases behind on every
OpenTelemetry artifact it consumes, which means it misses upstream bug fixes in the
Prometheus and OTLP exporters and drifts further from the semantic-convention baseline.
OpenTelemetry 1.65.0 also stops publishing
opentelemetry-exporter-zipkinand removes abatch of APIs that were deprecated in earlier releases, so staying on 1.62.0 only makes the
eventual upgrade larger.
Modifications
Version catalog (
gradle/libs.versions.toml):io.opentelemetry:opentelemetry-bom(and-bom-alpha)io.opentelemetry.instrumentation:opentelemetry-instrumentation-bom(and-bom-alpha)io.opentelemetry.semconv:opentelemetry-semconvio.opentelemetry.contrib:opentelemetry-gcp-resourcesopentelemetry-java-contrib1.59.0 is the release that targetsopentelemetry-java-instrumentation2.30.0, so the four stay aligned.opentelemetry-gcp-resourcesis a version-catalog entry only: it resolves on no module'sclasspath and is in no LICENSE file, but it is not inert --
pulsar-dependenciesturns everycatalog alias into a constraint, so it appears in the published BOM's
<dependencyManagement>.Instrumentation 2.30.0 is built against SDK 1.64.0 while Pulsar pins core at 1.65.0. That is a
forward pin (Gradle resolves
1.64.0 -> 1.65.0, no downgrade, no conflict), and it was checkedfor link-safety rather than assumed: all 796
io.opentelemetry.*class/method/field referencesin the four shipped instrumentation jars resolve against core 1.65.0 + semconv 1.43.0 with zero
missing classes and zero missing members.
Binary LICENSE files for the server and shell distributions were updated to match the bundled
jars. This includes the transitive Prometheus client bump (
io.prometheus:prometheus-metrics-*1.5.1 -> 1.8.0) that
opentelemetry-exporter-prometheusbrings in. No jar was added to orremoved from either distribution -- every change is a version rename.
No source changes were needed. Details below.
Deprecated code review
The build runs with
-Xlint:deprecation(pulsar.java-conventions.gradle.kts). After theupgrade,
./gradlew sanityCheckcompiles every module's main and test sources with zeroOpenTelemetry deprecation warnings -- while still reporting unrelated Netty and Pulsar
deprecations, so the lint is doing its job.
Individually checked against the release notes:
opentelemetry-exporter-zipkinunpublished (1.65.0) -- Pulsar never depended on it.PrometheusMetricReaderdeprecated constructors dropped (1.64.0) -- Pulsar usesPrometheusHttpServer, not those constructors.InstrumentationConfigUtil.peerServiceMappingremoved (1.64.0),ExtendedAttributesremoved (1.63.0),
ViewConfig/ViewConfigCustomizerremoved (1.63.0),otel.experimental.config.fileremoved (1.63.0), deprecated OTLP SPI property namesremoved (1.63.0) -- none are referenced anywhere in the repo.
does not use declarative config.
RuntimeTelemetryBuilder-- its public API is byte-for-byte the same in 2.28.1 and2.30.0, so
OpenTelemetryService's use ofio.opentelemetry.instrumentation.runtimetelemetry.internal.{Internal,Experimental}todisable JFR and enable experimental JMX metrics is still the only supported way to do that.
ServiceAttributes(SERVICE_NAME,SERVICE_VERSION), which is stable and unchanged in 1.43.0.
User-visible change: Prometheus series identity
Some label values change, so Prometheus assigns new series identities across the upgrade and
rate()/increase()see a one-time discontinuity at restart. All of these affect onlydeployments that enable the OpenTelemetry Prometheus exporter (
otel.sdk.disableddefaults totrue), and none warrants a code change -- changing what Pulsar emits to avoid them would itself
be a break.
1.
otel_scope_versionon thejvm.*runtime metrics:2.28.1-alpha->2.30.0-alpha.RuntimeTelemetryBuilder.getMeter()sets the meter's instrumentation version from the versionfile embedded in the
opentelemetry-runtime-telemetryjar, and the Prometheus exporter emitsotel_scope_name/otel_scope_versionby default. This is routine for any instrumentation bump.It is limited to the
jvm.*series: the broker and worker meters(
PulsarBrokerOpenTelemetry,PulsarWorkerOpenTelemetry) are built withgetMeter(name)andcarry no version at all, and while the client meter does set one
(
InstrumentProvider.java:46,PulsarVersion.getVersion()), that value is a Pulsar version andis unaffected by this dependency bump.
2. Colliding label names now merge instead of dropping. When two OpenTelemetry attribute
keys normalize to the same Prometheus label name, 1.62.0 kept a single value; 1.65.0 sorts the
original keys and joins their values with
;("Merge colliding Prometheus label values" in the1.65.0 notes). None of Pulsar's own 126 metric names or 61 attribute keys collide -- the
differential below would have shown it -- so this is only reachable via user-supplied colliding
keys, e.g.
OTEL_RESOURCE_ATTRIBUTES=foo.bar=a,foo-bar=bnow yieldingfoo_bar="a;b".3.
process_command_argslabel format -- described next.User-visible change:
process_command_argslabel formatOpenTelemetry 1.64.0 fixed serialization of array-valued resource and scope attributes in
the Prometheus exporter (open-telemetry/opentelemetry-java#8497).
Otel2PrometheusConverternow routes them through
toLabelValue()->toJsonStr(); before, the resource-attribute pathused a plain
Object.toString(). Array-valued labels therefore render as JSON:On a default deployment the only such label is
process_command_args, emitted byProcessResourceProvider(SPI-registered inopentelemetry-resources; itsemitCommandAttributes()gate returns true unlessotel.instrumentation.common.v3-previewisset, and Pulsar sets no
otel.instrumentation.*property). BecauseOpenTelemetryServicesets
setAllowedResourceAttributesFilter(s -> true), that label is copied onto everyexported series -- so Prometheus assigns new series identities to all Pulsar OpenTelemetry
metrics across the upgrade, and
rate()/increase()see a one-time discontinuity at restart.This makes per-metric labels consistent with
target_info, which already used the JSON form.This affects only deployments that enable the OpenTelemetry Prometheus exporter
(
otel.sdk.disableddefaults to true), and hosts where the JVM cannot resolve processarguments emit
process_command_line(a plain string) instead and are unaffected.There is nothing to fix in Pulsar -- changing what Pulsar emits would itself be a break -- so
this is documented rather than worked around. Whether Pulsar should keep copying
process.command_argsonto every series at all is a separate question (upstream warns theattribute may carry sensitive information, and it is a sizeable constant payload per series);
that would be a narrowing of
setAllowedResourceAttributesFilterin its own PR.Also undocumented upstream and worth knowing: setting
otel.instrumentation.common.v3-preview=truenow silently dropsprocess.command_line/process.command_argsunlessotel.instrumentation.resources.experimental.process-command-attributes.enabled=trueis alsoset. No default deployment is affected.
Metric compatibility
1.64.0/1.65.0 rewrote the Prometheus naming path:
Otel2PrometheusConvertergained aTranslationStrategyand replaced its use of the Prometheus client'sPrometheusNaminghelpers with hand-rolled normalization, and reserved-suffix stripping moved from the
Prometheus client into OpenTelemetry (Prometheus client 1.6.0 moved suffix handling from
metric-creation time to scrape time). Since Pulsar exposes these metrics to users, the
emitted names are backward-compatibility surface.
To confirm nothing changed, every one of Pulsar's 126 OpenTelemetry metric names was rendered
through the Prometheus exporter on both 1.62.0 and 1.65.0 -- crossed with all 26 unit strings
Pulsar passes to
setUnit()and all four instrument kinds, carrying all 61 Pulsar attributekeys as labels plus resource attributes with
setAllowedResourceAttributesFilter(s -> true)as
OpenTelemetryServicedoes. After normalizing thetelemetry_sdk_versionlabel and theprocess_command_argsformat change described above, the two 95,006-line exposition dumps arebyte-identical: metric names, label names, units, types and
_totalhandling are unchanged,and the array-attribute rendering is the only difference in the entire scrape. (The harness
uses its own instrumentation scope, so it deliberately does not cover the
otel_scope_versionchange on the
jvm.*metrics noted above; that one was verified from the jars' embedded versionfiles and
RuntimeTelemetryBuilder.getMeter().)The default strategy is
UNDERSCORE_ESCAPING_WITH_SUFFIXES, which is the pre-1.64.0 behavior,and
PrometheusMetricReaderProviderdoes not wiresetTranslationStrategyto any autoconfigureproperty, so it cannot be flipped by configuration either.
Two upstream changes touch code paths Pulsar uses but are inert here:
PrometheusHttpServer.toBuilder()dropping the default handler.OpenTelemetryServicecallstoBuilder()in itsaddMetricReaderCustomizer, butsetDefaultHandleris only ever called inside the exporter module's own tests --PrometheusMetricReaderProvidernever sets one -- so nothing was being dropped.OpenTelemetryServicesupplies (otel.sdk.disabled,otel.java.metrics.cardinality.limit,otel.java.exporter.memory_mode,otel.exporter.prometheus.host) all still exist verbatim at v1.65.0. A renamed propertywould have failed silently, so these were checked against the tagged source rather than
inferred from the changelog.
Verifying this change
This change is already covered by existing tests, such as
OpenTelemetryServiceTest,pulsar-broker'sorg.apache.pulsar.broker.stats.OpenTelemetry*suites,PrometheusMetricsTest, thepulsar-clientimpl.metrics/impl.tracingtests, and theCI - Integration - Metricssuite (OpenTelemetrySanityTest), which scrapes theOpenTelemetry Prometheus endpoint of a real broker. All green locally alongside
sanityCheck,quickCheckandcheckBinaryLicense, and green in full CI on the fork.Does this pull request potentially affect one of the following parts:
Dependencies: the four OpenTelemetry version-catalog entries above, plus the transitive
io.prometheus:prometheus-metrics-*1.5.1 -> 1.8.0 bump they pull in(
opentelemetry-exporter-prometheusdeclares 1.5.1 at 1.62.0-alpha and 1.8.0 at 1.65.0-alpha).The module set is unchanged -- six Prometheus jars in, six out -- so the LICENSE delta is
version-only. This is deliberately not pinned in the version catalog: the platform is
consumed as an
enforcedPlatform, so a catalog entry would override the exporter's own requestand could silently force a stale Prometheus client onto a newer exporter.
The metrics: no metric name, label name, unit or type changes. What changes is label
values --
otel_scope_versionon thejvm.*runtime metrics, theprocess_command_argsformat, and merging of colliding label names -- all described under "User-visible change" above.
Together they shift Prometheus series identity on deployments that enable the OpenTelemetry
Prometheus exporter.
Documentation
doc-requireddoc-not-neededdocdoc-completeMatching PR in forked repository
PR in forked repository: lhotari#256 -- all 43 checks green
(one
OneWayReplicatorTest.testMultipleVersionSchemasfailure on the first run was an unrelatedpre-existing teardown race --
waitReplicatorStopped()waits for the replicator while thedelete-guard reads the topic's replication clusters -- and passed on rerun; that test file
contains no OpenTelemetry references).