github.com/vipernet-xyz/tm@v0.34.24/libs/log/tmfmt_logger.go (about)

     1  package log
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/hex"
     6  	"fmt"
     7  	"io"
     8  	"strings"
     9  	"sync"
    10  	"time"
    11  
    12  	kitlog "github.com/go-kit/log"
    13  	kitlevel "github.com/go-kit/log/level"
    14  	"github.com/go-logfmt/logfmt"
    15  )
    16  
    17  type tmfmtEncoder struct {
    18  	*logfmt.Encoder
    19  	buf bytes.Buffer
    20  }
    21  
    22  func (l *tmfmtEncoder) Reset() {
    23  	l.Encoder.Reset()
    24  	l.buf.Reset()
    25  }
    26  
    27  var tmfmtEncoderPool = sync.Pool{
    28  	New: func() interface{} {
    29  		var enc tmfmtEncoder
    30  		enc.Encoder = logfmt.NewEncoder(&enc.buf)
    31  		return &enc
    32  	},
    33  }
    34  
    35  type tmfmtLogger struct {
    36  	w io.Writer
    37  }
    38  
    39  // NewTMFmtLogger returns a logger that encodes keyvals to the Writer in
    40  // Tendermint custom format. Note complex types (structs, maps, slices)
    41  // formatted as "%+v".
    42  //
    43  // Each log event produces no more than one call to w.Write.
    44  // The passed Writer must be safe for concurrent use by multiple goroutines if
    45  // the returned Logger will be used concurrently.
    46  func NewTMFmtLogger(w io.Writer) kitlog.Logger {
    47  	return &tmfmtLogger{w}
    48  }
    49  
    50  func (l tmfmtLogger) Log(keyvals ...interface{}) error {
    51  	enc := tmfmtEncoderPool.Get().(*tmfmtEncoder)
    52  	enc.Reset()
    53  	defer tmfmtEncoderPool.Put(enc)
    54  
    55  	const unknown = "unknown"
    56  	lvl := "none"
    57  	msg := unknown
    58  	module := unknown
    59  
    60  	// indexes of keys to skip while encoding later
    61  	excludeIndexes := make([]int, 0)
    62  
    63  	for i := 0; i < len(keyvals)-1; i += 2 {
    64  		// Extract level
    65  		switch keyvals[i] {
    66  		case kitlevel.Key():
    67  			excludeIndexes = append(excludeIndexes, i)
    68  			switch keyvals[i+1].(type) { //nolint:gocritic
    69  			case string:
    70  				lvl = keyvals[i+1].(string)
    71  			case kitlevel.Value:
    72  				lvl = keyvals[i+1].(kitlevel.Value).String()
    73  			default:
    74  				panic(fmt.Sprintf("level value of unknown type %T", keyvals[i+1]))
    75  			}
    76  			// and message
    77  		case msgKey:
    78  			excludeIndexes = append(excludeIndexes, i)
    79  			msg = keyvals[i+1].(string)
    80  			// and module (could be multiple keyvals; if such case last keyvalue wins)
    81  		case moduleKey:
    82  			excludeIndexes = append(excludeIndexes, i)
    83  			module = keyvals[i+1].(string)
    84  		}
    85  
    86  		// Print []byte as a hexadecimal string (uppercased)
    87  		if b, ok := keyvals[i+1].([]byte); ok {
    88  			keyvals[i+1] = strings.ToUpper(hex.EncodeToString(b))
    89  		}
    90  
    91  		// Realize stringers
    92  		if s, ok := keyvals[i+1].(fmt.Stringer); ok {
    93  			keyvals[i+1] = s.String()
    94  		}
    95  
    96  	}
    97  
    98  	// Form a custom Tendermint line
    99  	//
   100  	// Example:
   101  	//     D[2016-05-02|11:06:44.322]   Stopping AddrBook (ignoring: already stopped)
   102  	//
   103  	// Description:
   104  	//     D										- first character of the level, uppercase (ASCII only)
   105  	//     [2016-05-02|11:06:44.322]    - our time format (see https://golang.org/src/time/format.go)
   106  	//     Stopping ...					- message
   107  	enc.buf.WriteString(fmt.Sprintf("%c[%s] %-44s ", lvl[0]-32, time.Now().Format("2006-01-02|15:04:05.000"), msg))
   108  
   109  	if module != unknown {
   110  		enc.buf.WriteString("module=" + module + " ")
   111  	}
   112  
   113  KeyvalueLoop:
   114  	for i := 0; i < len(keyvals)-1; i += 2 {
   115  		for _, j := range excludeIndexes {
   116  			if i == j {
   117  				continue KeyvalueLoop
   118  			}
   119  		}
   120  
   121  		err := enc.EncodeKeyval(keyvals[i], keyvals[i+1])
   122  		if err == logfmt.ErrUnsupportedValueType {
   123  			enc.EncodeKeyval(keyvals[i], fmt.Sprintf("%+v", keyvals[i+1])) //nolint:errcheck // no need to check error again
   124  		} else if err != nil {
   125  			return err
   126  		}
   127  	}
   128  
   129  	// Add newline to the end of the buffer
   130  	if err := enc.EndRecord(); err != nil {
   131  		return err
   132  	}
   133  
   134  	// The Logger interface requires implementations to be safe for concurrent
   135  	// use by multiple goroutines. For this implementation that means making
   136  	// only one call to l.w.Write() for each call to Log.
   137  	if _, err := l.w.Write(enc.buf.Bytes()); err != nil {
   138  		return err
   139  	}
   140  	return nil
   141  }