Skip to content

@opentelemetry/instrumentation-aws-lambda/AwsLambdaInstrumentation requestHook not working as expected. #2890

@MutazAshhab

Description

@MutazAshhab

What happened?

Here's the updated bug report with your actual tracing configuration:

Steps to Reproduce

  1. Create a Node.js AWS Lambda function using the lambda-api framework
  2. Configure OpenTelemetry with AWS Lambda auto-instrumentation using the following setup:
// config/tracing.ts
import { diag, DiagConsoleLogger, DiagLogLevel } from "@opentelemetry/api";
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
// import { AwsLambdaInstrumentation } from "@opentelemetry/instrumentation-aws-lambda"; dependency installed when needed and removed later
import { NodeSDK } from "@opentelemetry/sdk-node";

diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.ERROR);

export const tracingSdk = new NodeSDK({
  traceExporter: new OTLPTraceExporter(),
  instrumentations: [
    getNodeAutoInstrumentations({
      "@opentelemetry/instrumentation-fs": {
        enabled: false,
      },
      "@opentelemetry/instrumentation-aws-lambda": {
        enabled: true,
        requestHook: (span, hookInfo) => {
          console.log("Lambda instrumentation created span:", span.spanContext());
          span.setAttribute("handler", hookInfo.context.functionName);
          console.dir({ span, hookInfo }, { depth: null });
        },
      },
      "@opentelemetry/instrumentation-mongodb": {
        enabled: true,
        enhancedDatabaseReporting: true,
      },
    }),
     // new AwsLambdaInstrumentation(), // Ive also tried this, when i tried 
     // it i had commented out the "@opentelemetry/instrumentation-aws-lambda" section in getNodeAutoInstrumentations
  ],
});

tracingSdk.start();
  1. Import tracing first in main handler:
// CRITICAL: Import tracing FIRST before any other application imports
import { tracingSdk } from "./config/tracing";

import {
  APIGatewayProxyEvent,
  APIGatewayProxyResult,
  Context,
} from "aws-lambda";
import { api } from "./config/application";
import { MongodbClientService } from "./services/database/mongodb-client-service";
import { trace } from "@opentelemetry/api";

type RouterEvent = APIGatewayProxyEvent;

export const handler = async (
  event: RouterEvent,
  context: Context
): Promise<APIGatewayProxyResult> => {
  console.log(
    "🔍 STEP 1 - Handler entry:",
    trace.getActiveSpan() ? "Span exists" : "No span"
  );

  await MongodbClientService.connect();

  console.log(
    "🔍 STEP 2 - Before api.run():",
    trace.getActiveSpan() ? "Span exists" : "No span"
  );

  const result = await api.run(event, context);

  console.log(
    "🔍 STEP 3 - After api.run():",
    trace.getActiveSpan() ? "Span exists" : "No span"
  );

  tracingSdk.shutdown(); // Have tried with and without this line of code.
  return result;
};
  1. Deploy to AWS Lambda and invoke the function

Expected Result

  • AWS Lambda auto-instrumentation should automatically create a root span when the Lambda function is invoked
  • trace.getActiveSpan() should return a valid span object both in the main handler
  • The requestHook callback should be triggered and log span creation details
  • Traces should be exported to the configured OTLP endpoint

Actual Result

  • No root span is created by the AWS Lambda auto-instrumentation
  • trace.getActiveSpan() returns undefined throughout the entire execution flow
  • The requestHook callback is never triggered (no logs appear in CloudWatch)
  • No traces are exported
  • Both the auto-instrumentation configuration and explicit AwsLambdaInstrumentation instance fail to create a root span

CloudWatch logs show:

🔍 STEP 1 - Handler entry: No span
🔍 STEP 2 - Before api.run(): No span
🔍 STEP 3 - After api.run(): No span
Image

Additional Details

Framework and Dependencies:

  • lambda-api framework for handling HTTP routing in Lambda
  • Node.js 20.x runtime in AWS Lambda
  • TypeScript compilation
  • Module type: CommonJS

Configuration Notes:

  • Using both getNodeAutoInstrumentations with AWS Lambda instrumentation enabled AND an explicit AwsLambdaInstrumentation instance
  • DiagLogLevel set to ERROR - no OpenTelemetry diagnostic logs appear
  • MongoDB instrumentation works correctly when I manually create an active span

Environment:

  • AWS Lambda with Node.js 20.x runtime
  • Function invoked via API Gateway
  • OpenTelemetry environment variables configured:
    • OTEL_SERVICE_NAME='application'
    • OTEL_EXPORTER_OTLP_PROTOCOL='http/protobuf'
    • OTEL_EXPORTER_OTLP_TRACES_ENDPOINT='https://api.honeycomb.io/v1/traces'
    • OTEL_EXPORTER_OTLP_HEADERS='x-honeycomb-team=API_KEY'

