diff --git a/cmd/comparesrht/main.go b/cmd/comparesrht/main.go index 331844ce336f7618490a100c568c9feed550abe5..e7eca64380ea590ce7bc0b63260ee71991500b1e 100644 --- a/cmd/comparesrht/main.go +++ b/cmd/comparesrht/main.go @@ -28,7 +28,6 @@ import ( "fmt" "log/slog" "os" - "slices" "strings" "time" @@ -38,6 +37,7 @@ "github.com/vaughan0/go-ini" "go.bigb.es/auxilia/scribe" "sourcecraft.dev/bigbes/sr-ht-core/config" coreserver "sourcecraft.dev/bigbes/sr-ht-core/server" + "sourcecraft.dev/bigbes/sr-ht-ecore/logging" "sourcecraft.dev/bigbes/sr-ht-ecore/login" "sourcecraft.dev/bigbes/sr-ht-compare/authz" @@ -57,47 +57,46 @@ // initLogging installs the instance's log handler as slog's default. // // Setting the *default* is the load-bearing part, not the formatting: the // packages that log below this one hold no logger of their own — the web tier -// calls slog's package functions, and so does ecore's panic-recovery -// middleware, whose report (method, path, panic, stack) would otherwise go to -// Go's plain stderr handler with the stack as one unreadable field. +// calls slog's package functions, and so do ecore's panic-recovery middleware +// and its request logger, whose records would otherwise go to Go's plain +// stderr handler with the stack as one unreadable field. // -// -d is read straight out of the argument vector because core-go's server.New -// owns the real flag parse and does not run until after this: a daemon that -// only became verbose once it had finished starting would be silent for exactly -// the part of its life an operator passes -d to watch. -func initLogging(debug bool) { - level := slog.LevelInfo - if debug { - level = slog.LevelDebug - } - slog.SetDefault(slog.New(scribe.NewTintHandler( +// The policy is ecore's and the handler is ours, which is the split logging +// documents: what is masked and how verbose to be are facts about the instance, +// while a tinting handler is auxilia's and does not belong in a library every +// service links for its page chrome. +// +// Defaults(nil, "") because config.ini is not loaded yet, and deliberately so: +// -d is read straight out of the argument vector — core-go's server.New owns +// the real flag parse and does not run until after config validation, and a +// daemon that only became verbose once it had finished starting would be silent +// for exactly the part of its life an operator passes -d to watch. The two +// knobs that work here are -d and $LOG_LEVEL; a [compare.sr.ht] log-level key +// would be read too late to matter and is not supported. +func initLogging() { + opts := logging.Defaults(nil, "") + logging.Install(scribe.NewTintHandler( scribe.WithWriter(os.Stderr), - scribe.WithLevel(level), - scribe.WithSource(true), - scribe.WithTimeFormat(time.DateTime), + scribe.WithLevel(opts.Level), + scribe.WithSource(opts.AddSource), + scribe.WithTimeFormat(opts.TimeFormat), // Colour is for a terminal; under systemd stderr is the journal, where - // the escapes are noise in every stored record. Asked of the stdlib - // rather than of golang.org/x/term, which would be a whole new - // dependency for one predicate. - scribe.WithNoColor(!isTerminal(os.Stderr)), + // the escapes are noise in every stored record. logging.ColorEnabled + // answers that from one Stat, and honours NO_COLOR besides. + scribe.WithNoColor(!opts.Color), // This daemon logs no credential deliberately, which is precisely why // the masks are here: the one that leaks is the attribute somebody adds // later, and a request or a cookie is the likeliest thing to be handed // to a log line while debugging the very cookie path this service reads. - scribe.WithMaskKeys("token", "cookie", "authorization"), - scribe.WithMask(`(?i)(secret|token|api_?key|password)`, "***"), - ))) -} - -// isTerminal reports whether f is a character device — a tty rather than the -// pipe systemd, a build runner or a shell redirect hands the process. -func isTerminal(f *os.File) bool { - info, err := f.Stat() - return err == nil && info.Mode()&os.ModeCharDevice != 0 + // The list is the instance's, so a key another service learned to redact + // is redacted here without anybody editing this file. + scribe.WithMaskKeys(opts.MaskKeys...), + scribe.WithMask(opts.MaskPattern, opts.MaskReplacement), + )) } func main() { - initLogging(slices.Contains(os.Args[1:], "-d")) + initLogging() // LoadConfig never panics on a missing file (it returns a nil ini.File); // validateConfig turns any absent required key into a single clear fatal.