Skip to content

Commit c5df0fb

Browse files
committed
add basic header propagation using swift-distributed-tracing Instrument
1 parent 621e30d commit c5df0fb

File tree

5 files changed

+157
-1
lines changed

5 files changed

+157
-1
lines changed

[email protected]

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// swift-tools-version:6.0
2+
//===----------------------------------------------------------------------===//
3+
//
4+
// This source file is part of the AsyncHTTPClient open source project
5+
//
6+
// Copyright (c) 2018-2019 Apple Inc. and the AsyncHTTPClient project authors
7+
// Licensed under Apache License v2.0
8+
//
9+
// See LICENSE.txt for license information
10+
// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors
11+
//
12+
// SPDX-License-Identifier: Apache-2.0
13+
//
14+
//===----------------------------------------------------------------------===//
15+
16+
import PackageDescription
17+
18+
let strictConcurrencyDevelopment = false
19+
20+
let strictConcurrencySettings: [SwiftSetting] = {
21+
var initialSettings: [SwiftSetting] = []
22+
initialSettings.append(contentsOf: [
23+
.enableUpcomingFeature("StrictConcurrency"),
24+
.enableUpcomingFeature("InferSendableFromCaptures"),
25+
])
26+
27+
if strictConcurrencyDevelopment {
28+
// -warnings-as-errors here is a workaround so that IDE-based development can
29+
// get tripped up on -require-explicit-sendable.
30+
initialSettings.append(.unsafeFlags(["-Xfrontend", "-require-explicit-sendable", "-warnings-as-errors"]))
31+
}
32+
33+
return initialSettings
34+
}()
35+
36+
let package = Package(
37+
name: "async-http-client",
38+
platforms: [ // FIXME: must remove this
39+
.macOS("10.15")
40+
],
41+
products: [
42+
.library(name: "AsyncHTTPClient", targets: ["AsyncHTTPClient"])
43+
],
44+
traits: [
45+
.trait(name: "TracingSupport"),
46+
.default(enabledTraits: ["TracingSupport"]),
47+
],
48+
dependencies: [
49+
.package(url: "https://github.com/apple/swift-nio.git", from: "2.81.0"),
50+
.package(url: "https://github.com/apple/swift-nio-ssl.git", from: "2.30.0"),
51+
.package(url: "https://github.com/apple/swift-nio-http2.git", from: "1.36.0"),
52+
.package(url: "https://github.com/apple/swift-nio-extras.git", from: "1.26.0"),
53+
.package(url: "https://github.com/apple/swift-nio-transport-services.git", from: "1.24.0"),
54+
.package(url: "https://github.com/apple/swift-log.git", from: "1.6.0"),
55+
.package(url: "https://github.com/apple/swift-atomics.git", from: "1.0.2"),
56+
.package(url: "https://github.com/apple/swift-algorithms.git", from: "1.0.0"),
57+
.package(url: "https://github.com/apple/swift-distributed-tracing.git", from: "1.0.0"),
58+
],
59+
targets: [
60+
.target(
61+
name: "CAsyncHTTPClient",
62+
cSettings: [
63+
.define("_GNU_SOURCE")
64+
]
65+
),
66+
.target(
67+
name: "AsyncHTTPClient",
68+
dependencies: [
69+
.target(name: "CAsyncHTTPClient"),
70+
.product(name: "NIO", package: "swift-nio"),
71+
.product(name: "NIOTLS", package: "swift-nio"),
72+
.product(name: "NIOCore", package: "swift-nio"),
73+
.product(name: "NIOPosix", package: "swift-nio"),
74+
.product(name: "NIOHTTP1", package: "swift-nio"),
75+
.product(name: "NIOConcurrencyHelpers", package: "swift-nio"),
76+
.product(name: "NIOHTTP2", package: "swift-nio-http2"),
77+
.product(name: "NIOSSL", package: "swift-nio-ssl"),
78+
.product(name: "NIOHTTPCompression", package: "swift-nio-extras"),
79+
.product(name: "NIOSOCKS", package: "swift-nio-extras"),
80+
.product(name: "NIOTransportServices", package: "swift-nio-transport-services"),
81+
.product(name: "Logging", package: "swift-log"),
82+
.product(name: "Atomics", package: "swift-atomics"),
83+
.product(name: "Algorithms", package: "swift-algorithms"),
84+
// Observability support
85+
.product(name: "Tracing", package: "swift-distributed-tracing", condition: .when(traits: ["TracingSupport"])),
86+
],
87+
swiftSettings: strictConcurrencySettings
88+
),
89+
.testTarget(
90+
name: "AsyncHTTPClientTests",
91+
dependencies: [
92+
.target(name: "AsyncHTTPClient"),
93+
.product(name: "NIOTLS", package: "swift-nio"),
94+
.product(name: "NIOCore", package: "swift-nio"),
95+
.product(name: "NIOConcurrencyHelpers", package: "swift-nio"),
96+
.product(name: "NIOEmbedded", package: "swift-nio"),
97+
.product(name: "NIOFoundationCompat", package: "swift-nio"),
98+
.product(name: "NIOTestUtils", package: "swift-nio"),
99+
.product(name: "NIOSSL", package: "swift-nio-ssl"),
100+
.product(name: "NIOHTTP2", package: "swift-nio-http2"),
101+
.product(name: "NIOSOCKS", package: "swift-nio-extras"),
102+
.product(name: "Logging", package: "swift-log"),
103+
.product(name: "Atomics", package: "swift-atomics"),
104+
.product(name: "Algorithms", package: "swift-algorithms"),
105+
],
106+
resources: [
107+
.copy("Resources/self_signed_cert.pem"),
108+
.copy("Resources/self_signed_key.pem"),
109+
.copy("Resources/example.com.cert.pem"),
110+
.copy("Resources/example.com.private-key.pem"),
111+
],
112+
swiftSettings: strictConcurrencySettings
113+
),
114+
]
115+
)
116+
117+
// --- STANDARD CROSS-REPO SETTINGS DO NOT EDIT --- //
118+
for target in package.targets {
119+
switch target.type {
120+
case .regular, .test, .executable:
121+
var settings = target.swiftSettings ?? []
122+
// https://github.com/swiftlang/swift-evolution/blob/main/proposals/0444-member-import-visibility.md
123+
settings.append(.enableUpcomingFeature("MemberImportVisibility"))
124+
target.swiftSettings = settings
125+
case .macro, .plugin, .system, .binary:
126+
() // not applicable
127+
@unknown default:
128+
() // we don't know what to do here, do nothing
129+
}
130+
}
131+
// --- END: STANDARD CROSS-REPO SETTINGS DO NOT EDIT --- //

