Description
I need a way where multiple, independent libraries can accumulate metadata down the callstack.
So for a call stack looking like
LibA.someFunc
-> LibB.anotherFunc
-> LibC.yetAnotherOne
-> LibD.loggingFunction
I want LibA.someFunc, LibB.anotherFunc and LibC.yetAnotherOne to be able to accumulate metadata keys that are attached to messages logged by LibD.loggingFunction.
For example if LibD.loggingFunction calls logger.info("hello world"), I would expect the following log message:
DATETIME INFO hello world ["LibA-Key": "A", "LibB-Key": "B", "LibC-Key": "C"]
assuming that LibA.someFunc added LibA-Key and so on.
Given that we now have Baggage and TaskLocals, I would expect this to work without having to pass Logger down the stack manually.
Concrete ask
I propose to create a standard way where all programs & libraries can retrieve and modify a Logger from "the environment" (deliberately vague here). For example, that could be a Logger in a TaskLocal; or a Baggage in a TaskLocal where the Baggage has a well-known baggage.logger key; or something entirely different.
The important bottom line is that each function can add/modify metadata values as well as other properties (such as logLevel) of the Logger.
Why are MetadataProviders not enough?
Metadata providers require (at the MetadataProvider creation time) to know all the metadata that needs to be coalesced. That cannot in general because it's impossible to determine which libraries might get called just to fish out their own MetadataProviders.
Current, working solution (manually passing logger)
This all works totally fine today in this example program
func foo(logger: Logger) async throws {
var logger = logger
logger[metadataKey: "foo"] = "\(UUID())"
for f in 0..<10 {
try await bar(logger: logger)
}
}
func bar(logger: Logger) async throws {
var logger = logger
logger[metadataKey: "bar"] = "\(UUID())"
logger.logLevel = .trace
for f in 0..<10 {
try await httpClient.get("https://foo.bar/buz", logger: logger)
}
}
// main
let logger = Logger(label: "my-service")
try await foo(logger: logger)
But please note that I have to manually thread through the Logger.
Description
I need a way where multiple, independent libraries can accumulate metadata down the callstack.
So for a call stack looking like
I want
LibA.someFunc,LibB.anotherFuncandLibC.yetAnotherOneto be able to accumulate metadata keys that are attached to messages logged byLibD.loggingFunction.For example if
LibD.loggingFunctioncallslogger.info("hello world"), I would expect the following log message:assuming that
LibA.someFuncaddedLibA-Keyand so on.Given that we now have
Baggageand TaskLocals, I would expect this to work without having to passLoggerdown the stack manually.Concrete ask
I propose to create a standard way where all programs & libraries can retrieve and modify a
Loggerfrom "the environment" (deliberately vague here). For example, that could be aLoggerin a TaskLocal; or aBaggagein a TaskLocal where theBaggagehas a well-knownbaggage.loggerkey; or something entirely different.The important bottom line is that each function can add/modify metadata values as well as other properties (such as logLevel) of the Logger.
Why are MetadataProviders not enough?
Metadata providers require (at the
MetadataProvidercreation time) to know all the metadata that needs to be coalesced. That cannot in general because it's impossible to determine which libraries might get called just to fish out their own MetadataProviders.Current, working solution (manually passing logger)
This all works totally fine today in this example program
But please note that I have to manually thread through the
Logger.