Skip to content

Commit 1b594ca

Browse files
authored
Generate trace ID in correct format (#139)
motivation: more correct Xray TraceID changes: * add AmazonHeaders::generateXRayTraceID * Generate correct trace ID in LocalLambda Server * Generate correct trace ID in LambdaRuntimeClientTest
1 parent b8834b2 commit 1b594ca

File tree

5 files changed

+70
-4
lines changed

5 files changed

+70
-4
lines changed

Sources/AWSLambdaRuntimeCore/Lambda+LocalServer.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ private enum LocalLambda {
276276
response.headers = [
277277
(AmazonHeaders.requestID, self.requestID),
278278
(AmazonHeaders.invokedFunctionARN, "arn:aws:lambda:us-east-1:\(Int16.random(in: Int16.min ... Int16.max)):function:custom-runtime"),
279-
(AmazonHeaders.traceID, "Root=\(Int16.random(in: Int16.min ... Int16.max));Parent=\(Int16.random(in: Int16.min ... Int16.max));Sampled=1"),
279+
(AmazonHeaders.traceID, "Root=\(AmazonHeaders.generateXRayTraceID());Sampled=1"),
280280
(AmazonHeaders.deadline, "\(DispatchWallTime.distantFuture.millisSinceEpoch)"),
281281
]
282282
return response
@@ -295,4 +295,5 @@ private enum LocalLambda {
295295
case cantBind
296296
}
297297
}
298+
298299
#endif

Sources/AWSLambdaRuntimeCore/Utils.swift

+26
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,29 @@ extension String {
104104
bytes.append(UInt8(ascii: "\""))
105105
}
106106
}
107+
108+
extension AmazonHeaders {
109+
/// Generates (X-Ray) trace ID.
110+
/// # Trace ID Format
111+
/// A `trace_id` consists of three numbers separated by hyphens.
112+
/// For example, `1-58406520-a006649127e371903a2de979`. This includes:
113+
/// - The version number, that is, 1.
114+
/// - The time of the original request, in Unix epoch time, in **8 hexadecimal digits**.
115+
/// For example, 10:00AM December 1st, 2016 PST in epoch time is `1480615200` seconds, or `58406520` in hexadecimal digits.
116+
/// - A 96-bit identifier for the trace, globally unique, in **24 hexadecimal digits**.
117+
/// # References
118+
/// - [Generating trace IDs](https://docs.aws.amazon.com/xray/latest/devguide/xray-api-sendingdata.html#xray-api-traceids)
119+
/// - [Tracing header](https://docs.aws.amazon.com/xray/latest/devguide/xray-concepts.html#xray-concepts-tracingheader)
120+
internal static func generateXRayTraceID() -> String {
121+
// The version number, that is, 1.
122+
let version: UInt = 1
123+
// The time of the original request, in Unix epoch time, in 8 hexadecimal digits.
124+
let now = UInt32(DispatchWallTime.now().millisSinceEpoch / 1000)
125+
let dateValue = String(now, radix: 16, uppercase: false)
126+
let datePadding = String(repeating: "0", count: max(0, 8 - dateValue.count))
127+
// A 96-bit identifier for the trace, globally unique, in 24 hexadecimal digits.
128+
let identifier = String(UInt64.random(in: UInt64.min ... UInt64.max) | 1 << 63, radix: 16, uppercase: false)
129+
+ String(UInt32.random(in: UInt32.min ... UInt32.max) | 1 << 31, radix: 16, uppercase: false)
130+
return "\(version)-\(datePadding)\(dateValue)-\(identifier)"
131+
}
132+
}

Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeClientTest.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ class LambdaRuntimeClientTest: XCTestCase {
257257
(AmazonHeaders.requestID, "test"),
258258
(AmazonHeaders.deadline, String(Date(timeIntervalSinceNow: 60).millisSinceEpoch)),
259259
(AmazonHeaders.invokedFunctionARN, "arn:aws:lambda:us-east-1:123456789012:function:custom-runtime"),
260-
(AmazonHeaders.traceID, "Root=1-5bef4de7-ad49b0e87f6ef6c87fc2e700;Parent=9a9197af755a6419;Sampled=1"),
260+
(AmazonHeaders.traceID, "Root=\(AmazonHeaders.generateXRayTraceID());Sampled=1"),
261261
])
262262
var inv: Lambda.Invocation?
263263
XCTAssertNoThrow(inv = try Lambda.Invocation(headers: header))
@@ -297,7 +297,7 @@ class LambdaRuntimeClientTest: XCTestCase {
297297
(AmazonHeaders.requestID, "test"),
298298
(AmazonHeaders.deadline, String(Date(timeIntervalSinceNow: 60).millisSinceEpoch)),
299299
(AmazonHeaders.invokedFunctionARN, "arn:aws:lambda:us-east-1:123456789012:function:custom-runtime"),
300-
(AmazonHeaders.traceID, "Root=1-5bef4de7-ad49b0e87f6ef6c87fc2e700;Parent=9a9197af755a6419;Sampled=1"),
300+
(AmazonHeaders.traceID, "Root=\(AmazonHeaders.generateXRayTraceID());Sampled=1"),
301301
])
302302
var inv: Lambda.Invocation?
303303
XCTAssertNoThrow(inv = try Lambda.Invocation(headers: header))

Tests/AWSLambdaRuntimeCoreTests/MockLambdaServer.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ internal final class HTTPHandler: ChannelInboundHandler {
144144
responseHeaders = [
145145
(AmazonHeaders.requestID, requestId),
146146
(AmazonHeaders.invokedFunctionARN, "arn:aws:lambda:us-east-1:123456789012:function:custom-runtime"),
147-
(AmazonHeaders.traceID, "Root=1-5bef4de7-ad49b0e87f6ef6c87fc2e700;Parent=9a9197af755a6419;Sampled=1"),
147+
(AmazonHeaders.traceID, "Root=\(AmazonHeaders.generateXRayTraceID());Sampled=1"),
148148
(AmazonHeaders.deadline, String(deadline)),
149149
]
150150
case .failure(let error):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftAWSLambdaRuntime open source project
4+
//
5+
// Copyright (c) 2017-2018 Apple Inc. and the SwiftAWSLambdaRuntime project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
@testable import AWSLambdaRuntimeCore
16+
import XCTest
17+
18+
class UtilsTest: XCTestCase {
19+
func testGenerateXRayTraceID() {
20+
// the time and identifier should be in hexadecimal digits
21+
let invalidCharacters = CharacterSet(charactersIn: "abcdef0123456789").inverted
22+
let numTests = 1000
23+
var values = Set<String>()
24+
for _ in 0 ..< numTests {
25+
// check the format, see https://docs.aws.amazon.com/xray/latest/devguide/xray-api-sendingdata.html#xray-api-traceids)
26+
let traceId = AmazonHeaders.generateXRayTraceID()
27+
let segments = traceId.split(separator: "-")
28+
XCTAssertEqual(3, segments.count)
29+
XCTAssertEqual("1", segments[0])
30+
XCTAssertEqual(8, segments[1].count)
31+
XCTAssertNil(segments[1].rangeOfCharacter(from: invalidCharacters))
32+
XCTAssertEqual(24, segments[2].count)
33+
XCTAssertNil(segments[2].rangeOfCharacter(from: invalidCharacters))
34+
values.insert(traceId)
35+
}
36+
// check that the generated values are different
37+
XCTAssertEqual(values.count, numTests)
38+
}
39+
}

0 commit comments

Comments
 (0)