github.com/supragya/TendermintConnector@v0.0.0-20210619045051-113e32b84fb1/chains/tm34/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/kit/log"
    13  	kitlevel "github.com/go-kit/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  
    92  	// Form a custom Tendermint line
    93  	//
    94  	// Example:
    95  	//     D[2016-05-02|11:06:44.322]   Stopping AddrBook (ignoring: already stopped)
    96  	//
    97  	// Description:
    98  	//     D										- first character of the level, uppercase (ASCII only)
    99  	//     [2016-05-02|11:06:44.322]    - our time format (see https://golang.org/src/time/format.go)
   100  	//     Stopping ...					- message
   101  	enc.buf.WriteString(fmt.Sprintf("%c[%s] %-44s ", lvl[0]-32, time.Now().Format("2006-01-02|15:04:05.000"), msg))
   102  
   103  	if module != unknown {
   104  		enc.buf.WriteString("module=" + module + " ")
   105  	}
   106  
   107  KeyvalueLoop:
   108  	for i := 0; i < len(keyvals)-1; i += 2 {
   109  		for _, j := range excludeIndexes {
   110  			if i == j {
   111  				continue KeyvalueLoop
   112  			}
   113  		}
   114  
   115  		err := enc.EncodeKeyval(keyvals[i], keyvals[i+1])
   116  		if err == logfmt.ErrUnsupportedValueType {
   117  			enc.EncodeKeyval(keyvals[i], fmt.Sprintf("%+v", keyvals[i+1])) //nolint:errcheck // no need to check error again
   118  		} else if err != nil {
   119  			return err
   120  		}
   121  	}
   122  
   123  	// Add newline to the end of the buffer
   124  	if err := enc.EndRecord(); err != nil {
   125  		return err
   126  	}
   127  
   128  	// The Logger interface requires implementations to be safe for concurrent
   129  	// use by multiple goroutines. For this implementation that means making
   130  	// only one call to l.w.Write() for each call to Log.
   131  	if _, err := l.w.Write(enc.buf.Bytes()); err != nil {
   132  		return err
   133  	}
   134  	return nil
   135  }