-
Notifications
You must be signed in to change notification settings - Fork 142
Expand file tree
/
Copy pathHTTPClient+tracing.swift
More file actions
113 lines (96 loc) · 4.22 KB
/
HTTPClient+tracing.swift
File metadata and controls
113 lines (96 loc) · 4.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//===----------------------------------------------------------------------===//
//
// This source file is part of the AsyncHTTPClient open source project
//
// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import NIOHTTP1
import Tracing
#if canImport(FoundationEssentials)
import struct FoundationEssentials.URL
#else
import struct Foundation.URL
#endif
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
extension HTTPClient {
@inlinable
func withRequestSpan(
_ request: HTTPClientRequest,
_ body: () async throws -> HTTPClientResponse
) async rethrows -> HTTPClientResponse {
guard let tracer = self.tracer else {
return try await body()
}
return try await tracer.withSpan(request.method.rawValue, ofKind: .client) { span in
let keys = self.configuration.tracing.attributeKeys
let allowedHeaders = Set(self.configuration.tracing.allowedHeaders.map { $0.lowercased() })
span.attributes[keys.requestMethod] = request.method.rawValue
// set explicitly allowed request headers
var allowedRequestHeaders: [String: [String]] = [:]
for header in request.headers {
guard allowedHeaders.contains(header.name.lowercased()) else {
continue
}
let normalizedHeaderName = normalizedTracingHeaderName(header.name)
allowedRequestHeaders[normalizedHeaderName, default: []].append(header.value)
}
for (headerName, values) in allowedRequestHeaders {
span.attributes["\(keys.requestHeader).\(headerName)"] = values
}
// set url attributes
if let url = URL(string: request.url) {
span.attributes[keys.urlPath] = TracingSupport.sanitizePath(
url.path,
redactionComponents: self.configuration.tracing.sensitivePathComponents
)
if let scheme = url.scheme {
span.attributes[keys.urlScheme] = scheme
}
if let query = url.query {
span.attributes[keys.urlQuery] = TracingSupport.sanitizeQuery(
query,
redactionComponents: self.configuration.tracing.sensitiveQueryComponents
)
}
if let fragment = url.fragment {
span.attributes[keys.urlFragment] = fragment
}
if let host = url.host {
span.attributes[keys.serverHostname] = host
}
if let port = url.port {
span.attributes[keys.serverPort] = port
}
}
let response = try await body()
// set response span attributes
TracingSupport.handleResponseStatusCode(span, response.status, keys: tracing.attributeKeys)
// set explicitly allowed response headers
var allowedResponseHeaders: [String: [String]] = [:]
for header in response.headers {
guard allowedHeaders.contains(header.name.lowercased()) else {
continue
}
let normalizedHeaderName = normalizedTracingHeaderName(header.name)
allowedResponseHeaders[normalizedHeaderName, default: []].append(header.value)
}
for (headerName, values) in allowedResponseHeaders {
span.attributes["\(keys.responseHeader).\(headerName)"] = values
}
// set network protocol version
span.attributes[keys.networkProtocolVersion] = "\(response.version.major).\(response.version.minor)"
return response
}
}
@inlinable
func normalizedTracingHeaderName(_ name: String) -> String {
name.lowercased().replacingOccurrences(of: "-", with: "_")
}
}