json_logger.go 1.9 KB

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