Codebase list golang-github-go-kit-kit / a875889
Recover from panics caused by dereferencing a nil pointer to a value receiver implementation of the fmt.Stringer or error interfaces. Chris Hines 8 years ago
2 changed file(s) with 71 addition(s) and 5 deletion(s). Raw diff Collapse all Expand all
33 "encoding/json"
44 "fmt"
55 "io"
6 "reflect"
67 )
78
89 type jsonLogger struct {
2930 return json.NewEncoder(l.Writer).Encode(m)
3031 }
3132
32 func merge(dst map[string]interface{}, k, v interface{}) map[string]interface{} {
33 func merge(dst map[string]interface{}, k, v interface{}) {
3334 var key string
3435 switch x := k.(type) {
3536 case string:
3637 key = x
3738 case fmt.Stringer:
38 key = x.String()
39 key = safeString(x)
3940 default:
40 key = fmt.Sprintf("%v", x)
41 key = fmt.Sprint(x)
4142 }
4243 if x, ok := v.(error); ok {
43 v = x.Error()
44 v = safeError(x)
4445 }
4546 dst[key] = v
46 return dst
4747 }
48
49 func safeString(str fmt.Stringer) (s string) {
50 defer func() {
51 if panicVal := recover(); panicVal != nil {
52 if v := reflect.ValueOf(str); v.Kind() == reflect.Ptr && v.IsNil() {
53 s = "NULL"
54 } else {
55 panic(panicVal)
56 }
57 }
58 }()
59 s = str.String()
60 return
61 }
62
63 func safeError(err error) (s interface{}) {
64 defer func() {
65 if panicVal := recover(); panicVal != nil {
66 if v := reflect.ValueOf(err); v.Kind() == reflect.Ptr && v.IsNil() {
67 s = nil
68 } else {
69 panic(panicVal)
70 }
71 }
72 }()
73 s = err.Error()
74 return
75 }
3232 }
3333 }
3434
35 func TestJSONLoggerNilStringerKey(t *testing.T) {
36 t.Parallel()
37
38 buf := &bytes.Buffer{}
39 logger := log.NewJSONLogger(buf)
40 if err := logger.Log((*stringer)(nil), "v"); err != nil {
41 t.Fatal(err)
42 }
43 if want, have := `{"NULL":"v"}`+"\n", buf.String(); want != have {
44 t.Errorf("want %#v, have %#v", want, have)
45 }
46 }
47
48 func TestJSONLoggerNilErrorValue(t *testing.T) {
49 t.Parallel()
50
51 buf := &bytes.Buffer{}
52 logger := log.NewJSONLogger(buf)
53 if err := logger.Log("err", (*stringError)(nil)); err != nil {
54 t.Fatal(err)
55 }
56 if want, have := `{"err":null}`+"\n", buf.String(); want != have {
57 t.Errorf("want %#v, have %#v", want, have)
58 }
59 }
60
61 type stringer string
62
63 func (s stringer) String() string {
64 return string(s)
65 }
66
67 type stringError string
68
69 func (s stringError) Error() string {
70 return string(s)
71 }
72
3573 func BenchmarkJSONLoggerSimple(b *testing.B) {
3674 benchmarkRunner(b, log.NewJSONLogger(ioutil.Discard), baseMessage)
3775 }