Skip to content

Commit 797ad2d

Browse files
authored
feat: add wire logging middleware (#264)
1 parent c9624df commit 797ad2d

File tree

14 files changed

+187
-14
lines changed

14 files changed

+187
-14
lines changed

Packages/ClientRuntime/Sources/Config/Configuration.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,24 @@ open class Configuration {
1414
public let httpClientConfiguration: HttpClientConfiguration
1515
public let idempotencyTokenGenerator: IdempotencyTokenGenerator
1616
public let retrier: Retrier
17+
public var clientLogMode: ClientLogMode
18+
public let logger: LogAgent
1719

1820
public init(encoder: RequestEncoder? = nil,
1921
decoder: ResponseDecoder? = nil,
2022
httpClientEngine: HttpClientEngine? = nil,
2123
httpClientConfiguration: HttpClientConfiguration = HttpClientConfiguration(),
2224
retrier: Retrier? = nil,
25+
clientLogMode: ClientLogMode = .request,
26+
logger: LogAgent? = nil,
2327
idempotencyTokenGenerator: IdempotencyTokenGenerator = DefaultIdempotencyTokenGenerator()) throws {
2428
self.encoder = encoder
2529
self.decoder = decoder
2630
let engine = try httpClientEngine ?? CRTClientEngine()
2731
self.httpClientEngine = engine
2832
self.retrier = try retrier ?? SDKRetrier(clientEngine: engine)
29-
33+
self.clientLogMode = clientLogMode
34+
self.logger = logger ?? SwiftLogger(label: "Swift SDK Logger")
3035
self.httpClientConfiguration = httpClientConfiguration
3136
self.idempotencyTokenGenerator = idempotencyTokenGenerator
3237
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
public enum ClientLogMode {
9+
case request
10+
case requestWithBody
11+
case response
12+
case responseWithBody
13+
case requestAndResponse
14+
case requestAndResponseWithBody
15+
}

Packages/ClientRuntime/Sources/Logging/LogAgent.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import AwsCommonRuntimeKit
77

8-
protocol LogAgent {
8+
public protocol LogAgent {
99
/// name of the struct or class where the logger was instantiated from
1010
var name: String {get}
1111

@@ -31,7 +31,7 @@ protocol LogAgent {
3131
line: UInt)
3232
}
3333

34-
extension LogAgent {
34+
public extension LogAgent {
3535

3636
internal static func currentModule(filePath: String = #file) -> String {
3737
let utf8All = filePath.utf8

Packages/ClientRuntime/Sources/Logging/SwiftLog+LogAgent.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
import Logging
77
import enum AwsCommonRuntimeKit.LogLevel
88

9-
typealias SwiftLogger = Logger
9+
public typealias SwiftLogger = Logger
1010

1111
extension SwiftLogger: LogAgent {
1212

13-
var level: LogLevel {
13+
public var level: LogLevel {
1414
get {
1515
return LogLevel.fromString(string: logLevel.rawValue)
1616
}
@@ -19,11 +19,11 @@ extension SwiftLogger: LogAgent {
1919
}
2020
}
2121

22-
var name: String {
22+
public var name: String {
2323
return label
2424
}
2525

26-
func log(level: LogLevel,
26+
public func log(level: LogLevel,
2727
message: String,
2828
metadata: [String: String]?,
2929
source: String,
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
public struct LoggerMiddleware<Output: HttpResponseBinding,
9+
OutputError: HttpResponseBinding>: Middleware {
10+
11+
public let id: String = "Logger"
12+
13+
let clientLogMode: ClientLogMode
14+
15+
public init(clientLogMode: ClientLogMode) {
16+
self.clientLogMode = clientLogMode
17+
}
18+
19+
public func handle<H>(context: Context,
20+
input: SdkHttpRequest,
21+
next: H) -> Result<OperationOutput<Output>, SdkError<OutputError>>
22+
where H: Handler,
23+
Self.MInput == H.Input,
24+
Self.MOutput == H.Output,
25+
Self.Context == H.Context,
26+
Self.MError == H.MiddlewareError {
27+
28+
guard let logger = context.getLogger() else {
29+
return next.handle(context: context, input: input)
30+
}
31+
32+
if clientLogMode == .request || clientLogMode == .requestAndResponse {
33+
logger.debug("Request: \(input.debugDescription)")
34+
}else if clientLogMode == .requestAndResponseWithBody || clientLogMode == .requestWithBody {
35+
logger.debug("Request: \(input.debugDescriptionWithBody)")
36+
}
37+
38+
let response = next.handle(context: context, input: input)
39+
40+
do {
41+
let output = try response.get()
42+
if clientLogMode == .response || clientLogMode == .requestAndResponse {
43+
logger.debug("Response: \(output.httpResponse.debugDescription)")
44+
} else if clientLogMode == .requestAndResponseWithBody || clientLogMode == .responseWithBody {
45+
logger.debug("Response: \(output.httpResponse.debugDescriptionWithBody)")
46+
}
47+
48+
} catch {
49+
return response
50+
}
51+
52+
return response
53+
}
54+
55+
public typealias MInput = SdkHttpRequest
56+
public typealias MOutput = OperationOutput<Output>
57+
public typealias Context = HttpContext
58+
public typealias MError = SdkError<OutputError>
59+
}

Packages/ClientRuntime/Sources/Networking/Http/Headers.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,9 @@ extension Headers {
134134
}
135135
}
136136
}
137+
138+
extension Headers: CustomDebugStringConvertible {
139+
public var debugDescription: String {
140+
return dictionary.map {"\($0.key): \($0.value.joined(separator: ", "))"}.joined(separator: ", \n")
141+
}
142+
}

Packages/ClientRuntime/Sources/Networking/Http/HttpBody.swift

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,26 @@ extension HttpBody: Equatable {
2929
}
3030

3131
public extension HttpBody {
32-
3332
static var empty: HttpBody {
3433
.data(nil)
3534
}
3635
}
36+
37+
extension HttpBody: CustomDebugStringConvertible {
38+
public var debugDescription: String {
39+
var bodyAsString: String?
40+
switch self {
41+
case .data(let data):
42+
if let data = data {
43+
bodyAsString = String(data: data, encoding: .utf8)
44+
}
45+
case .streamSource(let stream):
46+
let byteBuffer = ByteBuffer(size: 1024)
47+
stream.unwrap().sendData(writeTo: byteBuffer)
48+
bodyAsString = String(data: byteBuffer.toData(), encoding: .utf8)
49+
default:
50+
bodyAsString = nil
51+
}
52+
return bodyAsString ?? ""
53+
}
54+
}

Packages/ClientRuntime/Sources/Networking/Http/HttpContext.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ public struct HttpContext: MiddlewareContext {
4141
public func getHostPrefix() -> String? {
4242
return attributes.get(key: AttributeKey<String>(name: "HostPrefix"))
4343
}
44+
45+
public func getLogger() -> LogAgent? {
46+
return attributes.get(key: AttributeKey<LogAgent>(name: "Logger"))
47+
}
4448
}
4549

4650
public class HttpContextBuilder {
@@ -58,6 +62,7 @@ public class HttpContextBuilder {
5862
let decoder = AttributeKey<ResponseDecoder>(name: "Decoder")
5963
let idempotencyTokenGenerator = AttributeKey<IdempotencyTokenGenerator>(name: "IdempotencyTokenGenerator")
6064
let hostPrefix = AttributeKey<String>(name: "HostPrefix")
65+
let logger = AttributeKey<LogAgent>(name: "Logger")
6166

6267
// We follow the convention of returning the builder object
6368
// itself from any configuration methods, and by adding the
@@ -130,6 +135,12 @@ public class HttpContextBuilder {
130135
return self
131136
}
132137

138+
@discardableResult
139+
public func withLogger(value: LogAgent) -> HttpContextBuilder {
140+
self.attributes.set(key: logger, value: value)
141+
return self
142+
}
143+
133144
public func build() -> HttpContext {
134145
return HttpContext(attributes: attributes)
135146
}

Packages/ClientRuntime/Sources/Networking/Http/HttpResponse.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,12 @@ public class HttpResponse: HttpUrlResponse {
2424
self.headers = headers
2525
}
2626
}
27+
28+
extension HttpResponse: CustomDebugStringConvertible {
29+
public var debugDescriptionWithBody: String {
30+
return debugDescription + "\n \(body)"
31+
}
32+
public var debugDescription: String {
33+
return "\(statusCode) \n \(headers)"
34+
}
35+
}

Packages/ClientRuntime/Sources/Networking/Http/SdkHttpRequest.swift

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import AwsCommonRuntimeKit
99

1010
// we need to maintain a reference to this same request while we add headers
1111
// in the CRT engine so that is why it's a class
12-
public class SdkHttpRequest: CustomStringConvertible {
12+
public class SdkHttpRequest {
1313
public var body: HttpBody
1414
public var headers: Headers
1515
public let queryItems: [URLQueryItem]?
@@ -27,10 +27,6 @@ public class SdkHttpRequest: CustomStringConvertible {
2727
self.body = body
2828
self.queryItems = queryItems
2929
}
30-
31-
public var description: String {
32-
return "SdkHttpRequest(body: \(body), headers: \(headers), queryItems: \(String(describing: queryItems)), endpoint: \(endpoint), method: \(method))"
33-
}
3430
}
3531

3632
extension SdkHttpRequest {
@@ -62,6 +58,21 @@ extension SdkHttpRequest {
6258
}
6359
}
6460

61+
extension SdkHttpRequest: CustomDebugStringConvertible, CustomStringConvertible {
62+
63+
public var debugDescriptionWithBody: String {
64+
return debugDescription + "\n \(body)"
65+
}
66+
67+
public var debugDescription: String {
68+
return "\(method.rawValue.uppercased()) \(endpoint.protocolType ?? ProtocolType.https):\(endpoint.port) \n Path: \(endpoint.path), \n \(headers) \n \(String(describing: queryItems))"
69+
}
70+
71+
public var description: String {
72+
return "\(method.rawValue.uppercased()) \(endpoint.protocolType ?? ProtocolType.https):\(endpoint.port) \n Path: \(endpoint.path) \n \(headers) \n \(String(describing: queryItems))"
73+
}
74+
}
75+
6576
extension SdkHttpRequestBuilder {
6677
public func update(from crtRequest: HttpRequest, originalRequest: SdkHttpRequest) -> SdkHttpRequestBuilder {
6778
headers = convertSignedHeadersToHeaders(crtRequest: crtRequest)

0 commit comments

Comments
 (0)