Send fx's own events through the service's logger (closes #183)
check / check (push) Successful in 3m46s

fx printed its dependency graph and its start and stop hooks through
its console logger on standard error. fx.WithLogger now hands those
events to the service's logger: building the graph at DEBUG, the hooks,
the start and the stopping signal at INFO, failures at ERROR. The fx
logger's constructor takes the configuration, so the level DEBUG=true
sets is in force before fx replays the events it held back. fx's
SlogLogger takes one level for everything that is not a failure, so
FxLogger in internal/logger holds one at each level. fx moves from
v1.20.1, which has no SlogLogger, to v1.24.0. The README names the Go
runtime as a writer whose output cannot be redirected.

Model: opus-5-5
This commit is contained in:
2026-10-02 14:36:32 +00:00
parent debe588bba
commit f0eb4f0650
7 changed files with 231 additions and 40 deletions
+38
View File
@@ -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)
}
}