Skip to content

Commit e39a01e

Browse files
authored
Merge pull request #110 from thockin/funcr-kv-sanitize
funcr: Move kvlist sanitization to entry-points
2 parents fefa637 + bd1384b commit e39a01e

File tree

2 files changed

+72
-31
lines changed

2 files changed

+72
-31
lines changed

funcr/funcr.go

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -229,15 +229,22 @@ func (f Formatter) render(builtins, args []interface{}) string {
229229
// flatten renders a list of key-value pairs into a buffer. If continuing is
230230
// true, it assumes that the buffer has previous values and will emit a
231231
// separator (which depends on the output format) before the first pair it
232-
// writes.
233-
func (f Formatter) flatten(buf *bytes.Buffer, kvList []interface{}, continuing bool) {
232+
// writes. This also returns a potentially modified version of kvList, which
233+
// ensures that there is a value for every key (adding a value if needed) and
234+
// that each key is a string (substituting a key if needed).
235+
func (f Formatter) flatten(buf *bytes.Buffer, kvList []interface{}, continuing bool) []interface{} {
234236
if len(kvList)%2 != 0 {
235237
kvList = append(kvList, "<no-value>")
236238
}
237239
for i := 0; i < len(kvList); i += 2 {
238240
k, ok := kvList[i].(string)
239241
if !ok {
240-
k = "<non-string-key>"
242+
snippet := f.pretty(kvList[i])
243+
if len(snippet) > 16 {
244+
snippet = snippet[:16]
245+
}
246+
k = fmt.Sprintf("<non-string-key: %s>", snippet)
247+
kvList[i] = k
241248
}
242249
v := kvList[i+1]
243250

@@ -250,6 +257,7 @@ func (f Formatter) flatten(buf *bytes.Buffer, kvList []interface{}, continuing b
250257
buf.WriteByte(' ')
251258
}
252259
}
260+
253261
buf.WriteByte('"')
254262
buf.WriteString(k)
255263
buf.WriteByte('"')
@@ -260,6 +268,7 @@ func (f Formatter) flatten(buf *bytes.Buffer, kvList []interface{}, continuing b
260268
}
261269
buf.WriteString(f.pretty(v))
262270
}
271+
return kvList
263272
}
264273

265274
func (f Formatter) pretty(value interface{}) string {
@@ -569,13 +578,11 @@ func (f *Formatter) AddName(name string) {
569578
// AddValues adds key-value pairs to the set of saved values to be logged with
570579
// each log line.
571580
func (f *Formatter) AddValues(kvList []interface{}) {
572-
// Three slice args forces a copy.
573-
n := len(f.values)
574-
f.values = append(f.values[:n:n], kvList...)
575-
576581
// Pre-render values, so we don't have to do it on each Info/Error call.
577582
buf := bytes.NewBuffer(make([]byte, 0, 1024))
578-
f.flatten(buf, f.values, false)
583+
// Three slice args forces a copy.
584+
n := len(f.values)
585+
f.values = f.flatten(buf, append(f.values[:n:n], kvList...), false)
579586
f.valuesStr = buf.String()
580587
}
581588

funcr/funcr_test.go

Lines changed: 57 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -461,51 +461,75 @@ func makeKV(args ...interface{}) []interface{} {
461461
func TestRender(t *testing.T) {
462462
testCases := []struct {
463463
name string
464-
kv []interface{}
464+
builtins []interface{}
465+
values []interface{}
466+
args []interface{}
465467
expectKV string
466468
expectJSON string
467469
}{{
468470
name: "nil",
469-
kv: nil,
470471
expectKV: "",
471472
expectJSON: "{}",
472473
}, {
473474
name: "empty",
474-
kv: []interface{}{},
475+
builtins: []interface{}{},
476+
values: []interface{}{},
477+
args: []interface{}{},
475478
expectKV: "",
476479
expectJSON: "{}",
477480
}, {
478481
name: "primitives",
479-
kv: makeKV("int", 1, "str", "ABC", "bool", true),
480-
expectKV: `"int"=1 "str"="ABC" "bool"=true`,
481-
expectJSON: `{"int":1,"str":"ABC","bool":true}`,
482+
builtins: makeKV("int1", 1, "int2", 2),
483+
values: makeKV("str1", "ABC", "str2", "DEF"),
484+
args: makeKV("bool1", true, "bool2", false),
485+
expectKV: `"int1"=1 "int2"=2 "str1"="ABC" "str2"="DEF" "bool1"=true "bool2"=false`,
486+
expectJSON: `{"int1":1,"int2":2,"str1":"ABC","str2":"DEF","bool1":true,"bool2":false}`,
482487
}, {
483488
name: "missing value",
484-
kv: makeKV("key"),
485-
expectKV: `"key"="<no-value>"`,
486-
expectJSON: `{"key":"<no-value>"}`,
489+
builtins: makeKV("builtin"),
490+
values: makeKV("value"),
491+
args: makeKV("arg"),
492+
expectKV: `"builtin"="<no-value>" "value"="<no-value>" "arg"="<no-value>"`,
493+
expectJSON: `{"builtin":"<no-value>","value":"<no-value>","arg":"<no-value>"}`,
487494
}, {
488-
name: "non-string key",
489-
kv: makeKV(123, "val"),
490-
expectKV: `"<non-string-key>"="val"`,
491-
expectJSON: `{"<non-string-key>":"val"}`,
495+
name: "non-string key int",
496+
args: makeKV(123, "val"),
497+
values: makeKV(456, "val"),
498+
builtins: makeKV(789, "val"),
499+
expectKV: `"<non-string-key: 789>"="val" "<non-string-key: 456>"="val" "<non-string-key: 123>"="val"`,
500+
expectJSON: `{"<non-string-key: 789>":"val","<non-string-key: 456>":"val","<non-string-key: 123>":"val"}`,
501+
}, {
502+
name: "non-string key struct",
503+
args: makeKV(struct {
504+
F1 string
505+
F2 int
506+
}{"arg", 123}, "val"),
507+
values: makeKV(struct {
508+
F1 string
509+
F2 int
510+
}{"value", 456}, "val"),
511+
builtins: makeKV(struct {
512+
F1 string
513+
F2 int
514+
}{"builtin", 789}, "val"),
515+
expectKV: `"<non-string-key: {"F1":"builtin",>"="val" "<non-string-key: {"F1":"value","F>"="val" "<non-string-key: {"F1":"arg","F2">"="val"`,
516+
expectJSON: `{"<non-string-key: {"F1":"builtin",>":"val","<non-string-key: {"F1":"value","F>":"val","<non-string-key: {"F1":"arg","F2">":"val"}`,
492517
}}
493518

494-
fKV := NewFormatter(Options{})
495-
fJSON := NewFormatterJSON(Options{})
496519
for _, tc := range testCases {
497520
t.Run(tc.name, func(t *testing.T) {
498-
t.Run("KV", func(t *testing.T) {
499-
r := fKV.render(tc.kv, nil)
500-
if r != tc.expectKV {
501-
t.Errorf("wrong KV output:\nexpected %q\n got %q", tc.expectKV, r)
521+
test := func(t *testing.T, formatter Formatter, expect string) {
522+
formatter.AddValues(tc.values)
523+
r := formatter.render(tc.builtins, tc.args)
524+
if r != expect {
525+
t.Errorf("wrong output:\nexpected %q\n got %q", expect, r)
502526
}
527+
}
528+
t.Run("KV", func(t *testing.T) {
529+
test(t, NewFormatter(Options{}), tc.expectKV)
503530
})
504531
t.Run("JSON", func(t *testing.T) {
505-
r := fJSON.render(tc.kv, nil)
506-
if r != tc.expectJSON {
507-
t.Errorf("wrong JSON output:\nexpected %q\n got %q", tc.expectJSON, r)
508-
}
532+
test(t, NewFormatterJSON(Options{}), tc.expectJSON)
509533
})
510534
})
511535
}
@@ -805,6 +829,11 @@ func TestInfoWithValues(t *testing.T) {
805829
values: makeKV("one", 1, "two", 2),
806830
args: makeKV("k", "v"),
807831
expect: ` "level"=0 "msg"="msg" "one"=1 "two"=2 "k"="v"`,
832+
}, {
833+
name: "dangling",
834+
values: makeKV("dangling"),
835+
args: makeKV("k", "v"),
836+
expect: ` "level"=0 "msg"="msg" "dangling"="<no-value>" "k"="v"`,
808837
}}
809838

810839
for _, tc := range testCases {
@@ -841,6 +870,11 @@ func TestErrorWithValues(t *testing.T) {
841870
values: makeKV("one", 1, "two", 2),
842871
args: makeKV("k", "v"),
843872
expect: ` "msg"="msg" "error"="err" "one"=1 "two"=2 "k"="v"`,
873+
}, {
874+
name: "dangling",
875+
values: makeKV("dangling"),
876+
args: makeKV("k", "v"),
877+
expect: ` "msg"="msg" "error"="err" "dangling"="<no-value>" "k"="v"`,
844878
}}
845879

846880
for _, tc := range testCases {

0 commit comments

Comments
 (0)