Use slog as default and only logger
Heya,
as discussed in #1032 and slack I would like to tackle the refactor from text based standard logger and zap structured logger to a single logger around golangs slog with the goal to simplify logging in Tegola, and moving logging closer to the standard libraries.
Status quo
- The old implementation uses global variables (e.g., logger, level, IsError, etc.) and manual flag management for log level checks
- even though we log structured logs, we do not use structured logging, we put a message in a structured log, that's it.
This approach was necessary 5 years ago, but fell out of time.
Migration stages
Stage 1
- drop
zapand std logger in favour ofslog - add a
tracelogto everyErrorlog for easier debugging. We achieve this with a custom log handler that adds a stacktrace field and populates it. The custom handler will be passed toslog.SetDefault()and will become the global logger. - use
sloginlog.Info,log.Infof(...) to keep compatibility and reduce the changes in stage 1 to a minimum - follow
slogs approach ofInfo,Debug,WarnandErrorto keep things simple - Drop
Fatal(f).Fatal(f)is really only anError(f)with anos.Exit(1). Theos.Exit(1)should be explicit, not implicitly hidden in a fatal log. - Drop
Trace, it's not being used anyways - logs will go to
os.Stderrinstead ofos.Stdout, use one output for simplicity.
Stage 2
- drop
log.Debug, log.Infofand the likes - modules will either import and use
slog.Default(), that at this point is out custom logger, - or they receive the logger via dependency injection.
- Module-specific loggers can be derived by adding context using
WithorWithGroup.
Can we agree on this? :)
I'm 100% for this! Thank you!
@iwpnd I also support this! And thank you for putting momentum behind this update.
even though we log structured logs, we do not use structured logging, we put a message in a structured log, that's it.
I agree with this point. One problem I find when implementing structured logging is that the "keys" are littered throughout the codebase in random places. An idea as we go through this transition:
- Let's add a
logging.gofile to each package that implements structured logging. - In the
logging.gofile we defineconstfor each log "key". For example:
package foo
const (
logKeyRequestID = "request_id"
logKeyRequestMapName = "map_name"
)
This would help drive consistency in the package as well as give us a single file to start investigating the package's logging structure. We could also expand what's in the logging.go file, but a minimum I think it would be good to define the log keys.
That would also allow us to have a single logger per package, instead of a global logger. That logger could have predefined default fields such as {“provider”:”“mvt_postgis“}.
@iwpnd yes, excellent point. We could use this same file to define our default grouping for the package.