diff --git a/log/experimental_level/level.go b/log/experimental_level/level.go index 3d29f15..2aec7bc 100644 --- a/log/experimental_level/level.go +++ b/log/experimental_level/level.go @@ -2,24 +2,24 @@ import "github.com/go-kit/kit/log" -// Error returns a logger that includes an error level keyval. +// Error returns a logger that includes a Key/ErrorValue pair. func Error(logger log.Logger) log.Logger { - return log.NewContext(logger).WithPrefix(levelKey, errorLevelValue) + return log.NewContext(logger).WithPrefix(key, errorValue) } -// Warn returns a logger that includes a warn level keyval. +// Warn returns a logger that includes a Key/WarnValue pair. func Warn(logger log.Logger) log.Logger { - return log.NewContext(logger).WithPrefix(levelKey, warnLevelValue) + return log.NewContext(logger).WithPrefix(key, warnValue) } -// Info returns a logger that includes an info level keyval. +// Info returns a logger that includes a Key/InfoValue pair. func Info(logger log.Logger) log.Logger { - return log.NewContext(logger).WithPrefix(levelKey, infoLevelValue) + return log.NewContext(logger).WithPrefix(key, infoValue) } -// Debug returns a logger that includes a debug level keyval. +// Debug returns a logger that includes a Key/DebugValue pair. func Debug(logger log.Logger) log.Logger { - return log.NewContext(logger).WithPrefix(levelKey, debugLevelValue) + return log.NewContext(logger).WithPrefix(key, debugValue) } // NewFilter wraps next and implements level filtering. See the commentary on @@ -123,12 +123,47 @@ return func(l *logger) { l.errNoLevel = err } } +func NewInjector(next log.Logger, level Value) log.Logger { + return &injector{ + next: next, + level: level, + } +} + +type injector struct { + next log.Logger + level interface{} +} + +func (l *injector) Log(keyvals ...interface{}) error { + for i := 1; i < len(keyvals); i += 2 { + if _, ok := keyvals[i].(*levelValue); ok { + return l.next.Log(keyvals...) + } + } + kvs := make([]interface{}, len(keyvals)+2) + kvs[0], kvs[1] = key, l.level + copy(kvs[2:], keyvals) + return l.next.Log(kvs...) +} + +type Value interface { + String() string + levelVal() +} + +func Key() interface{} { return key } +func ErrorValue() Value { return errorValue } +func WarnValue() Value { return warnValue } +func InfoValue() Value { return infoValue } +func DebugValue() Value { return debugValue } + var ( - levelKey interface{} = "level" - errorLevelValue = &levelValue{level: levelError, name: "error"} - warnLevelValue = &levelValue{level: levelWarn, name: "warn"} - infoLevelValue = &levelValue{level: levelInfo, name: "info"} - debugLevelValue = &levelValue{level: levelDebug, name: "debug"} + key interface{} = "level" + errorValue = &levelValue{level: levelError, name: "error"} + warnValue = &levelValue{level: levelWarn, name: "warn"} + infoValue = &levelValue{level: levelInfo, name: "info"} + debugValue = &levelValue{level: levelDebug, name: "debug"} ) type level byte @@ -145,6 +180,5 @@ level } -func (v *levelValue) String() string { - return v.name -} +func (v *levelValue) String() string { return v.name } +func (v *levelValue) levelVal() {} diff --git a/log/experimental_level/level_test.go b/log/experimental_level/level_test.go index b173072..8027daa 100644 --- a/log/experimental_level/level_test.go +++ b/log/experimental_level/level_test.go @@ -12,11 +12,13 @@ ) func TestVariousLevels(t *testing.T) { - for _, testcase := range []struct { + testCases := []struct { + name string allowed level.Option want string }{ { + "AllowAll", level.AllowAll(), strings.Join([]string{ `{"level":"debug","this is":"debug log"}`, @@ -26,6 +28,7 @@ }, "\n"), }, { + "AllowDebug", level.AllowDebug(), strings.Join([]string{ `{"level":"debug","this is":"debug log"}`, @@ -35,6 +38,7 @@ }, "\n"), }, { + "AllowDebug", level.AllowInfo(), strings.Join([]string{ `{"level":"info","this is":"info log"}`, @@ -43,6 +47,7 @@ }, "\n"), }, { + "AllowWarn", level.AllowWarn(), strings.Join([]string{ `{"level":"warn","this is":"warn log"}`, @@ -50,27 +55,33 @@ }, "\n"), }, { + "AllowError", level.AllowError(), strings.Join([]string{ `{"level":"error","this is":"error log"}`, }, "\n"), }, { + "AllowNone", level.AllowNone(), ``, }, - } { - var buf bytes.Buffer - logger := level.NewFilter(log.NewJSONLogger(&buf), testcase.allowed) - - level.Debug(logger).Log("this is", "debug log") - level.Info(logger).Log("this is", "info log") - level.Warn(logger).Log("this is", "warn log") - level.Error(logger).Log("this is", "error log") - - if want, have := testcase.want, strings.TrimSpace(buf.String()); want != have { - t.Errorf("given Allowed=%v: want\n%s\nhave\n%s", testcase.allowed, want, have) - } + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var buf bytes.Buffer + logger := level.NewFilter(log.NewJSONLogger(&buf), tc.allowed) + + level.Debug(logger).Log("this is", "debug log") + level.Info(logger).Log("this is", "info log") + level.Warn(logger).Log("this is", "warn log") + level.Error(logger).Log("this is", "error log") + + if want, have := tc.want, strings.TrimSpace(buf.String()); want != have { + t.Errorf("\nwant:\n%s\nhave:\n%s", want, have) + } + }) } } @@ -136,7 +147,7 @@ logger = log.NewContext(logger).With("caller", log.DefaultCaller) level.Info(logger).Log("foo", "bar") - if want, have := `level=info caller=level_test.go:138 foo=bar`, strings.TrimSpace(buf.String()); want != have { + if want, have := `level=info caller=level_test.go:149 foo=bar`, strings.TrimSpace(buf.String()); want != have { t.Errorf("\nwant '%s'\nhave '%s'", want, have) } } @@ -152,7 +163,7 @@ logger = level.NewFilter(logger, level.AllowAll()) level.Info(logger).Log("foo", "bar") - if want, have := `caller=level_test.go:154 level=info foo=bar`, strings.TrimSpace(buf.String()); want != have { + if want, have := `caller=level_test.go:165 level=info foo=bar`, strings.TrimSpace(buf.String()); want != have { t.Errorf("\nwant '%s'\nhave '%s'", want, have) } } @@ -187,3 +198,26 @@ }) } } + +func TestInjector(t *testing.T) { + var ( + output []interface{} + logger log.Logger + ) + + logger = log.LoggerFunc(func(keyvals ...interface{}) error { + output = keyvals + return nil + }) + logger = level.NewInjector(logger, level.InfoValue()) + + logger.Log("foo", "bar") + if got, want := len(output), 4; got != want { + t.Errorf("missing level not injected: got len==%d, want len==%d", got, want) + } + + level.Error(logger).Log("foo", "bar") + if got, want := len(output), 4; got != want { + t.Errorf("leveled record modified: got len==%d, want len==%d", got, want) + } +}