Package list golang-github-go-kit-kit / be51b44
log: Reimplement experimental_level package with a bit set. Chris Hines 4 years ago
5 changed file(s) with 144 addition(s) and 128 deletion(s). Raw diff Collapse all Expand all
4343 return l
4444 }},
4545 {"DisallowedLevel", func(l log.Logger) log.Logger {
46 return level.New(l, level.Allowed(level.AllowInfoAndAbove()))
46 return level.NewFilter(l, level.AllowInfoAndAbove())
4747 }},
4848 {"AllowedLevel", func(l log.Logger) log.Logger {
49 return level.New(l, level.Allowed(level.AllowAll()))
49 return level.NewFilter(l, level.AllowAll())
5050 }},
5151 }
5252
11 // definitely have breaking changes and may be deleted altogether. Be warned!
22 //
33 // To use the level package, create a logger as per normal in your func main,
4 // and wrap it with level.New.
4 // and wrap it with level.NewFilter.
55 //
66 // var logger log.Logger
77 // logger = log.NewLogfmtLogger(os.Stderr)
8 // logger = level.New(logger, level.Allowed(level.AllowInfoAndAbove())) // <--
8 // logger = level.NewFilter(logger, level.AllowInfoAndAbove()) // <--
99 // logger = log.NewContext(logger).With("ts", log.DefaultTimestampUTC)
1010 //
1111 // Then, at the callsites, use one of the level.Debug, Info, Warn, or Error
1818 // }
1919 //
2020 // The leveled logger allows precise control over what should happen if a log
21 // event is emitted without a level key, or if a squelched level is used. Check
22 // the Option functions for details. And, you can easily use non-default level
23 // values: create new string constants for whatever you want to change, pass
24 // them explicitly to the Allowed Option function, and write your own level.Foo-style
25 // helper methods.
21 // event is emitted without a level key, or if a squelched level is used.
22 // Check the Option functions for details.
2623 package level
0 package level_test
1
2 import (
3 "errors"
4 "os"
5
6 "github.com/go-kit/kit/log"
7 "github.com/go-kit/kit/log/experimental_level"
8 )
9
10 func Example_basic() {
11 // setup logger with level filter
12 logger := log.NewLogfmtLogger(os.Stdout)
13 logger = level.NewFilter(logger, level.AllowInfoAndAbove())
14 logger = log.NewContext(logger).With("caller", log.DefaultCaller)
15
16 // use level helpers to log at different levels
17 level.Error(logger).Log("err", errors.New("bad data"))
18 level.Info(logger).Log("event", "data saved")
19 level.Debug(logger).Log("next item", 17) // filtered
20
21 // Output:
22 // level=error caller=example_test.go:18 err="bad data"
23 // level=info caller=example_test.go:19 event="data saved"
24 }
00 package level
11
2 import (
3 "github.com/go-kit/kit/log"
4 )
2 import "github.com/go-kit/kit/log"
53
6 var (
7 levelKey = "level"
8 errorLevelValue = "error"
9 warnLevelValue = "warn"
10 infoLevelValue = "info"
11 debugLevelValue = "debug"
12 )
13
14 // AllowAll is an alias for AllowDebugAndAbove.
15 func AllowAll() []string {
16 return AllowDebugAndAbove()
17 }
18
19 // AllowDebugAndAbove allows all of the four default log levels.
20 // Its return value may be provided with the Allowed Option.
21 func AllowDebugAndAbove() []string {
22 return []string{errorLevelValue, warnLevelValue, infoLevelValue, debugLevelValue}
23 }
24
25 // AllowInfoAndAbove allows the default info, warn, and error log levels.
26 // Its return value may be provided with the Allowed Option.
27 func AllowInfoAndAbove() []string {
28 return []string{errorLevelValue, warnLevelValue, infoLevelValue}
29 }
30
31 // AllowWarnAndAbove allows the default warn and error log levels.
32 // Its return value may be provided with the Allowed Option.
33 func AllowWarnAndAbove() []string {
34 return []string{errorLevelValue, warnLevelValue}
35 }
36
37 // AllowErrorOnly allows only the default error log level.
38 // Its return value may be provided with the Allowed Option.
39 func AllowErrorOnly() []string {
40 return []string{errorLevelValue}
41 }
42
43 // AllowNone allows none of the default log levels.
44 // Its return value may be provided with the Allowed Option.
45 func AllowNone() []string {
46 return []string{}
47 }
48
49 // Error returns a logger with the level key set to ErrorLevelValue.
4 // Error returns a logger that includes an error level keyval.
505 func Error(logger log.Logger) log.Logger {
516 return log.NewContext(logger).WithPrefix(levelKey, errorLevelValue)
527 }
538
54 // Warn returns a logger with the level key set to WarnLevelValue.
9 // Warn returns a logger that includes a warn level keyval.
5510 func Warn(logger log.Logger) log.Logger {
5611 return log.NewContext(logger).WithPrefix(levelKey, warnLevelValue)
5712 }
5813
59 // Info returns a logger with the level key set to InfoLevelValue.
14 // Info returns a logger that includes an info level keyval.
6015 func Info(logger log.Logger) log.Logger {
6116 return log.NewContext(logger).WithPrefix(levelKey, infoLevelValue)
6217 }
6318
64 // Debug returns a logger with the level key set to DebugLevelValue.
19 // Debug returns a logger that includes a debug level keyval.
6520 func Debug(logger log.Logger) log.Logger {
6621 return log.NewContext(logger).WithPrefix(levelKey, debugLevelValue)
6722 }
6823
69 // New wraps the logger and implements level checking. See the commentary on the
70 // Option functions for a detailed description of how to configure levels.
71 // If no options are provided, all leveled log events created with level.Debug,
72 // Info, Warn or Error helper methods will be squelched.
73 func New(next log.Logger, options ...Option) log.Logger {
74 l := logger{
24 // NewFilter wraps next and implements level filtering. See the commentary on
25 // the Option functions for a detailed description of how to configure levels.
26 // If no options are provided, all leveled log events created with Debug,
27 // Info, Warn or Error helper methods are squelched and non-leveled log
28 // events are passed to next unmodified.
29 func NewFilter(next log.Logger, options ...Option) log.Logger {
30 l := &logger{
7531 next: next,
7632 }
7733 for _, option := range options {
78 option(&l)
34 option(l)
7935 }
80 return &l
36 return l
8137 }
82
83 // Allowed enumerates the accepted log levels. If a log event is encountered
84 // with a level key set to a value that isn't explicitly allowed, the event
85 // will be squelched, and ErrNotAllowed returned.
86 func Allowed(allowed []string) Option {
87 return func(l *logger) { l.allowed = makeSet(allowed) }
88 }
89
90 // ErrNoLevel is returned to the caller when SquelchNoLevel is true, and Log
91 // is invoked without a level key. By default, ErrNoLevel is nil; in this
92 // case, the log event is squelched with no error.
93 func ErrNotAllowed(err error) Option {
94 return func(l *logger) { l.errNotAllowed = err }
95 }
96
97 // SquelchNoLevel will squelch log events with no level key, so that they
98 // don't proceed through to the wrapped logger. If SquelchNoLevel is set to
99 // true and a log event is squelched in this way, ErrNoLevel is returned to
100 // the caller.
101 func SquelchNoLevel(squelch bool) Option {
102 return func(l *logger) { l.squelchNoLevel = squelch }
103 }
104
105 // ErrNoLevel is returned to the caller when SquelchNoLevel is true, and Log
106 // is invoked without a level key. By default, ErrNoLevel is nil; in this
107 // case, the log event is squelched with no error.
108 func ErrNoLevel(err error) Option {
109 return func(l *logger) { l.errNoLevel = err }
110 }
111
112 // Option sets a parameter for the leveled logger.
113 type Option func(*logger)
11438
11539 type logger struct {
11640 next log.Logger
117 allowed map[string]struct{}
41 allowed level
42 squelchNoLevel bool
11843 errNotAllowed error
119 squelchNoLevel bool
12044 errNoLevel error
12145 }
12246
12347 func (l *logger) Log(keyvals ...interface{}) error {
12448 var hasLevel, levelAllowed bool
125 for i := 0; i < len(keyvals); i += 2 {
126 if k, ok := keyvals[i].(string); !ok || k != levelKey {
127 continue
49 for i := 1; i < len(keyvals); i += 2 {
50 if v, ok := keyvals[i].(*levelValue); ok {
51 hasLevel = true
52 levelAllowed = l.allowed&v.level != 0
53 break
12854 }
129 hasLevel = true
130 if i >= len(keyvals) {
131 continue
132 }
133 v, ok := keyvals[i+1].(string)
134 if !ok {
135 continue
136 }
137 _, levelAllowed = l.allowed[v]
138 break
13955 }
14056 if !hasLevel && l.squelchNoLevel {
14157 return l.errNoLevel
14662 return l.next.Log(keyvals...)
14763 }
14864
149 func makeSet(a []string) map[string]struct{} {
150 m := make(map[string]struct{}, len(a))
151 for _, s := range a {
152 m[s] = struct{}{}
153 }
154 return m
65 // Option sets a parameter for the leveled logger.
66 type Option func(*logger)
67
68 // AllowAll is an alias for AllowDebugAndAbove.
69 func AllowAll() Option {
70 return AllowDebugAndAbove()
15571 }
72
73 // AllowDebugAndAbove allows all of the four default log levels.
74 func AllowDebugAndAbove() Option {
75 return allowed(levelDebug | levelInfo | levelWarn | levelError)
76 }
77
78 // AllowInfoAndAbove allows the default info, warn, and error log levels.
79 func AllowInfoAndAbove() Option {
80 return allowed(levelInfo | levelWarn | levelError)
81 }
82
83 // AllowWarnAndAbove allows the default warn and error log levels.
84 func AllowWarnAndAbove() Option {
85 return allowed(levelWarn | levelError)
86 }
87
88 // AllowErrorOnly allows only the default error log level.
89 func AllowErrorOnly() Option {
90 return allowed(levelError)
91 }
92
93 // AllowNone allows none of the default log levels.
94 func AllowNone() Option {
95 return allowed(0)
96 }
97
98 func allowed(allowed level) Option {
99 return func(l *logger) { l.allowed = allowed }
100 }
101
102 // ErrNotAllowed sets the error to return from Log when it squelches a log
103 // event below the configured filtering level. By default, ErrNotAllowed is
104 // nil; in this case, the log event is squelched with no error.
105 func ErrNotAllowed(err error) Option {
106 return func(l *logger) { l.errNotAllowed = err }
107 }
108
109 // SquelchNoLevel instructs Log to squelch log events with no level, so that
110 // they don't proceed through to the wrapped logger. If SquelchNoLevel is set
111 // to true and a log event is squelched in this way, the error value
112 // configured with ErrNoLevel is returned to the caller.
113 func SquelchNoLevel(squelch bool) Option {
114 return func(l *logger) { l.squelchNoLevel = squelch }
115 }
116
117 // ErrNoLevel sets the error to return from Log when it squelches a log event
118 // with no level. By default, ErrNoLevel is nil; in this case, the log event
119 // is squelched with no error.
120 func ErrNoLevel(err error) Option {
121 return func(l *logger) { l.errNoLevel = err }
122 }
123
124 const levelKey = "level"
125
126 var (
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"}
131 )
132
133 type level byte
134
135 const (
136 levelDebug level = 1 << iota
137 levelInfo
138 levelWarn
139 levelError
140 )
141
142 type levelValue struct {
143 name string
144 level
145 }
146
147 func (v *levelValue) String() string {
148 return v.name
149 }
1111
1212 func TestVariousLevels(t *testing.T) {
1313 for _, testcase := range []struct {
14 allowed []string
14 allowed level.Option
1515 want string
1616 }{
1717 {
5959 },
6060 } {
6161 var buf bytes.Buffer
62 logger := level.New(log.NewJSONLogger(&buf), level.Allowed(testcase.allowed))
62 logger := level.NewFilter(log.NewJSONLogger(&buf), testcase.allowed)
6363
6464 level.Debug(logger).Log("this is", "debug log")
6565 level.Info(logger).Log("this is", "info log")
7575 func TestErrNotAllowed(t *testing.T) {
7676 myError := errors.New("squelched!")
7777 opts := []level.Option{
78 level.Allowed(level.AllowWarnAndAbove()),
78 level.AllowWarnAndAbove(),
7979 level.ErrNotAllowed(myError),
8080 }
81 logger := level.New(log.NewNopLogger(), opts...)
81 logger := level.NewFilter(log.NewNopLogger(), opts...)
8282
8383 if want, have := myError, level.Info(logger).Log("foo", "bar"); want != have {
8484 t.Errorf("want %#+v, have %#+v", want, have)
9797 level.SquelchNoLevel(true),
9898 level.ErrNoLevel(myError),
9999 }
100 logger := level.New(log.NewJSONLogger(&buf), opts...)
100 logger := level.NewFilter(log.NewJSONLogger(&buf), opts...)
101101
102102 if want, have := myError, logger.Log("foo", "bar"); want != have {
103103 t.Errorf("want %v, have %v", want, have)
113113 level.SquelchNoLevel(false),
114114 level.ErrNoLevel(errors.New("I should never be returned!")),
115115 }
116 logger := level.New(log.NewJSONLogger(&buf), opts...)
116 logger := level.NewFilter(log.NewJSONLogger(&buf), opts...)
117117
118118 if want, have := error(nil), logger.Log("foo", "bar"); want != have {
119119 t.Errorf("want %v, have %v", want, have)
130130 // log.DefaultCaller as per normal.
131131 var logger log.Logger
132132 logger = log.NewLogfmtLogger(&buf)
133 logger = level.New(logger, level.Allowed(level.AllowAll()))
133 logger = level.NewFilter(logger, level.AllowAll())
134134 logger = log.NewContext(logger).With("caller", log.DefaultCaller)
135135
136136 level.Info(logger).Log("foo", "bar")
147147 var logger log.Logger
148148 logger = log.NewLogfmtLogger(&buf)
149149 logger = log.NewContext(logger).With("caller", log.Caller(5))
150 logger = level.New(logger, level.Allowed(level.AllowAll()))
150 logger = level.NewFilter(logger, level.AllowAll())
151151
152152 level.Info(logger).Log("foo", "bar")
153153 if want, have := `caller=level_test.go:153 level=info foo=bar`, strings.TrimSpace(buf.String()); want != have {