diff --git a/glide.lock b/glide.lock
index d3e47cc..0485428 100644
--- a/glide.lock
+++ b/glide.lock
@@ -1,8 +1,10 @@
-hash: c45e99daa5125bb488a17bdab27cccffdc19e3b3270cb70236c22cb9917d344d
-updated: 2017-08-27T23:26:20.061071223-04:00
+hash: 81e7b3e1a90ce2dfd387bb4fffa2c099317bfc2d1fe4080d62ecd5e613ae699f
+updated: 2017-08-30T22:05:18.909857815-04:00
imports:
- name: github.com/prometheus/client_golang
version: c5b7fccd204277076155f10851dad72b76a49317
+- name: go.uber.org/zap
+ version: 9d9d6135afe89b6fc4a05e9a8552526caba38048
- name: gopkg.in/routeros.v2
version: ffdb88bba0376a797b733f2279f539340926617f
testImports: []
diff --git a/glide.yaml b/glide.yaml
index a8137a7..d1697eb 100644
--- a/glide.yaml
+++ b/glide.yaml
@@ -3,3 +3,5 @@ import:
- package: github.com/prometheus/client_golang
version: v0.8.0
- package: gopkg.in/routeros.v2
+- package: go.uber.org/zap
+ version: ~1.5.0
diff --git a/vendor/go.uber.org/zap/.codecov.yml b/vendor/go.uber.org/zap/.codecov.yml
new file mode 100644
index 0000000..8e5ca7d
--- /dev/null
+++ b/vendor/go.uber.org/zap/.codecov.yml
@@ -0,0 +1,17 @@
+coverage:
+ range: 80..100
+ round: down
+ precision: 2
+
+ status:
+ project: # measuring the overall project coverage
+ default: # context, you can create multiple ones with custom titles
+ enabled: yes # must be yes|true to enable this status
+ target: 95% # specify the target coverage for each commit status
+ # option: "auto" (must increase from parent commit or pull request base)
+ # option: "X%" a static target percentage to hit
+ if_not_found: success # if parent is not found report status as success, error, or failure
+ if_ci_failed: error # if ci fails report status as success, error, or failure
+ignore:
+ - internal/readme/readme.go
+
diff --git a/vendor/go.uber.org/zap/.readme.tmpl b/vendor/go.uber.org/zap/.readme.tmpl
new file mode 100644
index 0000000..35cf8db
--- /dev/null
+++ b/vendor/go.uber.org/zap/.readme.tmpl
@@ -0,0 +1,98 @@
+# :zap: zap [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov]
+
+Blazing fast, structured, leveled logging in Go.
+
+## Installation
+
+`go get -u go.uber.org/zap`
+
+Note that zap only supports the two most recent minor versions of Go.
+
+## Quick Start
+
+In contexts where performance is nice, but not critical, use the
+`SugaredLogger`. It's 4-10x faster than than other structured logging
+packages and includes both structured and `printf`-style APIs.
+
+```go
+logger, _ := zap.NewProduction()
+defer logger.Sync() // flushes buffer, if any
+sugar := logger.Sugar()
+sugar.Infow("failed to fetch URL",
+ // Structured context as loosely typed key-value pairs.
+ "url", url,
+ "attempt", 3,
+ "backoff", time.Second,
+)
+sugar.Infof("Failed to fetch URL: %s", url)
+```
+
+When performance and type safety are critical, use the `Logger`. It's even
+faster than the `SugaredLogger` and allocates far less, but it only supports
+structured logging.
+
+```go
+logger, _ := zap.NewProduction()
+defer logger.Sync()
+logger.Info("failed to fetch URL",
+ // Structured context as strongly typed Field values.
+ zap.String("url", url),
+ zap.Int("attempt", 3),
+ zap.Duration("backoff", time.Second),
+)
+```
+
+See the [documentation][doc] and [FAQ](FAQ.md) for more details.
+
+## Performance
+
+For applications that log in the hot path, reflection-based serialization and
+string formatting are prohibitively expensive — they're CPU-intensive
+and make many small allocations. Put differently, using `encoding/json` and
+`fmt.Fprintf` to log tons of `interface{}`s makes your application slow.
+
+Zap takes a different approach. It includes a reflection-free, zero-allocation
+JSON encoder, and the base `Logger` strives to avoid serialization overhead
+and allocations wherever possible. By building the high-level `SugaredLogger`
+on that foundation, zap lets users *choose* when they need to count every
+allocation and when they'd prefer a more familiar, loosely typed API.
+
+As measured by its own [benchmarking suite][], not only is zap more performant
+than comparable structured logging packages — it's also faster than the
+standard library. Like all benchmarks, take these with a grain of salt.[1](#footnote-versions)
+
+Log a message and 10 fields:
+
+{{.BenchmarkAddingFields}}
+
+Log a message with a logger that already has 10 fields of context:
+
+{{.BenchmarkAccumulatedContext}}
+
+Log a static string, without any context or `printf`-style templating:
+
+{{.BenchmarkWithoutFields}}
+
+## Development Status: Stable
+
+All APIs are finalized, and no breaking changes will be made in the 1.x series
+of releases. Users of semver-aware dependency management systems should pin
+zap to `^1`.
+
+
+
+Released under the [MIT License](LICENSE.txt).
+
+ In particular, keep in mind that we may be
+benchmarking against slightly older versions of other packages. Versions are
+pinned in zap's [glide.lock][] file. [↩](#anchor-versions)
+
+[doc-img]: https://godoc.org/go.uber.org/zap?status.svg
+[doc]: https://godoc.org/go.uber.org/zap
+[ci-img]: https://travis-ci.org/uber-go/zap.svg?branch=master
+[ci]: https://travis-ci.org/uber-go/zap
+[cov-img]: https://codecov.io/gh/uber-go/zap/branch/master/graph/badge.svg
+[cov]: https://codecov.io/gh/uber-go/zap
+[benchmarking suite]: https://github.com/uber-go/zap/tree/master/benchmarks
+[glide.lock]: https://github.com/uber-go/zap/blob/master/glide.lock
diff --git a/vendor/go.uber.org/zap/.travis.yml b/vendor/go.uber.org/zap/.travis.yml
new file mode 100644
index 0000000..e31d520
--- /dev/null
+++ b/vendor/go.uber.org/zap/.travis.yml
@@ -0,0 +1,22 @@
+language: go
+sudo: false
+go:
+ - 1.7
+ - 1.8
+go_import_path: go.uber.org/zap
+env:
+ global:
+ - GO15VENDOREXPERIMENT=1
+ - TEST_TIMEOUT_SCALE=10
+cache:
+ directories:
+ - vendor
+install:
+ - make dependencies
+script:
+ - make lint
+ - make test
+ - make bench
+after_success:
+ - make cover
+ - bash <(curl -s https://codecov.io/bash)
diff --git a/vendor/go.uber.org/zap/CHANGELOG.md b/vendor/go.uber.org/zap/CHANGELOG.md
new file mode 100644
index 0000000..b7541f6
--- /dev/null
+++ b/vendor/go.uber.org/zap/CHANGELOG.md
@@ -0,0 +1,245 @@
+# Changelog
+
+## v1.5.0 (22 Jul 2017)
+
+Enhancements:
+
+* [#460][] and [#470][]: Support errors produced by `go.uber.org/multierr`.
+* [#465][]: Support user-supplied encoders for logger names.
+
+Bugfixes:
+
+* [#477][]: Fix a bug that incorrectly truncated deep stacktraces.
+
+Thanks to @richard-tunein and @pavius for their contributions to this release.
+
+## v1.4.1 (08 Jun 2017)
+
+This release fixes two bugs.
+
+Bugfixes:
+
+* [#435][]: Support a variety of case conventions when unmarshaling levels.
+* [#444][]: Fix a panic in the observer.
+
+## v1.4.0 (12 May 2017)
+
+This release adds a few small features and is fully backward-compatible.
+
+Enhancements:
+
+* [#424][]: Add a `LineEnding` field to `EncoderConfig`, allowing users to
+ override the Unix-style default.
+* [#425][]: Preserve time zones when logging times.
+* [#431][]: Make `zap.AtomicLevel` implement `fmt.Stringer`, which makes a
+ variety of operations a bit simpler.
+
+## v1.3.0 (25 Apr 2017)
+
+This release adds an enhancement to zap's testing helpers as well as the
+ability to marshal an AtomicLevel. It is fully backward-compatible.
+
+Enhancements:
+
+* [#415][]: Add a substring-filtering helper to zap's observer. This is
+ particularly useful when testing the `SugaredLogger`.
+* [#416][]: Make `AtomicLevel` implement `encoding.TextMarshaler`.
+
+## v1.2.0 (13 Apr 2017)
+
+This release adds a gRPC compatibility wrapper. It is fully backward-compatible.
+
+Enhancements:
+
+* [#402][]: Add a `zapgrpc` package that wraps zap's Logger and implements
+ `grpclog.Logger`.
+
+## v1.1.0 (31 Mar 2017)
+
+This release fixes two bugs and adds some enhancements to zap's testing helpers.
+It is fully backward-compatible.
+
+Bugfixes:
+
+* [#385][]: Fix caller path trimming on Windows.
+* [#396][]: Fix a panic when attempting to use non-existent directories with
+ zap's configuration struct.
+
+Enhancements:
+
+* [#386][]: Add filtering helpers to zaptest's observing logger.
+
+Thanks to @moitias for contributing to this release.
+
+## v1.0.0 (14 Mar 2017)
+
+This is zap's first stable release. All exported APIs are now final, and no
+further breaking changes will be made in the 1.x release series. Anyone using a
+semver-aware dependency manager should now pin to `^1`.
+
+Breaking changes:
+
+* [#366][]: Add byte-oriented APIs to encoders to log UTF-8 encoded text without
+ casting from `[]byte` to `string`.
+* [#364][]: To support buffering outputs, add `Sync` methods to `zapcore.Core`,
+ `zap.Logger`, and `zap.SugaredLogger`.
+* [#371][]: Rename the `testutils` package to `zaptest`, which is less likely to
+ clash with other testing helpers.
+
+Bugfixes:
+
+* [#362][]: Make the ISO8601 time formatters fixed-width, which is friendlier
+ for tab-separated console output.
+* [#369][]: Remove the automatic locks in `zapcore.NewCore`, which allows zap to
+ work with concurrency-safe `WriteSyncer` implementations.
+* [#347][]: Stop reporting errors when trying to `fsync` standard out on Linux
+ systems.
+* [#373][]: Report the correct caller from zap's standard library
+ interoperability wrappers.
+
+Enhancements:
+
+* [#348][]: Add a registry allowing third-party encodings to work with zap's
+ built-in `Config`.
+* [#327][]: Make the representation of logger callers configurable (like times,
+ levels, and durations).
+* [#376][]: Allow third-party encoders to use their own buffer pools, which
+ removes the last performance advantage that zap's encoders have over plugins.
+* [#346][]: Add `CombineWriteSyncers`, a convenience function to tee multiple
+ `WriteSyncer`s and lock the result.
+* [#365][]: Make zap's stacktraces compatible with mid-stack inlining (coming in
+ Go 1.9).
+* [#372][]: Export zap's observing logger as `zaptest/observer`. This makes it
+ easier for particularly punctilious users to unit test their application's
+ logging.
+
+Thanks to @suyash, @htrendev, @flisky, @Ulexus, and @skipor for their
+contributions to this release.
+
+## v1.0.0-rc.3 (7 Mar 2017)
+
+This is the third release candidate for zap's stable release. There are no
+breaking changes.
+
+Bugfixes:
+
+* [#339][]: Byte slices passed to `zap.Any` are now correctly treated as binary blobs
+ rather than `[]uint8`.
+
+Enhancements:
+
+* [#307][]: Users can opt into colored output for log levels.
+* [#353][]: In addition to hijacking the output of the standard library's
+ package-global logging functions, users can now construct a zap-backed
+ `log.Logger` instance.
+* [#311][]: Frames from common runtime functions and some of zap's internal
+ machinery are now omitted from stacktraces.
+
+Thanks to @ansel1 and @suyash for their contributions to this release.
+
+## v1.0.0-rc.2 (21 Feb 2017)
+
+This is the second release candidate for zap's stable release. It includes two
+breaking changes.
+
+Breaking changes:
+
+* [#316][]: Zap's global loggers are now fully concurrency-safe
+ (previously, users had to ensure that `ReplaceGlobals` was called before the
+ loggers were in use). However, they must now be accessed via the `L()` and
+ `S()` functions. Users can update their projects with
+
+ ```
+ gofmt -r "zap.L -> zap.L()" -w .
+ gofmt -r "zap.S -> zap.S()" -w .
+ ```
+* [#309][] and [#317][]: RC1 was mistakenly shipped with invalid
+ JSON and YAML struct tags on all config structs. This release fixes the tags
+ and adds static analysis to prevent similar bugs in the future.
+
+Bugfixes:
+
+* [#321][]: Redirecting the standard library's `log` output now
+ correctly reports the logger's caller.
+
+Enhancements:
+
+* [#325][] and [#333][]: Zap now transparently supports non-standard, rich
+ errors like those produced by `github.com/pkg/errors`.
+* [#326][]: Though `New(nil)` continues to return a no-op logger, `NewNop()` is
+ now preferred. Users can update their projects with `gofmt -r 'zap.New(nil) ->
+ zap.NewNop()' -w .`.
+* [#300][]: Incorrectly importing zap as `github.com/uber-go/zap` now returns a
+ more informative error.
+
+Thanks to @skipor and @chapsuk for their contributions to this release.
+
+## v1.0.0-rc.1 (14 Feb 2017)
+
+This is the first release candidate for zap's stable release. There are multiple
+breaking changes and improvements from the pre-release version. Most notably:
+
+* **Zap's import path is now "go.uber.org/zap"** — all users will
+ need to update their code.
+* User-facing types and functions remain in the `zap` package. Code relevant
+ largely to extension authors is now in the `zapcore` package.
+* The `zapcore.Core` type makes it easy for third-party packages to use zap's
+ internals but provide a different user-facing API.
+* `Logger` is now a concrete type instead of an interface.
+* A less verbose (though slower) logging API is included by default.
+* Package-global loggers `L` and `S` are included.
+* A human-friendly console encoder is included.
+* A declarative config struct allows common logger configurations to be managed
+ as configuration instead of code.
+* Sampling is more accurate, and doesn't depend on the standard library's shared
+ timer heap.
+
+## v0.1.0-beta.1 (6 Feb 2017)
+
+This is a minor version, tagged to allow users to pin to the pre-1.0 APIs and
+upgrade at their leisure. Since this is the first tagged release, there are no
+backward compatibility concerns and all functionality is new.
+
+Early zap adopters should pin to the 0.1.x minor version until they're ready to
+upgrade to the upcoming stable release.
+
+[#316]: https://github.com/uber-go/zap/pull/316
+[#309]: https://github.com/uber-go/zap/pull/309
+[#317]: https://github.com/uber-go/zap/pull/317
+[#321]: https://github.com/uber-go/zap/pull/321
+[#325]: https://github.com/uber-go/zap/pull/325
+[#333]: https://github.com/uber-go/zap/pull/333
+[#326]: https://github.com/uber-go/zap/pull/326
+[#300]: https://github.com/uber-go/zap/pull/300
+[#339]: https://github.com/uber-go/zap/pull/339
+[#307]: https://github.com/uber-go/zap/pull/307
+[#353]: https://github.com/uber-go/zap/pull/353
+[#311]: https://github.com/uber-go/zap/pull/311
+[#366]: https://github.com/uber-go/zap/pull/366
+[#364]: https://github.com/uber-go/zap/pull/364
+[#371]: https://github.com/uber-go/zap/pull/371
+[#362]: https://github.com/uber-go/zap/pull/362
+[#369]: https://github.com/uber-go/zap/pull/369
+[#347]: https://github.com/uber-go/zap/pull/347
+[#373]: https://github.com/uber-go/zap/pull/373
+[#348]: https://github.com/uber-go/zap/pull/348
+[#327]: https://github.com/uber-go/zap/pull/327
+[#376]: https://github.com/uber-go/zap/pull/376
+[#346]: https://github.com/uber-go/zap/pull/346
+[#365]: https://github.com/uber-go/zap/pull/365
+[#372]: https://github.com/uber-go/zap/pull/372
+[#385]: https://github.com/uber-go/zap/pull/385
+[#396]: https://github.com/uber-go/zap/pull/396
+[#386]: https://github.com/uber-go/zap/pull/386
+[#402]: https://github.com/uber-go/zap/pull/402
+[#415]: https://github.com/uber-go/zap/pull/415
+[#416]: https://github.com/uber-go/zap/pull/416
+[#424]: https://github.com/uber-go/zap/pull/424
+[#425]: https://github.com/uber-go/zap/pull/425
+[#431]: https://github.com/uber-go/zap/pull/431
+[#435]: https://github.com/uber-go/zap/pull/435
+[#444]: https://github.com/uber-go/zap/pull/444
+[#477]: https://github.com/uber-go/zap/pull/477
+[#465]: https://github.com/uber-go/zap/pull/465
+[#460]: https://github.com/uber-go/zap/pull/460
+[#470]: https://github.com/uber-go/zap/pull/470
diff --git a/vendor/go.uber.org/zap/CONTRIBUTING.md b/vendor/go.uber.org/zap/CONTRIBUTING.md
new file mode 100644
index 0000000..6007373
--- /dev/null
+++ b/vendor/go.uber.org/zap/CONTRIBUTING.md
@@ -0,0 +1,79 @@
+# Contributing
+
+We'd love your help making zap the very best structured logging library in Go!
+
+If you'd like to add new exported APIs, please [open an issue][open-issue]
+describing your proposal — discussing API changes ahead of time makes
+pull request review much smoother.
+
+Note that you'll need to sign [Uber's Contributor License Agreement][cla]
+before we can accept any of your contributions. If necessary, a bot will remind
+you to accept the CLA when you open your pull request.
+
+## Setup
+
+[Fork][fork], then clone the repository:
+
+```
+mkdir -p $GOPATH/src/go.uber.org
+cd $GOPATH/src/go.uber.org
+git clone git@github.com:your_github_username/zap.git
+cd zap
+git remote add upstream https://github.com/uber-go/zap.git
+git fetch upstream
+```
+
+Install zap's dependencies:
+
+```
+make dependencies
+```
+
+Make sure that the tests and the linters pass:
+
+```
+make test
+make lint
+```
+
+If you're not using the minor version of Go specified in the Makefile's
+`LINTABLE_MINOR_VERSIONS` variable, `make lint` doesn't do anything. This is
+fine, but it means that you'll only discover lint failures after you open your
+pull request.
+
+## Making Changes
+
+Start by creating a new branch for your changes:
+
+```
+cd $GOPATH/src/go.uber.org/zap
+git checkout master
+git fetch upstream
+git rebase upstream/master
+git checkout -b cool_new_feature
+```
+
+Make your changes, then ensure that `make lint` and `make test` still pass. If
+you're satisfied with your changes, push them to your fork.
+
+```
+git push origin cool_new_feature
+```
+
+Then use the GitHub UI to open a pull request.
+
+At this point, you're waiting on us to review your changes. We *try* to respond
+to issues and pull requests within a few business days, and we may suggest some
+improvements or alternatives. Once your changes are approved, one of the
+project maintainers will merge them.
+
+We're much more likely to approve your changes if you:
+
+* Add tests for new functionality.
+* Write a [good commit message][commit-message].
+* Maintain backward compatibility.
+
+[fork]: https://github.com/uber-go/zap/fork
+[open-issue]: https://github.com/uber-go/zap/issues/new
+[cla]: https://cla-assistant.io/uber-go/zap
+[commit-message]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
diff --git a/vendor/go.uber.org/zap/FAQ.md b/vendor/go.uber.org/zap/FAQ.md
new file mode 100644
index 0000000..96e1012
--- /dev/null
+++ b/vendor/go.uber.org/zap/FAQ.md
@@ -0,0 +1,140 @@
+# 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)
+```
+
+[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
diff --git a/vendor/go.uber.org/zap/LICENSE.txt b/vendor/go.uber.org/zap/LICENSE.txt
new file mode 100644
index 0000000..6652bed
--- /dev/null
+++ b/vendor/go.uber.org/zap/LICENSE.txt
@@ -0,0 +1,19 @@
+Copyright (c) 2016-2017 Uber Technologies, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/go.uber.org/zap/Makefile b/vendor/go.uber.org/zap/Makefile
new file mode 100644
index 0000000..8706cd1
--- /dev/null
+++ b/vendor/go.uber.org/zap/Makefile
@@ -0,0 +1,76 @@
+export GO15VENDOREXPERIMENT=1
+
+BENCH_FLAGS ?= -cpuprofile=cpu.pprof -memprofile=mem.pprof -benchmem
+PKGS ?= $(shell glide novendor)
+# Many Go tools take file globs or directories as arguments instead of packages.
+PKG_FILES ?= *.go zapcore benchmarks buffer zapgrpc zaptest zaptest/observer internal/bufferpool internal/exit internal/color
+
+# The linting tools evolve with each Go version, so run them only on the latest
+# stable release.
+GO_VERSION := $(shell go version | cut -d " " -f 3)
+GO_MINOR_VERSION := $(word 2,$(subst ., ,$(GO_VERSION)))
+LINTABLE_MINOR_VERSIONS := 8
+ifneq ($(filter $(LINTABLE_MINOR_VERSIONS),$(GO_MINOR_VERSION)),)
+SHOULD_LINT := true
+endif
+
+
+.PHONY: all
+all: lint test
+
+.PHONY: dependencies
+dependencies:
+ @echo "Installing Glide and locked dependencies..."
+ glide --version || go get -u -f github.com/Masterminds/glide
+ glide install
+ @echo "Installing test dependencies..."
+ go install ./vendor/github.com/axw/gocov/gocov
+ go install ./vendor/github.com/mattn/goveralls
+ifdef SHOULD_LINT
+ @echo "Installing golint..."
+ go install ./vendor/github.com/golang/lint/golint
+else
+ @echo "Not installing golint, since we don't expect to lint on" $(GO_VERSION)
+endif
+
+# Disable printf-like invocation checking due to testify.assert.Error()
+VET_RULES := -printf=false
+
+.PHONY: lint
+lint:
+ifdef SHOULD_LINT
+ @rm -rf lint.log
+ @echo "Checking formatting..."
+ @gofmt -d -s $(PKG_FILES) 2>&1 | tee lint.log
+ @echo "Installing test dependencies for vet..."
+ @go test -i $(PKGS)
+ @echo "Checking vet..."
+ @$(foreach dir,$(PKG_FILES),go tool vet $(VET_RULES) $(dir) 2>&1 | tee -a lint.log;)
+ @echo "Checking lint..."
+ @$(foreach dir,$(PKGS),golint $(dir) 2>&1 | tee -a lint.log;)
+ @echo "Checking for unresolved FIXMEs..."
+ @git grep -i fixme | grep -v -e vendor -e Makefile | tee -a lint.log
+ @echo "Checking for license headers..."
+ @./check_license.sh | tee -a lint.log
+ @[ ! -s lint.log ]
+else
+ @echo "Skipping linters on" $(GO_VERSION)
+endif
+
+.PHONY: test
+test:
+ go test -race $(PKGS)
+
+.PHONY: cover
+cover:
+ ./scripts/cover.sh $(PKGS)
+
+.PHONY: bench
+BENCH ?= .
+bench:
+ @$(foreach pkg,$(PKGS),go test -bench=$(BENCH) -run="^$$" $(BENCH_FLAGS) $(pkg);)
+
+.PHONY: updatereadme
+updatereadme:
+ rm -f README.md
+ cat .readme.tmpl | go run internal/readme/readme.go > README.md
diff --git a/vendor/go.uber.org/zap/README.md b/vendor/go.uber.org/zap/README.md
new file mode 100644
index 0000000..3bb6d2a
--- /dev/null
+++ b/vendor/go.uber.org/zap/README.md
@@ -0,0 +1,123 @@
+# :zap: zap [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov]
+
+Blazing fast, structured, leveled logging in Go.
+
+## Installation
+
+`go get -u go.uber.org/zap`
+
+Note that zap only supports the two most recent minor versions of Go.
+
+## Quick Start
+
+In contexts where performance is nice, but not critical, use the
+`SugaredLogger`. It's 4-10x faster than than other structured logging
+packages and includes both structured and `printf`-style APIs.
+
+```go
+logger, _ := zap.NewProduction()
+defer logger.Sync() // flushes buffer, if any
+sugar := logger.Sugar()
+sugar.Infow("failed to fetch URL",
+ // Structured context as loosely typed key-value pairs.
+ "url", url,
+ "attempt", 3,
+ "backoff", time.Second,
+)
+sugar.Infof("Failed to fetch URL: %s", url)
+```
+
+When performance and type safety are critical, use the `Logger`. It's even
+faster than the `SugaredLogger` and allocates far less, but it only supports
+structured logging.
+
+```go
+logger, _ := zap.NewProduction()
+defer logger.Sync()
+logger.Info("failed to fetch URL",
+ // Structured context as strongly typed Field values.
+ zap.String("url", url),
+ zap.Int("attempt", 3),
+ zap.Duration("backoff", time.Second),
+)
+```
+
+See the [documentation][doc] and [FAQ](FAQ.md) for more details.
+
+## Performance
+
+For applications that log in the hot path, reflection-based serialization and
+string formatting are prohibitively expensive — they're CPU-intensive
+and make many small allocations. Put differently, using `encoding/json` and
+`fmt.Fprintf` to log tons of `interface{}`s makes your application slow.
+
+Zap takes a different approach. It includes a reflection-free, zero-allocation
+JSON encoder, and the base `Logger` strives to avoid serialization overhead
+and allocations wherever possible. By building the high-level `SugaredLogger`
+on that foundation, zap lets users *choose* when they need to count every
+allocation and when they'd prefer a more familiar, loosely typed API.
+
+As measured by its own [benchmarking suite][], not only is zap more performant
+than comparable structured logging packages — it's also faster than the
+standard library. Like all benchmarks, take these with a grain of salt.[1](#footnote-versions)
+
+Log a message and 10 fields:
+
+| Package | Time | Bytes Allocated | Objects Allocated |
+| :--- | :---: | :---: | :---: |
+| :zap: zap | 1692 ns/op | 705 B/op | 2 allocs/op |
+| :zap: zap (sugared) | 2507 ns/op | 1610 B/op | 20 allocs/op |
+| go-kit | 6327 ns/op | 2895 B/op | 66 allocs/op |
+| lion | 8036 ns/op | 5807 B/op | 63 allocs/op |
+| logrus | 8970 ns/op | 6092 B/op | 78 allocs/op |
+| apex/log | 17101 ns/op | 3832 B/op | 65 allocs/op |
+| log15 | 21398 ns/op | 5632 B/op | 93 allocs/op |
+
+Log a message with a logger that already has 10 fields of context:
+
+| Package | Time | Bytes Allocated | Objects Allocated |
+| :--- | :---: | :---: | :---: |
+| :zap: zap | 467 ns/op | 0 B/op | 0 allocs/op |
+| :zap: zap (sugared) | 597 ns/op | 80 B/op | 2 allocs/op |
+| lion | 5172 ns/op | 4074 B/op | 38 allocs/op |
+| go-kit | 6892 ns/op | 3078 B/op | 53 allocs/op |
+| logrus | 8102 ns/op | 4564 B/op | 63 allocs/op |
+| apex/log | 15332 ns/op | 2897 B/op | 51 allocs/op |
+| log15 | 16905 ns/op | 2642 B/op | 44 allocs/op |
+
+Log a static string, without any context or `printf`-style templating:
+
+| Package | Time | Bytes Allocated | Objects Allocated |
+| :--- | :---: | :---: | :---: |
+| :zap: zap | 465 ns/op | 0 B/op | 0 allocs/op |
+| standard library | 602 ns/op | 80 B/op | 2 allocs/op |
+| :zap: zap (sugared) | 647 ns/op | 80 B/op | 2 allocs/op |
+| go-kit | 994 ns/op | 656 B/op | 13 allocs/op |
+| lion | 1402 ns/op | 1224 B/op | 10 allocs/op |
+| logrus | 2299 ns/op | 1505 B/op | 27 allocs/op |
+| apex/log | 3148 ns/op | 584 B/op | 11 allocs/op |
+| log15 | 6329 ns/op | 1592 B/op | 26 allocs/op |
+
+## Development Status: Stable
+
+All APIs are finalized, and no breaking changes will be made in the 1.x series
+of releases. Users of semver-aware dependency management systems should pin
+zap to `^1`.
+
+
+
+Released under the [MIT License](LICENSE.txt).
+
+ In particular, keep in mind that we may be
+benchmarking against slightly older versions of other packages. Versions are
+pinned in zap's [glide.lock][] file. [↩](#anchor-versions)
+
+[doc-img]: https://godoc.org/go.uber.org/zap?status.svg
+[doc]: https://godoc.org/go.uber.org/zap
+[ci-img]: https://travis-ci.org/uber-go/zap.svg?branch=master
+[ci]: https://travis-ci.org/uber-go/zap
+[cov-img]: https://codecov.io/gh/uber-go/zap/branch/master/graph/badge.svg
+[cov]: https://codecov.io/gh/uber-go/zap
+[benchmarking suite]: https://github.com/uber-go/zap/tree/master/benchmarks
+[glide.lock]: https://github.com/uber-go/zap/blob/master/glide.lock
diff --git a/vendor/go.uber.org/zap/array.go b/vendor/go.uber.org/zap/array.go
new file mode 100644
index 0000000..3d4d49f
--- /dev/null
+++ b/vendor/go.uber.org/zap/array.go
@@ -0,0 +1,320 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "time"
+
+ "go.uber.org/zap/zapcore"
+)
+
+// Array constructs a field with the given key and ArrayMarshaler. It provides
+// a flexible, but still type-safe and efficient, way to add array-like types
+// to the logging context. The struct's MarshalLogArray method is called lazily.
+func Array(key string, val zapcore.ArrayMarshaler) zapcore.Field {
+ return zapcore.Field{Key: key, Type: zapcore.ArrayMarshalerType, Interface: val}
+}
+
+// Bools constructs a field that carries a slice of bools.
+func Bools(key string, bs []bool) zapcore.Field {
+ return Array(key, bools(bs))
+}
+
+// ByteStrings constructs a field that carries a slice of []byte, each of which
+// must be UTF-8 encoded text.
+func ByteStrings(key string, bss [][]byte) zapcore.Field {
+ return Array(key, byteStringsArray(bss))
+}
+
+// Complex128s constructs a field that carries a slice of complex numbers.
+func Complex128s(key string, nums []complex128) zapcore.Field {
+ return Array(key, complex128s(nums))
+}
+
+// Complex64s constructs a field that carries a slice of complex numbers.
+func Complex64s(key string, nums []complex64) zapcore.Field {
+ return Array(key, complex64s(nums))
+}
+
+// Durations constructs a field that carries a slice of time.Durations.
+func Durations(key string, ds []time.Duration) zapcore.Field {
+ return Array(key, durations(ds))
+}
+
+// Float64s constructs a field that carries a slice of floats.
+func Float64s(key string, nums []float64) zapcore.Field {
+ return Array(key, float64s(nums))
+}
+
+// Float32s constructs a field that carries a slice of floats.
+func Float32s(key string, nums []float32) zapcore.Field {
+ return Array(key, float32s(nums))
+}
+
+// Ints constructs a field that carries a slice of integers.
+func Ints(key string, nums []int) zapcore.Field {
+ return Array(key, ints(nums))
+}
+
+// Int64s constructs a field that carries a slice of integers.
+func Int64s(key string, nums []int64) zapcore.Field {
+ return Array(key, int64s(nums))
+}
+
+// Int32s constructs a field that carries a slice of integers.
+func Int32s(key string, nums []int32) zapcore.Field {
+ return Array(key, int32s(nums))
+}
+
+// Int16s constructs a field that carries a slice of integers.
+func Int16s(key string, nums []int16) zapcore.Field {
+ return Array(key, int16s(nums))
+}
+
+// Int8s constructs a field that carries a slice of integers.
+func Int8s(key string, nums []int8) zapcore.Field {
+ return Array(key, int8s(nums))
+}
+
+// Strings constructs a field that carries a slice of strings.
+func Strings(key string, ss []string) zapcore.Field {
+ return Array(key, stringArray(ss))
+}
+
+// Times constructs a field that carries a slice of time.Times.
+func Times(key string, ts []time.Time) zapcore.Field {
+ return Array(key, times(ts))
+}
+
+// Uints constructs a field that carries a slice of unsigned integers.
+func Uints(key string, nums []uint) zapcore.Field {
+ return Array(key, uints(nums))
+}
+
+// Uint64s constructs a field that carries a slice of unsigned integers.
+func Uint64s(key string, nums []uint64) zapcore.Field {
+ return Array(key, uint64s(nums))
+}
+
+// Uint32s constructs a field that carries a slice of unsigned integers.
+func Uint32s(key string, nums []uint32) zapcore.Field {
+ return Array(key, uint32s(nums))
+}
+
+// Uint16s constructs a field that carries a slice of unsigned integers.
+func Uint16s(key string, nums []uint16) zapcore.Field {
+ return Array(key, uint16s(nums))
+}
+
+// Uint8s constructs a field that carries a slice of unsigned integers.
+func Uint8s(key string, nums []uint8) zapcore.Field {
+ return Array(key, uint8s(nums))
+}
+
+// Uintptrs constructs a field that carries a slice of pointer addresses.
+func Uintptrs(key string, us []uintptr) zapcore.Field {
+ return Array(key, uintptrs(us))
+}
+
+// Errors constructs a field that carries a slice of errors.
+func Errors(key string, errs []error) zapcore.Field {
+ return Array(key, errArray(errs))
+}
+
+type bools []bool
+
+func (bs bools) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range bs {
+ arr.AppendBool(bs[i])
+ }
+ return nil
+}
+
+type byteStringsArray [][]byte
+
+func (bss byteStringsArray) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range bss {
+ arr.AppendByteString(bss[i])
+ }
+ return nil
+}
+
+type complex128s []complex128
+
+func (nums complex128s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendComplex128(nums[i])
+ }
+ return nil
+}
+
+type complex64s []complex64
+
+func (nums complex64s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendComplex64(nums[i])
+ }
+ return nil
+}
+
+type durations []time.Duration
+
+func (ds durations) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range ds {
+ arr.AppendDuration(ds[i])
+ }
+ return nil
+}
+
+type float64s []float64
+
+func (nums float64s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendFloat64(nums[i])
+ }
+ return nil
+}
+
+type float32s []float32
+
+func (nums float32s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendFloat32(nums[i])
+ }
+ return nil
+}
+
+type ints []int
+
+func (nums ints) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendInt(nums[i])
+ }
+ return nil
+}
+
+type int64s []int64
+
+func (nums int64s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendInt64(nums[i])
+ }
+ return nil
+}
+
+type int32s []int32
+
+func (nums int32s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendInt32(nums[i])
+ }
+ return nil
+}
+
+type int16s []int16
+
+func (nums int16s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendInt16(nums[i])
+ }
+ return nil
+}
+
+type int8s []int8
+
+func (nums int8s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendInt8(nums[i])
+ }
+ return nil
+}
+
+type stringArray []string
+
+func (ss stringArray) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range ss {
+ arr.AppendString(ss[i])
+ }
+ return nil
+}
+
+type times []time.Time
+
+func (ts times) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range ts {
+ arr.AppendTime(ts[i])
+ }
+ return nil
+}
+
+type uints []uint
+
+func (nums uints) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendUint(nums[i])
+ }
+ return nil
+}
+
+type uint64s []uint64
+
+func (nums uint64s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendUint64(nums[i])
+ }
+ return nil
+}
+
+type uint32s []uint32
+
+func (nums uint32s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendUint32(nums[i])
+ }
+ return nil
+}
+
+type uint16s []uint16
+
+func (nums uint16s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendUint16(nums[i])
+ }
+ return nil
+}
+
+type uint8s []uint8
+
+func (nums uint8s) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendUint8(nums[i])
+ }
+ return nil
+}
+
+type uintptrs []uintptr
+
+func (nums uintptrs) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range nums {
+ arr.AppendUintptr(nums[i])
+ }
+ return nil
+}
diff --git a/vendor/go.uber.org/zap/array_test.go b/vendor/go.uber.org/zap/array_test.go
new file mode 100644
index 0000000..df84d92
--- /dev/null
+++ b/vendor/go.uber.org/zap/array_test.go
@@ -0,0 +1,107 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "testing"
+ "time"
+
+ "go.uber.org/zap/zapcore"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func BenchmarkBoolsArrayMarshaler(b *testing.B) {
+ // Keep this benchmark here to capture the overhead of the ArrayMarshaler
+ // wrapper.
+ bs := make([]bool, 50)
+ enc := zapcore.NewJSONEncoder(zapcore.EncoderConfig{})
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ Bools("array", bs).AddTo(enc.Clone())
+ }
+}
+
+func BenchmarkBoolsReflect(b *testing.B) {
+ bs := make([]bool, 50)
+ enc := zapcore.NewJSONEncoder(zapcore.EncoderConfig{})
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ Reflect("array", bs).AddTo(enc.Clone())
+ }
+}
+
+func TestArrayWrappers(t *testing.T) {
+ tests := []struct {
+ desc string
+ field zapcore.Field
+ expected []interface{}
+ }{
+ {"empty bools", Bools("", []bool{}), []interface{}(nil)},
+ {"empty byte strings", ByteStrings("", [][]byte{}), []interface{}(nil)},
+ {"empty complex128s", Complex128s("", []complex128{}), []interface{}(nil)},
+ {"empty complex64s", Complex64s("", []complex64{}), []interface{}(nil)},
+ {"empty durations", Durations("", []time.Duration{}), []interface{}(nil)},
+ {"empty float64s", Float64s("", []float64{}), []interface{}(nil)},
+ {"empty float32s", Float32s("", []float32{}), []interface{}(nil)},
+ {"empty ints", Ints("", []int{}), []interface{}(nil)},
+ {"empty int64s", Int64s("", []int64{}), []interface{}(nil)},
+ {"empty int32s", Int32s("", []int32{}), []interface{}(nil)},
+ {"empty int16s", Int16s("", []int16{}), []interface{}(nil)},
+ {"empty int8s", Int8s("", []int8{}), []interface{}(nil)},
+ {"empty strings", Strings("", []string{}), []interface{}(nil)},
+ {"empty times", Times("", []time.Time{}), []interface{}(nil)},
+ {"empty uints", Uints("", []uint{}), []interface{}(nil)},
+ {"empty uint64s", Uint64s("", []uint64{}), []interface{}(nil)},
+ {"empty uint32s", Uint32s("", []uint32{}), []interface{}(nil)},
+ {"empty uint16s", Uint16s("", []uint16{}), []interface{}(nil)},
+ {"empty uint8s", Uint8s("", []uint8{}), []interface{}(nil)},
+ {"empty uintptrs", Uintptrs("", []uintptr{}), []interface{}(nil)},
+ {"bools", Bools("", []bool{true, false}), []interface{}{true, false}},
+ {"byte strings", ByteStrings("", [][]byte{{1, 2}, {3, 4}}), []interface{}{[]byte{1, 2}, []byte{3, 4}}},
+ {"complex128s", Complex128s("", []complex128{1 + 2i, 3 + 4i}), []interface{}{1 + 2i, 3 + 4i}},
+ {"complex64s", Complex64s("", []complex64{1 + 2i, 3 + 4i}), []interface{}{complex64(1 + 2i), complex64(3 + 4i)}},
+ {"durations", Durations("", []time.Duration{1, 2}), []interface{}{time.Nanosecond, 2 * time.Nanosecond}},
+ {"float64s", Float64s("", []float64{1.2, 3.4}), []interface{}{1.2, 3.4}},
+ {"float32s", Float32s("", []float32{1.2, 3.4}), []interface{}{float32(1.2), float32(3.4)}},
+ {"ints", Ints("", []int{1, 2}), []interface{}{1, 2}},
+ {"int64s", Int64s("", []int64{1, 2}), []interface{}{int64(1), int64(2)}},
+ {"int32s", Int32s("", []int32{1, 2}), []interface{}{int32(1), int32(2)}},
+ {"int16s", Int16s("", []int16{1, 2}), []interface{}{int16(1), int16(2)}},
+ {"int8s", Int8s("", []int8{1, 2}), []interface{}{int8(1), int8(2)}},
+ {"strings", Strings("", []string{"foo", "bar"}), []interface{}{"foo", "bar"}},
+ {"times", Times("", []time.Time{time.Unix(0, 0), time.Unix(0, 0)}), []interface{}{time.Unix(0, 0), time.Unix(0, 0)}},
+ {"uints", Uints("", []uint{1, 2}), []interface{}{uint(1), uint(2)}},
+ {"uint64s", Uint64s("", []uint64{1, 2}), []interface{}{uint64(1), uint64(2)}},
+ {"uint32s", Uint32s("", []uint32{1, 2}), []interface{}{uint32(1), uint32(2)}},
+ {"uint16s", Uint16s("", []uint16{1, 2}), []interface{}{uint16(1), uint16(2)}},
+ {"uint8s", Uint8s("", []uint8{1, 2}), []interface{}{uint8(1), uint8(2)}},
+ {"uintptrs", Uintptrs("", []uintptr{1, 2}), []interface{}{uintptr(1), uintptr(2)}},
+ }
+
+ for _, tt := range tests {
+ enc := zapcore.NewMapObjectEncoder()
+ tt.field.Key = "k"
+ tt.field.AddTo(enc)
+ assert.Equal(t, tt.expected, enc.Fields["k"], "%s: unexpected map contents.", tt.desc)
+ assert.Equal(t, 1, len(enc.Fields), "%s: found extra keys in map: %v", tt.desc, enc.Fields)
+ }
+}
diff --git a/vendor/go.uber.org/zap/benchmarks/apex_test.go b/vendor/go.uber.org/zap/benchmarks/apex_test.go
new file mode 100644
index 0000000..939e4df
--- /dev/null
+++ b/vendor/go.uber.org/zap/benchmarks/apex_test.go
@@ -0,0 +1,58 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package benchmarks
+
+import (
+ "io/ioutil"
+ "time"
+
+ "github.com/apex/log"
+ "github.com/apex/log/handlers/json"
+)
+
+func newDisabledApexLog() *log.Logger {
+ return &log.Logger{
+ Handler: json.New(ioutil.Discard),
+ Level: log.ErrorLevel,
+ }
+}
+
+func newApexLog() *log.Logger {
+ return &log.Logger{
+ Handler: json.New(ioutil.Discard),
+ Level: log.DebugLevel,
+ }
+}
+
+func fakeApexFields() log.Fields {
+ return log.Fields{
+ "int": 1,
+ "int64": int64(1),
+ "float": 3.0,
+ "string": "four!",
+ "bool": true,
+ "time": time.Unix(0, 0),
+ "error": errExample.Error(),
+ "duration": time.Second,
+ "user-defined type": _jane,
+ "another string": "done!",
+ }
+}
diff --git a/vendor/go.uber.org/zap/benchmarks/doc.go b/vendor/go.uber.org/zap/benchmarks/doc.go
new file mode 100644
index 0000000..b79f79f
--- /dev/null
+++ b/vendor/go.uber.org/zap/benchmarks/doc.go
@@ -0,0 +1,23 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+// Package benchmarks contains only benchmarks comparing zap to other
+// structured logging libraries.
+package benchmarks
diff --git a/vendor/go.uber.org/zap/benchmarks/kit_test.go b/vendor/go.uber.org/zap/benchmarks/kit_test.go
new file mode 100644
index 0000000..b9da293
--- /dev/null
+++ b/vendor/go.uber.org/zap/benchmarks/kit_test.go
@@ -0,0 +1,31 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package benchmarks
+
+import (
+ "io/ioutil"
+
+ "github.com/go-kit/kit/log"
+)
+
+func newKitLog(fields ...interface{}) log.Logger {
+ return log.With(log.NewJSONLogger(ioutil.Discard), fields...)
+}
diff --git a/vendor/go.uber.org/zap/benchmarks/lion_test.go b/vendor/go.uber.org/zap/benchmarks/lion_test.go
new file mode 100644
index 0000000..6c41cb1
--- /dev/null
+++ b/vendor/go.uber.org/zap/benchmarks/lion_test.go
@@ -0,0 +1,31 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package benchmarks
+
+import (
+ "io/ioutil"
+
+ "go.pedge.io/lion"
+)
+
+func newLion() lion.Logger {
+ return lion.NewLogger(lion.NewJSONWritePusher(ioutil.Discard))
+}
diff --git a/vendor/go.uber.org/zap/benchmarks/log15_test.go b/vendor/go.uber.org/zap/benchmarks/log15_test.go
new file mode 100644
index 0000000..70a60b3
--- /dev/null
+++ b/vendor/go.uber.org/zap/benchmarks/log15_test.go
@@ -0,0 +1,33 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package benchmarks
+
+import (
+ "io/ioutil"
+
+ "gopkg.in/inconshreveable/log15.v2"
+)
+
+func newLog15() log15.Logger {
+ logger := log15.New()
+ logger.SetHandler(log15.StreamHandler(ioutil.Discard, log15.JsonFormat()))
+ return logger
+}
diff --git a/vendor/go.uber.org/zap/benchmarks/logrus_test.go b/vendor/go.uber.org/zap/benchmarks/logrus_test.go
new file mode 100644
index 0000000..86534aa
--- /dev/null
+++ b/vendor/go.uber.org/zap/benchmarks/logrus_test.go
@@ -0,0 +1,58 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package benchmarks
+
+import (
+ "io/ioutil"
+ "time"
+
+ "github.com/sirupsen/logrus"
+)
+
+func newDisabledLogrus() *logrus.Logger {
+ logger := newLogrus()
+ logger.Level = logrus.ErrorLevel
+ return logger
+}
+
+func newLogrus() *logrus.Logger {
+ return &logrus.Logger{
+ Out: ioutil.Discard,
+ Formatter: new(logrus.JSONFormatter),
+ Hooks: make(logrus.LevelHooks),
+ Level: logrus.DebugLevel,
+ }
+}
+
+func fakeLogrusFields() logrus.Fields {
+ return logrus.Fields{
+ "int": 1,
+ "int64": int64(1),
+ "float": 3.0,
+ "string": "four!",
+ "bool": true,
+ "time": time.Unix(0, 0),
+ "error": errExample.Error(),
+ "duration": time.Second,
+ "user-defined type": _jane,
+ "another string": "done!",
+ }
+}
diff --git a/vendor/go.uber.org/zap/benchmarks/scenario_bench_test.go b/vendor/go.uber.org/zap/benchmarks/scenario_bench_test.go
new file mode 100644
index 0000000..18230dc
--- /dev/null
+++ b/vendor/go.uber.org/zap/benchmarks/scenario_bench_test.go
@@ -0,0 +1,565 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package benchmarks
+
+import (
+ "io/ioutil"
+ "log"
+ "testing"
+ "time"
+
+ "go.uber.org/zap"
+)
+
+func BenchmarkDisabledWithoutFields(b *testing.B) {
+ b.Logf("Logging at a disabled level without any structured context.")
+ b.Run("Zap", func(b *testing.B) {
+ logger := newZapLogger(zap.ErrorLevel)
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Info(getMessage(0))
+ }
+ })
+ })
+ b.Run("Zap.Check", func(b *testing.B) {
+ logger := newZapLogger(zap.ErrorLevel)
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ if m := logger.Check(zap.InfoLevel, getMessage(0)); m != nil {
+ m.Write()
+ }
+ }
+ })
+ })
+ b.Run("Zap.Sugar", func(b *testing.B) {
+ logger := newZapLogger(zap.ErrorLevel).Sugar()
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Info(getMessage(0))
+ }
+ })
+ })
+ b.Run("Zap.SugarFormatting", func(b *testing.B) {
+ logger := newZapLogger(zap.ErrorLevel).Sugar()
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Infof("%v %v %v %s %v %v %v %v %v %s\n",
+ 1,
+ int64(1),
+ 3.0,
+ "four!",
+ true,
+ time.Unix(0, 0),
+ errExample,
+ time.Second,
+ _jane,
+ "done!",
+ )
+ }
+ })
+ })
+ b.Run("apex/log", func(b *testing.B) {
+ logger := newDisabledApexLog()
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Info(getMessage(0))
+ }
+ })
+ })
+ b.Run("sirupsen/logrus", func(b *testing.B) {
+ logger := newDisabledLogrus()
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Info(getMessage(0))
+ }
+ })
+ })
+}
+
+func BenchmarkDisabledAccumulatedContext(b *testing.B) {
+ b.Logf("Logging at a disabled level with some accumulated context.")
+ b.Run("Zap", func(b *testing.B) {
+ logger := newZapLogger(zap.ErrorLevel).With(fakeFields()...)
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Info(getMessage(0))
+ }
+ })
+ })
+ b.Run("Zap.Check", func(b *testing.B) {
+ logger := newZapLogger(zap.ErrorLevel).With(fakeFields()...)
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ if m := logger.Check(zap.InfoLevel, getMessage(0)); m != nil {
+ m.Write()
+ }
+ }
+ })
+ })
+ b.Run("Zap.Sugar", func(b *testing.B) {
+ logger := newZapLogger(zap.ErrorLevel).With(fakeFields()...).Sugar()
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Info(getMessage(0))
+ }
+ })
+ })
+ b.Run("Zap.SugarFormatting", func(b *testing.B) {
+ logger := newZapLogger(zap.ErrorLevel).With(fakeFields()...).Sugar()
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Infof("%v %v %v %s %v %v %v %v %v %s\n",
+ 1,
+ int64(1),
+ 3.0,
+ "four!",
+ true,
+ time.Unix(0, 0),
+ errExample,
+ time.Second,
+ _jane,
+ "done!",
+ )
+ }
+ })
+ })
+ b.Run("apex/log", func(b *testing.B) {
+ logger := newDisabledApexLog().WithFields(fakeApexFields())
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Info(getMessage(0))
+ }
+ })
+ })
+ b.Run("sirupsen/logrus", func(b *testing.B) {
+ logger := newDisabledLogrus().WithFields(fakeLogrusFields())
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Info(getMessage(0))
+ }
+ })
+ })
+}
+
+func BenchmarkDisabledAddingFields(b *testing.B) {
+ b.Logf("Logging at a disabled level, adding context at each log site.")
+ b.Run("Zap", func(b *testing.B) {
+ logger := newZapLogger(zap.ErrorLevel)
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Info(getMessage(0), fakeFields()...)
+ }
+ })
+ })
+ b.Run("Zap.Check", func(b *testing.B) {
+ logger := newZapLogger(zap.ErrorLevel)
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ if m := logger.Check(zap.InfoLevel, getMessage(0)); m != nil {
+ m.Write(fakeFields()...)
+ }
+ }
+ })
+ })
+ b.Run("Zap.Sugar", func(b *testing.B) {
+ logger := newZapLogger(zap.ErrorLevel).Sugar()
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Infow(getMessage(0), fakeSugarFields()...)
+ }
+ })
+ })
+ b.Run("apex/log", func(b *testing.B) {
+ logger := newDisabledApexLog()
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.WithFields(fakeApexFields()).Info(getMessage(0))
+ }
+ })
+ })
+ b.Run("sirupsen/logrus", func(b *testing.B) {
+ logger := newDisabledLogrus()
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.WithFields(fakeLogrusFields()).Info(getMessage(0))
+ }
+ })
+ })
+}
+
+func BenchmarkWithoutFields(b *testing.B) {
+ b.Logf("Logging without any structured context.")
+ b.Run("Zap", func(b *testing.B) {
+ logger := newZapLogger(zap.DebugLevel)
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Info(getMessage(0))
+ }
+ })
+ })
+ b.Run("Zap.Check", func(b *testing.B) {
+ logger := newZapLogger(zap.DebugLevel)
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ if ce := logger.Check(zap.InfoLevel, getMessage(0)); ce != nil {
+ ce.Write()
+ }
+ }
+ })
+ })
+ b.Run("Zap.CheckSampled", func(b *testing.B) {
+ logger := newSampledLogger(zap.DebugLevel)
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ i := 0
+ for pb.Next() {
+ i++
+ if ce := logger.Check(zap.InfoLevel, getMessage(i)); ce != nil {
+ ce.Write()
+ }
+ }
+ })
+ })
+ b.Run("Zap.Sugar", func(b *testing.B) {
+ logger := newZapLogger(zap.DebugLevel).Sugar()
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Info(getMessage(0))
+ }
+ })
+ })
+ b.Run("Zap.SugarFormatting", func(b *testing.B) {
+ logger := newZapLogger(zap.DebugLevel).Sugar()
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Infof("%v %v %v %s %v %v %v %v %v %s\n",
+ 1,
+ int64(1),
+ 3.0,
+ "four!",
+ true,
+ time.Unix(0, 0),
+ errExample,
+ time.Second,
+ _jane,
+ "done!",
+ )
+ }
+ })
+ })
+ b.Run("apex/log", func(b *testing.B) {
+ logger := newApexLog()
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Info(getMessage(0))
+ }
+ })
+ })
+ b.Run("go-kit/kit/log", func(b *testing.B) {
+ logger := newKitLog()
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Log(getMessage(0), getMessage(1))
+ }
+ })
+ })
+ b.Run("inconshreveable/log15", func(b *testing.B) {
+ logger := newLog15()
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Info(getMessage(0))
+ }
+ })
+ })
+ b.Run("sirupsen/logrus", func(b *testing.B) {
+ logger := newLogrus()
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Info(getMessage(0))
+ }
+ })
+ })
+ b.Run("go.pedge.io/lion", func(b *testing.B) {
+ logger := newLion()
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Printf(getMessage(0))
+ }
+ })
+ })
+ b.Run("stdlib.Println", func(b *testing.B) {
+ logger := log.New(ioutil.Discard, "", log.LstdFlags)
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Println(getMessage(0))
+ }
+ })
+ })
+ b.Run("stdlib.Printf", func(b *testing.B) {
+ logger := log.New(ioutil.Discard, "", log.LstdFlags)
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Printf("%v %v %v %s %v %v %v %v %v %s\n",
+ 1,
+ int64(1),
+ 3.0,
+ "four!",
+ true,
+ time.Unix(0, 0),
+ errExample,
+ time.Second,
+ _jane,
+ "done!",
+ )
+ }
+ })
+ })
+}
+
+func BenchmarkAccumulatedContext(b *testing.B) {
+ b.Logf("Logging with some accumulated context.")
+ b.Run("Zap", func(b *testing.B) {
+ logger := newZapLogger(zap.DebugLevel).With(fakeFields()...)
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Info(getMessage(0))
+ }
+ })
+ })
+ b.Run("Zap.Check", func(b *testing.B) {
+ logger := newZapLogger(zap.DebugLevel).With(fakeFields()...)
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ if ce := logger.Check(zap.InfoLevel, getMessage(0)); ce != nil {
+ ce.Write()
+ }
+ }
+ })
+ })
+ b.Run("Zap.CheckSampled", func(b *testing.B) {
+ logger := newSampledLogger(zap.DebugLevel).With(fakeFields()...)
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ i := 0
+ for pb.Next() {
+ i++
+ if ce := logger.Check(zap.InfoLevel, getMessage(i)); ce != nil {
+ ce.Write()
+ }
+ }
+ })
+ })
+ b.Run("Zap.Sugar", func(b *testing.B) {
+ logger := newZapLogger(zap.DebugLevel).With(fakeFields()...).Sugar()
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Info(getMessage(0))
+ }
+ })
+ })
+ b.Run("Zap.SugarFormatting", func(b *testing.B) {
+ logger := newZapLogger(zap.DebugLevel).With(fakeFields()...).Sugar()
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Infof("%v %v %v %s %v %v %v %v %v %s\n",
+ 1,
+ int64(1),
+ 3.0,
+ "four!",
+ true,
+ time.Unix(0, 0),
+ errExample,
+ time.Second,
+ _jane,
+ "done!",
+ )
+ }
+ })
+ })
+ b.Run("apex/log", func(b *testing.B) {
+ logger := newApexLog().WithFields(fakeApexFields())
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Info(getMessage(0))
+ }
+ })
+ })
+ b.Run("go-kit/kit/log", func(b *testing.B) {
+ logger := newKitLog(fakeSugarFields()...)
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Log(getMessage(0), getMessage(1))
+ }
+ })
+ })
+ b.Run("inconshreveable/log15", func(b *testing.B) {
+ logger := newLog15().New(fakeSugarFields())
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Info(getMessage(0))
+ }
+ })
+ })
+ b.Run("sirupsen/logrus", func(b *testing.B) {
+ logger := newLogrus().WithFields(fakeLogrusFields())
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Info(getMessage(0))
+ }
+ })
+ })
+ b.Run("go.pedge.io/lion", func(b *testing.B) {
+ logger := newLion().WithFields(fakeLogrusFields())
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Infof(getMessage(0))
+ }
+ })
+ })
+}
+
+func BenchmarkAddingFields(b *testing.B) {
+ b.Logf("Logging with additional context at each log site.")
+ b.Run("Zap", func(b *testing.B) {
+ logger := newZapLogger(zap.DebugLevel)
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Info(getMessage(0), fakeFields()...)
+ }
+ })
+ })
+ b.Run("Zap.Check", func(b *testing.B) {
+ logger := newZapLogger(zap.DebugLevel)
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ if ce := logger.Check(zap.InfoLevel, getMessage(0)); ce != nil {
+ ce.Write(fakeFields()...)
+ }
+ }
+ })
+ })
+ b.Run("Zap.CheckSampled", func(b *testing.B) {
+ logger := newSampledLogger(zap.DebugLevel)
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ i := 0
+ for pb.Next() {
+ i++
+ if ce := logger.Check(zap.InfoLevel, getMessage(i)); ce != nil {
+ ce.Write(fakeFields()...)
+ }
+ }
+ })
+ })
+ b.Run("Zap.Sugar", func(b *testing.B) {
+ logger := newZapLogger(zap.DebugLevel).Sugar()
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Infow(getMessage(0), fakeSugarFields()...)
+ }
+ })
+ })
+ b.Run("apex/log", func(b *testing.B) {
+ logger := newApexLog()
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.WithFields(fakeApexFields()).Info(getMessage(0))
+ }
+ })
+ })
+ b.Run("go-kit/kit/log", func(b *testing.B) {
+ logger := newKitLog()
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Log(fakeSugarFields()...)
+ }
+ })
+ })
+ b.Run("inconshreveable/log15", func(b *testing.B) {
+ logger := newLog15()
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Info(getMessage(0), fakeSugarFields()...)
+ }
+ })
+ })
+ b.Run("sirupsen/logrus", func(b *testing.B) {
+ logger := newLogrus()
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.WithFields(fakeLogrusFields()).Info(getMessage(0))
+ }
+ })
+ })
+ b.Run("go.pedge.io/lion", func(b *testing.B) {
+ logger := newLion()
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.WithFields(fakeLogrusFields()).Infof(getMessage(0))
+ }
+ })
+ })
+}
diff --git a/vendor/go.uber.org/zap/benchmarks/zap_test.go b/vendor/go.uber.org/zap/benchmarks/zap_test.go
new file mode 100644
index 0000000..e0f2b08
--- /dev/null
+++ b/vendor/go.uber.org/zap/benchmarks/zap_test.go
@@ -0,0 +1,117 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package benchmarks
+
+import (
+ "errors"
+ "fmt"
+ "time"
+
+ "go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
+ "go.uber.org/zap/zaptest"
+)
+
+var (
+ errExample = errors.New("fail")
+
+ _messages = fakeMessages(1000)
+)
+
+func fakeMessages(n int) []string {
+ messages := make([]string, n)
+ for i := range messages {
+ messages[i] = fmt.Sprintf("Test logging, but use a somewhat realistic message length. (#%v)", i)
+ }
+ return messages
+}
+
+func getMessage(iter int) string {
+ return _messages[iter%1000]
+}
+
+type user struct {
+ Name string `json:"name"`
+ Email string `json:"email"`
+ CreatedAt time.Time `json:"created_at"`
+}
+
+func (u user) MarshalLogObject(enc zapcore.ObjectEncoder) error {
+ enc.AddString("name", u.Name)
+ enc.AddString("email", u.Email)
+ enc.AddInt64("created_at", u.CreatedAt.UnixNano())
+ return nil
+}
+
+var _jane = user{
+ Name: "Jane Doe",
+ Email: "jane@test.com",
+ CreatedAt: time.Date(1980, 1, 1, 12, 0, 0, 0, time.UTC),
+}
+
+func newZapLogger(lvl zapcore.Level) *zap.Logger {
+ // use the canned production encoder configuration
+ enc := zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
+ return zap.New(zapcore.NewCore(
+ enc,
+ &zaptest.Discarder{},
+ lvl,
+ ))
+}
+
+func newSampledLogger(lvl zapcore.Level) *zap.Logger {
+ return zap.New(zapcore.NewSampler(
+ newZapLogger(zap.DebugLevel).Core(),
+ 100*time.Millisecond,
+ 10, // first
+ 10, // thereafter
+ ))
+}
+
+func fakeFields() []zapcore.Field {
+ return []zapcore.Field{
+ zap.Int("int", 1),
+ zap.Int64("int64", 2),
+ zap.Float64("float", 3.0),
+ zap.String("string", "four!"),
+ zap.Bool("bool", true),
+ zap.Time("time", time.Unix(0, 0)),
+ zap.Error(errExample),
+ zap.Duration("duration", time.Second),
+ zap.Object("user-defined type", _jane),
+ zap.String("another string", "done!"),
+ }
+}
+
+func fakeSugarFields() []interface{} {
+ return []interface{}{
+ "int", 1,
+ "int64", 2,
+ "float", 3.0,
+ "string", "four!",
+ "bool", true,
+ "time", time.Unix(0, 0),
+ "error", errExample,
+ "duration", time.Second,
+ "user-defined type", _jane,
+ "another string", "done!",
+ }
+}
diff --git a/vendor/go.uber.org/zap/buffer/buffer.go b/vendor/go.uber.org/zap/buffer/buffer.go
new file mode 100644
index 0000000..ea6fdc8
--- /dev/null
+++ b/vendor/go.uber.org/zap/buffer/buffer.go
@@ -0,0 +1,106 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+// Package buffer provides a thin wrapper around a byte slice. Unlike the
+// standard library's bytes.Buffer, it supports a portion of the strconv
+// package's zero-allocation formatters.
+package buffer
+
+import "strconv"
+
+const _size = 1024 // by default, create 1 KiB buffers
+
+// Buffer is a thin wrapper around a byte slice. It's intended to be pooled, so
+// the only way to construct one is via a Pool.
+type Buffer struct {
+ bs []byte
+ pool Pool
+}
+
+// AppendByte writes a single byte to the Buffer.
+func (b *Buffer) AppendByte(v byte) {
+ b.bs = append(b.bs, v)
+}
+
+// AppendString writes a string to the Buffer.
+func (b *Buffer) AppendString(s string) {
+ b.bs = append(b.bs, s...)
+}
+
+// AppendInt appends an integer to the underlying buffer (assuming base 10).
+func (b *Buffer) AppendInt(i int64) {
+ b.bs = strconv.AppendInt(b.bs, i, 10)
+}
+
+// AppendUint appends an unsigned integer to the underlying buffer (assuming
+// base 10).
+func (b *Buffer) AppendUint(i uint64) {
+ b.bs = strconv.AppendUint(b.bs, i, 10)
+}
+
+// AppendBool appends a bool to the underlying buffer.
+func (b *Buffer) AppendBool(v bool) {
+ b.bs = strconv.AppendBool(b.bs, v)
+}
+
+// AppendFloat appends a float to the underlying buffer. It doesn't quote NaN
+// or +/- Inf.
+func (b *Buffer) AppendFloat(f float64, bitSize int) {
+ b.bs = strconv.AppendFloat(b.bs, f, 'f', -1, bitSize)
+}
+
+// Len returns the length of the underlying byte slice.
+func (b *Buffer) Len() int {
+ return len(b.bs)
+}
+
+// Cap returns the capacity of the underlying byte slice.
+func (b *Buffer) Cap() int {
+ return cap(b.bs)
+}
+
+// Bytes returns a mutable reference to the underlying byte slice.
+func (b *Buffer) Bytes() []byte {
+ return b.bs
+}
+
+// String returns a string copy of the underlying byte slice.
+func (b *Buffer) String() string {
+ return string(b.bs)
+}
+
+// Reset resets the underlying byte slice. Subsequent writes re-use the slice's
+// backing array.
+func (b *Buffer) Reset() {
+ b.bs = b.bs[:0]
+}
+
+// Write implements io.Writer.
+func (b *Buffer) Write(bs []byte) (int, error) {
+ b.bs = append(b.bs, bs...)
+ return len(bs), nil
+}
+
+// Free returns the Buffer to its Pool.
+//
+// Callers must not retain references to the Buffer after calling Free.
+func (b *Buffer) Free() {
+ b.pool.put(b)
+}
diff --git a/vendor/go.uber.org/zap/buffer/buffer_test.go b/vendor/go.uber.org/zap/buffer/buffer_test.go
new file mode 100644
index 0000000..59bc08a
--- /dev/null
+++ b/vendor/go.uber.org/zap/buffer/buffer_test.go
@@ -0,0 +1,91 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package buffer
+
+import (
+ "bytes"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestBufferWrites(t *testing.T) {
+ buf := NewPool().Get()
+
+ tests := []struct {
+ desc string
+ f func()
+ want string
+ }{
+ {"AppendByte", func() { buf.AppendByte('v') }, "v"},
+ {"AppendString", func() { buf.AppendString("foo") }, "foo"},
+ {"AppendIntPositive", func() { buf.AppendInt(42) }, "42"},
+ {"AppendIntNegative", func() { buf.AppendInt(-42) }, "-42"},
+ {"AppendUint", func() { buf.AppendUint(42) }, "42"},
+ {"AppendBool", func() { buf.AppendBool(true) }, "true"},
+ {"AppendFloat64", func() { buf.AppendFloat(3.14, 64) }, "3.14"},
+ // Intenationally introduce some floating-point error.
+ {"AppendFloat32", func() { buf.AppendFloat(float64(float32(3.14)), 32) }, "3.14"},
+ {"AppendWrite", func() { buf.Write([]byte("foo")) }, "foo"},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.desc, func(t *testing.T) {
+ buf.Reset()
+ tt.f()
+ assert.Equal(t, tt.want, buf.String(), "Unexpected buffer.String().")
+ assert.Equal(t, tt.want, string(buf.Bytes()), "Unexpected string(buffer.Bytes()).")
+ assert.Equal(t, len(tt.want), buf.Len(), "Unexpected buffer length.")
+ // We're not writing more than a kibibyte in tests.
+ assert.Equal(t, _size, buf.Cap(), "Expected buffer capacity to remain constant.")
+ })
+ }
+}
+
+func BenchmarkBuffers(b *testing.B) {
+ // Because we use the strconv.AppendFoo functions so liberally, we can't
+ // use the standard library's bytes.Buffer anyways (without incurring a
+ // bunch of extra allocations). Nevertheless, let's make sure that we're
+ // not losing any precious nanoseconds.
+ str := strings.Repeat("a", 1024)
+ slice := make([]byte, 1024)
+ buf := bytes.NewBuffer(slice)
+ custom := NewPool().Get()
+ b.Run("ByteSlice", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ slice = append(slice, str...)
+ slice = slice[:0]
+ }
+ })
+ b.Run("BytesBuffer", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ buf.WriteString(str)
+ buf.Reset()
+ }
+ })
+ b.Run("CustomBuffer", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ custom.AppendString(str)
+ custom.Reset()
+ }
+ })
+}
diff --git a/vendor/go.uber.org/zap/buffer/pool.go b/vendor/go.uber.org/zap/buffer/pool.go
new file mode 100644
index 0000000..8fb3e20
--- /dev/null
+++ b/vendor/go.uber.org/zap/buffer/pool.go
@@ -0,0 +1,49 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package buffer
+
+import "sync"
+
+// A Pool is a type-safe wrapper around a sync.Pool.
+type Pool struct {
+ p *sync.Pool
+}
+
+// NewPool constructs a new Pool.
+func NewPool() Pool {
+ return Pool{p: &sync.Pool{
+ New: func() interface{} {
+ return &Buffer{bs: make([]byte, 0, _size)}
+ },
+ }}
+}
+
+// Get retrieves a Buffer from the pool, creating one if necessary.
+func (p Pool) Get() *Buffer {
+ buf := p.p.Get().(*Buffer)
+ buf.Reset()
+ buf.pool = p
+ return buf
+}
+
+func (p Pool) put(buf *Buffer) {
+ p.p.Put(buf)
+}
diff --git a/vendor/go.uber.org/zap/buffer/pool_test.go b/vendor/go.uber.org/zap/buffer/pool_test.go
new file mode 100644
index 0000000..a219815
--- /dev/null
+++ b/vendor/go.uber.org/zap/buffer/pool_test.go
@@ -0,0 +1,52 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package buffer
+
+import (
+ "sync"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestBuffers(t *testing.T) {
+ const dummyData = "dummy data"
+ p := NewPool()
+
+ var wg sync.WaitGroup
+ for g := 0; g < 10; g++ {
+ wg.Add(1)
+ go func() {
+ for i := 0; i < 100; i++ {
+ buf := p.Get()
+ assert.Zero(t, buf.Len(), "Expected truncated buffer")
+ assert.NotZero(t, buf.Cap(), "Expected non-zero capacity")
+
+ buf.AppendString(dummyData)
+ assert.Equal(t, buf.Len(), len(dummyData), "Expected buffer to contain dummy data")
+
+ buf.Free()
+ }
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+}
diff --git a/vendor/go.uber.org/zap/check_license.sh b/vendor/go.uber.org/zap/check_license.sh
new file mode 100755
index 0000000..345ac8b
--- /dev/null
+++ b/vendor/go.uber.org/zap/check_license.sh
@@ -0,0 +1,17 @@
+#!/bin/bash -e
+
+ERROR_COUNT=0
+while read -r file
+do
+ case "$(head -1 "${file}")" in
+ *"Copyright (c) "*" Uber Technologies, Inc.")
+ # everything's cool
+ ;;
+ *)
+ echo "$file is missing license header."
+ (( ERROR_COUNT++ ))
+ ;;
+ esac
+done < <(git ls-files "*\.go")
+
+exit $ERROR_COUNT
diff --git a/vendor/go.uber.org/zap/common_test.go b/vendor/go.uber.org/zap/common_test.go
new file mode 100644
index 0000000..b0a4a2e
--- /dev/null
+++ b/vendor/go.uber.org/zap/common_test.go
@@ -0,0 +1,57 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "sync"
+ "testing"
+
+ "go.uber.org/zap/zapcore"
+ "go.uber.org/zap/zaptest/observer"
+)
+
+func opts(opts ...Option) []Option {
+ return opts
+}
+
+// Here specifically to introduce an easily-identifiable filename for testing
+// stacktraces and caller skips.
+func withLogger(t testing.TB, e zapcore.LevelEnabler, opts []Option, f func(*Logger, *observer.ObservedLogs)) {
+ fac, logs := observer.New(e)
+ log := New(fac, opts...)
+ f(log, logs)
+}
+
+func withSugar(t testing.TB, e zapcore.LevelEnabler, opts []Option, f func(*SugaredLogger, *observer.ObservedLogs)) {
+ withLogger(t, e, opts, func(logger *Logger, logs *observer.ObservedLogs) { f(logger.Sugar(), logs) })
+}
+
+func runConcurrently(goroutines, iterations int, wg *sync.WaitGroup, f func()) {
+ wg.Add(goroutines)
+ for g := 0; g < goroutines; g++ {
+ go func() {
+ defer wg.Done()
+ for i := 0; i < iterations; i++ {
+ f()
+ }
+ }()
+ }
+}
diff --git a/vendor/go.uber.org/zap/config.go b/vendor/go.uber.org/zap/config.go
new file mode 100644
index 0000000..b0658ed
--- /dev/null
+++ b/vendor/go.uber.org/zap/config.go
@@ -0,0 +1,243 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "sort"
+ "time"
+
+ "go.uber.org/zap/zapcore"
+)
+
+// SamplingConfig sets a sampling strategy for the logger. Sampling caps the
+// global CPU and I/O load that logging puts on your process while attempting
+// to preserve a representative subset of your logs.
+//
+// Values configured here are per-second. See zapcore.NewSampler for details.
+type SamplingConfig struct {
+ Initial int `json:"initial" yaml:"initial"`
+ Thereafter int `json:"thereafter" yaml:"thereafter"`
+}
+
+// Config offers a declarative way to construct a logger. It doesn't do
+// anything that can't be done with New, Options, and the various
+// zapcore.WriteSyncer and zapcore.Core wrappers, but it's a simpler way to
+// toggle common options.
+//
+// Note that Config intentionally supports only the most common options. More
+// unusual logging setups (logging to network connections or message queues,
+// splitting output between multiple files, etc.) are possible, but require
+// direct use of the zapcore package. For sample code, see the package-level
+// BasicConfiguration and AdvancedConfiguration examples.
+//
+// For an example showing runtime log level changes, see the documentation for
+// AtomicLevel.
+type Config struct {
+ // Level is the minimum enabled logging level. Note that this is a dynamic
+ // level, so calling Config.Level.SetLevel will atomically change the log
+ // level of all loggers descended from this config.
+ Level AtomicLevel `json:"level" yaml:"level"`
+ // Development puts the logger in development mode, which changes the
+ // behavior of DPanicLevel and takes stacktraces more liberally.
+ Development bool `json:"development" yaml:"development"`
+ // DisableCaller stops annotating logs with the calling function's file
+ // name and line number. By default, all logs are annotated.
+ DisableCaller bool `json:"disableCaller" yaml:"disableCaller"`
+ // DisableStacktrace completely disables automatic stacktrace capturing. By
+ // default, stacktraces are captured for WarnLevel and above logs in
+ // development and ErrorLevel and above in production.
+ DisableStacktrace bool `json:"disableStacktrace" yaml:"disableStacktrace"`
+ // Sampling sets a sampling policy. A nil SamplingConfig disables sampling.
+ Sampling *SamplingConfig `json:"sampling" yaml:"sampling"`
+ // Encoding sets the logger's encoding. Valid values are "json" and
+ // "console", as well as any third-party encodings registered via
+ // RegisterEncoder.
+ Encoding string `json:"encoding" yaml:"encoding"`
+ // EncoderConfig sets options for the chosen encoder. See
+ // zapcore.EncoderConfig for details.
+ EncoderConfig zapcore.EncoderConfig `json:"encoderConfig" yaml:"encoderConfig"`
+ // OutputPaths is a list of paths to write logging output to. See Open for
+ // details.
+ OutputPaths []string `json:"outputPaths" yaml:"outputPaths"`
+ // ErrorOutputPaths is a list of paths to write internal logger errors to.
+ // The default is standard error.
+ //
+ // Note that this setting only affects internal errors; for sample code that
+ // sends error-level logs to a different location from info- and debug-level
+ // logs, see the package-level AdvancedConfiguration example.
+ ErrorOutputPaths []string `json:"errorOutputPaths" yaml:"errorOutputPaths"`
+ // InitialFields is a collection of fields to add to the root logger.
+ InitialFields map[string]interface{} `json:"initialFields" yaml:"initialFields"`
+}
+
+// NewProductionEncoderConfig returns an opinionated EncoderConfig for
+// production environments.
+func NewProductionEncoderConfig() zapcore.EncoderConfig {
+ return zapcore.EncoderConfig{
+ TimeKey: "ts",
+ LevelKey: "level",
+ NameKey: "logger",
+ CallerKey: "caller",
+ MessageKey: "msg",
+ StacktraceKey: "stacktrace",
+ LineEnding: zapcore.DefaultLineEnding,
+ EncodeLevel: zapcore.LowercaseLevelEncoder,
+ EncodeTime: zapcore.EpochTimeEncoder,
+ EncodeDuration: zapcore.SecondsDurationEncoder,
+ EncodeCaller: zapcore.ShortCallerEncoder,
+ }
+}
+
+// NewProductionConfig is a reasonable production logging configuration.
+// Logging is enabled at InfoLevel and above.
+//
+// It uses a JSON encoder, writes to standard error, and enables sampling.
+// Stacktraces are automatically included on logs of ErrorLevel and above.
+func NewProductionConfig() Config {
+ return Config{
+ Level: NewAtomicLevelAt(InfoLevel),
+ Development: false,
+ Sampling: &SamplingConfig{
+ Initial: 100,
+ Thereafter: 100,
+ },
+ Encoding: "json",
+ EncoderConfig: NewProductionEncoderConfig(),
+ OutputPaths: []string{"stderr"},
+ ErrorOutputPaths: []string{"stderr"},
+ }
+}
+
+// NewDevelopmentEncoderConfig returns an opinionated EncoderConfig for
+// development environments.
+func NewDevelopmentEncoderConfig() zapcore.EncoderConfig {
+ return zapcore.EncoderConfig{
+ // Keys can be anything except the empty string.
+ TimeKey: "T",
+ LevelKey: "L",
+ NameKey: "N",
+ CallerKey: "C",
+ MessageKey: "M",
+ StacktraceKey: "S",
+ LineEnding: zapcore.DefaultLineEnding,
+ EncodeLevel: zapcore.CapitalLevelEncoder,
+ EncodeTime: zapcore.ISO8601TimeEncoder,
+ EncodeDuration: zapcore.StringDurationEncoder,
+ EncodeCaller: zapcore.ShortCallerEncoder,
+ }
+}
+
+// NewDevelopmentConfig is a reasonable development logging configuration.
+// Logging is enabled at DebugLevel and above.
+//
+// It enables development mode (which makes DPanicLevel logs panic), uses a
+// console encoder, writes to standard error, and disables sampling.
+// Stacktraces are automatically included on logs of WarnLevel and above.
+func NewDevelopmentConfig() Config {
+ return Config{
+ Level: NewAtomicLevelAt(DebugLevel),
+ Development: true,
+ Encoding: "console",
+ EncoderConfig: NewDevelopmentEncoderConfig(),
+ OutputPaths: []string{"stderr"},
+ ErrorOutputPaths: []string{"stderr"},
+ }
+}
+
+// Build constructs a logger from the Config and Options.
+func (cfg Config) Build(opts ...Option) (*Logger, error) {
+ enc, err := cfg.buildEncoder()
+ if err != nil {
+ return nil, err
+ }
+
+ sink, errSink, err := cfg.openSinks()
+ if err != nil {
+ return nil, err
+ }
+
+ log := New(
+ zapcore.NewCore(enc, sink, cfg.Level),
+ cfg.buildOptions(errSink)...,
+ )
+ if len(opts) > 0 {
+ log = log.WithOptions(opts...)
+ }
+ return log, nil
+}
+
+func (cfg Config) buildOptions(errSink zapcore.WriteSyncer) []Option {
+ opts := []Option{ErrorOutput(errSink)}
+
+ if cfg.Development {
+ opts = append(opts, Development())
+ }
+
+ if !cfg.DisableCaller {
+ opts = append(opts, AddCaller())
+ }
+
+ stackLevel := ErrorLevel
+ if cfg.Development {
+ stackLevel = WarnLevel
+ }
+ if !cfg.DisableStacktrace {
+ opts = append(opts, AddStacktrace(stackLevel))
+ }
+
+ if cfg.Sampling != nil {
+ opts = append(opts, WrapCore(func(core zapcore.Core) zapcore.Core {
+ return zapcore.NewSampler(core, time.Second, int(cfg.Sampling.Initial), int(cfg.Sampling.Thereafter))
+ }))
+ }
+
+ if len(cfg.InitialFields) > 0 {
+ fs := make([]zapcore.Field, 0, len(cfg.InitialFields))
+ keys := make([]string, 0, len(cfg.InitialFields))
+ for k := range cfg.InitialFields {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+ for _, k := range keys {
+ fs = append(fs, Any(k, cfg.InitialFields[k]))
+ }
+ opts = append(opts, Fields(fs...))
+ }
+
+ return opts
+}
+
+func (cfg Config) openSinks() (zapcore.WriteSyncer, zapcore.WriteSyncer, error) {
+ sink, closeOut, err := Open(cfg.OutputPaths...)
+ if err != nil {
+ return nil, nil, err
+ }
+ errSink, _, err := Open(cfg.ErrorOutputPaths...)
+ if err != nil {
+ closeOut()
+ return nil, nil, err
+ }
+ return sink, errSink, nil
+}
+
+func (cfg Config) buildEncoder() (zapcore.Encoder, error) {
+ return newEncoder(cfg.Encoding, cfg.EncoderConfig)
+}
diff --git a/vendor/go.uber.org/zap/config_test.go b/vendor/go.uber.org/zap/config_test.go
new file mode 100644
index 0000000..65cabdf
--- /dev/null
+++ b/vendor/go.uber.org/zap/config_test.go
@@ -0,0 +1,108 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "io/ioutil"
+ "os"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestConfig(t *testing.T) {
+ tests := []struct {
+ desc string
+ cfg Config
+ expectN int64
+ expectRe string
+ }{
+ {
+ desc: "production",
+ cfg: NewProductionConfig(),
+ expectN: 2 + 100 + 1, // 2 from initial logs, 100 initial sampled logs, 1 from off-by-one in sampler
+ expectRe: `{"level":"info","caller":"zap/config_test.go:\d+","msg":"info","k":"v","z":"zz"}` + "\n" +
+ `{"level":"warn","caller":"zap/config_test.go:\d+","msg":"warn","k":"v","z":"zz"}` + "\n",
+ },
+ {
+ desc: "development",
+ cfg: NewDevelopmentConfig(),
+ expectN: 3 + 200, // 3 initial logs, all 200 subsequent logs
+ expectRe: "DEBUG\tzap/config_test.go:" + `\d+` + "\tdebug\t" + `{"k": "v", "z": "zz"}` + "\n" +
+ "INFO\tzap/config_test.go:" + `\d+` + "\tinfo\t" + `{"k": "v", "z": "zz"}` + "\n" +
+ "WARN\tzap/config_test.go:" + `\d+` + "\twarn\t" + `{"k": "v", "z": "zz"}` + "\n" +
+ `go.uber.org/zap.Stack`,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.desc, func(t *testing.T) {
+ temp, err := ioutil.TempFile("", "zap-prod-config-test")
+ require.NoError(t, err, "Failed to create temp file.")
+ defer os.Remove(temp.Name())
+
+ tt.cfg.OutputPaths = []string{temp.Name()}
+ tt.cfg.EncoderConfig.TimeKey = "" // no timestamps in tests
+ tt.cfg.InitialFields = map[string]interface{}{"z": "zz", "k": "v"}
+
+ hook, count := makeCountingHook()
+ logger, err := tt.cfg.Build(Hooks(hook))
+ require.NoError(t, err, "Unexpected error constructing logger.")
+
+ logger.Debug("debug")
+ logger.Info("info")
+ logger.Warn("warn")
+
+ byteContents, err := ioutil.ReadAll(temp)
+ require.NoError(t, err, "Couldn't read log contents from temp file.")
+ logs := string(byteContents)
+ assert.Regexp(t, tt.expectRe, logs, "Unexpected log output.")
+
+ for i := 0; i < 200; i++ {
+ logger.Info("sampling")
+ }
+ assert.Equal(t, tt.expectN, count.Load(), "Hook called an unexpected number of times.")
+ })
+ }
+}
+
+func TestConfigWithInvalidPaths(t *testing.T) {
+ tests := []struct {
+ desc string
+ output string
+ errOutput string
+ }{
+ {"output directory doesn't exist", "/tmp/not-there/foo.log", "stderr"},
+ {"error output directory doesn't exist", "stdout", "/tmp/not-there/foo-errors.log"},
+ {"neither output directory exists", "/tmp/not-there/foo.log", "/tmp/not-there/foo-errors.log"},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.desc, func(t *testing.T) {
+ cfg := NewProductionConfig()
+ cfg.OutputPaths = []string{tt.output}
+ cfg.ErrorOutputPaths = []string{tt.errOutput}
+ _, err := cfg.Build()
+ assert.Error(t, err, "Expected an error opening a non-existent directory.")
+ })
+ }
+}
diff --git a/vendor/go.uber.org/zap/doc.go b/vendor/go.uber.org/zap/doc.go
new file mode 100644
index 0000000..3f16a8d
--- /dev/null
+++ b/vendor/go.uber.org/zap/doc.go
@@ -0,0 +1,113 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+// Package zap provides fast, structured, leveled logging.
+//
+// For applications that log in the hot path, reflection-based serialization
+// and string formatting are prohibitively expensive - they're CPU-intensive
+// and make many small allocations. Put differently, using json.Marshal and
+// fmt.Fprintf to log tons of interface{} makes your application slow.
+//
+// Zap takes a different approach. It includes a reflection-free,
+// zero-allocation JSON encoder, and the base Logger strives to avoid
+// serialization overhead and allocations wherever possible. By building the
+// high-level SugaredLogger on that foundation, zap lets users choose when
+// they need to count every allocation and when they'd prefer a more familiar,
+// loosely typed API.
+//
+// Choosing a Logger
+//
+// In contexts where performance is nice, but not critical, use the
+// SugaredLogger. It's 4-10x faster than other structured logging packages and
+// supports both structured and printf-style logging. Like log15 and go-kit,
+// the SugaredLogger's structured logging APIs are loosely typed and accept a
+// variadic number of key-value pairs. (For more advanced use cases, they also
+// accept strongly typed fields - see the SugaredLogger.With documentation for
+// details.)
+// sugar := zap.NewExample().Sugar()
+// defer sugar.Sync()
+// sugar.Infow("failed to fetch URL",
+// "url", "http://example.com",
+// "attempt", 3,
+// "backoff", time.Second,
+// )
+// sugar.Printf("failed to fetch URL: %s", "http://example.com")
+//
+// By default, loggers are unbuffered. However, since zap's low-level APIs
+// allow buffering, calling Sync before letting your process exit is a good
+// habit.
+//
+// In the rare contexts where every microsecond and every allocation matter,
+// use the Logger. It's even faster than the SugaredLogger and allocates far
+// less, but it only supports strongly-typed, structured logging.
+// logger := zap.NewExample()
+// defer logger.Sync()
+// logger.Info("failed to fetch URL",
+// zap.String("url", "http://example.com"),
+// zap.Int("attempt", 3),
+// zap.Duration("backoff", time.Second),
+// )
+//
+// Choosing between the Logger and SugaredLogger doesn't need to be an
+// application-wide decision: converting between the two is simple and
+// inexpensive.
+// logger := zap.NewExample()
+// defer logger.Sync()
+// sugar := logger.Sugar()
+// plain := sugar.Desugar()
+//
+// Configuring Zap
+//
+// The simplest way to build a Logger is to use zap's opinionated presets:
+// NewExample, NewProduction, and NewDevelopment. These presets build a logger
+// with a single function call:
+// logger, err := zap.NewProduction()
+// if err != nil {
+// log.Fatalf("can't initialize zap logger: %v", err)
+// }
+// defer logger.Sync()
+//
+// Presets are fine for small projects, but larger projects and organizations
+// naturally require a bit more customization. For most users, zap's Config
+// struct strikes the right balance between flexibility and convenience. See
+// the package-level BasicConfiguration example for sample code.
+//
+// More unusual configurations (splitting output between files, sending logs
+// to a message queue, etc.) are possible, but require direct use of
+// go.uber.org/zap/zapcore. See the package-level AdvancedConfiguration
+// example for sample code.
+//
+// Extending Zap
+//
+// The zap package itself is a relatively thin wrapper around the interfaces
+// in go.uber.org/zap/zapcore. Extending zap to support a new encoding (e.g.,
+// BSON), a new log sink (e.g., Kafka), or something more exotic (perhaps an
+// exception aggregation service, like Sentry or Rollbar) typically requires
+// implementing the zapcore.Encoder, zapcore.WriteSyncer, or zapcore.Core
+// interfaces. See the zapcore documentation for details.
+//
+// Similarly, package authors can use the high-performance Encoder and Core
+// implementations in the zapcore package to build their own loggers.
+//
+// Frequently Asked Questions
+//
+// An FAQ covering everything from installation errors to design decisions is
+// available at https://github.com/uber-go/zap/blob/master/FAQ.md.
+package zap // import "go.uber.org/zap"
diff --git a/vendor/go.uber.org/zap/encoder.go b/vendor/go.uber.org/zap/encoder.go
new file mode 100644
index 0000000..2e9d3c3
--- /dev/null
+++ b/vendor/go.uber.org/zap/encoder.go
@@ -0,0 +1,75 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "errors"
+ "fmt"
+ "sync"
+
+ "go.uber.org/zap/zapcore"
+)
+
+var (
+ errNoEncoderNameSpecified = errors.New("no encoder name specified")
+
+ _encoderNameToConstructor = map[string]func(zapcore.EncoderConfig) (zapcore.Encoder, error){
+ "console": func(encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) {
+ return zapcore.NewConsoleEncoder(encoderConfig), nil
+ },
+ "json": func(encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) {
+ return zapcore.NewJSONEncoder(encoderConfig), nil
+ },
+ }
+ _encoderMutex sync.RWMutex
+)
+
+// RegisterEncoder registers an encoder constructor, which the Config struct
+// can then reference. By default, the "json" and "console" encoders are
+// registered.
+//
+// Attempting to register an encoder whose name is already taken returns an
+// error.
+func RegisterEncoder(name string, constructor func(zapcore.EncoderConfig) (zapcore.Encoder, error)) error {
+ _encoderMutex.Lock()
+ defer _encoderMutex.Unlock()
+ if name == "" {
+ return errNoEncoderNameSpecified
+ }
+ if _, ok := _encoderNameToConstructor[name]; ok {
+ return fmt.Errorf("encoder already registered for name %q", name)
+ }
+ _encoderNameToConstructor[name] = constructor
+ return nil
+}
+
+func newEncoder(name string, encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) {
+ _encoderMutex.RLock()
+ defer _encoderMutex.RUnlock()
+ if name == "" {
+ return nil, errNoEncoderNameSpecified
+ }
+ constructor, ok := _encoderNameToConstructor[name]
+ if !ok {
+ return nil, fmt.Errorf("no encoder registered for name %q", name)
+ }
+ return constructor(encoderConfig)
+}
diff --git a/vendor/go.uber.org/zap/encoder_test.go b/vendor/go.uber.org/zap/encoder_test.go
new file mode 100644
index 0000000..f6be665
--- /dev/null
+++ b/vendor/go.uber.org/zap/encoder_test.go
@@ -0,0 +1,88 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "testing"
+
+ "go.uber.org/zap/zapcore"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestRegisterDefaultEncoders(t *testing.T) {
+ testEncodersRegistered(t, "console", "json")
+}
+
+func TestRegisterEncoder(t *testing.T) {
+ testEncoders(func() {
+ assert.NoError(t, RegisterEncoder("foo", newNilEncoder), "expected to be able to register the encoder foo")
+ testEncodersRegistered(t, "foo")
+ })
+}
+
+func TestDuplicateRegisterEncoder(t *testing.T) {
+ testEncoders(func() {
+ RegisterEncoder("foo", newNilEncoder)
+ assert.Error(t, RegisterEncoder("foo", newNilEncoder), "expected an error when registering an encoder with the same name twice")
+ })
+}
+
+func TestRegisterEncoderNoName(t *testing.T) {
+ assert.Equal(t, errNoEncoderNameSpecified, RegisterEncoder("", newNilEncoder), "expected an error when registering an encoder with no name")
+}
+
+func TestNewEncoder(t *testing.T) {
+ testEncoders(func() {
+ RegisterEncoder("foo", newNilEncoder)
+ encoder, err := newEncoder("foo", zapcore.EncoderConfig{})
+ assert.NoError(t, err, "could not create an encoder for the registered name foo")
+ assert.Nil(t, encoder, "the encoder from newNilEncoder is not nil")
+ })
+}
+
+func TestNewEncoderNotRegistered(t *testing.T) {
+ _, err := newEncoder("foo", zapcore.EncoderConfig{})
+ assert.Error(t, err, "expected an error when trying to create an encoder of an unregistered name")
+}
+
+func TestNewEncoderNoName(t *testing.T) {
+ _, err := newEncoder("", zapcore.EncoderConfig{})
+ assert.Equal(t, errNoEncoderNameSpecified, err, "expected an error when creating an encoder with no name")
+}
+
+func testEncoders(f func()) {
+ existing := _encoderNameToConstructor
+ _encoderNameToConstructor = make(map[string]func(zapcore.EncoderConfig) (zapcore.Encoder, error))
+ defer func() { _encoderNameToConstructor = existing }()
+ f()
+}
+
+func testEncodersRegistered(t *testing.T, names ...string) {
+ assert.Len(t, _encoderNameToConstructor, len(names), "the expected number of registered encoders does not match the actual number")
+ for _, name := range names {
+ assert.NotNil(t, _encoderNameToConstructor[name], "no encoder is registered for name %s", name)
+ }
+}
+
+func newNilEncoder(_ zapcore.EncoderConfig) (zapcore.Encoder, error) {
+ return nil, nil
+}
diff --git a/vendor/go.uber.org/zap/error.go b/vendor/go.uber.org/zap/error.go
new file mode 100644
index 0000000..2bff30d
--- /dev/null
+++ b/vendor/go.uber.org/zap/error.go
@@ -0,0 +1,80 @@
+// Copyright (c) 2017 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "sync"
+
+ "go.uber.org/zap/zapcore"
+)
+
+var _errArrayElemPool = sync.Pool{New: func() interface{} {
+ return &errArrayElem{}
+}}
+
+// Error is shorthand for the common idiom NamedError("error", err).
+func Error(err error) zapcore.Field {
+ return NamedError("error", err)
+}
+
+// NamedError constructs a field that lazily stores err.Error() under the
+// provided key. Errors which also implement fmt.Formatter (like those produced
+// by github.com/pkg/errors) will also have their verbose representation stored
+// under key+"Verbose". If passed a nil error, the field is a no-op.
+//
+// For the common case in which the key is simply "error", the Error function
+// is shorter and less repetitive.
+func NamedError(key string, err error) zapcore.Field {
+ if err == nil {
+ return Skip()
+ }
+ return zapcore.Field{Key: key, Type: zapcore.ErrorType, Interface: err}
+}
+
+type errArray []error
+
+func (errs errArray) MarshalLogArray(arr zapcore.ArrayEncoder) error {
+ for i := range errs {
+ if errs[i] == nil {
+ continue
+ }
+ // To represent each error as an object with an "error" attribute and
+ // potentially an "errorVerbose" attribute, we need to wrap it in a
+ // type that implements LogObjectMarshaler. To prevent this from
+ // allocating, pool the wrapper type.
+ elem := _errArrayElemPool.Get().(*errArrayElem)
+ elem.error = errs[i]
+ arr.AppendObject(elem)
+ elem.error = nil
+ _errArrayElemPool.Put(elem)
+ }
+ return nil
+}
+
+type errArrayElem struct {
+ error
+}
+
+func (e *errArrayElem) MarshalLogObject(enc zapcore.ObjectEncoder) error {
+ // Re-use the error field's logic, which supports non-standard error types.
+ Error(e.error).AddTo(enc)
+ return nil
+}
diff --git a/vendor/go.uber.org/zap/error_test.go b/vendor/go.uber.org/zap/error_test.go
new file mode 100644
index 0000000..54ce4bb
--- /dev/null
+++ b/vendor/go.uber.org/zap/error_test.go
@@ -0,0 +1,99 @@
+// Copyright (c) 2017 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "errors"
+ "testing"
+
+ "go.uber.org/zap/zapcore"
+
+ richErrors "github.com/pkg/errors"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestErrorConstructors(t *testing.T) {
+ fail := errors.New("fail")
+
+ tests := []struct {
+ name string
+ field zapcore.Field
+ expect zapcore.Field
+ }{
+ {"Error", Skip(), Error(nil)},
+ {"Error", zapcore.Field{Key: "error", Type: zapcore.ErrorType, Interface: fail}, Error(fail)},
+ {"NamedError", Skip(), NamedError("foo", nil)},
+ {"NamedError", zapcore.Field{Key: "foo", Type: zapcore.ErrorType, Interface: fail}, NamedError("foo", fail)},
+ {"Any:Error", Any("k", errors.New("v")), NamedError("k", errors.New("v"))},
+ {"Any:Errors", Any("k", []error{errors.New("v")}), Errors("k", []error{errors.New("v")})},
+ }
+
+ for _, tt := range tests {
+ if !assert.Equal(t, tt.expect, tt.field, "Unexpected output from convenience field constructor %s.", tt.name) {
+ t.Logf("type expected: %T\nGot: %T", tt.expect.Interface, tt.field.Interface)
+ }
+ assertCanBeReused(t, tt.field)
+ }
+}
+
+func TestErrorArrayConstructor(t *testing.T) {
+ tests := []struct {
+ desc string
+ field zapcore.Field
+ expected []interface{}
+ }{
+ {"empty errors", Errors("", []error{}), []interface{}(nil)},
+ {
+ "errors",
+ Errors("", []error{nil, errors.New("foo"), nil, errors.New("bar")}),
+ []interface{}{map[string]interface{}{"error": "foo"}, map[string]interface{}{"error": "bar"}},
+ },
+ }
+
+ for _, tt := range tests {
+ enc := zapcore.NewMapObjectEncoder()
+ tt.field.Key = "k"
+ tt.field.AddTo(enc)
+ assert.Equal(t, tt.expected, enc.Fields["k"], "%s: unexpected map contents.", tt.desc)
+ assert.Equal(t, 1, len(enc.Fields), "%s: found extra keys in map: %v", tt.desc, enc.Fields)
+ }
+}
+
+func TestErrorsArraysHandleRichErrors(t *testing.T) {
+ errs := []error{richErrors.New("egad")}
+
+ enc := zapcore.NewMapObjectEncoder()
+ Errors("k", errs).AddTo(enc)
+ assert.Equal(t, 1, len(enc.Fields), "Expected only top-level field.")
+
+ val := enc.Fields["k"]
+ arr, ok := val.([]interface{})
+ require.True(t, ok, "Expected top-level field to be an array.")
+ require.Equal(t, 1, len(arr), "Expected only one error object in array.")
+
+ serialized := arr[0]
+ errMap, ok := serialized.(map[string]interface{})
+ require.True(t, ok, "Expected serialized error to be a map, got %T.", serialized)
+ assert.Equal(t, "egad", errMap["error"], "Unexpected standard error string.")
+ assert.Contains(t, errMap["errorVerbose"], "egad", "Verbose error string should be a superset of standard error.")
+ assert.Contains(t, errMap["errorVerbose"], "TestErrorsArraysHandleRichErrors", "Verbose error string should contain a stacktrace.")
+}
diff --git a/vendor/go.uber.org/zap/example_test.go b/vendor/go.uber.org/zap/example_test.go
new file mode 100644
index 0000000..b61f153
--- /dev/null
+++ b/vendor/go.uber.org/zap/example_test.go
@@ -0,0 +1,327 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap_test
+
+import (
+ "encoding/json"
+ "io/ioutil"
+ "log"
+ "os"
+ "time"
+
+ "go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
+)
+
+func Example_presets() {
+ // Using zap's preset constructors is the simplest way to get a feel for the
+ // package, but they don't allow much customization.
+ logger := zap.NewExample() // or NewProduction, or NewDevelopment
+ defer logger.Sync()
+
+ const url = "http://example.com"
+
+ // In most circumstances, use the SugaredLogger. It's 4-10x faster than most
+ // other structured logging packages and has a familiar, loosely-typed API.
+ sugar := logger.Sugar()
+ sugar.Infow("Failed to fetch URL.",
+ // Structured context as loosely typed key-value pairs.
+ "url", url,
+ "attempt", 3,
+ "backoff", time.Second,
+ )
+ sugar.Infof("Failed to fetch URL: %s", url)
+
+ // In the unusual situations where every microsecond matters, use the
+ // Logger. It's even faster than the SugaredLogger, but only supports
+ // structured logging.
+ logger.Info("Failed to fetch URL.",
+ // Structured context as strongly typed fields.
+ zap.String("url", url),
+ zap.Int("attempt", 3),
+ zap.Duration("backoff", time.Second),
+ )
+ // Output:
+ // {"level":"info","msg":"Failed to fetch URL.","url":"http://example.com","attempt":3,"backoff":"1s"}
+ // {"level":"info","msg":"Failed to fetch URL: http://example.com"}
+ // {"level":"info","msg":"Failed to fetch URL.","url":"http://example.com","attempt":3,"backoff":"1s"}
+}
+
+func Example_basicConfiguration() {
+ // For some users, the presets offered by the NewProduction, NewDevelopment,
+ // and NewExample constructors won't be appropriate. For most of those
+ // users, the bundled Config struct offers the right balance of flexibility
+ // and convenience. (For more complex needs, see the AdvancedConfiguration
+ // example.)
+ //
+ // See the documentation for Config and zapcore.EncoderConfig for all the
+ // available options.
+ rawJSON := []byte(`{
+ "level": "debug",
+ "encoding": "json",
+ "outputPaths": ["stdout", "/tmp/logs"],
+ "errorOutputPaths": ["stderr"],
+ "initialFields": {"foo": "bar"},
+ "encoderConfig": {
+ "messageKey": "message",
+ "levelKey": "level",
+ "levelEncoder": "lowercase"
+ }
+ }`)
+
+ var cfg zap.Config
+ if err := json.Unmarshal(rawJSON, &cfg); err != nil {
+ panic(err)
+ }
+ logger, err := cfg.Build()
+ if err != nil {
+ panic(err)
+ }
+ defer logger.Sync()
+
+ logger.Info("logger construction succeeded")
+ // Output:
+ // {"level":"info","message":"logger construction succeeded","foo":"bar"}
+}
+
+func Example_advancedConfiguration() {
+ // The bundled Config struct only supports the most common configuration
+ // options. More complex needs, like splitting logs between multiple files
+ // or writing to non-file outputs, require use of the zapcore package.
+ //
+ // In this example, imagine we're both sending our logs to Kafka and writing
+ // them to the console. We'd like to encode the console output and the Kafka
+ // topics differently, and we'd also like special treatment for
+ // high-priority logs.
+
+ // First, define our level-handling logic.
+ highPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
+ return lvl >= zapcore.ErrorLevel
+ })
+ lowPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
+ return lvl < zapcore.ErrorLevel
+ })
+
+ // Assume that we have clients for two Kafka topics. The clients implement
+ // zapcore.WriteSyncer and are safe for concurrent use. (If they only
+ // implement io.Writer, we can use zapcore.AddSync to add a no-op Sync
+ // method. If they're not safe for concurrent use, we can add a protecting
+ // mutex with zapcore.Lock.)
+ topicDebugging := zapcore.AddSync(ioutil.Discard)
+ topicErrors := zapcore.AddSync(ioutil.Discard)
+
+ // High-priority output should also go to standard error, and low-priority
+ // output should also go to standard out.
+ consoleDebugging := zapcore.Lock(os.Stdout)
+ consoleErrors := zapcore.Lock(os.Stderr)
+
+ // Optimize the Kafka output for machine consumption and the console output
+ // for human operators.
+ kafkaEncoder := zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
+ consoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())
+
+ // Join the outputs, encoders, and level-handling functions into
+ // zapcore.Cores, then tee the four cores together.
+ core := zapcore.NewTee(
+ zapcore.NewCore(kafkaEncoder, topicErrors, highPriority),
+ zapcore.NewCore(consoleEncoder, consoleErrors, highPriority),
+ zapcore.NewCore(kafkaEncoder, topicDebugging, lowPriority),
+ zapcore.NewCore(consoleEncoder, consoleDebugging, lowPriority),
+ )
+
+ // From a zapcore.Core, it's easy to construct a Logger.
+ logger := zap.New(core)
+ defer logger.Sync()
+ logger.Info("constructed a logger")
+}
+
+func ExampleNamespace() {
+ logger := zap.NewExample()
+ defer logger.Sync()
+
+ logger.With(
+ zap.Namespace("metrics"),
+ zap.Int("counter", 1),
+ ).Info("tracked some metrics")
+ // Output:
+ // {"level":"info","msg":"tracked some metrics","metrics":{"counter":1}}
+}
+
+func ExampleNewStdLog() {
+ logger := zap.NewExample()
+ defer logger.Sync()
+
+ std := zap.NewStdLog(logger)
+ std.Print("standard logger wrapper")
+ // Output:
+ // {"level":"info","msg":"standard logger wrapper"}
+}
+
+func ExampleRedirectStdLog() {
+ logger := zap.NewExample()
+ defer logger.Sync()
+
+ undo := zap.RedirectStdLog(logger)
+ defer undo()
+
+ log.Print("redirected standard library")
+ // Output:
+ // {"level":"info","msg":"redirected standard library"}
+}
+
+func ExampleReplaceGlobals() {
+ logger := zap.NewExample()
+ defer logger.Sync()
+
+ undo := zap.ReplaceGlobals(logger)
+ defer undo()
+
+ zap.L().Info("replaced zap's global loggers")
+ // Output:
+ // {"level":"info","msg":"replaced zap's global loggers"}
+}
+
+func ExampleAtomicLevel() {
+ atom := zap.NewAtomicLevel()
+
+ // To keep the example deterministic, disable timestamps in the output.
+ encoderCfg := zap.NewProductionEncoderConfig()
+ encoderCfg.TimeKey = ""
+
+ logger := zap.New(zapcore.NewCore(
+ zapcore.NewJSONEncoder(encoderCfg),
+ zapcore.Lock(os.Stdout),
+ atom,
+ ))
+ defer logger.Sync()
+
+ logger.Info("info logging enabled")
+
+ atom.SetLevel(zap.ErrorLevel)
+ logger.Info("info logging disabled")
+ // Output:
+ // {"level":"info","msg":"info logging enabled"}
+}
+
+func ExampleAtomicLevel_config() {
+ // The zap.Config struct includes an AtomicLevel. To use it, keep a
+ // reference to the Config.
+ rawJSON := []byte(`{
+ "level": "info",
+ "outputPaths": ["stdout"],
+ "errorOutputPaths": ["stderr"],
+ "encoding": "json",
+ "encoderConfig": {
+ "messageKey": "message",
+ "levelKey": "level",
+ "levelEncoder": "lowercase"
+ }
+ }`)
+ var cfg zap.Config
+ if err := json.Unmarshal(rawJSON, &cfg); err != nil {
+ panic(err)
+ }
+ logger, err := cfg.Build()
+ if err != nil {
+ panic(err)
+ }
+ defer logger.Sync()
+
+ logger.Info("info logging enabled")
+
+ cfg.Level.SetLevel(zap.ErrorLevel)
+ logger.Info("info logging disabled")
+ // Output:
+ // {"level":"info","message":"info logging enabled"}
+}
+
+func ExampleLogger_Check() {
+ logger := zap.NewExample()
+ defer logger.Sync()
+
+ if ce := logger.Check(zap.DebugLevel, "debugging"); ce != nil {
+ // If debug-level log output isn't enabled or if zap's sampling would have
+ // dropped this log entry, we don't allocate the slice that holds these
+ // fields.
+ ce.Write(
+ zap.String("foo", "bar"),
+ zap.String("baz", "quux"),
+ )
+ }
+
+ // Output:
+ // {"level":"debug","msg":"debugging","foo":"bar","baz":"quux"}
+}
+
+func ExampleLogger_Named() {
+ logger := zap.NewExample()
+ defer logger.Sync()
+
+ // By default, Loggers are unnamed.
+ logger.Info("no name")
+
+ // The first call to Named sets the Logger name.
+ main := logger.Named("main")
+ main.Info("main logger")
+
+ // Additional calls to Named create a period-separated path.
+ main.Named("subpackage").Info("sub-logger")
+ // Output:
+ // {"level":"info","msg":"no name"}
+ // {"level":"info","logger":"main","msg":"main logger"}
+ // {"level":"info","logger":"main.subpackage","msg":"sub-logger"}
+}
+
+func ExampleWrapCore_replace() {
+ // Replacing a Logger's core can alter fundamental behaviors. For example,
+ // example, it can convert a Logger to a no-op.
+ nop := zap.WrapCore(func(zapcore.Core) zapcore.Core {
+ return zapcore.NewNopCore()
+ })
+
+ logger := zap.NewExample()
+ defer logger.Sync()
+
+ logger.Info("working")
+ logger.WithOptions(nop).Info("no-op")
+ logger.Info("original logger still works")
+ // Output:
+ // {"level":"info","msg":"working"}
+ // {"level":"info","msg":"original logger still works"}
+}
+
+func ExampleWrapCore_wrap() {
+ // Wrapping a Logger's core can extend its functionality. As a trivial
+ // example, it can double-write all logs.
+ doubled := zap.WrapCore(func(c zapcore.Core) zapcore.Core {
+ return zapcore.NewTee(c, c)
+ })
+
+ logger := zap.NewExample()
+ defer logger.Sync()
+
+ logger.Info("single")
+ logger.WithOptions(doubled).Info("doubled")
+ // Output:
+ // {"level":"info","msg":"single"}
+ // {"level":"info","msg":"doubled"}
+ // {"level":"info","msg":"doubled"}
+}
diff --git a/vendor/go.uber.org/zap/field.go b/vendor/go.uber.org/zap/field.go
new file mode 100644
index 0000000..20eb487
--- /dev/null
+++ b/vendor/go.uber.org/zap/field.go
@@ -0,0 +1,306 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "fmt"
+ "math"
+ "time"
+
+ "go.uber.org/zap/zapcore"
+)
+
+// Skip constructs a no-op field, which is often useful when handling invalid
+// inputs in other Field constructors.
+func Skip() zapcore.Field {
+ return zapcore.Field{Type: zapcore.SkipType}
+}
+
+// Binary constructs a field that carries an opaque binary blob.
+//
+// Binary data is serialized in an encoding-appropriate format. For example,
+// zap's JSON encoder base64-encodes binary blobs. To log UTF-8 encoded text,
+// use ByteString.
+func Binary(key string, val []byte) zapcore.Field {
+ return zapcore.Field{Key: key, Type: zapcore.BinaryType, Interface: val}
+}
+
+// Bool constructs a field that carries a bool.
+func Bool(key string, val bool) zapcore.Field {
+ var ival int64
+ if val {
+ ival = 1
+ }
+ return zapcore.Field{Key: key, Type: zapcore.BoolType, Integer: ival}
+}
+
+// ByteString constructs a field that carries UTF-8 encoded text as a []byte.
+// To log opaque binary blobs (which aren't necessarily valid UTF-8), use
+// Binary.
+func ByteString(key string, val []byte) zapcore.Field {
+ return zapcore.Field{Key: key, Type: zapcore.ByteStringType, Interface: val}
+}
+
+// Complex128 constructs a field that carries a complex number. Unlike most
+// numeric fields, this costs an allocation (to convert the complex128 to
+// interface{}).
+func Complex128(key string, val complex128) zapcore.Field {
+ return zapcore.Field{Key: key, Type: zapcore.Complex128Type, Interface: val}
+}
+
+// Complex64 constructs a field that carries a complex number. Unlike most
+// numeric fields, this costs an allocation (to convert the complex64 to
+// interface{}).
+func Complex64(key string, val complex64) zapcore.Field {
+ return zapcore.Field{Key: key, Type: zapcore.Complex64Type, Interface: val}
+}
+
+// Float64 constructs a field that carries a float64. The way the
+// floating-point value is represented is encoder-dependent, so marshaling is
+// necessarily lazy.
+func Float64(key string, val float64) zapcore.Field {
+ return zapcore.Field{Key: key, Type: zapcore.Float64Type, Integer: int64(math.Float64bits(val))}
+}
+
+// Float32 constructs a field that carries a float32. The way the
+// floating-point value is represented is encoder-dependent, so marshaling is
+// necessarily lazy.
+func Float32(key string, val float32) zapcore.Field {
+ return zapcore.Field{Key: key, Type: zapcore.Float32Type, Integer: int64(math.Float32bits(val))}
+}
+
+// Int constructs a field with the given key and value.
+func Int(key string, val int) zapcore.Field {
+ return Int64(key, int64(val))
+}
+
+// Int64 constructs a field with the given key and value.
+func Int64(key string, val int64) zapcore.Field {
+ return zapcore.Field{Key: key, Type: zapcore.Int64Type, Integer: val}
+}
+
+// Int32 constructs a field with the given key and value.
+func Int32(key string, val int32) zapcore.Field {
+ return zapcore.Field{Key: key, Type: zapcore.Int32Type, Integer: int64(val)}
+}
+
+// Int16 constructs a field with the given key and value.
+func Int16(key string, val int16) zapcore.Field {
+ return zapcore.Field{Key: key, Type: zapcore.Int16Type, Integer: int64(val)}
+}
+
+// Int8 constructs a field with the given key and value.
+func Int8(key string, val int8) zapcore.Field {
+ return zapcore.Field{Key: key, Type: zapcore.Int8Type, Integer: int64(val)}
+}
+
+// String constructs a field with the given key and value.
+func String(key string, val string) zapcore.Field {
+ return zapcore.Field{Key: key, Type: zapcore.StringType, String: val}
+}
+
+// Uint constructs a field with the given key and value.
+func Uint(key string, val uint) zapcore.Field {
+ return Uint64(key, uint64(val))
+}
+
+// Uint64 constructs a field with the given key and value.
+func Uint64(key string, val uint64) zapcore.Field {
+ return zapcore.Field{Key: key, Type: zapcore.Uint64Type, Integer: int64(val)}
+}
+
+// Uint32 constructs a field with the given key and value.
+func Uint32(key string, val uint32) zapcore.Field {
+ return zapcore.Field{Key: key, Type: zapcore.Uint32Type, Integer: int64(val)}
+}
+
+// Uint16 constructs a field with the given key and value.
+func Uint16(key string, val uint16) zapcore.Field {
+ return zapcore.Field{Key: key, Type: zapcore.Uint16Type, Integer: int64(val)}
+}
+
+// Uint8 constructs a field with the given key and value.
+func Uint8(key string, val uint8) zapcore.Field {
+ return zapcore.Field{Key: key, Type: zapcore.Uint8Type, Integer: int64(val)}
+}
+
+// Uintptr constructs a field with the given key and value.
+func Uintptr(key string, val uintptr) zapcore.Field {
+ return zapcore.Field{Key: key, Type: zapcore.UintptrType, Integer: int64(val)}
+}
+
+// Reflect constructs a field with the given key and an arbitrary object. It uses
+// an encoding-appropriate, reflection-based function to lazily serialize nearly
+// any object into the logging context, but it's relatively slow and
+// allocation-heavy. Outside tests, Any is always a better choice.
+//
+// If encoding fails (e.g., trying to serialize a map[int]string to JSON), Reflect
+// includes the error message in the final log output.
+func Reflect(key string, val interface{}) zapcore.Field {
+ return zapcore.Field{Key: key, Type: zapcore.ReflectType, Interface: val}
+}
+
+// Namespace creates a named, isolated scope within the logger's context. All
+// subsequent fields will be added to the new namespace.
+//
+// This helps prevent key collisions when injecting loggers into sub-components
+// or third-party libraries.
+func Namespace(key string) zapcore.Field {
+ return zapcore.Field{Key: key, Type: zapcore.NamespaceType}
+}
+
+// Stringer constructs a field with the given key and the output of the value's
+// String method. The Stringer's String method is called lazily.
+func Stringer(key string, val fmt.Stringer) zapcore.Field {
+ return zapcore.Field{Key: key, Type: zapcore.StringerType, Interface: val}
+}
+
+// Time constructs a zapcore.Field with the given key and value. The encoder
+// controls how the time is serialized.
+func Time(key string, val time.Time) zapcore.Field {
+ return zapcore.Field{Key: key, Type: zapcore.TimeType, Integer: val.UnixNano(), Interface: val.Location()}
+}
+
+// Stack constructs a field that stores a stacktrace of the current goroutine
+// under provided key. Keep in mind that taking a stacktrace is eager and
+// expensive (relatively speaking); this function both makes an allocation and
+// takes about two microseconds.
+func Stack(key string) zapcore.Field {
+ // Returning the stacktrace as a string costs an allocation, but saves us
+ // from expanding the zapcore.Field union struct to include a byte slice. Since
+ // taking a stacktrace is already so expensive (~10us), the extra allocation
+ // is okay.
+ return String(key, takeStacktrace())
+}
+
+// Duration constructs a field with the given key and value. The encoder
+// controls how the duration is serialized.
+func Duration(key string, val time.Duration) zapcore.Field {
+ return zapcore.Field{Key: key, Type: zapcore.DurationType, Integer: int64(val)}
+}
+
+// Object constructs a field with the given key and ObjectMarshaler. It
+// provides a flexible, but still type-safe and efficient, way to add map- or
+// struct-like user-defined types to the logging context. The struct's
+// MarshalLogObject method is called lazily.
+func Object(key string, val zapcore.ObjectMarshaler) zapcore.Field {
+ return zapcore.Field{Key: key, Type: zapcore.ObjectMarshalerType, Interface: val}
+}
+
+// Any takes a key and an arbitrary value and chooses the best way to represent
+// them as a field, falling back to a reflection-based approach only if
+// necessary.
+//
+// Since byte/uint8 and rune/int32 are aliases, Any can't differentiate between
+// them. To minimize suprise, []byte values are treated as binary blobs, byte
+// values are treated as uint8, and runes are always treated as integers.
+func Any(key string, value interface{}) zapcore.Field {
+ switch val := value.(type) {
+ case zapcore.ObjectMarshaler:
+ return Object(key, val)
+ case zapcore.ArrayMarshaler:
+ return Array(key, val)
+ case bool:
+ return Bool(key, val)
+ case []bool:
+ return Bools(key, val)
+ case complex128:
+ return Complex128(key, val)
+ case []complex128:
+ return Complex128s(key, val)
+ case complex64:
+ return Complex64(key, val)
+ case []complex64:
+ return Complex64s(key, val)
+ case float64:
+ return Float64(key, val)
+ case []float64:
+ return Float64s(key, val)
+ case float32:
+ return Float32(key, val)
+ case []float32:
+ return Float32s(key, val)
+ case int:
+ return Int(key, val)
+ case []int:
+ return Ints(key, val)
+ case int64:
+ return Int64(key, val)
+ case []int64:
+ return Int64s(key, val)
+ case int32:
+ return Int32(key, val)
+ case []int32:
+ return Int32s(key, val)
+ case int16:
+ return Int16(key, val)
+ case []int16:
+ return Int16s(key, val)
+ case int8:
+ return Int8(key, val)
+ case []int8:
+ return Int8s(key, val)
+ case string:
+ return String(key, val)
+ case []string:
+ return Strings(key, val)
+ case uint:
+ return Uint(key, val)
+ case []uint:
+ return Uints(key, val)
+ case uint64:
+ return Uint64(key, val)
+ case []uint64:
+ return Uint64s(key, val)
+ case uint32:
+ return Uint32(key, val)
+ case []uint32:
+ return Uint32s(key, val)
+ case uint16:
+ return Uint16(key, val)
+ case []uint16:
+ return Uint16s(key, val)
+ case uint8:
+ return Uint8(key, val)
+ case []byte:
+ return Binary(key, val)
+ case uintptr:
+ return Uintptr(key, val)
+ case []uintptr:
+ return Uintptrs(key, val)
+ case time.Time:
+ return Time(key, val)
+ case []time.Time:
+ return Times(key, val)
+ case time.Duration:
+ return Duration(key, val)
+ case []time.Duration:
+ return Durations(key, val)
+ case error:
+ return NamedError(key, val)
+ case []error:
+ return Errors(key, val)
+ case fmt.Stringer:
+ return Stringer(key, val)
+ default:
+ return Reflect(key, val)
+ }
+}
diff --git a/vendor/go.uber.org/zap/field_test.go b/vendor/go.uber.org/zap/field_test.go
new file mode 100644
index 0000000..8979073
--- /dev/null
+++ b/vendor/go.uber.org/zap/field_test.go
@@ -0,0 +1,159 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "net"
+ "sync"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+ "go.uber.org/zap/zapcore"
+)
+
+type username string
+
+func (n username) MarshalLogObject(enc zapcore.ObjectEncoder) error {
+ enc.AddString("username", string(n))
+ return nil
+}
+
+func assertCanBeReused(t testing.TB, field zapcore.Field) {
+ var wg sync.WaitGroup
+
+ for i := 0; i < 100; i++ {
+ enc := zapcore.NewMapObjectEncoder()
+
+ // Ensure using the field in multiple encoders in separate goroutines
+ // does not cause any races or panics.
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ assert.NotPanics(t, func() {
+ field.AddTo(enc)
+ }, "Reusing a field should not cause issues")
+ }()
+ }
+
+ wg.Wait()
+}
+
+func TestFieldConstructors(t *testing.T) {
+ // Interface types.
+ addr := net.ParseIP("1.2.3.4")
+ name := username("phil")
+ ints := []int{5, 6}
+
+ tests := []struct {
+ name string
+ field zapcore.Field
+ expect zapcore.Field
+ }{
+ {"Skip", zapcore.Field{Type: zapcore.SkipType}, Skip()},
+ {"Binary", zapcore.Field{Key: "k", Type: zapcore.BinaryType, Interface: []byte("ab12")}, Binary("k", []byte("ab12"))},
+ {"Bool", zapcore.Field{Key: "k", Type: zapcore.BoolType, Integer: 1}, Bool("k", true)},
+ {"Bool", zapcore.Field{Key: "k", Type: zapcore.BoolType, Integer: 1}, Bool("k", true)},
+ {"ByteString", zapcore.Field{Key: "k", Type: zapcore.ByteStringType, Interface: []byte("ab12")}, ByteString("k", []byte("ab12"))},
+ {"Complex128", zapcore.Field{Key: "k", Type: zapcore.Complex128Type, Interface: 1 + 2i}, Complex128("k", 1+2i)},
+ {"Complex64", zapcore.Field{Key: "k", Type: zapcore.Complex64Type, Interface: complex64(1 + 2i)}, Complex64("k", 1+2i)},
+ {"Duration", zapcore.Field{Key: "k", Type: zapcore.DurationType, Integer: 1}, Duration("k", 1)},
+ {"Int", zapcore.Field{Key: "k", Type: zapcore.Int64Type, Integer: 1}, Int("k", 1)},
+ {"Int64", zapcore.Field{Key: "k", Type: zapcore.Int64Type, Integer: 1}, Int64("k", 1)},
+ {"Int32", zapcore.Field{Key: "k", Type: zapcore.Int32Type, Integer: 1}, Int32("k", 1)},
+ {"Int16", zapcore.Field{Key: "k", Type: zapcore.Int16Type, Integer: 1}, Int16("k", 1)},
+ {"Int8", zapcore.Field{Key: "k", Type: zapcore.Int8Type, Integer: 1}, Int8("k", 1)},
+ {"String", zapcore.Field{Key: "k", Type: zapcore.StringType, String: "foo"}, String("k", "foo")},
+ {"Time", zapcore.Field{Key: "k", Type: zapcore.TimeType, Integer: 0, Interface: time.UTC}, Time("k", time.Unix(0, 0).In(time.UTC))},
+ {"Time", zapcore.Field{Key: "k", Type: zapcore.TimeType, Integer: 1000, Interface: time.UTC}, Time("k", time.Unix(0, 1000).In(time.UTC))},
+ {"Uint", zapcore.Field{Key: "k", Type: zapcore.Uint64Type, Integer: 1}, Uint("k", 1)},
+ {"Uint64", zapcore.Field{Key: "k", Type: zapcore.Uint64Type, Integer: 1}, Uint64("k", 1)},
+ {"Uint32", zapcore.Field{Key: "k", Type: zapcore.Uint32Type, Integer: 1}, Uint32("k", 1)},
+ {"Uint16", zapcore.Field{Key: "k", Type: zapcore.Uint16Type, Integer: 1}, Uint16("k", 1)},
+ {"Uint8", zapcore.Field{Key: "k", Type: zapcore.Uint8Type, Integer: 1}, Uint8("k", 1)},
+ {"Uintptr", zapcore.Field{Key: "k", Type: zapcore.UintptrType, Integer: 10}, Uintptr("k", 0xa)},
+ {"Reflect", zapcore.Field{Key: "k", Type: zapcore.ReflectType, Interface: ints}, Reflect("k", ints)},
+ {"Stringer", zapcore.Field{Key: "k", Type: zapcore.StringerType, Interface: addr}, Stringer("k", addr)},
+ {"Object", zapcore.Field{Key: "k", Type: zapcore.ObjectMarshalerType, Interface: name}, Object("k", name)},
+ {"Any:ObjectMarshaler", Any("k", name), Object("k", name)},
+ {"Any:ArrayMarshaler", Any("k", bools([]bool{true})), Array("k", bools([]bool{true}))},
+ {"Any:Stringer", Any("k", addr), Stringer("k", addr)},
+ {"Any:Bool", Any("k", true), Bool("k", true)},
+ {"Any:Bools", Any("k", []bool{true}), Bools("k", []bool{true})},
+ {"Any:Byte", Any("k", byte(1)), Uint8("k", 1)},
+ {"Any:Bytes", Any("k", []byte{1}), Binary("k", []byte{1})},
+ {"Any:Complex128", Any("k", 1+2i), Complex128("k", 1+2i)},
+ {"Any:Complex128s", Any("k", []complex128{1 + 2i}), Complex128s("k", []complex128{1 + 2i})},
+ {"Any:Complex64", Any("k", complex64(1+2i)), Complex64("k", 1+2i)},
+ {"Any:Complex64s", Any("k", []complex64{1 + 2i}), Complex64s("k", []complex64{1 + 2i})},
+ {"Any:Float64", Any("k", 3.14), Float64("k", 3.14)},
+ {"Any:Float64s", Any("k", []float64{3.14}), Float64s("k", []float64{3.14})},
+ {"Any:Float32", Any("k", float32(3.14)), Float32("k", 3.14)},
+ {"Any:Float32s", Any("k", []float32{3.14}), Float32s("k", []float32{3.14})},
+ {"Any:Int", Any("k", 1), Int("k", 1)},
+ {"Any:Ints", Any("k", []int{1}), Ints("k", []int{1})},
+ {"Any:Int64", Any("k", int64(1)), Int64("k", 1)},
+ {"Any:Int64s", Any("k", []int64{1}), Int64s("k", []int64{1})},
+ {"Any:Int32", Any("k", int32(1)), Int32("k", 1)},
+ {"Any:Int32s", Any("k", []int32{1}), Int32s("k", []int32{1})},
+ {"Any:Int16", Any("k", int16(1)), Int16("k", 1)},
+ {"Any:Int16s", Any("k", []int16{1}), Int16s("k", []int16{1})},
+ {"Any:Int8", Any("k", int8(1)), Int8("k", 1)},
+ {"Any:Int8s", Any("k", []int8{1}), Int8s("k", []int8{1})},
+ {"Any:Rune", Any("k", rune(1)), Int32("k", 1)},
+ {"Any:Runes", Any("k", []rune{1}), Int32s("k", []int32{1})},
+ {"Any:String", Any("k", "v"), String("k", "v")},
+ {"Any:Strings", Any("k", []string{"v"}), Strings("k", []string{"v"})},
+ {"Any:Uint", Any("k", uint(1)), Uint("k", 1)},
+ {"Any:Uints", Any("k", []uint{1}), Uints("k", []uint{1})},
+ {"Any:Uint64", Any("k", uint64(1)), Uint64("k", 1)},
+ {"Any:Uint64s", Any("k", []uint64{1}), Uint64s("k", []uint64{1})},
+ {"Any:Uint32", Any("k", uint32(1)), Uint32("k", 1)},
+ {"Any:Uint32s", Any("k", []uint32{1}), Uint32s("k", []uint32{1})},
+ {"Any:Uint16", Any("k", uint16(1)), Uint16("k", 1)},
+ {"Any:Uint16s", Any("k", []uint16{1}), Uint16s("k", []uint16{1})},
+ {"Any:Uint8", Any("k", uint8(1)), Uint8("k", 1)},
+ {"Any:Uint8s", Any("k", []uint8{1}), Binary("k", []uint8{1})},
+ {"Any:Uintptr", Any("k", uintptr(1)), Uintptr("k", 1)},
+ {"Any:Uintptrs", Any("k", []uintptr{1}), Uintptrs("k", []uintptr{1})},
+ {"Any:Time", Any("k", time.Unix(0, 0)), Time("k", time.Unix(0, 0))},
+ {"Any:Times", Any("k", []time.Time{time.Unix(0, 0)}), Times("k", []time.Time{time.Unix(0, 0)})},
+ {"Any:Duration", Any("k", time.Second), Duration("k", time.Second)},
+ {"Any:Durations", Any("k", []time.Duration{time.Second}), Durations("k", []time.Duration{time.Second})},
+ {"Any:Fallback", Any("k", struct{}{}), Reflect("k", struct{}{})},
+ {"Namespace", Namespace("k"), zapcore.Field{Key: "k", Type: zapcore.NamespaceType}},
+ }
+
+ for _, tt := range tests {
+ if !assert.Equal(t, tt.expect, tt.field, "Unexpected output from convenience field constructor %s.", tt.name) {
+ t.Logf("type expected: %T\nGot: %T", tt.expect.Interface, tt.field.Interface)
+ }
+ assertCanBeReused(t, tt.field)
+ }
+}
+
+func TestStackField(t *testing.T) {
+ f := Stack("stacktrace")
+ assert.Equal(t, "stacktrace", f.Key, "Unexpected field key.")
+ assert.Equal(t, zapcore.StringType, f.Type, "Unexpected field type.")
+ assert.Contains(t, f.String, "zap.TestStackField", "Expected stacktrace to contain caller.")
+ assertCanBeReused(t, f)
+}
diff --git a/vendor/go.uber.org/zap/flag.go b/vendor/go.uber.org/zap/flag.go
new file mode 100644
index 0000000..1312875
--- /dev/null
+++ b/vendor/go.uber.org/zap/flag.go
@@ -0,0 +1,39 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "flag"
+
+ "go.uber.org/zap/zapcore"
+)
+
+// LevelFlag uses the standard library's flag.Var to declare a global flag
+// with the specified name, default, and usage guidance. The returned value is
+// a pointer to the value of the flag.
+//
+// If you don't want to use the flag package's global state, you can use any
+// non-nil *Level as a flag.Value with your own *flag.FlagSet.
+func LevelFlag(name string, defaultLevel zapcore.Level, usage string) *zapcore.Level {
+ lvl := defaultLevel
+ flag.Var(&lvl, name, usage)
+ return &lvl
+}
diff --git a/vendor/go.uber.org/zap/flag_test.go b/vendor/go.uber.org/zap/flag_test.go
new file mode 100644
index 0000000..b169894
--- /dev/null
+++ b/vendor/go.uber.org/zap/flag_test.go
@@ -0,0 +1,102 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "flag"
+ "io/ioutil"
+ "testing"
+
+ "go.uber.org/zap/zapcore"
+
+ "github.com/stretchr/testify/assert"
+)
+
+type flagTestCase struct {
+ args []string
+ wantLevel zapcore.Level
+ wantErr bool
+}
+
+func (tc flagTestCase) runImplicitSet(t testing.TB) {
+ origCommandLine := flag.CommandLine
+ flag.CommandLine = flag.NewFlagSet("test", flag.ContinueOnError)
+ flag.CommandLine.SetOutput(ioutil.Discard)
+ defer func() { flag.CommandLine = origCommandLine }()
+
+ level := LevelFlag("level", InfoLevel, "")
+ tc.run(t, flag.CommandLine, level)
+}
+
+func (tc flagTestCase) runExplicitSet(t testing.TB) {
+ var lvl zapcore.Level
+ set := flag.NewFlagSet("test", flag.ContinueOnError)
+ set.Var(&lvl, "level", "minimum enabled logging level")
+ tc.run(t, set, &lvl)
+}
+
+func (tc flagTestCase) run(t testing.TB, set *flag.FlagSet, actual *zapcore.Level) {
+ err := set.Parse(tc.args)
+ if tc.wantErr {
+ assert.Error(t, err, "Parse(%v) should fail.", tc.args)
+ return
+ }
+ if assert.NoError(t, err, "Parse(%v) should succeed.", tc.args) {
+ assert.Equal(t, tc.wantLevel, *actual, "Level mismatch.")
+ }
+}
+
+func TestLevelFlag(t *testing.T) {
+ tests := []flagTestCase{
+ {
+ args: nil,
+ wantLevel: zapcore.InfoLevel,
+ },
+ {
+ args: []string{"--level", "unknown"},
+ wantErr: true,
+ },
+ {
+ args: []string{"--level", "error"},
+ wantLevel: zapcore.ErrorLevel,
+ },
+ }
+
+ for _, tt := range tests {
+ tt.runExplicitSet(t)
+ tt.runImplicitSet(t)
+ }
+}
+
+func TestLevelFlagsAreIndependent(t *testing.T) {
+ origCommandLine := flag.CommandLine
+ flag.CommandLine = flag.NewFlagSet("test", flag.ContinueOnError)
+ flag.CommandLine.SetOutput(ioutil.Discard)
+ defer func() { flag.CommandLine = origCommandLine }()
+
+ // Make sure that these two flags are independent.
+ fileLevel := LevelFlag("file-level", InfoLevel, "")
+ consoleLevel := LevelFlag("console-level", InfoLevel, "")
+
+ assert.NoError(t, flag.CommandLine.Parse([]string{"-file-level", "debug"}), "Unexpected flag-parsing error.")
+ assert.Equal(t, InfoLevel, *consoleLevel, "Expected file logging level to remain unchanged.")
+ assert.Equal(t, DebugLevel, *fileLevel, "Expected console logging level to have changed.")
+}
diff --git a/vendor/go.uber.org/zap/glide.lock b/vendor/go.uber.org/zap/glide.lock
new file mode 100644
index 0000000..db50355
--- /dev/null
+++ b/vendor/go.uber.org/zap/glide.lock
@@ -0,0 +1,74 @@
+hash: 2b8be1588d6028696f61ce80eb1b50dbad19144b4d4ead760977ad54064f4f26
+updated: 2017-06-30T10:57:16.454459206-07:00
+imports:
+- name: go.uber.org/atomic
+ version: 4e336646b2ef9fc6e47be8e21594178f98e5ebcf
+- name: go.uber.org/multierr
+ version: 3c4937480c32f4c13a875a1829af76c98ca3d40a
+testImports:
+- name: github.com/apex/log
+ version: 8f3a15d95392c8fc202d1e1059f46df21dff2992
+ subpackages:
+ - handlers/json
+- name: github.com/axw/gocov
+ version: 3a69a0d2a4ef1f263e2d92b041a69593d6964fe8
+ subpackages:
+ - gocov
+- name: github.com/davecgh/go-spew
+ version: 346938d642f2ec3594ed81d874461961cd0faa76
+ subpackages:
+ - spew
+- name: github.com/fatih/color
+ version: 62e9147c64a1ed519147b62a56a14e83e2be02c1
+- name: github.com/go-kit/kit
+ version: 9917269ff0e9fc93df46474b411ea1716195a61b
+ subpackages:
+ - log
+- name: github.com/go-logfmt/logfmt
+ version: 390ab7935ee28ec6b286364bba9b4dd6410cb3d5
+- name: github.com/go-stack/stack
+ version: 7a2f19628aabfe68f0766b59e74d6315f8347d22
+- name: github.com/golang/lint
+ version: c5fb716d6688a859aae56d26d3e6070808df29f7
+ subpackages:
+ - golint
+- name: github.com/kr/logfmt
+ version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0
+- name: github.com/mattn/go-colorable
+ version: 5411d3eea5978e6cdc258b30de592b60df6aba96
+- name: github.com/mattn/go-isatty
+ version: 57fdcb988a5c543893cc61bce354a6e24ab70022
+- name: github.com/mattn/goveralls
+ version: a2cbbd7cdce4f5e051016fedf639c64bb05ef031
+- name: github.com/pborman/uuid
+ version: 1b00554d822231195d1babd97ff4a781231955c9
+- name: github.com/pkg/errors
+ version: 645ef00459ed84a119197bfb8d8205042c6df63d
+- name: github.com/pmezard/go-difflib
+ version: 792786c7400a136282c1664665ae0a8db921c6c2
+ subpackages:
+ - difflib
+- name: github.com/satori/go.uuid
+ version: 5bf94b69c6b68ee1b541973bb8e1144db23a194b
+- name: github.com/sirupsen/logrus
+ version: 68cec9f21fbf3ea8d8f98c044bc6ce05f17b267a
+- name: github.com/stretchr/testify
+ version: f6abca593680b2315d2075e0f5e2a9751e3f431a
+ subpackages:
+ - assert
+ - require
+- name: go.pedge.io/lion
+ version: 87958e8713f1fa138d993087133b97e976642159
+- name: golang.org/x/sys
+ version: d4feaf1a7e61e1d9e79e6c4e76c6349e9cab0a03
+ subpackages:
+ - unix
+- name: golang.org/x/tools
+ version: 1529f889eb4b594d1f047f2fb8d5b3cc85c8f006
+ subpackages:
+ - cover
+- name: gopkg.in/inconshreveable/log15.v2
+ version: b105bd37f74e5d9dc7b6ad7806715c7a2b83fd3f
+ subpackages:
+ - stack
+ - term
diff --git a/vendor/go.uber.org/zap/glide.yaml b/vendor/go.uber.org/zap/glide.yaml
new file mode 100644
index 0000000..b45a15f
--- /dev/null
+++ b/vendor/go.uber.org/zap/glide.yaml
@@ -0,0 +1,34 @@
+package: go.uber.org/zap
+license: MIT
+import:
+- package: go.uber.org/atomic
+ version: ^1
+- package: go.uber.org/multierr
+ version: ^1
+testImport:
+- package: github.com/satori/go.uuid
+- package: github.com/sirupsen/logrus
+- package: github.com/apex/log
+ subpackages:
+ - handlers/json
+- package: github.com/go-kit/kit
+ subpackages:
+ - log
+- package: github.com/stretchr/testify
+ subpackages:
+ - assert
+ - require
+- package: gopkg.in/inconshreveable/log15.v2
+- package: github.com/mattn/goveralls
+- package: github.com/pborman/uuid
+- package: github.com/pkg/errors
+- package: go.pedge.io/lion
+- package: golang.org/x/tools
+ subpackages:
+ - cover
+- package: github.com/golang/lint
+ subpackages:
+ - golint
+- package: github.com/axw/gocov
+ subpackages:
+ - gocov
diff --git a/vendor/go.uber.org/zap/global.go b/vendor/go.uber.org/zap/global.go
new file mode 100644
index 0000000..fbf0f75
--- /dev/null
+++ b/vendor/go.uber.org/zap/global.go
@@ -0,0 +1,109 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "bytes"
+ "log"
+ "os"
+ "sync"
+)
+
+const (
+ _stdLogDefaultDepth = 2
+ _loggerWriterDepth = 1
+)
+
+var (
+ _globalMu sync.RWMutex
+ _globalL = NewNop()
+ _globalS = _globalL.Sugar()
+)
+
+// L returns the global Logger, which can be reconfigured with ReplaceGlobals.
+// It's safe for concurrent use.
+func L() *Logger {
+ _globalMu.RLock()
+ l := _globalL
+ _globalMu.RUnlock()
+ return l
+}
+
+// S returns the global SugaredLogger, which can be reconfigured with
+// ReplaceGlobals. It's safe for concurrent use.
+func S() *SugaredLogger {
+ _globalMu.RLock()
+ s := _globalS
+ _globalMu.RUnlock()
+ return s
+}
+
+// ReplaceGlobals replaces the global Logger and SugaredLogger, and returns a
+// function to restore the original values. It's safe for concurrent use.
+func ReplaceGlobals(logger *Logger) func() {
+ _globalMu.Lock()
+ prev := _globalL
+ _globalL = logger
+ _globalS = logger.Sugar()
+ _globalMu.Unlock()
+ return func() { ReplaceGlobals(prev) }
+}
+
+// NewStdLog returns a *log.Logger which writes to the supplied zap Logger at
+// InfoLevel. To redirect the standard library's package-global logging
+// functions, use RedirectStdLog instead.
+func NewStdLog(l *Logger) *log.Logger {
+ return log.New(&loggerWriter{l.WithOptions(
+ AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth),
+ )}, "" /* prefix */, 0 /* flags */)
+}
+
+// RedirectStdLog redirects output from the standard library's package-global
+// logger to the supplied logger at InfoLevel. Since zap already handles caller
+// annotations, timestamps, etc., it automatically disables the standard
+// library's annotations and prefixing.
+//
+// It returns a function to restore the original prefix and flags and reset the
+// standard library's output to os.Stdout.
+func RedirectStdLog(l *Logger) func() {
+ flags := log.Flags()
+ prefix := log.Prefix()
+ log.SetFlags(0)
+ log.SetPrefix("")
+ log.SetOutput(&loggerWriter{l.WithOptions(
+ AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth),
+ )})
+ return func() {
+ log.SetFlags(flags)
+ log.SetPrefix(prefix)
+ log.SetOutput(os.Stderr)
+ }
+}
+
+type loggerWriter struct {
+ logger *Logger
+}
+
+func (l *loggerWriter) Write(p []byte) (int, error) {
+ p = bytes.TrimSpace(p)
+ l.logger.Info(string(p))
+ return len(p), nil
+}
diff --git a/vendor/go.uber.org/zap/global_test.go b/vendor/go.uber.org/zap/global_test.go
new file mode 100644
index 0000000..ed2d144
--- /dev/null
+++ b/vendor/go.uber.org/zap/global_test.go
@@ -0,0 +1,139 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "log"
+ "sync"
+ "testing"
+ "time"
+
+ "go.uber.org/zap/zapcore"
+ "go.uber.org/zap/zaptest"
+ "go.uber.org/zap/zaptest/observer"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "go.uber.org/atomic"
+)
+
+func TestReplaceGlobals(t *testing.T) {
+ initialL := *L()
+ initialS := *S()
+
+ withLogger(t, DebugLevel, nil, func(l *Logger, logs *observer.ObservedLogs) {
+ L().Info("no-op")
+ S().Info("no-op")
+ assert.Equal(t, 0, logs.Len(), "Expected initial logs to go to default no-op global.")
+
+ defer ReplaceGlobals(l)()
+
+ L().Info("captured")
+ S().Info("captured")
+ expected := observer.LoggedEntry{
+ Entry: zapcore.Entry{Message: "captured"},
+ Context: []zapcore.Field{},
+ }
+ assert.Equal(
+ t,
+ []observer.LoggedEntry{expected, expected},
+ logs.AllUntimed(),
+ "Unexpected global log output.",
+ )
+ })
+
+ assert.Equal(t, initialL, *L(), "Expected func returned from ReplaceGlobals to restore initial L.")
+ assert.Equal(t, initialS, *S(), "Expected func returned from ReplaceGlobals to restore initial S.")
+}
+
+func TestGlobalsConcurrentUse(t *testing.T) {
+ var (
+ stop atomic.Bool
+ wg sync.WaitGroup
+ )
+
+ for i := 0; i < 100; i++ {
+ wg.Add(2)
+ go func() {
+ for !stop.Load() {
+ ReplaceGlobals(NewNop())
+ }
+ wg.Done()
+ }()
+ go func() {
+ for !stop.Load() {
+ L().With(Int("foo", 42)).Named("main").WithOptions(Development()).Info("")
+ S().Info("")
+ }
+ wg.Done()
+ }()
+ }
+
+ zaptest.Sleep(100 * time.Millisecond)
+ stop.Toggle()
+ wg.Wait()
+}
+
+func TestNewStdLog(t *testing.T) {
+ withLogger(t, DebugLevel, []Option{AddCaller()}, func(l *Logger, logs *observer.ObservedLogs) {
+ std := NewStdLog(l)
+ std.Print("redirected")
+
+ require.Equal(t, 1, logs.Len(), "Expected exactly one entry to be logged.")
+ entry := logs.AllUntimed()[0]
+ assert.Equal(t, []zapcore.Field{}, entry.Context, "Unexpected entry context.")
+ assert.Equal(t, "redirected", entry.Entry.Message, "Unexpected entry message.")
+ assert.Regexp(
+ t,
+ `go.uber.org/zap/global_test.go:\d+$`,
+ entry.Entry.Caller.String(),
+ "Unexpected caller annotation.",
+ )
+ })
+}
+
+func TestRedirectStdLog(t *testing.T) {
+ initialFlags := log.Flags()
+ initialPrefix := log.Prefix()
+
+ withLogger(t, DebugLevel, nil, func(l *Logger, logs *observer.ObservedLogs) {
+ defer RedirectStdLog(l)()
+ log.Print("redirected")
+
+ assert.Equal(t, []observer.LoggedEntry{{
+ Entry: zapcore.Entry{Message: "redirected"},
+ Context: []zapcore.Field{},
+ }}, logs.AllUntimed(), "Unexpected global log output.")
+ })
+
+ assert.Equal(t, initialFlags, log.Flags(), "Expected to reset initial flags.")
+ assert.Equal(t, initialPrefix, log.Prefix(), "Expected to reset initial prefix.")
+}
+
+func TestRedirectStdLogCaller(t *testing.T) {
+ withLogger(t, DebugLevel, []Option{AddCaller()}, func(l *Logger, logs *observer.ObservedLogs) {
+ defer RedirectStdLog(l)()
+ log.Print("redirected")
+ entries := logs.All()
+ require.Len(t, entries, 1, "Unexpected number of logs.")
+ assert.Contains(t, entries[0].Entry.Caller.File, "global_test.go", "Unexpected caller annotation.")
+ })
+}
diff --git a/vendor/go.uber.org/zap/http_handler.go b/vendor/go.uber.org/zap/http_handler.go
new file mode 100644
index 0000000..f171c38
--- /dev/null
+++ b/vendor/go.uber.org/zap/http_handler.go
@@ -0,0 +1,81 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+
+ "go.uber.org/zap/zapcore"
+)
+
+// ServeHTTP is a simple JSON endpoint that can report on or change the current
+// logging level.
+//
+// GET requests return a JSON description of the current logging level. PUT
+// requests change the logging level and expect a payload like:
+// {"level":"info"}
+//
+// It's perfectly safe to change the logging level while a program is running.
+func (lvl AtomicLevel) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ type errorResponse struct {
+ Error string `json:"error"`
+ }
+ type payload struct {
+ Level *zapcore.Level `json:"level"`
+ }
+
+ enc := json.NewEncoder(w)
+
+ switch r.Method {
+
+ case "GET":
+ current := lvl.Level()
+ enc.Encode(payload{Level: ¤t})
+
+ case "PUT":
+ var req payload
+
+ if errmess := func() string {
+ if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
+ return fmt.Sprintf("Request body must be well-formed JSON: %v", err)
+ }
+ if req.Level == nil {
+ return "Must specify a logging level."
+ }
+ return ""
+ }(); errmess != "" {
+ w.WriteHeader(http.StatusBadRequest)
+ enc.Encode(errorResponse{Error: errmess})
+ return
+ }
+
+ lvl.SetLevel(*req.Level)
+ enc.Encode(req)
+
+ default:
+ w.WriteHeader(http.StatusMethodNotAllowed)
+ enc.Encode(errorResponse{
+ Error: "Only GET and PUT are supported.",
+ })
+ }
+}
diff --git a/vendor/go.uber.org/zap/http_handler_test.go b/vendor/go.uber.org/zap/http_handler_test.go
new file mode 100644
index 0000000..474b3c7
--- /dev/null
+++ b/vendor/go.uber.org/zap/http_handler_test.go
@@ -0,0 +1,131 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap_test
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "strings"
+ "testing"
+
+ . "go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func newHandler() (AtomicLevel, *Logger) {
+ lvl := NewAtomicLevel()
+ logger := New(zapcore.NewNopCore())
+ return lvl, logger
+}
+
+func assertCodeOK(t testing.TB, code int) {
+ assert.Equal(t, http.StatusOK, code, "Unexpected response status code.")
+}
+
+func assertCodeBadRequest(t testing.TB, code int) {
+ assert.Equal(t, http.StatusBadRequest, code, "Unexpected response status code.")
+}
+
+func assertCodeMethodNotAllowed(t testing.TB, code int) {
+ assert.Equal(t, http.StatusMethodNotAllowed, code, "Unexpected response status code.")
+}
+
+func assertResponse(t testing.TB, expectedLevel zapcore.Level, actualBody string) {
+ assert.Equal(t, fmt.Sprintf(`{"level":"%s"}`, expectedLevel)+"\n", actualBody, "Unexpected response body.")
+}
+
+func assertJSONError(t testing.TB, body string) {
+ // Don't need to test exact error message, but one should be present.
+ var payload map[string]interface{}
+ require.NoError(t, json.Unmarshal([]byte(body), &payload), "Expected error response to be JSON.")
+
+ msg, ok := payload["error"]
+ require.True(t, ok, "Error message is an unexpected type.")
+ assert.NotEqual(t, "", msg, "Expected an error message in response.")
+}
+
+func makeRequest(t testing.TB, method string, handler http.Handler, reader io.Reader) (int, string) {
+ ts := httptest.NewServer(handler)
+ defer ts.Close()
+
+ req, err := http.NewRequest(method, ts.URL, reader)
+ require.NoError(t, err, "Error constructing %s request.", method)
+
+ res, err := http.DefaultClient.Do(req)
+ require.NoError(t, err, "Error making %s request.", method)
+ defer res.Body.Close()
+
+ body, err := ioutil.ReadAll(res.Body)
+ require.NoError(t, err, "Error reading request body.")
+
+ return res.StatusCode, string(body)
+}
+
+func TestHTTPHandlerGetLevel(t *testing.T) {
+ lvl, _ := newHandler()
+ code, body := makeRequest(t, "GET", lvl, nil)
+ assertCodeOK(t, code)
+ assertResponse(t, lvl.Level(), body)
+}
+
+func TestHTTPHandlerPutLevel(t *testing.T) {
+ lvl, _ := newHandler()
+
+ code, body := makeRequest(t, "PUT", lvl, strings.NewReader(`{"level":"warn"}`))
+
+ assertCodeOK(t, code)
+ assertResponse(t, lvl.Level(), body)
+}
+
+func TestHTTPHandlerPutUnrecognizedLevel(t *testing.T) {
+ lvl, _ := newHandler()
+ code, body := makeRequest(t, "PUT", lvl, strings.NewReader(`{"level":"unrecognized-level"}`))
+ assertCodeBadRequest(t, code)
+ assertJSONError(t, body)
+}
+
+func TestHTTPHandlerNotJSON(t *testing.T) {
+ lvl, _ := newHandler()
+ code, body := makeRequest(t, "PUT", lvl, strings.NewReader(`{`))
+ assertCodeBadRequest(t, code)
+ assertJSONError(t, body)
+}
+
+func TestHTTPHandlerNoLevelSpecified(t *testing.T) {
+ lvl, _ := newHandler()
+ code, body := makeRequest(t, "PUT", lvl, strings.NewReader(`{}`))
+ assertCodeBadRequest(t, code)
+ assertJSONError(t, body)
+}
+
+func TestHTTPHandlerMethodNotAllowed(t *testing.T) {
+ lvl, _ := newHandler()
+ code, body := makeRequest(t, "POST", lvl, strings.NewReader(`{`))
+ assertCodeMethodNotAllowed(t, code)
+ assertJSONError(t, body)
+}
diff --git a/vendor/go.uber.org/zap/internal/bufferpool/bufferpool.go b/vendor/go.uber.org/zap/internal/bufferpool/bufferpool.go
new file mode 100644
index 0000000..dad583a
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/bufferpool/bufferpool.go
@@ -0,0 +1,31 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+// Package bufferpool houses zap's shared internal buffer pool. Third-party
+// packages can recreate the same functionality with buffers.NewPool.
+package bufferpool
+
+import "go.uber.org/zap/buffer"
+
+var (
+ _pool = buffer.NewPool()
+ // Get retrieves a buffer from the pool, creating one if necessary.
+ Get = _pool.Get
+)
diff --git a/vendor/go.uber.org/zap/internal/color/color.go b/vendor/go.uber.org/zap/internal/color/color.go
new file mode 100644
index 0000000..c4d5d02
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/color/color.go
@@ -0,0 +1,44 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+// Package color adds coloring functionality for TTY output.
+package color
+
+import "fmt"
+
+// Foreground colors.
+const (
+ Black Color = iota + 30
+ Red
+ Green
+ Yellow
+ Blue
+ Magenta
+ Cyan
+ White
+)
+
+// Color represents a text color.
+type Color uint8
+
+// Add adds the coloring to the given string.
+func (c Color) Add(s string) string {
+ return fmt.Sprintf("\x1b[%dm%s\x1b[0m", uint8(c), s)
+}
diff --git a/vendor/go.uber.org/zap/internal/color/color_test.go b/vendor/go.uber.org/zap/internal/color/color_test.go
new file mode 100644
index 0000000..4982903
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/color/color_test.go
@@ -0,0 +1,36 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package color
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestColorFormatting(t *testing.T) {
+ assert.Equal(
+ t,
+ "\x1b[31mfoo\x1b[0m",
+ Red.Add("foo"),
+ "Unexpected colored output.",
+ )
+}
diff --git a/vendor/go.uber.org/zap/internal/exit/exit.go b/vendor/go.uber.org/zap/internal/exit/exit.go
new file mode 100644
index 0000000..dfc5b05
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/exit/exit.go
@@ -0,0 +1,64 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+// Package exit provides stubs so that unit tests can exercise code that calls
+// os.Exit(1).
+package exit
+
+import "os"
+
+var real = func() { os.Exit(1) }
+
+// Exit normally terminates the process by calling os.Exit(1). If the package
+// is stubbed, it instead records a call in the testing spy.
+func Exit() {
+ real()
+}
+
+// A StubbedExit is a testing fake for os.Exit.
+type StubbedExit struct {
+ Exited bool
+ prev func()
+}
+
+// Stub substitutes a fake for the call to os.Exit(1).
+func Stub() *StubbedExit {
+ s := &StubbedExit{prev: real}
+ real = s.exit
+ return s
+}
+
+// WithStub runs the supplied function with Exit stubbed. It returns the stub
+// used, so that users can test whether the process would have crashed.
+func WithStub(f func()) *StubbedExit {
+ s := Stub()
+ defer s.Unstub()
+ f()
+ return s
+}
+
+// Unstub restores the previous exit function.
+func (se *StubbedExit) Unstub() {
+ real = se.prev
+}
+
+func (se *StubbedExit) exit() {
+ se.Exited = true
+}
diff --git a/vendor/go.uber.org/zap/internal/exit/exit_test.go b/vendor/go.uber.org/zap/internal/exit/exit_test.go
new file mode 100644
index 0000000..300cdc3
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/exit/exit_test.go
@@ -0,0 +1,42 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package exit
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestStub(t *testing.T) {
+ tests := []struct {
+ f func()
+ want bool
+ }{
+ {Exit, true},
+ {func() {}, false},
+ }
+
+ for _, tt := range tests {
+ s := WithStub(tt.f)
+ assert.Equal(t, tt.want, s.Exited, "Stub captured unexpected exit value.")
+ }
+}
diff --git a/vendor/go.uber.org/zap/internal/readme/readme.go b/vendor/go.uber.org/zap/internal/readme/readme.go
new file mode 100644
index 0000000..82c3418
--- /dev/null
+++ b/vendor/go.uber.org/zap/internal/readme/readme.go
@@ -0,0 +1,203 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+ "os/exec"
+ "sort"
+ "strconv"
+ "strings"
+ "text/template"
+ "time"
+)
+
+var (
+ libraryNameToMarkdownName = map[string]string{
+ "Zap": ":zap: zap",
+ "Zap.Sugar": ":zap: zap (sugared)",
+ "stdlib.Println": "standard library",
+ "sirupsen/logrus": "logrus",
+ "go-kit/kit/log": "go-kit",
+ "inconshreveable/log15": "log15",
+ "apex/log": "apex/log",
+ "go.pedge.io/lion": "lion",
+ }
+)
+
+func main() {
+ flag.Parse()
+ if err := do(); err != nil {
+ log.Fatal(err)
+ }
+}
+
+func do() error {
+ tmplData, err := getTmplData()
+ if err != nil {
+ return err
+ }
+ data, err := ioutil.ReadAll(os.Stdin)
+ if err != nil {
+ return err
+ }
+ t, err := template.New("tmpl").Parse(string(data))
+ if err != nil {
+ return err
+ }
+ if err := t.Execute(os.Stdout, tmplData); err != nil {
+ return err
+ }
+ return nil
+}
+
+func getTmplData() (*tmplData, error) {
+ tmplData := &tmplData{}
+ rows, err := getBenchmarkRows("BenchmarkAddingFields")
+ if err != nil {
+ return nil, err
+ }
+ tmplData.BenchmarkAddingFields = rows
+ rows, err = getBenchmarkRows("BenchmarkAccumulatedContext")
+ if err != nil {
+ return nil, err
+ }
+ tmplData.BenchmarkAccumulatedContext = rows
+ rows, err = getBenchmarkRows("BenchmarkWithoutFields")
+ if err != nil {
+ return nil, err
+ }
+ tmplData.BenchmarkWithoutFields = rows
+ return tmplData, nil
+}
+
+func getBenchmarkRows(benchmarkName string) (string, error) {
+ benchmarkOutput, err := getBenchmarkOutput(benchmarkName)
+ if err != nil {
+ return "", err
+ }
+ var benchmarkRows []*benchmarkRow
+ for libraryName := range libraryNameToMarkdownName {
+ benchmarkRow, err := getBenchmarkRow(benchmarkOutput, benchmarkName, libraryName)
+ if err != nil {
+ return "", err
+ }
+ if benchmarkRow == nil {
+ continue
+ }
+ benchmarkRows = append(benchmarkRows, benchmarkRow)
+ }
+ sort.Sort(benchmarkRowsByTime(benchmarkRows))
+ rows := []string{
+ "| Package | Time | Bytes Allocated | Objects Allocated |",
+ "| :--- | :---: | :---: | :---: |",
+ }
+ for _, benchmarkRow := range benchmarkRows {
+ rows = append(rows, benchmarkRow.String())
+ }
+ return strings.Join(rows, "\n"), nil
+}
+
+func getBenchmarkRow(input []string, benchmarkName string, libraryName string) (*benchmarkRow, error) {
+ line, err := findUniqueSubstring(input, fmt.Sprintf("%s/%s-", benchmarkName, libraryName))
+ if err != nil {
+ return nil, err
+ }
+ if line == "" {
+ return nil, nil
+ }
+ split := strings.Split(line, "\t")
+ if len(split) < 5 {
+ return nil, fmt.Errorf("unknown benchmark line: %s", line)
+ }
+ duration, err := time.ParseDuration(strings.Replace(strings.TrimSuffix(strings.TrimSpace(split[2]), "/op"), " ", "", -1))
+ if err != nil {
+ return nil, err
+ }
+ allocatedBytes, err := strconv.Atoi(strings.TrimSuffix(strings.TrimSpace(split[3]), " B/op"))
+ if err != nil {
+ return nil, err
+ }
+ allocatedObjects, err := strconv.Atoi(strings.TrimSuffix(strings.TrimSpace(split[4]), " allocs/op"))
+ if err != nil {
+ return nil, err
+ }
+ return &benchmarkRow{
+ libraryNameToMarkdownName[libraryName],
+ duration,
+ allocatedBytes,
+ allocatedObjects,
+ }, nil
+}
+
+func findUniqueSubstring(input []string, substring string) (string, error) {
+ var output string
+ for _, line := range input {
+ if strings.Contains(line, substring) {
+ if output != "" {
+ return "", fmt.Errorf("input has duplicate substring %s", substring)
+ }
+ output = line
+ }
+ }
+ return output, nil
+}
+
+func getBenchmarkOutput(benchmarkName string) ([]string, error) {
+ return getOutput("go", "test", fmt.Sprintf("-bench=%s", benchmarkName), "-benchmem", "./benchmarks")
+}
+
+func getOutput(name string, arg ...string) ([]string, error) {
+ output, err := exec.Command(name, arg...).CombinedOutput()
+ if err != nil {
+ return nil, fmt.Errorf("error running %s %s: %v\n%s", name, strings.Join(arg, " "), err, string(output))
+ }
+ return strings.Split(string(output), "\n"), nil
+}
+
+type tmplData struct {
+ BenchmarkAddingFields string
+ BenchmarkAccumulatedContext string
+ BenchmarkWithoutFields string
+}
+
+type benchmarkRow struct {
+ Name string
+ Time time.Duration
+ AllocatedBytes int
+ AllocatedObjects int
+}
+
+func (b *benchmarkRow) String() string {
+ return fmt.Sprintf("| %s | %d ns/op | %d B/op | %d allocs/op |", b.Name, b.Time.Nanoseconds(), b.AllocatedBytes, b.AllocatedObjects)
+}
+
+type benchmarkRowsByTime []*benchmarkRow
+
+func (b benchmarkRowsByTime) Len() int { return len(b) }
+func (b benchmarkRowsByTime) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
+func (b benchmarkRowsByTime) Less(i, j int) bool {
+ return b[i].Time.Nanoseconds() < b[j].Time.Nanoseconds()
+}
diff --git a/vendor/go.uber.org/zap/level.go b/vendor/go.uber.org/zap/level.go
new file mode 100644
index 0000000..166101f
--- /dev/null
+++ b/vendor/go.uber.org/zap/level.go
@@ -0,0 +1,132 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "go.uber.org/atomic"
+ "go.uber.org/zap/zapcore"
+)
+
+const (
+ // DebugLevel logs are typically voluminous, and are usually disabled in
+ // production.
+ DebugLevel = zapcore.DebugLevel
+ // InfoLevel is the default logging priority.
+ InfoLevel = zapcore.InfoLevel
+ // WarnLevel logs are more important than Info, but don't need individual
+ // human review.
+ WarnLevel = zapcore.WarnLevel
+ // ErrorLevel logs are high-priority. If an application is running smoothly,
+ // it shouldn't generate any error-level logs.
+ ErrorLevel = zapcore.ErrorLevel
+ // DPanicLevel logs are particularly important errors. In development the
+ // logger panics after writing the message.
+ DPanicLevel = zapcore.DPanicLevel
+ // PanicLevel logs a message, then panics.
+ PanicLevel = zapcore.PanicLevel
+ // FatalLevel logs a message, then calls os.Exit(1).
+ FatalLevel = zapcore.FatalLevel
+)
+
+// LevelEnablerFunc is a convenient way to implement zapcore.LevelEnabler with
+// an anonymous function.
+//
+// It's particularly useful when splitting log output between different
+// outputs (e.g., standard error and standard out). For sample code, see the
+// package-level AdvancedConfiguration example.
+type LevelEnablerFunc func(zapcore.Level) bool
+
+// Enabled calls the wrapped function.
+func (f LevelEnablerFunc) Enabled(lvl zapcore.Level) bool { return f(lvl) }
+
+// An AtomicLevel is an atomically changeable, dynamic logging level. It lets
+// you safely change the log level of a tree of loggers (the root logger and
+// any children created by adding context) at runtime.
+//
+// The AtomicLevel itself is an http.Handler that serves a JSON endpoint to
+// alter its level.
+//
+// AtomicLevels must be created with the NewAtomicLevel constructor to allocate
+// their internal atomic pointer.
+type AtomicLevel struct {
+ l *atomic.Int32
+}
+
+// NewAtomicLevel creates an AtomicLevel with InfoLevel and above logging
+// enabled.
+func NewAtomicLevel() AtomicLevel {
+ return AtomicLevel{
+ l: atomic.NewInt32(int32(InfoLevel)),
+ }
+}
+
+// NewAtomicLevelAt is a convienence function that creates an AtomicLevel
+// and then calls SetLevel with the given level.
+func NewAtomicLevelAt(l zapcore.Level) AtomicLevel {
+ a := NewAtomicLevel()
+ a.SetLevel(l)
+ return a
+}
+
+// Enabled implements the zapcore.LevelEnabler interface, which allows the
+// AtomicLevel to be used in place of traditional static levels.
+func (lvl AtomicLevel) Enabled(l zapcore.Level) bool {
+ return lvl.Level().Enabled(l)
+}
+
+// Level returns the minimum enabled log level.
+func (lvl AtomicLevel) Level() zapcore.Level {
+ return zapcore.Level(int8(lvl.l.Load()))
+}
+
+// SetLevel alters the logging level.
+func (lvl AtomicLevel) SetLevel(l zapcore.Level) {
+ lvl.l.Store(int32(l))
+}
+
+// String returns the string representation of the underlying Level.
+func (lvl AtomicLevel) String() string {
+ return lvl.Level().String()
+}
+
+// UnmarshalText unmarshals the text to an AtomicLevel. It uses the same text
+// representations as the static zapcore.Levels ("debug", "info", "warn",
+// "error", "dpanic", "panic", and "fatal").
+func (lvl *AtomicLevel) UnmarshalText(text []byte) error {
+ if lvl.l == nil {
+ lvl.l = &atomic.Int32{}
+ }
+
+ var l zapcore.Level
+ if err := l.UnmarshalText(text); err != nil {
+ return err
+ }
+
+ lvl.SetLevel(l)
+ return nil
+}
+
+// MarshalText marshals the AtomicLevel to a byte slice. It uses the same
+// text representation as the static zapcore.Levels ("debug", "info", "warn",
+// "error", "dpanic", "panic", and "fatal").
+func (lvl AtomicLevel) MarshalText() (text []byte, err error) {
+ return lvl.Level().MarshalText()
+}
diff --git a/vendor/go.uber.org/zap/level_test.go b/vendor/go.uber.org/zap/level_test.go
new file mode 100644
index 0000000..a8fb650
--- /dev/null
+++ b/vendor/go.uber.org/zap/level_test.go
@@ -0,0 +1,117 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "sync"
+ "testing"
+
+ "go.uber.org/zap/zapcore"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestLevelEnablerFunc(t *testing.T) {
+ enab := LevelEnablerFunc(func(l zapcore.Level) bool { return l == zapcore.InfoLevel })
+ tests := []struct {
+ level zapcore.Level
+ enabled bool
+ }{
+ {DebugLevel, false},
+ {InfoLevel, true},
+ {WarnLevel, false},
+ {ErrorLevel, false},
+ {DPanicLevel, false},
+ {PanicLevel, false},
+ {FatalLevel, false},
+ }
+ for _, tt := range tests {
+ assert.Equal(t, tt.enabled, enab.Enabled(tt.level), "Unexpected result applying LevelEnablerFunc to %s", tt.level)
+ }
+}
+
+func TestNewAtomicLevel(t *testing.T) {
+ lvl := NewAtomicLevel()
+ assert.Equal(t, InfoLevel, lvl.Level(), "Unexpected initial level.")
+ lvl.SetLevel(ErrorLevel)
+ assert.Equal(t, ErrorLevel, lvl.Level(), "Unexpected level after SetLevel.")
+ lvl = NewAtomicLevelAt(WarnLevel)
+ assert.Equal(t, WarnLevel, lvl.Level(), "Unexpected level after SetLevel.")
+}
+
+func TestAtomicLevelMutation(t *testing.T) {
+ lvl := NewAtomicLevel()
+ lvl.SetLevel(WarnLevel)
+ // Trigger races for non-atomic level mutations.
+ proceed := make(chan struct{})
+ wg := &sync.WaitGroup{}
+ runConcurrently(10, 100, wg, func() {
+ <-proceed
+ assert.Equal(t, WarnLevel, lvl.Level())
+ })
+ runConcurrently(10, 100, wg, func() {
+ <-proceed
+ lvl.SetLevel(WarnLevel)
+ })
+ close(proceed)
+ wg.Wait()
+}
+
+func TestAtomicLevelText(t *testing.T) {
+ tests := []struct {
+ text string
+ expect zapcore.Level
+ err bool
+ }{
+ {"debug", DebugLevel, false},
+ {"info", InfoLevel, false},
+ {"", InfoLevel, false},
+ {"warn", WarnLevel, false},
+ {"error", ErrorLevel, false},
+ {"dpanic", DPanicLevel, false},
+ {"panic", PanicLevel, false},
+ {"fatal", FatalLevel, false},
+ {"foobar", InfoLevel, true},
+ }
+
+ for _, tt := range tests {
+ var lvl AtomicLevel
+ // Test both initial unmarshaling and overwriting existing value.
+ for i := 0; i < 2; i++ {
+ if tt.err {
+ assert.Error(t, lvl.UnmarshalText([]byte(tt.text)), "Expected unmarshaling %q to fail.", tt.text)
+ } else {
+ assert.NoError(t, lvl.UnmarshalText([]byte(tt.text)), "Expected unmarshaling %q to succeed.", tt.text)
+ }
+ assert.Equal(t, tt.expect, lvl.Level(), "Unexpected level after unmarshaling.")
+ lvl.SetLevel(InfoLevel)
+ }
+
+ // Test marshalling
+ if tt.text != "" && !tt.err {
+ lvl.SetLevel(tt.expect)
+ marshaled, err := lvl.MarshalText()
+ assert.NoError(t, err, `Unexpected error marshalling level "%v" to text.`, tt.expect)
+ assert.Equal(t, tt.text, string(marshaled), "Expected marshaled text to match")
+ assert.Equal(t, tt.text, lvl.String(), "Expected Stringer call to match")
+ }
+ }
+}
diff --git a/vendor/go.uber.org/zap/logger.go b/vendor/go.uber.org/zap/logger.go
new file mode 100644
index 0000000..7d8824b
--- /dev/null
+++ b/vendor/go.uber.org/zap/logger.go
@@ -0,0 +1,305 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "runtime"
+ "strings"
+ "time"
+
+ "go.uber.org/zap/zapcore"
+)
+
+// A Logger provides fast, leveled, structured logging. All methods are safe
+// for concurrent use.
+//
+// The Logger is designed for contexts in which every microsecond and every
+// allocation matters, so its API intentionally favors performance and type
+// safety over brevity. For most applications, the SugaredLogger strikes a
+// better balance between performance and ergonomics.
+type Logger struct {
+ core zapcore.Core
+
+ development bool
+ name string
+ errorOutput zapcore.WriteSyncer
+
+ addCaller bool
+ addStack zapcore.LevelEnabler
+
+ callerSkip int
+}
+
+// New constructs a new Logger from the provided zapcore.Core and Options. If
+// the passed zapcore.Core is nil, it falls back to using a no-op
+// implementation.
+//
+// This is the most flexible way to construct a Logger, but also the most
+// verbose. For typical use cases, the highly-opinionated presets
+// (NewProduction, NewDevelopment, and NewExample) or the Config struct are
+// more convenient.
+//
+// For sample code, see the package-level AdvancedConfiguration example.
+func New(core zapcore.Core, options ...Option) *Logger {
+ if core == nil {
+ return NewNop()
+ }
+ log := &Logger{
+ core: core,
+ errorOutput: zapcore.Lock(os.Stderr),
+ addStack: zapcore.FatalLevel + 1,
+ }
+ return log.WithOptions(options...)
+}
+
+// NewNop returns a no-op Logger. It never writes out logs or internal errors,
+// and it never runs user-defined hooks.
+//
+// Using WithOptions to replace the Core or error output of a no-op Logger can
+// re-enable logging.
+func NewNop() *Logger {
+ return &Logger{
+ core: zapcore.NewNopCore(),
+ errorOutput: zapcore.AddSync(ioutil.Discard),
+ addStack: zapcore.FatalLevel + 1,
+ }
+}
+
+// NewProduction builds a sensible production Logger that writes InfoLevel and
+// above logs to standard error as JSON.
+//
+// It's a shortcut for NewProductionConfig().Build(...Option).
+func NewProduction(options ...Option) (*Logger, error) {
+ return NewProductionConfig().Build(options...)
+}
+
+// NewDevelopment builds a development Logger that writes DebugLevel and above
+// logs to standard error in a human-friendly format.
+//
+// It's a shortcut for NewDevelopmentConfig().Build(...Option).
+func NewDevelopment(options ...Option) (*Logger, error) {
+ return NewDevelopmentConfig().Build(options...)
+}
+
+// NewExample builds a Logger that's designed for use in zap's testable
+// examples. It writes DebugLevel and above logs to standard out as JSON, but
+// omits the timestamp and calling function to keep example output
+// short and deterministic.
+func NewExample(options ...Option) *Logger {
+ encoderCfg := zapcore.EncoderConfig{
+ MessageKey: "msg",
+ LevelKey: "level",
+ NameKey: "logger",
+ EncodeLevel: zapcore.LowercaseLevelEncoder,
+ EncodeTime: zapcore.ISO8601TimeEncoder,
+ EncodeDuration: zapcore.StringDurationEncoder,
+ }
+ core := zapcore.NewCore(zapcore.NewJSONEncoder(encoderCfg), os.Stdout, DebugLevel)
+ return New(core).WithOptions(options...)
+}
+
+// Sugar wraps the Logger to provide a more ergonomic, but slightly slower,
+// API. Sugaring a Logger is quite inexpensive, so it's reasonable for a
+// single application to use both Loggers and SugaredLoggers, converting
+// between them on the boundaries of performance-sensitive code.
+func (log *Logger) Sugar() *SugaredLogger {
+ core := log.clone()
+ core.callerSkip += 2
+ return &SugaredLogger{core}
+}
+
+// Named adds a new path segment to the logger's name. Segments are joined by
+// periods. By default, Loggers are unnamed.
+func (log *Logger) Named(s string) *Logger {
+ if s == "" {
+ return log
+ }
+ l := log.clone()
+ if log.name == "" {
+ l.name = s
+ } else {
+ l.name = strings.Join([]string{l.name, s}, ".")
+ }
+ return l
+}
+
+// WithOptions clones the current Logger, applies the supplied Options, and
+// returns the resulting Logger. It's safe to use concurrently.
+func (log *Logger) WithOptions(opts ...Option) *Logger {
+ c := log.clone()
+ for _, opt := range opts {
+ opt.apply(c)
+ }
+ return c
+}
+
+// With creates a child logger and adds structured context to it. Fields added
+// to the child don't affect the parent, and vice versa.
+func (log *Logger) With(fields ...zapcore.Field) *Logger {
+ if len(fields) == 0 {
+ return log
+ }
+ l := log.clone()
+ l.core = l.core.With(fields)
+ return l
+}
+
+// Check returns a CheckedEntry if logging a message at the specified level
+// is enabled. It's a completely optional optimization; in high-performance
+// applications, Check can help avoid allocating a slice to hold fields.
+func (log *Logger) Check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry {
+ return log.check(lvl, msg)
+}
+
+// Debug logs a message at DebugLevel. The message includes any fields passed
+// at the log site, as well as any fields accumulated on the logger.
+func (log *Logger) Debug(msg string, fields ...zapcore.Field) {
+ if ce := log.check(DebugLevel, msg); ce != nil {
+ ce.Write(fields...)
+ }
+}
+
+// Info logs a message at InfoLevel. The message includes any fields passed
+// at the log site, as well as any fields accumulated on the logger.
+func (log *Logger) Info(msg string, fields ...zapcore.Field) {
+ if ce := log.check(InfoLevel, msg); ce != nil {
+ ce.Write(fields...)
+ }
+}
+
+// Warn logs a message at WarnLevel. The message includes any fields passed
+// at the log site, as well as any fields accumulated on the logger.
+func (log *Logger) Warn(msg string, fields ...zapcore.Field) {
+ if ce := log.check(WarnLevel, msg); ce != nil {
+ ce.Write(fields...)
+ }
+}
+
+// Error logs a message at ErrorLevel. The message includes any fields passed
+// at the log site, as well as any fields accumulated on the logger.
+func (log *Logger) Error(msg string, fields ...zapcore.Field) {
+ if ce := log.check(ErrorLevel, msg); ce != nil {
+ ce.Write(fields...)
+ }
+}
+
+// DPanic logs a message at DPanicLevel. The message includes any fields
+// passed at the log site, as well as any fields accumulated on the logger.
+//
+// If the logger is in development mode, it then panics (DPanic means
+// "development panic"). This is useful for catching errors that are
+// recoverable, but shouldn't ever happen.
+func (log *Logger) DPanic(msg string, fields ...zapcore.Field) {
+ if ce := log.check(DPanicLevel, msg); ce != nil {
+ ce.Write(fields...)
+ }
+}
+
+// Panic logs a message at PanicLevel. The message includes any fields passed
+// at the log site, as well as any fields accumulated on the logger.
+//
+// The logger then panics, even if logging at PanicLevel is disabled.
+func (log *Logger) Panic(msg string, fields ...zapcore.Field) {
+ if ce := log.check(PanicLevel, msg); ce != nil {
+ ce.Write(fields...)
+ }
+}
+
+// Fatal logs a message at FatalLevel. The message includes any fields passed
+// at the log site, as well as any fields accumulated on the logger.
+//
+// The logger then calls os.Exit(1), even if logging at FatalLevel is
+// disabled.
+func (log *Logger) Fatal(msg string, fields ...zapcore.Field) {
+ if ce := log.check(FatalLevel, msg); ce != nil {
+ ce.Write(fields...)
+ }
+}
+
+// Sync calls the underlying Core's Sync method, flushing any buffered log
+// entries. Applications should take care to call Sync before exiting.
+func (log *Logger) Sync() error {
+ return log.core.Sync()
+}
+
+// Core returns the Logger's underlying zapcore.Core.
+func (log *Logger) Core() zapcore.Core {
+ return log.core
+}
+
+func (log *Logger) clone() *Logger {
+ copy := *log
+ return ©
+}
+
+func (log *Logger) check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry {
+ // check must always be called directly by a method in the Logger interface
+ // (e.g., Check, Info, Fatal).
+ const callerSkipOffset = 2
+
+ // Create basic checked entry thru the core; this will be non-nil if the
+ // log message will actually be written somewhere.
+ ent := zapcore.Entry{
+ LoggerName: log.name,
+ Time: time.Now(),
+ Level: lvl,
+ Message: msg,
+ }
+ ce := log.core.Check(ent, nil)
+ willWrite := ce != nil
+
+ // Set up any required terminal behavior.
+ switch ent.Level {
+ case zapcore.PanicLevel:
+ ce = ce.Should(ent, zapcore.WriteThenPanic)
+ case zapcore.FatalLevel:
+ ce = ce.Should(ent, zapcore.WriteThenFatal)
+ case zapcore.DPanicLevel:
+ if log.development {
+ ce = ce.Should(ent, zapcore.WriteThenPanic)
+ }
+ }
+
+ // Only do further annotation if we're going to write this message; checked
+ // entries that exist only for terminal behavior don't benefit from
+ // annotation.
+ if !willWrite {
+ return ce
+ }
+
+ // Thread the error output through to the CheckedEntry.
+ ce.ErrorOutput = log.errorOutput
+ if log.addCaller {
+ ce.Entry.Caller = zapcore.NewEntryCaller(runtime.Caller(log.callerSkip + callerSkipOffset))
+ if !ce.Entry.Caller.Defined {
+ fmt.Fprintf(log.errorOutput, "%v Logger.check error: failed to get caller\n", time.Now().UTC())
+ log.errorOutput.Sync()
+ }
+ }
+ if log.addStack.Enabled(ce.Entry.Level) {
+ ce.Entry.Stack = Stack("").String
+ }
+
+ return ce
+}
diff --git a/vendor/go.uber.org/zap/logger_bench_test.go b/vendor/go.uber.org/zap/logger_bench_test.go
new file mode 100644
index 0000000..1ba7c75
--- /dev/null
+++ b/vendor/go.uber.org/zap/logger_bench_test.go
@@ -0,0 +1,222 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "errors"
+ "testing"
+ "time"
+
+ "go.uber.org/zap/zapcore"
+ "go.uber.org/zap/zaptest"
+)
+
+type user struct {
+ Name string
+ Email string
+ CreatedAt time.Time
+}
+
+func (u *user) MarshalLogObject(enc zapcore.ObjectEncoder) error {
+ enc.AddString("name", u.Name)
+ enc.AddString("email", u.Email)
+ enc.AddInt64("created_at", u.CreatedAt.UnixNano())
+ return nil
+}
+
+var _jane = &user{
+ Name: "Jane Doe",
+ Email: "jane@test.com",
+ CreatedAt: time.Date(1980, 1, 1, 12, 0, 0, 0, time.UTC),
+}
+
+func withBenchedLogger(b *testing.B, f func(*Logger)) {
+ logger := New(
+ zapcore.NewCore(
+ zapcore.NewJSONEncoder(NewProductionConfig().EncoderConfig),
+ &zaptest.Discarder{},
+ DebugLevel,
+ ))
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ f(logger)
+ }
+ })
+}
+
+func BenchmarkNoContext(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("No context.")
+ })
+}
+
+func BenchmarkBoolField(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("Boolean.", Bool("foo", true))
+ })
+}
+
+func BenchmarkByteStringField(b *testing.B) {
+ val := []byte("bar")
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("ByteString.", ByteString("foo", val))
+ })
+}
+
+func BenchmarkFloat64Field(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("Floating point.", Float64("foo", 3.14))
+ })
+}
+
+func BenchmarkIntField(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("Integer.", Int("foo", 42))
+ })
+}
+
+func BenchmarkInt64Field(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("64-bit integer.", Int64("foo", 42))
+ })
+}
+
+func BenchmarkStringField(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("Strings.", String("foo", "bar"))
+ })
+}
+
+func BenchmarkStringerField(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("Level.", Stringer("foo", InfoLevel))
+ })
+}
+
+func BenchmarkTimeField(b *testing.B) {
+ t := time.Unix(0, 0)
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("Time.", Time("foo", t))
+ })
+}
+
+func BenchmarkDurationField(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("Duration", Duration("foo", time.Second))
+ })
+}
+
+func BenchmarkErrorField(b *testing.B) {
+ err := errors.New("egad")
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("Error.", Error(err))
+ })
+}
+
+func BenchmarkErrorsField(b *testing.B) {
+ errs := []error{
+ errors.New("egad"),
+ errors.New("oh no"),
+ errors.New("dear me"),
+ errors.New("such fail"),
+ }
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("Errors.", Errors("errors", errs))
+ })
+}
+
+func BenchmarkStackField(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("Error.", Stack("stacktrace"))
+ })
+}
+
+func BenchmarkObjectField(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("Arbitrary ObjectMarshaler.", Object("user", _jane))
+ })
+}
+
+func BenchmarkReflectField(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("Reflection-based serialization.", Reflect("user", _jane))
+ })
+}
+
+func BenchmarkAddCallerHook(b *testing.B) {
+ logger := New(
+ zapcore.NewCore(
+ zapcore.NewJSONEncoder(NewProductionConfig().EncoderConfig),
+ &zaptest.Discarder{},
+ InfoLevel,
+ ),
+ AddCaller(),
+ )
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ logger.Info("Caller.")
+ }
+ })
+}
+
+func Benchmark10Fields(b *testing.B) {
+ withBenchedLogger(b, func(log *Logger) {
+ log.Info("Ten fields, passed at the log site.",
+ Int("one", 1),
+ Int("two", 2),
+ Int("three", 3),
+ Int("four", 4),
+ Int("five", 5),
+ Int("six", 6),
+ Int("seven", 7),
+ Int("eight", 8),
+ Int("nine", 9),
+ Int("ten", 10),
+ )
+ })
+}
+
+func Benchmark100Fields(b *testing.B) {
+ const batchSize = 50
+ logger := New(zapcore.NewCore(
+ zapcore.NewJSONEncoder(NewProductionConfig().EncoderConfig),
+ &zaptest.Discarder{},
+ DebugLevel,
+ ))
+
+ // Don't include allocating these helper slices in the benchmark. Since
+ // access to them isn't synchronized, we can't run the benchmark in
+ // parallel.
+ first := make([]zapcore.Field, batchSize)
+ second := make([]zapcore.Field, batchSize)
+ b.ResetTimer()
+
+ for i := 0; i < b.N; i++ {
+ for i := 0; i < batchSize; i++ {
+ // We're duplicating keys, but that doesn't affect performance.
+ first[i] = Int("foo", i)
+ second[i] = Int("foo", i+batchSize)
+ }
+ logger.With(first...).Info("Child loggers with lots of context.", second...)
+ }
+}
diff --git a/vendor/go.uber.org/zap/logger_test.go b/vendor/go.uber.org/zap/logger_test.go
new file mode 100644
index 0000000..edd8950
--- /dev/null
+++ b/vendor/go.uber.org/zap/logger_test.go
@@ -0,0 +1,452 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "errors"
+ "sync"
+ "testing"
+
+ "go.uber.org/zap/internal/exit"
+ "go.uber.org/zap/zapcore"
+ "go.uber.org/zap/zaptest"
+ "go.uber.org/zap/zaptest/observer"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "go.uber.org/atomic"
+)
+
+func makeCountingHook() (func(zapcore.Entry) error, *atomic.Int64) {
+ count := &atomic.Int64{}
+ h := func(zapcore.Entry) error {
+ count.Inc()
+ return nil
+ }
+ return h, count
+}
+
+func TestLoggerAtomicLevel(t *testing.T) {
+ // Test that the dynamic level applies to all ancestors and descendants.
+ dl := NewAtomicLevel()
+
+ withLogger(t, dl, nil, func(grandparent *Logger, _ *observer.ObservedLogs) {
+ parent := grandparent.With(Int("generation", 1))
+ child := parent.With(Int("generation", 2))
+
+ tests := []struct {
+ setLevel zapcore.Level
+ testLevel zapcore.Level
+ enabled bool
+ }{
+ {DebugLevel, DebugLevel, true},
+ {InfoLevel, DebugLevel, false},
+ {WarnLevel, PanicLevel, true},
+ }
+
+ for _, tt := range tests {
+ dl.SetLevel(tt.setLevel)
+ for _, logger := range []*Logger{grandparent, parent, child} {
+ if tt.enabled {
+ assert.NotNil(
+ t,
+ logger.Check(tt.testLevel, ""),
+ "Expected level %s to be enabled after setting level %s.", tt.testLevel, tt.setLevel,
+ )
+ } else {
+ assert.Nil(
+ t,
+ logger.Check(tt.testLevel, ""),
+ "Expected level %s to be enabled after setting level %s.", tt.testLevel, tt.setLevel,
+ )
+ }
+ }
+ }
+ })
+}
+
+func TestLoggerInitialFields(t *testing.T) {
+ fieldOpts := opts(Fields(Int("foo", 42), String("bar", "baz")))
+ withLogger(t, DebugLevel, fieldOpts, func(logger *Logger, logs *observer.ObservedLogs) {
+ logger.Info("")
+ assert.Equal(
+ t,
+ observer.LoggedEntry{Context: []zapcore.Field{Int("foo", 42), String("bar", "baz")}},
+ logs.AllUntimed()[0],
+ "Unexpected output with initial fields set.",
+ )
+ })
+}
+
+func TestLoggerWith(t *testing.T) {
+ fieldOpts := opts(Fields(Int("foo", 42)))
+ withLogger(t, DebugLevel, fieldOpts, func(logger *Logger, logs *observer.ObservedLogs) {
+ // Child loggers should have copy-on-write semantics, so two children
+ // shouldn't stomp on each other's fields or affect the parent's fields.
+ logger.With(String("one", "two")).Info("")
+ logger.With(String("three", "four")).Info("")
+ logger.Info("")
+
+ assert.Equal(t, []observer.LoggedEntry{
+ {Context: []zapcore.Field{Int("foo", 42), String("one", "two")}},
+ {Context: []zapcore.Field{Int("foo", 42), String("three", "four")}},
+ {Context: []zapcore.Field{Int("foo", 42)}},
+ }, logs.AllUntimed(), "Unexpected cross-talk between child loggers.")
+ })
+}
+
+func TestLoggerLogPanic(t *testing.T) {
+ for _, tt := range []struct {
+ do func(*Logger)
+ should bool
+ expected string
+ }{
+ {func(logger *Logger) { logger.Check(PanicLevel, "bar").Write() }, true, "bar"},
+ {func(logger *Logger) { logger.Panic("baz") }, true, "baz"},
+ } {
+ withLogger(t, DebugLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) {
+ if tt.should {
+ assert.Panics(t, func() { tt.do(logger) }, "Expected panic")
+ } else {
+ assert.NotPanics(t, func() { tt.do(logger) }, "Expected no panic")
+ }
+
+ output := logs.AllUntimed()
+ assert.Equal(t, 1, len(output), "Unexpected number of logs.")
+ assert.Equal(t, 0, len(output[0].Context), "Unexpected context on first log.")
+ assert.Equal(
+ t,
+ zapcore.Entry{Message: tt.expected, Level: PanicLevel},
+ output[0].Entry,
+ "Unexpected output from panic-level Log.",
+ )
+ })
+ }
+}
+
+func TestLoggerLogFatal(t *testing.T) {
+ for _, tt := range []struct {
+ do func(*Logger)
+ expected string
+ }{
+ {func(logger *Logger) { logger.Check(FatalLevel, "bar").Write() }, "bar"},
+ {func(logger *Logger) { logger.Fatal("baz") }, "baz"},
+ } {
+ withLogger(t, DebugLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) {
+ stub := exit.WithStub(func() {
+ tt.do(logger)
+ })
+ assert.True(t, stub.Exited, "Expected Fatal logger call to terminate process.")
+ output := logs.AllUntimed()
+ assert.Equal(t, 1, len(output), "Unexpected number of logs.")
+ assert.Equal(t, 0, len(output[0].Context), "Unexpected context on first log.")
+ assert.Equal(
+ t,
+ zapcore.Entry{Message: tt.expected, Level: FatalLevel},
+ output[0].Entry,
+ "Unexpected output from fatal-level Log.",
+ )
+ })
+ }
+}
+
+func TestLoggerLeveledMethods(t *testing.T) {
+ withLogger(t, DebugLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) {
+ tests := []struct {
+ method func(string, ...zapcore.Field)
+ expectedLevel zapcore.Level
+ }{
+ {logger.Debug, DebugLevel},
+ {logger.Info, InfoLevel},
+ {logger.Warn, WarnLevel},
+ {logger.Error, ErrorLevel},
+ {logger.DPanic, DPanicLevel},
+ }
+ for i, tt := range tests {
+ tt.method("")
+ output := logs.AllUntimed()
+ assert.Equal(t, i+1, len(output), "Unexpected number of logs.")
+ assert.Equal(t, 0, len(output[i].Context), "Unexpected context on first log.")
+ assert.Equal(
+ t,
+ zapcore.Entry{Level: tt.expectedLevel},
+ output[i].Entry,
+ "Unexpected output from %s-level logger method.", tt.expectedLevel)
+ }
+ })
+}
+
+func TestLoggerAlwaysPanics(t *testing.T) {
+ // Users can disable writing out panic-level logs, but calls to logger.Panic()
+ // should still call panic().
+ withLogger(t, FatalLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) {
+ msg := "Even if output is disabled, logger.Panic should always panic."
+ assert.Panics(t, func() { logger.Panic("foo") }, msg)
+ assert.Panics(t, func() {
+ if ce := logger.Check(PanicLevel, "foo"); ce != nil {
+ ce.Write()
+ }
+ }, msg)
+ assert.Equal(t, 0, logs.Len(), "Panics shouldn't be written out if PanicLevel is disabled.")
+ })
+}
+
+func TestLoggerAlwaysFatals(t *testing.T) {
+ // Users can disable writing out fatal-level logs, but calls to logger.Fatal()
+ // should still terminate the process.
+ withLogger(t, FatalLevel+1, nil, func(logger *Logger, logs *observer.ObservedLogs) {
+ stub := exit.WithStub(func() { logger.Fatal("") })
+ assert.True(t, stub.Exited, "Expected calls to logger.Fatal to terminate process.")
+
+ stub = exit.WithStub(func() {
+ if ce := logger.Check(FatalLevel, ""); ce != nil {
+ ce.Write()
+ }
+ })
+ assert.True(t, stub.Exited, "Expected calls to logger.Check(FatalLevel, ...) to terminate process.")
+
+ assert.Equal(t, 0, logs.Len(), "Shouldn't write out logs when fatal-level logging is disabled.")
+ })
+}
+
+func TestLoggerDPanic(t *testing.T) {
+ withLogger(t, DebugLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) {
+ assert.NotPanics(t, func() { logger.DPanic("") })
+ assert.Equal(
+ t,
+ []observer.LoggedEntry{{Entry: zapcore.Entry{Level: DPanicLevel}, Context: []zapcore.Field{}}},
+ logs.AllUntimed(),
+ "Unexpected log output from DPanic in production mode.",
+ )
+ })
+ withLogger(t, DebugLevel, opts(Development()), func(logger *Logger, logs *observer.ObservedLogs) {
+ assert.Panics(t, func() { logger.DPanic("") })
+ assert.Equal(
+ t,
+ []observer.LoggedEntry{{Entry: zapcore.Entry{Level: DPanicLevel}, Context: []zapcore.Field{}}},
+ logs.AllUntimed(),
+ "Unexpected log output from DPanic in development mode.",
+ )
+ })
+}
+
+func TestLoggerNoOpsDisabledLevels(t *testing.T) {
+ withLogger(t, WarnLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) {
+ logger.Info("silence!")
+ assert.Equal(
+ t,
+ []observer.LoggedEntry{},
+ logs.AllUntimed(),
+ "Expected logging at a disabled level to produce no output.",
+ )
+ })
+}
+
+func TestLoggerNames(t *testing.T) {
+ tests := []struct {
+ names []string
+ expected string
+ }{
+ {nil, ""},
+ {[]string{""}, ""},
+ {[]string{"foo"}, "foo"},
+ {[]string{"foo", ""}, "foo"},
+ {[]string{"foo", "bar"}, "foo.bar"},
+ {[]string{"foo.bar", "baz"}, "foo.bar.baz"},
+ // Garbage in, garbage out.
+ {[]string{"foo.", "bar"}, "foo..bar"},
+ {[]string{"foo", ".bar"}, "foo..bar"},
+ {[]string{"foo.", ".bar"}, "foo...bar"},
+ }
+
+ for _, tt := range tests {
+ withLogger(t, DebugLevel, nil, func(log *Logger, logs *observer.ObservedLogs) {
+ for _, n := range tt.names {
+ log = log.Named(n)
+ }
+ log.Info("")
+ require.Equal(t, 1, logs.Len(), "Expected only one log entry to be written.")
+ assert.Equal(t, tt.expected, logs.AllUntimed()[0].Entry.LoggerName, "Unexpected logger name.")
+ })
+ withSugar(t, DebugLevel, nil, func(log *SugaredLogger, logs *observer.ObservedLogs) {
+ for _, n := range tt.names {
+ log = log.Named(n)
+ }
+ log.Infow("")
+ require.Equal(t, 1, logs.Len(), "Expected only one log entry to be written.")
+ assert.Equal(t, tt.expected, logs.AllUntimed()[0].Entry.LoggerName, "Unexpected logger name.")
+ })
+ }
+}
+
+func TestLoggerWriteFailure(t *testing.T) {
+ errSink := &zaptest.Buffer{}
+ logger := New(
+ zapcore.NewCore(
+ zapcore.NewJSONEncoder(NewProductionConfig().EncoderConfig),
+ zapcore.Lock(zapcore.AddSync(zaptest.FailWriter{})),
+ DebugLevel,
+ ),
+ ErrorOutput(errSink),
+ )
+
+ logger.Info("foo")
+ // Should log the error.
+ assert.Regexp(t, `write error: failed`, errSink.Stripped(), "Expected to log the error to the error output.")
+ assert.True(t, errSink.Called(), "Expected logging an internal error to call Sync the error sink.")
+}
+
+func TestLoggerSync(t *testing.T) {
+ withLogger(t, DebugLevel, nil, func(logger *Logger, _ *observer.ObservedLogs) {
+ assert.NoError(t, logger.Sync(), "Expected syncing a test logger to succeed.")
+ assert.NoError(t, logger.Sugar().Sync(), "Expected syncing a sugared logger to succeed.")
+ })
+}
+
+func TestLoggerSyncFail(t *testing.T) {
+ noSync := &zaptest.Buffer{}
+ err := errors.New("fail")
+ noSync.SetError(err)
+ logger := New(zapcore.NewCore(
+ zapcore.NewJSONEncoder(zapcore.EncoderConfig{}),
+ noSync,
+ DebugLevel,
+ ))
+ assert.Equal(t, err, logger.Sync(), "Expected Logger.Sync to propagate errors.")
+ assert.Equal(t, err, logger.Sugar().Sync(), "Expected SugaredLogger.Sync to propagate errors.")
+}
+
+func TestLoggerAddCaller(t *testing.T) {
+ tests := []struct {
+ options []Option
+ pat string
+ }{
+ {opts(AddCaller()), `.+/logger_test.go:[\d]+$`},
+ {opts(AddCaller(), AddCallerSkip(1), AddCallerSkip(-1)), `.+/zap/logger_test.go:[\d]+$`},
+ {opts(AddCaller(), AddCallerSkip(1)), `.+/zap/common_test.go:[\d]+$`},
+ {opts(AddCaller(), AddCallerSkip(1), AddCallerSkip(3)), `.+/src/runtime/.*:[\d]+$`},
+ }
+ for _, tt := range tests {
+ withLogger(t, DebugLevel, tt.options, func(logger *Logger, logs *observer.ObservedLogs) {
+ // Make sure that sugaring and desugaring resets caller skip properly.
+ logger = logger.Sugar().Desugar()
+ logger.Info("")
+ output := logs.AllUntimed()
+ assert.Equal(t, 1, len(output), "Unexpected number of logs written out.")
+ assert.Regexp(
+ t,
+ tt.pat,
+ output[0].Entry.Caller,
+ "Expected to find package name and file name in output.",
+ )
+ })
+ }
+}
+
+func TestLoggerAddCallerFail(t *testing.T) {
+ errBuf := &zaptest.Buffer{}
+ withLogger(t, DebugLevel, opts(AddCaller(), ErrorOutput(errBuf)), func(log *Logger, logs *observer.ObservedLogs) {
+ log.callerSkip = 1e3
+ log.Info("Failure.")
+ assert.Regexp(
+ t,
+ `Logger.check error: failed to get caller`,
+ errBuf.String(),
+ "Didn't find expected failure message.",
+ )
+ assert.Equal(
+ t,
+ logs.AllUntimed()[0].Entry.Message,
+ "Failure.",
+ "Expected original message to survive failures in runtime.Caller.")
+ })
+}
+
+func TestLoggerAddStacktrace(t *testing.T) {
+ assertHasStack := func(t testing.TB, obs observer.LoggedEntry) {
+ assert.Contains(t, obs.Entry.Stack, "zap.TestLoggerAddStacktrace", "Expected to find test function in stacktrace.")
+ }
+ withLogger(t, DebugLevel, opts(AddStacktrace(InfoLevel)), func(logger *Logger, logs *observer.ObservedLogs) {
+ logger.Debug("")
+ assert.Empty(
+ t,
+ logs.AllUntimed()[0].Entry.Stack,
+ "Unexpected stacktrack at DebugLevel.",
+ )
+
+ logger.Info("")
+ assertHasStack(t, logs.AllUntimed()[1])
+
+ logger.Warn("")
+ assertHasStack(t, logs.AllUntimed()[2])
+ })
+}
+
+func TestLoggerReplaceCore(t *testing.T) {
+ replace := WrapCore(func(zapcore.Core) zapcore.Core {
+ return zapcore.NewNopCore()
+ })
+ withLogger(t, DebugLevel, opts(replace), func(logger *Logger, logs *observer.ObservedLogs) {
+ logger.Debug("")
+ logger.Info("")
+ logger.Warn("")
+ assert.Equal(t, 0, logs.Len(), "Expected no-op core to write no logs.")
+ })
+}
+
+func TestLoggerHooks(t *testing.T) {
+ hook, seen := makeCountingHook()
+ withLogger(t, DebugLevel, opts(Hooks(hook)), func(logger *Logger, logs *observer.ObservedLogs) {
+ logger.Debug("")
+ logger.Info("")
+ })
+ assert.Equal(t, int64(2), seen.Load(), "Hook saw an unexpected number of logs.")
+}
+
+func TestLoggerConcurrent(t *testing.T) {
+ withLogger(t, DebugLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) {
+ child := logger.With(String("foo", "bar"))
+
+ wg := &sync.WaitGroup{}
+ runConcurrently(5, 10, wg, func() {
+ logger.Info("", String("foo", "bar"))
+ })
+ runConcurrently(5, 10, wg, func() {
+ child.Info("")
+ })
+
+ wg.Wait()
+
+ // Make sure the output doesn't contain interspersed entries.
+ assert.Equal(t, 100, logs.Len(), "Unexpected number of logs written out.")
+ for _, obs := range logs.AllUntimed() {
+ assert.Equal(
+ t,
+ observer.LoggedEntry{
+ Entry: zapcore.Entry{Level: InfoLevel},
+ Context: []zapcore.Field{String("foo", "bar")},
+ },
+ obs,
+ "Unexpected log output.",
+ )
+ }
+ })
+}
diff --git a/vendor/go.uber.org/zap/options.go b/vendor/go.uber.org/zap/options.go
new file mode 100644
index 0000000..d0f9422
--- /dev/null
+++ b/vendor/go.uber.org/zap/options.go
@@ -0,0 +1,109 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import "go.uber.org/zap/zapcore"
+
+// An Option configures a Logger.
+type Option interface {
+ apply(*Logger)
+}
+
+// optionFunc wraps a func so it satisfies the Option interface.
+type optionFunc func(*Logger)
+
+func (f optionFunc) apply(log *Logger) {
+ f(log)
+}
+
+// WrapCore wraps or replaces the Logger's underlying zapcore.Core.
+func WrapCore(f func(zapcore.Core) zapcore.Core) Option {
+ return optionFunc(func(log *Logger) {
+ log.core = f(log.core)
+ })
+}
+
+// Hooks registers functions which will be called each time the Logger writes
+// out an Entry. Repeated use of Hooks is additive.
+//
+// Hooks are useful for simple side effects, like capturing metrics for the
+// number of emitted logs. More complex side effects, including anything that
+// requires access to the Entry's structured fields, should be implemented as
+// a zapcore.Core instead. See zapcore.RegisterHooks for details.
+func Hooks(hooks ...func(zapcore.Entry) error) Option {
+ return optionFunc(func(log *Logger) {
+ log.core = zapcore.RegisterHooks(log.core, hooks...)
+ })
+}
+
+// Fields adds fields to the Logger.
+func Fields(fs ...zapcore.Field) Option {
+ return optionFunc(func(log *Logger) {
+ log.core = log.core.With(fs)
+ })
+}
+
+// ErrorOutput sets the destination for errors generated by the Logger. Note
+// that this option only affects internal errors; for sample code that sends
+// error-level logs to a different location from info- and debug-level logs,
+// see the package-level AdvancedConfiguration example.
+//
+// The supplied WriteSyncer must be safe for concurrent use. The Open and
+// zapcore.Lock functions are the simplest ways to protect files with a mutex.
+func ErrorOutput(w zapcore.WriteSyncer) Option {
+ return optionFunc(func(log *Logger) {
+ log.errorOutput = w
+ })
+}
+
+// Development puts the logger in development mode, which makes DPanic-level
+// logs panic instead of simply logging an error.
+func Development() Option {
+ return optionFunc(func(log *Logger) {
+ log.development = true
+ })
+}
+
+// AddCaller configures the Logger to annotate each message with the filename
+// and line number of zap's caller.
+func AddCaller() Option {
+ return optionFunc(func(log *Logger) {
+ log.addCaller = true
+ })
+}
+
+// AddCallerSkip increases the number of callers skipped by caller annotation
+// (as enabled by the AddCaller option). When building wrappers around the
+// Logger and SugaredLogger, supplying this Option prevents zap from always
+// reporting the wrapper code as the caller.
+func AddCallerSkip(skip int) Option {
+ return optionFunc(func(log *Logger) {
+ log.callerSkip += skip
+ })
+}
+
+// AddStacktrace configures the Logger to record a stack trace for all messages at
+// or above a given level.
+func AddStacktrace(lvl zapcore.LevelEnabler) Option {
+ return optionFunc(func(log *Logger) {
+ log.addStack = lvl
+ })
+}
diff --git a/vendor/go.uber.org/zap/scripts/cover.sh b/vendor/go.uber.org/zap/scripts/cover.sh
new file mode 100755
index 0000000..0da503c
--- /dev/null
+++ b/vendor/go.uber.org/zap/scripts/cover.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+set -e
+echo "" > cover.out
+
+for d in $(go list $@); do
+ go test -race -coverprofile=profile.out $d
+ if [ -f profile.out ]; then
+ cat profile.out >> cover.out
+ rm profile.out
+ fi
+done
diff --git a/vendor/go.uber.org/zap/stacktrace.go b/vendor/go.uber.org/zap/stacktrace.go
new file mode 100644
index 0000000..d2dc7cc
--- /dev/null
+++ b/vendor/go.uber.org/zap/stacktrace.go
@@ -0,0 +1,98 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "runtime"
+ "strings"
+ "sync"
+
+ "go.uber.org/zap/internal/bufferpool"
+)
+
+var (
+ _stacktraceIgnorePrefixes = []string{
+ "runtime.goexit",
+ "runtime.main",
+ }
+ _stacktracePool = sync.Pool{
+ New: func() interface{} {
+ return newProgramCounters(64)
+ },
+ }
+)
+
+func takeStacktrace() string {
+ buffer := bufferpool.Get()
+ defer buffer.Free()
+ programCounters := _stacktracePool.Get().(*programCounters)
+ defer _stacktracePool.Put(programCounters)
+
+ var numFrames int
+ for {
+ // Skip the call to runtime.Counters and takeStacktrace so that the
+ // program counters start at the caller of takeStacktrace.
+ numFrames = runtime.Callers(2, programCounters.pcs)
+ if numFrames < len(programCounters.pcs) {
+ break
+ }
+ // Don't put the too-short counter slice back into the pool; this lets
+ // the pool adjust if we consistently take deep stacktraces.
+ programCounters = newProgramCounters(len(programCounters.pcs) * 2)
+ }
+
+ i := 0
+ frames := runtime.CallersFrames(programCounters.pcs[:numFrames])
+ for frame, more := frames.Next(); more; frame, more = frames.Next() {
+ if shouldIgnoreStacktraceFunction(frame.Function) {
+ continue
+ }
+ if i != 0 {
+ buffer.AppendByte('\n')
+ }
+ i++
+ buffer.AppendString(frame.Function)
+ buffer.AppendByte('\n')
+ buffer.AppendByte('\t')
+ buffer.AppendString(frame.File)
+ buffer.AppendByte(':')
+ buffer.AppendInt(int64(frame.Line))
+ }
+
+ return buffer.String()
+}
+
+func shouldIgnoreStacktraceFunction(function string) bool {
+ for _, prefix := range _stacktraceIgnorePrefixes {
+ if strings.HasPrefix(function, prefix) {
+ return true
+ }
+ }
+ return false
+}
+
+type programCounters struct {
+ pcs []uintptr
+}
+
+func newProgramCounters(size int) *programCounters {
+ return &programCounters{make([]uintptr, size)}
+}
diff --git a/vendor/go.uber.org/zap/stacktrace_test.go b/vendor/go.uber.org/zap/stacktrace_test.go
new file mode 100644
index 0000000..1d53576
--- /dev/null
+++ b/vendor/go.uber.org/zap/stacktrace_test.go
@@ -0,0 +1,41 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestTakeStacktrace(t *testing.T) {
+ trace := takeStacktrace()
+ lines := strings.Split(trace, "\n")
+ require.True(t, len(lines) > 0, "Expected stacktrace to have at least one frame.")
+ assert.Contains(
+ t,
+ lines[0],
+ "TestTakeStacktrace",
+ "Expected stacktrace to start with this test function, but top frame is %s.", lines[0],
+ )
+}
diff --git a/vendor/go.uber.org/zap/sugar.go b/vendor/go.uber.org/zap/sugar.go
new file mode 100644
index 0000000..9cda009
--- /dev/null
+++ b/vendor/go.uber.org/zap/sugar.go
@@ -0,0 +1,304 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "fmt"
+
+ "go.uber.org/zap/zapcore"
+
+ "go.uber.org/multierr"
+)
+
+const (
+ _oddNumberErrMsg = "Ignored key without a value."
+ _nonStringKeyErrMsg = "Ignored key-value pairs with non-string keys."
+)
+
+// A SugaredLogger wraps the base Logger functionality in a slower, but less
+// verbose, API. Any Logger can be converted to a SugaredLogger with its Sugar
+// method.
+//
+// Unlike the Logger, the SugaredLogger doesn't insist on structured logging.
+// For each log level, it exposes three methods: one for loosely-typed
+// structured logging, one for println-style formatting, and one for
+// printf-style formatting. For example, SugaredLoggers can produce InfoLevel
+// output with Infow ("info with" structured context), Info, or Infof.
+type SugaredLogger struct {
+ base *Logger
+}
+
+// Desugar unwraps a SugaredLogger, exposing the original Logger. Desugaring
+// is quite inexpensive, so it's reasonable for a single application to use
+// both Loggers and SugaredLoggers, converting between them on the boundaries
+// of performance-sensitive code.
+func (s *SugaredLogger) Desugar() *Logger {
+ base := s.base.clone()
+ base.callerSkip -= 2
+ return base
+}
+
+// Named adds a sub-scope to the logger's name. See Logger.Named for details.
+func (s *SugaredLogger) Named(name string) *SugaredLogger {
+ return &SugaredLogger{base: s.base.Named(name)}
+}
+
+// With adds a variadic number of fields to the logging context. It accepts a
+// mix of strongly-typed zapcore.Field objects and loosely-typed key-value
+// pairs. When processing pairs, the first element of the pair is used as the
+// field key and the second as the field value.
+//
+// For example,
+// sugaredLogger.With(
+// "hello", "world",
+// "failure", errors.New("oh no"),
+// Stack(),
+// "count", 42,
+// "user", User{Name: "alice"},
+// )
+// is the equivalent of
+// unsugared.With(
+// String("hello", "world"),
+// String("failure", "oh no"),
+// Stack(),
+// Int("count", 42),
+// Object("user", User{Name: "alice"}),
+// )
+//
+// Note that the keys in key-value pairs should be strings. In development,
+// passing a non-string key panics. In production, the logger is more
+// forgiving: a separate error is logged, but the key-value pair is skipped
+// and execution continues. Passing an orphaned key triggers similar behavior:
+// panics in development and errors in production.
+func (s *SugaredLogger) With(args ...interface{}) *SugaredLogger {
+ return &SugaredLogger{base: s.base.With(s.sweetenFields(args)...)}
+}
+
+// Debug uses fmt.Sprint to construct and log a message.
+func (s *SugaredLogger) Debug(args ...interface{}) {
+ s.log(DebugLevel, "", args, nil)
+}
+
+// Info uses fmt.Sprint to construct and log a message.
+func (s *SugaredLogger) Info(args ...interface{}) {
+ s.log(InfoLevel, "", args, nil)
+}
+
+// Warn uses fmt.Sprint to construct and log a message.
+func (s *SugaredLogger) Warn(args ...interface{}) {
+ s.log(WarnLevel, "", args, nil)
+}
+
+// Error uses fmt.Sprint to construct and log a message.
+func (s *SugaredLogger) Error(args ...interface{}) {
+ s.log(ErrorLevel, "", args, nil)
+}
+
+// DPanic uses fmt.Sprint to construct and log a message. In development, the
+// logger then panics. (See DPanicLevel for details.)
+func (s *SugaredLogger) DPanic(args ...interface{}) {
+ s.log(DPanicLevel, "", args, nil)
+}
+
+// Panic uses fmt.Sprint to construct and log a message, then panics.
+func (s *SugaredLogger) Panic(args ...interface{}) {
+ s.log(PanicLevel, "", args, nil)
+}
+
+// Fatal uses fmt.Sprint to construct and log a message, then calls os.Exit.
+func (s *SugaredLogger) Fatal(args ...interface{}) {
+ s.log(FatalLevel, "", args, nil)
+}
+
+// Debugf uses fmt.Sprintf to log a templated message.
+func (s *SugaredLogger) Debugf(template string, args ...interface{}) {
+ s.log(DebugLevel, template, args, nil)
+}
+
+// Infof uses fmt.Sprintf to log a templated message.
+func (s *SugaredLogger) Infof(template string, args ...interface{}) {
+ s.log(InfoLevel, template, args, nil)
+}
+
+// Warnf uses fmt.Sprintf to log a templated message.
+func (s *SugaredLogger) Warnf(template string, args ...interface{}) {
+ s.log(WarnLevel, template, args, nil)
+}
+
+// Errorf uses fmt.Sprintf to log a templated message.
+func (s *SugaredLogger) Errorf(template string, args ...interface{}) {
+ s.log(ErrorLevel, template, args, nil)
+}
+
+// DPanicf uses fmt.Sprintf to log a templated message. In development, the
+// logger then panics. (See DPanicLevel for details.)
+func (s *SugaredLogger) DPanicf(template string, args ...interface{}) {
+ s.log(DPanicLevel, template, args, nil)
+}
+
+// Panicf uses fmt.Sprintf to log a templated message, then panics.
+func (s *SugaredLogger) Panicf(template string, args ...interface{}) {
+ s.log(PanicLevel, template, args, nil)
+}
+
+// Fatalf uses fmt.Sprintf to log a templated message, then calls os.Exit.
+func (s *SugaredLogger) Fatalf(template string, args ...interface{}) {
+ s.log(FatalLevel, template, args, nil)
+}
+
+// Debugw logs a message with some additional context. The variadic key-value
+// pairs are treated as they are in With.
+//
+// When debug-level logging is disabled, this is much faster than
+// s.With(keysAndValues).Debug(msg)
+func (s *SugaredLogger) Debugw(msg string, keysAndValues ...interface{}) {
+ s.log(DebugLevel, msg, nil, keysAndValues)
+}
+
+// Infow logs a message with some additional context. The variadic key-value
+// pairs are treated as they are in With.
+func (s *SugaredLogger) Infow(msg string, keysAndValues ...interface{}) {
+ s.log(InfoLevel, msg, nil, keysAndValues)
+}
+
+// Warnw logs a message with some additional context. The variadic key-value
+// pairs are treated as they are in With.
+func (s *SugaredLogger) Warnw(msg string, keysAndValues ...interface{}) {
+ s.log(WarnLevel, msg, nil, keysAndValues)
+}
+
+// Errorw logs a message with some additional context. The variadic key-value
+// pairs are treated as they are in With.
+func (s *SugaredLogger) Errorw(msg string, keysAndValues ...interface{}) {
+ s.log(ErrorLevel, msg, nil, keysAndValues)
+}
+
+// DPanicw logs a message with some additional context. In development, the
+// logger then panics. (See DPanicLevel for details.) The variadic key-value
+// pairs are treated as they are in With.
+func (s *SugaredLogger) DPanicw(msg string, keysAndValues ...interface{}) {
+ s.log(DPanicLevel, msg, nil, keysAndValues)
+}
+
+// Panicw logs a message with some additional context, then panics. The
+// variadic key-value pairs are treated as they are in With.
+func (s *SugaredLogger) Panicw(msg string, keysAndValues ...interface{}) {
+ s.log(PanicLevel, msg, nil, keysAndValues)
+}
+
+// Fatalw logs a message with some additional context, then calls os.Exit. The
+// variadic key-value pairs are treated as they are in With.
+func (s *SugaredLogger) Fatalw(msg string, keysAndValues ...interface{}) {
+ s.log(FatalLevel, msg, nil, keysAndValues)
+}
+
+// Sync flushes any buffered log entries.
+func (s *SugaredLogger) Sync() error {
+ return s.base.Sync()
+}
+
+func (s *SugaredLogger) log(lvl zapcore.Level, template string, fmtArgs []interface{}, context []interface{}) {
+ // If logging at this level is completely disabled, skip the overhead of
+ // string formatting.
+ if lvl < DPanicLevel && !s.base.Core().Enabled(lvl) {
+ return
+ }
+
+ // Format with Sprint, Sprintf, or neither.
+ msg := template
+ if msg == "" && len(fmtArgs) > 0 {
+ msg = fmt.Sprint(fmtArgs...)
+ } else if msg != "" && len(fmtArgs) > 0 {
+ msg = fmt.Sprintf(template, fmtArgs...)
+ }
+
+ if ce := s.base.Check(lvl, msg); ce != nil {
+ ce.Write(s.sweetenFields(context)...)
+ }
+}
+
+func (s *SugaredLogger) sweetenFields(args []interface{}) []zapcore.Field {
+ if len(args) == 0 {
+ return nil
+ }
+
+ // Allocate enough space for the worst case; if users pass only structured
+ // fields, we shouldn't penalize them with extra allocations.
+ fields := make([]zapcore.Field, 0, len(args))
+ var invalid invalidPairs
+
+ for i := 0; i < len(args); {
+ // This is a strongly-typed field. Consume it and move on.
+ if f, ok := args[i].(zapcore.Field); ok {
+ fields = append(fields, f)
+ i++
+ continue
+ }
+
+ // Make sure this element isn't a dangling key.
+ if i == len(args)-1 {
+ s.base.DPanic(_oddNumberErrMsg, Any("ignored", args[i]))
+ break
+ }
+
+ // Consume this value and the next, treating them as a key-value pair. If the
+ // key isn't a string, add this pair to the slice of invalid pairs.
+ key, val := args[i], args[i+1]
+ if keyStr, ok := key.(string); !ok {
+ // Subsequent errors are likely, so allocate once up front.
+ if cap(invalid) == 0 {
+ invalid = make(invalidPairs, 0, len(args)/2)
+ }
+ invalid = append(invalid, invalidPair{i, key, val})
+ } else {
+ fields = append(fields, Any(keyStr, val))
+ }
+ i += 2
+ }
+
+ // If we encountered any invalid key-value pairs, log an error.
+ if len(invalid) > 0 {
+ s.base.DPanic(_nonStringKeyErrMsg, Array("invalid", invalid))
+ }
+ return fields
+}
+
+type invalidPair struct {
+ position int
+ key, value interface{}
+}
+
+func (p invalidPair) MarshalLogObject(enc zapcore.ObjectEncoder) error {
+ enc.AddInt64("position", int64(p.position))
+ Any("key", p.key).AddTo(enc)
+ Any("value", p.value).AddTo(enc)
+ return nil
+}
+
+type invalidPairs []invalidPair
+
+func (ps invalidPairs) MarshalLogArray(enc zapcore.ArrayEncoder) error {
+ var err error
+ for i := range ps {
+ err = multierr.Append(err, enc.AppendObject(ps[i]))
+ }
+ return err
+}
diff --git a/vendor/go.uber.org/zap/sugar_test.go b/vendor/go.uber.org/zap/sugar_test.go
new file mode 100644
index 0000000..69eb1fa
--- /dev/null
+++ b/vendor/go.uber.org/zap/sugar_test.go
@@ -0,0 +1,374 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "testing"
+
+ "go.uber.org/zap/internal/exit"
+ "go.uber.org/zap/zapcore"
+ "go.uber.org/zap/zaptest"
+ "go.uber.org/zap/zaptest/observer"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestSugarWith(t *testing.T) {
+ // Convenience functions to create expected error logs.
+ ignored := func(msg interface{}) observer.LoggedEntry {
+ return observer.LoggedEntry{
+ Entry: zapcore.Entry{Level: DPanicLevel, Message: _oddNumberErrMsg},
+ Context: []zapcore.Field{Any("ignored", msg)},
+ }
+ }
+ nonString := func(pairs ...invalidPair) observer.LoggedEntry {
+ return observer.LoggedEntry{
+ Entry: zapcore.Entry{Level: DPanicLevel, Message: _nonStringKeyErrMsg},
+ Context: []zapcore.Field{Array("invalid", invalidPairs(pairs))},
+ }
+ }
+
+ tests := []struct {
+ desc string
+ args []interface{}
+ expected []zapcore.Field
+ errLogs []observer.LoggedEntry
+ }{
+ {
+ desc: "nil args",
+ args: nil,
+ expected: []zapcore.Field{},
+ errLogs: nil,
+ },
+ {
+ desc: "empty slice of args",
+ args: []interface{}{},
+ expected: []zapcore.Field{},
+ errLogs: nil,
+ },
+ {
+ desc: "just a dangling key",
+ args: []interface{}{"should ignore"},
+ expected: []zapcore.Field{},
+ errLogs: []observer.LoggedEntry{ignored("should ignore")},
+ },
+ {
+ desc: "well-formed key-value pairs",
+ args: []interface{}{"foo", 42, "true", "bar"},
+ expected: []zapcore.Field{Int("foo", 42), String("true", "bar")},
+ errLogs: nil,
+ },
+ {
+ desc: "just a structured field",
+ args: []interface{}{Int("foo", 42)},
+ expected: []zapcore.Field{Int("foo", 42)},
+ errLogs: nil,
+ },
+ {
+ desc: "structured field and a dangling key",
+ args: []interface{}{Int("foo", 42), "dangling"},
+ expected: []zapcore.Field{Int("foo", 42)},
+ errLogs: []observer.LoggedEntry{ignored("dangling")},
+ },
+ {
+ desc: "structured field and a dangling non-string key",
+ args: []interface{}{Int("foo", 42), 13},
+ expected: []zapcore.Field{Int("foo", 42)},
+ errLogs: []observer.LoggedEntry{ignored(13)},
+ },
+ {
+ desc: "key-value pair and a dangling key",
+ args: []interface{}{"foo", 42, "dangling"},
+ expected: []zapcore.Field{Int("foo", 42)},
+ errLogs: []observer.LoggedEntry{ignored("dangling")},
+ },
+ {
+ desc: "pairs, a structured field, and a dangling key",
+ args: []interface{}{"first", "field", Int("foo", 42), "baz", "quux", "dangling"},
+ expected: []zapcore.Field{String("first", "field"), Int("foo", 42), String("baz", "quux")},
+ errLogs: []observer.LoggedEntry{ignored("dangling")},
+ },
+ {
+ desc: "one non-string key",
+ args: []interface{}{"foo", 42, true, "bar"},
+ expected: []zapcore.Field{Int("foo", 42)},
+ errLogs: []observer.LoggedEntry{nonString(invalidPair{2, true, "bar"})},
+ },
+ {
+ desc: "pairs, structured fields, non-string keys, and a dangling key",
+ args: []interface{}{"foo", 42, true, "bar", Int("structure", 11), 42, "reversed", "baz", "quux", "dangling"},
+ expected: []zapcore.Field{Int("foo", 42), Int("structure", 11), String("baz", "quux")},
+ errLogs: []observer.LoggedEntry{
+ ignored("dangling"),
+ nonString(invalidPair{2, true, "bar"}, invalidPair{5, 42, "reversed"}),
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ withSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) {
+ logger.With(tt.args...).Info("")
+ output := logs.AllUntimed()
+ if len(tt.errLogs) > 0 {
+ for i := range tt.errLogs {
+ assert.Equal(t, tt.errLogs[i], output[i], "Unexpected error log at position %d for scenario %s.", i, tt.desc)
+ }
+ }
+ assert.Equal(t, len(tt.errLogs)+1, len(output), "Expected only one non-error message to be logged in scenario %s.", tt.desc)
+ assert.Equal(t, tt.expected, output[len(tt.errLogs)].Context, "Unexpected message context in scenario %s.", tt.desc)
+ })
+ }
+}
+
+func TestSugarFieldsInvalidPairs(t *testing.T) {
+ withSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) {
+ logger.With(42, "foo", []string{"bar"}, "baz").Info("")
+ output := logs.AllUntimed()
+
+ // Double-check that the actual message was logged.
+ require.Equal(t, 2, len(output), "Unexpected number of entries logged.")
+ require.Equal(t, observer.LoggedEntry{Context: []zapcore.Field{}}, output[1], "Unexpected non-error log entry.")
+
+ // Assert that the error message's structured fields serialize properly.
+ require.Equal(t, 1, len(output[0].Context), "Expected one field in error entry context.")
+ enc := zapcore.NewMapObjectEncoder()
+ output[0].Context[0].AddTo(enc)
+ assert.Equal(t, []interface{}{
+ map[string]interface{}{"position": int64(0), "key": int64(42), "value": "foo"},
+ map[string]interface{}{"position": int64(2), "key": []interface{}{"bar"}, "value": "baz"},
+ }, enc.Fields["invalid"], "Unexpected output when logging invalid key-value pairs.")
+ })
+}
+
+type stringerF func() string
+
+func (f stringerF) String() string { return f() }
+
+func TestSugarStructuredLogging(t *testing.T) {
+ tests := []struct {
+ msg string
+ expectMsg string
+ }{
+ {"foo", "foo"},
+ {"", ""},
+ }
+
+ // Common to all test cases.
+ context := []interface{}{"foo", "bar"}
+ extra := []interface{}{"baz", false}
+ expectedFields := []zapcore.Field{String("foo", "bar"), Bool("baz", false)}
+
+ for _, tt := range tests {
+ withSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) {
+ logger.With(context...).Debugw(tt.msg, extra...)
+ logger.With(context...).Infow(tt.msg, extra...)
+ logger.With(context...).Warnw(tt.msg, extra...)
+ logger.With(context...).Errorw(tt.msg, extra...)
+ logger.With(context...).DPanicw(tt.msg, extra...)
+
+ expected := make([]observer.LoggedEntry, 5)
+ for i, lvl := range []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel} {
+ expected[i] = observer.LoggedEntry{
+ Entry: zapcore.Entry{Message: tt.expectMsg, Level: lvl},
+ Context: expectedFields,
+ }
+ }
+ assert.Equal(t, expected, logs.AllUntimed(), "Unexpected log output.")
+ })
+ }
+}
+
+func TestSugarConcatenatingLogging(t *testing.T) {
+ tests := []struct {
+ args []interface{}
+ expect string
+ }{
+ {[]interface{}{nil}, ""},
+ }
+
+ // Common to all test cases.
+ context := []interface{}{"foo", "bar"}
+ expectedFields := []zapcore.Field{String("foo", "bar")}
+
+ for _, tt := range tests {
+ withSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) {
+ logger.With(context...).Debug(tt.args...)
+ logger.With(context...).Info(tt.args...)
+ logger.With(context...).Warn(tt.args...)
+ logger.With(context...).Error(tt.args...)
+ logger.With(context...).DPanic(tt.args...)
+
+ expected := make([]observer.LoggedEntry, 5)
+ for i, lvl := range []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel} {
+ expected[i] = observer.LoggedEntry{
+ Entry: zapcore.Entry{Message: tt.expect, Level: lvl},
+ Context: expectedFields,
+ }
+ }
+ assert.Equal(t, expected, logs.AllUntimed(), "Unexpected log output.")
+ })
+ }
+}
+
+func TestSugarTemplatedLogging(t *testing.T) {
+ tests := []struct {
+ format string
+ args []interface{}
+ expect string
+ }{
+ {"", nil, ""},
+ {"foo", nil, "foo"},
+ // If the user fails to pass a template, degrade to fmt.Sprint.
+ {"", []interface{}{"foo"}, "foo"},
+ }
+
+ // Common to all test cases.
+ context := []interface{}{"foo", "bar"}
+ expectedFields := []zapcore.Field{String("foo", "bar")}
+
+ for _, tt := range tests {
+ withSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) {
+ logger.With(context...).Debugf(tt.format, tt.args...)
+ logger.With(context...).Infof(tt.format, tt.args...)
+ logger.With(context...).Warnf(tt.format, tt.args...)
+ logger.With(context...).Errorf(tt.format, tt.args...)
+ logger.With(context...).DPanicf(tt.format, tt.args...)
+
+ expected := make([]observer.LoggedEntry, 5)
+ for i, lvl := range []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel} {
+ expected[i] = observer.LoggedEntry{
+ Entry: zapcore.Entry{Message: tt.expect, Level: lvl},
+ Context: expectedFields,
+ }
+ }
+ assert.Equal(t, expected, logs.AllUntimed(), "Unexpected log output.")
+ })
+ }
+}
+
+func TestSugarPanicLogging(t *testing.T) {
+ tests := []struct {
+ loggerLevel zapcore.Level
+ f func(*SugaredLogger)
+ expectedMsg string
+ }{
+ {FatalLevel, func(s *SugaredLogger) { s.Panic("foo") }, ""},
+ {PanicLevel, func(s *SugaredLogger) { s.Panic("foo") }, "foo"},
+ {DebugLevel, func(s *SugaredLogger) { s.Panic("foo") }, "foo"},
+ {FatalLevel, func(s *SugaredLogger) { s.Panicf("%s", "foo") }, ""},
+ {PanicLevel, func(s *SugaredLogger) { s.Panicf("%s", "foo") }, "foo"},
+ {DebugLevel, func(s *SugaredLogger) { s.Panicf("%s", "foo") }, "foo"},
+ {FatalLevel, func(s *SugaredLogger) { s.Panicw("foo") }, ""},
+ {PanicLevel, func(s *SugaredLogger) { s.Panicw("foo") }, "foo"},
+ {DebugLevel, func(s *SugaredLogger) { s.Panicw("foo") }, "foo"},
+ }
+
+ for _, tt := range tests {
+ withSugar(t, tt.loggerLevel, nil, func(sugar *SugaredLogger, logs *observer.ObservedLogs) {
+ assert.Panics(t, func() { tt.f(sugar) }, "Expected panic-level logger calls to panic.")
+ if tt.expectedMsg != "" {
+ assert.Equal(t, []observer.LoggedEntry{{
+ Context: []zapcore.Field{},
+ Entry: zapcore.Entry{Message: tt.expectedMsg, Level: PanicLevel},
+ }}, logs.AllUntimed(), "Unexpected log output.")
+ } else {
+ assert.Equal(t, 0, logs.Len(), "Didn't expect any log output.")
+ }
+ })
+ }
+}
+
+func TestSugarFatalLogging(t *testing.T) {
+ tests := []struct {
+ loggerLevel zapcore.Level
+ f func(*SugaredLogger)
+ expectedMsg string
+ }{
+ {FatalLevel + 1, func(s *SugaredLogger) { s.Fatal("foo") }, ""},
+ {FatalLevel, func(s *SugaredLogger) { s.Fatal("foo") }, "foo"},
+ {DebugLevel, func(s *SugaredLogger) { s.Fatal("foo") }, "foo"},
+ {FatalLevel + 1, func(s *SugaredLogger) { s.Fatalf("%s", "foo") }, ""},
+ {FatalLevel, func(s *SugaredLogger) { s.Fatalf("%s", "foo") }, "foo"},
+ {DebugLevel, func(s *SugaredLogger) { s.Fatalf("%s", "foo") }, "foo"},
+ {FatalLevel + 1, func(s *SugaredLogger) { s.Fatalw("foo") }, ""},
+ {FatalLevel, func(s *SugaredLogger) { s.Fatalw("foo") }, "foo"},
+ {DebugLevel, func(s *SugaredLogger) { s.Fatalw("foo") }, "foo"},
+ }
+
+ for _, tt := range tests {
+ withSugar(t, tt.loggerLevel, nil, func(sugar *SugaredLogger, logs *observer.ObservedLogs) {
+ stub := exit.WithStub(func() { tt.f(sugar) })
+ assert.True(t, stub.Exited, "Expected all calls to fatal logger methods to exit process.")
+ if tt.expectedMsg != "" {
+ assert.Equal(t, []observer.LoggedEntry{{
+ Context: []zapcore.Field{},
+ Entry: zapcore.Entry{Message: tt.expectedMsg, Level: FatalLevel},
+ }}, logs.AllUntimed(), "Unexpected log output.")
+ } else {
+ assert.Equal(t, 0, logs.Len(), "Didn't expect any log output.")
+ }
+ })
+ }
+}
+
+func TestSugarAddCaller(t *testing.T) {
+ tests := []struct {
+ options []Option
+ pat string
+ }{
+ {opts(AddCaller()), `.+/sugar_test.go:[\d]+$`},
+ {opts(AddCaller(), AddCallerSkip(1), AddCallerSkip(-1)), `.+/zap/sugar_test.go:[\d]+$`},
+ {opts(AddCaller(), AddCallerSkip(1)), `.+/zap/common_test.go:[\d]+$`},
+ {opts(AddCaller(), AddCallerSkip(1), AddCallerSkip(5)), `.+/src/runtime/.*:[\d]+$`},
+ }
+ for _, tt := range tests {
+ withSugar(t, DebugLevel, tt.options, func(logger *SugaredLogger, logs *observer.ObservedLogs) {
+ logger.Info("")
+ output := logs.AllUntimed()
+ assert.Equal(t, 1, len(output), "Unexpected number of logs written out.")
+ assert.Regexp(
+ t,
+ tt.pat,
+ output[0].Entry.Caller,
+ "Expected to find package name and file name in output.",
+ )
+ })
+ }
+}
+
+func TestSugarAddCallerFail(t *testing.T) {
+ errBuf := &zaptest.Buffer{}
+ withSugar(t, DebugLevel, opts(AddCaller(), AddCallerSkip(1e3), ErrorOutput(errBuf)), func(log *SugaredLogger, logs *observer.ObservedLogs) {
+ log.Info("Failure.")
+ assert.Regexp(
+ t,
+ `Logger.check error: failed to get caller`,
+ errBuf.String(),
+ "Didn't find expected failure message.",
+ )
+ assert.Equal(
+ t,
+ logs.AllUntimed()[0].Entry.Message,
+ "Failure.",
+ "Expected original message to survive failures in runtime.Caller.")
+ })
+}
diff --git a/vendor/go.uber.org/zap/time.go b/vendor/go.uber.org/zap/time.go
new file mode 100644
index 0000000..c5a1f16
--- /dev/null
+++ b/vendor/go.uber.org/zap/time.go
@@ -0,0 +1,27 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import "time"
+
+func timeToMillis(t time.Time) int64 {
+ return t.UnixNano() / int64(time.Millisecond)
+}
diff --git a/vendor/go.uber.org/zap/time_test.go b/vendor/go.uber.org/zap/time_test.go
new file mode 100644
index 0000000..cb993ab
--- /dev/null
+++ b/vendor/go.uber.org/zap/time_test.go
@@ -0,0 +1,42 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestTimeToMillis(t *testing.T) {
+ tests := []struct {
+ t time.Time
+ stamp int64
+ }{
+ {t: time.Unix(0, 0), stamp: 0},
+ {t: time.Unix(1, 0), stamp: 1000},
+ {t: time.Unix(1, int64(500*time.Millisecond)), stamp: 1500},
+ }
+ for _, tt := range tests {
+ assert.Equal(t, tt.stamp, timeToMillis(tt.t), "Unexpected timestamp for time %v.", tt.t)
+ }
+}
diff --git a/vendor/go.uber.org/zap/writer.go b/vendor/go.uber.org/zap/writer.go
new file mode 100644
index 0000000..16f55ce
--- /dev/null
+++ b/vendor/go.uber.org/zap/writer.go
@@ -0,0 +1,96 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "io/ioutil"
+ "os"
+
+ "go.uber.org/zap/zapcore"
+
+ "go.uber.org/multierr"
+)
+
+// Open is a high-level wrapper that takes a variadic number of paths, opens or
+// creates each of the specified files, and combines them into a locked
+// WriteSyncer. It also returns any error encountered and a function to close
+// any opened files.
+//
+// Passing no paths returns a no-op WriteSyncer. The special paths "stdout" and
+// "stderr" are interpreted as os.Stdout and os.Stderr, respectively.
+func Open(paths ...string) (zapcore.WriteSyncer, func(), error) {
+ writers, close, err := open(paths)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ writer := CombineWriteSyncers(writers...)
+ return writer, close, nil
+}
+
+func open(paths []string) ([]zapcore.WriteSyncer, func(), error) {
+ var openErr error
+ writers := make([]zapcore.WriteSyncer, 0, len(paths))
+ files := make([]*os.File, 0, len(paths))
+ close := func() {
+ for _, f := range files {
+ f.Close()
+ }
+ }
+ for _, path := range paths {
+ switch path {
+ case "stdout":
+ writers = append(writers, os.Stdout)
+ // Don't close standard out.
+ continue
+ case "stderr":
+ writers = append(writers, os.Stderr)
+ // Don't close standard error.
+ continue
+ }
+ f, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
+ openErr = multierr.Append(openErr, err)
+ if err == nil {
+ writers = append(writers, f)
+ files = append(files, f)
+ }
+ }
+
+ if openErr != nil {
+ close()
+ return writers, nil, openErr
+ }
+
+ return writers, close, nil
+}
+
+// CombineWriteSyncers is a utility that combines multiple WriteSyncers into a
+// single, locked WriteSyncer. If no inputs are supplied, it returns a no-op
+// WriteSyncer.
+//
+// It's provided purely as a convenience; the result is no different from
+// using zapcore.NewMultiWriteSyncer and zapcore.Lock individually.
+func CombineWriteSyncers(writers ...zapcore.WriteSyncer) zapcore.WriteSyncer {
+ if len(writers) == 0 {
+ return zapcore.AddSync(ioutil.Discard)
+ }
+ return zapcore.Lock(zapcore.NewMultiWriteSyncer(writers...))
+}
diff --git a/vendor/go.uber.org/zap/writer_test.go b/vendor/go.uber.org/zap/writer_test.go
new file mode 100644
index 0000000..1293832
--- /dev/null
+++ b/vendor/go.uber.org/zap/writer_test.go
@@ -0,0 +1,125 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zap
+
+import (
+ "io/ioutil"
+ "os"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "go.uber.org/zap/zapcore"
+)
+
+func TestOpenNoPaths(t *testing.T) {
+ ws, cleanup, err := Open()
+ defer cleanup()
+
+ assert.NoError(t, err, "Expected opening no paths to succeed.")
+ assert.Equal(
+ t,
+ zapcore.AddSync(ioutil.Discard),
+ ws,
+ "Expected opening no paths to return a no-op WriteSyncer.",
+ )
+}
+
+func TestOpen(t *testing.T) {
+ temp, err := ioutil.TempFile("", "zap-open-test")
+ require.NoError(t, err, "Couldn't create a temporary file for test.")
+ defer os.Remove(temp.Name())
+
+ tests := []struct {
+ paths []string
+ filenames []string
+ error string
+ }{
+ {[]string{"stdout"}, []string{os.Stdout.Name()}, ""},
+ {[]string{"stderr"}, []string{os.Stderr.Name()}, ""},
+ {[]string{temp.Name()}, []string{temp.Name()}, ""},
+ {[]string{"/foo/bar/baz"}, []string{}, "open /foo/bar/baz: no such file or directory"},
+ {
+ paths: []string{"stdout", "/foo/bar/baz", temp.Name(), "/baz/quux"},
+ filenames: []string{os.Stdout.Name(), temp.Name()},
+ error: "open /foo/bar/baz: no such file or directory; open /baz/quux: no such file or directory",
+ },
+ }
+
+ for _, tt := range tests {
+ wss, cleanup, err := open(tt.paths)
+ if err == nil {
+ defer cleanup()
+ }
+
+ if tt.error == "" {
+ assert.NoError(t, err, "Unexpected error opening paths %v.", tt.paths)
+ } else {
+ assert.Equal(t, tt.error, err.Error(), "Unexpected error opening paths %v.", tt.paths)
+ }
+ names := make([]string, len(wss))
+ for i, ws := range wss {
+ f, ok := ws.(*os.File)
+ require.True(t, ok, "Expected all WriteSyncers returned from open() to be files.")
+ names[i] = f.Name()
+ }
+ assert.Equal(t, tt.filenames, names, "Opened unexpected files given paths %v.", tt.paths)
+ }
+}
+
+func TestOpenFails(t *testing.T) {
+ tests := []struct {
+ paths []string
+ }{
+ {
+ paths: []string{"./non-existent-dir/file"},
+ },
+ {
+ paths: []string{"stdout", "./non-existent-dir/file"},
+ },
+ }
+
+ for _, tt := range tests {
+ _, cleanup, err := Open(tt.paths...)
+ require.Nil(t, cleanup, "Cleanup function should never be nil")
+ assert.Error(t, err, "Open with non-existent directory should fail")
+ }
+}
+
+type testWriter struct {
+ expected string
+ t testing.TB
+}
+
+func (w *testWriter) Write(actual []byte) (int, error) {
+ assert.Equal(w.t, []byte(w.expected), actual, "Unexpected write error.")
+ return len(actual), nil
+}
+
+func (w *testWriter) Sync() error {
+ return nil
+}
+
+func TestCombineWriteSyncers(t *testing.T) {
+ tw := &testWriter{"test", t}
+ w := CombineWriteSyncers(tw)
+ w.Write([]byte("test"))
+}
diff --git a/vendor/go.uber.org/zap/zapcore/console_encoder.go b/vendor/go.uber.org/zap/zapcore/console_encoder.go
new file mode 100644
index 0000000..b787596
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/console_encoder.go
@@ -0,0 +1,147 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "fmt"
+ "sync"
+
+ "go.uber.org/zap/buffer"
+ "go.uber.org/zap/internal/bufferpool"
+)
+
+var _sliceEncoderPool = sync.Pool{
+ New: func() interface{} {
+ return &sliceArrayEncoder{elems: make([]interface{}, 0, 2)}
+ },
+}
+
+func getSliceEncoder() *sliceArrayEncoder {
+ return _sliceEncoderPool.Get().(*sliceArrayEncoder)
+}
+
+func putSliceEncoder(e *sliceArrayEncoder) {
+ e.elems = e.elems[:0]
+ _sliceEncoderPool.Put(e)
+}
+
+type consoleEncoder struct {
+ *jsonEncoder
+}
+
+// NewConsoleEncoder creates an encoder whose output is designed for human -
+// rather than machine - consumption. It serializes the core log entry data
+// (message, level, timestamp, etc.) in a plain-text format and leaves the
+// structured context as JSON.
+//
+// Note that although the console encoder doesn't use the keys specified in the
+// encoder configuration, it will omit any element whose key is set to the empty
+// string.
+func NewConsoleEncoder(cfg EncoderConfig) Encoder {
+ return consoleEncoder{newJSONEncoder(cfg, true)}
+}
+
+func (c consoleEncoder) Clone() Encoder {
+ return consoleEncoder{c.jsonEncoder.Clone().(*jsonEncoder)}
+}
+
+func (c consoleEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, error) {
+ line := bufferpool.Get()
+
+ // We don't want the entry's metadata to be quoted and escaped (if it's
+ // encoded as strings), which means that we can't use the JSON encoder. The
+ // simplest option is to use the memory encoder and fmt.Fprint.
+ //
+ // If this ever becomes a performance bottleneck, we can implement
+ // ArrayEncoder for our plain-text format.
+ arr := getSliceEncoder()
+ if c.TimeKey != "" && c.EncodeTime != nil {
+ c.EncodeTime(ent.Time, arr)
+ }
+ if c.LevelKey != "" && c.EncodeLevel != nil {
+ c.EncodeLevel(ent.Level, arr)
+ }
+ if ent.LoggerName != "" && c.NameKey != "" {
+ nameEncoder := c.EncodeName
+
+ if nameEncoder == nil {
+ // Fall back to FullNameEncoder for backward compatibility.
+ nameEncoder = FullNameEncoder
+ }
+
+ nameEncoder(ent.LoggerName, arr)
+ }
+ if ent.Caller.Defined && c.CallerKey != "" && c.EncodeCaller != nil {
+ c.EncodeCaller(ent.Caller, arr)
+ }
+ for i := range arr.elems {
+ if i > 0 {
+ line.AppendByte('\t')
+ }
+ fmt.Fprint(line, arr.elems[i])
+ }
+ putSliceEncoder(arr)
+
+ // Add the message itself.
+ if c.MessageKey != "" {
+ c.addTabIfNecessary(line)
+ line.AppendString(ent.Message)
+ }
+
+ // Add any structured context.
+ c.writeContext(line, fields)
+
+ // If there's no stacktrace key, honor that; this allows users to force
+ // single-line output.
+ if ent.Stack != "" && c.StacktraceKey != "" {
+ line.AppendByte('\n')
+ line.AppendString(ent.Stack)
+ }
+
+ if c.LineEnding != "" {
+ line.AppendString(c.LineEnding)
+ } else {
+ line.AppendString(DefaultLineEnding)
+ }
+ return line, nil
+}
+
+func (c consoleEncoder) writeContext(line *buffer.Buffer, extra []Field) {
+ context := c.jsonEncoder.Clone().(*jsonEncoder)
+ defer context.buf.Free()
+
+ addFields(context, extra)
+ context.closeOpenNamespaces()
+ if context.buf.Len() == 0 {
+ return
+ }
+
+ c.addTabIfNecessary(line)
+ line.AppendByte('{')
+ line.Write(context.buf.Bytes())
+ line.AppendByte('}')
+}
+
+func (c consoleEncoder) addTabIfNecessary(line *buffer.Buffer) {
+ if line.Len() > 0 {
+ line.AppendByte('\t')
+ }
+}
diff --git a/vendor/go.uber.org/zap/zapcore/console_encoder_bench_test.go b/vendor/go.uber.org/zap/zapcore/console_encoder_bench_test.go
new file mode 100644
index 0000000..62feaea
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/console_encoder_bench_test.go
@@ -0,0 +1,49 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore_test
+
+import (
+ "testing"
+
+ . "go.uber.org/zap/zapcore"
+)
+
+func BenchmarkZapConsole(b *testing.B) {
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ enc := NewConsoleEncoder(humanEncoderConfig())
+ enc.AddString("str", "foo")
+ enc.AddInt64("int64-1", 1)
+ enc.AddInt64("int64-2", 2)
+ enc.AddFloat64("float64", 1.0)
+ enc.AddString("string1", "\n")
+ enc.AddString("string2", "💩")
+ enc.AddString("string3", "🤔")
+ enc.AddString("string4", "🙊")
+ enc.AddBool("bool", true)
+ buf, _ := enc.EncodeEntry(Entry{
+ Message: "fake",
+ Level: DebugLevel,
+ }, nil)
+ buf.Free()
+ }
+ })
+}
diff --git a/vendor/go.uber.org/zap/zapcore/core.go b/vendor/go.uber.org/zap/zapcore/core.go
new file mode 100644
index 0000000..a1ef8b0
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/core.go
@@ -0,0 +1,113 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+// Core is a minimal, fast logger interface. It's designed for library authors
+// to wrap in a more user-friendly API.
+type Core interface {
+ LevelEnabler
+
+ // With adds structured context to the Core.
+ With([]Field) Core
+ // Check determines whether the supplied Entry should be logged (using the
+ // embedded LevelEnabler and possibly some extra logic). If the entry
+ // should be logged, the Core adds itself to the CheckedEntry and returns
+ // the result.
+ //
+ // Callers must use Check before calling Write.
+ Check(Entry, *CheckedEntry) *CheckedEntry
+ // Write serializes the Entry and any Fields supplied at the log site and
+ // writes them to their destination.
+ //
+ // If called, Write should always log the Entry and Fields; it should not
+ // replicate the logic of Check.
+ Write(Entry, []Field) error
+ // Sync flushes buffered logs (if any).
+ Sync() error
+}
+
+type nopCore struct{}
+
+// NewNopCore returns a no-op Core.
+func NewNopCore() Core { return nopCore{} }
+func (nopCore) Enabled(Level) bool { return false }
+func (n nopCore) With([]Field) Core { return n }
+func (nopCore) Check(_ Entry, ce *CheckedEntry) *CheckedEntry { return ce }
+func (nopCore) Write(Entry, []Field) error { return nil }
+func (nopCore) Sync() error { return nil }
+
+// NewCore creates a Core that writes logs to a WriteSyncer.
+func NewCore(enc Encoder, ws WriteSyncer, enab LevelEnabler) Core {
+ return &ioCore{
+ LevelEnabler: enab,
+ enc: enc,
+ out: ws,
+ }
+}
+
+type ioCore struct {
+ LevelEnabler
+ enc Encoder
+ out WriteSyncer
+}
+
+func (c *ioCore) With(fields []Field) Core {
+ clone := c.clone()
+ addFields(clone.enc, fields)
+ return clone
+}
+
+func (c *ioCore) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {
+ if c.Enabled(ent.Level) {
+ return ce.AddCore(ent, c)
+ }
+ return ce
+}
+
+func (c *ioCore) Write(ent Entry, fields []Field) error {
+ buf, err := c.enc.EncodeEntry(ent, fields)
+ if err != nil {
+ return err
+ }
+ _, err = c.out.Write(buf.Bytes())
+ buf.Free()
+ if err != nil {
+ return err
+ }
+ if ent.Level > ErrorLevel {
+ // Since we may be crashing the program, sync the output. Ignore Sync
+ // errors, pending a clean solution to issue #370.
+ c.Sync()
+ }
+ return nil
+}
+
+func (c *ioCore) Sync() error {
+ return c.out.Sync()
+}
+
+func (c *ioCore) clone() *ioCore {
+ return &ioCore{
+ LevelEnabler: c.LevelEnabler,
+ enc: c.enc.Clone(),
+ out: c.out,
+ }
+}
diff --git a/vendor/go.uber.org/zap/zapcore/core_test.go b/vendor/go.uber.org/zap/zapcore/core_test.go
new file mode 100644
index 0000000..20a4720
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/core_test.go
@@ -0,0 +1,163 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore_test
+
+import (
+ "errors"
+ "io/ioutil"
+ "os"
+ "testing"
+ "time"
+
+ . "go.uber.org/zap/zapcore"
+ "go.uber.org/zap/zaptest"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func makeInt64Field(key string, val int) Field {
+ return Field{Type: Int64Type, Integer: int64(val), Key: key}
+}
+
+func TestNopCore(t *testing.T) {
+ entry := Entry{
+ Message: "test",
+ Level: InfoLevel,
+ Time: time.Now(),
+ LoggerName: "main",
+ Stack: "fake-stack",
+ }
+ ce := &CheckedEntry{}
+
+ allLevels := []Level{
+ DebugLevel,
+ InfoLevel,
+ WarnLevel,
+ ErrorLevel,
+ DPanicLevel,
+ PanicLevel,
+ FatalLevel,
+ }
+ core := NewNopCore()
+ assert.Equal(t, core, core.With([]Field{makeInt64Field("k", 42)}), "Expected no-op With.")
+ for _, level := range allLevels {
+ assert.False(t, core.Enabled(level), "Expected all levels to be disabled in no-op core.")
+ assert.Equal(t, ce, core.Check(entry, ce), "Expected no-op Check to return checked entry unchanged.")
+ assert.NoError(t, core.Write(entry, nil), "Expected no-op Writes to always succeed.")
+ assert.NoError(t, core.Sync(), "Expected no-op Syncs to always succeed.")
+ }
+}
+
+func TestIOCore(t *testing.T) {
+ temp, err := ioutil.TempFile("", "zapcore-test-iocore")
+ require.NoError(t, err, "Failed to create temp file.")
+ defer os.Remove(temp.Name())
+
+ // Drop timestamps for simpler assertions (timestamp encoding is tested
+ // elsewhere).
+ cfg := testEncoderConfig()
+ cfg.TimeKey = ""
+
+ core := NewCore(
+ NewJSONEncoder(cfg),
+ temp,
+ InfoLevel,
+ ).With([]Field{makeInt64Field("k", 1)})
+ defer assert.NoError(t, core.Sync(), "Expected Syncing a temp file to succeed.")
+
+ if ce := core.Check(Entry{Level: DebugLevel, Message: "debug"}, nil); ce != nil {
+ ce.Write(makeInt64Field("k", 2))
+ }
+ if ce := core.Check(Entry{Level: InfoLevel, Message: "info"}, nil); ce != nil {
+ ce.Write(makeInt64Field("k", 3))
+ }
+ if ce := core.Check(Entry{Level: WarnLevel, Message: "warn"}, nil); ce != nil {
+ ce.Write(makeInt64Field("k", 4))
+ }
+
+ logged, err := ioutil.ReadFile(temp.Name())
+ require.NoError(t, err, "Failed to read from temp file.")
+ require.Equal(
+ t,
+ `{"level":"info","msg":"info","k":1,"k":3}`+"\n"+
+ `{"level":"warn","msg":"warn","k":1,"k":4}`+"\n",
+ string(logged),
+ "Unexpected log output.",
+ )
+}
+
+func TestIOCoreSyncFail(t *testing.T) {
+ sink := &zaptest.Discarder{}
+ err := errors.New("failed")
+ sink.SetError(err)
+
+ core := NewCore(
+ NewJSONEncoder(testEncoderConfig()),
+ sink,
+ DebugLevel,
+ )
+
+ assert.Equal(
+ t,
+ err,
+ core.Sync(),
+ "Expected core.Sync to return errors from underlying WriteSyncer.",
+ )
+}
+
+func TestIOCoreSyncsOutput(t *testing.T) {
+ tests := []struct {
+ entry Entry
+ shouldSync bool
+ }{
+ {Entry{Level: DebugLevel}, false},
+ {Entry{Level: InfoLevel}, false},
+ {Entry{Level: WarnLevel}, false},
+ {Entry{Level: ErrorLevel}, false},
+ {Entry{Level: DPanicLevel}, true},
+ {Entry{Level: PanicLevel}, true},
+ {Entry{Level: FatalLevel}, true},
+ }
+
+ for _, tt := range tests {
+ sink := &zaptest.Discarder{}
+ core := NewCore(
+ NewJSONEncoder(testEncoderConfig()),
+ sink,
+ DebugLevel,
+ )
+
+ core.Write(tt.entry, nil)
+ assert.Equal(t, tt.shouldSync, sink.Called(), "Incorrect Sync behavior.")
+ }
+}
+
+func TestIOCoreWriteFailure(t *testing.T) {
+ core := NewCore(
+ NewJSONEncoder(testEncoderConfig()),
+ Lock(&zaptest.FailWriter{}),
+ DebugLevel,
+ )
+ err := core.Write(Entry{}, nil)
+ // Should log the error.
+ assert.Error(t, err, "Expected writing Entry to fail.")
+}
diff --git a/vendor/go.uber.org/zap/zapcore/doc.go b/vendor/go.uber.org/zap/zapcore/doc.go
new file mode 100644
index 0000000..31000e9
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/doc.go
@@ -0,0 +1,24 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+// Package zapcore defines and implements the low-level interfaces upon which
+// zap is built. By providing alternate implementations of these interfaces,
+// external packages can extend zap's capabilities.
+package zapcore // import "go.uber.org/zap/zapcore"
diff --git a/vendor/go.uber.org/zap/zapcore/encoder.go b/vendor/go.uber.org/zap/zapcore/encoder.go
new file mode 100644
index 0000000..f050952
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/encoder.go
@@ -0,0 +1,348 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "time"
+
+ "go.uber.org/zap/buffer"
+)
+
+// DefaultLineEnding defines the default line ending when writing logs.
+// Alternate line endings specified in EncoderConfig can override this
+// behavior.
+const DefaultLineEnding = "\n"
+
+// A LevelEncoder serializes a Level to a primitive type.
+type LevelEncoder func(Level, PrimitiveArrayEncoder)
+
+// LowercaseLevelEncoder serializes a Level to a lowercase string. For example,
+// InfoLevel is serialized to "info".
+func LowercaseLevelEncoder(l Level, enc PrimitiveArrayEncoder) {
+ enc.AppendString(l.String())
+}
+
+// LowercaseColorLevelEncoder serializes a Level to a lowercase string and adds coloring.
+// For example, InfoLevel is serialized to "info" and colored blue.
+func LowercaseColorLevelEncoder(l Level, enc PrimitiveArrayEncoder) {
+ s, ok := _levelToLowercaseColorString[l]
+ if !ok {
+ s = _unknownLevelColor.Add(l.String())
+ }
+ enc.AppendString(s)
+}
+
+// CapitalLevelEncoder serializes a Level to an all-caps string. For example,
+// InfoLevel is serialized to "INFO".
+func CapitalLevelEncoder(l Level, enc PrimitiveArrayEncoder) {
+ enc.AppendString(l.CapitalString())
+}
+
+// CapitalColorLevelEncoder serializes a Level to an all-caps string and adds color.
+// For example, InfoLevel is serialized to "INFO" and colored blue.
+func CapitalColorLevelEncoder(l Level, enc PrimitiveArrayEncoder) {
+ s, ok := _levelToCapitalColorString[l]
+ if !ok {
+ s = _unknownLevelColor.Add(l.CapitalString())
+ }
+ enc.AppendString(s)
+}
+
+// UnmarshalText unmarshals text to a LevelEncoder. "capital" is unmarshaled to
+// CapitalLevelEncoder, "coloredCapital" is unmarshaled to CapitalColorLevelEncoder,
+// "colored" is unmarshaled to LowercaseColorLevelEncoder, and anything else
+// is unmarshaled to LowercaseLevelEncoder.
+func (e *LevelEncoder) UnmarshalText(text []byte) error {
+ switch string(text) {
+ case "capital":
+ *e = CapitalLevelEncoder
+ case "capitalColor":
+ *e = CapitalColorLevelEncoder
+ case "color":
+ *e = LowercaseColorLevelEncoder
+ default:
+ *e = LowercaseLevelEncoder
+ }
+ return nil
+}
+
+// A TimeEncoder serializes a time.Time to a primitive type.
+type TimeEncoder func(time.Time, PrimitiveArrayEncoder)
+
+// EpochTimeEncoder serializes a time.Time to a floating-point number of seconds
+// since the Unix epoch.
+func EpochTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
+ nanos := t.UnixNano()
+ sec := float64(nanos) / float64(time.Second)
+ enc.AppendFloat64(sec)
+}
+
+// EpochMillisTimeEncoder serializes a time.Time to a floating-point number of
+// milliseconds since the Unix epoch.
+func EpochMillisTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
+ nanos := t.UnixNano()
+ millis := float64(nanos) / float64(time.Millisecond)
+ enc.AppendFloat64(millis)
+}
+
+// EpochNanosTimeEncoder serializes a time.Time to an integer number of
+// nanoseconds since the Unix epoch.
+func EpochNanosTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
+ enc.AppendInt64(t.UnixNano())
+}
+
+// ISO8601TimeEncoder serializes a time.Time to an ISO8601-formatted string
+// with millisecond precision.
+func ISO8601TimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
+ enc.AppendString(t.Format("2006-01-02T15:04:05.000Z0700"))
+}
+
+// UnmarshalText unmarshals text to a TimeEncoder. "iso8601" and "ISO8601" are
+// unmarshaled to ISO8601TimeEncoder, "millis" is unmarshaled to
+// EpochMillisTimeEncoder, and anything else is unmarshaled to EpochTimeEncoder.
+func (e *TimeEncoder) UnmarshalText(text []byte) error {
+ switch string(text) {
+ case "iso8601", "ISO8601":
+ *e = ISO8601TimeEncoder
+ case "millis":
+ *e = EpochMillisTimeEncoder
+ case "nanos":
+ *e = EpochNanosTimeEncoder
+ default:
+ *e = EpochTimeEncoder
+ }
+ return nil
+}
+
+// A DurationEncoder serializes a time.Duration to a primitive type.
+type DurationEncoder func(time.Duration, PrimitiveArrayEncoder)
+
+// SecondsDurationEncoder serializes a time.Duration to a floating-point number of seconds elapsed.
+func SecondsDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) {
+ enc.AppendFloat64(float64(d) / float64(time.Second))
+}
+
+// NanosDurationEncoder serializes a time.Duration to an integer number of
+// nanoseconds elapsed.
+func NanosDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) {
+ enc.AppendInt64(int64(d))
+}
+
+// StringDurationEncoder serializes a time.Duration using its built-in String
+// method.
+func StringDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) {
+ enc.AppendString(d.String())
+}
+
+// UnmarshalText unmarshals text to a DurationEncoder. "string" is unmarshaled
+// to StringDurationEncoder, and anything else is unmarshaled to
+// NanosDurationEncoder.
+func (e *DurationEncoder) UnmarshalText(text []byte) error {
+ switch string(text) {
+ case "string":
+ *e = StringDurationEncoder
+ case "nanos":
+ *e = NanosDurationEncoder
+ default:
+ *e = SecondsDurationEncoder
+ }
+ return nil
+}
+
+// A CallerEncoder serializes an EntryCaller to a primitive type.
+type CallerEncoder func(EntryCaller, PrimitiveArrayEncoder)
+
+// FullCallerEncoder serializes a caller in /full/path/to/package/file:line
+// format.
+func FullCallerEncoder(caller EntryCaller, enc PrimitiveArrayEncoder) {
+ // TODO: consider using a byte-oriented API to save an allocation.
+ enc.AppendString(caller.String())
+}
+
+// ShortCallerEncoder serializes a caller in package/file:line format, trimming
+// all but the final directory from the full path.
+func ShortCallerEncoder(caller EntryCaller, enc PrimitiveArrayEncoder) {
+ // TODO: consider using a byte-oriented API to save an allocation.
+ enc.AppendString(caller.TrimmedPath())
+}
+
+// UnmarshalText unmarshals text to a CallerEncoder. "full" is unmarshaled to
+// FullCallerEncoder and anything else is unmarshaled to ShortCallerEncoder.
+func (e *CallerEncoder) UnmarshalText(text []byte) error {
+ switch string(text) {
+ case "full":
+ *e = FullCallerEncoder
+ default:
+ *e = ShortCallerEncoder
+ }
+ return nil
+}
+
+// A NameEncoder serializes a period-separated logger name to a primitive
+// type.
+type NameEncoder func(string, PrimitiveArrayEncoder)
+
+// FullNameEncoder serializes the logger name as-is.
+func FullNameEncoder(loggerName string, enc PrimitiveArrayEncoder) {
+ enc.AppendString(loggerName)
+}
+
+// UnmarshalText unmarshals text to a NameEncoder. Currently, everything is
+// unmarshaled to FullNameEncoder.
+func (e *NameEncoder) UnmarshalText(text []byte) error {
+ switch string(text) {
+ case "full":
+ *e = FullNameEncoder
+ default:
+ *e = FullNameEncoder
+ }
+ return nil
+}
+
+// An EncoderConfig allows users to configure the concrete encoders supplied by
+// zapcore.
+type EncoderConfig struct {
+ // Set the keys used for each log entry. If any key is empty, that portion
+ // of the entry is omitted.
+ MessageKey string `json:"messageKey" yaml:"messageKey"`
+ LevelKey string `json:"levelKey" yaml:"levelKey"`
+ TimeKey string `json:"timeKey" yaml:"timeKey"`
+ NameKey string `json:"nameKey" yaml:"nameKey"`
+ CallerKey string `json:"callerKey" yaml:"callerKey"`
+ StacktraceKey string `json:"stacktraceKey" yaml:"stacktraceKey"`
+ LineEnding string `json:"lineEnding" yaml:"lineEnding"`
+ // Configure the primitive representations of common complex types. For
+ // example, some users may want all time.Times serialized as floating-point
+ // seconds since epoch, while others may prefer ISO8601 strings.
+ EncodeLevel LevelEncoder `json:"levelEncoder" yaml:"levelEncoder"`
+ EncodeTime TimeEncoder `json:"timeEncoder" yaml:"timeEncoder"`
+ EncodeDuration DurationEncoder `json:"durationEncoder" yaml:"durationEncoder"`
+ EncodeCaller CallerEncoder `json:"callerEncoder" yaml:"callerEncoder"`
+ // Unlike the other primitive type encoders, EncodeName is optional. The
+ // zero value falls back to FullNameEncoder.
+ EncodeName NameEncoder `json:"nameEncoder" yaml:"nameEncoder"`
+}
+
+// ObjectEncoder is a strongly-typed, encoding-agnostic interface for adding a
+// map- or struct-like object to the logging context. Like maps, ObjectEncoders
+// aren't safe for concurrent use (though typical use shouldn't require locks).
+type ObjectEncoder interface {
+ // Logging-specific marshalers.
+ AddArray(key string, marshaler ArrayMarshaler) error
+ AddObject(key string, marshaler ObjectMarshaler) error
+
+ // Built-in types.
+ AddBinary(key string, value []byte) // for arbitrary bytes
+ AddByteString(key string, value []byte) // for UTF-8 encoded bytes
+ AddBool(key string, value bool)
+ AddComplex128(key string, value complex128)
+ AddComplex64(key string, value complex64)
+ AddDuration(key string, value time.Duration)
+ AddFloat64(key string, value float64)
+ AddFloat32(key string, value float32)
+ AddInt(key string, value int)
+ AddInt64(key string, value int64)
+ AddInt32(key string, value int32)
+ AddInt16(key string, value int16)
+ AddInt8(key string, value int8)
+ AddString(key, value string)
+ AddTime(key string, value time.Time)
+ AddUint(key string, value uint)
+ AddUint64(key string, value uint64)
+ AddUint32(key string, value uint32)
+ AddUint16(key string, value uint16)
+ AddUint8(key string, value uint8)
+ AddUintptr(key string, value uintptr)
+
+ // AddReflected uses reflection to serialize arbitrary objects, so it's slow
+ // and allocation-heavy.
+ AddReflected(key string, value interface{}) error
+ // OpenNamespace opens an isolated namespace where all subsequent fields will
+ // be added. Applications can use namespaces to prevent key collisions when
+ // injecting loggers into sub-components or third-party libraries.
+ OpenNamespace(key string)
+}
+
+// ArrayEncoder is a strongly-typed, encoding-agnostic interface for adding
+// array-like objects to the logging context. Of note, it supports mixed-type
+// arrays even though they aren't typical in Go. Like slices, ArrayEncoders
+// aren't safe for concurrent use (though typical use shouldn't require locks).
+type ArrayEncoder interface {
+ // Built-in types.
+ PrimitiveArrayEncoder
+
+ // Time-related types.
+ AppendDuration(time.Duration)
+ AppendTime(time.Time)
+
+ // Logging-specific marshalers.
+ AppendArray(ArrayMarshaler) error
+ AppendObject(ObjectMarshaler) error
+
+ // AppendReflected uses reflection to serialize arbitrary objects, so it's
+ // slow and allocation-heavy.
+ AppendReflected(value interface{}) error
+}
+
+// PrimitiveArrayEncoder is the subset of the ArrayEncoder interface that deals
+// only in Go's built-in types. It's included only so that Duration- and
+// TimeEncoders cannot trigger infinite recursion.
+type PrimitiveArrayEncoder interface {
+ // Built-in types.
+ AppendBool(bool)
+ AppendByteString([]byte) // for UTF-8 encoded bytes
+ AppendComplex128(complex128)
+ AppendComplex64(complex64)
+ AppendFloat64(float64)
+ AppendFloat32(float32)
+ AppendInt(int)
+ AppendInt64(int64)
+ AppendInt32(int32)
+ AppendInt16(int16)
+ AppendInt8(int8)
+ AppendString(string)
+ AppendUint(uint)
+ AppendUint64(uint64)
+ AppendUint32(uint32)
+ AppendUint16(uint16)
+ AppendUint8(uint8)
+ AppendUintptr(uintptr)
+}
+
+// Encoder is a format-agnostic interface for all log entry marshalers. Since
+// log encoders don't need to support the same wide range of use cases as
+// general-purpose marshalers, it's possible to make them faster and
+// lower-allocation.
+//
+// Implementations of the ObjectEncoder interface's methods can, of course,
+// freely modify the receiver. However, the Clone and EncodeEntry methods will
+// be called concurrently and shouldn't modify the receiver.
+type Encoder interface {
+ ObjectEncoder
+
+ // Clone copies the encoder, ensuring that adding fields to the copy doesn't
+ // affect the original.
+ Clone() Encoder
+
+ // EncodeEntry encodes an entry and fields, along with any accumulated
+ // context, into a byte buffer and returns it.
+ EncodeEntry(Entry, []Field) (*buffer.Buffer, error)
+}
diff --git a/vendor/go.uber.org/zap/zapcore/encoder_test.go b/vendor/go.uber.org/zap/zapcore/encoder_test.go
new file mode 100644
index 0000000..0464167
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/encoder_test.go
@@ -0,0 +1,636 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore_test
+
+import (
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
+ . "go.uber.org/zap/zapcore"
+)
+
+var (
+ _epoch = time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC)
+ _testEntry = Entry{
+ LoggerName: "main",
+ Level: InfoLevel,
+ Message: `hello`,
+ Time: _epoch,
+ Stack: "fake-stack",
+ Caller: EntryCaller{Defined: true, File: "foo.go", Line: 42},
+ }
+)
+
+func testEncoderConfig() EncoderConfig {
+ return EncoderConfig{
+ MessageKey: "msg",
+ LevelKey: "level",
+ NameKey: "name",
+ TimeKey: "ts",
+ CallerKey: "caller",
+ StacktraceKey: "stacktrace",
+ LineEnding: "\n",
+ EncodeTime: EpochTimeEncoder,
+ EncodeLevel: LowercaseLevelEncoder,
+ EncodeDuration: SecondsDurationEncoder,
+ EncodeCaller: ShortCallerEncoder,
+ }
+}
+
+func humanEncoderConfig() EncoderConfig {
+ cfg := testEncoderConfig()
+ cfg.EncodeTime = ISO8601TimeEncoder
+ cfg.EncodeLevel = CapitalLevelEncoder
+ cfg.EncodeDuration = StringDurationEncoder
+ return cfg
+}
+
+func withJSONEncoder(f func(Encoder)) {
+ f(NewJSONEncoder(testEncoderConfig()))
+}
+
+func withConsoleEncoder(f func(Encoder)) {
+ f(NewConsoleEncoder(humanEncoderConfig()))
+}
+
+func capitalNameEncoder(loggerName string, enc PrimitiveArrayEncoder) {
+ enc.AppendString(strings.ToUpper(loggerName))
+}
+
+func TestEncoderConfiguration(t *testing.T) {
+ base := testEncoderConfig()
+
+ tests := []struct {
+ desc string
+ cfg EncoderConfig
+ amendEntry func(Entry) Entry
+ extra func(Encoder)
+ expectedJSON string
+ expectedConsole string
+ }{
+ {
+ desc: "messages to be escaped",
+ cfg: base,
+ amendEntry: func(ent Entry) Entry {
+ ent.Message = `hello\`
+ return ent
+ },
+ expectedJSON: `{"level":"info","ts":0,"name":"main","caller":"foo.go:42","msg":"hello\\","stacktrace":"fake-stack"}` + "\n",
+ expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\\\nfake-stack\n",
+ },
+ {
+ desc: "use custom entry keys in JSON output and ignore them in console output",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n",
+ expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\nfake-stack\n",
+ },
+ {
+ desc: "skip level if LevelKey is omitted",
+ cfg: EncoderConfig{
+ LevelKey: "",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ expectedJSON: `{"T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n",
+ expectedConsole: "0\tmain\tfoo.go:42\thello\nfake-stack\n",
+ },
+ {
+ desc: "skip timestamp if TimeKey is omitted",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ expectedJSON: `{"L":"info","N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n",
+ expectedConsole: "info\tmain\tfoo.go:42\thello\nfake-stack\n",
+ },
+ {
+ desc: "skip message if MessageKey is omitted",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "",
+ NameKey: "N",
+ CallerKey: "C",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","S":"fake-stack"}` + "\n",
+ expectedConsole: "0\tinfo\tmain\tfoo.go:42\nfake-stack\n",
+ },
+ {
+ desc: "skip name if NameKey is omitted",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "",
+ CallerKey: "C",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ expectedJSON: `{"L":"info","T":0,"C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n",
+ expectedConsole: "0\tinfo\tfoo.go:42\thello\nfake-stack\n",
+ },
+ {
+ desc: "skip caller if CallerKey is omitted",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ expectedJSON: `{"L":"info","T":0,"N":"main","M":"hello","S":"fake-stack"}` + "\n",
+ expectedConsole: "0\tinfo\tmain\thello\nfake-stack\n",
+ },
+ {
+ desc: "skip stacktrace if StacktraceKey is omitted",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ StacktraceKey: "",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello"}` + "\n",
+ expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\n",
+ },
+ {
+ desc: "use the supplied EncodeTime, for both the entry and any times added",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: func(t time.Time, enc PrimitiveArrayEncoder) { enc.AppendString(t.String()) },
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ extra: func(enc Encoder) {
+ enc.AddTime("extra", _epoch)
+ enc.AddArray("extras", ArrayMarshalerFunc(func(enc ArrayEncoder) error {
+ enc.AppendTime(_epoch)
+ return nil
+ }))
+ },
+ expectedJSON: `{"L":"info","T":"1970-01-01 00:00:00 +0000 UTC","N":"main","C":"foo.go:42","M":"hello","extra":"1970-01-01 00:00:00 +0000 UTC","extras":["1970-01-01 00:00:00 +0000 UTC"],"S":"fake-stack"}` + "\n",
+ expectedConsole: "1970-01-01 00:00:00 +0000 UTC\tinfo\tmain\tfoo.go:42\thello\t" + // plain-text preamble
+ `{"extra": "1970-01-01 00:00:00 +0000 UTC", "extras": ["1970-01-01 00:00:00 +0000 UTC"]}` + // JSON context
+ "\nfake-stack\n", // stacktrace after newline
+ },
+ {
+ desc: "use the supplied EncodeDuration for any durations added",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: StringDurationEncoder,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ extra: func(enc Encoder) {
+ enc.AddDuration("extra", time.Second)
+ enc.AddArray("extras", ArrayMarshalerFunc(func(enc ArrayEncoder) error {
+ enc.AppendDuration(time.Minute)
+ return nil
+ }))
+ },
+ expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","extra":"1s","extras":["1m0s"],"S":"fake-stack"}` + "\n",
+ expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\t" + // preamble
+ `{"extra": "1s", "extras": ["1m0s"]}` + // context
+ "\nfake-stack\n", // stacktrace
+ },
+ {
+ desc: "use the supplied EncodeLevel",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: CapitalLevelEncoder,
+ EncodeCaller: base.EncodeCaller,
+ },
+ expectedJSON: `{"L":"INFO","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n",
+ expectedConsole: "0\tINFO\tmain\tfoo.go:42\thello\nfake-stack\n",
+ },
+ {
+ desc: "use the supplied EncodeName",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ EncodeName: capitalNameEncoder,
+ },
+ expectedJSON: `{"L":"info","T":0,"N":"MAIN","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n",
+ expectedConsole: "0\tinfo\tMAIN\tfoo.go:42\thello\nfake-stack\n",
+ },
+ {
+ desc: "close all open namespaces",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ extra: func(enc Encoder) {
+ enc.OpenNamespace("outer")
+ enc.OpenNamespace("inner")
+ enc.AddString("foo", "bar")
+ enc.OpenNamespace("innermost")
+ },
+ expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","outer":{"inner":{"foo":"bar","innermost":{}}},"S":"fake-stack"}` + "\n",
+ expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\t" +
+ `{"outer": {"inner": {"foo": "bar", "innermost": {}}}}` +
+ "\nfake-stack\n",
+ },
+ {
+ desc: "handle no-op EncodeTime",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: func(time.Time, PrimitiveArrayEncoder) {},
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ extra: func(enc Encoder) { enc.AddTime("sometime", time.Unix(0, 100)) },
+ expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","sometime":100,"S":"fake-stack"}` + "\n",
+ expectedConsole: "info\tmain\tfoo.go:42\thello\t" + `{"sometime": 100}` + "\nfake-stack\n",
+ },
+ {
+ desc: "handle no-op EncodeDuration",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: func(time.Duration, PrimitiveArrayEncoder) {},
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ extra: func(enc Encoder) { enc.AddDuration("someduration", time.Microsecond) },
+ expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","someduration":1000,"S":"fake-stack"}` + "\n",
+ expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\t" + `{"someduration": 1000}` + "\nfake-stack\n",
+ },
+ {
+ desc: "handle no-op EncodeLevel",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: func(Level, PrimitiveArrayEncoder) {},
+ EncodeCaller: base.EncodeCaller,
+ },
+ expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n",
+ expectedConsole: "0\tmain\tfoo.go:42\thello\nfake-stack\n",
+ },
+ {
+ desc: "handle no-op EncodeCaller",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: func(EntryCaller, PrimitiveArrayEncoder) {},
+ },
+ expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n",
+ expectedConsole: "0\tinfo\tmain\thello\nfake-stack\n",
+ },
+ {
+ desc: "handle no-op EncodeName",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ StacktraceKey: "S",
+ LineEnding: base.LineEnding,
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ EncodeName: func(string, PrimitiveArrayEncoder) {},
+ },
+ expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n",
+ expectedConsole: "0\tinfo\tfoo.go:42\thello\nfake-stack\n",
+ },
+ {
+ desc: "use custom line separator",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ StacktraceKey: "S",
+ LineEnding: "\r\n",
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\r\n",
+ expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\nfake-stack\r\n",
+ },
+ {
+ desc: "omit line separator definition - fall back to default",
+ cfg: EncoderConfig{
+ LevelKey: "L",
+ TimeKey: "T",
+ MessageKey: "M",
+ NameKey: "N",
+ CallerKey: "C",
+ StacktraceKey: "S",
+ EncodeTime: base.EncodeTime,
+ EncodeDuration: base.EncodeDuration,
+ EncodeLevel: base.EncodeLevel,
+ EncodeCaller: base.EncodeCaller,
+ },
+ expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + DefaultLineEnding,
+ expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\nfake-stack" + DefaultLineEnding,
+ },
+ }
+
+ for i, tt := range tests {
+ json := NewJSONEncoder(tt.cfg)
+ console := NewConsoleEncoder(tt.cfg)
+ if tt.extra != nil {
+ tt.extra(json)
+ tt.extra(console)
+ }
+ entry := _testEntry
+ if tt.amendEntry != nil {
+ entry = tt.amendEntry(_testEntry)
+ }
+ jsonOut, jsonErr := json.EncodeEntry(entry, nil)
+ if assert.NoError(t, jsonErr, "Unexpected error JSON-encoding entry in case #%d.", i) {
+ assert.Equal(
+ t,
+ tt.expectedJSON,
+ jsonOut.String(),
+ "Unexpected JSON output: expected to %v.", tt.desc,
+ )
+ }
+ consoleOut, consoleErr := console.EncodeEntry(entry, nil)
+ if assert.NoError(t, consoleErr, "Unexpected error console-encoding entry in case #%d.", i) {
+ assert.Equal(
+ t,
+ tt.expectedConsole,
+ consoleOut.String(),
+ "Unexpected console output: expected to %v.", tt.desc,
+ )
+ }
+ }
+}
+
+func TestLevelEncoders(t *testing.T) {
+ tests := []struct {
+ name string
+ expected interface{} // output of encoding InfoLevel
+ }{
+ {"capital", "INFO"},
+ {"lower", "info"},
+ {"", "info"},
+ {"something-random", "info"},
+ }
+
+ for _, tt := range tests {
+ var le LevelEncoder
+ require.NoError(t, le.UnmarshalText([]byte(tt.name)), "Unexpected error unmarshaling %q.", tt.name)
+ assertAppended(
+ t,
+ tt.expected,
+ func(arr ArrayEncoder) { le(InfoLevel, arr) },
+ "Unexpected output serializing InfoLevel with %q.", tt.name,
+ )
+ }
+}
+
+func TestTimeEncoders(t *testing.T) {
+ moment := time.Unix(100, 50005000).UTC()
+ tests := []struct {
+ name string
+ expected interface{} // output of serializing moment
+ }{
+ {"iso8601", "1970-01-01T00:01:40.050Z"},
+ {"ISO8601", "1970-01-01T00:01:40.050Z"},
+ {"millis", 100050.005},
+ {"nanos", int64(100050005000)},
+ {"", 100.050005},
+ {"something-random", 100.050005},
+ }
+
+ for _, tt := range tests {
+ var te TimeEncoder
+ require.NoError(t, te.UnmarshalText([]byte(tt.name)), "Unexpected error unmarshaling %q.", tt.name)
+ assertAppended(
+ t,
+ tt.expected,
+ func(arr ArrayEncoder) { te(moment, arr) },
+ "Unexpected output serializing %v with %q.", moment, tt.name,
+ )
+ }
+}
+
+func TestDurationEncoders(t *testing.T) {
+ elapsed := time.Second + 500*time.Nanosecond
+ tests := []struct {
+ name string
+ expected interface{} // output of serializing elapsed
+ }{
+ {"string", "1.0000005s"},
+ {"nanos", int64(1000000500)},
+ {"", 1.0000005},
+ {"something-random", 1.0000005},
+ }
+
+ for _, tt := range tests {
+ var de DurationEncoder
+ require.NoError(t, de.UnmarshalText([]byte(tt.name)), "Unexpected error unmarshaling %q.", tt.name)
+ assertAppended(
+ t,
+ tt.expected,
+ func(arr ArrayEncoder) { de(elapsed, arr) },
+ "Unexpected output serializing %v with %q.", elapsed, tt.name,
+ )
+ }
+}
+
+func TestCallerEncoders(t *testing.T) {
+ caller := EntryCaller{Defined: true, File: "/home/jack/src/github.com/foo/foo.go", Line: 42}
+ tests := []struct {
+ name string
+ expected interface{} // output of serializing caller
+ }{
+ {"", "foo/foo.go:42"},
+ {"something-random", "foo/foo.go:42"},
+ {"short", "foo/foo.go:42"},
+ {"full", "/home/jack/src/github.com/foo/foo.go:42"},
+ }
+
+ for _, tt := range tests {
+ var ce CallerEncoder
+ require.NoError(t, ce.UnmarshalText([]byte(tt.name)), "Unexpected error unmarshaling %q.", tt.name)
+ assertAppended(
+ t,
+ tt.expected,
+ func(arr ArrayEncoder) { ce(caller, arr) },
+ "Unexpected output serializing file name as %v with %q.", tt.expected, tt.name,
+ )
+ }
+}
+
+func TestNameEncoders(t *testing.T) {
+ tests := []struct {
+ name string
+ expected interface{} // output of encoding InfoLevel
+ }{
+ {"", "main"},
+ {"full", "main"},
+ {"something-random", "main"},
+ }
+
+ for _, tt := range tests {
+ var ne NameEncoder
+ require.NoError(t, ne.UnmarshalText([]byte(tt.name)), "Unexpected error unmarshaling %q.", tt.name)
+ assertAppended(
+ t,
+ tt.expected,
+ func(arr ArrayEncoder) { ne("main", arr) },
+ "Unexpected output serializing logger name with %q.", tt.name,
+ )
+ }
+}
+
+func assertAppended(t testing.TB, expected interface{}, f func(ArrayEncoder), msgAndArgs ...interface{}) {
+ mem := NewMapObjectEncoder()
+ mem.AddArray("k", ArrayMarshalerFunc(func(arr ArrayEncoder) error {
+ f(arr)
+ return nil
+ }))
+ arr := mem.Fields["k"].([]interface{})
+ require.Equal(t, 1, len(arr), "Expected to append exactly one element to array.")
+ assert.Equal(t, expected, arr[0], msgAndArgs...)
+}
diff --git a/vendor/go.uber.org/zap/zapcore/entry.go b/vendor/go.uber.org/zap/zapcore/entry.go
new file mode 100644
index 0000000..7d9893f
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/entry.go
@@ -0,0 +1,257 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "fmt"
+ "strings"
+ "sync"
+ "time"
+
+ "go.uber.org/zap/internal/bufferpool"
+ "go.uber.org/zap/internal/exit"
+
+ "go.uber.org/multierr"
+)
+
+var (
+ _cePool = sync.Pool{New: func() interface{} {
+ // Pre-allocate some space for cores.
+ return &CheckedEntry{
+ cores: make([]Core, 4),
+ }
+ }}
+)
+
+func getCheckedEntry() *CheckedEntry {
+ ce := _cePool.Get().(*CheckedEntry)
+ ce.reset()
+ return ce
+}
+
+func putCheckedEntry(ce *CheckedEntry) {
+ if ce == nil {
+ return
+ }
+ _cePool.Put(ce)
+}
+
+// NewEntryCaller makes an EntryCaller from the return signature of
+// runtime.Caller.
+func NewEntryCaller(pc uintptr, file string, line int, ok bool) EntryCaller {
+ if !ok {
+ return EntryCaller{}
+ }
+ return EntryCaller{
+ PC: pc,
+ File: file,
+ Line: line,
+ Defined: true,
+ }
+}
+
+// EntryCaller represents the caller of a logging function.
+type EntryCaller struct {
+ Defined bool
+ PC uintptr
+ File string
+ Line int
+}
+
+// String returns the full path and line number of the caller.
+func (ec EntryCaller) String() string {
+ return ec.FullPath()
+}
+
+// FullPath returns a /full/path/to/package/file:line description of the
+// caller.
+func (ec EntryCaller) FullPath() string {
+ if !ec.Defined {
+ return "undefined"
+ }
+ buf := bufferpool.Get()
+ buf.AppendString(ec.File)
+ buf.AppendByte(':')
+ buf.AppendInt(int64(ec.Line))
+ caller := buf.String()
+ buf.Free()
+ return caller
+}
+
+// TrimmedPath returns a package/file:line description of the caller,
+// preserving only the leaf directory name and file name.
+func (ec EntryCaller) TrimmedPath() string {
+ if !ec.Defined {
+ return "undefined"
+ }
+ // nb. To make sure we trim the path correctly on Windows too, we
+ // counter-intuitively need to use '/' and *not* os.PathSeparator here,
+ // because the path given originates from Go stdlib, specifically
+ // runtime.Caller() which (as of Mar/17) returns forward slashes even on
+ // Windows.
+ //
+ // See https://github.com/golang/go/issues/3335
+ // and https://github.com/golang/go/issues/18151
+ //
+ // for discussion on the issue on Go side.
+ //
+ // Find the last separator.
+ //
+ idx := strings.LastIndexByte(ec.File, '/')
+ if idx == -1 {
+ return ec.FullPath()
+ }
+ // Find the penultimate separator.
+ idx = strings.LastIndexByte(ec.File[:idx], '/')
+ if idx == -1 {
+ return ec.FullPath()
+ }
+ buf := bufferpool.Get()
+ // Keep everything after the penultimate separator.
+ buf.AppendString(ec.File[idx+1:])
+ buf.AppendByte(':')
+ buf.AppendInt(int64(ec.Line))
+ caller := buf.String()
+ buf.Free()
+ return caller
+}
+
+// An Entry represents a complete log message. The entry's structured context
+// is already serialized, but the log level, time, message, and call site
+// information are available for inspection and modification.
+//
+// Entries are pooled, so any functions that accept them MUST be careful not to
+// retain references to them.
+type Entry struct {
+ Level Level
+ Time time.Time
+ LoggerName string
+ Message string
+ Caller EntryCaller
+ Stack string
+}
+
+// CheckWriteAction indicates what action to take after a log entry is
+// processed. Actions are ordered in increasing severity.
+type CheckWriteAction uint8
+
+const (
+ // WriteThenNoop indicates that nothing special needs to be done. It's the
+ // default behavior.
+ WriteThenNoop CheckWriteAction = iota
+ // WriteThenPanic causes a panic after Write.
+ WriteThenPanic
+ // WriteThenFatal causes a fatal os.Exit after Write.
+ WriteThenFatal
+)
+
+// CheckedEntry is an Entry together with a collection of Cores that have
+// already agreed to log it.
+//
+// CheckedEntry references should be created by calling AddCore or Should on a
+// nil *CheckedEntry. References are returned to a pool after Write, and MUST
+// NOT be retained after calling their Write method.
+type CheckedEntry struct {
+ Entry
+ ErrorOutput WriteSyncer
+ dirty bool // best-effort detection of pool misuse
+ should CheckWriteAction
+ cores []Core
+}
+
+func (ce *CheckedEntry) reset() {
+ ce.Entry = Entry{}
+ ce.ErrorOutput = nil
+ ce.dirty = false
+ ce.should = WriteThenNoop
+ for i := range ce.cores {
+ // don't keep references to cores
+ ce.cores[i] = nil
+ }
+ ce.cores = ce.cores[:0]
+}
+
+// Write writes the entry to the stored Cores, returns any errors, and returns
+// the CheckedEntry reference to a pool for immediate re-use. Finally, it
+// executes any required CheckWriteAction.
+func (ce *CheckedEntry) Write(fields ...Field) {
+ if ce == nil {
+ return
+ }
+
+ if ce.dirty {
+ if ce.ErrorOutput != nil {
+ // Make a best effort to detect unsafe re-use of this CheckedEntry.
+ // If the entry is dirty, log an internal error; because the
+ // CheckedEntry is being used after it was returned to the pool,
+ // the message may be an amalgamation from multiple call sites.
+ fmt.Fprintf(ce.ErrorOutput, "%v Unsafe CheckedEntry re-use near Entry %+v.\n", time.Now(), ce.Entry)
+ ce.ErrorOutput.Sync()
+ }
+ return
+ }
+ ce.dirty = true
+
+ var err error
+ for i := range ce.cores {
+ err = multierr.Append(err, ce.cores[i].Write(ce.Entry, fields))
+ }
+ if ce.ErrorOutput != nil {
+ if err != nil {
+ fmt.Fprintf(ce.ErrorOutput, "%v write error: %v\n", time.Now(), err)
+ ce.ErrorOutput.Sync()
+ }
+ }
+
+ should, msg := ce.should, ce.Message
+ putCheckedEntry(ce)
+
+ switch should {
+ case WriteThenPanic:
+ panic(msg)
+ case WriteThenFatal:
+ exit.Exit()
+ }
+}
+
+// AddCore adds a Core that has agreed to log this CheckedEntry. It's intended to be
+// used by Core.Check implementations, and is safe to call on nil CheckedEntry
+// references.
+func (ce *CheckedEntry) AddCore(ent Entry, core Core) *CheckedEntry {
+ if ce == nil {
+ ce = getCheckedEntry()
+ ce.Entry = ent
+ }
+ ce.cores = append(ce.cores, core)
+ return ce
+}
+
+// Should sets this CheckedEntry's CheckWriteAction, which controls whether a
+// Core will panic or fatal after writing this log entry. Like AddCore, it's
+// safe to call on nil CheckedEntry references.
+func (ce *CheckedEntry) Should(ent Entry, should CheckWriteAction) *CheckedEntry {
+ if ce == nil {
+ ce = getCheckedEntry()
+ ce.Entry = ent
+ }
+ ce.should = should
+ return ce
+}
diff --git a/vendor/go.uber.org/zap/zapcore/entry_test.go b/vendor/go.uber.org/zap/zapcore/entry_test.go
new file mode 100644
index 0000000..569c4e1
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/entry_test.go
@@ -0,0 +1,107 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "sync"
+ "testing"
+
+ "go.uber.org/zap/internal/exit"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestPutNilEntry(t *testing.T) {
+ // Pooling nil entries defeats the purpose.
+ var wg sync.WaitGroup
+ wg.Add(2)
+
+ go func() {
+ defer wg.Done()
+ for i := 0; i < 1000; i++ {
+ putCheckedEntry(nil)
+ }
+ }()
+
+ go func() {
+ defer wg.Done()
+ for i := 0; i < 1000; i++ {
+ ce := getCheckedEntry()
+ assert.NotNil(t, ce, "Expected only non-nil CheckedEntries in pool.")
+ assert.False(t, ce.dirty, "Unexpected dirty bit set.")
+ assert.Nil(t, ce.ErrorOutput, "Non-nil ErrorOutput.")
+ assert.Equal(t, WriteThenNoop, ce.should, "Unexpected terminal behavior.")
+ assert.Equal(t, 0, len(ce.cores), "Expected empty slice of cores.")
+ assert.True(t, cap(ce.cores) > 0, "Expected pooled CheckedEntries to pre-allocate slice of Cores.")
+ }
+ }()
+
+ wg.Wait()
+}
+
+func TestEntryCaller(t *testing.T) {
+ tests := []struct {
+ caller EntryCaller
+ full string
+ short string
+ }{
+ {
+ caller: NewEntryCaller(100, "/path/to/foo.go", 42, false),
+ full: "undefined",
+ short: "undefined",
+ },
+ {
+ caller: NewEntryCaller(100, "/path/to/foo.go", 42, true),
+ full: "/path/to/foo.go:42",
+ short: "to/foo.go:42",
+ },
+ {
+ caller: NewEntryCaller(100, "to/foo.go", 42, true),
+ full: "to/foo.go:42",
+ short: "to/foo.go:42",
+ },
+ }
+
+ for _, tt := range tests {
+ assert.Equal(t, tt.full, tt.caller.String(), "Unexpected string from EntryCaller.")
+ assert.Equal(t, tt.full, tt.caller.FullPath(), "Unexpected FullPath from EntryCaller.")
+ assert.Equal(t, tt.short, tt.caller.TrimmedPath(), "Unexpected TrimmedPath from EntryCaller.")
+ }
+}
+
+func TestCheckedEntryWrite(t *testing.T) {
+ // Nil checked entries are safe.
+ var ce *CheckedEntry
+ assert.NotPanics(t, func() { ce.Write() }, "Unexpected panic writing nil CheckedEntry.")
+
+ // WriteThenPanic
+ ce = ce.Should(Entry{}, WriteThenPanic)
+ assert.Panics(t, func() { ce.Write() }, "Expected to panic when WriteThenPanic is set.")
+ ce.reset()
+
+ // WriteThenFatal
+ ce = ce.Should(Entry{}, WriteThenFatal)
+ stub := exit.WithStub(func() {
+ ce.Write()
+ })
+ assert.True(t, stub.Exited, "Expected to exit when WriteThenFatal is set.")
+ ce.reset()
+}
diff --git a/vendor/go.uber.org/zap/zapcore/error.go b/vendor/go.uber.org/zap/zapcore/error.go
new file mode 100644
index 0000000..a67c7ba
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/error.go
@@ -0,0 +1,120 @@
+// Copyright (c) 2017 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "fmt"
+ "sync"
+)
+
+// Encodes the given error into fields of an object. A field with the given
+// name is added for the error message.
+//
+// If the error implements fmt.Formatter, a field with the name ${key}Verbose
+// is also added with the full verbose error message.
+//
+// Finally, if the error implements errorGroup (from go.uber.org/multierr) or
+// causer (from github.com/pkg/errors), a ${key}Causes field is added with an
+// array of objects containing the errors this error was comprised of.
+//
+// {
+// "error": err.Error(),
+// "errorVerbose": fmt.Sprintf("%+v", err),
+// "errorCauses": [
+// ...
+// ],
+// }
+func encodeError(key string, err error, enc ObjectEncoder) error {
+ basic := err.Error()
+ enc.AddString(key, basic)
+
+ switch e := err.(type) {
+ case errorGroup:
+ return enc.AddArray(key+"Causes", errArray(e.Errors()))
+ case fmt.Formatter:
+ verbose := fmt.Sprintf("%+v", e)
+ if verbose != basic {
+ // This is a rich error type, like those produced by
+ // github.com/pkg/errors.
+ enc.AddString(key+"Verbose", verbose)
+ }
+ }
+ return nil
+}
+
+type errorGroup interface {
+ // Provides read-only access to the underlying list of errors, preferably
+ // without causing any allocs.
+ Errors() []error
+}
+
+type causer interface {
+ // Provides access to the error that caused this error.
+ Cause() error
+}
+
+// Note that errArry and errArrayElem are very similar to the version
+// implemented in the top-level error.go file. We can't re-use this because
+// that would require exporting errArray as part of the zapcore API.
+
+// Encodes a list of errors using the standard error encoding logic.
+type errArray []error
+
+func (errs errArray) MarshalLogArray(arr ArrayEncoder) error {
+ for i := range errs {
+ if errs[i] == nil {
+ continue
+ }
+
+ el := newErrArrayElem(errs[i])
+ arr.AppendObject(el)
+ el.Free()
+ }
+ return nil
+}
+
+var _errArrayElemPool = sync.Pool{New: func() interface{} {
+ return &errArrayElem{}
+}}
+
+// Encodes any error into a {"error": ...} re-using the same errors logic.
+//
+// May be passed in place of an array to build a single-element array.
+type errArrayElem struct{ err error }
+
+func newErrArrayElem(err error) *errArrayElem {
+ e := _errArrayElemPool.Get().(*errArrayElem)
+ e.err = err
+ return e
+}
+
+func (e *errArrayElem) MarshalLogArray(arr ArrayEncoder) error {
+ return arr.AppendObject(e)
+}
+
+func (e *errArrayElem) MarshalLogObject(enc ObjectEncoder) error {
+ return encodeError("error", e.err, enc)
+}
+
+func (e *errArrayElem) Free() {
+ e.err = nil
+ _errArrayElemPool.Put(e)
+}
diff --git a/vendor/go.uber.org/zap/zapcore/error_test.go b/vendor/go.uber.org/zap/zapcore/error_test.go
new file mode 100644
index 0000000..900f520
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/error_test.go
@@ -0,0 +1,177 @@
+// Copyright (c) 2017 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore_test
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "testing"
+
+ richErrors "github.com/pkg/errors"
+ "github.com/stretchr/testify/assert"
+
+ "go.uber.org/multierr"
+ . "go.uber.org/zap/zapcore"
+)
+
+type errTooManyUsers int
+
+func (e errTooManyUsers) Error() string {
+ return fmt.Sprintf("%d too many users", int(e))
+}
+
+func (e errTooManyUsers) Format(s fmt.State, verb rune) {
+ // Implement fmt.Formatter, but don't add any information beyond the basic
+ // Error method.
+ if verb == 'v' && s.Flag('+') {
+ io.WriteString(s, e.Error())
+ }
+}
+
+type customMultierr struct{}
+
+func (e customMultierr) Error() string {
+ return "great sadness"
+}
+
+func (e customMultierr) Errors() []error {
+ return []error{
+ errors.New("foo"),
+ nil,
+ multierr.Append(
+ errors.New("bar"),
+ errors.New("baz"),
+ ),
+ }
+}
+
+func TestErrorEncoding(t *testing.T) {
+ tests := []struct {
+ k string
+ t FieldType // defaults to ErrorType
+ iface interface{}
+ want map[string]interface{}
+ }{
+ {
+ k: "k",
+ iface: errTooManyUsers(2),
+ want: map[string]interface{}{
+ "k": "2 too many users",
+ },
+ },
+ {
+ k: "err",
+ iface: multierr.Combine(
+ errors.New("foo"),
+ errors.New("bar"),
+ errors.New("baz"),
+ ),
+ want: map[string]interface{}{
+ "err": "foo; bar; baz",
+ "errCauses": []interface{}{
+ map[string]interface{}{"error": "foo"},
+ map[string]interface{}{"error": "bar"},
+ map[string]interface{}{"error": "baz"},
+ },
+ },
+ },
+ {
+ k: "e",
+ iface: customMultierr{},
+ want: map[string]interface{}{
+ "e": "great sadness",
+ "eCauses": []interface{}{
+ map[string]interface{}{"error": "foo"},
+ map[string]interface{}{
+ "error": "bar; baz",
+ "errorCauses": []interface{}{
+ map[string]interface{}{"error": "bar"},
+ map[string]interface{}{"error": "baz"},
+ },
+ },
+ },
+ },
+ },
+ {
+ k: "k",
+ iface: richErrors.WithMessage(errors.New("egad"), "failed"),
+ want: map[string]interface{}{
+ "k": "failed: egad",
+ "kVerbose": "egad\nfailed",
+ },
+ },
+ {
+ k: "error",
+ iface: multierr.Combine(
+ richErrors.WithMessage(
+ multierr.Combine(errors.New("foo"), errors.New("bar")),
+ "hello",
+ ),
+ errors.New("baz"),
+ richErrors.WithMessage(errors.New("qux"), "world"),
+ ),
+ want: map[string]interface{}{
+ "error": "hello: foo; bar; baz; world: qux",
+ "errorCauses": []interface{}{
+ map[string]interface{}{
+ "error": "hello: foo; bar",
+ "errorVerbose": "the following errors occurred:\n" +
+ " - foo\n" +
+ " - bar\n" +
+ "hello",
+ },
+ map[string]interface{}{"error": "baz"},
+ map[string]interface{}{"error": "world: qux", "errorVerbose": "qux\nworld"},
+ },
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ if tt.t == UnknownType {
+ tt.t = ErrorType
+ }
+
+ enc := NewMapObjectEncoder()
+ f := Field{Key: tt.k, Type: tt.t, Interface: tt.iface}
+ f.AddTo(enc)
+ assert.Equal(t, tt.want, enc.Fields, "Unexpected output from field %+v.", f)
+ }
+}
+
+func TestRichErrorSupport(t *testing.T) {
+ f := Field{
+ Type: ErrorType,
+ Interface: richErrors.WithMessage(richErrors.New("egad"), "failed"),
+ Key: "k",
+ }
+ enc := NewMapObjectEncoder()
+ f.AddTo(enc)
+ assert.Equal(t, "failed: egad", enc.Fields["k"], "Unexpected basic error message.")
+
+ serialized := enc.Fields["kVerbose"]
+ // Don't assert the exact format used by a third-party package, but ensure
+ // that some critical elements are present.
+ assert.Regexp(t, `egad`, serialized, "Expected original error message to be present.")
+ assert.Regexp(t, `failed`, serialized, "Expected error annotation to be present.")
+ assert.Regexp(t, `TestRichErrorSupport`, serialized, "Expected calling function to be present in stacktrace.")
+}
diff --git a/vendor/go.uber.org/zap/zapcore/field.go b/vendor/go.uber.org/zap/zapcore/field.go
new file mode 100644
index 0000000..6a5e33e
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/field.go
@@ -0,0 +1,201 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "bytes"
+ "fmt"
+ "math"
+ "reflect"
+ "time"
+)
+
+// A FieldType indicates which member of the Field union struct should be used
+// and how it should be serialized.
+type FieldType uint8
+
+const (
+ // UnknownType is the default field type. Attempting to add it to an encoder will panic.
+ UnknownType FieldType = iota
+ // ArrayMarshalerType indicates that the field carries an ArrayMarshaler.
+ ArrayMarshalerType
+ // ObjectMarshalerType indicates that the field carries an ObjectMarshaler.
+ ObjectMarshalerType
+ // BinaryType indicates that the field carries an opaque binary blob.
+ BinaryType
+ // BoolType indicates that the field carries a bool.
+ BoolType
+ // ByteStringType indicates that the field carries UTF-8 encoded bytes.
+ ByteStringType
+ // Complex128Type indicates that the field carries a complex128.
+ Complex128Type
+ // Complex64Type indicates that the field carries a complex128.
+ Complex64Type
+ // DurationType indicates that the field carries a time.Duration.
+ DurationType
+ // Float64Type indicates that the field carries a float64.
+ Float64Type
+ // Float32Type indicates that the field carries a float32.
+ Float32Type
+ // Int64Type indicates that the field carries an int64.
+ Int64Type
+ // Int32Type indicates that the field carries an int32.
+ Int32Type
+ // Int16Type indicates that the field carries an int16.
+ Int16Type
+ // Int8Type indicates that the field carries an int8.
+ Int8Type
+ // StringType indicates that the field carries a string.
+ StringType
+ // TimeType indicates that the field carries a time.Time.
+ TimeType
+ // Uint64Type indicates that the field carries a uint64.
+ Uint64Type
+ // Uint32Type indicates that the field carries a uint32.
+ Uint32Type
+ // Uint16Type indicates that the field carries a uint16.
+ Uint16Type
+ // Uint8Type indicates that the field carries a uint8.
+ Uint8Type
+ // UintptrType indicates that the field carries a uintptr.
+ UintptrType
+ // ReflectType indicates that the field carries an interface{}, which should
+ // be serialized using reflection.
+ ReflectType
+ // NamespaceType signals the beginning of an isolated namespace. All
+ // subsequent fields should be added to the new namespace.
+ NamespaceType
+ // StringerType indicates that the field carries a fmt.Stringer.
+ StringerType
+ // ErrorType indicates that the field carries an error.
+ ErrorType
+ // SkipType indicates that the field is a no-op.
+ SkipType
+)
+
+// A Field is a marshaling operation used to add a key-value pair to a logger's
+// context. Most fields are lazily marshaled, so it's inexpensive to add fields
+// to disabled debug-level log statements.
+type Field struct {
+ Key string
+ Type FieldType
+ Integer int64
+ String string
+ Interface interface{}
+}
+
+// AddTo exports a field through the ObjectEncoder interface. It's primarily
+// useful to library authors, and shouldn't be necessary in most applications.
+func (f Field) AddTo(enc ObjectEncoder) {
+ var err error
+
+ switch f.Type {
+ case ArrayMarshalerType:
+ err = enc.AddArray(f.Key, f.Interface.(ArrayMarshaler))
+ case ObjectMarshalerType:
+ err = enc.AddObject(f.Key, f.Interface.(ObjectMarshaler))
+ case BinaryType:
+ enc.AddBinary(f.Key, f.Interface.([]byte))
+ case BoolType:
+ enc.AddBool(f.Key, f.Integer == 1)
+ case ByteStringType:
+ enc.AddByteString(f.Key, f.Interface.([]byte))
+ case Complex128Type:
+ enc.AddComplex128(f.Key, f.Interface.(complex128))
+ case Complex64Type:
+ enc.AddComplex64(f.Key, f.Interface.(complex64))
+ case DurationType:
+ enc.AddDuration(f.Key, time.Duration(f.Integer))
+ case Float64Type:
+ enc.AddFloat64(f.Key, math.Float64frombits(uint64(f.Integer)))
+ case Float32Type:
+ enc.AddFloat32(f.Key, math.Float32frombits(uint32(f.Integer)))
+ case Int64Type:
+ enc.AddInt64(f.Key, f.Integer)
+ case Int32Type:
+ enc.AddInt32(f.Key, int32(f.Integer))
+ case Int16Type:
+ enc.AddInt16(f.Key, int16(f.Integer))
+ case Int8Type:
+ enc.AddInt8(f.Key, int8(f.Integer))
+ case StringType:
+ enc.AddString(f.Key, f.String)
+ case TimeType:
+ if f.Interface != nil {
+ enc.AddTime(f.Key, time.Unix(0, f.Integer).In(f.Interface.(*time.Location)))
+ } else {
+ // Fall back to UTC if location is nil.
+ enc.AddTime(f.Key, time.Unix(0, f.Integer))
+ }
+ case Uint64Type:
+ enc.AddUint64(f.Key, uint64(f.Integer))
+ case Uint32Type:
+ enc.AddUint32(f.Key, uint32(f.Integer))
+ case Uint16Type:
+ enc.AddUint16(f.Key, uint16(f.Integer))
+ case Uint8Type:
+ enc.AddUint8(f.Key, uint8(f.Integer))
+ case UintptrType:
+ enc.AddUintptr(f.Key, uintptr(f.Integer))
+ case ReflectType:
+ err = enc.AddReflected(f.Key, f.Interface)
+ case NamespaceType:
+ enc.OpenNamespace(f.Key)
+ case StringerType:
+ enc.AddString(f.Key, f.Interface.(fmt.Stringer).String())
+ case ErrorType:
+ encodeError(f.Key, f.Interface.(error), enc)
+ case SkipType:
+ break
+ default:
+ panic(fmt.Sprintf("unknown field type: %v", f))
+ }
+
+ if err != nil {
+ enc.AddString(fmt.Sprintf("%sError", f.Key), err.Error())
+ }
+}
+
+// Equals returns whether two fields are equal. For non-primitive types such as
+// errors, marshalers, or reflect types, it uses reflect.DeepEqual.
+func (f Field) Equals(other Field) bool {
+ if f.Type != other.Type {
+ return false
+ }
+ if f.Key != other.Key {
+ return false
+ }
+
+ switch f.Type {
+ case BinaryType, ByteStringType:
+ return bytes.Equal(f.Interface.([]byte), other.Interface.([]byte))
+ case ArrayMarshalerType, ObjectMarshalerType, ErrorType, ReflectType:
+ return reflect.DeepEqual(f.Interface, other.Interface)
+ default:
+ return f == other
+ }
+}
+
+func addFields(enc ObjectEncoder, fields []Field) {
+ for i := range fields {
+ fields[i].AddTo(enc)
+ }
+}
diff --git a/vendor/go.uber.org/zap/zapcore/field_test.go b/vendor/go.uber.org/zap/zapcore/field_test.go
new file mode 100644
index 0000000..2d8a45f
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/field_test.go
@@ -0,0 +1,231 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore_test
+
+import (
+ "errors"
+ "fmt"
+ "math"
+ "testing"
+ "time"
+
+ "go.uber.org/zap"
+
+ "github.com/stretchr/testify/assert"
+
+ . "go.uber.org/zap/zapcore"
+)
+
+type users int
+
+func (u users) String() string {
+ return fmt.Sprintf("%d users", int(u))
+}
+
+func (u users) MarshalLogObject(enc ObjectEncoder) error {
+ if int(u) < 0 {
+ return errors.New("too few users")
+ }
+ enc.AddInt("users", int(u))
+ return nil
+}
+
+func (u users) MarshalLogArray(enc ArrayEncoder) error {
+ if int(u) < 0 {
+ return errors.New("too few users")
+ }
+ for i := 0; i < int(u); i++ {
+ enc.AppendString("user")
+ }
+ return nil
+}
+
+func TestUnknownFieldType(t *testing.T) {
+ unknown := Field{Key: "k", String: "foo"}
+ assert.Equal(t, UnknownType, unknown.Type, "Expected zero value of FieldType to be UnknownType.")
+ assert.Panics(t, func() {
+ unknown.AddTo(NewMapObjectEncoder())
+ }, "Expected using a field with unknown type to panic.")
+}
+
+func TestFieldAddingError(t *testing.T) {
+ tests := []struct {
+ t FieldType
+ want interface{}
+ }{
+ {ArrayMarshalerType, []interface{}(nil)},
+ {ObjectMarshalerType, map[string]interface{}{}},
+ }
+ for _, tt := range tests {
+ f := Field{Key: "k", Interface: users(-1), Type: tt.t}
+ enc := NewMapObjectEncoder()
+ assert.NotPanics(t, func() { f.AddTo(enc) }, "Unexpected panic when adding fields returns an error.")
+ assert.Equal(t, tt.want, enc.Fields["k"], "On error, expected zero value in field.Key.")
+ assert.Equal(t, "too few users", enc.Fields["kError"], "Expected error message in log context.")
+ }
+}
+
+func TestFields(t *testing.T) {
+ tests := []struct {
+ t FieldType
+ i int64
+ s string
+ iface interface{}
+ want interface{}
+ }{
+ {t: ArrayMarshalerType, iface: users(2), want: []interface{}{"user", "user"}},
+ {t: ObjectMarshalerType, iface: users(2), want: map[string]interface{}{"users": 2}},
+ {t: BinaryType, iface: []byte("foo"), want: []byte("foo")},
+ {t: BoolType, i: 0, want: false},
+ {t: ByteStringType, iface: []byte("foo"), want: []byte("foo")},
+ {t: Complex128Type, iface: 1 + 2i, want: 1 + 2i},
+ {t: Complex64Type, iface: complex64(1 + 2i), want: complex64(1 + 2i)},
+ {t: DurationType, i: 1000, want: time.Microsecond},
+ {t: Float64Type, i: int64(math.Float64bits(3.14)), want: 3.14},
+ {t: Float32Type, i: int64(math.Float32bits(3.14)), want: float32(3.14)},
+ {t: Int64Type, i: 42, want: int64(42)},
+ {t: Int32Type, i: 42, want: int32(42)},
+ {t: Int16Type, i: 42, want: int16(42)},
+ {t: Int8Type, i: 42, want: int8(42)},
+ {t: StringType, s: "foo", want: "foo"},
+ {t: TimeType, i: 1000, iface: time.UTC, want: time.Unix(0, 1000).In(time.UTC)},
+ {t: TimeType, i: 1000, want: time.Unix(0, 1000)},
+ {t: Uint64Type, i: 42, want: uint64(42)},
+ {t: Uint32Type, i: 42, want: uint32(42)},
+ {t: Uint16Type, i: 42, want: uint16(42)},
+ {t: Uint8Type, i: 42, want: uint8(42)},
+ {t: UintptrType, i: 42, want: uintptr(42)},
+ {t: ReflectType, iface: users(2), want: users(2)},
+ {t: NamespaceType, want: map[string]interface{}{}},
+ {t: StringerType, iface: users(2), want: "2 users"},
+ {t: SkipType, want: interface{}(nil)},
+ }
+
+ for _, tt := range tests {
+ enc := NewMapObjectEncoder()
+ f := Field{Key: "k", Type: tt.t, Integer: tt.i, Interface: tt.iface, String: tt.s}
+ f.AddTo(enc)
+ assert.Equal(t, tt.want, enc.Fields["k"], "Unexpected output from field %+v.", f)
+
+ delete(enc.Fields, "k")
+ assert.Equal(t, 0, len(enc.Fields), "Unexpected extra fields present.")
+
+ assert.True(t, f.Equals(f), "Field does not equal itself")
+ }
+}
+
+func TestEquals(t *testing.T) {
+ tests := []struct {
+ a, b Field
+ want bool
+ }{
+ {
+ a: zap.Int16("a", 1),
+ b: zap.Int32("a", 1),
+ want: false,
+ },
+ {
+ a: zap.String("k", "a"),
+ b: zap.String("k", "a"),
+ want: true,
+ },
+ {
+ a: zap.String("k", "a"),
+ b: zap.String("k2", "a"),
+ want: false,
+ },
+ {
+ a: zap.String("k", "a"),
+ b: zap.String("k", "b"),
+ want: false,
+ },
+ {
+ a: zap.Time("k", time.Unix(1000, 1000)),
+ b: zap.Time("k", time.Unix(1000, 1000)),
+ want: true,
+ },
+ {
+ a: zap.Time("k", time.Unix(1000, 1000).In(time.UTC)),
+ b: zap.Time("k", time.Unix(1000, 1000).In(time.FixedZone("TEST", -8))),
+ want: false,
+ },
+ {
+ a: zap.Time("k", time.Unix(1000, 1000)),
+ b: zap.Time("k", time.Unix(1000, 2000)),
+ want: false,
+ },
+ {
+ a: zap.Binary("k", []byte{1, 2}),
+ b: zap.Binary("k", []byte{1, 2}),
+ want: true,
+ },
+ {
+ a: zap.Binary("k", []byte{1, 2}),
+ b: zap.Binary("k", []byte{1, 3}),
+ want: false,
+ },
+ {
+ a: zap.ByteString("k", []byte("abc")),
+ b: zap.ByteString("k", []byte("abc")),
+ want: true,
+ },
+ {
+ a: zap.ByteString("k", []byte("abc")),
+ b: zap.ByteString("k", []byte("abd")),
+ want: false,
+ },
+ {
+ a: zap.Ints("k", []int{1, 2}),
+ b: zap.Ints("k", []int{1, 2}),
+ want: true,
+ },
+ {
+ a: zap.Ints("k", []int{1, 2}),
+ b: zap.Ints("k", []int{1, 3}),
+ want: false,
+ },
+ {
+ a: zap.Object("k", users(10)),
+ b: zap.Object("k", users(10)),
+ want: true,
+ },
+ {
+ a: zap.Object("k", users(10)),
+ b: zap.Object("k", users(20)),
+ want: false,
+ },
+ {
+ a: zap.Any("k", map[string]string{"a": "b"}),
+ b: zap.Any("k", map[string]string{"a": "b"}),
+ want: true,
+ },
+ {
+ a: zap.Any("k", map[string]string{"a": "b"}),
+ b: zap.Any("k", map[string]string{"a": "d"}),
+ want: false,
+ },
+ }
+
+ for _, tt := range tests {
+ assert.Equal(t, tt.want, tt.a.Equals(tt.b), "a.Equals(b) a: %#v b: %#v", tt.a, tt.b)
+ assert.Equal(t, tt.want, tt.b.Equals(tt.a), "b.Equals(a) a: %#v b: %#v", tt.a, tt.b)
+ }
+}
diff --git a/vendor/go.uber.org/zap/zapcore/hook.go b/vendor/go.uber.org/zap/zapcore/hook.go
new file mode 100644
index 0000000..5db4afb
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/hook.go
@@ -0,0 +1,68 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import "go.uber.org/multierr"
+
+type hooked struct {
+ Core
+ funcs []func(Entry) error
+}
+
+// RegisterHooks wraps a Core and runs a collection of user-defined callback
+// hooks each time a message is logged. Execution of the callbacks is blocking.
+//
+// This offers users an easy way to register simple callbacks (e.g., metrics
+// collection) without implementing the full Core interface.
+func RegisterHooks(core Core, hooks ...func(Entry) error) Core {
+ funcs := append([]func(Entry) error{}, hooks...)
+ return &hooked{
+ Core: core,
+ funcs: funcs,
+ }
+}
+
+func (h *hooked) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {
+ // Let the wrapped Core decide whether to log this message or not. This
+ // also gives the downstream a chance to register itself directly with the
+ // CheckedEntry.
+ if downstream := h.Core.Check(ent, ce); downstream != nil {
+ return downstream.AddCore(ent, h)
+ }
+ return ce
+}
+
+func (h *hooked) With(fields []Field) Core {
+ return &hooked{
+ Core: h.Core.With(fields),
+ funcs: h.funcs,
+ }
+}
+
+func (h *hooked) Write(ent Entry, _ []Field) error {
+ // Since our downstream had a chance to register itself directly with the
+ // CheckedMessage, we don't need to call it here.
+ var err error
+ for i := range h.funcs {
+ err = multierr.Append(err, h.funcs[i](ent))
+ }
+ return err
+}
diff --git a/vendor/go.uber.org/zap/zapcore/hook_test.go b/vendor/go.uber.org/zap/zapcore/hook_test.go
new file mode 100644
index 0000000..0764888
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/hook_test.go
@@ -0,0 +1,73 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore_test
+
+import (
+ "testing"
+
+ . "go.uber.org/zap/zapcore"
+ "go.uber.org/zap/zaptest/observer"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestHooks(t *testing.T) {
+ tests := []struct {
+ entryLevel Level
+ coreLevel Level
+ expectCall bool
+ }{
+ {DebugLevel, InfoLevel, false},
+ {InfoLevel, InfoLevel, true},
+ {WarnLevel, InfoLevel, true},
+ }
+
+ for _, tt := range tests {
+ fac, logs := observer.New(tt.coreLevel)
+ intField := makeInt64Field("foo", 42)
+ ent := Entry{Message: "bar", Level: tt.entryLevel}
+
+ var called int
+ f := func(e Entry) error {
+ called++
+ assert.Equal(t, ent, e, "Hook called with unexpected Entry.")
+ return nil
+ }
+
+ h := RegisterHooks(fac, f)
+ if ce := h.With([]Field{intField}).Check(ent, nil); ce != nil {
+ ce.Write()
+ }
+
+ if tt.expectCall {
+ assert.Equal(t, 1, called, "Expected to call hook once.")
+ assert.Equal(
+ t,
+ []observer.LoggedEntry{{Entry: ent, Context: []Field{intField}}},
+ logs.AllUntimed(),
+ "Unexpected logs written out.",
+ )
+ } else {
+ assert.Equal(t, 0, called, "Didn't expect to call hook.")
+ assert.Equal(t, 0, logs.Len(), "Unexpected logs written out.")
+ }
+ }
+}
diff --git a/vendor/go.uber.org/zap/zapcore/json_encoder.go b/vendor/go.uber.org/zap/zapcore/json_encoder.go
new file mode 100644
index 0000000..1006ba2
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/json_encoder.go
@@ -0,0 +1,480 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "encoding/base64"
+ "encoding/json"
+ "math"
+ "sync"
+ "time"
+ "unicode/utf8"
+
+ "go.uber.org/zap/buffer"
+ "go.uber.org/zap/internal/bufferpool"
+)
+
+// For JSON-escaping; see jsonEncoder.safeAddString below.
+const _hex = "0123456789abcdef"
+
+var _jsonPool = sync.Pool{New: func() interface{} {
+ return &jsonEncoder{}
+}}
+
+func getJSONEncoder() *jsonEncoder {
+ return _jsonPool.Get().(*jsonEncoder)
+}
+
+func putJSONEncoder(enc *jsonEncoder) {
+ enc.EncoderConfig = nil
+ enc.buf = nil
+ enc.spaced = false
+ enc.openNamespaces = 0
+ _jsonPool.Put(enc)
+}
+
+type jsonEncoder struct {
+ *EncoderConfig
+ buf *buffer.Buffer
+ spaced bool // include spaces after colons and commas
+ openNamespaces int
+}
+
+// NewJSONEncoder creates a fast, low-allocation JSON encoder. The encoder
+// appropriately escapes all field keys and values.
+//
+// Note that the encoder doesn't deduplicate keys, so it's possible to produce
+// a message like
+// {"foo":"bar","foo":"baz"}
+// This is permitted by the JSON specification, but not encouraged. Many
+// libraries will ignore duplicate key-value pairs (typically keeping the last
+// pair) when unmarshaling, but users should attempt to avoid adding duplicate
+// keys.
+func NewJSONEncoder(cfg EncoderConfig) Encoder {
+ return newJSONEncoder(cfg, false)
+}
+
+func newJSONEncoder(cfg EncoderConfig, spaced bool) *jsonEncoder {
+ return &jsonEncoder{
+ EncoderConfig: &cfg,
+ buf: bufferpool.Get(),
+ spaced: spaced,
+ }
+}
+
+func (enc *jsonEncoder) AddArray(key string, arr ArrayMarshaler) error {
+ enc.addKey(key)
+ return enc.AppendArray(arr)
+}
+
+func (enc *jsonEncoder) AddObject(key string, obj ObjectMarshaler) error {
+ enc.addKey(key)
+ return enc.AppendObject(obj)
+}
+
+func (enc *jsonEncoder) AddBinary(key string, val []byte) {
+ enc.AddString(key, base64.StdEncoding.EncodeToString(val))
+}
+
+func (enc *jsonEncoder) AddByteString(key string, val []byte) {
+ enc.addKey(key)
+ enc.AppendByteString(val)
+}
+
+func (enc *jsonEncoder) AddBool(key string, val bool) {
+ enc.addKey(key)
+ enc.AppendBool(val)
+}
+
+func (enc *jsonEncoder) AddComplex128(key string, val complex128) {
+ enc.addKey(key)
+ enc.AppendComplex128(val)
+}
+
+func (enc *jsonEncoder) AddDuration(key string, val time.Duration) {
+ enc.addKey(key)
+ enc.AppendDuration(val)
+}
+
+func (enc *jsonEncoder) AddFloat64(key string, val float64) {
+ enc.addKey(key)
+ enc.AppendFloat64(val)
+}
+
+func (enc *jsonEncoder) AddInt64(key string, val int64) {
+ enc.addKey(key)
+ enc.AppendInt64(val)
+}
+
+func (enc *jsonEncoder) AddReflected(key string, obj interface{}) error {
+ marshaled, err := json.Marshal(obj)
+ if err != nil {
+ return err
+ }
+ enc.addKey(key)
+ _, err = enc.buf.Write(marshaled)
+ return err
+}
+
+func (enc *jsonEncoder) OpenNamespace(key string) {
+ enc.addKey(key)
+ enc.buf.AppendByte('{')
+ enc.openNamespaces++
+}
+
+func (enc *jsonEncoder) AddString(key, val string) {
+ enc.addKey(key)
+ enc.AppendString(val)
+}
+
+func (enc *jsonEncoder) AddTime(key string, val time.Time) {
+ enc.addKey(key)
+ enc.AppendTime(val)
+}
+
+func (enc *jsonEncoder) AddUint64(key string, val uint64) {
+ enc.addKey(key)
+ enc.AppendUint64(val)
+}
+
+func (enc *jsonEncoder) AppendArray(arr ArrayMarshaler) error {
+ enc.addElementSeparator()
+ enc.buf.AppendByte('[')
+ err := arr.MarshalLogArray(enc)
+ enc.buf.AppendByte(']')
+ return err
+}
+
+func (enc *jsonEncoder) AppendObject(obj ObjectMarshaler) error {
+ enc.addElementSeparator()
+ enc.buf.AppendByte('{')
+ err := obj.MarshalLogObject(enc)
+ enc.buf.AppendByte('}')
+ return err
+}
+
+func (enc *jsonEncoder) AppendBool(val bool) {
+ enc.addElementSeparator()
+ enc.buf.AppendBool(val)
+}
+
+func (enc *jsonEncoder) AppendByteString(val []byte) {
+ enc.addElementSeparator()
+ enc.buf.AppendByte('"')
+ enc.safeAddByteString(val)
+ enc.buf.AppendByte('"')
+}
+
+func (enc *jsonEncoder) AppendComplex128(val complex128) {
+ enc.addElementSeparator()
+ // Cast to a platform-independent, fixed-size type.
+ r, i := float64(real(val)), float64(imag(val))
+ enc.buf.AppendByte('"')
+ // Because we're always in a quoted string, we can use strconv without
+ // special-casing NaN and +/-Inf.
+ enc.buf.AppendFloat(r, 64)
+ enc.buf.AppendByte('+')
+ enc.buf.AppendFloat(i, 64)
+ enc.buf.AppendByte('i')
+ enc.buf.AppendByte('"')
+}
+
+func (enc *jsonEncoder) AppendDuration(val time.Duration) {
+ cur := enc.buf.Len()
+ enc.EncodeDuration(val, enc)
+ if cur == enc.buf.Len() {
+ // User-supplied EncodeDuration is a no-op. Fall back to nanoseconds to keep
+ // JSON valid.
+ enc.AppendInt64(int64(val))
+ }
+}
+
+func (enc *jsonEncoder) AppendInt64(val int64) {
+ enc.addElementSeparator()
+ enc.buf.AppendInt(val)
+}
+
+func (enc *jsonEncoder) AppendReflected(val interface{}) error {
+ marshaled, err := json.Marshal(val)
+ if err != nil {
+ return err
+ }
+ enc.addElementSeparator()
+ _, err = enc.buf.Write(marshaled)
+ return err
+}
+
+func (enc *jsonEncoder) AppendString(val string) {
+ enc.addElementSeparator()
+ enc.buf.AppendByte('"')
+ enc.safeAddString(val)
+ enc.buf.AppendByte('"')
+}
+
+func (enc *jsonEncoder) AppendTime(val time.Time) {
+ cur := enc.buf.Len()
+ enc.EncodeTime(val, enc)
+ if cur == enc.buf.Len() {
+ // User-supplied EncodeTime is a no-op. Fall back to nanos since epoch to keep
+ // output JSON valid.
+ enc.AppendInt64(val.UnixNano())
+ }
+}
+
+func (enc *jsonEncoder) AppendUint64(val uint64) {
+ enc.addElementSeparator()
+ enc.buf.AppendUint(val)
+}
+
+func (enc *jsonEncoder) AddComplex64(k string, v complex64) { enc.AddComplex128(k, complex128(v)) }
+func (enc *jsonEncoder) AddFloat32(k string, v float32) { enc.AddFloat64(k, float64(v)) }
+func (enc *jsonEncoder) AddInt(k string, v int) { enc.AddInt64(k, int64(v)) }
+func (enc *jsonEncoder) AddInt32(k string, v int32) { enc.AddInt64(k, int64(v)) }
+func (enc *jsonEncoder) AddInt16(k string, v int16) { enc.AddInt64(k, int64(v)) }
+func (enc *jsonEncoder) AddInt8(k string, v int8) { enc.AddInt64(k, int64(v)) }
+func (enc *jsonEncoder) AddUint(k string, v uint) { enc.AddUint64(k, uint64(v)) }
+func (enc *jsonEncoder) AddUint32(k string, v uint32) { enc.AddUint64(k, uint64(v)) }
+func (enc *jsonEncoder) AddUint16(k string, v uint16) { enc.AddUint64(k, uint64(v)) }
+func (enc *jsonEncoder) AddUint8(k string, v uint8) { enc.AddUint64(k, uint64(v)) }
+func (enc *jsonEncoder) AddUintptr(k string, v uintptr) { enc.AddUint64(k, uint64(v)) }
+func (enc *jsonEncoder) AppendComplex64(v complex64) { enc.AppendComplex128(complex128(v)) }
+func (enc *jsonEncoder) AppendFloat64(v float64) { enc.appendFloat(v, 64) }
+func (enc *jsonEncoder) AppendFloat32(v float32) { enc.appendFloat(float64(v), 32) }
+func (enc *jsonEncoder) AppendInt(v int) { enc.AppendInt64(int64(v)) }
+func (enc *jsonEncoder) AppendInt32(v int32) { enc.AppendInt64(int64(v)) }
+func (enc *jsonEncoder) AppendInt16(v int16) { enc.AppendInt64(int64(v)) }
+func (enc *jsonEncoder) AppendInt8(v int8) { enc.AppendInt64(int64(v)) }
+func (enc *jsonEncoder) AppendUint(v uint) { enc.AppendUint64(uint64(v)) }
+func (enc *jsonEncoder) AppendUint32(v uint32) { enc.AppendUint64(uint64(v)) }
+func (enc *jsonEncoder) AppendUint16(v uint16) { enc.AppendUint64(uint64(v)) }
+func (enc *jsonEncoder) AppendUint8(v uint8) { enc.AppendUint64(uint64(v)) }
+func (enc *jsonEncoder) AppendUintptr(v uintptr) { enc.AppendUint64(uint64(v)) }
+
+func (enc *jsonEncoder) Clone() Encoder {
+ clone := enc.clone()
+ clone.buf.Write(enc.buf.Bytes())
+ return clone
+}
+
+func (enc *jsonEncoder) clone() *jsonEncoder {
+ clone := getJSONEncoder()
+ clone.EncoderConfig = enc.EncoderConfig
+ clone.spaced = enc.spaced
+ clone.openNamespaces = enc.openNamespaces
+ clone.buf = bufferpool.Get()
+ return clone
+}
+
+func (enc *jsonEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, error) {
+ final := enc.clone()
+ final.buf.AppendByte('{')
+
+ if final.LevelKey != "" {
+ final.addKey(final.LevelKey)
+ cur := final.buf.Len()
+ final.EncodeLevel(ent.Level, final)
+ if cur == final.buf.Len() {
+ // User-supplied EncodeLevel was a no-op. Fall back to strings to keep
+ // output JSON valid.
+ final.AppendString(ent.Level.String())
+ }
+ }
+ if final.TimeKey != "" {
+ final.AddTime(final.TimeKey, ent.Time)
+ }
+ if ent.LoggerName != "" && final.NameKey != "" {
+ final.addKey(final.NameKey)
+ cur := final.buf.Len()
+ nameEncoder := final.EncodeName
+
+ // if no name encoder provided, fall back to FullNameEncoder for backwards
+ // compatibility
+ if nameEncoder == nil {
+ nameEncoder = FullNameEncoder
+ }
+
+ nameEncoder(ent.LoggerName, final)
+ if cur == final.buf.Len() {
+ // User-supplied EncodeName was a no-op. Fall back to strings to
+ // keep output JSON valid.
+ final.AppendString(ent.LoggerName)
+ }
+ }
+ if ent.Caller.Defined && final.CallerKey != "" {
+ final.addKey(final.CallerKey)
+ cur := final.buf.Len()
+ final.EncodeCaller(ent.Caller, final)
+ if cur == final.buf.Len() {
+ // User-supplied EncodeCaller was a no-op. Fall back to strings to
+ // keep output JSON valid.
+ final.AppendString(ent.Caller.String())
+ }
+ }
+ if final.MessageKey != "" {
+ final.addKey(enc.MessageKey)
+ final.AppendString(ent.Message)
+ }
+ if enc.buf.Len() > 0 {
+ final.addElementSeparator()
+ final.buf.Write(enc.buf.Bytes())
+ }
+ addFields(final, fields)
+ final.closeOpenNamespaces()
+ if ent.Stack != "" && final.StacktraceKey != "" {
+ final.AddString(final.StacktraceKey, ent.Stack)
+ }
+ final.buf.AppendByte('}')
+ if final.LineEnding != "" {
+ final.buf.AppendString(final.LineEnding)
+ } else {
+ final.buf.AppendString(DefaultLineEnding)
+ }
+
+ ret := final.buf
+ putJSONEncoder(final)
+ return ret, nil
+}
+
+func (enc *jsonEncoder) truncate() {
+ enc.buf.Reset()
+}
+
+func (enc *jsonEncoder) closeOpenNamespaces() {
+ for i := 0; i < enc.openNamespaces; i++ {
+ enc.buf.AppendByte('}')
+ }
+}
+
+func (enc *jsonEncoder) addKey(key string) {
+ enc.addElementSeparator()
+ enc.buf.AppendByte('"')
+ enc.safeAddString(key)
+ enc.buf.AppendByte('"')
+ enc.buf.AppendByte(':')
+ if enc.spaced {
+ enc.buf.AppendByte(' ')
+ }
+}
+
+func (enc *jsonEncoder) addElementSeparator() {
+ last := enc.buf.Len() - 1
+ if last < 0 {
+ return
+ }
+ switch enc.buf.Bytes()[last] {
+ case '{', '[', ':', ',', ' ':
+ return
+ default:
+ enc.buf.AppendByte(',')
+ if enc.spaced {
+ enc.buf.AppendByte(' ')
+ }
+ }
+}
+
+func (enc *jsonEncoder) appendFloat(val float64, bitSize int) {
+ enc.addElementSeparator()
+ switch {
+ case math.IsNaN(val):
+ enc.buf.AppendString(`"NaN"`)
+ case math.IsInf(val, 1):
+ enc.buf.AppendString(`"+Inf"`)
+ case math.IsInf(val, -1):
+ enc.buf.AppendString(`"-Inf"`)
+ default:
+ enc.buf.AppendFloat(val, bitSize)
+ }
+}
+
+// safeAddString JSON-escapes a string and appends it to the internal buffer.
+// Unlike the standard library's encoder, it doesn't attempt to protect the
+// user from browser vulnerabilities or JSONP-related problems.
+func (enc *jsonEncoder) safeAddString(s string) {
+ for i := 0; i < len(s); {
+ if enc.tryAddRuneSelf(s[i]) {
+ i++
+ continue
+ }
+ r, size := utf8.DecodeRuneInString(s[i:])
+ if enc.tryAddRuneError(r, size) {
+ i++
+ continue
+ }
+ enc.buf.AppendString(s[i : i+size])
+ i += size
+ }
+}
+
+// safeAddByteString is no-alloc equivalent of safeAddString(string(s)) for s []byte.
+func (enc *jsonEncoder) safeAddByteString(s []byte) {
+ for i := 0; i < len(s); {
+ if enc.tryAddRuneSelf(s[i]) {
+ i++
+ continue
+ }
+ r, size := utf8.DecodeRune(s[i:])
+ if enc.tryAddRuneError(r, size) {
+ i++
+ continue
+ }
+ enc.buf.Write(s[i : i+size])
+ i += size
+ }
+}
+
+// tryAddRuneSelf appends b if it is valid UTF-8 character represented in a single byte.
+func (enc *jsonEncoder) tryAddRuneSelf(b byte) bool {
+ if b >= utf8.RuneSelf {
+ return false
+ }
+ if 0x20 <= b && b != '\\' && b != '"' {
+ enc.buf.AppendByte(b)
+ return true
+ }
+ switch b {
+ case '\\', '"':
+ enc.buf.AppendByte('\\')
+ enc.buf.AppendByte(b)
+ case '\n':
+ enc.buf.AppendByte('\\')
+ enc.buf.AppendByte('n')
+ case '\r':
+ enc.buf.AppendByte('\\')
+ enc.buf.AppendByte('r')
+ case '\t':
+ enc.buf.AppendByte('\\')
+ enc.buf.AppendByte('t')
+ default:
+ // Encode bytes < 0x20, except for the escape sequences above.
+ enc.buf.AppendString(`\u00`)
+ enc.buf.AppendByte(_hex[b>>4])
+ enc.buf.AppendByte(_hex[b&0xF])
+ }
+ return true
+}
+
+func (enc *jsonEncoder) tryAddRuneError(r rune, size int) bool {
+ if r == utf8.RuneError && size == 1 {
+ enc.buf.AppendString(`\ufffd`)
+ return true
+ }
+ return false
+}
diff --git a/vendor/go.uber.org/zap/zapcore/json_encoder_bench_test.go b/vendor/go.uber.org/zap/zapcore/json_encoder_bench_test.go
new file mode 100644
index 0000000..4bd5033
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/json_encoder_bench_test.go
@@ -0,0 +1,91 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore_test
+
+import (
+ "encoding/json"
+ "testing"
+ "time"
+
+ . "go.uber.org/zap/zapcore"
+)
+
+func BenchmarkJSONLogMarshalerFunc(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ enc := NewJSONEncoder(testEncoderConfig())
+ enc.AddObject("nested", ObjectMarshalerFunc(func(enc ObjectEncoder) error {
+ enc.AddInt64("i", int64(i))
+ return nil
+ }))
+ }
+}
+
+func BenchmarkZapJSON(b *testing.B) {
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ enc := NewJSONEncoder(testEncoderConfig())
+ enc.AddString("str", "foo")
+ enc.AddInt64("int64-1", 1)
+ enc.AddInt64("int64-2", 2)
+ enc.AddFloat64("float64", 1.0)
+ enc.AddString("string1", "\n")
+ enc.AddString("string2", "💩")
+ enc.AddString("string3", "🤔")
+ enc.AddString("string4", "🙊")
+ enc.AddBool("bool", true)
+ buf, _ := enc.EncodeEntry(Entry{
+ Message: "fake",
+ Level: DebugLevel,
+ }, nil)
+ buf.Free()
+ }
+ })
+}
+
+func BenchmarkStandardJSON(b *testing.B) {
+ record := struct {
+ Level string `json:"level"`
+ Message string `json:"msg"`
+ Time time.Time `json:"ts"`
+ Fields map[string]interface{} `json:"fields"`
+ }{
+ Level: "debug",
+ Message: "fake",
+ Time: time.Unix(0, 0),
+ Fields: map[string]interface{}{
+ "str": "foo",
+ "int64-1": int64(1),
+ "int64-2": int64(1),
+ "float64": float64(1.0),
+ "string1": "\n",
+ "string2": "💩",
+ "string3": "🤔",
+ "string4": "🙊",
+ "bool": true,
+ },
+ }
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ json.Marshal(record)
+ }
+ })
+}
diff --git a/vendor/go.uber.org/zap/zapcore/json_encoder_impl_test.go b/vendor/go.uber.org/zap/zapcore/json_encoder_impl_test.go
new file mode 100644
index 0000000..a2dd722
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/json_encoder_impl_test.go
@@ -0,0 +1,475 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "encoding/json"
+ "errors"
+ "math"
+ "math/rand"
+ "reflect"
+ "testing"
+ "testing/quick"
+ "time"
+
+ "go.uber.org/zap/internal/bufferpool"
+
+ "github.com/stretchr/testify/assert"
+ "go.uber.org/multierr"
+)
+
+func TestJSONClone(t *testing.T) {
+ // The parent encoder is created with plenty of excess capacity.
+ parent := &jsonEncoder{buf: bufferpool.Get()}
+ clone := parent.Clone()
+
+ // Adding to the parent shouldn't affect the clone, and vice versa.
+ parent.AddString("foo", "bar")
+ clone.AddString("baz", "bing")
+
+ assertJSON(t, `"foo":"bar"`, parent)
+ assertJSON(t, `"baz":"bing"`, clone.(*jsonEncoder))
+}
+
+func TestJSONEscaping(t *testing.T) {
+ enc := &jsonEncoder{buf: bufferpool.Get()}
+ // Test all the edge cases of JSON escaping directly.
+ cases := map[string]string{
+ // ASCII.
+ `foo`: `foo`,
+ // Special-cased characters.
+ `"`: `\"`,
+ `\`: `\\`,
+ // Special-cased characters within everyday ASCII.
+ `foo"foo`: `foo\"foo`,
+ "foo\n": `foo\n`,
+ // Special-cased control characters.
+ "\n": `\n`,
+ "\r": `\r`,
+ "\t": `\t`,
+ // \b and \f are sometimes backslash-escaped, but this representation is also
+ // conformant.
+ "\b": `\u0008`,
+ "\f": `\u000c`,
+ // The standard lib special-cases angle brackets and ampersands by default,
+ // because it wants to protect users from browser exploits. In a logging
+ // context, we shouldn't special-case these characters.
+ "<": "<",
+ ">": ">",
+ "&": "&",
+ // ASCII bell - not special-cased.
+ string(byte(0x07)): `\u0007`,
+ // Astral-plane unicode.
+ `☃`: `☃`,
+ // Decodes to (RuneError, 1)
+ "\xed\xa0\x80": `\ufffd\ufffd\ufffd`,
+ "foo\xed\xa0\x80": `foo\ufffd\ufffd\ufffd`,
+ }
+
+ t.Run("String", func(t *testing.T) {
+ for input, output := range cases {
+ enc.truncate()
+ enc.safeAddString(input)
+ assertJSON(t, output, enc)
+ }
+ })
+
+ t.Run("ByteString", func(t *testing.T) {
+ for input, output := range cases {
+ enc.truncate()
+ enc.safeAddByteString([]byte(input))
+ assertJSON(t, output, enc)
+ }
+ })
+}
+
+func TestJSONEncoderObjectFields(t *testing.T) {
+ tests := []struct {
+ desc string
+ expected string
+ f func(Encoder)
+ }{
+ {"binary", `"k":"YWIxMg=="`, func(e Encoder) { e.AddBinary("k", []byte("ab12")) }},
+ {"bool", `"k\\":true`, func(e Encoder) { e.AddBool(`k\`, true) }}, // test key escaping once
+ {"bool", `"k":true`, func(e Encoder) { e.AddBool("k", true) }},
+ {"bool", `"k":false`, func(e Encoder) { e.AddBool("k", false) }},
+ {"byteString", `"k":"v\\"`, func(e Encoder) { e.AddByteString(`k`, []byte(`v\`)) }},
+ {"byteString", `"k":"v"`, func(e Encoder) { e.AddByteString("k", []byte("v")) }},
+ {"byteString", `"k":""`, func(e Encoder) { e.AddByteString("k", []byte{}) }},
+ {"byteString", `"k":""`, func(e Encoder) { e.AddByteString("k", nil) }},
+ {"complex128", `"k":"1+2i"`, func(e Encoder) { e.AddComplex128("k", 1+2i) }},
+ {"complex64", `"k":"1+2i"`, func(e Encoder) { e.AddComplex64("k", 1+2i) }},
+ {"duration", `"k":0.000000001`, func(e Encoder) { e.AddDuration("k", 1) }},
+ {"float64", `"k":1`, func(e Encoder) { e.AddFloat64("k", 1.0) }},
+ {"float64", `"k":10000000000`, func(e Encoder) { e.AddFloat64("k", 1e10) }},
+ {"float64", `"k":"NaN"`, func(e Encoder) { e.AddFloat64("k", math.NaN()) }},
+ {"float64", `"k":"+Inf"`, func(e Encoder) { e.AddFloat64("k", math.Inf(1)) }},
+ {"float64", `"k":"-Inf"`, func(e Encoder) { e.AddFloat64("k", math.Inf(-1)) }},
+ {"float32", `"k":1`, func(e Encoder) { e.AddFloat32("k", 1.0) }},
+ {"float32", `"k":10000000000`, func(e Encoder) { e.AddFloat32("k", 1e10) }},
+ {"float32", `"k":"NaN"`, func(e Encoder) { e.AddFloat32("k", float32(math.NaN())) }},
+ {"float32", `"k":"+Inf"`, func(e Encoder) { e.AddFloat32("k", float32(math.Inf(1))) }},
+ {"float32", `"k":"-Inf"`, func(e Encoder) { e.AddFloat32("k", float32(math.Inf(-1))) }},
+ {"int", `"k":42`, func(e Encoder) { e.AddInt("k", 42) }},
+ {"int64", `"k":42`, func(e Encoder) { e.AddInt64("k", 42) }},
+ {"int32", `"k":42`, func(e Encoder) { e.AddInt32("k", 42) }},
+ {"int16", `"k":42`, func(e Encoder) { e.AddInt16("k", 42) }},
+ {"int8", `"k":42`, func(e Encoder) { e.AddInt8("k", 42) }},
+ {"string", `"k":"v\\"`, func(e Encoder) { e.AddString(`k`, `v\`) }},
+ {"string", `"k":"v"`, func(e Encoder) { e.AddString("k", "v") }},
+ {"string", `"k":""`, func(e Encoder) { e.AddString("k", "") }},
+ {"time", `"k":1`, func(e Encoder) { e.AddTime("k", time.Unix(1, 0)) }},
+ {"uint", `"k":42`, func(e Encoder) { e.AddUint("k", 42) }},
+ {"uint64", `"k":42`, func(e Encoder) { e.AddUint64("k", 42) }},
+ {"uint32", `"k":42`, func(e Encoder) { e.AddUint32("k", 42) }},
+ {"uint16", `"k":42`, func(e Encoder) { e.AddUint16("k", 42) }},
+ {"uint8", `"k":42`, func(e Encoder) { e.AddUint8("k", 42) }},
+ {"uintptr", `"k":42`, func(e Encoder) { e.AddUintptr("k", 42) }},
+ {
+ desc: "object (success)",
+ expected: `"k":{"loggable":"yes"}`,
+ f: func(e Encoder) {
+ assert.NoError(t, e.AddObject("k", loggable{true}), "Unexpected error calling MarshalLogObject.")
+ },
+ },
+ {
+ desc: "object (error)",
+ expected: `"k":{}`,
+ f: func(e Encoder) {
+ assert.Error(t, e.AddObject("k", loggable{false}), "Expected an error calling MarshalLogObject.")
+ },
+ },
+ {
+ desc: "object (with nested array)",
+ expected: `"turducken":{"ducks":[{"in":"chicken"},{"in":"chicken"}]}`,
+ f: func(e Encoder) {
+ assert.NoError(
+ t,
+ e.AddObject("turducken", turducken{}),
+ "Unexpected error calling MarshalLogObject with nested ObjectMarshalers and ArrayMarshalers.",
+ )
+ },
+ },
+ {
+ desc: "array (with nested object)",
+ expected: `"turduckens":[{"ducks":[{"in":"chicken"},{"in":"chicken"}]},{"ducks":[{"in":"chicken"},{"in":"chicken"}]}]`,
+ f: func(e Encoder) {
+ assert.NoError(
+ t,
+ e.AddArray("turduckens", turduckens(2)),
+ "Unexpected error calling MarshalLogObject with nested ObjectMarshalers and ArrayMarshalers.",
+ )
+ },
+ },
+ {
+ desc: "array (success)",
+ expected: `"k":[true]`,
+ f: func(e Encoder) {
+ assert.NoError(t, e.AddArray(`k`, loggable{true}), "Unexpected error calling MarshalLogArray.")
+ },
+ },
+ {
+ desc: "array (error)",
+ expected: `"k":[]`,
+ f: func(e Encoder) {
+ assert.Error(t, e.AddArray("k", loggable{false}), "Expected an error calling MarshalLogArray.")
+ },
+ },
+ {
+ desc: "reflect (success)",
+ expected: `"k":{"loggable":"yes"}`,
+ f: func(e Encoder) {
+ assert.NoError(t, e.AddReflected("k", map[string]string{"loggable": "yes"}), "Unexpected error JSON-serializing a map.")
+ },
+ },
+ {
+ desc: "reflect (failure)",
+ expected: "",
+ f: func(e Encoder) {
+ assert.Error(t, e.AddReflected("k", noJSON{}), "Unexpected success JSON-serializing a noJSON.")
+ },
+ },
+ {
+ desc: "namespace",
+ // EncodeEntry is responsible for closing all open namespaces.
+ expected: `"outermost":{"outer":{"foo":1,"inner":{"foo":2,"innermost":{`,
+ f: func(e Encoder) {
+ e.OpenNamespace("outermost")
+ e.OpenNamespace("outer")
+ e.AddInt("foo", 1)
+ e.OpenNamespace("inner")
+ e.AddInt("foo", 2)
+ e.OpenNamespace("innermost")
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ assertOutput(t, tt.desc, tt.expected, tt.f)
+ }
+}
+
+func TestJSONEncoderArrays(t *testing.T) {
+ tests := []struct {
+ desc string
+ expected string // expect f to be called twice
+ f func(ArrayEncoder)
+ }{
+ {"bool", `[true,true]`, func(e ArrayEncoder) { e.AppendBool(true) }},
+ {"byteString", `["k","k"]`, func(e ArrayEncoder) { e.AppendByteString([]byte("k")) }},
+ {"byteString", `["k\\","k\\"]`, func(e ArrayEncoder) { e.AppendByteString([]byte(`k\`)) }},
+ {"complex128", `["1+2i","1+2i"]`, func(e ArrayEncoder) { e.AppendComplex128(1 + 2i) }},
+ {"complex64", `["1+2i","1+2i"]`, func(e ArrayEncoder) { e.AppendComplex64(1 + 2i) }},
+ {"durations", `[0.000000002,0.000000002]`, func(e ArrayEncoder) { e.AppendDuration(2) }},
+ {"float64", `[3.14,3.14]`, func(e ArrayEncoder) { e.AppendFloat64(3.14) }},
+ {"float32", `[3.14,3.14]`, func(e ArrayEncoder) { e.AppendFloat32(3.14) }},
+ {"int", `[42,42]`, func(e ArrayEncoder) { e.AppendInt(42) }},
+ {"int64", `[42,42]`, func(e ArrayEncoder) { e.AppendInt64(42) }},
+ {"int32", `[42,42]`, func(e ArrayEncoder) { e.AppendInt32(42) }},
+ {"int16", `[42,42]`, func(e ArrayEncoder) { e.AppendInt16(42) }},
+ {"int8", `[42,42]`, func(e ArrayEncoder) { e.AppendInt8(42) }},
+ {"string", `["k","k"]`, func(e ArrayEncoder) { e.AppendString("k") }},
+ {"string", `["k\\","k\\"]`, func(e ArrayEncoder) { e.AppendString(`k\`) }},
+ {"times", `[1,1]`, func(e ArrayEncoder) { e.AppendTime(time.Unix(1, 0)) }},
+ {"uint", `[42,42]`, func(e ArrayEncoder) { e.AppendUint(42) }},
+ {"uint64", `[42,42]`, func(e ArrayEncoder) { e.AppendUint64(42) }},
+ {"uint32", `[42,42]`, func(e ArrayEncoder) { e.AppendUint32(42) }},
+ {"uint16", `[42,42]`, func(e ArrayEncoder) { e.AppendUint16(42) }},
+ {"uint8", `[42,42]`, func(e ArrayEncoder) { e.AppendUint8(42) }},
+ {"uintptr", `[42,42]`, func(e ArrayEncoder) { e.AppendUintptr(42) }},
+ {
+ desc: "arrays (success)",
+ expected: `[[true],[true]]`,
+ f: func(arr ArrayEncoder) {
+ assert.NoError(t, arr.AppendArray(ArrayMarshalerFunc(func(inner ArrayEncoder) error {
+ inner.AppendBool(true)
+ return nil
+ })), "Unexpected error appending an array.")
+ },
+ },
+ {
+ desc: "arrays (error)",
+ expected: `[[true],[true]]`,
+ f: func(arr ArrayEncoder) {
+ assert.Error(t, arr.AppendArray(ArrayMarshalerFunc(func(inner ArrayEncoder) error {
+ inner.AppendBool(true)
+ return errors.New("fail")
+ })), "Expected an error appending an array.")
+ },
+ },
+ {
+ desc: "objects (success)",
+ expected: `[{"loggable":"yes"},{"loggable":"yes"}]`,
+ f: func(arr ArrayEncoder) {
+ assert.NoError(t, arr.AppendObject(loggable{true}), "Unexpected error appending an object.")
+ },
+ },
+ {
+ desc: "objects (error)",
+ expected: `[{},{}]`,
+ f: func(arr ArrayEncoder) {
+ assert.Error(t, arr.AppendObject(loggable{false}), "Expected an error appending an object.")
+ },
+ },
+ {
+ desc: "reflect (success)",
+ expected: `[{"foo":5},{"foo":5}]`,
+ f: func(arr ArrayEncoder) {
+ assert.NoError(
+ t,
+ arr.AppendReflected(map[string]int{"foo": 5}),
+ "Unexpected an error appending an object with reflection.",
+ )
+ },
+ },
+ {
+ desc: "reflect (error)",
+ expected: `[]`,
+ f: func(arr ArrayEncoder) {
+ assert.Error(
+ t,
+ arr.AppendReflected(noJSON{}),
+ "Unexpected an error appending an object with reflection.",
+ )
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ f := func(enc Encoder) error {
+ return enc.AddArray("array", ArrayMarshalerFunc(func(arr ArrayEncoder) error {
+ tt.f(arr)
+ tt.f(arr)
+ return nil
+ }))
+ }
+ assertOutput(t, tt.desc, `"array":`+tt.expected, func(enc Encoder) {
+ err := f(enc)
+ assert.NoError(t, err, "Unexpected error adding array to JSON encoder.")
+ })
+ }
+}
+
+func assertJSON(t *testing.T, expected string, enc *jsonEncoder) {
+ assert.Equal(t, expected, enc.buf.String(), "Encoded JSON didn't match expectations.")
+}
+
+func assertOutput(t testing.TB, desc string, expected string, f func(Encoder)) {
+ enc := &jsonEncoder{buf: bufferpool.Get(), EncoderConfig: &EncoderConfig{
+ EncodeTime: EpochTimeEncoder,
+ EncodeDuration: SecondsDurationEncoder,
+ }}
+ f(enc)
+ assert.Equal(t, expected, enc.buf.String(), "Unexpected encoder output after adding a %s.", desc)
+
+ enc.truncate()
+ enc.AddString("foo", "bar")
+ f(enc)
+ expectedPrefix := `"foo":"bar"`
+ if expected != "" {
+ // If we expect output, it should be comma-separated from the previous
+ // field.
+ expectedPrefix += ","
+ }
+ assert.Equal(t, expectedPrefix+expected, enc.buf.String(), "Unexpected encoder output after adding a %s as a second field.", desc)
+}
+
+// Nested Array- and ObjectMarshalers.
+type turducken struct{}
+
+func (t turducken) MarshalLogObject(enc ObjectEncoder) error {
+ return enc.AddArray("ducks", ArrayMarshalerFunc(func(arr ArrayEncoder) error {
+ for i := 0; i < 2; i++ {
+ arr.AppendObject(ObjectMarshalerFunc(func(inner ObjectEncoder) error {
+ inner.AddString("in", "chicken")
+ return nil
+ }))
+ }
+ return nil
+ }))
+}
+
+type turduckens int
+
+func (t turduckens) MarshalLogArray(enc ArrayEncoder) error {
+ var err error
+ tur := turducken{}
+ for i := 0; i < int(t); i++ {
+ err = multierr.Append(err, enc.AppendObject(tur))
+ }
+ return err
+}
+
+type loggable struct{ bool }
+
+func (l loggable) MarshalLogObject(enc ObjectEncoder) error {
+ if !l.bool {
+ return errors.New("can't marshal")
+ }
+ enc.AddString("loggable", "yes")
+ return nil
+}
+
+func (l loggable) MarshalLogArray(enc ArrayEncoder) error {
+ if !l.bool {
+ return errors.New("can't marshal")
+ }
+ enc.AppendBool(true)
+ return nil
+}
+
+type noJSON struct{}
+
+func (nj noJSON) MarshalJSON() ([]byte, error) {
+ return nil, errors.New("no")
+}
+
+func zapEncode(encode func(*jsonEncoder, string)) func(s string) []byte {
+ return func(s string) []byte {
+ enc := &jsonEncoder{buf: bufferpool.Get()}
+ // Escape and quote a string using our encoder.
+ var ret []byte
+ encode(enc, s)
+ ret = make([]byte, 0, enc.buf.Len()+2)
+ ret = append(ret, '"')
+ ret = append(ret, enc.buf.Bytes()...)
+ ret = append(ret, '"')
+ return ret
+ }
+}
+
+func roundTripsCorrectly(encode func(string) []byte, original string) bool {
+ // Encode using our encoder, decode using the standard library, and assert
+ // that we haven't lost any information.
+ encoded := encode(original)
+
+ var decoded string
+ err := json.Unmarshal(encoded, &decoded)
+ if err != nil {
+ return false
+ }
+ return original == decoded
+}
+
+func roundTripsCorrectlyString(original string) bool {
+ return roundTripsCorrectly(zapEncode((*jsonEncoder).safeAddString), original)
+}
+
+func roundTripsCorrectlyByteString(original string) bool {
+ return roundTripsCorrectly(
+ zapEncode(func(enc *jsonEncoder, s string) {
+ enc.safeAddByteString([]byte(s))
+ }),
+ original)
+}
+
+type ASCII string
+
+func (s ASCII) Generate(r *rand.Rand, size int) reflect.Value {
+ bs := make([]byte, size)
+ for i := range bs {
+ bs[i] = byte(r.Intn(128))
+ }
+ a := ASCII(bs)
+ return reflect.ValueOf(a)
+}
+
+func asciiRoundTripsCorrectlyString(s ASCII) bool {
+ return roundTripsCorrectlyString(string(s))
+}
+
+func asciiRoundTripsCorrectlyByteString(s ASCII) bool {
+ return roundTripsCorrectlyByteString(string(s))
+}
+
+func TestJSONQuick(t *testing.T) {
+ check := func(f interface{}) {
+ err := quick.Check(f, &quick.Config{MaxCountScale: 100.0})
+ assert.NoError(t, err)
+ }
+ // Test the full range of UTF-8 strings.
+ check(roundTripsCorrectlyString)
+ check(roundTripsCorrectlyByteString)
+
+ // Focus on ASCII strings.
+ check(asciiRoundTripsCorrectlyString)
+ check(asciiRoundTripsCorrectlyByteString)
+}
diff --git a/vendor/go.uber.org/zap/zapcore/level.go b/vendor/go.uber.org/zap/zapcore/level.go
new file mode 100644
index 0000000..e575c9f
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/level.go
@@ -0,0 +1,175 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+)
+
+var errUnmarshalNilLevel = errors.New("can't unmarshal a nil *Level")
+
+// A Level is a logging priority. Higher levels are more important.
+type Level int8
+
+const (
+ // DebugLevel logs are typically voluminous, and are usually disabled in
+ // production.
+ DebugLevel Level = iota - 1
+ // InfoLevel is the default logging priority.
+ InfoLevel
+ // WarnLevel logs are more important than Info, but don't need individual
+ // human review.
+ WarnLevel
+ // ErrorLevel logs are high-priority. If an application is running smoothly,
+ // it shouldn't generate any error-level logs.
+ ErrorLevel
+ // DPanicLevel logs are particularly important errors. In development the
+ // logger panics after writing the message.
+ DPanicLevel
+ // PanicLevel logs a message, then panics.
+ PanicLevel
+ // FatalLevel logs a message, then calls os.Exit(1).
+ FatalLevel
+
+ _minLevel = DebugLevel
+ _maxLevel = FatalLevel
+)
+
+// String returns a lower-case ASCII representation of the log level.
+func (l Level) String() string {
+ switch l {
+ case DebugLevel:
+ return "debug"
+ case InfoLevel:
+ return "info"
+ case WarnLevel:
+ return "warn"
+ case ErrorLevel:
+ return "error"
+ case DPanicLevel:
+ return "dpanic"
+ case PanicLevel:
+ return "panic"
+ case FatalLevel:
+ return "fatal"
+ default:
+ return fmt.Sprintf("Level(%d)", l)
+ }
+}
+
+// CapitalString returns an all-caps ASCII representation of the log level.
+func (l Level) CapitalString() string {
+ // Printing levels in all-caps is common enough that we should export this
+ // functionality.
+ switch l {
+ case DebugLevel:
+ return "DEBUG"
+ case InfoLevel:
+ return "INFO"
+ case WarnLevel:
+ return "WARN"
+ case ErrorLevel:
+ return "ERROR"
+ case DPanicLevel:
+ return "DPANIC"
+ case PanicLevel:
+ return "PANIC"
+ case FatalLevel:
+ return "FATAL"
+ default:
+ return fmt.Sprintf("LEVEL(%d)", l)
+ }
+}
+
+// MarshalText marshals the Level to text. Note that the text representation
+// drops the -Level suffix (see example).
+func (l Level) MarshalText() ([]byte, error) {
+ return []byte(l.String()), nil
+}
+
+// UnmarshalText unmarshals text to a level. Like MarshalText, UnmarshalText
+// expects the text representation of a Level to drop the -Level suffix (see
+// example).
+//
+// In particular, this makes it easy to configure logging levels using YAML,
+// TOML, or JSON files.
+func (l *Level) UnmarshalText(text []byte) error {
+ if l == nil {
+ return errUnmarshalNilLevel
+ }
+ if !l.unmarshalText(text) && !l.unmarshalText(bytes.ToLower(text)) {
+ return fmt.Errorf("unrecognized level: %q", text)
+ }
+ return nil
+}
+
+func (l *Level) unmarshalText(text []byte) bool {
+ switch string(text) {
+ case "debug", "DEBUG":
+ *l = DebugLevel
+ case "info", "INFO", "": // make the zero value useful
+ *l = InfoLevel
+ case "warn", "WARN":
+ *l = WarnLevel
+ case "error", "ERROR":
+ *l = ErrorLevel
+ case "dpanic", "DPANIC":
+ *l = DPanicLevel
+ case "panic", "PANIC":
+ *l = PanicLevel
+ case "fatal", "FATAL":
+ *l = FatalLevel
+ default:
+ return false
+ }
+ return true
+}
+
+// Set sets the level for the flag.Value interface.
+func (l *Level) Set(s string) error {
+ return l.UnmarshalText([]byte(s))
+}
+
+// Get gets the level for the flag.Getter interface.
+func (l *Level) Get() interface{} {
+ return *l
+}
+
+// Enabled returns true if the given level is at or above this level.
+func (l Level) Enabled(lvl Level) bool {
+ return lvl >= l
+}
+
+// LevelEnabler decides whether a given logging level is enabled when logging a
+// message.
+//
+// Enablers are intended to be used to implement deterministic filters;
+// concerns like sampling are better implemented as a Core.
+//
+// Each concrete Level value implements a static LevelEnabler which returns
+// true for itself and all higher logging levels. For example WarnLevel.Enabled()
+// will return true for WarnLevel, ErrorLevel, DPanicLevel, PanicLevel, and
+// FatalLevel, but return false for InfoLevel and DebugLevel.
+type LevelEnabler interface {
+ Enabled(Level) bool
+}
diff --git a/vendor/go.uber.org/zap/zapcore/level_strings.go b/vendor/go.uber.org/zap/zapcore/level_strings.go
new file mode 100644
index 0000000..7af8dad
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/level_strings.go
@@ -0,0 +1,46 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import "go.uber.org/zap/internal/color"
+
+var (
+ _levelToColor = map[Level]color.Color{
+ DebugLevel: color.Magenta,
+ InfoLevel: color.Blue,
+ WarnLevel: color.Yellow,
+ ErrorLevel: color.Red,
+ DPanicLevel: color.Red,
+ PanicLevel: color.Red,
+ FatalLevel: color.Red,
+ }
+ _unknownLevelColor = color.Red
+
+ _levelToLowercaseColorString = make(map[Level]string, len(_levelToColor))
+ _levelToCapitalColorString = make(map[Level]string, len(_levelToColor))
+)
+
+func init() {
+ for level, color := range _levelToColor {
+ _levelToLowercaseColorString[level] = color.Add(level.String())
+ _levelToCapitalColorString[level] = color.Add(level.CapitalString())
+ }
+}
diff --git a/vendor/go.uber.org/zap/zapcore/level_strings_test.go b/vendor/go.uber.org/zap/zapcore/level_strings_test.go
new file mode 100644
index 0000000..14b0bac
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/level_strings_test.go
@@ -0,0 +1,38 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestAllLevelsCoveredByLevelString(t *testing.T) {
+ numLevels := int((_maxLevel - _minLevel) + 1)
+
+ isComplete := func(m map[Level]string) bool {
+ return len(m) == numLevels
+ }
+
+ assert.True(t, isComplete(_levelToLowercaseColorString), "Colored lowercase strings don't cover all levels.")
+ assert.True(t, isComplete(_levelToCapitalColorString), "Colored capital strings don't cover all levels.")
+}
diff --git a/vendor/go.uber.org/zap/zapcore/level_test.go b/vendor/go.uber.org/zap/zapcore/level_test.go
new file mode 100644
index 0000000..28b75b3
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/level_test.go
@@ -0,0 +1,177 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "bytes"
+ "flag"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestLevelString(t *testing.T) {
+ tests := map[Level]string{
+ DebugLevel: "debug",
+ InfoLevel: "info",
+ WarnLevel: "warn",
+ ErrorLevel: "error",
+ DPanicLevel: "dpanic",
+ PanicLevel: "panic",
+ FatalLevel: "fatal",
+ Level(-42): "Level(-42)",
+ }
+
+ for lvl, stringLevel := range tests {
+ assert.Equal(t, stringLevel, lvl.String(), "Unexpected lowercase level string.")
+ assert.Equal(t, strings.ToUpper(stringLevel), lvl.CapitalString(), "Unexpected all-caps level string.")
+ }
+}
+
+func TestLevelText(t *testing.T) {
+ tests := []struct {
+ text string
+ level Level
+ }{
+ {"debug", DebugLevel},
+ {"info", InfoLevel},
+ {"", InfoLevel}, // make the zero value useful
+ {"warn", WarnLevel},
+ {"error", ErrorLevel},
+ {"dpanic", DPanicLevel},
+ {"panic", PanicLevel},
+ {"fatal", FatalLevel},
+ }
+ for _, tt := range tests {
+ if tt.text != "" {
+ lvl := tt.level
+ marshaled, err := lvl.MarshalText()
+ assert.NoError(t, err, "Unexpected error marshaling level %v to text.", &lvl)
+ assert.Equal(t, tt.text, string(marshaled), "Marshaling level %v to text yielded unexpected result.", &lvl)
+ }
+
+ var unmarshaled Level
+ err := unmarshaled.UnmarshalText([]byte(tt.text))
+ assert.NoError(t, err, `Unexpected error unmarshaling text %q to level.`, tt.text)
+ assert.Equal(t, tt.level, unmarshaled, `Text %q unmarshaled to an unexpected level.`, tt.text)
+ }
+}
+
+func TestCapitalLevelsParse(t *testing.T) {
+ tests := []struct {
+ text string
+ level Level
+ }{
+ {"DEBUG", DebugLevel},
+ {"INFO", InfoLevel},
+ {"WARN", WarnLevel},
+ {"ERROR", ErrorLevel},
+ {"DPANIC", DPanicLevel},
+ {"PANIC", PanicLevel},
+ {"FATAL", FatalLevel},
+ }
+ for _, tt := range tests {
+ var unmarshaled Level
+ err := unmarshaled.UnmarshalText([]byte(tt.text))
+ assert.NoError(t, err, `Unexpected error unmarshaling text %q to level.`, tt.text)
+ assert.Equal(t, tt.level, unmarshaled, `Text %q unmarshaled to an unexpected level.`, tt.text)
+ }
+}
+
+func TestWeirdLevelsParse(t *testing.T) {
+ tests := []struct {
+ text string
+ level Level
+ }{
+ // I guess...
+ {"Debug", DebugLevel},
+ {"Info", InfoLevel},
+ {"Warn", WarnLevel},
+ {"Error", ErrorLevel},
+ {"Dpanic", DPanicLevel},
+ {"Panic", PanicLevel},
+ {"Fatal", FatalLevel},
+
+ // What even is...
+ {"DeBuG", DebugLevel},
+ {"InFo", InfoLevel},
+ {"WaRn", WarnLevel},
+ {"ErRor", ErrorLevel},
+ {"DpAnIc", DPanicLevel},
+ {"PaNiC", PanicLevel},
+ {"FaTaL", FatalLevel},
+ }
+ for _, tt := range tests {
+ var unmarshaled Level
+ err := unmarshaled.UnmarshalText([]byte(tt.text))
+ assert.NoError(t, err, `Unexpected error unmarshaling text %q to level.`, tt.text)
+ assert.Equal(t, tt.level, unmarshaled, `Text %q unmarshaled to an unexpected level.`, tt.text)
+ }
+}
+
+func TestLevelNils(t *testing.T) {
+ var l *Level
+
+ // The String() method will not handle nil level properly.
+ assert.Panics(t, func() {
+ assert.Equal(t, "Level(nil)", l.String(), "Unexpected result stringifying nil *Level.")
+ }, "Level(nil).String() should panic")
+
+ assert.Panics(t, func() {
+ l.MarshalText()
+ }, "Expected to panic when marshalling a nil level.")
+
+ err := l.UnmarshalText([]byte("debug"))
+ assert.Equal(t, errUnmarshalNilLevel, err, "Expected to error unmarshalling into a nil Level.")
+}
+
+func TestLevelUnmarshalUnknownText(t *testing.T) {
+ var l Level
+ err := l.UnmarshalText([]byte("foo"))
+ assert.Contains(t, err.Error(), "unrecognized level", "Expected unmarshaling arbitrary text to fail.")
+}
+
+func TestLevelAsFlagValue(t *testing.T) {
+ var (
+ buf bytes.Buffer
+ lvl Level
+ )
+ fs := flag.NewFlagSet("levelTest", flag.ContinueOnError)
+ fs.SetOutput(&buf)
+ fs.Var(&lvl, "level", "log level")
+
+ for _, expected := range []Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel, PanicLevel, FatalLevel} {
+ assert.NoError(t, fs.Parse([]string{"-level", expected.String()}))
+ assert.Equal(t, expected, lvl, "Unexpected level after parsing flag.")
+ assert.Equal(t, expected, lvl.Get(), "Unexpected output using flag.Getter API.")
+ assert.Empty(t, buf.String(), "Unexpected error output parsing level flag.")
+ buf.Reset()
+ }
+
+ assert.Error(t, fs.Parse([]string{"-level", "nope"}))
+ assert.Equal(
+ t,
+ `invalid value "nope" for flag -level: unrecognized level: "nope"`,
+ strings.Split(buf.String(), "\n")[0], // second line is help message
+ "Unexpected error output from invalid flag input.",
+ )
+}
diff --git a/vendor/go.uber.org/zap/zapcore/marshaler.go b/vendor/go.uber.org/zap/zapcore/marshaler.go
new file mode 100644
index 0000000..2627a65
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/marshaler.go
@@ -0,0 +1,53 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+// ObjectMarshaler allows user-defined types to efficiently add themselves to the
+// logging context, and to selectively omit information which shouldn't be
+// included in logs (e.g., passwords).
+type ObjectMarshaler interface {
+ MarshalLogObject(ObjectEncoder) error
+}
+
+// ObjectMarshalerFunc is a type adapter that turns a function into an
+// ObjectMarshaler.
+type ObjectMarshalerFunc func(ObjectEncoder) error
+
+// MarshalLogObject calls the underlying function.
+func (f ObjectMarshalerFunc) MarshalLogObject(enc ObjectEncoder) error {
+ return f(enc)
+}
+
+// ArrayMarshaler allows user-defined types to efficiently add themselves to the
+// logging context, and to selectively omit information which shouldn't be
+// included in logs (e.g., passwords).
+type ArrayMarshaler interface {
+ MarshalLogArray(ArrayEncoder) error
+}
+
+// ArrayMarshalerFunc is a type adapter that turns a function into an
+// ArrayMarshaler.
+type ArrayMarshalerFunc func(ArrayEncoder) error
+
+// MarshalLogArray calls the underlying function.
+func (f ArrayMarshalerFunc) MarshalLogArray(enc ArrayEncoder) error {
+ return f(enc)
+}
diff --git a/vendor/go.uber.org/zap/zapcore/memory_encoder.go b/vendor/go.uber.org/zap/zapcore/memory_encoder.go
new file mode 100644
index 0000000..4805c8a
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/memory_encoder.go
@@ -0,0 +1,179 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import "time"
+
+// MapObjectEncoder is an ObjectEncoder backed by a simple
+// map[string]interface{}. It's not fast enough for production use, but it's
+// helpful in tests.
+type MapObjectEncoder struct {
+ // Fields contains the entire encoded log context.
+ Fields map[string]interface{}
+ // cur is a pointer to the namespace we're currently writing to.
+ cur map[string]interface{}
+}
+
+// NewMapObjectEncoder creates a new map-backed ObjectEncoder.
+func NewMapObjectEncoder() *MapObjectEncoder {
+ m := make(map[string]interface{})
+ return &MapObjectEncoder{
+ Fields: m,
+ cur: m,
+ }
+}
+
+// AddArray implements ObjectEncoder.
+func (m *MapObjectEncoder) AddArray(key string, v ArrayMarshaler) error {
+ arr := &sliceArrayEncoder{}
+ err := v.MarshalLogArray(arr)
+ m.cur[key] = arr.elems
+ return err
+}
+
+// AddObject implements ObjectEncoder.
+func (m *MapObjectEncoder) AddObject(k string, v ObjectMarshaler) error {
+ newMap := NewMapObjectEncoder()
+ m.cur[k] = newMap.Fields
+ return v.MarshalLogObject(newMap)
+}
+
+// AddBinary implements ObjectEncoder.
+func (m *MapObjectEncoder) AddBinary(k string, v []byte) { m.cur[k] = v }
+
+// AddByteString implements ObjectEncoder.
+func (m *MapObjectEncoder) AddByteString(k string, v []byte) { m.cur[k] = v }
+
+// AddBool implements ObjectEncoder.
+func (m *MapObjectEncoder) AddBool(k string, v bool) { m.cur[k] = v }
+
+// AddDuration implements ObjectEncoder.
+func (m MapObjectEncoder) AddDuration(k string, v time.Duration) { m.cur[k] = v }
+
+// AddComplex128 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddComplex128(k string, v complex128) { m.cur[k] = v }
+
+// AddComplex64 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddComplex64(k string, v complex64) { m.cur[k] = v }
+
+// AddFloat64 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddFloat64(k string, v float64) { m.cur[k] = v }
+
+// AddFloat32 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddFloat32(k string, v float32) { m.cur[k] = v }
+
+// AddInt implements ObjectEncoder.
+func (m *MapObjectEncoder) AddInt(k string, v int) { m.cur[k] = v }
+
+// AddInt64 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddInt64(k string, v int64) { m.cur[k] = v }
+
+// AddInt32 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddInt32(k string, v int32) { m.cur[k] = v }
+
+// AddInt16 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddInt16(k string, v int16) { m.cur[k] = v }
+
+// AddInt8 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddInt8(k string, v int8) { m.cur[k] = v }
+
+// AddString implements ObjectEncoder.
+func (m *MapObjectEncoder) AddString(k string, v string) { m.cur[k] = v }
+
+// AddTime implements ObjectEncoder.
+func (m MapObjectEncoder) AddTime(k string, v time.Time) { m.cur[k] = v }
+
+// AddUint implements ObjectEncoder.
+func (m *MapObjectEncoder) AddUint(k string, v uint) { m.cur[k] = v }
+
+// AddUint64 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddUint64(k string, v uint64) { m.cur[k] = v }
+
+// AddUint32 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddUint32(k string, v uint32) { m.cur[k] = v }
+
+// AddUint16 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddUint16(k string, v uint16) { m.cur[k] = v }
+
+// AddUint8 implements ObjectEncoder.
+func (m *MapObjectEncoder) AddUint8(k string, v uint8) { m.cur[k] = v }
+
+// AddUintptr implements ObjectEncoder.
+func (m *MapObjectEncoder) AddUintptr(k string, v uintptr) { m.cur[k] = v }
+
+// AddReflected implements ObjectEncoder.
+func (m *MapObjectEncoder) AddReflected(k string, v interface{}) error {
+ m.cur[k] = v
+ return nil
+}
+
+// OpenNamespace implements ObjectEncoder.
+func (m *MapObjectEncoder) OpenNamespace(k string) {
+ ns := make(map[string]interface{})
+ m.cur[k] = ns
+ m.cur = ns
+}
+
+// sliceArrayEncoder is an ArrayEncoder backed by a simple []interface{}. Like
+// the MapObjectEncoder, it's not designed for production use.
+type sliceArrayEncoder struct {
+ elems []interface{}
+}
+
+func (s *sliceArrayEncoder) AppendArray(v ArrayMarshaler) error {
+ enc := &sliceArrayEncoder{}
+ err := v.MarshalLogArray(enc)
+ s.elems = append(s.elems, enc.elems)
+ return err
+}
+
+func (s *sliceArrayEncoder) AppendObject(v ObjectMarshaler) error {
+ m := NewMapObjectEncoder()
+ err := v.MarshalLogObject(m)
+ s.elems = append(s.elems, m.Fields)
+ return err
+}
+
+func (s *sliceArrayEncoder) AppendReflected(v interface{}) error {
+ s.elems = append(s.elems, v)
+ return nil
+}
+
+func (s *sliceArrayEncoder) AppendBool(v bool) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendByteString(v []byte) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendComplex128(v complex128) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendComplex64(v complex64) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendDuration(v time.Duration) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendFloat64(v float64) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendFloat32(v float32) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendInt(v int) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendInt64(v int64) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendInt32(v int32) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendInt16(v int16) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendInt8(v int8) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendString(v string) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendTime(v time.Time) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendUint(v uint) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendUint64(v uint64) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendUint32(v uint32) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendUint16(v uint16) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendUint8(v uint8) { s.elems = append(s.elems, v) }
+func (s *sliceArrayEncoder) AppendUintptr(v uintptr) { s.elems = append(s.elems, v) }
diff --git a/vendor/go.uber.org/zap/zapcore/memory_encoder_test.go b/vendor/go.uber.org/zap/zapcore/memory_encoder_test.go
new file mode 100644
index 0000000..e63454a
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/memory_encoder_test.go
@@ -0,0 +1,283 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestMapObjectEncoderAdd(t *testing.T) {
+ // Expected output of a turducken.
+ wantTurducken := map[string]interface{}{
+ "ducks": []interface{}{
+ map[string]interface{}{"in": "chicken"},
+ map[string]interface{}{"in": "chicken"},
+ },
+ }
+
+ tests := []struct {
+ desc string
+ f func(ObjectEncoder)
+ expected interface{}
+ }{
+ {
+ desc: "AddObject",
+ f: func(e ObjectEncoder) {
+ assert.NoError(t, e.AddObject("k", loggable{true}), "Expected AddObject to succeed.")
+ },
+ expected: map[string]interface{}{"loggable": "yes"},
+ },
+ {
+ desc: "AddObject (nested)",
+ f: func(e ObjectEncoder) {
+ assert.NoError(t, e.AddObject("k", turducken{}), "Expected AddObject to succeed.")
+ },
+ expected: wantTurducken,
+ },
+ {
+ desc: "AddArray",
+ f: func(e ObjectEncoder) {
+ assert.NoError(t, e.AddArray("k", ArrayMarshalerFunc(func(arr ArrayEncoder) error {
+ arr.AppendBool(true)
+ arr.AppendBool(false)
+ arr.AppendBool(true)
+ return nil
+ })), "Expected AddArray to succeed.")
+ },
+ expected: []interface{}{true, false, true},
+ },
+ {
+ desc: "AddArray (nested)",
+ f: func(e ObjectEncoder) {
+ assert.NoError(t, e.AddArray("k", turduckens(2)), "Expected AddArray to succeed.")
+ },
+ expected: []interface{}{wantTurducken, wantTurducken},
+ },
+ {
+ desc: "AddBinary",
+ f: func(e ObjectEncoder) { e.AddBinary("k", []byte("foo")) },
+ expected: []byte("foo"),
+ },
+ {
+ desc: "AddBool",
+ f: func(e ObjectEncoder) { e.AddBool("k", true) },
+ expected: true,
+ },
+ {
+ desc: "AddComplex128",
+ f: func(e ObjectEncoder) { e.AddComplex128("k", 1+2i) },
+ expected: 1 + 2i,
+ },
+ {
+ desc: "AddComplex64",
+ f: func(e ObjectEncoder) { e.AddComplex64("k", 1+2i) },
+ expected: complex64(1 + 2i),
+ },
+ {
+ desc: "AddDuration",
+ f: func(e ObjectEncoder) { e.AddDuration("k", time.Millisecond) },
+ expected: time.Millisecond,
+ },
+ {
+ desc: "AddFloat64",
+ f: func(e ObjectEncoder) { e.AddFloat64("k", 3.14) },
+ expected: 3.14,
+ },
+ {
+ desc: "AddFloat32",
+ f: func(e ObjectEncoder) { e.AddFloat32("k", 3.14) },
+ expected: float32(3.14),
+ },
+ {
+ desc: "AddInt",
+ f: func(e ObjectEncoder) { e.AddInt("k", 42) },
+ expected: 42,
+ },
+ {
+ desc: "AddInt64",
+ f: func(e ObjectEncoder) { e.AddInt64("k", 42) },
+ expected: int64(42),
+ },
+ {
+ desc: "AddInt32",
+ f: func(e ObjectEncoder) { e.AddInt32("k", 42) },
+ expected: int32(42),
+ },
+ {
+ desc: "AddInt16",
+ f: func(e ObjectEncoder) { e.AddInt16("k", 42) },
+ expected: int16(42),
+ },
+ {
+ desc: "AddInt8",
+ f: func(e ObjectEncoder) { e.AddInt8("k", 42) },
+ expected: int8(42),
+ },
+ {
+ desc: "AddString",
+ f: func(e ObjectEncoder) { e.AddString("k", "v") },
+ expected: "v",
+ },
+ {
+ desc: "AddTime",
+ f: func(e ObjectEncoder) { e.AddTime("k", time.Unix(0, 100)) },
+ expected: time.Unix(0, 100),
+ },
+ {
+ desc: "AddUint",
+ f: func(e ObjectEncoder) { e.AddUint("k", 42) },
+ expected: uint(42),
+ },
+ {
+ desc: "AddUint64",
+ f: func(e ObjectEncoder) { e.AddUint64("k", 42) },
+ expected: uint64(42),
+ },
+ {
+ desc: "AddUint32",
+ f: func(e ObjectEncoder) { e.AddUint32("k", 42) },
+ expected: uint32(42),
+ },
+ {
+ desc: "AddUint16",
+ f: func(e ObjectEncoder) { e.AddUint16("k", 42) },
+ expected: uint16(42),
+ },
+ {
+ desc: "AddUint8",
+ f: func(e ObjectEncoder) { e.AddUint8("k", 42) },
+ expected: uint8(42),
+ },
+ {
+ desc: "AddUintptr",
+ f: func(e ObjectEncoder) { e.AddUintptr("k", 42) },
+ expected: uintptr(42),
+ },
+ {
+ desc: "AddReflected",
+ f: func(e ObjectEncoder) {
+ assert.NoError(t, e.AddReflected("k", map[string]interface{}{"foo": 5}), "Expected AddReflected to succeed.")
+ },
+ expected: map[string]interface{}{"foo": 5},
+ },
+ {
+ desc: "OpenNamespace",
+ f: func(e ObjectEncoder) {
+ e.OpenNamespace("k")
+ e.AddInt("foo", 1)
+ e.OpenNamespace("middle")
+ e.AddInt("foo", 2)
+ e.OpenNamespace("inner")
+ e.AddInt("foo", 3)
+ },
+ expected: map[string]interface{}{
+ "foo": 1,
+ "middle": map[string]interface{}{
+ "foo": 2,
+ "inner": map[string]interface{}{
+ "foo": 3,
+ },
+ },
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ enc := NewMapObjectEncoder()
+ tt.f(enc)
+ assert.Equal(t, tt.expected, enc.Fields["k"], "Unexpected encoder output.")
+ }
+}
+func TestSliceArrayEncoderAppend(t *testing.T) {
+ tests := []struct {
+ desc string
+ f func(ArrayEncoder)
+ expected interface{}
+ }{
+ // AppendObject and AppendArray are covered by the AddObject (nested) and
+ // AddArray (nested) cases above.
+ {"AppendBool", func(e ArrayEncoder) { e.AppendBool(true) }, true},
+ {"AppendComplex128", func(e ArrayEncoder) { e.AppendComplex128(1 + 2i) }, 1 + 2i},
+ {"AppendComplex64", func(e ArrayEncoder) { e.AppendComplex64(1 + 2i) }, complex64(1 + 2i)},
+ {"AppendDuration", func(e ArrayEncoder) { e.AppendDuration(time.Second) }, time.Second},
+ {"AppendFloat64", func(e ArrayEncoder) { e.AppendFloat64(3.14) }, 3.14},
+ {"AppendFloat32", func(e ArrayEncoder) { e.AppendFloat32(3.14) }, float32(3.14)},
+ {"AppendInt", func(e ArrayEncoder) { e.AppendInt(42) }, 42},
+ {"AppendInt64", func(e ArrayEncoder) { e.AppendInt64(42) }, int64(42)},
+ {"AppendInt32", func(e ArrayEncoder) { e.AppendInt32(42) }, int32(42)},
+ {"AppendInt16", func(e ArrayEncoder) { e.AppendInt16(42) }, int16(42)},
+ {"AppendInt8", func(e ArrayEncoder) { e.AppendInt8(42) }, int8(42)},
+ {"AppendString", func(e ArrayEncoder) { e.AppendString("foo") }, "foo"},
+ {"AppendTime", func(e ArrayEncoder) { e.AppendTime(time.Unix(0, 100)) }, time.Unix(0, 100)},
+ {"AppendUint", func(e ArrayEncoder) { e.AppendUint(42) }, uint(42)},
+ {"AppendUint64", func(e ArrayEncoder) { e.AppendUint64(42) }, uint64(42)},
+ {"AppendUint32", func(e ArrayEncoder) { e.AppendUint32(42) }, uint32(42)},
+ {"AppendUint16", func(e ArrayEncoder) { e.AppendUint16(42) }, uint16(42)},
+ {"AppendUint8", func(e ArrayEncoder) { e.AppendUint8(42) }, uint8(42)},
+ {"AppendUintptr", func(e ArrayEncoder) { e.AppendUintptr(42) }, uintptr(42)},
+ {
+ desc: "AppendReflected",
+ f: func(e ArrayEncoder) { e.AppendReflected(map[string]interface{}{"foo": 5}) },
+ expected: map[string]interface{}{"foo": 5},
+ },
+ {
+ desc: "AppendArray (arrays of arrays)",
+ f: func(e ArrayEncoder) {
+ e.AppendArray(ArrayMarshalerFunc(func(inner ArrayEncoder) error {
+ inner.AppendBool(true)
+ inner.AppendBool(false)
+ return nil
+ }))
+ },
+ expected: []interface{}{true, false},
+ },
+ }
+
+ for _, tt := range tests {
+ enc := NewMapObjectEncoder()
+ assert.NoError(t, enc.AddArray("k", ArrayMarshalerFunc(func(arr ArrayEncoder) error {
+ tt.f(arr)
+ tt.f(arr)
+ return nil
+ })), "Expected AddArray to succeed.")
+
+ arr, ok := enc.Fields["k"].([]interface{})
+ if !ok {
+ t.Errorf("Test case %s didn't encode an array.", tt.desc)
+ continue
+ }
+ assert.Equal(t, []interface{}{tt.expected, tt.expected}, arr, "Unexpected encoder output.")
+ }
+}
+
+func TestMapObjectEncoderReflectionFailures(t *testing.T) {
+ enc := NewMapObjectEncoder()
+ assert.Error(t, enc.AddObject("object", loggable{false}), "Expected AddObject to fail.")
+ assert.Equal(
+ t,
+ map[string]interface{}{"object": map[string]interface{}{}},
+ enc.Fields,
+ "Expected encoder to use empty values on errors.",
+ )
+}
diff --git a/vendor/go.uber.org/zap/zapcore/sampler.go b/vendor/go.uber.org/zap/zapcore/sampler.go
new file mode 100644
index 0000000..e316418
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/sampler.go
@@ -0,0 +1,134 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "time"
+
+ "go.uber.org/atomic"
+)
+
+const (
+ _numLevels = _maxLevel - _minLevel + 1
+ _countersPerLevel = 4096
+)
+
+type counter struct {
+ resetAt atomic.Int64
+ counter atomic.Uint64
+}
+
+type counters [_numLevels][_countersPerLevel]counter
+
+func newCounters() *counters {
+ return &counters{}
+}
+
+func (cs *counters) get(lvl Level, key string) *counter {
+ i := lvl - _minLevel
+ j := fnv32a(key) % _countersPerLevel
+ return &cs[i][j]
+}
+
+// fnv32a, adapted from "hash/fnv", but without a []byte(string) alloc
+func fnv32a(s string) uint32 {
+ const (
+ offset32 = 2166136261
+ prime32 = 16777619
+ )
+ hash := uint32(offset32)
+ for i := 0; i < len(s); i++ {
+ hash ^= uint32(s[i])
+ hash *= prime32
+ }
+ return hash
+}
+
+func (c *counter) IncCheckReset(t time.Time, tick time.Duration) uint64 {
+ tn := t.UnixNano()
+ resetAfter := c.resetAt.Load()
+ if resetAfter > tn {
+ return c.counter.Inc()
+ }
+
+ c.counter.Store(1)
+
+ newResetAfter := tn + tick.Nanoseconds()
+ if !c.resetAt.CAS(resetAfter, newResetAfter) {
+ // We raced with another goroutine trying to reset, and it also reset
+ // the counter to 1, so we need to reincrement the counter.
+ return c.counter.Inc()
+ }
+
+ return 1
+}
+
+type sampler struct {
+ Core
+
+ counts *counters
+ tick time.Duration
+ first, thereafter uint64
+}
+
+// NewSampler creates a Core that samples incoming entries, which caps the CPU
+// and I/O load of logging while attempting to preserve a representative subset
+// of your logs.
+//
+// Zap samples by logging the first N entries with a given level and message
+// each tick. If more Entries with the same level and message are seen during
+// the same interval, every Mth message is logged and the rest are dropped.
+//
+// Keep in mind that zap's sampling implementation is optimized for speed over
+// absolute precision; under load, each tick may be slightly over- or
+// under-sampled.
+func NewSampler(core Core, tick time.Duration, first, thereafter int) Core {
+ return &sampler{
+ Core: core,
+ tick: tick,
+ counts: newCounters(),
+ first: uint64(first),
+ thereafter: uint64(thereafter),
+ }
+}
+
+func (s *sampler) With(fields []Field) Core {
+ return &sampler{
+ Core: s.Core.With(fields),
+ tick: s.tick,
+ counts: s.counts,
+ first: s.first,
+ thereafter: s.thereafter,
+ }
+}
+
+func (s *sampler) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {
+ if !s.Enabled(ent.Level) {
+ return ce
+ }
+
+ counter := s.counts.get(ent.Level, ent.Message)
+ n := counter.IncCheckReset(ent.Time, s.tick)
+ if n > s.first && (n-s.first)%s.thereafter != 0 {
+ return ce
+ }
+ return s.Core.Check(ent, ce)
+}
diff --git a/vendor/go.uber.org/zap/zapcore/sampler_bench_test.go b/vendor/go.uber.org/zap/zapcore/sampler_bench_test.go
new file mode 100644
index 0000000..3c79932
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/sampler_bench_test.go
@@ -0,0 +1,230 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore_test
+
+import (
+ "fmt"
+ "testing"
+ "time"
+
+ . "go.uber.org/zap/zapcore"
+ "go.uber.org/zap/zaptest"
+)
+
+var counterTestCases = [][]string{
+ // some stuff I made up
+ {
+ "foo",
+ "bar",
+ "baz",
+ "alpha",
+ "bravo",
+ "charlie",
+ "delta",
+ },
+
+ // shuf -n50 /usr/share/dict/words
+ {
+ "unbracing",
+ "stereotomy",
+ "supranervian",
+ "moaning",
+ "exchangeability",
+ "gunyang",
+ "sulcation",
+ "dariole",
+ "archheresy",
+ "synchronistically",
+ "clips",
+ "unsanctioned",
+ "Argoan",
+ "liparomphalus",
+ "layship",
+ "Fregatae",
+ "microzoology",
+ "glaciaria",
+ "Frugivora",
+ "patterist",
+ "Grossulariaceae",
+ "lithotint",
+ "bargander",
+ "opisthographical",
+ "cacography",
+ "chalkstone",
+ "nonsubstantialism",
+ "sardonicism",
+ "calamiform",
+ "lodginghouse",
+ "predisposedly",
+ "topotypic",
+ "broideress",
+ "outrange",
+ "gingivolabial",
+ "monoazo",
+ "sparlike",
+ "concameration",
+ "untoothed",
+ "Camorrism",
+ "reissuer",
+ "soap",
+ "palaiotype",
+ "countercharm",
+ "yellowbird",
+ "palterly",
+ "writinger",
+ "boatfalls",
+ "tuglike",
+ "underbitten",
+ },
+
+ // shuf -n100 /usr/share/dict/words
+ {
+ "rooty",
+ "malcultivation",
+ "degrade",
+ "pseudoindependent",
+ "stillatory",
+ "antiseptize",
+ "protoamphibian",
+ "antiar",
+ "Esther",
+ "pseudelminth",
+ "superfluitance",
+ "teallite",
+ "disunity",
+ "spirignathous",
+ "vergency",
+ "myliobatid",
+ "inosic",
+ "overabstemious",
+ "patriarchally",
+ "foreimagine",
+ "coetaneity",
+ "hemimellitene",
+ "hyperspatial",
+ "aulophyte",
+ "electropoion",
+ "antitrope",
+ "Amarantus",
+ "smaltine",
+ "lighthead",
+ "syntonically",
+ "incubous",
+ "versation",
+ "cirsophthalmia",
+ "Ulidian",
+ "homoeography",
+ "Velella",
+ "Hecatean",
+ "serfage",
+ "Spermaphyta",
+ "palatoplasty",
+ "electroextraction",
+ "aconite",
+ "avirulence",
+ "initiator",
+ "besmear",
+ "unrecognizably",
+ "euphoniousness",
+ "balbuties",
+ "pascuage",
+ "quebracho",
+ "Yakala",
+ "auriform",
+ "sevenbark",
+ "superorganism",
+ "telesterion",
+ "ensand",
+ "nagaika",
+ "anisuria",
+ "etching",
+ "soundingly",
+ "grumpish",
+ "drillmaster",
+ "perfumed",
+ "dealkylate",
+ "anthracitiferous",
+ "predefiance",
+ "sulphoxylate",
+ "freeness",
+ "untucking",
+ "misworshiper",
+ "Nestorianize",
+ "nonegoistical",
+ "construe",
+ "upstroke",
+ "teated",
+ "nasolachrymal",
+ "Mastodontidae",
+ "gallows",
+ "radioluminescent",
+ "uncourtierlike",
+ "phasmatrope",
+ "Clunisian",
+ "drainage",
+ "sootless",
+ "brachyfacial",
+ "antiheroism",
+ "irreligionize",
+ "ked",
+ "unfact",
+ "nonprofessed",
+ "milady",
+ "conjecture",
+ "Arctomys",
+ "guapilla",
+ "Sassenach",
+ "emmetrope",
+ "rosewort",
+ "raphidiferous",
+ "pooh",
+ "Tyndallize",
+ },
+}
+
+func BenchmarkSampler_Check(b *testing.B) {
+ for _, keys := range counterTestCases {
+ b.Run(fmt.Sprintf("%v keys", len(keys)), func(b *testing.B) {
+ fac := NewSampler(
+ NewCore(
+ NewJSONEncoder(testEncoderConfig()),
+ &zaptest.Discarder{},
+ DebugLevel,
+ ),
+ time.Millisecond, 1, 1000)
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ i := 0
+ for pb.Next() {
+ ent := Entry{
+ Level: DebugLevel + Level(i%4),
+ Message: keys[i],
+ }
+ _ = fac.Check(ent, nil)
+ i++
+ if n := len(keys); i >= n {
+ i -= n
+ }
+ }
+ })
+ })
+ }
+}
diff --git a/vendor/go.uber.org/zap/zapcore/sampler_test.go b/vendor/go.uber.org/zap/zapcore/sampler_test.go
new file mode 100644
index 0000000..82588c9
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/sampler_test.go
@@ -0,0 +1,225 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore_test
+
+import (
+ "fmt"
+ "sync"
+ "testing"
+ "time"
+
+ "go.uber.org/atomic"
+ . "go.uber.org/zap/zapcore"
+ "go.uber.org/zap/zaptest"
+ "go.uber.org/zap/zaptest/observer"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func fakeSampler(lvl LevelEnabler, tick time.Duration, first, thereafter int) (Core, *observer.ObservedLogs) {
+ core, logs := observer.New(lvl)
+ core = NewSampler(core, tick, first, thereafter)
+ return core, logs
+}
+
+func assertSequence(t testing.TB, logs []observer.LoggedEntry, lvl Level, seq ...int64) {
+ seen := make([]int64, len(logs))
+ for i, entry := range logs {
+ require.Equal(t, "", entry.Message, "Message wasn't created by writeSequence.")
+ require.Equal(t, 1, len(entry.Context), "Unexpected number of fields.")
+ require.Equal(t, lvl, entry.Level, "Unexpected level.")
+ f := entry.Context[0]
+ require.Equal(t, "iter", f.Key, "Unexpected field key.")
+ require.Equal(t, Int64Type, f.Type, "Unexpected field type")
+ seen[i] = f.Integer
+ }
+ assert.Equal(t, seq, seen, "Unexpected sequence logged at level %v.", lvl)
+}
+
+func writeSequence(core Core, n int, lvl Level) {
+ // All tests using writeSequence verify that counters are shared between
+ // parent and child cores.
+ core = core.With([]Field{makeInt64Field("iter", n)})
+ if ce := core.Check(Entry{Level: lvl, Time: time.Now()}, nil); ce != nil {
+ ce.Write()
+ }
+}
+
+func TestSampler(t *testing.T) {
+ for _, lvl := range []Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel, PanicLevel, FatalLevel} {
+ sampler, logs := fakeSampler(DebugLevel, time.Minute, 2, 3)
+
+ // Ensure that counts aren't shared between levels.
+ probeLevel := DebugLevel
+ if lvl == DebugLevel {
+ probeLevel = InfoLevel
+ }
+ for i := 0; i < 10; i++ {
+ writeSequence(sampler, 1, probeLevel)
+ }
+ // Clear any output.
+ logs.TakeAll()
+
+ for i := 1; i < 10; i++ {
+ writeSequence(sampler, i, lvl)
+ }
+ assertSequence(t, logs.TakeAll(), lvl, 1, 2, 5, 8)
+ }
+}
+
+func TestSamplerDisabledLevels(t *testing.T) {
+ sampler, logs := fakeSampler(InfoLevel, time.Minute, 1, 100)
+
+ // Shouldn't be counted, because debug logging isn't enabled.
+ writeSequence(sampler, 1, DebugLevel)
+ writeSequence(sampler, 2, InfoLevel)
+ assertSequence(t, logs.TakeAll(), InfoLevel, 2)
+}
+
+func TestSamplerTicking(t *testing.T) {
+ // Ensure that we're resetting the sampler's counter every tick.
+ sampler, logs := fakeSampler(DebugLevel, 10*time.Millisecond, 5, 10)
+
+ // If we log five or fewer messages every tick, none of them should be
+ // dropped.
+ for tick := 0; tick < 2; tick++ {
+ for i := 1; i <= 5; i++ {
+ writeSequence(sampler, i, InfoLevel)
+ }
+ zaptest.Sleep(15 * time.Millisecond)
+ }
+ assertSequence(
+ t,
+ logs.TakeAll(),
+ InfoLevel,
+ 1, 2, 3, 4, 5, // first tick
+ 1, 2, 3, 4, 5, // second tick
+ )
+
+ // If we log quickly, we should drop some logs. The first five statements
+ // each tick should be logged, then every tenth.
+ for tick := 0; tick < 3; tick++ {
+ for i := 1; i < 18; i++ {
+ writeSequence(sampler, i, InfoLevel)
+ }
+ zaptest.Sleep(10 * time.Millisecond)
+ }
+
+ assertSequence(
+ t,
+ logs.TakeAll(),
+ InfoLevel,
+ 1, 2, 3, 4, 5, 15, // first tick
+ 1, 2, 3, 4, 5, 15, // second tick
+ 1, 2, 3, 4, 5, 15, // third tick
+ )
+}
+
+type countingCore struct {
+ logs atomic.Uint32
+}
+
+func (c *countingCore) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {
+ return ce.AddCore(ent, c)
+}
+
+func (c *countingCore) Write(Entry, []Field) error {
+ c.logs.Inc()
+ return nil
+}
+
+func (c *countingCore) With([]Field) Core { return c }
+func (*countingCore) Enabled(Level) bool { return true }
+func (*countingCore) Sync() error { return nil }
+
+func TestSamplerConcurrent(t *testing.T) {
+ const (
+ logsPerTick = 10
+ numMessages = 5
+ numTicks = 25
+ numGoroutines = 10
+ expectedCount = numMessages * logsPerTick * numTicks
+ )
+
+ tick := zaptest.Timeout(10 * time.Millisecond)
+ cc := &countingCore{}
+ sampler := NewSampler(cc, tick, logsPerTick, 100000)
+
+ var (
+ done atomic.Bool
+ wg sync.WaitGroup
+ )
+ for i := 0; i < numGoroutines; i++ {
+ wg.Add(1)
+ go func(i int) {
+ defer wg.Done()
+
+ for {
+ if done.Load() {
+ return
+ }
+ msg := fmt.Sprintf("msg%v", i%numMessages)
+ ent := Entry{Level: DebugLevel, Message: msg, Time: time.Now()}
+ if ce := sampler.Check(ent, nil); ce != nil {
+ ce.Write()
+ }
+
+ // Give a chance for other goroutines to run.
+ time.Sleep(time.Microsecond)
+ }
+ }(i)
+ }
+
+ time.AfterFunc(numTicks*tick, func() {
+ done.Store(true)
+ })
+ wg.Wait()
+
+ assert.InDelta(
+ t,
+ expectedCount,
+ cc.logs.Load(),
+ expectedCount/10,
+ "Unexpected number of logs",
+ )
+}
+
+func TestSamplerRaces(t *testing.T) {
+ sampler, _ := fakeSampler(DebugLevel, time.Minute, 1, 1000)
+
+ var wg sync.WaitGroup
+ start := make(chan struct{})
+
+ for i := 0; i < 100; i++ {
+ wg.Add(1)
+ go func() {
+ <-start
+ for j := 0; j < 100; j++ {
+ writeSequence(sampler, j, InfoLevel)
+ }
+ wg.Done()
+ }()
+ }
+
+ close(start)
+ wg.Wait()
+}
diff --git a/vendor/go.uber.org/zap/zapcore/tee.go b/vendor/go.uber.org/zap/zapcore/tee.go
new file mode 100644
index 0000000..07a32ee
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/tee.go
@@ -0,0 +1,81 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import "go.uber.org/multierr"
+
+type multiCore []Core
+
+// NewTee creates a Core that duplicates log entries into two or more
+// underlying Cores.
+//
+// Calling it with a single Core returns the input unchanged, and calling
+// it with no input returns a no-op Core.
+func NewTee(cores ...Core) Core {
+ switch len(cores) {
+ case 0:
+ return NewNopCore()
+ case 1:
+ return cores[0]
+ default:
+ return multiCore(cores)
+ }
+}
+
+func (mc multiCore) With(fields []Field) Core {
+ clone := make(multiCore, len(mc))
+ for i := range mc {
+ clone[i] = mc[i].With(fields)
+ }
+ return clone
+}
+
+func (mc multiCore) Enabled(lvl Level) bool {
+ for i := range mc {
+ if mc[i].Enabled(lvl) {
+ return true
+ }
+ }
+ return false
+}
+
+func (mc multiCore) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {
+ for i := range mc {
+ ce = mc[i].Check(ent, ce)
+ }
+ return ce
+}
+
+func (mc multiCore) Write(ent Entry, fields []Field) error {
+ var err error
+ for i := range mc {
+ err = multierr.Append(err, mc[i].Write(ent, fields))
+ }
+ return err
+}
+
+func (mc multiCore) Sync() error {
+ var err error
+ for i := range mc {
+ err = multierr.Append(err, mc[i].Sync())
+ }
+ return err
+}
diff --git a/vendor/go.uber.org/zap/zapcore/tee_logger_bench_test.go b/vendor/go.uber.org/zap/zapcore/tee_logger_bench_test.go
new file mode 100644
index 0000000..041f10a
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/tee_logger_bench_test.go
@@ -0,0 +1,62 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore_test
+
+import (
+ "testing"
+
+ . "go.uber.org/zap/zapcore"
+ "go.uber.org/zap/zaptest"
+)
+
+func withBenchedTee(b *testing.B, f func(Core)) {
+ fac := NewTee(
+ NewCore(NewJSONEncoder(testEncoderConfig()), &zaptest.Discarder{}, DebugLevel),
+ NewCore(NewJSONEncoder(testEncoderConfig()), &zaptest.Discarder{}, InfoLevel),
+ )
+ b.ResetTimer()
+ f(fac)
+}
+
+func BenchmarkTeeCheck(b *testing.B) {
+ cases := []struct {
+ lvl Level
+ msg string
+ }{
+ {DebugLevel, "foo"},
+ {InfoLevel, "bar"},
+ {WarnLevel, "baz"},
+ {ErrorLevel, "babble"},
+ }
+ withBenchedTee(b, func(core Core) {
+ b.RunParallel(func(pb *testing.PB) {
+ i := 0
+ for pb.Next() {
+ tt := cases[i]
+ entry := Entry{Level: tt.lvl, Message: tt.msg}
+ if cm := core.Check(entry, nil); cm != nil {
+ cm.Write(Field{Key: "i", Integer: int64(i), Type: Int64Type})
+ }
+ i = (i + 1) % len(cases)
+ }
+ })
+ })
+}
diff --git a/vendor/go.uber.org/zap/zapcore/tee_test.go b/vendor/go.uber.org/zap/zapcore/tee_test.go
new file mode 100644
index 0000000..754abbb
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/tee_test.go
@@ -0,0 +1,153 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore_test
+
+import (
+ "errors"
+ "testing"
+
+ . "go.uber.org/zap/zapcore"
+ "go.uber.org/zap/zaptest"
+ "go.uber.org/zap/zaptest/observer"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func withTee(f func(core Core, debugLogs, warnLogs *observer.ObservedLogs)) {
+ debugLogger, debugLogs := observer.New(DebugLevel)
+ warnLogger, warnLogs := observer.New(WarnLevel)
+ tee := NewTee(debugLogger, warnLogger)
+ f(tee, debugLogs, warnLogs)
+}
+
+func TestTeeUnusualInput(t *testing.T) {
+ // Verify that Tee handles receiving one and no inputs correctly.
+ t.Run("one input", func(t *testing.T) {
+ obs, _ := observer.New(DebugLevel)
+ assert.Equal(t, obs, NewTee(obs), "Expected to return single inputs unchanged.")
+ })
+ t.Run("no input", func(t *testing.T) {
+ assert.Equal(t, NewNopCore(), NewTee(), "Expected to return NopCore.")
+ })
+}
+
+func TestTeeCheck(t *testing.T) {
+ withTee(func(tee Core, debugLogs, warnLogs *observer.ObservedLogs) {
+ debugEntry := Entry{Level: DebugLevel, Message: "log-at-debug"}
+ infoEntry := Entry{Level: InfoLevel, Message: "log-at-info"}
+ warnEntry := Entry{Level: WarnLevel, Message: "log-at-warn"}
+ errorEntry := Entry{Level: ErrorLevel, Message: "log-at-error"}
+ for _, ent := range []Entry{debugEntry, infoEntry, warnEntry, errorEntry} {
+ if ce := tee.Check(ent, nil); ce != nil {
+ ce.Write()
+ }
+ }
+
+ assert.Equal(t, []observer.LoggedEntry{
+ {Entry: debugEntry, Context: []Field{}},
+ {Entry: infoEntry, Context: []Field{}},
+ {Entry: warnEntry, Context: []Field{}},
+ {Entry: errorEntry, Context: []Field{}},
+ }, debugLogs.All())
+
+ assert.Equal(t, []observer.LoggedEntry{
+ {Entry: warnEntry, Context: []Field{}},
+ {Entry: errorEntry, Context: []Field{}},
+ }, warnLogs.All())
+ })
+}
+
+func TestTeeWrite(t *testing.T) {
+ // Calling the tee's Write method directly should always log, regardless of
+ // the configured level.
+ withTee(func(tee Core, debugLogs, warnLogs *observer.ObservedLogs) {
+ debugEntry := Entry{Level: DebugLevel, Message: "log-at-debug"}
+ warnEntry := Entry{Level: WarnLevel, Message: "log-at-warn"}
+ for _, ent := range []Entry{debugEntry, warnEntry} {
+ tee.Write(ent, nil)
+ }
+
+ for _, logs := range []*observer.ObservedLogs{debugLogs, warnLogs} {
+ assert.Equal(t, []observer.LoggedEntry{
+ {Entry: debugEntry, Context: []Field{}},
+ {Entry: warnEntry, Context: []Field{}},
+ }, logs.All())
+ }
+ })
+}
+
+func TestTeeWith(t *testing.T) {
+ withTee(func(tee Core, debugLogs, warnLogs *observer.ObservedLogs) {
+ f := makeInt64Field("k", 42)
+ tee = tee.With([]Field{f})
+ ent := Entry{Level: WarnLevel, Message: "log-at-warn"}
+ if ce := tee.Check(ent, nil); ce != nil {
+ ce.Write()
+ }
+
+ for _, logs := range []*observer.ObservedLogs{debugLogs, warnLogs} {
+ assert.Equal(t, []observer.LoggedEntry{
+ {Entry: ent, Context: []Field{f}},
+ }, logs.All())
+ }
+ })
+}
+
+func TestTeeEnabled(t *testing.T) {
+ infoLogger, _ := observer.New(InfoLevel)
+ warnLogger, _ := observer.New(WarnLevel)
+ tee := NewTee(infoLogger, warnLogger)
+ tests := []struct {
+ lvl Level
+ enabled bool
+ }{
+ {DebugLevel, false},
+ {InfoLevel, true},
+ {WarnLevel, true},
+ {ErrorLevel, true},
+ {DPanicLevel, true},
+ {PanicLevel, true},
+ {FatalLevel, true},
+ }
+
+ for _, tt := range tests {
+ assert.Equal(t, tt.enabled, tee.Enabled(tt.lvl), "Unexpected Enabled result for level %s.", tt.lvl)
+ }
+}
+
+func TestTeeSync(t *testing.T) {
+ infoLogger, _ := observer.New(InfoLevel)
+ warnLogger, _ := observer.New(WarnLevel)
+ tee := NewTee(infoLogger, warnLogger)
+ assert.NoError(t, tee.Sync(), "Unexpected error from Syncing a tee.")
+
+ sink := &zaptest.Discarder{}
+ err := errors.New("failed")
+ sink.SetError(err)
+
+ noSync := NewCore(
+ NewJSONEncoder(testEncoderConfig()),
+ sink,
+ DebugLevel,
+ )
+ tee = NewTee(tee, noSync)
+ assert.Equal(t, err, tee.Sync(), "Expected an error when part of tee can't Sync.")
+}
diff --git a/vendor/go.uber.org/zap/zapcore/write_syncer.go b/vendor/go.uber.org/zap/zapcore/write_syncer.go
new file mode 100644
index 0000000..209e25f
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/write_syncer.go
@@ -0,0 +1,123 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "io"
+ "sync"
+
+ "go.uber.org/multierr"
+)
+
+// A WriteSyncer is an io.Writer that can also flush any buffered data. Note
+// that *os.File (and thus, os.Stderr and os.Stdout) implement WriteSyncer.
+type WriteSyncer interface {
+ io.Writer
+ Sync() error
+}
+
+// AddSync converts an io.Writer to a WriteSyncer. It attempts to be
+// intelligent: if the concrete type of the io.Writer implements WriteSyncer,
+// we'll use the existing Sync method. If it doesn't, we'll add a no-op Sync.
+func AddSync(w io.Writer) WriteSyncer {
+ switch w := w.(type) {
+ case WriteSyncer:
+ return w
+ default:
+ return writerWrapper{w}
+ }
+}
+
+type lockedWriteSyncer struct {
+ sync.Mutex
+ ws WriteSyncer
+}
+
+// Lock wraps a WriteSyncer in a mutex to make it safe for concurrent use. In
+// particular, *os.Files must be locked before use.
+func Lock(ws WriteSyncer) WriteSyncer {
+ if _, ok := ws.(*lockedWriteSyncer); ok {
+ // no need to layer on another lock
+ return ws
+ }
+ return &lockedWriteSyncer{ws: ws}
+}
+
+func (s *lockedWriteSyncer) Write(bs []byte) (int, error) {
+ s.Lock()
+ n, err := s.ws.Write(bs)
+ s.Unlock()
+ return n, err
+}
+
+func (s *lockedWriteSyncer) Sync() error {
+ s.Lock()
+ err := s.ws.Sync()
+ s.Unlock()
+ return err
+}
+
+type writerWrapper struct {
+ io.Writer
+}
+
+func (w writerWrapper) Sync() error {
+ return nil
+}
+
+type multiWriteSyncer []WriteSyncer
+
+// NewMultiWriteSyncer creates a WriteSyncer that duplicates its writes
+// and sync calls, much like io.MultiWriter.
+func NewMultiWriteSyncer(ws ...WriteSyncer) WriteSyncer {
+ if len(ws) == 1 {
+ return ws[0]
+ }
+ // Copy to protect against https://github.com/golang/go/issues/7809
+ return multiWriteSyncer(append([]WriteSyncer(nil), ws...))
+}
+
+// See https://golang.org/src/io/multi.go
+// When not all underlying syncers write the same number of bytes,
+// the smallest number is returned even though Write() is called on
+// all of them.
+func (ws multiWriteSyncer) Write(p []byte) (int, error) {
+ var writeErr error
+ nWritten := 0
+ for _, w := range ws {
+ n, err := w.Write(p)
+ writeErr = multierr.Append(writeErr, err)
+ if nWritten == 0 && n != 0 {
+ nWritten = n
+ } else if n < nWritten {
+ nWritten = n
+ }
+ }
+ return nWritten, writeErr
+}
+
+func (ws multiWriteSyncer) Sync() error {
+ var err error
+ for _, w := range ws {
+ err = multierr.Append(err, w.Sync())
+ }
+ return err
+}
diff --git a/vendor/go.uber.org/zap/zapcore/write_syncer_bench_test.go b/vendor/go.uber.org/zap/zapcore/write_syncer_bench_test.go
new file mode 100644
index 0000000..0adcad8
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/write_syncer_bench_test.go
@@ -0,0 +1,56 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "testing"
+
+ "go.uber.org/zap/zaptest"
+)
+
+func BenchmarkMultiWriteSyncer(b *testing.B) {
+ b.Run("2", func(b *testing.B) {
+ w := NewMultiWriteSyncer(
+ &zaptest.Discarder{},
+ &zaptest.Discarder{},
+ )
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ w.Write([]byte("foobarbazbabble"))
+ }
+ })
+ })
+ b.Run("4", func(b *testing.B) {
+ w := NewMultiWriteSyncer(
+ &zaptest.Discarder{},
+ &zaptest.Discarder{},
+ &zaptest.Discarder{},
+ &zaptest.Discarder{},
+ )
+ b.ResetTimer()
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ w.Write([]byte("foobarbazbabble"))
+ }
+ })
+ })
+}
diff --git a/vendor/go.uber.org/zap/zapcore/write_syncer_test.go b/vendor/go.uber.org/zap/zapcore/write_syncer_test.go
new file mode 100644
index 0000000..084b94a
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapcore/write_syncer_test.go
@@ -0,0 +1,137 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapcore
+
+import (
+ "bytes"
+ "errors"
+ "testing"
+
+ "io"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "go.uber.org/zap/zaptest"
+)
+
+type writeSyncSpy struct {
+ io.Writer
+ zaptest.Syncer
+}
+
+func requireWriteWorks(t testing.TB, ws WriteSyncer) {
+ n, err := ws.Write([]byte("foo"))
+ require.NoError(t, err, "Unexpected error writing to WriteSyncer.")
+ require.Equal(t, 3, n, "Wrote an unexpected number of bytes.")
+}
+
+func TestAddSyncWriteSyncer(t *testing.T) {
+ buf := &bytes.Buffer{}
+ concrete := &writeSyncSpy{Writer: buf}
+ ws := AddSync(concrete)
+ requireWriteWorks(t, ws)
+
+ require.NoError(t, ws.Sync(), "Unexpected error syncing a WriteSyncer.")
+ require.True(t, concrete.Called(), "Expected to dispatch to concrete type's Sync method.")
+
+ concrete.SetError(errors.New("fail"))
+ assert.Error(t, ws.Sync(), "Expected to propagate errors from concrete type's Sync method.")
+}
+
+func TestAddSyncWriter(t *testing.T) {
+ // If we pass a plain io.Writer, make sure that we still get a WriteSyncer
+ // with a no-op Sync.
+ buf := &bytes.Buffer{}
+ ws := AddSync(buf)
+ requireWriteWorks(t, ws)
+ assert.NoError(t, ws.Sync(), "Unexpected error calling a no-op Sync method.")
+}
+
+func TestNewMultiWriteSyncerWorksForSingleWriter(t *testing.T) {
+ w := &zaptest.Buffer{}
+
+ ws := NewMultiWriteSyncer(w)
+ assert.Equal(t, w, ws, "Expected NewMultiWriteSyncer to return the same WriteSyncer object for a single argument.")
+
+ ws.Sync()
+ assert.True(t, w.Called(), "Expected Sync to be called on the created WriteSyncer")
+}
+
+func TestMultiWriteSyncerWritesBoth(t *testing.T) {
+ first := &bytes.Buffer{}
+ second := &bytes.Buffer{}
+ ws := NewMultiWriteSyncer(AddSync(first), AddSync(second))
+
+ msg := []byte("dumbledore")
+ n, err := ws.Write(msg)
+ require.NoError(t, err, "Expected successful buffer write")
+ assert.Equal(t, len(msg), n)
+
+ assert.Equal(t, msg, first.Bytes())
+ assert.Equal(t, msg, second.Bytes())
+}
+
+func TestMultiWriteSyncerFailsWrite(t *testing.T) {
+ ws := NewMultiWriteSyncer(AddSync(&zaptest.FailWriter{}))
+ _, err := ws.Write([]byte("test"))
+ assert.Error(t, err, "Write error should propagate")
+}
+
+func TestMultiWriteSyncerFailsShortWrite(t *testing.T) {
+ ws := NewMultiWriteSyncer(AddSync(&zaptest.ShortWriter{}))
+ n, err := ws.Write([]byte("test"))
+ assert.NoError(t, err, "Expected fake-success from short write")
+ assert.Equal(t, 3, n, "Expected byte count to return from underlying writer")
+}
+
+func TestWritestoAllSyncs_EvenIfFirstErrors(t *testing.T) {
+ failer := &zaptest.FailWriter{}
+ second := &bytes.Buffer{}
+ ws := NewMultiWriteSyncer(AddSync(failer), AddSync(second))
+
+ _, err := ws.Write([]byte("fail"))
+ assert.Error(t, err, "Expected error from call to a writer that failed")
+ assert.Equal(t, []byte("fail"), second.Bytes(), "Expected second sink to be written after first error")
+}
+
+func TestMultiWriteSyncerSync_PropagatesErrors(t *testing.T) {
+ badsink := &zaptest.Buffer{}
+ badsink.SetError(errors.New("sink is full"))
+ ws := NewMultiWriteSyncer(&zaptest.Discarder{}, badsink)
+
+ assert.Error(t, ws.Sync(), "Expected sync error to propagate")
+}
+
+func TestMultiWriteSyncerSync_NoErrorsOnDiscard(t *testing.T) {
+ ws := NewMultiWriteSyncer(&zaptest.Discarder{})
+ assert.NoError(t, ws.Sync(), "Expected error-free sync to /dev/null")
+}
+
+func TestMultiWriteSyncerSync_AllCalled(t *testing.T) {
+ failed, second := &zaptest.Buffer{}, &zaptest.Buffer{}
+
+ failed.SetError(errors.New("disposal broken"))
+ ws := NewMultiWriteSyncer(failed, second)
+
+ assert.Error(t, ws.Sync(), "Expected first sink to fail")
+ assert.True(t, failed.Called(), "Expected first sink to have Sync method called.")
+ assert.True(t, second.Called(), "Expected call to Sync even with first failure.")
+}
diff --git a/vendor/go.uber.org/zap/zapgrpc/zapgrpc.go b/vendor/go.uber.org/zap/zapgrpc/zapgrpc.go
new file mode 100644
index 0000000..1181e6a
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapgrpc/zapgrpc.go
@@ -0,0 +1,100 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+// Package zapgrpc provides a logger that is compatible with grpclog.
+package zapgrpc // import "go.uber.org/zap/zapgrpc"
+
+import "go.uber.org/zap"
+
+// An Option overrides a Logger's default configuration.
+type Option interface {
+ apply(*Logger)
+}
+
+type optionFunc func(*Logger)
+
+func (f optionFunc) apply(log *Logger) {
+ f(log)
+}
+
+// WithDebug configures a Logger to print at zap's DebugLevel instead of
+// InfoLevel.
+func WithDebug() Option {
+ return optionFunc(func(logger *Logger) {
+ logger.print = (*zap.SugaredLogger).Debug
+ logger.printf = (*zap.SugaredLogger).Debugf
+ })
+}
+
+// NewLogger returns a new Logger.
+//
+// By default, Loggers print at zap's InfoLevel.
+func NewLogger(l *zap.Logger, options ...Option) *Logger {
+ logger := &Logger{
+ log: l.Sugar(),
+ fatal: (*zap.SugaredLogger).Fatal,
+ fatalf: (*zap.SugaredLogger).Fatalf,
+ print: (*zap.SugaredLogger).Info,
+ printf: (*zap.SugaredLogger).Infof,
+ }
+ for _, option := range options {
+ option.apply(logger)
+ }
+ return logger
+}
+
+// Logger adapts zap's Logger to be compatible with grpclog.Logger.
+type Logger struct {
+ log *zap.SugaredLogger
+ fatal func(*zap.SugaredLogger, ...interface{})
+ fatalf func(*zap.SugaredLogger, string, ...interface{})
+ print func(*zap.SugaredLogger, ...interface{})
+ printf func(*zap.SugaredLogger, string, ...interface{})
+}
+
+// Fatal implements grpclog.Logger.
+func (l *Logger) Fatal(args ...interface{}) {
+ l.fatal(l.log, args...)
+}
+
+// Fatalf implements grpclog.Logger.
+func (l *Logger) Fatalf(format string, args ...interface{}) {
+ l.fatalf(l.log, format, args...)
+}
+
+// Fatalln implements grpclog.Logger.
+func (l *Logger) Fatalln(args ...interface{}) {
+ l.fatal(l.log, args...)
+}
+
+// Print implements grpclog.Logger.
+func (l *Logger) Print(args ...interface{}) {
+ l.print(l.log, args...)
+}
+
+// Printf implements grpclog.Logger.
+func (l *Logger) Printf(format string, args ...interface{}) {
+ l.printf(l.log, format, args...)
+}
+
+// Println implements grpclog.Logger.
+func (l *Logger) Println(args ...interface{}) {
+ l.print(l.log, args...)
+}
diff --git a/vendor/go.uber.org/zap/zapgrpc/zapgrpc_test.go b/vendor/go.uber.org/zap/zapgrpc/zapgrpc_test.go
new file mode 100644
index 0000000..036f3d7
--- /dev/null
+++ b/vendor/go.uber.org/zap/zapgrpc/zapgrpc_test.go
@@ -0,0 +1,115 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zapgrpc
+
+import (
+ "testing"
+
+ "go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
+ "go.uber.org/zap/zaptest/observer"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestLoggerInfoExpected(t *testing.T) {
+ checkMessages(t, zapcore.DebugLevel, nil, zapcore.InfoLevel, []string{
+ "hello",
+ "world",
+ "foo",
+ }, func(logger *Logger) {
+ logger.Print("hello")
+ logger.Printf("world")
+ logger.Println("foo")
+ })
+}
+
+func TestLoggerDebugExpected(t *testing.T) {
+ checkMessages(t, zapcore.DebugLevel, []Option{WithDebug()}, zapcore.DebugLevel, []string{
+ "hello",
+ "world",
+ "foo",
+ }, func(logger *Logger) {
+ logger.Print("hello")
+ logger.Printf("world")
+ logger.Println("foo")
+ })
+}
+
+func TestLoggerDebugSuppressed(t *testing.T) {
+ checkMessages(t, zapcore.InfoLevel, []Option{WithDebug()}, zapcore.DebugLevel, nil, func(logger *Logger) {
+ logger.Print("hello")
+ logger.Printf("world")
+ logger.Println("foo")
+ })
+}
+
+func TestLoggerFatalExpected(t *testing.T) {
+ checkMessages(t, zapcore.DebugLevel, nil, zapcore.FatalLevel, []string{
+ "hello",
+ "world",
+ "foo",
+ }, func(logger *Logger) {
+ logger.Fatal("hello")
+ logger.Fatalf("world")
+ logger.Fatalln("foo")
+ })
+}
+
+func checkMessages(
+ t testing.TB,
+ enab zapcore.LevelEnabler,
+ opts []Option,
+ expectedLevel zapcore.Level,
+ expectedMessages []string,
+ f func(*Logger),
+) {
+ if expectedLevel == zapcore.FatalLevel {
+ expectedLevel = zapcore.WarnLevel
+ }
+ withLogger(enab, opts, func(logger *Logger, observedLogs *observer.ObservedLogs) {
+ f(logger)
+ logEntries := observedLogs.All()
+ require.Equal(t, len(expectedMessages), len(logEntries))
+ for i, logEntry := range logEntries {
+ require.Equal(t, expectedLevel, logEntry.Level)
+ require.Equal(t, expectedMessages[i], logEntry.Message)
+ }
+ })
+}
+
+func withLogger(
+ enab zapcore.LevelEnabler,
+ opts []Option,
+ f func(*Logger, *observer.ObservedLogs),
+) {
+ core, observedLogs := observer.New(enab)
+ f(NewLogger(zap.New(core), append(opts, withWarn())...), observedLogs)
+}
+
+// withWarn redirects the fatal level to the warn level, which makes testing
+// easier.
+func withWarn() Option {
+ return optionFunc(func(logger *Logger) {
+ logger.fatal = (*zap.SugaredLogger).Warn
+ logger.fatalf = (*zap.SugaredLogger).Warnf
+ })
+}
diff --git a/vendor/go.uber.org/zap/zaptest/doc.go b/vendor/go.uber.org/zap/zaptest/doc.go
new file mode 100644
index 0000000..464f589
--- /dev/null
+++ b/vendor/go.uber.org/zap/zaptest/doc.go
@@ -0,0 +1,27 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+// Package zaptest provides low-level helpers for testing log output. These
+// utilities are helpful in zap's own unit tests, but any assertions using
+// them are strongly coupled to a single encoding.
+//
+// Most users should use go.uber.org/zap/zaptest/observer instead of this
+// package.
+package zaptest // import "go.uber.org/zap/zaptest"
diff --git a/vendor/go.uber.org/zap/zaptest/observer/observer.go b/vendor/go.uber.org/zap/zaptest/observer/observer.go
new file mode 100644
index 0000000..0cf57a1
--- /dev/null
+++ b/vendor/go.uber.org/zap/zaptest/observer/observer.go
@@ -0,0 +1,174 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+// Package observer provides a zapcore.Core that keeps an in-memory,
+// encoding-agnostic repesentation of log entries. It's useful for
+// applications that want to unit test their log output without tying their
+// tests to a particular output encoding.
+package observer // import "go.uber.org/zap/zaptest/observer"
+
+import (
+ "strings"
+ "sync"
+ "time"
+
+ "go.uber.org/zap/zapcore"
+)
+
+// An LoggedEntry is an encoding-agnostic representation of a log message.
+// Field availability is context dependant.
+type LoggedEntry struct {
+ zapcore.Entry
+ Context []zapcore.Field
+}
+
+// ObservedLogs is a concurrency-safe, ordered collection of observed logs.
+type ObservedLogs struct {
+ mu sync.RWMutex
+ logs []LoggedEntry
+}
+
+// Len returns the number of items in the collection.
+func (o *ObservedLogs) Len() int {
+ o.mu.RLock()
+ n := len(o.logs)
+ o.mu.RUnlock()
+ return n
+}
+
+// All returns a copy of all the observed logs.
+func (o *ObservedLogs) All() []LoggedEntry {
+ o.mu.RLock()
+ ret := make([]LoggedEntry, len(o.logs))
+ for i := range o.logs {
+ ret[i] = o.logs[i]
+ }
+ o.mu.RUnlock()
+ return ret
+}
+
+// TakeAll returns a copy of all the observed logs, and truncates the observed
+// slice.
+func (o *ObservedLogs) TakeAll() []LoggedEntry {
+ o.mu.Lock()
+ ret := o.logs
+ o.logs = nil
+ o.mu.Unlock()
+ return ret
+}
+
+// AllUntimed returns a copy of all the observed logs, but overwrites the
+// observed timestamps with time.Time's zero value. This is useful when making
+// assertions in tests.
+func (o *ObservedLogs) AllUntimed() []LoggedEntry {
+ ret := o.All()
+ for i := range ret {
+ ret[i].Time = time.Time{}
+ }
+ return ret
+}
+
+// FilterMessage filters entries to those that have the specified message.
+func (o *ObservedLogs) FilterMessage(msg string) *ObservedLogs {
+ return o.filter(func(e LoggedEntry) bool {
+ return e.Message == msg
+ })
+}
+
+// FilterMessageSnippet filters entries to those that have a message containing the specified snippet.
+func (o *ObservedLogs) FilterMessageSnippet(snippet string) *ObservedLogs {
+ return o.filter(func(e LoggedEntry) bool {
+ return strings.Contains(e.Message, snippet)
+ })
+}
+
+// FilterField filters entries to those that have the specified field.
+func (o *ObservedLogs) FilterField(field zapcore.Field) *ObservedLogs {
+ return o.filter(func(e LoggedEntry) bool {
+ for _, ctxField := range e.Context {
+ if ctxField.Equals(field) {
+ return true
+ }
+ }
+ return false
+ })
+}
+
+func (o *ObservedLogs) filter(match func(LoggedEntry) bool) *ObservedLogs {
+ o.mu.RLock()
+ defer o.mu.RUnlock()
+
+ var filtered []LoggedEntry
+ for _, entry := range o.logs {
+ if match(entry) {
+ filtered = append(filtered, entry)
+ }
+ }
+ return &ObservedLogs{logs: filtered}
+}
+
+func (o *ObservedLogs) add(log LoggedEntry) {
+ o.mu.Lock()
+ o.logs = append(o.logs, log)
+ o.mu.Unlock()
+}
+
+// New creates a new Core that buffers logs in memory (without any encoding).
+// It's particularly useful in tests.
+func New(enab zapcore.LevelEnabler) (zapcore.Core, *ObservedLogs) {
+ ol := &ObservedLogs{}
+ return &contextObserver{
+ LevelEnabler: enab,
+ logs: ol,
+ }, ol
+}
+
+type contextObserver struct {
+ zapcore.LevelEnabler
+ logs *ObservedLogs
+ context []zapcore.Field
+}
+
+func (co *contextObserver) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {
+ if co.Enabled(ent.Level) {
+ return ce.AddCore(ent, co)
+ }
+ return ce
+}
+
+func (co *contextObserver) With(fields []zapcore.Field) zapcore.Core {
+ return &contextObserver{
+ LevelEnabler: co.LevelEnabler,
+ logs: co.logs,
+ context: append(co.context[:len(co.context):len(co.context)], fields...),
+ }
+}
+
+func (co *contextObserver) Write(ent zapcore.Entry, fields []zapcore.Field) error {
+ all := make([]zapcore.Field, 0, len(fields)+len(co.context))
+ all = append(all, co.context...)
+ all = append(all, fields...)
+ co.logs.add(LoggedEntry{ent, all})
+ return nil
+}
+
+func (co *contextObserver) Sync() error {
+ return nil
+}
diff --git a/vendor/go.uber.org/zap/zaptest/observer/observer_test.go b/vendor/go.uber.org/zap/zaptest/observer/observer_test.go
new file mode 100644
index 0000000..e1a0da7
--- /dev/null
+++ b/vendor/go.uber.org/zap/zaptest/observer/observer_test.go
@@ -0,0 +1,215 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package observer_test
+
+import (
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
+ "go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
+ . "go.uber.org/zap/zaptest/observer"
+)
+
+func assertEmpty(t testing.TB, logs *ObservedLogs) {
+ assert.Equal(t, 0, logs.Len(), "Expected empty ObservedLogs to have zero length.")
+ assert.Equal(t, []LoggedEntry{}, logs.All(), "Unexpected LoggedEntries in empty ObservedLogs.")
+}
+
+func TestObserver(t *testing.T) {
+ observer, logs := New(zap.InfoLevel)
+ assertEmpty(t, logs)
+
+ assert.NoError(t, observer.Sync(), "Unexpected failure in no-op Sync")
+
+ obs := zap.New(observer).With(zap.Int("i", 1))
+ obs.Info("foo")
+ obs.Debug("bar")
+ want := []LoggedEntry{{
+ Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "foo"},
+ Context: []zapcore.Field{zap.Int("i", 1)},
+ }}
+
+ assert.Equal(t, 1, logs.Len(), "Unexpected observed logs Len.")
+ assert.Equal(t, want, logs.AllUntimed(), "Unexpected contents from AllUntimed.")
+
+ all := logs.All()
+ require.Equal(t, 1, len(all), "Unexpected numbed of LoggedEntries returned from All.")
+ assert.NotEqual(t, time.Time{}, all[0].Time, "Expected non-zero time on LoggedEntry.")
+
+ // copy & zero time for stable assertions
+ untimed := append([]LoggedEntry{}, all...)
+ untimed[0].Time = time.Time{}
+ assert.Equal(t, want, untimed, "Unexpected LoggedEntries from All.")
+
+ assert.Equal(t, all, logs.TakeAll(), "Expected All and TakeAll to return identical results.")
+ assertEmpty(t, logs)
+}
+
+func TestObserverWith(t *testing.T) {
+ sf1, logs := New(zap.InfoLevel)
+
+ // need to pad out enough initial fields so that the underlying slice cap()
+ // gets ahead of its len() so that the sf3/4 With append's could choose
+ // not to copy (if the implementation doesn't force them)
+ sf1 = sf1.With([]zapcore.Field{zap.Int("a", 1), zap.Int("b", 2)})
+
+ sf2 := sf1.With([]zapcore.Field{zap.Int("c", 3)})
+ sf3 := sf2.With([]zapcore.Field{zap.Int("d", 4)})
+ sf4 := sf2.With([]zapcore.Field{zap.Int("e", 5)})
+ ent := zapcore.Entry{Level: zap.InfoLevel, Message: "hello"}
+
+ for i, core := range []zapcore.Core{sf2, sf3, sf4} {
+ if ce := core.Check(ent, nil); ce != nil {
+ ce.Write(zap.Int("i", i))
+ }
+ }
+
+ assert.Equal(t, []LoggedEntry{
+ {
+ Entry: ent,
+ Context: []zapcore.Field{
+ zap.Int("a", 1),
+ zap.Int("b", 2),
+ zap.Int("c", 3),
+ zap.Int("i", 0),
+ },
+ },
+ {
+ Entry: ent,
+ Context: []zapcore.Field{
+ zap.Int("a", 1),
+ zap.Int("b", 2),
+ zap.Int("c", 3),
+ zap.Int("d", 4),
+ zap.Int("i", 1),
+ },
+ },
+ {
+ Entry: ent,
+ Context: []zapcore.Field{
+ zap.Int("a", 1),
+ zap.Int("b", 2),
+ zap.Int("c", 3),
+ zap.Int("e", 5),
+ zap.Int("i", 2),
+ },
+ },
+ }, logs.All(), "expected no field sharing between With siblings")
+}
+
+func TestFilters(t *testing.T) {
+ logs := []LoggedEntry{
+ {
+ Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "log a"},
+ Context: []zapcore.Field{zap.String("fStr", "1"), zap.Int("a", 1)},
+ },
+ {
+ Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "log a"},
+ Context: []zapcore.Field{zap.String("fStr", "2"), zap.Int("b", 2)},
+ },
+ {
+ Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "log b"},
+ Context: []zapcore.Field{zap.Int("a", 1), zap.Int("b", 2)},
+ },
+ {
+ Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "log c"},
+ Context: []zapcore.Field{zap.Int("a", 1), zap.Namespace("ns"), zap.Int("a", 2)},
+ },
+ {
+ Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "msg 1"},
+ Context: []zapcore.Field{zap.Int("a", 1), zap.Namespace("ns")},
+ },
+ {
+ Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "any map"},
+ Context: []zapcore.Field{zap.Any("map", map[string]string{"a": "b"})},
+ },
+ {
+ Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "any slice"},
+ Context: []zapcore.Field{zap.Any("slice", []string{"a"})},
+ },
+ }
+
+ logger, sink := New(zap.InfoLevel)
+ for _, log := range logs {
+ logger.Write(log.Entry, log.Context)
+ }
+
+ tests := []struct {
+ msg string
+ filtered *ObservedLogs
+ want []LoggedEntry
+ }{
+ {
+ msg: "filter by message",
+ filtered: sink.FilterMessage("log a"),
+ want: logs[0:2],
+ },
+ {
+ msg: "filter by field",
+ filtered: sink.FilterField(zap.String("fStr", "1")),
+ want: logs[0:1],
+ },
+ {
+ msg: "filter by message and field",
+ filtered: sink.FilterMessage("log a").FilterField(zap.Int("b", 2)),
+ want: logs[1:2],
+ },
+ {
+ msg: "filter by field with duplicate fields",
+ filtered: sink.FilterField(zap.Int("a", 2)),
+ want: logs[3:4],
+ },
+ {
+ msg: "filter doesn't match any messages",
+ filtered: sink.FilterMessage("no match"),
+ want: []LoggedEntry{},
+ },
+ {
+ msg: "filter by snippet",
+ filtered: sink.FilterMessageSnippet("log"),
+ want: logs[0:4],
+ },
+ {
+ msg: "filter by snippet and field",
+ filtered: sink.FilterMessageSnippet("a").FilterField(zap.Int("b", 2)),
+ want: logs[1:2],
+ },
+ {
+ msg: "filter for map",
+ filtered: sink.FilterField(zap.Any("map", map[string]string{"a": "b"})),
+ want: logs[5:6],
+ },
+ {
+ msg: "filter for slice",
+ filtered: sink.FilterField(zap.Any("slice", []string{"a"})),
+ want: logs[6:7],
+ },
+ }
+
+ for _, tt := range tests {
+ got := tt.filtered.AllUntimed()
+ assert.Equal(t, tt.want, got, tt.msg)
+ }
+}
diff --git a/vendor/go.uber.org/zap/zaptest/timeout.go b/vendor/go.uber.org/zap/zaptest/timeout.go
new file mode 100644
index 0000000..ed24c98
--- /dev/null
+++ b/vendor/go.uber.org/zap/zaptest/timeout.go
@@ -0,0 +1,51 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zaptest
+
+import (
+ "log"
+ "os"
+ "strconv"
+ "time"
+)
+
+var _timeoutScale = 1.0
+
+// Timeout scales the provided duration by $TEST_TIMEOUT_SCALE.
+func Timeout(base time.Duration) time.Duration {
+ return time.Duration(float64(base) * _timeoutScale)
+}
+
+// Sleep scales the sleep duration by $TEST_TIMEOUT_SCALE.
+func Sleep(base time.Duration) {
+ time.Sleep(Timeout(base))
+}
+
+func init() {
+ if v := os.Getenv("TEST_TIMEOUT_SCALE"); v != "" {
+ fv, err := strconv.ParseFloat(v, 64)
+ if err != nil {
+ panic(err)
+ }
+ _timeoutScale = fv
+ log.Printf("Scaling timeouts by %vx.\n", _timeoutScale)
+ }
+}
diff --git a/vendor/go.uber.org/zap/zaptest/writer.go b/vendor/go.uber.org/zap/zaptest/writer.go
new file mode 100644
index 0000000..0b8b254
--- /dev/null
+++ b/vendor/go.uber.org/zap/zaptest/writer.go
@@ -0,0 +1,96 @@
+// Copyright (c) 2016 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package zaptest
+
+import (
+ "bytes"
+ "errors"
+ "io/ioutil"
+ "strings"
+)
+
+// A Syncer is a spy for the Sync portion of zapcore.WriteSyncer.
+type Syncer struct {
+ err error
+ called bool
+}
+
+// SetError sets the error that the Sync method will return.
+func (s *Syncer) SetError(err error) {
+ s.err = err
+}
+
+// Sync records that it was called, then returns the user-supplied error (if
+// any).
+func (s *Syncer) Sync() error {
+ s.called = true
+ return s.err
+}
+
+// Called reports whether the Sync method was called.
+func (s *Syncer) Called() bool {
+ return s.called
+}
+
+// A Discarder sends all writes to ioutil.Discard.
+type Discarder struct{ Syncer }
+
+// Write implements io.Writer.
+func (d *Discarder) Write(b []byte) (int, error) {
+ return ioutil.Discard.Write(b)
+}
+
+// FailWriter is a WriteSyncer that always returns an error on writes.
+type FailWriter struct{ Syncer }
+
+// Write implements io.Writer.
+func (w FailWriter) Write(b []byte) (int, error) {
+ return len(b), errors.New("failed")
+}
+
+// ShortWriter is a WriteSyncer whose write method never fails, but
+// nevertheless fails to the last byte of the input.
+type ShortWriter struct{ Syncer }
+
+// Write implements io.Writer.
+func (w ShortWriter) Write(b []byte) (int, error) {
+ return len(b) - 1, nil
+}
+
+// Buffer is an implementation of zapcore.WriteSyncer that sends all writes to
+// a bytes.Buffer. It has convenience methods to split the accumulated buffer
+// on newlines.
+type Buffer struct {
+ bytes.Buffer
+ Syncer
+}
+
+// Lines returns the current buffer contents, split on newlines.
+func (b *Buffer) Lines() []string {
+ output := strings.Split(b.String(), "\n")
+ return output[:len(output)-1]
+}
+
+// Stripped returns the current buffer contents with the last trailing newline
+// stripped.
+func (b *Buffer) Stripped() string {
+ return strings.TrimRight(b.String(), "\n")
+}