feat(logger): deliver stderr to systemd journal with per-record priority
When stderr is connected to journald, switch from the plain text handler to the native journal protocol via github.com/systemd/slog-journal. Each record now carries PRIORITY, MESSAGE, and slog attributes as structured fields, so journalctl -p and LogLevelMax= in unit files filter by severity instead of dropping everything at once. Detection follows systemd.exec(5): parse JOURNAL_STREAM as <dev>:<ino> and fstat(2) stderr to confirm the match, guarding against processes that inherit the env var but have stderr redirected elsewhere. The fstat path is Linux-only; other platforms fall through to the existing text handler. Also honour DEBUG_INVOCATION by raising stderr to Debug level, matching the behaviour of systemd.service(5) RestartMode=debug.
This commit is contained in:
@@ -128,6 +128,15 @@ func setupStdErrHandler() slog.Handler {
|
||||
}
|
||||
}
|
||||
|
||||
// DEBUG_INVOCATION is set by systemd on a restart attempt of a unit
|
||||
// configured with RestartMode=debug when the previous start failed.
|
||||
// Treat it as a request to raise stderr verbosity to Debug so the
|
||||
// next failing cycle yields maximum diagnostics. OTLPLevel is left
|
||||
// alone — that stays under server/admin control.
|
||||
if os.Getenv("DEBUG_INVOCATION") != "" {
|
||||
Level.Set(slog.LevelDebug)
|
||||
}
|
||||
|
||||
logOptions := &slog.HandlerOptions{Level: Level}
|
||||
|
||||
if len(os.Getenv("INVOCATION_ID")) > 0 {
|
||||
@@ -137,8 +146,15 @@ func setupStdErrHandler() slog.Handler {
|
||||
logOptions.ReplaceAttr = logRemoveTime
|
||||
}
|
||||
|
||||
var base slog.Handler
|
||||
if h := newJournalHandler(logOptions); h != nil {
|
||||
base = h
|
||||
} else {
|
||||
base = slog.NewTextHandler(os.Stderr, logOptions)
|
||||
}
|
||||
|
||||
logHandler := slogtraceid.OtelHandler{
|
||||
Next: slog.NewTextHandler(os.Stderr, logOptions),
|
||||
Next: base,
|
||||
}
|
||||
|
||||
return logHandler
|
||||
|
||||
Reference in New Issue
Block a user