Skip to content

Bug: fetch requests don't forward X-Ray trace header #3467

Closed
@dreamorosi

Description

@dreamorosi

Expected Behavior

When I make requests using the Fetch API and using Tracer, the X-Amzn-Trace-Id header should be constructed and forwarded along with the request so that I can get a full trace for a request.

For example, if I have an API Gateway A -> Lambda A -> API Gateway B -> Lambda B, and the first function in the chain is instrumented with Tracer, we should be able to see a trace like this:

Image

This is already supported when using the global http and https modules and their derivatives (i.e. axios) but doesn't work in our implementation when using Tracer with fetch.

Current Behavior

Currently the setup described above generates two disconnected traces, one for each API Gateway -> Lambda pair.

Code snippet

import { Logger } from "@aws-lambda-powertools/logger";
import { Tracer } from "@aws-lambda-powertools/tracer";

const logger = new Logger();
const tracer = new Tracer();

export const handler = async (event: { userId: number }) => {
	const parent = tracer.getSegment();
	const segment = parent?.addNewSubsegment("### functionA handler");
	segment && tracer.setSegment(segment);

	await fetch(`${process.env.API_URL}/functionB`, {
		method: "POST",
		body: JSON.stringify({ userId: event.userId }),
	});

	segment?.close();
	parent && tracer.setSegment(parent);

	return {
		statusCode: 200,
	};
};

Steps to Reproduce

  1. Deploy the infrastructure below
/// <reference path="./.sst/platform/config.d.ts" />

export default $config({
	app(input) {
		return {
			name: "missing-trace",
			removal: input?.stage === "production" ? "retain" : "remove",
			home: "aws",
		};
	},
	async run() {
		$transform(sst.aws.Function, (args) => {
			args.permissions = [
				{
					actions: [
						"xray:PutTraceSegments",
						"xray:PutTelemetryRecords",
						"xray:GetSamplingRules",
						"xray:GetSamplingTargets",
						"xray:GetSamplingStatisticSummaries",
					],
					resources: ["*"],
				},
			];
			args.transform = {
				function(args, opts, name) {
					args.tracingConfig = {
						mode: "Active",
					};
				},
			};
		});

		const apiB = new sst.aws.ApiGatewayV1("ApiB", {
			transform: {
				stage(args, opts, name) {
					args.xrayTracingEnabled = true;
				},
			},
		});

		apiB.route("POST /functionB", {
			handler: "src/functionB.handler",
			runtime: "nodejs22.x",
			timeout: "30 seconds",
			memory: "256 MB",
			logging: {
				retention: "1 day",
			},
		});

		apiB.deploy();

		const apiA = new sst.aws.ApiGatewayV1("ApiA", {
			transform: {
				stage(args, opts, name) {
					args.xrayTracingEnabled = true;
				},
			},
		});

		apiA.route("POST /functionA", {
			handler: "src/functionA.handler",
			runtime: "nodejs22.x",
			timeout: "30 seconds",
			memory: "256 MB",
			logging: {
				retention: "1 day",
			},
			environment: {
				API_URL: apiB.url,
			},
		});

		apiA.deploy();
	},
});
  1. Make a request to the first API Gateway endpoint (i.e. http POST https://api-id.execute-api.eu-west-1.amazonaws.com/stage/functionA userId=1
  2. Observe the traces

Possible Solution

Our implementation of the fetch module diverges significantly from the one in aws-xray-sdk-node-fetch. Their implementation relies on monkey patching, which as far as I can tell would only work with CJS.

On our side we decided (#1619) to instead use the node:diagnostics_channel which is the recommended way to instrument requests made with fetch.

During the implementation, we followed the types present in @types/node which suggest it's not possible to modify the Request object when instrumenting.

In reality however request object in the message has a addHeader() method that can be used to forward the X-Amzn-Trace-Id.

To make this work, we should construct the header to include the Root, Parent, and Sampled fields and add it to the request. I made a PoC of this and I already got it working. The screenshot at the top of the issue comes from a request instrumented with Tracer using Fetch.

Powertools for AWS Lambda (TypeScript) version

latest

AWS Lambda function runtime

22.x

Packaging format used

npm

Execution logs

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingcompletedThis item is complete and has been merged/shippedtracerThis item relates to the Tracer Utility

Type

No type

Projects

Status

Shipped

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions