forked from tendermint/tendermint
-
Notifications
You must be signed in to change notification settings - Fork 0
/
filter.go
195 lines (169 loc) · 5.75 KB
/
filter.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
package log
import "fmt"
type level byte
const (
levelDebug level = 1 << iota
levelInfo
levelError
)
type filter struct {
next Logger
allowed level // XOR'd levels for default case
initiallyAllowed level // XOR'd levels for initial case
allowedKeyvals map[keyval]level // When key-value match, use this level
}
type keyval struct {
key interface{}
value interface{}
}
// NewFilter wraps next and implements filtering. See the commentary on the
// Option functions for a detailed description of how to configure levels. If
// no options are provided, all leveled log events created with Debug, Info or
// Error helper methods are squelched.
func NewFilter(next Logger, options ...Option) Logger {
l := &filter{
next: next,
allowedKeyvals: make(map[keyval]level),
}
for _, option := range options {
option(l)
}
l.initiallyAllowed = l.allowed
return l
}
func (l *filter) Info(msg string, keyvals ...interface{}) {
levelAllowed := l.allowed&levelInfo != 0
if !levelAllowed {
return
}
l.next.Info(msg, keyvals...)
}
func (l *filter) Debug(msg string, keyvals ...interface{}) {
levelAllowed := l.allowed&levelDebug != 0
if !levelAllowed {
return
}
l.next.Debug(msg, keyvals...)
}
func (l *filter) Error(msg string, keyvals ...interface{}) {
levelAllowed := l.allowed&levelError != 0
if !levelAllowed {
return
}
l.next.Error(msg, keyvals...)
}
// With implements Logger by constructing a new filter with a keyvals appended
// to the logger.
//
// If custom level was set for a keyval pair using one of the
// Allow*With methods, it is used as the logger's level.
//
// Examples:
// logger = log.NewFilter(logger, log.AllowError(), log.AllowInfoWith("module", "crypto"))
// logger.With("module", "crypto").Info("Hello") # produces "I... Hello module=crypto"
//
// logger = log.NewFilter(logger, log.AllowError(),
// log.AllowInfoWith("module", "crypto"),
// log.AllowNoneWith("user", "Sam"))
// logger.With("module", "crypto", "user", "Sam").Info("Hello") # returns nil
//
// logger = log.NewFilter(logger,
// log.AllowError(),
// log.AllowInfoWith("module", "crypto"), log.AllowNoneWith("user", "Sam"))
// logger.With("user", "Sam").With("module", "crypto").Info("Hello") # produces "I... Hello module=crypto user=Sam"
func (l *filter) With(keyvals ...interface{}) Logger {
keyInAllowedKeyvals := false
for i := len(keyvals) - 2; i >= 0; i -= 2 {
for kv, allowed := range l.allowedKeyvals {
if keyvals[i] == kv.key {
keyInAllowedKeyvals = true
// Example:
// logger = log.NewFilter(logger, log.AllowError(), log.AllowInfoWith("module", "crypto"))
// logger.With("module", "crypto")
if keyvals[i+1] == kv.value {
return &filter{
next: l.next.With(keyvals...),
allowed: allowed, // set the desired level
allowedKeyvals: l.allowedKeyvals,
initiallyAllowed: l.initiallyAllowed,
}
}
}
}
}
// Example:
// logger = log.NewFilter(logger, log.AllowError(), log.AllowInfoWith("module", "crypto"))
// logger.With("module", "main")
if keyInAllowedKeyvals {
return &filter{
next: l.next.With(keyvals...),
allowed: l.initiallyAllowed, // return back to initially allowed
allowedKeyvals: l.allowedKeyvals,
initiallyAllowed: l.initiallyAllowed,
}
}
return &filter{
next: l.next.With(keyvals...),
allowed: l.allowed, // simply continue with the current level
allowedKeyvals: l.allowedKeyvals,
initiallyAllowed: l.initiallyAllowed,
}
}
//--------------------------------------------------------------------------------
// Option sets a parameter for the filter.
type Option func(*filter)
// AllowLevel returns an option for the given level or error if no option exist
// for such level.
func AllowLevel(lvl string) (Option, error) {
switch lvl {
case "debug":
return AllowDebug(), nil
case "info":
return AllowInfo(), nil
case "error":
return AllowError(), nil
case "none":
return AllowNone(), nil
default:
return nil, fmt.Errorf("expected either \"info\", \"debug\", \"error\" or \"none\" level, given %s", lvl)
}
}
// AllowAll is an alias for AllowDebug.
func AllowAll() Option {
return AllowDebug()
}
// AllowDebug allows error, info and debug level log events to pass.
func AllowDebug() Option {
return allowed(levelError | levelInfo | levelDebug)
}
// AllowInfo allows error and info level log events to pass.
func AllowInfo() Option {
return allowed(levelError | levelInfo)
}
// AllowError allows only error level log events to pass.
func AllowError() Option {
return allowed(levelError)
}
// AllowNone allows no leveled log events to pass.
func AllowNone() Option {
return allowed(0)
}
func allowed(allowed level) Option {
return func(l *filter) { l.allowed = allowed }
}
// AllowDebugWith allows error, info and debug level log events to pass for a specific key value pair.
func AllowDebugWith(key interface{}, value interface{}) Option {
return func(l *filter) { l.allowedKeyvals[keyval{key, value}] = levelError | levelInfo | levelDebug }
}
// AllowInfoWith allows error and info level log events to pass for a specific key value pair.
func AllowInfoWith(key interface{}, value interface{}) Option {
return func(l *filter) { l.allowedKeyvals[keyval{key, value}] = levelError | levelInfo }
}
// AllowErrorWith allows only error level log events to pass for a specific key value pair.
func AllowErrorWith(key interface{}, value interface{}) Option {
return func(l *filter) { l.allowedKeyvals[keyval{key, value}] = levelError }
}
// AllowNoneWith allows no leveled log events to pass for a specific key value pair.
func AllowNoneWith(key interface{}, value interface{}) Option {
return func(l *filter) { l.allowedKeyvals[keyval{key, value}] = 0 }
}