Codebase list golang-github-go-kit-kit / c7049fa
Merge pull request #92 from go-kit/no-log-panics No log panics. Chris Hines 8 years ago
4 changed file(s) with 146 addition(s) and 39 deletion(s). Raw diff Collapse all Expand all
33 "encoding/json"
44 "fmt"
55 "io"
6 "reflect"
67 )
78
89 type jsonLogger struct {
1617 }
1718
1819 func (l *jsonLogger) Log(keyvals ...interface{}) error {
19 if len(keyvals)%2 == 1 {
20 panic("odd number of keyvals")
21 }
22 m := make(map[string]interface{}, len(keyvals)/2)
20 n := (len(keyvals) + 1) / 2 // +1 to handle case when len is odd
21 m := make(map[string]interface{}, n)
2322 for i := 0; i < len(keyvals); i += 2 {
24 merge(m, keyvals[i], keyvals[i+1])
23 k := keyvals[i]
24 var v interface{} = ErrMissingValue
25 if i+1 < len(keyvals) {
26 v = keyvals[i+1]
27 }
28 merge(m, k, v)
2529 }
2630 return json.NewEncoder(l.Writer).Encode(m)
2731 }
2832
29 func merge(dst map[string]interface{}, k, v interface{}) map[string]interface{} {
33 func merge(dst map[string]interface{}, k, v interface{}) {
3034 var key string
3135 switch x := k.(type) {
3236 case string:
3337 key = x
3438 case fmt.Stringer:
35 key = x.String()
39 key = safeString(x)
3640 default:
37 key = fmt.Sprintf("%v", x)
41 key = fmt.Sprint(x)
3842 }
3943 if x, ok := v.(error); ok {
40 v = x.Error()
44 v = safeError(x)
4145 }
4246 dst[key] = v
43 return dst
4447 }
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 }
99 )
1010
1111 func TestJSONLogger(t *testing.T) {
12 t.Parallel()
1213 buf := &bytes.Buffer{}
1314 logger := log.NewJSONLogger(buf)
1415 if err := logger.Log("err", errors.New("err"), "m", map[string]int{"0": 0}, "a", []int{1, 2, 3}); err != nil {
1516 t.Fatal(err)
1617 }
1718 if want, have := `{"a":[1,2,3],"err":"err","m":{"0":0}}`+"\n", buf.String(); want != have {
18 t.Errorf("want %#v, have %#v", want, have)
19 t.Errorf("\nwant %#v\nhave %#v", want, have)
1920 }
21 }
22
23 func TestJSONLoggerMissingValue(t *testing.T) {
24 t.Parallel()
25 buf := &bytes.Buffer{}
26 logger := log.NewJSONLogger(buf)
27 if err := logger.Log("k"); err != nil {
28 t.Fatal(err)
29 }
30 if want, have := `{"k":"(MISSING)"}`+"\n", buf.String(); want != have {
31 t.Errorf("\nwant %#v\nhave %#v", want, have)
32 }
33 }
34
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("\nwant %#v\nhave %#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("\nwant %#v\nhave %#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)
2071 }
2172
2273 func BenchmarkJSONLoggerSimple(b *testing.B) {
33 // key/value data.
44 package log
55
6 import "sync/atomic"
6 import (
7 "errors"
8 "sync/atomic"
9 )
710
811 // Logger is the fundamental interface for all log operations. Log creates a
912 // log event from keyvals, a variadic sequence of alternating keys and values.
1316 type Logger interface {
1417 Log(keyvals ...interface{}) error
1518 }
19
20 // ErrMissingValue is appended to keyvals slices with odd length to substitute
21 // the missing value.
22 var ErrMissingValue = errors.New("(MISSING)")
1623
1724 // NewContext returns a new Context that logs to logger.
1825 func NewContext(logger Logger) Context {
3643 // stored context with their generated value, appends keyvals, and passes the
3744 // result to the wrapped Logger.
3845 func (l Context) Log(keyvals ...interface{}) error {
39 if len(keyvals)%2 != 0 {
40 panic("bad keyvals")
46 kvs := append(l.keyvals, keyvals...)
47 if len(kvs)%2 != 0 {
48 kvs = append(kvs, ErrMissingValue)
4149 }
42 kvs := append(l.keyvals, keyvals...)
4350 if l.hasValuer {
4451 // If no keyvals were appended above then we must copy l.keyvals so
4552 // that future log events will reevaluate the stored Valuers.
5663 if len(keyvals) == 0 {
5764 return l
5865 }
59 if len(keyvals)%2 != 0 {
60 panic("bad keyvals")
66 kvs := append(l.keyvals, keyvals...)
67 if len(kvs)%2 != 0 {
68 kvs = append(kvs, ErrMissingValue)
6169 }
62 // Limiting the capacity of the stored keyvals ensures that a new
63 // backing array is created if the slice must grow in Log or With.
64 // Using the extra capacity without copying risks a data race that
65 // would violate the Logger interface contract.
66 n := len(l.keyvals) + len(keyvals)
6770 return Context{
68 logger: l.logger,
69 keyvals: append(l.keyvals, keyvals...)[:n:n],
71 logger: l.logger,
72 // Limiting the capacity of the stored keyvals ensures that a new
73 // backing array is created if the slice must grow in Log or With.
74 // Using the extra capacity without copying risks a data race that
75 // would violate the Logger interface contract.
76 keyvals: kvs[:len(kvs):len(kvs)],
7077 hasValuer: l.hasValuer || containsValuer(keyvals),
7178 }
7279 }
7784 if len(keyvals) == 0 {
7885 return l
7986 }
80 if len(keyvals)%2 != 0 {
81 panic("bad keyvals")
82 }
8387 // Limiting the capacity of the stored keyvals ensures that a new
8488 // backing array is created if the slice must grow in Log or With.
8589 // Using the extra capacity without copying risks a data race that
8690 // would violate the Logger interface contract.
8791 n := len(l.keyvals) + len(keyvals)
92 if len(keyvals)%2 != 0 {
93 n++
94 }
8895 kvs := make([]interface{}, 0, n)
8996 kvs = append(kvs, keyvals...)
97 if len(kvs)%2 != 0 {
98 kvs = append(kvs, ErrMissingValue)
99 }
90100 kvs = append(kvs, l.keyvals...)
91101 return Context{
92102 logger: l.logger,
1010 var discard = log.Logger(log.LoggerFunc(func(...interface{}) error { return nil }))
1111
1212 func TestContext(t *testing.T) {
13 t.Parallel()
1314 buf := &bytes.Buffer{}
1415 logger := log.NewLogfmtLogger(buf)
1516
3536 }
3637 }
3738
38 func TestContextWithPrefix(t *testing.T) {
39 buf := &bytes.Buffer{}
40 kvs := []interface{}{"a", 123}
41 logger := log.NewJSONLogger(buf)
42 lc := log.NewContext(logger).With(kvs...)
43 kvs[1] = 0 // WithPrefix should copy its key values
44 lc = lc.With("b", "c") // WithPrefix should stack
45 if err := lc.Log("msg", "message"); err != nil {
46 t.Fatal(err)
47 }
48 if want, have := `{"a":123,"b":"c","msg":"message"}`+"\n", buf.String(); want != have {
49 t.Errorf("\nwant: %s\nhave: %s", want, have)
39 func TestContextMissingValue(t *testing.T) {
40 t.Parallel()
41 var output []interface{}
42 logger := log.Logger(log.LoggerFunc(func(keyvals ...interface{}) error {
43 output = keyvals
44 return nil
45 }))
46
47 lc := log.NewContext(logger)
48
49 lc.Log("k")
50 if want, have := 2, len(output); want != have {
51 t.Errorf("want len(output) == %v, have %v", want, have)
52 }
53 if want, have := log.ErrMissingValue, output[1]; want != have {
54 t.Errorf("want %#v, have %#v", want, have)
55 }
56
57 lc.With("k1").WithPrefix("k0").Log("k2")
58 if want, have := 6, len(output); want != have {
59 t.Errorf("want len(output) == %v, have %v", want, have)
60 }
61 for i := 1; i < 6; i += 2 {
62 if want, have := log.ErrMissingValue, output[i]; want != have {
63 t.Errorf("want output[%d] == %#v, have %#v", i, want, have)
64 }
5065 }
5166 }
5267