├── .codecov.yml ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ └── feature_request.md ├── dependabot.yml └── workflows │ └── go.yml ├── .gitignore ├── .golangci.yml ├── .readme.tmpl ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── FAQ.md ├── LICENSE ├── Makefile ├── README.md ├── array.go ├── array_test.go ├── assets ├── README.md ├── go.mod └── logo.png ├── benchmarks ├── apex_test.go ├── doc.go ├── go.mod ├── go.sum ├── kit_test.go ├── log15_test.go ├── logrus_test.go ├── scenario_bench_test.go ├── slog_test.go ├── zap_test.go └── zerolog_test.go ├── buffer ├── buffer.go ├── buffer_test.go ├── pool.go └── pool_test.go ├── checklicense.sh ├── clock_test.go ├── common_test.go ├── config.go ├── config_test.go ├── doc.go ├── encoder.go ├── encoder_test.go ├── error.go ├── error_test.go ├── example_test.go ├── exp ├── CHANGELOG.md ├── LICENSE ├── go.mod ├── go.sum ├── zapfield │ ├── zapfield.go │ └── zapfield_test.go └── zapslog │ ├── doc.go │ ├── example_test.go │ ├── handler.go │ ├── handler_test.go │ └── options.go ├── field.go ├── field_test.go ├── flag.go ├── flag_test.go ├── glide.yaml ├── global.go ├── global_test.go ├── go.mod ├── go.sum ├── http_handler.go ├── http_handler_test.go ├── increase_level_test.go ├── internal ├── bufferpool │ └── bufferpool.go ├── color │ ├── color.go │ └── color_test.go ├── exit │ ├── exit.go │ └── exit_test.go ├── level_enabler.go ├── pool │ ├── pool.go │ └── pool_test.go ├── readme │ └── readme.go ├── stacktrace │ ├── stack.go │ └── stack_test.go └── ztest │ ├── clock.go │ ├── clock_test.go │ ├── doc.go │ ├── timeout.go │ └── writer.go ├── leak_test.go ├── level.go ├── level_test.go ├── logger.go ├── logger_bench_test.go ├── logger_test.go ├── options.go ├── sink.go ├── sink_test.go ├── sink_windows_test.go ├── stacktrace_ext_test.go ├── sugar.go ├── sugar_test.go ├── time.go ├── time_test.go ├── tools ├── go.mod ├── go.sum └── tools.go ├── writer.go ├── writer_test.go ├── zapcore ├── buffered_write_syncer.go ├── buffered_write_syncer_bench_test.go ├── buffered_write_syncer_test.go ├── clock.go ├── clock_test.go ├── console_encoder.go ├── console_encoder_bench_test.go ├── console_encoder_test.go ├── core.go ├── core_test.go ├── doc.go ├── encoder.go ├── encoder_test.go ├── entry.go ├── entry_ext_test.go ├── entry_test.go ├── error.go ├── error_test.go ├── field.go ├── field_test.go ├── hook.go ├── hook_test.go ├── increase_level.go ├── increase_level_test.go ├── json_encoder.go ├── json_encoder_bench_test.go ├── json_encoder_impl_test.go ├── json_encoder_test.go ├── lazy_with.go ├── lazy_with_test.go ├── leak_test.go ├── level.go ├── level_strings.go ├── level_strings_test.go ├── level_test.go ├── marshaler.go ├── memory_encoder.go ├── memory_encoder_test.go ├── reflected_encoder.go ├── sampler.go ├── sampler_bench_test.go ├── sampler_test.go ├── tee.go ├── tee_logger_bench_test.go ├── tee_test.go ├── write_syncer.go ├── write_syncer_bench_test.go └── write_syncer_test.go ├── zapgrpc ├── internal │ └── test │ │ ├── README.md │ │ ├── go.mod │ │ ├── go.sum │ │ ├── grpc.go │ │ └── grpc_test.go ├── zapgrpc.go └── zapgrpc_test.go ├── zapio ├── example_test.go ├── writer.go └── writer_test.go └── zaptest ├── doc.go ├── logger.go ├── logger_test.go ├── observer ├── logged_entry.go ├── logged_entry_test.go ├── observer.go └── observer_test.go ├── testingt.go ├── testingt_test.go ├── timeout.go ├── timeout_test.go ├── writer.go └── writer_test.go /.codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | range: 80..100 3 | round: down 4 | precision: 2 5 | 6 | status: 7 | project: # measuring the overall project coverage 8 | default: # context, you can create multiple ones with custom titles 9 | enabled: yes # must be yes|true to enable this status 10 | target: 95% # specify the target coverage for each commit status 11 | # option: "auto" (must increase from parent commit or pull request base) 12 | # option: "X%" a static target percentage to hit 13 | if_not_found: success # if parent is not found report status as success, error, or failure 14 | if_ci_failed: error # if ci fails report status as success, error, or failure 15 | ignore: 16 | - internal/readme/readme.go 17 | 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Additional context** 20 | Add any other context about the problem here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | contact_links: 2 | - name: Questions 3 | about: Please use our Discussions page 4 | url: https://github.com/uber-go/zap/discussions 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Is this a breaking change?** 19 | We do not accept breaking changes to the existing API. Please consider if your proposed solution is backwards compatible. If not, we can help you make it backwards compatible, but this must be considered when we consider new features. 20 | 21 | **Additional context** 22 | Add any other context or screenshots about the feature request here. 23 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | 8 | # Since Zap is a library, we don't want to update its dependency requirements 9 | # regularly--not until we need a newer version of a dependency for a feature 10 | # or specific fix. 11 | # This way, users of Zap aren't forced to upgrade all their transitive 12 | # dependencies every time they upgrade Zap. 13 | # 14 | # However, we do want to regularly update dependencies used inside the tools 15 | # submodule because that holds linters and other development tools. 16 | - package-ecosystem: "gomod" 17 | directory: "/tools" 18 | schedule: 19 | interval: "weekly" 20 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | tags: ['v*'] 7 | pull_request: 8 | branches: ['*'] 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | 15 | build: 16 | runs-on: ubuntu-latest 17 | strategy: 18 | matrix: 19 | go: ["1.22.x", "1.23.x"] 20 | include: 21 | - go: 1.23.x 22 | 23 | steps: 24 | - name: Checkout code 25 | uses: actions/checkout@v4 26 | 27 | - name: Setup Go 28 | uses: actions/setup-go@v5 29 | with: 30 | go-version: ${{ matrix.go }} 31 | cache-dependency-path: '**/go.sum' 32 | 33 | - name: Download Dependencies 34 | run: | 35 | go mod download 36 | (cd tools && go mod download) 37 | (cd benchmarks && go mod download) 38 | (cd zapgrpc/internal/test && go mod download) 39 | 40 | - name: Test 41 | run: make cover 42 | 43 | - name: Upload coverage to codecov.io 44 | uses: codecov/codecov-action@v5 45 | with: 46 | verbose: true 47 | token: ${{ secrets.CODECOV_TOKEN }} 48 | 49 | lint: 50 | name: Lint 51 | runs-on: ubuntu-latest 52 | 53 | steps: 54 | - uses: actions/checkout@v4 55 | name: Check out repository 56 | - uses: actions/setup-go@v5 57 | name: Set up Go 58 | with: 59 | go-version: 1.23.x 60 | cache: false # managed by golangci-lint 61 | 62 | - uses: golangci/golangci-lint-action@v6 63 | name: Install golangci-lint 64 | with: 65 | version: latest 66 | # Hack: Use the official action to download, but not run. 67 | # make lint below will handle actually running the linter. 68 | args: --help 69 | 70 | - run: make lint 71 | name: Lint 72 | 73 | - name: vulncheck 74 | run: make vulncheck 75 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | vendor 10 | 11 | # Architecture specific extensions/prefixes 12 | *.[568vq] 13 | [568vq].out 14 | 15 | *.cgo1.go 16 | *.cgo2.c 17 | _cgo_defun.c 18 | _cgo_gotypes.go 19 | _cgo_export.* 20 | 21 | _testmain.go 22 | 23 | *.exe 24 | *.test 25 | *.prof 26 | *.pprof 27 | *.out 28 | *.log 29 | 30 | /bin 31 | cover.out 32 | cover.html 33 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | output: 2 | # Make output more digestible with quickfix in vim/emacs/etc. 3 | sort-results: true 4 | print-issued-lines: false 5 | 6 | linters: 7 | # We'll track the golangci-lint default linters manually 8 | # instead of letting them change without our control. 9 | disable-all: true 10 | enable: 11 | # golangci-lint defaults: 12 | - errcheck 13 | - gosimple 14 | - govet 15 | - ineffassign 16 | - staticcheck 17 | - unused 18 | 19 | # Our own extras: 20 | - gofumpt 21 | - nolintlint # lints nolint directives 22 | - revive 23 | 24 | linters-settings: 25 | govet: 26 | # These govet checks are disabled by default, but they're useful. 27 | enable: 28 | - nilness 29 | - reflectvaluecompare 30 | - sortslice 31 | - unusedwrite 32 | 33 | errcheck: 34 | exclude-functions: 35 | # These methods can not fail. 36 | # They operate on an in-memory buffer. 37 | - (*go.uber.org/zap/buffer.Buffer).Write 38 | - (*go.uber.org/zap/buffer.Buffer).WriteByte 39 | - (*go.uber.org/zap/buffer.Buffer).WriteString 40 | 41 | - (*go.uber.org/zap/zapio.Writer).Close 42 | - (*go.uber.org/zap/zapio.Writer).Sync 43 | - (*go.uber.org/zap/zapio.Writer).Write 44 | # Write to zapio.Writer cannot fail, 45 | # so io.WriteString on it cannot fail. 46 | - io.WriteString(*go.uber.org/zap/zapio.Writer) 47 | 48 | # Writing a plain string to a fmt.State cannot fail. 49 | - io.WriteString(fmt.State) 50 | 51 | issues: 52 | # Print all issues reported by all linters. 53 | max-issues-per-linter: 0 54 | max-same-issues: 0 55 | 56 | # Don't ignore some of the issues that golangci-lint considers okay. 57 | # This includes documenting all exported entities. 58 | exclude-use-default: false 59 | 60 | exclude-rules: 61 | # Don't warn on unused parameters. 62 | # Parameter names are useful; replacing them with '_' is undesirable. 63 | - linters: [revive] 64 | text: 'unused-parameter: parameter \S+ seems to be unused, consider removing or renaming it as _' 65 | 66 | # staticcheck already has smarter checks for empty blocks. 67 | # revive's empty-block linter has false positives. 68 | # For example, as of writing this, the following is not allowed. 69 | # for foo() { } 70 | - linters: [revive] 71 | text: 'empty-block: this block is empty, you can remove it' 72 | 73 | # Ignore logger.Sync() errcheck failures in example_test.go 74 | # since those are intended to be uncomplicated examples. 75 | - linters: [errcheck] 76 | path: example_test.go 77 | text: 'Error return value of `logger.Sync` is not checked' 78 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, 8 | body size, disability, ethnicity, gender identity and expression, level of 9 | experience, nationality, personal appearance, race, religion, or sexual 10 | identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an 52 | appointed representative at an online or offline event. Representation of a 53 | project may be further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at oss-conduct@uber.com. The project 59 | team will review and investigate all complaints, and will respond in a way 60 | that it deems appropriate to the circumstances. The project team is obligated 61 | to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 71 | version 1.4, available at 72 | [http://contributor-covenant.org/version/1/4][version]. 73 | 74 | [homepage]: https://contributor-covenant.org 75 | [version]: https://contributor-covenant.org/version/1/4/ 76 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We'd love your help making zap the very best structured logging library in Go! 4 | 5 | If you'd like to add new exported APIs, please [open an issue][open-issue] 6 | describing your proposal — discussing API changes ahead of time makes 7 | pull request review much smoother. In your issue, pull request, and any other 8 | communications, please remember to treat your fellow contributors with 9 | respect! We take our [code of conduct](CODE_OF_CONDUCT.md) seriously. 10 | 11 | Note that you'll need to sign [Uber's Contributor License Agreement][cla] 12 | before we can accept any of your contributions. If necessary, a bot will remind 13 | you to accept the CLA when you open your pull request. 14 | 15 | ## Setup 16 | 17 | [Fork][fork], then clone the repository: 18 | 19 | ```bash 20 | mkdir -p $GOPATH/src/go.uber.org 21 | cd $GOPATH/src/go.uber.org 22 | git clone git@github.com:your_github_username/zap.git 23 | cd zap 24 | git remote add upstream https://github.com/uber-go/zap.git 25 | git fetch upstream 26 | ``` 27 | 28 | Make sure that the tests and the linters pass: 29 | 30 | ```bash 31 | make test 32 | make lint 33 | ``` 34 | 35 | ## Making Changes 36 | 37 | Start by creating a new branch for your changes: 38 | 39 | ```bash 40 | cd $GOPATH/src/go.uber.org/zap 41 | git checkout master 42 | git fetch upstream 43 | git rebase upstream/master 44 | git checkout -b cool_new_feature 45 | ``` 46 | 47 | Make your changes, then ensure that `make lint` and `make test` still pass. If 48 | you're satisfied with your changes, push them to your fork. 49 | 50 | ```bash 51 | git push origin cool_new_feature 52 | ``` 53 | 54 | Then use the GitHub UI to open a pull request. 55 | 56 | At this point, you're waiting on us to review your changes. We _try_ to respond 57 | to issues and pull requests within a few business days, and we may suggest some 58 | improvements or alternatives. Once your changes are approved, one of the 59 | project maintainers will merge them. 60 | 61 | We're much more likely to approve your changes if you: 62 | 63 | - Add tests for new functionality. 64 | - Write a [good commit message][commit-message]. 65 | - Maintain backward compatibility. 66 | 67 | [fork]: https://github.com/uber-go/zap/fork 68 | [open-issue]: https://github.com/uber-go/zap/issues/new 69 | [cla]: https://cla-assistant.io/uber-go/zap 70 | [commit-message]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html 71 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016-2024 Uber Technologies, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Directory containing the Makefile. 2 | PROJECT_ROOT = $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) 3 | 4 | export GOBIN ?= $(PROJECT_ROOT)/bin 5 | export PATH := $(GOBIN):$(PATH) 6 | 7 | GOVULNCHECK = $(GOBIN)/govulncheck 8 | BENCH_FLAGS ?= -cpuprofile=cpu.pprof -memprofile=mem.pprof -benchmem 9 | 10 | # Directories containing independent Go modules. 11 | MODULE_DIRS = . ./exp ./benchmarks ./zapgrpc/internal/test 12 | 13 | # Directories that we want to track coverage for. 14 | COVER_DIRS = . ./exp 15 | 16 | .PHONY: all 17 | all: lint test 18 | 19 | .PHONY: lint 20 | lint: golangci-lint tidy-lint license-lint 21 | 22 | .PHONY: golangci-lint 23 | golangci-lint: 24 | @$(foreach mod,$(MODULE_DIRS), \ 25 | (cd $(mod) && \ 26 | echo "[lint] golangci-lint: $(mod)" && \ 27 | golangci-lint run --path-prefix $(mod) ./...) &&) true 28 | 29 | .PHONY: tidy 30 | tidy: 31 | @$(foreach dir,$(MODULE_DIRS), \ 32 | (cd $(dir) && go mod tidy) &&) true 33 | 34 | .PHONY: tidy-lint 35 | tidy-lint: 36 | @$(foreach mod,$(MODULE_DIRS), \ 37 | (cd $(mod) && \ 38 | echo "[lint] tidy: $(mod)" && \ 39 | go mod tidy && \ 40 | git diff --exit-code -- go.mod go.sum) &&) true 41 | 42 | 43 | .PHONY: license-lint 44 | license-lint: 45 | ./checklicense.sh 46 | 47 | $(GOVULNCHECK): 48 | cd tools && go install golang.org/x/vuln/cmd/govulncheck 49 | 50 | .PHONY: test 51 | test: 52 | @$(foreach dir,$(MODULE_DIRS),(cd $(dir) && go test -race ./...) &&) true 53 | 54 | .PHONY: cover 55 | cover: 56 | @$(foreach dir,$(COVER_DIRS), ( \ 57 | cd $(dir) && \ 58 | go test -race -coverprofile=cover.out -coverpkg=./... ./... \ 59 | && go tool cover -html=cover.out -o cover.html) &&) true 60 | 61 | .PHONY: bench 62 | BENCH ?= . 63 | bench: 64 | @$(foreach dir,$(MODULE_DIRS), ( \ 65 | cd $(dir) && \ 66 | go list ./... | xargs -n1 go test -bench=$(BENCH) -run="^$$" $(BENCH_FLAGS) \ 67 | ) &&) true 68 | 69 | .PHONY: updatereadme 70 | updatereadme: 71 | rm -f README.md 72 | cat .readme.tmpl | go run internal/readme/readme.go > README.md 73 | 74 | .PHONY: vulncheck 75 | vulncheck: $(GOVULNCHECK) 76 | $(GOVULNCHECK) ./... 77 | -------------------------------------------------------------------------------- /assets/README.md: -------------------------------------------------------------------------------- 1 | The logo for Zap was designed by [Abhinav Gupta](https://abhinavg.net/) 2 | and is made available under the Creative Commons 4.0 Attribution License. 3 | 4 | It is based on the Go Gopher mascot originally created by Renee French, 5 | which is also licensed under the Creative Commons 4.0 Attribution License. 6 | -------------------------------------------------------------------------------- /assets/go.mod: -------------------------------------------------------------------------------- 1 | module go.uber.org/zap/assets 2 | 3 | go 1.21.5 4 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uber-go/zap/a6afd05063133cc713487d6c883760decd673ca0/assets/logo.png -------------------------------------------------------------------------------- /benchmarks/apex_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package benchmarks 22 | 23 | import ( 24 | "io" 25 | 26 | "github.com/apex/log" 27 | "github.com/apex/log/handlers/json" 28 | ) 29 | 30 | func newDisabledApexLog() *log.Logger { 31 | return &log.Logger{ 32 | Handler: json.New(io.Discard), 33 | Level: log.ErrorLevel, 34 | } 35 | } 36 | 37 | func newApexLog() *log.Logger { 38 | return &log.Logger{ 39 | Handler: json.New(io.Discard), 40 | Level: log.DebugLevel, 41 | } 42 | } 43 | 44 | func fakeApexFields() log.Fields { 45 | return log.Fields{ 46 | "int": _tenInts[0], 47 | "ints": _tenInts, 48 | "string": _tenStrings[0], 49 | "strings": _tenStrings, 50 | "time": _tenTimes[0], 51 | "times": _tenTimes, 52 | "user1": _oneUser, 53 | "user2": _oneUser, 54 | "users": _tenUsers, 55 | "error": errExample, 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /benchmarks/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // Package benchmarks contains only benchmarks comparing zap to other 22 | // structured logging libraries. 23 | package benchmarks 24 | -------------------------------------------------------------------------------- /benchmarks/go.mod: -------------------------------------------------------------------------------- 1 | module go.uber.org/zap/benchmarks 2 | 3 | go 1.21 4 | 5 | replace go.uber.org/zap => ../ 6 | 7 | require ( 8 | github.com/apex/log v1.9.0 9 | github.com/go-kit/log v0.2.1 10 | github.com/rs/zerolog v1.30.0 11 | github.com/sirupsen/logrus v1.9.3 12 | go.uber.org/multierr v1.11.0 13 | go.uber.org/zap v1.23.0 14 | gopkg.in/inconshreveable/log15.v2 v2.16.0 15 | ) 16 | 17 | require ( 18 | github.com/go-logfmt/logfmt v0.6.0 // indirect 19 | github.com/go-stack/stack v1.8.1 // indirect 20 | github.com/mattn/go-colorable v0.1.13 // indirect 21 | github.com/mattn/go-isatty v0.0.19 // indirect 22 | github.com/pkg/errors v0.9.1 // indirect 23 | golang.org/x/sys v0.12.0 // indirect 24 | golang.org/x/term v0.12.0 // indirect 25 | ) 26 | -------------------------------------------------------------------------------- /benchmarks/kit_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package benchmarks 22 | 23 | import ( 24 | "io" 25 | 26 | "github.com/go-kit/log" 27 | ) 28 | 29 | func newKitLog(fields ...interface{}) log.Logger { 30 | return log.With(log.NewJSONLogger(io.Discard), fields...) 31 | } 32 | -------------------------------------------------------------------------------- /benchmarks/log15_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package benchmarks 22 | 23 | import ( 24 | "io" 25 | 26 | "gopkg.in/inconshreveable/log15.v2" 27 | ) 28 | 29 | func newLog15() log15.Logger { 30 | logger := log15.New() 31 | logger.SetHandler(log15.StreamHandler(io.Discard, log15.JsonFormat())) 32 | return logger 33 | } 34 | -------------------------------------------------------------------------------- /benchmarks/logrus_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package benchmarks 22 | 23 | import ( 24 | "io" 25 | 26 | "github.com/sirupsen/logrus" 27 | ) 28 | 29 | func newDisabledLogrus() *logrus.Logger { 30 | logger := newLogrus() 31 | logger.Level = logrus.ErrorLevel 32 | return logger 33 | } 34 | 35 | func newLogrus() *logrus.Logger { 36 | return &logrus.Logger{ 37 | Out: io.Discard, 38 | Formatter: new(logrus.JSONFormatter), 39 | Hooks: make(logrus.LevelHooks), 40 | Level: logrus.DebugLevel, 41 | } 42 | } 43 | 44 | func fakeLogrusFields() logrus.Fields { 45 | return logrus.Fields{ 46 | "int": _tenInts[0], 47 | "ints": _tenInts, 48 | "string": _tenStrings[0], 49 | "strings": _tenStrings, 50 | "time": _tenTimes[0], 51 | "times": _tenTimes, 52 | "user1": _oneUser, 53 | "user2": _oneUser, 54 | "users": _tenUsers, 55 | "error": errExample, 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /benchmarks/slog_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package benchmarks 22 | 23 | import ( 24 | "io" 25 | "log/slog" 26 | ) 27 | 28 | func newSlog(fields ...slog.Attr) *slog.Logger { 29 | return slog.New(slog.NewJSONHandler(io.Discard, nil).WithAttrs(fields)) 30 | } 31 | 32 | func newDisabledSlog(fields ...slog.Attr) *slog.Logger { 33 | return slog.New(slog.NewJSONHandler(io.Discard, &slog.HandlerOptions{Level: slog.LevelError}).WithAttrs(fields)) 34 | } 35 | 36 | func fakeSlogFields() []slog.Attr { 37 | return []slog.Attr{ 38 | slog.Int("int", _tenInts[0]), 39 | slog.Any("ints", _tenInts), 40 | slog.String("string", _tenStrings[0]), 41 | slog.Any("strings", _tenStrings), 42 | slog.Time("time", _tenTimes[0]), 43 | slog.Any("times", _tenTimes), 44 | slog.Any("user1", _oneUser), 45 | slog.Any("user2", _oneUser), 46 | slog.Any("users", _tenUsers), 47 | slog.Any("error", errExample), 48 | } 49 | } 50 | 51 | func fakeSlogArgs() []any { 52 | return []any{ 53 | "int", _tenInts[0], 54 | "ints", _tenInts, 55 | "string", _tenStrings[0], 56 | "strings", _tenStrings, 57 | "time", _tenTimes[0], 58 | "times", _tenTimes, 59 | "user1", _oneUser, 60 | "user2", _oneUser, 61 | "users", _tenUsers, 62 | "error", errExample, 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /benchmarks/zerolog_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package benchmarks 22 | 23 | import ( 24 | "io" 25 | 26 | "github.com/rs/zerolog" 27 | ) 28 | 29 | func newZerolog() zerolog.Logger { 30 | return zerolog.New(io.Discard).With().Timestamp().Logger() 31 | } 32 | 33 | func newDisabledZerolog() zerolog.Logger { 34 | return newZerolog().Level(zerolog.Disabled) 35 | } 36 | 37 | func (u *user) MarshalZerologObject(e *zerolog.Event) { 38 | e.Str("name", u.Name). 39 | Str("email", u.Email). 40 | Int64("createdAt", u.CreatedAt.UnixNano()) 41 | } 42 | 43 | func (uu users) MarshalZerologArray(a *zerolog.Array) { 44 | for _, u := range uu { 45 | a.Object(u) 46 | } 47 | } 48 | 49 | func fakeZerologFields(e *zerolog.Event) *zerolog.Event { 50 | return e. 51 | Int("int", _tenInts[0]). 52 | Ints("ints", _tenInts). 53 | Str("string", _tenStrings[0]). 54 | Strs("strings", _tenStrings). 55 | Time("time", _tenTimes[0]). 56 | Times("times", _tenTimes). 57 | Object("user1", _oneUser). 58 | Object("user2", _oneUser). 59 | Array("users", _tenUsers). 60 | Err(errExample) 61 | } 62 | 63 | func fakeZerologContext(c zerolog.Context) zerolog.Context { 64 | return c. 65 | Int("int", _tenInts[0]). 66 | Ints("ints", _tenInts). 67 | Str("string", _tenStrings[0]). 68 | Strs("strings", _tenStrings). 69 | Time("time", _tenTimes[0]). 70 | Times("times", _tenTimes). 71 | Object("user1", _oneUser). 72 | Object("user2", _oneUser). 73 | Array("users", _tenUsers). 74 | Err(errExample) 75 | } 76 | -------------------------------------------------------------------------------- /buffer/buffer_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package buffer 22 | 23 | import ( 24 | "bytes" 25 | "strings" 26 | "testing" 27 | "time" 28 | 29 | "github.com/stretchr/testify/assert" 30 | ) 31 | 32 | func TestBufferWrites(t *testing.T) { 33 | buf := NewPool().Get() 34 | 35 | tests := []struct { 36 | desc string 37 | f func() 38 | want string 39 | }{ 40 | {"AppendByte", func() { buf.AppendByte('v') }, "v"}, 41 | {"AppendString", func() { buf.AppendString("foo") }, "foo"}, 42 | {"AppendIntPositive", func() { buf.AppendInt(42) }, "42"}, 43 | {"AppendIntNegative", func() { buf.AppendInt(-42) }, "-42"}, 44 | {"AppendUint", func() { buf.AppendUint(42) }, "42"}, 45 | {"AppendBool", func() { buf.AppendBool(true) }, "true"}, 46 | {"AppendFloat64", func() { buf.AppendFloat(3.14, 64) }, "3.14"}, 47 | // Intentionally introduce some floating-point error. 48 | {"AppendFloat32", func() { buf.AppendFloat(float64(float32(3.14)), 32) }, "3.14"}, 49 | {"AppendWrite", func() { buf.Write([]byte("foo")) }, "foo"}, 50 | {"AppendTime", func() { buf.AppendTime(time.Date(2000, 1, 2, 3, 4, 5, 6, time.UTC), time.RFC3339) }, "2000-01-02T03:04:05Z"}, 51 | {"WriteByte", func() { buf.WriteByte('v') }, "v"}, 52 | {"WriteString", func() { buf.WriteString("foo") }, "foo"}, 53 | } 54 | 55 | for _, tt := range tests { 56 | t.Run(tt.desc, func(t *testing.T) { 57 | buf.Reset() 58 | tt.f() 59 | assert.Equal(t, tt.want, buf.String(), "Unexpected buffer.String().") 60 | assert.Equal(t, tt.want, string(buf.Bytes()), "Unexpected string(buffer.Bytes()).") 61 | assert.Equal(t, len(tt.want), buf.Len(), "Unexpected buffer length.") 62 | // We're not writing more than a kibibyte in tests. 63 | assert.Equal(t, _size, buf.Cap(), "Expected buffer capacity to remain constant.") 64 | }) 65 | } 66 | } 67 | 68 | func BenchmarkBuffers(b *testing.B) { 69 | // Because we use the strconv.AppendFoo functions so liberally, we can't 70 | // use the standard library's bytes.Buffer anyways (without incurring a 71 | // bunch of extra allocations). Nevertheless, let's make sure that we're 72 | // not losing any precious nanoseconds. 73 | str := strings.Repeat("a", 1024) 74 | slice := make([]byte, 0, 1024) 75 | buf := bytes.NewBuffer(slice) 76 | custom := NewPool().Get() 77 | b.Run("ByteSlice", func(b *testing.B) { 78 | for i := 0; i < b.N; i++ { 79 | slice = append(slice, str...) 80 | slice = slice[:0] 81 | } 82 | }) 83 | b.Run("BytesBuffer", func(b *testing.B) { 84 | for i := 0; i < b.N; i++ { 85 | buf.WriteString(str) 86 | buf.Reset() 87 | } 88 | }) 89 | b.Run("CustomBuffer", func(b *testing.B) { 90 | for i := 0; i < b.N; i++ { 91 | custom.AppendString(str) 92 | custom.Reset() 93 | } 94 | }) 95 | } 96 | -------------------------------------------------------------------------------- /buffer/pool.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package buffer 22 | 23 | import ( 24 | "go.uber.org/zap/internal/pool" 25 | ) 26 | 27 | // A Pool is a type-safe wrapper around a sync.Pool. 28 | type Pool struct { 29 | p *pool.Pool[*Buffer] 30 | } 31 | 32 | // NewPool constructs a new Pool. 33 | func NewPool() Pool { 34 | return Pool{ 35 | p: pool.New(func() *Buffer { 36 | return &Buffer{ 37 | bs: make([]byte, 0, _size), 38 | } 39 | }), 40 | } 41 | } 42 | 43 | // Get retrieves a Buffer from the pool, creating one if necessary. 44 | func (p Pool) Get() *Buffer { 45 | buf := p.p.Get() 46 | buf.Reset() 47 | buf.pool = p 48 | return buf 49 | } 50 | 51 | func (p Pool) put(buf *Buffer) { 52 | p.p.Put(buf) 53 | } 54 | -------------------------------------------------------------------------------- /buffer/pool_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package buffer 22 | 23 | import ( 24 | "sync" 25 | "testing" 26 | 27 | "github.com/stretchr/testify/assert" 28 | ) 29 | 30 | func TestBuffers(t *testing.T) { 31 | const dummyData = "dummy data" 32 | p := NewPool() 33 | 34 | var wg sync.WaitGroup 35 | for g := 0; g < 10; g++ { 36 | wg.Add(1) 37 | go func() { 38 | for i := 0; i < 100; i++ { 39 | buf := p.Get() 40 | assert.Zero(t, buf.Len(), "Expected truncated buffer") 41 | assert.NotZero(t, buf.Cap(), "Expected non-zero capacity") 42 | 43 | buf.AppendString(dummyData) 44 | assert.Equal(t, buf.Len(), len(dummyData), "Expected buffer to contain dummy data") 45 | 46 | buf.Free() 47 | } 48 | wg.Done() 49 | }() 50 | } 51 | wg.Wait() 52 | } 53 | -------------------------------------------------------------------------------- /checklicense.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | ERROR_COUNT=0 4 | while read -r file 5 | do 6 | case "$(head -1 "${file}")" in 7 | *"Copyright (c) "*" Uber Technologies, Inc.") 8 | # everything's cool 9 | ;; 10 | *) 11 | echo "$file is missing license header." 12 | (( ERROR_COUNT++ )) 13 | ;; 14 | esac 15 | done < <(git ls-files "*\.go") 16 | 17 | exit $ERROR_COUNT 18 | -------------------------------------------------------------------------------- /clock_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zap 22 | 23 | import ( 24 | "testing" 25 | "time" 26 | 27 | "github.com/stretchr/testify/assert" 28 | "github.com/stretchr/testify/require" 29 | "go.uber.org/zap/zaptest/observer" 30 | ) 31 | 32 | type constantClock time.Time 33 | 34 | func (c constantClock) Now() time.Time { return time.Time(c) } 35 | func (c constantClock) NewTicker(d time.Duration) *time.Ticker { 36 | return &time.Ticker{} 37 | } 38 | 39 | func TestWithClock(t *testing.T) { 40 | date := time.Date(2077, 1, 23, 10, 15, 13, 441, time.UTC) 41 | clock := constantClock(date) 42 | withLogger(t, DebugLevel, []Option{WithClock(clock)}, func(log *Logger, logs *observer.ObservedLogs) { 43 | log.Info("") 44 | require.Equal(t, 1, logs.Len(), "Expected only one log entry to be written.") 45 | assert.Equal(t, date, logs.All()[0].Time, "Unexpected entry time.") 46 | }) 47 | } 48 | -------------------------------------------------------------------------------- /common_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zap 22 | 23 | import ( 24 | "sync" 25 | "testing" 26 | 27 | "go.uber.org/zap/zapcore" 28 | "go.uber.org/zap/zaptest/observer" 29 | ) 30 | 31 | func opts(opts ...Option) []Option { 32 | return opts 33 | } 34 | 35 | // Here specifically to introduce an easily-identifiable filename for testing 36 | // stacktraces and caller skips. 37 | func withLogger(t testing.TB, e zapcore.LevelEnabler, opts []Option, f func(*Logger, *observer.ObservedLogs)) { 38 | fac, logs := observer.New(e) 39 | log := New(fac, opts...) 40 | f(log, logs) 41 | } 42 | 43 | func withSugar(t testing.TB, e zapcore.LevelEnabler, opts []Option, f func(*SugaredLogger, *observer.ObservedLogs)) { 44 | withLogger(t, e, opts, func(logger *Logger, logs *observer.ObservedLogs) { f(logger.Sugar(), logs) }) 45 | } 46 | 47 | func runConcurrently(goroutines, iterations int, wg *sync.WaitGroup, f func()) { 48 | wg.Add(goroutines) 49 | for g := 0; g < goroutines; g++ { 50 | go func() { 51 | defer wg.Done() 52 | for i := 0; i < iterations; i++ { 53 | f() 54 | } 55 | }() 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /encoder.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zap 22 | 23 | import ( 24 | "errors" 25 | "fmt" 26 | "sync" 27 | 28 | "go.uber.org/zap/zapcore" 29 | ) 30 | 31 | var ( 32 | errNoEncoderNameSpecified = errors.New("no encoder name specified") 33 | 34 | _encoderNameToConstructor = map[string]func(zapcore.EncoderConfig) (zapcore.Encoder, error){ 35 | "console": func(encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) { 36 | return zapcore.NewConsoleEncoder(encoderConfig), nil 37 | }, 38 | "json": func(encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) { 39 | return zapcore.NewJSONEncoder(encoderConfig), nil 40 | }, 41 | } 42 | _encoderMutex sync.RWMutex 43 | ) 44 | 45 | // RegisterEncoder registers an encoder constructor, which the Config struct 46 | // can then reference. By default, the "json" and "console" encoders are 47 | // registered. 48 | // 49 | // Attempting to register an encoder whose name is already taken returns an 50 | // error. 51 | func RegisterEncoder(name string, constructor func(zapcore.EncoderConfig) (zapcore.Encoder, error)) error { 52 | _encoderMutex.Lock() 53 | defer _encoderMutex.Unlock() 54 | if name == "" { 55 | return errNoEncoderNameSpecified 56 | } 57 | if _, ok := _encoderNameToConstructor[name]; ok { 58 | return fmt.Errorf("encoder already registered for name %q", name) 59 | } 60 | _encoderNameToConstructor[name] = constructor 61 | return nil 62 | } 63 | 64 | func newEncoder(name string, encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) { 65 | if encoderConfig.TimeKey != "" && encoderConfig.EncodeTime == nil { 66 | return nil, errors.New("missing EncodeTime in EncoderConfig") 67 | } 68 | 69 | _encoderMutex.RLock() 70 | defer _encoderMutex.RUnlock() 71 | if name == "" { 72 | return nil, errNoEncoderNameSpecified 73 | } 74 | constructor, ok := _encoderNameToConstructor[name] 75 | if !ok { 76 | return nil, fmt.Errorf("no encoder registered for name %q", name) 77 | } 78 | return constructor(encoderConfig) 79 | } 80 | -------------------------------------------------------------------------------- /encoder_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zap 22 | 23 | import ( 24 | "testing" 25 | 26 | "go.uber.org/zap/zapcore" 27 | 28 | "github.com/stretchr/testify/assert" 29 | ) 30 | 31 | func TestRegisterDefaultEncoders(t *testing.T) { 32 | testEncodersRegistered(t, "console", "json") 33 | } 34 | 35 | func TestRegisterEncoder(t *testing.T) { 36 | testEncoders(func() { 37 | assert.NoError(t, RegisterEncoder("foo", newNilEncoder), "expected to be able to register the encoder foo") 38 | testEncodersRegistered(t, "foo") 39 | }) 40 | } 41 | 42 | func TestDuplicateRegisterEncoder(t *testing.T) { 43 | testEncoders(func() { 44 | assert.NoError(t, RegisterEncoder("foo", newNilEncoder), "expected to be able to register the encoder foo") 45 | assert.Error(t, RegisterEncoder("foo", newNilEncoder), "expected an error when registering an encoder with the same name twice") 46 | }) 47 | } 48 | 49 | func TestRegisterEncoderNoName(t *testing.T) { 50 | assert.Equal(t, errNoEncoderNameSpecified, RegisterEncoder("", newNilEncoder), "expected an error when registering an encoder with no name") 51 | } 52 | 53 | func TestNewEncoder(t *testing.T) { 54 | testEncoders(func() { 55 | assert.NoError(t, RegisterEncoder("foo", newNilEncoder), "expected to be able to register the encoder foo") 56 | encoder, err := newEncoder("foo", zapcore.EncoderConfig{}) 57 | assert.NoError(t, err, "could not create an encoder for the registered name foo") 58 | assert.Nil(t, encoder, "the encoder from newNilEncoder is not nil") 59 | }) 60 | } 61 | 62 | func TestNewEncoderNotRegistered(t *testing.T) { 63 | _, err := newEncoder("foo", zapcore.EncoderConfig{}) 64 | assert.Error(t, err, "expected an error when trying to create an encoder of an unregistered name") 65 | } 66 | 67 | func TestNewEncoderNoName(t *testing.T) { 68 | _, err := newEncoder("", zapcore.EncoderConfig{}) 69 | assert.Equal(t, errNoEncoderNameSpecified, err, "expected an error when creating an encoder with no name") 70 | } 71 | 72 | func testEncoders(f func()) { 73 | existing := _encoderNameToConstructor 74 | _encoderNameToConstructor = make(map[string]func(zapcore.EncoderConfig) (zapcore.Encoder, error)) 75 | defer func() { _encoderNameToConstructor = existing }() 76 | f() 77 | } 78 | 79 | func testEncodersRegistered(t *testing.T, names ...string) { 80 | assert.Len(t, _encoderNameToConstructor, len(names), "the expected number of registered encoders does not match the actual number") 81 | for _, name := range names { 82 | assert.NotNil(t, _encoderNameToConstructor[name], "no encoder is registered for name %s", name) 83 | } 84 | } 85 | 86 | func newNilEncoder(_ zapcore.EncoderConfig) (zapcore.Encoder, error) { 87 | return nil, nil 88 | } 89 | -------------------------------------------------------------------------------- /error.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zap 22 | 23 | import ( 24 | "go.uber.org/zap/internal/pool" 25 | "go.uber.org/zap/zapcore" 26 | ) 27 | 28 | var _errArrayElemPool = pool.New(func() *errArrayElem { 29 | return &errArrayElem{} 30 | }) 31 | 32 | // Error is shorthand for the common idiom NamedError("error", err). 33 | func Error(err error) Field { 34 | return NamedError("error", err) 35 | } 36 | 37 | // NamedError constructs a field that lazily stores err.Error() under the 38 | // provided key. Errors which also implement fmt.Formatter (like those produced 39 | // by github.com/pkg/errors) will also have their verbose representation stored 40 | // under key+"Verbose". If passed a nil error, the field is a no-op. 41 | // 42 | // For the common case in which the key is simply "error", the Error function 43 | // is shorter and less repetitive. 44 | func NamedError(key string, err error) Field { 45 | if err == nil { 46 | return Skip() 47 | } 48 | return Field{Key: key, Type: zapcore.ErrorType, Interface: err} 49 | } 50 | 51 | type errArray []error 52 | 53 | func (errs errArray) MarshalLogArray(arr zapcore.ArrayEncoder) error { 54 | for i := range errs { 55 | if errs[i] == nil { 56 | continue 57 | } 58 | // To represent each error as an object with an "error" attribute and 59 | // potentially an "errorVerbose" attribute, we need to wrap it in a 60 | // type that implements LogObjectMarshaler. To prevent this from 61 | // allocating, pool the wrapper type. 62 | elem := _errArrayElemPool.Get() 63 | elem.error = errs[i] 64 | err := arr.AppendObject(elem) 65 | elem.error = nil 66 | _errArrayElemPool.Put(elem) 67 | if err != nil { 68 | return err 69 | } 70 | } 71 | return nil 72 | } 73 | 74 | type errArrayElem struct { 75 | error 76 | } 77 | 78 | func (e *errArrayElem) MarshalLogObject(enc zapcore.ObjectEncoder) error { 79 | // Re-use the error field's logic, which supports non-standard error types. 80 | Error(e.error).AddTo(enc) 81 | return nil 82 | } 83 | -------------------------------------------------------------------------------- /exp/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 5 | 6 | ## 0.3.0 - 22 Oct 2024 7 | 8 | Breaking changes: 9 | * [#1339][]: zapslog: Drop `HandlerOptions` in favor of `HandlerOption`, 10 | which uses the functional options pattern. 11 | * [#1339][]: zapslog: Rename `AddSource` option to `WithCaller`. 12 | 13 | Enhancements: 14 | * [#1339][]: zapslog: Record stack traces for error logs or higher. 15 | The new `AddStackAt` option changes this level. 16 | 17 | Bug fixes: 18 | * [#1344][], [#1408][]: zapslog: Comply fully with `slog.Handler` contract. 19 | This includes ignoring empty `Attr`s, inlining `Group`s with empty names, 20 | and omitting groups with no attributes. 21 | 22 | [#1344]: https://github.com/uber-go/zap/pull/1344 23 | [#1339]: https://github.com/uber-go/zap/pull/1339 24 | [#1408]: https://github.com/uber-go/zap/pull/1408 25 | 26 | Thanks to @zekth and @arukiidou for their contributions to this release. 27 | 28 | ## 0.2.0 - 9 Sep 2023 29 | 30 | Breaking changes: 31 | * [#1315][]: zapslog: Drop HandlerOptions.New in favor of just the NewHandler constructor. 32 | * [#1320][], [#1338][]: zapslog: Drop support for golang.org/x/exp/slog in favor of log/slog released in Go 1.21. 33 | 34 | [#1315]: https://github.com/uber-go/zap/pull/1315 35 | [#1320]: https://github.com/uber-go/zap/pull/1320 36 | [#1338]: https://github.com/uber-go/zap/pull/1338 37 | 38 | ## 0.1.0 - 1 Aug 2023 39 | 40 | Initial release of go.uber.org/zap/exp. 41 | This submodule contains experimental features for Zap. 42 | Features incubated here may be promoted to the root Zap module, 43 | but it's not guaranteed. 44 | -------------------------------------------------------------------------------- /exp/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016-2024 Uber Technologies, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /exp/go.mod: -------------------------------------------------------------------------------- 1 | module go.uber.org/zap/exp 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/stretchr/testify v1.8.1 7 | go.uber.org/zap v1.26.0 8 | ) 9 | 10 | require ( 11 | github.com/davecgh/go-spew v1.1.1 // indirect 12 | github.com/pmezard/go-difflib v1.0.0 // indirect 13 | go.uber.org/multierr v1.10.0 // indirect 14 | gopkg.in/yaml.v3 v3.0.1 // indirect 15 | ) 16 | 17 | replace go.uber.org/zap => ../ 18 | -------------------------------------------------------------------------------- /exp/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 5 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 6 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 7 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 8 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 9 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 10 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 11 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 12 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 13 | go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= 14 | go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= 15 | go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 16 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 17 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 18 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 19 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 20 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 21 | -------------------------------------------------------------------------------- /exp/zapfield/zapfield.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2023 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // Package zapfield provides experimental zap.Field helpers whose APIs may be unstable. 22 | package zapfield 23 | 24 | import ( 25 | "go.uber.org/zap" 26 | "go.uber.org/zap/zapcore" 27 | ) 28 | 29 | // Str constructs a field with the given string-like key and value. 30 | func Str[K ~string, V ~string](k K, v V) zap.Field { 31 | return zap.String(string(k), string(v)) 32 | } 33 | 34 | type stringArray[T ~string] []T 35 | 36 | func (a stringArray[T]) MarshalLogArray(enc zapcore.ArrayEncoder) error { 37 | for i := range a { 38 | enc.AppendString(string(a[i])) 39 | } 40 | return nil 41 | } 42 | 43 | // Strs constructs a field that carries a slice of string-like values. 44 | func Strs[K ~string, V ~[]S, S ~string](k K, v V) zap.Field { 45 | return zap.Array(string(k), stringArray[S](v)) 46 | } 47 | -------------------------------------------------------------------------------- /exp/zapfield/zapfield_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2023 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zapfield 22 | 23 | import ( 24 | "sync" 25 | "testing" 26 | 27 | "github.com/stretchr/testify/assert" 28 | "go.uber.org/zap" 29 | "go.uber.org/zap/zapcore" 30 | ) 31 | 32 | type ( 33 | MyKey string 34 | MyValue string 35 | MyValues []MyValue 36 | ) 37 | 38 | func TestFieldConstructors(t *testing.T) { 39 | var ( 40 | key = MyKey("test key") 41 | value = MyValue("test value") 42 | values = []MyValue{ 43 | MyValue("test value 1"), 44 | MyValue("test value 2"), 45 | } 46 | ) 47 | 48 | tests := []struct { 49 | name string 50 | expect zap.Field 51 | field zap.Field 52 | }{ 53 | {"Str", zap.Field{Type: zapcore.StringType, Key: "test key", String: "test value"}, Str(key, value)}, 54 | {"Strs", zap.Array("test key", stringArray[MyValue]{"test value 1", "test value 2"}), Strs(key, values)}, 55 | } 56 | 57 | for _, tt := range tests { 58 | if !assert.Equal(t, tt.expect, tt.field, "Unexpected output from convenience field constructor %s.", tt.name) { 59 | t.Logf("type expected: %T\nGot: %T", tt.expect.Interface, tt.field.Interface) 60 | } 61 | assertCanBeReused(t, tt.field) 62 | } 63 | } 64 | 65 | func assertCanBeReused(t testing.TB, field zap.Field) { 66 | var wg sync.WaitGroup 67 | 68 | for i := 0; i < 100; i++ { 69 | enc := zapcore.NewMapObjectEncoder() 70 | 71 | // Ensure using the field in multiple encoders in separate goroutines 72 | // does not cause any races or panics. 73 | wg.Add(1) 74 | go func() { 75 | defer wg.Done() 76 | assert.NotPanics(t, func() { 77 | field.AddTo(enc) 78 | }, "Reusing a field should not cause issues") 79 | }() 80 | } 81 | 82 | wg.Wait() 83 | } 84 | -------------------------------------------------------------------------------- /exp/zapslog/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // Package zapslog provides an implementation of slog.Handler which writes to 22 | // the supplied zapcore.Core. 23 | // 24 | // Use of this package requires at least Go 1.21. 25 | package zapslog // import "go.uber.org/zap/exp/zapslog" 26 | -------------------------------------------------------------------------------- /exp/zapslog/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | //go:build go1.21 22 | 23 | package zapslog_test 24 | 25 | import ( 26 | "context" 27 | "log/slog" 28 | "net" 29 | "time" 30 | 31 | "go.uber.org/zap" 32 | "go.uber.org/zap/exp/zapslog" 33 | ) 34 | 35 | type Password string 36 | 37 | func (p Password) LogValue() slog.Value { 38 | return slog.StringValue("REDACTED") 39 | } 40 | 41 | func Example_slog() { 42 | logger := zap.NewExample(zap.IncreaseLevel(zap.InfoLevel)) 43 | defer logger.Sync() 44 | 45 | sl := slog.New(zapslog.NewHandler(logger.Core())) 46 | ctx := context.Background() 47 | 48 | sl.Info("user", "name", "Al", "secret", Password("secret")) 49 | sl.Error("oops", "err", net.ErrClosed, "status", 500) 50 | sl.LogAttrs( 51 | ctx, 52 | slog.LevelError, 53 | "oops", 54 | slog.Any("err", net.ErrClosed), 55 | slog.Int("status", 500), 56 | ) 57 | sl.Info("message", 58 | slog.Group("group", 59 | slog.Float64("pi", 3.14), 60 | slog.Duration("1min", time.Minute), 61 | ), 62 | ) 63 | sl.WithGroup("s").LogAttrs( 64 | ctx, 65 | slog.LevelWarn, 66 | "warn msg", // message 67 | slog.Uint64("u", 1), 68 | slog.Any("m", map[string]any{ 69 | "foo": "bar", 70 | })) 71 | sl.LogAttrs(ctx, slog.LevelDebug, "not show up") 72 | 73 | // Output: 74 | // {"level":"info","msg":"user","name":"Al","secret":"REDACTED"} 75 | // {"level":"error","msg":"oops","err":"use of closed network connection","status":500} 76 | // {"level":"error","msg":"oops","err":"use of closed network connection","status":500} 77 | // {"level":"info","msg":"message","group":{"pi":3.14,"1min":"1m0s"}} 78 | // {"level":"warn","msg":"warn msg","s":{"u":1,"m":{"foo":"bar"}}} 79 | } 80 | -------------------------------------------------------------------------------- /exp/zapslog/options.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | //go:build go1.21 22 | 23 | package zapslog 24 | 25 | import "log/slog" 26 | 27 | // A HandlerOption configures a slog Handler. 28 | type HandlerOption interface { 29 | apply(*Handler) 30 | } 31 | 32 | // handlerOptionFunc wraps a func so it satisfies the Option interface. 33 | type handlerOptionFunc func(*Handler) 34 | 35 | func (f handlerOptionFunc) apply(handler *Handler) { 36 | f(handler) 37 | } 38 | 39 | // WithName configures the Logger to annotate each message with the logger name. 40 | func WithName(name string) HandlerOption { 41 | return handlerOptionFunc(func(h *Handler) { 42 | h.name = name 43 | }) 44 | } 45 | 46 | // WithCaller configures the Logger to include the filename and line number 47 | // of the caller in log messages--if available. 48 | func WithCaller(enabled bool) HandlerOption { 49 | return handlerOptionFunc(func(handler *Handler) { 50 | handler.addCaller = enabled 51 | }) 52 | } 53 | 54 | // WithCallerSkip increases the number of callers skipped by caller annotation 55 | // (as enabled by the [WithCaller] option). 56 | // 57 | // When building wrappers around the Logger, 58 | // supplying this Option prevents Zap from always reporting 59 | // the wrapper code as the caller. 60 | func WithCallerSkip(skip int) HandlerOption { 61 | return handlerOptionFunc(func(log *Handler) { 62 | log.callerSkip += skip 63 | }) 64 | } 65 | 66 | // AddStacktraceAt configures the Logger to record a stack trace 67 | // for all messages at or above a given level. 68 | func AddStacktraceAt(lvl slog.Level) HandlerOption { 69 | return handlerOptionFunc(func(log *Handler) { 70 | log.addStackAt = lvl 71 | }) 72 | } 73 | -------------------------------------------------------------------------------- /flag.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zap 22 | 23 | import ( 24 | "flag" 25 | 26 | "go.uber.org/zap/zapcore" 27 | ) 28 | 29 | // LevelFlag uses the standard library's flag.Var to declare a global flag 30 | // with the specified name, default, and usage guidance. The returned value is 31 | // a pointer to the value of the flag. 32 | // 33 | // If you don't want to use the flag package's global state, you can use any 34 | // non-nil *Level as a flag.Value with your own *flag.FlagSet. 35 | func LevelFlag(name string, defaultLevel zapcore.Level, usage string) *zapcore.Level { 36 | lvl := defaultLevel 37 | flag.Var(&lvl, name, usage) 38 | return &lvl 39 | } 40 | -------------------------------------------------------------------------------- /flag_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zap 22 | 23 | import ( 24 | "flag" 25 | "io" 26 | "testing" 27 | 28 | "go.uber.org/zap/zapcore" 29 | 30 | "github.com/stretchr/testify/assert" 31 | ) 32 | 33 | type flagTestCase struct { 34 | args []string 35 | wantLevel zapcore.Level 36 | wantErr bool 37 | } 38 | 39 | func (tc flagTestCase) runImplicitSet(t testing.TB) { 40 | origCommandLine := flag.CommandLine 41 | flag.CommandLine = flag.NewFlagSet("test", flag.ContinueOnError) 42 | flag.CommandLine.SetOutput(io.Discard) 43 | defer func() { flag.CommandLine = origCommandLine }() 44 | 45 | level := LevelFlag("level", InfoLevel, "") 46 | tc.run(t, flag.CommandLine, level) 47 | } 48 | 49 | func (tc flagTestCase) runExplicitSet(t testing.TB) { 50 | var lvl zapcore.Level 51 | set := flag.NewFlagSet("test", flag.ContinueOnError) 52 | set.SetOutput(io.Discard) 53 | set.Var(&lvl, "level", "minimum enabled logging level") 54 | tc.run(t, set, &lvl) 55 | } 56 | 57 | func (tc flagTestCase) run(t testing.TB, set *flag.FlagSet, actual *zapcore.Level) { 58 | err := set.Parse(tc.args) 59 | if tc.wantErr { 60 | assert.Error(t, err, "Parse(%v) should fail.", tc.args) 61 | return 62 | } 63 | if assert.NoError(t, err, "Parse(%v) should succeed.", tc.args) { 64 | assert.Equal(t, tc.wantLevel, *actual, "Level mismatch.") 65 | } 66 | } 67 | 68 | func TestLevelFlag(t *testing.T) { 69 | tests := []flagTestCase{ 70 | { 71 | args: nil, 72 | wantLevel: zapcore.InfoLevel, 73 | }, 74 | { 75 | args: []string{"--level", "unknown"}, 76 | wantErr: true, 77 | }, 78 | { 79 | args: []string{"--level", "error"}, 80 | wantLevel: zapcore.ErrorLevel, 81 | }, 82 | } 83 | 84 | for _, tt := range tests { 85 | tt.runExplicitSet(t) 86 | tt.runImplicitSet(t) 87 | } 88 | } 89 | 90 | func TestLevelFlagsAreIndependent(t *testing.T) { 91 | origCommandLine := flag.CommandLine 92 | flag.CommandLine = flag.NewFlagSet("test", flag.ContinueOnError) 93 | flag.CommandLine.SetOutput(io.Discard) 94 | defer func() { flag.CommandLine = origCommandLine }() 95 | 96 | // Make sure that these two flags are independent. 97 | fileLevel := LevelFlag("file-level", InfoLevel, "") 98 | consoleLevel := LevelFlag("console-level", InfoLevel, "") 99 | 100 | assert.NoError(t, flag.CommandLine.Parse([]string{"-file-level", "debug"}), "Unexpected flag-parsing error.") 101 | assert.Equal(t, InfoLevel, *consoleLevel, "Expected file logging level to remain unchanged.") 102 | assert.Equal(t, DebugLevel, *fileLevel, "Expected console logging level to have changed.") 103 | } 104 | -------------------------------------------------------------------------------- /glide.yaml: -------------------------------------------------------------------------------- 1 | package: go.uber.org/zap 2 | license: MIT 3 | import: 4 | - package: go.uber.org/atomic 5 | version: ^1 6 | - package: go.uber.org/multierr 7 | version: ^1 8 | testImport: 9 | - package: github.com/satori/go.uuid 10 | - package: github.com/sirupsen/logrus 11 | - package: github.com/apex/log 12 | subpackages: 13 | - handlers/json 14 | - package: github.com/go-kit/kit 15 | subpackages: 16 | - log 17 | - package: github.com/stretchr/testify 18 | subpackages: 19 | - assert 20 | - require 21 | - package: gopkg.in/inconshreveable/log15.v2 22 | - package: github.com/mattn/goveralls 23 | - package: github.com/pborman/uuid 24 | - package: github.com/pkg/errors 25 | - package: github.com/rs/zerolog 26 | - package: golang.org/x/tools 27 | subpackages: 28 | - cover 29 | - package: golang.org/x/lint 30 | subpackages: 31 | - golint 32 | - package: github.com/axw/gocov 33 | subpackages: 34 | - gocov 35 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module go.uber.org/zap 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/stretchr/testify v1.8.1 7 | go.uber.org/goleak v1.3.0 8 | go.uber.org/multierr v1.10.0 9 | gopkg.in/yaml.v3 v3.0.1 10 | ) 11 | 12 | require ( 13 | github.com/davecgh/go-spew v1.1.1 // indirect 14 | github.com/kr/text v0.2.0 // indirect 15 | github.com/pmezard/go-difflib v1.0.0 // indirect 16 | ) 17 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 6 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 7 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 8 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 9 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 10 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 11 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 12 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 13 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 14 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 15 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 16 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 17 | go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= 18 | go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 19 | go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= 20 | go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 21 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 22 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 23 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 24 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 25 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 26 | -------------------------------------------------------------------------------- /increase_level_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zap 22 | 23 | import ( 24 | "bytes" 25 | "testing" 26 | 27 | "github.com/stretchr/testify/assert" 28 | "go.uber.org/zap/zapcore" 29 | "go.uber.org/zap/zaptest/observer" 30 | ) 31 | 32 | func newLoggedEntry(level zapcore.Level, msg string, fields ...zapcore.Field) observer.LoggedEntry { 33 | if len(fields) == 0 { 34 | fields = []zapcore.Field{} 35 | } 36 | return observer.LoggedEntry{ 37 | Entry: zapcore.Entry{Level: level, Message: msg}, 38 | Context: fields, 39 | } 40 | } 41 | 42 | func TestIncreaseLevelTryDecrease(t *testing.T) { 43 | errorOut := &bytes.Buffer{} 44 | opts := []Option{ 45 | ErrorOutput(zapcore.AddSync(errorOut)), 46 | } 47 | withLogger(t, WarnLevel, opts, func(logger *Logger, logs *observer.ObservedLogs) { 48 | logger.Warn("original warn log") 49 | 50 | debugLogger := logger.WithOptions(IncreaseLevel(DebugLevel)) 51 | debugLogger.Debug("ignored debug log") 52 | debugLogger.Warn("increase level warn log") 53 | debugLogger.Error("increase level error log") 54 | 55 | assert.Equal(t, []observer.LoggedEntry{ 56 | newLoggedEntry(WarnLevel, "original warn log"), 57 | newLoggedEntry(WarnLevel, "increase level warn log"), 58 | newLoggedEntry(ErrorLevel, "increase level error log"), 59 | }, logs.AllUntimed(), "unexpected logs") 60 | assert.Equal(t, 61 | "failed to IncreaseLevel: invalid increase level, as level \"info\" is allowed by increased level, but not by existing core\n", 62 | errorOut.String(), 63 | "unexpected error output", 64 | ) 65 | }) 66 | } 67 | 68 | func TestIncreaseLevel(t *testing.T) { 69 | errorOut := &bytes.Buffer{} 70 | opts := []Option{ 71 | ErrorOutput(zapcore.AddSync(errorOut)), 72 | } 73 | withLogger(t, WarnLevel, opts, func(logger *Logger, logs *observer.ObservedLogs) { 74 | logger.Warn("original warn log") 75 | 76 | errorLogger := logger.WithOptions(IncreaseLevel(ErrorLevel)) 77 | errorLogger.Debug("ignored debug log") 78 | errorLogger.Warn("ignored warn log") 79 | errorLogger.Error("increase level error log") 80 | 81 | withFields := errorLogger.With(String("k", "v")) 82 | withFields.Debug("ignored debug log with fields") 83 | withFields.Warn("ignored warn log with fields") 84 | withFields.Error("increase level error log with fields") 85 | 86 | assert.Equal(t, []observer.LoggedEntry{ 87 | newLoggedEntry(WarnLevel, "original warn log"), 88 | newLoggedEntry(ErrorLevel, "increase level error log"), 89 | newLoggedEntry(ErrorLevel, "increase level error log with fields", String("k", "v")), 90 | }, logs.AllUntimed(), "unexpected logs") 91 | 92 | assert.Empty(t, errorOut.String(), "expect no error output") 93 | }) 94 | } 95 | -------------------------------------------------------------------------------- /internal/bufferpool/bufferpool.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // Package bufferpool houses zap's shared internal buffer pool. Third-party 22 | // packages can recreate the same functionality with buffers.NewPool. 23 | package bufferpool 24 | 25 | import "go.uber.org/zap/buffer" 26 | 27 | var ( 28 | _pool = buffer.NewPool() 29 | // Get retrieves a buffer from the pool, creating one if necessary. 30 | Get = _pool.Get 31 | ) 32 | -------------------------------------------------------------------------------- /internal/color/color.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // Package color adds coloring functionality for TTY output. 22 | package color 23 | 24 | import "fmt" 25 | 26 | // Foreground colors. 27 | const ( 28 | Black Color = iota + 30 29 | Red 30 | Green 31 | Yellow 32 | Blue 33 | Magenta 34 | Cyan 35 | White 36 | ) 37 | 38 | // Color represents a text color. 39 | type Color uint8 40 | 41 | // Add adds the coloring to the given string. 42 | func (c Color) Add(s string) string { 43 | return fmt.Sprintf("\x1b[%dm%s\x1b[0m", uint8(c), s) 44 | } 45 | -------------------------------------------------------------------------------- /internal/color/color_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package color 22 | 23 | import ( 24 | "testing" 25 | 26 | "github.com/stretchr/testify/assert" 27 | ) 28 | 29 | func TestColorFormatting(t *testing.T) { 30 | assert.Equal( 31 | t, 32 | "\x1b[31mfoo\x1b[0m", 33 | Red.Add("foo"), 34 | "Unexpected colored output.", 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /internal/exit/exit.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // Package exit provides stubs so that unit tests can exercise code that calls 22 | // os.Exit(1). 23 | package exit 24 | 25 | import "os" 26 | 27 | var _exit = os.Exit 28 | 29 | // With terminates the process by calling os.Exit(code). If the package is 30 | // stubbed, it instead records a call in the testing spy. 31 | func With(code int) { 32 | _exit(code) 33 | } 34 | 35 | // A StubbedExit is a testing fake for os.Exit. 36 | type StubbedExit struct { 37 | Exited bool 38 | Code int 39 | prev func(code int) 40 | } 41 | 42 | // Stub substitutes a fake for the call to os.Exit(1). 43 | func Stub() *StubbedExit { 44 | s := &StubbedExit{prev: _exit} 45 | _exit = s.exit 46 | return s 47 | } 48 | 49 | // WithStub runs the supplied function with Exit stubbed. It returns the stub 50 | // used, so that users can test whether the process would have crashed. 51 | func WithStub(f func()) *StubbedExit { 52 | s := Stub() 53 | defer s.Unstub() 54 | f() 55 | return s 56 | } 57 | 58 | // Unstub restores the previous exit function. 59 | func (se *StubbedExit) Unstub() { 60 | _exit = se.prev 61 | } 62 | 63 | func (se *StubbedExit) exit(code int) { 64 | se.Exited = true 65 | se.Code = code 66 | } 67 | -------------------------------------------------------------------------------- /internal/exit/exit_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package exit_test 22 | 23 | import ( 24 | "testing" 25 | 26 | "github.com/stretchr/testify/assert" 27 | "go.uber.org/zap/internal/exit" 28 | ) 29 | 30 | func TestStub(t *testing.T) { 31 | type want struct { 32 | exit bool 33 | code int 34 | } 35 | tests := []struct { 36 | f func() 37 | want want 38 | }{ 39 | {func() { exit.With(42) }, want{exit: true, code: 42}}, 40 | {func() {}, want{}}, 41 | } 42 | 43 | for _, tt := range tests { 44 | s := exit.WithStub(tt.f) 45 | assert.Equal(t, tt.want.exit, s.Exited, "Stub captured unexpected exit value.") 46 | assert.Equal(t, tt.want.code, s.Code, "Stub captured unexpected exit value.") 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /internal/level_enabler.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // Package internal and its subpackages hold types and functionality 22 | // that are not part of Zap's public API. 23 | package internal 24 | 25 | import "go.uber.org/zap/zapcore" 26 | 27 | // LeveledEnabler is an interface satisfied by LevelEnablers that are able to 28 | // report their own level. 29 | // 30 | // This interface is defined to use more conveniently in tests and non-zapcore 31 | // packages. 32 | // This cannot be imported from zapcore because of the cyclic dependency. 33 | type LeveledEnabler interface { 34 | zapcore.LevelEnabler 35 | 36 | Level() zapcore.Level 37 | } 38 | -------------------------------------------------------------------------------- /internal/pool/pool.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // Package pool provides internal pool utilities. 22 | package pool 23 | 24 | import ( 25 | "sync" 26 | ) 27 | 28 | // A Pool is a generic wrapper around [sync.Pool] to provide strongly-typed 29 | // object pooling. 30 | // 31 | // Note that SA6002 (ref: https://staticcheck.io/docs/checks/#SA6002) will 32 | // not be detected, so all internal pool use must take care to only store 33 | // pointer types. 34 | type Pool[T any] struct { 35 | pool sync.Pool 36 | } 37 | 38 | // New returns a new [Pool] for T, and will use fn to construct new Ts when 39 | // the pool is empty. 40 | func New[T any](fn func() T) *Pool[T] { 41 | return &Pool[T]{ 42 | pool: sync.Pool{ 43 | New: func() any { 44 | return fn() 45 | }, 46 | }, 47 | } 48 | } 49 | 50 | // Get gets a T from the pool, or creates a new one if the pool is empty. 51 | func (p *Pool[T]) Get() T { 52 | return p.pool.Get().(T) 53 | } 54 | 55 | // Put returns x into the pool. 56 | func (p *Pool[T]) Put(x T) { 57 | p.pool.Put(x) 58 | } 59 | -------------------------------------------------------------------------------- /internal/pool/pool_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package pool_test 22 | 23 | import ( 24 | "runtime/debug" 25 | "sync" 26 | "testing" 27 | 28 | "github.com/stretchr/testify/require" 29 | "go.uber.org/zap/internal/pool" 30 | ) 31 | 32 | type pooledValue[T any] struct { 33 | value T 34 | } 35 | 36 | func TestNew(t *testing.T) { 37 | // Disable GC to avoid the victim cache during the test. 38 | defer debug.SetGCPercent(debug.SetGCPercent(-1)) 39 | 40 | p := pool.New(func() *pooledValue[string] { 41 | return &pooledValue[string]{ 42 | value: "new", 43 | } 44 | }) 45 | 46 | // Probabilistically, 75% of sync.Pool.Put calls will succeed when -race 47 | // is enabled (see ref below); attempt to make this quasi-deterministic by 48 | // brute force (i.e., put significantly more objects in the pool than we 49 | // will need for the test) in order to avoid testing without race enabled. 50 | // 51 | // ref: https://cs.opensource.google/go/go/+/refs/tags/go1.20.2:src/sync/pool.go;l=100-103 52 | for i := 0; i < 1_000; i++ { 53 | p.Put(&pooledValue[string]{ 54 | value: t.Name(), 55 | }) 56 | } 57 | 58 | // Ensure that we always get the expected value. Note that this must only 59 | // run a fraction of the number of times that Put is called above. 60 | for i := 0; i < 10; i++ { 61 | func() { 62 | x := p.Get() 63 | defer p.Put(x) 64 | require.Equal(t, t.Name(), x.value) 65 | }() 66 | } 67 | 68 | // Depool all objects that might be in the pool to ensure that it's empty. 69 | for i := 0; i < 1_000; i++ { 70 | p.Get() 71 | } 72 | 73 | // Now that the pool is empty, it should use the value specified in the 74 | // underlying sync.Pool.New func. 75 | require.Equal(t, "new", p.Get().value) 76 | } 77 | 78 | func TestNew_Race(t *testing.T) { 79 | p := pool.New(func() *pooledValue[int] { 80 | return &pooledValue[int]{ 81 | value: -1, 82 | } 83 | }) 84 | 85 | var wg sync.WaitGroup 86 | defer wg.Wait() 87 | 88 | // Run a number of goroutines that read and write pool object fields to 89 | // tease out races. 90 | for i := 0; i < 1_000; i++ { 91 | i := i 92 | 93 | wg.Add(1) 94 | go func() { 95 | defer wg.Done() 96 | 97 | x := p.Get() 98 | defer p.Put(x) 99 | 100 | // Must both read and write the field. 101 | if n := x.value; n >= -1 { 102 | x.value = i 103 | } 104 | }() 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /internal/stacktrace/stack_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package stacktrace 22 | 23 | import ( 24 | "bytes" 25 | "strings" 26 | "testing" 27 | 28 | "github.com/stretchr/testify/assert" 29 | "github.com/stretchr/testify/require" 30 | ) 31 | 32 | func TestTake(t *testing.T) { 33 | trace := Take(0) 34 | lines := strings.Split(trace, "\n") 35 | require.NotEmpty(t, lines, "Expected stacktrace to have at least one frame.") 36 | assert.Contains( 37 | t, 38 | lines[0], 39 | "go.uber.org/zap/internal/stacktrace.TestTake", 40 | "Expected stacktrace to start with the test.", 41 | ) 42 | } 43 | 44 | func TestTakeWithSkip(t *testing.T) { 45 | trace := Take(1) 46 | lines := strings.Split(trace, "\n") 47 | require.NotEmpty(t, lines, "Expected stacktrace to have at least one frame.") 48 | assert.Contains( 49 | t, 50 | lines[0], 51 | "testing.", 52 | "Expected stacktrace to start with the test runner (skipping our own frame).", 53 | ) 54 | } 55 | 56 | func TestTakeWithSkipInnerFunc(t *testing.T) { 57 | var trace string 58 | func() { 59 | trace = Take(2) 60 | }() 61 | lines := strings.Split(trace, "\n") 62 | require.NotEmpty(t, lines, "Expected stacktrace to have at least one frame.") 63 | assert.Contains( 64 | t, 65 | lines[0], 66 | "testing.", 67 | "Expected stacktrace to start with the test function (skipping the test function).", 68 | ) 69 | } 70 | 71 | func TestTakeDeepStack(t *testing.T) { 72 | const ( 73 | N = 500 74 | withStackDepthName = "go.uber.org/zap/internal/stacktrace.withStackDepth" 75 | ) 76 | withStackDepth(N, func() { 77 | trace := Take(0) 78 | for found := 0; found < N; found++ { 79 | i := strings.Index(trace, withStackDepthName) 80 | if i < 0 { 81 | t.Fatalf(`expected %v occurrences of %q, found %d`, 82 | N, withStackDepthName, found) 83 | } 84 | trace = trace[i+len(withStackDepthName):] 85 | } 86 | }) 87 | } 88 | 89 | func BenchmarkTake(b *testing.B) { 90 | for i := 0; i < b.N; i++ { 91 | Take(0) 92 | } 93 | } 94 | 95 | func withStackDepth(depth int, f func()) { 96 | var recurse func(rune) rune 97 | recurse = func(r rune) rune { 98 | if r > 0 { 99 | bytes.Map(recurse, []byte(string([]rune{r - 1}))) 100 | } else { 101 | f() 102 | } 103 | return 0 104 | } 105 | recurse(rune(depth)) 106 | } 107 | -------------------------------------------------------------------------------- /internal/ztest/clock_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package ztest 22 | 23 | import ( 24 | "sync/atomic" 25 | "testing" 26 | "time" 27 | 28 | "github.com/stretchr/testify/assert" 29 | ) 30 | 31 | func TestMockClock_NewTicker(t *testing.T) { 32 | var n atomic.Int32 33 | clock := NewMockClock() 34 | 35 | done := make(chan struct{}) 36 | defer func() { <-done }() // wait for end 37 | 38 | quit := make(chan struct{}) 39 | // Create a channel to increment every microsecond. 40 | go func(ticker *time.Ticker) { 41 | defer close(done) 42 | for { 43 | select { 44 | case <-quit: 45 | ticker.Stop() 46 | return 47 | case <-ticker.C: 48 | n.Add(1) 49 | } 50 | } 51 | }(clock.NewTicker(time.Microsecond)) 52 | 53 | // Move clock forward. 54 | clock.Add(2 * time.Microsecond) 55 | assert.Equal(t, int32(2), n.Load()) 56 | close(quit) 57 | } 58 | 59 | func TestMockClock_NewTicker_slowConsumer(t *testing.T) { 60 | clock := NewMockClock() 61 | 62 | ticker := clock.NewTicker(time.Microsecond) 63 | defer ticker.Stop() 64 | 65 | // Two ticks, only one consumed. 66 | clock.Add(2 * time.Microsecond) 67 | <-ticker.C 68 | 69 | select { 70 | case <-ticker.C: 71 | t.Fatal("unexpected tick") 72 | default: 73 | // ok 74 | } 75 | } 76 | 77 | func TestMockClock_Add_negative(t *testing.T) { 78 | clock := NewMockClock() 79 | assert.Panics(t, func() { clock.Add(-1) }) 80 | } 81 | -------------------------------------------------------------------------------- /internal/ztest/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // Package ztest provides low-level helpers for testing log output. These 22 | // utilities are helpful in zap's own unit tests, but any assertions using 23 | // them are strongly coupled to a single encoding. 24 | package ztest // import "go.uber.org/zap/internal/ztest" 25 | -------------------------------------------------------------------------------- /internal/ztest/timeout.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package ztest 22 | 23 | import ( 24 | "log" 25 | "os" 26 | "strconv" 27 | "time" 28 | ) 29 | 30 | var _timeoutScale = 1.0 31 | 32 | // Timeout scales the provided duration by $TEST_TIMEOUT_SCALE. 33 | func Timeout(base time.Duration) time.Duration { 34 | return time.Duration(float64(base) * _timeoutScale) 35 | } 36 | 37 | // Sleep scales the sleep duration by $TEST_TIMEOUT_SCALE. 38 | func Sleep(base time.Duration) { 39 | time.Sleep(Timeout(base)) 40 | } 41 | 42 | // Initialize checks the environment and alters the timeout scale accordingly. 43 | // It returns a function to undo the scaling. 44 | func Initialize(factor string) func() { 45 | fv, err := strconv.ParseFloat(factor, 64) 46 | if err != nil { 47 | panic(err) 48 | } 49 | original := _timeoutScale 50 | _timeoutScale = fv 51 | return func() { _timeoutScale = original } 52 | } 53 | 54 | func init() { 55 | if v := os.Getenv("TEST_TIMEOUT_SCALE"); v != "" { 56 | Initialize(v) 57 | log.Printf("Scaling timeouts by %vx.\n", _timeoutScale) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /internal/ztest/writer.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package ztest 22 | 23 | import ( 24 | "bytes" 25 | "errors" 26 | "io" 27 | "strings" 28 | ) 29 | 30 | // A Syncer is a spy for the Sync portion of zapcore.WriteSyncer. 31 | type Syncer struct { 32 | err error 33 | called bool 34 | } 35 | 36 | // SetError sets the error that the Sync method will return. 37 | func (s *Syncer) SetError(err error) { 38 | s.err = err 39 | } 40 | 41 | // Sync records that it was called, then returns the user-supplied error (if 42 | // any). 43 | func (s *Syncer) Sync() error { 44 | s.called = true 45 | return s.err 46 | } 47 | 48 | // Called reports whether the Sync method was called. 49 | func (s *Syncer) Called() bool { 50 | return s.called 51 | } 52 | 53 | // A Discarder sends all writes to io.Discard. 54 | type Discarder struct{ Syncer } 55 | 56 | // Write implements io.Writer. 57 | func (d *Discarder) Write(b []byte) (int, error) { 58 | return io.Discard.Write(b) 59 | } 60 | 61 | // FailWriter is a WriteSyncer that always returns an error on writes. 62 | type FailWriter struct{ Syncer } 63 | 64 | // Write implements io.Writer. 65 | func (w FailWriter) Write(b []byte) (int, error) { 66 | return len(b), errors.New("failed") 67 | } 68 | 69 | // ShortWriter is a WriteSyncer whose write method never fails, but 70 | // nevertheless fails to the last byte of the input. 71 | type ShortWriter struct{ Syncer } 72 | 73 | // Write implements io.Writer. 74 | func (w ShortWriter) Write(b []byte) (int, error) { 75 | return len(b) - 1, nil 76 | } 77 | 78 | // Buffer is an implementation of zapcore.WriteSyncer that sends all writes to 79 | // a bytes.Buffer. It has convenience methods to split the accumulated buffer 80 | // on newlines. 81 | type Buffer struct { 82 | bytes.Buffer 83 | Syncer 84 | } 85 | 86 | // Lines returns the current buffer contents, split on newlines. 87 | func (b *Buffer) Lines() []string { 88 | output := strings.Split(b.String(), "\n") 89 | return output[:len(output)-1] 90 | } 91 | 92 | // Stripped returns the current buffer contents with the last trailing newline 93 | // stripped. 94 | func (b *Buffer) Stripped() string { 95 | return strings.TrimRight(b.String(), "\n") 96 | } 97 | -------------------------------------------------------------------------------- /leak_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zap 22 | 23 | import ( 24 | "testing" 25 | 26 | "go.uber.org/goleak" 27 | ) 28 | 29 | func TestMain(m *testing.M) { 30 | goleak.VerifyTestMain(m) 31 | } 32 | -------------------------------------------------------------------------------- /sink_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zap 22 | 23 | import ( 24 | "bytes" 25 | "io" 26 | "net/url" 27 | "strings" 28 | "testing" 29 | 30 | "github.com/stretchr/testify/assert" 31 | "github.com/stretchr/testify/require" 32 | 33 | "go.uber.org/zap/zapcore" 34 | ) 35 | 36 | func stubSinkRegistry(t testing.TB) *sinkRegistry { 37 | origSinkRegistry := _sinkRegistry 38 | t.Cleanup(func() { 39 | _sinkRegistry = origSinkRegistry 40 | }) 41 | 42 | r := newSinkRegistry() 43 | _sinkRegistry = r 44 | return r 45 | } 46 | 47 | func TestRegisterSink(t *testing.T) { 48 | stubSinkRegistry(t) 49 | 50 | const ( 51 | memScheme = "mem" 52 | nopScheme = "no-op.1234" 53 | ) 54 | var memCalls, nopCalls int 55 | 56 | buf := bytes.NewBuffer(nil) 57 | memFactory := func(u *url.URL) (Sink, error) { 58 | assert.Equal(t, u.Scheme, memScheme, "Scheme didn't match registration.") 59 | memCalls++ 60 | return nopCloserSink{zapcore.AddSync(buf)}, nil 61 | } 62 | nopFactory := func(u *url.URL) (Sink, error) { 63 | assert.Equal(t, u.Scheme, nopScheme, "Scheme didn't match registration.") 64 | nopCalls++ 65 | return nopCloserSink{zapcore.AddSync(io.Discard)}, nil 66 | } 67 | 68 | require.NoError(t, RegisterSink(strings.ToUpper(memScheme), memFactory), "Failed to register scheme %q.", memScheme) 69 | require.NoError(t, RegisterSink(nopScheme, nopFactory), "Failed to register scheme %q.", nopScheme) 70 | 71 | sink, closeSink, err := Open( 72 | memScheme+"://somewhere", 73 | nopScheme+"://somewhere-else", 74 | ) 75 | require.NoError(t, err, "Unexpected error opening URLs with registered schemes.") 76 | defer closeSink() 77 | 78 | assert.Equal(t, 1, memCalls, "Unexpected number of calls to memory factory.") 79 | assert.Equal(t, 1, nopCalls, "Unexpected number of calls to no-op factory.") 80 | 81 | _, err = sink.Write([]byte("foo")) 82 | assert.NoError(t, err, "Failed to write to combined WriteSyncer.") 83 | assert.Equal(t, "foo", buf.String(), "Unexpected buffer contents.") 84 | } 85 | 86 | func TestRegisterSinkErrors(t *testing.T) { 87 | nopFactory := func(_ *url.URL) (Sink, error) { 88 | return nopCloserSink{zapcore.AddSync(io.Discard)}, nil 89 | } 90 | tests := []struct { 91 | scheme string 92 | err string 93 | }{ 94 | {"", "empty string"}, 95 | {"FILE", "already registered"}, 96 | {"42", "not a valid scheme"}, 97 | {"http*", "not a valid scheme"}, 98 | } 99 | 100 | for _, tt := range tests { 101 | t.Run("scheme-"+tt.scheme, func(t *testing.T) { 102 | r := newSinkRegistry() 103 | err := r.RegisterSink(tt.scheme, nopFactory) 104 | assert.ErrorContains(t, err, tt.err) 105 | }) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /sink_windows_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | //go:build windows 22 | 23 | package zap 24 | 25 | import ( 26 | "os" 27 | "testing" 28 | 29 | "github.com/stretchr/testify/assert" 30 | ) 31 | 32 | func TestWindowsPaths(t *testing.T) { 33 | // See https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats 34 | tests := []struct { 35 | msg string 36 | path string 37 | }{ 38 | { 39 | msg: "local path with drive", 40 | path: `c:\log.json`, 41 | }, 42 | { 43 | msg: "local path with drive using forward slash", 44 | path: `c:/log.json`, 45 | }, 46 | { 47 | msg: "local path without drive", 48 | path: `\Temp\log.json`, 49 | }, 50 | { 51 | msg: "unc path", 52 | path: `\\Server2\Logs\log.json`, 53 | }, 54 | } 55 | 56 | for _, tt := range tests { 57 | t.Run(tt.msg, func(t *testing.T) { 58 | sr := newSinkRegistry() 59 | 60 | openFilename := "" 61 | sr.openFile = func(filename string, _ int, _ os.FileMode) (*os.File, error) { 62 | openFilename = filename 63 | return nil, assert.AnError 64 | } 65 | 66 | _, err := sr.newSink(tt.path) 67 | assert.Equal(t, assert.AnError, err, "expect stub error from OpenFile") 68 | assert.Equal(t, tt.path, openFilename, "unexpected path opened") 69 | }) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /time.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zap 22 | 23 | import "time" 24 | 25 | func timeToMillis(t time.Time) int64 { 26 | return t.UnixNano() / int64(time.Millisecond) 27 | } 28 | -------------------------------------------------------------------------------- /time_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zap 22 | 23 | import ( 24 | "testing" 25 | "time" 26 | 27 | "github.com/stretchr/testify/assert" 28 | ) 29 | 30 | func TestTimeToMillis(t *testing.T) { 31 | tests := []struct { 32 | t time.Time 33 | stamp int64 34 | }{ 35 | {t: time.Unix(0, 0), stamp: 0}, 36 | {t: time.Unix(1, 0), stamp: 1000}, 37 | {t: time.Unix(1, int64(500*time.Millisecond)), stamp: 1500}, 38 | } 39 | for _, tt := range tests { 40 | assert.Equal(t, tt.stamp, timeToMillis(tt.t), "Unexpected timestamp for time %v.", tt.t) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tools/go.mod: -------------------------------------------------------------------------------- 1 | module go.uber.org/zap/tools 2 | 3 | require golang.org/x/vuln v1.1.3 4 | 5 | require ( 6 | golang.org/x/mod v0.19.0 // indirect 7 | golang.org/x/sync v0.7.0 // indirect 8 | golang.org/x/sys v0.22.0 // indirect 9 | golang.org/x/telemetry v0.0.0-20240522233618-39ace7a40ae7 // indirect 10 | golang.org/x/tools v0.23.0 // indirect 11 | ) 12 | 13 | go 1.21 14 | 15 | toolchain go1.22.2 16 | -------------------------------------------------------------------------------- /tools/go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786 h1:rcv+Ippz6RAtvaGgKxc+8FQIpxHgsF+HBzPyYL2cyVU= 2 | github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786/go.mod h1:apVn/GCasLZUVpAJ6oWAuyP7Ne7CEsQbTnc0plM3m+o= 3 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 4 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 5 | github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= 6 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 7 | golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= 8 | golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 9 | golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= 10 | golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 11 | golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= 12 | golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 13 | golang.org/x/telemetry v0.0.0-20240522233618-39ace7a40ae7 h1:FemxDzfMUcK2f3YY4H+05K9CDzbSVr2+q/JKN45pey0= 14 | golang.org/x/telemetry v0.0.0-20240522233618-39ace7a40ae7/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= 15 | golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= 16 | golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= 17 | golang.org/x/vuln v1.1.3 h1:NPGnvPOTgnjBc9HTaUx+nj+EaUYxl5SJOWqaDYGaFYw= 18 | golang.org/x/vuln v1.1.3/go.mod h1:7Le6Fadm5FOqE9C926BCD0g12NWyhg7cxV4BwcPFuNY= 19 | -------------------------------------------------------------------------------- /tools/tools.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | //go:build tools 22 | // +build tools 23 | 24 | package tools 25 | 26 | import ( 27 | // Tools we use during development. 28 | _ "golang.org/x/vuln/cmd/govulncheck" 29 | ) 30 | -------------------------------------------------------------------------------- /writer.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2022 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zap 22 | 23 | import ( 24 | "fmt" 25 | "io" 26 | 27 | "go.uber.org/zap/zapcore" 28 | 29 | "go.uber.org/multierr" 30 | ) 31 | 32 | // Open is a high-level wrapper that takes a variadic number of URLs, opens or 33 | // creates each of the specified resources, and combines them into a locked 34 | // WriteSyncer. It also returns any error encountered and a function to close 35 | // any opened files. 36 | // 37 | // Passing no URLs returns a no-op WriteSyncer. Zap handles URLs without a 38 | // scheme and URLs with the "file" scheme. Third-party code may register 39 | // factories for other schemes using RegisterSink. 40 | // 41 | // URLs with the "file" scheme must use absolute paths on the local 42 | // filesystem. No user, password, port, fragments, or query parameters are 43 | // allowed, and the hostname must be empty or "localhost". 44 | // 45 | // Since it's common to write logs to the local filesystem, URLs without a 46 | // scheme (e.g., "/var/log/foo.log") are treated as local file paths. Without 47 | // a scheme, the special paths "stdout" and "stderr" are interpreted as 48 | // os.Stdout and os.Stderr. When specified without a scheme, relative file 49 | // paths also work. 50 | func Open(paths ...string) (zapcore.WriteSyncer, func(), error) { 51 | writers, closeAll, err := open(paths) 52 | if err != nil { 53 | return nil, nil, err 54 | } 55 | 56 | writer := CombineWriteSyncers(writers...) 57 | return writer, closeAll, nil 58 | } 59 | 60 | func open(paths []string) ([]zapcore.WriteSyncer, func(), error) { 61 | writers := make([]zapcore.WriteSyncer, 0, len(paths)) 62 | closers := make([]io.Closer, 0, len(paths)) 63 | closeAll := func() { 64 | for _, c := range closers { 65 | _ = c.Close() 66 | } 67 | } 68 | 69 | var openErr error 70 | for _, path := range paths { 71 | sink, err := _sinkRegistry.newSink(path) 72 | if err != nil { 73 | openErr = multierr.Append(openErr, fmt.Errorf("open sink %q: %w", path, err)) 74 | continue 75 | } 76 | writers = append(writers, sink) 77 | closers = append(closers, sink) 78 | } 79 | if openErr != nil { 80 | closeAll() 81 | return nil, nil, openErr 82 | } 83 | 84 | return writers, closeAll, nil 85 | } 86 | 87 | // CombineWriteSyncers is a utility that combines multiple WriteSyncers into a 88 | // single, locked WriteSyncer. If no inputs are supplied, it returns a no-op 89 | // WriteSyncer. 90 | // 91 | // It's provided purely as a convenience; the result is no different from 92 | // using zapcore.NewMultiWriteSyncer and zapcore.Lock individually. 93 | func CombineWriteSyncers(writers ...zapcore.WriteSyncer) zapcore.WriteSyncer { 94 | if len(writers) == 0 { 95 | return zapcore.AddSync(io.Discard) 96 | } 97 | return zapcore.Lock(zapcore.NewMultiWriteSyncer(writers...)) 98 | } 99 | -------------------------------------------------------------------------------- /zapcore/buffered_write_syncer_bench_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zapcore 22 | 23 | import ( 24 | "os" 25 | "testing" 26 | 27 | "github.com/stretchr/testify/assert" 28 | "github.com/stretchr/testify/require" 29 | ) 30 | 31 | func BenchmarkBufferedWriteSyncer(b *testing.B) { 32 | b.Run("write file with buffer", func(b *testing.B) { 33 | file, err := os.CreateTemp(b.TempDir(), "test.log") 34 | require.NoError(b, err) 35 | 36 | defer func() { 37 | assert.NoError(b, file.Close()) 38 | }() 39 | 40 | w := &BufferedWriteSyncer{ 41 | WS: AddSync(file), 42 | } 43 | defer func() { 44 | assert.NoError(b, w.Stop(), "failed to stop buffered write syncer") 45 | }() 46 | b.ResetTimer() 47 | b.RunParallel(func(pb *testing.PB) { 48 | for pb.Next() { 49 | if _, err := w.Write([]byte("foobarbazbabble")); err != nil { 50 | b.Fatal(err) 51 | } 52 | } 53 | }) 54 | }) 55 | } 56 | -------------------------------------------------------------------------------- /zapcore/clock.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zapcore 22 | 23 | import "time" 24 | 25 | // DefaultClock is the default clock used by Zap in operations that require 26 | // time. This clock uses the system clock for all operations. 27 | var DefaultClock = systemClock{} 28 | 29 | // Clock is a source of time for logged entries. 30 | type Clock interface { 31 | // Now returns the current local time. 32 | Now() time.Time 33 | 34 | // NewTicker returns *time.Ticker that holds a channel 35 | // that delivers "ticks" of a clock. 36 | NewTicker(time.Duration) *time.Ticker 37 | } 38 | 39 | // systemClock implements default Clock that uses system time. 40 | type systemClock struct{} 41 | 42 | func (systemClock) Now() time.Time { 43 | return time.Now() 44 | } 45 | 46 | func (systemClock) NewTicker(duration time.Duration) *time.Ticker { 47 | return time.NewTicker(duration) 48 | } 49 | -------------------------------------------------------------------------------- /zapcore/clock_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zapcore 22 | 23 | import ( 24 | "testing" 25 | "time" 26 | 27 | "go.uber.org/zap/internal/ztest" 28 | ) 29 | 30 | // Verify that the mock clock satisfies the Clock interface. 31 | var _ Clock = (*ztest.MockClock)(nil) 32 | 33 | func TestSystemClock_NewTicker(t *testing.T) { 34 | want := 3 35 | 36 | var n int 37 | timer := DefaultClock.NewTicker(time.Millisecond) 38 | for range timer.C { 39 | n++ 40 | if n == want { 41 | return 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /zapcore/console_encoder_bench_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zapcore_test 22 | 23 | import ( 24 | "testing" 25 | 26 | //revive:disable:dot-imports 27 | . "go.uber.org/zap/zapcore" 28 | ) 29 | 30 | func BenchmarkZapConsole(b *testing.B) { 31 | b.RunParallel(func(pb *testing.PB) { 32 | for pb.Next() { 33 | enc := NewConsoleEncoder(humanEncoderConfig()) 34 | enc.AddString("str", "foo") 35 | enc.AddInt64("int64-1", 1) 36 | enc.AddInt64("int64-2", 2) 37 | enc.AddFloat64("float64", 1.0) 38 | enc.AddString("string1", "\n") 39 | enc.AddString("string2", "💩") 40 | enc.AddString("string3", "🤔") 41 | enc.AddString("string4", "🙊") 42 | enc.AddBool("bool", true) 43 | buf, _ := enc.EncodeEntry(Entry{ 44 | Message: "fake", 45 | Level: DebugLevel, 46 | }, nil) 47 | buf.Free() 48 | } 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /zapcore/console_encoder_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | package zapcore_test 21 | 22 | import ( 23 | "testing" 24 | "time" 25 | 26 | "github.com/stretchr/testify/assert" 27 | //revive:disable:dot-imports 28 | . "go.uber.org/zap/zapcore" 29 | ) 30 | 31 | var testEntry = Entry{ 32 | LoggerName: "main", 33 | Level: InfoLevel, 34 | Message: `hello`, 35 | Time: _epoch, 36 | Stack: "fake-stack", 37 | Caller: EntryCaller{Defined: true, File: "foo.go", Line: 42, Function: "foo.Foo"}, 38 | } 39 | 40 | func TestConsoleEncodeEntry(t *testing.T) { 41 | tests := []struct { 42 | desc string 43 | expected string 44 | ent Entry 45 | fields []Field 46 | }{ 47 | { 48 | desc: "info no fields", 49 | expected: "2018-06-19T16:33:42Z\tinfo\tbob\tlob law\n", 50 | ent: Entry{ 51 | Level: InfoLevel, 52 | Time: time.Date(2018, 6, 19, 16, 33, 42, 99, time.UTC), 53 | LoggerName: "bob", 54 | Message: "lob law", 55 | }, 56 | }, 57 | { 58 | desc: "zero_time_omitted", 59 | expected: "info\tname\tmessage\n", 60 | ent: Entry{ 61 | Level: InfoLevel, 62 | Time: time.Time{}, 63 | LoggerName: "name", 64 | Message: "message", 65 | }, 66 | }, 67 | } 68 | 69 | cfg := testEncoderConfig() 70 | cfg.EncodeTime = RFC3339TimeEncoder 71 | enc := NewConsoleEncoder(cfg) 72 | 73 | for _, tt := range tests { 74 | t.Run(tt.desc, func(t *testing.T) { 75 | buf, err := enc.EncodeEntry(tt.ent, tt.fields) 76 | if assert.NoError(t, err, "Unexpected console encoding error.") { 77 | assert.Equal(t, tt.expected, buf.String(), "Incorrect encoded entry.") 78 | } 79 | buf.Free() 80 | }) 81 | } 82 | } 83 | 84 | func TestConsoleSeparator(t *testing.T) { 85 | tests := []struct { 86 | desc string 87 | separator string 88 | wantConsole string 89 | }{ 90 | { 91 | desc: "space console separator", 92 | separator: " ", 93 | wantConsole: "0 info main foo.go:42 foo.Foo hello\nfake-stack\n", 94 | }, 95 | { 96 | desc: "default console separator", 97 | separator: "", 98 | wantConsole: "0\tinfo\tmain\tfoo.go:42\tfoo.Foo\thello\nfake-stack\n", 99 | }, 100 | { 101 | desc: "tag console separator", 102 | separator: "\t", 103 | wantConsole: "0\tinfo\tmain\tfoo.go:42\tfoo.Foo\thello\nfake-stack\n", 104 | }, 105 | { 106 | desc: "dash console separator", 107 | separator: "--", 108 | wantConsole: "0--info--main--foo.go:42--foo.Foo--hello\nfake-stack\n", 109 | }, 110 | } 111 | 112 | for _, tt := range tests { 113 | console := NewConsoleEncoder(encoderTestEncoderConfig(tt.separator)) 114 | t.Run(tt.desc, func(t *testing.T) { 115 | entry := testEntry 116 | consoleOut, err := console.EncodeEntry(entry, nil) 117 | if !assert.NoError(t, err) { 118 | return 119 | } 120 | assert.Equal( 121 | t, 122 | tt.wantConsole, 123 | consoleOut.String(), 124 | "Unexpected console output", 125 | ) 126 | }) 127 | 128 | } 129 | } 130 | 131 | func encoderTestEncoderConfig(separator string) EncoderConfig { 132 | testEncoder := testEncoderConfig() 133 | testEncoder.ConsoleSeparator = separator 134 | return testEncoder 135 | } 136 | -------------------------------------------------------------------------------- /zapcore/core.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zapcore 22 | 23 | // Core is a minimal, fast logger interface. It's designed for library authors 24 | // to wrap in a more user-friendly API. 25 | type Core interface { 26 | LevelEnabler 27 | 28 | // With adds structured context to the Core. 29 | With([]Field) Core 30 | // Check determines whether the supplied Entry should be logged (using the 31 | // embedded LevelEnabler and possibly some extra logic). If the entry 32 | // should be logged, the Core adds itself to the CheckedEntry and returns 33 | // the result. 34 | // 35 | // Callers must use Check before calling Write. 36 | Check(Entry, *CheckedEntry) *CheckedEntry 37 | // Write serializes the Entry and any Fields supplied at the log site and 38 | // writes them to their destination. 39 | // 40 | // If called, Write should always log the Entry and Fields; it should not 41 | // replicate the logic of Check. 42 | Write(Entry, []Field) error 43 | // Sync flushes buffered logs (if any). 44 | Sync() error 45 | } 46 | 47 | type nopCore struct{} 48 | 49 | // NewNopCore returns a no-op Core. 50 | func NewNopCore() Core { return nopCore{} } 51 | func (nopCore) Enabled(Level) bool { return false } 52 | func (n nopCore) With([]Field) Core { return n } 53 | func (nopCore) Check(_ Entry, ce *CheckedEntry) *CheckedEntry { return ce } 54 | func (nopCore) Write(Entry, []Field) error { return nil } 55 | func (nopCore) Sync() error { return nil } 56 | 57 | // NewCore creates a Core that writes logs to a WriteSyncer. 58 | func NewCore(enc Encoder, ws WriteSyncer, enab LevelEnabler) Core { 59 | return &ioCore{ 60 | LevelEnabler: enab, 61 | enc: enc, 62 | out: ws, 63 | } 64 | } 65 | 66 | type ioCore struct { 67 | LevelEnabler 68 | enc Encoder 69 | out WriteSyncer 70 | } 71 | 72 | var ( 73 | _ Core = (*ioCore)(nil) 74 | _ leveledEnabler = (*ioCore)(nil) 75 | ) 76 | 77 | func (c *ioCore) Level() Level { 78 | return LevelOf(c.LevelEnabler) 79 | } 80 | 81 | func (c *ioCore) With(fields []Field) Core { 82 | clone := c.clone() 83 | addFields(clone.enc, fields) 84 | return clone 85 | } 86 | 87 | func (c *ioCore) Check(ent Entry, ce *CheckedEntry) *CheckedEntry { 88 | if c.Enabled(ent.Level) { 89 | return ce.AddCore(ent, c) 90 | } 91 | return ce 92 | } 93 | 94 | func (c *ioCore) Write(ent Entry, fields []Field) error { 95 | buf, err := c.enc.EncodeEntry(ent, fields) 96 | if err != nil { 97 | return err 98 | } 99 | _, err = c.out.Write(buf.Bytes()) 100 | buf.Free() 101 | if err != nil { 102 | return err 103 | } 104 | if ent.Level > ErrorLevel { 105 | // Since we may be crashing the program, sync the output. 106 | // Ignore Sync errors, pending a clean solution to issue #370. 107 | _ = c.Sync() 108 | } 109 | return nil 110 | } 111 | 112 | func (c *ioCore) Sync() error { 113 | return c.out.Sync() 114 | } 115 | 116 | func (c *ioCore) clone() *ioCore { 117 | return &ioCore{ 118 | LevelEnabler: c.LevelEnabler, 119 | enc: c.enc.Clone(), 120 | out: c.out, 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /zapcore/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // Package zapcore defines and implements the low-level interfaces upon which 22 | // zap is built. By providing alternate implementations of these interfaces, 23 | // external packages can extend zap's capabilities. 24 | package zapcore // import "go.uber.org/zap/zapcore" 25 | -------------------------------------------------------------------------------- /zapcore/entry_ext_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zapcore_test 22 | 23 | import ( 24 | "bytes" 25 | "testing" 26 | "time" 27 | 28 | "github.com/stretchr/testify/assert" 29 | "go.uber.org/zap" 30 | "go.uber.org/zap/zapcore" 31 | "go.uber.org/zap/zaptest" 32 | ) 33 | 34 | func TestCheckedEntryIllegalReuse(t *testing.T) { 35 | t.Parallel() 36 | 37 | var errOut bytes.Buffer 38 | 39 | testCore := zaptest.NewLogger(t).Core() 40 | ce := testCore.Check(zapcore.Entry{ 41 | Level: zapcore.InfoLevel, 42 | Time: time.Now(), 43 | Message: "hello", 44 | }, nil) 45 | ce.ErrorOutput = zapcore.AddSync(&errOut) 46 | 47 | // The first write should succeed. 48 | ce.Write(zap.String("k", "v"), zap.Int("n", 42)) 49 | assert.Empty(t, errOut.String(), "Expected no errors on first write.") 50 | 51 | // The second write should fail. 52 | ce.Write(zap.String("foo", "bar"), zap.Int("x", 1)) 53 | assert.Contains(t, errOut.String(), "Unsafe CheckedEntry re-use near Entry", 54 | "Expected error logged on second write.") 55 | } 56 | -------------------------------------------------------------------------------- /zapcore/hook.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zapcore 22 | 23 | import "go.uber.org/multierr" 24 | 25 | type hooked struct { 26 | Core 27 | funcs []func(Entry) error 28 | } 29 | 30 | var ( 31 | _ Core = (*hooked)(nil) 32 | _ leveledEnabler = (*hooked)(nil) 33 | ) 34 | 35 | // RegisterHooks wraps a Core and runs a collection of user-defined callback 36 | // hooks each time a message is logged. Execution of the callbacks is blocking. 37 | // 38 | // This offers users an easy way to register simple callbacks (e.g., metrics 39 | // collection) without implementing the full Core interface. 40 | func RegisterHooks(core Core, hooks ...func(Entry) error) Core { 41 | funcs := append([]func(Entry) error{}, hooks...) 42 | return &hooked{ 43 | Core: core, 44 | funcs: funcs, 45 | } 46 | } 47 | 48 | func (h *hooked) Level() Level { 49 | return LevelOf(h.Core) 50 | } 51 | 52 | func (h *hooked) Check(ent Entry, ce *CheckedEntry) *CheckedEntry { 53 | // Let the wrapped Core decide whether to log this message or not. This 54 | // also gives the downstream a chance to register itself directly with the 55 | // CheckedEntry. 56 | if downstream := h.Core.Check(ent, ce); downstream != nil { 57 | return downstream.AddCore(ent, h) 58 | } 59 | return ce 60 | } 61 | 62 | func (h *hooked) With(fields []Field) Core { 63 | return &hooked{ 64 | Core: h.Core.With(fields), 65 | funcs: h.funcs, 66 | } 67 | } 68 | 69 | func (h *hooked) Write(ent Entry, _ []Field) error { 70 | // Since our downstream had a chance to register itself directly with the 71 | // CheckedMessage, we don't need to call it here. 72 | var err error 73 | for i := range h.funcs { 74 | err = multierr.Append(err, h.funcs[i](ent)) 75 | } 76 | return err 77 | } 78 | -------------------------------------------------------------------------------- /zapcore/hook_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zapcore_test 22 | 23 | import ( 24 | "testing" 25 | 26 | //revive:disable:dot-imports 27 | . "go.uber.org/zap/zapcore" 28 | "go.uber.org/zap/zaptest/observer" 29 | 30 | "github.com/stretchr/testify/assert" 31 | "github.com/stretchr/testify/require" 32 | ) 33 | 34 | func TestHooks(t *testing.T) { 35 | tests := []struct { 36 | entryLevel Level 37 | coreLevel Level 38 | expectCall bool 39 | }{ 40 | {DebugLevel, InfoLevel, false}, 41 | {InfoLevel, InfoLevel, true}, 42 | {WarnLevel, InfoLevel, true}, 43 | } 44 | 45 | for _, tt := range tests { 46 | fac, logs := observer.New(tt.coreLevel) 47 | 48 | // sanity check 49 | require.Equal(t, tt.coreLevel, LevelOf(fac), "Original logger has the wrong level") 50 | 51 | intField := makeInt64Field("foo", 42) 52 | ent := Entry{Message: "bar", Level: tt.entryLevel} 53 | 54 | var called int 55 | f := func(e Entry) error { 56 | called++ 57 | assert.Equal(t, ent, e, "Hook called with unexpected Entry.") 58 | return nil 59 | } 60 | 61 | h := RegisterHooks(fac, f) 62 | if ce := h.With([]Field{intField}).Check(ent, nil); ce != nil { 63 | ce.Write() 64 | } 65 | 66 | t.Run("LevelOf", func(t *testing.T) { 67 | assert.Equal(t, tt.coreLevel, LevelOf(h), "Wrapped logger has the wrong log level") 68 | }) 69 | 70 | if tt.expectCall { 71 | assert.Equal(t, 1, called, "Expected to call hook once.") 72 | assert.Equal( 73 | t, 74 | []observer.LoggedEntry{{Entry: ent, Context: []Field{intField}}}, 75 | logs.AllUntimed(), 76 | "Unexpected logs written out.", 77 | ) 78 | } else { 79 | assert.Equal(t, 0, called, "Didn't expect to call hook.") 80 | assert.Equal(t, 0, logs.Len(), "Unexpected logs written out.") 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /zapcore/increase_level.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zapcore 22 | 23 | import "fmt" 24 | 25 | type levelFilterCore struct { 26 | core Core 27 | level LevelEnabler 28 | } 29 | 30 | var ( 31 | _ Core = (*levelFilterCore)(nil) 32 | _ leveledEnabler = (*levelFilterCore)(nil) 33 | ) 34 | 35 | // NewIncreaseLevelCore creates a core that can be used to increase the level of 36 | // an existing Core. It cannot be used to decrease the logging level, as it acts 37 | // as a filter before calling the underlying core. If level decreases the log level, 38 | // an error is returned. 39 | func NewIncreaseLevelCore(core Core, level LevelEnabler) (Core, error) { 40 | for l := _maxLevel; l >= _minLevel; l-- { 41 | if !core.Enabled(l) && level.Enabled(l) { 42 | return nil, fmt.Errorf("invalid increase level, as level %q is allowed by increased level, but not by existing core", l) 43 | } 44 | } 45 | 46 | return &levelFilterCore{core, level}, nil 47 | } 48 | 49 | func (c *levelFilterCore) Enabled(lvl Level) bool { 50 | return c.level.Enabled(lvl) 51 | } 52 | 53 | func (c *levelFilterCore) Level() Level { 54 | return LevelOf(c.level) 55 | } 56 | 57 | func (c *levelFilterCore) With(fields []Field) Core { 58 | return &levelFilterCore{c.core.With(fields), c.level} 59 | } 60 | 61 | func (c *levelFilterCore) Check(ent Entry, ce *CheckedEntry) *CheckedEntry { 62 | if !c.Enabled(ent.Level) { 63 | return ce 64 | } 65 | 66 | return c.core.Check(ent, ce) 67 | } 68 | 69 | func (c *levelFilterCore) Write(ent Entry, fields []Field) error { 70 | return c.core.Write(ent, fields) 71 | } 72 | 73 | func (c *levelFilterCore) Sync() error { 74 | return c.core.Sync() 75 | } 76 | -------------------------------------------------------------------------------- /zapcore/increase_level_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zapcore_test 22 | 23 | import ( 24 | "fmt" 25 | "testing" 26 | 27 | "github.com/stretchr/testify/assert" 28 | "github.com/stretchr/testify/require" 29 | "go.uber.org/zap" 30 | 31 | //revive:disable:dot-imports 32 | . "go.uber.org/zap/zapcore" 33 | "go.uber.org/zap/zaptest/observer" 34 | ) 35 | 36 | func TestIncreaseLevel(t *testing.T) { 37 | tests := []struct { 38 | coreLevel Level 39 | increaseLevel Level 40 | wantErr bool 41 | with []Field 42 | }{ 43 | { 44 | coreLevel: InfoLevel, 45 | increaseLevel: DebugLevel, 46 | wantErr: true, 47 | }, 48 | { 49 | coreLevel: InfoLevel, 50 | increaseLevel: InfoLevel, 51 | }, 52 | { 53 | coreLevel: InfoLevel, 54 | increaseLevel: ErrorLevel, 55 | }, 56 | { 57 | coreLevel: InfoLevel, 58 | increaseLevel: ErrorLevel, 59 | with: []Field{zap.String("k", "v")}, 60 | }, 61 | { 62 | coreLevel: ErrorLevel, 63 | increaseLevel: DebugLevel, 64 | wantErr: true, 65 | }, 66 | { 67 | coreLevel: ErrorLevel, 68 | increaseLevel: InfoLevel, 69 | wantErr: true, 70 | }, 71 | { 72 | coreLevel: ErrorLevel, 73 | increaseLevel: WarnLevel, 74 | wantErr: true, 75 | }, 76 | { 77 | coreLevel: ErrorLevel, 78 | increaseLevel: PanicLevel, 79 | }, 80 | } 81 | 82 | for _, tt := range tests { 83 | msg := fmt.Sprintf("increase %v to %v", tt.coreLevel, tt.increaseLevel) 84 | t.Run(msg, func(t *testing.T) { 85 | logger, logs := observer.New(tt.coreLevel) 86 | 87 | // sanity check 88 | require.Equal(t, tt.coreLevel, LevelOf(logger), "Original logger has the wrong level") 89 | 90 | filteredLogger, err := NewIncreaseLevelCore(logger, tt.increaseLevel) 91 | if tt.wantErr { 92 | assert.ErrorContains(t, err, "invalid increase level") 93 | return 94 | } 95 | 96 | if len(tt.with) > 0 { 97 | filteredLogger = filteredLogger.With(tt.with) 98 | } 99 | 100 | require.NoError(t, err) 101 | 102 | t.Run("LevelOf", func(t *testing.T) { 103 | assert.Equal(t, tt.increaseLevel, LevelOf(filteredLogger), "Filtered logger has the wrong level") 104 | }) 105 | 106 | for l := DebugLevel; l <= FatalLevel; l++ { 107 | enabled := filteredLogger.Enabled(l) 108 | entry := Entry{Level: l} 109 | ce := filteredLogger.Check(entry, nil) 110 | ce.Write() 111 | entries := logs.TakeAll() 112 | 113 | if l >= tt.increaseLevel { 114 | assert.True(t, enabled, "expect %v to be enabled", l) 115 | assert.NotNil(t, ce, "expect non-nil Check") 116 | assert.NotEmpty(t, entries, "Expect log to be written") 117 | } else { 118 | assert.False(t, enabled, "expect %v to be disabled", l) 119 | assert.Nil(t, ce, "expect nil Check") 120 | assert.Empty(t, entries, "No logs should have been written") 121 | } 122 | 123 | // Write should always log the entry as per the Core interface 124 | require.NoError(t, filteredLogger.Write(entry, nil), "Write failed") 125 | require.NoError(t, filteredLogger.Sync(), "Sync failed") 126 | assert.NotEmpty(t, logs.TakeAll(), "Write should always log") 127 | } 128 | }) 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /zapcore/json_encoder_bench_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zapcore_test 22 | 23 | import ( 24 | "encoding/json" 25 | "fmt" 26 | "testing" 27 | "time" 28 | 29 | //revive:disable:dot-imports 30 | . "go.uber.org/zap/zapcore" 31 | ) 32 | 33 | func BenchmarkJSONLogMarshalerFunc(b *testing.B) { 34 | for i := 0; i < b.N; i++ { 35 | enc := NewJSONEncoder(testEncoderConfig()) 36 | err := enc.AddObject("nested", ObjectMarshalerFunc(func(enc ObjectEncoder) error { 37 | enc.AddInt64("i", int64(i)) 38 | return nil 39 | })) 40 | if err != nil { 41 | b.Fatal(err) 42 | } 43 | } 44 | } 45 | 46 | func BenchmarkZapJSONFloat32AndComplex64(b *testing.B) { 47 | b.RunParallel(func(pb *testing.PB) { 48 | for pb.Next() { 49 | enc := NewJSONEncoder(testEncoderConfig()) 50 | enc.AddFloat32("float32", 3.14) 51 | enc.AddComplex64("complex64", 2.71+3.14i) 52 | } 53 | }) 54 | } 55 | 56 | const _sliceSize = 5000 57 | 58 | type StringSlice []string 59 | 60 | func (s StringSlice) MarshalLogArray(encoder ArrayEncoder) error { 61 | for _, str := range s { 62 | encoder.AppendString(str) 63 | } 64 | return nil 65 | } 66 | 67 | func generateStringSlice(n int) StringSlice { 68 | output := make(StringSlice, 0, n) 69 | for i := 0; i < n; i++ { 70 | output = append(output, fmt.Sprint("00000000-0000-0000-0000-0000000000", i)) 71 | } 72 | return output 73 | } 74 | 75 | func BenchmarkZapJSON(b *testing.B) { 76 | additional := generateStringSlice(_sliceSize) 77 | b.ResetTimer() 78 | b.RunParallel(func(pb *testing.PB) { 79 | for pb.Next() { 80 | enc := NewJSONEncoder(testEncoderConfig()) 81 | enc.AddString("str", "foo") 82 | enc.AddInt64("int64-1", 1) 83 | enc.AddInt64("int64-2", 2) 84 | enc.AddFloat64("float64", 1.0) 85 | enc.AddString("string1", "\n") 86 | enc.AddString("string2", "💩") 87 | enc.AddString("string3", "🤔") 88 | enc.AddString("string4", "🙊") 89 | enc.AddBool("bool", true) 90 | _ = enc.AddArray("test", additional) 91 | buf, _ := enc.EncodeEntry(Entry{ 92 | Message: "fake", 93 | Level: DebugLevel, 94 | }, nil) 95 | buf.Free() 96 | } 97 | }) 98 | } 99 | 100 | func BenchmarkStandardJSON(b *testing.B) { 101 | record := struct { 102 | Level string `json:"level"` 103 | Message string `json:"msg"` 104 | Time time.Time `json:"ts"` 105 | Fields map[string]interface{} `json:"fields"` 106 | Additional StringSlice 107 | }{ 108 | Level: "debug", 109 | Message: "fake", 110 | Time: time.Unix(0, 0), 111 | Fields: map[string]interface{}{ 112 | "str": "foo", 113 | "int64-1": int64(1), 114 | "int64-2": int64(1), 115 | "float64": float64(1.0), 116 | "string1": "\n", 117 | "string2": "💩", 118 | "string3": "🤔", 119 | "string4": "🙊", 120 | "bool": true, 121 | }, 122 | Additional: generateStringSlice(_sliceSize), 123 | } 124 | b.ResetTimer() 125 | b.RunParallel(func(pb *testing.PB) { 126 | for pb.Next() { 127 | if _, err := json.Marshal(record); err != nil { 128 | b.Fatal(err) 129 | } 130 | } 131 | }) 132 | } 133 | -------------------------------------------------------------------------------- /zapcore/lazy_with.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zapcore 22 | 23 | import "sync" 24 | 25 | type lazyWithCore struct { 26 | Core 27 | sync.Once 28 | fields []Field 29 | } 30 | 31 | // NewLazyWith wraps a Core with a "lazy" Core that will only encode fields if 32 | // the logger is written to (or is further chained in a lon-lazy manner). 33 | func NewLazyWith(core Core, fields []Field) Core { 34 | return &lazyWithCore{ 35 | Core: core, 36 | fields: fields, 37 | } 38 | } 39 | 40 | func (d *lazyWithCore) initOnce() { 41 | d.Once.Do(func() { 42 | d.Core = d.Core.With(d.fields) 43 | }) 44 | } 45 | 46 | func (d *lazyWithCore) With(fields []Field) Core { 47 | d.initOnce() 48 | return d.Core.With(fields) 49 | } 50 | 51 | func (d *lazyWithCore) Check(e Entry, ce *CheckedEntry) *CheckedEntry { 52 | d.initOnce() 53 | return d.Core.Check(e, ce) 54 | } 55 | -------------------------------------------------------------------------------- /zapcore/leak_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zapcore 22 | 23 | import ( 24 | "testing" 25 | 26 | "go.uber.org/goleak" 27 | ) 28 | 29 | func TestMain(m *testing.M) { 30 | goleak.VerifyTestMain(m) 31 | } 32 | -------------------------------------------------------------------------------- /zapcore/level_strings.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zapcore 22 | 23 | import "go.uber.org/zap/internal/color" 24 | 25 | var ( 26 | _levelToColor = map[Level]color.Color{ 27 | DebugLevel: color.Magenta, 28 | InfoLevel: color.Blue, 29 | WarnLevel: color.Yellow, 30 | ErrorLevel: color.Red, 31 | DPanicLevel: color.Red, 32 | PanicLevel: color.Red, 33 | FatalLevel: color.Red, 34 | } 35 | _unknownLevelColor = color.Red 36 | 37 | _levelToLowercaseColorString = make(map[Level]string, len(_levelToColor)) 38 | _levelToCapitalColorString = make(map[Level]string, len(_levelToColor)) 39 | ) 40 | 41 | func init() { 42 | for level, color := range _levelToColor { 43 | _levelToLowercaseColorString[level] = color.Add(level.String()) 44 | _levelToCapitalColorString[level] = color.Add(level.CapitalString()) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /zapcore/level_strings_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zapcore 22 | 23 | import ( 24 | "testing" 25 | 26 | "github.com/stretchr/testify/assert" 27 | ) 28 | 29 | func TestAllLevelsCoveredByLevelString(t *testing.T) { 30 | numLevels := int((_maxLevel - _minLevel) + 1) 31 | 32 | isComplete := func(m map[Level]string) bool { 33 | return len(m) == numLevels 34 | } 35 | 36 | assert.True(t, isComplete(_levelToLowercaseColorString), "Colored lowercase strings don't cover all levels.") 37 | assert.True(t, isComplete(_levelToCapitalColorString), "Colored capital strings don't cover all levels.") 38 | } 39 | -------------------------------------------------------------------------------- /zapcore/marshaler.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zapcore 22 | 23 | // ObjectMarshaler allows user-defined types to efficiently add themselves to the 24 | // logging context, and to selectively omit information which shouldn't be 25 | // included in logs (e.g., passwords). 26 | // 27 | // Note: ObjectMarshaler is only used when zap.Object is used or when 28 | // passed directly to zap.Any. It is not used when reflection-based 29 | // encoding is used. 30 | type ObjectMarshaler interface { 31 | MarshalLogObject(ObjectEncoder) error 32 | } 33 | 34 | // ObjectMarshalerFunc is a type adapter that turns a function into an 35 | // ObjectMarshaler. 36 | type ObjectMarshalerFunc func(ObjectEncoder) error 37 | 38 | // MarshalLogObject calls the underlying function. 39 | func (f ObjectMarshalerFunc) MarshalLogObject(enc ObjectEncoder) error { 40 | return f(enc) 41 | } 42 | 43 | // ArrayMarshaler allows user-defined types to efficiently add themselves to the 44 | // logging context, and to selectively omit information which shouldn't be 45 | // included in logs (e.g., passwords). 46 | // 47 | // Note: ArrayMarshaler is only used when zap.Array is used or when 48 | // passed directly to zap.Any. It is not used when reflection-based 49 | // encoding is used. 50 | type ArrayMarshaler interface { 51 | MarshalLogArray(ArrayEncoder) error 52 | } 53 | 54 | // ArrayMarshalerFunc is a type adapter that turns a function into an 55 | // ArrayMarshaler. 56 | type ArrayMarshalerFunc func(ArrayEncoder) error 57 | 58 | // MarshalLogArray calls the underlying function. 59 | func (f ArrayMarshalerFunc) MarshalLogArray(enc ArrayEncoder) error { 60 | return f(enc) 61 | } 62 | -------------------------------------------------------------------------------- /zapcore/reflected_encoder.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zapcore 22 | 23 | import ( 24 | "encoding/json" 25 | "io" 26 | ) 27 | 28 | // ReflectedEncoder serializes log fields that can't be serialized with Zap's 29 | // JSON encoder. These have the ReflectType field type. 30 | // Use EncoderConfig.NewReflectedEncoder to set this. 31 | type ReflectedEncoder interface { 32 | // Encode encodes and writes to the underlying data stream. 33 | Encode(interface{}) error 34 | } 35 | 36 | func defaultReflectedEncoder(w io.Writer) ReflectedEncoder { 37 | enc := json.NewEncoder(w) 38 | // For consistency with our custom JSON encoder. 39 | enc.SetEscapeHTML(false) 40 | return enc 41 | } 42 | -------------------------------------------------------------------------------- /zapcore/tee.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2022 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zapcore 22 | 23 | import "go.uber.org/multierr" 24 | 25 | type multiCore []Core 26 | 27 | var ( 28 | _ leveledEnabler = multiCore(nil) 29 | _ Core = multiCore(nil) 30 | ) 31 | 32 | // NewTee creates a Core that duplicates log entries into two or more 33 | // underlying Cores. 34 | // 35 | // Calling it with a single Core returns the input unchanged, and calling 36 | // it with no input returns a no-op Core. 37 | func NewTee(cores ...Core) Core { 38 | switch len(cores) { 39 | case 0: 40 | return NewNopCore() 41 | case 1: 42 | return cores[0] 43 | default: 44 | return multiCore(cores) 45 | } 46 | } 47 | 48 | func (mc multiCore) With(fields []Field) Core { 49 | clone := make(multiCore, len(mc)) 50 | for i := range mc { 51 | clone[i] = mc[i].With(fields) 52 | } 53 | return clone 54 | } 55 | 56 | func (mc multiCore) Level() Level { 57 | minLvl := _maxLevel // mc is never empty 58 | for i := range mc { 59 | if lvl := LevelOf(mc[i]); lvl < minLvl { 60 | minLvl = lvl 61 | } 62 | } 63 | return minLvl 64 | } 65 | 66 | func (mc multiCore) Enabled(lvl Level) bool { 67 | for i := range mc { 68 | if mc[i].Enabled(lvl) { 69 | return true 70 | } 71 | } 72 | return false 73 | } 74 | 75 | func (mc multiCore) Check(ent Entry, ce *CheckedEntry) *CheckedEntry { 76 | for i := range mc { 77 | ce = mc[i].Check(ent, ce) 78 | } 79 | return ce 80 | } 81 | 82 | func (mc multiCore) Write(ent Entry, fields []Field) error { 83 | var err error 84 | for i := range mc { 85 | err = multierr.Append(err, mc[i].Write(ent, fields)) 86 | } 87 | return err 88 | } 89 | 90 | func (mc multiCore) Sync() error { 91 | var err error 92 | for i := range mc { 93 | err = multierr.Append(err, mc[i].Sync()) 94 | } 95 | return err 96 | } 97 | -------------------------------------------------------------------------------- /zapcore/tee_logger_bench_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zapcore_test 22 | 23 | import ( 24 | "testing" 25 | 26 | "go.uber.org/zap/internal/ztest" 27 | //revive:disable:dot-imports 28 | . "go.uber.org/zap/zapcore" 29 | ) 30 | 31 | func withBenchedTee(b *testing.B, f func(Core)) { 32 | fac := NewTee( 33 | NewCore(NewJSONEncoder(testEncoderConfig()), &ztest.Discarder{}, DebugLevel), 34 | NewCore(NewJSONEncoder(testEncoderConfig()), &ztest.Discarder{}, InfoLevel), 35 | ) 36 | b.ResetTimer() 37 | f(fac) 38 | } 39 | 40 | func BenchmarkTeeCheck(b *testing.B) { 41 | cases := []struct { 42 | lvl Level 43 | msg string 44 | }{ 45 | {DebugLevel, "foo"}, 46 | {InfoLevel, "bar"}, 47 | {WarnLevel, "baz"}, 48 | {ErrorLevel, "babble"}, 49 | } 50 | withBenchedTee(b, func(core Core) { 51 | b.RunParallel(func(pb *testing.PB) { 52 | i := 0 53 | for pb.Next() { 54 | tt := cases[i] 55 | entry := Entry{Level: tt.lvl, Message: tt.msg} 56 | if cm := core.Check(entry, nil); cm != nil { 57 | cm.Write(Field{Key: "i", Integer: int64(i), Type: Int64Type}) 58 | } 59 | i = (i + 1) % len(cases) 60 | } 61 | }) 62 | }) 63 | } 64 | -------------------------------------------------------------------------------- /zapcore/write_syncer.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zapcore 22 | 23 | import ( 24 | "io" 25 | "sync" 26 | 27 | "go.uber.org/multierr" 28 | ) 29 | 30 | // A WriteSyncer is an io.Writer that can also flush any buffered data. Note 31 | // that *os.File (and thus, os.Stderr and os.Stdout) implement WriteSyncer. 32 | type WriteSyncer interface { 33 | io.Writer 34 | Sync() error 35 | } 36 | 37 | // AddSync converts an io.Writer to a WriteSyncer. It attempts to be 38 | // intelligent: if the concrete type of the io.Writer implements WriteSyncer, 39 | // we'll use the existing Sync method. If it doesn't, we'll add a no-op Sync. 40 | func AddSync(w io.Writer) WriteSyncer { 41 | switch w := w.(type) { 42 | case WriteSyncer: 43 | return w 44 | default: 45 | return writerWrapper{w} 46 | } 47 | } 48 | 49 | type lockedWriteSyncer struct { 50 | sync.Mutex 51 | ws WriteSyncer 52 | } 53 | 54 | // Lock wraps a WriteSyncer in a mutex to make it safe for concurrent use. In 55 | // particular, *os.Files must be locked before use. 56 | func Lock(ws WriteSyncer) WriteSyncer { 57 | if _, ok := ws.(*lockedWriteSyncer); ok { 58 | // no need to layer on another lock 59 | return ws 60 | } 61 | return &lockedWriteSyncer{ws: ws} 62 | } 63 | 64 | func (s *lockedWriteSyncer) Write(bs []byte) (int, error) { 65 | s.Lock() 66 | n, err := s.ws.Write(bs) 67 | s.Unlock() 68 | return n, err 69 | } 70 | 71 | func (s *lockedWriteSyncer) Sync() error { 72 | s.Lock() 73 | err := s.ws.Sync() 74 | s.Unlock() 75 | return err 76 | } 77 | 78 | type writerWrapper struct { 79 | io.Writer 80 | } 81 | 82 | func (w writerWrapper) Sync() error { 83 | return nil 84 | } 85 | 86 | type multiWriteSyncer []WriteSyncer 87 | 88 | // NewMultiWriteSyncer creates a WriteSyncer that duplicates its writes 89 | // and sync calls, much like io.MultiWriter. 90 | func NewMultiWriteSyncer(ws ...WriteSyncer) WriteSyncer { 91 | if len(ws) == 1 { 92 | return ws[0] 93 | } 94 | return multiWriteSyncer(ws) 95 | } 96 | 97 | // See https://golang.org/src/io/multi.go 98 | // When not all underlying syncers write the same number of bytes, 99 | // the smallest number is returned even though Write() is called on 100 | // all of them. 101 | func (ws multiWriteSyncer) Write(p []byte) (int, error) { 102 | var writeErr error 103 | nWritten := 0 104 | for _, w := range ws { 105 | n, err := w.Write(p) 106 | writeErr = multierr.Append(writeErr, err) 107 | if nWritten == 0 && n != 0 { 108 | nWritten = n 109 | } else if n < nWritten { 110 | nWritten = n 111 | } 112 | } 113 | return nWritten, writeErr 114 | } 115 | 116 | func (ws multiWriteSyncer) Sync() error { 117 | var err error 118 | for _, w := range ws { 119 | err = multierr.Append(err, w.Sync()) 120 | } 121 | return err 122 | } 123 | -------------------------------------------------------------------------------- /zapcore/write_syncer_bench_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zapcore 22 | 23 | import ( 24 | "os" 25 | "testing" 26 | 27 | "github.com/stretchr/testify/assert" 28 | "github.com/stretchr/testify/require" 29 | "go.uber.org/zap/internal/ztest" 30 | ) 31 | 32 | func BenchmarkMultiWriteSyncer(b *testing.B) { 33 | b.Run("2 discarder", func(b *testing.B) { 34 | w := NewMultiWriteSyncer( 35 | &ztest.Discarder{}, 36 | &ztest.Discarder{}, 37 | ) 38 | b.ResetTimer() 39 | b.RunParallel(func(pb *testing.PB) { 40 | for pb.Next() { 41 | if _, err := w.Write([]byte("foobarbazbabble")); err != nil { 42 | b.Fatal(err) 43 | } 44 | } 45 | }) 46 | }) 47 | b.Run("4 discarder", func(b *testing.B) { 48 | w := NewMultiWriteSyncer( 49 | &ztest.Discarder{}, 50 | &ztest.Discarder{}, 51 | &ztest.Discarder{}, 52 | &ztest.Discarder{}, 53 | ) 54 | b.ResetTimer() 55 | b.RunParallel(func(pb *testing.PB) { 56 | for pb.Next() { 57 | if _, err := w.Write([]byte("foobarbazbabble")); err != nil { 58 | b.Fatal(err) 59 | } 60 | } 61 | }) 62 | }) 63 | b.Run("4 discarder with buffer", func(b *testing.B) { 64 | w := &BufferedWriteSyncer{ 65 | WS: NewMultiWriteSyncer( 66 | &ztest.Discarder{}, 67 | &ztest.Discarder{}, 68 | &ztest.Discarder{}, 69 | &ztest.Discarder{}, 70 | ), 71 | } 72 | defer func() { 73 | assert.NoError(b, w.Stop(), "Unexpected error stopping buffered write syncer.") 74 | }() 75 | b.ResetTimer() 76 | b.RunParallel(func(pb *testing.PB) { 77 | for pb.Next() { 78 | if _, err := w.Write([]byte("foobarbazbabble")); err != nil { 79 | b.Fatal(err) 80 | } 81 | } 82 | }) 83 | }) 84 | } 85 | 86 | func BenchmarkWriteSyncer(b *testing.B) { 87 | b.Run("write file with no buffer", func(b *testing.B) { 88 | file, err := os.CreateTemp(b.TempDir(), "test.log") 89 | require.NoError(b, err) 90 | 91 | w := AddSync(file) 92 | b.ResetTimer() 93 | b.RunParallel(func(pb *testing.PB) { 94 | for pb.Next() { 95 | if _, err := w.Write([]byte("foobarbazbabble")); err != nil { 96 | b.Fatal(err) 97 | } 98 | } 99 | }) 100 | }) 101 | } 102 | -------------------------------------------------------------------------------- /zapgrpc/internal/test/README.md: -------------------------------------------------------------------------------- 1 | This submodule exists to test zapgrpc against grpc-go without adding a 2 | dependency on grpc-go to Zap. 3 | -------------------------------------------------------------------------------- /zapgrpc/internal/test/go.mod: -------------------------------------------------------------------------------- 1 | module go.uber.org/zap/zapgrpc/internal/test 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/stretchr/testify v1.8.1 7 | go.uber.org/zap v1.16.0 8 | google.golang.org/grpc v1.56.3 9 | ) 10 | 11 | require ( 12 | github.com/davecgh/go-spew v1.1.1 // indirect 13 | github.com/kr/pretty v0.3.0 // indirect 14 | github.com/pmezard/go-difflib v1.0.0 // indirect 15 | github.com/rogpeppe/go-internal v1.8.0 // indirect 16 | go.uber.org/multierr v1.10.0 // indirect 17 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect 18 | gopkg.in/yaml.v3 v3.0.1 // indirect 19 | ) 20 | 21 | replace go.uber.org/zap => ../../.. 22 | -------------------------------------------------------------------------------- /zapgrpc/internal/test/go.sum: -------------------------------------------------------------------------------- 1 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 6 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 7 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= 8 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 9 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 10 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 11 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 12 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 13 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 14 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 15 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 16 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= 17 | github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= 18 | github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= 19 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 20 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 21 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 22 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 23 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 24 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 25 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 26 | go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= 27 | go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= 28 | go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 29 | google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc= 30 | google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= 31 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 32 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 33 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 34 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 35 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 36 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 37 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 38 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 39 | -------------------------------------------------------------------------------- /zapgrpc/internal/test/grpc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // Package grpc tests Zap's zapgrpc package without requiring a dependency on 22 | // grpc from Zap itself. 23 | package grpc 24 | 25 | // This file exists to treat this directory as a valid package with at least 26 | // one non-test file. 27 | -------------------------------------------------------------------------------- /zapgrpc/internal/test/grpc_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package grpc 22 | 23 | import ( 24 | "testing" 25 | 26 | "github.com/stretchr/testify/assert" 27 | "github.com/stretchr/testify/require" 28 | "go.uber.org/zap" 29 | "go.uber.org/zap/zapcore" 30 | "go.uber.org/zap/zapgrpc" 31 | "go.uber.org/zap/zaptest/observer" 32 | "google.golang.org/grpc/grpclog" 33 | ) 34 | 35 | func TestLoggerV2(t *testing.T) { 36 | core, observedLogs := observer.New(zapcore.InfoLevel) 37 | zlog := zap.New(core) 38 | 39 | grpclog.SetLoggerV2(zapgrpc.NewLogger(zlog)) 40 | 41 | grpclog.Info("hello from grpc") 42 | 43 | logs := observedLogs.TakeAll() 44 | require.Len(t, logs, 1, "Expected one log entry.") 45 | entry := logs[0] 46 | 47 | assert.Equal(t, zapcore.InfoLevel, entry.Level, 48 | "Log entry level did not match.") 49 | assert.Equal(t, "hello from grpc", entry.Message, 50 | "Log entry message did not match.") 51 | } 52 | -------------------------------------------------------------------------------- /zapio/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zapio_test 22 | 23 | import ( 24 | "io" 25 | "log" 26 | 27 | "go.uber.org/zap" 28 | "go.uber.org/zap/zapio" 29 | ) 30 | 31 | func ExampleWriter() { 32 | logger := zap.NewExample() 33 | w := &zapio.Writer{Log: logger} 34 | 35 | io.WriteString(w, "starting up\n") 36 | io.WriteString(w, "running\n") 37 | io.WriteString(w, "shutting down\n") 38 | 39 | if err := w.Close(); err != nil { 40 | log.Fatal(err) 41 | } 42 | 43 | // Output: 44 | // {"level":"info","msg":"starting up"} 45 | // {"level":"info","msg":"running"} 46 | // {"level":"info","msg":"shutting down"} 47 | } 48 | -------------------------------------------------------------------------------- /zaptest/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // Package zaptest provides a variety of helpers for testing log output. 22 | package zaptest // import "go.uber.org/zap/zaptest" 23 | -------------------------------------------------------------------------------- /zaptest/observer/logged_entry.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package observer 22 | 23 | import "go.uber.org/zap/zapcore" 24 | 25 | // A LoggedEntry is an encoding-agnostic representation of a log message. 26 | // Field availability is context dependent. 27 | type LoggedEntry struct { 28 | zapcore.Entry 29 | Context []zapcore.Field 30 | } 31 | 32 | // ContextMap returns a map for all fields in Context. 33 | func (e LoggedEntry) ContextMap() map[string]interface{} { 34 | encoder := zapcore.NewMapObjectEncoder() 35 | for _, f := range e.Context { 36 | f.AddTo(encoder) 37 | } 38 | return encoder.Fields 39 | } 40 | -------------------------------------------------------------------------------- /zaptest/observer/logged_entry_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package observer 22 | 23 | import ( 24 | "testing" 25 | 26 | "go.uber.org/zap" 27 | "go.uber.org/zap/zapcore" 28 | 29 | "github.com/stretchr/testify/assert" 30 | ) 31 | 32 | func TestLoggedEntryContextMap(t *testing.T) { 33 | tests := []struct { 34 | msg string 35 | fields []zapcore.Field 36 | want map[string]interface{} 37 | }{ 38 | { 39 | msg: "no fields", 40 | fields: nil, 41 | want: map[string]interface{}{}, 42 | }, 43 | { 44 | msg: "simple", 45 | fields: []zapcore.Field{ 46 | zap.String("k1", "v"), 47 | zap.Int64("k2", 10), 48 | }, 49 | want: map[string]interface{}{ 50 | "k1": "v", 51 | "k2": int64(10), 52 | }, 53 | }, 54 | { 55 | msg: "overwrite", 56 | fields: []zapcore.Field{ 57 | zap.String("k1", "v1"), 58 | zap.String("k1", "v2"), 59 | }, 60 | want: map[string]interface{}{ 61 | "k1": "v2", 62 | }, 63 | }, 64 | { 65 | msg: "nested", 66 | fields: []zapcore.Field{ 67 | zap.String("k1", "v1"), 68 | zap.Namespace("nested"), 69 | zap.String("k2", "v2"), 70 | }, 71 | want: map[string]interface{}{ 72 | "k1": "v1", 73 | "nested": map[string]interface{}{ 74 | "k2": "v2", 75 | }, 76 | }, 77 | }, 78 | } 79 | 80 | for _, tt := range tests { 81 | t.Run(tt.msg, func(t *testing.T) { 82 | entry := LoggedEntry{ 83 | Context: tt.fields, 84 | } 85 | assert.Equal(t, tt.want, entry.ContextMap()) 86 | }) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /zaptest/testingt.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zaptest 22 | 23 | // TestingT is a subset of the API provided by all *testing.T and *testing.B 24 | // objects. 25 | type TestingT interface { 26 | // Logs the given message without failing the test. 27 | Logf(string, ...interface{}) 28 | 29 | // Logs the given message and marks the test as failed. 30 | Errorf(string, ...interface{}) 31 | 32 | // Marks the test as failed. 33 | Fail() 34 | 35 | // Returns true if the test has been marked as failed. 36 | Failed() bool 37 | 38 | // Returns the name of the test. 39 | Name() string 40 | 41 | // Marks the test as failed and stops execution of that test. 42 | FailNow() 43 | } 44 | 45 | // Note: We currently only rely on Logf. We are including Errorf and FailNow 46 | // in the interface in anticipation of future need since we can't extend the 47 | // interface without a breaking change. 48 | -------------------------------------------------------------------------------- /zaptest/testingt_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zaptest 22 | 23 | import "testing" 24 | 25 | // Just a compile-time test to ensure that TestingT matches the testing.TB 26 | // interface. We could do this in testingt.go but that would put a dependency 27 | // on the "testing" package from zaptest. 28 | 29 | var _ TestingT = (testing.TB)(nil) 30 | -------------------------------------------------------------------------------- /zaptest/timeout.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zaptest 22 | 23 | import ( 24 | "time" 25 | 26 | "go.uber.org/zap/internal/ztest" 27 | ) 28 | 29 | // Timeout scales the provided duration by $TEST_TIMEOUT_SCALE. 30 | // 31 | // Deprecated: This function is intended for internal testing and shouldn't be 32 | // used outside zap itself. It was introduced before Go supported internal 33 | // packages. 34 | func Timeout(base time.Duration) time.Duration { 35 | return ztest.Timeout(base) 36 | } 37 | 38 | // Sleep scales the sleep duration by $TEST_TIMEOUT_SCALE. 39 | // 40 | // Deprecated: This function is intended for internal testing and shouldn't be 41 | // used outside zap itself. It was introduced before Go supported internal 42 | // packages. 43 | func Sleep(base time.Duration) { 44 | ztest.Sleep(base) 45 | } 46 | -------------------------------------------------------------------------------- /zaptest/timeout_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zaptest 22 | 23 | import ( 24 | "testing" 25 | "time" 26 | 27 | "github.com/stretchr/testify/assert" 28 | "go.uber.org/zap/internal/ztest" 29 | ) 30 | 31 | func TestTimeout(t *testing.T) { 32 | defer ztest.Initialize("2")() 33 | assert.Equal(t, time.Duration(100), Timeout(50), "Expected to scale up timeout.") 34 | } 35 | 36 | func TestSleep(t *testing.T) { 37 | defer ztest.Initialize("2")() 38 | const sleepFor = 50 * time.Millisecond 39 | now := time.Now() 40 | Sleep(sleepFor) 41 | elapsed := time.Since(now) 42 | assert.True(t, 2*sleepFor <= elapsed, "Expected to scale up timeout.") 43 | } 44 | -------------------------------------------------------------------------------- /zaptest/writer.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zaptest 22 | 23 | import "go.uber.org/zap/internal/ztest" 24 | 25 | type ( 26 | // A Syncer is a spy for the Sync portion of zapcore.WriteSyncer. 27 | Syncer = ztest.Syncer 28 | 29 | // A Discarder sends all writes to io.Discard. 30 | Discarder = ztest.Discarder 31 | 32 | // FailWriter is a WriteSyncer that always returns an error on writes. 33 | FailWriter = ztest.FailWriter 34 | 35 | // ShortWriter is a WriteSyncer whose write method never returns an error, 36 | // but always reports that it wrote one byte less than the input slice's 37 | // length (thus, a "short write"). 38 | ShortWriter = ztest.ShortWriter 39 | 40 | // Buffer is an implementation of zapcore.WriteSyncer that sends all writes to 41 | // a bytes.Buffer. It has convenience methods to split the accumulated buffer 42 | // on newlines. 43 | Buffer = ztest.Buffer 44 | ) 45 | -------------------------------------------------------------------------------- /zaptest/writer_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package zaptest 22 | 23 | import ( 24 | "errors" 25 | "testing" 26 | 27 | "github.com/stretchr/testify/assert" 28 | ) 29 | 30 | func TestSyncer(t *testing.T) { 31 | err := errors.New("sentinel") 32 | s := &Syncer{} 33 | s.SetError(err) 34 | assert.Equal(t, err, s.Sync(), "Expected Sync to fail with provided error.") 35 | assert.True(t, s.Called(), "Expected to record that Sync was called.") 36 | } 37 | 38 | func TestDiscarder(t *testing.T) { 39 | d := &Discarder{} 40 | payload := []byte("foo") 41 | n, err := d.Write(payload) 42 | assert.NoError(t, err, "Unexpected error writing to Discarder.") 43 | assert.Equal(t, len(payload), n, "Wrong number of bytes written.") 44 | } 45 | 46 | func TestFailWriter(t *testing.T) { 47 | w := &FailWriter{} 48 | payload := []byte("foo") 49 | n, err := w.Write(payload) 50 | assert.Error(t, err, "Expected an error writing to FailWriter.") 51 | assert.Equal(t, len(payload), n, "Wrong number of bytes written.") 52 | } 53 | 54 | func TestShortWriter(t *testing.T) { 55 | w := &ShortWriter{} 56 | payload := []byte("foo") 57 | n, err := w.Write(payload) 58 | assert.NoError(t, err, "Unexpected error writing to ShortWriter.") 59 | assert.Equal(t, len(payload)-1, n, "Wrong number of bytes written.") 60 | } 61 | 62 | func TestBuffer(t *testing.T) { 63 | buf := &Buffer{} 64 | buf.WriteString("foo\n") 65 | buf.WriteString("bar\n") 66 | assert.Equal(t, []string{"foo", "bar"}, buf.Lines(), "Unexpected output from Lines.") 67 | assert.Equal(t, "foo\nbar", buf.Stripped(), "Unexpected output from Stripped.") 68 | } 69 | --------------------------------------------------------------------------------