adding in logging

This commit is contained in:
Steve Brunton 2017-08-30 22:05:59 -04:00
parent 403b464aec
commit 123cd935a3
107 changed files with 14413 additions and 2 deletions

6
glide.lock generated
View File

@ -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: []

View File

@ -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
View 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
View 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
View 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
View 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"** &mdash; 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
View 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 &mdash; 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
View 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 &mdash; *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
&mdash; especially the reason for the crash &mdash; 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
View 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
View 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
View 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 &mdash; 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 &mdash; 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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!",
}
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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: &current})
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
View 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)
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 &copy
}
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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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')
}
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
}

View 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)
}
})
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
}

View 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
View 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
View 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
}

View 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
View 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