Skip to content

[API] Environment Variables as Context Propagation Carriers#3834

Open
perhapsmaple wants to merge 9 commits intoopen-telemetry:mainfrom
perhapsmaple:environment-carrier
Open

[API] Environment Variables as Context Propagation Carriers#3834
perhapsmaple wants to merge 9 commits intoopen-telemetry:mainfrom
perhapsmaple:environment-carrier

Conversation

@perhapsmaple
Copy link
Contributor

@perhapsmaple perhapsmaple commented Jan 28, 2026

Fixes #3817

Written with inspiration from: Go PR

Changes

Need help from maintainers on below:

  • I have followed the Go implementation of having a single class. The Python implementation on the other hand uses two separate classes: Python PR
  • Is the raw pointer correct for this use case? From my view, the carrier shouldn't own the map. Should we use nostd::shared_ptr for safety?
  • I believe Windows has a character limit? Do we need to handle this?
  • Keys() only returns known OTel variables (traceparent, tracestate, baggage), not all environment variables. The Go Implementation returns all environment variables.
  • Carrier is NOT thread-safe. Do we need to handle this?

For significant contributions please make sure you have completed the following items:

  • CHANGELOG.md updated for non-trivial changes
  • Unit tests have been added
  • Changes in public API reviewed

Signed-off-by: Harish <140232061+perhapsmaple@users.noreply.github.com>
@codecov
Copy link

codecov bot commented Jan 28, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.02%. Comparing base (3143f93) to head (dcce272).
⚠️ Report is 3 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #3834      +/-   ##
==========================================
+ Coverage   89.98%   90.02%   +0.05%     
==========================================
  Files         225      226       +1     
  Lines        7170     7193      +23     
==========================================
+ Hits         6451     6475      +24     
+ Misses        719      718       -1     
Files with missing lines Coverage Δ
...elemetry/context/propagation/environment_carrier.h 100.00% <100.00%> (ø)

... and 1 file with indirect coverage changes

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

@marcalff
Copy link
Member

marcalff commented Feb 8, 2026

@perhapsmaple

Thanks for the feature.

Need help from maintainers on below:

* I have followed the Go implementation of having a single class. The Python implementation on the other hand uses two separate classes: [Python PR](https://github.com/open-telemetry/opentelemetry-python/pull/4609)

The spec proposes either a single interface (Propagator), or two separate interfaces (Injector, Extractor) for propagation.

Given that opentelemetry-cpp uses a single interface with a TextMapCarrier, I think keeping a single class makes sense.

* Is the raw pointer correct for this use case? From my view, the carrier shouldn't own the map. Should we use nostd::shared_ptr for safety?

A shared pointer is better, to avoid misuse.

* I believe Windows has a character limit? Do we need to handle this?

Every operating system will have a character limit, not only windows.

Given that the code does not do a setenv() itself, but produces a map of environment variables to set, I propose to leave this to the code making the actual call to fork/exec/etc, so no limits to check preemptively.

* Keys() only returns known OTel variables (traceparent, tracestate, baggage), not all environment variables. The Go Implementation returns all environment variables.

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.

Notice how none of the carriers, including the HttpTextMapCarrier, implement the Keys() method, the only supported pattern is set/get with a known key name.

I propose to not implement Keys(), used to inspect the whole container, either.

* Carrier is NOT thread-safe. Do we need to handle this?

For HTTP, when two threads make an http call and need to inject keys to talk to two different services, each thread will use its own carrier to inject into its own http message.

For environment variables, the pattern should be the same, the EnvironmentCarrier object will be per thread, not shared, so there should be no thread safety issue.


This PR is looking good already.

A great addition will be an example with a parent process that forks a child, so people can see in practice how to implement the whole thing.

ThomsonTan and others added 4 commits February 7, 2026 20:59
Signed-off-by: Harish <140232061+perhapsmaple@users.noreply.github.com>
Signed-off-by: Harish <140232061+perhapsmaple@users.noreply.github.com>
Signed-off-by: Harish <140232061+perhapsmaple@users.noreply.github.com>
@perhapsmaple perhapsmaple marked this pull request as ready for review February 8, 2026 08:22
@perhapsmaple perhapsmaple requested a review from a team as a code owner February 8, 2026 08:22
Signed-off-by: Harish <140232061+perhapsmaple@users.noreply.github.com>
Signed-off-by: Harish <140232061+perhapsmaple@users.noreply.github.com>
@perhapsmaple
Copy link
Contributor Author

Thanks for the review. I have added a basic example that should work in unix environments. Do we need an example for windows as well?

perhapsmaple and others added 2 commits February 10, 2026 20:28
Signed-off-by: Harish <140232061+perhapsmaple@users.noreply.github.com>
std::shared_ptr<std::map<std::string, std::string>> env_map_ptr_;
mutable std::map<std::string, std::string> cache_;

// Converts a header name to an environment variable name.
Copy link
Member

@marcalff marcalff Feb 18, 2026

Choose a reason for hiding this comment

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

Also map:

  • my-key to MY_KEY
  • my.complex.key to MY_COMPLEX_KEY

so any character that does not fit an environment variable name should be replaced by underscore.

Copy link
Member

@marcalff marcalff left a comment

Choose a reason for hiding this comment

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

LGTM, thanks for the feature.

See a minor comment.

@marcalff marcalff changed the title Environment Variables as Context Propagation Carriers [API] Environment Variables as Context Propagation Carriers Feb 18, 2026
@marcalff marcalff added the pr:please-review This PR is ready for review label Feb 18, 2026
@marcalff
Copy link
Member

Thanks for the review. I have added a basic example that should work in unix environments. Do we need an example for windows as well?

Thanks for the example added. It looks good as is, it shows how to use the environment carrier.

@marcalff
Copy link
Member

@lalitb

Could you review as well, given that this change touches the API ? Thanks.

{
std::string env_name(key);
std::transform(env_name.begin(), env_name.end(), env_name.begin(),
[](unsigned char c) { return static_cast<char>(std::toupper(c)); });
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: std::toupper(c) needs header file #include <cctype>.

if (value != nullptr)
{
// Cache for lifetime management (string_view requires stable storage)
cache_[std::string(key)] = std::string(value);
Copy link
Contributor

Choose a reason for hiding this comment

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

Get is marked as const, but it does mutate the internal state cache_. Perhaps this should be documented in the API doc?

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

Labels

pr:please-review This PR is ready for review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feature request] Support the environment variable propagation spec in cpp

3 participants

Comments