-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathconsole_encoder.go
224 lines (192 loc) · 5.63 KB
/
console_encoder.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
package flume
import (
"encoding/hex"
"github.com/mgutz/ansi"
"go.uber.org/zap"
"go.uber.org/zap/buffer"
"go.uber.org/zap/zapcore"
)
//nolint:gochecknoinits
func init() {
_ = zap.RegisterEncoder("term", func(cfg zapcore.EncoderConfig) (zapcore.Encoder, error) {
return NewConsoleEncoder((*EncoderConfig)(&cfg)), nil
})
_ = zap.RegisterEncoder("term-color", func(cfg zapcore.EncoderConfig) (zapcore.Encoder, error) {
return NewColorizedConsoleEncoder((*EncoderConfig)(&cfg), nil), nil
})
}
// Colorizer returns ansi escape sequences for the colors for each log level.
// See Colors for a default implementation.
type Colorizer interface {
Level(l Level) string
}
// Colors is an implementation of the Colorizer interface, which assigns colors
// to the default log levels.
type Colors struct {
Debug, Info, Warn, Error string
}
// Level implements Colorizer
func (c *Colors) Level(l Level) string {
if l < DebugLevel {
return Dim
}
switch l {
case DebugLevel:
return c.Debug
case InfoLevel:
return c.Info
case Level(zapcore.WarnLevel):
return c.Warn
default:
return c.Error
}
}
// DefaultColors is the default instance of Colors, used as the default colors if
// a nil Colorizer is passed to NewColorizedConsoleEncoder.
var DefaultColors = Colors{
Debug: ansi.ColorCode("cyan"),
Info: ansi.ColorCode("green+h"),
Warn: ansi.ColorCode("yellow+bh"),
Error: ansi.ColorCode("red+bh"),
}
type consoleEncoder struct {
*ltsvEncoder
colorizer Colorizer
}
// NewConsoleEncoder creates an encoder whose output is designed for human -
// rather than machine - consumption. It serializes the core log entry data
// (message, level, timestamp, etc.) in a plain-text format. The context is
// encoded in LTSV.
//
// Note that although the console encoder doesn't use the keys specified in the
// encoder configuration, it will omit any element whose key is set to the empty
// string.
func NewConsoleEncoder(cfg *EncoderConfig) Encoder {
ltsvEncoder := NewLTSVEncoder(cfg).(*ltsvEncoder)
ltsvEncoder.allowNewLines = true
ltsvEncoder.allowTabs = true
ltsvEncoder.blankKey = "value"
ltsvEncoder.binaryEncoder = hex.Dump
return &consoleEncoder{ltsvEncoder: ltsvEncoder}
}
// NewColorizedConsoleEncoder creates a console encoder, like NewConsoleEncoder, but
// colors the text with ansi escape codes. `colorize` configures which colors to
// use for each level.
//
// If `colorizer` is nil, it will default to DefaultColors.
//
// `github.com/mgutz/ansi` is a convenient package for getting color codes, e.g.:
//
// ansi.ColorCode("red")
func NewColorizedConsoleEncoder(cfg *EncoderConfig, colorizer Colorizer) Encoder {
e := NewConsoleEncoder(cfg).(*consoleEncoder)
e.colorizer = colorizer
if e.colorizer == nil {
e.colorizer = &DefaultColors
}
return e
}
// Clone implements the Encoder interface
func (c *consoleEncoder) Clone() zapcore.Encoder {
return &consoleEncoder{
ltsvEncoder: c.ltsvEncoder.Clone().(*ltsvEncoder),
colorizer: c.colorizer,
}
}
// Dim is the color used for context keys, time, and caller information
var Dim = ansi.ColorCode("240")
// Bright is the color used for the message
var Bright = ansi.ColorCode("default+b")
// EncodeEntry implements the Encoder interface
func (c *consoleEncoder) EncodeEntry(ent zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) {
final := *c.ltsvEncoder
context := final.buf
final.buf = bufPool.Get()
origLen := final.buf.Len()
if c.TimeKey != "" {
c.colorDim(final.buf)
final.skipNextElementSeparator = true
c.EncodeTime(ent.Time, &final)
}
if c.LevelKey != "" {
c.colorLevel(final.buf, ent.Level)
if final.buf.Len() > origLen {
final.buf.AppendByte(' ')
}
final.skipNextElementSeparator = true
c.EncodeLevel(ent.Level, &final)
}
if final.buf.Len() > origLen {
c.colorDim(final.buf)
final.buf.AppendString(" | ")
} else {
final.buf.Reset()
}
// Add the message itself.
if c.MessageKey != "" {
c.colorReset(final.buf)
// c.colorBright(&final)
final.safeAddString(ent.Message, false)
// ensure a minimum of 2 spaces between the message and the fields,
// to improve readability
final.buf.AppendString(" ")
}
c.colorDim(final.buf)
// Add fields.
for _, f := range fields {
f.AddTo(&final)
}
// Add context
if context.Len() > 0 {
final.addFieldSeparator()
_, _ = final.buf.Write(context.Bytes())
}
// Add callsite
c.writeCallSite(&final, ent.LoggerName, ent.Caller)
// If there's no stacktrace key, honor that; this allows users to force
// single-line output.
if ent.Stack != "" && c.StacktraceKey != "" {
final.buf.AppendByte('\n')
final.buf.AppendString(ent.Stack)
}
c.colorReset(final.buf)
final.buf.AppendByte('\n')
return final.buf, nil
}
func (c *consoleEncoder) writeCallSite(final *ltsvEncoder, name string, caller zapcore.EntryCaller) {
shouldWriteName := name != "" && c.NameKey != ""
shouldWriteCaller := caller.Defined && c.CallerKey != ""
if !shouldWriteName && !shouldWriteCaller {
return
}
final.addKey("@")
if shouldWriteName {
final.buf.AppendString(name)
if shouldWriteCaller {
final.buf.AppendByte('@')
}
}
if shouldWriteCaller {
final.skipNextElementSeparator = true
final.EncodeCaller(caller, final)
}
}
func (c *consoleEncoder) colorDim(buf *buffer.Buffer) {
c.applyColor(buf, Dim)
}
func (c *consoleEncoder) colorLevel(buf *buffer.Buffer, level zapcore.Level) {
if c.colorizer != nil {
c.applyColor(buf, c.colorizer.Level(Level(level)))
}
}
func (c *consoleEncoder) applyColor(buf *buffer.Buffer, s string) {
if c.colorizer != nil {
buf.AppendString(ansi.Reset)
if s != "" {
buf.AppendString(s)
}
}
}
func (c *consoleEncoder) colorReset(buf *buffer.Buffer) {
c.applyColor(buf, "")
}