OSDN Git Service

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