-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathformatter.go
295 lines (249 loc) · 8.66 KB
/
formatter.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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package fabenc
import (
"fmt"
"io"
"regexp"
"runtime"
"strings"
"sync"
"sync/atomic"
"go.uber.org/zap/zapcore"
)
// formatRegexp is broken into three groups:
// 1. the format verb
// 2. an optional colon that is ungrouped with '?:'
// 3. an optional, non-greedy format directive
//
// The grouping simplifies the verb proccessing during spec parsing.
var formatRegexp = regexp.MustCompile(`%{(color|id|level|message|module|shortfunc|time)(?::(.*?))?}`)
// ParseFormat parses a log format spec and returns a slice of formatters
// that should be iterated over to build a formatted log record.
//
// The op-loggng specifiers supported by this formatter are:
// - %{color} - level specific SGR color escape or SGR reset
// - %{id} - a unique log sequence number
// - %{level} - the log level of the entry
// - %{message} - the log message
// - %{module} - the zap logger name
// - %{shortfunc} - the name of the function creating the log record
// - %{time} - the time the log entry was created
//
// Specifiers may include an optional format verb:
// - color: reset|bold
// - id: a fmt style numeric formatter without the leading %
// - level: a fmt style string formatter without the leading %
// - message: a fmt style string formatter without the leading %
// - module: a fmt style string formatter without the leading %
//
func ParseFormat(spec string) ([]Formatter, error) {
cursor := 0
formatters := []Formatter{}
// iterate over the regex groups and convert to formatters
matches := formatRegexp.FindAllStringSubmatchIndex(spec, -1)
for _, m := range matches {
start, end := m[0], m[1]
verbStart, verbEnd := m[2], m[3]
formatStart, formatEnd := m[4], m[5]
if start > cursor {
formatters = append(formatters, StringFormatter{Value: spec[cursor:start]})
}
var format string
if formatStart >= 0 {
format = spec[formatStart:formatEnd]
}
formatter, err := NewFormatter(spec[verbStart:verbEnd], format)
if err != nil {
return nil, err
}
formatters = append(formatters, formatter)
cursor = end
}
// handle any trailing suffix
if cursor != len(spec) {
formatters = append(formatters, StringFormatter{Value: spec[cursor:]})
}
return formatters, nil
}
// A MultiFormatter presents multiple formatters as a single Formatter. It can
// be used to change the set of formatters associated with an encoder at
// runtime.
type MultiFormatter struct {
mutex sync.RWMutex
formatters []Formatter
}
// NewMultiFormatter creates a new MultiFormatter that delegates to the
// provided formatters. The formatters are used in the order they are
// presented.
func NewMultiFormatter(formatters ...Formatter) *MultiFormatter {
return &MultiFormatter{
formatters: formatters,
}
}
// Format iterates over its delegates to format a log record to the provided
// buffer.
func (m *MultiFormatter) Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field) {
m.mutex.RLock()
for i := range m.formatters {
m.formatters[i].Format(w, entry, fields)
}
m.mutex.RUnlock()
}
// SetFormatters replaces the delegate formatters.
func (m *MultiFormatter) SetFormatters(formatters []Formatter) {
m.mutex.Lock()
m.formatters = formatters
m.mutex.Unlock()
}
// A StringFormatter formats a fixed string.
type StringFormatter struct{ Value string }
// Format writes the formatter's fixed string to provided writer.
func (s StringFormatter) Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field) {
fmt.Fprintf(w, "%s", s.Value)
}
// NewFormatter creates the formatter for the provided verb. When a format is
// not provided, the default format for the verb is used.
func NewFormatter(verb, format string) (Formatter, error) {
switch verb {
case "color":
return newColorFormatter(format)
case "id":
return newSequenceFormatter(format), nil
case "level":
return newLevelFormatter(format), nil
case "message":
return newMessageFormatter(format), nil
case "module":
return newModuleFormatter(format), nil
case "shortfunc":
return newShortFuncFormatter(format), nil
case "time":
return newTimeFormatter(format), nil
default:
return nil, fmt.Errorf("unknown verb: %s", verb)
}
}
// A ColorFormatter formats an SGR color code.
type ColorFormatter struct {
Bold bool // set the bold attribute
Reset bool // reset colors and attributes
}
func newColorFormatter(f string) (ColorFormatter, error) {
switch f {
case "bold":
return ColorFormatter{Bold: true}, nil
case "reset":
return ColorFormatter{Reset: true}, nil
case "":
return ColorFormatter{}, nil
default:
return ColorFormatter{}, fmt.Errorf("invalid color option: %s", f)
}
}
// LevelColor returns the Color associated with a specific zap logging level.
func (c ColorFormatter) LevelColor(l zapcore.Level) Color {
switch l {
case zapcore.DebugLevel:
return ColorCyan
case zapcore.InfoLevel:
return ColorBlue
case zapcore.WarnLevel:
return ColorYellow
case zapcore.ErrorLevel:
return ColorRed
case zapcore.DPanicLevel, zapcore.PanicLevel:
return ColorMagenta
case zapcore.FatalLevel:
return ColorMagenta
default:
return ColorNone
}
}
// Format writes the SGR color code to the provided writer.
func (c ColorFormatter) Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field) {
switch {
case c.Reset:
fmt.Fprintf(w, ResetColor())
case c.Bold:
fmt.Fprintf(w, c.LevelColor(entry.Level).Bold())
default:
fmt.Fprintf(w, c.LevelColor(entry.Level).Normal())
}
}
// LevelFormatter formats a log level.
type LevelFormatter struct{ FormatVerb string }
func newLevelFormatter(f string) LevelFormatter {
return LevelFormatter{FormatVerb: "%" + stringOrDefault(f, "s")}
}
// Format writes the logging level to the provided writer.
func (l LevelFormatter) Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field) {
fmt.Fprintf(w, l.FormatVerb, entry.Level.CapitalString())
}
// MessageFormatter formats a log message.
type MessageFormatter struct{ FormatVerb string }
func newMessageFormatter(f string) MessageFormatter {
return MessageFormatter{FormatVerb: "%" + stringOrDefault(f, "s")}
}
// Format writes the log entry message to the provided writer.
func (m MessageFormatter) Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field) {
fmt.Fprintf(w, m.FormatVerb, strings.TrimRight(entry.Message, "\n"))
}
// ModuleFormatter formats the zap logger name.
type ModuleFormatter struct{ FormatVerb string }
func newModuleFormatter(f string) ModuleFormatter {
return ModuleFormatter{FormatVerb: "%" + stringOrDefault(f, "s")}
}
// Format writes the zap logger name to the specified writer.
func (m ModuleFormatter) Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field) {
fmt.Fprintf(w, m.FormatVerb, entry.LoggerName)
}
// sequence maintains the global sequence number shared by all SequeneFormatter
// instances.
var sequence uint64
// SetSequence explicitly sets the global sequence number.
func SetSequence(s uint64) { atomic.StoreUint64(&sequence, s) }
// SequenceFormatter formats a global sequence number.
type SequenceFormatter struct{ FormatVerb string }
func newSequenceFormatter(f string) SequenceFormatter {
return SequenceFormatter{FormatVerb: "%" + stringOrDefault(f, "d")}
}
// SequenceFormatter increments a global sequence number and writes it to the
// provided writer.
func (s SequenceFormatter) Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field) {
fmt.Fprintf(w, s.FormatVerb, atomic.AddUint64(&sequence, 1))
}
// ShortFuncFormatter formats the name of the function creating the log record.
type ShortFuncFormatter struct{ FormatVerb string }
func newShortFuncFormatter(f string) ShortFuncFormatter {
return ShortFuncFormatter{FormatVerb: "%" + stringOrDefault(f, "s")}
}
// Format writes the calling function name to the provided writer. The name is obtained from
// the runtime and the package and line numbers are discarded.
func (s ShortFuncFormatter) Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field) {
f := runtime.FuncForPC(entry.Caller.PC)
if f == nil {
fmt.Fprintf(w, s.FormatVerb, "(unknown)")
return
}
fname := f.Name()
funcIdx := strings.LastIndex(fname, ".")
fmt.Fprintf(w, s.FormatVerb, fname[funcIdx+1:])
}
// TimeFormatter formats the time from the zap log entry.
type TimeFormatter struct{ Layout string }
func newTimeFormatter(f string) TimeFormatter {
return TimeFormatter{Layout: stringOrDefault(f, "2006-01-02T15:04:05.999Z07:00")}
}
// Format writes the log record time stamp to the provided writer.
func (t TimeFormatter) Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field) {
fmt.Fprint(w, entry.Time.Format(t.Layout))
}
func stringOrDefault(str, dflt string) string {
if str != "" {
return str
}
return dflt
}