Compare commits
3
Commits
08ed36cbf7
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f0cd5dd5f | ||
|
|
86436449c5 | ||
|
|
a5fdadba76 |
@@ -27,7 +27,8 @@ files it depends on: .golangci.yml, REPO_POLICIES.md, .editorconfig,
|
|||||||
* 2026-08-10: fixed every handler discarding slog attributes: console,
|
* 2026-08-10: fixed every handler discarding slog attributes: console,
|
||||||
JSON and webhook handlers now emit record attributes, accumulate
|
JSON and webhook handlers now emit record attributes, accumulate
|
||||||
WithAttrs without mutating the receiver, and honour WithGroup;
|
WithAttrs without mutating the receiver, and honour WithGroup;
|
||||||
slog.Group values nest and LogValuer values are resolved
|
slog.Group values nest and LogValuer values are resolved; console
|
||||||
|
keys and values holding invalid UTF-8 are quoted
|
||||||
* 2026-08-07: added canonical `.golangci.yml` (v2 schema), pinned the
|
* 2026-08-07: added canonical `.golangci.yml` (v2 schema), pinned the
|
||||||
`Dockerfile` lint stage to golangci-lint v2.12.2 (tag+digest), and
|
`Dockerfile` lint stage to golangci-lint v2.12.2 (tag+digest), and
|
||||||
fixed all findings the v1→v2 jump surfaced without changing any
|
fixed all findings the v1→v2 jump surfaced without changing any
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
// handlerAttrs is the attribute state every handler carries: the attributes
|
// handlerAttrs is the attribute state every handler carries: the attributes
|
||||||
@@ -262,16 +263,20 @@ func appendAttrText(out *strings.Builder, prefix string, attr slog.Attr) {
|
|||||||
out.WriteString(quoteIfNeeded(value.String()))
|
out.WriteString(quoteIfNeeded(value.String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// quoteIfNeeded quotes a key or a value only when leaving it bare would make
|
// quoteIfNeeded quotes a key or a value, as slog.NewTextHandler does, when
|
||||||
// the key=value pairs ambiguous, matching how the stdlib text handler reads.
|
// leaving it bare would make the key=value pairs ambiguous or would write a
|
||||||
|
// control character or invalid UTF-8 to the terminal. Unlike the stdlib, it
|
||||||
|
// also quotes DEL (0x7f), which is a control character too.
|
||||||
func quoteIfNeeded(text string) string {
|
func quoteIfNeeded(text string) string {
|
||||||
if text == "" {
|
if text == "" {
|
||||||
return `""`
|
return `""`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ranging over a string yields utf8.RuneError for each invalid byte;
|
||||||
|
// strconv.Quote then escapes that byte, as in "bad\xffkey".
|
||||||
for _, r := range text {
|
for _, r := range text {
|
||||||
if unicode.IsSpace(r) || !unicode.IsPrint(r) ||
|
if unicode.IsSpace(r) || !unicode.IsPrint(r) ||
|
||||||
r == '"' || r == '=' {
|
r == utf8.RuneError || r == '"' || r == '=' {
|
||||||
return strconv.Quote(text)
|
return strconv.Quote(text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -400,6 +400,27 @@ func TestConsoleHandlerQuotesKeysNeedingIt(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestConsoleHandlerQuotesInvalidUTF8 checks that a key or a value holding
|
||||||
|
// invalid UTF-8 is quoted with the bad byte escaped, as slog.NewTextHandler
|
||||||
|
// does, so the raw byte never reaches the terminal. Valid non-ASCII stays bare.
|
||||||
|
func TestConsoleHandlerQuotesInvalidUTF8(t *testing.T) {
|
||||||
|
output := captureStdout(t, func() {
|
||||||
|
handler := simplelog.NewConsoleHandler()
|
||||||
|
record := testRecord(
|
||||||
|
"casting",
|
||||||
|
slog.String("bad\xffkey", "v"),
|
||||||
|
slog.String("raw", "bad\xffvalue"),
|
||||||
|
slog.String("title", "Amélie"),
|
||||||
|
)
|
||||||
|
handle(t, handler, record)
|
||||||
|
})
|
||||||
|
|
||||||
|
wantContains(t, output, ` "bad\xffkey"=v`)
|
||||||
|
wantContains(t, output, ` raw="bad\xffvalue"`)
|
||||||
|
wantContains(t, output, " title=Amélie")
|
||||||
|
wantNotContains(t, output, "\xff")
|
||||||
|
}
|
||||||
|
|
||||||
func TestConsoleHandlerWithAttrsAccumulates(t *testing.T) {
|
func TestConsoleHandlerWithAttrsAccumulates(t *testing.T) {
|
||||||
output := captureStdout(t, func() {
|
output := captureStdout(t, func() {
|
||||||
handler := simplelog.NewConsoleHandler().
|
handler := simplelog.NewConsoleHandler().
|
||||||
|
|||||||
Reference in New Issue
Block a user