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 }