build: upgrade dependency and build with go 1.21
This commit is contained in:
1 parent
adb38c3820
commit
993cfaa9d4
353 files changed
+47533
-16238
No files matched your search
-19
@@ -1,19 +0,0 @@
|
||||
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: 100 # 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
|
||||
|
||||
# Also update COVER_IGNORE_PKGS in the Makefile.
|
||||
ignore:
|
||||
- /internal/gen-atomicint/
|
||||
- /internal/gen-valuewrapper/
|
||||
Generated
-12
@@ -1,12 +0,0 @@
|
||||
/bin
|
||||
.DS_Store
|
||||
/vendor
|
||||
cover.html
|
||||
cover.out
|
||||
lint.log
|
||||
|
||||
# Binaries
|
||||
*.test
|
||||
|
||||
# Profiling output
|
||||
*.prof
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
sudo: false
|
||||
language: go
|
||||
go_import_path: go.uber.org/atomic
|
||||
|
||||
env:
|
||||
global:
|
||||
- GO111MODULE=on
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- go: oldstable
|
||||
- go: stable
|
||||
env: LINT=1
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- vendor
|
||||
|
||||
before_install:
|
||||
- go version
|
||||
|
||||
script:
|
||||
- test -z "$LINT" || make lint
|
||||
- make cover
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.7.0] - 2020-09-14
|
||||
### Added
|
||||
- Support JSON serialization and deserialization of primitive atomic types.
|
||||
- Support Text marshalling and unmarshalling for string atomics.
|
||||
|
||||
### Changed
|
||||
- Disallow incorrect comparison of atomic values in a non-atomic way.
|
||||
|
||||
### Removed
|
||||
- Remove dependency on `golang.org/x/{lint, tools}`.
|
||||
|
||||
## [1.6.0] - 2020-02-24
|
||||
### Changed
|
||||
- Drop library dependency on `golang.org/x/{lint, tools}`.
|
||||
|
||||
## [1.5.1] - 2019-11-19
|
||||
- Fix bug where `Bool.CAS` and `Bool.Toggle` do work correctly together
|
||||
causing `CAS` to fail even though the old value matches.
|
||||
|
||||
## [1.5.0] - 2019-10-29
|
||||
### Changed
|
||||
- With Go modules, only the `go.uber.org/atomic` import path is supported now.
|
||||
If you need to use the old import path, please add a `replace` directive to
|
||||
your `go.mod`.
|
||||
|
||||
## [1.4.0] - 2019-05-01
|
||||
### Added
|
||||
- Add `atomic.Error` type for atomic operations on `error` values.
|
||||
|
||||
## [1.3.2] - 2018-05-02
|
||||
### Added
|
||||
- Add `atomic.Duration` type for atomic operations on `time.Duration` values.
|
||||
|
||||
## [1.3.1] - 2017-11-14
|
||||
### Fixed
|
||||
- Revert optimization for `atomic.String.Store("")` which caused data races.
|
||||
|
||||
## [1.3.0] - 2017-11-13
|
||||
### Added
|
||||
- Add `atomic.Bool.CAS` for compare-and-swap semantics on bools.
|
||||
|
||||
### Changed
|
||||
- Optimize `atomic.String.Store("")` by avoiding an allocation.
|
||||
|
||||
## [1.2.0] - 2017-04-12
|
||||
### Added
|
||||
- Shadow `atomic.Value` from `sync/atomic`.
|
||||
|
||||
## [1.1.0] - 2017-03-10
|
||||
### Added
|
||||
- Add atomic `Float64` type.
|
||||
|
||||
### Changed
|
||||
- Support new `go.uber.org/atomic` import path.
|
||||
|
||||
## [1.0.0] - 2016-07-18
|
||||
|
||||
- Initial release.
|
||||
|
||||
[1.7.0]: https://github.com/uber-go/atomic/compare/v1.6.0...v1.7.0
|
||||
[1.6.0]: https://github.com/uber-go/atomic/compare/v1.5.1...v1.6.0
|
||||
[1.5.1]: https://github.com/uber-go/atomic/compare/v1.5.0...v1.5.1
|
||||
[1.5.0]: https://github.com/uber-go/atomic/compare/v1.4.0...v1.5.0
|
||||
[1.4.0]: https://github.com/uber-go/atomic/compare/v1.3.2...v1.4.0
|
||||
[1.3.2]: https://github.com/uber-go/atomic/compare/v1.3.1...v1.3.2
|
||||
[1.3.1]: https://github.com/uber-go/atomic/compare/v1.3.0...v1.3.1
|
||||
[1.3.0]: https://github.com/uber-go/atomic/compare/v1.2.0...v1.3.0
|
||||
[1.2.0]: https://github.com/uber-go/atomic/compare/v1.1.0...v1.2.0
|
||||
[1.1.0]: https://github.com/uber-go/atomic/compare/v1.0.0...v1.1.0
|
||||
[1.0.0]: https://github.com/uber-go/atomic/releases/tag/v1.0.0
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
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.
|
||||
-78
@@ -1,78 +0,0 @@
|
||||
# Directory to place `go install`ed binaries into.
|
||||
export GOBIN ?= $(shell pwd)/bin
|
||||
|
||||
GOLINT = $(GOBIN)/golint
|
||||
GEN_ATOMICINT = $(GOBIN)/gen-atomicint
|
||||
GEN_ATOMICWRAPPER = $(GOBIN)/gen-atomicwrapper
|
||||
STATICCHECK = $(GOBIN)/staticcheck
|
||||
|
||||
GO_FILES ?= $(shell find . '(' -path .git -o -path vendor ')' -prune -o -name '*.go' -print)
|
||||
|
||||
# Also update ignore section in .codecov.yml.
|
||||
COVER_IGNORE_PKGS = \
|
||||
go.uber.org/atomic/internal/gen-atomicint \
|
||||
go.uber.org/atomic/internal/gen-atomicwrapper
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
go build ./...
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
go test -race ./...
|
||||
|
||||
.PHONY: gofmt
|
||||
gofmt:
|
||||
$(eval FMT_LOG := $(shell mktemp -t gofmt.XXXXX))
|
||||
gofmt -e -s -l $(GO_FILES) > $(FMT_LOG) || true
|
||||
@[ ! -s "$(FMT_LOG)" ] || (echo "gofmt failed:" && cat $(FMT_LOG) && false)
|
||||
|
||||
$(GOLINT):
|
||||
cd tools && go install golang.org/x/lint/golint
|
||||
|
||||
$(STATICCHECK):
|
||||
cd tools && go install honnef.co/go/tools/cmd/staticcheck
|
||||
|
||||
$(GEN_ATOMICWRAPPER): $(wildcard ./internal/gen-atomicwrapper/*)
|
||||
go build -o $@ ./internal/gen-atomicwrapper
|
||||
|
||||
$(GEN_ATOMICINT): $(wildcard ./internal/gen-atomicint/*)
|
||||
go build -o $@ ./internal/gen-atomicint
|
||||
|
||||
.PHONY: golint
|
||||
golint: $(GOLINT)
|
||||
$(GOLINT) ./...
|
||||
|
||||
.PHONY: staticcheck
|
||||
staticcheck: $(STATICCHECK)
|
||||
$(STATICCHECK) ./...
|
||||
|
||||
.PHONY: lint
|
||||
lint: gofmt golint staticcheck generatenodirty
|
||||
|
||||
# comma separated list of packages to consider for code coverage.
|
||||
COVER_PKG = $(shell \
|
||||
go list -find ./... | \
|
||||
grep -v $(foreach pkg,$(COVER_IGNORE_PKGS),-e "^$(pkg)$$") | \
|
||||
paste -sd, -)
|
||||
|
||||
.PHONY: cover
|
||||
cover:
|
||||
go test -coverprofile=cover.out -coverpkg $(COVER_PKG) -v ./...
|
||||
go tool cover -html=cover.out -o cover.html
|
||||
|
||||
.PHONY: generate
|
||||
generate: $(GEN_ATOMICINT) $(GEN_ATOMICWRAPPER)
|
||||
go generate ./...
|
||||
|
||||
.PHONY: generatenodirty
|
||||
generatenodirty:
|
||||
@[ -z "$$(git status --porcelain)" ] || ( \
|
||||
echo "Working tree is dirty. Commit your changes first."; \
|
||||
exit 1 )
|
||||
@make generate
|
||||
@status=$$(git status --porcelain); \
|
||||
[ -z "$$status" ] || ( \
|
||||
echo "Working tree is dirty after `make generate`:"; \
|
||||
echo "$$status"; \
|
||||
echo "Please ensure that the generated code is up-to-date." )
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
# atomic [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] [![Go Report Card][reportcard-img]][reportcard]
|
||||
|
||||
Simple wrappers for primitive types to enforce atomic access.
|
||||
|
||||
## Installation
|
||||
|
||||
```shell
|
||||
$ go get -u go.uber.org/atomic@v1
|
||||
```
|
||||
|
||||
### Legacy Import Path
|
||||
|
||||
As of v1.5.0, the import path `go.uber.org/atomic` is the only supported way
|
||||
of using this package. If you are using Go modules, this package will fail to
|
||||
compile with the legacy import path path `github.com/uber-go/atomic`.
|
||||
|
||||
We recommend migrating your code to the new import path but if you're unable
|
||||
to do so, or if your dependencies are still using the old import path, you
|
||||
will have to add a `replace` directive to your `go.mod` file downgrading the
|
||||
legacy import path to an older version.
|
||||
|
||||
```
|
||||
replace github.com/uber-go/atomic => github.com/uber-go/atomic v1.4.0
|
||||
```
|
||||
|
||||
You can do so automatically by running the following command.
|
||||
|
||||
```shell
|
||||
$ go mod edit -replace github.com/uber-go/atomic=github.com/uber-go/atomic@v1.4.0
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
The standard library's `sync/atomic` is powerful, but it's easy to forget which
|
||||
variables must be accessed atomically. `go.uber.org/atomic` preserves all the
|
||||
functionality of the standard library, but wraps the primitive types to
|
||||
provide a safer, more convenient API.
|
||||
|
||||
```go
|
||||
var atom atomic.Uint32
|
||||
atom.Store(42)
|
||||
atom.Sub(2)
|
||||
atom.CAS(40, 11)
|
||||
```
|
||||
|
||||
See the [documentation][doc] for a complete API specification.
|
||||
|
||||
## Development Status
|
||||
|
||||
Stable.
|
||||
|
||||
---
|
||||
|
||||
Released under the [MIT License](LICENSE.txt).
|
||||
|
||||
[doc-img]: https://godoc.org/github.com/uber-go/atomic?status.svg
|
||||
[doc]: https://godoc.org/go.uber.org/atomic
|
||||
[ci-img]: https://travis-ci.com/uber-go/atomic.svg?branch=master
|
||||
[ci]: https://travis-ci.com/uber-go/atomic
|
||||
[cov-img]: https://codecov.io/gh/uber-go/atomic/branch/master/graph/badge.svg
|
||||
[cov]: https://codecov.io/gh/uber-go/atomic
|
||||
[reportcard-img]: https://goreportcard.com/badge/go.uber.org/atomic
|
||||
[reportcard]: https://goreportcard.com/report/go.uber.org/atomic
|
||||
-81
@@ -1,81 +0,0 @@
|
||||
// @generated Code generated by gen-atomicwrapper.
|
||||
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// Bool is an atomic type-safe wrapper for bool values.
|
||||
type Bool struct {
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
|
||||
v Uint32
|
||||
}
|
||||
|
||||
var _zeroBool bool
|
||||
|
||||
// NewBool creates a new Bool.
|
||||
func NewBool(v bool) *Bool {
|
||||
x := &Bool{}
|
||||
if v != _zeroBool {
|
||||
x.Store(v)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped bool.
|
||||
func (x *Bool) Load() bool {
|
||||
return truthy(x.v.Load())
|
||||
}
|
||||
|
||||
// Store atomically stores the passed bool.
|
||||
func (x *Bool) Store(v bool) {
|
||||
x.v.Store(boolToInt(v))
|
||||
}
|
||||
|
||||
// CAS is an atomic compare-and-swap for bool values.
|
||||
func (x *Bool) CAS(o, n bool) bool {
|
||||
return x.v.CAS(boolToInt(o), boolToInt(n))
|
||||
}
|
||||
|
||||
// Swap atomically stores the given bool and returns the old
|
||||
// value.
|
||||
func (x *Bool) Swap(o bool) bool {
|
||||
return truthy(x.v.Swap(boolToInt(o)))
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped bool into JSON.
|
||||
func (x *Bool) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(x.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes a bool from JSON.
|
||||
func (x *Bool) UnmarshalJSON(b []byte) error {
|
||||
var v bool
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
x.Store(v)
|
||||
return nil
|
||||
}
|
||||
-82
@@ -1,82 +0,0 @@
|
||||
// @generated Code generated by gen-atomicwrapper.
|
||||
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Duration is an atomic type-safe wrapper for time.Duration values.
|
||||
type Duration struct {
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
|
||||
v Int64
|
||||
}
|
||||
|
||||
var _zeroDuration time.Duration
|
||||
|
||||
// NewDuration creates a new Duration.
|
||||
func NewDuration(v time.Duration) *Duration {
|
||||
x := &Duration{}
|
||||
if v != _zeroDuration {
|
||||
x.Store(v)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped time.Duration.
|
||||
func (x *Duration) Load() time.Duration {
|
||||
return time.Duration(x.v.Load())
|
||||
}
|
||||
|
||||
// Store atomically stores the passed time.Duration.
|
||||
func (x *Duration) Store(v time.Duration) {
|
||||
x.v.Store(int64(v))
|
||||
}
|
||||
|
||||
// CAS is an atomic compare-and-swap for time.Duration values.
|
||||
func (x *Duration) CAS(o, n time.Duration) bool {
|
||||
return x.v.CAS(int64(o), int64(n))
|
||||
}
|
||||
|
||||
// Swap atomically stores the given time.Duration and returns the old
|
||||
// value.
|
||||
func (x *Duration) Swap(o time.Duration) time.Duration {
|
||||
return time.Duration(x.v.Swap(int64(o)))
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped time.Duration into JSON.
|
||||
func (x *Duration) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(x.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes a time.Duration from JSON.
|
||||
func (x *Duration) UnmarshalJSON(b []byte) error {
|
||||
var v time.Duration
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
x.Store(v)
|
||||
return nil
|
||||
}
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
import "time"
|
||||
|
||||
//go:generate bin/gen-atomicwrapper -name=Duration -type=time.Duration -wrapped=Int64 -pack=int64 -unpack=time.Duration -cas -swap -json -imports time -file=duration.go
|
||||
|
||||
// Add atomically adds to the wrapped time.Duration and returns the new value.
|
||||
func (d *Duration) Add(n time.Duration) time.Duration {
|
||||
return time.Duration(d.v.Add(int64(n)))
|
||||
}
|
||||
|
||||
// Sub atomically subtracts from the wrapped time.Duration and returns the new value.
|
||||
func (d *Duration) Sub(n time.Duration) time.Duration {
|
||||
return time.Duration(d.v.Sub(int64(n)))
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (d *Duration) String() string {
|
||||
return d.Load().String()
|
||||
}
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
// @generated Code generated by gen-atomicwrapper.
|
||||
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
// Error is an atomic type-safe wrapper for error values.
|
||||
type Error struct {
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
|
||||
v Value
|
||||
}
|
||||
|
||||
var _zeroError error
|
||||
|
||||
// NewError creates a new Error.
|
||||
func NewError(v error) *Error {
|
||||
x := &Error{}
|
||||
if v != _zeroError {
|
||||
x.Store(v)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped error.
|
||||
func (x *Error) Load() error {
|
||||
return unpackError(x.v.Load())
|
||||
}
|
||||
|
||||
// Store atomically stores the passed error.
|
||||
func (x *Error) Store(v error) {
|
||||
x.v.Store(packError(v))
|
||||
}
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
// atomic.Value panics on nil inputs, or if the underlying type changes.
|
||||
// Stabilize by always storing a custom struct that we control.
|
||||
|
||||
//go:generate bin/gen-atomicwrapper -name=Error -type=error -wrapped=Value -pack=packError -unpack=unpackError -file=error.go
|
||||
|
||||
type packedError struct{ Value error }
|
||||
|
||||
func packError(v error) interface{} {
|
||||
return packedError{v}
|
||||
}
|
||||
|
||||
func unpackError(v interface{}) error {
|
||||
if err, ok := v.(packedError); ok {
|
||||
return err.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
// @generated Code generated by gen-atomicwrapper.
|
||||
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math"
|
||||
)
|
||||
|
||||
// Float64 is an atomic type-safe wrapper for float64 values.
|
||||
type Float64 struct {
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
|
||||
v Uint64
|
||||
}
|
||||
|
||||
var _zeroFloat64 float64
|
||||
|
||||
// NewFloat64 creates a new Float64.
|
||||
func NewFloat64(v float64) *Float64 {
|
||||
x := &Float64{}
|
||||
if v != _zeroFloat64 {
|
||||
x.Store(v)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped float64.
|
||||
func (x *Float64) Load() float64 {
|
||||
return math.Float64frombits(x.v.Load())
|
||||
}
|
||||
|
||||
// Store atomically stores the passed float64.
|
||||
func (x *Float64) Store(v float64) {
|
||||
x.v.Store(math.Float64bits(v))
|
||||
}
|
||||
|
||||
// CAS is an atomic compare-and-swap for float64 values.
|
||||
func (x *Float64) CAS(o, n float64) bool {
|
||||
return x.v.CAS(math.Float64bits(o), math.Float64bits(n))
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped float64 into JSON.
|
||||
func (x *Float64) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(x.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes a float64 from JSON.
|
||||
func (x *Float64) UnmarshalJSON(b []byte) error {
|
||||
var v float64
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
x.Store(v)
|
||||
return nil
|
||||
}
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
import "strconv"
|
||||
|
||||
//go:generate bin/gen-atomicwrapper -name=Float64 -type=float64 -wrapped=Uint64 -pack=math.Float64bits -unpack=math.Float64frombits -cas -json -imports math -file=float64.go
|
||||
|
||||
// Add atomically adds to the wrapped float64 and returns the new value.
|
||||
func (f *Float64) Add(s float64) float64 {
|
||||
for {
|
||||
old := f.Load()
|
||||
new := old + s
|
||||
if f.CAS(old, new) {
|
||||
return new
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sub atomically subtracts from the wrapped float64 and returns the new value.
|
||||
func (f *Float64) Sub(s float64) float64 {
|
||||
return f.Add(-s)
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (f *Float64) String() string {
|
||||
// 'g' is the behavior for floats with %v.
|
||||
return strconv.FormatFloat(f.Load(), 'g', -1, 64)
|
||||
}
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
//go:generate bin/gen-atomicint -name=Int32 -wrapped=int32 -file=int32.go
|
||||
//go:generate bin/gen-atomicint -name=Int64 -wrapped=int64 -file=int64.go
|
||||
//go:generate bin/gen-atomicint -name=Uint32 -wrapped=uint32 -unsigned -file=uint32.go
|
||||
//go:generate bin/gen-atomicint -name=Uint64 -wrapped=uint64 -unsigned -file=uint64.go
|
||||
-102
@@ -1,102 +0,0 @@
|
||||
// @generated Code generated by gen-atomicint.
|
||||
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Int32 is an atomic wrapper around int32.
|
||||
type Int32 struct {
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
|
||||
v int32
|
||||
}
|
||||
|
||||
// NewInt32 creates a new Int32.
|
||||
func NewInt32(i int32) *Int32 {
|
||||
return &Int32{v: i}
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped value.
|
||||
func (i *Int32) Load() int32 {
|
||||
return atomic.LoadInt32(&i.v)
|
||||
}
|
||||
|
||||
// Add atomically adds to the wrapped int32 and returns the new value.
|
||||
func (i *Int32) Add(n int32) int32 {
|
||||
return atomic.AddInt32(&i.v, n)
|
||||
}
|
||||
|
||||
// Sub atomically subtracts from the wrapped int32 and returns the new value.
|
||||
func (i *Int32) Sub(n int32) int32 {
|
||||
return atomic.AddInt32(&i.v, -n)
|
||||
}
|
||||
|
||||
// Inc atomically increments the wrapped int32 and returns the new value.
|
||||
func (i *Int32) Inc() int32 {
|
||||
return i.Add(1)
|
||||
}
|
||||
|
||||
// Dec atomically decrements the wrapped int32 and returns the new value.
|
||||
func (i *Int32) Dec() int32 {
|
||||
return i.Sub(1)
|
||||
}
|
||||
|
||||
// CAS is an atomic compare-and-swap.
|
||||
func (i *Int32) CAS(old, new int32) bool {
|
||||
return atomic.CompareAndSwapInt32(&i.v, old, new)
|
||||
}
|
||||
|
||||
// Store atomically stores the passed value.
|
||||
func (i *Int32) Store(n int32) {
|
||||
atomic.StoreInt32(&i.v, n)
|
||||
}
|
||||
|
||||
// Swap atomically swaps the wrapped int32 and returns the old value.
|
||||
func (i *Int32) Swap(n int32) int32 {
|
||||
return atomic.SwapInt32(&i.v, n)
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped int32 into JSON.
|
||||
func (i *Int32) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(i.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes JSON into the wrapped int32.
|
||||
func (i *Int32) UnmarshalJSON(b []byte) error {
|
||||
var v int32
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (i *Int32) String() string {
|
||||
v := i.Load()
|
||||
return strconv.FormatInt(int64(v), 10)
|
||||
}
|
||||
-102
@@ -1,102 +0,0 @@
|
||||
// @generated Code generated by gen-atomicint.
|
||||
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Int64 is an atomic wrapper around int64.
|
||||
type Int64 struct {
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
|
||||
v int64
|
||||
}
|
||||
|
||||
// NewInt64 creates a new Int64.
|
||||
func NewInt64(i int64) *Int64 {
|
||||
return &Int64{v: i}
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped value.
|
||||
func (i *Int64) Load() int64 {
|
||||
return atomic.LoadInt64(&i.v)
|
||||
}
|
||||
|
||||
// Add atomically adds to the wrapped int64 and returns the new value.
|
||||
func (i *Int64) Add(n int64) int64 {
|
||||
return atomic.AddInt64(&i.v, n)
|
||||
}
|
||||
|
||||
// Sub atomically subtracts from the wrapped int64 and returns the new value.
|
||||
func (i *Int64) Sub(n int64) int64 {
|
||||
return atomic.AddInt64(&i.v, -n)
|
||||
}
|
||||
|
||||
// Inc atomically increments the wrapped int64 and returns the new value.
|
||||
func (i *Int64) Inc() int64 {
|
||||
return i.Add(1)
|
||||
}
|
||||
|
||||
// Dec atomically decrements the wrapped int64 and returns the new value.
|
||||
func (i *Int64) Dec() int64 {
|
||||
return i.Sub(1)
|
||||
}
|
||||
|
||||
// CAS is an atomic compare-and-swap.
|
||||
func (i *Int64) CAS(old, new int64) bool {
|
||||
return atomic.CompareAndSwapInt64(&i.v, old, new)
|
||||
}
|
||||
|
||||
// Store atomically stores the passed value.
|
||||
func (i *Int64) Store(n int64) {
|
||||
atomic.StoreInt64(&i.v, n)
|
||||
}
|
||||
|
||||
// Swap atomically swaps the wrapped int64 and returns the old value.
|
||||
func (i *Int64) Swap(n int64) int64 {
|
||||
return atomic.SwapInt64(&i.v, n)
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped int64 into JSON.
|
||||
func (i *Int64) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(i.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes JSON into the wrapped int64.
|
||||
func (i *Int64) UnmarshalJSON(b []byte) error {
|
||||
var v int64
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (i *Int64) String() string {
|
||||
v := i.Load()
|
||||
return strconv.FormatInt(int64(v), 10)
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
// nocmp is an uncomparable struct. Embed this inside another struct to make
|
||||
// it uncomparable.
|
||||
//
|
||||
// type Foo struct {
|
||||
// nocmp
|
||||
// // ...
|
||||
// }
|
||||
//
|
||||
// This DOES NOT:
|
||||
//
|
||||
// - Disallow shallow copies of structs
|
||||
// - Disallow comparison of pointers to uncomparable structs
|
||||
type nocmp [0]func()
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
// @generated Code generated by gen-atomicwrapper.
|
||||
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
// String is an atomic type-safe wrapper for string values.
|
||||
type String struct {
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
|
||||
v Value
|
||||
}
|
||||
|
||||
var _zeroString string
|
||||
|
||||
// NewString creates a new String.
|
||||
func NewString(v string) *String {
|
||||
x := &String{}
|
||||
if v != _zeroString {
|
||||
x.Store(v)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped string.
|
||||
func (x *String) Load() string {
|
||||
if v := x.v.Load(); v != nil {
|
||||
return v.(string)
|
||||
}
|
||||
return _zeroString
|
||||
}
|
||||
|
||||
// Store atomically stores the passed string.
|
||||
func (x *String) Store(v string) {
|
||||
x.v.Store(v)
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
//go:generate bin/gen-atomicwrapper -name=String -type=string -wrapped=Value -file=string.go
|
||||
|
||||
// String returns the wrapped value.
|
||||
func (s *String) String() string {
|
||||
return s.Load()
|
||||
}
|
||||
|
||||
// MarshalText encodes the wrapped string into a textual form.
|
||||
//
|
||||
// This makes it encodable as JSON, YAML, XML, and more.
|
||||
func (s *String) MarshalText() ([]byte, error) {
|
||||
return []byte(s.Load()), nil
|
||||
}
|
||||
|
||||
// UnmarshalText decodes text and replaces the wrapped string with it.
|
||||
//
|
||||
// This makes it decodable from JSON, YAML, XML, and more.
|
||||
func (s *String) UnmarshalText(b []byte) error {
|
||||
s.Store(string(b))
|
||||
return nil
|
||||
}
|
||||
-102
@@ -1,102 +0,0 @@
|
||||
// @generated Code generated by gen-atomicint.
|
||||
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Uint32 is an atomic wrapper around uint32.
|
||||
type Uint32 struct {
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
|
||||
v uint32
|
||||
}
|
||||
|
||||
// NewUint32 creates a new Uint32.
|
||||
func NewUint32(i uint32) *Uint32 {
|
||||
return &Uint32{v: i}
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped value.
|
||||
func (i *Uint32) Load() uint32 {
|
||||
return atomic.LoadUint32(&i.v)
|
||||
}
|
||||
|
||||
// Add atomically adds to the wrapped uint32 and returns the new value.
|
||||
func (i *Uint32) Add(n uint32) uint32 {
|
||||
return atomic.AddUint32(&i.v, n)
|
||||
}
|
||||
|
||||
// Sub atomically subtracts from the wrapped uint32 and returns the new value.
|
||||
func (i *Uint32) Sub(n uint32) uint32 {
|
||||
return atomic.AddUint32(&i.v, ^(n - 1))
|
||||
}
|
||||
|
||||
// Inc atomically increments the wrapped uint32 and returns the new value.
|
||||
func (i *Uint32) Inc() uint32 {
|
||||
return i.Add(1)
|
||||
}
|
||||
|
||||
// Dec atomically decrements the wrapped uint32 and returns the new value.
|
||||
func (i *Uint32) Dec() uint32 {
|
||||
return i.Sub(1)
|
||||
}
|
||||
|
||||
// CAS is an atomic compare-and-swap.
|
||||
func (i *Uint32) CAS(old, new uint32) bool {
|
||||
return atomic.CompareAndSwapUint32(&i.v, old, new)
|
||||
}
|
||||
|
||||
// Store atomically stores the passed value.
|
||||
func (i *Uint32) Store(n uint32) {
|
||||
atomic.StoreUint32(&i.v, n)
|
||||
}
|
||||
|
||||
// Swap atomically swaps the wrapped uint32 and returns the old value.
|
||||
func (i *Uint32) Swap(n uint32) uint32 {
|
||||
return atomic.SwapUint32(&i.v, n)
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped uint32 into JSON.
|
||||
func (i *Uint32) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(i.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes JSON into the wrapped uint32.
|
||||
func (i *Uint32) UnmarshalJSON(b []byte) error {
|
||||
var v uint32
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (i *Uint32) String() string {
|
||||
v := i.Load()
|
||||
return strconv.FormatUint(uint64(v), 10)
|
||||
}
|
||||
-102
@@ -1,102 +0,0 @@
|
||||
// @generated Code generated by gen-atomicint.
|
||||
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Uint64 is an atomic wrapper around uint64.
|
||||
type Uint64 struct {
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
|
||||
v uint64
|
||||
}
|
||||
|
||||
// NewUint64 creates a new Uint64.
|
||||
func NewUint64(i uint64) *Uint64 {
|
||||
return &Uint64{v: i}
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped value.
|
||||
func (i *Uint64) Load() uint64 {
|
||||
return atomic.LoadUint64(&i.v)
|
||||
}
|
||||
|
||||
// Add atomically adds to the wrapped uint64 and returns the new value.
|
||||
func (i *Uint64) Add(n uint64) uint64 {
|
||||
return atomic.AddUint64(&i.v, n)
|
||||
}
|
||||
|
||||
// Sub atomically subtracts from the wrapped uint64 and returns the new value.
|
||||
func (i *Uint64) Sub(n uint64) uint64 {
|
||||
return atomic.AddUint64(&i.v, ^(n - 1))
|
||||
}
|
||||
|
||||
// Inc atomically increments the wrapped uint64 and returns the new value.
|
||||
func (i *Uint64) Inc() uint64 {
|
||||
return i.Add(1)
|
||||
}
|
||||
|
||||
// Dec atomically decrements the wrapped uint64 and returns the new value.
|
||||
func (i *Uint64) Dec() uint64 {
|
||||
return i.Sub(1)
|
||||
}
|
||||
|
||||
// CAS is an atomic compare-and-swap.
|
||||
func (i *Uint64) CAS(old, new uint64) bool {
|
||||
return atomic.CompareAndSwapUint64(&i.v, old, new)
|
||||
}
|
||||
|
||||
// Store atomically stores the passed value.
|
||||
func (i *Uint64) Store(n uint64) {
|
||||
atomic.StoreUint64(&i.v, n)
|
||||
}
|
||||
|
||||
// Swap atomically swaps the wrapped uint64 and returns the old value.
|
||||
func (i *Uint64) Swap(n uint64) uint64 {
|
||||
return atomic.SwapUint64(&i.v, n)
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped uint64 into JSON.
|
||||
func (i *Uint64) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(i.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes JSON into the wrapped uint64.
|
||||
func (i *Uint64) UnmarshalJSON(b []byte) error {
|
||||
var v uint64
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (i *Uint64) String() string {
|
||||
v := i.Load()
|
||||
return strconv.FormatUint(uint64(v), 10)
|
||||
}
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
import "sync/atomic"
|
||||
|
||||
// Value shadows the type of the same name from sync/atomic
|
||||
// https://godoc.org/sync/atomic#Value
|
||||
type Value struct {
|
||||
atomic.Value
|
||||
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
}
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
sudo: false
|
||||
language: go
|
||||
go_import_path: go.uber.org/multierr
|
||||
|
||||
env:
|
||||
global:
|
||||
- GO111MODULE=on
|
||||
|
||||
go:
|
||||
- oldstable
|
||||
- stable
|
||||
|
||||
before_install:
|
||||
- go version
|
||||
|
||||
script:
|
||||
- |
|
||||
set -e
|
||||
make lint
|
||||
make cover
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
+28
@@ -1,6 +1,34 @@
|
||||
Releases
|
||||
========
|
||||
|
||||
v1.10.0 (2023-03-08)
|
||||
====================
|
||||
|
||||
- Comply with Go 1.20's multiple-error interface.
|
||||
- Drop Go 1.18 support.
|
||||
Per the support policy, only Go 1.19 and 1.20 are supported now.
|
||||
- Drop all non-test external dependencies.
|
||||
|
||||
v1.9.0 (2022-12-12)
|
||||
===================
|
||||
|
||||
- Add `AppendFunc` that allow passsing functions to similar to
|
||||
`AppendInvoke`.
|
||||
|
||||
- Bump up yaml.v3 dependency to 3.0.1.
|
||||
|
||||
v1.8.0 (2022-02-28)
|
||||
===================
|
||||
|
||||
- `Combine`: perform zero allocations when there are no errors.
|
||||
|
||||
|
||||
v1.7.0 (2021-05-06)
|
||||
===================
|
||||
|
||||
- Add `AppendInvoke` to append into errors from `defer` blocks.
|
||||
|
||||
|
||||
v1.6.0 (2020-09-14)
|
||||
===================
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2017 Uber Technologies, Inc.
|
||||
Copyright (c) 2017-2021 Uber Technologies, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+1
-5
@@ -34,9 +34,5 @@ lint: gofmt golint staticcheck
|
||||
|
||||
.PHONY: cover
|
||||
cover:
|
||||
go test -coverprofile=cover.out -coverpkg=./... -v ./...
|
||||
go test -race -coverprofile=cover.out -coverpkg=./... -v ./...
|
||||
go tool cover -html=cover.out -o cover.html
|
||||
|
||||
update-license:
|
||||
@cd tools && go install go.uber.org/tools/update-license
|
||||
@$(GOBIN)/update-license $(GO_FILES)
|
||||
+25
-5
@@ -2,9 +2,29 @@
|
||||
|
||||
`multierr` allows combining one or more Go `error`s together.
|
||||
|
||||
## Features
|
||||
|
||||
- **Idiomatic**:
|
||||
multierr follows best practices in Go, and keeps your code idiomatic.
|
||||
- It keeps the underlying error type hidden,
|
||||
allowing you to deal in `error` values exclusively.
|
||||
- It provides APIs to safely append into an error from a `defer` statement.
|
||||
- **Performant**:
|
||||
multierr is optimized for performance:
|
||||
- It avoids allocations where possible.
|
||||
- It utilizes slice resizing semantics to optimize common cases
|
||||
like appending into the same error object from a loop.
|
||||
- **Interoperable**:
|
||||
multierr interoperates with the Go standard library's error APIs seamlessly:
|
||||
- The `errors.Is` and `errors.As` functions *just work*.
|
||||
- **Lightweight**:
|
||||
multierr comes with virtually no dependencies.
|
||||
|
||||
## Installation
|
||||
|
||||
go get -u go.uber.org/multierr
|
||||
```bash
|
||||
go get -u go.uber.org/multierr@latest
|
||||
```
|
||||
|
||||
## Status
|
||||
|
||||
@@ -15,9 +35,9 @@ Stable: No breaking changes will be made before 2.0.
|
||||
Released under the [MIT License].
|
||||
|
||||
[MIT License]: LICENSE.txt
|
||||
[doc-img]: https://godoc.org/go.uber.org/multierr?status.svg
|
||||
[doc]: https://godoc.org/go.uber.org/multierr
|
||||
[ci-img]: https://travis-ci.com/uber-go/multierr.svg?branch=master
|
||||
[doc-img]: https://pkg.go.dev/badge/go.uber.org/multierr
|
||||
[doc]: https://pkg.go.dev/go.uber.org/multierr
|
||||
[ci-img]: https://github.com/uber-go/multierr/actions/workflows/go.yml/badge.svg
|
||||
[cov-img]: https://codecov.io/gh/uber-go/multierr/branch/master/graph/badge.svg
|
||||
[ci]: https://travis-ci.com/uber-go/multierr
|
||||
[ci]: https://github.com/uber-go/multierr/actions/workflows/go.yml
|
||||
[cov]: https://codecov.io/gh/uber-go/multierr
|
||||
+292
-89
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2019 Uber Technologies, Inc.
|
||||
// Copyright (c) 2017-2023 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -20,54 +20,109 @@
|
||||
|
||||
// Package multierr allows combining one or more errors together.
|
||||
//
|
||||
// Overview
|
||||
// # Overview
|
||||
//
|
||||
// Errors can be combined with the use of the Combine function.
|
||||
//
|
||||
// multierr.Combine(
|
||||
// reader.Close(),
|
||||
// writer.Close(),
|
||||
// conn.Close(),
|
||||
// )
|
||||
// multierr.Combine(
|
||||
// reader.Close(),
|
||||
// writer.Close(),
|
||||
// conn.Close(),
|
||||
// )
|
||||
//
|
||||
// If only two errors are being combined, the Append function may be used
|
||||
// instead.
|
||||
//
|
||||
// err = multierr.Append(reader.Close(), writer.Close())
|
||||
//
|
||||
// This makes it possible to record resource cleanup failures from deferred
|
||||
// blocks with the help of named return values.
|
||||
//
|
||||
// func sendRequest(req Request) (err error) {
|
||||
// conn, err := openConnection()
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// defer func() {
|
||||
// err = multierr.Append(err, conn.Close())
|
||||
// }()
|
||||
// // ...
|
||||
// }
|
||||
// err = multierr.Append(reader.Close(), writer.Close())
|
||||
//
|
||||
// The underlying list of errors for a returned error object may be retrieved
|
||||
// with the Errors function.
|
||||
//
|
||||
// errors := multierr.Errors(err)
|
||||
// if len(errors) > 0 {
|
||||
// fmt.Println("The following errors occurred:", errors)
|
||||
// }
|
||||
// errors := multierr.Errors(err)
|
||||
// if len(errors) > 0 {
|
||||
// fmt.Println("The following errors occurred:", errors)
|
||||
// }
|
||||
//
|
||||
// Advanced Usage
|
||||
// # Appending from a loop
|
||||
//
|
||||
// You sometimes need to append into an error from a loop.
|
||||
//
|
||||
// var err error
|
||||
// for _, item := range items {
|
||||
// err = multierr.Append(err, process(item))
|
||||
// }
|
||||
//
|
||||
// Cases like this may require knowledge of whether an individual instance
|
||||
// failed. This usually requires introduction of a new variable.
|
||||
//
|
||||
// var err error
|
||||
// for _, item := range items {
|
||||
// if perr := process(item); perr != nil {
|
||||
// log.Warn("skipping item", item)
|
||||
// err = multierr.Append(err, perr)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// multierr includes AppendInto to simplify cases like this.
|
||||
//
|
||||
// var err error
|
||||
// for _, item := range items {
|
||||
// if multierr.AppendInto(&err, process(item)) {
|
||||
// log.Warn("skipping item", item)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// This will append the error into the err variable, and return true if that
|
||||
// individual error was non-nil.
|
||||
//
|
||||
// See [AppendInto] for more information.
|
||||
//
|
||||
// # Deferred Functions
|
||||
//
|
||||
// Go makes it possible to modify the return value of a function in a defer
|
||||
// block if the function was using named returns. This makes it possible to
|
||||
// record resource cleanup failures from deferred blocks.
|
||||
//
|
||||
// func sendRequest(req Request) (err error) {
|
||||
// conn, err := openConnection()
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// defer func() {
|
||||
// err = multierr.Append(err, conn.Close())
|
||||
// }()
|
||||
// // ...
|
||||
// }
|
||||
//
|
||||
// multierr provides the Invoker type and AppendInvoke function to make cases
|
||||
// like the above simpler and obviate the need for a closure. The following is
|
||||
// roughly equivalent to the example above.
|
||||
//
|
||||
// func sendRequest(req Request) (err error) {
|
||||
// conn, err := openConnection()
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// defer multierr.AppendInvoke(&err, multierr.Close(conn))
|
||||
// // ...
|
||||
// }
|
||||
//
|
||||
// See [AppendInvoke] and [Invoker] for more information.
|
||||
//
|
||||
// NOTE: If you're modifying an error from inside a defer, you MUST use a named
|
||||
// return value for that function.
|
||||
//
|
||||
// # Advanced Usage
|
||||
//
|
||||
// Errors returned by Combine and Append MAY implement the following
|
||||
// interface.
|
||||
//
|
||||
// type errorGroup interface {
|
||||
// // Returns a slice containing the underlying list of errors.
|
||||
// //
|
||||
// // This slice MUST NOT be modified by the caller.
|
||||
// Errors() []error
|
||||
// }
|
||||
// type errorGroup interface {
|
||||
// // Returns a slice containing the underlying list of errors.
|
||||
// //
|
||||
// // This slice MUST NOT be modified by the caller.
|
||||
// Errors() []error
|
||||
// }
|
||||
//
|
||||
// Note that if you need access to list of errors behind a multierr error, you
|
||||
// should prefer using the Errors function. That said, if you need cheap
|
||||
@@ -76,13 +131,13 @@
|
||||
// because errors returned by Combine and Append are not guaranteed to
|
||||
// implement this interface.
|
||||
//
|
||||
// var errors []error
|
||||
// group, ok := err.(errorGroup)
|
||||
// if ok {
|
||||
// errors = group.Errors()
|
||||
// } else {
|
||||
// errors = []error{err}
|
||||
// }
|
||||
// var errors []error
|
||||
// group, ok := err.(errorGroup)
|
||||
// if ok {
|
||||
// errors = group.Errors()
|
||||
// } else {
|
||||
// errors = []error{err}
|
||||
// }
|
||||
package multierr // import "go.uber.org/multierr"
|
||||
|
||||
import (
|
||||
@@ -91,8 +146,7 @@ import (
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"go.uber.org/atomic"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -132,8 +186,8 @@ type errorGroup interface {
|
||||
// Errors returns a slice containing zero or more errors that the supplied
|
||||
// error is composed of. If the error is nil, a nil slice is returned.
|
||||
//
|
||||
// err := multierr.Append(r.Close(), w.Close())
|
||||
// errors := multierr.Errors(err)
|
||||
// err := multierr.Append(r.Close(), w.Close())
|
||||
// errors := multierr.Errors(err)
|
||||
//
|
||||
// If the error is not composed of other errors, the returned slice contains
|
||||
// just the error that was passed in.
|
||||
@@ -156,10 +210,7 @@ func Errors(err error) []error {
|
||||
return []error{err}
|
||||
}
|
||||
|
||||
errors := eg.Errors()
|
||||
result := make([]error, len(errors))
|
||||
copy(result, errors)
|
||||
return result
|
||||
return append(([]error)(nil), eg.Errors()...)
|
||||
}
|
||||
|
||||
// multiError is an error that holds one or more errors.
|
||||
@@ -292,6 +343,14 @@ func inspect(errors []error) (res inspectResult) {
|
||||
|
||||
// fromSlice converts the given list of errors into a single error.
|
||||
func fromSlice(errors []error) error {
|
||||
// Don't pay to inspect small slices.
|
||||
switch len(errors) {
|
||||
case 0:
|
||||
return nil
|
||||
case 1:
|
||||
return errors[0]
|
||||
}
|
||||
|
||||
res := inspect(errors)
|
||||
switch res.Count {
|
||||
case 0:
|
||||
@@ -301,8 +360,12 @@ func fromSlice(errors []error) error {
|
||||
return errors[res.FirstErrorIdx]
|
||||
case len(errors):
|
||||
if !res.ContainsMultiError {
|
||||
// already flat
|
||||
return &multiError{errors: errors}
|
||||
// Error list is flat. Make a copy of it
|
||||
// Otherwise "errors" escapes to the heap
|
||||
// unconditionally for all other cases.
|
||||
// This lets us optimize for the "no errors" case.
|
||||
out := append(([]error)(nil), errors...)
|
||||
return &multiError{errors: out}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,32 +390,32 @@ func fromSlice(errors []error) error {
|
||||
// If zero arguments were passed or if all items are nil, a nil error is
|
||||
// returned.
|
||||
//
|
||||
// Combine(nil, nil) // == nil
|
||||
// Combine(nil, nil) // == nil
|
||||
//
|
||||
// If only a single error was passed, it is returned as-is.
|
||||
//
|
||||
// Combine(err) // == err
|
||||
// Combine(err) // == err
|
||||
//
|
||||
// Combine skips over nil arguments so this function may be used to combine
|
||||
// together errors from operations that fail independently of each other.
|
||||
//
|
||||
// multierr.Combine(
|
||||
// reader.Close(),
|
||||
// writer.Close(),
|
||||
// pipe.Close(),
|
||||
// )
|
||||
// multierr.Combine(
|
||||
// reader.Close(),
|
||||
// writer.Close(),
|
||||
// pipe.Close(),
|
||||
// )
|
||||
//
|
||||
// If any of the passed errors is a multierr error, it will be flattened along
|
||||
// with the other errors.
|
||||
//
|
||||
// multierr.Combine(multierr.Combine(err1, err2), err3)
|
||||
// // is the same as
|
||||
// multierr.Combine(err1, err2, err3)
|
||||
// multierr.Combine(multierr.Combine(err1, err2), err3)
|
||||
// // is the same as
|
||||
// multierr.Combine(err1, err2, err3)
|
||||
//
|
||||
// The returned error formats into a readable multi-line error message if
|
||||
// formatted with %+v.
|
||||
//
|
||||
// fmt.Sprintf("%+v", multierr.Combine(err1, err2))
|
||||
// fmt.Sprintf("%+v", multierr.Combine(err1, err2))
|
||||
func Combine(errors ...error) error {
|
||||
return fromSlice(errors)
|
||||
}
|
||||
@@ -362,16 +425,19 @@ func Combine(errors ...error) error {
|
||||
// This function is a specialization of Combine for the common case where
|
||||
// there are only two errors.
|
||||
//
|
||||
// err = multierr.Append(reader.Close(), writer.Close())
|
||||
// err = multierr.Append(reader.Close(), writer.Close())
|
||||
//
|
||||
// The following pattern may also be used to record failure of deferred
|
||||
// operations without losing information about the original error.
|
||||
//
|
||||
// func doSomething(..) (err error) {
|
||||
// f := acquireResource()
|
||||
// defer func() {
|
||||
// err = multierr.Append(err, f.Close())
|
||||
// }()
|
||||
// func doSomething(..) (err error) {
|
||||
// f := acquireResource()
|
||||
// defer func() {
|
||||
// err = multierr.Append(err, f.Close())
|
||||
// }()
|
||||
//
|
||||
// Note that the variable MUST be a named return to append an error to it from
|
||||
// the defer statement. See also [AppendInvoke].
|
||||
func Append(left error, right error) error {
|
||||
switch {
|
||||
case left == nil:
|
||||
@@ -401,37 +467,37 @@ func Append(left error, right error) error {
|
||||
// AppendInto appends an error into the destination of an error pointer and
|
||||
// returns whether the error being appended was non-nil.
|
||||
//
|
||||
// var err error
|
||||
// multierr.AppendInto(&err, r.Close())
|
||||
// multierr.AppendInto(&err, w.Close())
|
||||
// var err error
|
||||
// multierr.AppendInto(&err, r.Close())
|
||||
// multierr.AppendInto(&err, w.Close())
|
||||
//
|
||||
// The above is equivalent to,
|
||||
//
|
||||
// err := multierr.Append(r.Close(), w.Close())
|
||||
// err := multierr.Append(r.Close(), w.Close())
|
||||
//
|
||||
// As AppendInto reports whether the provided error was non-nil, it may be
|
||||
// used to build a multierr error in a loop more ergonomically. For example:
|
||||
//
|
||||
// var err error
|
||||
// for line := range lines {
|
||||
// var item Item
|
||||
// if multierr.AppendInto(&err, parse(line, &item)) {
|
||||
// continue
|
||||
// }
|
||||
// items = append(items, item)
|
||||
// }
|
||||
// var err error
|
||||
// for line := range lines {
|
||||
// var item Item
|
||||
// if multierr.AppendInto(&err, parse(line, &item)) {
|
||||
// continue
|
||||
// }
|
||||
// items = append(items, item)
|
||||
// }
|
||||
//
|
||||
// Compare this with a verison that relies solely on Append:
|
||||
// Compare this with a version that relies solely on Append:
|
||||
//
|
||||
// var err error
|
||||
// for line := range lines {
|
||||
// var item Item
|
||||
// if parseErr := parse(line, &item); parseErr != nil {
|
||||
// err = multierr.Append(err, parseErr)
|
||||
// continue
|
||||
// }
|
||||
// items = append(items, item)
|
||||
// }
|
||||
// var err error
|
||||
// for line := range lines {
|
||||
// var item Item
|
||||
// if parseErr := parse(line, &item); parseErr != nil {
|
||||
// err = multierr.Append(err, parseErr)
|
||||
// continue
|
||||
// }
|
||||
// items = append(items, item)
|
||||
// }
|
||||
func AppendInto(into *error, err error) (errored bool) {
|
||||
if into == nil {
|
||||
// We panic if 'into' is nil. This is not documented above
|
||||
@@ -447,3 +513,140 @@ func AppendInto(into *error, err error) (errored bool) {
|
||||
*into = Append(*into, err)
|
||||
return true
|
||||
}
|
||||
|
||||
// Invoker is an operation that may fail with an error. Use it with
|
||||
// AppendInvoke to append the result of calling the function into an error.
|
||||
// This allows you to conveniently defer capture of failing operations.
|
||||
//
|
||||
// See also, [Close] and [Invoke].
|
||||
type Invoker interface {
|
||||
Invoke() error
|
||||
}
|
||||
|
||||
// Invoke wraps a function which may fail with an error to match the Invoker
|
||||
// interface. Use it to supply functions matching this signature to
|
||||
// AppendInvoke.
|
||||
//
|
||||
// For example,
|
||||
//
|
||||
// func processReader(r io.Reader) (err error) {
|
||||
// scanner := bufio.NewScanner(r)
|
||||
// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err))
|
||||
// for scanner.Scan() {
|
||||
// // ...
|
||||
// }
|
||||
// // ...
|
||||
// }
|
||||
//
|
||||
// In this example, the following line will construct the Invoker right away,
|
||||
// but defer the invocation of scanner.Err() until the function returns.
|
||||
//
|
||||
// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err))
|
||||
//
|
||||
// Note that the error you're appending to from the defer statement MUST be a
|
||||
// named return.
|
||||
type Invoke func() error
|
||||
|
||||
// Invoke calls the supplied function and returns its result.
|
||||
func (i Invoke) Invoke() error { return i() }
|
||||
|
||||
// Close builds an Invoker that closes the provided io.Closer. Use it with
|
||||
// AppendInvoke to close io.Closers and append their results into an error.
|
||||
//
|
||||
// For example,
|
||||
//
|
||||
// func processFile(path string) (err error) {
|
||||
// f, err := os.Open(path)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// defer multierr.AppendInvoke(&err, multierr.Close(f))
|
||||
// return processReader(f)
|
||||
// }
|
||||
//
|
||||
// In this example, multierr.Close will construct the Invoker right away, but
|
||||
// defer the invocation of f.Close until the function returns.
|
||||
//
|
||||
// defer multierr.AppendInvoke(&err, multierr.Close(f))
|
||||
//
|
||||
// Note that the error you're appending to from the defer statement MUST be a
|
||||
// named return.
|
||||
func Close(closer io.Closer) Invoker {
|
||||
return Invoke(closer.Close)
|
||||
}
|
||||
|
||||
// AppendInvoke appends the result of calling the given Invoker into the
|
||||
// provided error pointer. Use it with named returns to safely defer
|
||||
// invocation of fallible operations until a function returns, and capture the
|
||||
// resulting errors.
|
||||
//
|
||||
// func doSomething(...) (err error) {
|
||||
// // ...
|
||||
// f, err := openFile(..)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// // multierr will call f.Close() when this function returns and
|
||||
// // if the operation fails, its append its error into the
|
||||
// // returned error.
|
||||
// defer multierr.AppendInvoke(&err, multierr.Close(f))
|
||||
//
|
||||
// scanner := bufio.NewScanner(f)
|
||||
// // Similarly, this scheduled scanner.Err to be called and
|
||||
// // inspected when the function returns and append its error
|
||||
// // into the returned error.
|
||||
// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err))
|
||||
//
|
||||
// // ...
|
||||
// }
|
||||
//
|
||||
// NOTE: If used with a defer, the error variable MUST be a named return.
|
||||
//
|
||||
// Without defer, AppendInvoke behaves exactly like AppendInto.
|
||||
//
|
||||
// err := // ...
|
||||
// multierr.AppendInvoke(&err, mutltierr.Invoke(foo))
|
||||
//
|
||||
// // ...is roughly equivalent to...
|
||||
//
|
||||
// err := // ...
|
||||
// multierr.AppendInto(&err, foo())
|
||||
//
|
||||
// The advantage of the indirection introduced by Invoker is to make it easy
|
||||
// to defer the invocation of a function. Without this indirection, the
|
||||
// invoked function will be evaluated at the time of the defer block rather
|
||||
// than when the function returns.
|
||||
//
|
||||
// // BAD: This is likely not what the caller intended. This will evaluate
|
||||
// // foo() right away and append its result into the error when the
|
||||
// // function returns.
|
||||
// defer multierr.AppendInto(&err, foo())
|
||||
//
|
||||
// // GOOD: This will defer invocation of foo unutil the function returns.
|
||||
// defer multierr.AppendInvoke(&err, multierr.Invoke(foo))
|
||||
//
|
||||
// multierr provides a few Invoker implementations out of the box for
|
||||
// convenience. See [Invoker] for more information.
|
||||
func AppendInvoke(into *error, invoker Invoker) {
|
||||
AppendInto(into, invoker.Invoke())
|
||||
}
|
||||
|
||||
// AppendFunc is a shorthand for [AppendInvoke].
|
||||
// It allows using function or method value directly
|
||||
// without having to wrap it into an [Invoker] interface.
|
||||
//
|
||||
// func doSomething(...) (err error) {
|
||||
// w, err := startWorker(...)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// // multierr will call w.Stop() when this function returns and
|
||||
// // if the operation fails, it appends its error into the
|
||||
// // returned error.
|
||||
// defer multierr.AppendFunc(&err, w.Stop)
|
||||
// }
|
||||
func AppendFunc(into *error, fn func() error) {
|
||||
AppendInvoke(into, Invoke(fn))
|
||||
}
|
||||
Generated
Vendored
+10
-4
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
// Copyright (c) 2017-2023 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -18,6 +18,12 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
// Package atomic provides simple wrappers around numerics to enforce atomic
|
||||
// access.
|
||||
package atomic
|
||||
//go:build go1.20
|
||||
// +build go1.20
|
||||
|
||||
package multierr
|
||||
|
||||
// Unwrap returns a list of errors wrapped by this multierr.
|
||||
func (merr *multiError) Unwrap() []error {
|
||||
return merr.Errors()
|
||||
}
|
||||
Generated
Vendored
+9
-2
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2019 Uber Technologies, Inc.
|
||||
// Copyright (c) 2017-2023 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -18,12 +18,19 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
// +build go1.13
|
||||
//go:build !go1.20
|
||||
// +build !go1.20
|
||||
|
||||
package multierr
|
||||
|
||||
import "errors"
|
||||
|
||||
// Versions of Go before 1.20 did not support the Unwrap() []error method.
|
||||
// This provides a similar behavior by implementing the Is(..) and As(..)
|
||||
// methods.
|
||||
// See the errors.Join proposal for details:
|
||||
// https://github.com/golang/go/issues/53435
|
||||
|
||||
// As attempts to find the first error in the error list that matches the type
|
||||
// of the value that target points to.
|
||||
//
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
package: go.uber.org/multierr
|
||||
import:
|
||||
- package: go.uber.org/atomic
|
||||
version: ^1
|
||||
testImport:
|
||||
- package: github.com/stretchr/testify
|
||||
subpackages:
|
||||
- assert
|
||||
+34
@@ -3,6 +3,40 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## 1.25.0 (1 Aug 2023)
|
||||
|
||||
This release contains several improvements including performance, API additions,
|
||||
and two new experimental packages whose APIs are unstable and may change in the
|
||||
future.
|
||||
|
||||
Enhancements:
|
||||
* [#1246][]: Add `zap/exp/zapslog` package for integration with slog.
|
||||
* [#1273][]: Add `Name` to `Logger` which returns the Logger's name if one is set.
|
||||
* [#1281][]: Add `zap/exp/expfield` package which contains helper methods
|
||||
`Str` and `Strs` for constructing String-like zap.Fields.
|
||||
* [#1310][]: Reduce stack size on `Any`.
|
||||
|
||||
Thanks to @knight42, @dzakaammar, @bcspragu, and @rexywork for their contributions
|
||||
to this release.
|
||||
|
||||
[#1246]: https://github.com/uber-go/zap/pull/1246
|
||||
[#1273]: https://github.com/uber-go/zap/pull/1273
|
||||
[#1281]: https://github.com/uber-go/zap/pull/1281
|
||||
[#1310]: https://github.com/uber-go/zap/pull/1310
|
||||
|
||||
## 1.24.0 (30 Nov 2022)
|
||||
|
||||
Enhancements:
|
||||
* [#1148][]: Add `Level` to both `Logger` and `SugaredLogger` that reports the
|
||||
current minimum enabled log level.
|
||||
* [#1185][]: `SugaredLogger` turns errors to zap.Error automatically.
|
||||
|
||||
Thanks to @Abirdcfly, @craigpastro, @nnnkkk7, and @sashamelentyev for their
|
||||
contributions to this release.
|
||||
|
||||
[#1148]: https://github.coml/uber-go/zap/pull/1148
|
||||
[#1185]: https://github.coml/uber-go/zap/pull/1185
|
||||
|
||||
## 1.23.0 (24 Aug 2022)
|
||||
|
||||
Enhancements:
|
||||
|
||||
+15
-6
@@ -1,13 +1,14 @@
|
||||
export GOBIN ?= $(shell pwd)/bin
|
||||
|
||||
GOLINT = $(GOBIN)/golint
|
||||
REVIVE = $(GOBIN)/revive
|
||||
STATICCHECK = $(GOBIN)/staticcheck
|
||||
GOVULNCHECK = $(GOBIN)/govulncheck
|
||||
BENCH_FLAGS ?= -cpuprofile=cpu.pprof -memprofile=mem.pprof -benchmem
|
||||
|
||||
# Directories containing independent Go modules.
|
||||
#
|
||||
# We track coverage only for the main module.
|
||||
MODULE_DIRS = . ./benchmarks ./zapgrpc/internal/test
|
||||
MODULE_DIRS = . ./exp ./benchmarks ./zapgrpc/internal/test
|
||||
|
||||
# Many Go tools take file globs or directories as arguments instead of packages.
|
||||
GO_FILES := $(shell \
|
||||
@@ -18,14 +19,15 @@ GO_FILES := $(shell \
|
||||
all: lint test
|
||||
|
||||
.PHONY: lint
|
||||
lint: $(GOLINT) $(STATICCHECK)
|
||||
lint: $(REVIVE) $(STATICCHECK)
|
||||
@rm -rf lint.log
|
||||
@echo "Checking formatting..."
|
||||
@gofmt -d -s $(GO_FILES) 2>&1 | tee lint.log
|
||||
@echo "Checking vet..."
|
||||
@$(foreach dir,$(MODULE_DIRS),(cd $(dir) && go vet ./... 2>&1) &&) true | tee -a lint.log
|
||||
@echo "Checking lint..."
|
||||
@$(foreach dir,$(MODULE_DIRS),(cd $(dir) && $(GOLINT) ./... 2>&1) &&) true | tee -a lint.log
|
||||
@$(foreach dir,$(MODULE_DIRS),(cd $(dir) && \
|
||||
$(REVIVE) -set_exit_status ./... 2>&1) &&) true | tee -a lint.log
|
||||
@echo "Checking staticcheck..."
|
||||
@$(foreach dir,$(MODULE_DIRS),(cd $(dir) && $(STATICCHECK) ./... 2>&1) &&) true | tee -a lint.log
|
||||
@echo "Checking for unresolved FIXMEs..."
|
||||
@@ -40,8 +42,11 @@ lint: $(GOLINT) $(STATICCHECK)
|
||||
git --no-pager diff; \
|
||||
fi
|
||||
|
||||
$(GOLINT):
|
||||
cd tools && go install golang.org/x/lint/golint
|
||||
$(REVIVE):
|
||||
cd tools && go install github.com/mgechev/revive
|
||||
|
||||
$(GOVULNCHECK):
|
||||
cd tools && go install golang.org/x/vuln/cmd/govulncheck
|
||||
|
||||
$(STATICCHECK):
|
||||
cd tools && go install honnef.co/go/tools/cmd/staticcheck
|
||||
@@ -71,3 +76,7 @@ updatereadme:
|
||||
.PHONY: tidy
|
||||
tidy:
|
||||
@$(foreach dir,$(MODULE_DIRS),(cd $(dir) && go mod tidy) &&) true
|
||||
|
||||
.PHONY: vulncheck
|
||||
vulncheck: $(GOVULNCHECK)
|
||||
$(GOVULNCHECK) ./...
|
||||
+33
-29
@@ -54,7 +54,7 @@ and make many small allocations. Put differently, using `encoding/json` and
|
||||
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
|
||||
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
|
||||
@@ -64,40 +64,43 @@ id="anchor-versions">[1](#footnote-versions)</sup>
|
||||
|
||||
Log a message and 10 fields:
|
||||
|
||||
| Package | Time | Time % to zap | Objects Allocated |
|
||||
| :------------------ | :---------: | :-----------: | :---------------: |
|
||||
| :zap: zap | 2900 ns/op | +0% | 5 allocs/op |
|
||||
| :zap: zap (sugared) | 3475 ns/op | +20% | 10 allocs/op |
|
||||
| zerolog | 10639 ns/op | +267% | 32 allocs/op |
|
||||
| go-kit | 14434 ns/op | +398% | 59 allocs/op |
|
||||
| logrus | 17104 ns/op | +490% | 81 allocs/op |
|
||||
| apex/log | 32424 ns/op | +1018% | 66 allocs/op |
|
||||
| log15 | 33579 ns/op | +1058% | 76 allocs/op |
|
||||
| Package | Time | Time % to zap | Objects Allocated |
|
||||
| :------ | :--: | :-----------: | :---------------: |
|
||||
| :zap: zap | 1744 ns/op | +0% | 5 allocs/op
|
||||
| :zap: zap (sugared) | 2483 ns/op | +42% | 10 allocs/op
|
||||
| zerolog | 918 ns/op | -47% | 1 allocs/op
|
||||
| go-kit | 5590 ns/op | +221% | 57 allocs/op
|
||||
| slog | 5640 ns/op | +223% | 40 allocs/op
|
||||
| apex/log | 21184 ns/op | +1115% | 63 allocs/op
|
||||
| logrus | 24338 ns/op | +1296% | 79 allocs/op
|
||||
| log15 | 26054 ns/op | +1394% | 74 allocs/op
|
||||
|
||||
Log a message with a logger that already has 10 fields of context:
|
||||
|
||||
| Package | Time | Time % to zap | Objects Allocated |
|
||||
| :------------------ | :---------: | :-----------: | :---------------: |
|
||||
| :zap: zap | 373 ns/op | +0% | 0 allocs/op |
|
||||
| :zap: zap (sugared) | 452 ns/op | +21% | 1 allocs/op |
|
||||
| zerolog | 288 ns/op | -23% | 0 allocs/op |
|
||||
| go-kit | 11785 ns/op | +3060% | 58 allocs/op |
|
||||
| logrus | 19629 ns/op | +5162% | 70 allocs/op |
|
||||
| log15 | 21866 ns/op | +5762% | 72 allocs/op |
|
||||
| apex/log | 30890 ns/op | +8182% | 55 allocs/op |
|
||||
| Package | Time | Time % to zap | Objects Allocated |
|
||||
| :------ | :--: | :-----------: | :---------------: |
|
||||
| :zap: zap | 193 ns/op | +0% | 0 allocs/op
|
||||
| :zap: zap (sugared) | 227 ns/op | +18% | 1 allocs/op
|
||||
| zerolog | 81 ns/op | -58% | 0 allocs/op
|
||||
| slog | 322 ns/op | +67% | 0 allocs/op
|
||||
| go-kit | 5377 ns/op | +2686% | 56 allocs/op
|
||||
| apex/log | 19518 ns/op | +10013% | 53 allocs/op
|
||||
| log15 | 19812 ns/op | +10165% | 70 allocs/op
|
||||
| logrus | 21997 ns/op | +11297% | 68 allocs/op
|
||||
|
||||
Log a static string, without any context or `printf`-style templating:
|
||||
|
||||
| Package | Time | Time % to zap | Objects Allocated |
|
||||
| :------------------ | :--------: | :-----------: | :---------------: |
|
||||
| :zap: zap | 381 ns/op | +0% | 0 allocs/op |
|
||||
| :zap: zap (sugared) | 410 ns/op | +8% | 1 allocs/op |
|
||||
| zerolog | 369 ns/op | -3% | 0 allocs/op |
|
||||
| standard library | 385 ns/op | +1% | 2 allocs/op |
|
||||
| go-kit | 606 ns/op | +59% | 11 allocs/op |
|
||||
| logrus | 1730 ns/op | +354% | 25 allocs/op |
|
||||
| apex/log | 1998 ns/op | +424% | 7 allocs/op |
|
||||
| log15 | 4546 ns/op | +1093% | 22 allocs/op |
|
||||
| Package | Time | Time % to zap | Objects Allocated |
|
||||
| :------ | :--: | :-----------: | :---------------: |
|
||||
| :zap: zap | 165 ns/op | +0% | 0 allocs/op
|
||||
| :zap: zap (sugared) | 212 ns/op | +28% | 1 allocs/op
|
||||
| zerolog | 95 ns/op | -42% | 0 allocs/op
|
||||
| slog | 296 ns/op | +79% | 0 allocs/op
|
||||
| go-kit | 415 ns/op | +152% | 9 allocs/op
|
||||
| standard library | 422 ns/op | +156% | 2 allocs/op
|
||||
| apex/log | 1601 ns/op | +870% | 5 allocs/op
|
||||
| logrus | 3017 ns/op | +1728% | 23 allocs/op
|
||||
| log15 | 3469 ns/op | +2002% | 20 allocs/op
|
||||
|
||||
## Development Status: Stable
|
||||
|
||||
@@ -131,3 +134,4 @@ pinned in the [benchmarks/go.mod][] file. [↩](#anchor-versions)
|
||||
[cov]: https://codecov.io/gh/uber-go/zap
|
||||
[benchmarking suite]: https://github.com/uber-go/zap/tree/master/benchmarks
|
||||
[benchmarks/go.mod]: https://github.com/uber-go/zap/blob/master/benchmarks/go.mod
|
||||
|
||||
+4
-4
@@ -76,9 +76,9 @@ func (os objects[T]) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// objectMarshalerPtr is a constraint that specifies that the given type
|
||||
// ObjectMarshalerPtr is a constraint that specifies that the given type
|
||||
// implements zapcore.ObjectMarshaler on a pointer receiver.
|
||||
type objectMarshalerPtr[T any] interface {
|
||||
type ObjectMarshalerPtr[T any] interface {
|
||||
*T
|
||||
zapcore.ObjectMarshaler
|
||||
}
|
||||
@@ -105,11 +105,11 @@ type objectMarshalerPtr[T any] interface {
|
||||
//
|
||||
// var requests []*Request = ...
|
||||
// logger.Info("sending requests", zap.Objects("requests", requests))
|
||||
func ObjectValues[T any, P objectMarshalerPtr[T]](key string, values []T) Field {
|
||||
func ObjectValues[T any, P ObjectMarshalerPtr[T]](key string, values []T) Field {
|
||||
return Array(key, objectValues[T, P](values))
|
||||
}
|
||||
|
||||
type objectValues[T any, P objectMarshalerPtr[T]] []T
|
||||
type objectValues[T any, P ObjectMarshalerPtr[T]] []T
|
||||
|
||||
func (os objectValues[T, P]) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
for i := range os {
|
||||
|
||||
+12
-8
@@ -20,25 +20,29 @@
|
||||
|
||||
package buffer
|
||||
|
||||
import "sync"
|
||||
import (
|
||||
"go.uber.org/zap/internal/pool"
|
||||
)
|
||||
|
||||
// A Pool is a type-safe wrapper around a sync.Pool.
|
||||
type Pool struct {
|
||||
p *sync.Pool
|
||||
p *pool.Pool[*Buffer]
|
||||
}
|
||||
|
||||
// NewPool constructs a new Pool.
|
||||
func NewPool() Pool {
|
||||
return Pool{p: &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &Buffer{bs: make([]byte, 0, _size)}
|
||||
},
|
||||
}}
|
||||
return Pool{
|
||||
p: pool.New(func() *Buffer {
|
||||
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 := p.p.Get()
|
||||
buf.Reset()
|
||||
buf.pool = p
|
||||
return buf
|
||||
|
||||
+75
-9
@@ -95,6 +95,32 @@ type Config struct {
|
||||
|
||||
// NewProductionEncoderConfig returns an opinionated EncoderConfig for
|
||||
// production environments.
|
||||
//
|
||||
// Messages encoded with this configuration will be JSON-formatted
|
||||
// and will have the following keys by default:
|
||||
//
|
||||
// - "level": The logging level (e.g. "info", "error").
|
||||
// - "ts": The current time in number of seconds since the Unix epoch.
|
||||
// - "msg": The message passed to the log statement.
|
||||
// - "caller": If available, a short path to the file and line number
|
||||
// where the log statement was issued.
|
||||
// The logger configuration determines whether this field is captured.
|
||||
// - "stacktrace": If available, a stack trace from the line
|
||||
// where the log statement was issued.
|
||||
// The logger configuration determines whether this field is captured.
|
||||
//
|
||||
// By default, the following formats are used for different types:
|
||||
//
|
||||
// - Time is formatted as floating-point number of seconds since the Unix
|
||||
// epoch.
|
||||
// - Duration is formatted as floating-point number of seconds.
|
||||
//
|
||||
// You may change these by setting the appropriate fields in the returned
|
||||
// object.
|
||||
// For example, use the following to change the time encoding format:
|
||||
//
|
||||
// cfg := zap.NewProductionEncoderConfig()
|
||||
// cfg.EncodeTime = zapcore.ISO8601TimeEncoder
|
||||
func NewProductionEncoderConfig() zapcore.EncoderConfig {
|
||||
return zapcore.EncoderConfig{
|
||||
TimeKey: "ts",
|
||||
@@ -112,11 +138,22 @@ func NewProductionEncoderConfig() zapcore.EncoderConfig {
|
||||
}
|
||||
}
|
||||
|
||||
// NewProductionConfig is a reasonable production logging configuration.
|
||||
// Logging is enabled at InfoLevel and above.
|
||||
// NewProductionConfig builds a reasonable default production logging
|
||||
// configuration.
|
||||
// Logging is enabled at InfoLevel and above, and uses a JSON encoder.
|
||||
// Logs are written to standard error.
|
||||
// Stacktraces are included on logs of ErrorLevel and above.
|
||||
// DPanicLevel logs will not panic, but will write a stacktrace.
|
||||
//
|
||||
// It uses a JSON encoder, writes to standard error, and enables sampling.
|
||||
// Stacktraces are automatically included on logs of ErrorLevel and above.
|
||||
// Sampling is enabled at 100:100 by default,
|
||||
// meaning that after the first 100 log entries
|
||||
// with the same level and message in the same second,
|
||||
// it will log every 100th entry
|
||||
// with the same level and message in the same second.
|
||||
// You may disable this behavior by setting Sampling to nil.
|
||||
//
|
||||
// See [NewProductionEncoderConfig] for information
|
||||
// on the default encoder configuration.
|
||||
func NewProductionConfig() Config {
|
||||
return Config{
|
||||
Level: NewAtomicLevelAt(InfoLevel),
|
||||
@@ -134,6 +171,32 @@ func NewProductionConfig() Config {
|
||||
|
||||
// NewDevelopmentEncoderConfig returns an opinionated EncoderConfig for
|
||||
// development environments.
|
||||
//
|
||||
// Messages encoded with this configuration will use Zap's console encoder
|
||||
// intended to print human-readable output.
|
||||
// It will print log messages with the following information:
|
||||
//
|
||||
// - The log level (e.g. "INFO", "ERROR").
|
||||
// - The time in ISO8601 format (e.g. "2017-01-01T12:00:00Z").
|
||||
// - The message passed to the log statement.
|
||||
// - If available, a short path to the file and line number
|
||||
// where the log statement was issued.
|
||||
// The logger configuration determines whether this field is captured.
|
||||
// - If available, a stacktrace from the line
|
||||
// where the log statement was issued.
|
||||
// The logger configuration determines whether this field is captured.
|
||||
//
|
||||
// By default, the following formats are used for different types:
|
||||
//
|
||||
// - Time is formatted in ISO8601 format (e.g. "2017-01-01T12:00:00Z").
|
||||
// - Duration is formatted as a string (e.g. "1.234s").
|
||||
//
|
||||
// You may change these by setting the appropriate fields in the returned
|
||||
// object.
|
||||
// For example, use the following to change the time encoding format:
|
||||
//
|
||||
// cfg := zap.NewDevelopmentEncoderConfig()
|
||||
// cfg.EncodeTime = zapcore.ISO8601TimeEncoder
|
||||
func NewDevelopmentEncoderConfig() zapcore.EncoderConfig {
|
||||
return zapcore.EncoderConfig{
|
||||
// Keys can be anything except the empty string.
|
||||
@@ -152,12 +215,15 @@ func NewDevelopmentEncoderConfig() zapcore.EncoderConfig {
|
||||
}
|
||||
}
|
||||
|
||||
// NewDevelopmentConfig is a reasonable development logging configuration.
|
||||
// Logging is enabled at DebugLevel and above.
|
||||
// NewDevelopmentConfig builds a reasonable default development logging
|
||||
// configuration.
|
||||
// Logging is enabled at DebugLevel and above, and uses a console encoder.
|
||||
// Logs are written to standard error.
|
||||
// Stacktraces are included on logs of WarnLevel and above.
|
||||
// DPanicLevel logs will panic.
|
||||
//
|
||||
// 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.
|
||||
// See [NewDevelopmentEncoderConfig] for information
|
||||
// on the default encoder configuration.
|
||||
func NewDevelopmentConfig() Config {
|
||||
return Config{
|
||||
Level: NewAtomicLevelAt(DebugLevel),
|
||||
|
||||
+4
-5
@@ -21,14 +21,13 @@
|
||||
package zap
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"go.uber.org/zap/internal/pool"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
var _errArrayElemPool = sync.Pool{New: func() interface{} {
|
||||
var _errArrayElemPool = pool.New(func() *errArrayElem {
|
||||
return &errArrayElem{}
|
||||
}}
|
||||
})
|
||||
|
||||
// Error is shorthand for the common idiom NamedError("error", err).
|
||||
func Error(err error) Field {
|
||||
@@ -60,7 +59,7 @@ func (errs errArray) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
// 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 := _errArrayElemPool.Get()
|
||||
elem.error = errs[i]
|
||||
arr.AppendObject(elem)
|
||||
elem.error = nil
|
||||
|
||||
+105
-64
@@ -410,6 +410,43 @@ func Inline(val zapcore.ObjectMarshaler) Field {
|
||||
}
|
||||
}
|
||||
|
||||
// We discovered an issue where zap.Any can cause a performance degradation
|
||||
// when used in new goroutines.
|
||||
//
|
||||
// This happens because the compiler assigns 4.8kb (one zap.Field per arm of
|
||||
// switch statement) of stack space for zap.Any when it takes the form:
|
||||
//
|
||||
// switch v := v.(type) {
|
||||
// case string:
|
||||
// return String(key, v)
|
||||
// case int:
|
||||
// return Int(key, v)
|
||||
// // ...
|
||||
// default:
|
||||
// return Reflect(key, v)
|
||||
// }
|
||||
//
|
||||
// To avoid this, we use the type switch to assign a value to a single local variable
|
||||
// and then call a function on it.
|
||||
// The local variable is just a function reference so it doesn't allocate
|
||||
// when converted to an interface{}.
|
||||
//
|
||||
// A fair bit of experimentation went into this.
|
||||
// See also:
|
||||
//
|
||||
// - https://github.com/uber-go/zap/pull/1301
|
||||
// - https://github.com/uber-go/zap/pull/1303
|
||||
// - https://github.com/uber-go/zap/pull/1304
|
||||
// - https://github.com/uber-go/zap/pull/1305
|
||||
// - https://github.com/uber-go/zap/pull/1308
|
||||
type anyFieldC[T any] func(string, T) Field
|
||||
|
||||
func (f anyFieldC[T]) Any(key string, val any) Field {
|
||||
v, _ := val.(T)
|
||||
// val is guaranteed to be a T, except when it's nil.
|
||||
return f(key, v)
|
||||
}
|
||||
|
||||
// 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.
|
||||
@@ -418,132 +455,136 @@ func Inline(val zapcore.ObjectMarshaler) Field {
|
||||
// them. To minimize surprises, []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{}) Field {
|
||||
switch val := value.(type) {
|
||||
var c interface{ Any(string, any) Field }
|
||||
|
||||
switch value.(type) {
|
||||
case zapcore.ObjectMarshaler:
|
||||
return Object(key, val)
|
||||
c = anyFieldC[zapcore.ObjectMarshaler](Object)
|
||||
case zapcore.ArrayMarshaler:
|
||||
return Array(key, val)
|
||||
c = anyFieldC[zapcore.ArrayMarshaler](Array)
|
||||
case bool:
|
||||
return Bool(key, val)
|
||||
c = anyFieldC[bool](Bool)
|
||||
case *bool:
|
||||
return Boolp(key, val)
|
||||
c = anyFieldC[*bool](Boolp)
|
||||
case []bool:
|
||||
return Bools(key, val)
|
||||
c = anyFieldC[[]bool](Bools)
|
||||
case complex128:
|
||||
return Complex128(key, val)
|
||||
c = anyFieldC[complex128](Complex128)
|
||||
case *complex128:
|
||||
return Complex128p(key, val)
|
||||
c = anyFieldC[*complex128](Complex128p)
|
||||
case []complex128:
|
||||
return Complex128s(key, val)
|
||||
c = anyFieldC[[]complex128](Complex128s)
|
||||
case complex64:
|
||||
return Complex64(key, val)
|
||||
c = anyFieldC[complex64](Complex64)
|
||||
case *complex64:
|
||||
return Complex64p(key, val)
|
||||
c = anyFieldC[*complex64](Complex64p)
|
||||
case []complex64:
|
||||
return Complex64s(key, val)
|
||||
c = anyFieldC[[]complex64](Complex64s)
|
||||
case float64:
|
||||
return Float64(key, val)
|
||||
c = anyFieldC[float64](Float64)
|
||||
case *float64:
|
||||
return Float64p(key, val)
|
||||
c = anyFieldC[*float64](Float64p)
|
||||
case []float64:
|
||||
return Float64s(key, val)
|
||||
c = anyFieldC[[]float64](Float64s)
|
||||
case float32:
|
||||
return Float32(key, val)
|
||||
c = anyFieldC[float32](Float32)
|
||||
case *float32:
|
||||
return Float32p(key, val)
|
||||
c = anyFieldC[*float32](Float32p)
|
||||
case []float32:
|
||||
return Float32s(key, val)
|
||||
c = anyFieldC[[]float32](Float32s)
|
||||
case int:
|
||||
return Int(key, val)
|
||||
c = anyFieldC[int](Int)
|
||||
case *int:
|
||||
return Intp(key, val)
|
||||
c = anyFieldC[*int](Intp)
|
||||
case []int:
|
||||
return Ints(key, val)
|
||||
c = anyFieldC[[]int](Ints)
|
||||
case int64:
|
||||
return Int64(key, val)
|
||||
c = anyFieldC[int64](Int64)
|
||||
case *int64:
|
||||
return Int64p(key, val)
|
||||
c = anyFieldC[*int64](Int64p)
|
||||
case []int64:
|
||||
return Int64s(key, val)
|
||||
c = anyFieldC[[]int64](Int64s)
|
||||
case int32:
|
||||
return Int32(key, val)
|
||||
c = anyFieldC[int32](Int32)
|
||||
case *int32:
|
||||
return Int32p(key, val)
|
||||
c = anyFieldC[*int32](Int32p)
|
||||
case []int32:
|
||||
return Int32s(key, val)
|
||||
c = anyFieldC[[]int32](Int32s)
|
||||
case int16:
|
||||
return Int16(key, val)
|
||||
c = anyFieldC[int16](Int16)
|
||||
case *int16:
|
||||
return Int16p(key, val)
|
||||
c = anyFieldC[*int16](Int16p)
|
||||
case []int16:
|
||||
return Int16s(key, val)
|
||||
c = anyFieldC[[]int16](Int16s)
|
||||
case int8:
|
||||
return Int8(key, val)
|
||||
c = anyFieldC[int8](Int8)
|
||||
case *int8:
|
||||
return Int8p(key, val)
|
||||
c = anyFieldC[*int8](Int8p)
|
||||
case []int8:
|
||||
return Int8s(key, val)
|
||||
c = anyFieldC[[]int8](Int8s)
|
||||
case string:
|
||||
return String(key, val)
|
||||
c = anyFieldC[string](String)
|
||||
case *string:
|
||||
return Stringp(key, val)
|
||||
c = anyFieldC[*string](Stringp)
|
||||
case []string:
|
||||
return Strings(key, val)
|
||||
c = anyFieldC[[]string](Strings)
|
||||
case uint:
|
||||
return Uint(key, val)
|
||||
c = anyFieldC[uint](Uint)
|
||||
case *uint:
|
||||
return Uintp(key, val)
|
||||
c = anyFieldC[*uint](Uintp)
|
||||
case []uint:
|
||||
return Uints(key, val)
|
||||
c = anyFieldC[[]uint](Uints)
|
||||
case uint64:
|
||||
return Uint64(key, val)
|
||||
c = anyFieldC[uint64](Uint64)
|
||||
case *uint64:
|
||||
return Uint64p(key, val)
|
||||
c = anyFieldC[*uint64](Uint64p)
|
||||
case []uint64:
|
||||
return Uint64s(key, val)
|
||||
c = anyFieldC[[]uint64](Uint64s)
|
||||
case uint32:
|
||||
return Uint32(key, val)
|
||||
c = anyFieldC[uint32](Uint32)
|
||||
case *uint32:
|
||||
return Uint32p(key, val)
|
||||
c = anyFieldC[*uint32](Uint32p)
|
||||
case []uint32:
|
||||
return Uint32s(key, val)
|
||||
c = anyFieldC[[]uint32](Uint32s)
|
||||
case uint16:
|
||||
return Uint16(key, val)
|
||||
c = anyFieldC[uint16](Uint16)
|
||||
case *uint16:
|
||||
return Uint16p(key, val)
|
||||
c = anyFieldC[*uint16](Uint16p)
|
||||
case []uint16:
|
||||
return Uint16s(key, val)
|
||||
c = anyFieldC[[]uint16](Uint16s)
|
||||
case uint8:
|
||||
return Uint8(key, val)
|
||||
c = anyFieldC[uint8](Uint8)
|
||||
case *uint8:
|
||||
return Uint8p(key, val)
|
||||
c = anyFieldC[*uint8](Uint8p)
|
||||
case []byte:
|
||||
return Binary(key, val)
|
||||
c = anyFieldC[[]byte](Binary)
|
||||
case uintptr:
|
||||
return Uintptr(key, val)
|
||||
c = anyFieldC[uintptr](Uintptr)
|
||||
case *uintptr:
|
||||
return Uintptrp(key, val)
|
||||
c = anyFieldC[*uintptr](Uintptrp)
|
||||
case []uintptr:
|
||||
return Uintptrs(key, val)
|
||||
c = anyFieldC[[]uintptr](Uintptrs)
|
||||
case time.Time:
|
||||
return Time(key, val)
|
||||
c = anyFieldC[time.Time](Time)
|
||||
case *time.Time:
|
||||
return Timep(key, val)
|
||||
c = anyFieldC[*time.Time](Timep)
|
||||
case []time.Time:
|
||||
return Times(key, val)
|
||||
c = anyFieldC[[]time.Time](Times)
|
||||
case time.Duration:
|
||||
return Duration(key, val)
|
||||
c = anyFieldC[time.Duration](Duration)
|
||||
case *time.Duration:
|
||||
return Durationp(key, val)
|
||||
c = anyFieldC[*time.Duration](Durationp)
|
||||
case []time.Duration:
|
||||
return Durations(key, val)
|
||||
c = anyFieldC[[]time.Duration](Durations)
|
||||
case error:
|
||||
return NamedError(key, val)
|
||||
c = anyFieldC[error](NamedError)
|
||||
case []error:
|
||||
return Errors(key, val)
|
||||
c = anyFieldC[[]error](Errors)
|
||||
case fmt.Stringer:
|
||||
return Stringer(key, val)
|
||||
c = anyFieldC[fmt.Stringer](Stringer)
|
||||
default:
|
||||
return Reflect(key, val)
|
||||
c = anyFieldC[any](Reflect)
|
||||
}
|
||||
|
||||
return c.Any(key, value)
|
||||
}
|
||||
+2
@@ -18,6 +18,8 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
// Package internal and its subpackages hold types and functionality
|
||||
// that are not part of Zap's public API.
|
||||
package internal
|
||||
|
||||
import "go.uber.org/zap/zapcore"
|
||||
|
||||
Generated
Vendored
+29
-24
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
// Copyright (c) 2023 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -18,36 +18,41 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
// Package pool provides internal pool utilities.
|
||||
package pool
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
//go:generate bin/gen-atomicwrapper -name=Bool -type=bool -wrapped=Uint32 -pack=boolToInt -unpack=truthy -cas -swap -json -file=bool.go
|
||||
|
||||
func truthy(n uint32) bool {
|
||||
return n == 1
|
||||
// A Pool is a generic wrapper around [sync.Pool] to provide strongly-typed
|
||||
// object pooling.
|
||||
//
|
||||
// Note that SA6002 (ref: https://staticcheck.io/docs/checks/#SA6002) will
|
||||
// not be detected, so all internal pool use must take care to only store
|
||||
// pointer types.
|
||||
type Pool[T any] struct {
|
||||
pool sync.Pool
|
||||
}
|
||||
|
||||
func boolToInt(b bool) uint32 {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Toggle atomically negates the Boolean and returns the previous value.
|
||||
func (b *Bool) Toggle() bool {
|
||||
for {
|
||||
old := b.Load()
|
||||
if b.CAS(old, !old) {
|
||||
return old
|
||||
}
|
||||
// New returns a new [Pool] for T, and will use fn to construct new Ts when
|
||||
// the pool is empty.
|
||||
func New[T any](fn func() T) *Pool[T] {
|
||||
return &Pool[T]{
|
||||
pool: sync.Pool{
|
||||
New: func() any {
|
||||
return fn()
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (b *Bool) String() string {
|
||||
return strconv.FormatBool(b.Load())
|
||||
// Get gets a T from the pool, or creates a new one if the pool is empty.
|
||||
func (p *Pool[T]) Get() T {
|
||||
return p.pool.Get().(T)
|
||||
}
|
||||
|
||||
// Put returns x into the pool.
|
||||
func (p *Pool[T]) Put(x T) {
|
||||
p.pool.Put(x)
|
||||
}
|
||||
+5
-4
@@ -21,7 +21,8 @@
|
||||
package zap
|
||||
|
||||
import (
|
||||
"go.uber.org/atomic"
|
||||
"sync/atomic"
|
||||
|
||||
"go.uber.org/zap/internal"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
@@ -76,9 +77,9 @@ var _ internal.LeveledEnabler = AtomicLevel{}
|
||||
// NewAtomicLevel creates an AtomicLevel with InfoLevel and above logging
|
||||
// enabled.
|
||||
func NewAtomicLevel() AtomicLevel {
|
||||
return AtomicLevel{
|
||||
l: atomic.NewInt32(int32(InfoLevel)),
|
||||
}
|
||||
lvl := AtomicLevel{l: new(atomic.Int32)}
|
||||
lvl.l.Store(int32(InfoLevel))
|
||||
return lvl
|
||||
}
|
||||
|
||||
// NewAtomicLevelAt is a convenience function that creates an AtomicLevel
|
||||
|
||||
+13
@@ -183,6 +183,13 @@ func (log *Logger) With(fields ...Field) *Logger {
|
||||
return l
|
||||
}
|
||||
|
||||
// Level reports the minimum enabled level for this logger.
|
||||
//
|
||||
// For NopLoggers, this is [zapcore.InvalidLevel].
|
||||
func (log *Logger) Level() zapcore.Level {
|
||||
return zapcore.LevelOf(log.core)
|
||||
}
|
||||
|
||||
// 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.
|
||||
@@ -274,6 +281,12 @@ func (log *Logger) Core() zapcore.Core {
|
||||
return log.core
|
||||
}
|
||||
|
||||
// Name returns the Logger's underlying name,
|
||||
// or an empty string if the logger is unnamed.
|
||||
func (log *Logger) Name() string {
|
||||
return log.name
|
||||
}
|
||||
|
||||
func (log *Logger) clone() *Logger {
|
||||
copy := *log
|
||||
return ©
|
||||
|
||||
+2
-1
@@ -133,7 +133,8 @@ func IncreaseLevel(lvl zapcore.LevelEnabler) Option {
|
||||
}
|
||||
|
||||
// OnFatal sets the action to take on fatal logs.
|
||||
// Deprecated: Use WithFatalHook instead.
|
||||
//
|
||||
// Deprecated: Use [WithFatalHook] instead.
|
||||
func OnFatal(action zapcore.CheckWriteAction) Option {
|
||||
return WithFatalHook(action)
|
||||
}
|
||||
|
||||
+59
-41
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2016 Uber Technologies, Inc.
|
||||
// Copyright (c) 2016-2022 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -34,23 +35,7 @@ import (
|
||||
|
||||
const schemeFile = "file"
|
||||
|
||||
var (
|
||||
_sinkMutex sync.RWMutex
|
||||
_sinkFactories map[string]func(*url.URL) (Sink, error) // keyed by scheme
|
||||
)
|
||||
|
||||
func init() {
|
||||
resetSinkRegistry()
|
||||
}
|
||||
|
||||
func resetSinkRegistry() {
|
||||
_sinkMutex.Lock()
|
||||
defer _sinkMutex.Unlock()
|
||||
|
||||
_sinkFactories = map[string]func(*url.URL) (Sink, error){
|
||||
schemeFile: newFileSink,
|
||||
}
|
||||
}
|
||||
var _sinkRegistry = newSinkRegistry()
|
||||
|
||||
// Sink defines the interface to write to and close logger destinations.
|
||||
type Sink interface {
|
||||
@@ -58,10 +43,6 @@ type Sink interface {
|
||||
io.Closer
|
||||
}
|
||||
|
||||
type nopCloserSink struct{ zapcore.WriteSyncer }
|
||||
|
||||
func (nopCloserSink) Close() error { return nil }
|
||||
|
||||
type errSinkNotFound struct {
|
||||
scheme string
|
||||
}
|
||||
@@ -70,16 +51,29 @@ func (e *errSinkNotFound) Error() string {
|
||||
return fmt.Sprintf("no sink found for scheme %q", e.scheme)
|
||||
}
|
||||
|
||||
// RegisterSink registers a user-supplied factory for all sinks with a
|
||||
// particular scheme.
|
||||
//
|
||||
// All schemes must be ASCII, valid under section 3.1 of RFC 3986
|
||||
// (https://tools.ietf.org/html/rfc3986#section-3.1), and must not already
|
||||
// have a factory registered. Zap automatically registers a factory for the
|
||||
// "file" scheme.
|
||||
func RegisterSink(scheme string, factory func(*url.URL) (Sink, error)) error {
|
||||
_sinkMutex.Lock()
|
||||
defer _sinkMutex.Unlock()
|
||||
type nopCloserSink struct{ zapcore.WriteSyncer }
|
||||
|
||||
func (nopCloserSink) Close() error { return nil }
|
||||
|
||||
type sinkRegistry struct {
|
||||
mu sync.Mutex
|
||||
factories map[string]func(*url.URL) (Sink, error) // keyed by scheme
|
||||
openFile func(string, int, os.FileMode) (*os.File, error) // type matches os.OpenFile
|
||||
}
|
||||
|
||||
func newSinkRegistry() *sinkRegistry {
|
||||
sr := &sinkRegistry{
|
||||
factories: make(map[string]func(*url.URL) (Sink, error)),
|
||||
openFile: os.OpenFile,
|
||||
}
|
||||
sr.RegisterSink(schemeFile, sr.newFileSinkFromURL)
|
||||
return sr
|
||||
}
|
||||
|
||||
// RegisterScheme registers the given factory for the specific scheme.
|
||||
func (sr *sinkRegistry) RegisterSink(scheme string, factory func(*url.URL) (Sink, error)) error {
|
||||
sr.mu.Lock()
|
||||
defer sr.mu.Unlock()
|
||||
|
||||
if scheme == "" {
|
||||
return errors.New("can't register a sink factory for empty string")
|
||||
@@ -88,14 +82,22 @@ func RegisterSink(scheme string, factory func(*url.URL) (Sink, error)) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("%q is not a valid scheme: %v", scheme, err)
|
||||
}
|
||||
if _, ok := _sinkFactories[normalized]; ok {
|
||||
if _, ok := sr.factories[normalized]; ok {
|
||||
return fmt.Errorf("sink factory already registered for scheme %q", normalized)
|
||||
}
|
||||
_sinkFactories[normalized] = factory
|
||||
sr.factories[normalized] = factory
|
||||
return nil
|
||||
}
|
||||
|
||||
func newSink(rawURL string) (Sink, error) {
|
||||
func (sr *sinkRegistry) newSink(rawURL string) (Sink, error) {
|
||||
// URL parsing doesn't work well for Windows paths such as `c:\log.txt`, as scheme is set to
|
||||
// the drive, and path is unset unless `c:/log.txt` is used.
|
||||
// To avoid Windows-specific URL handling, we instead check IsAbs to open as a file.
|
||||
// filepath.IsAbs is OS-specific, so IsAbs('c:/log.txt') is false outside of Windows.
|
||||
if filepath.IsAbs(rawURL) {
|
||||
return sr.newFileSinkFromPath(rawURL)
|
||||
}
|
||||
|
||||
u, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't parse %q as a URL: %v", rawURL, err)
|
||||
@@ -104,16 +106,27 @@ func newSink(rawURL string) (Sink, error) {
|
||||
u.Scheme = schemeFile
|
||||
}
|
||||
|
||||
_sinkMutex.RLock()
|
||||
factory, ok := _sinkFactories[u.Scheme]
|
||||
_sinkMutex.RUnlock()
|
||||
sr.mu.Lock()
|
||||
factory, ok := sr.factories[u.Scheme]
|
||||
sr.mu.Unlock()
|
||||
if !ok {
|
||||
return nil, &errSinkNotFound{u.Scheme}
|
||||
}
|
||||
return factory(u)
|
||||
}
|
||||
|
||||
func newFileSink(u *url.URL) (Sink, error) {
|
||||
// RegisterSink registers a user-supplied factory for all sinks with a
|
||||
// particular scheme.
|
||||
//
|
||||
// All schemes must be ASCII, valid under section 0.1 of RFC 3986
|
||||
// (https://tools.ietf.org/html/rfc3983#section-3.1), and must not already
|
||||
// have a factory registered. Zap automatically registers a factory for the
|
||||
// "file" scheme.
|
||||
func RegisterSink(scheme string, factory func(*url.URL) (Sink, error)) error {
|
||||
return _sinkRegistry.RegisterSink(scheme, factory)
|
||||
}
|
||||
|
||||
func (sr *sinkRegistry) newFileSinkFromURL(u *url.URL) (Sink, error) {
|
||||
if u.User != nil {
|
||||
return nil, fmt.Errorf("user and password not allowed with file URLs: got %v", u)
|
||||
}
|
||||
@@ -130,13 +143,18 @@ func newFileSink(u *url.URL) (Sink, error) {
|
||||
if hn := u.Hostname(); hn != "" && hn != "localhost" {
|
||||
return nil, fmt.Errorf("file URLs must leave host empty or use localhost: got %v", u)
|
||||
}
|
||||
switch u.Path {
|
||||
|
||||
return sr.newFileSinkFromPath(u.Path)
|
||||
}
|
||||
|
||||
func (sr *sinkRegistry) newFileSinkFromPath(path string) (Sink, error) {
|
||||
switch path {
|
||||
case "stdout":
|
||||
return nopCloserSink{os.Stdout}, nil
|
||||
case "stderr":
|
||||
return nopCloserSink{os.Stderr}, nil
|
||||
}
|
||||
return os.OpenFile(u.Path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
|
||||
return sr.openFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
|
||||
}
|
||||
|
||||
func normalizeScheme(s string) (string, error) {
|
||||
|
||||
+8
-10
@@ -22,19 +22,17 @@ package zap
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"go.uber.org/zap/buffer"
|
||||
"go.uber.org/zap/internal/bufferpool"
|
||||
"go.uber.org/zap/internal/pool"
|
||||
)
|
||||
|
||||
var _stacktracePool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &stacktrace{
|
||||
storage: make([]uintptr, 64),
|
||||
}
|
||||
},
|
||||
}
|
||||
var _stacktracePool = pool.New(func() *stacktrace {
|
||||
return &stacktrace{
|
||||
storage: make([]uintptr, 64),
|
||||
}
|
||||
})
|
||||
|
||||
type stacktrace struct {
|
||||
pcs []uintptr // program counters; always a subslice of storage
|
||||
@@ -68,7 +66,7 @@ const (
|
||||
//
|
||||
// The caller must call Free on the returned stacktrace after using it.
|
||||
func captureStacktrace(skip int, depth stacktraceDepth) *stacktrace {
|
||||
stack := _stacktracePool.Get().(*stacktrace)
|
||||
stack := _stacktracePool.Get()
|
||||
|
||||
switch depth {
|
||||
case stacktraceFirst:
|
||||
@@ -154,7 +152,7 @@ func newStackFormatter(b *buffer.Buffer) stackFormatter {
|
||||
// the final runtime.main/runtime.goexit frame.
|
||||
func (sf *stackFormatter) FormatStack(stack *stacktrace) {
|
||||
// Note: On the last iteration, frames.Next() returns false, with a valid
|
||||
// frame, but we ignore this frame. The last frame is a a runtime frame which
|
||||
// frame, but we ignore this frame. The last frame is a runtime frame which
|
||||
// adds noise, since it's only either runtime.main or runtime.goexit.
|
||||
for frame, more := stack.Next(); more; frame, more = stack.Next() {
|
||||
sf.FormatFrame(frame)
|
||||
|
||||
+72
-28
@@ -31,6 +31,7 @@ import (
|
||||
const (
|
||||
_oddNumberErrMsg = "Ignored key without a value."
|
||||
_nonStringKeyErrMsg = "Ignored key-value pairs with non-string keys."
|
||||
_multipleErrMsg = "Multiple errors without a key."
|
||||
)
|
||||
|
||||
// A SugaredLogger wraps the base Logger functionality in a slower, but less
|
||||
@@ -114,74 +115,95 @@ 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.
|
||||
// Level reports the minimum enabled level for this logger.
|
||||
//
|
||||
// For NopLoggers, this is [zapcore.InvalidLevel].
|
||||
func (s *SugaredLogger) Level() zapcore.Level {
|
||||
return zapcore.LevelOf(s.base.core)
|
||||
}
|
||||
|
||||
// Debug logs the provided arguments at [DebugLevel].
|
||||
// Spaces are added between arguments when neither is a string.
|
||||
func (s *SugaredLogger) Debug(args ...interface{}) {
|
||||
s.log(DebugLevel, "", args, nil)
|
||||
}
|
||||
|
||||
// Info uses fmt.Sprint to construct and log a message.
|
||||
// Info logs the provided arguments at [InfoLevel].
|
||||
// Spaces are added between arguments when neither is a string.
|
||||
func (s *SugaredLogger) Info(args ...interface{}) {
|
||||
s.log(InfoLevel, "", args, nil)
|
||||
}
|
||||
|
||||
// Warn uses fmt.Sprint to construct and log a message.
|
||||
// Warn logs the provided arguments at [WarnLevel].
|
||||
// Spaces are added between arguments when neither is a string.
|
||||
func (s *SugaredLogger) Warn(args ...interface{}) {
|
||||
s.log(WarnLevel, "", args, nil)
|
||||
}
|
||||
|
||||
// Error uses fmt.Sprint to construct and log a message.
|
||||
// Error logs the provided arguments at [ErrorLevel].
|
||||
// Spaces are added between arguments when neither is a string.
|
||||
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.)
|
||||
// DPanic logs the provided arguments at [DPanicLevel].
|
||||
// In development, the logger then panics. (See [DPanicLevel] for details.)
|
||||
// Spaces are added between arguments when neither is a string.
|
||||
func (s *SugaredLogger) DPanic(args ...interface{}) {
|
||||
s.log(DPanicLevel, "", args, nil)
|
||||
}
|
||||
|
||||
// Panic uses fmt.Sprint to construct and log a message, then panics.
|
||||
// Panic constructs a message with the provided arguments and panics.
|
||||
// Spaces are added between arguments when neither is a string.
|
||||
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.
|
||||
// Fatal constructs a message with the provided arguments and calls os.Exit.
|
||||
// Spaces are added between arguments when neither is a string.
|
||||
func (s *SugaredLogger) Fatal(args ...interface{}) {
|
||||
s.log(FatalLevel, "", args, nil)
|
||||
}
|
||||
|
||||
// Debugf uses fmt.Sprintf to log a templated message.
|
||||
// Debugf formats the message according to the format specifier
|
||||
// and logs it at [DebugLevel].
|
||||
func (s *SugaredLogger) Debugf(template string, args ...interface{}) {
|
||||
s.log(DebugLevel, template, args, nil)
|
||||
}
|
||||
|
||||
// Infof uses fmt.Sprintf to log a templated message.
|
||||
// Infof formats the message according to the format specifier
|
||||
// and logs it at [InfoLevel].
|
||||
func (s *SugaredLogger) Infof(template string, args ...interface{}) {
|
||||
s.log(InfoLevel, template, args, nil)
|
||||
}
|
||||
|
||||
// Warnf uses fmt.Sprintf to log a templated message.
|
||||
// Warnf formats the message according to the format specifier
|
||||
// and logs it at [WarnLevel].
|
||||
func (s *SugaredLogger) Warnf(template string, args ...interface{}) {
|
||||
s.log(WarnLevel, template, args, nil)
|
||||
}
|
||||
|
||||
// Errorf uses fmt.Sprintf to log a templated message.
|
||||
// Errorf formats the message according to the format specifier
|
||||
// and logs it at [ErrorLevel].
|
||||
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.)
|
||||
// DPanicf formats the message according to the format specifier
|
||||
// and logs it at [DPanicLevel].
|
||||
// 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.
|
||||
// Panicf formats the message according to the format specifier
|
||||
// and 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.
|
||||
// Fatalf formats the message according to the format specifier
|
||||
// and calls os.Exit.
|
||||
func (s *SugaredLogger) Fatalf(template string, args ...interface{}) {
|
||||
s.log(FatalLevel, template, args, nil)
|
||||
}
|
||||
@@ -233,38 +255,45 @@ func (s *SugaredLogger) Fatalw(msg string, keysAndValues ...interface{}) {
|
||||
s.log(FatalLevel, msg, nil, keysAndValues)
|
||||
}
|
||||
|
||||
// Debugln uses fmt.Sprintln to construct and log a message.
|
||||
// Debugln logs a message at [DebugLevel].
|
||||
// Spaces are always added between arguments.
|
||||
func (s *SugaredLogger) Debugln(args ...interface{}) {
|
||||
s.logln(DebugLevel, args, nil)
|
||||
}
|
||||
|
||||
// Infoln uses fmt.Sprintln to construct and log a message.
|
||||
// Infoln logs a message at [InfoLevel].
|
||||
// Spaces are always added between arguments.
|
||||
func (s *SugaredLogger) Infoln(args ...interface{}) {
|
||||
s.logln(InfoLevel, args, nil)
|
||||
}
|
||||
|
||||
// Warnln uses fmt.Sprintln to construct and log a message.
|
||||
// Warnln logs a message at [WarnLevel].
|
||||
// Spaces are always added between arguments.
|
||||
func (s *SugaredLogger) Warnln(args ...interface{}) {
|
||||
s.logln(WarnLevel, args, nil)
|
||||
}
|
||||
|
||||
// Errorln uses fmt.Sprintln to construct and log a message.
|
||||
// Errorln logs a message at [ErrorLevel].
|
||||
// Spaces are always added between arguments.
|
||||
func (s *SugaredLogger) Errorln(args ...interface{}) {
|
||||
s.logln(ErrorLevel, args, nil)
|
||||
}
|
||||
|
||||
// DPanicln uses fmt.Sprintln to construct and log a message. In development, the
|
||||
// logger then panics. (See DPanicLevel for details.)
|
||||
// DPanicln logs a message at [DPanicLevel].
|
||||
// In development, the logger then panics. (See [DPanicLevel] for details.)
|
||||
// Spaces are always added between arguments.
|
||||
func (s *SugaredLogger) DPanicln(args ...interface{}) {
|
||||
s.logln(DPanicLevel, args, nil)
|
||||
}
|
||||
|
||||
// Panicln uses fmt.Sprintln to construct and log a message, then panics.
|
||||
// Panicln logs a message at [PanicLevel] and panics.
|
||||
// Spaces are always added between arguments.
|
||||
func (s *SugaredLogger) Panicln(args ...interface{}) {
|
||||
s.logln(PanicLevel, args, nil)
|
||||
}
|
||||
|
||||
// Fatalln uses fmt.Sprintln to construct and log a message, then calls os.Exit.
|
||||
// Fatalln logs a message at [FatalLevel] and calls os.Exit.
|
||||
// Spaces are always added between arguments.
|
||||
func (s *SugaredLogger) Fatalln(args ...interface{}) {
|
||||
s.logln(FatalLevel, args, nil)
|
||||
}
|
||||
@@ -329,10 +358,13 @@ func (s *SugaredLogger) sweetenFields(args []interface{}) []Field {
|
||||
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([]Field, 0, len(args))
|
||||
var invalid invalidPairs
|
||||
var (
|
||||
// Allocate enough space for the worst case; if users pass only structured
|
||||
// fields, we shouldn't penalize them with extra allocations.
|
||||
fields = make([]Field, 0, len(args))
|
||||
invalid invalidPairs
|
||||
seenError bool
|
||||
)
|
||||
|
||||
for i := 0; i < len(args); {
|
||||
// This is a strongly-typed field. Consume it and move on.
|
||||
@@ -342,6 +374,18 @@ func (s *SugaredLogger) sweetenFields(args []interface{}) []Field {
|
||||
continue
|
||||
}
|
||||
|
||||
// If it is an error, consume it and move on.
|
||||
if err, ok := args[i].(error); ok {
|
||||
if !seenError {
|
||||
seenError = true
|
||||
fields = append(fields, Error(err))
|
||||
} else {
|
||||
s.base.Error(_multipleErrMsg, Error(err))
|
||||
}
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
// Make sure this element isn't a dangling key.
|
||||
if i == len(args)-1 {
|
||||
s.base.Error(_oddNumberErrMsg, Any("ignored", args[i]))
|
||||
|
||||
+1
-1
@@ -68,7 +68,7 @@ func open(paths []string) ([]zapcore.WriteSyncer, func(), error) {
|
||||
|
||||
var openErr error
|
||||
for _, path := range paths {
|
||||
sink, err := newSink(path)
|
||||
sink, err := _sinkRegistry.newSink(path)
|
||||
if err != nil {
|
||||
openErr = multierr.Append(openErr, fmt.Errorf("open sink %q: %w", path, err))
|
||||
continue
|
||||
|
||||
+7
-7
@@ -22,20 +22,20 @@ package zapcore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"go.uber.org/zap/buffer"
|
||||
"go.uber.org/zap/internal/bufferpool"
|
||||
"go.uber.org/zap/internal/pool"
|
||||
)
|
||||
|
||||
var _sliceEncoderPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &sliceArrayEncoder{elems: make([]interface{}, 0, 2)}
|
||||
},
|
||||
}
|
||||
var _sliceEncoderPool = pool.New(func() *sliceArrayEncoder {
|
||||
return &sliceArrayEncoder{
|
||||
elems: make([]interface{}, 0, 2),
|
||||
}
|
||||
})
|
||||
|
||||
func getSliceEncoder() *sliceArrayEncoder {
|
||||
return _sliceEncoderPool.Get().(*sliceArrayEncoder)
|
||||
return _sliceEncoderPool.Get()
|
||||
}
|
||||
|
||||
func putSliceEncoder(e *sliceArrayEncoder) {
|
||||
|
||||
+10
-11
@@ -24,25 +24,23 @@ import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.uber.org/multierr"
|
||||
"go.uber.org/zap/internal/bufferpool"
|
||||
"go.uber.org/zap/internal/exit"
|
||||
"go.uber.org/zap/internal/pool"
|
||||
)
|
||||
|
||||
var (
|
||||
_cePool = sync.Pool{New: func() interface{} {
|
||||
// Pre-allocate some space for cores.
|
||||
return &CheckedEntry{
|
||||
cores: make([]Core, 4),
|
||||
}
|
||||
}}
|
||||
)
|
||||
var _cePool = pool.New(func() *CheckedEntry {
|
||||
// Pre-allocate some space for cores.
|
||||
return &CheckedEntry{
|
||||
cores: make([]Core, 4),
|
||||
}
|
||||
})
|
||||
|
||||
func getCheckedEntry() *CheckedEntry {
|
||||
ce := _cePool.Get().(*CheckedEntry)
|
||||
ce := _cePool.Get()
|
||||
ce.reset()
|
||||
return ce
|
||||
}
|
||||
@@ -281,7 +279,8 @@ func (ce *CheckedEntry) AddCore(ent Entry, core Core) *CheckedEntry {
|
||||
// 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.
|
||||
// Deprecated: Use After(ent Entry, after CheckWriteHook) instead.
|
||||
//
|
||||
// Deprecated: Use [CheckedEntry.After] instead.
|
||||
func (ce *CheckedEntry) Should(ent Entry, should CheckWriteAction) *CheckedEntry {
|
||||
return ce.After(ent, should)
|
||||
}
|
||||
|
||||
+5
-4
@@ -23,7 +23,8 @@ package zapcore
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"go.uber.org/zap/internal/pool"
|
||||
)
|
||||
|
||||
// Encodes the given error into fields of an object. A field with the given
|
||||
@@ -103,9 +104,9 @@ func (errs errArray) MarshalLogArray(arr ArrayEncoder) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var _errArrayElemPool = sync.Pool{New: func() interface{} {
|
||||
var _errArrayElemPool = pool.New(func() *errArrayElem {
|
||||
return &errArrayElem{}
|
||||
}}
|
||||
})
|
||||
|
||||
// Encodes any error into a {"error": ...} re-using the same errors logic.
|
||||
//
|
||||
@@ -113,7 +114,7 @@ var _errArrayElemPool = sync.Pool{New: func() interface{} {
|
||||
type errArrayElem struct{ err error }
|
||||
|
||||
func newErrArrayElem(err error) *errArrayElem {
|
||||
e := _errArrayElemPool.Get().(*errArrayElem)
|
||||
e := _errArrayElemPool.Get()
|
||||
e.err = err
|
||||
return e
|
||||
}
|
||||
|
||||
+5
-9
@@ -23,24 +23,20 @@ package zapcore
|
||||
import (
|
||||
"encoding/base64"
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"go.uber.org/zap/buffer"
|
||||
"go.uber.org/zap/internal/bufferpool"
|
||||
"go.uber.org/zap/internal/pool"
|
||||
)
|
||||
|
||||
// For JSON-escaping; see jsonEncoder.safeAddString below.
|
||||
const _hex = "0123456789abcdef"
|
||||
|
||||
var _jsonPool = sync.Pool{New: func() interface{} {
|
||||
var _jsonPool = pool.New(func() *jsonEncoder {
|
||||
return &jsonEncoder{}
|
||||
}}
|
||||
|
||||
func getJSONEncoder() *jsonEncoder {
|
||||
return _jsonPool.Get().(*jsonEncoder)
|
||||
}
|
||||
})
|
||||
|
||||
func putJSONEncoder(enc *jsonEncoder) {
|
||||
if enc.reflectBuf != nil {
|
||||
@@ -354,7 +350,7 @@ func (enc *jsonEncoder) Clone() Encoder {
|
||||
}
|
||||
|
||||
func (enc *jsonEncoder) clone() *jsonEncoder {
|
||||
clone := getJSONEncoder()
|
||||
clone := _jsonPool.Get()
|
||||
clone.EncoderConfig = enc.EncoderConfig
|
||||
clone.spaced = enc.spaced
|
||||
clone.openNamespaces = enc.openNamespaces
|
||||
@@ -527,7 +523,7 @@ func (enc *jsonEncoder) tryAddRuneSelf(b byte) bool {
|
||||
if b >= utf8.RuneSelf {
|
||||
return false
|
||||
}
|
||||
if 0x20 <= b && b != '\\' && b != '"' {
|
||||
if b >= 0x20 && b != '\\' && b != '"' {
|
||||
enc.buf.AppendByte(b)
|
||||
return true
|
||||
}
|
||||
|
||||
+4
-5
@@ -21,9 +21,8 @@
|
||||
package zapcore
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"go.uber.org/atomic"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -66,16 +65,16 @@ 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()
|
||||
return c.counter.Add(1)
|
||||
}
|
||||
|
||||
c.counter.Store(1)
|
||||
|
||||
newResetAfter := tn + tick.Nanoseconds()
|
||||
if !c.resetAt.CAS(resetAfter, newResetAfter) {
|
||||
if !c.resetAt.CompareAndSwap(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 c.counter.Add(1)
|
||||
}
|
||||
|
||||
return 1
|
||||
|
||||
Reference in new issue
Block a user