diff --git a/plugins/node/opentelemetry-instrumentation-aws-lambda/README.md b/plugins/node/opentelemetry-instrumentation-aws-lambda/README.md index d4e090b46df..8a3c533c5cb 100644 --- a/plugins/node/opentelemetry-instrumentation-aws-lambda/README.md +++ b/plugins/node/opentelemetry-instrumentation-aws-lambda/README.md @@ -52,6 +52,7 @@ In your Lambda function configuration, add or update the `NODE_OPTIONS` environm | `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` | +| `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 @@ -69,6 +70,18 @@ new AwsLambdaInstrumentation({ }) ``` +### Specifying the Lambda Handler + +The instrumentation will attempt to automatically determine the Lambda handler function to instrument. To do this, it relies on the `_HANDLER` environment variable which is [set by the Lambda runtime](https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html#configuration-envvars-runtime). For most use cases, this will accurately represent the handler that should be targeted by this instrumentation. + +There exist use cases where the `_HANDLER` environment variable does not accurately represent the module that should be targeted by this instrumentation. For these use cases, the `lambdaHandler` option can be used to explicitly specify the Lambda handler that should be instrumented. + +To better explain when `lambdaHandler` should be specified, consider how some telemetry tools, such as [Datadog](https://www.datadoghq.com/), are instrumented into the Lambda runtime. Datadog does this by overriding the handler function with a wrapper function that is loaded via a [Lambda Layer](https://docs.aws.amazon.com/lambda/latest/dg/chapter-layers.html). In these examples, the Lambda's handler will point to the Datadog wrapper and not to the actual handler that should be instrumented. In cases like this, `lambdaHandler` should be used to explicitly specify the handler that should be instrumented. + +The `lambdaHandler` should be specified as a string in the format `.`, where `` is the name of the file that contains the handler and `` is the name of the handler function. For example, if the handler is defined in the file `index.js` and the handler function is named `handler`, the `lambdaHandler` should be specified as `index.handler`. + +One way to determine if the `lambdaHandler` option should be used is to check the handler defined on your Lambda. This can be done by determining the value of the `_HANDLER` environment variable or by viewing the **Runtime Settings** of your Lambda in AWS Console. If the handler is what you expect, then the instrumentation should work without the `lambdaHandler` option. If the handler points to something else, then the `lambdaHandler` option should be used to explicitly specify the handler that should be instrumented. + ## Useful links - For more information on OpenTelemetry, visit: diff --git a/plugins/node/opentelemetry-instrumentation-aws-lambda/src/instrumentation.ts b/plugins/node/opentelemetry-instrumentation-aws-lambda/src/instrumentation.ts index 75adb7e3319..ca3d173d822 100644 --- a/plugins/node/opentelemetry-instrumentation-aws-lambda/src/instrumentation.ts +++ b/plugins/node/opentelemetry-instrumentation-aws-lambda/src/instrumentation.ts @@ -97,10 +97,14 @@ export class AwsLambdaInstrumentation extends InstrumentationBase { init() { const taskRoot = process.env.LAMBDA_TASK_ROOT; - const handlerDef = process.env._HANDLER; + const handlerDef = this._config.lambdaHandler ?? process.env._HANDLER; // _HANDLER and LAMBDA_TASK_ROOT are always defined in Lambda but guard bail out if in the future this changes. if (!taskRoot || !handlerDef) { + diag.error( + 'Unable to initialize instrumentation for lambda. Cannot identify lambda handler or task root.', + { taskRoot, handlerDef } + ); return []; } @@ -123,6 +127,16 @@ export class AwsLambdaInstrumentation extends InstrumentationBase { } } + diag.debug('Instrumenting lambda handler', { + taskRoot, + handlerDef, + handler, + moduleRoot, + module, + filename, + functionName, + }); + return [ new InstrumentationNodeModuleDefinition( // NB: The patching infrastructure seems to match names backwards, this must be the filename, while diff --git a/plugins/node/opentelemetry-instrumentation-aws-lambda/src/types.ts b/plugins/node/opentelemetry-instrumentation-aws-lambda/src/types.ts index da507efc094..279eb1a9a7a 100644 --- a/plugins/node/opentelemetry-instrumentation-aws-lambda/src/types.ts +++ b/plugins/node/opentelemetry-instrumentation-aws-lambda/src/types.ts @@ -40,4 +40,5 @@ export interface AwsLambdaInstrumentationConfig extends InstrumentationConfig { responseHook?: ResponseHook; disableAwsContextPropagation?: boolean; eventContextExtractor?: EventContextExtractor; + lambdaHandler?: string; } diff --git a/plugins/node/opentelemetry-instrumentation-aws-lambda/test/integrations/lambda-handler.test.ts b/plugins/node/opentelemetry-instrumentation-aws-lambda/test/integrations/lambda-handler.test.ts index 573cab81400..e8f9bea0836 100644 --- a/plugins/node/opentelemetry-instrumentation-aws-lambda/test/integrations/lambda-handler.test.ts +++ b/plugins/node/opentelemetry-instrumentation-aws-lambda/test/integrations/lambda-handler.test.ts @@ -964,4 +964,25 @@ describe('lambda handler', () => { }); }); }); + + describe('custom handler', () => { + it('prioritizes instrumenting the handler specified on the config over the handler implied from the _HANDLER env var', async () => { + initializeHandler('not-a-real-handler', { + lambdaHandler: 'lambda-test/async.handler', + }); + + const otherEvent = {}; + const result = await lambdaRequire('lambda-test/async').handler( + otherEvent, + ctx + ); + + assert.strictEqual(result, 'ok'); + const spans = memoryExporter.getFinishedSpans(); + const [span] = spans; + assert.strictEqual(spans.length, 1); + assertSpanSuccess(span); + assert.strictEqual(span.parentSpanId, undefined); + }); + }); });