- Each module logs through diary.For(<package>): cabana, lagoon, attach, cooler, postcard, beachcomber, lighthouse, centrifugo, flare, phrasebook, wristband, bouncer - App-published *slog.Logger overrides stay first; messages and attributes unchanged - sunscreen: InstallDefault documented as the compatibility entry point; diary wraps every channel - READMEs list diary as a dependency; logging docs: plugins use diary.For
4.3 KiB
sunscreen
Credential-redacting slog handler that keeps API keys, tokens and passwords out of application logs.
import "git.golem15.com/golem15/summercms/modules/sunscreen"
Name: Protects you from exposure; it keeps secrets out of the logs.
Overview
sunscreen wraps any log/slog handler and rewrites every record before it is written: values of sensitive attribute keys become [REDACTED], and credential shapes inside messages and string values are scrubbed. It is the SummerCMS counterpart of a Monolog tap that a WinterCMS application wires into its log channels to scrub contexts and messages. Every application main that summer build generates installs diary, which wraps each of its log channels (console, stderr, daily files, null) with sunscreen.Wrap, so component loggers, the default logger and the standard log package all log through it from the start. sunscreen.InstallDefault stays as the compatibility entry point for programs that install their own default logger; there is no other install function.
Role in the framework
A dependency-free leaf that diary wraps around every log channel. It is separate because it wraps the standard slog handler and applies to all log output, whichever module writes it.
Use cases:
- Keep API keys, tokens and passwords out of logs, including logs from third-party code.
- Log request and configuration details while debugging without leaking secrets.
- Meet a "no credentials in logs" audit requirement in one place.
Features
- Sensitive keys (
sunscreen.RedactedKeys):api_key,apikey,authorization,bearer,password,secret,token,webhook_secret,admin_password,openai_api_key,anthropic_api_keyandperplexity_api_key, compared case-insensitively at any group depth. The value of such an attribute becomessunscreen.Redacted, whatever its kind. - Scrub patterns (
sunscreen.Scrub):Bearer <token>becomesBearer [REDACTED],sk-followed by 20 or more key characters (dashed keys included) becomessk-[REDACTED], andx-api-key: <value>becomesx-api-key: [REDACTED]. The Bearer and x-api-key matches are case-insensitive. - Applied to the record message, string values, error values, byte slices and the formatted text of any other value; maps with string keys, such as
http.Header, are redacted key by key. slog.LogValuervalues are resolved before redaction, groups are walked recursively, and attributes added withWithAttrsare redacted before they reach the wrapped handler.sunscreen.InstallDefaultbuilds a fresh text handler instead of wrapping the existing default, whose output goes through thelogpackage thatslog.SetDefaultredirects back into the new handler.
Usage
A program that does not use diary installs it as the default logger:
func main() {
sunscreen.InstallDefault(os.Stderr)
// ...
}
To redact another handler, for example a JSON handler in a custom binary, wrap it:
logger := slog.New(sunscreen.Wrap(slog.NewJSONHandler(os.Stderr, nil)))
logger.Error("vendor call failed", "api_key", key, "err", err)
// {"level":"ERROR","msg":"vendor call failed","api_key":"[REDACTED]","err":"..."}
Redaction is a safety net, not a licence: log ids and outcomes, never request bodies, tokens or arguments that carry them.
API reference
| Identifier | Description |
|---|---|
sunscreen.Wrap |
Returns a slog.Handler that redacts every record and every WithAttrs attribute before the wrapped handler sees it. Wrapping a sunscreen handler again returns it unchanged. |
sunscreen.InstallDefault |
Makes a redacting text handler writing to the given writer the process default logger; the compatibility entry point for programs that do not install diary. |
sunscreen.Scrub |
Replaces the Bearer, sk- and x-api-key: credential shapes in a string. |
sunscreen.Redacted |
The replacement text, [REDACTED]. |
sunscreen.RedactedKeys |
Returns a copy of the attribute keys whose values are always redacted. |
Dependencies
- SummerCMS modules: none.
- Third-party: none.
- Standard library:
context,fmt,io,log/slog,reflect,regexp,slices,strings.
Testing
go test ./modules/sunscreen/...
The tests log through the handler into a buffer and need no external services.