@@ -23,6 +23,7 @@ import NIOPosix
23
23
import NIOSSL
24
24
import NIOTLS
25
25
import NIOTransportServices
26
+ import Tracing
26
27
27
28
extension Logger {
28
29
private func requestInfo( _ request: HTTPClient . Request ) -> Logger . Metadata . Value {
@@ -62,15 +63,29 @@ public final class HTTPClient: Sendable {
62
63
///
63
64
/// All HTTP transactions will occur on loops owned by this group.
64
65
public let eventLoopGroup : EventLoopGroup
65
- let configuration : Configuration
66
66
let poolManager : HTTPConnectionPool . Manager
67
67
68
+ @usableFromInline
69
+ let configuration : Configuration
70
+
68
71
/// Shared thread pool used for file IO. It is lazily created on first access of ``Task/fileIOThreadPool``.
69
72
private let fileIOThreadPool : NIOLockedValueBox < NIOThreadPool ? >
70
73
71
74
private let state : NIOLockedValueBox < State >
72
75
private let canBeShutDown : Bool
73
76
77
+ /// Tracer configured for this HTTPClient at configuration time.
78
+ @available ( macOS 10 . 15 , iOS 13 , tvOS 13 , watchOS 6 , * )
79
+ public var tracer : ( any Tracer ) ? {
80
+ configuration. tracing. tracer
81
+ }
82
+
83
+ /// Access to tracing configuration in order to get configured attribute keys etc.
84
+ @usableFromInline
85
+ package var tracing : TracingConfiguration {
86
+ self . configuration. tracing
87
+ }
88
+
74
89
static let loggingDisabled = Logger ( label: " AHC-do-not-log " , factory: { _ in SwiftLogNoOpLogHandler ( ) } )
75
90
76
91
/// Create an ``HTTPClient`` with specified `EventLoopGroup` provider and configuration.
@@ -705,6 +720,7 @@ public final class HTTPClient: Sendable {
705
720
request,
706
721
requestID: globalRequestID. wrappingIncrementThenLoad ( ordering: . relaxed)
707
722
)
723
+
708
724
let taskEL : EventLoop
709
725
switch eventLoopPreference. preference {
710
726
case . indifferent:
@@ -734,7 +750,7 @@ public final class HTTPClient: Sendable {
734
750
]
735
751
)
736
752
737
- let failedTask : Task < Delegate . Response > ? = self . state. withLockedValue { state in
753
+ let failedTask : Task < Delegate . Response > ? = self . state. withLockedValue { state -> ( Task < Delegate . Response > ? ) in
738
754
switch state {
739
755
case . upAndRunning:
740
756
return nil
@@ -744,6 +760,7 @@ public final class HTTPClient: Sendable {
744
760
eventLoop: taskEL,
745
761
error: HTTPClientError . alreadyShutdown,
746
762
logger: logger,
763
+ tracing: tracing,
747
764
makeOrGetFileIOThreadPool: self . makeOrGetFileIOThreadPool
748
765
)
749
766
}
@@ -768,11 +785,14 @@ public final class HTTPClient: Sendable {
768
785
}
769
786
} ( )
770
787
771
- let task = Task < Delegate . Response > (
772
- eventLoop: taskEL,
773
- logger: logger,
774
- makeOrGetFileIOThreadPool: self . makeOrGetFileIOThreadPool
775
- )
788
+ let task : HTTPClient . Task < Delegate . Response > =
789
+ Task < Delegate . Response > (
790
+ eventLoop: taskEL,
791
+ logger: logger,
792
+ tracing: self . tracing,
793
+ makeOrGetFileIOThreadPool: self . makeOrGetFileIOThreadPool
794
+ )
795
+
776
796
do {
777
797
let requestBag = try RequestBag (
778
798
request: request,
@@ -884,6 +904,9 @@ public final class HTTPClient: Sendable {
884
904
/// A method with access to the HTTP/2 stream channel that is called when creating the stream.
885
905
public var http2StreamChannelDebugInitializer : ( @Sendable ( Channel ) -> EventLoopFuture < Void > ) ?
886
906
907
+ /// Configuration how distributed traces are created and handled.
908
+ public var tracing : TracingConfiguration = . init( )
909
+
887
910
public init (
888
911
tlsConfiguration: TLSConfiguration ? = nil ,
889
912
redirectConfiguration: RedirectConfiguration ? = nil ,
@@ -1012,6 +1035,84 @@ public final class HTTPClient: Sendable {
1012
1035
self . http2ConnectionDebugInitializer = http2ConnectionDebugInitializer
1013
1036
self . http2StreamChannelDebugInitializer = http2StreamChannelDebugInitializer
1014
1037
}
1038
+
1039
+ public init (
1040
+ tlsConfiguration: TLSConfiguration ? = nil ,
1041
+ redirectConfiguration: RedirectConfiguration ? = nil ,
1042
+ timeout: Timeout = Timeout ( ) ,
1043
+ connectionPool: ConnectionPool = ConnectionPool ( ) ,
1044
+ proxy: Proxy ? = nil ,
1045
+ ignoreUncleanSSLShutdown: Bool = false ,
1046
+ decompression: Decompression = . disabled,
1047
+ http1_1ConnectionDebugInitializer: ( @Sendable ( Channel ) -> EventLoopFuture < Void > ) ? = nil ,
1048
+ http2ConnectionDebugInitializer: ( @Sendable ( Channel ) -> EventLoopFuture < Void > ) ? = nil ,
1049
+ http2StreamChannelDebugInitializer: ( @Sendable ( Channel ) -> EventLoopFuture < Void > ) ? = nil ,
1050
+ tracing: TracingConfiguration = . init( )
1051
+ ) {
1052
+ self . init (
1053
+ tlsConfiguration: tlsConfiguration,
1054
+ redirectConfiguration: redirectConfiguration,
1055
+ timeout: timeout,
1056
+ connectionPool: connectionPool,
1057
+ proxy: proxy,
1058
+ ignoreUncleanSSLShutdown: ignoreUncleanSSLShutdown,
1059
+ decompression: decompression
1060
+ )
1061
+ self . http1_1ConnectionDebugInitializer = http1_1ConnectionDebugInitializer
1062
+ self . http2ConnectionDebugInitializer = http2ConnectionDebugInitializer
1063
+ self . http2StreamChannelDebugInitializer = http2StreamChannelDebugInitializer
1064
+ self . tracing = tracing
1065
+ }
1066
+ }
1067
+
1068
+ public struct TracingConfiguration : Sendable {
1069
+
1070
+ @usableFromInline
1071
+ var _tracer : Optional < any Sendable > // erasure trick so we don't have to make Configuration @available
1072
+
1073
+ /// Tracer that should be used by the HTTPClient.
1074
+ ///
1075
+ /// This is selected at configuration creation time, and if no tracer is passed explicitly,
1076
+ /// (including `nil` in order to disable traces), the default global bootstrapped tracer will
1077
+ /// be stored in this property, and used for all subsequent requests made by this client.
1078
+ @inlinable
1079
+ @available ( macOS 10 . 15 , iOS 13 , tvOS 13 , watchOS 6 , * )
1080
+ public var tracer : ( any Tracer ) ? {
1081
+ get {
1082
+ guard let _tracer else {
1083
+ return nil
1084
+ }
1085
+ return _tracer as! ( any Tracer ) ?
1086
+ }
1087
+ set {
1088
+ self . _tracer = newValue
1089
+ }
1090
+ }
1091
+
1092
+ // TODO: Open up customization of keys we use?
1093
+ /// Configuration for tracing attributes set by the HTTPClient.
1094
+ @usableFromInline
1095
+ package var attributeKeys : AttributeKeys
1096
+
1097
+ public init ( ) {
1098
+ self . _tracer = nil
1099
+ self . attributeKeys = . init( )
1100
+ }
1101
+
1102
+ /// Span attribute keys that the HTTPClient should set automatically.
1103
+ /// This struct allows the configuration of the attribute names (keys) which will be used for the apropriate values.
1104
+ @usableFromInline
1105
+ package struct AttributeKeys : Sendable {
1106
+ @usableFromInline package var requestMethod : String = " http.request.method "
1107
+ @usableFromInline package var requestBodySize : String = " http.request.body.size "
1108
+
1109
+ @usableFromInline package var responseBodySize : String = " http.response.body.size "
1110
+ @usableFromInline package var responseStatusCode : String = " http.status_code "
1111
+
1112
+ @usableFromInline package var httpFlavor : String = " http.flavor "
1113
+
1114
+ @usableFromInline package init ( ) { }
1115
+ }
1015
1116
}
1016
1117
1017
1118
/// Specifies how `EventLoopGroup` will be created and establishes lifecycle ownership.
0 commit comments