/
log.go
168 lines (147 loc) · 4.68 KB
/
log.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
package log
// Logger is the minimal interface for logging methods
type Logger interface {
Log(keyvals ...interface{})
}
// Disableable is an optional interface that a logger can implement to signal it is disabled and should not
// be sent log messages for optimization reasons
type Disableable interface {
Disabled() bool
}
// ErrorHandler can handle errors that various loggers may return
type ErrorHandler interface {
ErrorLogger(error) Logger
}
// ErrorHandlingLogger wraps Logger and ErrorHandler
type ErrorHandlingLogger interface {
Logger
ErrorHandler
}
// ErrorHandlingDisableableLogger wraps Logger and ErrorHandler and Disabled
type ErrorHandlingDisableableLogger interface {
Logger
ErrorHandler
Disableable
}
// ErrorHandlerFunc converts a function into a ErrorHandler
type ErrorHandlerFunc func(error) Logger
// ErrorLogger calls the wrapped function
func (f ErrorHandlerFunc) ErrorLogger(e error) Logger {
return f(e)
}
// Context allows users to create a logger that appends key/values to log statements. Note that a nil Context is ok to
// use and works generally as expected, allowing optional logging in default struct{} objects.
type Context struct {
Logger Logger
KeyVals []interface{}
}
// NewContext creates a context out of a logger
func NewContext(logger Logger) *Context {
if logger == nil {
return nil
}
if c, ok := logger.(*Context); ok {
return c
}
ctx := &Context{
Logger: logger,
}
return ctx
}
// IsDisabled returns true if the wrapped logger implements Disableable (or is nil). Will signal that it's not worth
// sending a logger messages.
func IsDisabled(l Logger) bool {
if l == nil {
return true
}
if disable, ok := l.(Disableable); ok && disable.Disabled() {
return true
}
return false
}
// addArrays will add two arrays. Note that we CANNOT append(a, b...) because that will use the extra
// capacity of a and if two groutines call this at the same time they will race with each other.
func addArrays(a, b []interface{}) []interface{} {
if len(a) == 0 && len(b) == 0 {
return []interface{}{}
}
n := len(a) + len(b)
ret := make([]interface{}, 0, n)
ret = append(ret, a...)
ret = append(ret, b...)
return ret
}
// Log calls Log() on the wrapped logger appending the Context's values
func (l *Context) Log(keyvals ...interface{}) {
// Note: The assumption here is that copyIfDynamic is "slow" since dynamic values
// could be slow to calculate. So to optimize the case of "logs are turned off"
// we enable the ability to early return if the logger is off.
if l == nil || IsDisabled(l.Logger) {
return
}
l.Logger.Log(copyIfDynamic(addArrays(l.KeyVals, keyvals))...)
}
// Disabled returns true if the wrapped logger is disabled
func (l *Context) Disabled() bool {
return l == nil || IsDisabled(l.Logger)
}
// With returns a new context that adds key/values to log statements
func (l *Context) With(keyvals ...interface{}) *Context {
if len(keyvals)%2 != 0 {
panic("Programmer error. Please call log.Context.With() only with an even number of arguments.")
}
if len(keyvals) == 0 || l == nil {
return l
}
return &Context{
Logger: l.Logger,
KeyVals: addArrays(l.KeyVals, keyvals),
}
}
// WithPrefix is like With but adds keyvalus to the beginning of the eventual log statement
func (l *Context) WithPrefix(keyvals ...interface{}) *Context {
if len(keyvals)%2 != 0 {
panic("Programmer error. Please call log.Context.WithPrefix() only with an even number of arguments.")
}
if len(keyvals) == 0 || l == nil {
return l
}
return &Context{
Logger: l.Logger,
KeyVals: addArrays(keyvals, l.KeyVals),
}
}
// LoggerFunc converts a function into a Logger
type LoggerFunc func(...interface{})
// Log calls the wrapped function
func (f LoggerFunc) Log(keyvals ...interface{}) {
f(keyvals...)
}
// IfErr is a shorthand that will log an error if err is not nil
func IfErr(l Logger, err error) {
if err != nil {
l.Log(Err, err)
}
}
// IfErrAndReturn is a shorthand that will log an error if err is not nil and returns the original err
func IfErrAndReturn(l Logger, err error) error {
if err != nil {
l.Log(Err, err)
}
return err
}
// IfErrWithKeys logs an error with the supplied logger if it is not nil.
// The error will be appended to the end of the supplied keys/messages
func IfErrWithKeys(l Logger, err error, intf ...interface{}) {
if err != nil && l != nil {
l.Log(append(intf, err)...)
}
}
// IfErrWithKeysAndReturn logs an error with the supplied logger if it is not nil and then returns the original error.
// The error will be appended to the end of the supplied keys/messages
func IfErrWithKeysAndReturn(l Logger, err error, intf ...interface{}) error {
if err != nil && l != nil {
l.Log(append(intf, err)...)
}
return err
}