How to be a good slog module?

216 views
Skip to first unread message

TheDiveO

unread,
Aug 23, 2023, 2:09:32 PM8/23/23
to golang-nuts
Up front, I admit my sin of arrogance in giving structured logging the slip these past years and misusing logrus just as a text logger with multiple log levels. This question is kind of my atoning...

For preparation, I've read through (today's?) Go slog blog post, as well as some comparably recent 3rd party blog posts about Golangs new slog.

What I haven't found (or maybe stubbornly refused to see) is: as I want to convert my existing consumable modules to slog, how am I going to be a good citizen, so other modules can easily consume my modules with proper control over logging? How am I giving my module "customers" control over per-module (but not necessarily per-package) logging? Are there already best practises and what are they?

While ponding these questions, I would suspect, that creating a per-module default "slogger" when none has been set in a module's init() would be a bad idea: due to the order of module initialization, all my module customers/consumers would end up with the default slogger that mostly would not fit their needs.

Simply exposing a public variable would be either suffering the init problem, or alternatively, I would create default sloggers that then have to be garbage collected anyway.

And now, Ladies and Gentlemens, show me your consumable slogging please! I want to learn!

Sean Liao

unread,
Aug 23, 2023, 3:29:31 PM8/23/23
to golang-nuts
If your modules are mostly structs with lifecycle methods,
do it like http.Server where the logger is a field (or option passed into the constructor).

Else if your modules take contexts, you could consider having your own 
WithLogger / FromContext funcs to pass a logger in a context.
An  advantage of this over a (declined in slog) global context key 
is that this allows your consumers to target your module with a specific logger.

Finally, you can do the same as log / slog are doing with Default() SetDefault()
possibly guarded with an RWMutex, or just document that it's unsafe to change after start.

Personal preference is for 1,
and whether you take a Logger vs Handler 
would just be if you have a preference for a different frontend.

- sean


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/cc4f2bcb-4972-4966-b2ff-341f29339d67n%40googlegroups.com.

Andrew Harris

unread,
Aug 23, 2023, 4:10:08 PM8/23/23
to golang-nuts
I've found implementing LogValuer can be a useful approach as well. Depending on the package, it can be enough to suggest a package doesn't need to be doing any logging, and if refactoring code from unstructured to structured logging it's useful in any case.

TheDiveO

unread,
Aug 24, 2023, 5:24:18 AM8/24/23
to golang-nuts
While my modules can be used in HTTP handlers, they are also often used outside such use cases in other tools. Some modules spawn long-running background go routines, others do complex system scanning operations, finally returning a complex cyclic information model. What I would like to avoid is having to check at every logging call site whether there's a logger present or not. Is there some best practice/pattern as to how create a default slogger that "does nothing", avoiding having to guard all logging call sites? I would like my module consumers to be able to disable logging for this module.
Reply all
Reply to author
Forward
0 new messages