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