Adopt swift-log task-local logger (Logger.current)#685
Conversation
|
@0xTim or @adam-fowler Can you have a look at this PR when you will have 1h ? Feedback and comments welcome. |
|
I've gone about this completely differently with Hummingbird. I've treated Hummingbird as a top level object that defines the logger (this is what it was before). Then I wrap each connection in a For me moving to using the task local logger internally is a breaking change as it changes how you set the logger. You now have a confused API where you can set the logger either from the runtime configuration or via task locals. Which one takes precedence? |
|
Thank you @adam-fowler. This PR doesn't change how users provide a logger. It's actually close to what you describe for Hummingbird: the runtime owns the logger definition, and wraps each invocation in a The only behavioral change is the default value of the existing "which logger wins"? There is only one input: the That said, your underlying concern is fair: because the default is The resolving order at init time is like this:
Per invocation, the runtime derives a request logger from that base (adding The automatic There are two approaches to use a logger, let the runtime manage the logger or provide my own logger. Approach 1: let the runtime manage the logger Before this PR: let runtime = LambdaRuntime { (event: Request, context: LambdaContext) in
context.logger.info("Processing request")
helperFunction(event, logger: context.logger) // logger threaded manually
}
try await runtime.run()After this PR (Swift 6.2+), the same code works, plus let runtime = LambdaRuntime { (event: Request, context: LambdaContext) in
context.logger.info("Processing request") // unchanged
helperFunction(event) // no logger argument needed
}
try await runtime.run()
func helperFunction(_ event: Request) {
Logger.current.debug("Validating") // picks up requestID / traceID automatically (Swift 6.2+)
}Approach 2: provide my own logger Unchanged before and after: the explicit parameter still wins: var myLogger = Logger(label: "my-function")
myLogger.logLevel = .trace
let runtime = LambdaRuntime(logger: myLogger) { (event: Request, context: LambdaContext) in
context.logger.info("Processing request")
}
try await runtime.run() |
swift-log 1.14 added a task-local logger (SLG-0006). This wires it into the runtime so handlers can read
Logger.currentinstead of threadingcontext.loggerthrough every function they call.The runtime now binds the per-invocation logger as
Logger.currentaround the handler call. Any code in the handler's call tree picks it up automatically, with the request ID and trace ID metadata already attached.context.loggerkeeps working exactly as before, and inside a handler the two are equivalent.No public API breaks. This is purely additive:
LambdaContextandLoggingConfigurationinitializers that useLogger.current. The old logger-taking ones are deprecated and will go away in the next major.Logger(label: "...")toLogger.current, so a runtime built inside an app'swithLogger(...)scope inherits that logger.A note on toolchains: the async
withLoggeroverload needs Swift 6.2, so the binding in the run loop is behind#if compiler(>=6.2). On 6.1 the handler still runs, just without the automaticLogger.currentbinding (context.loggeris unaffected). We can drop the guard when 6.1 support goes away (when Swift 6.4 is GA). swift-log floor bumped to 1.14.0 in both Package files.Examples and docs:
Logger.currentwith no logger parameter.withLoggerat startup and readsLogger.currentin its DB helpers.Tests cover the run loop binding the logger, metadata propagating through structured concurrency, and the new logger-free initializers.