log: Add level.NewInjector and export Key and [Level]Value functions.
Chris Hines
7 years ago
1 | 1 | |
2 | 2 | import "github.com/go-kit/kit/log" |
3 | 3 | |
4 | // Error returns a logger that includes an error level keyval. | |
4 | // Error returns a logger that includes a Key/ErrorValue pair. | |
5 | 5 | func Error(logger log.Logger) log.Logger { |
6 | return log.NewContext(logger).WithPrefix(levelKey, errorLevelValue) | |
6 | return log.NewContext(logger).WithPrefix(key, errorValue) | |
7 | 7 | } |
8 | 8 | |
9 | // Warn returns a logger that includes a warn level keyval. | |
9 | // Warn returns a logger that includes a Key/WarnValue pair. | |
10 | 10 | func Warn(logger log.Logger) log.Logger { |
11 | return log.NewContext(logger).WithPrefix(levelKey, warnLevelValue) | |
11 | return log.NewContext(logger).WithPrefix(key, warnValue) | |
12 | 12 | } |
13 | 13 | |
14 | // Info returns a logger that includes an info level keyval. | |
14 | // Info returns a logger that includes a Key/InfoValue pair. | |
15 | 15 | func Info(logger log.Logger) log.Logger { |
16 | return log.NewContext(logger).WithPrefix(levelKey, infoLevelValue) | |
16 | return log.NewContext(logger).WithPrefix(key, infoValue) | |
17 | 17 | } |
18 | 18 | |
19 | // Debug returns a logger that includes a debug level keyval. | |
19 | // Debug returns a logger that includes a Key/DebugValue pair. | |
20 | 20 | func Debug(logger log.Logger) log.Logger { |
21 | return log.NewContext(logger).WithPrefix(levelKey, debugLevelValue) | |
21 | return log.NewContext(logger).WithPrefix(key, debugValue) | |
22 | 22 | } |
23 | 23 | |
24 | 24 | // NewFilter wraps next and implements level filtering. See the commentary on |
122 | 122 | return func(l *logger) { l.errNoLevel = err } |
123 | 123 | } |
124 | 124 | |
125 | func NewInjector(next log.Logger, level Value) log.Logger { | |
126 | return &injector{ | |
127 | next: next, | |
128 | level: level, | |
129 | } | |
130 | } | |
131 | ||
132 | type injector struct { | |
133 | next log.Logger | |
134 | level interface{} | |
135 | } | |
136 | ||
137 | func (l *injector) Log(keyvals ...interface{}) error { | |
138 | for i := 1; i < len(keyvals); i += 2 { | |
139 | if _, ok := keyvals[i].(*levelValue); ok { | |
140 | return l.next.Log(keyvals...) | |
141 | } | |
142 | } | |
143 | kvs := make([]interface{}, len(keyvals)+2) | |
144 | kvs[0], kvs[1] = key, l.level | |
145 | copy(kvs[2:], keyvals) | |
146 | return l.next.Log(kvs...) | |
147 | } | |
148 | ||
149 | type Value interface { | |
150 | String() string | |
151 | levelVal() | |
152 | } | |
153 | ||
154 | func Key() interface{} { return key } | |
155 | func ErrorValue() Value { return errorValue } | |
156 | func WarnValue() Value { return warnValue } | |
157 | func InfoValue() Value { return infoValue } | |
158 | func DebugValue() Value { return debugValue } | |
159 | ||
125 | 160 | var ( |
126 | levelKey interface{} = "level" | |
127 | errorLevelValue = &levelValue{level: levelError, name: "error"} | |
128 | warnLevelValue = &levelValue{level: levelWarn, name: "warn"} | |
129 | infoLevelValue = &levelValue{level: levelInfo, name: "info"} | |
130 | debugLevelValue = &levelValue{level: levelDebug, name: "debug"} | |
161 | key interface{} = "level" | |
162 | errorValue = &levelValue{level: levelError, name: "error"} | |
163 | warnValue = &levelValue{level: levelWarn, name: "warn"} | |
164 | infoValue = &levelValue{level: levelInfo, name: "info"} | |
165 | debugValue = &levelValue{level: levelDebug, name: "debug"} | |
131 | 166 | ) |
132 | 167 | |
133 | 168 | type level byte |
144 | 179 | level |
145 | 180 | } |
146 | 181 | |
147 | func (v *levelValue) String() string { | |
148 | return v.name | |
149 | } | |
182 | func (v *levelValue) String() string { return v.name } | |
183 | func (v *levelValue) levelVal() {} |
11 | 11 | ) |
12 | 12 | |
13 | 13 | func TestVariousLevels(t *testing.T) { |
14 | for _, testcase := range []struct { | |
14 | testCases := []struct { | |
15 | name string | |
15 | 16 | allowed level.Option |
16 | 17 | want string |
17 | 18 | }{ |
18 | 19 | { |
20 | "AllowAll", | |
19 | 21 | level.AllowAll(), |
20 | 22 | strings.Join([]string{ |
21 | 23 | `{"level":"debug","this is":"debug log"}`, |
25 | 27 | }, "\n"), |
26 | 28 | }, |
27 | 29 | { |
30 | "AllowDebug", | |
28 | 31 | level.AllowDebug(), |
29 | 32 | strings.Join([]string{ |
30 | 33 | `{"level":"debug","this is":"debug log"}`, |
34 | 37 | }, "\n"), |
35 | 38 | }, |
36 | 39 | { |
40 | "AllowDebug", | |
37 | 41 | level.AllowInfo(), |
38 | 42 | strings.Join([]string{ |
39 | 43 | `{"level":"info","this is":"info log"}`, |
42 | 46 | }, "\n"), |
43 | 47 | }, |
44 | 48 | { |
49 | "AllowWarn", | |
45 | 50 | level.AllowWarn(), |
46 | 51 | strings.Join([]string{ |
47 | 52 | `{"level":"warn","this is":"warn log"}`, |
49 | 54 | }, "\n"), |
50 | 55 | }, |
51 | 56 | { |
57 | "AllowError", | |
52 | 58 | level.AllowError(), |
53 | 59 | strings.Join([]string{ |
54 | 60 | `{"level":"error","this is":"error log"}`, |
55 | 61 | }, "\n"), |
56 | 62 | }, |
57 | 63 | { |
64 | "AllowNone", | |
58 | 65 | level.AllowNone(), |
59 | 66 | ``, |
60 | 67 | }, |
61 | } { | |
62 | var buf bytes.Buffer | |
63 | logger := level.NewFilter(log.NewJSONLogger(&buf), testcase.allowed) | |
64 | ||
65 | level.Debug(logger).Log("this is", "debug log") | |
66 | level.Info(logger).Log("this is", "info log") | |
67 | level.Warn(logger).Log("this is", "warn log") | |
68 | level.Error(logger).Log("this is", "error log") | |
69 | ||
70 | if want, have := testcase.want, strings.TrimSpace(buf.String()); want != have { | |
71 | t.Errorf("given Allowed=%v: want\n%s\nhave\n%s", testcase.allowed, want, have) | |
72 | } | |
68 | } | |
69 | ||
70 | for _, tc := range testCases { | |
71 | t.Run(tc.name, func(t *testing.T) { | |
72 | var buf bytes.Buffer | |
73 | logger := level.NewFilter(log.NewJSONLogger(&buf), tc.allowed) | |
74 | ||
75 | level.Debug(logger).Log("this is", "debug log") | |
76 | level.Info(logger).Log("this is", "info log") | |
77 | level.Warn(logger).Log("this is", "warn log") | |
78 | level.Error(logger).Log("this is", "error log") | |
79 | ||
80 | if want, have := tc.want, strings.TrimSpace(buf.String()); want != have { | |
81 | t.Errorf("\nwant:\n%s\nhave:\n%s", want, have) | |
82 | } | |
83 | }) | |
73 | 84 | } |
74 | 85 | } |
75 | 86 | |
135 | 146 | logger = log.NewContext(logger).With("caller", log.DefaultCaller) |
136 | 147 | |
137 | 148 | level.Info(logger).Log("foo", "bar") |
138 | if want, have := `level=info caller=level_test.go:138 foo=bar`, strings.TrimSpace(buf.String()); want != have { | |
149 | if want, have := `level=info caller=level_test.go:149 foo=bar`, strings.TrimSpace(buf.String()); want != have { | |
139 | 150 | t.Errorf("\nwant '%s'\nhave '%s'", want, have) |
140 | 151 | } |
141 | 152 | } |
151 | 162 | logger = level.NewFilter(logger, level.AllowAll()) |
152 | 163 | |
153 | 164 | level.Info(logger).Log("foo", "bar") |
154 | if want, have := `caller=level_test.go:154 level=info foo=bar`, strings.TrimSpace(buf.String()); want != have { | |
165 | if want, have := `caller=level_test.go:165 level=info foo=bar`, strings.TrimSpace(buf.String()); want != have { | |
155 | 166 | t.Errorf("\nwant '%s'\nhave '%s'", want, have) |
156 | 167 | } |
157 | 168 | } |
186 | 197 | }) |
187 | 198 | } |
188 | 199 | } |
200 | ||
201 | func TestInjector(t *testing.T) { | |
202 | var ( | |
203 | output []interface{} | |
204 | logger log.Logger | |
205 | ) | |
206 | ||
207 | logger = log.LoggerFunc(func(keyvals ...interface{}) error { | |
208 | output = keyvals | |
209 | return nil | |
210 | }) | |
211 | logger = level.NewInjector(logger, level.InfoValue()) | |
212 | ||
213 | logger.Log("foo", "bar") | |
214 | if got, want := len(output), 4; got != want { | |
215 | t.Errorf("missing level not injected: got len==%d, want len==%d", got, want) | |
216 | } | |
217 | ||
218 | level.Error(logger).Log("foo", "bar") | |
219 | if got, want := len(output), 4; got != want { | |
220 | t.Errorf("leveled record modified: got len==%d, want len==%d", got, want) | |
221 | } | |
222 | } |