Skip to content

Commit 2b49514

Browse files
committed
Refactor for Context only logging
1 parent f7a90d8 commit 2b49514

21 files changed

+230
-150
lines changed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ go get cdr.dev/slog
3535
Many more examples available at [godoc](https://godoc.org/cdr.dev/slog#pkg-examples).
3636

3737
```go
38-
log := sloghuman.Make(os.Stdout)
38+
ctx := sloghuman.Make(ctx, os.Stdout)
3939

40-
log.Info(context.Background(), "my message here",
40+
slog.Info(ctx, "my message here",
4141
slog.F("field_name", "something or the other"),
4242
slog.F("some_map", slog.M(
4343
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.
8787

8888
1. Full [context.Context](https://blog.golang.org/context) support
8989
- `slog` lets you set fields in a `context.Context` such that any log with the context prints those fields.
90+
- `slog` stores the actual logger in the `context.Context`, following the example of
91+
[the Go trace library](https://golang.org/pkg/runtime/trace/). Our logger doesn't bloat type and function signatures.
9092
- We wanted to be able to pull up all relevant logs for a given trace, user or request. With zap, we were plugging
9193
these fields in for every relevant log or passing around a logger with the fields set. This became very verbose.
9294

context.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package slog
2+
3+
import "context"
4+
5+
type loggerCtxKey = struct{}
6+
7+
// SinkContext is used by slog.Make to compose many loggers together.
8+
type SinkContext struct {
9+
Sink
10+
context.Context
11+
}
12+
13+
func contextWithLogger(ctx context.Context, l logger) SinkContext {
14+
ctx = context.WithValue(ctx, loggerCtxKey{}, l)
15+
return SinkContext{
16+
Context: ctx,
17+
Sink: l,
18+
}
19+
}
20+
21+
func loggerFromContext(ctx context.Context) (logger, bool) {
22+
v := ctx.Value(loggerCtxKey{})
23+
if v == nil {
24+
return logger{}, false
25+
}
26+
return v.(logger), true
27+
}

example_helper_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ import (
1212
func httpLogHelper(ctx context.Context, status int) {
1313
slog.Helper()
1414

15-
l.Info(ctx, "sending HTTP response",
15+
slog.Info(ctx, "sending HTTP response",
1616
slog.F("status", status),
1717
)
1818
}
1919

20-
var l = sloghuman.Make(os.Stdout)
20+
var l = sloghuman.Make(context.Background(), os.Stdout)
2121

2222
func ExampleHelper() {
2323
ctx := context.Background()

example_marshaller_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ func (s myStruct) MarshalJSON() ([]byte, error) {
2121
}
2222

2323
func Example_marshaller() {
24-
l := sloghuman.Make(os.Stdout)
24+
ctx := sloghuman.Make(context.Background(), os.Stdout)
2525

26-
l.Info(context.Background(), "wow",
26+
slog.Info(ctx, "wow",
2727
slog.F("myStruct", myStruct{
2828
foo: 1,
2929
bar: 2,

example_test.go

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@ import (
1818
)
1919

2020
func Example() {
21-
log := sloghuman.Make(os.Stdout)
21+
ctx := sloghuman.Make(context.Background(), os.Stdout)
2222

23-
log.Info(context.Background(), "my message here",
23+
slog.Info(ctx, "my message here",
2424
slog.F("field_name", "something or the other"),
2525
slog.F("some_map", slog.M(
2626
slog.F("nested_fields", time.Date(2000, time.February, 5, 4, 4, 4, 0, time.UTC)),
2727
)),
28-
slog.Error(
28+
slog.Err(
2929
xerrors.Errorf("wrap1: %w",
3030
xerrors.Errorf("wrap2: %w",
3131
io.EOF,
@@ -45,15 +45,15 @@ func Example() {
4545
}
4646

4747
func Example_struct() {
48-
l := sloghuman.Make(os.Stdout)
48+
ctx := sloghuman.Make(context.Background(), os.Stdout)
4949

5050
type hello struct {
5151
Meow int `json:"meow"`
5252
Bar string `json:"bar"`
5353
M time.Time `json:"m"`
5454
}
5555

56-
l.Info(context.Background(), "check out my structure",
56+
slog.Info(ctx, "check out my structure",
5757
slog.F("hello", hello{
5858
Meow: 1,
5959
Bar: "barbar",
@@ -76,68 +76,69 @@ func Example_testing() {
7676
}
7777

7878
func Example_tracing() {
79-
log := sloghuman.Make(os.Stdout)
79+
var ctx context.Context
80+
ctx = sloghuman.Make(context.Background(), os.Stdout)
8081

81-
ctx, _ := trace.StartSpan(context.Background(), "spanName")
82+
ctx, _ = trace.StartSpan(ctx, "spanName")
8283

83-
log.Info(ctx, "my msg", slog.F("hello", "hi"))
84+
slog.Info(ctx, "my msg", slog.F("hello", "hi"))
8485

8586
// 2019-12-09 21:59:48.110 [INFO] <example_test.go:62> my msg {"trace": "f143d018d00de835688453d8dc55c9fd", "span": "f214167bf550afc3", "hello": "hi"}
8687
}
8788

8889
func Example_multiple() {
89-
l := sloghuman.Make(os.Stdout)
90+
ctx := sloghuman.Make(context.Background(), os.Stdout)
9091

9192
f, err := os.OpenFile("stackdriver", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
9293
if err != nil {
93-
l.Fatal(context.Background(), "failed to open stackdriver log file", slog.Error(err))
94+
slog.Fatal(ctx, "failed to open stackdriver log file", slog.Err(err))
9495
}
9596

96-
l = slog.Make(l, slogstackdriver.Make(f))
97+
ctx = slog.Make(l, slogstackdriver.Make(ctx, f))
9798

98-
l.Info(context.Background(), "log to stdout and stackdriver")
99+
slog.Info(ctx, "log to stdout and stackdriver")
99100

100101
// 2019-12-07 20:59:55.790 [INFO] <example_test.go:46> log to stdout and stackdriver
101102
}
102103

103104
func ExampleWith() {
104105
ctx := slog.With(context.Background(), slog.F("field", 1))
105106

106-
l := sloghuman.Make(os.Stdout)
107-
l.Info(ctx, "msg")
107+
ctx = sloghuman.Make(ctx, os.Stdout)
108+
slog.Info(ctx, "msg")
108109

109110
// 2019-12-07 20:54:23.986 [INFO] <example_test.go:20> msg {"field": 1}
110111
}
111112

112113
func ExampleStdlib() {
113114
ctx := slog.With(context.Background(), slog.F("field", 1))
114-
l := slog.Stdlib(ctx, sloghuman.Make(os.Stdout))
115+
l := slog.Stdlib(sloghuman.Make(ctx, os.Stdout))
115116

116117
l.Print("msg")
117118

118119
// 2019-12-07 20:54:23.986 [INFO] (stdlib) <example_test.go:29> msg {"field": 1}
119120
}
120121

121-
func ExampleLogger_Named() {
122+
func ExampleNamed() {
122123
ctx := context.Background()
123124

124-
l := sloghuman.Make(os.Stdout)
125-
l = l.Named("http")
126-
l.Info(ctx, "received request", slog.F("remote address", net.IPv4(127, 0, 0, 1)))
125+
ctx = sloghuman.Make(ctx, os.Stdout)
126+
ctx = slog.Named(ctx, "http")
127+
slog.Info(ctx, "received request", slog.F("remote address", net.IPv4(127, 0, 0, 1)))
127128

128129
// 2019-12-07 21:20:56.974 [INFO] (http) <example_test.go:85> received request {"remote address": "127.0.0.1"}
129130
}
130131

131-
func ExampleLogger_Leveled() {
132+
func ExampleLeveled() {
132133
ctx := context.Background()
133134

134-
l := sloghuman.Make(os.Stdout)
135-
l.Debug(ctx, "testing1")
136-
l.Info(ctx, "received request")
135+
ctx = sloghuman.Make(ctx, os.Stdout)
136+
slog.Debug(ctx, "testing1")
137+
slog.Info(ctx, "received request")
137138

138-
l = l.Leveled(slog.LevelDebug)
139+
ctx = slog.Leveled(ctx, slog.LevelDebug)
139140

140-
l.Debug(ctx, "testing2")
141+
slog.Debug(ctx, "testing2")
141142

142143
// 2019-12-07 21:26:20.945 [INFO] <example_test.go:95> received request
143144
// 2019-12-07 21:26:20.945 [DEBUG] <example_test.go:99> testing2

export_test.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
package slog
22

3-
func (l *Logger) SetExit(fn func(int)) {
3+
import "context"
4+
5+
func SetExit(ctx context.Context, fn func(int)) context.Context {
6+
l, ok := loggerFromContext(ctx)
7+
if !ok {
8+
return ctx
9+
}
410
l.exit = fn
11+
return contextWithLogger(ctx, l)
512
}

map.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ func encodeJSON(v interface{}) []byte {
128128
b, err := json.Marshal(v)
129129
if err != nil {
130130
return encode(M(
131-
Error(xerrors.Errorf("failed to marshal to JSON: %w", err)),
131+
Err(xerrors.Errorf("failed to marshal to JSON: %w", err)),
132132
F("type", reflect.TypeOf(v)),
133133
F("value", fmt.Sprintf("%+v", v)),
134134
))

map_test.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ func TestMap(t *testing.T) {
3737
}
3838

3939
test(t, slog.M(
40-
slog.Error(
40+
slog.Err(
4141
xerrors.Errorf("wrap1: %w",
4242
xerrors.Errorf("wrap2: %w",
4343
io.EOF,
@@ -222,10 +222,6 @@ func TestMap(t *testing.T) {
222222
})
223223
}
224224

225-
type meow struct {
226-
a int
227-
}
228-
229225
func indentJSON(t *testing.T, j string) string {
230226
b := &bytes.Buffer{}
231227
err := json.Indent(b, []byte(j), "", strings.Repeat(" ", 4))

s.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package slog
33
import (
44
"context"
55
"log"
6+
"os"
67
"strings"
78
)
89

@@ -15,22 +16,26 @@ import (
1516
// You can redirect the stdlib default logger with log.SetOutput
1617
// to the Writer on the logger returned by this function.
1718
// See the example.
18-
func Stdlib(ctx context.Context, l Logger) *log.Logger {
19-
l.skip += 3
19+
func Stdlib(ctx context.Context) *log.Logger {
20+
ctx = Named(ctx, "stdlib")
2021

21-
l = l.Named("stdlib")
22+
l, ok := loggerFromContext(ctx)
23+
if !ok {
24+
// Give stderr logger if no slog.
25+
return log.New(os.Stderr, "", 0)
26+
}
27+
l.skip += 3
28+
ctx = contextWithLogger(ctx, l)
2229

2330
w := &stdlogWriter{
2431
ctx: ctx,
25-
l: l,
2632
}
2733

2834
return log.New(w, "", 0)
2935
}
3036

3137
type stdlogWriter struct {
3238
ctx context.Context
33-
l Logger
3439
}
3540

3641
func (w stdlogWriter) Write(p []byte) (n int, err error) {
@@ -39,7 +44,7 @@ func (w stdlogWriter) Write(p []byte) (n int, err error) {
3944
// we do not want.
4045
msg = strings.TrimSuffix(msg, "\n")
4146

42-
w.l.Info(w.ctx, msg)
47+
Info(w.ctx, msg)
4348

4449
return len(p), nil
4550
}

s_test.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package slog_test
22

33
import (
44
"bytes"
5+
"context"
56
"testing"
67

78
"cdr.dev/slog"
@@ -14,14 +15,16 @@ func TestStdlib(t *testing.T) {
1415
t.Parallel()
1516

1617
b := &bytes.Buffer{}
17-
l := slog.Make(sloghuman.Make(b)).With(
18+
ctx := context.Background()
19+
ctx = slog.Make(sloghuman.Make(ctx, b))
20+
ctx = slog.With(ctx,
1821
slog.F("hi", "we"),
1922
)
20-
stdlibLog := slog.Stdlib(bg, l)
23+
stdlibLog := slog.Stdlib(ctx)
2124
stdlibLog.Println("stdlib")
2225

2326
et, rest, err := entryhuman.StripTimestamp(b.String())
2427
assert.Success(t, "strip timestamp", err)
2528
assert.False(t, "timestamp", et.IsZero())
26-
assert.Equal(t, "entry", " [INFO]\t(stdlib)\t<s_test.go:21>\tstdlib\t{\"hi\": \"we\"}\n", rest)
29+
assert.Equal(t, "entry", " [INFO]\t(stdlib)\t<s_test.go:24>\tstdlib\t{\"hi\": \"we\"}\n", rest)
2730
}

0 commit comments

Comments
 (0)