Documentation
¶
Overview ¶
Package logging owns the bridge's structured-logging entry point. All `internal/` packages route their telemetry through a `Component(name)` logger so user-submitted logs carry a stable, grep-friendly `component=<name>` attribute and key/value pairs for paths, IDs, error text — diagnosing field issues no longer requires regexing concatenated `log.Printf` strings.
Convention:
- Every package gets `var logger = logging.Component("scanner")` (or `enricher`, `admin`, `auth`, `manifest`, `api`, `tls`, `updater`).
- Levels: `Error` for caught failures (caller logs once and continues or returns), `Warn` for degraded-but-functional state, `Info` for state transitions worth a line in the log, `Debug` for development noise (default handler hides these).
- Attribute keys are short and stable: `path`, `mbid`, `addr`, `rows`, `err`. Don't invent per-callsite key names.
- CLI commands in `cmd/bridge/` keep `fmt.Fprintf(stdout/stderr)` — those are user output, not telemetry.
**Why a dynamic handler under the hood**: Go evaluates package- level `var logger = logging.Component(...)` declarations during package initialization, BEFORE main()'s `logging.Init(...)` runs. A naive Component returning `slog.New(handler)` would capture whatever handler exists at init time and stick with it forever — post-Init Windows-service-log redirects or operator-supplied destinations would never reach the package-level loggers (high- priority bot review on PR #77).
We work around that with `dynamicHandler`, a thin shim that resolves `slog.Default().Handler()` on every Handle() call. The shim remembers attrs and groups locally so chained `.With("component", name)` survives the indirection. After `logging.Init(w)` calls `slog.SetDefault(...)`, every previously- constructed logger naturally picks up the new handler — no re-instantiation required.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Component ¶
Component returns a logger that tags every record with `component=<name>` and resolves the underlying handler at log-time via `slog.Default()`. Safe to call before `Init` — calls before Init route to slog's default-default (stderr text handler) and pick up the operator-supplied destination on the first record after Init runs.
var logger = logging.Component("scanner")
logger.Info("starting scan", "roots", len(roots))
func Init ¶
Init configures slog's default Logger to write text-format records to w. Called once at process startup from `cmd/bridge/main.go`. Repeat calls are no-ops — the destination is locked in after the first configuration so a misuse from a test can't flip the destination underneath an in-flight log line.
nil w falls back to os.Stderr.
`Init` mutates `slog.Default()` rather than holding a private root: every Component-returned logger reads through `dynamicHandler`, which calls `slog.Default().Handler()` at log time. Updating the default is what propagates the new handler to every existing component logger.
func RegisterLogHook ¶ added in v0.1.4
func RegisterLogHook(f func(level string))
RegisterLogHook installs a callback invoked on every log emission. Typically called from `internal/metrics.init()` to wire the log- level counter. Safe to call multiple times — last-writer-wins; pass nil to disable.
`atomic.Pointer[func]` load is ~1 ns on amd64/arm64; the hot-path cost of the call site below is negligible relative to the downstream handler's I/O.
Types ¶
This section is empty.