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.

125 lines
3.1 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. // MustNewDefaultLogger delegates a call NewDefaultLogger where it panics on
  53. // error.
  54. func MustNewDefaultLogger(format, level string) Logger {
  55. logger, err := NewDefaultLogger(format, level)
  56. if err != nil {
  57. panic(err)
  58. }
  59. return logger
  60. }
  61. func (l defaultLogger) Info(msg string, keyVals ...interface{}) {
  62. l.Logger.Info().Fields(getLogFields(keyVals...)).Msg(msg)
  63. }
  64. func (l defaultLogger) Error(msg string, keyVals ...interface{}) {
  65. l.Logger.Error().Fields(getLogFields(keyVals...)).Msg(msg)
  66. }
  67. func (l defaultLogger) Debug(msg string, keyVals ...interface{}) {
  68. l.Logger.Debug().Fields(getLogFields(keyVals...)).Msg(msg)
  69. }
  70. func (l defaultLogger) With(keyVals ...interface{}) Logger {
  71. return &defaultLogger{
  72. Logger: l.Logger.With().Fields(getLogFields(keyVals...)).Logger(),
  73. }
  74. }
  75. // OverrideWithNewLogger replaces an existing logger's internal with
  76. // a new logger, and makes it possible to reconfigure an existing
  77. // logger that has already been propagated to callers.
  78. func OverrideWithNewLogger(logger Logger, format, level string) error {
  79. ol, ok := logger.(*defaultLogger)
  80. if !ok {
  81. return fmt.Errorf("logger %T cannot be overridden", logger)
  82. }
  83. newLogger, err := NewDefaultLogger(format, level)
  84. if err != nil {
  85. return err
  86. }
  87. nl, ok := newLogger.(*defaultLogger)
  88. if !ok {
  89. return fmt.Errorf("logger %T cannot be overridden by %T", logger, newLogger)
  90. }
  91. ol.Logger = nl.Logger
  92. return nil
  93. }
  94. func getLogFields(keyVals ...interface{}) map[string]interface{} {
  95. if len(keyVals)%2 != 0 {
  96. return nil
  97. }
  98. fields := make(map[string]interface{}, len(keyVals))
  99. for i := 0; i < len(keyVals); i += 2 {
  100. fields[fmt.Sprint(keyVals[i])] = keyVals[i+1]
  101. }
  102. return fields
  103. }