Codebase list golang-github-go-kit-kit / dec54f2
Merge pull request #47 from go-kit/logfmt-logger Replace PrefixLogger with LogfmtLogger (fix for #34). Chris Hines 8 years ago
8 changed file(s) with 101 addition(s) and 104 deletion(s). Raw diff Collapse all Expand all
6060
6161 // `package log` domain
6262 var logger kitlog.Logger
63 logger = kitlog.NewPrefixLogger(os.Stderr)
63 logger = kitlog.NewLogfmtLogger(os.Stderr)
6464 logger = kitlog.With(logger, "ts", kitlog.DefaultTimestampUTC, "caller", kitlog.DefaultCaller)
6565 stdlog.SetOutput(kitlog.NewStdlibAdapter(logger)) // redirect stdlib logging to us
6666 stdlog.SetFlags(0) // flags are handled in our logger
88
99 func TestDefaultLevels(t *testing.T) {
1010 buf := bytes.Buffer{}
11 levels := log.NewLevels(log.NewPrefixLogger(&buf))
11 levels := log.NewLevels(log.NewLogfmtLogger(&buf))
1212
1313 levels.Debug.Log("msg", "👨") // of course you'd want to do this
1414 if want, have := "level=DEBUG msg=👨\n", buf.String(); want != have {
132132 }
133133
134134 buf.Reset()
135 prefix := log.NewPrefixLogger(buf)
135 prefix := log.NewLogfmtLogger(buf)
136136 logger.Swap(prefix)
137137
138138 if err := logger.Log("k", "v"); err != nil {
0 package log
1
2 import (
3 "io"
4
5 "gopkg.in/logfmt.v0"
6 )
7
8 type logfmtLogger struct {
9 w io.Writer
10 }
11
12 // NewLogfmtLogger returns a logger that encodes keyvals to the Writer in
13 // logfmt format. The passed Writer must be safe for concurrent use by
14 // multiple goroutines if the returned Logger will be used concurrently.
15 func NewLogfmtLogger(w io.Writer) Logger {
16 return &logfmtLogger{w}
17 }
18
19 func (l logfmtLogger) Log(keyvals ...interface{}) error {
20 // The Logger interface requires implementations to be safe for concurrent
21 // use by multiple goroutines. For this implementation that means making
22 // only one call to l.w.Write() for each call to Log. We first collect all
23 // of the bytes into b, and then call l.w.Write(b).
24 b, err := logfmt.MarshalKeyvals(keyvals...)
25 if err != nil {
26 return err
27 }
28 b = append(b, '\n')
29 if _, err := l.w.Write(b); err != nil {
30 return err
31 }
32 return nil
33 }
0 package log_test
1
2 import (
3 "bytes"
4 "errors"
5 "io/ioutil"
6 "testing"
7
8 "github.com/go-kit/kit/log"
9 "gopkg.in/logfmt.v0"
10 )
11
12 func TestLogfmtLogger(t *testing.T) {
13 buf := &bytes.Buffer{}
14 logger := log.NewLogfmtLogger(buf)
15
16 if err := logger.Log("hello", "world"); err != nil {
17 t.Fatal(err)
18 }
19 if want, have := "hello=world\n", buf.String(); want != have {
20 t.Errorf("want %#v, have %#v", want, have)
21 }
22
23 buf.Reset()
24 if err := logger.Log("a", 1, "err", errors.New("error")); err != nil {
25 t.Fatal(err)
26 }
27 if want, have := "a=1 err=error\n", buf.String(); want != have {
28 t.Errorf("want %#v, have %#v", want, have)
29 }
30
31 buf.Reset()
32 if err := logger.Log("std_map", map[int]int{1: 2}, "my_map", mymap{0: 0}); err != nil {
33 t.Fatal(err)
34 }
35 if want, have := "std_map=\""+logfmt.ErrUnsupportedValueType.Error()+"\" my_map=special_behavior\n", buf.String(); want != have {
36 t.Errorf("want %#v, have %#v", want, have)
37 }
38 }
39
40 func BenchmarkLogfmtLoggerSimple(b *testing.B) {
41 benchmarkRunner(b, log.NewLogfmtLogger(ioutil.Discard), baseMessage)
42 }
43
44 func BenchmarkLogfmtLoggerContextual(b *testing.B) {
45 benchmarkRunner(b, log.NewLogfmtLogger(ioutil.Discard), withMessage)
46 }
47
48 func TestLogfmtLoggerConcurrency(t *testing.T) {
49 testConcurrency(t, log.NewLogfmtLogger(ioutil.Discard))
50 }
51
52 type mymap map[int]int
53
54 func (m mymap) String() string { return "special_behavior" }
+0
-38
log/prefix_logger.go less more
0 package log
1
2 import (
3 "bytes"
4 "fmt"
5 "io"
6 )
7
8 type prefixLogger struct {
9 io.Writer
10 }
11
12 // NewPrefixLogger returns a basic logger that encodes keyvals as simple "k=v"
13 // pairs to the Writer.
14 func NewPrefixLogger(w io.Writer) Logger {
15 return &prefixLogger{w}
16 }
17
18 func (l prefixLogger) Log(keyvals ...interface{}) error {
19 if len(keyvals)%2 == 1 {
20 panic("odd number of keyvals")
21 }
22 buf := &bytes.Buffer{}
23 for i := 0; i < len(keyvals); i += 2 {
24 if i != 0 {
25 if _, err := fmt.Fprint(buf, " "); err != nil {
26 return err
27 }
28 }
29 if _, err := fmt.Fprintf(buf, "%s=%v", keyvals[i], keyvals[i+1]); err != nil {
30 return err
31 }
32 }
33 if _, err := fmt.Fprintln(l.Writer, buf.String()); err != nil {
34 return err
35 }
36 return nil
37 }
+0
-54
log/prefix_logger_test.go less more
0 package log_test
1
2 import (
3 "bytes"
4 "errors"
5 "io/ioutil"
6 "testing"
7
8 "github.com/go-kit/kit/log"
9 )
10
11 func TestPrefixLogger(t *testing.T) {
12 buf := &bytes.Buffer{}
13 logger := log.NewPrefixLogger(buf)
14
15 if err := logger.Log("hello", "world"); err != nil {
16 t.Fatal(err)
17 }
18 if want, have := "hello=world\n", buf.String(); want != have {
19 t.Errorf("want %#v, have %#v", want, have)
20 }
21
22 buf.Reset()
23 if err := logger.Log("a", 1, "err", errors.New("error")); err != nil {
24 t.Fatal(err)
25 }
26 if want, have := "a=1 err=error\n", buf.String(); want != have {
27 t.Errorf("want %#v, have %#v", want, have)
28 }
29
30 buf.Reset()
31 if err := logger.Log("std_map", map[int]int{1: 2}, "my_map", mymap{0: 0}); err != nil {
32 t.Fatal(err)
33 }
34 if want, have := "std_map=map[1:2] my_map=special_behavior\n", buf.String(); want != have {
35 t.Errorf("want %#v, have %#v", want, have)
36 }
37 }
38
39 func BenchmarkPrefixLoggerSimple(b *testing.B) {
40 benchmarkRunner(b, log.NewPrefixLogger(ioutil.Discard), baseMessage)
41 }
42
43 func BenchmarkPrefixLoggerContextual(b *testing.B) {
44 benchmarkRunner(b, log.NewPrefixLogger(ioutil.Discard), withMessage)
45 }
46
47 func TestPrefixLoggerConcurrency(t *testing.T) {
48 testConcurrency(t, log.NewPrefixLogger(ioutil.Discard))
49 }
50
51 type mymap map[int]int
52
53 func (m mymap) String() string { return "special_behavior" }
1111 buf := &bytes.Buffer{}
1212 log.SetOutput(buf)
1313 log.SetFlags(log.LstdFlags)
14 logger := NewPrefixLogger(StdlibWriter{})
14 logger := NewLogfmtLogger(StdlibWriter{})
1515 logger.Log("key", "val")
1616 timestamp := time.Now().Format("2006/01/02 15:04:05")
1717 if want, have := timestamp+" key=val\n", buf.String(); want != have {
2121
2222 func TestStdlibAdapterUsage(t *testing.T) {
2323 buf := &bytes.Buffer{}
24 logger := NewPrefixLogger(buf)
24 logger := NewLogfmtLogger(buf)
2525 writer := NewStdlibAdapter(logger)
2626 stdlog := log.New(writer, "", 0)
2727
3333 0: "msg=hello\n",
3434 log.Ldate: "ts=" + date + " msg=hello\n",
3535 log.Ltime: "ts=" + time + " msg=hello\n",
36 log.Ldate | log.Ltime: "ts=" + date + " " + time + " msg=hello\n",
36 log.Ldate | log.Ltime: "ts=\"" + date + " " + time + "\" msg=hello\n",
3737 log.Lshortfile: "file=stdlib_test.go:44 msg=hello\n",
3838 log.Lshortfile | log.Ldate: "ts=" + date + " file=stdlib_test.go:44 msg=hello\n",
39 log.Lshortfile | log.Ldate | log.Ltime: "ts=" + date + " " + time + " file=stdlib_test.go:44 msg=hello\n",
39 log.Lshortfile | log.Ldate | log.Ltime: "ts=\"" + date + " " + time + "\" file=stdlib_test.go:44 msg=hello\n",
4040 } {
4141 buf.Reset()
4242 stdlog.SetFlags(flag)
4949
5050 func TestStdLibAdapterExtraction(t *testing.T) {
5151 buf := &bytes.Buffer{}
52 logger := NewPrefixLogger(buf)
52 logger := NewLogfmtLogger(buf)
5353 writer := NewStdlibAdapter(logger)
5454 for input, want := range map[string]string{
5555 "hello": "msg=hello\n",
5656 "2009/01/23: hello": "ts=2009/01/23 msg=hello\n",
57 "2009/01/23 01:23:23: hello": "ts=2009/01/23 01:23:23 msg=hello\n",
57 "2009/01/23 01:23:23: hello": "ts=\"2009/01/23 01:23:23\" msg=hello\n",
5858 "01:23:23: hello": "ts=01:23:23 msg=hello\n",
59 "2009/01/23 01:23:23.123123: hello": "ts=2009/01/23 01:23:23.123123 msg=hello\n",
60 "2009/01/23 01:23:23.123123 /a/b/c/d.go:23: hello": "ts=2009/01/23 01:23:23.123123 file=/a/b/c/d.go:23 msg=hello\n",
59 "2009/01/23 01:23:23.123123: hello": "ts=\"2009/01/23 01:23:23.123123\" msg=hello\n",
60 "2009/01/23 01:23:23.123123 /a/b/c/d.go:23: hello": "ts=\"2009/01/23 01:23:23.123123\" file=/a/b/c/d.go:23 msg=hello\n",
6161 "01:23:23.123123 /a/b/c/d.go:23: hello": "ts=01:23:23.123123 file=/a/b/c/d.go:23 msg=hello\n",
62 "2009/01/23 01:23:23 /a/b/c/d.go:23: hello": "ts=2009/01/23 01:23:23 file=/a/b/c/d.go:23 msg=hello\n",
62 "2009/01/23 01:23:23 /a/b/c/d.go:23: hello": "ts=\"2009/01/23 01:23:23\" file=/a/b/c/d.go:23 msg=hello\n",
6363 "2009/01/23 /a/b/c/d.go:23: hello": "ts=2009/01/23 file=/a/b/c/d.go:23 msg=hello\n",
6464 "/a/b/c/d.go:23: hello": "file=/a/b/c/d.go:23 msg=hello\n",
6565 } {