Documentation
¶
Overview ¶
Package logging holds process-boot logging hardening that sits on top of slogconf's default handler: secret redaction (this file) and the gitignored chatz.log file sink (filesink.go). Both are wired from cmd/init.go, the framework's designated hook for custom slog handlers.
Index ¶
Constants ¶
const ( ScopeKeyRequestID = "request_id" ScopeKeyUserID = "user_id" ScopeKeyIsAdmin = "is_admin" )
The identity attributes chatz puts on the request scope. They are set once at the HTTP boundary and every log line under that context carries them, so they live here rather than in the HTTP package: the usage recorder reads the request id to attribute token spend, and it must not import the HTTP layer to do it.
These belong on the scope tier, not the global one — they describe the WORK, and a process-wide value would survive into the next request.
const ( ScopeKeyService = "service" ScopeKeyVersion = "version" ScopeKeyCommit = "commit" )
The facts that describe the BINARY rather than any one request. Set once at startup on the global tier, which ToJSON never serializes — sending a service name across a hop would overwrite the receiving service's own.
const LogFilePath = "chatz.log"
LogFilePath is where the file sink writes, relative to the process CWD (/app in the production image — see docker-compose.yml's bind mount of ./chatz.log:/app/chatz.log so it's readable from the host). Hardcoded per config.go's convention: no existing CHATZ_* env var covers a log-file path, so this stays a constant rather than adding config surface for a single fixed value.
Variables ¶
This section is empty.
Functions ¶
func AddFileSink ¶
AddFileSink opens LogFilePath (truncating any content from a previous boot — this file is a per-run diagnostic tail, not a rotating archive) and stacks a JSON handler writing to it alongside whatever slogconf already set up (stdout/stderr). The app's normal stdout stream — the one `docker compose logs` reads — is untouched.
Returns the opened file so the caller can close it on shutdown; callers that don't care about a clean close (the CLI process just exits) may ignore it.
func RedactText ¶
RedactText masks sensitive key/value pairs inside text before the text is assigned to an otherwise non-sensitive log key such as message content. JSON is traversed structurally; ordinary text uses a conservative key/value-pattern fallback.
func RequestIDFromScope ¶
RequestIDFromScope returns the correlation id for the current request, or "" when the caller runs outside one (a background job, a test).
It exists so the key is written down once: a caller that hand-typed "request_id" would keep compiling after a rename and silently read nothing.
func WrapDefaultWithRedaction ¶
func WrapDefaultWithRedaction()
WrapDefaultWithRedaction rewraps slog.Default()'s current handler (the stdout/stderr split slogconf already set up at its own init time) in a RedactingHandler, so every log line — not just future AddSink sinks like the chatz.log file — gets secret-shaped fields masked.
Must run AFTER slogconf's blank-import init (guaranteed by Go's init ordering: imported packages initialize before the importing package's own init() runs) and BEFORE AddFileSink, so the file sink is added on top of the now-redacting default rather than bypassing it.
Types ¶
type RedactingHandler ¶
type RedactingHandler struct {
// contains filtered or unexported fields
}
RedactingHandler wraps an inner slog.Handler and masks the value of any attr (at any nesting depth, including inside slog groups) whose key matches sensitiveKeyRE. It's the safety net for the DEBUG-level SQL logging already enabled in this deployment and for any future MCP bearer-token / API-key field that ends up in a log call.
func NewRedactingHandler ¶
func NewRedactingHandler(inner slog.Handler) *RedactingHandler
NewRedactingHandler wraps inner so every record it handles is redacted first.
func (*RedactingHandler) Handle ¶
Handle redacts every attr on the record (recursively through groups) before delegating to the inner handler.