mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-11 21:27:31 +00:00
types/logger: add AsJSON
Printing out JSON representation things in log output is pretty common. Updates #cleanup Change-Id: Ife2d2e321a18e6e1185efa8b699a23061ac5e5a4 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:

committed by
Brad Fitzpatrick

parent
a79b1d23b8
commit
590c693b96
@@ -353,3 +353,23 @@ func LogfCloser(logf Logf) (newLogf Logf, close func()) {
|
||||
}
|
||||
return newLogf, close
|
||||
}
|
||||
|
||||
// AsJSON returns a formatter that formats v as JSON. The value is suitable to
|
||||
// passing to a regular %v printf argument. (%s is not required)
|
||||
//
|
||||
// If json.Marshal returns an error, the output is "%%!JSON-ERROR:" followed by
|
||||
// the error string.
|
||||
func AsJSON(v any) fmt.Formatter {
|
||||
return asJSONResult{v}
|
||||
}
|
||||
|
||||
type asJSONResult struct{ v any }
|
||||
|
||||
func (a asJSONResult) Format(s fmt.State, verb rune) {
|
||||
v, err := json.Marshal(a.v)
|
||||
if err != nil {
|
||||
fmt.Fprintf(s, "%%!JSON-ERROR:%v", err)
|
||||
return
|
||||
}
|
||||
s.Write(v)
|
||||
}
|
||||
|
@@ -221,3 +221,33 @@ func TestJSON(t *testing.T) {
|
||||
t.Errorf("mismatch\n got: %q\nwant: %q\n", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAsJSON(t *testing.T) {
|
||||
got := fmt.Sprintf("got %v", AsJSON(struct {
|
||||
Foo string
|
||||
Bar int
|
||||
}{"hi", 123}))
|
||||
const want = `got {"Foo":"hi","Bar":123}`
|
||||
if got != want {
|
||||
t.Errorf("got %#q; want %#q", got, want)
|
||||
}
|
||||
|
||||
got = fmt.Sprintf("got %v", AsJSON(func() {}))
|
||||
const wantErr = `got %!JSON-ERROR:json: unsupported type: func()`
|
||||
if got != wantErr {
|
||||
t.Errorf("for marshal error, got %#q; want %#q", got, wantErr)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
n := int(testing.AllocsPerRun(1000, func() {
|
||||
buf.Reset()
|
||||
fmt.Fprintf(&buf, "got %v", AsJSON("hi"))
|
||||
}))
|
||||
if n > 2 {
|
||||
// the JSON AsMarshal itself + boxing
|
||||
// the asJSONResult into an interface (which needs
|
||||
// to happen at some point to get to fmt, so might
|
||||
// as well return an interface from AsJSON))
|
||||
t.Errorf("allocs = %v; want max 2", n)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user