Skip to content

Commit

Permalink
Implement scoped loggers from ModuleLoggers 🍰
Browse files Browse the repository at this point in the history
On certain occasions, one needs to pass in a simple `Logger` instance
but want log events forwarded to a larger logging infrastructure.

If we're dealing with a `ModuleLogger` with multiple registered modules,
one may want to derive a simpler `Logger` that sends all events under a
single module to the upstream logger. To achieve that, `ModuleLogger`
can now create scoped loggers via a new `scopedLogger(for:)` helper.

## Changes

- Create `ModuleLogger.scopedLogger(for:)` helper.

- Create new `Log.ForwardingLogger` private helper class to forward log
events to an upstream (Module) logger.
  • Loading branch information
p4checo committed Apr 6, 2021
1 parent 85f7848 commit 7ee0ea1
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 0 deletions.
38 changes: 38 additions & 0 deletions Sources/Logging/Loggers/ModuleLogger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -190,3 +190,41 @@ public extension ModuleLogger where Self: LogDestination {
}
}
}

public extension ModuleLogger {

/// Scopes the logger to one that sends all log events from a single `module` via `self`.
///
/// This can be useful when we need to pass in a simple `Logger` instance but want log events forwarded to a
/// larger logging infrastructure (e.g. with multiple modules), under a single module.
///
/// - Parameter module: The module to scope log events with.
/// - Returns: A new logger that sends all events with the chosen `module` to `self`.
func scopedLogger(for module: Module) -> Logger { Log.ForwardingLogger(scoping: module, from: self) }
}

private extension Log {

final class ForwardingLogger: Logger {

let upstreamLog: (Log.Level, () -> String, StaticString, UInt, StaticString) -> Void

init<L: ModuleLogger>(scoping module: L.Module, from logger: L) {

self.upstreamLog = { level, message, file, line, function in
logger.log(module: module, level: level, message: message(), file: file, line: line, function: function)
}
}

func log(
level: Log.Level,
message: @autoclosure () -> String,
file: StaticString,
line: UInt,
function: StaticString
) {

upstreamLog(level, message, file, line, function)
}
}
}
82 changes: 82 additions & 0 deletions Tests/AlicerceTests/Logging/Loggers/ModuleLoggerTestCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,88 @@ class ModuleLoggerTestCase: XCTestCase {

log.error("message", file: "filename.ext", line: 1337, function: "function")
}

// scopedLogger(for:)

func testScopedLogger_WithVerboseLog_ShouldInvokeUpstreamLogWithCorrectModuleAndLogLevel() {

let scopedLogger = log.scopedLogger(for: .🤖)

log.moduleLogInvokedClosure = { module, level, message, file, line, function in
XCTAssertEqual(module, MockModule.🤖)
XCTAssertEqual(level, .verbose)
XCTAssertEqual(message, "message")
XCTAssertEqual(file.description, "filename.ext")
XCTAssertEqual(line, 1337)
XCTAssertEqual(function.description, "function")
}

scopedLogger.verbose("message", file: "filename.ext", line: 1337, function: "function")
}

func testScopedLogger_WithDebugLog_ShouldInvokeUpstreamLogWithCorrectModuleAndLogLevel() {

let scopedLogger = log.scopedLogger(for: .🤖)

log.moduleLogInvokedClosure = { module, level, message, file, line, function in
XCTAssertEqual(module, MockModule.🤖)
XCTAssertEqual(level, .debug)
XCTAssertEqual(message, "message")
XCTAssertEqual(file.description, "filename.ext")
XCTAssertEqual(line, 1337)
XCTAssertEqual(function.description, "function")
}

scopedLogger.debug("message", file: "filename.ext", line: 1337, function: "function")
}

func testScopedLogger_WithInfoLog_ShouldInvokeUpstreamLogWithCorrectModuleAndLogLevel() {

let scopedLogger = log.scopedLogger(for: .🤖)

log.moduleLogInvokedClosure = { module, level, message, file, line, function in
XCTAssertEqual(module, MockModule.🤖)
XCTAssertEqual(level, .info)
XCTAssertEqual(message, "message")
XCTAssertEqual(file.description, "filename.ext")
XCTAssertEqual(line, 1337)
XCTAssertEqual(function.description, "function")
}

scopedLogger.info("message", file: "filename.ext", line: 1337, function: "function")
}

func testScopedLogger_WithWarningLog_ShouldInvokeUpstreamLogWithCorrectModuleAndLogLevel() {

let scopedLogger = log.scopedLogger(for: .🤖)

log.moduleLogInvokedClosure = { module, level, message, file, line, function in
XCTAssertEqual(module, MockModule.🤖)
XCTAssertEqual(level, .warning)
XCTAssertEqual(message, "message")
XCTAssertEqual(file.description, "filename.ext")
XCTAssertEqual(line, 1337)
XCTAssertEqual(function.description, "function")
}

scopedLogger.warning("message", file: "filename.ext", line: 1337, function: "function")
}

func testScopedLogger_WithErrorLog_ShouldInvokeUpstreamLogWithCorrectModuleAndLogLevel() {

let scopedLogger = log.scopedLogger(for: .🤖)

log.moduleLogInvokedClosure = { module, level, message, file, line, function in
XCTAssertEqual(module, MockModule.🤖)
XCTAssertEqual(level, .error)
XCTAssertEqual(message, "message")
XCTAssertEqual(file.description, "filename.ext")
XCTAssertEqual(line, 1337)
XCTAssertEqual(function.description, "function")
}

scopedLogger.error("message", file: "filename.ext", line: 1337, function: "function")
}
}

private enum MockModule: String, LogModule {
Expand Down

0 comments on commit 7ee0ea1

Please sign in to comment.