Skip to content

Commit 30dbf5f

Browse files
authored
Merge branch 'main' into prefactor-db-info
2 parents ccf4871 + d63c7ad commit 30dbf5f

File tree

1,258 files changed

+5354
-5219
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,258 files changed

+5354
-5219
lines changed

.github/agents/code-review-and-fix.agent.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@ Auto-fix boundaries:
134134

135135
- Safe to fix:
136136
- import cleanup or direct style-guide conformance
137+
- normalization of existing `@SuppressWarnings` syntax or placement, but preserve any
138+
accurate explanatory comment attached to the suppression instead of deleting it as
139+
style noise
137140
- obvious assertion API migrations (e.g., AssertJ preference) and idiomatic
138141
simplifications listed in `testing-general-patterns.md` § AssertJ Idiomatic
139142
Simplifications (e.g., `assertThat(list.size()).isEqualTo(N)`
@@ -142,6 +145,8 @@ Auto-fix boundaries:
142145
lambdas where the lambda parameter is already an `AbstractAssert` (e.g.,
143146
`AbstractStringAssert`). Calls like `taskId.contains(jobName)` on the assert object are
144147
already valid AssertJ assertions; do not wrap them in `assertThat(...).isTrue()`
148+
- AssertJ `.as(...)` descriptions and `.withFailMessage(...)` in tests — remove them
149+
and prefer direct assertions whose failure output already exposes the unexpected values
145150
- deterministic semconv constant handling aligned with repository rules
146151
- missing test-task wiring patterns with clear canonical form
147152
- missing `testInstrumentation` cross-version references — when a javaagent module belongs
@@ -207,6 +212,16 @@ Auto-fix boundaries:
207212
to avoid allocating on every invocation. Only convert singletons used at
208213
registration/initialization time (e.g., `Instrumenter` builder chains, `Singletons`
209214
setup)
215+
- try-with-resources wrapping most of a test body for an `AutoCloseable` that only
216+
needs cleanup at test end — convert to `AutoCleanupExtension` with `deferCleanup(...)`.
217+
Add a `@RegisterExtension static final AutoCleanupExtension cleanup =
218+
AutoCleanupExtension.create();` field if one does not already exist, then replace
219+
the try-with-resources with `cleanup.deferCleanup(resource);` and un-indent the body.
220+
Keep try-with-resources for semantically scoped resources whose lifetime must end
221+
mid-test (e.g., `Scope` / `Context.makeCurrent()`, `MockedStatic`, short-lived
222+
readers/writers/streams/response bodies).
223+
Do not apply this conversion in non-JUnit helper methods, `@BeforeAll`, or shared
224+
setup code.
210225
- `hasAttributesSatisfying(...)` calls in test assertions — replace with
211226
`hasAttributesSatisfyingExactly(...)` because it is more precise (the non-exact
212227
variant silently ignores unexpected attributes)

.github/agents/knowledge/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ Load only files relevant to the current scope to reduce noise and avoid over-con
1313
| `general-rules.md` | Always — review checklist table and core rules enforced on every review |
1414
| `gradle-conventions.md` | `build.gradle.kts` or `settings.gradle.kts` changes, custom test task registration or wiring |
1515
| `javaagent-advice-patterns.md` | ByteBuddy `@Advice` class or advice-method changes |
16-
| `javaagent-module-patterns.md` | `InstrumentationModule`, `TypeInstrumentation`, `Singletons`, `VirtualField`, `CallDepth` |
16+
| `javaagent-module-patterns.md` | `InstrumentationModule`, `TypeInstrumentation`, `VirtualField`, `CallDepth` |
17+
| `javaagent-singletons-patterns.md` | `*Singletons` holder classes, singleton accessors, callers of singleton accessors/fields |
1718
| `library-patterns.md` | Library instrumentation telemetry, builder, getter, or setter pattern changes |
1819
| `module-naming.md` | New or renamed modules or packages; settings includes |
19-
| `testing-general-patterns.md` | Test files in scope — assertion style, attribute assertion patterns, `satisfies()` lambda usage |
20+
| `testing-general-patterns.md` | Test files in scope — assertion style, resource cleanup patterns, attribute assertion patterns, `satisfies()` lambda usage |
2021
| `testing-experimental-flags.md` | `testExperimental` task or experimental span-attribute assertions |
2122
| `testing-semconv-stability.md` | Semconv opt-in modes, `emitOld*`/`emitStable*`, `maybeStable`, Semconv test tasks |
2223

.github/agents/knowledge/config-property-stability.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ Defined in [VERSIONING.md](../../../VERSIONING.md):
5252
Examples (flat ↔ YAML):
5353

5454
- `otel.instrumentation.http.client.capture-request-headers``request_captured_headers`**stable**
55-
- `otel.instrumentation.common.experimental.db-sqlcommenter.enabled``sqlcommenter/development: { enabled: true }`**experimental**
55+
- `otel.instrumentation.common.db.experimental.sqlcommenter.enabled``sqlcommenter/development: { enabled: true }`**experimental**
5656
- `otel.instrumentation.http.client.emit-experimental-telemetry``emit_experimental_telemetry/development: true`**experimental**
5757

5858
## Deprecation Communication

.github/agents/knowledge/general-rules.md

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,15 @@ When a "Knowledge File" is listed, load it from `knowledge/` before reviewing th
1717
| Style | Style guide | Always ||
1818
| Style | Uppercase field names should reflect semantic constants or immutable value constants such as `Duration` timeouts/intervals, not simply `static final` | Always ||
1919
| Naming | Getter naming (`get` / `is`) | Always ||
20-
| Naming | Boolean/collection option naming (`set*Enabled`, `setCapture*`, `setEmitExperimental*`) | `Experimental` or `TelemetryBuilder` setter changes | `library-patterns.md` |
20+
| Style | Prefer `e` for used exceptions, prefer `f` for used exceptions in nested catch clauses when an outer catch already uses `e`, allow `error` for specific `*Error` catch types, prefer `t` for used `Throwable` catch values, prefer `ignored` for intentionally unused catch parameters, and use `ignore` for nested intentionally unused catch parameters when `ignored` would shadow an outer catch variable | Catch clauses | |
2121
| Naming | Module/package naming | New or renamed modules/packages | `module-naming.md` |
2222
| Javaagent | Advice patterns | `@Advice` classes | `javaagent-advice-patterns.md` |
23-
| Javaagent | Module structure patterns | `InstrumentationModule`, `TypeInstrumentation`, `Singletons` | `javaagent-module-patterns.md` |
23+
| Javaagent | Module structure patterns | `InstrumentationModule`, `TypeInstrumentation` | `javaagent-module-patterns.md` |
24+
| Javaagent | Singletons patterns | `*Singletons` holder classes, singleton accessors, callers of singleton accessors/fields | `javaagent-singletons-patterns.md` |
2425
| Javaagent | Incorrect `classLoaderMatcher()` | `classLoaderMatcher()` override that is redundant (muzzle already handles it) or missing when needed (muzzle cannot distinguish version range) | `javaagent-module-patterns.md` |
2526
| Semconv | Library vs javaagent semconv constant usage | Semconv constants/assertions ||
2627
| Semconv | Dual semconv testing | `SemconvStability`, `maybeStable`, semconv Gradle tasks | `testing-semconv-stability.md` |
27-
| Testing | General test patterns | Test files in scope | `testing-general-patterns.md` |
28+
| Testing | General test patterns | Test files in scope — assertion style, resource cleanup, attribute assertions | `testing-general-patterns.md` |
2829
| Testing | Experimental flag tests | `testExperimental`, experimental attribute assertions, `experimental` flags in JVM args or system properties | `testing-experimental-flags.md` |
2930
| Library | TelemetryBuilder/getter/setter patterns | Library instrumentation classes | `library-patterns.md` |
3031
| API | Deprecation and breaking-change policy | Public API changes | `api-deprecation-policy.md` |
@@ -56,6 +57,21 @@ Flag real defects, including:
5657

5758
Only flag substantive problems, not stylistic preference.
5859

60+
## [Javaagent] Best-Effort Suppressed Failures
61+
62+
When javaagent runtime code intentionally suppresses a `Throwable` to avoid breaking the
63+
application, do not silently swallow it.
64+
65+
- Prefer `ExceptionLogger.logSuppressedError(...)` for suppressed failures in javaagent code.
66+
It logs at `FINE` and matches the agent's default ByteBuddy advice suppression path in
67+
`ExceptionHandlers.defaultExceptionHandler()`.
68+
- Use this for best-effort hooks such as response customizers, bootstrap fallbacks, or other
69+
optional extension points where failure must not change application behavior.
70+
- Do not introduce ad-hoc local loggers for one-off suppressed javaagent failures when
71+
`ExceptionLogger` is available from bootstrap.
72+
- Keep the message action-oriented and specific (for example, "Failed to customize Netty 4.1
73+
HTTP server response").
74+
5975
## [Style] Style Guide
6076

6177
Read and apply `docs/contributing/style-guide.md`.
@@ -73,11 +89,44 @@ Do not flag the following patterns (common false positives):
7389
The project disables javac's `-Xlint:deprecation` globally and uses a custom Error Prone
7490
check (`OtelDeprecatedApiUsage`) instead. Only add the annotation when it is actually
7591
required to fix an Error Prone error — not speculatively.
92+
- Preserve concise explanatory comments attached to existing `@SuppressWarnings` annotations
93+
when they explain why the suppression exists (for example `// using deprecated semconv`
94+
or `// using deprecated config property`). This repository has established precedent for
95+
such comments, and test code disables Error Prone's `SuppressWarningsWithoutExplanation`
96+
check only because enforcing it everywhere causes too many failures, not because these
97+
comments are undesirable.
98+
- When normalizing or moving an existing `@SuppressWarnings`, keep any accurate explanatory
99+
comment with it. Remove the comment only if it is incorrect, obsolete, or contradicted by
100+
the code after your change.
76101

77102
## [Naming] Getter Naming
78103

79104
Public API getters should use `get*` (or `is*` for booleans).
80105

106+
## [Style] Catch Exception Variable Naming
107+
108+
Prefer `e` as the exception variable name in catch clauses when the exception is
109+
used.
110+
111+
For nested catch clauses, when an outer catch already uses `e`, prefer `f` as the
112+
inner exception variable name when the exception is used.
113+
114+
`error` is also acceptable when the caught type is a specific `*Error` subtype.
115+
116+
Prefer `t` for used `Throwable` catch values, including `catch (Throwable t)`.
117+
118+
Do not apply these catch-variable naming preferences to method parameters,
119+
lambda parameters, fields, or other non-catch identifiers.
120+
121+
If a catch parameter is intentionally unused, prefer `ignored` over `ignore`.
122+
123+
For nested catch clauses, if `ignored` would shadow an outer catch variable,
124+
prefer `ignore` for the inner intentionally unused catch parameter.
125+
126+
Both `ignored` and `ignore` are recognized by IntelliJ's `CatchMayIgnoreException`
127+
inspection as intentional unused-catch names. In test sources, IntelliJ also
128+
recognizes `expected` and `ok`.
129+
81130
## [Style] Prefer Instance Creation Over Singletons
82131

83132
Stateless implementations of telemetry interfaces — `TextMapGetter`, `TextMapSetter`,

.github/agents/knowledge/gradle-conventions.md

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,18 +51,37 @@ Verify `build.gradle.kts` applies the correct plugin for the module type:
5151
| `library/` | `otel.library-instrumentation` |
5252
| `testing/` | `otel.java-conventions` |
5353

54-
## Boolean Project Properties
54+
## Shared Gradle Project Properties
5555

56-
For simple boolean Gradle project properties in `build.gradle.kts`, prefer the repository's most
57-
common pattern:
56+
In `build.gradle.kts`, prefer the shared `otelProps` extension for repository-wide Gradle project
57+
properties that are already modeled there, including:
58+
59+
- `testLatestDeps`
60+
- `denyUnsafe`
61+
- `collectMetadata`
62+
- `testJavaVersion`
63+
- `testJavaVM`
64+
- `maxTestRetries`
65+
- `enableStrictContext`
66+
67+
Examples:
5868

5969
```kotlin
60-
val myFlag = findProperty("myFlag") == "true"
61-
if (myFlag) {
70+
if (otelProps.testLatestDeps) {
6271
// ...
6372
}
73+
74+
tasks.withType<Test>().configureEach {
75+
systemProperty("collectMetadata", otelProps.collectMetadata)
76+
}
6477
```
6578

79+
For module-local one-off properties that are not part of `otelProps`, using `findProperty(...)`
80+
directly is still fine.
81+
82+
`settings.gradle.kts` is the exception: it cannot use project extensions like `otelProps`, so
83+
direct `gradle.startParameter.projectProperties[...]` access is expected there.
84+
6685
## `testInstrumentation` Dependencies
6786

6887
The `testInstrumentation` configuration declares which other javaagent instrumentation modules
@@ -175,6 +194,9 @@ When a module has custom test tasks (e.g., `testStableSemconv`), system properti
175194
args that apply to **all** test tasks should be set once in a `withType<Test>().configureEach`
176195
block, not repeated on each individual task.
177196

197+
If a property or JVM arg is moved into `withType<Test>().configureEach`, remove any now-redundant
198+
copies from individual tasks unless a task intentionally overrides the shared value.
199+
178200
When there is only one test task, `tasks.test { ... }` is fine — do not convert it to
179201
`withType<Test>().configureEach` and do not flag it.
180202

@@ -186,7 +208,7 @@ should apply to all tasks. If so, move them to `withType<Test>().configureEach`.
186208
```kotlin
187209
tasks {
188210
withType<Test>().configureEach {
189-
systemProperty("collectMetadata", findProperty("collectMetadata"))
211+
systemProperty("collectMetadata", otelProps.collectMetadata)
190212
// ... other properties common to all test tasks
191213
}
192214

@@ -205,7 +227,7 @@ review**. Only verify correctness when they are already present.
205227

206228
| Property | Type | Value |
207229
| --- | --- | --- |
208-
| `collectMetadata` | System property | Pass-through of the `collectMetadata` Gradle project property; defaults to `"false"` |
230+
| `collectMetadata` | System property | Pass-through of `otelProps.collectMetadata`; defaults to `false` |
209231
| `metadataConfig` | System property | A single `key=value` string describing the non-default configuration active during this test run |
210232

211233
When already present, verify:

.github/agents/knowledge/javaagent-advice-patterns.md

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,95 @@ Because `none()` matches no methods, ByteBuddy never inlines this advice into an
172172
`suppress = Throwable.class` on such a method is entirely meaningless — **do not add it**,
173173
and **do not flag its absence** during review.
174174

175+
## AdviceScope Patterns
176+
177+
`AdviceScope` usage in this repository falls into **two justified state patterns**.
178+
Review new code against these patterns instead of treating every existing variation as equally
179+
canonical.
180+
181+
### Pattern 1 — Nullable `AdviceScope` for ordinary advice
182+
183+
Use this by default when enter advice may decide not to start instrumentation.
184+
185+
- `@Advice.OnMethodEnter` returns `@Nullable AdviceScope`
186+
- The factory method returns `null` when preconditions fail or instrumentation should not start
187+
- If an `AdviceScope` is created, its `Context` and `Scope` fields should be non-null
188+
- The `end()` method should close `scope` unconditionally; the null check belongs in exit advice,
189+
not inside `AdviceScope.end()`
190+
191+
In this document, use `start()` / `end()` as the canonical `AdviceScope` naming.
192+
For new code, prefer `start()` for the factory method and `end()` for the completion method.
193+
Do not introduce one-off names such as `create()` for ordinary `AdviceScope` factories.
194+
195+
Preferred shape:
196+
197+
```java
198+
public static class AdviceScope {
199+
private final Context context;
200+
private final Scope scope;
201+
202+
private AdviceScope(Context context) {
203+
this.context = context;
204+
this.scope = context.makeCurrent();
205+
}
206+
207+
@Nullable
208+
public static AdviceScope start(Request request) {
209+
Context parentContext = Context.current();
210+
if (!instrumenter().shouldStart(parentContext, request)) {
211+
return null;
212+
}
213+
Context context = instrumenter().start(parentContext, request);
214+
return new AdviceScope(context);
215+
}
216+
217+
public void end(@Nullable Throwable throwable) {
218+
scope.close();
219+
instrumenter().end(context, request, null, throwable);
220+
}
221+
}
222+
223+
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
224+
public static void onExit(
225+
@Advice.Thrown @Nullable Throwable throwable,
226+
@Advice.Enter @Nullable AdviceScope adviceScope) {
227+
if (adviceScope != null) {
228+
adviceScope.end(throwable);
229+
}
230+
}
231+
```
232+
233+
### Pattern 2 — Non-null placeholder `AdviceScope` for bookkeeping
234+
235+
Use a non-null `AdviceScope` with nullable internals only when exit advice still needs state even
236+
if no tracing scope was started, for example:
237+
238+
- `CallDepth` tracking
239+
- wrapped arguments or listeners that must be carried from enter to exit
240+
- other bookkeeping that must survive even on the "no span started" path
241+
242+
In this pattern, `AdviceScope` is the carrier object for control-flow state, not just tracing
243+
state. Nullable inner fields and exit guards are justified here.
244+
245+
### Do not use the defensive hybrid as a standard pattern
246+
247+
Avoid the hybrid shape where:
248+
249+
- `@Advice.Enter` is `@Nullable AdviceScope`
250+
- `AdviceScope` still stores a nullable `Scope`
251+
- `AdviceScope.end()` has an extra `if (scope == null)` guard
252+
253+
That double-guards the same condition and makes simple advice harder to reason about.
254+
If the helper that creates the context can return `null`, prefer returning `null` from
255+
`AdviceScope.start()` and only creating `AdviceScope` when the context is present.
256+
257+
### Async completion is not a separate `AdviceScope` state pattern
258+
259+
Async instrumentations may end spans from listeners, callbacks, or wrapped handlers instead of
260+
directly in method exit. That affects **where** the scope/span is completed, but it does not
261+
justify a third core `AdviceScope` state model. Async advice should still use either Pattern 1 or
262+
Pattern 2 as appropriate.
263+
175264
## Never Throw Exceptions in Javaagent Code
176265

177266
Javaagent instrumentations must never throw exceptions. The goal is to be invisible to the
@@ -193,3 +282,5 @@ the instrumentation automatically rather than letting it fail at runtime.
193282
- **`@Advice.OnMethodExit` method named `onEnter`** (or vice versa) — the method name should match the annotation. A mismatch is a copy-paste bug that compiles but confuses readers and may mask intent errors.
194283
- **Advice referenced in `transform()` using anything other than `getClass().getName() + "$InnerAdvice"`** — see `javaagent-module-patterns.md` for the canonical pattern. Flag `this.getClass().getName() + "$InnerAdvice"` as a redundant qualifier, and flag both `InnerAdvice.class.getName()` and `OuterInstrumentation.class.getName() + "$InnerAdvice"` because any `.class` literal in a `transform()` method triggers unwanted class loading.
195284
- **`onThrowable = Throwable.class` on return-only exit advice** — if the exit method only processes `@Advice.Return` and has no `@Advice.Enter` state to clean up, `onThrowable` should be omitted. The return value is `null`/zero on the exceptional path, and dereferencing it causes a suppressed exception for no benefit. Keep `suppress = Throwable.class` but remove `onThrowable`.
285+
- **One-off `AdviceScope` method naming** — for new ordinary advice, prefer `start()` / `end()` for `AdviceScope` methods, and avoid introducing unique names such as `create()`.
286+
- **Defensive hybrid `AdviceScope` in simple advice** — if `@Advice.Enter` is already nullable, flag simple advice that also stores a nullable inner `Scope`/`Context` and re-checks it inside `AdviceScope.end()`. Prefer returning `null` from the factory and keeping the created `AdviceScope` fully initialized.

0 commit comments

Comments
 (0)