Skip to content

feat(propagator-env-carrier): add environment variable carrier helpers#6774

Open
pellared wants to merge 10 commits into
open-telemetry:mainfrom
pellared:fix-6223
Open

feat(propagator-env-carrier): add environment variable carrier helpers#6774
pellared wants to merge 10 commits into
open-telemetry:mainfrom
pellared:fix-6223

Conversation

@pellared
Copy link
Copy Markdown
Member

@pellared pellared commented May 28, 2026

Which problem is this PR solving?

Fixes #6223

Short description of the changes

Implement environment variable carrier following https://opentelemetry.io/docs/specs/otel/context/env-carriers/ and open-telemetry/opentelemetry-specification#5102

This is important for interoperability with other languages (https://github.com/open-telemetry/opentelemetry-specification/blob/main/spec-compliance-matrix.md?plain=1#L254) and tooling.

Type of change

  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

Example usage from README.md.

Checklist:

  • Followed the style guidelines of this project
  • Unit tests have been added
  • Documentation has been updated

@pellared pellared requested a review from a team as a code owner May 28, 2026 15:30
@pellared pellared marked this pull request as draft May 28, 2026 15:30
@codecov
Copy link
Copy Markdown

codecov Bot commented May 28, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.94%. Comparing base (c0bfea7) to head (5e21cb4).
⚠️ Report is 3 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6774      +/-   ##
==========================================
+ Coverage   94.92%   94.94%   +0.01%     
==========================================
  Files         377      378       +1     
  Lines       12817    12858      +41     
  Branches     2924     2932       +8     
==========================================
+ Hits        12167    12208      +41     
  Misses        650      650              
Files with missing lines Coverage Δ
...elemetry-propagator-env-carrier/src/env-carrier.ts 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.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@pellared pellared marked this pull request as ready for review May 28, 2026 15:59
@pellared
Copy link
Copy Markdown
Member Author

CC @open-telemetry/semconv-cicd-approvers

@pellared pellared requested a review from kamphaus June 1, 2026 11:44
Copy link
Copy Markdown
Contributor

@maryliag maryliag left a comment

Choose a reason for hiding this comment

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

Thank you for working on this! Added a few questions/comments

}
}

get(_carrier: unknown, key: string): string | undefined {
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.

can you just not have _carrier here and below if the function don't use them? If you need this format, consider using never as type, so users don't try to pass something

Copy link
Copy Markdown
Member Author

@pellared pellared Jun 3, 2026

Choose a reason for hiding this comment

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

Addressed in 2eb8181

I switched EnvironmentGetter to TextMapGetter<void>. That still prevents callers from passing an object directly, but avoids the awkward undefined as never pattern. This also follows what is already done in

class Getter implements TextMapGetter<void> {
private ocGetter: oc.HeaderGetter;
constructor(ocGetter: oc.HeaderGetter) {
this.ocGetter = ocGetter;
}
keys(): string[] {
return [];
}
get(carrier: void, key: string) {
return this.ocGetter.getHeader(key);
}
}

*/
export class EnvironmentSetter implements TextMapSetter<EnvironmentCarrierMap> {
set(carrier: EnvironmentCarrierMap, key: string, value: string): void {
carrier[normalizeKey(key)] = value;
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.

the set uses local carrier, and the get uses the global, this opens for chances of the global one not getting updated and the get not working as expected.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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


const getter = new EnvironmentGetter();

assert.deepStrictEqual(getter.keys({}), ['TRACEPARENT']);
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.

can we have documented somewhere about which one takes precedent? So users can expect the same results if running the test multiple times
right now is dependent on the order to which the key was set, if that is indeed the expectation, make it clear, or change to something that always give the same results, such as returning the keys in alphabetical order

Copy link
Copy Markdown
Member Author

@pellared pellared Jun 3, 2026

Choose a reason for hiding this comment

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

Good call. I strongly believe we have agreed in some reviews or maybe even Spec SIG call that this is an error scenario (name collision) and the behavior is not specified for this case. I think it would be good to cover call it out explicitly in the specification and document it here in EnvironmentGetter. Thoughts?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Improved the docs 5a67cf4

const originalEnv = { ...process.env };

beforeEach(() => {
clearEnv();
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.

no need to delete twice, you're already deleting at the end of each test

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Note that env vars such as TRACEPARENT can be used by CI/CD automation. For instance GitHub Actions may use it in future and it may make these tests flaky. Therefore, I think it is safer to even clear before the test. Side note: one of the main reason why env var carrier was added was the work of the CI/CD SIG.

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.

in that case, I suggest adding env names that you know won't conflict with pre existing values

Copy link
Copy Markdown
Member Author

@pellared pellared Jun 3, 2026

Choose a reason for hiding this comment

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

What about integration tests under describe('propagator integration' ? Do we want to remove them? Note that there are similar tests in https://github.com/open-telemetry/opentelemetry-js/blob/main/experimental/packages/shim-opencensus/test/propagation.test.ts

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.

no need to remove the test, but I want to make them deterministic, if there is a chance an env variable changed outside of the tests could affect the results and cause flaky tests, we want to avoid that.
Even if you delete TRACEPARENT before a test, but you say that other processes might be using it, there is no guarantee that in between the before and the actual test, that value can be added to the env and the test fails.

The majority of your test don't need to use the env TRACEPARENT, do they? you can call yabadabadoo and should still work 😄

For the test of propagator integration, seems like that is the env variable you want to test, so you can leave it there, and if we ever get flaky tests we can decide what to do

@pichlermarc pichlermarc self-assigned this Jun 3, 2026
/**
* TextMapSetter that writes propagation values to an environment map.
*/
export class EnvironmentSetter implements TextMapSetter<EnvironmentCarrierMap> {
Copy link
Copy Markdown
Member Author

@pellared pellared Jun 3, 2026

Choose a reason for hiding this comment

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

I think it is safer to have EnvironmentSetter implements TextMapSetter<void> and accept Record<string, string> or EnvironmentCarrierMap in the constructor. This follows the pattern used in

class Setter implements TextMapSetter<void> {
private ocSetter: oc.HeaderSetter;
constructor(ocSetter: oc.HeaderSetter) {
this.ocSetter = ocSetter;
}
set(carrier: void, key: string, value: string): void {
this.ocSetter.setHeader(key, value);
}
}
and it does not force users to think what type of carrier they need to pass to the propagator.

See test scenarios it('should inject for reference.

@maryliag, thoughts?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think this is better 5e21cb4

@pellared pellared requested a review from maryliag June 3, 2026 20:27
const originalEnv = { ...process.env };

beforeEach(() => {
clearEnv();
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.

no need to remove the test, but I want to make them deterministic, if there is a chance an env variable changed outside of the tests could affect the results and cause flaky tests, we want to avoid that.
Even if you delete TRACEPARENT before a test, but you say that other processes might be using it, there is no guarantee that in between the before and the actual test, that value can be added to the env and the test fails.

The majority of your test don't need to use the env TRACEPARENT, do they? you can call yabadabadoo and should still work 😄

For the test of propagator integration, seems like that is the env variable you want to test, so you can leave it there, and if we ever get flaky tests we can decide what to do

const ASCII_UNDERSCORE = '_'.charCodeAt(0);
const ASCII_CASE_OFFSET = ASCII_LOWER_A - ASCII_UPPER_A;

/*
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.

Suggested change
/*
/**

nit

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.

Support the environment variable propagation spec in Javascript

4 participants