Skip to content

feat(context): prototype context-scoped attributes (#8532) - #8947

Open
rooks-ns wants to merge 2 commits into
open-telemetry:mainfrom
rooks-ns:feat/context-scoped-attributes
Open

rooks-ns wants to merge 2 commits into
open-telemetry:mainfrom
rooks-ns:feat/context-scoped-attributes

Conversation

@rooks-ns

@rooks-ns rooks-ns commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Overview

Prototypes Context-scoped attributes (OTEP #4931, #8532), allowing Traces, Logs, and synchronous Metrics to automatically inherit attributes from the active Context without manually adding them at every call site.


Key Decisions (Spec Gaps Resolved)

  • Bound Instruments ignore Context Attributes:
    • Gap: OTEP Prepare release 1.20.0 #4931 omitted bound instruments because core specs removed bind(), but Java incubator retains it.
    • Resolution: Bound handles (BoundStorageHandle) record only to their pre-bound timeseries with zero runtime attribute merging. This guarantees zero allocations on hot paths and prevents cardinality explosion on shared infrastructure (e.g., connection pools). Unbound instruments continue to merge context attributes dynamically.
  • setNoParent() preserves Context Attributes:
    • Gap: setNoParent() historically reset context to Context.root(), unintentionally deleting context attributes and baggage.
    • Resolution: Replaced Context.root() with current.with(Span.getInvalid()). This detaches the parent span while keeping context attributes and baggage available for Sampler and SpanProcessor. Use setParent(Context.root()) for full isolation.

Signal Implementation Summary

  • API (api:incubator): ContextAttributes.add(Context, Attributes) (merges into context) and get(Context) (SDK reader).
  • Precedence: Attributes set directly on a span, log, or metric measurement always override context attributes on key collisions.
  • Trace (sdk:trace): Merges context attributes before Sampler.shouldSample(); unifies context across sampling, span creation, and SpanProcessor.onStart().
  • Logs (sdk:logs): Merges on emit() into an isolated record attributes map to prevent attribute leakage when reusing LogRecordBuilder.
  • Metrics (sdk:metrics): Merges context attributes on unbound instruments before view filtering; bypasses canUseRootContext optimization when active.
  • Config: Disabled by default. Enabled programmatically via Sdk*ProviderUtil or declaratively via context_scoped_attributes/development.

Verification

  • Trace: ContextScopedAttributesSpanTest (attributes, precedence, limits, and setNoParent() context preservation) & SdkSpanBuilderTest (legacy compatibility).
  • Metrics: ContextScopedAttributesMetricTest (dynamic unbound enrichment, bound handle isolation, same-context co-existence).
  • Logs: ContextScopedAttributesLogTest (merging, collision precedence, builder reuse).
  • Integration & Static Analysis: ContextScopedAttributesIntegrationTest (multi-signal E2E); clean runs across spotless, checkstyle, jApiCmp, and animalsniffer.

@opentelemetry-pr-dashboard

opentelemetry-pr-dashboard Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Pull request dashboard status

Waiting on the author · refreshed 2026-10-29 18:51 UTC

Respond to 8 review items (e.g. link a commit, explain why not, ask a follow-up):

  • Inline threads: 1, 2, 3, 4, 5, 6, 7, 8
Status above doesn't look right?
  • Just replied or pushed? Anything around or after the refresh time above may not be picked up yet — give it a few minutes.
  • Should this be with reviewers? Comment /dashboard route:reviewers to route it to them.
  • Anything wrong — including the routing? Report it with what you expected; it helps us improve the dashboard.

@codecov

codecov Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.02564% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.45%. Comparing base (877820b) to head (ef1468f).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...ava/io/opentelemetry/sdk/trace/SdkSpanBuilder.java 76.47% 1 Missing and 3 partials ⚠️
...metry/sdk/logs/internal/SdkLoggerProviderUtil.java 75.00% 2 Missing ⚠️
...try/sdk/metrics/internal/SdkMeterProviderUtil.java 75.00% 2 Missing ⚠️
...etry/sdk/trace/internal/SdkTracerProviderUtil.java 75.00% 2 Missing ⚠️
...io/opentelemetry/sdk/logs/SdkLogRecordBuilder.java 92.30% 0 Missing and 1 partial ⚠️
.../opentelemetry/sdk/metrics/AbstractInstrument.java 50.00% 0 Missing and 1 partial ⚠️
...in/java/io/opentelemetry/sdk/metrics/SdkMeter.java 50.00% 0 Missing and 1 partial ⚠️
...trics/internal/state/SynchronousMetricStorage.java 0.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #8947      +/-   ##
============================================
+ Coverage     90.41%   90.45%   +0.03%     
- Complexity    10981    11033      +52     
============================================
  Files          1019     1021       +2     
  Lines         30016    30160     +144     
  Branches       3917     3937      +20     
============================================
+ Hits          27140    27282     +142     
+ Misses         1923     1915       -8     
- Partials        953      963      +10     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@rooks-ns
rooks-ns force-pushed the feat/context-scoped-attributes branch from 403a57c to 7d8f465 Compare October 13, 2026 22:24
@rooks-ns
rooks-ns marked this pull request as ready for review October 14, 2026 18:25
@rooks-ns
rooks-ns requested a review from a team as a code owner October 14, 2026 18:25
@rooks-ns
rooks-ns force-pushed the feat/context-scoped-attributes branch 3 times, most recently from 3b2ce44 to e7872d7 Compare October 14, 2026 21:45
@rooks-ns
rooks-ns marked this pull request as draft October 18, 2026 12:22
@rooks-ns
rooks-ns force-pushed the feat/context-scoped-attributes branch from e7872d7 to 657d862 Compare October 19, 2026 23:48
@rooks-ns
rooks-ns marked this pull request as ready for review October 20, 2026 14:20
Implements prototype for Context-scoped attributes per OTEP open-telemetry#4931 and Issue open-telemetry#8532:
- Add ContextAttributes API in :api:incubator for setting/getting/extracting attributes on Context
- Support opt-in context-scoped attributes configuration across SdkTracerProvider, SdkLoggerProvider, and SdkMeterProvider
- Inherit context attributes in SdkSpanBuilder, SdkLogRecordBuilder, and synchronous metric storage (Delta & Cumulative)
- Expose configuration via declarative-config (tracer_provider, logger_provider, meter_provider)
- Add comprehensive unit, incubating, and multi-signal integration tests
@rooks-ns
rooks-ns force-pushed the feat/context-scoped-attributes branch from 657d862 to ef1468f Compare October 22, 2026 16:49
@rooks-ns

Copy link
Copy Markdown
Contributor Author

@jack-berg Please take a look . Thank you.

if (context == null) {
return Attributes.empty();
}
Attributes attributes = context.get(CONTEXT_ATTRIBUTES_KEY);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see usefulness in this feature but I find it hard to understand how to decide with instrumentations should include the attributes.

How to assert or qualify the carnality of the attribute?
Which instrumentations should skip an attribute?
Should we have a handler to decide which instrumentations opt to an attribute?
Are there attributes that make no sense across an entire context lifespan?

@jack-berg jack-berg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty good stuff - close to what I expected. Left a few comments but nothing should be too tricky.

private LoggerProviderModelAccessor() {}

static final String LOGGER_CONFIGURATOR = "logger_configurator/development";
static final String CONTEXT_SCOPED_ATTRIBUTES = "context_scoped_attributes/development";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are generated from the JSON schema defined in opentelemetry-configuration. We'll have to skip declarative config support until someone goes and defines the config surface area in the schema.

* Utility for associating {@link Attributes} with a {@link Context} for automatic propagation to
* telemetry signals produced within that context.
*/
public final class ContextAttributes {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The otep was merged for this, but there has been no attempt yet to codify the otep in a spec PR. This repo is bound to content in the spec (otep != spec). I'm going to make a judgement call here and go ahead with this on the basis of the otep, but it can't live on indefinitely if nobody makes progress in the spec.

I want there to be a comment here reminding us of this.

Suggested change
public final class ContextAttributes {
// TODO (jack-berg): delete after 2027-07-01 if progress hasn't been made on https://github.com/open-telemetry/opentelemetry-java/issues/8532
public final class ContextAttributes {

* SdkLoggerProviderUtil#setContextScopedAttributesEnabled(SdkLoggerProviderBuilder, boolean)}.
*/
SdkLoggerProviderBuilder setContextScopedAttributesEnabled(
boolean contextScopedAttributesEnabled) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know the otep talks about configuration as a boolean, but this is not going to be the final form. Its not expressive enough to handle cases where you have high and low cardinality attributes, and you want to include the low cardinality attribute on all signals and the high cardinality attributes only on spans and logs.

I'd like to start with a BiPredicate<InstrumentationScopeInfo, AttributeKey<?>>. This should give you expressiveness where you want / need it, without compromising convenience since you can just configure (unused, unused) -> true just as easily as true, and shouldn't impact performance.

if (!contextAttributes.isEmpty()) {
AttributesMap mergedAttributes =
recordAttributes != null
? AttributesMap.copyOf(recordAttributes)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copying the builder's attributes seems like a safety mechanism to prevent context attributes polluting the builder attributes in case of reuse. But builder is already not safe for reuse since attributes are passed to the SdkReadWriteLogRecord where a processor can mutate them and which allows a caller to further mutate the attributes after the fact.

So I think we should take steps to explicitly prevent builder reuse (separately from this PR) but don't need to spend the extra resources making a copy here.

AttributesMap spanAttributes = attributes();
contextAttributes.forEach(
(key, value) -> {
if (spanAttributes.get(key) == null) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check (and the corresponding in logs) checks for collisions using attribute name and type, where as put(AttributeKey<?>, Object) only considers attribute name (correctly since its invalid to have attributes of the same name and different type). Can you make this check consistent?

@SuppressWarnings({"rawtypes", "ReferenceEquality"})
public Span startSpan() {
Context parentContext = parent == null ? Context.current() : parent;
if (noParent

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should enabling attribute inheritance also change which unrelated context values samplers and processors receive for setNoParent()? Could we source inherited attributes from the ambient context without changing the existing parent-context behavior?

* and {@code attributes}. If the same attribute key is present in both, the value from {@code
* attributes} takes precedence.
*/
public static Context add(Context context, Attributes attributes) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably make this set instead of add, since set is more flexible: with add you can't get replacement semantics, but with set you can replace or add (via get then set) depending on what you want.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants