diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 28b78539210..082995d1265 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -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 diff --git a/experimental/packages/opentelemetry-propagator-env-carrier/README.md b/experimental/packages/opentelemetry-propagator-env-carrier/README.md index 9e2baa17278..712be568f61 100644 --- a/experimental/packages/opentelemetry-propagator-env-carrier/README.md +++ b/experimental/packages/opentelemetry-propagator-env-carrier/README.md @@ -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'); @@ -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 @@ -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, diff --git a/experimental/packages/opentelemetry-propagator-env-carrier/src/env-carrier.ts b/experimental/packages/opentelemetry-propagator-env-carrier/src/env-carrier.ts index d24830d6792..1b1bcf813bb 100644 --- a/experimental/packages/opentelemetry-propagator-env-carrier/src/env-carrier.ts +++ b/experimental/packages/opentelemetry-propagator-env-carrier/src/env-carrier.ts @@ -94,35 +94,33 @@ 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 { - private readonly _carrier: Record = {}; + get(_carrier: void, key: string): string | undefined { + const normalizedKey = normalizeKey(key); + return process.env[normalizedKey]; + } + + keys(_carrier: void): string[] { + const keys: string[] = []; - constructor() { for (const [key, value] of Object.entries(process.env)) { if (value !== undefined && isNormalizedKey(key)) { - this._carrier[key] = value; + keys.push(key); } } - } - get(_carrier: void, key: string): string | undefined { - return this._carrier[normalizeKey(key)]; - } - - keys(_carrier: void): string[] { - return Object.keys(this._carrier); + return keys; } } diff --git a/experimental/packages/opentelemetry-propagator-env-carrier/test/env-carrier.test.ts b/experimental/packages/opentelemetry-propagator-env-carrier/test/env-carrier.test.ts index b4a03c4dd68..826b258eebf 100644 --- a/experimental/packages/opentelemetry-propagator-env-carrier/test/env-carrier.test.ts +++ b/experimental/packages/opentelemetry-propagator-env-carrier/test/env-carrier.test.ts @@ -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'; @@ -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', @@ -191,17 +191,13 @@ describe('EnvironmentGetter and EnvironmentSetter', () => { }); it('should ignore non-normalized environment names when reading values', () => { - process.env.traceparent = 'ignored'; process.env['trace-state'] = 'ignored'; process.env['x-b3-traceid'] = 'ignored'; - process.env['1ABC'] = 'ignored'; const getter = new EnvironmentGetter(); - assert.strictEqual(getter.get(undefined, 'traceparent'), undefined); assert.strictEqual(getter.get(undefined, 'trace-state'), undefined); assert.strictEqual(getter.get(undefined, 'x-b3-traceid'), undefined); - assert.strictEqual(getter.get(undefined, '1abc'), undefined); assert.deepStrictEqual(getter.keys(undefined), []); }); @@ -213,18 +209,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(); @@ -258,7 +264,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'; @@ -278,20 +284,6 @@ describe('EnvironmentGetter and EnvironmentSetter', () => { assert.strictEqual(spanContext?.traceState?.get('vendor2'), 'value2'); }); - it('should ignore W3C trace context in non-normalized environment names', () => { - process.env.traceparent = `00-${traceId}-${spanId}-01`; - process.env.tracestate = 'vendor1=value1,vendor2=value2'; - - const propagator = new W3CTraceContextPropagator(); - const context = propagator.extract( - ROOT_CONTEXT, - undefined, - new EnvironmentGetter() - ); - - assert.strictEqual(trace.getSpanContext(context), undefined); - }); - it('should inject W3C baggage into an environment carrier', () => { const propagator = new W3CBaggagePropagator(); const context = propagation.setBaggage( @@ -308,7 +300,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();