From d726a1172c5da52078430928c645bdede3bedd6e Mon Sep 17 00:00:00 2001 From: Yiming Luo <10097700+lym953@users.noreply.github.com> Date: Mon, 13 Apr 2026 15:10:50 -0400 Subject: [PATCH 1/3] remove logDebug from durable function tag application Co-Authored-By: Claude Sonnet 4.6 --- src/trace/listener.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/trace/listener.ts b/src/trace/listener.ts index 217ba7a8..76951f4b 100644 --- a/src/trace/listener.ts +++ b/src/trace/listener.ts @@ -227,7 +227,6 @@ export class TraceListener { } } if (this.durableFunctionContext) { - logDebug("Applying durable function context to the aws.lambda span"); for (const [key, value] of Object.entries(this.durableFunctionContext)) { if (value !== undefined) { this.tracerWrapper.currentSpan.setTag(key, value); From 466a04add0e36887c019fa5d2a46b8d6f3241105 Mon Sep 17 00:00:00 2001 From: Yiming Luo <10097700+lym953@users.noreply.github.com> Date: Mon, 13 Apr 2026 15:30:18 -0400 Subject: [PATCH 2/3] add back logDebug for durable function tag application Co-Authored-By: Claude Sonnet 4.6 --- src/trace/listener.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/trace/listener.ts b/src/trace/listener.ts index 76951f4b..217ba7a8 100644 --- a/src/trace/listener.ts +++ b/src/trace/listener.ts @@ -227,6 +227,7 @@ export class TraceListener { } } if (this.durableFunctionContext) { + logDebug("Applying durable function context to the aws.lambda span"); for (const [key, value] of Object.entries(this.durableFunctionContext)) { if (value !== undefined) { this.tracerWrapper.currentSpan.setTag(key, value); From f651e85398c106347a044622627f2b61923273ab Mon Sep 17 00:00:00 2001 From: Yiming Luo <10097700+lym953@users.noreply.github.com> Date: Mon, 13 Apr 2026 14:21:22 -0400 Subject: [PATCH 3/3] feat: [SVLS-8493] add first_invocation tag for durable functions Tag `aws_lambda.durable_function.first_invocation` is set to "true" when `InitialExecutionState.Operations` has exactly one entry (first invocation) and "false" when it has more (replay). Omitted when InitialExecutionState is absent. Co-Authored-By: Claude Sonnet 4.6 --- src/trace/durable-function-context.spec.ts | 38 ++++++++++++++++++++++ src/trace/durable-function-context.ts | 11 ++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/trace/durable-function-context.spec.ts b/src/trace/durable-function-context.spec.ts index ff1ca6ed..689caa5d 100644 --- a/src/trace/durable-function-context.spec.ts +++ b/src/trace/durable-function-context.spec.ts @@ -58,9 +58,47 @@ describe("durable-function-context", () => { expect(result).toEqual({ "aws_lambda.durable_function.execution_name": "my-execution", "aws_lambda.durable_function.execution_id": "550e8400-e29b-41d4-a716-446655440004", + "aws_lambda.durable_function.first_invocation": "false", }); }); + it("sets first_invocation to true when Operations has exactly one entry", () => { + const event = { + DurableExecutionArn: + "arn:aws:lambda:us-east-1:123456789012:function:my-func:1/durable-execution/my-execution/550e8400-e29b-41d4-a716-446655440004", + InitialExecutionState: { + Operations: [{ type: "TaskScheduled" }], + }, + }; + const result = extractDurableFunctionContext(event); + + expect(result?.["aws_lambda.durable_function.first_invocation"]).toBe("true"); + }); + + it("sets first_invocation to false when Operations has more than one entry", () => { + const event = { + DurableExecutionArn: + "arn:aws:lambda:us-east-1:123456789012:function:my-func:1/durable-execution/my-execution/550e8400-e29b-41d4-a716-446655440004", + InitialExecutionState: { + Operations: [{ type: "TaskScheduled" }, { type: "TaskCompleted" }], + }, + }; + const result = extractDurableFunctionContext(event); + + expect(result?.["aws_lambda.durable_function.first_invocation"]).toBe("false"); + }); + + it("omits first_invocation when InitialExecutionState is absent", () => { + const event = { + DurableExecutionArn: + "arn:aws:lambda:us-east-1:123456789012:function:my-func:1/durable-execution/my-execution/550e8400-e29b-41d4-a716-446655440004", + }; + const result = extractDurableFunctionContext(event); + + expect(result).toBeDefined(); + expect(result?.["aws_lambda.durable_function.first_invocation"]).toBeUndefined(); + }); + it("returns undefined for regular Lambda event without DurableExecutionArn", () => { const event = { body: '{"key": "value"}', diff --git a/src/trace/durable-function-context.ts b/src/trace/durable-function-context.ts index 2acf5765..29d4c53c 100644 --- a/src/trace/durable-function-context.ts +++ b/src/trace/durable-function-context.ts @@ -3,6 +3,7 @@ import { logDebug } from "../utils"; export interface DurableFunctionContext { "aws_lambda.durable_function.execution_name": string; "aws_lambda.durable_function.execution_id": string; + "aws_lambda.durable_function.first_invocation"?: string; } export function extractDurableFunctionContext(event: any): DurableFunctionContext | undefined { @@ -18,10 +19,18 @@ export function extractDurableFunctionContext(event: any): DurableFunctionContex return undefined; } - return { + const context: DurableFunctionContext = { "aws_lambda.durable_function.execution_name": parsed.executionName, "aws_lambda.durable_function.execution_id": parsed.executionId, }; + + // Use the number of operations to determine if it's the first invocation. + const operations = event?.InitialExecutionState?.Operations; + if (Array.isArray(operations)) { + context["aws_lambda.durable_function.first_invocation"] = String(operations.length === 1); + } + + return context; } /**