Skip to content

Commit b1fa205

Browse files
committed
Tracing support brought back with enabled-by-default trait
1 parent 7dc119c commit b1fa205

15 files changed

+875
-13
lines changed

Package.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// swift-tools-version:5.10
1+
// swift-tools-version:6.1
22
//===----------------------------------------------------------------------===//
33
//
44
// This source file is part of the AsyncHTTPClient open source project
@@ -35,9 +35,16 @@ let strictConcurrencySettings: [SwiftSetting] = {
3535

3636
let package = Package(
3737
name: "async-http-client",
38+
platforms: [ // FIXME: must remove this
39+
.macOS("10.15")
40+
],
3841
products: [
3942
.library(name: "AsyncHTTPClient", targets: ["AsyncHTTPClient"])
4043
],
44+
traits: [
45+
.trait(name: "TracingSupport"),
46+
.default(enabledTraits: ["TracingSupport"]),
47+
],
4148
dependencies: [
4249
.package(url: "https://github.com/apple/swift-nio.git", from: "2.81.0"),
4350
.package(url: "https://github.com/apple/swift-nio-ssl.git", from: "2.30.0"),
@@ -47,6 +54,7 @@ let package = Package(
4754
.package(url: "https://github.com/apple/swift-log.git", from: "1.6.0"),
4855
.package(url: "https://github.com/apple/swift-atomics.git", from: "1.0.2"),
4956
.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.2.0"),
5058
],
5159
targets: [
5260
.target(
@@ -73,6 +81,8 @@ let package = Package(
7381
.product(name: "Logging", package: "swift-log"),
7482
.product(name: "Atomics", package: "swift-atomics"),
7583
.product(name: "Algorithms", package: "swift-algorithms"),
84+
// Observability support
85+
.product(name: "Tracing", package: "swift-distributed-tracing", condition: .when(traits: ["TracingSupport"])),
7686
],
7787
swiftSettings: strictConcurrencySettings
7888
),

Package@swift-6.1.swift

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// swift-tools-version:6.1
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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
import Logging
1616
import NIOCore
1717
import NIOHTTP1
18+
#if TracingSupport
19+
import Tracing
20+
#endif
1821

1922
import struct Foundation.URL
2023

Sources/AsyncHTTPClient/AsyncAwait/Transaction.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ import NIOCore
1818
import NIOHTTP1
1919
import NIOSSL
2020

21+
#if TracingSupport
22+
import Tracing
23+
#endif // TracingSupport
24+
2125
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
2226
@usableFromInline
2327
final class Transaction:
@@ -51,7 +55,8 @@ final class Transaction:
5155
}
5256

5357
func cancel() {
54-
self.fail(CancellationError())
58+
let error = CancellationError()
59+
self.fail(error)
5560
}
5661

5762
// MARK: Request body helpers

Sources/AsyncHTTPClient/HTTPClient.swift

Lines changed: 85 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ import NIOSSL
2424
import NIOTLS
2525
import NIOTransportServices
2626

27+
#if TracingSupport
28+
import Tracing
29+
#endif
30+
2731
extension Logger {
2832
private func requestInfo(_ request: HTTPClient.Request) -> Logger.Metadata.Value {
2933
"\(request.method) \(request.url)"
@@ -57,6 +61,9 @@ let globalRequestID = ManagedAtomic(0)
5761
/// }
5862
/// }
5963
/// ```
64+
// #if TracingSupport
65+
// @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
66+
// #endif
6067
public final class HTTPClient: Sendable {
6168
/// The `EventLoopGroup` in use by this ``HTTPClient``.
6269
///
@@ -71,7 +78,14 @@ public final class HTTPClient: Sendable {
7178
private let state: NIOLockedValueBox<State>
7279
private let canBeShutDown: Bool
7380

74-
static let loggingDisabled = Logger(label: "AHC-do-not-log", factory: { _ in SwiftLogNoOpLogHandler() })
81+
#if TracingSupport
82+
@_spi(Tracing)
83+
public var tracer: (any Tracer)? { // Tracer requires macOS 10.15... so we're forced into annotating HTTPClient :-/
84+
configuration.tracer
85+
}
86+
#endif // TracingSupport
87+
88+
public static let loggingDisabled = Logger(label: "AHC-do-not-log", factory: { _ in SwiftLogNoOpLogHandler() })
7589

7690
/// Create an ``HTTPClient`` with specified `EventLoopGroup` provider and configuration.
7791
///
@@ -354,7 +368,7 @@ public final class HTTPClient: Sendable {
354368
/// - url: Remote URL.
355369
/// - deadline: Point in time by which the request must complete.
356370
public func get(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture<Response> {
357-
self.get(url: url, deadline: deadline, logger: HTTPClient.loggingDisabled)
371+
return self.get(url: url, deadline: deadline, logger: HTTPClient.loggingDisabled)
358372
}
359373

360374
/// Execute `GET` request using specified URL.
@@ -559,7 +573,7 @@ public final class HTTPClient: Sendable {
559573
/// - deadline: Point in time by which the request must complete.
560574
/// - logger: The logger to use for this request.
561575
public func execute(request: Request, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response> {
562-
let accumulator = ResponseAccumulator(request: request)
576+
let accumulator = ResponseAccumulator(request: request) // FIXME: end here?
563577
return self.execute(request: request, delegate: accumulator, deadline: deadline, logger: logger).futureResult
564578
}
565579

@@ -672,7 +686,7 @@ public final class HTTPClient: Sendable {
672686
deadline: NIODeadline? = nil,
673687
logger: Logger?
674688
) -> Task<Delegate.Response> {
675-
self._execute(
689+
return self._execute(
676690
request: request,
677691
delegate: delegate,
678692
eventLoop: eventLoopPreference,
@@ -740,12 +754,22 @@ public final class HTTPClient: Sendable {
740754
return nil
741755
case .shuttingDown, .shutDown:
742756
logger.debug("client is shutting down, failing request")
757+
#if TracingSupport
758+
return Task<Delegate.Response>.failedTask(
759+
eventLoop: taskEL,
760+
error: HTTPClientError.alreadyShutdown,
761+
logger: logger,
762+
tracer: self.configuration.tracer,
763+
makeOrGetFileIOThreadPool: self.makeOrGetFileIOThreadPool
764+
)
765+
#else // not TracingSupport
743766
return Task<Delegate.Response>.failedTask(
744767
eventLoop: taskEL,
745768
error: HTTPClientError.alreadyShutdown,
746769
logger: logger,
747770
makeOrGetFileIOThreadPool: self.makeOrGetFileIOThreadPool
748771
)
772+
#endif // TracingSupport
749773
}
750774
}
751775

@@ -756,6 +780,7 @@ public final class HTTPClient: Sendable {
756780
let redirectHandler: RedirectHandler<Delegate.Response>? = {
757781
guard let redirectState = redirectState else { return nil }
758782

783+
// TODO: span for redirects?
759784
return .init(request: request, redirectState: redirectState) { newRequest, newRedirectState in
760785
self._execute(
761786
request: newRequest,
@@ -768,11 +793,21 @@ public final class HTTPClient: Sendable {
768793
}
769794
}()
770795

796+
#if TracingSupport
771797
let task = Task<Delegate.Response>(
772-
eventLoop: taskEL,
773-
logger: logger,
774-
makeOrGetFileIOThreadPool: self.makeOrGetFileIOThreadPool
775-
)
798+
eventLoop: taskEL,
799+
logger: logger,
800+
tracer: tracer,
801+
makeOrGetFileIOThreadPool: self.makeOrGetFileIOThreadPool
802+
)
803+
#else
804+
let task = Task<Delegate.Response>(
805+
eventLoop: taskEL,
806+
logger: logger,
807+
makeOrGetFileIOThreadPool: self.makeOrGetFileIOThreadPool
808+
)
809+
#endif // TracingSupport
810+
776811
do {
777812
let requestBag = try RequestBag(
778813
request: request,
@@ -884,6 +919,15 @@ public final class HTTPClient: Sendable {
884919
/// A method with access to the HTTP/2 stream channel that is called when creating the stream.
885920
public var http2StreamChannelDebugInitializer: (@Sendable (Channel) -> EventLoopFuture<Void>)?
886921

922+
#if TracingSupport
923+
/// Tracer that should be used by the HTTPClient.
924+
///
925+
/// This is selected at configuration creation time, and if no tracer is passed explicitly,
926+
/// (including `nil` in order to disable traces), the default global bootstrapped tracer will
927+
/// be stored in this property, and used for all subsequent requests made by this client.
928+
public var tracer: (any Tracer)? = InstrumentationSystem.tracer
929+
#endif
930+
887931
public init(
888932
tlsConfiguration: TLSConfiguration? = nil,
889933
redirectConfiguration: RedirectConfiguration? = nil,
@@ -1011,7 +1055,40 @@ public final class HTTPClient: Sendable {
10111055
self.http1_1ConnectionDebugInitializer = http1_1ConnectionDebugInitializer
10121056
self.http2ConnectionDebugInitializer = http2ConnectionDebugInitializer
10131057
self.http2StreamChannelDebugInitializer = http2StreamChannelDebugInitializer
1058+
#if TracingSupport
1059+
self.tracer = InstrumentationSystem.tracer
1060+
#endif
10141061
}
1062+
1063+
#if TracingSupport
1064+
public init(
1065+
tlsConfiguration: TLSConfiguration? = nil,
1066+
redirectConfiguration: RedirectConfiguration? = nil,
1067+
timeout: Timeout = Timeout(),
1068+
connectionPool: ConnectionPool = ConnectionPool(),
1069+
proxy: Proxy? = nil,
1070+
ignoreUncleanSSLShutdown: Bool = false,
1071+
decompression: Decompression = .disabled,
1072+
http1_1ConnectionDebugInitializer: (@Sendable (Channel) -> EventLoopFuture<Void>)? = nil,
1073+
http2ConnectionDebugInitializer: (@Sendable (Channel) -> EventLoopFuture<Void>)? = nil,
1074+
http2StreamChannelDebugInitializer: (@Sendable (Channel) -> EventLoopFuture<Void>)? = nil,
1075+
tracer: (any Tracer)? = InstrumentationSystem.tracer
1076+
) {
1077+
self.init(
1078+
tlsConfiguration: tlsConfiguration,
1079+
redirectConfiguration: redirectConfiguration,
1080+
timeout: timeout,
1081+
connectionPool: connectionPool,
1082+
proxy: proxy,
1083+
ignoreUncleanSSLShutdown: ignoreUncleanSSLShutdown,
1084+
decompression: decompression
1085+
)
1086+
self.http1_1ConnectionDebugInitializer = http1_1ConnectionDebugInitializer
1087+
self.http2ConnectionDebugInitializer = http2ConnectionDebugInitializer
1088+
self.http2StreamChannelDebugInitializer = http2StreamChannelDebugInitializer
1089+
self.tracer = tracer
1090+
}
1091+
#endif
10151092
}
10161093

10171094
/// Specifies how `EventLoopGroup` will be created and establishes lifecycle ownership.

0 commit comments

Comments
 (0)