Skip to content

Commit d91d213

Browse files
authored
feat: create SDKLoggingSystem to expose logging interface for devs (#287)
1 parent d81dfbd commit d91d213

File tree

9 files changed

+179
-25
lines changed

9 files changed

+179
-25
lines changed

Packages/ClientRuntime/Sources/Config/Configuration.swift

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,20 @@ 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
1917

2018
public init(encoder: RequestEncoder? = nil,
2119
decoder: ResponseDecoder? = nil,
2220
httpClientEngine: HttpClientEngine? = nil,
2321
httpClientConfiguration: HttpClientConfiguration = HttpClientConfiguration(),
2422
retrier: Retrier? = nil,
25-
clientLogMode: ClientLogMode = .request,
26-
logger: LogAgent? = nil,
2723
idempotencyTokenGenerator: IdempotencyTokenGenerator = DefaultIdempotencyTokenGenerator()) throws {
2824
self.encoder = encoder
2925
self.decoder = decoder
3026
let engine = try httpClientEngine ?? CRTClientEngine()
3127
self.httpClientEngine = engine
3228
self.retrier = try retrier ?? SDKRetrier(clientEngine: engine)
33-
self.clientLogMode = clientLogMode
34-
self.logger = logger ?? SwiftLogger(label: "Swift SDK Logger")
3529
self.httpClientConfiguration = httpClientConfiguration
3630
self.idempotencyTokenGenerator = idempotencyTokenGenerator
3731
}
32+
3833
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
import Logging
9+
10+
public protocol SDKLogHandlerFactory {
11+
var label: String { get }
12+
func construct(label: String) -> LogHandler
13+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
import Logging
9+
10+
public enum SDKLogLevel: String, Codable, CaseIterable {
11+
case trace
12+
case debug
13+
case info
14+
case notice
15+
case warning
16+
case error
17+
case critical
18+
19+
public func toLoggerType() -> Logger.Level {
20+
switch self {
21+
case .trace:
22+
return .trace
23+
case .debug:
24+
return .debug
25+
case .info:
26+
return .info
27+
case .notice:
28+
return .notice
29+
case .warning:
30+
return .warning
31+
case .error:
32+
return .error
33+
case .critical:
34+
return .critical
35+
}
36+
}
37+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
import Logging
9+
10+
public class SDKLoggingSystem {
11+
private static var factories: [String: SDKLogHandlerFactory] = [:]
12+
13+
public class func add(logHandlerFactory: SDKLogHandlerFactory) {
14+
let label = logHandlerFactory.label
15+
factories[label] = logHandlerFactory
16+
}
17+
18+
public class func initialize() {
19+
LoggingSystem.bootstrap { label in
20+
if let factory = factories[label] {
21+
return factory.construct(label: label)
22+
}
23+
var handler = StreamLogHandler.standardOutput(label: label)
24+
handler.logLevel = .info
25+
return handler
26+
}
27+
}
28+
29+
public class func initialize(logLevel: SDKLogLevel) {
30+
LoggingSystem.bootstrap { label in
31+
var handler = StreamLogHandler.standardOutput(label: label)
32+
handler.logLevel = logLevel.toLoggerType()
33+
return handler
34+
}
35+
}
36+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
import Logging
9+
10+
public struct CRTClientEngineLogHandlerFactory: SDKLogHandlerFactory {
11+
public var label = "CRTClientEngine"
12+
let logLevel: SDKLogLevel
13+
14+
public func construct(label: String) -> LogHandler {
15+
var handler = StreamLogHandler.standardOutput(label: label)
16+
handler.logLevel = logLevel.toLoggerType()
17+
return handler
18+
}
19+
20+
public init(logLevel: SDKLogLevel) {
21+
self.logLevel = logLevel
22+
}
23+
}

smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/SwiftDependency.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ enum class SwiftDependency(
1818
var packageName: String
1919
) : SymbolDependencyContainer {
2020
BIG("ComplexModule", null, "0.0.5", "https://github.com/apple/swift-numerics", "", "swift-numerics"),
21+
SWIFT_LOG("Logging", null, "", "", "", ""),
2122
CLIENT_RUNTIME(
2223
"ClientRuntime",
2324
"master",

smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolClientGenerator.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ open class HttpProtocolClientGenerator(
4646
fun render() {
4747
val serviceSymbol = symbolProvider.toSymbol(serviceShape)
4848
writer.addImport(SwiftDependency.CLIENT_RUNTIME.target)
49+
writer.addImport(SwiftDependency.SWIFT_LOG.target)
4950
writer.addFoundationImport()
5051
httpProtocolCustomizable.renderInternals(ctx)
5152
httpProtocolServiceClient.render(serviceSymbol)

smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/integration/HttpProtocolServiceClient.kt

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,28 @@ open class HttpProtocolServiceClient(
3939
writer.write("client.close()")
4040
}
4141
writer.write("")
42-
// FIXME: possible move generation of the config to a separate file or above the service client
4342
renderConfig(serviceSymbol)
4443
}
44+
writer.write("")
45+
renderLogHandlerFactory(serviceSymbol)
46+
writer.write("")
47+
}
48+
49+
private fun renderLogHandlerFactory(serviceSymbol: Symbol) {
50+
writer.openBlock("public struct ${serviceSymbol.name}LogHandlerFactory: SDKLogHandlerFactory {", "}") {
51+
writer.write("public var label = \"${serviceSymbol.name}\"")
52+
writer.write("let logLevel: SDKLogLevel")
53+
54+
writer.openBlock("public func construct(label: String) -> LogHandler {", "}") {
55+
writer.write("var handler = StreamLogHandler.standardOutput(label: label)")
56+
writer.write("handler.logLevel = logLevel.toLoggerType()")
57+
writer.write("return handler")
58+
}
59+
60+
writer.openBlock("public init(logLevel: SDKLogLevel) {", "}") {
61+
writer.write("self.logLevel = logLevel")
62+
}
63+
}
4564
}
4665

4766
private fun renderConfig(serviceSymbol: Symbol) {
@@ -54,31 +73,35 @@ open class HttpProtocolServiceClient(
5473
writer.write("public var ${it.name}: ${it.type}")
5574
}
5675
writer.write("")
57-
renderConfigInit(configFields)
76+
writer.write("public let clientLogMode: ClientLogMode")
77+
writer.write("public let logger: LogAgent")
78+
writer.write("")
79+
renderConfigInit(configFields, serviceSymbol)
5880
writer.write("")
5981
serviceConfig.renderConvenienceInits(serviceSymbol)
6082
writer.write("")
6183
serviceConfig.renderStaticDefaultImplementation(serviceSymbol)
6284
}
6385
}
6486

65-
private fun renderConfigInit(configFields: List<ConfigField>) {
66-
if (configFields.isNotEmpty()) {
67-
val configFieldsSortedByName = configFields.sortedBy { it.name }
68-
writer.openBlock("public init (", ") throws") {
69-
for ((index, member) in configFieldsSortedByName.withIndex()) {
70-
val memberName = member.name
71-
val memberSymbol = member.type
72-
if (memberName == null) continue
73-
val terminator = if (index == configFieldsSortedByName.size - 1) "" else ","
74-
writer.write("\$L: \$L$terminator", memberName, memberSymbol)
75-
}
87+
private fun renderConfigInit(configFields: List<ConfigField>, serviceSymbol: Symbol) {
88+
val configFieldsSortedByName = configFields.sortedBy { it.name }
89+
writer.openBlock("public init (", ") throws") {
90+
for (member in configFieldsSortedByName) {
91+
val memberName = member.name
92+
val memberSymbol = member.type
93+
if (memberName == null) continue
94+
writer.write("\$L: \$L,", memberName, memberSymbol)
7695
}
77-
writer.openBlock("{", "}") {
78-
configFieldsSortedByName.forEach {
79-
writer.write("self.\$1L = \$1L", it.name)
80-
}
96+
writer.write("clientLogMode: ClientLogMode = .request,")
97+
writer.write("logger: LogAgent? = nil")
98+
}
99+
writer.openBlock("{", "}") {
100+
configFieldsSortedByName.forEach {
101+
writer.write("self.\$1L = \$1L", it.name)
81102
}
103+
writer.write("self.clientLogMode = clientLogMode")
104+
writer.write("self.logger = logger ?? SwiftLogger(label: \"${serviceSymbol.name}\")")
82105
}
83106
}
84107
}

smithy-swift-codegen/src/test/kotlin/HttpProtocolClientGeneratorTests.kt

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,18 +53,43 @@ class HttpProtocolClientGeneratorTests {
5353
self.decoder = config.decoder
5454
self.config = config
5555
}
56-
56+
5757
deinit {
5858
client.close()
5959
}
60-
60+
6161
public class ExampleClientConfiguration: ClientRuntime.Configuration {
6262
63+
public let clientLogMode: ClientLogMode
64+
public let logger: LogAgent
65+
66+
public init (
67+
clientLogMode: ClientLogMode = .request,
68+
logger: LogAgent? = nil
69+
) throws
70+
{
71+
self.clientLogMode = clientLogMode
72+
self.logger = logger ?? SwiftLogger(label: "ExampleClient")
73+
}
74+
6375
public static func `default`() throws -> ExampleClientConfiguration {
6476
return try ExampleClientConfiguration()
6577
}
6678
}
6779
}
80+
81+
public struct ExampleClientLogHandlerFactory: SDKLogHandlerFactory {
82+
public var label = "ExampleClient"
83+
let logLevel: SDKLogLevel
84+
public func construct(label: String) -> LogHandler {
85+
var handler = StreamLogHandler.standardOutput(label: label)
86+
handler.logLevel = logLevel.toLoggerType()
87+
return handler
88+
}
89+
public init(logLevel: SDKLogLevel) {
90+
self.logLevel = logLevel
91+
}
92+
}
6893
""".trimIndent()
6994
)
7095
}

0 commit comments

Comments
 (0)