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.

135 lines
3.4 KiB

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