]> Cypherpunks repositories - gostls13.git/commitdiff
log/slog: ensure ReplaceAttr does not see a group
authorJonathan Amsterdam <jba@google.com>
Mon, 25 Sep 2023 13:22:48 +0000 (09:22 -0400)
committerJonathan Amsterdam <jba@google.com>
Mon, 25 Sep 2023 20:53:33 +0000 (20:53 +0000)
The ReplaceAttr function should not see groups, only leaf attributes.

Previously, we checked an Value for being a group, then resolved it,
then called ReplaceAttr. We neglected to see if it was a group
after resolving it.

Now we resolve first, then check.

Fixes #62731.

Change-Id: I2fc40758e77c445f82deb2c9de8cae7a3b0e22cf
Reviewed-on: https://go-review.googlesource.com/c/go/+/530478
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>

src/log/slog/handler.go
src/log/slog/handler_test.go

index 16044f44c6d95565cddf9f045f6bc7411ee908e7..c9183997fa890c433b0ad07c325157c76b073b15 100644 (file)
@@ -438,16 +438,17 @@ func (s *handleState) closeGroup(name string) {
 // It handles replacement and checking for an empty key.
 // after replacement).
 func (s *handleState) appendAttr(a Attr) {
+       a.Value = a.Value.Resolve()
        if rep := s.h.opts.ReplaceAttr; rep != nil && a.Value.Kind() != KindGroup {
                var gs []string
                if s.groups != nil {
                        gs = *s.groups
                }
-               // Resolve before calling ReplaceAttr, so the user doesn't have to.
-               a.Value = a.Value.Resolve()
+               // a.Value is resolved before calling ReplaceAttr, so the user doesn't have to.
                a = rep(gs, a)
+               // The ReplaceAttr function may return an unresolved Attr.
+               a.Value = a.Value.Resolve()
        }
-       a.Value = a.Value.Resolve()
        // Elide empty Attrs.
        if a.isEmpty() {
                return
index 4ffd74a495370b43f2642e223f525be237f6dcef..ec200d4b8501f3da4cce9c63a83228bebbece926 100644 (file)
@@ -491,6 +491,18 @@ func TestJSONAndTextHandlers(t *testing.T) {
                        wantText: "g.x=0 g.n=4 g.h.b=2",
                        wantJSON: `{"g":{"x":0,"n":4,"h":{"b":2,"i":{}}}}`,
                },
+               {
+                       name: "replace resolved group",
+                       replace: func(groups []string, a Attr) Attr {
+                               if a.Value.Kind() == KindGroup {
+                                       return Attr{"bad", IntValue(1)}
+                               }
+                               return removeKeys(TimeKey, LevelKey, MessageKey)(groups, a)
+                       },
+                       attrs:    []Attr{Any("name", logValueName{"Perry", "Platypus"})},
+                       wantText: "name.first=Perry name.last=Platypus",
+                       wantJSON: `{"name":{"first":"Perry","last":"Platypus"}}`,
+               },
        } {
                r := NewRecord(testTime, LevelInfo, "message", callerPC(2))
                line := strconv.Itoa(r.source().Line)