Skip to content

Commit 4e44d73

Browse files
[SVLS-7360] Fix Exception Replay in Lambda (#8043)
## Summary of changes - Sends the placeholder tracer span to the Lambda extension as `dd-tracer-serverless-span` - Sets the Lambda request ID on the span so that the extension can copy the tracer tags to the `aws.lambda` span that is created in the extension ## Reason for change To support Exception Replay in Lambda ## Test coverage Manually tested with Lambda <img width="2140" height="1456" alt="image" src="https://github.com/user-attachments/assets/ab52c958-914f-4e39-ace4-3ce73f0e78fe" /> ## Other details <!-- Fixes #{issue} --> [SVLS-7360](https://datadoghq.atlassian.net/browse/SVLS-7360) <!-- ⚠️ Note: Where possible, please obtain 2 approvals prior to merging. Unless CODEOWNERS specifies otherwise, for external teams it is typically best to have one review from a team member, and one review from apm-dotnet. Trivial changes do not require 2 reviews. MergeQueue is NOT enabled in this repository. If you have write access to the repo, the PR has 1-2 approvals (see above), and all of the required checks have passed, you can use the Squash and Merge button to merge the PR. If you don't have write access, or you need help, reach out in the #apm-dotnet channel in Slack. --> [SVLS-7360]: https://datadoghq.atlassian.net/browse/SVLS-7360?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
1 parent 3263efa commit 4e44d73

File tree

2 files changed

+812
-169
lines changed

2 files changed

+812
-169
lines changed

tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AWS/Lambda/LambdaCommon.cs

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,35 +20,45 @@ namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.AWS.Lambda;
2020

2121
internal abstract class LambdaCommon
2222
{
23-
private const string PlaceholderServiceName = "placeholder-service";
24-
private const string PlaceholderOperationName = "placeholder-operation";
23+
// Name of the placeholder span to be filtered out by the Lambda Extension
24+
private const string InvocationSpanName = "dd-tracer-serverless-span";
2525
private const double ServerlessMaxWaitingFlushTime = 3;
2626
private const string LogLevelEnvName = "DD_LOG_LEVEL";
2727
private const string LambdaRuntimeAwsRequestIdHeader = "lambda-runtime-aws-request-id";
2828

29-
internal static Scope CreatePlaceholderScope(Tracer tracer, NameValueHeadersCollection headers)
29+
internal static Scope CreatePlaceholderScope(Tracer tracer, NameValueHeadersCollection headers, string awsRequestId = null)
3030
{
3131
var context = tracer.TracerManager.SpanContextPropagator.Extract(headers).MergeBaggageInto(Baggage.Current);
3232

3333
var span = tracer.StartSpan(
34-
PlaceholderOperationName,
34+
operationName: InvocationSpanName,
3535
tags: null,
3636
parent: context.SpanContext,
37-
serviceName: PlaceholderServiceName,
38-
addToTraceContext: false);
37+
serviceName: InvocationSpanName,
38+
addToTraceContext: true);
39+
40+
// The Lambda extension uses the resource name to identify placeholder span
41+
span.ResourceName = InvocationSpanName;
42+
43+
// Need to set request_id to copy tracer tags to the aws.lambda span
44+
if (awsRequestId != null)
45+
{
46+
span.SetTag("request_id", awsRequestId);
47+
}
3948

4049
TelemetryFactory.Metrics.RecordCountSpanCreated(MetricTags.IntegrationName.AwsLambda);
41-
return tracer.TracerManager.ScopeManager.Activate(span, false);
50+
return tracer.TracerManager.ScopeManager.Activate(span, finishOnClose: true);
4251
}
4352

4453
internal static Scope SendStartInvocation(ILambdaExtensionRequest requestBuilder, string data, ILambdaContext context)
4554
{
4655
var request = requestBuilder.GetStartInvocationRequest();
4756
WriteRequestPayload(request, data);
4857
WriteRequestHeaders(request, context?.ClientContext?.Custom);
49-
if (context?.AwsRequestId != null)
58+
var awsRequestId = context?.AwsRequestId;
59+
if (awsRequestId != null)
5060
{
51-
request.Headers.Add(LambdaRuntimeAwsRequestIdHeader, context.AwsRequestId);
61+
request.Headers.Add(LambdaRuntimeAwsRequestIdHeader, awsRequestId);
5262
}
5363

5464
using var response = (HttpWebResponse)request.GetResponse();
@@ -60,7 +70,7 @@ internal static Scope SendStartInvocation(ILambdaExtensionRequest requestBuilder
6070
}
6171

6272
var tracer = Tracer.Instance;
63-
return CreatePlaceholderScope(tracer, headers);
73+
return CreatePlaceholderScope(tracer, headers, awsRequestId);
6474
}
6575

6676
internal static void SendEndInvocation(ILambdaExtensionRequest requestBuilder, CallTargetState stateObject, bool isError, string data)
@@ -78,6 +88,14 @@ internal static void SendEndInvocation(ILambdaExtensionRequest requestBuilder, C
7888
internal static async Task EndInvocationAsync(string returnValue, Exception exception, CallTargetState stateObject, ILambdaExtensionRequest requestBuilder)
7989
{
8090
var scope = stateObject.Scope;
91+
92+
if (exception != null && scope is { Span: var span })
93+
{
94+
span.SetException(exception);
95+
}
96+
97+
scope?.Dispose();
98+
8199
try
82100
{
83101
await Task.WhenAll(
@@ -94,19 +112,12 @@ await Task.WhenAll(
94112

95113
try
96114
{
97-
if (exception != null && scope is { Span: var span })
98-
{
99-
span.SetException(exception);
100-
}
101-
102115
SendEndInvocation(requestBuilder, stateObject, exception != null, returnValue);
103116
}
104117
catch (Exception ex)
105118
{
106119
Log("Could not send payload to the extension", ex, false);
107120
}
108-
109-
scope?.Dispose();
110121
}
111122

112123
private static bool ValidateOkStatus(HttpWebResponse response)

0 commit comments

Comments
 (0)