From 95bedef83a9a6095cb34fa5c12570630b0513ca4 Mon Sep 17 00:00:00 2001 From: Eugene Blikh Date: Sun, 9 Aug 2026 00:23:25 +0300 Subject: [PATCH] logging: take the log policy from ecore setupLogging's level parser, its os.Stderr.Stat colour probe and its three-key mask list were one of six copies. Defaults(conf, section) resolves all of it now and the handler stays here, which is the split that package documents. Three things follow from taking the instance's list instead of this service's own: the mask set gains the config-file private keys and the migration DSN that siblings had already learned to redact, NO_COLOR is honoured, and verbosity can be set for one run with $LOG_LEVEL or -d. [dolt.sr.ht]log-level is unchanged. --- cmd/doltsrht/logging.go | 59 ++++++++++++++++++++--------------------- config.example.ini | 4 +++ 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/cmd/doltsrht/logging.go b/cmd/doltsrht/logging.go index cb03a0c53d21fc9a95ea27180ee6f1589fc0b757..d0e5d13c0f2ae727b209955d47ead074d23669ed 100644 --- a/cmd/doltsrht/logging.go +++ b/cmd/doltsrht/logging.go @@ -1,15 +1,13 @@ package main import ( - "log/slog" "os" - "time" "github.com/vaughan0/go-ini" "go.bigb.es/auxilia/scribe" - "sourcecraft.dev/bigbes/sr-ht-core/config" + "sourcecraft.dev/bigbes/sr-ht-ecore/logging" ) // setupLogging installs the process-wide slog handler. @@ -23,38 +21,39 @@ import ( // without a level, without source positions, and above all without the masking // below. // -// The level comes from [dolt.sr.ht]log-level ("debug", "info", "warn", -// "error"); an unreadable value is info, because a daemon that refused to boot -// over a typo in a log level would be trading an operator's whole service for -// their logging preference. +// The policy is sr-ht-ecore's and the handler is ours, which is the split that +// package documents: the level, the colour decision and — the part that is not +// presentation — the set of attribute keys that must never reach a log file are +// facts about this instance and are maintained once, while the handler that +// spends them stays here, because a service wanting a JSON handler for a log +// shipper should not have to link a tinting one to share a mask list. +// +// The verbosity is [dolt.sr.ht]log-level ("debug", "info", "warn", "error") as +// before, and now also $LOG_LEVEL and the -d every SourceHut daemon takes. -d is +// resolved here rather than left to server.New because server.New runs after +// config loading and validation: a daemon that becomes verbose only once it has +// started is silent for exactly the window an operator passes -d to watch. An +// unreadable value is info, because a daemon that refused to boot over a typo in +// a log level would be trading an operator's whole service for their logging +// preference. func setupLogging(conf ini.File) { - var level slog.Level - if err := level.UnmarshalText([]byte(config.GetString(conf, serviceName, "log-level", "info"))); err != nil { - level = slog.LevelInfo - } + opts := logging.Defaults(conf, serviceName) - slog.SetDefault(slog.New(scribe.NewTintHandler( + 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 or a container's log // collector the escapes are noise in the journal. - scribe.WithNoColor(!isTerminal(os.Stderr)), + scribe.WithNoColor(!opts.Color), // The masks are keyed on the attribute *path*, not on the message, so // they cost nothing in prose and cannot be defeated by a sentence that - // happens to contain the word "token". These three are the credentials - // this service handles: the unified-login cookie, the "Internal" - // service-to-service fernet token and the Authorization header the - // remotesapi interceptors read a PAT or a keypair JWT out of. - scribe.WithMaskKeys("token", "cookie", "authorization"), - scribe.WithMask(`(?i)(secret|token|api_?key|password|pubkey|credential)`, "***"), - ))) -} - -// isTerminal reports whether f is a character device, which is the whole of -// what the colour decision needs and does not require a dependency to answer. -func isTerminal(f *os.File) bool { - info, err := f.Stat() - return err == nil && info.Mode()&os.ModeCharDevice != 0 + // happens to contain the word "token". Taking the instance's list rather + // than this service's three adds the keys the siblings had learned to + // redact and this one had not — the network and webhook private keys out + // of config.ini, and the migration DSN, which carries a password. + scribe.WithMaskKeys(opts.MaskKeys...), + scribe.WithMask(opts.MaskPattern, opts.MaskReplacement), + )) } diff --git a/config.example.ini b/config.example.ini index 756bb0fcfc91d0a76e0961cb8930c02eca63831e..048833a87d2fd6d38149cf3cb92462e78b8835f1 100644 --- a/config.example.ini +++ b/config.example.ini @@ -42,6 +42,10 @@ migrate-on-upgrade=yes ; Verbosity of the daemon's structured log: debug, info, warn or error. An ; unreadable value is read as info rather than refusing to boot — a typo in a ; logging preference must not cost the service. +; +; This is the persistent setting. For one run, $LOG_LEVEL overrides it and the +; -d flag overrides both — and both apply from the first line of startup, before +; the config below has been read. log-level=info ; ---------------------------------------------------------------------------