forked from uber-go/zap
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ansi_encoder.go
157 lines (136 loc) · 3.8 KB
/
ansi_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
package zap
import (
"fmt"
"io"
"sync"
"time"
"github.com/mgutz/ansi"
)
var (
ansiPool = sync.Pool{
New: func() interface{} {
return &ansiEncoder{
textEncoder: textEncoder{
bytes: make([]byte, 0, _initialBufSize),
},
}
},
}
resetColor = ansi.ColorCode("reset")
// Default colors for levels.
defaultDebugColor = ansi.ColorCode("green")
defaultInfoColor = ansi.ColorCode("green")
defaultWarnColor = ansi.ColorCode("yellow")
defaultErrorColor = ansi.ColorCode("red")
defaultPanicColor = ansi.ColorCode("red")
defaultFatalColor = ansi.ColorCode("red")
)
type ansiEncoder struct {
textEncoder
debugColor string
infoColor string
warnColor string
errorColor string
panicColor string
fatalColor string
}
// A ANSIOption is used to set options for a ANSI encoder.
type ANSIOption interface {
apply(*ansiEncoder)
}
type ansiOptionFunc func(*ansiEncoder)
func (opt ansiOptionFunc) apply(enc *ansiEncoder) {
opt(enc)
}
// NewANSIEncoder creates a line-oriented text encoder whose output is optimized
// for human, rather than machine, consumption. Log levels are color-coded using
// ANSI escape codes. By default, the encoder uses RFC3339-formatted timestamps.
func NewANSIEncoder(options ...ANSIOption) Encoder {
enc := ansiPool.Get().(*ansiEncoder)
enc.truncate()
enc.timeFmt = time.RFC3339
enc.debugColor = defaultDebugColor
enc.infoColor = defaultInfoColor
enc.warnColor = defaultWarnColor
enc.errorColor = defaultErrorColor
enc.panicColor = defaultPanicColor
enc.fatalColor = defaultFatalColor
for _, opt := range options {
opt.apply(enc)
}
return enc
}
func (enc *ansiEncoder) Clone() Encoder {
clone := ansiPool.Get().(*ansiEncoder)
clone.truncate()
clone.bytes = append(clone.bytes, enc.bytes...)
clone.timeFmt = enc.timeFmt
clone.debugColor = enc.debugColor
clone.infoColor = enc.infoColor
clone.warnColor = enc.warnColor
clone.errorColor = enc.errorColor
clone.panicColor = enc.panicColor
clone.fatalColor = enc.fatalColor
return clone
}
func (enc *ansiEncoder) Free() {
ansiPool.Put(enc)
}
func (enc *ansiEncoder) WriteEntry(sink io.Writer, name string, msg string, lvl Level, t time.Time) error {
if sink == nil {
return errNilSink
}
final := textPool.Get().(*textEncoder)
final.truncate()
enc.addLevelColor(final, lvl)
enc.textEncoder.addLevel(final, lvl)
enc.textEncoder.addTime(final, t)
enc.textEncoder.addName(final, name)
enc.textEncoder.addMessage(final, msg)
if len(enc.textEncoder.bytes) > 0 {
final.bytes = append(final.bytes, ' ')
final.bytes = append(final.bytes, enc.textEncoder.bytes...)
}
enc.clearLevelColor(final, lvl)
final.bytes = append(final.bytes, '\n')
expectedBytes := len(final.bytes)
n, err := sink.Write(final.bytes)
final.Free()
if err != nil {
return err
}
if n != expectedBytes {
return fmt.Errorf("incomplete write: only wrote %v of %v bytes", n, expectedBytes)
}
return nil
}
func (enc *ansiEncoder) addLevelColor(final *textEncoder, lvl Level) {
switch lvl {
case DebugLevel:
final.bytes = append(final.bytes, enc.debugColor...)
case InfoLevel:
final.bytes = append(final.bytes, enc.infoColor...)
case WarnLevel:
final.bytes = append(final.bytes, enc.warnColor...)
case ErrorLevel:
final.bytes = append(final.bytes, enc.errorColor...)
case PanicLevel:
final.bytes = append(final.bytes, enc.panicColor...)
case FatalLevel:
final.bytes = append(final.bytes, enc.fatalColor...)
default:
}
}
func (enc *ansiEncoder) clearLevelColor(final *textEncoder, lvl Level) {
switch lvl {
case DebugLevel, InfoLevel, WarnLevel, ErrorLevel, PanicLevel, FatalLevel:
final.bytes = append(final.bytes, resetColor...)
default:
}
}
// AnsiTextOption allows passing TextOptions to an ANSI Encoder
func AnsiTextOption(to TextOption) ANSIOption {
return ansiOptionFunc(func(enc *ansiEncoder) {
to.apply(&enc.textEncoder)
})
}