From a926f536733a2add23464bd26988cf253bdda305 Mon Sep 17 00:00:00 2001 From: Martin Kuba Date: Fri, 1 Nov 2024 00:12:50 -0700 Subject: [PATCH] feat(aws-lambda)!: Remove explicit x-ray context in favor of global propagator (#2369) Co-authored-by: Tyler Benson <734411+tylerbenson@users.noreply.github.com> Co-authored-by: Amir Blum --- package-lock.json | 94 ++++- .../README.md | 59 ++- .../package.json | 3 +- .../src/instrumentation.ts | 51 +-- .../src/types.ts | 1 - .../test/integrations/lambda-handler.test.ts | 369 ++---------------- 6 files changed, 182 insertions(+), 395 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8679f80198..3bbeb82d4c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36201,6 +36201,8 @@ "devDependencies": { "@opentelemetry/api": "^1.3.0", "@opentelemetry/core": "^1.8.0", + "@opentelemetry/propagator-aws-xray": "^1.25.1", + "@opentelemetry/propagator-aws-xray-lambda": "^0.52.1", "@opentelemetry/sdk-metrics": "^1.8.0", "@opentelemetry/sdk-trace-base": "^1.8.0", "@opentelemetry/sdk-trace-node": "^1.8.0", @@ -36217,6 +36219,60 @@ "@opentelemetry/api": "^1.3.0" } }, + "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dev": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/core/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/propagator-aws-xray": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-aws-xray/-/propagator-aws-xray-1.25.1.tgz", + "integrity": "sha512-soZQdO9EAROMwa9bL2C0VLadbrfRjSA9t7g6X8sL0X1B8V59pzOayYMyTW9qTECn9uuJV98A7qOnJm6KH6yk8w==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@opentelemetry/propagator-aws-xray-lambda": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-aws-xray-lambda/-/propagator-aws-xray-lambda-0.52.1.tgz", + "integrity": "sha512-LBAu/1SRVuWrsWTzqUxZmzWinsS76UH/qApgYjmaN6Q8RcObXeZOmR0+FLSwkRX7OLGhLLQKiu3CQmlgXbpNCw==", + "dev": true, + "dependencies": { + "@opentelemetry/propagator-aws-xray": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, "plugins/node/opentelemetry-instrumentation-aws-lambda/node_modules/@types/node": { "version": "18.18.14", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.14.tgz", @@ -46655,7 +46711,8 @@ "@opentelemetry/api": "^1.3.0", "@opentelemetry/core": "^1.8.0", "@opentelemetry/instrumentation": "^0.54.0", - "@opentelemetry/propagator-aws-xray": "^1.3.1", + "@opentelemetry/propagator-aws-xray": "^1.25.1", + "@opentelemetry/propagator-aws-xray-lambda": "^0.52.1", "@opentelemetry/sdk-metrics": "^1.8.0", "@opentelemetry/sdk-trace-base": "^1.8.0", "@opentelemetry/sdk-trace-node": "^1.8.0", @@ -46668,6 +46725,41 @@ "typescript": "4.4.4" }, "dependencies": { + "@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dev": true, + "requires": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "dependencies": { + "@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "dev": true + } + } + }, + "@opentelemetry/propagator-aws-xray": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-aws-xray/-/propagator-aws-xray-1.25.1.tgz", + "integrity": "sha512-soZQdO9EAROMwa9bL2C0VLadbrfRjSA9t7g6X8sL0X1B8V59pzOayYMyTW9qTECn9uuJV98A7qOnJm6KH6yk8w==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.25.1" + } + }, + "@opentelemetry/propagator-aws-xray-lambda": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-aws-xray-lambda/-/propagator-aws-xray-lambda-0.52.1.tgz", + "integrity": "sha512-LBAu/1SRVuWrsWTzqUxZmzWinsS76UH/qApgYjmaN6Q8RcObXeZOmR0+FLSwkRX7OLGhLLQKiu3CQmlgXbpNCw==", + "dev": true, + "requires": { + "@opentelemetry/propagator-aws-xray": "1.25.1" + } + }, "@types/node": { "version": "18.18.14", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.14.tgz", diff --git a/plugins/node/opentelemetry-instrumentation-aws-lambda/README.md b/plugins/node/opentelemetry-instrumentation-aws-lambda/README.md index 20d4eb9a1b..2adef75ff4 100644 --- a/plugins/node/opentelemetry-instrumentation-aws-lambda/README.md +++ b/plugins/node/opentelemetry-instrumentation-aws-lambda/README.md @@ -54,8 +54,7 @@ In your Lambda function configuration, add or update the `NODE_OPTIONS` environm | --- | --- | --- | | `requestHook` | `RequestHook` (function) | Hook for adding custom attributes before lambda starts handling the request. Receives params: `span, { event, context }` | | `responseHook` | `ResponseHook` (function) | Hook for adding custom attributes before lambda returns the response. Receives params: `span, { err?, res? }` | -| `disableAwsContextPropagation` | `boolean` | By default, this instrumentation will try to read the context from the `_X_AMZN_TRACE_ID` environment variable set by Lambda, set this to `true` or set the environment variable `OTEL_LAMBDA_DISABLE_AWS_CONTEXT_PROPAGATION=true` to disable this behavior | -| `eventContextExtractor` | `EventContextExtractor` (function) | Function for providing custom context extractor in order to support different event types that are handled by AWS Lambda (e.g., SQS, CloudWatch, Kinesis, API Gateway). Applied only when `disableAwsContextPropagation` is set to `true`. Receives params: `event, context` | +| `eventContextExtractor` | `EventContextExtractor` (function) | Function for providing custom context extractor in order to support different event types that are handled by AWS Lambda (e.g., SQS, CloudWatch, Kinesis, API Gateway). | | `lambdaHandler` | `string` | By default, this instrumentation automatically determines the Lambda handler function to instrument. This option is used to override that behavior by explicitly specifying the Lambda handler to instrument. See [Specifying the Lambda Handler](#specifying-the-lambda-handler) for additional information. | ### Hooks Usage Example @@ -86,6 +85,62 @@ The `lambdaHandler` should be specified as a string in the format `.0.46.0`, the Active Tracing context is no longer used by default. In order to enable it, include the [AWSXRayLambdaPropagator](https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/propagators/propagator-aws-xray-lambda) propagator in the list of propagators provided to the TracerProvider via its configuration, or by including `xray-lambda` in the OTEL_PROPAGATORS environment variable (see the example below on using the env variable). + +Note that there are two AWS-related propagators: [AWSXRayPropagator](https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/propagators/propagator-aws-xray) and [AWSXRayLambdaPropagator](https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/propagators/propagator-aws-xray-lambda). Here is a guideline for when to use one or the other: + +- If you export traces to AWS X-Ray, then use the `AWSXRayLambdaPropagator` or the `xray-lambda` value in the OTEL_PROPAGATORS environment variable. This will handle the active tracing lambda context as well as X-Ray HTTP headers. +- If you export traces to a backend other than AWS X-Ray, then use the `AWSXrayPropagator` or `xray` in the environment variable. This propagator only handles the X-Ray HTTP headers. + +Examples: + +1. Active Tracing is enabled and the OpenTelemetry SDK is configured to export traces to AWS X-Ray. In this case, configure the SDK to use the `AWSXRayLambdaPropagator`. + +```js +const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node'); +const { AWSXRayLambdaPropagator } = require('@opentelemetry/propagator-aws-xray-lambda'); + +const provider = new NodeTracerProvider(); +provider.register({ + propagator: new AWSXRayLambdaPropagator() +}); +``` + +Alternatively, use the `getPropagators()` function from the [auto-configuration-propagators](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/metapackages/auto-configuration-propagators/README.md) package, and set the OTEL_PROPAGATORS environment variable to `xray-lambda`. + +```js +const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node'); +const { getPropagator } = require('@opentelemetry/auto-configuration-propagators'); + +const provider = new NodeTracerProvider(); +provider.register({ + propagator: getPropagator() +}); +``` + +1. The OpenTelemetry SDK is configured to export traces to a backend other than AWX X-Ray, but the lambda function is invoked by other AWS services which send the context using the X-Ray HTTP headers. In this case, include the `AWSXRayPropagator`, which extracts context from the HTTP header but not the Lambda Active Tracing context. + +```js +const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node'); +const { AWSXRayLambdaPropagator } = require('@opentelemetry/propagator-aws-xray-lambda'); + +const provider = new NodeTracerProvider(); +provider.register({ + propagator: new AWSXRayPropagator() +}); +``` + +Alternatively, use the `auto-configuration-package` as in example #1 and set the OTEL_PROPAGATORS environment variable to `xray`. + +For additional information, see the [documentation for lambda semantic conventions](https://github.com/open-telemetry/semantic-conventions/blob/main/docs/faas/aws-lambda.md#aws-x-ray-active-tracing-considerations). + ## Semantic Conventions This package uses `@opentelemetry/semantic-conventions` version `1.22+`, which implements Semantic Convention [Version 1.7.0](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.7.0/semantic_conventions/README.md) diff --git a/plugins/node/opentelemetry-instrumentation-aws-lambda/package.json b/plugins/node/opentelemetry-instrumentation-aws-lambda/package.json index 8ec0615c19..a2202f1435 100644 --- a/plugins/node/opentelemetry-instrumentation-aws-lambda/package.json +++ b/plugins/node/opentelemetry-instrumentation-aws-lambda/package.json @@ -45,6 +45,8 @@ "devDependencies": { "@opentelemetry/api": "^1.3.0", "@opentelemetry/core": "^1.8.0", + "@opentelemetry/propagator-aws-xray": "^1.25.1", + "@opentelemetry/propagator-aws-xray-lambda": "^0.52.1", "@opentelemetry/sdk-metrics": "^1.8.0", "@opentelemetry/sdk-trace-base": "^1.8.0", "@opentelemetry/sdk-trace-node": "^1.8.0", @@ -56,7 +58,6 @@ }, "dependencies": { "@opentelemetry/instrumentation": "^0.54.0", - "@opentelemetry/propagator-aws-xray": "^1.3.1", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/aws-lambda": "8.10.143" }, diff --git a/plugins/node/opentelemetry-instrumentation-aws-lambda/src/instrumentation.ts b/plugins/node/opentelemetry-instrumentation-aws-lambda/src/instrumentation.ts index db25f2d77b..3dbe50e7ef 100644 --- a/plugins/node/opentelemetry-instrumentation-aws-lambda/src/instrumentation.ts +++ b/plugins/node/opentelemetry-instrumentation-aws-lambda/src/instrumentation.ts @@ -35,14 +35,9 @@ import { SpanKind, SpanStatusCode, TextMapGetter, - TraceFlags, TracerProvider, ROOT_CONTEXT, } from '@opentelemetry/api'; -import { - AWSXRAY_TRACE_ID_HEADER, - AWSXRayPropagator, -} from '@opentelemetry/propagator-aws-xray'; import { SEMATTRS_FAAS_EXECUTION, SEMRESATTRS_CLOUD_ACCOUNT_ID, @@ -60,10 +55,8 @@ import { import { AwsLambdaInstrumentationConfig, EventContextExtractor } from './types'; /** @knipignore */ import { PACKAGE_NAME, PACKAGE_VERSION } from './version'; -import { env } from 'process'; import { LambdaModule } from './internal-types'; -const awsPropagator = new AWSXRayPropagator(); const headerGetter: TextMapGetter = { keys(carrier): string[] { return Object.keys(carrier); @@ -73,7 +66,6 @@ const headerGetter: TextMapGetter = { }, }; -export const traceContextEnvironmentKey = '_X_AMZN_TRACE_ID'; export const lambdaMaxInitInMilliseconds = 10_000; export class AwsLambdaInstrumentation extends InstrumentationBase { @@ -81,18 +73,6 @@ export class AwsLambdaInstrumentation extends InstrumentationBase Promise; constructor(config: AwsLambdaInstrumentationConfig = {}) { - if (config.disableAwsContextPropagation == null) { - if ( - typeof env['OTEL_LAMBDA_DISABLE_AWS_CONTEXT_PROPAGATION'] === - 'string' && - env[ - 'OTEL_LAMBDA_DISABLE_AWS_CONTEXT_PROPAGATION' - ].toLocaleLowerCase() === 'true' - ) { - config = { ...config, disableAwsContextPropagation: true }; - } - } - super(PACKAGE_NAME, PACKAGE_VERSION, config); } @@ -230,7 +210,6 @@ export class AwsLambdaInstrumentation extends InstrumentationBase eventContextExtractor(event, context), e => { @@ -473,10 +428,6 @@ export class AwsLambdaInstrumentation extends InstrumentationBase { new AWSXRayPropagator() ); - const sampledHttpSpanContext: SpanContext = { - traceId: '8a3c60f7d188f8fa79d48a391a778fa7', - spanId: '0000000000000457', - traceFlags: 1, - isRemote: true, - }; - const sampledHttpHeader = serializeSpanContext( - sampledHttpSpanContext, - new W3CTraceContextPropagator() - ); - - const unsampledAwsSpanContext: SpanContext = { - traceId: '8a3c60f7d188f8fa79d48a391a778fa8', - spanId: '0000000000000458', - traceFlags: 0, - isRemote: true, - }; - const unsampledAwsHeader = serializeSpanContext( - unsampledAwsSpanContext, - new AWSXRayPropagator() - ); - - const unsampledHttpSpanContext: SpanContext = { - traceId: '8a3c60f7d188f8fa79d48a391a778fa9', - spanId: '0000000000000459', - traceFlags: 0, - isRemote: true, - }; - const unsampledHttpHeader = serializeSpanContext( - unsampledHttpSpanContext, - new W3CTraceContextPropagator() - ); - const sampledGenericSpanContext: SpanContext = { traceId: '8a3c60f7d188f8fa79d48a391a778faa', spanId: '0000000000000460', @@ -535,47 +503,17 @@ describe('lambda handler', () => { }); describe('with remote parent', () => { - it('uses lambda context if sampled and no http context', async () => { - process.env[traceContextEnvironmentKey] = sampledAwsHeader; - initializeHandler('lambda-test/async.handler'); - - const result = await lambdaRequire('lambda-test/async').handler( - 'arg', - ctx - ); - assert.strictEqual(result, 'ok'); - const spans = memoryExporter.getFinishedSpans(); - const [span] = spans; - assert.strictEqual(spans.length, 1); - assertSpanSuccess(span); - assert.strictEqual( - span.spanContext().traceId, - sampledAwsSpanContext.traceId - ); - assert.strictEqual(span.parentSpanId, sampledAwsSpanContext.spanId); - }); - - it('uses lambda context if unsampled and no http context', async () => { - process.env[traceContextEnvironmentKey] = unsampledAwsHeader; - initializeHandler('lambda-test/async.handler'); - - const result = await lambdaRequire('lambda-test/async').handler( - 'arg', - ctx - ); - assert.strictEqual(result, 'ok'); - const spans = memoryExporter.getFinishedSpans(); - // Parent unsampled so no exported spans. - assert.strictEqual(spans.length, 0); + beforeEach(() => { + propagation.disable(); }); - it('uses lambda context if sampled and http context present', async () => { - process.env[traceContextEnvironmentKey] = sampledAwsHeader; + it('uses globally registered propagator', async () => { + propagation.setGlobalPropagator(new AWSXRayPropagator()); initializeHandler('lambda-test/async.handler'); const proxyEvent = { headers: { - traceparent: sampledHttpHeader, + 'x-amzn-trace-id': sampledAwsHeader, }, }; @@ -585,213 +523,55 @@ describe('lambda handler', () => { ); assert.strictEqual(result, 'ok'); const spans = memoryExporter.getFinishedSpans(); - const [span] = spans; - assert.strictEqual(spans.length, 1); - assertSpanSuccess(span); - assert.strictEqual( - span.spanContext().traceId, - sampledAwsSpanContext.traceId - ); - assert.strictEqual(span.parentSpanId, sampledAwsSpanContext.spanId); - }); - - it('uses http context if sampled and lambda context unsampled', async () => { - process.env[traceContextEnvironmentKey] = unsampledAwsHeader; - initializeHandler('lambda-test/async.handler'); - const proxyEvent = { - headers: { - traceparent: sampledHttpHeader, - }, - }; - - const result = await lambdaRequire('lambda-test/async').handler( - proxyEvent, - ctx - ); - assert.strictEqual(result, 'ok'); - const spans = memoryExporter.getFinishedSpans(); - const [span] = spans; - assert.strictEqual(spans.length, 1); - assertSpanSuccess(span); - assert.strictEqual( - span.spanContext().traceId, - sampledHttpSpanContext.traceId - ); - assert.strictEqual(span.parentSpanId, sampledHttpSpanContext.spanId); - }); - - it('uses http context if unsampled and lambda context unsampled', async () => { - process.env[traceContextEnvironmentKey] = unsampledAwsHeader; - initializeHandler('lambda-test/async.handler'); - - const proxyEvent = { - headers: { - traceparent: unsampledHttpHeader, - }, - }; - - const result = await lambdaRequire('lambda-test/async').handler( - proxyEvent, - ctx - ); - assert.strictEqual(result, 'ok'); - const spans = memoryExporter.getFinishedSpans(); - // Parent unsampled so no spans exported. - assert.strictEqual(spans.length, 0); - }); - - it('ignores sampled lambda context if env OTEL_LAMBDA_DISABLE_AWS_CONTEXT_PROPAGATION is set to "true"', async () => { - process.env['OTEL_LAMBDA_DISABLE_AWS_CONTEXT_PROPAGATION'] = 'true'; - process.env[traceContextEnvironmentKey] = sampledAwsHeader; - initializeHandler('lambda-test/async.handler', {}); - - const result = await lambdaRequire('lambda-test/async').handler( - 'arg', - ctx - ); - assert.strictEqual(result, 'ok'); - const spans = memoryExporter.getFinishedSpans(); - const [span] = spans; assert.strictEqual(spans.length, 1); - assertSpanSuccess(span); - assert.notDeepStrictEqual( - span.spanContext().traceId, + assert.equal( + spans[0].spanContext().traceId, sampledAwsSpanContext.traceId ); - assert.strictEqual(span.parentSpanId, undefined); + assert.equal(spans[0].parentSpanId, sampledAwsSpanContext.spanId); }); - it('ignores sampled lambda context if env OTEL_LAMBDA_DISABLE_AWS_CONTEXT_PROPAGATION is set to "TRUE"', async () => { - process.env['OTEL_LAMBDA_DISABLE_AWS_CONTEXT_PROPAGATION'] = 'TRUE'; - process.env[traceContextEnvironmentKey] = sampledAwsHeader; - initializeHandler('lambda-test/async.handler', {}); - - const result = await lambdaRequire('lambda-test/async').handler( - 'arg', - ctx - ); - assert.strictEqual(result, 'ok'); - const spans = memoryExporter.getFinishedSpans(); - const [span] = spans; - assert.strictEqual(spans.length, 1); - assertSpanSuccess(span); - assert.notDeepStrictEqual( - span.spanContext().traceId, - sampledAwsSpanContext.traceId - ); - assert.strictEqual(span.parentSpanId, undefined); - }); - - it('ignores sampled lambda context if env OTEL_LAMBDA_DISABLE_AWS_CONTEXT_PROPAGATION is set to "True"', async () => { - process.env['OTEL_LAMBDA_DISABLE_AWS_CONTEXT_PROPAGATION'] = 'True'; - process.env[traceContextEnvironmentKey] = sampledAwsHeader; - initializeHandler('lambda-test/async.handler', {}); + it('can extract context from lambda context env variable using a global propagator', async () => { + process.env['_X_AMZN_TRACE_ID'] = sampledAwsHeader; + propagation.setGlobalPropagator(new AWSXRayLambdaPropagator()); + initializeHandler('lambda-test/async.handler'); const result = await lambdaRequire('lambda-test/async').handler( 'arg', ctx ); - assert.strictEqual(result, 'ok'); - const spans = memoryExporter.getFinishedSpans(); - const [span] = spans; - assert.strictEqual(spans.length, 1); - assertSpanSuccess(span); - assert.notDeepStrictEqual( - span.spanContext().traceId, - sampledAwsSpanContext.traceId - ); - assert.strictEqual(span.parentSpanId, undefined); - }); - - it('ignores OTEL_LAMBDA_DISABLE_AWS_CONTEXT_PROPAGATION if `config.disableAwsContextPropagation` is set', async () => { - process.env['OTEL_LAMBDA_DISABLE_AWS_CONTEXT_PROPAGATION'] = 'true'; - process.env[traceContextEnvironmentKey] = sampledAwsHeader; - initializeHandler('lambda-test/async.handler', { - disableAwsContextPropagation: false, - }); - const result = await lambdaRequire('lambda-test/async').handler( - 'arg', - ctx - ); assert.strictEqual(result, 'ok'); const spans = memoryExporter.getFinishedSpans(); - const [span] = spans; - assert.strictEqual(spans.length, 1); - assertSpanSuccess(span); - assert.strictEqual( - span.spanContext().traceId, - sampledAwsSpanContext.traceId - ); - assert.strictEqual(span.parentSpanId, sampledAwsSpanContext.spanId); - }); - it('ignores sampled lambda context if "disableAwsContextPropagation" config option is true', async () => { - process.env[traceContextEnvironmentKey] = sampledAwsHeader; - initializeHandler('lambda-test/async.handler', { - disableAwsContextPropagation: true, - }); - - const result = await lambdaRequire('lambda-test/async').handler( - 'arg', - ctx - ); - assert.strictEqual(result, 'ok'); - const spans = memoryExporter.getFinishedSpans(); - const [span] = spans; assert.strictEqual(spans.length, 1); - assertSpanSuccess(span); - assert.notDeepStrictEqual( - span.spanContext().traceId, + assert.equal( + spans[0].spanContext().traceId, sampledAwsSpanContext.traceId ); - assert.strictEqual(span.parentSpanId, undefined); - }); - - it('takes sampled http context over sampled lambda context if "disableAwsContextPropagation" config option is true', async () => { - process.env[traceContextEnvironmentKey] = sampledAwsHeader; - initializeHandler('lambda-test/async.handler', { - disableAwsContextPropagation: true, - }); - - const proxyEvent = { - headers: { - traceparent: sampledHttpHeader, - }, - }; - - const result = await lambdaRequire('lambda-test/async').handler( - proxyEvent, - ctx - ); - - assert.strictEqual(result, 'ok'); - const spans = memoryExporter.getFinishedSpans(); - const [span] = spans; - assert.strictEqual(spans.length, 1); - assertSpanSuccess(span); - assert.strictEqual( - span.spanContext().traceId, - sampledHttpSpanContext.traceId - ); - assert.strictEqual(span.parentSpanId, sampledHttpSpanContext.spanId); + assert.equal(spans[0].parentSpanId, sampledAwsSpanContext.spanId); }); - it('takes sampled custom context over sampled lambda context if "eventContextExtractor" is defined', async () => { - process.env[traceContextEnvironmentKey] = sampledAwsHeader; + it('used custom eventContextExtractor over global propagator if defined', async () => { + propagation.setGlobalPropagator(new W3CTraceContextPropagator()); const customExtractor = (event: any): OtelContext => { - return propagation.extract(context.active(), event.contextCarrier); + const propagator = new AWSXRayPropagator(); + return propagator.extract( + context.active(), + event.contextCarrier, + defaultTextMapGetter + ); }; initializeHandler('lambda-test/async.handler', { - disableAwsContextPropagation: true, eventContextExtractor: customExtractor, }); const otherEvent = { contextCarrier: { traceparent: sampledGenericSpan, + 'x-amzn-trace-id': sampledAwsHeader, }, }; @@ -807,44 +587,12 @@ describe('lambda handler', () => { assertSpanSuccess(span); assert.strictEqual( span.spanContext().traceId, - sampledGenericSpanContext.traceId - ); - assert.strictEqual(span.parentSpanId, sampledGenericSpanContext.spanId); - }); - - it('prefers to extract baggage over sampled lambda context if "eventContextExtractor" is defined', async () => { - process.env[traceContextEnvironmentKey] = sampledAwsHeader; - const customExtractor = (event: any): OtelContext => { - return propagation.extract( - context.active(), - event.customContextCarrier - ); - }; - - initializeHandler('lambda-test/async.handler_return_baggage', { - disableAwsContextPropagation: true, - eventContextExtractor: customExtractor, - }); - - const baggage = 'abcd=1234'; - const customRemoteEvent = { - customContextCarrier: { - traceparent: sampledGenericSpan, - baggage, - }, - }; - - const lambdaTestAsync = lambdaRequire('lambda-test/async'); - const actual = await lambdaTestAsync.handler_return_baggage( - customRemoteEvent, - ctx + sampledAwsSpanContext.traceId ); - - assert.strictEqual(actual, baggage); + assert.strictEqual(span.parentSpanId, sampledAwsSpanContext.spanId); }); - it('creates trace from ROOT_CONTEXT when "disableAwsContextPropagation" is true, eventContextExtractor is provided, and no custom context is found', async () => { - process.env[traceContextEnvironmentKey] = sampledAwsHeader; + it('creates trace from ROOT_CONTEXT eventContextExtractor is provided, and no custom context is found', async () => { const customExtractor = (event: any): OtelContext => { if (!event.contextCarrier) { return ROOT_CONTEXT; @@ -854,7 +602,6 @@ describe('lambda handler', () => { }; const provider = initializeHandler('lambda-test/async.handler', { - disableAwsContextPropagation: true, eventContextExtractor: customExtractor, }); @@ -873,64 +620,6 @@ describe('lambda handler', () => { const [span] = spans; assert.strictEqual(span.parentSpanId, undefined); }); - - it('passes the lambda context object into the eventContextExtractor for scenarios where it is the otel context carrier', async () => { - process.env[traceContextEnvironmentKey] = sampledAwsHeader; - const customExtractor = ( - event: any, - handlerContext: Context - ): OtelContext => { - const contextCarrier = handlerContext.clientContext?.Custom ?? {}; - return propagation.extract(context.active(), contextCarrier); - }; - - initializeHandler('lambda-test/async.handler', { - disableAwsContextPropagation: true, - eventContextExtractor: customExtractor, - }); - - const otherEvent = {}; - const ctxWithCustomData = { - functionName: 'my_function', - invokedFunctionArn: 'my_arn', - awsRequestId: 'aws_request_id', - clientContext: { - client: { - installationId: '', - appTitle: '', - appVersionName: '', - appVersionCode: '', - appPackageName: '', - }, - Custom: { - traceparent: sampledGenericSpan, - }, - env: { - platformVersion: '', - platform: '', - make: '', - model: '', - locale: '', - }, - }, - } as Context; - - const result = await lambdaRequire('lambda-test/async').handler( - otherEvent, - ctxWithCustomData - ); - - assert.strictEqual(result, 'ok'); - const spans = memoryExporter.getFinishedSpans(); - const [span] = spans; - assert.strictEqual(spans.length, 1); - assertSpanSuccess(span); - assert.strictEqual( - span.spanContext().traceId, - sampledGenericSpanContext.traceId - ); - assert.strictEqual(span.parentSpanId, sampledGenericSpanContext.spanId); - }); }); describe('hooks', () => {