☝ data in honeycomb is present when i manually create an active span, im not facing a honey comb issue.

Behavior observations:

  • The tracing module loads successfully
  • OpenTelemetry SDK starts without errors
  • Neither the auto-instrumentation nor explicit instrumentation instances trigger
  • Manual span creation using tracer.startActiveSpan() works correctly and creates proper trace context
  • Other auto-instrumentations (MongoDB) work as expected when manual root spans are present

Code execution flow:

  1. AWS Lambda runtime invokes handler function
  2. Handler performs setup operations (database connection, etc.)
  3. Handler delegates to lambda-api via api.run(event, context)
  4. lambda-api processes routing and calls controller methods
  5. Throughout this entire flow, no automatic spans are created despite dual Lambda instrumentation configuration

Successful workaround:
Manual root span creation using tracer.startActiveSpan() in the handler successfully creates trace context that propagates correctly to all downstream operations including controller methods and MongoDB operations.

OpenTelemetry Setup Code

import { diag, DiagConsoleLogger, DiagLogLevel } from "@opentelemetry/api";
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { AwsLambdaInstrumentation } from "@opentelemetry/instrumentation-aws-lambda";
import { NodeSDK } from "@opentelemetry/sdk-node";

diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.ERROR);

export const tracingSdk = new NodeSDK({
  traceExporter: new OTLPTraceExporter(),
  instrumentations: [
    getNodeAutoInstrumentations({
      "@opentelemetry/instrumentation-fs": {
        enabled: false,
      },
      "@opentelemetry/instrumentation-aws-lambda": {
        enabled: true,
        requestHook: (span, hookInfo) => {
          console.log("Lambda instrumentation created span:", span.spanContext());
          span.setAttribute("handler", hookInfo.context.functionName);
          console.dir({ span, hookInfo }, { depth: null });
        },
      },
      "@opentelemetry/instrumentation-mongodb": {
        enabled: true,
        enhancedDatabaseReporting: true,
      },
    }),
    new AwsLambdaInstrumentation(), // Explicit additional instance
  ],
});

tracingSdk.start();

package.json

{
  "name": "demo-lambda",
  "version": "0.1.0",
  "private": true,
  "sideEffects": false,
  "scripts": {
    "test": "jest --passWithNoTests",
    "build": "node esbuild-builder.js",
    "local": "node ./node_modules/dotenv-cli/cli.js -e ../../.env.overrides -e ../../.env -- ts-node ./src/local.ts",
    "lint:fix": "node ./node_modules/prettier/bin/prettier.cjs --config ../../.prettierrc --write ./src/"
  },
  "unbundledModules": [],
  "engines": {
    "node": ">=20.0.0"
  },
  "dependencies": {
    "aws-lambda": "~1.0.7",
    "lambda-api": "~1.1.0",
    "mongodb": "~6.16.0",
    "@aws-sdk/client-s3": "~3.821.0",
    "@aws-sdk/s3-request-presigner": "~3.821.0",
    "@aws-sdk/client-cognito-identity-provider": "~3.821.0",
    "openai": "~5.3.0",
    "joi": "~17.13.3",
    "@opentelemetry/auto-instrumentations-node": "~0.60.1",
    "@opentelemetry/exporter-trace-otlp-http": "~0.202.0",
    "@opentelemetry/sdk-node": "~0.202.0",
    "@opentelemetry/api": "~1.9.0"
  },
  "devDependencies": {
    "@types/jest": "^29.5.12",
    "@types/node": "18.15.3",
    "jest": "^29.7.0",
    "ts-jest": "^29.1.2",
    "ts-node": "^10.9.2",
    "typescript": "^5.4.2",
    "esbuild": "0.24.0",
    "rimraf": "~6.0.1",
    "@types/aws-lambda": "~8.10.146",
    "dotenv-cli": "~7.2.1",
    "prettier": "~3.5.3"
  }
}

Relevant log output

In cloudwatch when I run with otel debug level diagnostics i get this suspicious log

2025-06-25T02:51:46.740Z	undefined	WARN	@opentelemetry/instrumentation-aws-lambda Module /var/task/main.js has been loaded before @opentelemetry/instrumentation-aws-lambda so it might not work, please initialize it before requiring /var/task/main.js

Operating System and Version

nodejs:20.v65 lambda runtime

Runtime and Version

nodejs:20.v65 lambda runtime

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingpriority:p2Bugs and spec inconsistencies which cause telemetry to be incomplete or incorrect

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions