Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions experimental/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ For notes on migrating to 2.x / 0.200.x see [the upgrade guide](doc/upgrade-to-2
* feat(configuration): bump config schema to v1.1.0; rename `without_scope_info` → `scope_info_enabled` and `without_target_info/development` → `target_info_enabled/development` on the Prometheus pull exporter (semantics inverted), rename `with_resource_constant_labels` → `resource_constant_labels`. Validate `file_format` per the configuration versioning spec: accept any minor version of major `1` (e.g. `1.0`, `1.1`), warn when the minor version is newer than supported, and reject other major versions. [#6781](https://github.com/open-telemetry/opentelemetry-js/pull/6781) @MikeGoldsmith
* feat(sdk-node): wire up `id_generator` from declarative config [#6782](https://github.com/open-telemetry/opentelemetry-js/pull/6782) @MikeGoldsmith
* feat(propagator-env-carrier): empty name normalization [#6827](https://github.com/open-telemetry/opentelemetry-js/pull/6827) @pellared
* feat(propagator-env-carrier): make `EnvironmentGetter` read the current `process.env` [#6853](https://github.com/open-telemetry/opentelemetry-js/pull/6853) @pellared

### :bug: Bug Fixes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ console.log(output);
```

Use `EnvironmentGetter` during process startup to extract context that was
provided through environment variables. It reads from its own snapshot, so pass
`undefined` as the carrier when extracting.
provided through environment variables. It reads from the current `process.env`,
so pass `undefined` as the carrier when extracting.

```javascript
const { ROOT_CONTEXT, trace } = require('@opentelemetry/api');
Expand All @@ -72,9 +72,9 @@ console.log(spanContext.traceId);
// 0102030405060708090a0b0c0d0e0f10
```

`EnvironmentGetter` snapshots `process.env` when it is constructed. Later
changes to `process.env` are not reflected in that getter. Only environment
variables whose names are already normalized are included in the snapshot.
`EnvironmentGetter` reads `process.env` each time `get()` or `keys()` is called.
Later changes to `process.env` are reflected in the same getter. Its `keys()`
method returns only environment variables whose names are already normalized.

## Key Normalization

Expand All @@ -92,10 +92,10 @@ For example, `traceparent` becomes `TRACEPARENT`, `trace-state` becomes

`EnvironmentSetter` always writes normalized key names to its environment map.
`EnvironmentGetter` normalizes the requested propagator key and reads the
corresponding normalized environment variable name from its snapshot. Its
`keys()` method returns only environment variable names that are already
normalized. For example, a propagator request for `x-b3-traceid` matches
`X_B3_TRACEID` from `process.env`, but does not match `x-b3-traceid`.
corresponding normalized environment variable name from the current
`process.env`. Its `keys()` method returns only environment variable names that
are already normalized. For example, a propagator request for `x-b3-traceid`
matches `X_B3_TRACEID` from `process.env`, but does not match `x-b3-traceid`.

Name collisions after normalization should be avoided. For example,
`trace-state` and `TRACE_STATE` both normalize to `TRACE_STATE`. When injecting,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,35 +94,40 @@ function normalizeKey(key: string): string {
}

/**
* TextMapGetter that reads propagation values from a process environment
* snapshot.
* TextMapGetter that reads propagation values from the process environment.
*
* `EnvironmentGetter` snapshots `process.env` when it is constructed and
* ignores the carrier passed to `get()` and `keys()`. Pass `undefined` as the
* carrier when using this getter with a `TextMapPropagator`.
* `EnvironmentGetter` reads the current `process.env` and ignores the carrier
* passed to `get()` and `keys()`. Pass `undefined` as the carrier when using
* this getter with a `TextMapPropagator`.
*
* Only environment variables whose names are already normalized are stored in
* the snapshot. The requested propagator key is normalized before lookup.
* `keys()` returns only environment variables whose names are already
* normalized. The requested propagator key is normalized before lookup.
*
* @see https://opentelemetry.io/docs/specs/otel/context/env-carriers/
*/
export class EnvironmentGetter implements TextMapGetter<void> {
private readonly _carrier: Record<string, string> = {};
get(_carrier: void, key: string): string | undefined {
const normalizedKey = normalizeKey(key);

constructor() {
for (const [key, value] of Object.entries(process.env)) {
if (value !== undefined && isNormalizedKey(key)) {
this._carrier[key] = value;
for (const [envKey, value] of Object.entries(process.env)) {
if (value !== undefined && envKey === normalizedKey) {
return value;
}
}
Comment thread
pellared marked this conversation as resolved.
Outdated
}

get(_carrier: void, key: string): string | undefined {
return this._carrier[normalizeKey(key)];
return undefined;
}

keys(_carrier: void): string[] {
return Object.keys(this._carrier);
const keys: string[] = [];

for (const [key, value] of Object.entries(process.env)) {
if (value !== undefined && isNormalizedKey(key)) {
keys.push(key);
}
}

return keys;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ describe('EnvironmentGetter and EnvironmentSetter', () => {
assert.strictEqual(getter.get(undefined, 'x-b3-traceid'), 'b3-value');
});

it('should return only already normalized snapshot keys', () => {
it('should return only already normalized environment keys', () => {
process.env.TRACEPARENT = 'traceparent-value';
process.env.TRACE_STATE = 'tracestate-value';
process.env['trace-state'] = 'ignored';
Expand All @@ -159,13 +159,13 @@ describe('EnvironmentGetter and EnvironmentSetter', () => {
]);
});

it('should return empty keys for an empty environment snapshot', () => {
it('should return empty keys for an empty environment', () => {
const getter = new EnvironmentGetter();

assert.deepStrictEqual(getter.keys(undefined), []);
});

it('should ignore empty environment names when snapshotting', () => {
it('should ignore empty environment names when listing keys', () => {
const env = process.env;
process.env = {
'': 'ignored',
Expand Down Expand Up @@ -213,18 +213,28 @@ describe('EnvironmentGetter and EnvironmentSetter', () => {
assert.strictEqual(getter.get(undefined, 'empty'), '');
});

it('should snapshot process.env at construction time', () => {
it('should read current process.env values after construction', () => {
process.env.TRACEPARENT = 'original';
process.env.TRACESTATE = 'initial';
const getter = new EnvironmentGetter();

process.env.TRACEPARENT = 'updated';
process.env.TRACESTATE = 'added-after-construction';
process.env.BAGGAGE = 'added-after-construction';
delete process.env.TRACESTATE;

assert.strictEqual(getter.get(undefined, 'traceparent'), 'original');
assert.strictEqual(getter.get(undefined, 'traceparent'), 'updated');
assert.strictEqual(getter.get(undefined, 'tracestate'), undefined);
assert.strictEqual(
getter.get(undefined, 'baggage'),
'added-after-construction'
);
assert.deepStrictEqual(getter.keys(undefined).sort(), [
'BAGGAGE',
'TRACEPARENT',
]);
});

it('should read from the environment snapshot without a carrier', () => {
it('should read from process.env without a carrier', () => {
process.env.TRACEPARENT = 'environment-value';

const getter = new EnvironmentGetter();
Expand Down Expand Up @@ -258,7 +268,7 @@ describe('EnvironmentGetter and EnvironmentSetter', () => {
});
});

it('should extract W3C trace context from an environment snapshot', () => {
it('should extract W3C trace context from process.env', () => {
process.env.TRACEPARENT = `00-${traceId}-${spanId}-01`;
process.env.TRACESTATE = 'vendor1=value1,vendor2=value2';

Expand Down Expand Up @@ -308,7 +318,7 @@ describe('EnvironmentGetter and EnvironmentSetter', () => {
assert.strictEqual(carrier.BAGGAGE, 'first=one,second=two');
});

it('should extract W3C baggage from an environment snapshot', () => {
it('should extract W3C baggage from process.env', () => {
process.env.BAGGAGE = 'first=one,second=two';

const propagator = new W3CBaggagePropagator();
Expand Down