|
|
- package log
-
- import (
- "fmt"
- "io"
- "os"
- "strings"
- "time"
-
- "github.com/rs/zerolog"
- )
-
- var _ Logger = (*defaultLogger)(nil)
-
- type defaultLogger struct {
- zerolog.Logger
- }
-
- // NewDefaultLogger returns a default logger that can be used within Tendermint
- // and that fulfills the Logger interface. The underlying logging provider is a
- // zerolog logger that supports typical log levels along with JSON and plain/text
- // log formats.
- //
- // Since zerolog supports typed structured logging and it is difficult to reflect
- // that in a generic interface, all logging methods accept a series of key/value
- // pair tuples, where the key must be a string.
- func NewDefaultLogger(format, level string) (Logger, error) {
- var logWriter io.Writer
- switch strings.ToLower(format) {
- case LogFormatPlain, LogFormatText:
- logWriter = zerolog.ConsoleWriter{
- Out: os.Stderr,
- NoColor: true,
- TimeFormat: time.RFC3339,
- FormatLevel: func(i interface{}) string {
- if ll, ok := i.(string); ok {
- return strings.ToUpper(ll)
- }
- return "????"
- },
- }
-
- case LogFormatJSON:
- logWriter = os.Stderr
-
- default:
- return nil, fmt.Errorf("unsupported log format: %s", format)
- }
-
- logLevel, err := zerolog.ParseLevel(level)
- if err != nil {
- return nil, fmt.Errorf("failed to parse log level (%s): %w", level, err)
- }
-
- // make the writer thread-safe
- logWriter = newSyncWriter(logWriter)
-
- return &defaultLogger{
- Logger: zerolog.New(logWriter).Level(logLevel).With().Timestamp().Logger(),
- }, nil
- }
-
- func (l defaultLogger) Info(msg string, keyVals ...interface{}) {
- l.Logger.Info().Fields(getLogFields(keyVals...)).Msg(msg)
- }
-
- func (l defaultLogger) Error(msg string, keyVals ...interface{}) {
- l.Logger.Error().Fields(getLogFields(keyVals...)).Msg(msg)
- }
-
- func (l defaultLogger) Debug(msg string, keyVals ...interface{}) {
- l.Logger.Debug().Fields(getLogFields(keyVals...)).Msg(msg)
- }
-
- func (l defaultLogger) With(keyVals ...interface{}) Logger {
- return &defaultLogger{
- Logger: l.Logger.With().Fields(getLogFields(keyVals...)).Logger(),
- }
- }
-
- // OverrideWithNewLogger replaces an existing logger's internal with
- // a new logger, and makes it possible to reconfigure an existing
- // logger that has already been propagated to callers.
- func OverrideWithNewLogger(logger Logger, format, level string) error {
- ol, ok := logger.(*defaultLogger)
- if !ok {
- return fmt.Errorf("logger %T cannot be overridden", logger)
- }
-
- newLogger, err := NewDefaultLogger(format, level)
- if err != nil {
- return err
- }
- nl, ok := newLogger.(*defaultLogger)
- if !ok {
- return fmt.Errorf("logger %T cannot be overridden by %T", logger, newLogger)
- }
-
- ol.Logger = nl.Logger
- return nil
- }
-
- func getLogFields(keyVals ...interface{}) map[string]interface{} {
- if len(keyVals)%2 != 0 {
- return nil
- }
-
- fields := make(map[string]interface{}, len(keyVals))
- for i := 0; i < len(keyVals); i += 2 {
- fields[fmt.Sprint(keyVals[i])] = keyVals[i+1]
- }
-
- return fields
- }
|