diff --git a/benchmark_test.go b/benchmark_test.go index d3695b8..126bfa5 100644 --- a/benchmark_test.go +++ b/benchmark_test.go @@ -7,7 +7,7 @@ import ( ) func benchmarkRunner(b *testing.B, logger log.Logger, f func(log.Logger)) { - lc := log.NewContext(logger).With("common_key", "common_value") + lc := log.With(logger, "common_key", "common_value") b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { @@ -17,5 +17,5 @@ func benchmarkRunner(b *testing.B, logger log.Logger, f func(log.Logger)) { var ( baseMessage = func(logger log.Logger) { logger.Log("foo_key", "foo_value") } - withMessage = func(logger log.Logger) { log.NewContext(logger).With("a", "b").Log("c", "d") } + withMessage = func(logger log.Logger) { log.With(logger, "a", "b").Log("c", "d") } ) diff --git a/deprecated_levels/levels.go b/deprecated_levels/levels.go index da6b681..a034212 100644 --- a/deprecated_levels/levels.go +++ b/deprecated_levels/levels.go @@ -7,7 +7,7 @@ import "github.com/go-kit/kit/log" // want a different set of levels, you can create your own levels type very // easily, and you can elide the configuration. type Levels struct { - ctx *log.Context + logger log.Logger levelKey string // We have a choice between storing level values in string fields or @@ -34,7 +34,7 @@ type Levels struct { // New creates a new leveled logger, wrapping the passed logger. func New(logger log.Logger, options ...Option) Levels { l := Levels{ - ctx: log.NewContext(logger), + logger: logger, levelKey: "level", debugValue: "debug", @@ -52,7 +52,7 @@ func New(logger log.Logger, options ...Option) Levels { // With returns a new leveled logger that includes keyvals in all log events. func (l Levels) With(keyvals ...interface{}) Levels { return Levels{ - ctx: l.ctx.With(keyvals...), + logger: log.With(l.logger, keyvals...), levelKey: l.levelKey, debugValue: l.debugValue, infoValue: l.infoValue, @@ -64,27 +64,27 @@ func (l Levels) With(keyvals ...interface{}) Levels { // Debug returns a debug level logger. func (l Levels) Debug() log.Logger { - return l.ctx.WithPrefix(l.levelKey, l.debugValue) + return log.WithPrefix(l.logger, l.levelKey, l.debugValue) } // Info returns an info level logger. func (l Levels) Info() log.Logger { - return l.ctx.WithPrefix(l.levelKey, l.infoValue) + return log.WithPrefix(l.logger, l.levelKey, l.infoValue) } // Warn returns a warning level logger. func (l Levels) Warn() log.Logger { - return l.ctx.WithPrefix(l.levelKey, l.warnValue) + return log.WithPrefix(l.logger, l.levelKey, l.warnValue) } // Error returns an error level logger. func (l Levels) Error() log.Logger { - return l.ctx.WithPrefix(l.levelKey, l.errorValue) + return log.WithPrefix(l.logger, l.levelKey, l.errorValue) } // Crit returns a critical level logger. func (l Levels) Crit() log.Logger { - return l.ctx.WithPrefix(l.levelKey, l.critValue) + return log.WithPrefix(l.logger, l.levelKey, l.critValue) } // Option sets a parameter for leveled loggers. diff --git a/doc.go b/doc.go index ad1128e..bb92e57 100644 --- a/doc.go +++ b/doc.go @@ -42,7 +42,7 @@ // resulting log output. We can use a context to improve the RunTask example. // // func RunTask(task Task, logger log.Logger) string { -// logger = log.NewContext(logger).With("taskID", task.ID) +// logger = log.With(logger, "taskID", task.ID) // logger.Log("event", "starting task") // ... // taskHelper(task.Cmd, logger) @@ -72,7 +72,7 @@ // entries contain a timestamp and source location looks like this: // // logger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout)) -// logger = log.NewContext(logger).With("ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller) +// logger = log.With(logger, "ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller) // // Concurrent Safety // diff --git a/example_test.go b/example_test.go index 809593c..99487a6 100644 --- a/example_test.go +++ b/example_test.go @@ -43,7 +43,7 @@ func Example_context() { } RunTask := func(task Task, logger log.Logger) { - logger = log.NewContext(logger).With("taskID", task.ID) + logger = log.With(logger, "taskID", task.ID) logger.Log("event", "starting task") taskHelper(task.Cmd, logger) @@ -68,7 +68,7 @@ func Example_valuer() { return count } - logger = log.NewContext(logger).With("count", log.Valuer(counter)) + logger = log.With(logger, "count", log.Valuer(counter)) logger.Log("call", "first") logger.Log("call", "second") @@ -88,7 +88,7 @@ func Example_debugInfo() { return baseTime } - logger = log.NewContext(logger).With("time", log.Timestamp(mockTime), "caller", log.DefaultCaller) + logger = log.With(logger, "time", log.Timestamp(mockTime), "caller", log.DefaultCaller) logger.Log("call", "first") logger.Log("call", "second") diff --git a/json_logger_test.go b/json_logger_test.go index 42df70c..00e6910 100644 --- a/json_logger_test.go +++ b/json_logger_test.go @@ -13,7 +13,7 @@ func TestJSONLoggerCaller(t *testing.T) { t.Parallel() buf := &bytes.Buffer{} logger := log.NewJSONLogger(buf) - logger = log.NewContext(logger).With("caller", log.DefaultCaller) + logger = log.With(logger, "caller", log.DefaultCaller) if err := logger.Log(); err != nil { t.Fatal(err) diff --git a/level/benchmark_test.go b/level/benchmark_test.go index 49ea57e..4fca6f0 100644 --- a/level/benchmark_test.go +++ b/level/benchmark_test.go @@ -17,13 +17,13 @@ func Benchmark(b *testing.B) { return l }}, {"TimeContext", func(l log.Logger) log.Logger { - return log.NewContext(l).With("time", log.DefaultTimestampUTC) + return log.With(l, "time", log.DefaultTimestampUTC) }}, {"CallerContext", func(l log.Logger) log.Logger { - return log.NewContext(l).With("caller", log.DefaultCaller) + return log.With(l, "caller", log.DefaultCaller) }}, {"TimeCallerReqIDContext", func(l log.Logger) log.Logger { - return log.NewContext(l).With("time", log.DefaultTimestampUTC, "caller", log.DefaultCaller, "reqID", 29) + return log.With(l, "time", log.DefaultTimestampUTC, "caller", log.DefaultCaller, "reqID", 29) }}, } diff --git a/level/example_test.go b/level/example_test.go index e2d357c..fed52e5 100644 --- a/level/example_test.go +++ b/level/example_test.go @@ -12,7 +12,7 @@ func Example_basic() { // setup logger with level filter logger := log.NewLogfmtLogger(os.Stdout) logger = level.NewFilter(logger, level.AllowInfo()) - logger = log.NewContext(logger).With("caller", log.DefaultCaller) + logger = log.With(logger, "caller", log.DefaultCaller) // use level helpers to log at different levels level.Error(logger).Log("err", errors.New("bad data")) diff --git a/level/level.go b/level/level.go index 9120e61..6833b0d 100644 --- a/level/level.go +++ b/level/level.go @@ -4,22 +4,22 @@ import "github.com/go-kit/kit/log" // Error returns a logger that includes a Key/ErrorValue pair. func Error(logger log.Logger) log.Logger { - return log.NewContext(logger).WithPrefix(Key(), ErrorValue()) + return log.WithPrefix(logger, Key(), ErrorValue()) } // Warn returns a logger that includes a Key/WarnValue pair. func Warn(logger log.Logger) log.Logger { - return log.NewContext(logger).WithPrefix(Key(), WarnValue()) + return log.WithPrefix(logger, Key(), WarnValue()) } // Info returns a logger that includes a Key/InfoValue pair. func Info(logger log.Logger) log.Logger { - return log.NewContext(logger).WithPrefix(Key(), InfoValue()) + return log.WithPrefix(logger, Key(), InfoValue()) } // Debug returns a logger that includes a Key/DebugValue pair. func Debug(logger log.Logger) log.Logger { - return log.NewContext(logger).WithPrefix(Key(), DebugValue()) + return log.WithPrefix(logger, Key(), DebugValue()) } // NewFilter wraps next and implements level filtering. See the commentary on diff --git a/level/level_test.go b/level/level_test.go index 6514af4..4d2aca5 100644 --- a/level/level_test.go +++ b/level/level_test.go @@ -144,7 +144,7 @@ func TestLevelContext(t *testing.T) { var logger log.Logger logger = log.NewLogfmtLogger(&buf) logger = level.NewFilter(logger, level.AllowAll()) - logger = log.NewContext(logger).With("caller", log.DefaultCaller) + logger = log.With(logger, "caller", log.DefaultCaller) level.Info(logger).Log("foo", "bar") if want, have := `level=info caller=level_test.go:149 foo=bar`, strings.TrimSpace(buf.String()); want != have { @@ -159,7 +159,7 @@ func TestContextLevel(t *testing.T) { // to specify a higher callstack depth value. var logger log.Logger logger = log.NewLogfmtLogger(&buf) - logger = log.NewContext(logger).With("caller", log.Caller(5)) + logger = log.With(logger, "caller", log.Caller(5)) logger = level.NewFilter(logger, level.AllowAll()) level.Info(logger).Log("foo", "bar") diff --git a/log.go b/log.go index 97990fe..afe42dc 100644 --- a/log.go +++ b/log.go @@ -15,12 +15,12 @@ type Logger interface { // the missing value. var ErrMissingValue = errors.New("(MISSING)") -// NewContext returns a new Context that logs to logger. -func NewContext(logger Logger) *Context { - if c, ok := logger.(*Context); ok { +// newContext returns a new context that logs to logger. +func newContext(logger Logger) *context { + if c, ok := logger.(*context); ok { return c } - return &Context{logger: logger} + return &context{logger: logger} } // Context must always have the same number of stack frames between calls to @@ -57,11 +57,11 @@ func NewContext(logger Logger) *Context { // Context.Log through a variable with type Context. Using pointer receivers // avoids this problem. -// A Context wraps a Logger and holds keyvals that it includes in all log -// events. When logging, a Context replaces all value elements (odd indexes) +// A context wraps a Logger and holds keyvals that it includes in all log +// events. When logging, a context replaces all value elements (odd indexes) // containing a Valuer with their generated value for each call to its Log // method. -type Context struct { +type context struct { logger Logger keyvals []interface{} hasValuer bool @@ -70,7 +70,7 @@ type Context struct { // Log replaces all value elements (odd indexes) containing a Valuer in the // stored context with their generated value, appends keyvals, and passes the // result to the wrapped Logger. -func (l *Context) Log(keyvals ...interface{}) error { +func (l *context) Log(keyvals ...interface{}) error { kvs := append(l.keyvals, keyvals...) if len(kvs)%2 != 0 { kvs = append(kvs, ErrMissingValue) @@ -86,16 +86,17 @@ func (l *Context) Log(keyvals ...interface{}) error { return l.logger.Log(kvs...) } -// With returns a new Context with keyvals appended to those of the receiver. -func (l *Context) With(keyvals ...interface{}) *Context { +// With returns a new context with keyvals appended to those of the receiver. +func With(logger Logger, keyvals ...interface{}) Logger { if len(keyvals) == 0 { - return l + return logger } + l := newContext(logger) kvs := append(l.keyvals, keyvals...) if len(kvs)%2 != 0 { kvs = append(kvs, ErrMissingValue) } - return &Context{ + return &context{ logger: l.logger, // Limiting the capacity of the stored keyvals ensures that a new // backing array is created if the slice must grow in Log or With. @@ -106,12 +107,13 @@ func (l *Context) With(keyvals ...interface{}) *Context { } } -// WithPrefix returns a new Context with keyvals prepended to those of the +// WithPrefix returns a new context with keyvals prepended to those of the // receiver. -func (l *Context) WithPrefix(keyvals ...interface{}) *Context { +func WithPrefix(logger Logger, keyvals ...interface{}) Logger { if len(keyvals) == 0 { - return l + return logger } + l := newContext(logger) // Limiting the capacity of the stored keyvals ensures that a new // backing array is created if the slice must grow in Log or With. // Using the extra capacity without copying risks a data race that @@ -126,7 +128,7 @@ func (l *Context) WithPrefix(keyvals ...interface{}) *Context { kvs = append(kvs, ErrMissingValue) } kvs = append(kvs, l.keyvals...) - return &Context{ + return &context{ logger: l.logger, keyvals: kvs, hasValuer: l.hasValuer || containsValuer(keyvals), diff --git a/log_test.go b/log_test.go index 7c44095..55edcbe 100644 --- a/log_test.go +++ b/log_test.go @@ -16,10 +16,10 @@ func TestContext(t *testing.T) { logger := log.NewLogfmtLogger(buf) kvs := []interface{}{"a", 123} - lc := log.NewContext(logger).With(kvs...) + lc := log.With(logger, kvs...) kvs[1] = 0 // With should copy its key values - lc = lc.With("b", "c") // With should stack + lc = log.With(lc, "b", "c") // With should stack if err := lc.Log("msg", "message"); err != nil { t.Fatal(err) } @@ -28,7 +28,7 @@ func TestContext(t *testing.T) { } buf.Reset() - lc = lc.WithPrefix("p", "first") + lc = log.WithPrefix(lc, "p", "first") if err := lc.Log("msg", "message"); err != nil { t.Fatal(err) } @@ -45,17 +45,7 @@ func TestContextMissingValue(t *testing.T) { return nil })) - lc := log.NewContext(logger) - - lc.Log("k") - if want, have := 2, len(output); want != have { - t.Errorf("want len(output) == %v, have %v", want, have) - } - if want, have := log.ErrMissingValue, output[1]; want != have { - t.Errorf("want %#v, have %#v", want, have) - } - - lc.With("k1").WithPrefix("k0").Log("k2") + log.WithPrefix(log.With(logger, "k1"), "k0").Log("k2") if want, have := 6, len(output); want != have { t.Errorf("want len(output) == %v, have %v", want, have) } @@ -67,9 +57,7 @@ func TestContextMissingValue(t *testing.T) { } // Test that Context.Log has a consistent function stack depth when binding -// log.Valuers, regardless of how many times Context.With has been called or -// whether Context.Log is called via an interface typed variable or a concrete -// typed variable. +// log.Valuers, regardless of how many times Context.With has been called. func TestContextStackDepth(t *testing.T) { t.Parallel() fn := fmt.Sprintf("%n", stack.Caller(0)) @@ -91,32 +79,25 @@ func TestContextStackDepth(t *testing.T) { return nil }) - concrete := log.NewContext(logger).With("stack", stackValuer) - var iface log.Logger = concrete + logger = log.With(logger, "stack", stackValuer) // Call through interface to get baseline. - iface.Log("k", "v") + logger.Log("k", "v") want := output[1].(int) for len(output) < 10 { - concrete.Log("k", "v") - if have := output[1]; have != want { - t.Errorf("%d Withs: have %v, want %v", len(output)/2-1, have, want) - } - - iface.Log("k", "v") + logger.Log("k", "v") if have := output[1]; have != want { t.Errorf("%d Withs: have %v, want %v", len(output)/2-1, have, want) } - wrapped := log.NewContext(concrete) + wrapped := log.With(logger) wrapped.Log("k", "v") if have := output[1]; have != want { t.Errorf("%d Withs: have %v, want %v", len(output)/2-1, have, want) } - concrete = concrete.With("k", "v") - iface = concrete + logger = log.With(logger, "k", "v") } } @@ -140,7 +121,7 @@ func TestWithConcurrent(t *testing.T) { // With must be careful about handling slices that can grow without // copying the underlying array, so give it a challenge. - l := log.NewContext(logger).With(make([]interface{}, 0, 2)...) + l := log.With(logger, make([]interface{}, 0, 2)...) // Start logging concurrently. Each goroutine logs its id so the logger // can bucket the event counts. @@ -175,7 +156,7 @@ func BenchmarkDiscard(b *testing.B) { func BenchmarkOneWith(b *testing.B) { logger := log.NewNopLogger() - lc := log.NewContext(logger).With("k", "v") + lc := log.With(logger, "k", "v") b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { @@ -185,9 +166,9 @@ func BenchmarkOneWith(b *testing.B) { func BenchmarkTwoWith(b *testing.B) { logger := log.NewNopLogger() - lc := log.NewContext(logger).With("k", "v") + lc := log.With(logger, "k", "v") for i := 1; i < 2; i++ { - lc = lc.With("k", "v") + lc = log.With(lc, "k", "v") } b.ReportAllocs() b.ResetTimer() @@ -198,9 +179,9 @@ func BenchmarkTwoWith(b *testing.B) { func BenchmarkTenWith(b *testing.B) { logger := log.NewNopLogger() - lc := log.NewContext(logger).With("k", "v") + lc := log.With(logger, "k", "v") for i := 1; i < 10; i++ { - lc = lc.With("k", "v") + lc = log.With(lc, "k", "v") } b.ReportAllocs() b.ResetTimer() diff --git a/nop_logger_test.go b/nop_logger_test.go index 70e69cd..908ddd8 100644 --- a/nop_logger_test.go +++ b/nop_logger_test.go @@ -12,7 +12,7 @@ func TestNopLogger(t *testing.T) { if err := logger.Log("abc", 123); err != nil { t.Error(err) } - if err := log.NewContext(logger).With("def", "ghi").Log(); err != nil { + if err := log.With(logger, "def", "ghi").Log(); err != nil { t.Error(err) } } diff --git a/term/colorlogger_test.go b/term/colorlogger_test.go index 7f7b7be..c27ac59 100644 --- a/term/colorlogger_test.go +++ b/term/colorlogger_test.go @@ -56,7 +56,7 @@ func TestColorLoggerConcurrency(t *testing.T) { // copied from log/benchmark_test.go func benchmarkRunner(b *testing.B, logger log.Logger, f func(log.Logger)) { - lc := log.NewContext(logger).With("common_key", "common_value") + lc := log.With(logger, "common_key", "common_value") b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { @@ -66,7 +66,7 @@ func benchmarkRunner(b *testing.B, logger log.Logger, f func(log.Logger)) { var ( baseMessage = func(logger log.Logger) { logger.Log("foo_key", "foo_value") } - withMessage = func(logger log.Logger) { log.NewContext(logger).With("a", "b").Log("c", "d") } + withMessage = func(logger log.Logger) { log.With(logger, "a", "b").Log("c", "d") } ) // copied from log/concurrency_test.go diff --git a/value_test.go b/value_test.go index 44e6478..f67eece 100644 --- a/value_test.go +++ b/value_test.go @@ -24,7 +24,7 @@ func TestValueBinding(t *testing.T) { return now } - lc := log.NewContext(logger).With("ts", log.Timestamp(mocktime), "caller", log.DefaultCaller) + lc := log.With(logger, "ts", log.Timestamp(mocktime), "caller", log.DefaultCaller) lc.Log("foo", "bar") timestamp, ok := output[1].(time.Time) @@ -68,7 +68,7 @@ func TestValueBinding_loggingZeroKeyvals(t *testing.T) { return now } - logger = log.NewContext(logger).With("ts", log.Timestamp(mocktime)) + logger = log.With(logger, "ts", log.Timestamp(mocktime)) logger.Log() timestamp, ok := output[1].(time.Time) @@ -92,7 +92,7 @@ func TestValueBinding_loggingZeroKeyvals(t *testing.T) { func BenchmarkValueBindingTimestamp(b *testing.B) { logger := log.NewNopLogger() - lc := log.NewContext(logger).With("ts", log.DefaultTimestamp) + lc := log.With(logger, "ts", log.DefaultTimestamp) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { @@ -102,7 +102,7 @@ func BenchmarkValueBindingTimestamp(b *testing.B) { func BenchmarkValueBindingCaller(b *testing.B) { logger := log.NewNopLogger() - lc := log.NewContext(logger).With("caller", log.DefaultCaller) + lc := log.With(logger, "caller", log.DefaultCaller) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ {