package main import ( "log/slog" "os" "time" "github.com/vaughan0/go-ini" "go.bigb.es/auxilia/scribe" "sourcecraft.dev/bigbes/sr-ht-core/config" ) // setupLogging installs the process-wide slog handler. // // It sets the *default* logger rather than building one to thread through // constructors, and that is the point rather than a shortcut. sr-ht-ecore's // middleware reports a recovered panic through slog's default; so does every // library package in this service that has no constructor to be handed a logger // through (authn's cookie and JWT resolvers, the web renderer). A daemon that // skipped this call would still log all of it — into Go's plain stderr handler, // 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. 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 } slog.SetDefault(slog.New(scribe.NewTintHandler( scribe.WithWriter(os.Stderr), scribe.WithLevel(level), scribe.WithSource(true), scribe.WithTimeFormat(time.DateTime), // 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)), // 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 }