Skip to content

Decouple the responsibilities of the environment variable propagation carrier#4961

Merged
jack-berg merged 17 commits intoopen-telemetry:mainfrom
pellared:ref-envvar-car
Apr 1, 2026
Merged

Decouple the responsibilities of the environment variable propagation carrier#4961
jack-berg merged 17 commits intoopen-telemetry:mainfrom
pellared:ref-envvar-car

Conversation

@pellared
Copy link
Copy Markdown
Member

@pellared pellared commented Mar 18, 2026

Follows #4944

Per #4944 (comment)

Per #4961 (comment)

Changes

Decouple the responsibilities of the environment variable propagation carrier from concerns that are:

  1. specific to individual propagators used with it,
  2. already handled by the underlying environment variable APIs (error handling and limits when setting values).

@pellared pellared added enhancement New feature or request spec:context Related to the specification/context directory labels Mar 18, 2026
@pellared
Copy link
Copy Markdown
Member Author

CC @open-telemetry/semconv-cicd-approvers

@pellared pellared marked this pull request as ready for review March 18, 2026 18:14
@pellared pellared requested review from a team as code owners March 18, 2026 18:14
@marcalff
Copy link
Copy Markdown
Member

This is consistent with how C++ implemented it:

open-telemetry/opentelemetry-cpp#3834 (comment)

The Carrier is supposed to be ignorant of which propagator is used, only propagators will know the keys.

Conceptually, the environment variable carrier is supposed to work with any propagator, including B3, so it should not hard code any known keys.

@pellared
Copy link
Copy Markdown
Member Author

pellared commented Mar 18, 2026

This is consistent with how C++ implemented it:

open-telemetry/opentelemetry-cpp#3834 (comment)

The Carrier is supposed to be ignorant of which propagator is used, only propagators will know the keys.
Conceptually, the environment variable carrier is supposed to work with any propagator, including B3, so it should not hard code any known keys.

Exactly! The same applies for:

I added the C++ implementation for reference.

In my opinion, the current Swift implementation is not good. My intuition tells me that this should be done by implementing Setter and Getter (disclaimer: I never wrote a single line of code in Swift). I am considering removing this implementation from this document as I think it is a misleading reference. However, I do not want to do this in this PR.
CC @open-telemetry/swift-core-approvers

Comment thread specification/context/env-carriers.md Outdated
@pellared pellared changed the title Decouple the responsibilities of the environment variable carrier Decouple the responsibilities of the environment variable propagation carrier Mar 19, 2026
@pellared pellared requested a review from kamphaus March 19, 2026 07:40
Comment thread specification/context/env-carriers.md Outdated
Co-authored-by: Reiley Yang <reyang@microsoft.com>
Comment thread specification/context/env-carriers.md Outdated
@adrielp
Copy link
Copy Markdown
Member

adrielp commented Mar 22, 2026

This is consistent with how C++ implemented it:
open-telemetry/opentelemetry-cpp#3834 (comment)

The Carrier is supposed to be ignorant of which propagator is used, only propagators will know the keys.
Conceptually, the environment variable carrier is supposed to work with any propagator, including B3, so it should not hard code any known keys.

Exactly! The same applies for:

I added the C++ implementation for reference.

In my opinion, the current Swift implementation is not good. My intuition tells me that this should be done by implementing Setter and Getter (disclaimer: I never wrote a single line of code in Swift). I am considering removing this implementation from this document as I think it is a misleading reference. However, I do not want to do this in this PR. CC @open-telemetry/swift-core-approvers

It's a long standing, prior art implementation.

As a user of the OTel libraries, I've preferred experiences with propagator wrappers because I know what I'm getting. Whereas, with the carrier, it's less obvious (as in I have to dig) to figure out what is available.

Does this guidance now mean that the B3 propagator, as an example, will have to have normalization logic added in order to convert X_B3_TRACEID to X-B3-TraceId? Or do they already have logic that handles that?

@pellared
Copy link
Copy Markdown
Member Author

pellared commented Mar 24, 2026

@adrielp,

As a user of the OTel libraries, I've preferred experiences with propagator wrappers because I know what I'm getting. Whereas, with the carrier, it's less obvious (as in I have to dig) to figure out what is available.

Can you please be more specific and provide code examples that demonstrate how using carrier is less obvious (and what does it mean). It rather seems to opposite as e.g. it would requiring creating a new propagator each time you want to spawn a new process. This seems also against the idea of defining/setting propagator once and using it with different carriers. How would one use the same propagator for HTTP instrumentation where HTTP headers are used as propagation carrier and something that spawns a new CLI command needs to propagate the context via env vars? How would making a propagation wrapper would make this example better: https://go.dev/play/p/LV7REU1UXmJ ?

