Skip to content

Commit 285a4ed

Browse files
committed
backcompat
1 parent 891979c commit 285a4ed

6 files changed

Lines changed: 196 additions & 6 deletions

File tree

.github/agents/knowledge/api-deprecation-policy.md

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,23 @@
22

33
## Quick Reference
44

5-
- Use when: reviewing public API removals/renames, `@Deprecated` usage, stable-vs-alpha compatibility
6-
- Review focus: deprecate-then-remove timing, delegation direction, required Javadoc/CHANGELOG coverage
5+
- Use when: reviewing public API removals/renames, `@Deprecated` usage, stable-vs-alpha compatibility, or any module rename that touches user-facing config keys or emitted telemetry identity
6+
- Review focus: deprecate-then-remove timing, delegation direction, required Javadoc/CHANGELOG coverage, v3-preview gating for config keys and scope names
7+
8+
## What Counts as "Public API"
9+
10+
"API" here means **anything a user's code or configuration depends on by name**, including:
11+
12+
- Java symbols in published artifacts (classes, methods, fields in `:library`, `:testing`,
13+
`instrumentation-api*`).
14+
- User-facing configuration keys — `otel.instrumentation.<name>.enabled`, any
15+
`otel.instrumentation.*` property, and the equivalent declarative YAML keys.
16+
- Outgoing telemetry identity — anything users can match on in their backend, including
17+
`otel.scope.name`, span names, metric names, attribute keys, and attribute values. Users
18+
build dashboards, filters, and alerts on these values, so silently renaming them is a
19+
breaking change.
20+
21+
A rename of any of these surfaces is a breaking change even if no Java symbol moved.
722

823
## When Are Breaking Changes Allowed?
924

@@ -75,6 +90,74 @@ default void configure(IgnoredTypesBuilder builder, ConfigProperties config) {
7590
}
7691
```
7792

93+
## Module renames: config keys and emitted scope names
94+
95+
A module rename touches **two user-facing API surfaces** that must each be preserved by default
96+
and only change under `otel.instrumentation.common.v3-preview`. The mechanism differs because
97+
the surfaces are different: config keys can coexist as aliases, emitted scope names cannot.
98+
99+
### 1. `InstrumentationModule` names (controls `otel.instrumentation.<name>.enabled`)
100+
101+
The names passed to the `InstrumentationModule` constructor drive the
102+
`otel.instrumentation.<name>.enabled` config keys — any of them, not just the first. A rename
103+
silently breaks users who have the old key in their config.
104+
105+
Keep the pre-rename name by passing it inline alongside the current name using the
106+
{@code "<current>|deprecated:<old>"} marker recognized by the `InstrumentationModule`
107+
constructor:
108+
109+
```java
110+
public CxfInstrumentationModule() {
111+
super("cxf", "jaxws-2.0-cxf-3.0|deprecated:jaxws-cxf-3.0", "jaxws");
112+
}
113+
```
114+
115+
The framework (`DeprecatedInstrumentationNames.expand`) splits the marker and registers both
116+
names, so both `otel.instrumentation.jaxws-2.0-cxf-3.0.enabled` and
117+
`otel.instrumentation.jaxws-cxf-3.0.enabled` keep working (flat properties and YAML alike).
118+
Under `otel.instrumentation.common.v3-preview=true` the deprecated name is dropped; if the
119+
legacy key is explicitly set, a one-time WARNING is logged pointing at the new key.
120+
121+
No per-module `AgentCommonConfig` branching, `isV3Preview()` checks, or bespoke logging are
122+
needed — one string literal is the entire change.
123+
124+
### 2. Emitted instrumentation scope name (`INSTRUMENTATION_NAME` in `*Singletons`)
125+
126+
The `INSTRUMENTATION_NAME` string passed to `Instrumenter.builder(...)` becomes the
127+
`otel.scope.name` attribute on every emitted span / metric / log. A rename silently breaks
128+
dashboards and filters that match on the old scope.
129+
130+
Keep emitting the **pre-rename** scope name by default, and switch to the new one only under
131+
v3-preview:
132+
133+
```java
134+
public class CxfSingletons {
135+
private static final String INSTRUMENTATION_NAME =
136+
AgentCommonConfig.get().isV3Preview()
137+
? "io.opentelemetry.jaxws-2.0-cxf-3.0"
138+
// keep the pre-rename scope name so existing dashboards/filters on
139+
// otel.scope.name="io.opentelemetry.jaxws-cxf-3.0" continue to work
140+
: "io.opentelemetry.jaxws-cxf-3.0";
141+
...
142+
}
143+
```
144+
145+
Note the asymmetry with the config-key case: there the list carries **both** names at once
146+
(aliases); here only **one** scope name is emitted at a time, and the default is the **old**
147+
one. Do not log a deprecation warning here — users cannot switch the scope name without
148+
enabling v3-preview globally (which affects every module), so a warning would push them
149+
toward something they are not expected to enable generally.
150+
151+
### CHANGELOG
152+
153+
The rename is **not** a breaking change or a deprecation in the current release: by default
154+
the old config keys and scope names continue to work unchanged, and the new names are only
155+
visible under `otel.instrumentation.common.v3-preview` — a preview flag that users are not
156+
generally encouraged to enable. Do not add a `⚠️ Breaking changes to non-stable APIs` or
157+
`🚫 Deprecations` entry for this kind of rename. If mentioned at all, it belongs in whatever
158+
section tracks v3-preview changes. The breaking change will be recorded when v3-preview
159+
becomes the default in 3.0.
160+
78161
## What to Flag in Review
79162

80163
- **Breaking change without a prior deprecation**: a method/class was removed or its signature
@@ -98,3 +181,10 @@ default void configure(IgnoredTypesBuilder builder, ConfigProperties config) {
98181

99182
- **Missing CHANGELOG entry**: a breaking change PR that does not add an
100183
`⚠️ Breaking changes to non-stable APIs` bullet in the `Unreleased` section of `CHANGELOG.md`.
184+
185+
- **Module rename without backcompat**: `InstrumentationModule` constructor uses only the new
186+
name, dropping the pre-rename name that drove the legacy
187+
`otel.instrumentation.<old>.enabled` config key (it should be appended to the new name with
188+
the {@code "|deprecated:<old>"} marker so `DeprecatedInstrumentationNames` can gate it on
189+
v3-preview); and/or `*Singletons#INSTRUMENTATION_NAME` was changed unconditionally instead
190+
of being gated on `AgentCommonConfig.get().isV3Preview()`.

instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxws/v2_0/cxf/v3_0/CxfInstrumentationModule.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
package io.opentelemetry.javaagent.instrumentation.jaxws.v2_0.cxf.v3_0;
77

8+
import static io.opentelemetry.javaagent.extension.instrumentation.internal.DeprecatedInstrumentationNames.expandDeprecatedNames;
89
import static java.util.Collections.singletonList;
910

1011
import com.google.auto.service.AutoService;
@@ -14,8 +15,9 @@
1415

1516
@AutoService(InstrumentationModule.class)
1617
public class CxfInstrumentationModule extends InstrumentationModule {
18+
1719
public CxfInstrumentationModule() {
18-
super("cxf", "jaxws-2.0-cxf-3.0", "jaxws");
20+
super("cxf", expandDeprecatedNames("jaxws-2.0-cxf-3.0|deprecated:jaxws-cxf-3.0", "jaxws"));
1921
}
2022

2123
@Override

instrumentation/jaxws/jaxws-2.0-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxws/v2_0/cxf/v3_0/CxfSingletons.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,14 @@
77

88
import io.opentelemetry.api.GlobalOpenTelemetry;
99
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
10+
import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig;
1011
import io.opentelemetry.javaagent.bootstrap.internal.ExperimentalConfig;
1112

1213
public class CxfSingletons {
13-
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.jaxws-2.0-cxf-3.0";
14+
private static final String INSTRUMENTATION_NAME =
15+
AgentCommonConfig.get().isV3Preview()
16+
? "io.opentelemetry.jaxws-2.0-cxf-3.0"
17+
: "io.opentelemetry.jaxws-cxf-3.0";
1418

1519
private static final Instrumenter<CxfRequest, Void> instrumenter;
1620

instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxws/v2_0/metro/v2_2/MetroInstrumentationModule.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
package io.opentelemetry.javaagent.instrumentation.jaxws.v2_0.metro.v2_2;
77

8+
import static io.opentelemetry.javaagent.extension.instrumentation.internal.DeprecatedInstrumentationNames.expandDeprecatedNames;
89
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
910
import static java.util.Arrays.asList;
1011

@@ -16,8 +17,10 @@
1617

1718
@AutoService(InstrumentationModule.class)
1819
public class MetroInstrumentationModule extends InstrumentationModule {
20+
1921
public MetroInstrumentationModule() {
20-
super("metro", "jaxws-2.0-metro-2.2", "jaxws");
22+
super(
23+
"metro", expandDeprecatedNames("jaxws-2.0-metro-2.2|deprecated:jaxws-metro-2.2", "jaxws"));
2124
}
2225

2326
@Override

instrumentation/jaxws/jaxws-2.0-metro-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxws/v2_0/metro/v2_2/MetroSingletons.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,14 @@
77

88
import io.opentelemetry.api.GlobalOpenTelemetry;
99
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
10+
import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig;
1011
import io.opentelemetry.javaagent.bootstrap.internal.ExperimentalConfig;
1112

1213
public class MetroSingletons {
13-
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.jaxws-2.0-metro-2.2";
14+
private static final String INSTRUMENTATION_NAME =
15+
AgentCommonConfig.get().isV3Preview()
16+
? "io.opentelemetry.jaxws-2.0-metro-2.2"
17+
: "io.opentelemetry.jaxws-metro-2.2";
1418

1519
private static final Instrumenter<MetroRequest, Void> instrumenter;
1620

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.extension.instrumentation.internal;
7+
8+
import static java.util.Collections.singletonList;
9+
import static java.util.logging.Level.WARNING;
10+
11+
import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig;
12+
import java.util.ArrayList;
13+
import java.util.List;
14+
import java.util.logging.Logger;
15+
16+
/**
17+
* Helper for {@code InstrumentationModule}s whose names have been renamed: expands names containing
18+
* the inline marker {@code "<current>|deprecated:<old>"} into the current name followed by the
19+
* deprecated name.
20+
*
21+
* <p>Under {@code otel.instrumentation.common.v3-preview=true} the deprecated name is dropped, so
22+
* only the current name is registered. Otherwise, if the deprecated name has been explicitly set
23+
* via {@code otel.instrumentation.<old>.enabled} (flat properties or YAML), a warning is logged
24+
* pointing users at the current name.
25+
*
26+
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
27+
* at any time.
28+
*/
29+
public final class DeprecatedInstrumentationNames {
30+
31+
private static final Logger logger =
32+
Logger.getLogger(DeprecatedInstrumentationNames.class.getName());
33+
34+
private static final String DEPRECATED_MARKER = "|deprecated:";
35+
36+
/**
37+
* Expands names containing {@code "<current>|deprecated:<old>"} into the current name followed by
38+
* the deprecated name. Names without the marker are returned unchanged.
39+
*
40+
* <p>Under {@code otel.instrumentation.common.v3-preview=true} the deprecated name is dropped.
41+
* Otherwise, if the deprecated name has been explicitly set via {@code
42+
* otel.instrumentation.<old>.enabled} (flat properties or YAML), a warning is logged pointing
43+
* users at the current name.
44+
*/
45+
public static String[] expandDeprecatedNames(String... names) {
46+
boolean hasMarker = false;
47+
for (String name : names) {
48+
if (name.contains(DEPRECATED_MARKER)) {
49+
hasMarker = true;
50+
break;
51+
}
52+
}
53+
if (!hasMarker) {
54+
return names;
55+
}
56+
57+
boolean v3Preview = AgentCommonConfig.get().isV3Preview();
58+
AgentDistributionConfig config = AgentDistributionConfig.get();
59+
List<String> expanded = new ArrayList<>(names.length + 1);
60+
for (String name : names) {
61+
int idx = name.indexOf(DEPRECATED_MARKER);
62+
if (idx < 0) {
63+
expanded.add(name);
64+
continue;
65+
}
66+
String current = name.substring(0, idx);
67+
String deprecated = name.substring(idx + DEPRECATED_MARKER.length());
68+
expanded.add(current);
69+
if (v3Preview) {
70+
continue;
71+
}
72+
expanded.add(deprecated);
73+
List<String> probe = singletonList(deprecated);
74+
if (config.isInstrumentationEnabled(probe, true)
75+
!= config.isInstrumentationEnabled(probe, false)) {
76+
logger.log(
77+
WARNING,
78+
"otel.instrumentation.{0}.enabled is deprecated; "
79+
+ "use otel.instrumentation.{1}.enabled instead.",
80+
new Object[] {deprecated, current});
81+
}
82+
}
83+
return expanded.toArray(new String[0]);
84+
}
85+
86+
private DeprecatedInstrumentationNames() {}
87+
}

0 commit comments

Comments
 (0)