Compare commits
2 Commits
fix/handle
...
0ebae4b70a
| Author | SHA1 | Date | |
|---|---|---|---|
| 0ebae4b70a | |||
| 5fc919e523 |
@@ -91,11 +91,8 @@ it.
|
||||
The record's own fields are named `Time`, `Level`, `Message` and `PC`, and
|
||||
they own those names: an attribute keyed after one of them is dropped from
|
||||
the json and webhook output. A key logged more than once keeps its last
|
||||
value there for the same reason, with one exception: when both are
|
||||
`slog.Group` values sharing a key, the two groups are merged into one
|
||||
object holding the members of both, rather than the second replacing the
|
||||
first. Neither the collision nor the merge applies to the console output,
|
||||
which is a line of text: every pair appears, in order. If you need a field
|
||||
value there for the same reason. Neither applies to the console output,
|
||||
which is a line of text: both pairs appear, in order. If you need a field
|
||||
called `message`, pick a key that does not collide - the collision is
|
||||
silent.
|
||||
|
||||
|
||||
22
attrs.go
22
attrs.go
@@ -229,27 +229,23 @@ func appendAttrText(out *strings.Builder, prefix string, attr slog.Attr) {
|
||||
}
|
||||
|
||||
out.WriteString(" ")
|
||||
// The key is quoted on the same terms as the value, and as a whole
|
||||
// including its group prefix, because that is the token a reader has to
|
||||
// find the "=" in. slog.NewTextHandler quotes "prefix+key" the same way,
|
||||
// so a key like "a=b" reads as "a=b"=v2 rather than the ambiguous
|
||||
// a=b=v2, which parses as the key "a" with the value "b=v2".
|
||||
out.WriteString(quoteIfNeeded(prefix + attr.Key))
|
||||
out.WriteString(prefix)
|
||||
out.WriteString(attr.Key)
|
||||
out.WriteString("=")
|
||||
out.WriteString(quoteIfNeeded(value.String()))
|
||||
}
|
||||
|
||||
// quoteIfNeeded quotes a key or a value only when leaving it bare would make
|
||||
// the key=value pairs ambiguous, matching how the stdlib text handler reads.
|
||||
func quoteIfNeeded(text string) string {
|
||||
if text == "" {
|
||||
// quoteIfNeeded quotes a value only when leaving it bare would make the
|
||||
// key=value pairs ambiguous, matching how the stdlib text handler reads.
|
||||
func quoteIfNeeded(value string) string {
|
||||
if value == "" {
|
||||
return `""`
|
||||
}
|
||||
for _, r := range text {
|
||||
for _, r := range value {
|
||||
if unicode.IsSpace(r) || !unicode.IsPrint(r) ||
|
||||
r == '"' || r == '=' {
|
||||
return strconv.Quote(text)
|
||||
return strconv.Quote(value)
|
||||
}
|
||||
}
|
||||
return text
|
||||
return value
|
||||
}
|
||||
|
||||
@@ -297,97 +297,6 @@ func TestConsoleHandlerQuotesValuesNeedingIt(t *testing.T) {
|
||||
wantContains(t, output, `file="The Movie.mp4"`)
|
||||
}
|
||||
|
||||
// TestConsoleHandlerQuotesKeysNeedingIt pins the key side of the same rule.
|
||||
// A key is quoted on the same terms as a value, and as one token including
|
||||
// its group prefix, so that the "=" separating the pair is always the first
|
||||
// one outside quotes. Every case here was compared against
|
||||
// slog.NewTextHandler, which renders each of them identically.
|
||||
func TestConsoleHandlerQuotesKeysNeedingIt(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
build func() slog.Handler
|
||||
attrs []slog.Attr
|
||||
want string
|
||||
notWant string
|
||||
}{
|
||||
{
|
||||
name: "an ordinary key is left bare",
|
||||
build: func() slog.Handler { return NewConsoleHandler() },
|
||||
attrs: []slog.Attr{slog.String("device", "livingroom")},
|
||||
want: " device=livingroom",
|
||||
notWant: `"device"`,
|
||||
},
|
||||
{
|
||||
name: "a key containing an equals sign is quoted",
|
||||
build: func() slog.Handler { return NewConsoleHandler() },
|
||||
attrs: []slog.Attr{slog.String("a=b", "v2")},
|
||||
want: ` "a=b"=v2`,
|
||||
notWant: " a=b=v2",
|
||||
},
|
||||
{
|
||||
name: "a key containing a space is quoted",
|
||||
build: func() slog.Handler { return NewConsoleHandler() },
|
||||
attrs: []slog.Attr{slog.String("my key", "v")},
|
||||
want: ` "my key"=v`,
|
||||
notWant: " my key=v",
|
||||
},
|
||||
{
|
||||
name: "a key containing a quote is escaped",
|
||||
build: func() slog.Handler { return NewConsoleHandler() },
|
||||
attrs: []slog.Attr{slog.String(`he"llo`, "v")},
|
||||
want: ` "he\"llo"=v`,
|
||||
notWant: ` he"llo=v`,
|
||||
},
|
||||
{
|
||||
name: "a printable non-ascii key is left bare",
|
||||
build: func() slog.Handler { return NewConsoleHandler() },
|
||||
attrs: []slog.Attr{slog.String("キー", "v")},
|
||||
want: " キー=v",
|
||||
notWant: `"キー"`,
|
||||
},
|
||||
{
|
||||
name: "a group prefix is quoted together with its key",
|
||||
build: func() slog.Handler { return NewConsoleHandler() },
|
||||
attrs: []slog.Attr{
|
||||
slog.Group("grp", slog.String("a=b", "v")),
|
||||
},
|
||||
want: ` "grp.a=b"=v`,
|
||||
notWant: " grp.a=b=v",
|
||||
},
|
||||
{
|
||||
name: "a WithGroup prefix needing quotes quotes the whole key",
|
||||
build: func() slog.Handler {
|
||||
return NewConsoleHandler().WithGroup("my grp")
|
||||
},
|
||||
attrs: []slog.Attr{slog.String("k", "v")},
|
||||
want: ` "my grp.k"=v`,
|
||||
notWant: " my grp.k=v",
|
||||
},
|
||||
{
|
||||
name: "an empty key is quoted rather than left as a gap",
|
||||
build: func() slog.Handler { return NewConsoleHandler() },
|
||||
attrs: []slog.Attr{slog.String("", "v")},
|
||||
want: ` ""=v`,
|
||||
notWant: " =v",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
output := captureStdout(t, func() {
|
||||
handler := test.build()
|
||||
record := testRecord("casting", test.attrs...)
|
||||
if err := handler.Handle(context.Background(), record); err != nil {
|
||||
t.Fatalf("Handle: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
wantContains(t, output, test.want)
|
||||
wantNotContains(t, output, test.notWant)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConsoleHandlerWithAttrsAccumulates(t *testing.T) {
|
||||
output := captureStdout(t, func() {
|
||||
handler := NewConsoleHandler().
|
||||
|
||||
Reference in New Issue
Block a user