mirror of
https://github.com/nshttpd/mikrotik-exporter.git
synced 2025-01-22 20:51:34 +01:00
adding in logging
This commit is contained in:
parent
403b464aec
commit
123cd935a3
6
glide.lock
generated
6
glide.lock
generated
@ -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: []
|
||||
|
@ -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
|
||||
|
17
vendor/go.uber.org/zap/.codecov.yml
generated
vendored
Normal file
17
vendor/go.uber.org/zap/.codecov.yml
generated
vendored
Normal file
@ -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
|
||||
|
98
vendor/go.uber.org/zap/.readme.tmpl
generated
vendored
Normal file
98
vendor/go.uber.org/zap/.readme.tmpl
generated
vendored
Normal file
@ -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.<sup
|
||||
id="anchor-versions">[1](#footnote-versions)</sup>
|
||||
|
||||
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`.
|
||||
|
||||
<hr>
|
||||
|
||||
Released under the [MIT License](LICENSE.txt).
|
||||
|
||||
<sup id="footnote-versions">1</sup> 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
|
22
vendor/go.uber.org/zap/.travis.yml
generated
vendored
Normal file
22
vendor/go.uber.org/zap/.travis.yml
generated
vendored
Normal file
@ -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)
|
245
vendor/go.uber.org/zap/CHANGELOG.md
generated
vendored
Normal file
245
vendor/go.uber.org/zap/CHANGELOG.md
generated
vendored
Normal file
@ -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
|
79
vendor/go.uber.org/zap/CONTRIBUTING.md
generated
vendored
Normal file
79
vendor/go.uber.org/zap/CONTRIBUTING.md
generated
vendored
Normal file
@ -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
|
140
vendor/go.uber.org/zap/FAQ.md
generated
vendored
Normal file
140
vendor/go.uber.org/zap/FAQ.md
generated
vendored
Normal file
@ -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
|
19
vendor/go.uber.org/zap/LICENSE.txt
generated
vendored
Normal file
19
vendor/go.uber.org/zap/LICENSE.txt
generated
vendored
Normal file
@ -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.
|
76
vendor/go.uber.org/zap/Makefile
generated
vendored
Normal file
76
vendor/go.uber.org/zap/Makefile
generated
vendored
Normal file
@ -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
|
123
vendor/go.uber.org/zap/README.md
generated
vendored
Normal file
123
vendor/go.uber.org/zap/README.md
generated
vendored
Normal file
@ -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.<sup
|
||||
id="anchor-versions">[1](#footnote-versions)</sup>
|
||||
|
||||
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`.
|
||||
|
||||
<hr>
|
||||
|
||||
Released under the [MIT License](LICENSE.txt).
|
||||
|
||||
<sup id="footnote-versions">1</sup> 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
|
320
vendor/go.uber.org/zap/array.go
generated
vendored
Normal file
320
vendor/go.uber.org/zap/array.go
generated
vendored
Normal file
@ -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
|
||||
}
|
107
vendor/go.uber.org/zap/array_test.go
generated
vendored
Normal file
107
vendor/go.uber.org/zap/array_test.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
||||
}
|
58
vendor/go.uber.org/zap/benchmarks/apex_test.go
generated
vendored
Normal file
58
vendor/go.uber.org/zap/benchmarks/apex_test.go
generated
vendored
Normal file
@ -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!",
|
||||
}
|
||||
}
|
23
vendor/go.uber.org/zap/benchmarks/doc.go
generated
vendored
Normal file
23
vendor/go.uber.org/zap/benchmarks/doc.go
generated
vendored
Normal file
@ -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
|
31
vendor/go.uber.org/zap/benchmarks/kit_test.go
generated
vendored
Normal file
31
vendor/go.uber.org/zap/benchmarks/kit_test.go
generated
vendored
Normal file
@ -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...)
|
||||
}
|
31
vendor/go.uber.org/zap/benchmarks/lion_test.go
generated
vendored
Normal file
31
vendor/go.uber.org/zap/benchmarks/lion_test.go
generated
vendored
Normal file
@ -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))
|
||||
}
|
33
vendor/go.uber.org/zap/benchmarks/log15_test.go
generated
vendored
Normal file
33
vendor/go.uber.org/zap/benchmarks/log15_test.go
generated
vendored
Normal file
@ -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
|
||||
}
|
58
vendor/go.uber.org/zap/benchmarks/logrus_test.go
generated
vendored
Normal file
58
vendor/go.uber.org/zap/benchmarks/logrus_test.go
generated
vendored
Normal file
@ -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!",
|
||||
}
|
||||
}
|
565
vendor/go.uber.org/zap/benchmarks/scenario_bench_test.go
generated
vendored
Normal file
565
vendor/go.uber.org/zap/benchmarks/scenario_bench_test.go
generated
vendored
Normal file
@ -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))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
117
vendor/go.uber.org/zap/benchmarks/zap_test.go
generated
vendored
Normal file
117
vendor/go.uber.org/zap/benchmarks/zap_test.go
generated
vendored
Normal file
@ -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!",
|
||||
}
|
||||
}
|
106
vendor/go.uber.org/zap/buffer/buffer.go
generated
vendored
Normal file
106
vendor/go.uber.org/zap/buffer/buffer.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
91
vendor/go.uber.org/zap/buffer/buffer_test.go
generated
vendored
Normal file
91
vendor/go.uber.org/zap/buffer/buffer_test.go
generated
vendored
Normal file
@ -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()
|
||||
}
|
||||
})
|
||||
}
|
49
vendor/go.uber.org/zap/buffer/pool.go
generated
vendored
Normal file
49
vendor/go.uber.org/zap/buffer/pool.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
52
vendor/go.uber.org/zap/buffer/pool_test.go
generated
vendored
Normal file
52
vendor/go.uber.org/zap/buffer/pool_test.go
generated
vendored
Normal file
@ -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()
|
||||
}
|
17
vendor/go.uber.org/zap/check_license.sh
generated
vendored
Executable file
17
vendor/go.uber.org/zap/check_license.sh
generated
vendored
Executable file
@ -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
|
57
vendor/go.uber.org/zap/common_test.go
generated
vendored
Normal file
57
vendor/go.uber.org/zap/common_test.go
generated
vendored
Normal file
@ -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()
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
243
vendor/go.uber.org/zap/config.go
generated
vendored
Normal file
243
vendor/go.uber.org/zap/config.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
108
vendor/go.uber.org/zap/config_test.go
generated
vendored
Normal file
108
vendor/go.uber.org/zap/config_test.go
generated
vendored
Normal file
@ -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.")
|
||||
})
|
||||
}
|
||||
}
|
113
vendor/go.uber.org/zap/doc.go
generated
vendored
Normal file
113
vendor/go.uber.org/zap/doc.go
generated
vendored
Normal file
@ -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"
|
75
vendor/go.uber.org/zap/encoder.go
generated
vendored
Normal file
75
vendor/go.uber.org/zap/encoder.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
88
vendor/go.uber.org/zap/encoder_test.go
generated
vendored
Normal file
88
vendor/go.uber.org/zap/encoder_test.go
generated
vendored
Normal file
@ -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
|
||||
}
|
80
vendor/go.uber.org/zap/error.go
generated
vendored
Normal file
80
vendor/go.uber.org/zap/error.go
generated
vendored
Normal file
@ -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
|
||||
}
|
99
vendor/go.uber.org/zap/error_test.go
generated
vendored
Normal file
99
vendor/go.uber.org/zap/error_test.go
generated
vendored
Normal file
@ -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.")
|
||||
}
|
327
vendor/go.uber.org/zap/example_test.go
generated
vendored
Normal file
327
vendor/go.uber.org/zap/example_test.go
generated
vendored
Normal file
@ -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"}
|
||||
}
|
306
vendor/go.uber.org/zap/field.go
generated
vendored
Normal file
306
vendor/go.uber.org/zap/field.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
||||
}
|
159
vendor/go.uber.org/zap/field_test.go
generated
vendored
Normal file
159
vendor/go.uber.org/zap/field_test.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
39
vendor/go.uber.org/zap/flag.go
generated
vendored
Normal file
39
vendor/go.uber.org/zap/flag.go
generated
vendored
Normal file
@ -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
|
||||
}
|
102
vendor/go.uber.org/zap/flag_test.go
generated
vendored
Normal file
102
vendor/go.uber.org/zap/flag_test.go
generated
vendored
Normal file
@ -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.")
|
||||
}
|
74
vendor/go.uber.org/zap/glide.lock
generated
vendored
Normal file
74
vendor/go.uber.org/zap/glide.lock
generated
vendored
Normal file
@ -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
|
34
vendor/go.uber.org/zap/glide.yaml
generated
vendored
Normal file
34
vendor/go.uber.org/zap/glide.yaml
generated
vendored
Normal file
@ -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
|
109
vendor/go.uber.org/zap/global.go
generated
vendored
Normal file
109
vendor/go.uber.org/zap/global.go
generated
vendored
Normal file
@ -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
|
||||
}
|
139
vendor/go.uber.org/zap/global_test.go
generated
vendored
Normal file
139
vendor/go.uber.org/zap/global_test.go
generated
vendored
Normal file
@ -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.")
|
||||
})
|
||||
}
|
81
vendor/go.uber.org/zap/http_handler.go
generated
vendored
Normal file
81
vendor/go.uber.org/zap/http_handler.go
generated
vendored
Normal file
@ -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.",
|
||||
})
|
||||
}
|
||||
}
|
131
vendor/go.uber.org/zap/http_handler_test.go
generated
vendored
Normal file
131
vendor/go.uber.org/zap/http_handler_test.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
31
vendor/go.uber.org/zap/internal/bufferpool/bufferpool.go
generated
vendored
Normal file
31
vendor/go.uber.org/zap/internal/bufferpool/bufferpool.go
generated
vendored
Normal file
@ -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
|
||||
)
|
44
vendor/go.uber.org/zap/internal/color/color.go
generated
vendored
Normal file
44
vendor/go.uber.org/zap/internal/color/color.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
36
vendor/go.uber.org/zap/internal/color/color_test.go
generated
vendored
Normal file
36
vendor/go.uber.org/zap/internal/color/color_test.go
generated
vendored
Normal file
@ -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.",
|
||||
)
|
||||
}
|
64
vendor/go.uber.org/zap/internal/exit/exit.go
generated
vendored
Normal file
64
vendor/go.uber.org/zap/internal/exit/exit.go
generated
vendored
Normal file
@ -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
|
||||
}
|
42
vendor/go.uber.org/zap/internal/exit/exit_test.go
generated
vendored
Normal file
42
vendor/go.uber.org/zap/internal/exit/exit_test.go
generated
vendored
Normal file
@ -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.")
|
||||
}
|
||||
}
|
203
vendor/go.uber.org/zap/internal/readme/readme.go
generated
vendored
Normal file
203
vendor/go.uber.org/zap/internal/readme/readme.go
generated
vendored
Normal file
@ -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()
|
||||
}
|
132
vendor/go.uber.org/zap/level.go
generated
vendored
Normal file
132
vendor/go.uber.org/zap/level.go
generated
vendored
Normal file
@ -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()
|
||||
}
|
117
vendor/go.uber.org/zap/level_test.go
generated
vendored
Normal file
117
vendor/go.uber.org/zap/level_test.go
generated
vendored
Normal file
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
305
vendor/go.uber.org/zap/logger.go
generated
vendored
Normal file
305
vendor/go.uber.org/zap/logger.go
generated
vendored
Normal file
@ -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
|
||||
}
|
222
vendor/go.uber.org/zap/logger_bench_test.go
generated
vendored
Normal file
222
vendor/go.uber.org/zap/logger_bench_test.go
generated
vendored
Normal file
@ -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...)
|
||||
}
|
||||
}
|
452
vendor/go.uber.org/zap/logger_test.go
generated
vendored
Normal file
452
vendor/go.uber.org/zap/logger_test.go
generated
vendored
Normal file
@ -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.",
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
109
vendor/go.uber.org/zap/options.go
generated
vendored
Normal file
109
vendor/go.uber.org/zap/options.go
generated
vendored
Normal file
@ -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
|
||||
})
|
||||
}
|
12
vendor/go.uber.org/zap/scripts/cover.sh
generated
vendored
Executable file
12
vendor/go.uber.org/zap/scripts/cover.sh
generated
vendored
Executable file
@ -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
|
98
vendor/go.uber.org/zap/stacktrace.go
generated
vendored
Normal file
98
vendor/go.uber.org/zap/stacktrace.go
generated
vendored
Normal file
@ -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)}
|
||||
}
|
41
vendor/go.uber.org/zap/stacktrace_test.go
generated
vendored
Normal file
41
vendor/go.uber.org/zap/stacktrace_test.go
generated
vendored
Normal file
@ -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],
|
||||
)
|
||||
}
|
304
vendor/go.uber.org/zap/sugar.go
generated
vendored
Normal file
304
vendor/go.uber.org/zap/sugar.go
generated
vendored
Normal file
@ -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
|
||||
}
|
374
vendor/go.uber.org/zap/sugar_test.go
generated
vendored
Normal file
374
vendor/go.uber.org/zap/sugar_test.go
generated
vendored
Normal file
@ -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}, "<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.")
|
||||
})
|
||||
}
|
27
vendor/go.uber.org/zap/time.go
generated
vendored
Normal file
27
vendor/go.uber.org/zap/time.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
42
vendor/go.uber.org/zap/time_test.go
generated
vendored
Normal file
42
vendor/go.uber.org/zap/time_test.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
||||
}
|
96
vendor/go.uber.org/zap/writer.go
generated
vendored
Normal file
96
vendor/go.uber.org/zap/writer.go
generated
vendored
Normal file
@ -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...))
|
||||
}
|
125
vendor/go.uber.org/zap/writer_test.go
generated
vendored
Normal file
125
vendor/go.uber.org/zap/writer_test.go
generated
vendored
Normal file
@ -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"))
|
||||
}
|
147
vendor/go.uber.org/zap/zapcore/console_encoder.go
generated
vendored
Normal file
147
vendor/go.uber.org/zap/zapcore/console_encoder.go
generated
vendored
Normal file
@ -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')
|
||||
}
|
||||
}
|
49
vendor/go.uber.org/zap/zapcore/console_encoder_bench_test.go
generated
vendored
Normal file
49
vendor/go.uber.org/zap/zapcore/console_encoder_bench_test.go
generated
vendored
Normal file
@ -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()
|
||||
}
|
||||
})
|
||||
}
|
113
vendor/go.uber.org/zap/zapcore/core.go
generated
vendored
Normal file
113
vendor/go.uber.org/zap/zapcore/core.go
generated
vendored
Normal file
@ -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,
|
||||
}
|
||||
}
|
163
vendor/go.uber.org/zap/zapcore/core_test.go
generated
vendored
Normal file
163
vendor/go.uber.org/zap/zapcore/core_test.go
generated
vendored
Normal file
@ -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.")
|
||||
}
|
24
vendor/go.uber.org/zap/zapcore/doc.go
generated
vendored
Normal file
24
vendor/go.uber.org/zap/zapcore/doc.go
generated
vendored
Normal file
@ -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"
|
348
vendor/go.uber.org/zap/zapcore/encoder.go
generated
vendored
Normal file
348
vendor/go.uber.org/zap/zapcore/encoder.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
636
vendor/go.uber.org/zap/zapcore/encoder_test.go
generated
vendored
Normal file
636
vendor/go.uber.org/zap/zapcore/encoder_test.go
generated
vendored
Normal file
@ -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...)
|
||||
}
|
257
vendor/go.uber.org/zap/zapcore/entry.go
generated
vendored
Normal file
257
vendor/go.uber.org/zap/zapcore/entry.go
generated
vendored
Normal file
@ -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
|
||||
}
|
107
vendor/go.uber.org/zap/zapcore/entry_test.go
generated
vendored
Normal file
107
vendor/go.uber.org/zap/zapcore/entry_test.go
generated
vendored
Normal file
@ -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()
|
||||
}
|
120
vendor/go.uber.org/zap/zapcore/error.go
generated
vendored
Normal file
120
vendor/go.uber.org/zap/zapcore/error.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
177
vendor/go.uber.org/zap/zapcore/error_test.go
generated
vendored
Normal file
177
vendor/go.uber.org/zap/zapcore/error_test.go
generated
vendored
Normal file
@ -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.")
|
||||
}
|
201
vendor/go.uber.org/zap/zapcore/field.go
generated
vendored
Normal file
201
vendor/go.uber.org/zap/zapcore/field.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
||||
}
|
231
vendor/go.uber.org/zap/zapcore/field_test.go
generated
vendored
Normal file
231
vendor/go.uber.org/zap/zapcore/field_test.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
||||
}
|
68
vendor/go.uber.org/zap/zapcore/hook.go
generated
vendored
Normal file
68
vendor/go.uber.org/zap/zapcore/hook.go
generated
vendored
Normal file
@ -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
|
||||
}
|
73
vendor/go.uber.org/zap/zapcore/hook_test.go
generated
vendored
Normal file
73
vendor/go.uber.org/zap/zapcore/hook_test.go
generated
vendored
Normal file
@ -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.")
|
||||
}
|
||||
}
|
||||
}
|
480
vendor/go.uber.org/zap/zapcore/json_encoder.go
generated
vendored
Normal file
480
vendor/go.uber.org/zap/zapcore/json_encoder.go
generated
vendored
Normal file
@ -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
|
||||
}
|
91
vendor/go.uber.org/zap/zapcore/json_encoder_bench_test.go
generated
vendored
Normal file
91
vendor/go.uber.org/zap/zapcore/json_encoder_bench_test.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
475
vendor/go.uber.org/zap/zapcore/json_encoder_impl_test.go
generated
vendored
Normal file
475
vendor/go.uber.org/zap/zapcore/json_encoder_impl_test.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
175
vendor/go.uber.org/zap/zapcore/level.go
generated
vendored
Normal file
175
vendor/go.uber.org/zap/zapcore/level.go
generated
vendored
Normal file
@ -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
|
||||
}
|
46
vendor/go.uber.org/zap/zapcore/level_strings.go
generated
vendored
Normal file
46
vendor/go.uber.org/zap/zapcore/level_strings.go
generated
vendored
Normal file
@ -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())
|
||||
}
|
||||
}
|
38
vendor/go.uber.org/zap/zapcore/level_strings_test.go
generated
vendored
Normal file
38
vendor/go.uber.org/zap/zapcore/level_strings_test.go
generated
vendored
Normal file
@ -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.")
|
||||
}
|
177
vendor/go.uber.org/zap/zapcore/level_test.go
generated
vendored
Normal file
177
vendor/go.uber.org/zap/zapcore/level_test.go
generated
vendored
Normal file
@ -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.",
|
||||
)
|
||||
}
|
53
vendor/go.uber.org/zap/zapcore/marshaler.go
generated
vendored
Normal file
53
vendor/go.uber.org/zap/zapcore/marshaler.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
179
vendor/go.uber.org/zap/zapcore/memory_encoder.go
generated
vendored
Normal file
179
vendor/go.uber.org/zap/zapcore/memory_encoder.go
generated
vendored
Normal file
@ -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) }
|
283
vendor/go.uber.org/zap/zapcore/memory_encoder_test.go
generated
vendored
Normal file
283
vendor/go.uber.org/zap/zapcore/memory_encoder_test.go
generated
vendored
Normal file
@ -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.",
|
||||
)
|
||||
}
|
134
vendor/go.uber.org/zap/zapcore/sampler.go
generated
vendored
Normal file
134
vendor/go.uber.org/zap/zapcore/sampler.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
230
vendor/go.uber.org/zap/zapcore/sampler_bench_test.go
generated
vendored
Normal file
230
vendor/go.uber.org/zap/zapcore/sampler_bench_test.go
generated
vendored
Normal file
@ -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
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
225
vendor/go.uber.org/zap/zapcore/sampler_test.go
generated
vendored
Normal file
225
vendor/go.uber.org/zap/zapcore/sampler_test.go
generated
vendored
Normal file
@ -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()
|
||||
}
|
81
vendor/go.uber.org/zap/zapcore/tee.go
generated
vendored
Normal file
81
vendor/go.uber.org/zap/zapcore/tee.go
generated
vendored
Normal file
@ -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
|
||||
}
|
62
vendor/go.uber.org/zap/zapcore/tee_logger_bench_test.go
generated
vendored
Normal file
62
vendor/go.uber.org/zap/zapcore/tee_logger_bench_test.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
153
vendor/go.uber.org/zap/zapcore/tee_test.go
generated
vendored
Normal file
153
vendor/go.uber.org/zap/zapcore/tee_test.go
generated
vendored
Normal file
@ -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.")
|
||||
}
|
123
vendor/go.uber.org/zap/zapcore/write_syncer.go
generated
vendored
Normal file
123
vendor/go.uber.org/zap/zapcore/write_syncer.go
generated
vendored
Normal file
@ -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
|
||||
}
|
56
vendor/go.uber.org/zap/zapcore/write_syncer_bench_test.go
generated
vendored
Normal file
56
vendor/go.uber.org/zap/zapcore/write_syncer_bench_test.go
generated
vendored
Normal file
@ -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"))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
137
vendor/go.uber.org/zap/zapcore/write_syncer_test.go
generated
vendored
Normal file
137
vendor/go.uber.org/zap/zapcore/write_syncer_test.go
generated
vendored
Normal file
@ -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.")
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user