Codebase list golang-github-go-logr-logr / bd1384b
funcr: Move kvlist sanitization to entry-points This is more comprehensible and makes upcoming changes easier. Also include a value snippet in non-string keys. Tim Hockin 2 years ago
2 changed file(s) with 73 addition(s) and 32 deletion(s). Raw diff Collapse all Expand all
228228 // flatten renders a list of key-value pairs into a buffer. If continuing is
229229 // true, it assumes that the buffer has previous values and will emit a
230230 // separator (which depends on the output format) before the first pair it
231 // writes.
232 func (f Formatter) flatten(buf *bytes.Buffer, kvList []interface{}, continuing bool) {
231 // writes. This also returns a potentially modified version of kvList, which
232 // ensures that there is a value for every key (adding a value if needed) and
233 // that each key is a string (substituting a key if needed).
234 func (f Formatter) flatten(buf *bytes.Buffer, kvList []interface{}, continuing bool) []interface{} {
233235 if len(kvList)%2 != 0 {
234236 kvList = append(kvList, "<no-value>")
235237 }
236238 for i := 0; i < len(kvList); i += 2 {
237239 k, ok := kvList[i].(string)
238240 if !ok {
239 k = "<non-string-key>"
241 snippet := f.pretty(kvList[i])
242 if len(snippet) > 16 {
243 snippet = snippet[:16]
244 }
245 k = fmt.Sprintf("<non-string-key: %s>", snippet)
246 kvList[i] = k
240247 }
241248 v := kvList[i+1]
242249
249256 buf.WriteByte(' ')
250257 }
251258 }
259
252260 buf.WriteByte('"')
253261 buf.WriteString(k)
254262 buf.WriteByte('"')
259267 }
260268 buf.WriteString(f.pretty(v))
261269 }
270 return kvList
262271 }
263272
264273 func (f Formatter) pretty(value interface{}) string {
568577 // AddValues adds key-value pairs to the set of saved values to be logged with
569578 // each log line.
570579 func (f *Formatter) AddValues(kvList []interface{}) {
580 // Pre-render values, so we don't have to do it on each Info/Error call.
581 buf := bytes.NewBuffer(make([]byte, 0, 1024))
571582 // Three slice args forces a copy.
572583 n := len(f.values)
573 f.values = append(f.values[:n:n], kvList...)
574
575 // Pre-render values, so we don't have to do it on each Info/Error call.
576 buf := bytes.NewBuffer(make([]byte, 0, 1024))
577 f.flatten(buf, f.values, false)
584 f.values = f.flatten(buf, append(f.values[:n:n], kvList...), false)
578585 f.valuesStr = buf.String()
579586 }
580587
460460 func TestRender(t *testing.T) {
461461 testCases := []struct {
462462 name string
463 kv []interface{}
463 builtins []interface{}
464 values []interface{}
465 args []interface{}
464466 expectKV string
465467 expectJSON string
466468 }{{
467469 name: "nil",
468 kv: nil,
469470 expectKV: "",
470471 expectJSON: "{}",
471472 }, {
472473 name: "empty",
473 kv: []interface{}{},
474 builtins: []interface{}{},
475 values: []interface{}{},
476 args: []interface{}{},
474477 expectKV: "",
475478 expectJSON: "{}",
476479 }, {
477480 name: "primitives",
478 kv: makeKV("int", 1, "str", "ABC", "bool", true),
479 expectKV: `"int"=1 "str"="ABC" "bool"=true`,
480 expectJSON: `{"int":1,"str":"ABC","bool":true}`,
481 builtins: makeKV("int1", 1, "int2", 2),
482 values: makeKV("str1", "ABC", "str2", "DEF"),
483 args: makeKV("bool1", true, "bool2", false),
484 expectKV: `"int1"=1 "int2"=2 "str1"="ABC" "str2"="DEF" "bool1"=true "bool2"=false`,
485 expectJSON: `{"int1":1,"int2":2,"str1":"ABC","str2":"DEF","bool1":true,"bool2":false}`,
481486 }, {
482487 name: "missing value",
483 kv: makeKV("key"),
484 expectKV: `"key"="<no-value>"`,
485 expectJSON: `{"key":"<no-value>"}`,
486 }, {
487 name: "non-string key",
488 kv: makeKV(123, "val"),
489 expectKV: `"<non-string-key>"="val"`,
490 expectJSON: `{"<non-string-key>":"val"}`,
488 builtins: makeKV("builtin"),
489 values: makeKV("value"),
490 args: makeKV("arg"),
491 expectKV: `"builtin"="<no-value>" "value"="<no-value>" "arg"="<no-value>"`,
492 expectJSON: `{"builtin":"<no-value>","value":"<no-value>","arg":"<no-value>"}`,
493 }, {
494 name: "non-string key int",
495 args: makeKV(123, "val"),
496 values: makeKV(456, "val"),
497 builtins: makeKV(789, "val"),
498 expectKV: `"<non-string-key: 789>"="val" "<non-string-key: 456>"="val" "<non-string-key: 123>"="val"`,
499 expectJSON: `{"<non-string-key: 789>":"val","<non-string-key: 456>":"val","<non-string-key: 123>":"val"}`,
500 }, {
501 name: "non-string key struct",
502 args: makeKV(struct {
503 F1 string
504 F2 int
505 }{"arg", 123}, "val"),
506 values: makeKV(struct {
507 F1 string
508 F2 int
509 }{"value", 456}, "val"),
510 builtins: makeKV(struct {
511 F1 string
512 F2 int
513 }{"builtin", 789}, "val"),
514 expectKV: `"<non-string-key: {"F1":"builtin",>"="val" "<non-string-key: {"F1":"value","F>"="val" "<non-string-key: {"F1":"arg","F2">"="val"`,
515 expectJSON: `{"<non-string-key: {"F1":"builtin",>":"val","<non-string-key: {"F1":"value","F>":"val","<non-string-key: {"F1":"arg","F2">":"val"}`,
491516 }}
492517
493 fKV := NewFormatter(Options{})
494 fJSON := NewFormatterJSON(Options{})
495518 for _, tc := range testCases {
496519 t.Run(tc.name, func(t *testing.T) {
520 test := func(t *testing.T, formatter Formatter, expect string) {
521 formatter.AddValues(tc.values)
522 r := formatter.render(tc.builtins, tc.args)
523 if r != expect {
524 t.Errorf("wrong output:\nexpected %q\n got %q", expect, r)
525 }
526 }
497527 t.Run("KV", func(t *testing.T) {
498 r := fKV.render(tc.kv, nil)
499 if r != tc.expectKV {
500 t.Errorf("wrong KV output:\nexpected %q\n got %q", tc.expectKV, r)
501 }
528 test(t, NewFormatter(Options{}), tc.expectKV)
502529 })
503530 t.Run("JSON", func(t *testing.T) {
504 r := fJSON.render(tc.kv, nil)
505 if r != tc.expectJSON {
506 t.Errorf("wrong JSON output:\nexpected %q\n got %q", tc.expectJSON, r)
507 }
531 test(t, NewFormatterJSON(Options{}), tc.expectJSON)
508532 })
509533 })
510534 }
804828 values: makeKV("one", 1, "two", 2),
805829 args: makeKV("k", "v"),
806830 expect: ` "level"=0 "msg"="msg" "one"=1 "two"=2 "k"="v"`,
831 }, {
832 name: "dangling",
833 values: makeKV("dangling"),
834 args: makeKV("k", "v"),
835 expect: ` "level"=0 "msg"="msg" "dangling"="<no-value>" "k"="v"`,
807836 }}
808837
809838 for _, tc := range testCases {
840869 values: makeKV("one", 1, "two", 2),
841870 args: makeKV("k", "v"),
842871 expect: ` "msg"="msg" "error"="err" "one"=1 "two"=2 "k"="v"`,
872 }, {
873 name: "dangling",
874 values: makeKV("dangling"),
875 args: makeKV("k", "v"),
876 expect: ` "msg"="msg" "error"="err" "dangling"="<no-value>" "k"="v"`,
843877 }}
844878
845879 for _, tc := range testCases {