Skip to content

Commit 8edd386

Browse files
committed
Merge pull request #76 from go-kit/gophercon-log-api
New API for package log (post-GopherCon) Fixes #63 and some usability concerns.
2 parents 6080588 + a50819e commit 8edd386

File tree

12 files changed

+361
-151
lines changed

12 files changed

+361
-151
lines changed

addsvc/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func main() {
6262
// `package log` domain
6363
var logger kitlog.Logger
6464
logger = kitlog.NewLogfmtLogger(os.Stderr)
65-
logger = kitlog.With(logger, "ts", kitlog.DefaultTimestampUTC)
65+
logger = kitlog.NewContext(logger).With("ts", kitlog.DefaultTimestampUTC)
6666
stdlog.SetOutput(kitlog.NewStdlibAdapter(logger)) // redirect stdlib logging to us
6767
stdlog.SetFlags(0) // flags are handled in our logger
6868

log/benchmark_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ import (
77
)
88

99
func benchmarkRunner(b *testing.B, logger log.Logger, f func(log.Logger)) {
10-
logger = log.With(logger, "common_key", "common_value")
10+
lc := log.NewContext(logger).With("common_key", "common_value")
1111
b.ReportAllocs()
1212
b.ResetTimer()
1313
for i := 0; i < b.N; i++ {
14-
f(logger)
14+
f(lc)
1515
}
1616
}
1717

1818
var (
1919
baseMessage = func(logger log.Logger) { logger.Log("foo_key", "foo_value") }
20-
withMessage = func(logger log.Logger) { log.With(logger, "a", "b").Log("c", "d") }
20+
withMessage = func(logger log.Logger) { log.NewContext(logger).With("a", "b").Log("c", "d") }
2121
)

log/example_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package log_test
2+
3+
import (
4+
"os"
5+
6+
"github.com/go-kit/kit/log"
7+
)
8+
9+
func ExampleContext() {
10+
logger := log.NewLogfmtLogger(os.Stdout)
11+
logger.Log("foo", 123)
12+
ctx := log.NewContext(logger).With("level", "info")
13+
ctx.Log()
14+
ctx = ctx.With("msg", "hello")
15+
ctx.Log()
16+
ctx.With("a", 1).Log("b", 2)
17+
18+
// Output:
19+
// foo=123
20+
// level=info
21+
// level=info msg=hello
22+
// level=info msg=hello a=1 b=2
23+
}

log/levels.go

Lines changed: 0 additions & 60 deletions
This file was deleted.

log/levels/levels.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package levels
2+
3+
import "github.com/go-kit/kit/log"
4+
5+
// Levels provides a leveled logging wrapper around a logger. It has five
6+
// levels: debug, info, warning (warn), error, and critical (crit). If you
7+
// want a different set of levels, you can create your own levels type very
8+
// easily, and you can elide the configuration.
9+
type Levels struct {
10+
ctx log.Context
11+
levelKey string
12+
13+
// We have a choice between storing level values in string fields or
14+
// making a separate context for each level. When using string fields the
15+
// Log method must combine the base context, the level data, and the
16+
// logged keyvals; but the With method only requires updating one context.
17+
// If we instead keep a separate context for each level the Log method
18+
// must only append the new keyvals; but the With method would have to
19+
// update all five contexts.
20+
21+
// Roughly speaking, storing multiple contexts breaks even if the ratio of
22+
// Log/With calls is more than the number of levels. We have chosen to
23+
// make the With method cheap and the Log method a bit more costly because
24+
// we do not expect most applications to Log more than five times for each
25+
// call to With.
26+
27+
debugValue string
28+
infoValue string
29+
warnValue string
30+
errorValue string
31+
critValue string
32+
}
33+
34+
// New creates a new leveled logger, wrapping the passed logger.
35+
func New(logger log.Logger, options ...Option) Levels {
36+
l := Levels{
37+
ctx: log.NewContext(logger),
38+
levelKey: "level",
39+
40+
debugValue: "debug",
41+
infoValue: "info",
42+
warnValue: "warn",
43+
errorValue: "error",
44+
critValue: "crit",
45+
}
46+
for _, option := range options {
47+
option(&l)
48+
}
49+
return l
50+
}
51+
52+
// With returns a new leveled logger that includes keyvals in all log events.
53+
func (l Levels) With(keyvals ...interface{}) Levels {
54+
return Levels{
55+
ctx: l.ctx.With(keyvals...),
56+
levelKey: l.levelKey,
57+
debugValue: l.debugValue,
58+
infoValue: l.infoValue,
59+
warnValue: l.warnValue,
60+
errorValue: l.errorValue,
61+
critValue: l.critValue,
62+
}
63+
}
64+
65+
// Debug logs a debug event along with keyvals.
66+
func (l Levels) Debug(keyvals ...interface{}) error {
67+
return l.ctx.WithPrefix(l.levelKey, l.debugValue).Log(keyvals...)
68+
}
69+
70+
// Info logs an info event along with keyvals.
71+
func (l Levels) Info(keyvals ...interface{}) error {
72+
return l.ctx.WithPrefix(l.levelKey, l.infoValue).Log(keyvals...)
73+
}
74+
75+
// Warn logs a warn event along with keyvals.
76+
func (l Levels) Warn(keyvals ...interface{}) error {
77+
return l.ctx.WithPrefix(l.levelKey, l.warnValue).Log(keyvals...)
78+
}
79+
80+
// Error logs an error event along with keyvals.
81+
func (l Levels) Error(keyvals ...interface{}) error {
82+
return l.ctx.WithPrefix(l.levelKey, l.errorValue).Log(keyvals...)
83+
}
84+
85+
// Crit logs a crit event along with keyvals.
86+
func (l Levels) Crit(keyvals ...interface{}) error {
87+
return l.ctx.WithPrefix(l.levelKey, l.critValue).Log(keyvals...)
88+
}
89+
90+
// Option sets a parameter for leveled loggers.
91+
type Option func(*Levels)
92+
93+
// Key sets the key for the field used to indicate log level. By default,
94+
// the key is "level".
95+
func Key(key string) Option {
96+
return func(l *Levels) { l.levelKey = key }
97+
}
98+
99+
// DebugValue sets the value for the field used to indicate the debug log
100+
// level. By default, the value is "debug".
101+
func DebugValue(value string) Option {
102+
return func(l *Levels) { l.debugValue = value }
103+
}
104+
105+
// InfoValue sets the value for the field used to indicate the info log level.
106+
// By default, the value is "info".
107+
func InfoValue(value string) Option {
108+
return func(l *Levels) { l.infoValue = value }
109+
}
110+
111+
// WarnValue sets the value for the field used to indicate the warning log
112+
// level. By default, the value is "warn".
113+
func WarnValue(value string) Option {
114+
return func(l *Levels) { l.warnValue = value }
115+
}
116+
117+
// ErrorValue sets the value for the field used to indicate the error log
118+
// level. By default, the value is "error".
119+
func ErrorValue(value string) Option {
120+
return func(l *Levels) { l.errorValue = value }
121+
}
122+
123+
// CritValue sets the value for the field used to indicate the critical log
124+
// level. By default, the value is "crit".
125+
func CritValue(value string) Option {
126+
return func(l *Levels) { l.critValue = value }
127+
}

log/levels/levels_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package levels_test
2+
3+
import (
4+
"bytes"
5+
"os"
6+
"testing"
7+
8+
"github.com/go-kit/kit/log"
9+
"github.com/go-kit/kit/log/levels"
10+
)
11+
12+
func TestDefaultLevels(t *testing.T) {
13+
buf := bytes.Buffer{}
14+
logger := levels.New(log.NewLogfmtLogger(&buf))
15+
16+
logger.Debug("msg", "résumé") // of course you'd want to do this
17+
if want, have := "level=debug msg=résumé\n", buf.String(); want != have {
18+
t.Errorf("want %#v, have %#v", want, have)
19+
}
20+
21+
buf.Reset()
22+
logger.Info("msg", "Åhus")
23+
if want, have := "level=info msg=Åhus\n", buf.String(); want != have {
24+
t.Errorf("want %#v, have %#v", want, have)
25+
}
26+
27+
buf.Reset()
28+
logger.Error("msg", "© violation")
29+
if want, have := "level=error msg=\"© violation\"\n", buf.String(); want != have {
30+
t.Errorf("want %#v, have %#v", want, have)
31+
}
32+
}
33+
34+
func TestModifiedLevels(t *testing.T) {
35+
buf := bytes.Buffer{}
36+
logger := levels.New(
37+
log.NewJSONLogger(&buf),
38+
levels.Key("l"),
39+
levels.DebugValue("dbg"),
40+
)
41+
logger.With("easter_island", "176°").Debug("msg", "moai")
42+
if want, have := `{"easter_island":"176°","l":"dbg","msg":"moai"}`+"\n", buf.String(); want != have {
43+
t.Errorf("want %#v, have %#v", want, have)
44+
}
45+
}
46+
47+
func ExampleLevels() {
48+
logger := levels.New(log.NewLogfmtLogger(os.Stdout))
49+
logger.Debug("msg", "hello")
50+
logger.With("context", "foo").Warn("err", "error")
51+
52+
// Output:
53+
// level=debug msg=hello
54+
// level=warn context=foo err=error
55+
}

log/levels_test.go

Lines changed: 0 additions & 45 deletions
This file was deleted.

0 commit comments

Comments
 (0)