github.com/hellobchain/third_party@v0.0.0-20230331131523-deb0478a2e52/go-kit/kit/log/json_logger.go (about) 1 package log 2 3 import ( 4 "encoding" 5 "encoding/json" 6 "fmt" 7 "io" 8 "reflect" 9 ) 10 11 type jsonLogger struct { 12 io.Writer 13 } 14 15 // NewJSONLogger returns a Logger that encodes keyvals to the Writer as a 16 // single JSON object. Each log event produces no more than one call to 17 // w.Write. The passed Writer must be safe for concurrent use by multiple 18 // goroutines if the returned Logger will be used concurrently. 19 func NewJSONLogger(w io.Writer) Logger { 20 return &jsonLogger{w} 21 } 22 23 func (l *jsonLogger) Log(keyvals ...interface{}) error { 24 n := (len(keyvals) + 1) / 2 // +1 to handle case when len is odd 25 m := make(map[string]interface{}, n) 26 for i := 0; i < len(keyvals); i += 2 { 27 k := keyvals[i] 28 var v interface{} = ErrMissingValue 29 if i+1 < len(keyvals) { 30 v = keyvals[i+1] 31 } 32 merge(m, k, v) 33 } 34 return json.NewEncoder(l.Writer).Encode(m) 35 } 36 37 func merge(dst map[string]interface{}, k, v interface{}) { 38 var key string 39 switch x := k.(type) { 40 case string: 41 key = x 42 case fmt.Stringer: 43 key = safeString(x) 44 default: 45 key = fmt.Sprint(x) 46 } 47 48 // We want json.Marshaler and encoding.TextMarshaller to take priority over 49 // err.Error() and v.String(). But json.Marshall (called later) does that by 50 // default so we force a no-op if it's one of those 2 case. 51 switch x := v.(type) { 52 case json.Marshaler: 53 case encoding.TextMarshaler: 54 case error: 55 v = safeError(x) 56 case fmt.Stringer: 57 v = safeString(x) 58 } 59 60 dst[key] = v 61 } 62 63 func safeString(str fmt.Stringer) (s string) { 64 defer func() { 65 if panicVal := recover(); panicVal != nil { 66 if v := reflect.ValueOf(str); v.Kind() == reflect.Ptr && v.IsNil() { 67 s = "NULL" 68 } else { 69 panic(panicVal) 70 } 71 } 72 }() 73 s = str.String() 74 return 75 } 76 77 func safeError(err error) (s interface{}) { 78 defer func() { 79 if panicVal := recover(); panicVal != nil { 80 if v := reflect.ValueOf(err); v.Kind() == reflect.Ptr && v.IsNil() { 81 s = nil 82 } else { 83 panic(panicVal) 84 } 85 } 86 }() 87 s = err.Error() 88 return 89 }