From 2b49514fd5bbc6081a1d863ccbc3161d488e419b Mon Sep 17 00:00:00 2001 From: Ammar Bandukwala Date: Tue, 31 Mar 2020 13:25:08 -0500 Subject: [PATCH 01/12] Refactor for Context only logging --- README.md | 6 +- context.go | 27 ++++ example_helper_test.go | 4 +- example_marshaller_test.go | 4 +- example_test.go | 51 ++++---- export_test.go | 9 +- map.go | 2 +- map_test.go | 6 +- s.go | 17 ++- s_test.go | 9 +- slog.go | 117 ++++++++++++------ slog_test.go | 54 ++++---- sloggers/sloghuman/sloghuman.go | 6 +- sloggers/sloghuman/sloghuman_test.go | 11 +- sloggers/slogjson/slogjson.go | 6 +- sloggers/slogjson/slogjson_test.go | 6 +- sloggers/slogstackdriver/slogstackdriver.go | 8 +- .../slogstackdriver/slogstackdriver_test.go | 6 +- sloggers/slogtest/assert/assert.go | 2 +- sloggers/slogtest/t.go | 20 +-- sloggers/slogtest/t_test.go | 9 +- 21 files changed, 230 insertions(+), 150 deletions(-) create mode 100644 context.go diff --git a/README.md b/README.md index 9fd2562..50550b8 100644 --- a/README.md +++ b/README.md @@ -35,9 +35,9 @@ go get cdr.dev/slog Many more examples available at [godoc](https://godoc.org/cdr.dev/slog#pkg-examples). ```go -log := sloghuman.Make(os.Stdout) +ctx := sloghuman.Make(ctx, os.Stdout) -log.Info(context.Background(), "my message here", +slog.Info(ctx, "my message here", slog.F("field_name", "something or the other"), slog.F("some_map", slog.M( slog.F("nested_fields", time.Date(2000, time.February, 5, 4, 4, 4, 0, time.UTC)), @@ -87,6 +87,8 @@ Here is a list of reasons how we improved on zap with slog. 1. Full [context.Context](https://blog.golang.org/context) support - `slog` lets you set fields in a `context.Context` such that any log with the context prints those fields. + - `slog` stores the actual logger in the `context.Context`, following the example of + [the Go trace library](https://golang.org/pkg/runtime/trace/). Our logger doesn't bloat type and function signatures. - We wanted to be able to pull up all relevant logs for a given trace, user or request. With zap, we were plugging these fields in for every relevant log or passing around a logger with the fields set. This became very verbose. diff --git a/context.go b/context.go new file mode 100644 index 0000000..e3f20c1 --- /dev/null +++ b/context.go @@ -0,0 +1,27 @@ +package slog + +import "context" + +type loggerCtxKey = struct{} + +// SinkContext is used by slog.Make to compose many loggers together. +type SinkContext struct { + Sink + context.Context +} + +func contextWithLogger(ctx context.Context, l logger) SinkContext { + ctx = context.WithValue(ctx, loggerCtxKey{}, l) + return SinkContext{ + Context: ctx, + Sink: l, + } +} + +func loggerFromContext(ctx context.Context) (logger, bool) { + v := ctx.Value(loggerCtxKey{}) + if v == nil { + return logger{}, false + } + return v.(logger), true +} diff --git a/example_helper_test.go b/example_helper_test.go index e06d372..853341d 100644 --- a/example_helper_test.go +++ b/example_helper_test.go @@ -12,12 +12,12 @@ import ( func httpLogHelper(ctx context.Context, status int) { slog.Helper() - l.Info(ctx, "sending HTTP response", + slog.Info(ctx, "sending HTTP response", slog.F("status", status), ) } -var l = sloghuman.Make(os.Stdout) +var l = sloghuman.Make(context.Background(), os.Stdout) func ExampleHelper() { ctx := context.Background() diff --git a/example_marshaller_test.go b/example_marshaller_test.go index e4e6992..514ab62 100644 --- a/example_marshaller_test.go +++ b/example_marshaller_test.go @@ -21,9 +21,9 @@ func (s myStruct) MarshalJSON() ([]byte, error) { } func Example_marshaller() { - l := sloghuman.Make(os.Stdout) + ctx := sloghuman.Make(context.Background(), os.Stdout) - l.Info(context.Background(), "wow", + slog.Info(ctx, "wow", slog.F("myStruct", myStruct{ foo: 1, bar: 2, diff --git a/example_test.go b/example_test.go index f3577ac..3627532 100644 --- a/example_test.go +++ b/example_test.go @@ -18,14 +18,14 @@ import ( ) func Example() { - log := sloghuman.Make(os.Stdout) + ctx := sloghuman.Make(context.Background(), os.Stdout) - log.Info(context.Background(), "my message here", + slog.Info(ctx, "my message here", slog.F("field_name", "something or the other"), slog.F("some_map", slog.M( slog.F("nested_fields", time.Date(2000, time.February, 5, 4, 4, 4, 0, time.UTC)), )), - slog.Error( + slog.Err( xerrors.Errorf("wrap1: %w", xerrors.Errorf("wrap2: %w", io.EOF, @@ -45,7 +45,7 @@ func Example() { } func Example_struct() { - l := sloghuman.Make(os.Stdout) + ctx := sloghuman.Make(context.Background(), os.Stdout) type hello struct { Meow int `json:"meow"` @@ -53,7 +53,7 @@ func Example_struct() { M time.Time `json:"m"` } - l.Info(context.Background(), "check out my structure", + slog.Info(ctx, "check out my structure", slog.F("hello", hello{ Meow: 1, Bar: "barbar", @@ -76,26 +76,27 @@ func Example_testing() { } func Example_tracing() { - log := sloghuman.Make(os.Stdout) + var ctx context.Context + ctx = sloghuman.Make(context.Background(), os.Stdout) - ctx, _ := trace.StartSpan(context.Background(), "spanName") + ctx, _ = trace.StartSpan(ctx, "spanName") - log.Info(ctx, "my msg", slog.F("hello", "hi")) + slog.Info(ctx, "my msg", slog.F("hello", "hi")) // 2019-12-09 21:59:48.110 [INFO] my msg {"trace": "f143d018d00de835688453d8dc55c9fd", "span": "f214167bf550afc3", "hello": "hi"} } func Example_multiple() { - l := sloghuman.Make(os.Stdout) + ctx := sloghuman.Make(context.Background(), os.Stdout) f, err := os.OpenFile("stackdriver", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) if err != nil { - l.Fatal(context.Background(), "failed to open stackdriver log file", slog.Error(err)) + slog.Fatal(ctx, "failed to open stackdriver log file", slog.Err(err)) } - l = slog.Make(l, slogstackdriver.Make(f)) + ctx = slog.Make(l, slogstackdriver.Make(ctx, f)) - l.Info(context.Background(), "log to stdout and stackdriver") + slog.Info(ctx, "log to stdout and stackdriver") // 2019-12-07 20:59:55.790 [INFO] log to stdout and stackdriver } @@ -103,41 +104,41 @@ func Example_multiple() { func ExampleWith() { ctx := slog.With(context.Background(), slog.F("field", 1)) - l := sloghuman.Make(os.Stdout) - l.Info(ctx, "msg") + ctx = sloghuman.Make(ctx, os.Stdout) + slog.Info(ctx, "msg") // 2019-12-07 20:54:23.986 [INFO] msg {"field": 1} } func ExampleStdlib() { ctx := slog.With(context.Background(), slog.F("field", 1)) - l := slog.Stdlib(ctx, sloghuman.Make(os.Stdout)) + l := slog.Stdlib(sloghuman.Make(ctx, os.Stdout)) l.Print("msg") // 2019-12-07 20:54:23.986 [INFO] (stdlib) msg {"field": 1} } -func ExampleLogger_Named() { +func ExampleNamed() { ctx := context.Background() - l := sloghuman.Make(os.Stdout) - l = l.Named("http") - l.Info(ctx, "received request", slog.F("remote address", net.IPv4(127, 0, 0, 1))) + ctx = sloghuman.Make(ctx, os.Stdout) + ctx = slog.Named(ctx, "http") + slog.Info(ctx, "received request", slog.F("remote address", net.IPv4(127, 0, 0, 1))) // 2019-12-07 21:20:56.974 [INFO] (http) received request {"remote address": "127.0.0.1"} } -func ExampleLogger_Leveled() { +func ExampleLeveled() { ctx := context.Background() - l := sloghuman.Make(os.Stdout) - l.Debug(ctx, "testing1") - l.Info(ctx, "received request") + ctx = sloghuman.Make(ctx, os.Stdout) + slog.Debug(ctx, "testing1") + slog.Info(ctx, "received request") - l = l.Leveled(slog.LevelDebug) + ctx = slog.Leveled(ctx, slog.LevelDebug) - l.Debug(ctx, "testing2") + slog.Debug(ctx, "testing2") // 2019-12-07 21:26:20.945 [INFO] received request // 2019-12-07 21:26:20.945 [DEBUG] testing2 diff --git a/export_test.go b/export_test.go index 1266811..a682e20 100644 --- a/export_test.go +++ b/export_test.go @@ -1,5 +1,12 @@ package slog -func (l *Logger) SetExit(fn func(int)) { +import "context" + +func SetExit(ctx context.Context, fn func(int)) context.Context { + l, ok := loggerFromContext(ctx) + if !ok { + return ctx + } l.exit = fn + return contextWithLogger(ctx, l) } diff --git a/map.go b/map.go index 636f4ee..a1b594b 100644 --- a/map.go +++ b/map.go @@ -128,7 +128,7 @@ func encodeJSON(v interface{}) []byte { b, err := json.Marshal(v) if err != nil { return encode(M( - Error(xerrors.Errorf("failed to marshal to JSON: %w", err)), + Err(xerrors.Errorf("failed to marshal to JSON: %w", err)), F("type", reflect.TypeOf(v)), F("value", fmt.Sprintf("%+v", v)), )) diff --git a/map_test.go b/map_test.go index e15a6ee..03d7886 100644 --- a/map_test.go +++ b/map_test.go @@ -37,7 +37,7 @@ func TestMap(t *testing.T) { } test(t, slog.M( - slog.Error( + slog.Err( xerrors.Errorf("wrap1: %w", xerrors.Errorf("wrap2: %w", io.EOF, @@ -222,10 +222,6 @@ func TestMap(t *testing.T) { }) } -type meow struct { - a int -} - func indentJSON(t *testing.T, j string) string { b := &bytes.Buffer{} err := json.Indent(b, []byte(j), "", strings.Repeat(" ", 4)) diff --git a/s.go b/s.go index fa8917e..e85195c 100644 --- a/s.go +++ b/s.go @@ -3,6 +3,7 @@ package slog import ( "context" "log" + "os" "strings" ) @@ -15,14 +16,19 @@ import ( // You can redirect the stdlib default logger with log.SetOutput // to the Writer on the logger returned by this function. // See the example. -func Stdlib(ctx context.Context, l Logger) *log.Logger { - l.skip += 3 +func Stdlib(ctx context.Context) *log.Logger { + ctx = Named(ctx, "stdlib") - l = l.Named("stdlib") + l, ok := loggerFromContext(ctx) + if !ok { + // Give stderr logger if no slog. + return log.New(os.Stderr, "", 0) + } + l.skip += 3 + ctx = contextWithLogger(ctx, l) w := &stdlogWriter{ ctx: ctx, - l: l, } return log.New(w, "", 0) @@ -30,7 +36,6 @@ func Stdlib(ctx context.Context, l Logger) *log.Logger { type stdlogWriter struct { ctx context.Context - l Logger } func (w stdlogWriter) Write(p []byte) (n int, err error) { @@ -39,7 +44,7 @@ func (w stdlogWriter) Write(p []byte) (n int, err error) { // we do not want. msg = strings.TrimSuffix(msg, "\n") - w.l.Info(w.ctx, msg) + Info(w.ctx, msg) return len(p), nil } diff --git a/s_test.go b/s_test.go index a589460..5006c04 100644 --- a/s_test.go +++ b/s_test.go @@ -2,6 +2,7 @@ package slog_test import ( "bytes" + "context" "testing" "cdr.dev/slog" @@ -14,14 +15,16 @@ func TestStdlib(t *testing.T) { t.Parallel() b := &bytes.Buffer{} - l := slog.Make(sloghuman.Make(b)).With( + ctx := context.Background() + ctx = slog.Make(sloghuman.Make(ctx, b)) + ctx = slog.With(ctx, slog.F("hi", "we"), ) - stdlibLog := slog.Stdlib(bg, l) + stdlibLog := slog.Stdlib(ctx) stdlibLog.Println("stdlib") et, rest, err := entryhuman.StripTimestamp(b.String()) assert.Success(t, "strip timestamp", err) assert.False(t, "timestamp", et.IsZero()) - assert.Equal(t, "entry", " [INFO]\t(stdlib)\t\tstdlib\t{\"hi\": \"we\"}\n", rest) + assert.Equal(t, "entry", " [INFO]\t(stdlib)\t\tstdlib\t{\"hi\": \"we\"}\n", rest) } diff --git a/slog.go b/slog.go index a291f27..bfbecde 100644 --- a/slog.go +++ b/slog.go @@ -4,8 +4,8 @@ // // The examples are the best way to understand how to use this library effectively. // -// The Logger type implements a high level API around the Sink interface. -// Logger implements Sink as well to allow composition. +// The logger type implements a high level API around the Sink interface. +// logger implements Sink as well to allow composition. // // Implementations of the Sink interface are available in the sloggers subdirectory. package slog // import "cdr.dev/slog" @@ -21,7 +21,7 @@ import ( "go.opencensus.io/trace" ) -// Sink is the destination of a Logger. +// Sink is the destination of a logger. // // All sinks must be safe for concurrent use. type Sink interface { @@ -33,7 +33,7 @@ type Sink interface { // underlying sinks. // // It extends the entry with the set fields and names. -func (l Logger) LogEntry(ctx context.Context, e SinkEntry) { +func (l logger) LogEntry(ctx context.Context, e SinkEntry) { if e.Level < l.level { return } @@ -46,17 +46,27 @@ func (l Logger) LogEntry(ctx context.Context, e SinkEntry) { } } -// Sync calls Sync on all the underlying sinks. -func (l Logger) Sync() { +func (l logger) Sync() { for _, s := range l.sinks { s.Sync() } } -// Logger wraps Sink with a nice API to log entries. +// Sync calls Sync on all the underlying sinks. +func Sync(ctx context.Context) { + l, ok := loggerFromContext(ctx) + if !ok { + return + } + l.Sync() + return +} + +// logger wraps Sink with a nice API to log entries. // -// Logger is safe for concurrent use. -type Logger struct { +// logger is safe for concurrent use. +// It is unexported because callers should only log via a context. +type logger struct { sinks []Sink level Level @@ -68,34 +78,53 @@ type Logger struct { } // Make creates a logger that writes logs to the passed sinks at LevelInfo. -func Make(sinks ...Sink) Logger { - return Logger{ - sinks: sinks, - level: LevelInfo, - - exit: os.Exit, +func Make(ctx context.Context, sinks ...Sink) SinkContext { + // Just in case the ctx has a logger, start with it. + l, _ := loggerFromContext(ctx) + l.sinks = append(l.sinks, sinks...) + if l.level == 0 { + l.level = LevelInfo } + l.exit = os.Exit + + return contextWithLogger(ctx, l) } // Debug logs the msg and fields at LevelDebug. -func (l Logger) Debug(ctx context.Context, msg string, fields ...Field) { +func Debug(ctx context.Context, msg string, fields ...Field) { + l, ok := loggerFromContext(ctx) + if !ok { + return + } l.log(ctx, LevelDebug, msg, fields) } // Info logs the msg and fields at LevelInfo. -func (l Logger) Info(ctx context.Context, msg string, fields ...Field) { +func Info(ctx context.Context, msg string, fields ...Field) { + l, ok := loggerFromContext(ctx) + if !ok { + return + } l.log(ctx, LevelInfo, msg, fields) } // Warn logs the msg and fields at LevelWarn. -func (l Logger) Warn(ctx context.Context, msg string, fields ...Field) { +func Warn(ctx context.Context, msg string, fields ...Field) { + l, ok := loggerFromContext(ctx) + if !ok { + return + } l.log(ctx, LevelWarn, msg, fields) } // Error logs the msg and fields at LevelError. // // It will then Sync(). -func (l Logger) Error(ctx context.Context, msg string, fields ...Field) { +func Error(ctx context.Context, msg string, fields ...Field) { + l, ok := loggerFromContext(ctx) + if !ok { + return + } l.log(ctx, LevelError, msg, fields) l.Sync() } @@ -103,7 +132,11 @@ func (l Logger) Error(ctx context.Context, msg string, fields ...Field) { // Critical logs the msg and fields at LevelCritical. // // It will then Sync(). -func (l Logger) Critical(ctx context.Context, msg string, fields ...Field) { +func Critical(ctx context.Context, msg string, fields ...Field) { + l, ok := loggerFromContext(ctx) + if !ok { + return + } l.log(ctx, LevelCritical, msg, fields) l.Sync() } @@ -111,41 +144,47 @@ func (l Logger) Critical(ctx context.Context, msg string, fields ...Field) { // Fatal logs the msg and fields at LevelFatal. // // It will then Sync() and os.Exit(1). -func (l Logger) Fatal(ctx context.Context, msg string, fields ...Field) { +func Fatal(ctx context.Context, msg string, fields ...Field) { + l, ok := loggerFromContext(ctx) + if !ok { + os.Stderr.WriteString("Fatal called but no Logger in context") + // The caller expects the program to terminate after Fatal no matter what. + l.exit(1) + return + } l.log(ctx, LevelFatal, msg, fields) l.Sync() l.exit(1) } -// With returns a Logger that prepends the given fields on every -// logged entry. -// -// It will append to any fields already in the Logger. -func (l Logger) With(fields ...Field) Logger { - l.fields = l.fields.append(fields) - return l -} - // Named appends the name to the set names // on the logger. -func (l Logger) Named(name string) Logger { +func Named(ctx context.Context, name string) context.Context { + l, ok := loggerFromContext(ctx) + if !ok { + return ctx + } l.names = appendNames(l.names, name) - return l + return contextWithLogger(ctx, l) } -// Leveled returns a Logger that only logs entries +// Leveled returns a logger that only logs entries // equal to or above the given level. -func (l Logger) Leveled(level Level) Logger { +func Leveled(ctx context.Context, level Level) context.Context { + l, ok := loggerFromContext(ctx) + if !ok { + return ctx + } l.level = level - return l + return contextWithLogger(ctx, l) } -func (l Logger) log(ctx context.Context, level Level, msg string, fields Map) { +func (l logger) log(ctx context.Context, level Level, msg string, fields Map) { ent := l.entry(ctx, level, msg, fields) l.LogEntry(ctx, ent) } -func (l Logger) entry(ctx context.Context, level Level, msg string, fields Map) SinkEntry { +func (l logger) entry(ctx context.Context, level Level, msg string, fields Map) SinkEntry { ent := SinkEntry{ Time: time.Now().UTC(), Level: level, @@ -226,8 +265,8 @@ func M(fs ...Field) Map { return fs } -// Error is the standard key used for logging a Go error value. -func Error(err error) Field { +// Err is the standard key used for logging a Go error value. +func Err(err error) Field { return F("error", err) } diff --git a/slog_test.go b/slog_test.go index a55def2..3c88989 100644 --- a/slog_test.go +++ b/slog_test.go @@ -28,8 +28,6 @@ func (s *fakeSink) Sync() { s.syncs++ } -var bg = context.Background() - func TestLogger(t *testing.T) { t.Parallel() @@ -38,11 +36,12 @@ func TestLogger(t *testing.T) { s1 := &fakeSink{} s2 := &fakeSink{} - l := slog.Make(s1, s2) - l = l.Leveled(slog.LevelError) + var ctx context.Context + ctx = slog.Make(context.Background(), s1, s2) + ctx = slog.Leveled(ctx, slog.LevelError) - l.Info(bg, "wow", slog.Error(io.EOF)) - l.Error(bg, "meow", slog.Error(io.ErrUnexpectedEOF)) + slog.Info(ctx, "wow", slog.Err(io.EOF)) + slog.Error(ctx, "meow", slog.Err(io.ErrUnexpectedEOF)) assert.Equal(t, "syncs", 1, s1.syncs) assert.Len(t, "entries", 1, s1.entries) @@ -54,13 +53,14 @@ func TestLogger(t *testing.T) { t.Parallel() s := &fakeSink{} - l := slog.Make(s) + var ctx context.Context + ctx = slog.Make(context.Background(), s) h := func(ctx context.Context) { slog.Helper() - l.Info(ctx, "logging in helper") + slog.Info(ctx, "logging in helper") } - ctx := slog.With(bg, slog.F( + ctx = slog.With(ctx, slog.F( "ctx", 1024), ) h(ctx) @@ -86,15 +86,16 @@ func TestLogger(t *testing.T) { t.Parallel() s := &fakeSink{} - l := slog.Make(s) - l = l.Named("hello") - l = l.Named("hello2") + var ctx context.Context + ctx = slog.Make(context.Background(), s) + ctx = slog.Named(ctx, "hello") + ctx = slog.Named(ctx, "hello2") - ctx, span := trace.StartSpan(bg, "trace") + ctx, span := trace.StartSpan(ctx, "trace") ctx = slog.With(ctx, slog.F("ctx", io.EOF)) - l = l.With(slog.F("with", 2)) + ctx = slog.With(ctx, slog.F("with", 2)) - l.Info(ctx, "meow", slog.F("hi", "xd")) + slog.Info(ctx, "meow", slog.F("hi", "xd")) assert.Len(t, "entries", 1, s.entries) assert.Equal(t, "entry", slog.SinkEntry{ @@ -107,13 +108,13 @@ func TestLogger(t *testing.T) { File: slogTestFile, Func: "cdr.dev/slog_test.TestLogger.func3", - Line: 97, + Line: 98, SpanContext: span.SpanContext(), Fields: slog.M( - slog.F("with", 2), slog.F("ctx", io.EOF), + slog.F("with", 2), slog.F("hi", "xd"), ), }, s.entries[0]) @@ -123,20 +124,21 @@ func TestLogger(t *testing.T) { t.Parallel() s := &fakeSink{} - l := slog.Make(s) + var ctx context.Context + ctx = slog.Make(context.Background(), s) exits := 0 - l.SetExit(func(int) { + ctx = slog.SetExit(ctx, func(int) { exits++ }) - l = l.Leveled(slog.LevelDebug) - l.Debug(bg, "") - l.Info(bg, "") - l.Warn(bg, "") - l.Error(bg, "") - l.Critical(bg, "") - l.Fatal(bg, "") + ctx = slog.Leveled(ctx, slog.LevelDebug) + slog.Debug(ctx, "") + slog.Info(ctx, "") + slog.Warn(ctx, "") + slog.Error(ctx, "") + slog.Critical(ctx, "") + slog.Fatal(ctx, "") assert.Len(t, "entries", 6, s.entries) assert.Equal(t, "syncs", 3, s.syncs) diff --git a/sloggers/sloghuman/sloghuman.go b/sloggers/sloghuman/sloghuman.go index 719db7c..18cbb58 100644 --- a/sloggers/sloghuman/sloghuman.go +++ b/sloggers/sloghuman/sloghuman.go @@ -17,8 +17,8 @@ import ( // // If the writer implements Sync() error then // it will be called when syncing. -func Make(w io.Writer) slog.Logger { - return slog.Make(&humanSink{ +func Make(ctx context.Context, w io.Writer) slog.SinkContext { + return slog.Make(ctx, &humanSink{ w: syncwriter.New(w), w2: w, }) @@ -29,7 +29,7 @@ type humanSink struct { w2 io.Writer } -func (s humanSink) LogEntry(ctx context.Context, ent slog.SinkEntry) { +func (s humanSink) LogEntry(_ context.Context, ent slog.SinkEntry) { str := entryhuman.Fmt(s.w2, ent) lines := strings.Split(str, "\n") diff --git a/sloggers/sloghuman/sloghuman_test.go b/sloggers/sloghuman/sloghuman_test.go index 5c28afb..86fe888 100644 --- a/sloggers/sloghuman/sloghuman_test.go +++ b/sloggers/sloghuman/sloghuman_test.go @@ -11,18 +11,17 @@ import ( "cdr.dev/slog/sloggers/sloghuman" ) -var bg = context.Background() - func TestMake(t *testing.T) { t.Parallel() b := &bytes.Buffer{} - l := sloghuman.Make(b) - l.Info(bg, "line1\n\nline2", slog.F("wowow", "me\nyou")) - l.Sync() + ctx := context.Background() + ctx = sloghuman.Make(ctx, b) + slog.Info(ctx, "line1\n\nline2", slog.F("wowow", "me\nyou")) + slog.Sync(ctx) et, rest, err := entryhuman.StripTimestamp(b.String()) assert.Success(t, "strip timestamp", err) assert.False(t, "timestamp", et.IsZero()) - assert.Equal(t, "entry", " [INFO]\t\t...\t{\"wowow\": \"me\\nyou\"}\n \"msg\": line1\n\n line2\n", rest) + assert.Equal(t, "entry", " [INFO]\t\t...\t{\"wowow\": \"me\\nyou\"}\n \"msg\": line1\n\n line2\n", rest) } diff --git a/sloggers/slogjson/slogjson.go b/sloggers/slogjson/slogjson.go index 5069ddc..06c0446 100644 --- a/sloggers/slogjson/slogjson.go +++ b/sloggers/slogjson/slogjson.go @@ -34,8 +34,8 @@ import ( // for the format. // If the writer implements Sync() error then // it will be called when syncing. -func Make(w io.Writer) slog.Logger { - return slog.Make(jsonSink{ +func Make(ctx context.Context, w io.Writer) context.Context { + return slog.Make(ctx, jsonSink{ w: syncwriter.New(w), }) } @@ -44,7 +44,7 @@ type jsonSink struct { w *syncwriter.Writer } -func (s jsonSink) LogEntry(ctx context.Context, ent slog.SinkEntry) { +func (s jsonSink) LogEntry(_ context.Context, ent slog.SinkEntry) { m := slog.M( slog.F("ts", ent.Time), slog.F("level", ent.Level), diff --git a/sloggers/slogjson/slogjson_test.go b/sloggers/slogjson/slogjson_test.go index 77ee4e4..103be7a 100644 --- a/sloggers/slogjson/slogjson_test.go +++ b/sloggers/slogjson/slogjson_test.go @@ -24,9 +24,9 @@ func TestMake(t *testing.T) { ctx, s := trace.StartSpan(bg, "meow") b := &bytes.Buffer{} - l := slogjson.Make(b) - l = l.Named("named") - l.Error(ctx, "line1\n\nline2", slog.F("wowow", "me\nyou")) + ctx = slogjson.Make(ctx, b) + ctx = slog.Named(ctx, "named") + slog.Error(ctx, "line1\n\nline2", slog.F("wowow", "me\nyou")) j := entryjson.Filter(b.String(), "ts") exp := fmt.Sprintf(`{"level":"ERROR","msg":"line1\n\nline2","caller":"%v:29","func":"cdr.dev/slog/sloggers/slogjson_test.TestMake","logger_names":["named"],"trace":"%v","span":"%v","fields":{"wowow":"me\nyou"}} diff --git a/sloggers/slogstackdriver/slogstackdriver.go b/sloggers/slogstackdriver/slogstackdriver.go index 3b4045e..bac1294 100644 --- a/sloggers/slogstackdriver/slogstackdriver.go +++ b/sloggers/slogstackdriver/slogstackdriver.go @@ -17,14 +17,14 @@ import ( "cdr.dev/slog/internal/syncwriter" ) -// Make creates a slog.Logger configured to write JSON logs +// Make creates a slog.logger configured to write JSON logs // to stdout for stackdriver. // // See https://cloud.google.com/logging/docs/agent -func Make(w io.Writer) slog.Logger { +func Make(ctx context.Context, w io.Writer) slog.SinkContext { projectID, _ := metadata.ProjectID() - return slog.Make(stackdriverSink{ + return slog.Make(ctx, stackdriverSink{ projectID: projectID, w: syncwriter.New(w), }) @@ -35,7 +35,7 @@ type stackdriverSink struct { w *syncwriter.Writer } -func (s stackdriverSink) LogEntry(ctx context.Context, ent slog.SinkEntry) { +func (s stackdriverSink) LogEntry(_ context.Context, ent slog.SinkEntry) { // https://cloud.google.com/logging/docs/agent/configuration#special-fields e := slog.M( slog.F("severity", sev(ent.Level)), diff --git a/sloggers/slogstackdriver/slogstackdriver_test.go b/sloggers/slogstackdriver/slogstackdriver_test.go index 3a4a295..025ad9d 100644 --- a/sloggers/slogstackdriver/slogstackdriver_test.go +++ b/sloggers/slogstackdriver/slogstackdriver_test.go @@ -24,9 +24,9 @@ func TestStackdriver(t *testing.T) { ctx, s := trace.StartSpan(bg, "meow") b := &bytes.Buffer{} - l := slogstackdriver.Make(b) - l = l.Named("meow") - l.Error(ctx, "line1\n\nline2", slog.F("wowow", "me\nyou")) + ctx = slogstackdriver.Make(ctx, b) + ctx = slog.Named(ctx, "meow") + slog.Error(ctx, "line1\n\nline2", slog.F("wowow", "me\nyou")) j := entryjson.Filter(b.String(), "timestamp") exp := fmt.Sprintf(`{"severity":"ERROR","message":"line1\n\nline2","logging.googleapis.com/sourceLocation":{"file":"%v","line":29,"function":"cdr.dev/slog/sloggers/slogstackdriver_test.TestStackdriver"},"logging.googleapis.com/operation":{"producer":"meow"},"logging.googleapis.com/trace":"projects//traces/%v","logging.googleapis.com/spanId":"%v","logging.googleapis.com/trace_sampled":false,"wowow":"me\nyou"} diff --git a/sloggers/slogtest/assert/assert.go b/sloggers/slogtest/assert/assert.go index e246a2f..7aafdd4 100644 --- a/sloggers/slogtest/assert/assert.go +++ b/sloggers/slogtest/assert/assert.go @@ -39,7 +39,7 @@ func Success(t testing.TB, name string, err error) { if err != nil { slogtest.Fatal(t, "unexpected error", slog.F("name", name), - slog.Error(err), + slog.Err(err), ) } } diff --git a/sloggers/slogtest/t.go b/sloggers/slogtest/t.go index e315184..ffb972a 100644 --- a/sloggers/slogtest/t.go +++ b/sloggers/slogtest/t.go @@ -18,8 +18,8 @@ import ( // Ensure all stdlib logs go through slog. func init() { - l := sloghuman.Make(os.Stderr) - log.SetOutput(slog.Stdlib(context.Background(), l).Writer()) + ctx := sloghuman.Make(ctx, os.Stderr) + log.SetOutput(slog.Stdlib(ctx).Writer()) } // Options represents the options for the logger returned @@ -30,12 +30,12 @@ type Options struct { IgnoreErrors bool } -// Make creates a Logger that writes logs to tb in a human readable format. -func Make(tb testing.TB, opts *Options) slog.Logger { +// Make creates a logger that writes logs to tb in a human readable format. +func Make(tb testing.TB, opts *Options) slog.SinkContext { if opts == nil { opts = &Options{} } - return slog.Make(testSink{ + return slog.Make(context.Background(), testSink{ tb: tb, opts: opts, }) @@ -72,30 +72,30 @@ func (ts testSink) Sync() {} var ctx = context.Background() -func l(t testing.TB) slog.Logger { +func l(t testing.TB) context.Context { return Make(t, nil) } // Debug logs the given msg and fields to t via t.Log at the debug level. func Debug(t testing.TB, msg string, fields ...slog.Field) { slog.Helper() - l(t).Debug(ctx, msg, fields...) + slog.Debug(l(t), msg, fields...) } // Info logs the given msg and fields to t via t.Log at the info level. func Info(t testing.TB, msg string, fields ...slog.Field) { slog.Helper() - l(t).Info(ctx, msg, fields...) + slog.Info(l(t), msg, fields...) } // Error logs the given msg and fields to t via t.Error at the error level. func Error(t testing.TB, msg string, fields ...slog.Field) { slog.Helper() - l(t).Error(ctx, msg, fields...) + slog.Error(l(t), msg, fields...) } // Fatal logs the given msg and fields to t via t.Fatal at the fatal level. func Fatal(t testing.TB, msg string, fields ...slog.Field) { slog.Helper() - l(t).Fatal(ctx, msg, fields...) + slog.Fatal(l(t), msg, fields...) } diff --git a/sloggers/slogtest/t_test.go b/sloggers/slogtest/t_test.go index 9169cbe..77942a5 100644 --- a/sloggers/slogtest/t_test.go +++ b/sloggers/slogtest/t_test.go @@ -31,11 +31,12 @@ func TestIgnoreErrors(t *testing.T) { t.Parallel() tb := &fakeTB{} - l := slog.Make(slogtest.Make(tb, &slogtest.Options{ + ctx := context.Background() + ctx = slog.Make(ctx, slogtest.Make(tb, &slogtest.Options{ IgnoreErrors: true, })) - l.Error(bg, "hello") + slog.Error(ctx, "hello") assert.Equal(t, "errors", 0, tb.errors) defer func() { @@ -43,11 +44,9 @@ func TestIgnoreErrors(t *testing.T) { assert.Equal(t, "fatals", 0, tb.fatals) }() - l.Fatal(bg, "hello") + slog.Fatal(ctx, "hello") } -var bg = context.Background() - type fakeTB struct { testing.TB From 6a41f9d85660abd7fa70daad28a4731a59b358d0 Mon Sep 17 00:00:00 2001 From: Ammar Bandukwala Date: Tue, 31 Mar 2020 14:54:42 -0500 Subject: [PATCH 02/12] Revert "Make SinkContext a struct" It's better as an interface so users don't have to call the `Sink` method on the struct during Make. - Move SinkContext into context.go --- context.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/context.go b/context.go index e3f20c1..d44a049 100644 --- a/context.go +++ b/context.go @@ -4,15 +4,21 @@ import "context" type loggerCtxKey = struct{} -// SinkContext is used by slog.Make to compose many loggers together. -type SinkContext struct { +type sinkContext struct { + context.Context + Sink +} + +// SinkContext is a context that implements Sink. +// It may be returned by log creators to allow for composition. +type SinkContext interface { Sink context.Context } func contextWithLogger(ctx context.Context, l logger) SinkContext { ctx = context.WithValue(ctx, loggerCtxKey{}, l) - return SinkContext{ + return &sinkContext{ Context: ctx, Sink: l, } From 0d999259b6559610bb00e2adadc253806c75a9d7 Mon Sep 17 00:00:00 2001 From: Ammar Bandukwala Date: Tue, 31 Mar 2020 15:11:45 -0500 Subject: [PATCH 03/12] Add /v2 to import path --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index c965c2f..bfca1fb 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module cdr.dev/slog +module cdr.dev/slog/v2 go 1.13 From fa507d2d3d23988679b85ef51a6ca2c7bccb9f6d Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Thu, 9 Apr 2020 02:17:45 -0400 Subject: [PATCH 04/12] Revert "Add /v2 to import path" This reverts commit 0d999259b6559610bb00e2adadc253806c75a9d7. --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index bfca1fb..c965c2f 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module cdr.dev/slog/v2 +module cdr.dev/slog go 1.13 From 958f7eb3e915b963508e7f3bd23fa702ff673ffe Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Thu, 9 Apr 2020 02:17:46 -0400 Subject: [PATCH 05/12] Revert "Merge pull request #86 from cdr/sink-context-iface" This reverts commit e88ff39eeaafb6157a32889067ceb7caf0e7744f, reversing changes made to 95bc0f1f03c0bdd180fc467c42383cee7d46fcb4. --- context.go | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/context.go b/context.go index d44a049..e3f20c1 100644 --- a/context.go +++ b/context.go @@ -4,21 +4,15 @@ import "context" type loggerCtxKey = struct{} -type sinkContext struct { - context.Context - Sink -} - -// SinkContext is a context that implements Sink. -// It may be returned by log creators to allow for composition. -type SinkContext interface { +// SinkContext is used by slog.Make to compose many loggers together. +type SinkContext struct { Sink context.Context } func contextWithLogger(ctx context.Context, l logger) SinkContext { ctx = context.WithValue(ctx, loggerCtxKey{}, l) - return &sinkContext{ + return SinkContext{ Context: ctx, Sink: l, } From b1eadde3a554ee742d8f9e5aad6ccac61052fe8a Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Thu, 9 Apr 2020 02:17:53 -0400 Subject: [PATCH 06/12] Revert "Merge pull request #85 from cdr/ctx-log" This reverts commit 95bc0f1f03c0bdd180fc467c42383cee7d46fcb4, reversing changes made to f7a90d8c4e3665558b8c161a9c69bcb2ec1edfb9. --- README.md | 6 +- context.go | 27 ---- example_helper_test.go | 4 +- example_marshaller_test.go | 4 +- example_test.go | 51 ++++---- export_test.go | 9 +- map.go | 2 +- map_test.go | 6 +- s.go | 17 +-- s_test.go | 9 +- slog.go | 117 ++++++------------ slog_test.go | 54 ++++---- sloggers/sloghuman/sloghuman.go | 6 +- sloggers/sloghuman/sloghuman_test.go | 11 +- sloggers/slogjson/slogjson.go | 6 +- sloggers/slogjson/slogjson_test.go | 6 +- sloggers/slogstackdriver/slogstackdriver.go | 8 +- .../slogstackdriver/slogstackdriver_test.go | 6 +- sloggers/slogtest/assert/assert.go | 2 +- sloggers/slogtest/t.go | 20 +-- sloggers/slogtest/t_test.go | 9 +- 21 files changed, 150 insertions(+), 230 deletions(-) delete mode 100644 context.go diff --git a/README.md b/README.md index 50550b8..9fd2562 100644 --- a/README.md +++ b/README.md @@ -35,9 +35,9 @@ go get cdr.dev/slog Many more examples available at [godoc](https://godoc.org/cdr.dev/slog#pkg-examples). ```go -ctx := sloghuman.Make(ctx, os.Stdout) +log := sloghuman.Make(os.Stdout) -slog.Info(ctx, "my message here", +log.Info(context.Background(), "my message here", slog.F("field_name", "something or the other"), slog.F("some_map", slog.M( slog.F("nested_fields", time.Date(2000, time.February, 5, 4, 4, 4, 0, time.UTC)), @@ -87,8 +87,6 @@ Here is a list of reasons how we improved on zap with slog. 1. Full [context.Context](https://blog.golang.org/context) support - `slog` lets you set fields in a `context.Context` such that any log with the context prints those fields. - - `slog` stores the actual logger in the `context.Context`, following the example of - [the Go trace library](https://golang.org/pkg/runtime/trace/). Our logger doesn't bloat type and function signatures. - We wanted to be able to pull up all relevant logs for a given trace, user or request. With zap, we were plugging these fields in for every relevant log or passing around a logger with the fields set. This became very verbose. diff --git a/context.go b/context.go deleted file mode 100644 index e3f20c1..0000000 --- a/context.go +++ /dev/null @@ -1,27 +0,0 @@ -package slog - -import "context" - -type loggerCtxKey = struct{} - -// SinkContext is used by slog.Make to compose many loggers together. -type SinkContext struct { - Sink - context.Context -} - -func contextWithLogger(ctx context.Context, l logger) SinkContext { - ctx = context.WithValue(ctx, loggerCtxKey{}, l) - return SinkContext{ - Context: ctx, - Sink: l, - } -} - -func loggerFromContext(ctx context.Context) (logger, bool) { - v := ctx.Value(loggerCtxKey{}) - if v == nil { - return logger{}, false - } - return v.(logger), true -} diff --git a/example_helper_test.go b/example_helper_test.go index 853341d..e06d372 100644 --- a/example_helper_test.go +++ b/example_helper_test.go @@ -12,12 +12,12 @@ import ( func httpLogHelper(ctx context.Context, status int) { slog.Helper() - slog.Info(ctx, "sending HTTP response", + l.Info(ctx, "sending HTTP response", slog.F("status", status), ) } -var l = sloghuman.Make(context.Background(), os.Stdout) +var l = sloghuman.Make(os.Stdout) func ExampleHelper() { ctx := context.Background() diff --git a/example_marshaller_test.go b/example_marshaller_test.go index 514ab62..e4e6992 100644 --- a/example_marshaller_test.go +++ b/example_marshaller_test.go @@ -21,9 +21,9 @@ func (s myStruct) MarshalJSON() ([]byte, error) { } func Example_marshaller() { - ctx := sloghuman.Make(context.Background(), os.Stdout) + l := sloghuman.Make(os.Stdout) - slog.Info(ctx, "wow", + l.Info(context.Background(), "wow", slog.F("myStruct", myStruct{ foo: 1, bar: 2, diff --git a/example_test.go b/example_test.go index 3627532..f3577ac 100644 --- a/example_test.go +++ b/example_test.go @@ -18,14 +18,14 @@ import ( ) func Example() { - ctx := sloghuman.Make(context.Background(), os.Stdout) + log := sloghuman.Make(os.Stdout) - slog.Info(ctx, "my message here", + log.Info(context.Background(), "my message here", slog.F("field_name", "something or the other"), slog.F("some_map", slog.M( slog.F("nested_fields", time.Date(2000, time.February, 5, 4, 4, 4, 0, time.UTC)), )), - slog.Err( + slog.Error( xerrors.Errorf("wrap1: %w", xerrors.Errorf("wrap2: %w", io.EOF, @@ -45,7 +45,7 @@ func Example() { } func Example_struct() { - ctx := sloghuman.Make(context.Background(), os.Stdout) + l := sloghuman.Make(os.Stdout) type hello struct { Meow int `json:"meow"` @@ -53,7 +53,7 @@ func Example_struct() { M time.Time `json:"m"` } - slog.Info(ctx, "check out my structure", + l.Info(context.Background(), "check out my structure", slog.F("hello", hello{ Meow: 1, Bar: "barbar", @@ -76,27 +76,26 @@ func Example_testing() { } func Example_tracing() { - var ctx context.Context - ctx = sloghuman.Make(context.Background(), os.Stdout) + log := sloghuman.Make(os.Stdout) - ctx, _ = trace.StartSpan(ctx, "spanName") + ctx, _ := trace.StartSpan(context.Background(), "spanName") - slog.Info(ctx, "my msg", slog.F("hello", "hi")) + log.Info(ctx, "my msg", slog.F("hello", "hi")) // 2019-12-09 21:59:48.110 [INFO] my msg {"trace": "f143d018d00de835688453d8dc55c9fd", "span": "f214167bf550afc3", "hello": "hi"} } func Example_multiple() { - ctx := sloghuman.Make(context.Background(), os.Stdout) + l := sloghuman.Make(os.Stdout) f, err := os.OpenFile("stackdriver", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) if err != nil { - slog.Fatal(ctx, "failed to open stackdriver log file", slog.Err(err)) + l.Fatal(context.Background(), "failed to open stackdriver log file", slog.Error(err)) } - ctx = slog.Make(l, slogstackdriver.Make(ctx, f)) + l = slog.Make(l, slogstackdriver.Make(f)) - slog.Info(ctx, "log to stdout and stackdriver") + l.Info(context.Background(), "log to stdout and stackdriver") // 2019-12-07 20:59:55.790 [INFO] log to stdout and stackdriver } @@ -104,41 +103,41 @@ func Example_multiple() { func ExampleWith() { ctx := slog.With(context.Background(), slog.F("field", 1)) - ctx = sloghuman.Make(ctx, os.Stdout) - slog.Info(ctx, "msg") + l := sloghuman.Make(os.Stdout) + l.Info(ctx, "msg") // 2019-12-07 20:54:23.986 [INFO] msg {"field": 1} } func ExampleStdlib() { ctx := slog.With(context.Background(), slog.F("field", 1)) - l := slog.Stdlib(sloghuman.Make(ctx, os.Stdout)) + l := slog.Stdlib(ctx, sloghuman.Make(os.Stdout)) l.Print("msg") // 2019-12-07 20:54:23.986 [INFO] (stdlib) msg {"field": 1} } -func ExampleNamed() { +func ExampleLogger_Named() { ctx := context.Background() - ctx = sloghuman.Make(ctx, os.Stdout) - ctx = slog.Named(ctx, "http") - slog.Info(ctx, "received request", slog.F("remote address", net.IPv4(127, 0, 0, 1))) + l := sloghuman.Make(os.Stdout) + l = l.Named("http") + l.Info(ctx, "received request", slog.F("remote address", net.IPv4(127, 0, 0, 1))) // 2019-12-07 21:20:56.974 [INFO] (http) received request {"remote address": "127.0.0.1"} } -func ExampleLeveled() { +func ExampleLogger_Leveled() { ctx := context.Background() - ctx = sloghuman.Make(ctx, os.Stdout) - slog.Debug(ctx, "testing1") - slog.Info(ctx, "received request") + l := sloghuman.Make(os.Stdout) + l.Debug(ctx, "testing1") + l.Info(ctx, "received request") - ctx = slog.Leveled(ctx, slog.LevelDebug) + l = l.Leveled(slog.LevelDebug) - slog.Debug(ctx, "testing2") + l.Debug(ctx, "testing2") // 2019-12-07 21:26:20.945 [INFO] received request // 2019-12-07 21:26:20.945 [DEBUG] testing2 diff --git a/export_test.go b/export_test.go index a682e20..1266811 100644 --- a/export_test.go +++ b/export_test.go @@ -1,12 +1,5 @@ package slog -import "context" - -func SetExit(ctx context.Context, fn func(int)) context.Context { - l, ok := loggerFromContext(ctx) - if !ok { - return ctx - } +func (l *Logger) SetExit(fn func(int)) { l.exit = fn - return contextWithLogger(ctx, l) } diff --git a/map.go b/map.go index a1b594b..636f4ee 100644 --- a/map.go +++ b/map.go @@ -128,7 +128,7 @@ func encodeJSON(v interface{}) []byte { b, err := json.Marshal(v) if err != nil { return encode(M( - Err(xerrors.Errorf("failed to marshal to JSON: %w", err)), + Error(xerrors.Errorf("failed to marshal to JSON: %w", err)), F("type", reflect.TypeOf(v)), F("value", fmt.Sprintf("%+v", v)), )) diff --git a/map_test.go b/map_test.go index 03d7886..e15a6ee 100644 --- a/map_test.go +++ b/map_test.go @@ -37,7 +37,7 @@ func TestMap(t *testing.T) { } test(t, slog.M( - slog.Err( + slog.Error( xerrors.Errorf("wrap1: %w", xerrors.Errorf("wrap2: %w", io.EOF, @@ -222,6 +222,10 @@ func TestMap(t *testing.T) { }) } +type meow struct { + a int +} + func indentJSON(t *testing.T, j string) string { b := &bytes.Buffer{} err := json.Indent(b, []byte(j), "", strings.Repeat(" ", 4)) diff --git a/s.go b/s.go index e85195c..fa8917e 100644 --- a/s.go +++ b/s.go @@ -3,7 +3,6 @@ package slog import ( "context" "log" - "os" "strings" ) @@ -16,19 +15,14 @@ import ( // You can redirect the stdlib default logger with log.SetOutput // to the Writer on the logger returned by this function. // See the example. -func Stdlib(ctx context.Context) *log.Logger { - ctx = Named(ctx, "stdlib") - - l, ok := loggerFromContext(ctx) - if !ok { - // Give stderr logger if no slog. - return log.New(os.Stderr, "", 0) - } +func Stdlib(ctx context.Context, l Logger) *log.Logger { l.skip += 3 - ctx = contextWithLogger(ctx, l) + + l = l.Named("stdlib") w := &stdlogWriter{ ctx: ctx, + l: l, } return log.New(w, "", 0) @@ -36,6 +30,7 @@ func Stdlib(ctx context.Context) *log.Logger { type stdlogWriter struct { ctx context.Context + l Logger } func (w stdlogWriter) Write(p []byte) (n int, err error) { @@ -44,7 +39,7 @@ func (w stdlogWriter) Write(p []byte) (n int, err error) { // we do not want. msg = strings.TrimSuffix(msg, "\n") - Info(w.ctx, msg) + w.l.Info(w.ctx, msg) return len(p), nil } diff --git a/s_test.go b/s_test.go index 5006c04..a589460 100644 --- a/s_test.go +++ b/s_test.go @@ -2,7 +2,6 @@ package slog_test import ( "bytes" - "context" "testing" "cdr.dev/slog" @@ -15,16 +14,14 @@ func TestStdlib(t *testing.T) { t.Parallel() b := &bytes.Buffer{} - ctx := context.Background() - ctx = slog.Make(sloghuman.Make(ctx, b)) - ctx = slog.With(ctx, + l := slog.Make(sloghuman.Make(b)).With( slog.F("hi", "we"), ) - stdlibLog := slog.Stdlib(ctx) + stdlibLog := slog.Stdlib(bg, l) stdlibLog.Println("stdlib") et, rest, err := entryhuman.StripTimestamp(b.String()) assert.Success(t, "strip timestamp", err) assert.False(t, "timestamp", et.IsZero()) - assert.Equal(t, "entry", " [INFO]\t(stdlib)\t\tstdlib\t{\"hi\": \"we\"}\n", rest) + assert.Equal(t, "entry", " [INFO]\t(stdlib)\t\tstdlib\t{\"hi\": \"we\"}\n", rest) } diff --git a/slog.go b/slog.go index bfbecde..a291f27 100644 --- a/slog.go +++ b/slog.go @@ -4,8 +4,8 @@ // // The examples are the best way to understand how to use this library effectively. // -// The logger type implements a high level API around the Sink interface. -// logger implements Sink as well to allow composition. +// The Logger type implements a high level API around the Sink interface. +// Logger implements Sink as well to allow composition. // // Implementations of the Sink interface are available in the sloggers subdirectory. package slog // import "cdr.dev/slog" @@ -21,7 +21,7 @@ import ( "go.opencensus.io/trace" ) -// Sink is the destination of a logger. +// Sink is the destination of a Logger. // // All sinks must be safe for concurrent use. type Sink interface { @@ -33,7 +33,7 @@ type Sink interface { // underlying sinks. // // It extends the entry with the set fields and names. -func (l logger) LogEntry(ctx context.Context, e SinkEntry) { +func (l Logger) LogEntry(ctx context.Context, e SinkEntry) { if e.Level < l.level { return } @@ -46,27 +46,17 @@ func (l logger) LogEntry(ctx context.Context, e SinkEntry) { } } -func (l logger) Sync() { +// Sync calls Sync on all the underlying sinks. +func (l Logger) Sync() { for _, s := range l.sinks { s.Sync() } } -// Sync calls Sync on all the underlying sinks. -func Sync(ctx context.Context) { - l, ok := loggerFromContext(ctx) - if !ok { - return - } - l.Sync() - return -} - -// logger wraps Sink with a nice API to log entries. +// Logger wraps Sink with a nice API to log entries. // -// logger is safe for concurrent use. -// It is unexported because callers should only log via a context. -type logger struct { +// Logger is safe for concurrent use. +type Logger struct { sinks []Sink level Level @@ -78,53 +68,34 @@ type logger struct { } // Make creates a logger that writes logs to the passed sinks at LevelInfo. -func Make(ctx context.Context, sinks ...Sink) SinkContext { - // Just in case the ctx has a logger, start with it. - l, _ := loggerFromContext(ctx) - l.sinks = append(l.sinks, sinks...) - if l.level == 0 { - l.level = LevelInfo - } - l.exit = os.Exit +func Make(sinks ...Sink) Logger { + return Logger{ + sinks: sinks, + level: LevelInfo, - return contextWithLogger(ctx, l) + exit: os.Exit, + } } // Debug logs the msg and fields at LevelDebug. -func Debug(ctx context.Context, msg string, fields ...Field) { - l, ok := loggerFromContext(ctx) - if !ok { - return - } +func (l Logger) Debug(ctx context.Context, msg string, fields ...Field) { l.log(ctx, LevelDebug, msg, fields) } // Info logs the msg and fields at LevelInfo. -func Info(ctx context.Context, msg string, fields ...Field) { - l, ok := loggerFromContext(ctx) - if !ok { - return - } +func (l Logger) Info(ctx context.Context, msg string, fields ...Field) { l.log(ctx, LevelInfo, msg, fields) } // Warn logs the msg and fields at LevelWarn. -func Warn(ctx context.Context, msg string, fields ...Field) { - l, ok := loggerFromContext(ctx) - if !ok { - return - } +func (l Logger) Warn(ctx context.Context, msg string, fields ...Field) { l.log(ctx, LevelWarn, msg, fields) } // Error logs the msg and fields at LevelError. // // It will then Sync(). -func Error(ctx context.Context, msg string, fields ...Field) { - l, ok := loggerFromContext(ctx) - if !ok { - return - } +func (l Logger) Error(ctx context.Context, msg string, fields ...Field) { l.log(ctx, LevelError, msg, fields) l.Sync() } @@ -132,11 +103,7 @@ func Error(ctx context.Context, msg string, fields ...Field) { // Critical logs the msg and fields at LevelCritical. // // It will then Sync(). -func Critical(ctx context.Context, msg string, fields ...Field) { - l, ok := loggerFromContext(ctx) - if !ok { - return - } +func (l Logger) Critical(ctx context.Context, msg string, fields ...Field) { l.log(ctx, LevelCritical, msg, fields) l.Sync() } @@ -144,47 +111,41 @@ func Critical(ctx context.Context, msg string, fields ...Field) { // Fatal logs the msg and fields at LevelFatal. // // It will then Sync() and os.Exit(1). -func Fatal(ctx context.Context, msg string, fields ...Field) { - l, ok := loggerFromContext(ctx) - if !ok { - os.Stderr.WriteString("Fatal called but no Logger in context") - // The caller expects the program to terminate after Fatal no matter what. - l.exit(1) - return - } +func (l Logger) Fatal(ctx context.Context, msg string, fields ...Field) { l.log(ctx, LevelFatal, msg, fields) l.Sync() l.exit(1) } +// With returns a Logger that prepends the given fields on every +// logged entry. +// +// It will append to any fields already in the Logger. +func (l Logger) With(fields ...Field) Logger { + l.fields = l.fields.append(fields) + return l +} + // Named appends the name to the set names // on the logger. -func Named(ctx context.Context, name string) context.Context { - l, ok := loggerFromContext(ctx) - if !ok { - return ctx - } +func (l Logger) Named(name string) Logger { l.names = appendNames(l.names, name) - return contextWithLogger(ctx, l) + return l } -// Leveled returns a logger that only logs entries +// Leveled returns a Logger that only logs entries // equal to or above the given level. -func Leveled(ctx context.Context, level Level) context.Context { - l, ok := loggerFromContext(ctx) - if !ok { - return ctx - } +func (l Logger) Leveled(level Level) Logger { l.level = level - return contextWithLogger(ctx, l) + return l } -func (l logger) log(ctx context.Context, level Level, msg string, fields Map) { +func (l Logger) log(ctx context.Context, level Level, msg string, fields Map) { ent := l.entry(ctx, level, msg, fields) l.LogEntry(ctx, ent) } -func (l logger) entry(ctx context.Context, level Level, msg string, fields Map) SinkEntry { +func (l Logger) entry(ctx context.Context, level Level, msg string, fields Map) SinkEntry { ent := SinkEntry{ Time: time.Now().UTC(), Level: level, @@ -265,8 +226,8 @@ func M(fs ...Field) Map { return fs } -// Err is the standard key used for logging a Go error value. -func Err(err error) Field { +// Error is the standard key used for logging a Go error value. +func Error(err error) Field { return F("error", err) } diff --git a/slog_test.go b/slog_test.go index 3c88989..a55def2 100644 --- a/slog_test.go +++ b/slog_test.go @@ -28,6 +28,8 @@ func (s *fakeSink) Sync() { s.syncs++ } +var bg = context.Background() + func TestLogger(t *testing.T) { t.Parallel() @@ -36,12 +38,11 @@ func TestLogger(t *testing.T) { s1 := &fakeSink{} s2 := &fakeSink{} - var ctx context.Context - ctx = slog.Make(context.Background(), s1, s2) - ctx = slog.Leveled(ctx, slog.LevelError) + l := slog.Make(s1, s2) + l = l.Leveled(slog.LevelError) - slog.Info(ctx, "wow", slog.Err(io.EOF)) - slog.Error(ctx, "meow", slog.Err(io.ErrUnexpectedEOF)) + l.Info(bg, "wow", slog.Error(io.EOF)) + l.Error(bg, "meow", slog.Error(io.ErrUnexpectedEOF)) assert.Equal(t, "syncs", 1, s1.syncs) assert.Len(t, "entries", 1, s1.entries) @@ -53,14 +54,13 @@ func TestLogger(t *testing.T) { t.Parallel() s := &fakeSink{} - var ctx context.Context - ctx = slog.Make(context.Background(), s) + l := slog.Make(s) h := func(ctx context.Context) { slog.Helper() - slog.Info(ctx, "logging in helper") + l.Info(ctx, "logging in helper") } - ctx = slog.With(ctx, slog.F( + ctx := slog.With(bg, slog.F( "ctx", 1024), ) h(ctx) @@ -86,16 +86,15 @@ func TestLogger(t *testing.T) { t.Parallel() s := &fakeSink{} - var ctx context.Context - ctx = slog.Make(context.Background(), s) - ctx = slog.Named(ctx, "hello") - ctx = slog.Named(ctx, "hello2") + l := slog.Make(s) + l = l.Named("hello") + l = l.Named("hello2") - ctx, span := trace.StartSpan(ctx, "trace") + ctx, span := trace.StartSpan(bg, "trace") ctx = slog.With(ctx, slog.F("ctx", io.EOF)) - ctx = slog.With(ctx, slog.F("with", 2)) + l = l.With(slog.F("with", 2)) - slog.Info(ctx, "meow", slog.F("hi", "xd")) + l.Info(ctx, "meow", slog.F("hi", "xd")) assert.Len(t, "entries", 1, s.entries) assert.Equal(t, "entry", slog.SinkEntry{ @@ -108,13 +107,13 @@ func TestLogger(t *testing.T) { File: slogTestFile, Func: "cdr.dev/slog_test.TestLogger.func3", - Line: 98, + Line: 97, SpanContext: span.SpanContext(), Fields: slog.M( - slog.F("ctx", io.EOF), slog.F("with", 2), + slog.F("ctx", io.EOF), slog.F("hi", "xd"), ), }, s.entries[0]) @@ -124,21 +123,20 @@ func TestLogger(t *testing.T) { t.Parallel() s := &fakeSink{} - var ctx context.Context - ctx = slog.Make(context.Background(), s) + l := slog.Make(s) exits := 0 - ctx = slog.SetExit(ctx, func(int) { + l.SetExit(func(int) { exits++ }) - ctx = slog.Leveled(ctx, slog.LevelDebug) - slog.Debug(ctx, "") - slog.Info(ctx, "") - slog.Warn(ctx, "") - slog.Error(ctx, "") - slog.Critical(ctx, "") - slog.Fatal(ctx, "") + l = l.Leveled(slog.LevelDebug) + l.Debug(bg, "") + l.Info(bg, "") + l.Warn(bg, "") + l.Error(bg, "") + l.Critical(bg, "") + l.Fatal(bg, "") assert.Len(t, "entries", 6, s.entries) assert.Equal(t, "syncs", 3, s.syncs) diff --git a/sloggers/sloghuman/sloghuman.go b/sloggers/sloghuman/sloghuman.go index 18cbb58..719db7c 100644 --- a/sloggers/sloghuman/sloghuman.go +++ b/sloggers/sloghuman/sloghuman.go @@ -17,8 +17,8 @@ import ( // // If the writer implements Sync() error then // it will be called when syncing. -func Make(ctx context.Context, w io.Writer) slog.SinkContext { - return slog.Make(ctx, &humanSink{ +func Make(w io.Writer) slog.Logger { + return slog.Make(&humanSink{ w: syncwriter.New(w), w2: w, }) @@ -29,7 +29,7 @@ type humanSink struct { w2 io.Writer } -func (s humanSink) LogEntry(_ context.Context, ent slog.SinkEntry) { +func (s humanSink) LogEntry(ctx context.Context, ent slog.SinkEntry) { str := entryhuman.Fmt(s.w2, ent) lines := strings.Split(str, "\n") diff --git a/sloggers/sloghuman/sloghuman_test.go b/sloggers/sloghuman/sloghuman_test.go index 86fe888..5c28afb 100644 --- a/sloggers/sloghuman/sloghuman_test.go +++ b/sloggers/sloghuman/sloghuman_test.go @@ -11,17 +11,18 @@ import ( "cdr.dev/slog/sloggers/sloghuman" ) +var bg = context.Background() + func TestMake(t *testing.T) { t.Parallel() b := &bytes.Buffer{} - ctx := context.Background() - ctx = sloghuman.Make(ctx, b) - slog.Info(ctx, "line1\n\nline2", slog.F("wowow", "me\nyou")) - slog.Sync(ctx) + l := sloghuman.Make(b) + l.Info(bg, "line1\n\nline2", slog.F("wowow", "me\nyou")) + l.Sync() et, rest, err := entryhuman.StripTimestamp(b.String()) assert.Success(t, "strip timestamp", err) assert.False(t, "timestamp", et.IsZero()) - assert.Equal(t, "entry", " [INFO]\t\t...\t{\"wowow\": \"me\\nyou\"}\n \"msg\": line1\n\n line2\n", rest) + assert.Equal(t, "entry", " [INFO]\t\t...\t{\"wowow\": \"me\\nyou\"}\n \"msg\": line1\n\n line2\n", rest) } diff --git a/sloggers/slogjson/slogjson.go b/sloggers/slogjson/slogjson.go index 06c0446..5069ddc 100644 --- a/sloggers/slogjson/slogjson.go +++ b/sloggers/slogjson/slogjson.go @@ -34,8 +34,8 @@ import ( // for the format. // If the writer implements Sync() error then // it will be called when syncing. -func Make(ctx context.Context, w io.Writer) context.Context { - return slog.Make(ctx, jsonSink{ +func Make(w io.Writer) slog.Logger { + return slog.Make(jsonSink{ w: syncwriter.New(w), }) } @@ -44,7 +44,7 @@ type jsonSink struct { w *syncwriter.Writer } -func (s jsonSink) LogEntry(_ context.Context, ent slog.SinkEntry) { +func (s jsonSink) LogEntry(ctx context.Context, ent slog.SinkEntry) { m := slog.M( slog.F("ts", ent.Time), slog.F("level", ent.Level), diff --git a/sloggers/slogjson/slogjson_test.go b/sloggers/slogjson/slogjson_test.go index 103be7a..77ee4e4 100644 --- a/sloggers/slogjson/slogjson_test.go +++ b/sloggers/slogjson/slogjson_test.go @@ -24,9 +24,9 @@ func TestMake(t *testing.T) { ctx, s := trace.StartSpan(bg, "meow") b := &bytes.Buffer{} - ctx = slogjson.Make(ctx, b) - ctx = slog.Named(ctx, "named") - slog.Error(ctx, "line1\n\nline2", slog.F("wowow", "me\nyou")) + l := slogjson.Make(b) + l = l.Named("named") + l.Error(ctx, "line1\n\nline2", slog.F("wowow", "me\nyou")) j := entryjson.Filter(b.String(), "ts") exp := fmt.Sprintf(`{"level":"ERROR","msg":"line1\n\nline2","caller":"%v:29","func":"cdr.dev/slog/sloggers/slogjson_test.TestMake","logger_names":["named"],"trace":"%v","span":"%v","fields":{"wowow":"me\nyou"}} diff --git a/sloggers/slogstackdriver/slogstackdriver.go b/sloggers/slogstackdriver/slogstackdriver.go index bac1294..3b4045e 100644 --- a/sloggers/slogstackdriver/slogstackdriver.go +++ b/sloggers/slogstackdriver/slogstackdriver.go @@ -17,14 +17,14 @@ import ( "cdr.dev/slog/internal/syncwriter" ) -// Make creates a slog.logger configured to write JSON logs +// Make creates a slog.Logger configured to write JSON logs // to stdout for stackdriver. // // See https://cloud.google.com/logging/docs/agent -func Make(ctx context.Context, w io.Writer) slog.SinkContext { +func Make(w io.Writer) slog.Logger { projectID, _ := metadata.ProjectID() - return slog.Make(ctx, stackdriverSink{ + return slog.Make(stackdriverSink{ projectID: projectID, w: syncwriter.New(w), }) @@ -35,7 +35,7 @@ type stackdriverSink struct { w *syncwriter.Writer } -func (s stackdriverSink) LogEntry(_ context.Context, ent slog.SinkEntry) { +func (s stackdriverSink) LogEntry(ctx context.Context, ent slog.SinkEntry) { // https://cloud.google.com/logging/docs/agent/configuration#special-fields e := slog.M( slog.F("severity", sev(ent.Level)), diff --git a/sloggers/slogstackdriver/slogstackdriver_test.go b/sloggers/slogstackdriver/slogstackdriver_test.go index 025ad9d..3a4a295 100644 --- a/sloggers/slogstackdriver/slogstackdriver_test.go +++ b/sloggers/slogstackdriver/slogstackdriver_test.go @@ -24,9 +24,9 @@ func TestStackdriver(t *testing.T) { ctx, s := trace.StartSpan(bg, "meow") b := &bytes.Buffer{} - ctx = slogstackdriver.Make(ctx, b) - ctx = slog.Named(ctx, "meow") - slog.Error(ctx, "line1\n\nline2", slog.F("wowow", "me\nyou")) + l := slogstackdriver.Make(b) + l = l.Named("meow") + l.Error(ctx, "line1\n\nline2", slog.F("wowow", "me\nyou")) j := entryjson.Filter(b.String(), "timestamp") exp := fmt.Sprintf(`{"severity":"ERROR","message":"line1\n\nline2","logging.googleapis.com/sourceLocation":{"file":"%v","line":29,"function":"cdr.dev/slog/sloggers/slogstackdriver_test.TestStackdriver"},"logging.googleapis.com/operation":{"producer":"meow"},"logging.googleapis.com/trace":"projects//traces/%v","logging.googleapis.com/spanId":"%v","logging.googleapis.com/trace_sampled":false,"wowow":"me\nyou"} diff --git a/sloggers/slogtest/assert/assert.go b/sloggers/slogtest/assert/assert.go index 7aafdd4..e246a2f 100644 --- a/sloggers/slogtest/assert/assert.go +++ b/sloggers/slogtest/assert/assert.go @@ -39,7 +39,7 @@ func Success(t testing.TB, name string, err error) { if err != nil { slogtest.Fatal(t, "unexpected error", slog.F("name", name), - slog.Err(err), + slog.Error(err), ) } } diff --git a/sloggers/slogtest/t.go b/sloggers/slogtest/t.go index ffb972a..e315184 100644 --- a/sloggers/slogtest/t.go +++ b/sloggers/slogtest/t.go @@ -18,8 +18,8 @@ import ( // Ensure all stdlib logs go through slog. func init() { - ctx := sloghuman.Make(ctx, os.Stderr) - log.SetOutput(slog.Stdlib(ctx).Writer()) + l := sloghuman.Make(os.Stderr) + log.SetOutput(slog.Stdlib(context.Background(), l).Writer()) } // Options represents the options for the logger returned @@ -30,12 +30,12 @@ type Options struct { IgnoreErrors bool } -// Make creates a logger that writes logs to tb in a human readable format. -func Make(tb testing.TB, opts *Options) slog.SinkContext { +// Make creates a Logger that writes logs to tb in a human readable format. +func Make(tb testing.TB, opts *Options) slog.Logger { if opts == nil { opts = &Options{} } - return slog.Make(context.Background(), testSink{ + return slog.Make(testSink{ tb: tb, opts: opts, }) @@ -72,30 +72,30 @@ func (ts testSink) Sync() {} var ctx = context.Background() -func l(t testing.TB) context.Context { +func l(t testing.TB) slog.Logger { return Make(t, nil) } // Debug logs the given msg and fields to t via t.Log at the debug level. func Debug(t testing.TB, msg string, fields ...slog.Field) { slog.Helper() - slog.Debug(l(t), msg, fields...) + l(t).Debug(ctx, msg, fields...) } // Info logs the given msg and fields to t via t.Log at the info level. func Info(t testing.TB, msg string, fields ...slog.Field) { slog.Helper() - slog.Info(l(t), msg, fields...) + l(t).Info(ctx, msg, fields...) } // Error logs the given msg and fields to t via t.Error at the error level. func Error(t testing.TB, msg string, fields ...slog.Field) { slog.Helper() - slog.Error(l(t), msg, fields...) + l(t).Error(ctx, msg, fields...) } // Fatal logs the given msg and fields to t via t.Fatal at the fatal level. func Fatal(t testing.TB, msg string, fields ...slog.Field) { slog.Helper() - slog.Fatal(l(t), msg, fields...) + l(t).Fatal(ctx, msg, fields...) } diff --git a/sloggers/slogtest/t_test.go b/sloggers/slogtest/t_test.go index 77942a5..9169cbe 100644 --- a/sloggers/slogtest/t_test.go +++ b/sloggers/slogtest/t_test.go @@ -31,12 +31,11 @@ func TestIgnoreErrors(t *testing.T) { t.Parallel() tb := &fakeTB{} - ctx := context.Background() - ctx = slog.Make(ctx, slogtest.Make(tb, &slogtest.Options{ + l := slog.Make(slogtest.Make(tb, &slogtest.Options{ IgnoreErrors: true, })) - slog.Error(ctx, "hello") + l.Error(bg, "hello") assert.Equal(t, "errors", 0, tb.errors) defer func() { @@ -44,9 +43,11 @@ func TestIgnoreErrors(t *testing.T) { assert.Equal(t, "fatals", 0, tb.fatals) }() - slog.Fatal(ctx, "hello") + l.Fatal(bg, "hello") } +var bg = context.Background() + type fakeTB struct { testing.TB From 086c53909eb0c0e14aacee49fe0571ae62ab2ee1 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Fri, 28 Aug 2020 12:25:54 -0400 Subject: [PATCH 07/12] Make Leveled work on nested loggers (#91) --- slog.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/slog.go b/slog.go index a291f27..3ad85d8 100644 --- a/slog.go +++ b/slog.go @@ -137,6 +137,12 @@ func (l Logger) Named(name string) Logger { // equal to or above the given level. func (l Logger) Leveled(level Level) Logger { l.level = level + l.sinks = append([]Sink(nil), l.sinks...) + for i, s := range l.sinks { + if l2, ok := s.(Logger); ok { + l.sinks[i] = l2.Leveled(level) + } + } return l } From 5519752b31c469cf4e04c220ec381eea4341f953 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Fri, 28 Aug 2020 12:29:57 -0400 Subject: [PATCH 08/12] Fix slogstackdriver test Needs project in path now. --- sloggers/slogstackdriver/slogstackdriver_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sloggers/slogstackdriver/slogstackdriver_test.go b/sloggers/slogstackdriver/slogstackdriver_test.go index 3a4a295..0c7c07b 100644 --- a/sloggers/slogstackdriver/slogstackdriver_test.go +++ b/sloggers/slogstackdriver/slogstackdriver_test.go @@ -28,9 +28,11 @@ func TestStackdriver(t *testing.T) { l = l.Named("meow") l.Error(ctx, "line1\n\nline2", slog.F("wowow", "me\nyou")) + projectID, _ := metadata.ProjectID() + j := entryjson.Filter(b.String(), "timestamp") - exp := fmt.Sprintf(`{"severity":"ERROR","message":"line1\n\nline2","logging.googleapis.com/sourceLocation":{"file":"%v","line":29,"function":"cdr.dev/slog/sloggers/slogstackdriver_test.TestStackdriver"},"logging.googleapis.com/operation":{"producer":"meow"},"logging.googleapis.com/trace":"projects//traces/%v","logging.googleapis.com/spanId":"%v","logging.googleapis.com/trace_sampled":false,"wowow":"me\nyou"} -`, slogstackdriverTestFile, s.SpanContext().TraceID, s.SpanContext().SpanID) + exp := fmt.Sprintf(`{"severity":"ERROR","message":"line1\n\nline2","logging.googleapis.com/sourceLocation":{"file":"%v","line":30,"function":"cdr.dev/slog/sloggers/slogstackdriver_test.TestStackdriver"},"logging.googleapis.com/operation":{"producer":"meow"},"logging.googleapis.com/trace":"projects/%v/traces/%v","logging.googleapis.com/spanId":"%v","logging.googleapis.com/trace_sampled":false,"wowow":"me\nyou"} +`, slogstackdriverTestFile, projectID, s.SpanContext().TraceID, s.SpanContext().SpanID) assert.Equal(t, "entry", exp, j) } From 676a68508ab2463928935b486ce10defff639a2b Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Wed, 2 Sep 2020 16:05:51 -0400 Subject: [PATCH 09/12] Add missing import in slogstackdriver_test.go --- sloggers/slogstackdriver/slogstackdriver_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/sloggers/slogstackdriver/slogstackdriver_test.go b/sloggers/slogstackdriver/slogstackdriver_test.go index 0c7c07b..5828c25 100644 --- a/sloggers/slogstackdriver/slogstackdriver_test.go +++ b/sloggers/slogstackdriver/slogstackdriver_test.go @@ -7,6 +7,7 @@ import ( "runtime" "testing" + "cloud.google.com/go/compute/metadata" "go.opencensus.io/trace" logpbtype "google.golang.org/genproto/googleapis/logging/type" From 552693cda63521f39269a927981695f67f73d3f3 Mon Sep 17 00:00:00 2001 From: Charles Moog Date: Thu, 4 Mar 2021 12:32:47 -0600 Subject: [PATCH 10/12] feat: modify slog.Logger to *not* implement slog.Sink (#95) The prior implementation allowed slog.Logger instances to themselves be passes as slog.Sink targets. This enabled many usage patterns that introduced extremely counter-intuitive behavior. --- example_helper_test.go | 2 +- example_marshaller_test.go | 2 +- example_test.go | 18 ++++++------ go.sum | 28 ------------------- s_test.go | 2 +- slog.go | 18 ++++++------ slog_test.go | 7 +++-- sloggers/sloghuman/sloghuman.go | 8 +++--- sloggers/sloghuman/sloghuman_test.go | 2 +- sloggers/slogjson/slogjson.go | 4 +-- sloggers/slogjson/slogjson_test.go | 2 +- sloggers/slogstackdriver/slogstackdriver.go | 8 +++--- .../slogstackdriver/slogstackdriver_test.go | 2 +- sloggers/slogtest/t.go | 2 +- sloggers/slogtest/t_test.go | 5 ++-- 15 files changed, 42 insertions(+), 68 deletions(-) diff --git a/example_helper_test.go b/example_helper_test.go index e06d372..549ac3d 100644 --- a/example_helper_test.go +++ b/example_helper_test.go @@ -17,7 +17,7 @@ func httpLogHelper(ctx context.Context, status int) { ) } -var l = sloghuman.Make(os.Stdout) +var l = slog.Make(sloghuman.Sink(os.Stdout)) func ExampleHelper() { ctx := context.Background() diff --git a/example_marshaller_test.go b/example_marshaller_test.go index e4e6992..0a03d74 100644 --- a/example_marshaller_test.go +++ b/example_marshaller_test.go @@ -21,7 +21,7 @@ func (s myStruct) MarshalJSON() ([]byte, error) { } func Example_marshaller() { - l := sloghuman.Make(os.Stdout) + l := slog.Make(sloghuman.Sink(os.Stdout)) l.Info(context.Background(), "wow", slog.F("myStruct", myStruct{ diff --git a/example_test.go b/example_test.go index f3577ac..c5e84ce 100644 --- a/example_test.go +++ b/example_test.go @@ -18,7 +18,7 @@ import ( ) func Example() { - log := sloghuman.Make(os.Stdout) + log := slog.Make(sloghuman.Sink(os.Stdout)) log.Info(context.Background(), "my message here", slog.F("field_name", "something or the other"), @@ -45,7 +45,7 @@ func Example() { } func Example_struct() { - l := sloghuman.Make(os.Stdout) + l := slog.Make(sloghuman.Sink(os.Stdout)) type hello struct { Meow int `json:"meow"` @@ -76,7 +76,7 @@ func Example_testing() { } func Example_tracing() { - log := sloghuman.Make(os.Stdout) + log := slog.Make(sloghuman.Sink(os.Stdout)) ctx, _ := trace.StartSpan(context.Background(), "spanName") @@ -86,14 +86,14 @@ func Example_tracing() { } func Example_multiple() { - l := sloghuman.Make(os.Stdout) + l := slog.Make(sloghuman.Sink(os.Stdout)) f, err := os.OpenFile("stackdriver", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) if err != nil { l.Fatal(context.Background(), "failed to open stackdriver log file", slog.Error(err)) } - l = slog.Make(l, slogstackdriver.Make(f)) + l = l.AppendSinks(slogstackdriver.Sink(f)) l.Info(context.Background(), "log to stdout and stackdriver") @@ -103,7 +103,7 @@ func Example_multiple() { func ExampleWith() { ctx := slog.With(context.Background(), slog.F("field", 1)) - l := sloghuman.Make(os.Stdout) + l := slog.Make(sloghuman.Sink(os.Stdout)) l.Info(ctx, "msg") // 2019-12-07 20:54:23.986 [INFO] msg {"field": 1} @@ -111,7 +111,7 @@ func ExampleWith() { func ExampleStdlib() { ctx := slog.With(context.Background(), slog.F("field", 1)) - l := slog.Stdlib(ctx, sloghuman.Make(os.Stdout)) + l := slog.Stdlib(ctx, slog.Make(sloghuman.Sink(os.Stdout))) l.Print("msg") @@ -121,7 +121,7 @@ func ExampleStdlib() { func ExampleLogger_Named() { ctx := context.Background() - l := sloghuman.Make(os.Stdout) + l := slog.Make(sloghuman.Sink(os.Stdout)) l = l.Named("http") l.Info(ctx, "received request", slog.F("remote address", net.IPv4(127, 0, 0, 1))) @@ -131,7 +131,7 @@ func ExampleLogger_Named() { func ExampleLogger_Leveled() { ctx := context.Background() - l := sloghuman.Make(os.Stdout) + l := slog.Make(sloghuman.Sink(os.Stdout)) l.Debug(ctx, "testing1") l.Info(ctx, "received request") diff --git a/go.sum b/go.sum index 6bd2924..623713b 100644 --- a/go.sum +++ b/go.sum @@ -33,11 +33,9 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E= github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ= github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dlclark/regexp2 v1.1.6 h1:CqB4MjHw0MFCDj+PHHjiESmHX+N7t0tJzKvC6M97BRg= github.com/dlclark/regexp2 v1.1.6/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk= github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= @@ -48,7 +46,6 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 h1:uHTyIjqVhYRhLbJ8nIiOJHkEZZ+5YoOsAbD3sk82NiE= github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -56,24 +53,20 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.2-0.20191216170541-340f1ebe299e h1:4WfjkTUTsO6siF8ghDQQk6t7x/FPsv3w6MXkc47do7Q= github.com/google/go-cmp v0.3.2-0.20191216170541-340f1ebe299e/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gorilla/csrf v1.6.0/go.mod h1:7tSf8kmjNYr7IWDCYhd3U8Ck34iQ/Yw5CJu7bAkHEGI= github.com/gorilla/handlers v1.4.1/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= @@ -90,7 +83,6 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -100,9 +92,7 @@ github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGe github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E= -github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -112,7 +102,6 @@ github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -122,10 +111,8 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -139,9 +126,7 @@ golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+o golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422 h1:QzoH/1pFpZguR8NrRHLcO6jKqfv2zpuSqZLgdm7ZmjI= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -158,20 +143,16 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -179,7 +160,6 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd h1:r7DufRZuZbWB7j439YfAzP8RPDa9unLkpwQKUYbIMPI= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -196,7 +176,6 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd h1:/e+gpKk9r3dJobndpTytxS2gOy6m5uvpg+ISQoEcusQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -205,18 +184,15 @@ golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0 h1:Dh6fw+p6FyRl5x/FvNswO1ji0lIGzm3KP8Y9VkS9PTE= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0 h1:9sdfJOzWlkqPltHAuzT2Cp+yrBeY1KRVYgms8soxMwM= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -224,12 +200,10 @@ google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsb google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb h1:i1Ppqkc3WQXikh8bXiwHqAN5Rv3/qDCcRk0/Otx73BY= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= @@ -239,9 +213,7 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1 h1:aQktFqmDE2yjveXJlVIfslDFmFnUXSqG0i6KRcJAeMc= google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1 h1:wdKvqQk7IttEw92GoRyKG2IDrUIpgpj6H6m81yfeMW0= diff --git a/s_test.go b/s_test.go index a589460..b79e2b6 100644 --- a/s_test.go +++ b/s_test.go @@ -14,7 +14,7 @@ func TestStdlib(t *testing.T) { t.Parallel() b := &bytes.Buffer{} - l := slog.Make(sloghuman.Make(b)).With( + l := slog.Make(sloghuman.Sink(b)).With( slog.F("hi", "we"), ) stdlibLog := slog.Stdlib(bg, l) diff --git a/slog.go b/slog.go index 3ad85d8..f89530d 100644 --- a/slog.go +++ b/slog.go @@ -29,11 +29,11 @@ type Sink interface { Sync() } -// LogEntry logs the given entry with the context to the +// Log logs the given entry with the context to the // underlying sinks. // // It extends the entry with the set fields and names. -func (l Logger) LogEntry(ctx context.Context, e SinkEntry) { +func (l Logger) Log(ctx context.Context, e SinkEntry) { if e.Level < l.level { return } @@ -138,17 +138,19 @@ func (l Logger) Named(name string) Logger { func (l Logger) Leveled(level Level) Logger { l.level = level l.sinks = append([]Sink(nil), l.sinks...) - for i, s := range l.sinks { - if l2, ok := s.(Logger); ok { - l.sinks[i] = l2.Leveled(level) - } - } + return l +} + +// AppendSinks appends the sinks to the set sink +// targets on the logger. +func (l Logger) AppendSinks(s ...Sink) Logger { + l.sinks = append(l.sinks, s...) return l } func (l Logger) log(ctx context.Context, level Level, msg string, fields Map) { ent := l.entry(ctx, level, msg, fields) - l.LogEntry(ctx, ent) + l.Log(ctx, ent) } func (l Logger) entry(ctx context.Context, level Level, msg string, fields Map) SinkEntry { diff --git a/slog_test.go b/slog_test.go index a55def2..b708b51 100644 --- a/slog_test.go +++ b/slog_test.go @@ -38,8 +38,9 @@ func TestLogger(t *testing.T) { s1 := &fakeSink{} s2 := &fakeSink{} - l := slog.Make(s1, s2) + l := slog.Make(s1) l = l.Leveled(slog.LevelError) + l = l.AppendSinks(s2) l.Info(bg, "wow", slog.Error(io.EOF)) l.Error(bg, "meow", slog.Error(io.ErrUnexpectedEOF)) @@ -74,7 +75,7 @@ func TestLogger(t *testing.T) { File: slogTestFile, Func: "cdr.dev/slog_test.TestLogger.func2", - Line: 66, + Line: 67, Fields: slog.M( slog.F("ctx", 1024), @@ -107,7 +108,7 @@ func TestLogger(t *testing.T) { File: slogTestFile, Func: "cdr.dev/slog_test.TestLogger.func3", - Line: 97, + Line: 98, SpanContext: span.SpanContext(), diff --git a/sloggers/sloghuman/sloghuman.go b/sloggers/sloghuman/sloghuman.go index 719db7c..b872c74 100644 --- a/sloggers/sloghuman/sloghuman.go +++ b/sloggers/sloghuman/sloghuman.go @@ -12,16 +12,16 @@ import ( "cdr.dev/slog/internal/syncwriter" ) -// Make creates a logger that writes logs in a human +// Sink creates a slog.Sink that writes logs in a human // readable YAML like format to the given writer. // // If the writer implements Sync() error then // it will be called when syncing. -func Make(w io.Writer) slog.Logger { - return slog.Make(&humanSink{ +func Sink(w io.Writer) slog.Sink { + return &humanSink{ w: syncwriter.New(w), w2: w, - }) + } } type humanSink struct { diff --git a/sloggers/sloghuman/sloghuman_test.go b/sloggers/sloghuman/sloghuman_test.go index 5c28afb..a315ca7 100644 --- a/sloggers/sloghuman/sloghuman_test.go +++ b/sloggers/sloghuman/sloghuman_test.go @@ -17,7 +17,7 @@ func TestMake(t *testing.T) { t.Parallel() b := &bytes.Buffer{} - l := sloghuman.Make(b) + l := slog.Make(sloghuman.Sink(b)) l.Info(bg, "line1\n\nline2", slog.F("wowow", "me\nyou")) l.Sync() diff --git a/sloggers/slogjson/slogjson.go b/sloggers/slogjson/slogjson.go index 5069ddc..93798df 100644 --- a/sloggers/slogjson/slogjson.go +++ b/sloggers/slogjson/slogjson.go @@ -29,12 +29,12 @@ import ( "cdr.dev/slog/internal/syncwriter" ) -// Make creates a logger that writes JSON logs +// Sink creates a slog.Sink that writes JSON logs // to the given writer. See package level docs // for the format. // If the writer implements Sync() error then // it will be called when syncing. -func Make(w io.Writer) slog.Logger { +func Sink(w io.Writer) slog.Logger { return slog.Make(jsonSink{ w: syncwriter.New(w), }) diff --git a/sloggers/slogjson/slogjson_test.go b/sloggers/slogjson/slogjson_test.go index 77ee4e4..769790d 100644 --- a/sloggers/slogjson/slogjson_test.go +++ b/sloggers/slogjson/slogjson_test.go @@ -24,7 +24,7 @@ func TestMake(t *testing.T) { ctx, s := trace.StartSpan(bg, "meow") b := &bytes.Buffer{} - l := slogjson.Make(b) + l := slogjson.Sink(b) l = l.Named("named") l.Error(ctx, "line1\n\nline2", slog.F("wowow", "me\nyou")) diff --git a/sloggers/slogstackdriver/slogstackdriver.go b/sloggers/slogstackdriver/slogstackdriver.go index 3b4045e..b49a97e 100644 --- a/sloggers/slogstackdriver/slogstackdriver.go +++ b/sloggers/slogstackdriver/slogstackdriver.go @@ -17,17 +17,17 @@ import ( "cdr.dev/slog/internal/syncwriter" ) -// Make creates a slog.Logger configured to write JSON logs +// Sink creates a slog.Sink configured to write JSON logs // to stdout for stackdriver. // // See https://cloud.google.com/logging/docs/agent -func Make(w io.Writer) slog.Logger { +func Sink(w io.Writer) slog.Sink { projectID, _ := metadata.ProjectID() - return slog.Make(stackdriverSink{ + return stackdriverSink{ projectID: projectID, w: syncwriter.New(w), - }) + } } type stackdriverSink struct { diff --git a/sloggers/slogstackdriver/slogstackdriver_test.go b/sloggers/slogstackdriver/slogstackdriver_test.go index 5828c25..2ef8e81 100644 --- a/sloggers/slogstackdriver/slogstackdriver_test.go +++ b/sloggers/slogstackdriver/slogstackdriver_test.go @@ -25,7 +25,7 @@ func TestStackdriver(t *testing.T) { ctx, s := trace.StartSpan(bg, "meow") b := &bytes.Buffer{} - l := slogstackdriver.Make(b) + l := slog.Make(slogstackdriver.Sink(b)) l = l.Named("meow") l.Error(ctx, "line1\n\nline2", slog.F("wowow", "me\nyou")) diff --git a/sloggers/slogtest/t.go b/sloggers/slogtest/t.go index e315184..2c7af7b 100644 --- a/sloggers/slogtest/t.go +++ b/sloggers/slogtest/t.go @@ -18,7 +18,7 @@ import ( // Ensure all stdlib logs go through slog. func init() { - l := sloghuman.Make(os.Stderr) + l := slog.Make(sloghuman.Sink(os.Stderr)) log.SetOutput(slog.Stdlib(context.Background(), l).Writer()) } diff --git a/sloggers/slogtest/t_test.go b/sloggers/slogtest/t_test.go index 9169cbe..cdac6ec 100644 --- a/sloggers/slogtest/t_test.go +++ b/sloggers/slogtest/t_test.go @@ -4,7 +4,6 @@ import ( "context" "testing" - "cdr.dev/slog" "cdr.dev/slog/internal/assert" "cdr.dev/slog/sloggers/slogtest" ) @@ -31,9 +30,9 @@ func TestIgnoreErrors(t *testing.T) { t.Parallel() tb := &fakeTB{} - l := slog.Make(slogtest.Make(tb, &slogtest.Options{ + l := slogtest.Make(tb, &slogtest.Options{ IgnoreErrors: true, - })) + }) l.Error(bg, "hello") assert.Equal(t, "errors", 0, tb.errors) From 14b50c2d4df3dbcc44d9784c6aeb63a3994b41ba Mon Sep 17 00:00:00 2001 From: Charlie Moog Date: Thu, 4 Mar 2021 18:40:36 +0000 Subject: [PATCH 11/12] fix: return sink from slogjson.Sink --- sloggers/slogjson/slogjson.go | 6 +++--- sloggers/slogjson/slogjson_test.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sloggers/slogjson/slogjson.go b/sloggers/slogjson/slogjson.go index 93798df..8340767 100644 --- a/sloggers/slogjson/slogjson.go +++ b/sloggers/slogjson/slogjson.go @@ -34,10 +34,10 @@ import ( // for the format. // If the writer implements Sync() error then // it will be called when syncing. -func Sink(w io.Writer) slog.Logger { - return slog.Make(jsonSink{ +func Sink(w io.Writer) slog.Sink { + return jsonSink{ w: syncwriter.New(w), - }) + } } type jsonSink struct { diff --git a/sloggers/slogjson/slogjson_test.go b/sloggers/slogjson/slogjson_test.go index 769790d..46f8590 100644 --- a/sloggers/slogjson/slogjson_test.go +++ b/sloggers/slogjson/slogjson_test.go @@ -24,7 +24,7 @@ func TestMake(t *testing.T) { ctx, s := trace.StartSpan(bg, "meow") b := &bytes.Buffer{} - l := slogjson.Sink(b) + l := slog.Make(slogjson.Sink(b)) l = l.Named("named") l.Error(ctx, "line1\n\nline2", slog.F("wowow", "me\nyou")) From 751ddae4f9b16cec7f4c05e4a820f2b483682701 Mon Sep 17 00:00:00 2001 From: Charlie Moog Date: Thu, 4 Mar 2021 19:05:10 +0000 Subject: [PATCH 12/12] fix: use proper Sink API in README example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9fd2562..b7ed085 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ go get cdr.dev/slog Many more examples available at [godoc](https://godoc.org/cdr.dev/slog#pkg-examples). ```go -log := sloghuman.Make(os.Stdout) +log := slog.Make(sloghuman.Sink(os.Stdout)) log.Info(context.Background(), "my message here", slog.F("field_name", "something or the other"),