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
}