Codebase list golang-github-go-kit-kit / 09ecc88
log/experimental_level: initial functionality Peter Bourgon 7 years ago
2 changed file(s) with 266 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 package level
1
2 import (
3 "github.com/go-kit/kit/log"
4 )
5
6 var (
7 // LevelKey is the key part of a level keyval.
8 LevelKey = "level"
9
10 // ErrorLevelValue is the val part of an error-level keyval.
11 ErrorLevelValue = "error"
12
13 // WarnLevelValue is the val part of a warn-level keyval.
14 WarnLevelValue = "warn"
15
16 // InfoLevelValue is the val part of an info-level keyval.
17 InfoLevelValue = "info"
18
19 // DebugLevelValue is the val part of a debug-level keyval.
20 DebugLevelValue = "debug"
21 )
22
23 var (
24 // AllowAll is an alias for AllowDebugAndAbove.
25 AllowAll = AllowDebugAndAbove
26
27 // AllowDebugAndAbove allows all of the four default log levels.
28 // It may be provided as the Allowed parameter in the Config struct.
29 AllowDebugAndAbove = []string{ErrorLevelValue, WarnLevelValue, InfoLevelValue, DebugLevelValue}
30
31 // AllowInfoAndAbove allows the default info, warn, and error log levels.
32 // It may be provided as the Allowed parameter in the Config struct.
33 AllowInfoAndAbove = []string{ErrorLevelValue, WarnLevelValue, InfoLevelValue}
34
35 // AllowWarnAndAbove allows the default warn and error log levels.
36 // It may be provided as the Allowed parameter in the Config struct.
37 AllowWarnAndAbove = []string{ErrorLevelValue, WarnLevelValue}
38
39 // AllowErrorOnly allows only the default error log level.
40 // It may be provided as the Allowed parameter in the Config struct.
41 AllowErrorOnly = []string{ErrorLevelValue}
42
43 // AllowNone allows none of the default log levels.
44 // It may be provided as the Allowed parameter in the Config struct.
45 AllowNone = []string{}
46 )
47
48 // Error returns a logger with the LevelKey set to ErrorLevelValue.
49 func Error(logger log.Logger) log.Logger {
50 return log.NewContext(logger).With(LevelKey, ErrorLevelValue)
51 }
52
53 // Warn returns a logger with the LevelKey set to WarnLevelValue.
54 func Warn(logger log.Logger) log.Logger {
55 return log.NewContext(logger).With(LevelKey, WarnLevelValue)
56 }
57
58 // Info returns a logger with the LevelKey set to InfoLevelValue.
59 func Info(logger log.Logger) log.Logger {
60 return log.NewContext(logger).With(LevelKey, InfoLevelValue)
61 }
62
63 // Debug returns a logger with the LevelKey set to DebugLevelValue.
64 func Debug(logger log.Logger) log.Logger {
65 return log.NewContext(logger).With(LevelKey, DebugLevelValue)
66 }
67
68 // Config parameterizes the leveled logger.
69 type Config struct {
70 // Allowed enumerates the accepted log levels. If a log event is encountered
71 // with a LevelKey set to a value that isn't explicitly allowed, the event
72 // will be equelched, and ErrSquelched returned.
73 Allowed []string
74
75 // ErrSquelched is returned to the caller when Log is invoked with a
76 // LevelKey that hasn't been explicitly allowed. By default, ErrSquelched is
77 // nil; in this case, the log event is squelched with no error.
78 ErrSquelched error
79
80 // AllowNoLevel will allow log events with no LevelKey to proceed through to
81 // the wrapped logger without error. By default, log events with no LevelKey
82 // will be squelched, and ErrNoLevel returned.
83 AllowNoLevel bool
84
85 // ErrNoLevel is returned to the caller when AllowNoLevel is false, and Log
86 // is invoked without a LevelKey. By default, ErrNoLevel is nil; in this
87 // case, the log event is squelched with no error.
88 ErrNoLevel error
89 }
90
91 // New wraps the logger and implements level checking. See the commentary on the
92 // Config object for a detailed description of how to configure levels.
93 func New(next log.Logger, config Config) log.Logger {
94 return &logger{
95 next: next,
96 allowed: makeSet(config.Allowed),
97 errSquelched: config.ErrSquelched,
98 allowNoLevel: config.AllowNoLevel,
99 errNoLevel: config.ErrNoLevel,
100 }
101 }
102
103 type logger struct {
104 next log.Logger
105 allowed map[string]struct{}
106 errSquelched error
107 allowNoLevel bool
108 errNoLevel error
109 }
110
111 func (l *logger) Log(keyvals ...interface{}) error {
112 var hasLevel, levelAllowed bool
113 for i := 0; i < len(keyvals); i += 2 {
114 if k, ok := keyvals[i].(string); !ok || k != LevelKey {
115 continue
116 }
117 hasLevel = true
118 if i >= len(keyvals) {
119 continue
120 }
121 v, ok := keyvals[i+1].(string)
122 if !ok {
123 continue
124 }
125 _, levelAllowed = l.allowed[v]
126 break
127 }
128 if !hasLevel && !l.allowNoLevel {
129 return l.errNoLevel
130 }
131 if hasLevel && !levelAllowed {
132 return l.errSquelched
133 }
134 return l.next.Log(keyvals...)
135 }
136
137 func makeSet(a []string) map[string]struct{} {
138 m := make(map[string]struct{}, len(a))
139 for _, s := range a {
140 m[s] = struct{}{}
141 }
142 return m
143 }
0 package level_test
1
2 import (
3 "bytes"
4 "errors"
5 "strings"
6 "testing"
7
8 "github.com/go-kit/kit/log"
9 "github.com/go-kit/kit/log/experimental_level"
10 )
11
12 func TestVariousLevels(t *testing.T) {
13 for _, testcase := range []struct {
14 allowed []string
15 want string
16 }{
17 {
18 level.AllowAll,
19 strings.Join([]string{
20 `{"level":"debug","this is":"debug log"}`,
21 `{"level":"info","this is":"info log"}`,
22 `{"level":"warn","this is":"warn log"}`,
23 `{"level":"error","this is":"error log"}`,
24 }, "\n"),
25 },
26 {
27 level.AllowDebugAndAbove,
28 strings.Join([]string{
29 `{"level":"debug","this is":"debug log"}`,
30 `{"level":"info","this is":"info log"}`,
31 `{"level":"warn","this is":"warn log"}`,
32 `{"level":"error","this is":"error log"}`,
33 }, "\n"),
34 },
35 {
36 level.AllowInfoAndAbove,
37 strings.Join([]string{
38 `{"level":"info","this is":"info log"}`,
39 `{"level":"warn","this is":"warn log"}`,
40 `{"level":"error","this is":"error log"}`,
41 }, "\n"),
42 },
43 {
44 level.AllowWarnAndAbove,
45 strings.Join([]string{
46 `{"level":"warn","this is":"warn log"}`,
47 `{"level":"error","this is":"error log"}`,
48 }, "\n"),
49 },
50 {
51 level.AllowErrorOnly,
52 strings.Join([]string{
53 `{"level":"error","this is":"error log"}`,
54 }, "\n"),
55 },
56 {
57 level.AllowNone,
58 ``,
59 },
60 } {
61 var buf bytes.Buffer
62 logger := level.New(log.NewJSONLogger(&buf), level.Config{Allowed: testcase.allowed})
63
64 level.Debug(logger).Log("this is", "debug log")
65 level.Info(logger).Log("this is", "info log")
66 level.Warn(logger).Log("this is", "warn log")
67 level.Error(logger).Log("this is", "error log")
68
69 if want, have := testcase.want, strings.TrimSpace(buf.String()); want != have {
70 t.Errorf("given Allowed=%v: want\n%s\nhave\n%s", testcase.allowed, want, have)
71 }
72 }
73 }
74
75 func TestErrSquelch(t *testing.T) {
76 myError := errors.New("squelched!")
77 logger := level.New(log.NewNopLogger(), level.Config{
78 Allowed: level.AllowWarnAndAbove,
79 ErrSquelched: myError,
80 })
81
82 if want, have := myError, level.Info(logger).Log("foo", "bar"); want != have {
83 t.Errorf("want %#+v, have %#+v", want, have)
84 }
85
86 if want, have := error(nil), level.Warn(logger).Log("foo", "bar"); want != have {
87 t.Errorf("want %#+v, have %#+v", want, have)
88 }
89 }
90
91 func TestErrNoLevel(t *testing.T) {
92 myError := errors.New("no level specified")
93
94 var buf bytes.Buffer
95 logger := level.New(log.NewJSONLogger(&buf), level.Config{
96 AllowNoLevel: false,
97 ErrNoLevel: myError,
98 })
99
100 if want, have := myError, logger.Log("foo", "bar"); want != have {
101 t.Errorf("want %v, have %v", want, have)
102 }
103 if want, have := ``, strings.TrimSpace(buf.String()); want != have {
104 t.Errorf("want %q, have %q", want, have)
105 }
106 }
107
108 func TestAllowNoLevel(t *testing.T) {
109 var buf bytes.Buffer
110 logger := level.New(log.NewJSONLogger(&buf), level.Config{
111 AllowNoLevel: true,
112 ErrNoLevel: errors.New("I should never be returned!"),
113 })
114
115 if want, have := error(nil), logger.Log("foo", "bar"); want != have {
116 t.Errorf("want %v, have %v", want, have)
117 }
118 if want, have := `{"foo":"bar"}`, strings.TrimSpace(buf.String()); want != have {
119 t.Errorf("want %q, have %q", want, have)
120 }
121 }