Skip to content

Commit 20ab272

Browse files
committed
Update docs for V5
1 parent c53c4ed commit 20ab272

File tree

9 files changed

+85
-79
lines changed

9 files changed

+85
-79
lines changed

README.md

+3-5
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ go get cdr.dev/slog
2323
- Prints multiline fields and errors nicely
2424
- Machine readable JSON output
2525
- [GCP Stackdriver](https://godoc.org/cdr.dev/slog/sloggers/slogstackdriver) support
26-
- [Tee](https://godoc.org/cdr.dev/slog#Tee) multiple loggers
2726
- [Stdlib](https://godoc.org/cdr.dev/slog#Stdlib) log adapter
2827
- Skip caller frames with [slog.Helper](https://godoc.org/cdr.dev/slog#Helper)
2928
- Encodes values as if with `json.Marshal`
3029
- Transparently log [opencensus](https://godoc.org/go.opencensus.io/trace) trace and span IDs
3130
- [Single dependency](https://godoc.org/cdr.dev/slog?imports) on go.opencensus.io
31+
- Log to multiple sinks
3232

3333
## Example
3434

@@ -92,10 +92,8 @@ Here is a list of reasons how we improved on zap with slog.
9292

9393
1. Simple and easy to extend
9494
- A new backend only has to implement the simple Sink interface.
95-
- The logger type provides a nice API around Sink and itself implements
96-
Sink allowing for composition and wrapping. E.g. one could implement
97-
a Sink that wraps around another sink to ensure no logs with a given
98-
component are logged.
95+
- The Logger type provides a nice API around Sink but also implements
96+
Sink to allow for composition.
9997
- zap is hard and confusing to extend. There are too many structures and configuration options.
10098

10199
1. Structured logging of Go structures with `json.Marshal`

internal/entryhuman/entry.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,10 @@ func Fmt(w io.Writer, ent slog.SinkEntry) string {
5858
level = c(w, levelColor(ent.Level)).Sprint(level)
5959
ents += fmt.Sprintf("%v\t", level)
6060

61-
if len(ent.Names) > 0 {
62-
component := "(" + quoteKey(strings.Join(ent.Names, ".")) + ")"
63-
component = c(w, color.FgMagenta).Sprint(component)
64-
ents += fmt.Sprintf("%v\t", component)
61+
if len(ent.LoggerNames) > 0 {
62+
loggerName := "(" + quoteKey(strings.Join(ent.LoggerNames, ".")) + ")"
63+
loggerName = c(w, color.FgMagenta).Sprint(loggerName)
64+
ents += fmt.Sprintf("%v\t", loggerName)
6565
}
6666

6767
loc := fmt.Sprintf("<%v:%v>", filepath.Base(ent.File), ent.Line)

internal/entryhuman/entry_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ line2`)
5151
t.Parallel()
5252

5353
test(t, slog.SinkEntry{
54-
Level: slog.LevelWarn,
55-
Names: []string{"named", "meow"},
54+
Level: slog.LevelWarn,
55+
LoggerNames: []string{"named", "meow"},
5656
}, `0001-01-01 00:00:00.000 [WARN] (named.meow) <.:0> ""`)
5757
})
5858

map.go

+7
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,10 @@ func (p *xerrorPrinter) write(s string) {
147147
p.e.Loc = s
148148
}
149149
}
150+
151+
func (m Map) append(m2 Map) Map {
152+
m3 := make(Map, 0, len(m)+len(m2))
153+
m3 = append(m3, m...)
154+
m3 = append(m3, m2...)
155+
return m3
156+
}

slog.go

+59-58
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
//
55
// The examples are the best way to understand how to use this library effectively.
66
//
7-
// This package provides a high level API around the Sink interface.
8-
// The implementations are in the sloggers subdirectory.
7+
// The Logger type implements a high level API around the Sink interface.
8+
// Logger implements Sink as well to allow composition.
9+
//
10+
// Implementations of the Sink interface are available in the sloggers subdirectory.
911
package slog // import "cdr.dev/slog"
1012

1113
import (
@@ -37,7 +39,7 @@ func (l Logger) LogEntry(ctx context.Context, e SinkEntry) {
3739
}
3840

3941
e.Fields = l.fields.append(e.Fields)
40-
e.Names = appendName(e.Names, l.names...)
42+
e.LoggerNames = appendNames(l.names, e.LoggerNames...)
4143

4244
for _, s := range l.sinks {
4345
s.LogEntry(ctx, e)
@@ -51,16 +53,17 @@ func (l Logger) Sync() {
5153
}
5254
}
5355

54-
// Logger wraps Sink with a easy to use API.
56+
// Logger wraps Sink with a nice API to log entries.
5557
//
5658
// Logger is safe for concurrent use.
5759
type Logger struct {
60+
sinks []Sink
61+
level Level
62+
5863
names []string
59-
sinks []Sink
60-
skip int
6164
fields Map
62-
level Level
6365

66+
skip int
6467
exit func(int)
6568
}
6669

@@ -91,54 +94,42 @@ func (l Logger) Warn(ctx context.Context, msg string, fields ...Field) {
9194

9295
// Error logs the msg and fields at LevelError.
9396
//
94-
// It will also Sync() before returning.
97+
// It will then Sync().
9598
func (l Logger) Error(ctx context.Context, msg string, fields ...Field) {
9699
l.log(ctx, LevelError, msg, fields)
97100
l.Sync()
98101
}
99102

100103
// Critical logs the msg and fields at LevelCritical.
101104
//
102-
// It will also Sync() before returning.
105+
// It will then Sync().
103106
func (l Logger) Critical(ctx context.Context, msg string, fields ...Field) {
104107
l.log(ctx, LevelCritical, msg, fields)
105108
l.Sync()
106109
}
107110

108111
// Fatal logs the msg and fields at LevelFatal.
109112
//
110-
// It will also Sync() before returning.
113+
// It will then Sync() and os.Exit(1).
111114
func (l Logger) Fatal(ctx context.Context, msg string, fields ...Field) {
112115
l.log(ctx, LevelFatal, msg, fields)
113116
l.Sync()
114117
l.exit(1)
115118
}
116119

117-
var helpers sync.Map
118-
119-
// Helper marks the calling function as a helper
120-
// and skips it for source location information.
121-
// It's the slog equivalent of testing.TB.Helper().
122-
func Helper() {
123-
_, _, fn := location(1)
124-
helpers.LoadOrStore(fn, struct{}{})
125-
}
126-
127120
// With returns a Logger that prepends the given fields on every
128121
// logged entry.
122+
//
129123
// It will append to any fields already in the Logger.
130124
func (l Logger) With(fields ...Field) Logger {
131125
l.fields = l.fields.append(fields)
132126
return l
133127
}
134128

135-
// Named names the logger.
136-
// If there is already a name set, it will be joined by ".".
137-
// E.g. if the name is currently "my_component" and then later
138-
// the name "my_pkg" is set, then the final component will be
139-
// "my_component.my_pkg".
129+
// Named appends the name to the set names
130+
// on the logger.
140131
func (l Logger) Named(name string) Logger {
141-
l.names = appendName(l.names, name)
132+
l.names = appendNames(l.names, name)
142133
return l
143134
}
144135

@@ -149,6 +140,33 @@ func (l Logger) Leveled(level Level) Logger {
149140
return l
150141
}
151142

143+
func (l Logger) log(ctx context.Context, level Level, msg string, fields Map) {
144+
ent := l.entry(ctx, level, msg, fields)
145+
l.LogEntry(ctx, ent)
146+
}
147+
148+
func (l Logger) entry(ctx context.Context, level Level, msg string, fields Map) SinkEntry {
149+
ent := SinkEntry{
150+
Time: time.Now().UTC(),
151+
Level: level,
152+
Message: msg,
153+
Fields: fieldsFromContext(ctx).append(fields),
154+
SpanContext: trace.FromContext(ctx).SpanContext(),
155+
}
156+
ent = ent.fillLoc(l.skip + 3)
157+
return ent
158+
}
159+
160+
var helpers sync.Map
161+
162+
// Helper marks the calling function as a helper
163+
// and skips it for source location information.
164+
// It's the slog equivalent of testing.TB.Helper().
165+
func Helper() {
166+
_, _, fn := location(1)
167+
helpers.LoadOrStore(fn, struct{}{})
168+
}
169+
152170
func (ent SinkEntry) fillFromFrame(f runtime.Frame) SinkEntry {
153171
ent.Func = f.Function
154172
ent.File = f.File
@@ -182,27 +200,14 @@ func location(skip int) (file string, line int, fn string) {
182200
return file, line, f.Name()
183201
}
184202

185-
func appendName(names []string, names2 ...string) []string {
186-
names = append([]string(nil), names...)
187-
names = append(names, names2...)
188-
return names
189-
}
190-
191-
func (l Logger) log(ctx context.Context, level Level, msg string, fields Map) {
192-
ent := l.entry(ctx, level, msg, fields)
193-
l.LogEntry(ctx, ent)
194-
}
195-
196-
func (l Logger) entry(ctx context.Context, level Level, msg string, fields Map) SinkEntry {
197-
ent := SinkEntry{
198-
Time: time.Now().UTC(),
199-
Level: level,
200-
Message: msg,
201-
Fields: fieldsFromContext(ctx).append(fields),
202-
SpanContext: trace.FromContext(ctx).SpanContext(),
203+
func appendNames(names []string, names2 ...string) []string {
204+
if len(names2) == 0 {
205+
return names
203206
}
204-
ent = ent.fillLoc(l.skip + 3)
205-
return ent
207+
names3 := make([]string, 0, len(names)+len(names2))
208+
names3 = append(names3, names...)
209+
names3 = append(names3, names2...)
210+
return names3
206211
}
207212

208213
// Field represents a log field.
@@ -222,6 +227,7 @@ func M(fs ...Field) Map {
222227
}
223228

224229
// Value represents a log value.
230+
//
225231
// Implement SlogValue in your own types to override
226232
// the value encoded when logging.
227233
type Value interface {
@@ -245,8 +251,9 @@ func fieldsFromContext(ctx context.Context) Map {
245251
}
246252

247253
// With returns a context that contains the given fields.
248-
// Any logs written with the provided context will have
249-
// the given logs prepended.
254+
//
255+
// Any logs written with the provided context will have the given logs prepended.
256+
//
250257
// It will append to any fields already in ctx.
251258
func With(ctx context.Context, fields ...Field) context.Context {
252259
f1 := fieldsFromContext(ctx)
@@ -262,9 +269,7 @@ type SinkEntry struct {
262269
Level Level
263270
Message string
264271

265-
// Names represents the chain of names on the
266-
// logger constructed with Named.
267-
Names []string
272+
LoggerNames []string
268273

269274
Func string
270275
File string
@@ -279,6 +284,8 @@ type SinkEntry struct {
279284
type Level int
280285

281286
// The supported log levels.
287+
//
288+
// The default level is Info.
282289
const (
283290
LevelDebug Level = iota
284291
LevelInfo
@@ -297,17 +304,11 @@ var levelStrings = map[Level]string{
297304
LevelFatal: "FATAL",
298305
}
299306

307+
// String implements fmt.Stringer.
300308
func (l Level) String() string {
301309
s, ok := levelStrings[l]
302310
if !ok {
303311
return fmt.Sprintf("slog.Level(%v)", int(l))
304312
}
305313
return s
306314
}
307-
308-
func (f1 Map) append(f2 Map) Map {
309-
f3 := make(Map, 0, len(f1)+len(f2))
310-
f3 = append(f3, f1...)
311-
f3 = append(f3, f2...)
312-
return f3
313-
}

slog_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ func TestLogger(t *testing.T) {
103103
Level: slog.LevelInfo,
104104
Message: "meow",
105105

106-
Names: []string{"hello", "hello2"},
106+
LoggerNames: []string{"hello", "hello2"},
107107

108108
File: slogTestFile,
109109
Func: "cdr.dev/slog_test.TestLogger.func3",

sloggers/slogjson/slogjson.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
// Package slogjson contains the slogger that writes logs in a JSON format.
1+
// Package slogjson contains the slogger that writes logs in JSON.
22
//
33
// Format
44
//
55
// {
66
// "ts": "2019-09-10T20:19:07.159852-05:00",
77
// "level": "INFO",
8-
// "component": "comp.subcomp",
8+
// "logger_names": ["comp", "subcomp"],
99
// "msg": "hi",
1010
// "caller": "slog/examples_test.go:62",
1111
// "func": "cdr.dev/slog/sloggers/slogtest_test.TestExampleTest",
1212
// "trace": "<traceid>",
1313
// "span": "<spanid>",
1414
// "fields": {
15-
// "myField": "fieldValue"
15+
// "my_field": "field value"
1616
// }
1717
// }
1818
package slogjson // import "cdr.dev/slog/sloggers/slogjson"
@@ -53,8 +53,8 @@ func (s jsonSink) LogEntry(ctx context.Context, ent slog.SinkEntry) {
5353
slog.F("func", ent.Func),
5454
)
5555

56-
if len(ent.Names) > 0 {
57-
m = append(m, slog.F("component", ent.Names))
56+
if len(ent.LoggerNames) > 0 {
57+
m = append(m, slog.F("logger_names", ent.LoggerNames))
5858
}
5959

6060
if ent.SpanContext != (trace.SpanContext{}) {

sloggers/slogjson/slogjson_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func TestMake(t *testing.T) {
2929
l.Error(ctx, "line1\n\nline2", slog.F("wowow", "me\nyou"))
3030

3131
j := entryjson.Filter(b.String(), "ts")
32-
exp := fmt.Sprintf(`{"level":"ERROR","msg":"line1\n\nline2","caller":"%v:29","func":"cdr.dev/slog/sloggers/slogjson_test.TestMake","component":["named"],"trace":"%v","span":"%v","fields":{"wowow":"me\nyou"}}
32+
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"}}
3333
`, slogjsonTestFile, s.SpanContext().TraceID, s.SpanContext().SpanID)
3434
assert.Equal(t, exp, j, "entry")
3535
}

sloggers/slogstackdriver/slogstackdriver.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Package slogstackdriver contains the slogger for GCP.
1+
// Package slogstackdriver contains the slogger for google cloud's stackdriver.
22
package slogstackdriver // import "cdr.dev/slog/sloggers/slogstackdriver"
33

44
import (
@@ -48,9 +48,9 @@ func (s stackdriverSink) LogEntry(ctx context.Context, ent slog.SinkEntry) {
4848
}),
4949
)
5050

51-
if len(ent.Names) > 0 {
51+
if len(ent.LoggerNames) > 0 {
5252
e = append(e, slog.F("logging.googleapis.com/operation", &logpb.LogEntryOperation{
53-
Producer: strings.Join(ent.Names, "."),
53+
Producer: strings.Join(ent.LoggerNames, "."),
5454
}))
5555
}
5656

0 commit comments

Comments
 (0)