Does this guidance now mean that the B3 propagator, as an example, will have to have normalization logic added in order to convert X_B3_TRACEID to X-B3-TraceId? Or do they already have logic that handles that?

If it does then I made some mistakes in this PR. Is there any part that says that? The B3 propagator should have nothing to do with converting X_B3_TRACEID to X-B3-TraceId. It is the concern of the env var propagation carrier (or propagator wrapper if we find a use case where it is better than implementing via propagation carrier which we do not seem to currently have). Or maybe are there parts if this PR better described?

@adrielp
Copy link
Copy Markdown
Member

adrielp commented Mar 30, 2026

@adrielp,

As a user of the OTel libraries, I've preferred experiences with propagator wrappers because I know what I'm getting. Whereas, with the carrier, it's less obvious (as in I have to dig) to figure out what is available.

Can you please be more specific and provide code examples that demonstrate how using carrier is less obvious (and what does it mean). It rather seems to opposite as e.g. it would requiring creating a new propagator each time you want to spawn a new process. This seems also against the idea of defining/setting propagator once and using it with different carriers. How would one use the same propagator for HTTP instrumentation where HTTP headers are used as propagation carrier and something that spawns a new CLI command needs to propagate the context via env vars? How would making a propagation wrapper would make this example better: https://go.dev/play/p/LV7REU1UXmJ ?

Does this guidance now mean that the B3 propagator, as an example, will have to have normalization logic added in order to convert X_B3_TRACEID to X-B3-TraceId? Or do they already have logic that handles that?

If it does then I made some mistakes in this PR. Is there any part that says that? The B3 propagator should have nothing to do with converting X_B3_TRACEID to X-B3-TraceId. It is the concern of the env var propagation carrier (or propagator wrapper if we find a use case where it is better than implementing via propagation carrier which we do not seem to currently have). Or maybe are there parts if this PR better described?

In one of the early working prototypes I had, the propagator had the mapping directly defined similar to how some of the other propagators did it. Because it was clearly defined in the code, it was quickly readable.

With carrier, you have to dive deeper to figure out what, when normalized, will be respected by the w/e propagator is being used.

AFAICT, the behavior being described here seems to require that the propagators have to do some type of remapping once the values are carried inside the code. TRACEPARENT works because there is no _ or - and so when normalized it matches the header key defined in the spec. X_B3_TRACEID would not match X-B3-TraceId unless 1) it's renormalized to replace _ with - in the b3 propagator and 2) the propagator doesn't care about case sensitivity or already normalizes the string characters to lower/upper.

Sure, everything will get carried, but as far as knowing what gets carried vs what gets propagated is harder in my mind to understand. Maybe I'm missing something, and maybe this shouldn't be specifically addressed in this pull request.

@pellared
Copy link
Copy Markdown
Member Author

pellared commented Mar 30, 2026

AFAICT, the behavior being described here seems to require that the propagators have to do some type of remapping once the values are carried inside the code.

The propagators are already responsible for encoding the values that are encoded in the propagation header values. These are already handled by the propagator implementation. For instance here is an implementation of Inject for B3 propagator in Go: https://github.com/open-telemetry/opentelemetry-go/blob/3ff9ad7291e201d583145061afa10ec6824cdeaf/propagation/baggage.go#L29-L35. If the propagator is used used with HTTP headers as carrier then it sets the encoded value in baggage HTTP header. If it is used with env vars as carrier then it sets the encoded value in BAGGAGE env var. My guess is that you are worried of the remapping that is already implemented in the propagators.

The env var carrier normalizes the X-B3-TraceId key to X-B3-TRACEID so that it would interoperate during both injection and extraction.

You may also be interested in this thread: #4944 (comment)

EDIT: I created a PR for Swift to follow the same pattern as in other languages open-telemetry/opentelemetry-swift-core#47

Comment thread specification/context/env-carriers.md
Comment thread specification/context/env-carriers.md
@adrielp
Copy link
Copy Markdown
Member

adrielp commented Mar 31, 2026

#4944 (comment)

yes, that's exactly what I was worried about. thank you for the clarification.

@jack-berg jack-berg added this pull request to the merge queue Apr 1, 2026
Merged via the queue into open-telemetry:main with commit e482a39 Apr 1, 2026
8 of 9 checks passed
@pellared pellared deleted the ref-envvar-car branch April 1, 2026 15:39
github-merge-queue bot pushed a commit that referenced this pull request Apr 13, 2026
… environment variables (#5003)

Per
#4961 (comment)
and other similar feedback

## Changes

This PR removes the non-normative guidance that presented a dedicated
`EnvironmentContextPropagator` as a first-class alternative for
environment variable context propagation.

The supplementary guidance now describes the carrier-based pattern
directly:

- use the existing `TextMapPropagator` with environment-specific
carriers (`EnvironmentGetter` and `EnvironmentSetter`)
- reference the existing C++, Go, Java, and Python implementation (I
also created a PR for Swift that follows the same pattern
open-telemetry/opentelemetry-swift-core#47)

The previous (removed) “Approach 1” guidance suggested that a dedicated
environment-variable-specific propagator was a viable recommendation.
Based on the discussion in #4961, that approach is seen as
misleading/buggy:
- the environment variable carrier should remain format-agnostic
- propagator-specific behavior belongs in the propagator, not in the
carrier
- the Swift example did not reflect the direction we want to recommend
here

This change keeps the document aligned with the carrier-based model used
by the referenced implementations.

## Side note

I think we can look into stabilization of this document after this PR is
merged. I plan to create a stabilization issue and update the compliance
matrix.

---------

Co-authored-by: Alex Boten <223565+codeboten@users.noreply.github.com>
@carlosalberto carlosalberto mentioned this pull request Apr 15, 2026
pull bot pushed a commit to MC-MAGA/opentelemetry-specification that referenced this pull request Apr 20, 2026
### Context

- Align environment variable context propagation name restrictions with
POSIX.1-2024
  and define normalization behavior.

([open-telemetry#4944](open-telemetry#4944))
- Decouple the responsibilities of the environment variable propagation
carrier.

([open-telemetry#4961](open-telemetry#4961))
- Remove misleading implementation approach the environment variable
propagation.

([open-telemetry#5003](open-telemetry#5003))
- Change Environment Variables as Context Propagation Carriers document
status to Beta.

([open-telemetry#5020](open-telemetry#5020))

### Traces

- Stabilize Tracer `enabled` operation

([open-telemetry#4941](open-telemetry#4941))
- Stabilize `AlwaysRecord` sampler.

([open-telemetry#4934](open-telemetry#4934))

### Metrics

- Add development `maxExportBatchSize` parameter to Periodic exporting
MetricReader.

([open-telemetry#4895](open-telemetry#4895))

### Logs

- Add event to span event bridge.

([open-telemetry#5006](open-telemetry#5006))

### Resource

- Clarify that a Resource describes the observed entity, not the
component
  that technically emits telemetry.

([open-telemetry#4905](open-telemetry#4905))

### Compatibility

- Deprecate OpenTracing compatibility requirements in the specification.

([open-telemetry#4938](open-telemetry#4938))
- Stabilize sections of Prometheus and OpenMetrics Compatibility.
- Stabilize Prometheus Classic Histogram to OTLP Explicit Histogram
transformation.

([open-telemetry#4874](open-telemetry#4874))
  - Stabilize Prometheus Timestamp and Start timestamp transformation.

([open-telemetry#4953](open-telemetry#4953))
- Clarify Prometheus Native Histogram to OTLP Exponential Histogram
conversion,
add conversion rules for Native Histograms with Custom Buckets (NHCB) to
OTLP
    Histogram.

([open-telemetry#4898](open-telemetry#4898))
  - Stabilize Prometheus Dropped Types transformation.

([open-telemetry#4952](open-telemetry#4952))
- Stabilize OpenTelemetry Attributes to Prometheus labels
transformation.

([open-telemetry#4963](open-telemetry#4963))
- Stabilize Prometheus Exemplar to OpenTelemetry Exemplar
transformation.

([open-telemetry#4962](open-telemetry#4962))
  - Stabilize Prometheus Metadata transformation.

([open-telemetry#4954](open-telemetry#4954))
- Stabilize OpenTelemetry Metric Metadata to Prometheus metric metadata.

([open-telemetry#4966](open-telemetry#4966))
- Stabilize OpenTelemetry Exemplar to Prometheus Exemplar
transformation.

([open-telemetry#4964](open-telemetry#4964))
- Stabilize sections of Prometheus Metrics Exporter.
  - Stabilize host configuration.

([open-telemetry#5025](open-telemetry#5025))

### SDK Configuration

- Declarative configuration: add in-development guidance for exposing
the
  effective `Resource` returned by `Create`.

([open-telemetry#4949](open-telemetry#4949))
- Require spec changes to consider declarative config schema

([open-telemetry#4916](open-telemetry#4916))
- Add strict YAML parsing guidance to configuration supplementary
guidelines.

([open-telemetry#4878](open-telemetry#4878))

### OTEPs

- Process Context: Sharing Resource Attributes with External Readers.

([open-telemetry#4719](open-telemetry#4719))
- Support multiple Resources within an SDK.

([open-telemetry#4665](open-telemetry#4665))

---------

Co-authored-by: Jack Berg <34418638+jack-berg@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request spec:context Related to the specification/context directory

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants