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 d44a049..0000000 --- a/context.go +++ /dev/null @@ -1,33 +0,0 @@ -package slog - -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 { - 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/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 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