Skip to content

Commit e69591f

Browse files
sichanyooSichan Yoo
andauthored
feat!: Clean up logging (#870)
* Clean up logging * Update tests * Update test * Make message param autoclosure for LogAgent * Revert breaking API changes * Misc changes * Use [] syntax for capturing self's variable. --------- Co-authored-by: Sichan Yoo <[email protected]>
1 parent fe0f2da commit e69591f

File tree

11 files changed

+141
-88
lines changed

11 files changed

+141
-88
lines changed

Sources/ClientRuntime/Networking/Http/CRT/SDKDefaultIO.swift

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,11 @@ public final class SDKDefaultIO: @unchecked Sendable {
1616
/// Provide singleton access since we want to share and re-use the instance properties
1717
public static let shared = SDKDefaultIO()
1818

19-
/// The setter for changing log level of SDKDefaultIO logger.
20-
/// If any log level other than the default log level of `.none` is desired,
21-
/// this setter needs to be called as the first thing in the program.
19+
/// The public setter for setting log level of CRT logger.
20+
///
21+
/// If any log level other than the default log level of `.none` is desired, this setter **MUST** be called before accessing the `SDKDefaultIO.shared` static field.
2222
public static func setLogLevel(level: LogLevel) {
23-
do {
24-
try Logger.initialize(target: .standardOutput, level: level)
25-
} catch {
26-
failOnLogger()
27-
}
23+
SDKDefaultIO.setupLogger(level: level)
2824
}
2925

3026
private init() {

Sources/ClientRuntime/Telemetry/Logging/ClientLogMode.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// SPDX-License-Identifier: Apache-2.0
66
//
77

8+
/// Additional logging opt-in for request / response flow. For each selected option other than `.none`, the additional info gets logged at `.debug` level by the `LoggingMiddleware`.
89
public enum ClientLogMode {
910
case none
1011
case request

Sources/ClientRuntime/Telemetry/Logging/LogLevel+StringExtension.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import AwsCommonRuntimeKit
77

8+
/// Extension for CRT's LogLevel enum
89
extension LogLevel {
910
public var stringValue: String {
1011
switch self {

Sources/ClientRuntime/Telemetry/Logging/SDKLogHandlerFactory.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import Logging
99

10+
/// Implement this protocol and add an instance of the implementation to `SDKLoggingSystem` to use custom log handlers.
1011
public protocol SDKLogHandlerFactory {
1112
var label: String { get }
1213
func construct(label: String) -> LogHandler

Sources/ClientRuntime/Telemetry/Logging/SDKLogLevel.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import Logging
99

10+
/// Wrapper for Logger.Level; used by SDKLoggingSystem.
1011
public enum SDKLogLevel: String, Codable, CaseIterable {
1112
case trace
1213
case debug

Sources/ClientRuntime/Telemetry/Logging/SDKLoggingSystem.swift

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,35 @@
77

88
import Logging
99

10+
/// Use this to turn SDK logging on.
1011
public actor SDKLoggingSystem {
1112
private var isInitialized = false
12-
private var factories: [String: SDKLogHandlerFactory] = [:]
13+
private var logHandlerFactories: [String: SDKLogHandlerFactory] = [:]
1314

1415
public init() {}
1516

17+
/// Adds custom log handler factory to `this.logHandlerFactories`.
18+
///
19+
/// The added log handler factory will be dedicated log handler for any logger with identical label.
1620
public func add(logHandlerFactory: SDKLogHandlerFactory) {
17-
let label = logHandlerFactory.label
18-
factories[label] = logHandlerFactory
21+
logHandlerFactories[logHandlerFactory.label] = logHandlerFactory
1922
}
2023

24+
/// Initializes the logging handler factory for the SDK.
25+
/// The default behavior is to log messages at `.error` or more severe levels.
26+
///
27+
/// The handler factory closure first checks if there exists a custom handler factory in `this.logHandlerFactories` with the same label as the label given to logger initializer. If it exists, then that factory gets used to create the log handler.
28+
///
29+
/// If no custom handler factory is found for the given label, the factory closure creates and returns a `StreamLogHandler` with minimum log level set to `logLevel`.
30+
///
31+
/// Loggers output log only if the log level of the message is equal to or more severe than the underlying log handler's log level. E.g., `logger.info(...)` executes only if the underlying log handler's log level is `.info`, `.debug`, or `.trace`. It does not execute if the underlying log handler's minimum log level is any one of the following levels that are more severe than `.info`: `.notice`, `.warning`, `error`, `critical`.
32+
///
33+
/// - parameters:
34+
/// - logLevel: The minimum log level to use for the log handler if no custom log handler factory was found. Default is `.error`.
2135
public func initialize(defaultLogLevel: SDKLogLevel = .error) async {
2236
if isInitialized { return } else { isInitialized = true }
23-
let ptr = factories
24-
LoggingSystem.bootstrap { label in
25-
if let factory = ptr[label] {
37+
LoggingSystem.bootstrap { [logHandlerFactories] label in
38+
if let factory = logHandlerFactories[label] {
2639
return factory.construct(label: label)
2740
}
2841
var handler = StreamLogHandler.standardOutput(label: label)
@@ -32,11 +45,6 @@ public actor SDKLoggingSystem {
3245
}
3346

3447
public func initialize(logLevel: SDKLogLevel) async {
35-
if isInitialized { return } else { isInitialized = true }
36-
LoggingSystem.bootstrap { label in
37-
var handler = StreamLogHandler.standardOutput(label: label)
38-
handler.logLevel = logLevel.toLoggerType()
39-
return handler
40-
}
48+
await self.initialize(defaultLogLevel: logLevel)
4149
}
4250
}

Sources/Smithy/Logging/LogAgent.swift

Lines changed: 70 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@ public protocol LogAgent {
99
/// name of the struct or class where the logger was instantiated from
1010
var name: String { get }
1111

12-
/// Get or set the configured log level.
13-
var level: LogAgentLevel { get set }
14-
1512
/// This method is called when a `LogAgent` must emit a log message.
1613
///
1714
/// - parameters:
@@ -31,76 +28,114 @@ public protocol LogAgent {
3128
line: UInt)
3229
}
3330

34-
public enum LogAgentLevel: String, Codable, CaseIterable {
35-
case trace
36-
case debug
37-
case info
38-
case warn
39-
case error
40-
case fatal
41-
}
42-
31+
/// Convenience wrapper functions that call `self.log()` with corresponding log level.
4332
public extension LogAgent {
33+
/// Use for messages that are typically seen during tracing.
34+
func trace(
35+
_ message: @autoclosure() -> String,
36+
file: String = #fileID,
37+
function: String = #function,
38+
line: UInt = #line
39+
) {
40+
self.log(level: .trace,
41+
message: message(),
42+
metadata: nil,
43+
source: currentModule(fileID: file),
44+
file: file,
45+
function: function,
46+
line: line)
47+
}
4448

45-
/// Log a message passing with the `.info` log level.
46-
func info(_ message: String, file: String = #fileID, function: String = #function, line: UInt = #line) {
47-
self.log(level: .info,
48-
message: message,
49+
/// Use for messages that are typically seen during debugging.
50+
func debug(
51+
_ message: @autoclosure() -> String,
52+
file: String = #fileID,
53+
function: String = #function,
54+
line: UInt = #line
55+
) {
56+
self.log(level: .debug,
57+
message: message(),
4958
metadata: nil,
5059
source: currentModule(fileID: file),
5160
file: file,
5261
function: function,
5362
line: line)
5463
}
5564

56-
/// Log a message passing with the `LogLevel.warn` log level.
57-
func warn(_ message: String, file: String = #fileID, function: String = #function, line: UInt = #line) {
58-
self.log(level: .warn,
59-
message: message,
65+
/// Use for informational messages.
66+
func info(
67+
_ message: @autoclosure() -> String,
68+
file: String = #fileID,
69+
function: String = #function,
70+
line: UInt = #line
71+
) {
72+
self.log(level: .info,
73+
message: message(),
6074
metadata: nil,
6175
source: currentModule(fileID: file),
6276
file: file,
6377
function: function,
6478
line: line)
6579
}
6680

67-
/// Log a message passing with the `.debug` log level.
68-
func debug(_ message: String, file: String = #fileID, function: String = #function, line: UInt = #line) {
69-
self.log(level: .debug,
70-
message: message,
81+
/// Use for non-error messages that may need special attention.
82+
func notice(
83+
_ message: @autoclosure() -> String,
84+
file: String = #fileID,
85+
function: String = #function,
86+
line: UInt = #line
87+
) {
88+
self.log(level: .notice,
89+
message: message(),
7190
metadata: nil,
7291
source: currentModule(fileID: file),
7392
file: file,
7493
function: function,
7594
line: line)
7695
}
7796

78-
/// Log a message passing with the `.error` log level.
79-
func error(_ message: String, file: String = #fileID, function: String = #function, line: UInt = #line) {
80-
self.log(level: .error,
81-
message: message,
97+
/// Use for non-error messages that are more severe than `.notice`.
98+
func warn(
99+
_ message: @autoclosure() -> String,
100+
file: String = #fileID,
101+
function: String = #function,
102+
line: UInt = #line
103+
) {
104+
self.log(level: .warn,
105+
message: message(),
82106
metadata: nil,
83107
source: currentModule(fileID: file),
84108
file: file,
85109
function: function,
86110
line: line)
87111
}
88112

89-
/// Log a message passing with the `.trace` log level.
90-
func trace(_ message: String, file: String = #fileID, function: String = #function, line: UInt = #line) {
91-
self.log(level: .trace,
92-
message: message,
113+
/// Use for errors.
114+
func error(
115+
_ message: @autoclosure() -> String,
116+
file: String = #fileID,
117+
function: String = #function,
118+
line: UInt = #line
119+
) {
120+
self.log(level: .error,
121+
message: message(),
93122
metadata: nil,
94123
source: currentModule(fileID: file),
95124
file: file,
96125
function: function,
97126
line: line)
98127
}
99128

100-
/// Log a message passing with the `.fatal` log level.
101-
func fatal(_ message: String, file: String = #fileID, function: String = #function, line: UInt = #line) {
129+
/// Appropriate for critical error conditions that usually require immediate
130+
/// attention.
131+
func fatal(
132+
_ message: @autoclosure() -> String,
133+
file: String = #fileID,
134+
function: String = #function,
135+
line: UInt = #line
136+
) {
102137
self.log(level: .fatal,
103-
message: message,
138+
message: message(),
104139
metadata: nil,
105140
source: currentModule(fileID: file),
106141
file: file,
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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+
/// Wrapper for Logger.Level; used by LogAgent and SwiftLogger.
11+
public enum LogAgentLevel: String, Codable, CaseIterable {
12+
case trace
13+
case debug
14+
case info
15+
case notice
16+
case warn
17+
case error
18+
case fatal
19+
20+
func toLoggerLevel() -> Logger.Level {
21+
switch self {
22+
case .trace:
23+
return .trace
24+
case .debug:
25+
return .debug
26+
case .info:
27+
return .info
28+
case .notice:
29+
return .notice
30+
case .warn:
31+
return .warning
32+
case .error:
33+
return .error
34+
case .fatal:
35+
return .critical
36+
}
37+
}
38+
}

Sources/Smithy/Logging/SwiftLog+LogAgent.swift

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,22 @@
88
import Logging
99

1010
public struct SwiftLogger: LogAgent {
11-
public var level: LogAgentLevel
12-
1311
private let logger: Logger
1412
private let label: String
1513

1614
public init(label: String) {
1715
self.label = label
1816
self.logger = Logger(label: label)
19-
self.level = LogAgentLevel.info
2017
}
2118

19+
@available(*, deprecated, message: "This API is deprecated. Use init(label:) instead.")
2220
public init(label: String, logLevel: LogAgentLevel) {
23-
self.label = label
24-
self.logger = Logger(label: label)
25-
self.level = logLevel
21+
self.init(label: label)
2622
}
2723

2824
// This initializer is currently only used in tests, to inject a mock LogHandler.
29-
init(label: String, logLevel: LogAgentLevel, factory: (String) -> any LogHandler) {
25+
init(label: String, factory: (String) -> any LogHandler) {
3026
self.label = label
31-
self.level = logLevel
3227
self.logger = Logger(label: label, factory: factory)
3328
}
3429

@@ -56,23 +51,3 @@ public struct SwiftLogger: LogAgent {
5651
)
5752
}
5853
}
59-
60-
extension LogAgentLevel {
61-
62-
func toLoggerLevel() -> Logger.Level {
63-
switch self {
64-
case .trace:
65-
return .trace
66-
case .debug:
67-
return .debug
68-
case .info:
69-
return .info
70-
case .warn:
71-
return .warning
72-
case .error:
73-
return .error
74-
case .fatal:
75-
return .critical
76-
}
77-
}
78-
}

Tests/ClientRuntimeTests/LoggingTests/SwiftLoggerTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ final class SwiftLoggerTests: XCTestCase {
3838

3939
private func logsLeveledMessage(
4040
logLevel: Logger.Level,
41-
loggerBlock: (SwiftLogger) -> (String, String, String, UInt) -> Void,
41+
loggerBlock: (SwiftLogger) -> (@autoclosure() -> String, String, String, UInt) -> Void,
4242
testFile: StaticString = #filePath,
4343
testLine: UInt = #line
4444
) throws {
@@ -53,7 +53,7 @@ final class SwiftLoggerTests: XCTestCase {
5353
// Create a TestLogHandler, then create a SwiftLogger (the test subject)
5454
// with it.
5555
var logHandler: TestLogHandler!
56-
let subject = SwiftLogger(label: "Test", logLevel: .trace, factory: { label in
56+
let subject = SwiftLogger(label: "Test", factory: { label in
5757
logHandler = TestLogHandler(label: label)
5858
return logHandler
5959
})

Tests/ClientRuntimeTests/NetworkingTests/URLSession/FoundationStreamBridgeTests.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,9 @@ private class TestLogger: LogAgent {
109109

110110
var messages: [(level: LogAgentLevel, message: String)] = []
111111

112-
var level: LogAgentLevel
113-
114112
init(name: String = "Test", messages: [(level: LogAgentLevel, message: String)] = [], level: LogAgentLevel = .info) {
115113
self.name = name
116114
self.messages = messages
117-
self.level = level
118115
}
119116

120117
func log(level: LogAgentLevel = .info, message: @autoclosure () -> String, metadata: @autoclosure () -> [String : String]? = nil, source: @autoclosure () -> String = "ChecksumUnitTests", file: String = #file, function: String = #function, line: UInt = #line) {

0 commit comments

Comments
 (0)