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  }