-
Notifications
You must be signed in to change notification settings - Fork 117
Correctly propagate the trace ID #652
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
7f4308d
103a0f9
6f3481d
7dd9665
dd1ad1a
53095b4
7f4dee7
fb2a7db
b2c43fb
d457c2f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,7 @@ import Dispatch | |
| import Logging | ||
| import NIOCore | ||
| import NIOPosix | ||
| import ServiceContextModule | ||
|
|
||
| #if os(macOS) | ||
| import Darwin.C | ||
|
|
@@ -90,12 +91,13 @@ public enum Lambda { | |
|
|
||
| logger.trace("Waiting for next invocation") | ||
| let (invocation, writer) = try await runtimeClient.nextInvocation() | ||
| let traceId = invocation.metadata.traceID | ||
|
|
||
| // Create a per-request logger with request-specific metadata | ||
| let requestLogger = loggingConfiguration.makeLogger( | ||
| label: "Lambda", | ||
| requestID: invocation.metadata.requestID, | ||
| traceID: invocation.metadata.traceID | ||
| traceID: traceId | ||
| ) | ||
|
|
||
| // when log level is trace or lower, print the first 6 Mb of the payload | ||
|
|
@@ -116,26 +118,46 @@ public enum Lambda { | |
| metadata: metadata | ||
| ) | ||
|
|
||
| do { | ||
| try await handler.handle( | ||
| invocation.event, | ||
| responseWriter: writer, | ||
| context: LambdaContext( | ||
| requestID: invocation.metadata.requestID, | ||
| traceID: invocation.metadata.traceID, | ||
| tenantID: invocation.metadata.tenantID, | ||
| invokedFunctionARN: invocation.metadata.invokedFunctionARN, | ||
| deadline: LambdaClock.Instant( | ||
| millisecondsSinceEpoch: invocation.metadata.deadlineInMillisSinceEpoch | ||
| ), | ||
| logger: requestLogger | ||
| // Wrap handler invocation in a ServiceContext scope so that | ||
| // downstream libraries can access the trace ID via | ||
| // ServiceContext.current?.traceID without depending on AWSLambdaRuntime. | ||
| // In single-concurrency mode, also set the _X_AMZN_TRACE_ID env var | ||
| // for backward compatibility with legacy tooling. | ||
| var serviceContext = ServiceContext.current ?? ServiceContext.topLevel | ||
| serviceContext.traceID = traceId | ||
| try await ServiceContext.withValue(serviceContext) { | ||
| if isSingleConcurrencyMode { | ||
| setenv("_X_AMZN_TRACE_ID", traceId, 1) | ||
| } | ||
| defer { | ||
| if isSingleConcurrencyMode { | ||
| unsetenv("_X_AMZN_TRACE_ID") | ||
| } | ||
| } | ||
|
|
||
| do { | ||
| try await handler.handle( | ||
| invocation.event, | ||
| responseWriter: writer, | ||
| context: LambdaContext( | ||
| requestID: invocation.metadata.requestID, | ||
| traceID: traceId, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nitpick: |
||
| tenantID: invocation.metadata.tenantID, | ||
| invokedFunctionARN: invocation.metadata.invokedFunctionARN, | ||
| deadline: LambdaClock.Instant( | ||
| millisecondsSinceEpoch: invocation.metadata.deadlineInMillisSinceEpoch | ||
| ), | ||
| logger: requestLogger | ||
| ) | ||
| ) | ||
| requestLogger.trace("Handler finished processing invocation") | ||
| } catch { | ||
| requestLogger.trace( | ||
| "Handler failed processing invocation", | ||
| metadata: ["Handler error": "\(error)"] | ||
| ) | ||
| ) | ||
| requestLogger.trace("Handler finished processing invocation") | ||
| } catch { | ||
| requestLogger.trace("Handler failed processing invocation", metadata: ["Handler error": "\(error)"]) | ||
| try await writer.reportError(error) | ||
| continue | ||
| try await writer.reportError(error) | ||
| } | ||
| } | ||
| } | ||
| } catch is CancellationError { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // This source file is part of the SwiftAWSLambdaRuntime open source project | ||
| // | ||
| // Copyright SwiftAWSLambdaRuntime project authors | ||
| // Copyright (c) Amazon.com, Inc. or its affiliates. | ||
| // Licensed under Apache License v2.0 | ||
| // | ||
| // See LICENSE.txt for license information | ||
| // See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors | ||
| // | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| import ServiceContextModule | ||
|
|
||
| // MARK: - ServiceContext integration | ||
|
|
||
| /// A ``ServiceContextKey`` for the AWS X-Ray trace ID. | ||
| /// | ||
| /// This allows downstream libraries that depend on `swift-service-context` | ||
| /// (but not on `AWSLambdaRuntime`) to access the current trace ID via | ||
| /// `ServiceContext.current?.traceID`. | ||
| private enum LambdaTraceIDKey: ServiceContextKey { | ||
| typealias Value = String | ||
| static var nameOverride: String? { AmazonHeaders.traceID } | ||
| } | ||
|
|
||
| extension ServiceContext { | ||
| /// The AWS X-Ray trace ID for the current Lambda invocation, if available. | ||
| /// | ||
| /// This value is automatically set by the Lambda runtime before calling the handler | ||
| /// and is available to all code running within the handler's async task tree. | ||
| /// | ||
| /// Downstream libraries can read this without depending on `AWSLambdaRuntime`: | ||
| /// ```swift | ||
| /// if let traceID = ServiceContext.current?.traceID { | ||
| /// // propagate traceID to outgoing HTTP requests, etc. | ||
| /// } | ||
| /// ``` | ||
| public var traceID: String? { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is a bit problematic to declare the key in the lambda runtime, it'd be better if it was declared inside some other library that is "the xray library" and aws lambda runtime would depend on it for the key. This way other libs which want to use xray specifically could do so without conflicting here... I think what we may need to do in the short term -- unless we spin out a lib -- would be to call this WDYT? Also cc @slashmo for opinions
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @maxday How runtimes are supporting this in other languages ?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jbelkins should we create a |
||
| get { | ||
| self[LambdaTraceIDKey.self] | ||
| } | ||
| set { | ||
| self[LambdaTraceIDKey.self] = newValue | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe you want to have a constant for this string somewhere?