Send fx's own events through the service's logger (closes #183)
check / check (push) Successful in 3m14s
check / check (push) Successful in 3m14s
fx printed its dependency graph and lifecycle hooks through its own console logger on standard error, so an operator shipping the JSON log to a collector got a second shape on a second stream for every start. The production app now passes fx.WithLogger with a small FxLogger in internal/logger that writes fx's events through the service's logger: graph events at debug, lifecycle at info, failures at error. Its constructor takes the configuration, so DEBUG=true applies before fx replays the events it held back. go.uber.org/fx moves from v1.20.1 to v1.24.0. Tests keep fx.NopLogger. The README says a failure before the logger exists, and the Go runtime's own output, still go to standard error as plain text. Model: opus-5-5
This commit was merged in pull request #443.
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"go.uber.org/fx"
|
||||
"go.uber.org/fx/fxevent"
|
||||
"sneak.berlin/go/webhooker/internal/globals"
|
||||
)
|
||||
|
||||
@@ -106,3 +107,40 @@ func (l *Logger) Identify() {
|
||||
func (l *Logger) Writer() io.Writer {
|
||||
return os.Stdout
|
||||
}
|
||||
|
||||
// FxLogger writes fx's own events through a slog logger: how the
|
||||
// dependency graph was built at DEBUG, since it repeats on every
|
||||
// start; the start and stop hooks, the start itself and the signal
|
||||
// that stops the service at INFO; every failure at ERROR.
|
||||
//
|
||||
// The formatting is fx's own fxevent.SlogLogger. That logger takes a
|
||||
// single level for every event that is not a failure, so FxLogger
|
||||
// holds one at each level and picks between them.
|
||||
type FxLogger struct {
|
||||
graph *fxevent.SlogLogger
|
||||
lifecycle *fxevent.SlogLogger
|
||||
}
|
||||
|
||||
// NewFxLogger returns an FxLogger that writes through log.
|
||||
func NewFxLogger(log *slog.Logger) *FxLogger {
|
||||
graph := &fxevent.SlogLogger{Logger: log}
|
||||
graph.UseLogLevel(slog.LevelDebug)
|
||||
|
||||
lifecycle := &fxevent.SlogLogger{Logger: log}
|
||||
lifecycle.UseLogLevel(slog.LevelInfo)
|
||||
|
||||
return &FxLogger{graph: graph, lifecycle: lifecycle}
|
||||
}
|
||||
|
||||
// LogEvent implements fxevent.Logger.
|
||||
func (f *FxLogger) LogEvent(event fxevent.Event) {
|
||||
switch event.(type) {
|
||||
case *fxevent.Supplied, *fxevent.Provided, *fxevent.Replaced,
|
||||
*fxevent.Decorated, *fxevent.BeforeRun, *fxevent.Run,
|
||||
*fxevent.Invoking, *fxevent.Invoked,
|
||||
*fxevent.LoggerInitialized:
|
||||
f.graph.LogEvent(event)
|
||||
default:
|
||||
f.lifecycle.LogEvent(event)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user