Wrt how to reach the logger, I think having global functions that "do the right thing" without the user having to pass around a logger (for the common cases) would be very valuable.
If I'm building some new component, and the "proper" way of logging is just as easy as calling
print is, I'll use the proper way for sure. But if I need to figure out some way to inject a logger into the new component or do some other setup, I'm probably going to fall back to using
print at least some of the time, if I'm honest with myself.
I thing the Rust
log crate would be a nice model to follow/build upon.
It's built on one premise that I think is a good one: In the default case, only binaries should control where and how logs end up. Libraries are encouraged to use the log macros that log into a global logger instance, but are discouraged from setting or touching that global logger in some other way. In fact, the global logger can only be set once. The default global logger when nothing is set is a noop-implementation.
This does not stop libraries from passing around their own logger instance to do specialized logging to some file or service or something, but makes sure that the path of least resistance (just writing
debug!("something")) doesn't mean clients of libraries have to deal with stuff in stdout/err they don't want.
Log trait is also very small, and doesn't include anything relating to nesting etc. That is left to the individual loggers to design an API for, which keeps the
log crate very small and focused, just providing the logging macros, the
Log trait, and a way to set the global logger.
Then there are some other niceties:
Every log has a
target (module path by default), that the global logger can use to filter things (think different log levels for different modules, we could probably use
#file until a proper module system comes along).
They optimize performance when logging is disabled by asking the logger beforehand whether a log statement with a given target and log level would even be logged. If it wouldn't, the message is never constructed. We might not need this and just use an auto-closure instead, but I'm not sure how expensive those are to construct.
(whoops, wall of text I'll see if I have the time to hack together a quick Swift-clone of the log crate later today, to see how well it would translate)