~bigbes/sr-ht-dolt

ref: 4d178626e1f2d04330127c394e0684a78a2d7102 sr-ht-dolt/cmd/doltsrht/logging.go -rw-r--r-- 2.4 KiB
4d178626 — Eugene Blikh log: bridge dolt's remotesrv logger into slog 9 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
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
}