156 lines
6 KiB
Markdown
156 lines
6 KiB
Markdown
|
# Frequently Asked Questions
|
||
|
|
||
|
## Design
|
||
|
|
||
|
### Why spend so much effort on logger performance?
|
||
|
|
||
|
Of course, most applications won't notice the impact of a slow logger: they
|
||
|
already take tens or hundreds of milliseconds for each operation, so an extra
|
||
|
millisecond doesn't matter.
|
||
|
|
||
|
On the other hand, why *not* make structured logging fast? The `SugaredLogger`
|
||
|
isn't any harder to use than other logging packages, and the `Logger` makes
|
||
|
structured logging possible in performance-sensitive contexts. Across a fleet
|
||
|
of Go microservices, making each application even slightly more efficient adds
|
||
|
up quickly.
|
||
|
|
||
|
### Why aren't `Logger` and `SugaredLogger` interfaces?
|
||
|
|
||
|
Unlike the familiar `io.Writer` and `http.Handler`, `Logger` and
|
||
|
`SugaredLogger` interfaces would include *many* methods. As [Rob Pike points
|
||
|
out][go-proverbs], "The bigger the interface, the weaker the abstraction."
|
||
|
Interfaces are also rigid — *any* change requires releasing a new major
|
||
|
version, since it breaks all third-party implementations.
|
||
|
|
||
|
Making the `Logger` and `SugaredLogger` concrete types doesn't sacrifice much
|
||
|
abstraction, and it lets us add methods without introducing breaking changes.
|
||
|
Your applications should define and depend upon an interface that includes
|
||
|
just the methods you use.
|
||
|
|
||
|
### Why sample application logs?
|
||
|
|
||
|
Applications often experience runs of errors, either because of a bug or
|
||
|
because of a misbehaving user. Logging errors is usually a good idea, but it
|
||
|
can easily make this bad situation worse: not only is your application coping
|
||
|
with a flood of errors, it's also spending extra CPU cycles and I/O logging
|
||
|
those errors. Since writes are typically serialized, logging limits throughput
|
||
|
when you need it most.
|
||
|
|
||
|
Sampling fixes this problem by dropping repetitive log entries. Under normal
|
||
|
conditions, your application writes out every entry. When similar entries are
|
||
|
logged hundreds or thousands of times each second, though, zap begins dropping
|
||
|
duplicates to preserve throughput.
|
||
|
|
||
|
### Why do the structured logging APIs take a message in addition to fields?
|
||
|
|
||
|
Subjectively, we find it helpful to accompany structured context with a brief
|
||
|
description. This isn't critical during development, but it makes debugging
|
||
|
and operating unfamiliar systems much easier.
|
||
|
|
||
|
More concretely, zap's sampling algorithm uses the message to identify
|
||
|
duplicate entries. In our experience, this is a practical middle ground
|
||
|
between random sampling (which often drops the exact entry that you need while
|
||
|
debugging) and hashing the complete entry (which is prohibitively expensive).
|
||
|
|
||
|
### Why include package-global loggers?
|
||
|
|
||
|
Since so many other logging packages include a global logger, many
|
||
|
applications aren't designed to accept loggers as explicit parameters.
|
||
|
Changing function signatures is often a breaking change, so zap includes
|
||
|
global loggers to simplify migration.
|
||
|
|
||
|
Avoid them where possible.
|
||
|
|
||
|
### Why include dedicated Panic and Fatal log levels?
|
||
|
|
||
|
In general, application code should handle errors gracefully instead of using
|
||
|
`panic` or `os.Exit`. However, every rule has exceptions, and it's common to
|
||
|
crash when an error is truly unrecoverable. To avoid losing any information
|
||
|
— especially the reason for the crash — the logger must flush any
|
||
|
buffered entries before the process exits.
|
||
|
|
||
|
Zap makes this easy by offering `Panic` and `Fatal` logging methods that
|
||
|
automatically flush before exiting. Of course, this doesn't guarantee that
|
||
|
logs will never be lost, but it eliminates a common error.
|
||
|
|
||
|
See the discussion in uber-go/zap#207 for more details.
|
||
|
|
||
|
### What's `DPanic`?
|
||
|
|
||
|
`DPanic` stands for "panic in development." In development, it logs at
|
||
|
`PanicLevel`; otherwise, it logs at `ErrorLevel`. `DPanic` makes it easier to
|
||
|
catch errors that are theoretically possible, but shouldn't actually happen,
|
||
|
*without* crashing in production.
|
||
|
|
||
|
If you've ever written code like this, you need `DPanic`:
|
||
|
|
||
|
```go
|
||
|
if err != nil {
|
||
|
panic(fmt.Sprintf("shouldn't ever get here: %v", err))
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## Installation
|
||
|
|
||
|
### What does the error `expects import "go.uber.org/zap"` mean?
|
||
|
|
||
|
Either zap was installed incorrectly or you're referencing the wrong package
|
||
|
name in your code.
|
||
|
|
||
|
Zap's source code happens to be hosted on GitHub, but the [import
|
||
|
path][import-path] is `go.uber.org/zap`. This gives us, the project
|
||
|
maintainers, the freedom to move the source code if necessary. However, it
|
||
|
means that you need to take a little care when installing and using the
|
||
|
package.
|
||
|
|
||
|
If you follow two simple rules, everything should work: install zap with `go
|
||
|
get -u go.uber.org/zap`, and always import it in your code with `import
|
||
|
"go.uber.org/zap"`. Your code shouldn't contain *any* references to
|
||
|
`github.com/uber-go/zap`.
|
||
|
|
||
|
## Usage
|
||
|
|
||
|
### Does zap support log rotation?
|
||
|
|
||
|
Zap doesn't natively support rotating log files, since we prefer to leave this
|
||
|
to an external program like `logrotate`.
|
||
|
|
||
|
However, it's easy to integrate a log rotation package like
|
||
|
[`gopkg.in/natefinch/lumberjack.v2`][lumberjack] as a `zapcore.WriteSyncer`.
|
||
|
|
||
|
```go
|
||
|
// lumberjack.Logger is already safe for concurrent use, so we don't need to
|
||
|
// lock it.
|
||
|
w := zapcore.AddSync(&lumberjack.Logger{
|
||
|
Filename: "/var/log/myapp/foo.log",
|
||
|
MaxSize: 500, // megabytes
|
||
|
MaxBackups: 3,
|
||
|
MaxAge: 28, // days
|
||
|
})
|
||
|
core := zapcore.NewCore(
|
||
|
zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
|
||
|
w,
|
||
|
zap.InfoLevel,
|
||
|
)
|
||
|
logger := zap.New(core)
|
||
|
```
|
||
|
|
||
|
## Extensions
|
||
|
|
||
|
We'd love to support every logging need within zap itself, but we're only
|
||
|
familiar with a handful of log ingestion systems, flag-parsing packages, and
|
||
|
the like. Rather than merging code that we can't effectively debug and
|
||
|
support, we'd rather grow an ecosystem of zap extensions.
|
||
|
|
||
|
We're aware of the following extensions, but haven't used them ourselves:
|
||
|
|
||
|
| Package | Integration |
|
||
|
| --- | --- |
|
||
|
| `github.com/tchap/zapext` | Sentry, syslog |
|
||
|
| `github.com/fgrosse/zaptest` | Ginkgo |
|
||
|
| `github.com/blendle/zapdriver` | Stackdriver |
|
||
|
|
||
|
[go-proverbs]: https://go-proverbs.github.io/
|
||
|
[import-path]: https://golang.org/cmd/go/#hdr-Remote_import_paths
|
||
|
[lumberjack]: https://godoc.org/gopkg.in/natefinch/lumberjack.v2
|