Skip to content

Commit

Permalink
log: enhance README
Browse files Browse the repository at this point in the history
  • Loading branch information
peterbourgon committed May 20, 2016
1 parent 9b342ab commit fe70b2a
Showing 1 changed file with 95 additions and 71 deletions.
166 changes: 95 additions & 71 deletions log/README.md
Original file line number Diff line number Diff line change
@@ -1,59 +1,63 @@
# package log

`package log` provides a minimal interface for structured logging in services.
It may be wrapped to encode conventions, enforce type-safety, etc.
It may be wrapped to encode conventions, enforce type-safety, provide leveled logging, and so on.
It can be used for both typical application log events, and log-structured data streams.

## Rationale
## Structured logging

TODO
Structured logging is, basically, conceding to the reality that logs are _data_,
and warrant some level of schematic rigor.
Using a stricter, key/value-oriented message format for our logs,
containing contextual and semantic information,
makes it much easier to get insight into the operational activity of the systems we build.
Consequently, `package log` is of the strong belief that
"[the benefits of structured logging outweigh the minimal effort involved](https://www.thoughtworks.com/radar/techniques/structured-logging)".

## Usage

Typical application logging.
Migrating from unstructured to structured logging is probably a lot easier than you'd expect.

```go
import (
"os"

"github.com/go-kit/kit/log"
)
// Unstructured
log.Printf("HTTP server listening on %s", addr)

func main() {
logger := log.NewLogfmtLogger(os.Stderr)
logger.Log("question", "what is the meaning of life?", "answer", 42)
}
// Structured
logger.Log("transport", "HTTP", "addr", addr, "msg", "listening")
```

Output:
```
question="what is the meaning of life?" answer=42
```
## Usage

Contextual logging.
### Typical application logging

```go
func handle(logger log.Logger, req *Request) {
ctx := log.NewContext(logger).With("txid", req.TransactionID, "query", req.Query)
ctx.Log()
logger := log.NewLogfmtLogger(os.Stderr)
logger.Log("question", "what is the meaning of life?", "answer", 42)

// Output:
// question="what is the meaning of life?" answer=42
```

answer, err := process(ctx, req.Query)
if err != nil {
ctx.Log("err", err)
return
}
### Log contexts

```go
func main() {
var logger log.Logger
logger = log.NewLogfmtLogger(os.Stderr)
logger = log.NewContext(logger).With("instance_id", 123)

ctx.Log("answer", answer)
logger.Log("msg", "starting")
NewWorker(log.NewContext(logger).With("component", "worker")).Run()
NewSlacker(log.NewContext(logger).With("component", "slacker")).Run()
}
```

Output:
```
txid=12345 query="some=query"
txid=12345 query="some=query" answer=42
// Output:
// instance_id=123 msg=starting
// instance_id=123 component=worker msg=running
// instance_id=123 component=slacker msg=running
```

Redirect stdlib log to gokit logger.
### Interact with stdlib logger

Redirect stdlib logger to Go kit logger.

```go
import (
Expand All @@ -65,58 +69,78 @@ import (
func main() {
logger := kitlog.NewJSONLogger(os.Stdout)
stdlog.SetOutput(kitlog.NewStdlibAdapter(logger))

stdlog.Print("I sure like pie")
}
```

Output
// Output:
// {"msg":"I sure like pie","ts":"2016/01/01 12:34:56"}
```
{"msg":"I sure like pie","ts":"2016/01/28 19:41:08"}

Or, if, for legacy reasons,
you need to pipe all of your logging through the stdlib log package,
you can redirect Go kit logger to the stdlib logger.

```go
logger := kitlog.NewLogfmtLogger(kitlog.StdlibWriter{})
logger.Log("legacy", true, "msg", "at least it's something")

// Output:
// 2016/01/01 12:34:56 legacy=true msg="at least it's something"
```

Adding a timestamp to contextual logs
### Timestamps and callers

```go
func handle(logger log.Logger, req *Request) {
ctx := log.NewContext(logger).With("ts", log.DefaultTimestampUTC, "query", req.Query)
ctx.Log()
var logger log.Logger
logger = log.NewLogfmtLogger(os.Stderr)
logger = log.NewContext(logger).With("ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller)

answer, err := process(ctx, req.Query)
if err != nil {
ctx.Log("err", err)
return
}
logger.Log("msg", "hello")

ctx.Log("answer", answer)
}
// Output:
// ts=2016-01-01T12:34:56Z caller=main.go:15 msg=hello
```

Output
```
ts=2016-01-29T00:46:04Z query="some=query"
ts=2016-01-29T00:46:04Z query="some=query" answer=42
```
## Supported output formats

Adding caller info to contextual logs
- [Logfmt](https://brandur.org/logfmt)
- JSON

```go
func handle(logger log.Logger, req *Request) {
ctx := log.NewContext(logger).With("caller", log.DefaultCaller, "query", req.Query)
ctx.Log()
## Enhancements

answer, err := process(ctx, req.Query)
if err != nil {
ctx.Log("err", err)
return
}
`package log` is centered on the one-method Logger interface.

ctx.Log("answer", answer)
```go
type Logger interface {
Log(keyvals ...interface{}) error
}
```

Output
```
caller=logger.go:20 query="some=query"
caller=logger.go:28 query="some=query" answer=42
```
This interface, and its supporting code like [log.Context](https://godoc.org/github.com/go-kit/kit/log#Context),
is the product of much iteration and evaluation.
For more details on the evolution of the Logger interface,
see [The Hunt for a Logger Interface](http://go-talks.appspot.com/github.com/ChrisHines/talks/structured-logging/structured-logging.slide#1),
a talk by [Chris Hines](https://github.com/ChrisHines).
Also, see
[#63](https://github.com/go-kit/kit/issues/63),
[#76](https://github.com/go-kit/kit/pull/76),
[#131](https://github.com/go-kit/kit/issues/131),
[#157](https://github.com/go-kit/kit/pull/157), and
[#252](https://github.com/go-kit/kit/pull/252)
to review historical conversations about package log and the Logger interface.

Value-add packages and suggestions,
like improvements to [the leveled logger](https://godoc.org/github.com/go-kit/kit/log/levels),
are of course welcome.
Good proposals should

- Be composable with [log.Context](https://godoc.org/github.com/go-kit/kit/log#Context),
- Not break the behavior of [log.Caller](https://godoc.org/github.com/go-kit/kit/log#Caller) in any wrapped context, and
- Be friendly to packages that accept only an unadorned log.Logger.

## Benchmarks & comparisons

There are a few Go logging benchmarks and comparisons that include Go kit's package log.

- [imkira/go-loggers-bench](https://github.com/imkira/go-loggers-bench) includes kit/log
- [uber-common/zap](https://github.com/uber-common/zap), a logging library, include a comparison with kit/log

0 comments on commit fe70b2a

Please sign in to comment.