Merge pull request #272 from go-kit/log-readme
log: enhance README
Peter Bourgon
7 years ago
0 | 0 | # package log |
1 | 1 | |
2 | 2 | `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. | |
4 | 4 | It can be used for both typical application log events, and log-structured data streams. |
5 | 5 | |
6 | ## Rationale | |
6 | ## Structured logging | |
7 | 7 | |
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 | ``` | |
9 | 25 | |
10 | 26 | ## Usage |
11 | 27 | |
12 | Typical application logging. | |
28 | ### Typical application logging | |
13 | 29 | |
14 | 30 | ```go |
15 | import ( | |
16 | "os" | |
31 | logger := log.NewLogfmtLogger(os.Stderr) | |
32 | logger.Log("question", "what is the meaning of life?", "answer", 42) | |
17 | 33 | |
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 | |
25 | 36 | ``` |
26 | 37 | |
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 | |
30 | 55 | ``` |
31 | 56 | |
32 | Contextual logging. | |
57 | ### Interact with stdlib logger | |
33 | 58 | |
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. | |
56 | 60 | |
57 | 61 | ```go |
58 | 62 | import ( |
64 | 68 | func main() { |
65 | 69 | logger := kitlog.NewJSONLogger(os.Stdout) |
66 | 70 | stdlog.SetOutput(kitlog.NewStdlibAdapter(logger)) |
67 | ||
68 | 71 | 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 | |
69 | 115 | } |
70 | 116 | ``` |
71 | 117 | |
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, please 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), | |
128 | [#164](https://github.com/go-kit/kit/issues/164), and | |
129 | [#252](https://github.com/go-kit/kit/pull/252) | |
130 | to review historical conversations about package log and the Logger interface. | |
76 | 131 | |
77 | Adding a timestamp to contextual logs | |
132 | Value-add packages and suggestions, | |
133 | like improvements to [the leveled logger](https://godoc.org/github.com/go-kit/kit/log/levels), | |
134 | are of course welcome. | |
135 | Good proposals should | |
78 | 136 | |
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() | |
137 | - Be composable with [log.Context](https://godoc.org/github.com/go-kit/kit/log#Context), | |
138 | - Not break the behavior of [log.Caller](https://godoc.org/github.com/go-kit/kit/log#Caller) in any wrapped context, and | |
139 | - Be friendly to packages that accept only an unadorned log.Logger. | |
83 | 140 | |
84 | answer, err := process(ctx, req.Query) | |
85 | if err != nil { | |
86 | ctx.Log("err", err) | |
87 | return | |
88 | } | |
141 | ## Benchmarks & comparisons | |
89 | 142 | |
90 | ctx.Log("answer", answer) | |
91 | } | |
92 | ``` | |
143 | There are a few Go logging benchmarks and comparisons that include Go kit's package log. | |
93 | 144 | |
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 | ``` | |
145 | - [imkira/go-loggers-bench](https://github.com/imkira/go-loggers-bench) includes kit/log | |
146 | - [uber-common/zap](https://github.com/uber-common/zap), a zero-alloc logging library, includes a comparison with kit/log |