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.

114 lines
2.9 KiB

  1. package log
  2. import (
  3. "fmt"
  4. "io"
  5. "os"
  6. "strings"
  7. "time"
  8. "github.com/rs/zerolog"
  9. )
  10. var _ Logger = (*defaultLogger)(nil)
  11. type defaultLogger struct {
  12. zerolog.Logger
  13. }
  14. // NewDefaultLogger returns a default logger that can be used within Tendermint
  15. // and that fulfills the Logger interface. The underlying logging provider is a
  16. // zerolog logger that supports typical log levels along with JSON and plain/text
  17. // log formats.
  18. //
  19. // Since zerolog supports typed structured logging and it is difficult to reflect
  20. // that in a generic interface, all logging methods accept a series of key/value
  21. // pair tuples, where the key must be a string.
  22. func NewDefaultLogger(format, level string) (Logger, error) {
  23. var logWriter io.Writer
  24. switch strings.ToLower(format) {
  25. case LogFormatPlain, LogFormatText:
  26. logWriter = zerolog.ConsoleWriter{
  27. Out: os.Stderr,
  28. NoColor: true,
  29. TimeFormat: time.RFC3339,
  30. FormatLevel: func(i interface{}) string {
  31. if ll, ok := i.(string); ok {
  32. return strings.ToUpper(ll)
  33. }
  34. return "????"
  35. },
  36. }
  37. case LogFormatJSON:
  38. logWriter = os.Stderr
  39. default:
  40. return nil, fmt.Errorf("unsupported log format: %s", format)
  41. }
  42. logLevel, err := zerolog.ParseLevel(level)
  43. if err != nil {
  44. return nil, fmt.Errorf("failed to parse log level (%s): %w", level, err)
  45. }
  46. // make the writer thread-safe
  47. logWriter = newSyncWriter(logWriter)
  48. return &defaultLogger{
  49. Logger: zerolog.New(logWriter).Level(logLevel).With().Timestamp().Logger(),
  50. }, nil
  51. }
  52. func (l defaultLogger) Info(msg string, keyVals ...interface{}) {
  53. l.Logger.Info().Fields(getLogFields(keyVals...)).Msg(msg)
  54. }
  55. func (l defaultLogger) Error(msg string, keyVals ...interface{}) {
  56. l.Logger.Error().Fields(getLogFields(keyVals...)).Msg(msg)
  57. }
  58. func (l defaultLogger) Debug(msg string, keyVals ...interface{}) {
  59. l.Logger.Debug().Fields(getLogFields(keyVals...)).Msg(msg)
  60. }
  61. func (l defaultLogger) With(keyVals ...interface{}) Logger {
  62. return &defaultLogger{
  63. Logger: l.Logger.With().Fields(getLogFields(keyVals...)).Logger(),
  64. }
  65. }
  66. // OverrideWithNewLogger replaces an existing logger's internal with
  67. // a new logger, and makes it possible to reconfigure an existing
  68. // logger that has already been propagated to callers.
  69. func OverrideWithNewLogger(logger Logger, format, level string) error {
  70. ol, ok := logger.(*defaultLogger)
  71. if !ok {
  72. return fmt.Errorf("logger %T cannot be overridden", logger)
  73. }
  74. newLogger, err := NewDefaultLogger(format, level)
  75. if err != nil {
  76. return err
  77. }
  78. nl, ok := newLogger.(*defaultLogger)
  79. if !ok {
  80. return fmt.Errorf("logger %T cannot be overridden by %T", logger, newLogger)
  81. }
  82. ol.Logger = nl.Logger
  83. return nil
  84. }
  85. func getLogFields(keyVals ...interface{}) map[string]interface{} {
  86. if len(keyVals)%2 != 0 {
  87. return nil
  88. }
  89. fields := make(map[string]interface{}, len(keyVals))
  90. for i := 0; i < len(keyVals); i += 2 {
  91. fields[fmt.Sprint(keyVals[i])] = keyVals[i+1]
  92. }
  93. return fields
  94. }