/
json.go
97 lines (87 loc) · 2.1 KB
/
json.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package log
import (
"encoding"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"reflect"
"github.com/signalfx/golib/v3/errors"
)
// JSONLogger logs out JSON objects to a writer
type JSONLogger struct {
Out io.Writer
MissingValueKey Key
}
var _ ErrorLogger = &JSONLogger{}
// NewJSONLogger creates a new JSON logger
func NewJSONLogger(w io.Writer, errHandler ErrorHandler) Logger {
if w == ioutil.Discard {
return Discard
}
return &ErrorLogLogger{
RootLogger: &JSONLogger{
Out: w,
MissingValueKey: Msg,
},
ErrHandler: errHandler,
}
}
// Log will format a JSON map and write it to Out
func (j *JSONLogger) Log(keyvals ...interface{}) error {
m := mapFromKeyvals(j.MissingValueKey, keyvals...)
return errors.Annotate(json.NewEncoder(j.Out).Encode(m), "cannot JSON encode log")
}
func mapFromKeyvals(missingValueKey Key, keyvals ...interface{}) map[string]interface{} {
n := (len(keyvals) + 1) / 2 // +1 to handle case when len is odd
m := make(map[string]interface{}, n)
for i := 0; i < len(keyvals); i += 2 {
var k, v interface{}
if i == len(keyvals)-1 {
k, v = missingValueKey, keyvals[i]
} else {
k, v = keyvals[i], keyvals[i+1]
}
m[mapKey(k)] = mapValue(v)
}
return m
}
// Different from go-kit. People just shouldn't pass nil values and should know if they do
func mapKey(k interface{}) (s string) {
defer func() {
if panicVal := recover(); panicVal != nil {
s = nilCheck(k, panicVal, "NULL").(string)
}
}()
switch x := k.(type) {
case string:
return x
case fmt.Stringer:
return x.String()
default:
return fmt.Sprint(x)
}
}
func nilCheck(ptr, panicVal interface{}, onError interface{}) interface{} {
if vl := reflect.ValueOf(ptr); vl.Kind() == reflect.Ptr && vl.IsNil() {
return onError
}
panic(panicVal)
}
func mapValue(v interface{}) (s interface{}) {
defer func() {
if panicVal := recover(); panicVal != nil {
s = nilCheck(v, panicVal, nil)
}
}()
// See newTypeEncoder
switch x := v.(type) {
case json.Marshaler:
case encoding.TextMarshaler:
case error:
return x.Error()
case fmt.Stringer:
return x.String()
}
return v
}