You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

127 lines
3.2 KiB

  1. package log
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "sync"
  7. "time"
  8. kitlog "github.com/go-kit/kit/log"
  9. kitlevel "github.com/go-kit/kit/log/level"
  10. "github.com/go-logfmt/logfmt"
  11. )
  12. type tmfmtEncoder struct {
  13. *logfmt.Encoder
  14. buf bytes.Buffer
  15. }
  16. func (l *tmfmtEncoder) Reset() {
  17. l.Encoder.Reset()
  18. l.buf.Reset()
  19. }
  20. var tmfmtEncoderPool = sync.Pool{
  21. New: func() interface{} {
  22. var enc tmfmtEncoder
  23. enc.Encoder = logfmt.NewEncoder(&enc.buf)
  24. return &enc
  25. },
  26. }
  27. type tmfmtLogger struct {
  28. w io.Writer
  29. }
  30. // NewTMFmtLogger returns a logger that encodes keyvals to the Writer in
  31. // Tendermint custom format. Note complex types (structs, maps, slices)
  32. // formatted as "%+v".
  33. //
  34. // Each log event produces no more than one call to w.Write.
  35. // The passed Writer must be safe for concurrent use by multiple goroutines if
  36. // the returned Logger will be used concurrently.
  37. func NewTMFmtLogger(w io.Writer) kitlog.Logger {
  38. return &tmfmtLogger{w}
  39. }
  40. func (l tmfmtLogger) Log(keyvals ...interface{}) error {
  41. enc := tmfmtEncoderPool.Get().(*tmfmtEncoder)
  42. enc.Reset()
  43. defer tmfmtEncoderPool.Put(enc)
  44. const unknown = "unknown"
  45. lvl := "none"
  46. msg := unknown
  47. module := unknown
  48. // indexes of keys to skip while encoding later
  49. excludeIndexes := make([]int, 0)
  50. for i := 0; i < len(keyvals)-1; i += 2 {
  51. // Extract level
  52. if keyvals[i] == kitlevel.Key() {
  53. excludeIndexes = append(excludeIndexes, i)
  54. switch keyvals[i+1].(type) {
  55. case string:
  56. lvl = keyvals[i+1].(string)
  57. case kitlevel.Value:
  58. lvl = keyvals[i+1].(kitlevel.Value).String()
  59. default:
  60. panic(fmt.Sprintf("level value of unknown type %T", keyvals[i+1]))
  61. }
  62. // and message
  63. } else if keyvals[i] == msgKey {
  64. excludeIndexes = append(excludeIndexes, i)
  65. msg = keyvals[i+1].(string)
  66. // and module (could be multiple keyvals; if such case last keyvalue wins)
  67. } else if keyvals[i] == moduleKey {
  68. excludeIndexes = append(excludeIndexes, i)
  69. module = keyvals[i+1].(string)
  70. }
  71. }
  72. // Form a custom Tendermint line
  73. //
  74. // Example:
  75. // D[05-02|11:06:44.322] Stopping AddrBook (ignoring: already stopped)
  76. //
  77. // Description:
  78. // D - first character of the level, uppercase (ASCII only)
  79. // [05-02|11:06:44.322] - our time format (see https://golang.org/src/time/format.go)
  80. // Stopping ... - message
  81. enc.buf.WriteString(fmt.Sprintf("%c[%s] %-44s ", lvl[0]-32, time.Now().UTC().Format("01-02|15:04:05.000"), msg))
  82. if module != unknown {
  83. enc.buf.WriteString("module=" + module + " ")
  84. }
  85. KeyvalueLoop:
  86. for i := 0; i < len(keyvals)-1; i += 2 {
  87. for _, j := range excludeIndexes {
  88. if i == j {
  89. continue KeyvalueLoop
  90. }
  91. }
  92. err := enc.EncodeKeyval(keyvals[i], keyvals[i+1])
  93. if err == logfmt.ErrUnsupportedValueType {
  94. enc.EncodeKeyval(keyvals[i], fmt.Sprintf("%+v", keyvals[i+1]))
  95. } else if err != nil {
  96. return err
  97. }
  98. }
  99. // Add newline to the end of the buffer
  100. if err := enc.EndRecord(); err != nil {
  101. return err
  102. }
  103. // The Logger interface requires implementations to be safe for concurrent
  104. // use by multiple goroutines. For this implementation that means making
  105. // only one call to l.w.Write() for each call to Log.
  106. if _, err := l.w.Write(enc.buf.Bytes()); err != nil {
  107. return err
  108. }
  109. return nil
  110. }