- Go 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
The emphasis hint leaked into jsonl as a stray "hslog.emphasis":N
field:
{"time":"...","level":"INFO","msg":"msg","uid":42,"hslog.emphasis":2}
emphasis.go claimed this could not happen because "the key is
unexported [so JSONHandler] cannot even see it as ordinary structured
data". That reasoning was wrong: emphasisKey is unexported as a Go
identifier, but its *value* is an ordinary attr key string, and
slog.JSONHandler serialises attrs by key string. Unexported-ness only
stops callers constructing the attr by hand; it hides nothing from a
handler.
NewHandler now installs dropEmphasis as a ReplaceAttr on both jsonl
paths (plain and colourised). It chains to any caller-supplied
ReplaceAttr rather than replacing it, and removes the hint before that
callback runs, so user code never sees an internal attr either.
Only the text handler (plain/journal/syslog) renders emphasis, and it
already skipped the key when printing attrs, so text output is
unchanged.
Tests fail with the fix reverted (verified), covering: both jsonl
variants stay free of the hint and remain valid JSON with unrelated
attrs intact; chaining preserves a user ReplaceAttr's passthrough,
drop and rename behaviour; and the text handler still applies
emphasis.
Corrects the doc comments that asserted the impossible-by-construction
claim.
(co)authored by ai:claude-sonnet-5
|
||
| assets | ||
| AGENTS.md | ||
| color.go | ||
| color_jsonl.go | ||
| color_test.go | ||
| detect.go | ||
| detect_other.go | ||
| detect_test.go | ||
| detect_unix.go | ||
| doc.go | ||
| emphasis.go | ||
| emphasis_test.go | ||
| example_test.go | ||
| format.go | ||
| format_test.go | ||
| go.mod | ||
| go.sum | ||
| hslog.go | ||
| hslog_test.go | ||
| LICENSE | ||
| README.md | ||
| slogtest_test.go | ||
| syslog.go | ||
| text.go | ||
hslog
A thin, non-hiding wrapper around Go's log/slog.
hslog.New hands you a plain *slog.Logger; hslog.NewHandler hands you a
plain slog.Handler. Everything you can do with slog still works — this
package only adds a bit of convenient pre-configuration you'd otherwise
copy between projects.
See the documentation.
What it adds
-
A
--log-formatflag value (hslog.Format, implementsflag.Value) accepting:value meaning autodetect at runtime (see below) [color-]jsonlslog-native JSONL (colour variant tints level/msg) [color-]journal<severity>prefixed, systemd-journal friendly[color-]syslog<priority>prefixed, RFC 5424 style[color-]plainhuman-readable logfmt-ish -
Auto detection for
auto:- journal if started by systemd and our output is the journal
(
JOURNAL_STREAMmatches the writer's device/inode); - syslog if the writer is a socket (e.g. inherited
/dev/log); - plain otherwise — colourised when the writer is a colour-capable
terminal (
NO_COLORandTERM=dumbare honoured).
- journal if started by systemd and our output is the journal
(
-
Default level colours, dimmed for
DEBUGup to bold red forERRORand any application level above it (e.g. aCRITatslog.LevelError+4).
Nothing is hidden
hslog.Options embeds slog.HandlerOptions, so Level, AddSource and
ReplaceAttr behave exactly as in the standard library. Plain jsonl
output is produced by the stdlib slog.JSONHandler; color-jsonl only
injects ANSI around the level and msg values (strip the escapes and it
parses again — but prefer plain jsonl for machines).
Usage
package main
import (
"flag"
"log/slog"
"os"
"go.schlittermann.de/heiko/hslog"
)
func main() {
format := hslog.AutoFormat()
flag.Var(&format, "log-format", hslog.FlagUsage)
flag.Parse()
log := hslog.New(os.Stderr, format, &hslog.Options{
HandlerOptions: slog.HandlerOptions{Level: slog.LevelDebug},
})
slog.SetDefault(log)
slog.Info("service started", "port", 8080)
}
Custom levels and colours:
const LevelCrit = slog.LevelError + 4
pal := hslog.DefaultPalette() // tweak pal.LevelColor / pal.Key / pal.Message
log := hslog.New(os.Stderr, hslog.FormatColorPlain, &hslog.Options{
Palette: &pal,
})
log.Log(context.Background(), LevelCrit, "meltdown", "reactor", 4)
License
Apache-2.0. See LICENSE.