Sources/AsyncHTTPClient/AsyncAwait/HTTPClient+execute.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,13 @@ extension HTTPClient {
156156
) async throws -> HTTPClientResponse {
157157
let cancelHandler = TransactionCancelHandler()
158158

159+
#if TracingSupport
160+
var request = request
161+
if let context = ServiceContext.current {
162+
configuration.tracer?.inject(context, into: &request.head.headers, using: HTTPHeadersInjector.shared)
163+
}
164+
#endif
165+
159166
return try await withTaskCancellationHandler(
160167
operation: { () async throws -> HTTPClientResponse in
161168
let eventLoop = self.eventLoopGroup.any()

Sources/AsyncHTTPClient/HTTPClient.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -926,6 +926,8 @@ public final class HTTPClient: Sendable {
926926
/// (including `nil` in order to disable traces), the default global bootstrapped tracer will
927927
/// be stored in this property, and used for all subsequent requests made by this client.
928928
public var tracer: (any Tracer)? = InstrumentationSystem.tracer
929+
#else
930+
let tracer: TracingSupportDisabledTracer? = nil
929931
#endif
930932

931933
public init(

Sources/AsyncHTTPClient/RequestBag+Tracing.swift renamed to Sources/AsyncHTTPClient/TracingSupport.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,18 @@ import NIOSSL
2222
import Tracing
2323
#endif
2424

25+
#if TracingSupport
26+
struct HTTPHeadersInjector: Injector, @unchecked Sendable {
27+
static let shared: HTTPHeadersInjector = HTTPHeadersInjector()
28+
29+
private init() {}
30+
31+
func inject(_ value: String, forKey name: String, into headers: inout HTTPHeaders) {
32+
headers.add(name: name, value: value)
33+
}
34+
}
35+
#endif // TracingSupport
36+
2537
#if TracingSupport
2638
typealias HTTPClientTracingSupportTracerType = Tracer
2739
#else

Tests/AsyncHTTPClientTests/HTTPClientTracingTests.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,14 @@ final class HTTPClientTracingTests: XCTestCaseHTTPClientTestsBaseClass {
4848
return
4949
}
5050

51-
let expectedStatusCode: SpanAttribute = 200
51+
let expectedStatusCode: SpanAttribute = .int64(Int64(response.status.code))
5252
XCTAssertEqual(expectedStatusCode, span.attributes["http.response.status_code"]?.toSpanAttribute())
5353

5454
XCTAssertEqual("GET", span.operationName)
5555
#endif // !TracingSupport
5656
}
57+
58+
func testBasicHeaderPropagation() throws {
59+
// TODO: add a test that we propagate trace headers etc
60+
}
5761
}

0 commit comments

Comments
 (0)