diff --git a/Sources/Logging/Logging.swift b/Sources/Logging/Logging.swift index 3539faa6..b5be302c 100644 --- a/Sources/Logging/Logging.swift +++ b/Sources/Logging/Logging.swift @@ -81,6 +81,29 @@ extension Logger { } } + /// Log a message passing the log level as a parameter. + /// + /// If the `logLevel` passed to this method is more severe than the `Logger`'s `logLevel`, it will be logged, + /// otherwise nothing will happen. + /// + /// - parameters: + /// - level: The log level to log `message` at. For the available log levels, see `Logger.Level`. + /// - message: The message to be logged. `message` can be used with any string interpolation literal. + /// - metadata: One-off metadata to attach to this log message. + /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#file`). + /// - function: The function this log message originates from (there's usually no need to pass it explicitly as + /// it defaults to `#function`). + /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#line`). + @inlinable + public func log(level: Logger.Level, + _ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + file: String = #file, function: String = #function, line: UInt = #line) { + self.log(level: level, message(), metadata: metadata(), source: nil, file: file, function: function, line: line) + } + /// Add, change, or remove a logging metadata item. /// /// - note: Logging metadata behaves as a value that means a change to the logging metadata will only affect the @@ -137,6 +160,25 @@ extension Logger { self.log(level: .trace, message(), metadata: metadata(), source: source(), file: file, function: function, line: line) } + /// If `.trace` is at least as severe as the `Logger`'s `logLevel`, it will be logged, + /// otherwise nothing will happen. + /// + /// - parameters: + /// - message: The message to be logged. `message` can be used with any string interpolation literal. + /// - metadata: One-off metadata to attach to this log message + /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#file`). + /// - function: The function this log message originates from (there's usually no need to pass it explicitly as + /// it defaults to `#function`). + /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#line`). + @inlinable + public func trace(_ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + file: String = #file, function: String = #function, line: UInt = #line) { + self.trace(message(), metadata: metadata(), source: nil, file: file, line: line) + } + /// Log a message passing with the `Logger.Level.debug` log level. /// /// If `.debug` is at least as severe as the `Logger`'s `logLevel`, it will be logged, @@ -161,6 +203,27 @@ extension Logger { self.log(level: .debug, message(), metadata: metadata(), source: source(), file: file, function: function, line: line) } + /// Log a message passing with the `Logger.Level.debug` log level. + /// + /// If `.debug` is at least as severe as the `Logger`'s `logLevel`, it will be logged, + /// otherwise nothing will happen. + /// + /// - parameters: + /// - message: The message to be logged. `message` can be used with any string interpolation literal. + /// - metadata: One-off metadata to attach to this log message. + /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#file`). + /// - function: The function this log message originates from (there's usually no need to pass it explicitly as + /// it defaults to `#function`). + /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#line`). + @inlinable + public func debug(_ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + file: String = #file, function: String = #function, line: UInt = #line) { + self.debug(message(), metadata: metadata(), source: nil, file: file, function: function, line: line) + } + /// Log a message passing with the `Logger.Level.info` log level. /// /// If `.info` is at least as severe as the `Logger`'s `logLevel`, it will be logged, @@ -185,6 +248,27 @@ extension Logger { self.log(level: .info, message(), metadata: metadata(), source: source(), file: file, function: function, line: line) } + /// Log a message passing with the `Logger.Level.info` log level. + /// + /// If `.info` is at least as severe as the `Logger`'s `logLevel`, it will be logged, + /// otherwise nothing will happen. + /// + /// - parameters: + /// - message: The message to be logged. `message` can be used with any string interpolation literal. + /// - metadata: One-off metadata to attach to this log message. + /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#file`). + /// - function: The function this log message originates from (there's usually no need to pass it explicitly as + /// it defaults to `#function`). + /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#line`). + @inlinable + public func info(_ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + file: String = #file, function: String = #function, line: UInt = #line) { + self.info(message(), metadata: metadata(), source: nil, file: file, function: function, line: line) + } + /// Log a message passing with the `Logger.Level.notice` log level. /// /// If `.notice` is at least as severe as the `Logger`'s `logLevel`, it will be logged, @@ -209,6 +293,29 @@ extension Logger { self.log(level: .notice, message(), metadata: metadata(), source: source(), file: file, function: function, line: line) } + /// Log a message passing with the `Logger.Level.notice` log level. + /// + /// If `.notice` is at least as severe as the `Logger`'s `logLevel`, it will be logged, + /// otherwise nothing will happen. + /// + /// - parameters: + /// - message: The message to be logged. `message` can be used with any string interpolation literal. + /// - metadata: One-off metadata to attach to this log message. + /// - source: The source this log messages originates to. Currently, it defaults to the folder containing the + /// file that is emitting the log message, which usually is the module. + /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#file`). + /// - function: The function this log message originates from (there's usually no need to pass it explicitly as + /// it defaults to `#function`). + /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#line`). + @inlinable + public func notice(_ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + file: String = #file, function: String = #function, line: UInt = #line) { + self.notice(message(), metadata: metadata(), source: nil, file: file, function: function, line: line) + } + /// Log a message passing with the `Logger.Level.warning` log level. /// /// If `.warning` is at least as severe as the `Logger`'s `logLevel`, it will be logged, @@ -233,6 +340,27 @@ extension Logger { self.log(level: .warning, message(), metadata: metadata(), source: source(), file: file, function: function, line: line) } + /// Log a message passing with the `Logger.Level.warning` log level. + /// + /// If `.warning` is at least as severe as the `Logger`'s `logLevel`, it will be logged, + /// otherwise nothing will happen. + /// + /// - parameters: + /// - message: The message to be logged. `message` can be used with any string interpolation literal. + /// - metadata: One-off metadata to attach to this log message. + /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#file`). + /// - function: The function this log message originates from (there's usually no need to pass it explicitly as + /// it defaults to `#function`). + /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#line`). + @inlinable + public func warning(_ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + file: String = #file, function: String = #function, line: UInt = #line) { + self.warning(message(), metadata: metadata(), source: nil, file: file, function: function, line: line) + } + /// Log a message passing with the `Logger.Level.error` log level. /// /// If `.error` is at least as severe as the `Logger`'s `logLevel`, it will be logged, @@ -257,6 +385,27 @@ extension Logger { self.log(level: .error, message(), metadata: metadata(), source: source(), file: file, function: function, line: line) } + /// Log a message passing with the `Logger.Level.error` log level. + /// + /// If `.error` is at least as severe as the `Logger`'s `logLevel`, it will be logged, + /// otherwise nothing will happen. + /// + /// - parameters: + /// - message: The message to be logged. `message` can be used with any string interpolation literal. + /// - metadata: One-off metadata to attach to this log message. + /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#file`). + /// - function: The function this log message originates from (there's usually no need to pass it explicitly as + /// it defaults to `#function`). + /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#line`). + @inlinable + public func error(_ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + file: String = #file, function: String = #function, line: UInt = #line) { + self.error(message(), metadata: metadata(), source: nil, file: file, function: function, line: line) + } + /// Log a message passing with the `Logger.Level.critical` log level. /// /// `.critical` messages will always be logged. @@ -279,6 +428,28 @@ extension Logger { file: String = #file, function: String = #function, line: UInt = #line) { self.log(level: .critical, message(), metadata: metadata(), source: source(), file: file, function: function, line: line) } + + /// Log a message passing with the `Logger.Level.critical` log level. + /// + /// `.critical` messages will always be logged. + /// + /// - parameters: + /// - message: The message to be logged. `message` can be used with any string interpolation literal. + /// - metadata: One-off metadata to attach to this log message. + /// - source: The source this log messages originates to. Currently, it defaults to the folder containing the + /// file that is emitting the log message, which usually is the module. + /// - file: The file this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#file`). + /// - function: The function this log message originates from (there's usually no need to pass it explicitly as + /// it defaults to `#function`). + /// - line: The line this log message originates from (there's usually no need to pass it explicitly as it + /// defaults to `#line`). + @inlinable + public func critical(_ message: @autoclosure () -> Logger.Message, + metadata: @autoclosure () -> Logger.Metadata? = nil, + file: String = #file, function: String = #function, line: UInt = #line) { + self.critical(message(), metadata: metadata(), source: nil, file: file, function: function, line: line) + } } /// The `LoggingSystem` is a global facility where the default logging backend implementation (`LogHandler`) can be diff --git a/Tests/LoggingTests/LoggingTest+XCTest.swift b/Tests/LoggingTests/LoggingTest+XCTest.swift index 96c24eba..ecf0466d 100644 --- a/Tests/LoggingTests/LoggingTest+XCTest.swift +++ b/Tests/LoggingTests/LoggingTest+XCTest.swift @@ -39,6 +39,8 @@ extension LoggingTest { ("testCustomFactory", testCustomFactory), ("testAllLogLevelsExceptCriticalCanBeBlocked", testAllLogLevelsExceptCriticalCanBeBlocked), ("testAllLogLevelsWork", testAllLogLevelsWork), + ("testAllLogLevelByFunctionRefWithSource", testAllLogLevelByFunctionRefWithSource), + ("testAllLogLevelByFunctionRefWithoutSource", testAllLogLevelByFunctionRefWithoutSource), ("testLogMessageWithStringInterpolation", testLogMessageWithStringInterpolation), ("testLoggingAString", testLoggingAString), ("testMultiplexerIsValue", testMultiplexerIsValue), diff --git a/Tests/LoggingTests/LoggingTest.swift b/Tests/LoggingTests/LoggingTest.swift index dae41d3b..e1d2817f 100644 --- a/Tests/LoggingTests/LoggingTest.swift +++ b/Tests/LoggingTests/LoggingTest.swift @@ -408,6 +408,70 @@ class LoggingTest: XCTestCase { testLogging.history.assertExist(level: .critical, message: "yes: critical") } + func testAllLogLevelByFunctionRefWithSource() { + let testLogging = TestLogging() + LoggingSystem.bootstrapInternal(testLogging.make) + + var logger = Logger(label: "\(#function)") + logger.logLevel = .trace + + let trace = logger.trace(_:metadata:source:file:function:line:) + let debug = logger.debug(_:metadata:source:file:function:line:) + let info = logger.info(_:metadata:source:file:function:line:) + let notice = logger.notice(_:metadata:source:file:function:line:) + let warning = logger.warning(_:metadata:source:file:function:line:) + let error = logger.error(_:metadata:source:file:function:line:) + let critical = logger.critical(_:metadata:source:file:function:line:) + + trace("yes: trace", [:], "foo", #file, #function, #line) + debug("yes: debug", [:], "foo", #file, #function, #line) + info("yes: info", [:], "foo", #file, #function, #line) + notice("yes: notice", [:], "foo", #file, #function, #line) + warning("yes: warning", [:], "foo", #file, #function, #line) + error("yes: error", [:], "foo", #file, #function, #line) + critical("yes: critical", [:], "foo", #file, #function, #line) + + testLogging.history.assertExist(level: .trace, message: "yes: trace", source: "foo") + testLogging.history.assertExist(level: .debug, message: "yes: debug", source: "foo") + testLogging.history.assertExist(level: .info, message: "yes: info", source: "foo") + testLogging.history.assertExist(level: .notice, message: "yes: notice", source: "foo") + testLogging.history.assertExist(level: .warning, message: "yes: warning", source: "foo") + testLogging.history.assertExist(level: .error, message: "yes: error", source: "foo") + testLogging.history.assertExist(level: .critical, message: "yes: critical", source: "foo") + } + + func testAllLogLevelByFunctionRefWithoutSource() { + let testLogging = TestLogging() + LoggingSystem.bootstrapInternal(testLogging.make) + + var logger = Logger(label: "\(#function)") + logger.logLevel = .trace + + let trace = logger.trace(_:metadata:file:function:line:) + let debug = logger.debug(_:metadata:file:function:line:) + let info = logger.info(_:metadata:file:function:line:) + let notice = logger.notice(_:metadata:file:function:line:) + let warning = logger.warning(_:metadata:file:function:line:) + let error = logger.error(_:metadata:file:function:line:) + let critical = logger.critical(_:metadata:file:function:line:) + + trace("yes: trace", [:], #file, #function, #line) + debug("yes: debug", [:], #file, #function, #line) + info("yes: info", [:], #file, #function, #line) + notice("yes: notice", [:], #file, #function, #line) + warning("yes: warning", [:], #file, #function, #line) + error("yes: error", [:], #file, #function, #line) + critical("yes: critical", [:], #file, #function, #line) + + testLogging.history.assertExist(level: .trace, message: "yes: trace") + testLogging.history.assertExist(level: .debug, message: "yes: debug") + testLogging.history.assertExist(level: .info, message: "yes: info") + testLogging.history.assertExist(level: .notice, message: "yes: notice") + testLogging.history.assertExist(level: .warning, message: "yes: warning") + testLogging.history.assertExist(level: .error, message: "yes: error") + testLogging.history.assertExist(level: .critical, message: "yes: critical") + } + func testLogMessageWithStringInterpolation() { let testLogging = TestLogging() LoggingSystem.bootstrapInternal(testLogging.make) @@ -733,14 +797,14 @@ class LoggingTest: XCTestCase { var logger = Logger(label: "test") logger.logLevel = .error - logger.error(Dummy()) + logger.error(error: Dummy()) logging.history.assertExist(level: .error, message: "errorDescription") } } extension Logger { - public func error(_ error: Error, + public func error(error: Error, metadata: @autoclosure () -> Logger.Metadata? = nil, file: String = #file, function: String = #function, line: UInt = #line) { self.error("\(error.localizedDescription)", metadata: metadata(), file: file, function: function, line: line)