This repository has been archived by the owner on Jan 9, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
logger.go
226 lines (177 loc) · 6.38 KB
/
logger.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
package logur
import (
"fmt"
"strings"
)
// Logger is a unified interface for various logging use cases and practices, including:
// - leveled logging
// - leveled formatters
// - structured logging
type Logger interface {
// Trace logs a Trace event.
//
// Even more fine-grained information than Debug events.
// Loggers not supporting this level should fall back to Debug.
Trace(msg string, fields ...map[string]interface{})
// Debug logs a Debug event.
//
// A verbose series of information events.
// They are useful when debugging the system.
Debug(msg string, fields ...map[string]interface{})
// Info logs an Info event.
//
// General information about what's happening inside the system.
Info(msg string, fields ...map[string]interface{})
// Warn logs a Warn(ing) event.
//
// Non-critical events that should be looked at.
Warn(msg string, fields ...map[string]interface{})
// Error logs an Error event.
//
// Critical events that require immediate attention.
// Loggers commonly provide Fatal and Panic levels above Error level,
// but exiting and panicing is out of scope for a logging library.
Error(msg string, fields ...map[string]interface{})
}
// Fields is used to define structured fields which are appended to log events.
// It can be used as a shorthand for map[string]interface{}.
type Fields map[string]interface{}
// LogFunc is a function recording a log event.
type LogFunc func(msg string, fields ...map[string]interface{})
type noopLogger struct{}
// NewNoopLogger creates a no-op logger that discards all received log events.
// Useful in examples and as a fallback logger.
func NewNoopLogger() Logger {
return &noopLogger{}
}
func (*noopLogger) Trace(msg string, fields ...map[string]interface{}) {}
func (*noopLogger) Debug(msg string, fields ...map[string]interface{}) {}
func (*noopLogger) Info(msg string, fields ...map[string]interface{}) {}
func (*noopLogger) Warn(msg string, fields ...map[string]interface{}) {}
func (*noopLogger) Error(msg string, fields ...map[string]interface{}) {}
// WithFields returns a new logger instance that attaches the given fields to every subsequent log call.
func WithFields(logger Logger, fields map[string]interface{}) Logger {
if len(fields) == 0 {
return logger
}
// Do not add a new layer
// Create a new logger instead with the parent fields
if l, ok := logger.(*fieldLogger); ok && len(l.fields) > 0 {
_fields := make(map[string]interface{}, len(l.fields)+len(fields))
for key, value := range l.fields {
_fields[key] = value
}
for key, value := range fields {
_fields[key] = value
}
fields = _fields
logger = l.logger
}
l := &fieldLogger{logger: logger, fields: fields}
if levelEnabler, ok := logger.(LevelEnabler); ok {
l.levelEnabler = levelEnabler
}
return l
}
// fieldLogger holds a context and passes it to the underlying logger when a log event is recorded.
type fieldLogger struct {
logger Logger
fields map[string]interface{}
levelEnabler LevelEnabler
}
// Trace implements the logur.Logger interface.
func (l *fieldLogger) Trace(msg string, fields ...map[string]interface{}) {
l.log(Trace, l.logger.Trace, msg, fields)
}
// Debug implements the logur.Logger interface.
func (l *fieldLogger) Debug(msg string, fields ...map[string]interface{}) {
l.log(Debug, l.logger.Debug, msg, fields)
}
// Info implements the logur.Logger interface.
func (l *fieldLogger) Info(msg string, fields ...map[string]interface{}) {
l.log(Info, l.logger.Info, msg, fields)
}
// Warn implements the logur.Logger interface.
func (l *fieldLogger) Warn(msg string, fields ...map[string]interface{}) {
l.log(Warn, l.logger.Warn, msg, fields)
}
// Error implements the logur.Logger interface.
func (l *fieldLogger) Error(msg string, fields ...map[string]interface{}) {
l.log(Error, l.logger.Error, msg, fields)
}
// log deduplicates some field logger code.
func (l *fieldLogger) log(level Level, logFunc LogFunc, msg string, fields []map[string]interface{}) {
if !l.levelEnabled(level) {
return
}
var f = l.fields
if len(fields) > 0 {
f = l.mergeFields(fields[0])
}
logFunc(msg, f)
}
func (l *fieldLogger) mergeFields(fields map[string]interface{}) map[string]interface{} {
if len(fields) == 0 { // Not having any fields passed to the log function has a higher chance
return l.fields
}
if len(l.fields) == 0 { // This is possible too, but has a much lower probability
return fields
}
f := make(map[string]interface{}, len(fields)+len(l.fields))
for key, value := range l.fields {
f[key] = value
}
for key, value := range fields {
f[key] = value
}
return f
}
func (l *fieldLogger) levelEnabled(level Level) bool {
if l.levelEnabler != nil {
return l.levelEnabler.LevelEnabled(level)
}
return true
}
// PrintLogger logs messages with fmt.Print* function semantics.
type PrintLogger struct {
logger LogFunc
}
// NewPrintLogger returns a new PrintLogger.
func NewPrintLogger(logger LogFunc) *PrintLogger {
return &PrintLogger{logger}
}
// NewErrorPrintLogger returns a new PrintLogger that logs everything on error level.
func NewErrorPrintLogger(logger Logger) *PrintLogger {
return NewPrintLogger(LevelFunc(logger, Error))
}
// Print logs a line with fmt.Print semantics.
func (l *PrintLogger) Print(v ...interface{}) {
l.logger(fmt.Sprint(v...))
}
// Println logs a line with fmt.Println semantics.
func (l *PrintLogger) Println(v ...interface{}) {
l.logger(strings.TrimSuffix(fmt.Sprintln(v...), "\n"))
}
// Printf logs a line with fmt.Printf semantics.
func (l *PrintLogger) Printf(format string, args ...interface{}) {
l.logger(fmt.Sprintf(format, args...))
}
// MessageLogger simplifies the Logger interface by removing the second context parameter.
// Useful when there is no need for contextual logging.
type MessageLogger struct {
logger Logger
}
// NewMessageLogger returns a new MessageLogger instance.
func NewMessageLogger(logger Logger) *MessageLogger {
return &MessageLogger{logger}
}
// Trace logs a Trace level event.
func (l *MessageLogger) Trace(msg string) { l.logger.Trace(msg) }
// Debug logs a Debug level event.
func (l *MessageLogger) Debug(msg string) { l.logger.Debug(msg) }
// Info logs a Info level event.
func (l *MessageLogger) Info(msg string) { l.logger.Info(msg) }
// Warn logs a Warn level event.
func (l *MessageLogger) Warn(msg string) { l.logger.Warn(msg) }
// Error logs a Error level event.
func (l *MessageLogger) Error(msg string) { l.logger.Error(msg) }