Codebase list golang-github-go-kit-kit / fe70b2a
log: enhance README Peter Bourgon 7 years ago
1 changed file(s) with 109 addition(s) and 85 deletion(s). Raw diff Collapse all Expand all
00 # package log
11
22 `package log` provides a minimal interface for structured logging in services.
3 It may be wrapped to encode conventions, enforce type-safety, etc.
3 It may be wrapped to encode conventions, enforce type-safety, provide leveled logging, and so on.
44 It can be used for both typical application log events, and log-structured data streams.
55
6 ## Rationale
6 ## Structured logging
77
8 TODO
8 Structured logging is, basically, conceding to the reality that logs are _data_,
9 and warrant some level of schematic rigor.
10 Using a stricter, key/value-oriented message format for our logs,
11 containing contextual and semantic information,
12 makes it much easier to get insight into the operational activity of the systems we build.
13 Consequently, `package log` is of the strong belief that
14 "[the benefits of structured logging outweigh the minimal effort involved](https://www.thoughtworks.com/radar/techniques/structured-logging)".
15
16 Migrating from unstructured to structured logging is probably a lot easier than you'd expect.
17
18 ```go
19 // Unstructured
20 log.Printf("HTTP server listening on %s", addr)
21
22 // Structured
23 logger.Log("transport", "HTTP", "addr", addr, "msg", "listening")
24 ```
925
1026 ## Usage
1127
12 Typical application logging.
28 ### Typical application logging
1329
1430 ```go
15 import (
16 "os"
31 logger := log.NewLogfmtLogger(os.Stderr)
32 logger.Log("question", "what is the meaning of life?", "answer", 42)
1733
18 "github.com/go-kit/kit/log"
19 )
20
21 func main() {
22 logger := log.NewLogfmtLogger(os.Stderr)
23 logger.Log("question", "what is the meaning of life?", "answer", 42)
24 }
34 // Output:
35 // question="what is the meaning of life?" answer=42
2536 ```
2637
27 Output:
28 ```
29 question="what is the meaning of life?" answer=42
38 ### Log contexts
39
40 ```go
41 func main() {
42 var logger log.Logger
43 logger = log.NewLogfmtLogger(os.Stderr)
44 logger = log.NewContext(logger).With("instance_id", 123)
45
46 logger.Log("msg", "starting")
47 NewWorker(log.NewContext(logger).With("component", "worker")).Run()
48 NewSlacker(log.NewContext(logger).With("component", "slacker")).Run()
49 }
50
51 // Output:
52 // instance_id=123 msg=starting
53 // instance_id=123 component=worker msg=running
54 // instance_id=123 component=slacker msg=running
3055 ```
3156
32 Contextual logging.
57 ### Interact with stdlib logger
3358
34 ```go
35 func handle(logger log.Logger, req *Request) {
36 ctx := log.NewContext(logger).With("txid", req.TransactionID, "query", req.Query)
37 ctx.Log()
38
39 answer, err := process(ctx, req.Query)
40 if err != nil {
41 ctx.Log("err", err)
42 return
43 }
44
45 ctx.Log("answer", answer)
46 }
47 ```
48
49 Output:
50 ```
51 txid=12345 query="some=query"
52 txid=12345 query="some=query" answer=42
53 ```
54
55 Redirect stdlib log to gokit logger.
59 Redirect stdlib logger to Go kit logger.
5660
5761 ```go
5862 import (
6468 func main() {
6569 logger := kitlog.NewJSONLogger(os.Stdout)
6670 stdlog.SetOutput(kitlog.NewStdlibAdapter(logger))
67
6871 stdlog.Print("I sure like pie")
72 }
73
74 // Output:
75 // {"msg":"I sure like pie","ts":"2016/01/01 12:34:56"}
76 ```
77
78 Or, if, for legacy reasons,
79 you need to pipe all of your logging through the stdlib log package,
80 you can redirect Go kit logger to the stdlib logger.
81
82 ```go
83 logger := kitlog.NewLogfmtLogger(kitlog.StdlibWriter{})
84 logger.Log("legacy", true, "msg", "at least it's something")
85
86 // Output:
87 // 2016/01/01 12:34:56 legacy=true msg="at least it's something"
88 ```
89
90 ### Timestamps and callers
91
92 ```go
93 var logger log.Logger
94 logger = log.NewLogfmtLogger(os.Stderr)
95 logger = log.NewContext(logger).With("ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller)
96
97 logger.Log("msg", "hello")
98
99 // Output:
100 // ts=2016-01-01T12:34:56Z caller=main.go:15 msg=hello
101 ```
102
103 ## Supported output formats
104
105 - [Logfmt](https://brandur.org/logfmt)
106 - JSON
107
108 ## Enhancements
109
110 `package log` is centered on the one-method Logger interface.
111
112 ```go
113 type Logger interface {
114 Log(keyvals ...interface{}) error
69115 }
70116 ```
71117
72 Output
73 ```
74 {"msg":"I sure like pie","ts":"2016/01/28 19:41:08"}
75 ```
118 This interface, and its supporting code like [log.Context](https://godoc.org/github.com/go-kit/kit/log#Context),
119 is the product of much iteration and evaluation.
120 For more details on the evolution of the Logger interface,
121 see [The Hunt for a Logger Interface](http://go-talks.appspot.com/github.com/ChrisHines/talks/structured-logging/structured-logging.slide#1),
122 a talk by [Chris Hines](https://github.com/ChrisHines).
123 Also, see
124 [#63](https://github.com/go-kit/kit/issues/63),
125 [#76](https://github.com/go-kit/kit/pull/76),
126 [#131](https://github.com/go-kit/kit/issues/131),
127 [#157](https://github.com/go-kit/kit/pull/157), and
128 [#252](https://github.com/go-kit/kit/pull/252)
129 to review historical conversations about package log and the Logger interface.
76130
77 Adding a timestamp to contextual logs
131 Value-add packages and suggestions,
132 like improvements to [the leveled logger](https://godoc.org/github.com/go-kit/kit/log/levels),
133 are of course welcome.
134 Good proposals should
78135
79 ```go
80 func handle(logger log.Logger, req *Request) {
81 ctx := log.NewContext(logger).With("ts", log.DefaultTimestampUTC, "query", req.Query)
82 ctx.Log()
136 - Be composable with [log.Context](https://godoc.org/github.com/go-kit/kit/log#Context),
137 - Not break the behavior of [log.Caller](https://godoc.org/github.com/go-kit/kit/log#Caller) in any wrapped context, and
138 - Be friendly to packages that accept only an unadorned log.Logger.
83139
84 answer, err := process(ctx, req.Query)
85 if err != nil {
86 ctx.Log("err", err)
87 return
88 }
140 ## Benchmarks & comparisons
89141
90 ctx.Log("answer", answer)
91 }
92 ```
142 There are a few Go logging benchmarks and comparisons that include Go kit's package log.
93143
94 Output
95 ```
96 ts=2016-01-29T00:46:04Z query="some=query"
97 ts=2016-01-29T00:46:04Z query="some=query" answer=42
98 ```
99
100 Adding caller info to contextual logs
101
102 ```go
103 func handle(logger log.Logger, req *Request) {
104 ctx := log.NewContext(logger).With("caller", log.DefaultCaller, "query", req.Query)
105 ctx.Log()
106
107 answer, err := process(ctx, req.Query)
108 if err != nil {
109 ctx.Log("err", err)
110 return
111 }
112
113 ctx.Log("answer", answer)
114 }
115 ```
116
117 Output
118 ```
119 caller=logger.go:20 query="some=query"
120 caller=logger.go:28 query="some=query" answer=42
121 ```
144 - [imkira/go-loggers-bench](https://github.com/imkira/go-loggers-bench) includes kit/log
145 - [uber-common/zap](https://github.com/uber-common/zap), a logging library, include a comparison with kit/log