[
  {
    "path": ".codecov.yml",
    "content": "coverage:\n  range: 80..100\n  round: down\n  precision: 2\n\n  status:\n    project:                   # measuring the overall project coverage\n      default:                 # context, you can create multiple ones with custom titles\n        enabled: yes           # must be yes|true to enable this status\n        target: 95%            # specify the target coverage for each commit status\n                               #   option: \"auto\" (must increase from parent commit or pull request base)\n                               #   option: \"X%\" a static target percentage to hit\n        if_not_found: success  # if parent is not found report status as success, error, or failure\n        if_ci_failed: error    # if ci fails report status as success, error, or failure\nignore:\n  - internal/readme/readme.go\n\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "contact_links:\n  - name: Questions\n    about: Please use our Discussions page\n    url: https://github.com/uber-go/zap/discussions\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Is this a breaking change?**\nWe 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.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n\n  # Since Zap is a library, we don't want to update its dependency requirements\n  # regularly--not until we need a newer version of a dependency for a feature\n  # or specific fix.\n  # This way, users of Zap aren't forced to upgrade all their transitive\n  # dependencies every time they upgrade Zap.\n  #\n  # However, we do want to regularly update dependencies used inside the tools\n  # submodule because that holds linters and other development tools.\n  - package-ecosystem: \"gomod\"\n    directory: \"/tools\"\n    schedule:\n      interval: \"weekly\"\n"
  },
  {
    "path": ".github/workflows/go.yml",
    "content": "name: Go\n\non:\n  push:\n    branches: [master]\n    tags: ['v*']\n  pull_request:\n    branches: ['*']\n\npermissions:\n  contents: read\n\njobs:\n\n  build:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        go: [\"1.25.x\", \"1.26.x\"]\n        include:\n        - go: 1.26.x\n\n    steps:\n    - name: Checkout code\n      uses: actions/checkout@v4\n\n    - name: Setup Go\n      uses: actions/setup-go@v5\n      with:\n        go-version: ${{ matrix.go }}\n        cache-dependency-path: '**/go.sum'\n\n    - name: Download Dependencies\n      run: |\n        go mod download\n        (cd tools && go mod download)\n        (cd benchmarks && go mod download)\n        (cd zapgrpc/internal/test && go mod download)\n\n    - name: Test\n      run: make cover\n\n    - name: Upload coverage to codecov.io\n      uses: codecov/codecov-action@v5\n      with:\n        verbose: true\n        token: ${{ secrets.CODECOV_TOKEN }}\n\n  lint:\n    name: Lint\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v4\n      name: Check out repository\n    - uses: actions/setup-go@v5\n      name: Set up Go\n      with:\n        go-version: 1.26.x\n        cache: false  # managed by golangci-lint\n\n    - uses: golangci/golangci-lint-action@v6\n      name: Install golangci-lint\n      with:\n        version: latest\n        # Hack: Use the official action to download, but not run.\n        # make lint below will handle actually running the linter.\n        args: --help\n\n    - run: make lint\n      name: Lint\n\n    - name: vulncheck\n      run: make vulncheck\n"
  },
  {
    "path": ".gitignore",
    "content": "# Compiled Object files, Static and Dynamic libs (Shared Objects)\n*.o\n*.a\n*.so\n\n# Folders\n_obj\n_test\nvendor\n\n# Architecture specific extensions/prefixes\n*.[568vq]\n[568vq].out\n\n*.cgo1.go\n*.cgo2.c\n_cgo_defun.c\n_cgo_gotypes.go\n_cgo_export.*\n\n_testmain.go\n\n*.exe\n*.test\n*.prof\n*.pprof\n*.out\n*.log\n\n/bin\ncover.out\ncover.html\n"
  },
  {
    "path": ".golangci.yml",
    "content": "output:\n  # Make output more digestible with quickfix in vim/emacs/etc.\n  sort-results: true\n  print-issued-lines: false\n\nlinters:\n  # We'll track the golangci-lint default linters manually\n  # instead of letting them change without our control.\n  disable-all: true\n  enable:\n    # golangci-lint defaults:\n    - errcheck\n    - gosimple\n    - govet\n    - ineffassign\n    - staticcheck\n    - unused\n\n    # Our own extras:\n    - gofumpt\n    - nolintlint # lints nolint directives\n    - revive\n\nlinters-settings:\n  govet:\n    # These govet checks are disabled by default, but they're useful.\n    enable:\n      - nilness\n      - reflectvaluecompare\n      - sortslice\n      - unusedwrite\n\n  errcheck:\n    exclude-functions:\n      # These methods can not fail.\n      # They operate on an in-memory buffer.\n      - (*go.uber.org/zap/buffer.Buffer).Write\n      - (*go.uber.org/zap/buffer.Buffer).WriteByte\n      - (*go.uber.org/zap/buffer.Buffer).WriteString\n\n      - (*go.uber.org/zap/zapio.Writer).Close\n      - (*go.uber.org/zap/zapio.Writer).Sync\n      - (*go.uber.org/zap/zapio.Writer).Write\n      # Write to zapio.Writer cannot fail,\n      # so io.WriteString on it cannot fail.\n      - io.WriteString(*go.uber.org/zap/zapio.Writer)\n\n      # Writing a plain string to a fmt.State cannot fail.\n      - io.WriteString(fmt.State)\n\nissues:\n  # Print all issues reported by all linters.\n  max-issues-per-linter: 0\n  max-same-issues: 0\n\n  # Don't ignore some of the issues that golangci-lint considers okay.\n  # This includes documenting all exported entities.\n  exclude-use-default: false\n\n  exclude-rules:\n    # Don't warn on unused parameters.\n    # Parameter names are useful; replacing them with '_' is undesirable.\n    - linters: [revive]\n      text: 'unused-parameter: parameter \\S+ seems to be unused, consider removing or renaming it as _'\n\n    # staticcheck already has smarter checks for empty blocks.\n    # revive's empty-block linter has false positives.\n    # For example, as of writing this, the following is not allowed.\n    #   for foo() { }\n    - linters: [revive]\n      text: 'empty-block: this block is empty, you can remove it'\n\n    # Ignore logger.Sync() errcheck failures in example_test.go\n    # since those are intended to be uncomplicated examples.\n    - linters: [errcheck]\n      path: example_test.go\n      text: 'Error return value of `logger.Sync` is not checked'\n"
  },
  {
    "path": ".readme.tmpl",
    "content": "# :zap: zap [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov]\n\n<div align=\"center\">\n\nBlazing fast, structured, leveled logging in Go.\n\n![Zap logo](assets/logo.png)\n\n[![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov]\n\n</div>\n\n## Installation\n\n`go get -u go.uber.org/zap`\n\nNote that zap only supports the two most recent minor versions of Go.\n\n## Quick Start\n\nIn contexts where performance is nice, but not critical, use the\n`SugaredLogger`. It's 4-10x faster than other structured logging\npackages and includes both structured and `printf`-style APIs.\n\n```go\nlogger, _ := zap.NewProduction()\ndefer logger.Sync() // flushes buffer, if any\nsugar := logger.Sugar()\nsugar.Infow(\"failed to fetch URL\",\n  // Structured context as loosely typed key-value pairs.\n  \"url\", url,\n  \"attempt\", 3,\n  \"backoff\", time.Second,\n)\nsugar.Infof(\"Failed to fetch URL: %s\", url)\n```\n\nWhen performance and type safety are critical, use the `Logger`. It's even\nfaster than the `SugaredLogger` and allocates far less, but it only supports\nstructured logging.\n\n```go\nlogger, _ := zap.NewProduction()\ndefer logger.Sync()\nlogger.Info(\"failed to fetch URL\",\n  // Structured context as strongly typed Field values.\n  zap.String(\"url\", url),\n  zap.Int(\"attempt\", 3),\n  zap.Duration(\"backoff\", time.Second),\n)\n```\n\nSee the [documentation][doc] and [FAQ](FAQ.md) for more details.\n\n## Performance\n\nFor applications that log in the hot path, reflection-based serialization and\nstring formatting are prohibitively expensive &mdash; they're CPU-intensive\nand make many small allocations. Put differently, using `encoding/json` and\n`fmt.Fprintf` to log tons of `interface{}`s makes your application slow.\n\nZap takes a different approach. It includes a reflection-free, zero-allocation\nJSON encoder, and the base `Logger` strives to avoid serialization overhead\nand allocations wherever possible. By building the high-level `SugaredLogger`\non that foundation, zap lets users *choose* when they need to count every\nallocation and when they'd prefer a more familiar, loosely typed API.\n\nAs measured by its own [benchmarking suite][], not only is zap more performant\nthan comparable structured logging packages &mdash; it's also faster than the\nstandard library. Like all benchmarks, take these with a grain of salt.<sup\nid=\"anchor-versions\">[1](#footnote-versions)</sup>\n\nLog a message and 10 fields:\n\n{{.BenchmarkAddingFields}}\n\nLog a message with a logger that already has 10 fields of context:\n\n{{.BenchmarkAccumulatedContext}}\n\nLog a static string, without any context or `printf`-style templating:\n\n{{.BenchmarkWithoutFields}}\n\n## Development Status: Stable\n\nAll APIs are finalized, and no breaking changes will be made in the 1.x series\nof releases. Users of semver-aware dependency management systems should pin\nzap to `^1`.\n\n## Contributing\n\nWe encourage and support an active, healthy community of contributors &mdash;\nincluding you! Details are in the [contribution guide](CONTRIBUTING.md) and\nthe [code of conduct](CODE_OF_CONDUCT.md). The zap maintainers keep an eye on\nissues and pull requests, but you can also report any negative conduct to\noss-conduct@uber.com. That email list is a private, safe space; even the zap\nmaintainers don't have access, so don't hesitate to hold us to a high\nstandard.\n\n<hr>\n\nReleased under the [MIT License](LICENSE).\n\n<sup id=\"footnote-versions\">1</sup> In particular, keep in mind that we may be\nbenchmarking against slightly older versions of other packages. Versions are\npinned in the [benchmarks/go.mod][] file. [↩](#anchor-versions)\n\n[doc-img]: https://pkg.go.dev/badge/go.uber.org/zap\n[doc]: https://pkg.go.dev/go.uber.org/zap\n[ci-img]: https://github.com/uber-go/zap/actions/workflows/go.yml/badge.svg\n[ci]: https://github.com/uber-go/zap/actions/workflows/go.yml\n[cov-img]: https://codecov.io/gh/uber-go/zap/branch/master/graph/badge.svg\n[cov]: https://codecov.io/gh/uber-go/zap\n[benchmarking suite]: https://github.com/uber-go/zap/tree/master/benchmarks\n[benchmarks/go.mod]: https://github.com/uber-go/zap/blob/master/benchmarks/go.mod\n\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\nAll notable changes to this project will be documented in this file.\n\nThis project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## 1.27.1 (19 Nov 2025) \nEnhancements:\n* [#1501][]: prevent `Object` from panicking on nils \n* [#1511][]: Fix a race condition in `WithLazy`.\n\nThanks to @rabbbit, @alshopov, @jquirke, @arukiidou for their contributions to this release.\n\n[#1501]: https://github.com/uber-go/zap/pull/1501\n[#1511]: https://github.com/uber-go/zap/pull/1511\n\n## 1.27.0 (20 Feb 2024)\nEnhancements:\n* [#1378][]: Add `WithLazy` method for `SugaredLogger`.\n* [#1399][]: zaptest: Add `NewTestingWriter` for customizing TestingWriter with more flexibility than `NewLogger`.\n* [#1406][]: Add `Log`, `Logw`, `Logln` methods for `SugaredLogger`.\n* [#1416][]: Add `WithPanicHook` option for testing panic logs.\n\nThanks to @defval, @dimmo, @arxeiss, and @MKrupauskas for their contributions to this release.\n\n[#1378]: https://github.com/uber-go/zap/pull/1378\n[#1399]: https://github.com/uber-go/zap/pull/1399\n[#1406]: https://github.com/uber-go/zap/pull/1406\n[#1416]: https://github.com/uber-go/zap/pull/1416\n\n## 1.26.0 (14 Sep 2023)\nEnhancements:\n* [#1297][]: Add Dict as a Field.\n* [#1319][]: Add `WithLazy` method to `Logger` which lazily evaluates the structured\ncontext.\n* [#1350][]: String encoding is much (~50%) faster now.\n\nThanks to @hhk7734, @jquirke, and @cdvr1993 for their contributions to this release.\n\n[#1297]: https://github.com/uber-go/zap/pull/1297\n[#1319]: https://github.com/uber-go/zap/pull/1319\n[#1350]: https://github.com/uber-go/zap/pull/1350\n\n## 1.25.0 (1 Aug 2023)\n\nThis release contains several improvements including performance, API additions,\nand two new experimental packages whose APIs are unstable and may change in the\nfuture.\n\nEnhancements:\n* [#1246][]: Add `zap/exp/zapslog` package for integration with slog.\n* [#1273][]: Add `Name` to `Logger` which returns the Logger's name if one is set.\n* [#1281][]: Add `zap/exp/expfield` package which contains helper methods\n`Str` and `Strs` for constructing String-like zap.Fields.\n* [#1310][]: Reduce stack size on `Any`.\n\nThanks to @knight42, @dzakaammar, @bcspragu, and @rexywork for their contributions\nto this release.\n\n[#1246]: https://github.com/uber-go/zap/pull/1246\n[#1273]: https://github.com/uber-go/zap/pull/1273\n[#1281]: https://github.com/uber-go/zap/pull/1281\n[#1310]: https://github.com/uber-go/zap/pull/1310\n\n## 1.24.0 (30 Nov 2022)\n\nEnhancements:\n* [#1148][]: Add `Level` to both `Logger` and `SugaredLogger` that reports the\n  current minimum enabled log level.\n* [#1185][]: `SugaredLogger` turns errors to zap.Error automatically.\n\nThanks to @Abirdcfly, @craigpastro, @nnnkkk7, and @sashamelentyev for their\ncontributions to this release.\n\n[#1148]: https://github.coml/uber-go/zap/pull/1148\n[#1185]: https://github.coml/uber-go/zap/pull/1185\n\n## 1.23.0 (24 Aug 2022)\n\nEnhancements:\n* [#1147][]: Add a `zapcore.LevelOf` function to determine the level of a\n  `LevelEnabler` or `Core`.\n* [#1155][]: Add `zap.Stringers` field constructor to log arrays of objects\n  that implement `String() string`.\n\n[#1147]: https://github.com/uber-go/zap/pull/1147\n[#1155]: https://github.com/uber-go/zap/pull/1155\n\n## 1.22.0 (8 Aug 2022)\n\nEnhancements:\n* [#1071][]: Add `zap.Objects` and `zap.ObjectValues` field constructors to log\n  arrays of objects. With these two constructors, you don't need to implement\n  `zapcore.ArrayMarshaler` for use with `zap.Array` if those objects implement\n  `zapcore.ObjectMarshaler`.\n* [#1079][]: Add `SugaredLogger.WithOptions` to build a copy of an existing\n  `SugaredLogger` with the provided options applied.\n* [#1080][]: Add `*ln` variants to `SugaredLogger` for each log level.\n  These functions provide a string joining behavior similar to `fmt.Println`.\n* [#1088][]: Add `zap.WithFatalHook` option to control the behavior of the\n  logger for `Fatal`-level log entries. This defaults to exiting the program.\n* [#1108][]: Add a `zap.Must` function that you can use with `NewProduction` or\n  `NewDevelopment` to panic if the system was unable to build the logger.\n* [#1118][]: Add a `Logger.Log` method that allows specifying the log level for\n  a statement dynamically.\n\nThanks to @cardil, @craigpastro, @sashamelentyev, @shota3506, and @zhupeijun\nfor their contributions to this release.\n\n[#1071]: https://github.com/uber-go/zap/pull/1071\n[#1079]: https://github.com/uber-go/zap/pull/1079\n[#1080]: https://github.com/uber-go/zap/pull/1080\n[#1088]: https://github.com/uber-go/zap/pull/1088\n[#1108]: https://github.com/uber-go/zap/pull/1108\n[#1118]: https://github.com/uber-go/zap/pull/1118\n\n## 1.21.0 (7 Feb 2022)\n\nEnhancements:\n*  [#1047][]: Add `zapcore.ParseLevel` to parse a `Level` from a string.\n*  [#1048][]: Add `zap.ParseAtomicLevel` to parse an `AtomicLevel` from a\n   string.\n\nBugfixes:\n* [#1058][]: Fix panic in JSON encoder when `EncodeLevel` is unset.\n\nOther changes:\n* [#1052][]: Improve encoding performance when the `AddCaller` and\n  `AddStacktrace` options are used together.\n\n[#1047]: https://github.com/uber-go/zap/pull/1047\n[#1048]: https://github.com/uber-go/zap/pull/1048\n[#1052]: https://github.com/uber-go/zap/pull/1052\n[#1058]: https://github.com/uber-go/zap/pull/1058\n\nThanks to @aerosol and @Techassi for their contributions to this release.\n\n## 1.20.0 (4 Jan 2022)\n\nEnhancements:\n* [#989][]: Add `EncoderConfig.SkipLineEnding` flag to disable adding newline\n  characters between log statements.\n* [#1039][]: Add `EncoderConfig.NewReflectedEncoder` field to customize JSON\n  encoding of reflected log fields.\n\nBugfixes:\n* [#1011][]: Fix inaccurate precision when encoding complex64 as JSON.\n* [#554][], [#1017][]: Close JSON namespaces opened in `MarshalLogObject`\n  methods when the methods return.\n* [#1033][]: Avoid panicking in Sampler core if `thereafter` is zero.\n\nOther changes:\n* [#1028][]: Drop support for Go < 1.15.\n\n[#554]: https://github.com/uber-go/zap/pull/554\n[#989]: https://github.com/uber-go/zap/pull/989\n[#1011]: https://github.com/uber-go/zap/pull/1011\n[#1017]: https://github.com/uber-go/zap/pull/1017\n[#1028]: https://github.com/uber-go/zap/pull/1028\n[#1033]: https://github.com/uber-go/zap/pull/1033\n[#1039]: https://github.com/uber-go/zap/pull/1039\n\nThanks to @psrajat, @lruggieri, @sammyrnycreal for their contributions to this release.\n\n## 1.19.1 (8 Sep 2021)\n\nBugfixes:\n* [#1001][]: JSON: Fix complex number encoding with negative imaginary part. Thanks to @hemantjadon.\n* [#1003][]: JSON: Fix inaccurate precision when encoding float32.\n\n[#1001]: https://github.com/uber-go/zap/pull/1001\n[#1003]: https://github.com/uber-go/zap/pull/1003\n\n## 1.19.0 (9 Aug 2021)\n\nEnhancements:\n* [#975][]: Avoid panicking in Sampler core if the level is out of bounds.\n* [#984][]: Reduce the size of BufferedWriteSyncer by aligning the fields\n  better.\n\n[#975]: https://github.com/uber-go/zap/pull/975\n[#984]: https://github.com/uber-go/zap/pull/984\n\nThanks to @lancoLiu and @thockin for their contributions to this release.\n\n## 1.18.1 (28 Jun 2021)\n\nBugfixes:\n* [#974][]: Fix nil dereference in logger constructed by `zap.NewNop`.\n\n[#974]: https://github.com/uber-go/zap/pull/974\n\n## 1.18.0 (28 Jun 2021)\n\nEnhancements:\n* [#961][]: Add `zapcore.BufferedWriteSyncer`, a new `WriteSyncer` that buffers\n  messages in-memory and flushes them periodically.\n* [#971][]: Add `zapio.Writer` to use a Zap logger as an `io.Writer`.\n* [#897][]: Add `zap.WithClock` option to control the source of time via the\n  new `zapcore.Clock` interface.\n* [#949][]: Avoid panicking in `zap.SugaredLogger` when arguments of `*w`\n  methods don't match expectations.\n* [#943][]: Add support for filtering by level or arbitrary matcher function to\n  `zaptest/observer`.\n* [#691][]: Comply with `io.StringWriter` and `io.ByteWriter` in Zap's\n  `buffer.Buffer`.\n\nThanks to @atrn0, @ernado, @heyanfu, @hnlq715, @zchee\nfor their contributions to this release.\n\n[#691]: https://github.com/uber-go/zap/pull/691\n[#897]: https://github.com/uber-go/zap/pull/897\n[#943]: https://github.com/uber-go/zap/pull/943\n[#949]: https://github.com/uber-go/zap/pull/949\n[#961]: https://github.com/uber-go/zap/pull/961\n[#971]: https://github.com/uber-go/zap/pull/971\n\n## 1.17.0 (25 May 2021)\n\nBugfixes:\n* [#867][]: Encode `<nil>` for nil `error` instead of a panic.\n* [#931][], [#936][]: Update minimum version constraints to address\n  vulnerabilities in dependencies.\n\nEnhancements:\n* [#865][]: Improve alignment of fields of the Logger struct, reducing its\n  size from 96 to 80 bytes.\n* [#881][]: Support `grpclog.LoggerV2` in zapgrpc.\n* [#903][]: Support URL-encoded POST requests to the AtomicLevel HTTP handler\n  with the `application/x-www-form-urlencoded` content type.\n* [#912][]: Support multi-field encoding with `zap.Inline`.\n* [#913][]: Speed up SugaredLogger for calls with a single string.\n* [#928][]: Add support for filtering by field name to `zaptest/observer`.\n\nThanks to @ash2k, @FMLS, @jimmystewpot, @Oncilla, @tsoslow, @tylitianrui, @withshubh, and @wziww for their contributions to this release.\n\n[#865]: https://github.com/uber-go/zap/pull/865\n[#867]: https://github.com/uber-go/zap/pull/867\n[#881]: https://github.com/uber-go/zap/pull/881\n[#903]: https://github.com/uber-go/zap/pull/903\n[#912]: https://github.com/uber-go/zap/pull/912\n[#913]: https://github.com/uber-go/zap/pull/913\n[#928]: https://github.com/uber-go/zap/pull/928\n[#931]: https://github.com/uber-go/zap/pull/931\n[#936]: https://github.com/uber-go/zap/pull/936\n\n## 1.16.0 (1 Sep 2020)\n\nBugfixes:\n* [#828][]: Fix missing newline in IncreaseLevel error messages.\n* [#835][]: Fix panic in JSON encoder when encoding times or durations\n  without specifying a time or duration encoder.\n* [#843][]: Honor CallerSkip when taking stack traces.\n* [#862][]: Fix the default file permissions to use `0666` and rely on the umask instead.\n* [#854][]: Encode `<nil>` for nil `Stringer` instead of a panic error log.\n\nEnhancements:\n* [#629][]: Added `zapcore.TimeEncoderOfLayout` to easily create time encoders\n  for custom layouts.\n* [#697][]: Added support for a configurable delimiter in the console encoder.\n* [#852][]: Optimize console encoder by pooling the underlying JSON encoder.\n* [#844][]: Add ability to include the calling function as part of logs.\n* [#843][]: Add `StackSkip` for including truncated stacks as a field.\n* [#861][]: Add options to customize Fatal behaviour for better testability.\n\nThanks to @SteelPhase, @tmshn, @lixingwang, @wyxloading, @moul, @segevfiner, @andy-retailnext and @jcorbin for their contributions to this release.\n\n[#629]: https://github.com/uber-go/zap/pull/629\n[#697]: https://github.com/uber-go/zap/pull/697\n[#828]: https://github.com/uber-go/zap/pull/828\n[#835]: https://github.com/uber-go/zap/pull/835\n[#843]: https://github.com/uber-go/zap/pull/843\n[#844]: https://github.com/uber-go/zap/pull/844\n[#852]: https://github.com/uber-go/zap/pull/852\n[#854]: https://github.com/uber-go/zap/pull/854\n[#861]: https://github.com/uber-go/zap/pull/861\n[#862]: https://github.com/uber-go/zap/pull/862\n\n## 1.15.0 (23 Apr 2020)\n\nBugfixes:\n* [#804][]: Fix handling of `Time` values out of `UnixNano` range.\n* [#812][]: Fix `IncreaseLevel` being reset after a call to `With`.\n\nEnhancements:\n* [#806][]: Add `WithCaller` option to supersede the `AddCaller` option. This\n  allows disabling annotation of log entries with caller information if\n  previously enabled with `AddCaller`.\n* [#813][]: Deprecate `NewSampler` constructor in favor of\n  `NewSamplerWithOptions` which supports a `SamplerHook` option. This option\n   adds support for monitoring sampling decisions through a hook.\n\nThanks to @danielbprice for their contributions to this release.\n\n[#804]: https://github.com/uber-go/zap/pull/804\n[#812]: https://github.com/uber-go/zap/pull/812\n[#806]: https://github.com/uber-go/zap/pull/806\n[#813]: https://github.com/uber-go/zap/pull/813\n\n## 1.14.1 (14 Mar 2020)\n\nBugfixes:\n* [#791][]: Fix panic on attempting to build a logger with an invalid Config.\n* [#795][]: Vendoring Zap with `go mod vendor` no longer includes Zap's\n  development-time dependencies.\n* [#799][]: Fix issue introduced in 1.14.0 that caused invalid JSON output to\n  be generated for arrays of `time.Time` objects when using string-based time\n  formats.\n\nThanks to @YashishDua for their contributions to this release.\n\n[#791]: https://github.com/uber-go/zap/pull/791\n[#795]: https://github.com/uber-go/zap/pull/795\n[#799]: https://github.com/uber-go/zap/pull/799\n\n## 1.14.0 (20 Feb 2020)\n\nEnhancements:\n* [#771][]: Optimize calls for disabled log levels.\n* [#773][]: Add millisecond duration encoder.\n* [#775][]: Add option to increase the level of a logger.\n* [#786][]: Optimize time formatters using `Time.AppendFormat` where possible.\n\nThanks to @caibirdme for their contributions to this release.\n\n[#771]: https://github.com/uber-go/zap/pull/771\n[#773]: https://github.com/uber-go/zap/pull/773\n[#775]: https://github.com/uber-go/zap/pull/775\n[#786]: https://github.com/uber-go/zap/pull/786\n\n## 1.13.0 (13 Nov 2019)\n\nEnhancements:\n* [#758][]: Add `Intp`, `Stringp`, and other similar `*p` field constructors\n  to log pointers to primitives with support for `nil` values.\n\nThanks to @jbizzle for their contributions to this release.\n\n[#758]: https://github.com/uber-go/zap/pull/758\n\n## 1.12.0 (29 Oct 2019)\n\nEnhancements:\n* [#751][]: Migrate to Go modules.\n\n[#751]: https://github.com/uber-go/zap/pull/751\n\n## 1.11.0 (21 Oct 2019)\n\nEnhancements:\n* [#725][]: Add `zapcore.OmitKey` to omit keys in an `EncoderConfig`.\n* [#736][]: Add `RFC3339` and `RFC3339Nano` time encoders.\n\nThanks to @juicemia, @uhthomas for their contributions to this release.\n\n[#725]: https://github.com/uber-go/zap/pull/725\n[#736]: https://github.com/uber-go/zap/pull/736\n\n## 1.10.0 (29 Apr 2019)\n\nBugfixes:\n* [#657][]: Fix `MapObjectEncoder.AppendByteString` not adding value as a\n  string.\n* [#706][]: Fix incorrect call depth to determine caller in Go 1.12.\n\nEnhancements:\n* [#610][]: Add `zaptest.WrapOptions` to wrap `zap.Option` for creating test\n  loggers.\n* [#675][]: Don't panic when encoding a String field.\n* [#704][]: Disable HTML escaping for JSON objects encoded using the\n  reflect-based encoder.\n\nThanks to @iaroslav-ciupin, @lelenanam, @joa, @NWilson for their contributions\nto this release.\n\n[#657]: https://github.com/uber-go/zap/pull/657\n[#706]: https://github.com/uber-go/zap/pull/706\n[#610]: https://github.com/uber-go/zap/pull/610\n[#675]: https://github.com/uber-go/zap/pull/675\n[#704]: https://github.com/uber-go/zap/pull/704\n\n## 1.9.1 (06 Aug 2018)\n\nBugfixes:\n\n* [#614][]: MapObjectEncoder should not ignore empty slices.\n\n[#614]: https://github.com/uber-go/zap/pull/614\n\n## 1.9.0 (19 Jul 2018)\n\nEnhancements:\n* [#602][]: Reduce number of allocations when logging with reflection.\n* [#572][], [#606][]: Expose a registry for third-party logging sinks.\n\nThanks to @nfarah86, @AlekSi, @JeanMertz, @philippgille, @etsangsplk, and\n@dimroc for their contributions to this release.\n\n[#602]: https://github.com/uber-go/zap/pull/602\n[#572]: https://github.com/uber-go/zap/pull/572\n[#606]: https://github.com/uber-go/zap/pull/606\n\n## 1.8.0 (13 Apr 2018)\n\nEnhancements:\n* [#508][]: Make log level configurable when redirecting the standard\n  library's logger.\n* [#518][]: Add a logger that writes to a `*testing.TB`.\n* [#577][]: Add a top-level alias for `zapcore.Field` to clean up GoDoc.\n\nBugfixes:\n* [#574][]: Add a missing import comment to `go.uber.org/zap/buffer`.\n\nThanks to @DiSiqueira and @djui for their contributions to this release.\n\n[#508]: https://github.com/uber-go/zap/pull/508\n[#518]: https://github.com/uber-go/zap/pull/518\n[#577]: https://github.com/uber-go/zap/pull/577\n[#574]: https://github.com/uber-go/zap/pull/574\n\n## 1.7.1 (25 Sep 2017)\n\nBugfixes:\n* [#504][]: Store strings when using AddByteString with the map encoder.\n\n[#504]: https://github.com/uber-go/zap/pull/504\n\n## 1.7.0 (21 Sep 2017)\n\nEnhancements:\n\n* [#487][]: Add `NewStdLogAt`, which extends `NewStdLog` by allowing the user\n  to specify the level of the logged messages.\n\n[#487]: https://github.com/uber-go/zap/pull/487\n\n## 1.6.0 (30 Aug 2017)\n\nEnhancements:\n\n* [#491][]: Omit zap stack frames from stacktraces.\n* [#490][]: Add a `ContextMap` method to observer logs for simpler\n  field validation in tests.\n\n[#490]: https://github.com/uber-go/zap/pull/490\n[#491]: https://github.com/uber-go/zap/pull/491\n\n## 1.5.0 (22 Jul 2017)\n\nEnhancements:\n\n* [#460][] and [#470][]: Support errors produced by `go.uber.org/multierr`.\n* [#465][]: Support user-supplied encoders for logger names.\n\nBugfixes:\n\n* [#477][]: Fix a bug that incorrectly truncated deep stacktraces.\n\nThanks to @richard-tunein and @pavius for their contributions to this release.\n\n[#477]: https://github.com/uber-go/zap/pull/477\n[#465]: https://github.com/uber-go/zap/pull/465\n[#460]: https://github.com/uber-go/zap/pull/460\n[#470]: https://github.com/uber-go/zap/pull/470\n\n## 1.4.1 (08 Jun 2017)\n\nThis release fixes two bugs.\n\nBugfixes:\n\n* [#435][]: Support a variety of case conventions when unmarshaling levels.\n* [#444][]: Fix a panic in the observer.\n\n[#435]: https://github.com/uber-go/zap/pull/435\n[#444]: https://github.com/uber-go/zap/pull/444\n\n## 1.4.0 (12 May 2017)\n\nThis release adds a few small features and is fully backward-compatible.\n\nEnhancements:\n\n* [#424][]: Add a `LineEnding` field to `EncoderConfig`, allowing users to\n  override the Unix-style default.\n* [#425][]: Preserve time zones when logging times.\n* [#431][]: Make `zap.AtomicLevel` implement `fmt.Stringer`, which makes a\n  variety of operations a bit simpler.\n\n[#424]: https://github.com/uber-go/zap/pull/424\n[#425]: https://github.com/uber-go/zap/pull/425\n[#431]: https://github.com/uber-go/zap/pull/431\n\n## 1.3.0 (25 Apr 2017)\n\nThis release adds an enhancement to zap's testing helpers as well as the\nability to marshal an AtomicLevel. It is fully backward-compatible.\n\nEnhancements:\n\n* [#415][]: Add a substring-filtering helper to zap's observer. This is\n  particularly useful when testing the `SugaredLogger`.\n* [#416][]: Make `AtomicLevel` implement `encoding.TextMarshaler`.\n\n[#415]: https://github.com/uber-go/zap/pull/415\n[#416]: https://github.com/uber-go/zap/pull/416\n\n## 1.2.0 (13 Apr 2017)\n\nThis release adds a gRPC compatibility wrapper. It is fully backward-compatible.\n\nEnhancements:\n\n* [#402][]: Add a `zapgrpc` package that wraps zap's Logger and implements\n  `grpclog.Logger`.\n\n[#402]: https://github.com/uber-go/zap/pull/402\n\n## 1.1.0 (31 Mar 2017)\n\nThis release fixes two bugs and adds some enhancements to zap's testing helpers.\nIt is fully backward-compatible.\n\nBugfixes:\n\n* [#385][]: Fix caller path trimming on Windows.\n* [#396][]: Fix a panic when attempting to use non-existent directories with\n  zap's configuration struct.\n\nEnhancements:\n\n* [#386][]: Add filtering helpers to zaptest's observing logger.\n\nThanks to @moitias for contributing to this release.\n\n[#385]: https://github.com/uber-go/zap/pull/385\n[#396]: https://github.com/uber-go/zap/pull/396\n[#386]: https://github.com/uber-go/zap/pull/386\n\n## 1.0.0 (14 Mar 2017)\n\nThis is zap's first stable release. All exported APIs are now final, and no\nfurther breaking changes will be made in the 1.x release series. Anyone using a\nsemver-aware dependency manager should now pin to `^1`.\n\nBreaking changes:\n\n* [#366][]: Add byte-oriented APIs to encoders to log UTF-8 encoded text without\n  casting from `[]byte` to `string`.\n* [#364][]: To support buffering outputs, add `Sync` methods to `zapcore.Core`,\n  `zap.Logger`, and `zap.SugaredLogger`.\n* [#371][]: Rename the `testutils` package to `zaptest`, which is less likely to\n  clash with other testing helpers.\n\nBugfixes:\n\n* [#362][]: Make the ISO8601 time formatters fixed-width, which is friendlier\n  for tab-separated console output.\n* [#369][]: Remove the automatic locks in `zapcore.NewCore`, which allows zap to\n  work with concurrency-safe `WriteSyncer` implementations.\n* [#347][]: Stop reporting errors when trying to `fsync` standard out on Linux\n  systems.\n* [#373][]: Report the correct caller from zap's standard library\n  interoperability wrappers.\n\nEnhancements:\n\n* [#348][]: Add a registry allowing third-party encodings to work with zap's\n  built-in `Config`.\n* [#327][]: Make the representation of logger callers configurable (like times,\n  levels, and durations).\n* [#376][]: Allow third-party encoders to use their own buffer pools, which\n  removes the last performance advantage that zap's encoders have over plugins.\n* [#346][]: Add `CombineWriteSyncers`, a convenience function to tee multiple\n  `WriteSyncer`s and lock the result.\n* [#365][]: Make zap's stacktraces compatible with mid-stack inlining (coming in\n  Go 1.9).\n* [#372][]: Export zap's observing logger as `zaptest/observer`. This makes it\n  easier for particularly punctilious users to unit test their application's\n  logging.\n\nThanks to @suyash, @htrendev, @flisky, @Ulexus, and @skipor for their\ncontributions to this release.\n\n[#366]: https://github.com/uber-go/zap/pull/366\n[#364]: https://github.com/uber-go/zap/pull/364\n[#371]: https://github.com/uber-go/zap/pull/371\n[#362]: https://github.com/uber-go/zap/pull/362\n[#369]: https://github.com/uber-go/zap/pull/369\n[#347]: https://github.com/uber-go/zap/pull/347\n[#373]: https://github.com/uber-go/zap/pull/373\n[#348]: https://github.com/uber-go/zap/pull/348\n[#327]: https://github.com/uber-go/zap/pull/327\n[#376]: https://github.com/uber-go/zap/pull/376\n[#346]: https://github.com/uber-go/zap/pull/346\n[#365]: https://github.com/uber-go/zap/pull/365\n[#372]: https://github.com/uber-go/zap/pull/372\n\n## 1.0.0-rc.3 (7 Mar 2017)\n\nThis is the third release candidate for zap's stable release. There are no\nbreaking changes.\n\nBugfixes:\n\n* [#339][]: Byte slices passed to `zap.Any` are now correctly treated as binary blobs\n  rather than `[]uint8`.\n\nEnhancements:\n\n* [#307][]: Users can opt into colored output for log levels.\n* [#353][]: In addition to hijacking the output of the standard library's\n  package-global logging functions, users can now construct a zap-backed\n  `log.Logger` instance.\n* [#311][]: Frames from common runtime functions and some of zap's internal\n  machinery are now omitted from stacktraces.\n\nThanks to @ansel1 and @suyash for their contributions to this release.\n\n[#339]: https://github.com/uber-go/zap/pull/339\n[#307]: https://github.com/uber-go/zap/pull/307\n[#353]: https://github.com/uber-go/zap/pull/353\n[#311]: https://github.com/uber-go/zap/pull/311\n\n## 1.0.0-rc.2 (21 Feb 2017)\n\nThis is the second release candidate for zap's stable release. It includes two\nbreaking changes.\n\nBreaking changes:\n\n* [#316][]: Zap's global loggers are now fully concurrency-safe\n  (previously, users had to ensure that `ReplaceGlobals` was called before the\n  loggers were in use). However, they must now be accessed via the `L()` and\n  `S()` functions. Users can update their projects with\n\n  ```\n  gofmt -r \"zap.L -> zap.L()\" -w .\n  gofmt -r \"zap.S -> zap.S()\" -w .\n  ```\n* [#309][] and [#317][]: RC1 was mistakenly shipped with invalid\n  JSON and YAML struct tags on all config structs. This release fixes the tags\n  and adds static analysis to prevent similar bugs in the future.\n\nBugfixes:\n\n* [#321][]: Redirecting the standard library's `log` output now\n  correctly reports the logger's caller.\n\nEnhancements:\n\n* [#325][] and [#333][]: Zap now transparently supports non-standard, rich\n  errors like those produced by `github.com/pkg/errors`.\n* [#326][]: Though `New(nil)` continues to return a no-op logger, `NewNop()` is\n  now preferred. Users can update their projects with `gofmt -r 'zap.New(nil) ->\n  zap.NewNop()' -w .`.\n* [#300][]: Incorrectly importing zap as `github.com/uber-go/zap` now returns a\n  more informative error.\n\nThanks to @skipor and @chapsuk for their contributions to this release.\n\n[#316]: https://github.com/uber-go/zap/pull/316\n[#309]: https://github.com/uber-go/zap/pull/309\n[#317]: https://github.com/uber-go/zap/pull/317\n[#321]: https://github.com/uber-go/zap/pull/321\n[#325]: https://github.com/uber-go/zap/pull/325\n[#333]: https://github.com/uber-go/zap/pull/333\n[#326]: https://github.com/uber-go/zap/pull/326\n[#300]: https://github.com/uber-go/zap/pull/300\n\n## 1.0.0-rc.1 (14 Feb 2017)\n\nThis is the first release candidate for zap's stable release. There are multiple\nbreaking changes and improvements from the pre-release version. Most notably:\n\n* **Zap's import path is now \"go.uber.org/zap\"** &mdash; all users will\n  need to update their code.\n* User-facing types and functions remain in the `zap` package. Code relevant\n  largely to extension authors is now in the `zapcore` package.\n* The `zapcore.Core` type makes it easy for third-party packages to use zap's\n  internals but provide a different user-facing API.\n* `Logger` is now a concrete type instead of an interface.\n* A less verbose (though slower) logging API is included by default.\n* Package-global loggers `L` and `S` are included.\n* A human-friendly console encoder is included.\n* A declarative config struct allows common logger configurations to be managed\n  as configuration instead of code.\n* Sampling is more accurate, and doesn't depend on the standard library's shared\n  timer heap.\n\n## 0.1.0-beta.1 (6 Feb 2017)\n\nThis is a minor version, tagged to allow users to pin to the pre-1.0 APIs and\nupgrade at their leisure. Since this is the first tagged release, there are no\nbackward compatibility concerns and all functionality is new.\n\nEarly zap adopters should pin to the 0.1.x minor version until they're ready to\nupgrade to the upcoming stable release.\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age,\nbody size, disability, ethnicity, gender identity and expression, level of\nexperience, nationality, personal appearance, race, religion, or sexual\nidentity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\n  advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n  address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an\nappointed representative at an online or offline event. Representation of a\nproject may be further defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at oss-conduct@uber.com. The project\nteam will review and investigate all complaints, and will respond in a way\nthat it deems appropriate to the circumstances. The project team is obligated\nto maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 1.4, available at\n[http://contributor-covenant.org/version/1/4][version].\n\n[homepage]: https://contributor-covenant.org\n[version]: https://contributor-covenant.org/version/1/4/\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\nWe'd love your help making zap the very best structured logging library in Go!\n\nIf you'd like to add new exported APIs, please [open an issue][open-issue]\ndescribing your proposal &mdash; discussing API changes ahead of time makes\npull request review much smoother. In your issue, pull request, and any other\ncommunications, please remember to treat your fellow contributors with\nrespect! We take our [code of conduct](CODE_OF_CONDUCT.md) seriously.\n\nNote that you'll need to sign [Uber's Contributor License Agreement][cla]\nbefore we can accept any of your contributions. If necessary, a bot will remind\nyou to accept the CLA when you open your pull request.\n\n## Setup\n\n[Fork][fork], then clone the repository:\n\n```bash\nmkdir -p $GOPATH/src/go.uber.org\ncd $GOPATH/src/go.uber.org\ngit clone git@github.com:your_github_username/zap.git\ncd zap\ngit remote add upstream https://github.com/uber-go/zap.git\ngit fetch upstream\n```\n\nMake sure that the tests and the linters pass:\n\n```bash\nmake test\nmake lint\n```\n\n## Making Changes\n\nStart by creating a new branch for your changes:\n\n```bash\ncd $GOPATH/src/go.uber.org/zap\ngit checkout master\ngit fetch upstream\ngit rebase upstream/master\ngit checkout -b cool_new_feature\n```\n\nMake your changes, then ensure that `make lint` and `make test` still pass. If\nyou're satisfied with your changes, push them to your fork.\n\n```bash\ngit push origin cool_new_feature\n```\n\nThen use the GitHub UI to open a pull request.\n\nAt this point, you're waiting on us to review your changes. We _try_ to respond\nto issues and pull requests within a few business days, and we may suggest some\nimprovements or alternatives. Once your changes are approved, one of the\nproject maintainers will merge them.\n\nWe're much more likely to approve your changes if you:\n\n- Add tests for new functionality.\n- Write a [good commit message][commit-message].\n- Maintain backward compatibility.\n\n[fork]: https://github.com/uber-go/zap/fork\n[open-issue]: https://github.com/uber-go/zap/issues/new\n[cla]: https://cla-assistant.io/uber-go/zap\n[commit-message]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html\n"
  },
  {
    "path": "FAQ.md",
    "content": "# Frequently Asked Questions\n\n## Design\n\n### Why spend so much effort on logger performance?\n\nOf course, most applications won't notice the impact of a slow logger: they\nalready take tens or hundreds of milliseconds for each operation, so an extra\nmillisecond doesn't matter.\n\nOn the other hand, why *not* make structured logging fast? The `SugaredLogger`\nisn't any harder to use than other logging packages, and the `Logger` makes\nstructured logging possible in performance-sensitive contexts. Across a fleet\nof Go microservices, making each application even slightly more efficient adds\nup quickly.\n\n### Why aren't `Logger` and `SugaredLogger` interfaces?\n\nUnlike the familiar `io.Writer` and `http.Handler`, `Logger` and\n`SugaredLogger` interfaces would include *many* methods. As [Rob Pike points\nout][go-proverbs], \"The bigger the interface, the weaker the abstraction.\"\nInterfaces are also rigid &mdash; *any* change requires releasing a new major\nversion, since it breaks all third-party implementations.\n\nMaking the `Logger` and `SugaredLogger` concrete types doesn't sacrifice much\nabstraction, and it lets us add methods without introducing breaking changes.\nYour applications should define and depend upon an interface that includes\njust the methods you use.\n\n### Why are some of my logs missing?\n\nLogs are dropped intentionally by zap when sampling is enabled. The production\nconfiguration (as returned by `NewProductionConfig()` enables sampling which will\ncause repeated logs within a second to be sampled. See more details on why sampling\nis enabled in [Why sample application logs](https://github.com/uber-go/zap/blob/master/FAQ.md#why-sample-application-logs).\n\n### Why sample application logs?\n\nApplications often experience runs of errors, either because of a bug or\nbecause of a misbehaving user. Logging errors is usually a good idea, but it\ncan easily make this bad situation worse: not only is your application coping\nwith a flood of errors, it's also spending extra CPU cycles and I/O logging\nthose errors. Since writes are typically serialized, logging limits throughput\nwhen you need it most.\n\nSampling fixes this problem by dropping repetitive log entries. Under normal\nconditions, your application writes out every entry. When similar entries are\nlogged hundreds or thousands of times each second, though, zap begins dropping\nduplicates to preserve throughput.\n\n### Why do the structured logging APIs take a message in addition to fields?\n\nSubjectively, we find it helpful to accompany structured context with a brief\ndescription. This isn't critical during development, but it makes debugging\nand operating unfamiliar systems much easier.\n\nMore concretely, zap's sampling algorithm uses the message to identify\nduplicate entries. In our experience, this is a practical middle ground\nbetween random sampling (which often drops the exact entry that you need while\ndebugging) and hashing the complete entry (which is prohibitively expensive).\n\n### Why include package-global loggers?\n\nSince so many other logging packages include a global logger, many\napplications aren't designed to accept loggers as explicit parameters.\nChanging function signatures is often a breaking change, so zap includes\nglobal loggers to simplify migration.\n\nAvoid them where possible.\n\n### Why include dedicated Panic and Fatal log levels?\n\nIn general, application code should handle errors gracefully instead of using\n`panic` or `os.Exit`. However, every rule has exceptions, and it's common to\ncrash when an error is truly unrecoverable. To avoid losing any information\n&mdash; especially the reason for the crash &mdash; the logger must flush any\nbuffered entries before the process exits.\n\nZap makes this easy by offering `Panic` and `Fatal` logging methods that\nautomatically flush before exiting. Of course, this doesn't guarantee that\nlogs will never be lost, but it eliminates a common error.\n\nSee the discussion in uber-go/zap#207 for more details.\n\n### What's `DPanic`?\n\n`DPanic` stands for \"panic in development.\" In development, it logs at\n`PanicLevel`; otherwise, it logs at `ErrorLevel`. `DPanic` makes it easier to\ncatch errors that are theoretically possible, but shouldn't actually happen,\n*without* crashing in production.\n\nIf you've ever written code like this, you need `DPanic`:\n\n```go\nif err != nil {\n  panic(fmt.Sprintf(\"shouldn't ever get here: %v\", err))\n}\n```\n\n## Installation\n\n### What does the error `expects import \"go.uber.org/zap\"` mean?\n\nEither zap was installed incorrectly or you're referencing the wrong package\nname in your code.\n\nZap's source code happens to be hosted on GitHub, but the [import\npath][import-path] is `go.uber.org/zap`. This gives us, the project\nmaintainers, the freedom to move the source code if necessary. However, it\nmeans that you need to take a little care when installing and using the\npackage.\n\nIf you follow two simple rules, everything should work: install zap with `go\nget -u go.uber.org/zap`, and always import it in your code with `import\n\"go.uber.org/zap\"`. Your code shouldn't contain *any* references to\n`github.com/uber-go/zap`.\n\n## Usage\n\n### Does zap support log rotation?\n\nZap doesn't natively support rotating log files, since we prefer to leave this\nto an external program like `logrotate`.\n\nHowever, it's easy to integrate a log rotation package like\n[`gopkg.in/natefinch/lumberjack.v2`][lumberjack] as a `zapcore.WriteSyncer`.\n\n```go\n// lumberjack.Logger is already safe for concurrent use, so we don't need to\n// lock it.\nw := zapcore.AddSync(&lumberjack.Logger{\n  Filename:   \"/var/log/myapp/foo.log\",\n  MaxSize:    500, // megabytes\n  MaxBackups: 3,\n  MaxAge:     28, // days\n})\ncore := zapcore.NewCore(\n  zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),\n  w,\n  zap.InfoLevel,\n)\nlogger := zap.New(core)\n```\n\n## Extensions\n\nWe'd love to support every logging need within zap itself, but we're only\nfamiliar with a handful of log ingestion systems, flag-parsing packages, and\nthe like. Rather than merging code that we can't effectively debug and\nsupport, we'd rather grow an ecosystem of zap extensions.\n\nWe're aware of the following extensions, but haven't used them ourselves:\n\n| Package | Integration |\n| --- | --- |\n| `github.com/tchap/zapext` | Sentry, syslog |\n| `github.com/fgrosse/zaptest` | Ginkgo |\n| `github.com/blendle/zapdriver` | Stackdriver |\n| `github.com/moul/zapgorm` | Gorm |\n| `github.com/moul/zapfilter` | Advanced filtering rules |\n\n[go-proverbs]: https://go-proverbs.github.io/\n[import-path]: https://golang.org/cmd/go/#hdr-Remote_import_paths\n[lumberjack]: https://godoc.org/gopkg.in/natefinch/lumberjack.v2\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2016-2024 Uber Technologies, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "# Directory containing the Makefile.\nPROJECT_ROOT = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))\n\nexport GOBIN ?= $(PROJECT_ROOT)/bin\nexport PATH := $(GOBIN):$(PATH)\n\nGOVULNCHECK = $(GOBIN)/govulncheck\nBENCH_FLAGS ?= -cpuprofile=cpu.pprof -memprofile=mem.pprof -benchmem\n\n# Directories containing independent Go modules.\nMODULE_DIRS = . ./exp ./benchmarks ./zapgrpc/internal/test\n\n# Directories that we want to track coverage for.\nCOVER_DIRS = . ./exp\n\n.PHONY: all\nall: lint test\n\n.PHONY: lint\nlint: golangci-lint tidy-lint license-lint\n\n.PHONY: golangci-lint\ngolangci-lint:\n\t@$(foreach mod,$(MODULE_DIRS), \\\n\t\t(cd $(mod) && \\\n\t\techo \"[lint] golangci-lint: $(mod)\" && \\\n\t\tgolangci-lint run --path-prefix $(mod) ./...) &&) true\n\n.PHONY: tidy\ntidy:\n\t@$(foreach dir,$(MODULE_DIRS), \\\n\t\t(cd $(dir) && go mod tidy) &&) true\n\n.PHONY: tidy-lint\ntidy-lint:\n\t@$(foreach mod,$(MODULE_DIRS), \\\n\t\t(cd $(mod) && \\\n\t\techo \"[lint] tidy: $(mod)\" && \\\n\t\tgo mod tidy && \\\n\t\tgit diff --exit-code -- go.mod go.sum) &&) true\n\n\n.PHONY: license-lint\nlicense-lint:\n\t./checklicense.sh\n\n$(GOVULNCHECK):\n\tcd tools && go install golang.org/x/vuln/cmd/govulncheck\n\n.PHONY: test\ntest:\n\t@$(foreach dir,$(MODULE_DIRS),(cd $(dir) && go test -race ./...) &&) true\n\n.PHONY: cover\ncover:\n\t@$(foreach dir,$(COVER_DIRS), ( \\\n\t\tcd $(dir) && \\\n\t\tgo test -race -coverprofile=cover.out -coverpkg=./... ./... \\\n\t\t&& go tool cover -html=cover.out -o cover.html) &&) true\n\n.PHONY: bench\nBENCH ?= .\nbench:\n\t@$(foreach dir,$(MODULE_DIRS), ( \\\n\t\tcd $(dir) && \\\n\t\tgo list ./... | xargs -n1 go test -bench=$(BENCH) -run=\"^$$\" $(BENCH_FLAGS) \\\n\t) &&) true\n\n.PHONY: updatereadme\nupdatereadme:\n\trm -f README.md\n\tcat .readme.tmpl | go run internal/readme/readme.go > README.md\n\n.PHONY: vulncheck\nvulncheck: $(GOVULNCHECK)\n\t$(GOVULNCHECK) ./...\n"
  },
  {
    "path": "README.md",
    "content": "# :zap: zap\n\n\n<div align=\"center\">\n\nBlazing fast, structured, leveled logging in Go.\n\n![Zap logo](assets/logo.png)\n\n[![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov]\n\n</div>\n\n## Installation\n\n`go get -u go.uber.org/zap`\n\nNote that zap only supports the two most recent minor versions of Go.\n\n## Quick Start\n\nIn contexts where performance is nice, but not critical, use the\n`SugaredLogger`. It's 4-10x faster than other structured logging\npackages and includes both structured and `printf`-style APIs.\n\n```go\nlogger, _ := zap.NewProduction()\ndefer logger.Sync() // flushes buffer, if any\nsugar := logger.Sugar()\nsugar.Infow(\"failed to fetch URL\",\n  // Structured context as loosely typed key-value pairs.\n  \"url\", url,\n  \"attempt\", 3,\n  \"backoff\", time.Second,\n)\nsugar.Infof(\"Failed to fetch URL: %s\", url)\n```\n\nWhen performance and type safety are critical, use the `Logger`. It's even\nfaster than the `SugaredLogger` and allocates far less, but it only supports\nstructured logging.\n\n```go\nlogger, _ := zap.NewProduction()\ndefer logger.Sync()\nlogger.Info(\"failed to fetch URL\",\n  // Structured context as strongly typed Field values.\n  zap.String(\"url\", url),\n  zap.Int(\"attempt\", 3),\n  zap.Duration(\"backoff\", time.Second),\n)\n```\n\nSee the [documentation][doc] and [FAQ](FAQ.md) for more details.\n\n## Performance\n\nFor applications that log in the hot path, reflection-based serialization and\nstring formatting are prohibitively expensive &mdash; they're CPU-intensive\nand make many small allocations. Put differently, using `encoding/json` and\n`fmt.Fprintf` to log tons of `interface{}`s makes your application slow.\n\nZap takes a different approach. It includes a reflection-free, zero-allocation\nJSON encoder, and the base `Logger` strives to avoid serialization overhead\nand allocations wherever possible. By building the high-level `SugaredLogger`\non that foundation, zap lets users *choose* when they need to count every\nallocation and when they'd prefer a more familiar, loosely typed API.\n\nAs measured by its own [benchmarking suite][], not only is zap more performant\nthan comparable structured logging packages &mdash; it's also faster than the\nstandard library. Like all benchmarks, take these with a grain of salt.<sup\nid=\"anchor-versions\">[1](#footnote-versions)</sup>\n\nLog a message and 10 fields:\n\n| Package | Time | Time % to zap | Objects Allocated |\n| :------ | :--: | :-----------: | :---------------: |\n| :zap: zap | 656 ns/op | +0% | 5 allocs/op\n| :zap: zap (sugared) | 935 ns/op | +43% | 10 allocs/op\n| zerolog | 380 ns/op | -42% | 1 allocs/op\n| go-kit | 2249 ns/op | +243% | 57 allocs/op\n| slog (LogAttrs) | 2479 ns/op | +278% | 40 allocs/op\n| slog | 2481 ns/op | +278% | 42 allocs/op\n| apex/log | 9591 ns/op | +1362% | 63 allocs/op\n| log15 | 11393 ns/op | +1637% | 75 allocs/op\n| logrus | 11654 ns/op | +1677% | 79 allocs/op\n\nLog a message with a logger that already has 10 fields of context:\n\n| Package | Time | Time % to zap | Objects Allocated |\n| :------ | :--: | :-----------: | :---------------: |\n| :zap: zap | 67 ns/op | +0% | 0 allocs/op\n| :zap: zap (sugared) | 84 ns/op | +25% | 1 allocs/op\n| zerolog | 35 ns/op | -48% | 0 allocs/op\n| slog | 193 ns/op | +188% | 0 allocs/op\n| slog (LogAttrs) | 200 ns/op | +199% | 0 allocs/op\n| go-kit | 2460 ns/op | +3572% | 56 allocs/op\n| log15 | 9038 ns/op | +13390% | 70 allocs/op\n| apex/log | 9068 ns/op | +13434% | 53 allocs/op\n| logrus | 10521 ns/op | +15603% | 68 allocs/op\n\nLog a static string, without any context or `printf`-style templating:\n\n| Package | Time | Time % to zap | Objects Allocated |\n| :------ | :--: | :-----------: | :---------------: |\n| :zap: zap | 63 ns/op | +0% | 0 allocs/op\n| :zap: zap (sugared) | 81 ns/op | +29% | 1 allocs/op\n| zerolog | 32 ns/op | -49% | 0 allocs/op\n| standard library | 124 ns/op | +97% | 1 allocs/op\n| slog | 196 ns/op | +211% | 0 allocs/op\n| slog (LogAttrs) | 200 ns/op | +217% | 0 allocs/op\n| go-kit | 213 ns/op | +238% | 9 allocs/op\n| apex/log | 771 ns/op | +1124% | 5 allocs/op\n| logrus | 1439 ns/op | +2184% | 23 allocs/op\n| log15 | 2069 ns/op | +3184% | 20 allocs/op\n\n## Development Status: Stable\n\nAll APIs are finalized, and no breaking changes will be made in the 1.x series\nof releases. Users of semver-aware dependency management systems should pin\nzap to `^1`.\n\n## Contributing\n\nWe encourage and support an active, healthy community of contributors &mdash;\nincluding you! Details are in the [contribution guide](CONTRIBUTING.md) and\nthe [code of conduct](CODE_OF_CONDUCT.md). The zap maintainers keep an eye on\nissues and pull requests, but you can also report any negative conduct to\noss-conduct@uber.com. That email list is a private, safe space; even the zap\nmaintainers don't have access, so don't hesitate to hold us to a high\nstandard.\n\n<hr>\n\nReleased under the [MIT License](LICENSE).\n\n<sup id=\"footnote-versions\">1</sup> In particular, keep in mind that we may be\nbenchmarking against slightly older versions of other packages. Versions are\npinned in the [benchmarks/go.mod][] file. [↩](#anchor-versions)\n\n[doc-img]: https://pkg.go.dev/badge/go.uber.org/zap\n[doc]: https://pkg.go.dev/go.uber.org/zap\n[ci-img]: https://github.com/uber-go/zap/actions/workflows/go.yml/badge.svg\n[ci]: https://github.com/uber-go/zap/actions/workflows/go.yml\n[cov-img]: https://codecov.io/gh/uber-go/zap/branch/master/graph/badge.svg\n[cov]: https://codecov.io/gh/uber-go/zap\n[benchmarking suite]: https://github.com/uber-go/zap/tree/master/benchmarks\n[benchmarks/go.mod]: https://github.com/uber-go/zap/blob/master/benchmarks/go.mod\n\n"
  },
  {
    "path": "array.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.uber.org/zap/zapcore\"\n)\n\n// Array constructs a field with the given key and ArrayMarshaler. It provides\n// a flexible, but still type-safe and efficient, way to add array-like types\n// to the logging context. The struct's MarshalLogArray method is called lazily.\nfunc Array(key string, val zapcore.ArrayMarshaler) Field {\n\treturn Field{Key: key, Type: zapcore.ArrayMarshalerType, Interface: val}\n}\n\n// Bools constructs a field that carries a slice of bools.\nfunc Bools(key string, bs []bool) Field {\n\treturn Array(key, bools(bs))\n}\n\n// ByteStrings constructs a field that carries a slice of []byte, each of which\n// must be UTF-8 encoded text.\nfunc ByteStrings(key string, bss [][]byte) Field {\n\treturn Array(key, byteStringsArray(bss))\n}\n\n// Complex128s constructs a field that carries a slice of complex numbers.\nfunc Complex128s(key string, nums []complex128) Field {\n\treturn Array(key, complex128s(nums))\n}\n\n// Complex64s constructs a field that carries a slice of complex numbers.\nfunc Complex64s(key string, nums []complex64) Field {\n\treturn Array(key, complex64s(nums))\n}\n\n// Durations constructs a field that carries a slice of time.Durations.\nfunc Durations(key string, ds []time.Duration) Field {\n\treturn Array(key, durations(ds))\n}\n\n// Float64s constructs a field that carries a slice of floats.\nfunc Float64s(key string, nums []float64) Field {\n\treturn Array(key, float64s(nums))\n}\n\n// Float32s constructs a field that carries a slice of floats.\nfunc Float32s(key string, nums []float32) Field {\n\treturn Array(key, float32s(nums))\n}\n\n// Ints constructs a field that carries a slice of integers.\nfunc Ints(key string, nums []int) Field {\n\treturn Array(key, ints(nums))\n}\n\n// Int64s constructs a field that carries a slice of integers.\nfunc Int64s(key string, nums []int64) Field {\n\treturn Array(key, int64s(nums))\n}\n\n// Int32s constructs a field that carries a slice of integers.\nfunc Int32s(key string, nums []int32) Field {\n\treturn Array(key, int32s(nums))\n}\n\n// Int16s constructs a field that carries a slice of integers.\nfunc Int16s(key string, nums []int16) Field {\n\treturn Array(key, int16s(nums))\n}\n\n// Int8s constructs a field that carries a slice of integers.\nfunc Int8s(key string, nums []int8) Field {\n\treturn Array(key, int8s(nums))\n}\n\n// Objects constructs a field with the given key, holding a list of the\n// provided objects that can be marshaled by Zap.\n//\n// Note that these objects must implement zapcore.ObjectMarshaler directly.\n// That is, if you're trying to marshal a []Request, the MarshalLogObject\n// method must be declared on the Request type, not its pointer (*Request).\n// If it's on the pointer, use ObjectValues.\n//\n// Given an object that implements MarshalLogObject on the value receiver, you\n// can log a slice of those objects with Objects like so:\n//\n//\ttype Author struct{ ... }\n//\tfunc (a Author) MarshalLogObject(enc zapcore.ObjectEncoder) error\n//\n//\tvar authors []Author = ...\n//\tlogger.Info(\"loading article\", zap.Objects(\"authors\", authors))\n//\n// Similarly, given a type that implements MarshalLogObject on its pointer\n// receiver, you can log a slice of pointers to that object with Objects like\n// so:\n//\n//\ttype Request struct{ ... }\n//\tfunc (r *Request) MarshalLogObject(enc zapcore.ObjectEncoder) error\n//\n//\tvar requests []*Request = ...\n//\tlogger.Info(\"sending requests\", zap.Objects(\"requests\", requests))\n//\n// If instead, you have a slice of values of such an object, use the\n// ObjectValues constructor.\n//\n//\tvar requests []Request = ...\n//\tlogger.Info(\"sending requests\", zap.ObjectValues(\"requests\", requests))\nfunc Objects[T zapcore.ObjectMarshaler](key string, values []T) Field {\n\treturn Array(key, objects[T](values))\n}\n\ntype objects[T zapcore.ObjectMarshaler] []T\n\nfunc (os objects[T]) MarshalLogArray(arr zapcore.ArrayEncoder) error {\n\tfor _, o := range os {\n\t\tif err := arr.AppendObject(o); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// ObjectMarshalerPtr is a constraint that specifies that the given type\n// implements zapcore.ObjectMarshaler on a pointer receiver.\ntype ObjectMarshalerPtr[T any] interface {\n\t*T\n\tzapcore.ObjectMarshaler\n}\n\n// ObjectValues constructs a field with the given key, holding a list of the\n// provided objects, where pointers to these objects can be marshaled by Zap.\n//\n// Note that pointers to these objects must implement zapcore.ObjectMarshaler.\n// That is, if you're trying to marshal a []Request, the MarshalLogObject\n// method must be declared on the *Request type, not the value (Request).\n// If it's on the value, use Objects.\n//\n// Given an object that implements MarshalLogObject on the pointer receiver,\n// you can log a slice of those objects with ObjectValues like so:\n//\n//\ttype Request struct{ ... }\n//\tfunc (r *Request) MarshalLogObject(enc zapcore.ObjectEncoder) error\n//\n//\tvar requests []Request = ...\n//\tlogger.Info(\"sending requests\", zap.ObjectValues(\"requests\", requests))\n//\n// If instead, you have a slice of pointers of such an object, use the Objects\n// field constructor.\n//\n//\tvar requests []*Request = ...\n//\tlogger.Info(\"sending requests\", zap.Objects(\"requests\", requests))\nfunc ObjectValues[T any, P ObjectMarshalerPtr[T]](key string, values []T) Field {\n\treturn Array(key, objectValues[T, P](values))\n}\n\ntype objectValues[T any, P ObjectMarshalerPtr[T]] []T\n\nfunc (os objectValues[T, P]) MarshalLogArray(arr zapcore.ArrayEncoder) error {\n\tfor i := range os {\n\t\t// It is necessary for us to explicitly reference the \"P\" type.\n\t\t// We cannot simply pass \"&os[i]\" to AppendObject because its type\n\t\t// is \"*T\", which the type system does not consider as\n\t\t// implementing ObjectMarshaler.\n\t\t// Only the type \"P\" satisfies ObjectMarshaler, which we have\n\t\t// to convert \"*T\" to explicitly.\n\t\tvar p P = &os[i]\n\t\tif err := arr.AppendObject(p); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Strings constructs a field that carries a slice of strings.\nfunc Strings(key string, ss []string) Field {\n\treturn Array(key, stringArray(ss))\n}\n\n// Stringers constructs a field with the given key, holding a list of the\n// output provided by the value's String method\n//\n// Given an object that implements String on the value receiver, you\n// can log a slice of those objects with Objects like so:\n//\n//\ttype Request struct{ ... }\n//\tfunc (a Request) String() string\n//\n//\tvar requests []Request = ...\n//\tlogger.Info(\"sending requests\", zap.Stringers(\"requests\", requests))\n//\n// Note that these objects must implement fmt.Stringer directly.\n// That is, if you're trying to marshal a []Request, the String method\n// must be declared on the Request type, not its pointer (*Request).\nfunc Stringers[T fmt.Stringer](key string, values []T) Field {\n\treturn Array(key, stringers[T](values))\n}\n\ntype stringers[T fmt.Stringer] []T\n\nfunc (os stringers[T]) MarshalLogArray(arr zapcore.ArrayEncoder) error {\n\tfor _, o := range os {\n\t\tarr.AppendString(o.String())\n\t}\n\treturn nil\n}\n\n// Times constructs a field that carries a slice of time.Times.\nfunc Times(key string, ts []time.Time) Field {\n\treturn Array(key, times(ts))\n}\n\n// Uints constructs a field that carries a slice of unsigned integers.\nfunc Uints(key string, nums []uint) Field {\n\treturn Array(key, uints(nums))\n}\n\n// Uint64s constructs a field that carries a slice of unsigned integers.\nfunc Uint64s(key string, nums []uint64) Field {\n\treturn Array(key, uint64s(nums))\n}\n\n// Uint32s constructs a field that carries a slice of unsigned integers.\nfunc Uint32s(key string, nums []uint32) Field {\n\treturn Array(key, uint32s(nums))\n}\n\n// Uint16s constructs a field that carries a slice of unsigned integers.\nfunc Uint16s(key string, nums []uint16) Field {\n\treturn Array(key, uint16s(nums))\n}\n\n// Uint8s constructs a field that carries a slice of unsigned integers.\nfunc Uint8s(key string, nums []uint8) Field {\n\treturn Array(key, uint8s(nums))\n}\n\n// Uintptrs constructs a field that carries a slice of pointer addresses.\nfunc Uintptrs(key string, us []uintptr) Field {\n\treturn Array(key, uintptrs(us))\n}\n\n// Errors constructs a field that carries a slice of errors.\nfunc Errors(key string, errs []error) Field {\n\treturn Array(key, errArray(errs))\n}\n\ntype bools []bool\n\nfunc (bs bools) MarshalLogArray(arr zapcore.ArrayEncoder) error {\n\tfor i := range bs {\n\t\tarr.AppendBool(bs[i])\n\t}\n\treturn nil\n}\n\ntype byteStringsArray [][]byte\n\nfunc (bss byteStringsArray) MarshalLogArray(arr zapcore.ArrayEncoder) error {\n\tfor i := range bss {\n\t\tarr.AppendByteString(bss[i])\n\t}\n\treturn nil\n}\n\ntype complex128s []complex128\n\nfunc (nums complex128s) MarshalLogArray(arr zapcore.ArrayEncoder) error {\n\tfor i := range nums {\n\t\tarr.AppendComplex128(nums[i])\n\t}\n\treturn nil\n}\n\ntype complex64s []complex64\n\nfunc (nums complex64s) MarshalLogArray(arr zapcore.ArrayEncoder) error {\n\tfor i := range nums {\n\t\tarr.AppendComplex64(nums[i])\n\t}\n\treturn nil\n}\n\ntype durations []time.Duration\n\nfunc (ds durations) MarshalLogArray(arr zapcore.ArrayEncoder) error {\n\tfor i := range ds {\n\t\tarr.AppendDuration(ds[i])\n\t}\n\treturn nil\n}\n\ntype float64s []float64\n\nfunc (nums float64s) MarshalLogArray(arr zapcore.ArrayEncoder) error {\n\tfor i := range nums {\n\t\tarr.AppendFloat64(nums[i])\n\t}\n\treturn nil\n}\n\ntype float32s []float32\n\nfunc (nums float32s) MarshalLogArray(arr zapcore.ArrayEncoder) error {\n\tfor i := range nums {\n\t\tarr.AppendFloat32(nums[i])\n\t}\n\treturn nil\n}\n\ntype ints []int\n\nfunc (nums ints) MarshalLogArray(arr zapcore.ArrayEncoder) error {\n\tfor i := range nums {\n\t\tarr.AppendInt(nums[i])\n\t}\n\treturn nil\n}\n\ntype int64s []int64\n\nfunc (nums int64s) MarshalLogArray(arr zapcore.ArrayEncoder) error {\n\tfor i := range nums {\n\t\tarr.AppendInt64(nums[i])\n\t}\n\treturn nil\n}\n\ntype int32s []int32\n\nfunc (nums int32s) MarshalLogArray(arr zapcore.ArrayEncoder) error {\n\tfor i := range nums {\n\t\tarr.AppendInt32(nums[i])\n\t}\n\treturn nil\n}\n\ntype int16s []int16\n\nfunc (nums int16s) MarshalLogArray(arr zapcore.ArrayEncoder) error {\n\tfor i := range nums {\n\t\tarr.AppendInt16(nums[i])\n\t}\n\treturn nil\n}\n\ntype int8s []int8\n\nfunc (nums int8s) MarshalLogArray(arr zapcore.ArrayEncoder) error {\n\tfor i := range nums {\n\t\tarr.AppendInt8(nums[i])\n\t}\n\treturn nil\n}\n\ntype stringArray []string\n\nfunc (ss stringArray) MarshalLogArray(arr zapcore.ArrayEncoder) error {\n\tfor i := range ss {\n\t\tarr.AppendString(ss[i])\n\t}\n\treturn nil\n}\n\ntype times []time.Time\n\nfunc (ts times) MarshalLogArray(arr zapcore.ArrayEncoder) error {\n\tfor i := range ts {\n\t\tarr.AppendTime(ts[i])\n\t}\n\treturn nil\n}\n\ntype uints []uint\n\nfunc (nums uints) MarshalLogArray(arr zapcore.ArrayEncoder) error {\n\tfor i := range nums {\n\t\tarr.AppendUint(nums[i])\n\t}\n\treturn nil\n}\n\ntype uint64s []uint64\n\nfunc (nums uint64s) MarshalLogArray(arr zapcore.ArrayEncoder) error {\n\tfor i := range nums {\n\t\tarr.AppendUint64(nums[i])\n\t}\n\treturn nil\n}\n\ntype uint32s []uint32\n\nfunc (nums uint32s) MarshalLogArray(arr zapcore.ArrayEncoder) error {\n\tfor i := range nums {\n\t\tarr.AppendUint32(nums[i])\n\t}\n\treturn nil\n}\n\ntype uint16s []uint16\n\nfunc (nums uint16s) MarshalLogArray(arr zapcore.ArrayEncoder) error {\n\tfor i := range nums {\n\t\tarr.AppendUint16(nums[i])\n\t}\n\treturn nil\n}\n\ntype uint8s []uint8\n\nfunc (nums uint8s) MarshalLogArray(arr zapcore.ArrayEncoder) error {\n\tfor i := range nums {\n\t\tarr.AppendUint8(nums[i])\n\t}\n\treturn nil\n}\n\ntype uintptrs []uintptr\n\nfunc (nums uintptrs) MarshalLogArray(arr zapcore.ArrayEncoder) error {\n\tfor i := range nums {\n\t\tarr.AppendUintptr(nums[i])\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "array_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.uber.org/zap/zapcore\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc BenchmarkBoolsArrayMarshaler(b *testing.B) {\n\t// Keep this benchmark here to capture the overhead of the ArrayMarshaler\n\t// wrapper.\n\tbs := make([]bool, 50)\n\tenc := zapcore.NewJSONEncoder(zapcore.EncoderConfig{})\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tBools(\"array\", bs).AddTo(enc.Clone())\n\t}\n}\n\nfunc BenchmarkBoolsReflect(b *testing.B) {\n\tbs := make([]bool, 50)\n\tenc := zapcore.NewJSONEncoder(zapcore.EncoderConfig{})\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tReflect(\"array\", bs).AddTo(enc.Clone())\n\t}\n}\n\nfunc TestArrayWrappers(t *testing.T) {\n\ttests := []struct {\n\t\tdesc     string\n\t\tfield    Field\n\t\texpected []interface{}\n\t}{\n\t\t{\"empty bools\", Bools(\"\", []bool{}), []interface{}{}},\n\t\t{\"empty byte strings\", ByteStrings(\"\", [][]byte{}), []interface{}{}},\n\t\t{\"empty complex128s\", Complex128s(\"\", []complex128{}), []interface{}{}},\n\t\t{\"empty complex64s\", Complex64s(\"\", []complex64{}), []interface{}{}},\n\t\t{\"empty durations\", Durations(\"\", []time.Duration{}), []interface{}{}},\n\t\t{\"empty float64s\", Float64s(\"\", []float64{}), []interface{}{}},\n\t\t{\"empty float32s\", Float32s(\"\", []float32{}), []interface{}{}},\n\t\t{\"empty ints\", Ints(\"\", []int{}), []interface{}{}},\n\t\t{\"empty int64s\", Int64s(\"\", []int64{}), []interface{}{}},\n\t\t{\"empty int32s\", Int32s(\"\", []int32{}), []interface{}{}},\n\t\t{\"empty int16s\", Int16s(\"\", []int16{}), []interface{}{}},\n\t\t{\"empty int8s\", Int8s(\"\", []int8{}), []interface{}{}},\n\t\t{\"empty strings\", Strings(\"\", []string{}), []interface{}{}},\n\t\t{\"empty times\", Times(\"\", []time.Time{}), []interface{}{}},\n\t\t{\"empty uints\", Uints(\"\", []uint{}), []interface{}{}},\n\t\t{\"empty uint64s\", Uint64s(\"\", []uint64{}), []interface{}{}},\n\t\t{\"empty uint32s\", Uint32s(\"\", []uint32{}), []interface{}{}},\n\t\t{\"empty uint16s\", Uint16s(\"\", []uint16{}), []interface{}{}},\n\t\t{\"empty uint8s\", Uint8s(\"\", []uint8{}), []interface{}{}},\n\t\t{\"empty uintptrs\", Uintptrs(\"\", []uintptr{}), []interface{}{}},\n\t\t{\"bools\", Bools(\"\", []bool{true, false}), []interface{}{true, false}},\n\t\t{\"byte strings\", ByteStrings(\"\", [][]byte{{1, 2}, {3, 4}}), []interface{}{\"\\x01\\x02\", \"\\x03\\x04\"}},\n\t\t{\"complex128s\", Complex128s(\"\", []complex128{1 + 2i, 3 + 4i}), []interface{}{1 + 2i, 3 + 4i}},\n\t\t{\"complex64s\", Complex64s(\"\", []complex64{1 + 2i, 3 + 4i}), []interface{}{complex64(1 + 2i), complex64(3 + 4i)}},\n\t\t{\"durations\", Durations(\"\", []time.Duration{1, 2}), []interface{}{time.Nanosecond, 2 * time.Nanosecond}},\n\t\t{\"float64s\", Float64s(\"\", []float64{1.2, 3.4}), []interface{}{1.2, 3.4}},\n\t\t{\"float32s\", Float32s(\"\", []float32{1.2, 3.4}), []interface{}{float32(1.2), float32(3.4)}},\n\t\t{\"ints\", Ints(\"\", []int{1, 2}), []interface{}{1, 2}},\n\t\t{\"int64s\", Int64s(\"\", []int64{1, 2}), []interface{}{int64(1), int64(2)}},\n\t\t{\"int32s\", Int32s(\"\", []int32{1, 2}), []interface{}{int32(1), int32(2)}},\n\t\t{\"int16s\", Int16s(\"\", []int16{1, 2}), []interface{}{int16(1), int16(2)}},\n\t\t{\"int8s\", Int8s(\"\", []int8{1, 2}), []interface{}{int8(1), int8(2)}},\n\t\t{\"strings\", Strings(\"\", []string{\"foo\", \"bar\"}), []interface{}{\"foo\", \"bar\"}},\n\t\t{\"times\", Times(\"\", []time.Time{time.Unix(0, 0), time.Unix(0, 0)}), []interface{}{time.Unix(0, 0), time.Unix(0, 0)}},\n\t\t{\"uints\", Uints(\"\", []uint{1, 2}), []interface{}{uint(1), uint(2)}},\n\t\t{\"uint64s\", Uint64s(\"\", []uint64{1, 2}), []interface{}{uint64(1), uint64(2)}},\n\t\t{\"uint32s\", Uint32s(\"\", []uint32{1, 2}), []interface{}{uint32(1), uint32(2)}},\n\t\t{\"uint16s\", Uint16s(\"\", []uint16{1, 2}), []interface{}{uint16(1), uint16(2)}},\n\t\t{\"uint8s\", Uint8s(\"\", []uint8{1, 2}), []interface{}{uint8(1), uint8(2)}},\n\t\t{\"uintptrs\", Uintptrs(\"\", []uintptr{1, 2}), []interface{}{uintptr(1), uintptr(2)}},\n\t}\n\n\tfor _, tt := range tests {\n\t\tenc := zapcore.NewMapObjectEncoder()\n\t\ttt.field.Key = \"k\"\n\t\ttt.field.AddTo(enc)\n\t\tassert.Equal(t, tt.expected, enc.Fields[\"k\"], \"%s: unexpected map contents.\", tt.desc)\n\t\tassert.Equal(t, 1, len(enc.Fields), \"%s: found extra keys in map: %v\", tt.desc, enc.Fields)\n\t}\n}\n\nfunc TestObjectsAndObjectValues(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tdesc string\n\t\tgive Field\n\t\twant []any\n\t}{\n\t\t{\n\t\t\tdesc: \"Objects/nil slice\",\n\t\t\tgive: Objects[*emptyObject](\"\", nil),\n\t\t\twant: []any{},\n\t\t},\n\t\t{\n\t\t\tdesc: \"ObjectValues/nil slice\",\n\t\t\tgive: ObjectValues[emptyObject](\"\", nil),\n\t\t\twant: []any{},\n\t\t},\n\t\t{\n\t\t\tdesc: \"ObjectValues/empty slice\",\n\t\t\tgive: ObjectValues(\"\", []emptyObject{}),\n\t\t\twant: []any{},\n\t\t},\n\t\t{\n\t\t\tdesc: \"ObjectValues/single item\",\n\t\t\tgive: ObjectValues(\"\", []emptyObject{\n\t\t\t\t{},\n\t\t\t}),\n\t\t\twant: []any{\n\t\t\t\tmap[string]any{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"Objects/multiple different objects\",\n\t\t\tgive: Objects(\"\", []*fakeObject{\n\t\t\t\t{value: \"foo\"},\n\t\t\t\t{value: \"bar\"},\n\t\t\t\t{value: \"baz\"},\n\t\t\t}),\n\t\t\twant: []any{\n\t\t\t\tmap[string]any{\"value\": \"foo\"},\n\t\t\t\tmap[string]any{\"value\": \"bar\"},\n\t\t\t\tmap[string]any{\"value\": \"baz\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"ObjectValues/multiple different objects\",\n\t\t\tgive: ObjectValues(\"\", []fakeObject{\n\t\t\t\t{value: \"foo\"},\n\t\t\t\t{value: \"bar\"},\n\t\t\t\t{value: \"baz\"},\n\t\t\t}),\n\t\t\twant: []any{\n\t\t\t\tmap[string]any{\"value\": \"foo\"},\n\t\t\t\tmap[string]any{\"value\": \"bar\"},\n\t\t\t\tmap[string]any{\"value\": \"baz\"},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\ttt.give.Key = \"k\"\n\n\t\t\tenc := zapcore.NewMapObjectEncoder()\n\t\t\ttt.give.AddTo(enc)\n\t\t\tassert.Equal(t, tt.want, enc.Fields[\"k\"])\n\t\t})\n\t}\n}\n\ntype emptyObject struct{}\n\nfunc (*emptyObject) MarshalLogObject(zapcore.ObjectEncoder) error {\n\treturn nil\n}\n\ntype fakeObject struct {\n\tvalue string\n\terr   error // marshaling error, if any\n}\n\nfunc (o *fakeObject) MarshalLogObject(enc zapcore.ObjectEncoder) error {\n\tenc.AddString(\"value\", o.value)\n\treturn o.err\n}\n\nfunc TestObjectsAndObjectValues_marshalError(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tdesc    string\n\t\tgive    Field\n\t\twant    []any\n\t\twantErr string\n\t}{\n\t\t{\n\t\t\tdesc: \"Objects\",\n\t\t\tgive: Objects(\"\", []*fakeObject{\n\t\t\t\t{value: \"foo\"},\n\t\t\t\t{value: \"bar\", err: errors.New(\"great sadness\")},\n\t\t\t\t{value: \"baz\"}, // does not get marshaled\n\t\t\t}),\n\t\t\twant: []any{\n\t\t\t\tmap[string]any{\"value\": \"foo\"},\n\t\t\t\tmap[string]any{\"value\": \"bar\"},\n\t\t\t},\n\t\t\twantErr: \"great sadness\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"ObjectValues\",\n\t\t\tgive: ObjectValues(\"\", []fakeObject{\n\t\t\t\t{value: \"foo\"},\n\t\t\t\t{value: \"bar\", err: errors.New(\"stuff failed\")},\n\t\t\t\t{value: \"baz\"}, // does not get marshaled\n\t\t\t}),\n\t\t\twant: []any{\n\t\t\t\tmap[string]any{\"value\": \"foo\"},\n\t\t\t\tmap[string]any{\"value\": \"bar\"},\n\t\t\t},\n\t\t\twantErr: \"stuff failed\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\ttt.give.Key = \"k\"\n\n\t\t\tenc := zapcore.NewMapObjectEncoder()\n\t\t\ttt.give.AddTo(enc)\n\n\t\t\trequire.Contains(t, enc.Fields, \"k\")\n\t\t\tassert.Equal(t, tt.want, enc.Fields[\"k\"])\n\n\t\t\t// AddTo puts the error in a \"%vError\" field based on the name of the\n\t\t\t// original field.\n\t\t\trequire.Contains(t, enc.Fields, \"kError\")\n\t\t\tassert.Equal(t, tt.wantErr, enc.Fields[\"kError\"])\n\t\t})\n\t}\n}\n\ntype stringerObject struct {\n\tvalue string\n}\n\nfunc (s stringerObject) String() string {\n\treturn s.value\n}\n\nfunc TestStringers(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tdesc string\n\t\tgive Field\n\t\twant []any\n\t}{\n\t\t{\n\t\t\tdesc: \"Stringers\",\n\t\t\tgive: Stringers(\"\", []stringerObject{\n\t\t\t\t{value: \"foo\"},\n\t\t\t\t{value: \"bar\"},\n\t\t\t\t{value: \"baz\"},\n\t\t\t}),\n\t\t\twant: []any{\n\t\t\t\t\"foo\",\n\t\t\t\t\"bar\",\n\t\t\t\t\"baz\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"Stringers with []fmt.Stringer\",\n\t\t\tgive: Stringers(\"\", []fmt.Stringer{\n\t\t\t\tstringerObject{value: \"foo\"},\n\t\t\t\tstringerObject{value: \"bar\"},\n\t\t\t\tstringerObject{value: \"baz\"},\n\t\t\t}),\n\t\t\twant: []any{\n\t\t\t\t\"foo\",\n\t\t\t\t\"bar\",\n\t\t\t\t\"baz\",\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\ttt.give.Key = \"k\"\n\n\t\t\tenc := zapcore.NewMapObjectEncoder()\n\t\t\ttt.give.AddTo(enc)\n\t\t\tassert.Equal(t, tt.want, enc.Fields[\"k\"])\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "assets/README.md",
    "content": "The logo for Zap was designed by [Abhinav Gupta](https://abhinavg.net/)\nand is made available under the Creative Commons 4.0 Attribution License.\n\nIt is based on the Go Gopher mascot originally created by Renee French,\nwhich is also licensed under the Creative Commons 4.0 Attribution License.\n"
  },
  {
    "path": "assets/go.mod",
    "content": "module go.uber.org/zap/assets\n\ngo 1.21.5\n"
  },
  {
    "path": "benchmarks/apex_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage benchmarks\n\nimport (\n\t\"io\"\n\n\t\"github.com/apex/log\"\n\t\"github.com/apex/log/handlers/json\"\n)\n\nfunc newDisabledApexLog() *log.Logger {\n\treturn &log.Logger{\n\t\tHandler: json.New(io.Discard),\n\t\tLevel:   log.ErrorLevel,\n\t}\n}\n\nfunc newApexLog() *log.Logger {\n\treturn &log.Logger{\n\t\tHandler: json.New(io.Discard),\n\t\tLevel:   log.DebugLevel,\n\t}\n}\n\nfunc fakeApexFields() log.Fields {\n\treturn log.Fields{\n\t\t\"int\":     _tenInts[0],\n\t\t\"ints\":    _tenInts,\n\t\t\"string\":  _tenStrings[0],\n\t\t\"strings\": _tenStrings,\n\t\t\"time\":    _tenTimes[0],\n\t\t\"times\":   _tenTimes,\n\t\t\"user1\":   _oneUser,\n\t\t\"user2\":   _oneUser,\n\t\t\"users\":   _tenUsers,\n\t\t\"error\":   errExample,\n\t}\n}\n"
  },
  {
    "path": "benchmarks/doc.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// Package benchmarks contains only benchmarks comparing zap to other\n// structured logging libraries.\npackage benchmarks\n"
  },
  {
    "path": "benchmarks/go.mod",
    "content": "module go.uber.org/zap/benchmarks\n\ngo 1.21\n\nreplace go.uber.org/zap => ../\n\nrequire (\n\tgithub.com/apex/log v1.9.0\n\tgithub.com/go-kit/log v0.2.1\n\tgithub.com/rs/zerolog v1.30.0\n\tgithub.com/sirupsen/logrus v1.9.3\n\tgo.uber.org/multierr v1.11.0\n\tgo.uber.org/zap v1.23.0\n\tgopkg.in/inconshreveable/log15.v2 v2.16.0\n)\n\nrequire (\n\tgithub.com/go-logfmt/logfmt v0.6.0 // indirect\n\tgithub.com/go-stack/stack v1.8.1 // indirect\n\tgithub.com/mattn/go-colorable v0.1.13 // indirect\n\tgithub.com/mattn/go-isatty v0.0.19 // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgolang.org/x/sys v0.12.0 // indirect\n\tgolang.org/x/term v0.12.0 // indirect\n)\n"
  },
  {
    "path": "benchmarks/go.sum",
    "content": "github.com/apex/log v1.9.0 h1:FHtw/xuaM8AgmvDDTI9fiwoAL25Sq2cxojnZICUU8l0=\ngithub.com/apex/log v1.9.0/go.mod h1:m82fZlWIuiWzWP04XCTXmnX0xRkYYbCdYn8jbJeLBEA=\ngithub.com/apex/logs v1.0.0/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo=\ngithub.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE=\ngithub.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys=\ngithub.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=\ngithub.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=\ngithub.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=\ngithub.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=\ngithub.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=\ngithub.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=\ngithub.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=\ngithub.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=\ngithub.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=\ngithub.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=\ngithub.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0=\ngithub.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=\ngithub.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=\ngithub.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=\ngithub.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=\ngithub.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=\ngithub.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=\ngithub.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=\ngithub.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c=\ngithub.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=\ngithub.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=\ngithub.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=\ngithub.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=\ngithub.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngithub.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0=\ngithub.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk=\ngithub.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk=\ngithub.com/tj/go-buffer v1.1.0/go.mod h1:iyiJpfFcR2B9sXu7KvjbT9fpM4mOelRSDTbntVj52Uc=\ngithub.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0=\ngithub.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao=\ngithub.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4=\ngo.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=\ngo.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=\ngo.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=\ngo.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=\ngo.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=\ngo.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=\ngolang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU=\ngolang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=\ngopkg.in/inconshreveable/log15.v2 v2.16.0 h1:LWHLVX8KbBMkQFSqfno4901Z4Wg8L3B7Cu0n4K/Q7MA=\ngopkg.in/inconshreveable/log15.v2 v2.16.0/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "benchmarks/kit_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage benchmarks\n\nimport (\n\t\"io\"\n\n\t\"github.com/go-kit/log\"\n)\n\nfunc newKitLog(fields ...interface{}) log.Logger {\n\treturn log.With(log.NewJSONLogger(io.Discard), fields...)\n}\n"
  },
  {
    "path": "benchmarks/log15_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage benchmarks\n\nimport (\n\t\"io\"\n\n\t\"gopkg.in/inconshreveable/log15.v2\"\n)\n\nfunc newLog15() log15.Logger {\n\tlogger := log15.New()\n\tlogger.SetHandler(log15.StreamHandler(io.Discard, log15.JsonFormat()))\n\treturn logger\n}\n"
  },
  {
    "path": "benchmarks/logrus_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage benchmarks\n\nimport (\n\t\"io\"\n\n\t\"github.com/sirupsen/logrus\"\n)\n\nfunc newDisabledLogrus() *logrus.Logger {\n\tlogger := newLogrus()\n\tlogger.Level = logrus.ErrorLevel\n\treturn logger\n}\n\nfunc newLogrus() *logrus.Logger {\n\treturn &logrus.Logger{\n\t\tOut:       io.Discard,\n\t\tFormatter: new(logrus.JSONFormatter),\n\t\tHooks:     make(logrus.LevelHooks),\n\t\tLevel:     logrus.DebugLevel,\n\t}\n}\n\nfunc fakeLogrusFields() logrus.Fields {\n\treturn logrus.Fields{\n\t\t\"int\":     _tenInts[0],\n\t\t\"ints\":    _tenInts,\n\t\t\"string\":  _tenStrings[0],\n\t\t\"strings\": _tenStrings,\n\t\t\"time\":    _tenTimes[0],\n\t\t\"times\":   _tenTimes,\n\t\t\"user1\":   _oneUser,\n\t\t\"user2\":   _oneUser,\n\t\t\"users\":   _tenUsers,\n\t\t\"error\":   errExample,\n\t}\n}\n"
  },
  {
    "path": "benchmarks/scenario_bench_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage benchmarks\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"log/slog\"\n\t\"testing\"\n\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/internal/ztest\"\n)\n\nfunc BenchmarkDisabledWithoutFields(b *testing.B) {\n\tb.Logf(\"Logging at a disabled level without any structured context.\")\n\tb.Run(\"Zap\", func(b *testing.B) {\n\t\tlogger := newZapLogger(zap.ErrorLevel)\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"Zap.Check\", func(b *testing.B) {\n\t\tlogger := newZapLogger(zap.ErrorLevel)\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tif m := logger.Check(zap.InfoLevel, getMessage(0)); m != nil {\n\t\t\t\t\tm.Write()\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"Zap.Sugar\", func(b *testing.B) {\n\t\tlogger := newZapLogger(zap.ErrorLevel).Sugar()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"Zap.SugarFormatting\", func(b *testing.B) {\n\t\tlogger := newZapLogger(zap.ErrorLevel).Sugar()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Infof(\"%v %v %v %s %v %v %v %v %v %s\\n\", fakeFmtArgs()...)\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"apex/log\", func(b *testing.B) {\n\t\tlogger := newDisabledApexLog()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"sirupsen/logrus\", func(b *testing.B) {\n\t\tlogger := newDisabledLogrus()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"rs/zerolog\", func(b *testing.B) {\n\t\tlogger := newDisabledZerolog()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info().Msg(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"slog\", func(b *testing.B) {\n\t\tlogger := newDisabledSlog()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"slog.LogAttrs\", func(b *testing.B) {\n\t\tlogger := newDisabledSlog()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.LogAttrs(context.Background(), slog.LevelInfo, getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n}\n\nfunc BenchmarkDisabledAccumulatedContext(b *testing.B) {\n\tb.Logf(\"Logging at a disabled level with some accumulated context.\")\n\tb.Run(\"Zap\", func(b *testing.B) {\n\t\tlogger := newZapLogger(zap.ErrorLevel).With(fakeFields()...)\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"Zap.Check\", func(b *testing.B) {\n\t\tlogger := newZapLogger(zap.ErrorLevel).With(fakeFields()...)\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tif m := logger.Check(zap.InfoLevel, getMessage(0)); m != nil {\n\t\t\t\t\tm.Write()\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"Zap.Sugar\", func(b *testing.B) {\n\t\tlogger := newZapLogger(zap.ErrorLevel).With(fakeFields()...).Sugar()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"Zap.SugarFormatting\", func(b *testing.B) {\n\t\tlogger := newZapLogger(zap.ErrorLevel).With(fakeFields()...).Sugar()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Infof(\"%v %v %v %s %v %v %v %v %v %s\\n\", fakeFmtArgs()...)\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"apex/log\", func(b *testing.B) {\n\t\tlogger := newDisabledApexLog().WithFields(fakeApexFields())\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"sirupsen/logrus\", func(b *testing.B) {\n\t\tlogger := newDisabledLogrus().WithFields(fakeLogrusFields())\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"rs/zerolog\", func(b *testing.B) {\n\t\tlogger := fakeZerologContext(newDisabledZerolog().With()).Logger()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info().Msg(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"slog\", func(b *testing.B) {\n\t\tlogger := newDisabledSlog(fakeSlogFields()...)\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"slog.LogAttrs\", func(b *testing.B) {\n\t\tlogger := newDisabledSlog(fakeSlogFields()...)\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.LogAttrs(context.Background(), slog.LevelInfo, getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n}\n\nfunc BenchmarkDisabledAddingFields(b *testing.B) {\n\tb.Logf(\"Logging at a disabled level, adding context at each log site.\")\n\tb.Run(\"Zap\", func(b *testing.B) {\n\t\tlogger := newZapLogger(zap.ErrorLevel)\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info(getMessage(0), fakeFields()...)\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"Zap.Check\", func(b *testing.B) {\n\t\tlogger := newZapLogger(zap.ErrorLevel)\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tif m := logger.Check(zap.InfoLevel, getMessage(0)); m != nil {\n\t\t\t\t\tm.Write(fakeFields()...)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"Zap.Sugar\", func(b *testing.B) {\n\t\tlogger := newZapLogger(zap.ErrorLevel).Sugar()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Infow(getMessage(0), fakeSugarFields()...)\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"apex/log\", func(b *testing.B) {\n\t\tlogger := newDisabledApexLog()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.WithFields(fakeApexFields()).Info(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"sirupsen/logrus\", func(b *testing.B) {\n\t\tlogger := newDisabledLogrus()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.WithFields(fakeLogrusFields()).Info(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"rs/zerolog\", func(b *testing.B) {\n\t\tlogger := newDisabledZerolog()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tfakeZerologFields(logger.Info()).Msg(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"slog\", func(b *testing.B) {\n\t\tlogger := newDisabledSlog()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info(getMessage(0), fakeSlogArgs()...)\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"slog.LogAttrs\", func(b *testing.B) {\n\t\tlogger := newDisabledSlog()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.LogAttrs(context.Background(), slog.LevelInfo, getMessage(0), fakeSlogFields()...)\n\t\t\t}\n\t\t})\n\t})\n}\n\nfunc BenchmarkWithoutFields(b *testing.B) {\n\tb.Logf(\"Logging without any structured context.\")\n\tb.Run(\"Zap\", func(b *testing.B) {\n\t\tlogger := newZapLogger(zap.DebugLevel)\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"Zap.Check\", func(b *testing.B) {\n\t\tlogger := newZapLogger(zap.DebugLevel)\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tif ce := logger.Check(zap.InfoLevel, getMessage(0)); ce != nil {\n\t\t\t\t\tce.Write()\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"Zap.CheckSampled\", func(b *testing.B) {\n\t\tlogger := newSampledLogger(zap.DebugLevel)\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\ti := 0\n\t\t\tfor pb.Next() {\n\t\t\t\ti++\n\t\t\t\tif ce := logger.Check(zap.InfoLevel, getMessage(i)); ce != nil {\n\t\t\t\t\tce.Write()\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"Zap.Sugar\", func(b *testing.B) {\n\t\tlogger := newZapLogger(zap.DebugLevel).Sugar()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"Zap.SugarFormatting\", func(b *testing.B) {\n\t\tlogger := newZapLogger(zap.DebugLevel).Sugar()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Infof(\"%v %v %v %s %v %v %v %v %v %s\\n\", fakeFmtArgs()...)\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"apex/log\", func(b *testing.B) {\n\t\tlogger := newApexLog()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"go-kit/kit/log\", func(b *testing.B) {\n\t\tlogger := newKitLog()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tif err := logger.Log(getMessage(0), getMessage(1)); err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"inconshreveable/log15\", func(b *testing.B) {\n\t\tlogger := newLog15()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"sirupsen/logrus\", func(b *testing.B) {\n\t\tlogger := newLogrus()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"stdlib.Println\", func(b *testing.B) {\n\t\tlogger := log.New(&ztest.Discarder{}, \"\", log.LstdFlags)\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Println(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"stdlib.Printf\", func(b *testing.B) {\n\t\tlogger := log.New(&ztest.Discarder{}, \"\", log.LstdFlags)\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Printf(\"%v %v %v %s %v %v %v %v %v %s\\n\", fakeFmtArgs()...)\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"rs/zerolog\", func(b *testing.B) {\n\t\tlogger := newZerolog()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info().Msg(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"rs/zerolog.Formatting\", func(b *testing.B) {\n\t\tlogger := newZerolog()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info().Msgf(\"%v %v %v %s %v %v %v %v %v %s\\n\", fakeFmtArgs()...)\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"rs/zerolog.Check\", func(b *testing.B) {\n\t\tlogger := newZerolog()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tif e := logger.Info(); e.Enabled() {\n\t\t\t\t\te.Msg(getMessage(0))\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"slog\", func(b *testing.B) {\n\t\tlogger := newSlog()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"slog.LogAttrs\", func(b *testing.B) {\n\t\tlogger := newSlog()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.LogAttrs(context.Background(), slog.LevelInfo, getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n}\n\nfunc BenchmarkAccumulatedContext(b *testing.B) {\n\tb.Logf(\"Logging with some accumulated context.\")\n\tb.Run(\"Zap\", func(b *testing.B) {\n\t\tlogger := newZapLogger(zap.DebugLevel).With(fakeFields()...)\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"Zap.Check\", func(b *testing.B) {\n\t\tlogger := newZapLogger(zap.DebugLevel).With(fakeFields()...)\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tif ce := logger.Check(zap.InfoLevel, getMessage(0)); ce != nil {\n\t\t\t\t\tce.Write()\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"Zap.CheckSampled\", func(b *testing.B) {\n\t\tlogger := newSampledLogger(zap.DebugLevel).With(fakeFields()...)\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\ti := 0\n\t\t\tfor pb.Next() {\n\t\t\t\ti++\n\t\t\t\tif ce := logger.Check(zap.InfoLevel, getMessage(i)); ce != nil {\n\t\t\t\t\tce.Write()\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"Zap.Sugar\", func(b *testing.B) {\n\t\tlogger := newZapLogger(zap.DebugLevel).With(fakeFields()...).Sugar()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"Zap.SugarFormatting\", func(b *testing.B) {\n\t\tlogger := newZapLogger(zap.DebugLevel).With(fakeFields()...).Sugar()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Infof(\"%v %v %v %s %v %v %v %v %v %s\\n\", fakeFmtArgs()...)\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"apex/log\", func(b *testing.B) {\n\t\tlogger := newApexLog().WithFields(fakeApexFields())\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"go-kit/kit/log\", func(b *testing.B) {\n\t\tlogger := newKitLog(fakeSugarFields()...)\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tif err := logger.Log(getMessage(0), getMessage(1)); err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"inconshreveable/log15\", func(b *testing.B) {\n\t\tlogger := newLog15().New(fakeSugarFields())\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"sirupsen/logrus\", func(b *testing.B) {\n\t\tlogger := newLogrus().WithFields(fakeLogrusFields())\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"rs/zerolog\", func(b *testing.B) {\n\t\tlogger := fakeZerologContext(newZerolog().With()).Logger()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info().Msg(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"rs/zerolog.Check\", func(b *testing.B) {\n\t\tlogger := fakeZerologContext(newZerolog().With()).Logger()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tif e := logger.Info(); e.Enabled() {\n\t\t\t\t\te.Msg(getMessage(0))\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"rs/zerolog.Formatting\", func(b *testing.B) {\n\t\tlogger := fakeZerologContext(newZerolog().With()).Logger()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info().Msgf(\"%v %v %v %s %v %v %v %v %v %s\\n\", fakeFmtArgs()...)\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"slog\", func(b *testing.B) {\n\t\tlogger := newSlog(fakeSlogFields()...)\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"slog.LogAttrs\", func(b *testing.B) {\n\t\tlogger := newSlog(fakeSlogFields()...)\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.LogAttrs(context.Background(), slog.LevelInfo, getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n}\n\nfunc BenchmarkAddingFields(b *testing.B) {\n\tb.Logf(\"Logging with additional context at each log site.\")\n\tb.Run(\"Zap\", func(b *testing.B) {\n\t\tlogger := newZapLogger(zap.DebugLevel)\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info(getMessage(0), fakeFields()...)\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"Zap.Check\", func(b *testing.B) {\n\t\tlogger := newZapLogger(zap.DebugLevel)\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tif ce := logger.Check(zap.InfoLevel, getMessage(0)); ce != nil {\n\t\t\t\t\tce.Write(fakeFields()...)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"Zap.CheckSampled\", func(b *testing.B) {\n\t\tlogger := newSampledLogger(zap.DebugLevel)\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\ti := 0\n\t\t\tfor pb.Next() {\n\t\t\t\ti++\n\t\t\t\tif ce := logger.Check(zap.InfoLevel, getMessage(i)); ce != nil {\n\t\t\t\t\tce.Write(fakeFields()...)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"Zap.Sugar\", func(b *testing.B) {\n\t\tlogger := newZapLogger(zap.DebugLevel).Sugar()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Infow(getMessage(0), fakeSugarFields()...)\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"apex/log\", func(b *testing.B) {\n\t\tlogger := newApexLog()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.WithFields(fakeApexFields()).Info(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"go-kit/kit/log\", func(b *testing.B) {\n\t\tlogger := newKitLog()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tif err := logger.Log(fakeSugarFields()...); err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"inconshreveable/log15\", func(b *testing.B) {\n\t\tlogger := newLog15()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info(getMessage(0), fakeSugarFields()...)\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"sirupsen/logrus\", func(b *testing.B) {\n\t\tlogger := newLogrus()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.WithFields(fakeLogrusFields()).Info(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"rs/zerolog\", func(b *testing.B) {\n\t\tlogger := newZerolog()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tfakeZerologFields(logger.Info()).Msg(getMessage(0))\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"rs/zerolog.Check\", func(b *testing.B) {\n\t\tlogger := newZerolog()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tif e := logger.Info(); e.Enabled() {\n\t\t\t\t\tfakeZerologFields(e).Msg(getMessage(0))\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"slog\", func(b *testing.B) {\n\t\tlogger := newSlog()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Info(getMessage(0), fakeSlogArgs()...)\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"slog.LogAttrs\", func(b *testing.B) {\n\t\tlogger := newSlog()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.LogAttrs(context.Background(), slog.LevelInfo, getMessage(0), fakeSlogFields()...)\n\t\t\t}\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "benchmarks/slog_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage benchmarks\n\nimport (\n\t\"io\"\n\t\"log/slog\"\n)\n\nfunc newSlog(fields ...slog.Attr) *slog.Logger {\n\treturn slog.New(slog.NewJSONHandler(io.Discard, nil).WithAttrs(fields))\n}\n\nfunc newDisabledSlog(fields ...slog.Attr) *slog.Logger {\n\treturn slog.New(slog.NewJSONHandler(io.Discard, &slog.HandlerOptions{Level: slog.LevelError}).WithAttrs(fields))\n}\n\nfunc fakeSlogFields() []slog.Attr {\n\treturn []slog.Attr{\n\t\tslog.Int(\"int\", _tenInts[0]),\n\t\tslog.Any(\"ints\", _tenInts),\n\t\tslog.String(\"string\", _tenStrings[0]),\n\t\tslog.Any(\"strings\", _tenStrings),\n\t\tslog.Time(\"time\", _tenTimes[0]),\n\t\tslog.Any(\"times\", _tenTimes),\n\t\tslog.Any(\"user1\", _oneUser),\n\t\tslog.Any(\"user2\", _oneUser),\n\t\tslog.Any(\"users\", _tenUsers),\n\t\tslog.Any(\"error\", errExample),\n\t}\n}\n\nfunc fakeSlogArgs() []any {\n\treturn []any{\n\t\t\"int\", _tenInts[0],\n\t\t\"ints\", _tenInts,\n\t\t\"string\", _tenStrings[0],\n\t\t\"strings\", _tenStrings,\n\t\t\"time\", _tenTimes[0],\n\t\t\"times\", _tenTimes,\n\t\t\"user1\", _oneUser,\n\t\t\"user2\", _oneUser,\n\t\t\"users\", _tenUsers,\n\t\t\"error\", errExample,\n\t}\n}\n"
  },
  {
    "path": "benchmarks/zap_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage benchmarks\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.uber.org/multierr\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/internal/ztest\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\nvar (\n\terrExample = errors.New(\"fail\")\n\n\t_messages   = fakeMessages(1000)\n\t_tenInts    = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}\n\t_tenStrings = []string{\"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\", \"i\", \"j\"}\n\t_tenTimes   = []time.Time{\n\t\ttime.Unix(0, 0),\n\t\ttime.Unix(1, 0),\n\t\ttime.Unix(2, 0),\n\t\ttime.Unix(3, 0),\n\t\ttime.Unix(4, 0),\n\t\ttime.Unix(5, 0),\n\t\ttime.Unix(6, 0),\n\t\ttime.Unix(7, 0),\n\t\ttime.Unix(8, 0),\n\t\ttime.Unix(9, 0),\n\t}\n\t_oneUser = &user{\n\t\tName:      \"Jane Doe\",\n\t\tEmail:     \"jane@test.com\",\n\t\tCreatedAt: time.Date(1980, 1, 1, 12, 0, 0, 0, time.UTC),\n\t}\n\t_tenUsers = users{\n\t\t_oneUser,\n\t\t_oneUser,\n\t\t_oneUser,\n\t\t_oneUser,\n\t\t_oneUser,\n\t\t_oneUser,\n\t\t_oneUser,\n\t\t_oneUser,\n\t\t_oneUser,\n\t\t_oneUser,\n\t}\n)\n\nfunc fakeMessages(n int) []string {\n\tmessages := make([]string, n)\n\tfor i := range messages {\n\t\tmessages[i] = fmt.Sprintf(\"Test logging, but use a somewhat realistic message length. (#%v)\", i)\n\t}\n\treturn messages\n}\n\nfunc getMessage(iter int) string {\n\treturn _messages[iter%1000]\n}\n\ntype users []*user\n\nfunc (uu users) MarshalLogArray(arr zapcore.ArrayEncoder) error {\n\tvar err error\n\tfor i := range uu {\n\t\terr = multierr.Append(err, arr.AppendObject(uu[i]))\n\t}\n\treturn err\n}\n\ntype user struct {\n\tName      string    `json:\"name\"`\n\tEmail     string    `json:\"email\"`\n\tCreatedAt time.Time `json:\"created_at\"`\n}\n\nfunc (u *user) MarshalLogObject(enc zapcore.ObjectEncoder) error {\n\tenc.AddString(\"name\", u.Name)\n\tenc.AddString(\"email\", u.Email)\n\tenc.AddInt64(\"createdAt\", u.CreatedAt.UnixNano())\n\treturn nil\n}\n\nfunc newZapLogger(lvl zapcore.Level) *zap.Logger {\n\tec := zap.NewProductionEncoderConfig()\n\tec.EncodeDuration = zapcore.NanosDurationEncoder\n\tec.EncodeTime = zapcore.EpochNanosTimeEncoder\n\tenc := zapcore.NewJSONEncoder(ec)\n\treturn zap.New(zapcore.NewCore(\n\t\tenc,\n\t\t&ztest.Discarder{},\n\t\tlvl,\n\t))\n}\n\nfunc newSampledLogger(lvl zapcore.Level) *zap.Logger {\n\treturn zap.New(zapcore.NewSamplerWithOptions(\n\t\tnewZapLogger(zap.DebugLevel).Core(),\n\t\t100*time.Millisecond,\n\t\t10, // first\n\t\t10, // thereafter\n\t))\n}\n\nfunc fakeFields() []zap.Field {\n\treturn []zap.Field{\n\t\tzap.Int(\"int\", _tenInts[0]),\n\t\tzap.Ints(\"ints\", _tenInts),\n\t\tzap.String(\"string\", _tenStrings[0]),\n\t\tzap.Strings(\"strings\", _tenStrings),\n\t\tzap.Time(\"time\", _tenTimes[0]),\n\t\tzap.Times(\"times\", _tenTimes),\n\t\tzap.Object(\"user1\", _oneUser),\n\t\tzap.Object(\"user2\", _oneUser),\n\t\tzap.Object(\"user3\", nil),\n\t\tzap.Array(\"users\", _tenUsers),\n\t\tzap.Error(errExample),\n\t}\n}\n\nfunc fakeSugarFields() []interface{} {\n\treturn []interface{}{\n\t\t\"int\", _tenInts[0],\n\t\t\"ints\", _tenInts,\n\t\t\"string\", _tenStrings[0],\n\t\t\"strings\", _tenStrings,\n\t\t\"time\", _tenTimes[0],\n\t\t\"times\", _tenTimes,\n\t\t\"user1\", _oneUser,\n\t\t\"user2\", _oneUser,\n\t\t\"users\", _tenUsers,\n\t\t\"error\", errExample,\n\t}\n}\n\nfunc fakeFmtArgs() []interface{} {\n\t// Need to keep this a function instead of a package-global var so that we\n\t// pay the cast-to-interface{} penalty on each call.\n\treturn []interface{}{\n\t\t_tenInts[0],\n\t\t_tenInts,\n\t\t_tenStrings[0],\n\t\t_tenStrings,\n\t\t_tenTimes[0],\n\t\t_tenTimes,\n\t\t_oneUser,\n\t\t_oneUser,\n\t\t_tenUsers,\n\t\terrExample,\n\t}\n}\n"
  },
  {
    "path": "benchmarks/zerolog_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage benchmarks\n\nimport (\n\t\"io\"\n\n\t\"github.com/rs/zerolog\"\n)\n\nfunc newZerolog() zerolog.Logger {\n\treturn zerolog.New(io.Discard).With().Timestamp().Logger()\n}\n\nfunc newDisabledZerolog() zerolog.Logger {\n\treturn newZerolog().Level(zerolog.Disabled)\n}\n\nfunc (u *user) MarshalZerologObject(e *zerolog.Event) {\n\te.Str(\"name\", u.Name).\n\t\tStr(\"email\", u.Email).\n\t\tInt64(\"createdAt\", u.CreatedAt.UnixNano())\n}\n\nfunc (uu users) MarshalZerologArray(a *zerolog.Array) {\n\tfor _, u := range uu {\n\t\ta.Object(u)\n\t}\n}\n\nfunc fakeZerologFields(e *zerolog.Event) *zerolog.Event {\n\treturn e.\n\t\tInt(\"int\", _tenInts[0]).\n\t\tInts(\"ints\", _tenInts).\n\t\tStr(\"string\", _tenStrings[0]).\n\t\tStrs(\"strings\", _tenStrings).\n\t\tTime(\"time\", _tenTimes[0]).\n\t\tTimes(\"times\", _tenTimes).\n\t\tObject(\"user1\", _oneUser).\n\t\tObject(\"user2\", _oneUser).\n\t\tArray(\"users\", _tenUsers).\n\t\tErr(errExample)\n}\n\nfunc fakeZerologContext(c zerolog.Context) zerolog.Context {\n\treturn c.\n\t\tInt(\"int\", _tenInts[0]).\n\t\tInts(\"ints\", _tenInts).\n\t\tStr(\"string\", _tenStrings[0]).\n\t\tStrs(\"strings\", _tenStrings).\n\t\tTime(\"time\", _tenTimes[0]).\n\t\tTimes(\"times\", _tenTimes).\n\t\tObject(\"user1\", _oneUser).\n\t\tObject(\"user2\", _oneUser).\n\t\tArray(\"users\", _tenUsers).\n\t\tErr(errExample)\n}\n"
  },
  {
    "path": "buffer/buffer.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// Package buffer provides a thin wrapper around a byte slice. Unlike the\n// standard library's bytes.Buffer, it supports a portion of the strconv\n// package's zero-allocation formatters.\npackage buffer // import \"go.uber.org/zap/buffer\"\n\nimport (\n\t\"strconv\"\n\t\"time\"\n)\n\nconst _size = 1024 // by default, create 1 KiB buffers\n\n// Buffer is a thin wrapper around a byte slice. It's intended to be pooled, so\n// the only way to construct one is via a Pool.\ntype Buffer struct {\n\tbs   []byte\n\tpool Pool\n}\n\n// AppendByte writes a single byte to the Buffer.\nfunc (b *Buffer) AppendByte(v byte) {\n\tb.bs = append(b.bs, v)\n}\n\n// AppendBytes writes the given slice of bytes to the Buffer.\nfunc (b *Buffer) AppendBytes(v []byte) {\n\tb.bs = append(b.bs, v...)\n}\n\n// AppendString writes a string to the Buffer.\nfunc (b *Buffer) AppendString(s string) {\n\tb.bs = append(b.bs, s...)\n}\n\n// AppendInt appends an integer to the underlying buffer (assuming base 10).\nfunc (b *Buffer) AppendInt(i int64) {\n\tb.bs = strconv.AppendInt(b.bs, i, 10)\n}\n\n// AppendTime appends the time formatted using the specified layout.\nfunc (b *Buffer) AppendTime(t time.Time, layout string) {\n\tb.bs = t.AppendFormat(b.bs, layout)\n}\n\n// AppendUint appends an unsigned integer to the underlying buffer (assuming\n// base 10).\nfunc (b *Buffer) AppendUint(i uint64) {\n\tb.bs = strconv.AppendUint(b.bs, i, 10)\n}\n\n// AppendBool appends a bool to the underlying buffer.\nfunc (b *Buffer) AppendBool(v bool) {\n\tb.bs = strconv.AppendBool(b.bs, v)\n}\n\n// AppendFloat appends a float to the underlying buffer. It doesn't quote NaN\n// or +/- Inf.\nfunc (b *Buffer) AppendFloat(f float64, bitSize int) {\n\tb.bs = strconv.AppendFloat(b.bs, f, 'f', -1, bitSize)\n}\n\n// Len returns the length of the underlying byte slice.\nfunc (b *Buffer) Len() int {\n\treturn len(b.bs)\n}\n\n// Cap returns the capacity of the underlying byte slice.\nfunc (b *Buffer) Cap() int {\n\treturn cap(b.bs)\n}\n\n// Bytes returns a mutable reference to the underlying byte slice.\nfunc (b *Buffer) Bytes() []byte {\n\treturn b.bs\n}\n\n// String returns a string copy of the underlying byte slice.\nfunc (b *Buffer) String() string {\n\treturn string(b.bs)\n}\n\n// Reset resets the underlying byte slice. Subsequent writes re-use the slice's\n// backing array.\nfunc (b *Buffer) Reset() {\n\tb.bs = b.bs[:0]\n}\n\n// Write implements io.Writer.\nfunc (b *Buffer) Write(bs []byte) (int, error) {\n\tb.bs = append(b.bs, bs...)\n\treturn len(bs), nil\n}\n\n// WriteByte writes a single byte to the Buffer.\n//\n// Error returned is always nil, function signature is compatible\n// with bytes.Buffer and bufio.Writer\nfunc (b *Buffer) WriteByte(v byte) error {\n\tb.AppendByte(v)\n\treturn nil\n}\n\n// WriteString writes a string to the Buffer.\n//\n// Error returned is always nil, function signature is compatible\n// with bytes.Buffer and bufio.Writer\nfunc (b *Buffer) WriteString(s string) (int, error) {\n\tb.AppendString(s)\n\treturn len(s), nil\n}\n\n// TrimNewline trims any final \"\\n\" byte from the end of the buffer.\nfunc (b *Buffer) TrimNewline() {\n\tif i := len(b.bs) - 1; i >= 0 {\n\t\tif b.bs[i] == '\\n' {\n\t\t\tb.bs = b.bs[:i]\n\t\t}\n\t}\n}\n\n// Free returns the Buffer to its Pool.\n//\n// Callers must not retain references to the Buffer after calling Free.\nfunc (b *Buffer) Free() {\n\tb.pool.put(b)\n}\n"
  },
  {
    "path": "buffer/buffer_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage buffer\n\nimport (\n\t\"bytes\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestBufferWrites(t *testing.T) {\n\tbuf := NewPool().Get()\n\n\ttests := []struct {\n\t\tdesc string\n\t\tf    func()\n\t\twant string\n\t}{\n\t\t{\"AppendByte\", func() { buf.AppendByte('v') }, \"v\"},\n\t\t{\"AppendString\", func() { buf.AppendString(\"foo\") }, \"foo\"},\n\t\t{\"AppendIntPositive\", func() { buf.AppendInt(42) }, \"42\"},\n\t\t{\"AppendIntNegative\", func() { buf.AppendInt(-42) }, \"-42\"},\n\t\t{\"AppendUint\", func() { buf.AppendUint(42) }, \"42\"},\n\t\t{\"AppendBool\", func() { buf.AppendBool(true) }, \"true\"},\n\t\t{\"AppendFloat64\", func() { buf.AppendFloat(3.14, 64) }, \"3.14\"},\n\t\t// Intentionally introduce some floating-point error.\n\t\t{\"AppendFloat32\", func() { buf.AppendFloat(float64(float32(3.14)), 32) }, \"3.14\"},\n\t\t{\"AppendWrite\", func() { buf.Write([]byte(\"foo\")) }, \"foo\"},\n\t\t{\"AppendTime\", func() { buf.AppendTime(time.Date(2000, 1, 2, 3, 4, 5, 6, time.UTC), time.RFC3339) }, \"2000-01-02T03:04:05Z\"},\n\t\t{\"WriteByte\", func() { buf.WriteByte('v') }, \"v\"},\n\t\t{\"WriteString\", func() { buf.WriteString(\"foo\") }, \"foo\"},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tbuf.Reset()\n\t\t\ttt.f()\n\t\t\tassert.Equal(t, tt.want, buf.String(), \"Unexpected buffer.String().\")\n\t\t\tassert.Equal(t, tt.want, string(buf.Bytes()), \"Unexpected string(buffer.Bytes()).\")\n\t\t\tassert.Equal(t, len(tt.want), buf.Len(), \"Unexpected buffer length.\")\n\t\t\t// We're not writing more than a kibibyte in tests.\n\t\t\tassert.Equal(t, _size, buf.Cap(), \"Expected buffer capacity to remain constant.\")\n\t\t})\n\t}\n}\n\nfunc BenchmarkBuffers(b *testing.B) {\n\t// Because we use the strconv.AppendFoo functions so liberally, we can't\n\t// use the standard library's bytes.Buffer anyways (without incurring a\n\t// bunch of extra allocations). Nevertheless, let's make sure that we're\n\t// not losing any precious nanoseconds.\n\tstr := strings.Repeat(\"a\", 1024)\n\tslice := make([]byte, 0, 1024)\n\tbuf := bytes.NewBuffer(slice)\n\tcustom := NewPool().Get()\n\tb.Run(\"ByteSlice\", func(b *testing.B) {\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\tslice = append(slice, str...)\n\t\t\tslice = slice[:0]\n\t\t}\n\t})\n\tb.Run(\"BytesBuffer\", func(b *testing.B) {\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\tbuf.WriteString(str)\n\t\t\tbuf.Reset()\n\t\t}\n\t})\n\tb.Run(\"CustomBuffer\", func(b *testing.B) {\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\tcustom.AppendString(str)\n\t\t\tcustom.Reset()\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "buffer/pool.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage buffer\n\nimport (\n\t\"go.uber.org/zap/internal/pool\"\n)\n\n// A Pool is a type-safe wrapper around a sync.Pool.\ntype Pool struct {\n\tp *pool.Pool[*Buffer]\n}\n\n// NewPool constructs a new Pool.\nfunc NewPool() Pool {\n\treturn Pool{\n\t\tp: pool.New(func() *Buffer {\n\t\t\treturn &Buffer{\n\t\t\t\tbs: make([]byte, 0, _size),\n\t\t\t}\n\t\t}),\n\t}\n}\n\n// Get retrieves a Buffer from the pool, creating one if necessary.\nfunc (p Pool) Get() *Buffer {\n\tbuf := p.p.Get()\n\tbuf.Reset()\n\tbuf.pool = p\n\treturn buf\n}\n\nfunc (p Pool) put(buf *Buffer) {\n\tp.p.Put(buf)\n}\n"
  },
  {
    "path": "buffer/pool_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage buffer\n\nimport (\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestBuffers(t *testing.T) {\n\tconst dummyData = \"dummy data\"\n\tp := NewPool()\n\n\tvar wg sync.WaitGroup\n\tfor g := 0; g < 10; g++ {\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tfor i := 0; i < 100; i++ {\n\t\t\t\tbuf := p.Get()\n\t\t\t\tassert.Zero(t, buf.Len(), \"Expected truncated buffer\")\n\t\t\t\tassert.NotZero(t, buf.Cap(), \"Expected non-zero capacity\")\n\n\t\t\t\tbuf.AppendString(dummyData)\n\t\t\t\tassert.Equal(t, buf.Len(), len(dummyData), \"Expected buffer to contain dummy data\")\n\n\t\t\t\tbuf.Free()\n\t\t\t}\n\t\t\twg.Done()\n\t\t}()\n\t}\n\twg.Wait()\n}\n"
  },
  {
    "path": "checklicense.sh",
    "content": "#!/bin/bash -e\n\nERROR_COUNT=0\nwhile read -r file\ndo\n\tcase \"$(head -1 \"${file}\")\" in\n\t\t*\"Copyright (c) \"*\" Uber Technologies, Inc.\")\n\t\t\t# everything's cool\n\t\t\t;;\n\t\t*)\n\t\t\techo \"$file is missing license header.\"\n\t\t\t(( ERROR_COUNT++ ))\n\t\t\t;;\n\tesac\ndone < <(git ls-files \"*\\.go\")\n\nexit $ERROR_COUNT\n"
  },
  {
    "path": "clock_test.go",
    "content": "// Copyright (c) 2020 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/zap/zaptest/observer\"\n)\n\ntype constantClock time.Time\n\nfunc (c constantClock) Now() time.Time { return time.Time(c) }\nfunc (c constantClock) NewTicker(d time.Duration) *time.Ticker {\n\treturn &time.Ticker{}\n}\n\nfunc TestWithClock(t *testing.T) {\n\tdate := time.Date(2077, 1, 23, 10, 15, 13, 441, time.UTC)\n\tclock := constantClock(date)\n\twithLogger(t, DebugLevel, []Option{WithClock(clock)}, func(log *Logger, logs *observer.ObservedLogs) {\n\t\tlog.Info(\"\")\n\t\trequire.Equal(t, 1, logs.Len(), \"Expected only one log entry to be written.\")\n\t\tassert.Equal(t, date, logs.All()[0].Time, \"Unexpected entry time.\")\n\t})\n}\n"
  },
  {
    "path": "common_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"sync\"\n\t\"testing\"\n\n\t\"go.uber.org/zap/zapcore\"\n\t\"go.uber.org/zap/zaptest/observer\"\n)\n\nfunc opts(opts ...Option) []Option {\n\treturn opts\n}\n\n// Here specifically to introduce an easily-identifiable filename for testing\n// stacktraces and caller skips.\nfunc withLogger(t testing.TB, e zapcore.LevelEnabler, opts []Option, f func(*Logger, *observer.ObservedLogs)) {\n\tfac, logs := observer.New(e)\n\tlog := New(fac, opts...)\n\tf(log, logs)\n}\n\nfunc withSugar(t testing.TB, e zapcore.LevelEnabler, opts []Option, f func(*SugaredLogger, *observer.ObservedLogs)) {\n\twithLogger(t, e, opts, func(logger *Logger, logs *observer.ObservedLogs) { f(logger.Sugar(), logs) })\n}\n\nfunc runConcurrently(goroutines, iterations int, wg *sync.WaitGroup, f func()) {\n\twg.Add(goroutines)\n\tfor g := 0; g < goroutines; g++ {\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tfor i := 0; i < iterations; i++ {\n\t\t\t\tf()\n\t\t\t}\n\t\t}()\n\t}\n}\n"
  },
  {
    "path": "config.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"errors\"\n\t\"sort\"\n\t\"time\"\n\n\t\"go.uber.org/zap/zapcore\"\n)\n\n// SamplingConfig sets a sampling strategy for the logger. Sampling caps the\n// global CPU and I/O load that logging puts on your process while attempting\n// to preserve a representative subset of your logs.\n//\n// If specified, the Sampler will invoke the Hook after each decision.\n//\n// Values configured here are per-second. See zapcore.NewSamplerWithOptions for\n// details.\ntype SamplingConfig struct {\n\tInitial    int                                           `json:\"initial\" yaml:\"initial\"`\n\tThereafter int                                           `json:\"thereafter\" yaml:\"thereafter\"`\n\tHook       func(zapcore.Entry, zapcore.SamplingDecision) `json:\"-\" yaml:\"-\"`\n}\n\n// Config offers a declarative way to construct a logger. It doesn't do\n// anything that can't be done with New, Options, and the various\n// zapcore.WriteSyncer and zapcore.Core wrappers, but it's a simpler way to\n// toggle common options.\n//\n// Note that Config intentionally supports only the most common options. More\n// unusual logging setups (logging to network connections or message queues,\n// splitting output between multiple files, etc.) are possible, but require\n// direct use of the zapcore package. For sample code, see the package-level\n// BasicConfiguration and AdvancedConfiguration examples.\n//\n// For an example showing runtime log level changes, see the documentation for\n// AtomicLevel.\ntype Config struct {\n\t// Level is the minimum enabled logging level. Note that this is a dynamic\n\t// level, so calling Config.Level.SetLevel will atomically change the log\n\t// level of all loggers descended from this config.\n\tLevel AtomicLevel `json:\"level\" yaml:\"level\"`\n\t// Development puts the logger in development mode, which changes the\n\t// behavior of DPanicLevel and takes stacktraces more liberally.\n\tDevelopment bool `json:\"development\" yaml:\"development\"`\n\t// DisableCaller stops annotating logs with the calling function's file\n\t// name and line number. By default, all logs are annotated.\n\tDisableCaller bool `json:\"disableCaller\" yaml:\"disableCaller\"`\n\t// DisableStacktrace completely disables automatic stacktrace capturing. By\n\t// default, stacktraces are captured for WarnLevel and above logs in\n\t// development and ErrorLevel and above in production.\n\tDisableStacktrace bool `json:\"disableStacktrace\" yaml:\"disableStacktrace\"`\n\t// Sampling sets a sampling policy. A nil SamplingConfig disables sampling.\n\tSampling *SamplingConfig `json:\"sampling\" yaml:\"sampling\"`\n\t// Encoding sets the logger's encoding. Valid values are \"json\" and\n\t// \"console\", as well as any third-party encodings registered via\n\t// RegisterEncoder.\n\tEncoding string `json:\"encoding\" yaml:\"encoding\"`\n\t// EncoderConfig sets options for the chosen encoder. See\n\t// zapcore.EncoderConfig for details.\n\tEncoderConfig zapcore.EncoderConfig `json:\"encoderConfig\" yaml:\"encoderConfig\"`\n\t// OutputPaths is a list of URLs or file paths to write logging output to.\n\t// See Open for details.\n\tOutputPaths []string `json:\"outputPaths\" yaml:\"outputPaths\"`\n\t// ErrorOutputPaths is a list of URLs to write internal logger errors to.\n\t// The default is standard error.\n\t//\n\t// Note that this setting only affects internal errors; for sample code that\n\t// sends error-level logs to a different location from info- and debug-level\n\t// logs, see the package-level AdvancedConfiguration example.\n\tErrorOutputPaths []string `json:\"errorOutputPaths\" yaml:\"errorOutputPaths\"`\n\t// InitialFields is a collection of fields to add to the root logger.\n\tInitialFields map[string]interface{} `json:\"initialFields\" yaml:\"initialFields\"`\n}\n\n// NewProductionEncoderConfig returns an opinionated EncoderConfig for\n// production environments.\n//\n// Messages encoded with this configuration will be JSON-formatted\n// and will have the following keys by default:\n//\n//   - \"level\": The logging level (e.g. \"info\", \"error\").\n//   - \"ts\": The current time in number of seconds since the Unix epoch.\n//   - \"msg\": The message passed to the log statement.\n//   - \"caller\": If available, a short path to the file and line number\n//     where the log statement was issued.\n//     The logger configuration determines whether this field is captured.\n//   - \"stacktrace\": If available, a stack trace from the line\n//     where the log statement was issued.\n//     The logger configuration determines whether this field is captured.\n//\n// By default, the following formats are used for different types:\n//\n//   - Time is formatted as floating-point number of seconds since the Unix\n//     epoch.\n//   - Duration is formatted as floating-point number of seconds.\n//\n// You may change these by setting the appropriate fields in the returned\n// object.\n// For example, use the following to change the time encoding format:\n//\n//\tcfg := zap.NewProductionEncoderConfig()\n//\tcfg.EncodeTime = zapcore.ISO8601TimeEncoder\nfunc NewProductionEncoderConfig() zapcore.EncoderConfig {\n\treturn zapcore.EncoderConfig{\n\t\tTimeKey:        \"ts\",\n\t\tLevelKey:       \"level\",\n\t\tNameKey:        \"logger\",\n\t\tCallerKey:      \"caller\",\n\t\tFunctionKey:    zapcore.OmitKey,\n\t\tMessageKey:     \"msg\",\n\t\tStacktraceKey:  \"stacktrace\",\n\t\tLineEnding:     zapcore.DefaultLineEnding,\n\t\tEncodeLevel:    zapcore.LowercaseLevelEncoder,\n\t\tEncodeTime:     zapcore.EpochTimeEncoder,\n\t\tEncodeDuration: zapcore.SecondsDurationEncoder,\n\t\tEncodeCaller:   zapcore.ShortCallerEncoder,\n\t}\n}\n\n// NewProductionConfig builds a reasonable default production logging\n// configuration.\n// Logging is enabled at InfoLevel and above, and uses a JSON encoder.\n// Logs are written to standard error.\n// Stacktraces are included on logs of ErrorLevel and above.\n// DPanicLevel logs will not panic, but will write a stacktrace.\n//\n// Sampling is enabled at 100:100 by default,\n// meaning that after the first 100 log entries\n// with the same level and message in the same second,\n// it will log every 100th entry\n// with the same level and message in the same second.\n// You may disable this behavior by setting Sampling to nil.\n//\n// See [NewProductionEncoderConfig] for information\n// on the default encoder configuration.\nfunc NewProductionConfig() Config {\n\treturn Config{\n\t\tLevel:       NewAtomicLevelAt(InfoLevel),\n\t\tDevelopment: false,\n\t\tSampling: &SamplingConfig{\n\t\t\tInitial:    100,\n\t\t\tThereafter: 100,\n\t\t},\n\t\tEncoding:         \"json\",\n\t\tEncoderConfig:    NewProductionEncoderConfig(),\n\t\tOutputPaths:      []string{\"stderr\"},\n\t\tErrorOutputPaths: []string{\"stderr\"},\n\t}\n}\n\n// NewDevelopmentEncoderConfig returns an opinionated EncoderConfig for\n// development environments.\n//\n// Messages encoded with this configuration will use Zap's console encoder\n// intended to print human-readable output.\n// It will print log messages with the following information:\n//\n//   - The log level (e.g. \"INFO\", \"ERROR\").\n//   - The time in ISO8601 format (e.g. \"2017-01-01T12:00:00Z\").\n//   - The message passed to the log statement.\n//   - If available, a short path to the file and line number\n//     where the log statement was issued.\n//     The logger configuration determines whether this field is captured.\n//   - If available, a stacktrace from the line\n//     where the log statement was issued.\n//     The logger configuration determines whether this field is captured.\n//\n// By default, the following formats are used for different types:\n//\n//   - Time is formatted in ISO8601 format (e.g. \"2017-01-01T12:00:00Z\").\n//   - Duration is formatted as a string (e.g. \"1.234s\").\n//\n// You may change these by setting the appropriate fields in the returned\n// object.\n// For example, use the following to change the time encoding format:\n//\n//\tcfg := zap.NewDevelopmentEncoderConfig()\n//\tcfg.EncodeTime = zapcore.ISO8601TimeEncoder\nfunc NewDevelopmentEncoderConfig() zapcore.EncoderConfig {\n\treturn zapcore.EncoderConfig{\n\t\t// Keys can be anything except the empty string.\n\t\tTimeKey:        \"T\",\n\t\tLevelKey:       \"L\",\n\t\tNameKey:        \"N\",\n\t\tCallerKey:      \"C\",\n\t\tFunctionKey:    zapcore.OmitKey,\n\t\tMessageKey:     \"M\",\n\t\tStacktraceKey:  \"S\",\n\t\tLineEnding:     zapcore.DefaultLineEnding,\n\t\tEncodeLevel:    zapcore.CapitalLevelEncoder,\n\t\tEncodeTime:     zapcore.ISO8601TimeEncoder,\n\t\tEncodeDuration: zapcore.StringDurationEncoder,\n\t\tEncodeCaller:   zapcore.ShortCallerEncoder,\n\t}\n}\n\n// NewDevelopmentConfig builds a reasonable default development logging\n// configuration.\n// Logging is enabled at DebugLevel and above, and uses a console encoder.\n// Logs are written to standard error.\n// Stacktraces are included on logs of WarnLevel and above.\n// DPanicLevel logs will panic.\n//\n// See [NewDevelopmentEncoderConfig] for information\n// on the default encoder configuration.\nfunc NewDevelopmentConfig() Config {\n\treturn Config{\n\t\tLevel:            NewAtomicLevelAt(DebugLevel),\n\t\tDevelopment:      true,\n\t\tEncoding:         \"console\",\n\t\tEncoderConfig:    NewDevelopmentEncoderConfig(),\n\t\tOutputPaths:      []string{\"stderr\"},\n\t\tErrorOutputPaths: []string{\"stderr\"},\n\t}\n}\n\n// Build constructs a logger from the Config and Options.\nfunc (cfg Config) Build(opts ...Option) (*Logger, error) {\n\tenc, err := cfg.buildEncoder()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsink, errSink, err := cfg.openSinks()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif cfg.Level == (AtomicLevel{}) {\n\t\treturn nil, errors.New(\"missing Level\")\n\t}\n\n\tlog := New(\n\t\tzapcore.NewCore(enc, sink, cfg.Level),\n\t\tcfg.buildOptions(errSink)...,\n\t)\n\tif len(opts) > 0 {\n\t\tlog = log.WithOptions(opts...)\n\t}\n\treturn log, nil\n}\n\nfunc (cfg Config) buildOptions(errSink zapcore.WriteSyncer) []Option {\n\topts := []Option{ErrorOutput(errSink)}\n\n\tif cfg.Development {\n\t\topts = append(opts, Development())\n\t}\n\n\tif !cfg.DisableCaller {\n\t\topts = append(opts, AddCaller())\n\t}\n\n\tstackLevel := ErrorLevel\n\tif cfg.Development {\n\t\tstackLevel = WarnLevel\n\t}\n\tif !cfg.DisableStacktrace {\n\t\topts = append(opts, AddStacktrace(stackLevel))\n\t}\n\n\tif scfg := cfg.Sampling; scfg != nil {\n\t\topts = append(opts, WrapCore(func(core zapcore.Core) zapcore.Core {\n\t\t\tvar samplerOpts []zapcore.SamplerOption\n\t\t\tif scfg.Hook != nil {\n\t\t\t\tsamplerOpts = append(samplerOpts, zapcore.SamplerHook(scfg.Hook))\n\t\t\t}\n\t\t\treturn zapcore.NewSamplerWithOptions(\n\t\t\t\tcore,\n\t\t\t\ttime.Second,\n\t\t\t\tcfg.Sampling.Initial,\n\t\t\t\tcfg.Sampling.Thereafter,\n\t\t\t\tsamplerOpts...,\n\t\t\t)\n\t\t}))\n\t}\n\n\tif len(cfg.InitialFields) > 0 {\n\t\tfs := make([]Field, 0, len(cfg.InitialFields))\n\t\tkeys := make([]string, 0, len(cfg.InitialFields))\n\t\tfor k := range cfg.InitialFields {\n\t\t\tkeys = append(keys, k)\n\t\t}\n\t\tsort.Strings(keys)\n\t\tfor _, k := range keys {\n\t\t\tfs = append(fs, Any(k, cfg.InitialFields[k]))\n\t\t}\n\t\topts = append(opts, Fields(fs...))\n\t}\n\n\treturn opts\n}\n\nfunc (cfg Config) openSinks() (zapcore.WriteSyncer, zapcore.WriteSyncer, error) {\n\tsink, closeOut, err := Open(cfg.OutputPaths...)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\terrSink, _, err := Open(cfg.ErrorOutputPaths...)\n\tif err != nil {\n\t\tcloseOut()\n\t\treturn nil, nil, err\n\t}\n\treturn sink, errSink, nil\n}\n\nfunc (cfg Config) buildEncoder() (zapcore.Encoder, error) {\n\treturn newEncoder(cfg.Encoding, cfg.EncoderConfig)\n}\n"
  },
  {
    "path": "config_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"sync/atomic\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\nfunc TestConfig(t *testing.T) {\n\ttests := []struct {\n\t\tdesc     string\n\t\tcfg      Config\n\t\texpectN  int64\n\t\texpectRe string\n\t}{\n\t\t{\n\t\t\tdesc:    \"production\",\n\t\t\tcfg:     NewProductionConfig(),\n\t\t\texpectN: 2 + 100 + 1, // 2 from initial logs, 100 initial sampled logs, 1 from off-by-one in sampler\n\t\t\texpectRe: `{\"level\":\"info\",\"caller\":\"[a-z0-9_-]+/config_test.go:\\d+\",\"msg\":\"info\",\"k\":\"v\",\"z\":\"zz\"}` + \"\\n\" +\n\t\t\t\t`{\"level\":\"warn\",\"caller\":\"[a-z0-9_-]+/config_test.go:\\d+\",\"msg\":\"warn\",\"k\":\"v\",\"z\":\"zz\"}` + \"\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc:    \"development\",\n\t\t\tcfg:     NewDevelopmentConfig(),\n\t\t\texpectN: 3 + 200, // 3 initial logs, all 200 subsequent logs\n\t\t\texpectRe: \"DEBUG\\t[a-z0-9_-]+/config_test.go:\" + `\\d+` + \"\\tdebug\\t\" + `{\"k\": \"v\", \"z\": \"zz\"}` + \"\\n\" +\n\t\t\t\t\"INFO\\t[a-z0-9_-]+/config_test.go:\" + `\\d+` + \"\\tinfo\\t\" + `{\"k\": \"v\", \"z\": \"zz\"}` + \"\\n\" +\n\t\t\t\t\"WARN\\t[a-z0-9_-]+/config_test.go:\" + `\\d+` + \"\\twarn\\t\" + `{\"k\": \"v\", \"z\": \"zz\"}` + \"\\n\" +\n\t\t\t\t`go.uber.org/zap.TestConfig.\\w+`,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tlogOut := filepath.Join(t.TempDir(), \"test.log\")\n\n\t\t\ttt.cfg.OutputPaths = []string{logOut}\n\t\t\ttt.cfg.EncoderConfig.TimeKey = \"\" // no timestamps in tests\n\t\t\ttt.cfg.InitialFields = map[string]interface{}{\"z\": \"zz\", \"k\": \"v\"}\n\n\t\t\thook, count := makeCountingHook()\n\t\t\tlogger, err := tt.cfg.Build(Hooks(hook))\n\t\t\trequire.NoError(t, err, \"Unexpected error constructing logger.\")\n\n\t\t\tlogger.Debug(\"debug\")\n\t\t\tlogger.Info(\"info\")\n\t\t\tlogger.Warn(\"warn\")\n\n\t\t\tbyteContents, err := os.ReadFile(logOut)\n\t\t\trequire.NoError(t, err, \"Couldn't read log contents from temp file.\")\n\t\t\tlogs := string(byteContents)\n\t\t\tassert.Regexp(t, tt.expectRe, logs, \"Unexpected log output.\")\n\n\t\t\tfor i := 0; i < 200; i++ {\n\t\t\t\tlogger.Info(\"sampling\")\n\t\t\t}\n\t\t\tassert.Equal(t, tt.expectN, count.Load(), \"Hook called an unexpected number of times.\")\n\t\t})\n\t}\n}\n\nfunc TestConfigWithInvalidPaths(t *testing.T) {\n\ttests := []struct {\n\t\tdesc      string\n\t\toutput    string\n\t\terrOutput string\n\t}{\n\t\t{\"output directory doesn't exist\", \"/tmp/not-there/foo.log\", \"stderr\"},\n\t\t{\"error output directory doesn't exist\", \"stdout\", \"/tmp/not-there/foo-errors.log\"},\n\t\t{\"neither output directory exists\", \"/tmp/not-there/foo.log\", \"/tmp/not-there/foo-errors.log\"},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tcfg := NewProductionConfig()\n\t\t\tcfg.OutputPaths = []string{tt.output}\n\t\t\tcfg.ErrorOutputPaths = []string{tt.errOutput}\n\t\t\t_, err := cfg.Build()\n\t\t\tassert.Error(t, err, \"Expected an error opening a non-existent directory.\")\n\t\t})\n\t}\n}\n\nfunc TestConfigWithMissingAttributes(t *testing.T) {\n\ttests := []struct {\n\t\tdesc      string\n\t\tcfg       Config\n\t\texpectErr string\n\t}{\n\t\t{\n\t\t\tdesc: \"missing level\",\n\t\t\tcfg: Config{\n\t\t\t\tEncoding: \"json\",\n\t\t\t},\n\t\t\texpectErr: \"missing Level\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"missing encoder time in encoder config\",\n\t\t\tcfg: Config{\n\t\t\t\tLevel:    NewAtomicLevelAt(zapcore.InfoLevel),\n\t\t\t\tEncoding: \"json\",\n\t\t\t\tEncoderConfig: zapcore.EncoderConfig{\n\t\t\t\t\tMessageKey: \"msg\",\n\t\t\t\t\tTimeKey:    \"ts\",\n\t\t\t\t},\n\t\t\t},\n\t\t\texpectErr: \"missing EncodeTime in EncoderConfig\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tcfg := tt.cfg\n\t\t\t_, err := cfg.Build()\n\t\t\tassert.EqualError(t, err, tt.expectErr)\n\t\t})\n\t}\n}\n\nfunc makeSamplerCountingHook() (h func(zapcore.Entry, zapcore.SamplingDecision),\n\tdropped, sampled *atomic.Int64,\n) {\n\tdropped = new(atomic.Int64)\n\tsampled = new(atomic.Int64)\n\th = func(_ zapcore.Entry, dec zapcore.SamplingDecision) {\n\t\tif dec&zapcore.LogDropped > 0 {\n\t\t\tdropped.Add(1)\n\t\t} else if dec&zapcore.LogSampled > 0 {\n\t\t\tsampled.Add(1)\n\t\t}\n\t}\n\treturn h, dropped, sampled\n}\n\nfunc TestConfigWithSamplingHook(t *testing.T) {\n\tshook, dcount, scount := makeSamplerCountingHook()\n\tcfg := Config{\n\t\tLevel:       NewAtomicLevelAt(InfoLevel),\n\t\tDevelopment: false,\n\t\tSampling: &SamplingConfig{\n\t\t\tInitial:    100,\n\t\t\tThereafter: 100,\n\t\t\tHook:       shook,\n\t\t},\n\t\tEncoding:         \"json\",\n\t\tEncoderConfig:    NewProductionEncoderConfig(),\n\t\tOutputPaths:      []string{\"stderr\"},\n\t\tErrorOutputPaths: []string{\"stderr\"},\n\t}\n\texpectRe := `{\"level\":\"info\",\"caller\":\"[a-z0-9_-]+/config_test.go:\\d+\",\"msg\":\"info\",\"k\":\"v\",\"z\":\"zz\"}` + \"\\n\" +\n\t\t`{\"level\":\"warn\",\"caller\":\"[a-z0-9_-]+/config_test.go:\\d+\",\"msg\":\"warn\",\"k\":\"v\",\"z\":\"zz\"}` + \"\\n\"\n\texpectDropped := 99  // 200 - 100 initial - 1 thereafter\n\texpectSampled := 103 // 2 from initial + 100 + 1 thereafter\n\n\tlogOut := filepath.Join(t.TempDir(), \"test.log\")\n\tcfg.OutputPaths = []string{logOut}\n\tcfg.EncoderConfig.TimeKey = \"\" // no timestamps in tests\n\tcfg.InitialFields = map[string]interface{}{\"z\": \"zz\", \"k\": \"v\"}\n\n\tlogger, err := cfg.Build()\n\trequire.NoError(t, err, \"Unexpected error constructing logger.\")\n\n\tlogger.Debug(\"debug\")\n\tlogger.Info(\"info\")\n\tlogger.Warn(\"warn\")\n\n\tbyteContents, err := os.ReadFile(logOut)\n\trequire.NoError(t, err, \"Couldn't read log contents from temp file.\")\n\tlogs := string(byteContents)\n\tassert.Regexp(t, expectRe, logs, \"Unexpected log output.\")\n\n\tfor i := 0; i < 200; i++ {\n\t\tlogger.Info(\"sampling\")\n\t}\n\tassert.Equal(t, int64(expectDropped), dcount.Load())\n\tassert.Equal(t, int64(expectSampled), scount.Load())\n}\n"
  },
  {
    "path": "doc.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// Package zap provides fast, structured, leveled logging.\n//\n// For applications that log in the hot path, reflection-based serialization\n// and string formatting are prohibitively expensive - they're CPU-intensive\n// and make many small allocations. Put differently, using json.Marshal and\n// fmt.Fprintf to log tons of interface{} makes your application slow.\n//\n// Zap takes a different approach. It includes a reflection-free,\n// zero-allocation JSON encoder, and the base Logger strives to avoid\n// serialization overhead and allocations wherever possible. By building the\n// high-level SugaredLogger on that foundation, zap lets users choose when\n// they need to count every allocation and when they'd prefer a more familiar,\n// loosely typed API.\n//\n// # Choosing a Logger\n//\n// In contexts where performance is nice, but not critical, use the\n// SugaredLogger. It's 4-10x faster than other structured logging packages and\n// supports both structured and printf-style logging. Like log15 and go-kit,\n// the SugaredLogger's structured logging APIs are loosely typed and accept a\n// variadic number of key-value pairs. (For more advanced use cases, they also\n// accept strongly typed fields - see the SugaredLogger.With documentation for\n// details.)\n//\n//\tsugar := zap.NewExample().Sugar()\n//\tdefer sugar.Sync()\n//\tsugar.Infow(\"failed to fetch URL\",\n//\t  \"url\", \"http://example.com\",\n//\t  \"attempt\", 3,\n//\t  \"backoff\", time.Second,\n//\t)\n//\tsugar.Infof(\"failed to fetch URL: %s\", \"http://example.com\")\n//\n// By default, loggers are unbuffered. However, since zap's low-level APIs\n// allow buffering, calling Sync before letting your process exit is a good\n// habit.\n//\n// In the rare contexts where every microsecond and every allocation matter,\n// use the Logger. It's even faster than the SugaredLogger and allocates far\n// less, but it only supports strongly-typed, structured logging.\n//\n//\tlogger := zap.NewExample()\n//\tdefer logger.Sync()\n//\tlogger.Info(\"failed to fetch URL\",\n//\t  zap.String(\"url\", \"http://example.com\"),\n//\t  zap.Int(\"attempt\", 3),\n//\t  zap.Duration(\"backoff\", time.Second),\n//\t)\n//\n// Choosing between the Logger and SugaredLogger doesn't need to be an\n// application-wide decision: converting between the two is simple and\n// inexpensive.\n//\n//\tlogger := zap.NewExample()\n//\tdefer logger.Sync()\n//\tsugar := logger.Sugar()\n//\tplain := sugar.Desugar()\n//\n// # Configuring Zap\n//\n// The simplest way to build a Logger is to use zap's opinionated presets:\n// NewExample, NewProduction, and NewDevelopment. These presets build a logger\n// with a single function call:\n//\n//\tlogger, err := zap.NewProduction()\n//\tif err != nil {\n//\t  log.Fatalf(\"can't initialize zap logger: %v\", err)\n//\t}\n//\tdefer logger.Sync()\n//\n// Presets are fine for small projects, but larger projects and organizations\n// naturally require a bit more customization. For most users, zap's Config\n// struct strikes the right balance between flexibility and convenience. See\n// the package-level BasicConfiguration example for sample code.\n//\n// More unusual configurations (splitting output between files, sending logs\n// to a message queue, etc.) are possible, but require direct use of\n// go.uber.org/zap/zapcore. See the package-level AdvancedConfiguration\n// example for sample code.\n//\n// # Extending Zap\n//\n// The zap package itself is a relatively thin wrapper around the interfaces\n// in go.uber.org/zap/zapcore. Extending zap to support a new encoding (e.g.,\n// BSON), a new log sink (e.g., Kafka), or something more exotic (perhaps an\n// exception aggregation service, like Sentry or Rollbar) typically requires\n// implementing the zapcore.Encoder, zapcore.WriteSyncer, or zapcore.Core\n// interfaces. See the zapcore documentation for details.\n//\n// Similarly, package authors can use the high-performance Encoder and Core\n// implementations in the zapcore package to build their own loggers.\n//\n// # Frequently Asked Questions\n//\n// An FAQ covering everything from installation errors to design decisions is\n// available at https://github.com/uber-go/zap/blob/master/FAQ.md.\npackage zap // import \"go.uber.org/zap\"\n"
  },
  {
    "path": "encoder.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"sync\"\n\n\t\"go.uber.org/zap/zapcore\"\n)\n\nvar (\n\terrNoEncoderNameSpecified = errors.New(\"no encoder name specified\")\n\n\t_encoderNameToConstructor = map[string]func(zapcore.EncoderConfig) (zapcore.Encoder, error){\n\t\t\"console\": func(encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) {\n\t\t\treturn zapcore.NewConsoleEncoder(encoderConfig), nil\n\t\t},\n\t\t\"json\": func(encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) {\n\t\t\treturn zapcore.NewJSONEncoder(encoderConfig), nil\n\t\t},\n\t}\n\t_encoderMutex sync.RWMutex\n)\n\n// RegisterEncoder registers an encoder constructor, which the Config struct\n// can then reference. By default, the \"json\" and \"console\" encoders are\n// registered.\n//\n// Attempting to register an encoder whose name is already taken returns an\n// error.\nfunc RegisterEncoder(name string, constructor func(zapcore.EncoderConfig) (zapcore.Encoder, error)) error {\n\t_encoderMutex.Lock()\n\tdefer _encoderMutex.Unlock()\n\tif name == \"\" {\n\t\treturn errNoEncoderNameSpecified\n\t}\n\tif _, ok := _encoderNameToConstructor[name]; ok {\n\t\treturn fmt.Errorf(\"encoder already registered for name %q\", name)\n\t}\n\t_encoderNameToConstructor[name] = constructor\n\treturn nil\n}\n\nfunc newEncoder(name string, encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) {\n\tif encoderConfig.TimeKey != \"\" && encoderConfig.EncodeTime == nil {\n\t\treturn nil, errors.New(\"missing EncodeTime in EncoderConfig\")\n\t}\n\n\t_encoderMutex.RLock()\n\tdefer _encoderMutex.RUnlock()\n\tif name == \"\" {\n\t\treturn nil, errNoEncoderNameSpecified\n\t}\n\tconstructor, ok := _encoderNameToConstructor[name]\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"no encoder registered for name %q\", name)\n\t}\n\treturn constructor(encoderConfig)\n}\n"
  },
  {
    "path": "encoder_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"testing\"\n\n\t\"go.uber.org/zap/zapcore\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestRegisterDefaultEncoders(t *testing.T) {\n\ttestEncodersRegistered(t, \"console\", \"json\")\n}\n\nfunc TestRegisterEncoder(t *testing.T) {\n\ttestEncoders(func() {\n\t\tassert.NoError(t, RegisterEncoder(\"foo\", newNilEncoder), \"expected to be able to register the encoder foo\")\n\t\ttestEncodersRegistered(t, \"foo\")\n\t})\n}\n\nfunc TestDuplicateRegisterEncoder(t *testing.T) {\n\ttestEncoders(func() {\n\t\tassert.NoError(t, RegisterEncoder(\"foo\", newNilEncoder), \"expected to be able to register the encoder foo\")\n\t\tassert.Error(t, RegisterEncoder(\"foo\", newNilEncoder), \"expected an error when registering an encoder with the same name twice\")\n\t})\n}\n\nfunc TestRegisterEncoderNoName(t *testing.T) {\n\tassert.Equal(t, errNoEncoderNameSpecified, RegisterEncoder(\"\", newNilEncoder), \"expected an error when registering an encoder with no name\")\n}\n\nfunc TestNewEncoder(t *testing.T) {\n\ttestEncoders(func() {\n\t\tassert.NoError(t, RegisterEncoder(\"foo\", newNilEncoder), \"expected to be able to register the encoder foo\")\n\t\tencoder, err := newEncoder(\"foo\", zapcore.EncoderConfig{})\n\t\tassert.NoError(t, err, \"could not create an encoder for the registered name foo\")\n\t\tassert.Nil(t, encoder, \"the encoder from newNilEncoder is not nil\")\n\t})\n}\n\nfunc TestNewEncoderNotRegistered(t *testing.T) {\n\t_, err := newEncoder(\"foo\", zapcore.EncoderConfig{})\n\tassert.Error(t, err, \"expected an error when trying to create an encoder of an unregistered name\")\n}\n\nfunc TestNewEncoderNoName(t *testing.T) {\n\t_, err := newEncoder(\"\", zapcore.EncoderConfig{})\n\tassert.Equal(t, errNoEncoderNameSpecified, err, \"expected an error when creating an encoder with no name\")\n}\n\nfunc testEncoders(f func()) {\n\texisting := _encoderNameToConstructor\n\t_encoderNameToConstructor = make(map[string]func(zapcore.EncoderConfig) (zapcore.Encoder, error))\n\tdefer func() { _encoderNameToConstructor = existing }()\n\tf()\n}\n\nfunc testEncodersRegistered(t *testing.T, names ...string) {\n\tassert.Len(t, _encoderNameToConstructor, len(names), \"the expected number of registered encoders does not match the actual number\")\n\tfor _, name := range names {\n\t\tassert.NotNil(t, _encoderNameToConstructor[name], \"no encoder is registered for name %s\", name)\n\t}\n}\n\nfunc newNilEncoder(_ zapcore.EncoderConfig) (zapcore.Encoder, error) {\n\treturn nil, nil\n}\n"
  },
  {
    "path": "error.go",
    "content": "// Copyright (c) 2017 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"go.uber.org/zap/internal/pool\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\nvar _errArrayElemPool = pool.New(func() *errArrayElem {\n\treturn &errArrayElem{}\n})\n\n// Error is shorthand for the common idiom NamedError(\"error\", err).\nfunc Error(err error) Field {\n\treturn NamedError(\"error\", err)\n}\n\n// NamedError constructs a field that lazily stores err.Error() under the\n// provided key. Errors which also implement fmt.Formatter (like those produced\n// by github.com/pkg/errors) will also have their verbose representation stored\n// under key+\"Verbose\". If passed a nil error, the field is a no-op.\n//\n// For the common case in which the key is simply \"error\", the Error function\n// is shorter and less repetitive.\nfunc NamedError(key string, err error) Field {\n\tif err == nil {\n\t\treturn Skip()\n\t}\n\treturn Field{Key: key, Type: zapcore.ErrorType, Interface: err}\n}\n\ntype errArray []error\n\nfunc (errs errArray) MarshalLogArray(arr zapcore.ArrayEncoder) error {\n\tfor i := range errs {\n\t\tif errs[i] == nil {\n\t\t\tcontinue\n\t\t}\n\t\t// To represent each error as an object with an \"error\" attribute and\n\t\t// potentially an \"errorVerbose\" attribute, we need to wrap it in a\n\t\t// type that implements LogObjectMarshaler. To prevent this from\n\t\t// allocating, pool the wrapper type.\n\t\telem := _errArrayElemPool.Get()\n\t\telem.error = errs[i]\n\t\terr := arr.AppendObject(elem)\n\t\telem.error = nil\n\t\t_errArrayElemPool.Put(elem)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\ntype errArrayElem struct {\n\terror\n}\n\nfunc (e *errArrayElem) MarshalLogObject(enc zapcore.ObjectEncoder) error {\n\t// Re-use the error field's logic, which supports non-standard error types.\n\tError(e.error).AddTo(enc)\n\treturn nil\n}\n"
  },
  {
    "path": "error_test.go",
    "content": "// Copyright (c) 2017 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"go.uber.org/zap/zapcore\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestErrorConstructors(t *testing.T) {\n\tfail := errors.New(\"fail\")\n\n\ttests := []struct {\n\t\tname   string\n\t\tfield  Field\n\t\texpect Field\n\t}{\n\t\t{\"Error\", Skip(), Error(nil)},\n\t\t{\"Error\", Field{Key: \"error\", Type: zapcore.ErrorType, Interface: fail}, Error(fail)},\n\t\t{\"NamedError\", Skip(), NamedError(\"foo\", nil)},\n\t\t{\"NamedError\", Field{Key: \"foo\", Type: zapcore.ErrorType, Interface: fail}, NamedError(\"foo\", fail)},\n\t\t{\"Any:Error\", Any(\"k\", errors.New(\"v\")), NamedError(\"k\", errors.New(\"v\"))},\n\t\t{\"Any:Errors\", Any(\"k\", []error{errors.New(\"v\")}), Errors(\"k\", []error{errors.New(\"v\")})},\n\t}\n\n\tfor _, tt := range tests {\n\t\tif !assert.Equal(t, tt.expect, tt.field, \"Unexpected output from convenience field constructor %s.\", tt.name) {\n\t\t\tt.Logf(\"type expected: %T\\nGot: %T\", tt.expect.Interface, tt.field.Interface)\n\t\t}\n\t\tassertCanBeReused(t, tt.field)\n\t}\n}\n\nfunc TestErrorArrayConstructor(t *testing.T) {\n\ttests := []struct {\n\t\tdesc     string\n\t\tfield    Field\n\t\texpected []interface{}\n\t}{\n\t\t{\"empty errors\", Errors(\"\", []error{}), []interface{}{}},\n\t\t{\n\t\t\t\"errors\",\n\t\t\tErrors(\"\", []error{nil, errors.New(\"foo\"), nil, errors.New(\"bar\")}),\n\t\t\t[]interface{}{map[string]interface{}{\"error\": \"foo\"}, map[string]interface{}{\"error\": \"bar\"}},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tenc := zapcore.NewMapObjectEncoder()\n\t\ttt.field.Key = \"k\"\n\t\ttt.field.AddTo(enc)\n\t\tassert.Equal(t, tt.expected, enc.Fields[\"k\"], \"%s: unexpected map contents.\", tt.desc)\n\t\tassert.Equal(t, 1, len(enc.Fields), \"%s: found extra keys in map: %v\", tt.desc, enc.Fields)\n\t}\n}\n\nfunc TestErrorsArraysHandleRichErrors(t *testing.T) {\n\terrs := []error{fmt.Errorf(\"egad\")}\n\n\tenc := zapcore.NewMapObjectEncoder()\n\tErrors(\"k\", errs).AddTo(enc)\n\tassert.Equal(t, 1, len(enc.Fields), \"Expected only top-level field.\")\n\n\tval := enc.Fields[\"k\"]\n\tarr, ok := val.([]interface{})\n\trequire.True(t, ok, \"Expected top-level field to be an array.\")\n\trequire.Equal(t, 1, len(arr), \"Expected only one error object in array.\")\n\n\tserialized := arr[0]\n\terrMap, ok := serialized.(map[string]interface{})\n\trequire.True(t, ok, \"Expected serialized error to be a map, got %T.\", serialized)\n\tassert.Equal(t, \"egad\", errMap[\"error\"], \"Unexpected standard error string.\")\n}\n\nfunc TestErrArrayBrokenEncoder(t *testing.T) {\n\tt.Parallel()\n\n\tfailWith := errors.New(\"great sadness\")\n\terr := (brokenArrayObjectEncoder{\n\t\tErr:           failWith,\n\t\tObjectEncoder: zapcore.NewMapObjectEncoder(),\n\t}).AddArray(\"errors\", errArray{\n\t\terrors.New(\"foo\"),\n\t\terrors.New(\"bar\"),\n\t})\n\trequire.Error(t, err, \"Expected error from broken encoder.\")\n\tassert.ErrorIs(t, err, failWith, \"Unexpected error.\")\n}\n\n// brokenArrayObjectEncoder is an ObjectEncoder\n// that builds a broken ArrayEncoder.\ntype brokenArrayObjectEncoder struct {\n\tzapcore.ObjectEncoder\n\tzapcore.ArrayEncoder\n\n\tErr error // error to return\n}\n\nfunc (enc brokenArrayObjectEncoder) AddArray(key string, marshaler zapcore.ArrayMarshaler) error {\n\treturn enc.ObjectEncoder.AddArray(key,\n\t\tzapcore.ArrayMarshalerFunc(func(ae zapcore.ArrayEncoder) error {\n\t\t\tenc.ArrayEncoder = ae\n\t\t\treturn marshaler.MarshalLogArray(enc)\n\t\t}))\n}\n\nfunc (enc brokenArrayObjectEncoder) AppendObject(zapcore.ObjectMarshaler) error {\n\treturn enc.Err\n}\n"
  },
  {
    "path": "example_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap_test\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"time\"\n\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\nfunc Example_presets() {\n\t// Using zap's preset constructors is the simplest way to get a feel for the\n\t// package, but they don't allow much customization.\n\tlogger := zap.NewExample() // or NewProduction, or NewDevelopment\n\tdefer logger.Sync()\n\n\tconst url = \"http://example.com\"\n\n\t// In most circumstances, use the SugaredLogger. It's 4-10x faster than most\n\t// other structured logging packages and has a familiar, loosely-typed API.\n\tsugar := logger.Sugar()\n\tsugar.Infow(\"Failed to fetch URL.\",\n\t\t// Structured context as loosely typed key-value pairs.\n\t\t\"url\", url,\n\t\t\"attempt\", 3,\n\t\t\"backoff\", time.Second,\n\t)\n\tsugar.Infof(\"Failed to fetch URL: %s\", url)\n\n\t// In the unusual situations where every microsecond matters, use the\n\t// Logger. It's even faster than the SugaredLogger, but only supports\n\t// structured logging.\n\tlogger.Info(\"Failed to fetch URL.\",\n\t\t// Structured context as strongly typed fields.\n\t\tzap.String(\"url\", url),\n\t\tzap.Int(\"attempt\", 3),\n\t\tzap.Duration(\"backoff\", time.Second),\n\t)\n\t// Output:\n\t// {\"level\":\"info\",\"msg\":\"Failed to fetch URL.\",\"url\":\"http://example.com\",\"attempt\":3,\"backoff\":\"1s\"}\n\t// {\"level\":\"info\",\"msg\":\"Failed to fetch URL: http://example.com\"}\n\t// {\"level\":\"info\",\"msg\":\"Failed to fetch URL.\",\"url\":\"http://example.com\",\"attempt\":3,\"backoff\":\"1s\"}\n}\n\nfunc Example_basicConfiguration() {\n\t// For some users, the presets offered by the NewProduction, NewDevelopment,\n\t// and NewExample constructors won't be appropriate. For most of those\n\t// users, the bundled Config struct offers the right balance of flexibility\n\t// and convenience. (For more complex needs, see the AdvancedConfiguration\n\t// example.)\n\t//\n\t// See the documentation for Config and zapcore.EncoderConfig for all the\n\t// available options.\n\trawJSON := []byte(`{\n\t  \"level\": \"debug\",\n\t  \"encoding\": \"json\",\n\t  \"outputPaths\": [\"stdout\", \"/tmp/logs\"],\n\t  \"errorOutputPaths\": [\"stderr\"],\n\t  \"initialFields\": {\"foo\": \"bar\"},\n\t  \"encoderConfig\": {\n\t    \"messageKey\": \"message\",\n\t    \"levelKey\": \"level\",\n\t    \"levelEncoder\": \"lowercase\"\n\t  }\n\t}`)\n\n\tvar cfg zap.Config\n\tif err := json.Unmarshal(rawJSON, &cfg); err != nil {\n\t\tpanic(err)\n\t}\n\tlogger := zap.Must(cfg.Build())\n\tdefer logger.Sync()\n\n\tlogger.Info(\"logger construction succeeded\")\n\t// Output:\n\t// {\"level\":\"info\",\"message\":\"logger construction succeeded\",\"foo\":\"bar\"}\n}\n\nfunc Example_advancedConfiguration() {\n\t// The bundled Config struct only supports the most common configuration\n\t// options. More complex needs, like splitting logs between multiple files\n\t// or writing to non-file outputs, require use of the zapcore package.\n\t//\n\t// In this example, imagine we're both sending our logs to Kafka and writing\n\t// them to the console. We'd like to encode the console output and the Kafka\n\t// topics differently, and we'd also like special treatment for\n\t// high-priority logs.\n\n\t// First, define our level-handling logic.\n\thighPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {\n\t\treturn lvl >= zapcore.ErrorLevel\n\t})\n\tlowPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {\n\t\treturn lvl < zapcore.ErrorLevel\n\t})\n\n\t// Assume that we have clients for two Kafka topics. The clients implement\n\t// zapcore.WriteSyncer and are safe for concurrent use. (If they only\n\t// implement io.Writer, we can use zapcore.AddSync to add a no-op Sync\n\t// method. If they're not safe for concurrent use, we can add a protecting\n\t// mutex with zapcore.Lock.)\n\ttopicDebugging := zapcore.AddSync(io.Discard)\n\ttopicErrors := zapcore.AddSync(io.Discard)\n\n\t// High-priority output should also go to standard error, and low-priority\n\t// output should also go to standard out.\n\tconsoleDebugging := zapcore.Lock(os.Stdout)\n\tconsoleErrors := zapcore.Lock(os.Stderr)\n\n\t// Optimize the Kafka output for machine consumption and the console output\n\t// for human operators.\n\tkafkaEncoder := zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())\n\tconsoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())\n\n\t// Join the outputs, encoders, and level-handling functions into\n\t// zapcore.Cores, then tee the four cores together.\n\tcore := zapcore.NewTee(\n\t\tzapcore.NewCore(kafkaEncoder, topicErrors, highPriority),\n\t\tzapcore.NewCore(consoleEncoder, consoleErrors, highPriority),\n\t\tzapcore.NewCore(kafkaEncoder, topicDebugging, lowPriority),\n\t\tzapcore.NewCore(consoleEncoder, consoleDebugging, lowPriority),\n\t)\n\n\t// From a zapcore.Core, it's easy to construct a Logger.\n\tlogger := zap.New(core)\n\tdefer logger.Sync()\n\tlogger.Info(\"constructed a logger\")\n}\n\nfunc ExampleNamespace() {\n\tlogger := zap.NewExample()\n\tdefer logger.Sync()\n\n\tlogger.With(\n\t\tzap.Namespace(\"metrics\"),\n\t\tzap.Int(\"counter\", 1),\n\t).Info(\"tracked some metrics\")\n\t// Output:\n\t// {\"level\":\"info\",\"msg\":\"tracked some metrics\",\"metrics\":{\"counter\":1}}\n}\n\ntype addr struct {\n\tIP   string\n\tPort int\n}\n\ntype request struct {\n\tURL    string\n\tListen addr\n\tRemote addr\n}\n\nfunc (a addr) MarshalLogObject(enc zapcore.ObjectEncoder) error {\n\tenc.AddString(\"ip\", a.IP)\n\tenc.AddInt(\"port\", a.Port)\n\treturn nil\n}\n\nfunc (r *request) MarshalLogObject(enc zapcore.ObjectEncoder) error {\n\tenc.AddString(\"url\", r.URL)\n\tzap.Inline(r.Listen).AddTo(enc)\n\treturn enc.AddObject(\"remote\", r.Remote)\n}\n\nfunc ExampleObject() {\n\tlogger := zap.NewExample()\n\tdefer logger.Sync()\n\n\treq := &request{\n\t\tURL:    \"/test\",\n\t\tListen: addr{\"127.0.0.1\", 8080},\n\t\tRemote: addr{\"127.0.0.1\", 31200},\n\t}\n\tlogger.Info(\"new request, in nested object\", zap.Object(\"req\", req))\n\tlogger.Info(\"new request, inline\", zap.Inline(req))\n\t// Output:\n\t// {\"level\":\"info\",\"msg\":\"new request, in nested object\",\"req\":{\"url\":\"/test\",\"ip\":\"127.0.0.1\",\"port\":8080,\"remote\":{\"ip\":\"127.0.0.1\",\"port\":31200}}}\n\t// {\"level\":\"info\",\"msg\":\"new request, inline\",\"url\":\"/test\",\"ip\":\"127.0.0.1\",\"port\":8080,\"remote\":{\"ip\":\"127.0.0.1\",\"port\":31200}}\n}\n\nfunc ExampleNewStdLog() {\n\tlogger := zap.NewExample()\n\tdefer logger.Sync()\n\n\tstd := zap.NewStdLog(logger)\n\tstd.Print(\"standard logger wrapper\")\n\t// Output:\n\t// {\"level\":\"info\",\"msg\":\"standard logger wrapper\"}\n}\n\nfunc ExampleRedirectStdLog() {\n\tlogger := zap.NewExample()\n\tdefer logger.Sync()\n\n\tundo := zap.RedirectStdLog(logger)\n\tdefer undo()\n\n\tlog.Print(\"redirected standard library\")\n\t// Output:\n\t// {\"level\":\"info\",\"msg\":\"redirected standard library\"}\n}\n\nfunc ExampleReplaceGlobals() {\n\tlogger := zap.NewExample()\n\tdefer logger.Sync()\n\n\tundo := zap.ReplaceGlobals(logger)\n\tdefer undo()\n\n\tzap.L().Info(\"replaced zap's global loggers\")\n\t// Output:\n\t// {\"level\":\"info\",\"msg\":\"replaced zap's global loggers\"}\n}\n\nfunc ExampleAtomicLevel() {\n\tatom := zap.NewAtomicLevel()\n\n\t// To keep the example deterministic, disable timestamps in the output.\n\tencoderCfg := zap.NewProductionEncoderConfig()\n\tencoderCfg.TimeKey = \"\"\n\n\tlogger := zap.New(zapcore.NewCore(\n\t\tzapcore.NewJSONEncoder(encoderCfg),\n\t\tzapcore.Lock(os.Stdout),\n\t\tatom,\n\t))\n\tdefer logger.Sync()\n\n\tlogger.Info(\"info logging enabled\")\n\n\tatom.SetLevel(zap.ErrorLevel)\n\tlogger.Info(\"info logging disabled\")\n\t// Output:\n\t// {\"level\":\"info\",\"msg\":\"info logging enabled\"}\n}\n\nfunc ExampleAtomicLevel_config() {\n\t// The zap.Config struct includes an AtomicLevel. To use it, keep a\n\t// reference to the Config.\n\trawJSON := []byte(`{\n\t\t\"level\": \"info\",\n\t\t\"outputPaths\": [\"stdout\"],\n\t\t\"errorOutputPaths\": [\"stderr\"],\n\t\t\"encoding\": \"json\",\n\t\t\"encoderConfig\": {\n\t\t\t\"messageKey\": \"message\",\n\t\t\t\"levelKey\": \"level\",\n\t\t\t\"levelEncoder\": \"lowercase\"\n\t\t}\n\t}`)\n\tvar cfg zap.Config\n\tif err := json.Unmarshal(rawJSON, &cfg); err != nil {\n\t\tpanic(err)\n\t}\n\tlogger := zap.Must(cfg.Build())\n\tdefer logger.Sync()\n\n\tlogger.Info(\"info logging enabled\")\n\n\tcfg.Level.SetLevel(zap.ErrorLevel)\n\tlogger.Info(\"info logging disabled\")\n\t// Output:\n\t// {\"level\":\"info\",\"message\":\"info logging enabled\"}\n}\n\nfunc ExampleLogger_Check() {\n\tlogger := zap.NewExample()\n\tdefer logger.Sync()\n\n\tif ce := logger.Check(zap.DebugLevel, \"debugging\"); ce != nil {\n\t\t// If debug-level log output isn't enabled or if zap's sampling would have\n\t\t// dropped this log entry, we don't allocate the slice that holds these\n\t\t// fields.\n\t\tce.Write(\n\t\t\tzap.String(\"foo\", \"bar\"),\n\t\t\tzap.String(\"baz\", \"quux\"),\n\t\t)\n\t}\n\n\t// Output:\n\t// {\"level\":\"debug\",\"msg\":\"debugging\",\"foo\":\"bar\",\"baz\":\"quux\"}\n}\n\nfunc ExampleLogger_Named() {\n\tlogger := zap.NewExample()\n\tdefer logger.Sync()\n\n\t// By default, Loggers are unnamed.\n\tlogger.Info(\"no name\")\n\n\t// The first call to Named sets the Logger name.\n\tmain := logger.Named(\"main\")\n\tmain.Info(\"main logger\")\n\n\t// Additional calls to Named create a period-separated path.\n\tmain.Named(\"subpackage\").Info(\"sub-logger\")\n\t// Output:\n\t// {\"level\":\"info\",\"msg\":\"no name\"}\n\t// {\"level\":\"info\",\"logger\":\"main\",\"msg\":\"main logger\"}\n\t// {\"level\":\"info\",\"logger\":\"main.subpackage\",\"msg\":\"sub-logger\"}\n}\n\nfunc ExampleWrapCore_replace() {\n\t// Replacing a Logger's core can alter fundamental behaviors.\n\t// For example, it can convert a Logger to a no-op.\n\tnop := zap.WrapCore(func(zapcore.Core) zapcore.Core {\n\t\treturn zapcore.NewNopCore()\n\t})\n\n\tlogger := zap.NewExample()\n\tdefer logger.Sync()\n\n\tlogger.Info(\"working\")\n\tlogger.WithOptions(nop).Info(\"no-op\")\n\tlogger.Info(\"original logger still works\")\n\t// Output:\n\t// {\"level\":\"info\",\"msg\":\"working\"}\n\t// {\"level\":\"info\",\"msg\":\"original logger still works\"}\n}\n\nfunc ExampleWrapCore_wrap() {\n\t// Wrapping a Logger's core can extend its functionality. As a trivial\n\t// example, it can double-write all logs.\n\tdoubled := zap.WrapCore(func(c zapcore.Core) zapcore.Core {\n\t\treturn zapcore.NewTee(c, c)\n\t})\n\n\tlogger := zap.NewExample()\n\tdefer logger.Sync()\n\n\tlogger.Info(\"single\")\n\tlogger.WithOptions(doubled).Info(\"doubled\")\n\t// Output:\n\t// {\"level\":\"info\",\"msg\":\"single\"}\n\t// {\"level\":\"info\",\"msg\":\"doubled\"}\n\t// {\"level\":\"info\",\"msg\":\"doubled\"}\n}\n\nfunc ExampleDict() {\n\tlogger := zap.NewExample()\n\tdefer logger.Sync()\n\n\tlogger.Info(\"login event\",\n\t\tzap.Dict(\"event\",\n\t\t\tzap.Int(\"id\", 123),\n\t\t\tzap.String(\"name\", \"jane\"),\n\t\t\tzap.String(\"status\", \"pending\")))\n\t// Output:\n\t// {\"level\":\"info\",\"msg\":\"login event\",\"event\":{\"id\":123,\"name\":\"jane\",\"status\":\"pending\"}}\n}\n\nfunc ExampleObjects() {\n\tlogger := zap.NewExample()\n\tdefer logger.Sync()\n\n\t// Use the Objects field constructor when you have a list of objects,\n\t// all of which implement zapcore.ObjectMarshaler.\n\tlogger.Debug(\"opening connections\",\n\t\tzap.Objects(\"addrs\", []addr{\n\t\t\t{IP: \"123.45.67.89\", Port: 4040},\n\t\t\t{IP: \"127.0.0.1\", Port: 4041},\n\t\t\t{IP: \"192.168.0.1\", Port: 4042},\n\t\t}))\n\t// Output:\n\t// {\"level\":\"debug\",\"msg\":\"opening connections\",\"addrs\":[{\"ip\":\"123.45.67.89\",\"port\":4040},{\"ip\":\"127.0.0.1\",\"port\":4041},{\"ip\":\"192.168.0.1\",\"port\":4042}]}\n}\n\nfunc ExampleDictObject() {\n\tlogger := zap.NewExample()\n\tdefer logger.Sync()\n\n\t// Use DictObject to create zapcore.ObjectMarshaler implementations from Field arrays,\n\t// then use the Object and Objects field constructors to turn them back into a Field.\n\n\tlogger.Debug(\"worker received job\",\n\t\tzap.Object(\"w1\",\n\t\t\tzap.DictObject(\n\t\t\t\tzap.Int(\"id\", 402000),\n\t\t\t\tzap.String(\"description\", \"compress image data\"),\n\t\t\t\tzap.Int(\"priority\", 3),\n\t\t\t),\n\t\t))\n\n\td1 := 68 * time.Millisecond\n\td2 := 79 * time.Millisecond\n\td3 := 57 * time.Millisecond\n\n\tlogger.Info(\"worker status checks\",\n\t\tzap.Objects(\"job batch enqueued\",\n\t\t\t[]zapcore.ObjectMarshaler{\n\t\t\t\tzap.DictObject(\n\t\t\t\t\tzap.String(\"worker\", \"w1\"),\n\t\t\t\t\tzap.Int(\"load\", 419),\n\t\t\t\t\tzap.Duration(\"latency\", d1),\n\t\t\t\t),\n\t\t\t\tzap.DictObject(\n\t\t\t\t\tzap.String(\"worker\", \"w2\"),\n\t\t\t\t\tzap.Int(\"load\", 520),\n\t\t\t\t\tzap.Duration(\"latency\", d2),\n\t\t\t\t),\n\t\t\t\tzap.DictObject(\n\t\t\t\t\tzap.String(\"worker\", \"w3\"),\n\t\t\t\t\tzap.Int(\"load\", 310),\n\t\t\t\t\tzap.Duration(\"latency\", d3),\n\t\t\t\t),\n\t\t\t},\n\t\t))\n\t// Output:\n\t// {\"level\":\"debug\",\"msg\":\"worker received job\",\"w1\":{\"id\":402000,\"description\":\"compress image data\",\"priority\":3}}\n\t// {\"level\":\"info\",\"msg\":\"worker status checks\",\"job batch enqueued\":[{\"worker\":\"w1\",\"load\":419,\"latency\":\"68ms\"},{\"worker\":\"w2\",\"load\":520,\"latency\":\"79ms\"},{\"worker\":\"w3\",\"load\":310,\"latency\":\"57ms\"}]}\n}\n\nfunc ExampleObjectValues() {\n\tlogger := zap.NewExample()\n\tdefer logger.Sync()\n\n\t// Use the ObjectValues field constructor when you have a list of\n\t// objects that do not implement zapcore.ObjectMarshaler directly,\n\t// but on their pointer receivers.\n\tlogger.Debug(\"starting tunnels\",\n\t\tzap.ObjectValues(\"addrs\", []request{\n\t\t\t{\n\t\t\t\tURL:    \"/foo\",\n\t\t\t\tListen: addr{\"127.0.0.1\", 8080},\n\t\t\t\tRemote: addr{\"123.45.67.89\", 4040},\n\t\t\t},\n\t\t\t{\n\t\t\t\tURL:    \"/bar\",\n\t\t\t\tListen: addr{\"127.0.0.1\", 8080},\n\t\t\t\tRemote: addr{\"127.0.0.1\", 31200},\n\t\t\t},\n\t\t}))\n\t// Output:\n\t// {\"level\":\"debug\",\"msg\":\"starting tunnels\",\"addrs\":[{\"url\":\"/foo\",\"ip\":\"127.0.0.1\",\"port\":8080,\"remote\":{\"ip\":\"123.45.67.89\",\"port\":4040}},{\"url\":\"/bar\",\"ip\":\"127.0.0.1\",\"port\":8080,\"remote\":{\"ip\":\"127.0.0.1\",\"port\":31200}}]}\n}\n"
  },
  {
    "path": "exp/CHANGELOG.md",
    "content": "# Changelog\nAll notable changes to this project will be documented in this file.\n\nThis project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## 0.3.0 - 22 Oct 2024\n\nBreaking changes:\n* [#1339][]: zapslog: Drop `HandlerOptions` in favor of `HandlerOption`,\n  which uses the functional options pattern.\n* [#1339][]: zapslog: Rename `AddSource` option to `WithCaller`.\n\nEnhancements:\n* [#1339][]: zapslog: Record stack traces for error logs or higher.\n  The new `AddStackAt` option changes this level.\n\nBug fixes:\n* [#1344][], [#1408][]: zapslog: Comply fully with `slog.Handler` contract.\n  This includes ignoring empty `Attr`s, inlining `Group`s with empty names,\n  and omitting groups with no attributes.\n\n[#1344]: https://github.com/uber-go/zap/pull/1344\n[#1339]: https://github.com/uber-go/zap/pull/1339\n[#1408]: https://github.com/uber-go/zap/pull/1408\n\nThanks to @zekth and @arukiidou for their contributions to this release.\n\n## 0.2.0 - 9 Sep 2023\n\nBreaking changes:\n* [#1315][]: zapslog: Drop HandlerOptions.New in favor of just the NewHandler constructor.\n* [#1320][], [#1338][]: zapslog: Drop support for golang.org/x/exp/slog in favor of log/slog released in Go 1.21.\n\n[#1315]: https://github.com/uber-go/zap/pull/1315\n[#1320]: https://github.com/uber-go/zap/pull/1320\n[#1338]: https://github.com/uber-go/zap/pull/1338\n\n## 0.1.0 - 1 Aug 2023\n\nInitial release of go.uber.org/zap/exp.\nThis submodule contains experimental features for Zap.\nFeatures incubated here may be promoted to the root Zap module,\nbut it's not guaranteed.\n"
  },
  {
    "path": "exp/LICENSE",
    "content": "Copyright (c) 2016-2024 Uber Technologies, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "exp/go.mod",
    "content": "module go.uber.org/zap/exp\n\ngo 1.19\n\nrequire (\n\tgithub.com/stretchr/testify v1.8.1\n\tgo.uber.org/zap v1.26.0\n)\n\nrequire (\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgo.uber.org/multierr v1.10.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n\nreplace go.uber.org/zap => ../\n"
  },
  {
    "path": "exp/go.sum",
    "content": "github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngo.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=\ngo.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=\ngo.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=\ngo.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "exp/zapfield/zapfield.go",
    "content": "// Copyright (c) 2016-2023 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// Package zapfield provides experimental zap.Field helpers whose APIs may be unstable.\npackage zapfield\n\nimport (\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\n// Str constructs a field with the given string-like key and value.\nfunc Str[K ~string, V ~string](k K, v V) zap.Field {\n\treturn zap.String(string(k), string(v))\n}\n\ntype stringArray[T ~string] []T\n\nfunc (a stringArray[T]) MarshalLogArray(enc zapcore.ArrayEncoder) error {\n\tfor i := range a {\n\t\tenc.AppendString(string(a[i]))\n\t}\n\treturn nil\n}\n\n// Strs constructs a field that carries a slice of string-like values.\nfunc Strs[K ~string, V ~[]S, S ~string](k K, v V) zap.Field {\n\treturn zap.Array(string(k), stringArray[S](v))\n}\n"
  },
  {
    "path": "exp/zapfield/zapfield_test.go",
    "content": "// Copyright (c) 2016-2023 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapfield\n\nimport (\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\ntype (\n\tMyKey    string\n\tMyValue  string\n\tMyValues []MyValue\n)\n\nfunc TestFieldConstructors(t *testing.T) {\n\tvar (\n\t\tkey    = MyKey(\"test key\")\n\t\tvalue  = MyValue(\"test value\")\n\t\tvalues = []MyValue{\n\t\t\tMyValue(\"test value 1\"),\n\t\t\tMyValue(\"test value 2\"),\n\t\t}\n\t)\n\n\ttests := []struct {\n\t\tname   string\n\t\texpect zap.Field\n\t\tfield  zap.Field\n\t}{\n\t\t{\"Str\", zap.Field{Type: zapcore.StringType, Key: \"test key\", String: \"test value\"}, Str(key, value)},\n\t\t{\"Strs\", zap.Array(\"test key\", stringArray[MyValue]{\"test value 1\", \"test value 2\"}), Strs(key, values)},\n\t}\n\n\tfor _, tt := range tests {\n\t\tif !assert.Equal(t, tt.expect, tt.field, \"Unexpected output from convenience field constructor %s.\", tt.name) {\n\t\t\tt.Logf(\"type expected: %T\\nGot: %T\", tt.expect.Interface, tt.field.Interface)\n\t\t}\n\t\tassertCanBeReused(t, tt.field)\n\t}\n}\n\nfunc assertCanBeReused(t testing.TB, field zap.Field) {\n\tvar wg sync.WaitGroup\n\n\tfor i := 0; i < 100; i++ {\n\t\tenc := zapcore.NewMapObjectEncoder()\n\n\t\t// Ensure using the field in multiple encoders in separate goroutines\n\t\t// does not cause any races or panics.\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tassert.NotPanics(t, func() {\n\t\t\t\tfield.AddTo(enc)\n\t\t\t}, \"Reusing a field should not cause issues\")\n\t\t}()\n\t}\n\n\twg.Wait()\n}\n"
  },
  {
    "path": "exp/zapslog/doc.go",
    "content": "// Copyright (c) 2023 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// Package zapslog provides an implementation of slog.Handler which writes to\n// the supplied zapcore.Core.\n//\n// Use of this package requires at least Go 1.21.\npackage zapslog // import \"go.uber.org/zap/exp/zapslog\"\n"
  },
  {
    "path": "exp/zapslog/example_test.go",
    "content": "// Copyright (c) 2023 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n//go:build go1.21\n\npackage zapslog_test\n\nimport (\n\t\"context\"\n\t\"log/slog\"\n\t\"net\"\n\t\"time\"\n\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/exp/zapslog\"\n)\n\ntype Password string\n\nfunc (p Password) LogValue() slog.Value {\n\treturn slog.StringValue(\"REDACTED\")\n}\n\nfunc Example_slog() {\n\tlogger := zap.NewExample(zap.IncreaseLevel(zap.InfoLevel))\n\tdefer logger.Sync()\n\n\tsl := slog.New(zapslog.NewHandler(logger.Core()))\n\tctx := context.Background()\n\n\tsl.Info(\"user\", \"name\", \"Al\", \"secret\", Password(\"secret\"))\n\tsl.Error(\"oops\", \"err\", net.ErrClosed, \"status\", 500)\n\tsl.LogAttrs(\n\t\tctx,\n\t\tslog.LevelError,\n\t\t\"oops\",\n\t\tslog.Any(\"err\", net.ErrClosed),\n\t\tslog.Int(\"status\", 500),\n\t)\n\tsl.Info(\"message\",\n\t\tslog.Group(\"group\",\n\t\t\tslog.Float64(\"pi\", 3.14),\n\t\t\tslog.Duration(\"1min\", time.Minute),\n\t\t),\n\t)\n\tsl.WithGroup(\"s\").LogAttrs(\n\t\tctx,\n\t\tslog.LevelWarn,\n\t\t\"warn msg\", // message\n\t\tslog.Uint64(\"u\", 1),\n\t\tslog.Any(\"m\", map[string]any{\n\t\t\t\"foo\": \"bar\",\n\t\t}))\n\tsl.LogAttrs(ctx, slog.LevelDebug, \"not show up\")\n\n\t// Output:\n\t// {\"level\":\"info\",\"msg\":\"user\",\"name\":\"Al\",\"secret\":\"REDACTED\"}\n\t// {\"level\":\"error\",\"msg\":\"oops\",\"err\":\"use of closed network connection\",\"status\":500}\n\t// {\"level\":\"error\",\"msg\":\"oops\",\"err\":\"use of closed network connection\",\"status\":500}\n\t// {\"level\":\"info\",\"msg\":\"message\",\"group\":{\"pi\":3.14,\"1min\":\"1m0s\"}}\n\t// {\"level\":\"warn\",\"msg\":\"warn msg\",\"s\":{\"u\":1,\"m\":{\"foo\":\"bar\"}}}\n}\n"
  },
  {
    "path": "exp/zapslog/handler.go",
    "content": "// Copyright (c) 2023 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n//go:build go1.21\n\npackage zapslog\n\nimport (\n\t\"context\"\n\t\"log/slog\"\n\t\"runtime\"\n\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/internal/stacktrace\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\n// Handler implements the slog.Handler by writing to a zap Core.\ntype Handler struct {\n\tcore       zapcore.Core\n\tname       string // logger name\n\taddCaller  bool\n\taddStackAt slog.Level\n\tcallerSkip int\n\n\t// List of unapplied groups.\n\t//\n\t// These are applied only if we encounter a real field\n\t// to avoid creating empty namespaces -- which is disallowed by slog's\n\t// usage contract.\n\tgroups []string\n}\n\n// NewHandler builds a [Handler] that writes to the supplied [zapcore.Core]\n// with options.\nfunc NewHandler(core zapcore.Core, opts ...HandlerOption) *Handler {\n\th := &Handler{\n\t\tcore:       core,\n\t\taddStackAt: slog.LevelError,\n\t}\n\tfor _, v := range opts {\n\t\tv.apply(h)\n\t}\n\treturn h\n}\n\nvar _ slog.Handler = (*Handler)(nil)\n\n// groupObject holds all the Attrs saved in a slog.GroupValue.\ntype groupObject []slog.Attr\n\nfunc (gs groupObject) MarshalLogObject(enc zapcore.ObjectEncoder) error {\n\tfor _, attr := range gs {\n\t\tconvertAttrToField(attr).AddTo(enc)\n\t}\n\treturn nil\n}\n\nfunc convertAttrToField(attr slog.Attr) zapcore.Field {\n\tif attr.Equal(slog.Attr{}) {\n\t\t// Ignore empty attrs.\n\t\treturn zap.Skip()\n\t}\n\n\tswitch attr.Value.Kind() {\n\tcase slog.KindBool:\n\t\treturn zap.Bool(attr.Key, attr.Value.Bool())\n\tcase slog.KindDuration:\n\t\treturn zap.Duration(attr.Key, attr.Value.Duration())\n\tcase slog.KindFloat64:\n\t\treturn zap.Float64(attr.Key, attr.Value.Float64())\n\tcase slog.KindInt64:\n\t\treturn zap.Int64(attr.Key, attr.Value.Int64())\n\tcase slog.KindString:\n\t\treturn zap.String(attr.Key, attr.Value.String())\n\tcase slog.KindTime:\n\t\treturn zap.Time(attr.Key, attr.Value.Time())\n\tcase slog.KindUint64:\n\t\treturn zap.Uint64(attr.Key, attr.Value.Uint64())\n\tcase slog.KindGroup:\n\t\tif attr.Key == \"\" {\n\t\t\t// Inlines recursively.\n\t\t\treturn zap.Inline(groupObject(attr.Value.Group()))\n\t\t}\n\t\treturn zap.Object(attr.Key, groupObject(attr.Value.Group()))\n\tcase slog.KindLogValuer:\n\t\treturn convertAttrToField(slog.Attr{\n\t\t\tKey: attr.Key,\n\t\t\t// TODO: resolve the value in a lazy way.\n\t\t\t// This probably needs a new Zap field type\n\t\t\t// that can be resolved lazily.\n\t\t\tValue: attr.Value.Resolve(),\n\t\t})\n\tdefault:\n\t\treturn zap.Any(attr.Key, attr.Value.Any())\n\t}\n}\n\n// convertSlogLevel maps slog Levels to zap Levels.\n// Note that there is some room between slog levels while zap levels are continuous, so we can't 1:1 map them.\n// See also https://go.googlesource.com/proposal/+/master/design/56345-structured-logging.md?pli=1#levels\nfunc convertSlogLevel(l slog.Level) zapcore.Level {\n\tswitch {\n\tcase l >= slog.LevelError:\n\t\treturn zapcore.ErrorLevel\n\tcase l >= slog.LevelWarn:\n\t\treturn zapcore.WarnLevel\n\tcase l >= slog.LevelInfo:\n\t\treturn zapcore.InfoLevel\n\tdefault:\n\t\treturn zapcore.DebugLevel\n\t}\n}\n\n// Enabled reports whether the handler handles records at the given level.\nfunc (h *Handler) Enabled(ctx context.Context, level slog.Level) bool {\n\treturn h.core.Enabled(convertSlogLevel(level))\n}\n\n// Handle handles the Record.\nfunc (h *Handler) Handle(ctx context.Context, record slog.Record) error {\n\tent := zapcore.Entry{\n\t\tLevel:      convertSlogLevel(record.Level),\n\t\tTime:       record.Time,\n\t\tMessage:    record.Message,\n\t\tLoggerName: h.name,\n\t}\n\tce := h.core.Check(ent, nil)\n\tif ce == nil {\n\t\treturn nil\n\t}\n\n\tif h.addCaller && record.PC != 0 {\n\t\tframe, _ := runtime.CallersFrames([]uintptr{record.PC}).Next()\n\t\tif frame.PC != 0 {\n\t\t\tce.Caller = zapcore.EntryCaller{\n\t\t\t\tDefined:  true,\n\t\t\t\tPC:       frame.PC,\n\t\t\t\tFile:     frame.File,\n\t\t\t\tLine:     frame.Line,\n\t\t\t\tFunction: frame.Function,\n\t\t\t}\n\t\t}\n\t}\n\n\tif record.Level >= h.addStackAt {\n\t\t// Skipping 3:\n\t\t// zapslog/handler log/slog.(*Logger).log\n\t\t// slog/logger log/slog.(*Logger).log\n\t\t// slog/logger log/slog.(*Logger).<level>\n\t\tce.Stack = stacktrace.Take(3 + h.callerSkip)\n\t}\n\n\tfields := make([]zapcore.Field, 0, record.NumAttrs()+len(h.groups))\n\n\tvar addedNamespace bool\n\trecord.Attrs(func(attr slog.Attr) bool {\n\t\tf := convertAttrToField(attr)\n\t\tif !addedNamespace && len(h.groups) > 0 && f != zap.Skip() {\n\t\t\t// Namespaces are added only if at least one field is present\n\t\t\t// to avoid creating empty groups.\n\t\t\tfields = h.appendGroups(fields)\n\t\t\taddedNamespace = true\n\t\t}\n\t\tfields = append(fields, f)\n\t\treturn true\n\t})\n\n\tce.Write(fields...)\n\treturn nil\n}\n\nfunc (h *Handler) appendGroups(fields []zapcore.Field) []zapcore.Field {\n\tfor _, g := range h.groups {\n\t\tfields = append(fields, zap.Namespace(g))\n\t}\n\treturn fields\n}\n\n// WithAttrs returns a new Handler whose attributes consist of\n// both the receiver's attributes and the arguments.\nfunc (h *Handler) WithAttrs(attrs []slog.Attr) slog.Handler {\n\tfields := make([]zapcore.Field, 0, len(attrs)+len(h.groups))\n\tvar addedNamespace bool\n\tfor _, attr := range attrs {\n\t\tf := convertAttrToField(attr)\n\t\tif !addedNamespace && len(h.groups) > 0 && f != zap.Skip() {\n\t\t\t// Namespaces are added only if at least one field is present\n\t\t\t// to avoid creating empty groups.\n\t\t\tfields = h.appendGroups(fields)\n\t\t\taddedNamespace = true\n\t\t}\n\t\tfields = append(fields, f)\n\t}\n\n\tcloned := *h\n\tcloned.core = h.core.With(fields)\n\tif addedNamespace {\n\t\t// These groups have been applied so we can clear them.\n\t\tcloned.groups = nil\n\t}\n\treturn &cloned\n}\n\n// WithGroup returns a new Handler with the given group appended to\n// the receiver's existing groups.\nfunc (h *Handler) WithGroup(group string) slog.Handler {\n\tnewGroups := make([]string, len(h.groups)+1)\n\tcopy(newGroups, h.groups)\n\tnewGroups[len(h.groups)] = group\n\n\tcloned := *h\n\tcloned.groups = newGroups\n\treturn &cloned\n}\n"
  },
  {
    "path": "exp/zapslog/handler_test.go",
    "content": "// Copyright (c) 2023 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n//go:build go1.21\n\npackage zapslog\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"log/slog\"\n\t\"sync\"\n\t\"testing\"\n\t\"testing/slogtest\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/zap/zapcore\"\n\t\"go.uber.org/zap/zaptest\"\n\t\"go.uber.org/zap/zaptest/observer\"\n)\n\nfunc TestAddCaller(t *testing.T) {\n\tt.Parallel()\n\n\tfac, logs := observer.New(zapcore.DebugLevel)\n\tsl := slog.New(NewHandler(fac, WithCaller(true)))\n\tsl.Info(\"msg\")\n\n\trequire.Len(t, logs.AllUntimed(), 1, \"Expected exactly one entry to be logged\")\n\tentry := logs.AllUntimed()[0]\n\tassert.Equal(t, \"msg\", entry.Message, \"Unexpected message\")\n\tassert.Regexp(t,\n\t\t`/handler_test.go:\\d+$`,\n\t\tentry.Caller.String(),\n\t\t\"Unexpected caller annotation.\",\n\t)\n}\n\nfunc TestAddStack(t *testing.T) {\n\tfac, logs := observer.New(zapcore.DebugLevel)\n\tsl := slog.New(NewHandler(fac, AddStacktraceAt(slog.LevelDebug)))\n\tsl.Info(\"msg\")\n\n\trequire.Len(t, logs.AllUntimed(), 1, \"Expected exactly one entry to be logged\")\n\tentry := logs.AllUntimed()[0]\n\trequire.Equal(t, \"msg\", entry.Message, \"Unexpected message\")\n\tassert.Regexp(t,\n\t\t`^go.uber.org/zap/exp/zapslog.TestAddStack`,\n\t\tentry.Stack,\n\t\t\"Unexpected stack trace annotation.\",\n\t)\n\tassert.Regexp(t,\n\t\t`/zapslog/handler_test.go:\\d+`,\n\t\tentry.Stack,\n\t\t\"Unexpected stack trace annotation.\",\n\t)\n}\n\nfunc TestAddStackSkip(t *testing.T) {\n\tfac, logs := observer.New(zapcore.DebugLevel)\n\tsl := slog.New(NewHandler(fac, AddStacktraceAt(slog.LevelDebug), WithCallerSkip(1)))\n\tsl.Info(\"msg\")\n\n\trequire.Len(t, logs.AllUntimed(), 1, \"Expected exactly one entry to be logged\")\n\tentry := logs.AllUntimed()[0]\n\tassert.Regexp(t,\n\t\t`src/testing/testing.go:\\d+`,\n\t\tentry.Stack,\n\t\t\"Unexpected stack trace annotation.\",\n\t)\n}\n\nfunc TestEmptyAttr(t *testing.T) {\n\tt.Parallel()\n\n\tfac, observedLogs := observer.New(zapcore.DebugLevel)\n\tsl := slog.New(NewHandler(fac))\n\n\tt.Run(\"Handle\", func(t *testing.T) {\n\t\tsl.Info(\n\t\t\t\"msg\",\n\t\t\tslog.String(\"foo\", \"bar\"),\n\t\t\tslog.Attr{},\n\t\t)\n\n\t\tlogs := observedLogs.TakeAll()\n\t\trequire.Len(t, logs, 1, \"Expected exactly one entry to be logged\")\n\t\tassert.Equal(t, map[string]any{\n\t\t\t\"foo\": \"bar\",\n\t\t}, logs[0].ContextMap(), \"Unexpected context\")\n\t})\n\n\tt.Run(\"WithAttrs\", func(t *testing.T) {\n\t\tsl.With(slog.String(\"foo\", \"bar\"), slog.Attr{}).Info(\"msg\")\n\n\t\tlogs := observedLogs.TakeAll()\n\t\trequire.Len(t, logs, 1, \"Expected exactly one entry to be logged\")\n\t\tassert.Equal(t, map[string]any{\n\t\t\t\"foo\": \"bar\",\n\t\t}, logs[0].ContextMap(), \"Unexpected context\")\n\t})\n\n\tt.Run(\"Group\", func(t *testing.T) {\n\t\tsl.With(\"k\", slog.GroupValue(slog.String(\"foo\", \"bar\"), slog.Attr{})).Info(\"msg\")\n\n\t\tlogs := observedLogs.TakeAll()\n\t\trequire.Len(t, logs, 1, \"Expected exactly one entry to be logged\")\n\t\tassert.Equal(t, map[string]any{\n\t\t\t\"k\": map[string]any{\n\t\t\t\t\"foo\": \"bar\",\n\t\t\t},\n\t\t}, logs[0].ContextMap(), \"Unexpected context\")\n\t})\n}\n\nfunc TestWithName(t *testing.T) {\n\tfac, observedLogs := observer.New(zapcore.DebugLevel)\n\n\tt.Run(\"default\", func(t *testing.T) {\n\t\tsl := slog.New(NewHandler(fac))\n\t\tsl.Info(\"msg\")\n\n\t\tlogs := observedLogs.TakeAll()\n\t\trequire.Len(t, logs, 1, \"Expected exactly one entry to be logged\")\n\t\tentry := logs[0]\n\t\tassert.Equal(t, \"\", entry.LoggerName, \"Unexpected logger name\")\n\t})\n\tt.Run(\"with name\", func(t *testing.T) {\n\t\tsl := slog.New(NewHandler(fac, WithName(\"test-name\")))\n\t\tsl.Info(\"msg\")\n\n\t\tlogs := observedLogs.TakeAll()\n\t\trequire.Len(t, logs, 1, \"Expected exactly one entry to be logged\")\n\t\tentry := logs[0]\n\t\tassert.Equal(t, \"test-name\", entry.LoggerName, \"Unexpected logger name\")\n\t})\n}\n\nfunc TestInlineGroup(t *testing.T) {\n\tfac, observedLogs := observer.New(zapcore.DebugLevel)\n\n\tt.Run(\"simple\", func(t *testing.T) {\n\t\tsl := slog.New(NewHandler(fac))\n\t\tsl.Info(\"msg\", \"a\", \"b\", slog.Group(\"\", slog.String(\"c\", \"d\")), \"e\", \"f\")\n\n\t\tlogs := observedLogs.TakeAll()\n\t\trequire.Len(t, logs, 1, \"Expected exactly one entry to be logged\")\n\t\tassert.Equal(t, map[string]any{\n\t\t\t\"a\": \"b\",\n\t\t\t\"c\": \"d\",\n\t\t\t\"e\": \"f\",\n\t\t}, logs[0].ContextMap(), \"Unexpected context\")\n\t})\n\n\tt.Run(\"recursive\", func(t *testing.T) {\n\t\tsl := slog.New(NewHandler(fac))\n\t\tsl.Info(\"msg\", \"a\", \"b\", slog.Group(\"\", slog.Group(\"\", slog.Group(\"\", slog.String(\"c\", \"d\"))), slog.Group(\"\", \"e\", \"f\")))\n\n\t\tlogs := observedLogs.TakeAll()\n\t\trequire.Len(t, logs, 1, \"Expected exactly one entry to be logged\")\n\t\tassert.Equal(t, map[string]any{\n\t\t\t\"a\": \"b\",\n\t\t\t\"c\": \"d\",\n\t\t\t\"e\": \"f\",\n\t\t}, logs[0].ContextMap(), \"Unexpected context\")\n\t})\n}\n\nfunc TestWithGroup(t *testing.T) {\n\tfac, observedLogs := observer.New(zapcore.DebugLevel)\n\n\t// Groups can be nested inside each other.\n\tt.Run(\"nested\", func(t *testing.T) {\n\t\tsl := slog.New(NewHandler(fac))\n\t\tsl.With(\"a\", \"b\").WithGroup(\"G\").WithGroup(\"in\").Info(\"msg\", \"c\", \"d\")\n\n\t\tlogs := observedLogs.TakeAll()\n\t\trequire.Len(t, logs, 1, \"Expected exactly one entry to be logged\")\n\t\tassert.Equal(t, map[string]any{\n\t\t\t\"G\": map[string]any{\n\t\t\t\t\"in\": map[string]any{\n\t\t\t\t\t\"c\": \"d\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t\"a\": \"b\",\n\t\t}, logs[0].ContextMap(), \"Unexpected context\")\n\t})\n\n\tt.Run(\"nested empty\", func(t *testing.T) {\n\t\tsl := slog.New(NewHandler(fac))\n\t\tsl.With(\"a\", \"b\").WithGroup(\"G\").WithGroup(\"in\").Info(\"msg\")\n\n\t\tlogs := observedLogs.TakeAll()\n\t\trequire.Len(t, logs, 1, \"Expected exactly one entry to be logged\")\n\t\tassert.Equal(t, map[string]any{\n\t\t\t\"a\": \"b\",\n\t\t}, logs[0].ContextMap(), \"Unexpected context\")\n\t})\n\n\tt.Run(\"empty group\", func(t *testing.T) {\n\t\tsl := slog.New(NewHandler(fac))\n\t\tsl.With(\"a\", \"b\").WithGroup(\"G\").With(\"c\", \"d\").WithGroup(\"H\").Info(\"msg\")\n\n\t\tlogs := observedLogs.TakeAll()\n\t\trequire.Len(t, logs, 1, \"Expected exactly one entry to be logged\")\n\t\tassert.Equal(t, map[string]any{\n\t\t\t\"G\": map[string]any{\n\t\t\t\t\"c\": \"d\",\n\t\t\t},\n\t\t\t\"a\": \"b\",\n\t\t}, logs[0].ContextMap(), \"Unexpected context\")\n\t})\n\n\tt.Run(\"skipped field\", func(t *testing.T) {\n\t\tsl := slog.New(NewHandler(fac))\n\t\tsl.WithGroup(\"H\").With(slog.Attr{}).Info(\"msg\")\n\n\t\tlogs := observedLogs.TakeAll()\n\t\trequire.Len(t, logs, 1, \"Expected exactly one entry to be logged\")\n\t\tassert.Equal(t, map[string]any{}, logs[0].ContextMap(), \"Unexpected context\")\n\t})\n\n\tt.Run(\"reuse\", func(t *testing.T) {\n\t\tsl := slog.New(NewHandler(fac)).WithGroup(\"G\")\n\n\t\tsl.With(\"a\", \"b\").Info(\"msg1\", \"c\", \"d\")\n\t\tsl.With(\"e\", \"f\").Info(\"msg2\", \"g\", \"h\")\n\n\t\tlogs := observedLogs.TakeAll()\n\t\trequire.Len(t, logs, 2, \"Expected exactly two entries to be logged\")\n\n\t\tassert.Equal(t, map[string]any{\n\t\t\t\"G\": map[string]any{\n\t\t\t\t\"a\": \"b\",\n\t\t\t\t\"c\": \"d\",\n\t\t\t},\n\t\t}, logs[0].ContextMap(), \"Unexpected context\")\n\t\tassert.Equal(t, \"msg1\", logs[0].Message, \"Unexpected message\")\n\n\t\tassert.Equal(t, map[string]any{\n\t\t\t\"G\": map[string]any{\n\t\t\t\t\"e\": \"f\",\n\t\t\t\t\"g\": \"h\",\n\t\t\t},\n\t\t}, logs[1].ContextMap(), \"Unexpected context\")\n\t\tassert.Equal(t, \"msg2\", logs[1].Message, \"Unexpected message\")\n\t})\n}\n\n// Run a few different loggers with concurrent logs\n// in an attempt to trip up 'go test -race' and discover any data races.\nfunc TestConcurrentLogs(t *testing.T) {\n\tt.Parallel()\n\n\tconst (\n\t\tNumWorkers = 10\n\t\tNumLogs    = 100\n\t)\n\n\ttests := []struct {\n\t\tname         string\n\t\tbuildHandler func(zapcore.Core) slog.Handler\n\t}{\n\t\t{\n\t\t\tname: \"default\",\n\t\t\tbuildHandler: func(core zapcore.Core) slog.Handler {\n\t\t\t\treturn NewHandler(core)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"grouped\",\n\t\t\tbuildHandler: func(core zapcore.Core) slog.Handler {\n\t\t\t\treturn NewHandler(core).WithGroup(\"G\")\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"named\",\n\t\t\tbuildHandler: func(core zapcore.Core) slog.Handler {\n\t\t\t\treturn NewHandler(core, WithName(\"test-name\"))\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tfac, observedLogs := observer.New(zapcore.DebugLevel)\n\t\t\tsl := slog.New(tt.buildHandler(fac))\n\n\t\t\t// Use two wait groups to coordinate the workers:\n\t\t\t//\n\t\t\t// - ready: indicates when all workers should start logging.\n\t\t\t// - done: indicates when all workers have finished logging.\n\t\t\tvar ready, done sync.WaitGroup\n\t\t\tready.Add(NumWorkers)\n\t\t\tdone.Add(NumWorkers)\n\n\t\t\tfor i := 0; i < NumWorkers; i++ {\n\t\t\t\ti := i\n\t\t\t\tgo func() {\n\t\t\t\t\tdefer done.Done()\n\n\t\t\t\t\tready.Done() // I'm ready.\n\t\t\t\t\tready.Wait() // Are others?\n\n\t\t\t\t\tfor j := 0; j < NumLogs; j++ {\n\t\t\t\t\t\tsl.Info(\"msg\", \"worker\", i, \"log\", j)\n\t\t\t\t\t}\n\t\t\t\t}()\n\t\t\t}\n\n\t\t\tdone.Wait()\n\n\t\t\t// Ensure that all logs were recorded.\n\t\t\tlogs := observedLogs.TakeAll()\n\t\t\tassert.Len(t, logs, NumWorkers*NumLogs,\n\t\t\t\t\"Wrong number of logs recorded\")\n\t\t})\n\t}\n}\n\ntype Token string\n\nfunc (Token) LogValue() slog.Value {\n\treturn slog.StringValue(\"REDACTED_TOKEN\")\n}\n\nfunc TestAttrKinds(t *testing.T) {\n\tfac, logs := observer.New(zapcore.DebugLevel)\n\tsl := slog.New(NewHandler(fac))\n\ttestToken := Token(\"no\")\n\tsl.Info(\n\t\t\"msg\",\n\t\tslog.Bool(\"bool\", true),\n\t\tslog.Duration(\"duration\", time.Hour),\n\t\tslog.Float64(\"float64\", 42.0),\n\t\tslog.Int64(\"int64\", -1234),\n\t\tslog.Time(\"time\", time.Date(2015, 10, 21, 7, 28, 0o0, 0, time.UTC)),\n\t\tslog.Uint64(\"uint64\", 2),\n\t\tslog.Group(\"group\", slog.String(\"inner\", \"inner-group\")),\n\t\t\"logvaluer\", testToken,\n\t\t\"any\", \"what am i?\",\n\t)\n\n\trequire.Len(t, logs.AllUntimed(), 1, \"Expected exactly one entry to be logged\")\n\tentry := logs.AllUntimed()[0]\n\tassert.Equal(t,\n\t\tmap[string]any{\n\t\t\t\"bool\":      true,\n\t\t\t\"duration\":  time.Hour,\n\t\t\t\"float64\":   float64(42),\n\t\t\t\"group\":     map[string]any{\"inner\": \"inner-group\"},\n\t\t\t\"int64\":     int64(-1234),\n\t\t\t\"time\":      time.Date(2015, time.October, 21, 7, 28, 0, 0, time.UTC),\n\t\t\t\"uint64\":    uint64(2),\n\t\t\t\"logvaluer\": \"REDACTED_TOKEN\",\n\t\t\t\"any\":       \"what am i?\",\n\t\t},\n\t\tentry.ContextMap())\n}\n\nfunc TestSlogtest(t *testing.T) {\n\tvar buff bytes.Buffer\n\tcore := zapcore.NewCore(\n\t\tzapcore.NewJSONEncoder(zapcore.EncoderConfig{\n\t\t\tTimeKey:     slog.TimeKey,\n\t\t\tMessageKey:  slog.MessageKey,\n\t\t\tLevelKey:    slog.LevelKey,\n\t\t\tEncodeLevel: zapcore.CapitalLevelEncoder,\n\t\t\tEncodeTime:  zapcore.RFC3339TimeEncoder,\n\t\t}),\n\t\tzapcore.AddSync(&buff),\n\t\tzapcore.DebugLevel,\n\t)\n\n\t// zaptest doesn't expose the underlying core,\n\t// so we'll extract it from the logger.\n\ttestCore := zaptest.NewLogger(t).Core()\n\n\thandler := NewHandler(zapcore.NewTee(core, testCore))\n\terr := slogtest.TestHandler(\n\t\thandler,\n\t\tfunc() []map[string]any {\n\t\t\t// Parse the newline-delimted JSON in buff.\n\t\t\tvar entries []map[string]any\n\n\t\t\tdec := json.NewDecoder(bytes.NewReader(buff.Bytes()))\n\t\t\tfor dec.More() {\n\t\t\t\tvar ent map[string]any\n\t\t\t\trequire.NoError(t, dec.Decode(&ent), \"Error decoding log message\")\n\t\t\t\tentries = append(entries, ent)\n\t\t\t}\n\n\t\t\treturn entries\n\t\t},\n\t)\n\trequire.NoError(t, err, \"Unexpected error from slogtest.TestHandler\")\n}\n"
  },
  {
    "path": "exp/zapslog/options.go",
    "content": "// Copyright (c) 2023 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n//go:build go1.21\n\npackage zapslog\n\nimport \"log/slog\"\n\n// A HandlerOption configures a slog Handler.\ntype HandlerOption interface {\n\tapply(*Handler)\n}\n\n// handlerOptionFunc wraps a func so it satisfies the Option interface.\ntype handlerOptionFunc func(*Handler)\n\nfunc (f handlerOptionFunc) apply(handler *Handler) {\n\tf(handler)\n}\n\n// WithName configures the Logger to annotate each message with the logger name.\nfunc WithName(name string) HandlerOption {\n\treturn handlerOptionFunc(func(h *Handler) {\n\t\th.name = name\n\t})\n}\n\n// WithCaller configures the Logger to include the filename and line number\n// of the caller in log messages--if available.\nfunc WithCaller(enabled bool) HandlerOption {\n\treturn handlerOptionFunc(func(handler *Handler) {\n\t\thandler.addCaller = enabled\n\t})\n}\n\n// WithCallerSkip increases the number of callers skipped by caller annotation\n// (as enabled by the [WithCaller] option).\n//\n// When building wrappers around the Logger,\n// supplying this Option prevents Zap from always reporting\n// the wrapper code as the caller.\nfunc WithCallerSkip(skip int) HandlerOption {\n\treturn handlerOptionFunc(func(log *Handler) {\n\t\tlog.callerSkip += skip\n\t})\n}\n\n// AddStacktraceAt configures the Logger to record a stack trace\n// for all messages at or above a given level.\nfunc AddStacktraceAt(lvl slog.Level) HandlerOption {\n\treturn handlerOptionFunc(func(log *Handler) {\n\t\tlog.addStackAt = lvl\n\t})\n}\n"
  },
  {
    "path": "field.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"time\"\n\n\t\"go.uber.org/zap/internal/stacktrace\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\n// Field is an alias for Field. Aliasing this type dramatically\n// improves the navigability of this package's API documentation.\ntype Field = zapcore.Field\n\nvar (\n\t_minTimeInt64 = time.Unix(0, math.MinInt64)\n\t_maxTimeInt64 = time.Unix(0, math.MaxInt64)\n)\n\n// Skip constructs a no-op field, which is often useful when handling invalid\n// inputs in other Field constructors.\nfunc Skip() Field {\n\treturn Field{Type: zapcore.SkipType}\n}\n\n// nilField returns a field which will marshal explicitly as nil. See motivation\n// in https://github.com/uber-go/zap/issues/753 . If we ever make breaking\n// changes and add zapcore.NilType and zapcore.ObjectEncoder.AddNil, the\n// implementation here should be changed to reflect that.\nfunc nilField(key string) Field { return Reflect(key, nil) }\n\n// Binary constructs a field that carries an opaque binary blob.\n//\n// Binary data is serialized in an encoding-appropriate format. For example,\n// zap's JSON encoder base64-encodes binary blobs. To log UTF-8 encoded text,\n// use ByteString.\nfunc Binary(key string, val []byte) Field {\n\treturn Field{Key: key, Type: zapcore.BinaryType, Interface: val}\n}\n\n// Bool constructs a field that carries a bool.\nfunc Bool(key string, val bool) Field {\n\tvar ival int64\n\tif val {\n\t\tival = 1\n\t}\n\treturn Field{Key: key, Type: zapcore.BoolType, Integer: ival}\n}\n\n// Boolp constructs a field that carries a *bool. The returned Field will safely\n// and explicitly represent `nil` when appropriate.\nfunc Boolp(key string, val *bool) Field {\n\tif val == nil {\n\t\treturn nilField(key)\n\t}\n\treturn Bool(key, *val)\n}\n\n// ByteString constructs a field that carries UTF-8 encoded text as a []byte.\n// To log opaque binary blobs (which aren't necessarily valid UTF-8), use\n// Binary.\nfunc ByteString(key string, val []byte) Field {\n\treturn Field{Key: key, Type: zapcore.ByteStringType, Interface: val}\n}\n\n// Complex128 constructs a field that carries a complex number. Unlike most\n// numeric fields, this costs an allocation (to convert the complex128 to\n// interface{}).\nfunc Complex128(key string, val complex128) Field {\n\treturn Field{Key: key, Type: zapcore.Complex128Type, Interface: val}\n}\n\n// Complex128p constructs a field that carries a *complex128. The returned Field will safely\n// and explicitly represent `nil` when appropriate.\nfunc Complex128p(key string, val *complex128) Field {\n\tif val == nil {\n\t\treturn nilField(key)\n\t}\n\treturn Complex128(key, *val)\n}\n\n// Complex64 constructs a field that carries a complex number. Unlike most\n// numeric fields, this costs an allocation (to convert the complex64 to\n// interface{}).\nfunc Complex64(key string, val complex64) Field {\n\treturn Field{Key: key, Type: zapcore.Complex64Type, Interface: val}\n}\n\n// Complex64p constructs a field that carries a *complex64. The returned Field will safely\n// and explicitly represent `nil` when appropriate.\nfunc Complex64p(key string, val *complex64) Field {\n\tif val == nil {\n\t\treturn nilField(key)\n\t}\n\treturn Complex64(key, *val)\n}\n\n// Float64 constructs a field that carries a float64. The way the\n// floating-point value is represented is encoder-dependent, so marshaling is\n// necessarily lazy.\nfunc Float64(key string, val float64) Field {\n\treturn Field{Key: key, Type: zapcore.Float64Type, Integer: int64(math.Float64bits(val))}\n}\n\n// Float64p constructs a field that carries a *float64. The returned Field will safely\n// and explicitly represent `nil` when appropriate.\nfunc Float64p(key string, val *float64) Field {\n\tif val == nil {\n\t\treturn nilField(key)\n\t}\n\treturn Float64(key, *val)\n}\n\n// Float32 constructs a field that carries a float32. The way the\n// floating-point value is represented is encoder-dependent, so marshaling is\n// necessarily lazy.\nfunc Float32(key string, val float32) Field {\n\treturn Field{Key: key, Type: zapcore.Float32Type, Integer: int64(math.Float32bits(val))}\n}\n\n// Float32p constructs a field that carries a *float32. The returned Field will safely\n// and explicitly represent `nil` when appropriate.\nfunc Float32p(key string, val *float32) Field {\n\tif val == nil {\n\t\treturn nilField(key)\n\t}\n\treturn Float32(key, *val)\n}\n\n// Int constructs a field with the given key and value.\nfunc Int(key string, val int) Field {\n\treturn Int64(key, int64(val))\n}\n\n// Intp constructs a field that carries a *int. The returned Field will safely\n// and explicitly represent `nil` when appropriate.\nfunc Intp(key string, val *int) Field {\n\tif val == nil {\n\t\treturn nilField(key)\n\t}\n\treturn Int(key, *val)\n}\n\n// Int64 constructs a field with the given key and value.\nfunc Int64(key string, val int64) Field {\n\treturn Field{Key: key, Type: zapcore.Int64Type, Integer: val}\n}\n\n// Int64p constructs a field that carries a *int64. The returned Field will safely\n// and explicitly represent `nil` when appropriate.\nfunc Int64p(key string, val *int64) Field {\n\tif val == nil {\n\t\treturn nilField(key)\n\t}\n\treturn Int64(key, *val)\n}\n\n// Int32 constructs a field with the given key and value.\nfunc Int32(key string, val int32) Field {\n\treturn Field{Key: key, Type: zapcore.Int32Type, Integer: int64(val)}\n}\n\n// Int32p constructs a field that carries a *int32. The returned Field will safely\n// and explicitly represent `nil` when appropriate.\nfunc Int32p(key string, val *int32) Field {\n\tif val == nil {\n\t\treturn nilField(key)\n\t}\n\treturn Int32(key, *val)\n}\n\n// Int16 constructs a field with the given key and value.\nfunc Int16(key string, val int16) Field {\n\treturn Field{Key: key, Type: zapcore.Int16Type, Integer: int64(val)}\n}\n\n// Int16p constructs a field that carries a *int16. The returned Field will safely\n// and explicitly represent `nil` when appropriate.\nfunc Int16p(key string, val *int16) Field {\n\tif val == nil {\n\t\treturn nilField(key)\n\t}\n\treturn Int16(key, *val)\n}\n\n// Int8 constructs a field with the given key and value.\nfunc Int8(key string, val int8) Field {\n\treturn Field{Key: key, Type: zapcore.Int8Type, Integer: int64(val)}\n}\n\n// Int8p constructs a field that carries a *int8. The returned Field will safely\n// and explicitly represent `nil` when appropriate.\nfunc Int8p(key string, val *int8) Field {\n\tif val == nil {\n\t\treturn nilField(key)\n\t}\n\treturn Int8(key, *val)\n}\n\n// String constructs a field with the given key and value.\nfunc String(key string, val string) Field {\n\treturn Field{Key: key, Type: zapcore.StringType, String: val}\n}\n\n// Stringp constructs a field that carries a *string. The returned Field will safely\n// and explicitly represent `nil` when appropriate.\nfunc Stringp(key string, val *string) Field {\n\tif val == nil {\n\t\treturn nilField(key)\n\t}\n\treturn String(key, *val)\n}\n\n// Uint constructs a field with the given key and value.\nfunc Uint(key string, val uint) Field {\n\treturn Uint64(key, uint64(val))\n}\n\n// Uintp constructs a field that carries a *uint. The returned Field will safely\n// and explicitly represent `nil` when appropriate.\nfunc Uintp(key string, val *uint) Field {\n\tif val == nil {\n\t\treturn nilField(key)\n\t}\n\treturn Uint(key, *val)\n}\n\n// Uint64 constructs a field with the given key and value.\nfunc Uint64(key string, val uint64) Field {\n\treturn Field{Key: key, Type: zapcore.Uint64Type, Integer: int64(val)}\n}\n\n// Uint64p constructs a field that carries a *uint64. The returned Field will safely\n// and explicitly represent `nil` when appropriate.\nfunc Uint64p(key string, val *uint64) Field {\n\tif val == nil {\n\t\treturn nilField(key)\n\t}\n\treturn Uint64(key, *val)\n}\n\n// Uint32 constructs a field with the given key and value.\nfunc Uint32(key string, val uint32) Field {\n\treturn Field{Key: key, Type: zapcore.Uint32Type, Integer: int64(val)}\n}\n\n// Uint32p constructs a field that carries a *uint32. The returned Field will safely\n// and explicitly represent `nil` when appropriate.\nfunc Uint32p(key string, val *uint32) Field {\n\tif val == nil {\n\t\treturn nilField(key)\n\t}\n\treturn Uint32(key, *val)\n}\n\n// Uint16 constructs a field with the given key and value.\nfunc Uint16(key string, val uint16) Field {\n\treturn Field{Key: key, Type: zapcore.Uint16Type, Integer: int64(val)}\n}\n\n// Uint16p constructs a field that carries a *uint16. The returned Field will safely\n// and explicitly represent `nil` when appropriate.\nfunc Uint16p(key string, val *uint16) Field {\n\tif val == nil {\n\t\treturn nilField(key)\n\t}\n\treturn Uint16(key, *val)\n}\n\n// Uint8 constructs a field with the given key and value.\nfunc Uint8(key string, val uint8) Field {\n\treturn Field{Key: key, Type: zapcore.Uint8Type, Integer: int64(val)}\n}\n\n// Uint8p constructs a field that carries a *uint8. The returned Field will safely\n// and explicitly represent `nil` when appropriate.\nfunc Uint8p(key string, val *uint8) Field {\n\tif val == nil {\n\t\treturn nilField(key)\n\t}\n\treturn Uint8(key, *val)\n}\n\n// Uintptr constructs a field with the given key and value.\nfunc Uintptr(key string, val uintptr) Field {\n\treturn Field{Key: key, Type: zapcore.UintptrType, Integer: int64(val)}\n}\n\n// Uintptrp constructs a field that carries a *uintptr. The returned Field will safely\n// and explicitly represent `nil` when appropriate.\nfunc Uintptrp(key string, val *uintptr) Field {\n\tif val == nil {\n\t\treturn nilField(key)\n\t}\n\treturn Uintptr(key, *val)\n}\n\n// Reflect constructs a field with the given key and an arbitrary object. It uses\n// an encoding-appropriate, reflection-based function to lazily serialize nearly\n// any object into the logging context, but it's relatively slow and\n// allocation-heavy. Outside tests, Any is always a better choice.\n//\n// If encoding fails (e.g., trying to serialize a map[int]string to JSON), Reflect\n// includes the error message in the final log output.\nfunc Reflect(key string, val interface{}) Field {\n\treturn Field{Key: key, Type: zapcore.ReflectType, Interface: val}\n}\n\n// Namespace creates a named, isolated scope within the logger's context. All\n// subsequent fields will be added to the new namespace.\n//\n// This helps prevent key collisions when injecting loggers into sub-components\n// or third-party libraries.\nfunc Namespace(key string) Field {\n\treturn Field{Key: key, Type: zapcore.NamespaceType}\n}\n\n// Stringer constructs a field with the given key and the output of the value's\n// String method. The Stringer's String method is called lazily.\nfunc Stringer(key string, val fmt.Stringer) Field {\n\treturn Field{Key: key, Type: zapcore.StringerType, Interface: val}\n}\n\n// Time constructs a Field with the given key and value. The encoder\n// controls how the time is serialized.\nfunc Time(key string, val time.Time) Field {\n\tif val.Before(_minTimeInt64) || val.After(_maxTimeInt64) {\n\t\treturn Field{Key: key, Type: zapcore.TimeFullType, Interface: val}\n\t}\n\treturn Field{Key: key, Type: zapcore.TimeType, Integer: val.UnixNano(), Interface: val.Location()}\n}\n\n// Timep constructs a field that carries a *time.Time. The returned Field will safely\n// and explicitly represent `nil` when appropriate.\nfunc Timep(key string, val *time.Time) Field {\n\tif val == nil {\n\t\treturn nilField(key)\n\t}\n\treturn Time(key, *val)\n}\n\n// Stack constructs a field that stores a stacktrace of the current goroutine\n// under provided key. Keep in mind that taking a stacktrace is eager and\n// expensive (relatively speaking); this function both makes an allocation and\n// takes about two microseconds.\nfunc Stack(key string) Field {\n\treturn StackSkip(key, 1) // skip Stack\n}\n\n// StackSkip constructs a field similarly to Stack, but also skips the given\n// number of frames from the top of the stacktrace.\nfunc StackSkip(key string, skip int) Field {\n\t// Returning the stacktrace as a string costs an allocation, but saves us\n\t// from expanding the zapcore.Field union struct to include a byte slice. Since\n\t// taking a stacktrace is already so expensive (~10us), the extra allocation\n\t// is okay.\n\treturn String(key, stacktrace.Take(skip+1)) // skip StackSkip\n}\n\n// Duration constructs a field with the given key and value. The encoder\n// controls how the duration is serialized.\nfunc Duration(key string, val time.Duration) Field {\n\treturn Field{Key: key, Type: zapcore.DurationType, Integer: int64(val)}\n}\n\n// Durationp constructs a field that carries a *time.Duration. The returned Field will safely\n// and explicitly represent `nil` when appropriate.\nfunc Durationp(key string, val *time.Duration) Field {\n\tif val == nil {\n\t\treturn nilField(key)\n\t}\n\treturn Duration(key, *val)\n}\n\n// Object constructs a field with the given key and ObjectMarshaler. It\n// provides a flexible, but still type-safe and efficient, way to add map- or\n// struct-like user-defined types to the logging context. The struct's\n// MarshalLogObject method is called lazily.\nfunc Object(key string, val zapcore.ObjectMarshaler) Field {\n\tif val == nil {\n\t\treturn nilField(key)\n\t}\n\treturn Field{Key: key, Type: zapcore.ObjectMarshalerType, Interface: val}\n}\n\n// Inline constructs a Field that is similar to Object, but it\n// will add the elements of the provided ObjectMarshaler to the\n// current namespace.\nfunc Inline(val zapcore.ObjectMarshaler) Field {\n\treturn zapcore.Field{\n\t\tType:      zapcore.InlineMarshalerType,\n\t\tInterface: val,\n\t}\n}\n\n// Dict constructs a field containing the provided key-value pairs.\n// It acts similar to [Object], but with the fields specified as arguments.\nfunc Dict(key string, val ...Field) Field {\n\treturn dictField(key, val)\n}\n\n// We need a function with the signature (string, T) for zap.Any.\nfunc dictField(key string, val []Field) Field {\n\treturn Object(key, dictObject(val))\n}\n\ntype dictObject []Field\n\nfunc (d dictObject) MarshalLogObject(enc zapcore.ObjectEncoder) error {\n\tfor _, f := range d {\n\t\tf.AddTo(enc)\n\t}\n\treturn nil\n}\n\n// DictObject constructs a [zapcore.ObjectMarshaler] with the given list of fields.\n// The resulting object marshaler can be used as input to [Object], [Objects], or\n// any other functions that expect an object marshaler.\nfunc DictObject(val ...Field) zapcore.ObjectMarshaler {\n\treturn dictObject(val)\n}\n\n// We discovered an issue where zap.Any can cause a performance degradation\n// when used in new goroutines.\n//\n// This happens because the compiler assigns 4.8kb (one zap.Field per arm of\n// switch statement) of stack space for zap.Any when it takes the form:\n//\n//\tswitch v := v.(type) {\n//\tcase string:\n//\t\treturn String(key, v)\n//\tcase int:\n//\t\treturn Int(key, v)\n//\t\t// ...\n//\tdefault:\n//\t\treturn Reflect(key, v)\n//\t}\n//\n// To avoid this, we use the type switch to assign a value to a single local variable\n// and then call a function on it.\n// The local variable is just a function reference so it doesn't allocate\n// when converted to an interface{}.\n//\n// A fair bit of experimentation went into this.\n// See also:\n//\n// - https://github.com/uber-go/zap/pull/1301\n// - https://github.com/uber-go/zap/pull/1303\n// - https://github.com/uber-go/zap/pull/1304\n// - https://github.com/uber-go/zap/pull/1305\n// - https://github.com/uber-go/zap/pull/1308\n//\n// See https://github.com/golang/go/issues/62077 for upstream issue.\ntype anyFieldC[T any] func(string, T) Field\n\nfunc (f anyFieldC[T]) Any(key string, val any) Field {\n\tv, _ := val.(T)\n\t// val is guaranteed to be a T, except when it's nil.\n\treturn f(key, v)\n}\n\n// Any takes a key and an arbitrary value and chooses the best way to represent\n// them as a field, falling back to a reflection-based approach only if\n// necessary.\n//\n// Since byte/uint8 and rune/int32 are aliases, Any can't differentiate between\n// them. To minimize surprises, []byte values are treated as binary blobs, byte\n// values are treated as uint8, and runes are always treated as integers.\nfunc Any(key string, value interface{}) Field {\n\tvar c interface{ Any(string, any) Field }\n\n\tswitch value.(type) {\n\tcase zapcore.ObjectMarshaler:\n\t\tc = anyFieldC[zapcore.ObjectMarshaler](Object)\n\tcase zapcore.ArrayMarshaler:\n\t\tc = anyFieldC[zapcore.ArrayMarshaler](Array)\n\tcase []Field:\n\t\tc = anyFieldC[[]Field](dictField)\n\tcase bool:\n\t\tc = anyFieldC[bool](Bool)\n\tcase *bool:\n\t\tc = anyFieldC[*bool](Boolp)\n\tcase []bool:\n\t\tc = anyFieldC[[]bool](Bools)\n\tcase complex128:\n\t\tc = anyFieldC[complex128](Complex128)\n\tcase *complex128:\n\t\tc = anyFieldC[*complex128](Complex128p)\n\tcase []complex128:\n\t\tc = anyFieldC[[]complex128](Complex128s)\n\tcase complex64:\n\t\tc = anyFieldC[complex64](Complex64)\n\tcase *complex64:\n\t\tc = anyFieldC[*complex64](Complex64p)\n\tcase []complex64:\n\t\tc = anyFieldC[[]complex64](Complex64s)\n\tcase float64:\n\t\tc = anyFieldC[float64](Float64)\n\tcase *float64:\n\t\tc = anyFieldC[*float64](Float64p)\n\tcase []float64:\n\t\tc = anyFieldC[[]float64](Float64s)\n\tcase float32:\n\t\tc = anyFieldC[float32](Float32)\n\tcase *float32:\n\t\tc = anyFieldC[*float32](Float32p)\n\tcase []float32:\n\t\tc = anyFieldC[[]float32](Float32s)\n\tcase int:\n\t\tc = anyFieldC[int](Int)\n\tcase *int:\n\t\tc = anyFieldC[*int](Intp)\n\tcase []int:\n\t\tc = anyFieldC[[]int](Ints)\n\tcase int64:\n\t\tc = anyFieldC[int64](Int64)\n\tcase *int64:\n\t\tc = anyFieldC[*int64](Int64p)\n\tcase []int64:\n\t\tc = anyFieldC[[]int64](Int64s)\n\tcase int32:\n\t\tc = anyFieldC[int32](Int32)\n\tcase *int32:\n\t\tc = anyFieldC[*int32](Int32p)\n\tcase []int32:\n\t\tc = anyFieldC[[]int32](Int32s)\n\tcase int16:\n\t\tc = anyFieldC[int16](Int16)\n\tcase *int16:\n\t\tc = anyFieldC[*int16](Int16p)\n\tcase []int16:\n\t\tc = anyFieldC[[]int16](Int16s)\n\tcase int8:\n\t\tc = anyFieldC[int8](Int8)\n\tcase *int8:\n\t\tc = anyFieldC[*int8](Int8p)\n\tcase []int8:\n\t\tc = anyFieldC[[]int8](Int8s)\n\tcase string:\n\t\tc = anyFieldC[string](String)\n\tcase *string:\n\t\tc = anyFieldC[*string](Stringp)\n\tcase []string:\n\t\tc = anyFieldC[[]string](Strings)\n\tcase uint:\n\t\tc = anyFieldC[uint](Uint)\n\tcase *uint:\n\t\tc = anyFieldC[*uint](Uintp)\n\tcase []uint:\n\t\tc = anyFieldC[[]uint](Uints)\n\tcase uint64:\n\t\tc = anyFieldC[uint64](Uint64)\n\tcase *uint64:\n\t\tc = anyFieldC[*uint64](Uint64p)\n\tcase []uint64:\n\t\tc = anyFieldC[[]uint64](Uint64s)\n\tcase uint32:\n\t\tc = anyFieldC[uint32](Uint32)\n\tcase *uint32:\n\t\tc = anyFieldC[*uint32](Uint32p)\n\tcase []uint32:\n\t\tc = anyFieldC[[]uint32](Uint32s)\n\tcase uint16:\n\t\tc = anyFieldC[uint16](Uint16)\n\tcase *uint16:\n\t\tc = anyFieldC[*uint16](Uint16p)\n\tcase []uint16:\n\t\tc = anyFieldC[[]uint16](Uint16s)\n\tcase uint8:\n\t\tc = anyFieldC[uint8](Uint8)\n\tcase *uint8:\n\t\tc = anyFieldC[*uint8](Uint8p)\n\tcase []byte:\n\t\tc = anyFieldC[[]byte](Binary)\n\tcase uintptr:\n\t\tc = anyFieldC[uintptr](Uintptr)\n\tcase *uintptr:\n\t\tc = anyFieldC[*uintptr](Uintptrp)\n\tcase []uintptr:\n\t\tc = anyFieldC[[]uintptr](Uintptrs)\n\tcase time.Time:\n\t\tc = anyFieldC[time.Time](Time)\n\tcase *time.Time:\n\t\tc = anyFieldC[*time.Time](Timep)\n\tcase []time.Time:\n\t\tc = anyFieldC[[]time.Time](Times)\n\tcase time.Duration:\n\t\tc = anyFieldC[time.Duration](Duration)\n\tcase *time.Duration:\n\t\tc = anyFieldC[*time.Duration](Durationp)\n\tcase []time.Duration:\n\t\tc = anyFieldC[[]time.Duration](Durations)\n\tcase error:\n\t\tc = anyFieldC[error](NamedError)\n\tcase []error:\n\t\tc = anyFieldC[[]error](Errors)\n\tcase fmt.Stringer:\n\t\tc = anyFieldC[fmt.Stringer](Stringer)\n\tdefault:\n\t\tc = anyFieldC[any](Reflect)\n\t}\n\n\treturn c.Any(key, value)\n}\n"
  },
  {
    "path": "field_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"math\"\n\t\"net\"\n\t\"regexp\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/zap/internal/stacktrace\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\ntype username string\n\nfunc (n username) MarshalLogObject(enc zapcore.ObjectEncoder) error {\n\tenc.AddString(\"username\", string(n))\n\treturn nil\n}\n\nfunc assertCanBeReused(t testing.TB, field Field) {\n\tvar wg sync.WaitGroup\n\n\tfor i := 0; i < 100; i++ {\n\t\tenc := zapcore.NewMapObjectEncoder()\n\n\t\t// Ensure using the field in multiple encoders in separate goroutines\n\t\t// does not cause any races or panics.\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tassert.NotPanics(t, func() {\n\t\t\t\tfield.AddTo(enc)\n\t\t\t}, \"Reusing a field should not cause issues\")\n\t\t}()\n\t}\n\n\twg.Wait()\n}\n\nfunc TestFieldConstructors(t *testing.T) {\n\t// Interface types.\n\taddr := net.ParseIP(\"1.2.3.4\")\n\tname := username(\"phil\")\n\tints := []int{5, 6}\n\n\t// Helpful values for use in constructing pointers to primitives below.\n\tvar (\n\t\tboolVal       = bool(true)\n\t\tcomplex128Val = complex128(complex(0, 0))\n\t\tcomplex64Val  = complex64(complex(0, 0))\n\t\tdurationVal   = time.Duration(time.Second)\n\t\tfloat64Val    = float64(1.0)\n\t\tfloat32Val    = float32(1.0)\n\t\tintVal        = int(1)\n\t\tint64Val      = int64(1)\n\t\tint32Val      = int32(1)\n\t\tint16Val      = int16(1)\n\t\tint8Val       = int8(1)\n\t\tstringVal     = string(\"hello\")\n\t\ttimeVal       = time.Unix(100000, 0)\n\t\tuintVal       = uint(1)\n\t\tuint64Val     = uint64(1)\n\t\tuint32Val     = uint32(1)\n\t\tuint16Val     = uint16(1)\n\t\tuint8Val      = uint8(1)\n\t\tuintptrVal    = uintptr(1)\n\t\tnilErr        error\n\t)\n\n\ttests := []struct {\n\t\tname   string\n\t\tfield  Field\n\t\texpect Field\n\t}{\n\t\t{\"Skip\", Field{Type: zapcore.SkipType}, Skip()},\n\t\t{\"Binary\", Field{Key: \"k\", Type: zapcore.BinaryType, Interface: []byte(\"ab12\")}, Binary(\"k\", []byte(\"ab12\"))},\n\t\t{\"Bool\", Field{Key: \"k\", Type: zapcore.BoolType, Integer: 1}, Bool(\"k\", true)},\n\t\t{\"Bool\", Field{Key: \"k\", Type: zapcore.BoolType, Integer: 0}, Bool(\"k\", false)},\n\t\t{\"ByteString\", Field{Key: \"k\", Type: zapcore.ByteStringType, Interface: []byte(\"ab12\")}, ByteString(\"k\", []byte(\"ab12\"))},\n\t\t{\"Complex128\", Field{Key: \"k\", Type: zapcore.Complex128Type, Interface: 1 + 2i}, Complex128(\"k\", 1+2i)},\n\t\t{\"Complex64\", Field{Key: \"k\", Type: zapcore.Complex64Type, Interface: complex64(1 + 2i)}, Complex64(\"k\", 1+2i)},\n\t\t{\"Duration\", Field{Key: \"k\", Type: zapcore.DurationType, Integer: 1}, Duration(\"k\", 1)},\n\t\t{\"Int\", Field{Key: \"k\", Type: zapcore.Int64Type, Integer: 1}, Int(\"k\", 1)},\n\t\t{\"Int64\", Field{Key: \"k\", Type: zapcore.Int64Type, Integer: 1}, Int64(\"k\", 1)},\n\t\t{\"Int32\", Field{Key: \"k\", Type: zapcore.Int32Type, Integer: 1}, Int32(\"k\", 1)},\n\t\t{\"Int16\", Field{Key: \"k\", Type: zapcore.Int16Type, Integer: 1}, Int16(\"k\", 1)},\n\t\t{\"Int8\", Field{Key: \"k\", Type: zapcore.Int8Type, Integer: 1}, Int8(\"k\", 1)},\n\t\t{\"String\", Field{Key: \"k\", Type: zapcore.StringType, String: \"foo\"}, String(\"k\", \"foo\")},\n\t\t{\"Time\", Field{Key: \"k\", Type: zapcore.TimeType, Integer: 0, Interface: time.UTC}, Time(\"k\", time.Unix(0, 0).In(time.UTC))},\n\t\t{\"Time\", Field{Key: \"k\", Type: zapcore.TimeType, Integer: 1000, Interface: time.UTC}, Time(\"k\", time.Unix(0, 1000).In(time.UTC))},\n\t\t{\"Time\", Field{Key: \"k\", Type: zapcore.TimeType, Integer: math.MinInt64, Interface: time.UTC}, Time(\"k\", time.Unix(0, math.MinInt64).In(time.UTC))},\n\t\t{\"Time\", Field{Key: \"k\", Type: zapcore.TimeType, Integer: math.MaxInt64, Interface: time.UTC}, Time(\"k\", time.Unix(0, math.MaxInt64).In(time.UTC))},\n\t\t{\"Time\", Field{Key: \"k\", Type: zapcore.TimeFullType, Interface: time.Time{}}, Time(\"k\", time.Time{})},\n\t\t{\"Time\", Field{Key: \"k\", Type: zapcore.TimeFullType, Interface: time.Unix(math.MaxInt64, 0)}, Time(\"k\", time.Unix(math.MaxInt64, 0))},\n\t\t{\"Uint\", Field{Key: \"k\", Type: zapcore.Uint64Type, Integer: 1}, Uint(\"k\", 1)},\n\t\t{\"Uint64\", Field{Key: \"k\", Type: zapcore.Uint64Type, Integer: 1}, Uint64(\"k\", 1)},\n\t\t{\"Uint32\", Field{Key: \"k\", Type: zapcore.Uint32Type, Integer: 1}, Uint32(\"k\", 1)},\n\t\t{\"Uint16\", Field{Key: \"k\", Type: zapcore.Uint16Type, Integer: 1}, Uint16(\"k\", 1)},\n\t\t{\"Uint8\", Field{Key: \"k\", Type: zapcore.Uint8Type, Integer: 1}, Uint8(\"k\", 1)},\n\t\t{\"Uintptr\", Field{Key: \"k\", Type: zapcore.UintptrType, Integer: 10}, Uintptr(\"k\", 0xa)},\n\t\t{\"Reflect\", Field{Key: \"k\", Type: zapcore.ReflectType, Interface: ints}, Reflect(\"k\", ints)},\n\t\t{\"Reflect\", Field{Key: \"k\", Type: zapcore.ReflectType}, Reflect(\"k\", nil)},\n\t\t{\"Stringer\", Field{Key: \"k\", Type: zapcore.StringerType, Interface: addr}, Stringer(\"k\", addr)},\n\t\t{\"Object\", Field{Key: \"k\", Type: zapcore.ObjectMarshalerType, Interface: name}, Object(\"k\", name)},\n\t\t{\"Inline\", Field{Type: zapcore.InlineMarshalerType, Interface: name}, Inline(name)},\n\t\t{\"Any:ObjectMarshaler\", Any(\"k\", name), Object(\"k\", name)},\n\t\t{\"Any:ArrayMarshaler\", Any(\"k\", bools([]bool{true})), Array(\"k\", bools([]bool{true}))},\n\t\t{\"Any:Dict\", Any(\"k\", []Field{String(\"k\", \"v\")}), Dict(\"k\", String(\"k\", \"v\"))},\n\t\t{\"Any:Stringer\", Any(\"k\", addr), Stringer(\"k\", addr)},\n\t\t{\"Any:Bool\", Any(\"k\", true), Bool(\"k\", true)},\n\t\t{\"Any:Bools\", Any(\"k\", []bool{true}), Bools(\"k\", []bool{true})},\n\t\t{\"Any:Byte\", Any(\"k\", byte(1)), Uint8(\"k\", 1)},\n\t\t{\"Any:Bytes\", Any(\"k\", []byte{1}), Binary(\"k\", []byte{1})},\n\t\t{\"Any:Complex128\", Any(\"k\", 1+2i), Complex128(\"k\", 1+2i)},\n\t\t{\"Any:Complex128s\", Any(\"k\", []complex128{1 + 2i}), Complex128s(\"k\", []complex128{1 + 2i})},\n\t\t{\"Any:Complex64\", Any(\"k\", complex64(1+2i)), Complex64(\"k\", 1+2i)},\n\t\t{\"Any:Complex64s\", Any(\"k\", []complex64{1 + 2i}), Complex64s(\"k\", []complex64{1 + 2i})},\n\t\t{\"Any:Float64\", Any(\"k\", 3.14), Float64(\"k\", 3.14)},\n\t\t{\"Any:Float64s\", Any(\"k\", []float64{3.14}), Float64s(\"k\", []float64{3.14})},\n\t\t{\"Any:Float32\", Any(\"k\", float32(3.14)), Float32(\"k\", 3.14)},\n\t\t{\"Any:Float32s\", Any(\"k\", []float32{3.14}), Float32s(\"k\", []float32{3.14})},\n\t\t{\"Any:Int\", Any(\"k\", 1), Int(\"k\", 1)},\n\t\t{\"Any:Ints\", Any(\"k\", []int{1}), Ints(\"k\", []int{1})},\n\t\t{\"Any:Int64\", Any(\"k\", int64(1)), Int64(\"k\", 1)},\n\t\t{\"Any:Int64s\", Any(\"k\", []int64{1}), Int64s(\"k\", []int64{1})},\n\t\t{\"Any:Int32\", Any(\"k\", int32(1)), Int32(\"k\", 1)},\n\t\t{\"Any:Int32s\", Any(\"k\", []int32{1}), Int32s(\"k\", []int32{1})},\n\t\t{\"Any:Int16\", Any(\"k\", int16(1)), Int16(\"k\", 1)},\n\t\t{\"Any:Int16s\", Any(\"k\", []int16{1}), Int16s(\"k\", []int16{1})},\n\t\t{\"Any:Int8\", Any(\"k\", int8(1)), Int8(\"k\", 1)},\n\t\t{\"Any:Int8s\", Any(\"k\", []int8{1}), Int8s(\"k\", []int8{1})},\n\t\t{\"Any:Rune\", Any(\"k\", rune(1)), Int32(\"k\", 1)},\n\t\t{\"Any:Runes\", Any(\"k\", []rune{1}), Int32s(\"k\", []int32{1})},\n\t\t{\"Any:String\", Any(\"k\", \"v\"), String(\"k\", \"v\")},\n\t\t{\"Any:Strings\", Any(\"k\", []string{\"v\"}), Strings(\"k\", []string{\"v\"})},\n\t\t{\"Any:Uint\", Any(\"k\", uint(1)), Uint(\"k\", 1)},\n\t\t{\"Any:Uints\", Any(\"k\", []uint{1}), Uints(\"k\", []uint{1})},\n\t\t{\"Any:Uint64\", Any(\"k\", uint64(1)), Uint64(\"k\", 1)},\n\t\t{\"Any:Uint64s\", Any(\"k\", []uint64{1}), Uint64s(\"k\", []uint64{1})},\n\t\t{\"Any:Uint32\", Any(\"k\", uint32(1)), Uint32(\"k\", 1)},\n\t\t{\"Any:Uint32s\", Any(\"k\", []uint32{1}), Uint32s(\"k\", []uint32{1})},\n\t\t{\"Any:Uint16\", Any(\"k\", uint16(1)), Uint16(\"k\", 1)},\n\t\t{\"Any:Uint16s\", Any(\"k\", []uint16{1}), Uint16s(\"k\", []uint16{1})},\n\t\t{\"Any:Uint8\", Any(\"k\", uint8(1)), Uint8(\"k\", 1)},\n\t\t{\"Any:Uint8s\", Any(\"k\", []uint8{1}), Binary(\"k\", []uint8{1})},\n\t\t{\"Any:Uintptr\", Any(\"k\", uintptr(1)), Uintptr(\"k\", 1)},\n\t\t{\"Any:Uintptrs\", Any(\"k\", []uintptr{1}), Uintptrs(\"k\", []uintptr{1})},\n\t\t{\"Any:Time\", Any(\"k\", time.Unix(0, 0)), Time(\"k\", time.Unix(0, 0))},\n\t\t{\"Any:TimeFullType\", Any(\"k\", time.Time{}), Time(\"k\", time.Time{})},\n\t\t{\"Any:Times\", Any(\"k\", []time.Time{time.Unix(0, 0)}), Times(\"k\", []time.Time{time.Unix(0, 0)})},\n\t\t{\"Any:Duration\", Any(\"k\", time.Second), Duration(\"k\", time.Second)},\n\t\t{\"Any:Durations\", Any(\"k\", []time.Duration{time.Second}), Durations(\"k\", []time.Duration{time.Second})},\n\t\t{\"Any:Fallback\", Any(\"k\", struct{}{}), Reflect(\"k\", struct{}{})},\n\t\t{\"Ptr:Bool\", Boolp(\"k\", nil), nilField(\"k\")},\n\t\t{\"Ptr:Bool\", Boolp(\"k\", &boolVal), Bool(\"k\", boolVal)},\n\t\t{\"Any:PtrBool\", Any(\"k\", (*bool)(nil)), nilField(\"k\")},\n\t\t{\"Any:PtrBool\", Any(\"k\", &boolVal), Bool(\"k\", boolVal)},\n\t\t{\"Ptr:Complex128\", Complex128p(\"k\", nil), nilField(\"k\")},\n\t\t{\"Ptr:Complex128\", Complex128p(\"k\", &complex128Val), Complex128(\"k\", complex128Val)},\n\t\t{\"Any:PtrComplex128\", Any(\"k\", (*complex128)(nil)), nilField(\"k\")},\n\t\t{\"Any:PtrComplex128\", Any(\"k\", &complex128Val), Complex128(\"k\", complex128Val)},\n\t\t{\"Ptr:Complex64\", Complex64p(\"k\", nil), nilField(\"k\")},\n\t\t{\"Ptr:Complex64\", Complex64p(\"k\", &complex64Val), Complex64(\"k\", complex64Val)},\n\t\t{\"Any:PtrComplex64\", Any(\"k\", (*complex64)(nil)), nilField(\"k\")},\n\t\t{\"Any:PtrComplex64\", Any(\"k\", &complex64Val), Complex64(\"k\", complex64Val)},\n\t\t{\"Ptr:Duration\", Durationp(\"k\", nil), nilField(\"k\")},\n\t\t{\"Ptr:Duration\", Durationp(\"k\", &durationVal), Duration(\"k\", durationVal)},\n\t\t{\"Any:PtrDuration\", Any(\"k\", (*time.Duration)(nil)), nilField(\"k\")},\n\t\t{\"Any:PtrDuration\", Any(\"k\", &durationVal), Duration(\"k\", durationVal)},\n\t\t{\"Ptr:Float64\", Float64p(\"k\", nil), nilField(\"k\")},\n\t\t{\"Ptr:Float64\", Float64p(\"k\", &float64Val), Float64(\"k\", float64Val)},\n\t\t{\"Any:PtrFloat64\", Any(\"k\", (*float64)(nil)), nilField(\"k\")},\n\t\t{\"Any:PtrFloat64\", Any(\"k\", &float64Val), Float64(\"k\", float64Val)},\n\t\t{\"Ptr:Float32\", Float32p(\"k\", nil), nilField(\"k\")},\n\t\t{\"Ptr:Float32\", Float32p(\"k\", &float32Val), Float32(\"k\", float32Val)},\n\t\t{\"Any:PtrFloat32\", Any(\"k\", (*float32)(nil)), nilField(\"k\")},\n\t\t{\"Any:PtrFloat32\", Any(\"k\", &float32Val), Float32(\"k\", float32Val)},\n\t\t{\"Ptr:Int\", Intp(\"k\", nil), nilField(\"k\")},\n\t\t{\"Ptr:Int\", Intp(\"k\", &intVal), Int(\"k\", intVal)},\n\t\t{\"Any:PtrInt\", Any(\"k\", (*int)(nil)), nilField(\"k\")},\n\t\t{\"Any:PtrInt\", Any(\"k\", &intVal), Int(\"k\", intVal)},\n\t\t{\"Ptr:Int64\", Int64p(\"k\", nil), nilField(\"k\")},\n\t\t{\"Ptr:Int64\", Int64p(\"k\", &int64Val), Int64(\"k\", int64Val)},\n\t\t{\"Any:PtrInt64\", Any(\"k\", (*int64)(nil)), nilField(\"k\")},\n\t\t{\"Any:PtrInt64\", Any(\"k\", &int64Val), Int64(\"k\", int64Val)},\n\t\t{\"Ptr:Int32\", Int32p(\"k\", nil), nilField(\"k\")},\n\t\t{\"Ptr:Int32\", Int32p(\"k\", &int32Val), Int32(\"k\", int32Val)},\n\t\t{\"Any:PtrInt32\", Any(\"k\", (*int32)(nil)), nilField(\"k\")},\n\t\t{\"Any:PtrInt32\", Any(\"k\", &int32Val), Int32(\"k\", int32Val)},\n\t\t{\"Ptr:Int16\", Int16p(\"k\", nil), nilField(\"k\")},\n\t\t{\"Ptr:Int16\", Int16p(\"k\", &int16Val), Int16(\"k\", int16Val)},\n\t\t{\"Any:PtrInt16\", Any(\"k\", (*int16)(nil)), nilField(\"k\")},\n\t\t{\"Any:PtrInt16\", Any(\"k\", &int16Val), Int16(\"k\", int16Val)},\n\t\t{\"Ptr:Int8\", Int8p(\"k\", nil), nilField(\"k\")},\n\t\t{\"Ptr:Int8\", Int8p(\"k\", &int8Val), Int8(\"k\", int8Val)},\n\t\t{\"Any:PtrInt8\", Any(\"k\", (*int8)(nil)), nilField(\"k\")},\n\t\t{\"Any:PtrInt8\", Any(\"k\", &int8Val), Int8(\"k\", int8Val)},\n\t\t{\"Ptr:String\", Stringp(\"k\", nil), nilField(\"k\")},\n\t\t{\"Ptr:String\", Stringp(\"k\", &stringVal), String(\"k\", stringVal)},\n\t\t{\"Any:PtrString\", Any(\"k\", (*string)(nil)), nilField(\"k\")},\n\t\t{\"Any:PtrString\", Any(\"k\", &stringVal), String(\"k\", stringVal)},\n\t\t{\"Ptr:Time\", Timep(\"k\", nil), nilField(\"k\")},\n\t\t{\"Ptr:Time\", Timep(\"k\", &timeVal), Time(\"k\", timeVal)},\n\t\t{\"Any:PtrTime\", Any(\"k\", (*time.Time)(nil)), nilField(\"k\")},\n\t\t{\"Any:PtrTime\", Any(\"k\", &timeVal), Time(\"k\", timeVal)},\n\t\t{\"Any:PtrTimeFullType\", Any(\"k\", &time.Time{}), Time(\"k\", time.Time{})},\n\t\t{\"Ptr:Uint\", Uintp(\"k\", nil), nilField(\"k\")},\n\t\t{\"Ptr:Uint\", Uintp(\"k\", &uintVal), Uint(\"k\", uintVal)},\n\t\t{\"Any:PtrUint\", Any(\"k\", (*uint)(nil)), nilField(\"k\")},\n\t\t{\"Any:PtrUint\", Any(\"k\", &uintVal), Uint(\"k\", uintVal)},\n\t\t{\"Ptr:Uint64\", Uint64p(\"k\", nil), nilField(\"k\")},\n\t\t{\"Ptr:Uint64\", Uint64p(\"k\", &uint64Val), Uint64(\"k\", uint64Val)},\n\t\t{\"Any:PtrUint64\", Any(\"k\", (*uint64)(nil)), nilField(\"k\")},\n\t\t{\"Any:PtrUint64\", Any(\"k\", &uint64Val), Uint64(\"k\", uint64Val)},\n\t\t{\"Ptr:Uint32\", Uint32p(\"k\", nil), nilField(\"k\")},\n\t\t{\"Ptr:Uint32\", Uint32p(\"k\", &uint32Val), Uint32(\"k\", uint32Val)},\n\t\t{\"Any:PtrUint32\", Any(\"k\", (*uint32)(nil)), nilField(\"k\")},\n\t\t{\"Any:PtrUint32\", Any(\"k\", &uint32Val), Uint32(\"k\", uint32Val)},\n\t\t{\"Ptr:Uint16\", Uint16p(\"k\", nil), nilField(\"k\")},\n\t\t{\"Ptr:Uint16\", Uint16p(\"k\", &uint16Val), Uint16(\"k\", uint16Val)},\n\t\t{\"Any:PtrUint16\", Any(\"k\", (*uint16)(nil)), nilField(\"k\")},\n\t\t{\"Any:PtrUint16\", Any(\"k\", &uint16Val), Uint16(\"k\", uint16Val)},\n\t\t{\"Ptr:Uint8\", Uint8p(\"k\", nil), nilField(\"k\")},\n\t\t{\"Ptr:Uint8\", Uint8p(\"k\", &uint8Val), Uint8(\"k\", uint8Val)},\n\t\t{\"Any:PtrUint8\", Any(\"k\", (*uint8)(nil)), nilField(\"k\")},\n\t\t{\"Any:PtrUint8\", Any(\"k\", &uint8Val), Uint8(\"k\", uint8Val)},\n\t\t{\"Ptr:Uintptr\", Uintptrp(\"k\", nil), nilField(\"k\")},\n\t\t{\"Ptr:Uintptr\", Uintptrp(\"k\", &uintptrVal), Uintptr(\"k\", uintptrVal)},\n\t\t{\"Any:PtrUintptr\", Any(\"k\", (*uintptr)(nil)), nilField(\"k\")},\n\t\t{\"Any:PtrUintptr\", Any(\"k\", &uintptrVal), Uintptr(\"k\", uintptrVal)},\n\t\t{\"Any:ErrorNil\", Any(\"k\", nilErr), nilField(\"k\")},\n\t\t{\"Namespace\", Namespace(\"k\"), Field{Key: \"k\", Type: zapcore.NamespaceType}},\n\t\t{\"Object:Nil\", Object(\"k\", nil), nilField(\"k\")},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif !assert.Equal(t, tt.expect, tt.field, \"Unexpected output from convenience field constructor\") {\n\t\t\t\tt.Logf(\"type expected: %T\\nGot: %T\", tt.expect.Interface, tt.field.Interface)\n\t\t\t}\n\t\t\tassertCanBeReused(t, tt.field)\n\t\t})\n\t}\n}\n\nfunc TestStackField(t *testing.T) {\n\tf := Stack(\"stacktrace\")\n\tassert.Equal(t, \"stacktrace\", f.Key, \"Unexpected field key.\")\n\tassert.Equal(t, zapcore.StringType, f.Type, \"Unexpected field type.\")\n\tr := regexp.MustCompile(`field_test.go:(\\d+)`)\n\tassert.Equal(t, r.ReplaceAllString(stacktrace.Take(0), \"field_test.go\"), r.ReplaceAllString(f.String, \"field_test.go\"), \"Unexpected stack trace\")\n\tassertCanBeReused(t, f)\n}\n\nfunc TestStackSkipField(t *testing.T) {\n\tf := StackSkip(\"stacktrace\", 0)\n\tassert.Equal(t, \"stacktrace\", f.Key, \"Unexpected field key.\")\n\tassert.Equal(t, zapcore.StringType, f.Type, \"Unexpected field type.\")\n\tr := regexp.MustCompile(`field_test.go:(\\d+)`)\n\tassert.Equal(t, r.ReplaceAllString(stacktrace.Take(0), \"field_test.go\"), r.ReplaceAllString(f.String, \"field_test.go\"), f.String, \"Unexpected stack trace\")\n\tassertCanBeReused(t, f)\n}\n\nfunc TestStackSkipFieldWithSkip(t *testing.T) {\n\tf := StackSkip(\"stacktrace\", 1)\n\tassert.Equal(t, \"stacktrace\", f.Key, \"Unexpected field key.\")\n\tassert.Equal(t, zapcore.StringType, f.Type, \"Unexpected field type.\")\n\tassert.Equal(t, stacktrace.Take(1), f.String, \"Unexpected stack trace\")\n\tassertCanBeReused(t, f)\n}\n\nfunc TestDict(t *testing.T) {\n\ttests := []struct {\n\t\tdesc     string\n\t\tfield    Field\n\t\texpected any\n\t}{\n\t\t{\"empty\", Dict(\"\"), map[string]any{}},\n\t\t{\"single\", Dict(\"\", String(\"k\", \"v\")), map[string]any{\"k\": \"v\"}},\n\t\t{\"multiple\", Dict(\"\", String(\"k\", \"v\"), String(\"k2\", \"v2\")), map[string]any{\"k\": \"v\", \"k2\": \"v2\"}},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tenc := zapcore.NewMapObjectEncoder()\n\t\t\ttt.field.Key = \"k\"\n\t\t\ttt.field.AddTo(enc)\n\t\t\tassert.Equal(t, tt.expected, enc.Fields[\"k\"], \"unexpected map contents\")\n\t\t\tassert.Len(t, enc.Fields, 1, \"found extra keys in map: %v\", enc.Fields)\n\n\t\t\tassertCanBeReused(t, tt.field)\n\t\t})\n\t}\n}\n\nfunc TestDictObject(t *testing.T) {\n\ttests := []struct {\n\t\tdesc     string\n\t\tfield    Field\n\t\texpected any\n\t}{\n\t\t{\n\t\t\t\"empty\",\n\t\t\tObject(\"\", DictObject()),\n\t\t\tmap[string]any{},\n\t\t},\n\t\t{\n\t\t\t\"object\",\n\t\t\tObject(\"\", DictObject(String(\"k\", \"v\"))),\n\t\t\tmap[string]any{\"k\": \"v\"},\n\t\t},\n\t\t{\n\t\t\t\"objects\",\n\t\t\tObjects(\"\", []zapcore.ObjectMarshaler{\n\t\t\t\tDictObject(String(\"k\", \"v\")),\n\t\t\t\tDictObject(String(\"k2\", \"v2\")),\n\t\t\t}),\n\t\t\t[]any{\n\t\t\t\tmap[string]any{\"k\": \"v\"},\n\t\t\t\tmap[string]any{\"k2\": \"v2\"},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tenc := zapcore.NewMapObjectEncoder()\n\t\t\ttt.field.Key = \"k\"\n\t\t\ttt.field.AddTo(enc)\n\t\t\tassert.Equal(t, tt.expected, enc.Fields[\"k\"], \"unexpected map contents\")\n\t\t\tassert.Len(t, enc.Fields, 1, \"found extra keys in map: %v\", enc.Fields)\n\n\t\t\tassertCanBeReused(t, tt.field)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "flag.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"flag\"\n\n\t\"go.uber.org/zap/zapcore\"\n)\n\n// LevelFlag uses the standard library's flag.Var to declare a global flag\n// with the specified name, default, and usage guidance. The returned value is\n// a pointer to the value of the flag.\n//\n// If you don't want to use the flag package's global state, you can use any\n// non-nil *Level as a flag.Value with your own *flag.FlagSet.\nfunc LevelFlag(name string, defaultLevel zapcore.Level, usage string) *zapcore.Level {\n\tlvl := defaultLevel\n\tflag.Var(&lvl, name, usage)\n\treturn &lvl\n}\n"
  },
  {
    "path": "flag_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"flag\"\n\t\"io\"\n\t\"testing\"\n\n\t\"go.uber.org/zap/zapcore\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\ntype flagTestCase struct {\n\targs      []string\n\twantLevel zapcore.Level\n\twantErr   bool\n}\n\nfunc (tc flagTestCase) runImplicitSet(t testing.TB) {\n\torigCommandLine := flag.CommandLine\n\tflag.CommandLine = flag.NewFlagSet(\"test\", flag.ContinueOnError)\n\tflag.CommandLine.SetOutput(io.Discard)\n\tdefer func() { flag.CommandLine = origCommandLine }()\n\n\tlevel := LevelFlag(\"level\", InfoLevel, \"\")\n\ttc.run(t, flag.CommandLine, level)\n}\n\nfunc (tc flagTestCase) runExplicitSet(t testing.TB) {\n\tvar lvl zapcore.Level\n\tset := flag.NewFlagSet(\"test\", flag.ContinueOnError)\n\tset.SetOutput(io.Discard)\n\tset.Var(&lvl, \"level\", \"minimum enabled logging level\")\n\ttc.run(t, set, &lvl)\n}\n\nfunc (tc flagTestCase) run(t testing.TB, set *flag.FlagSet, actual *zapcore.Level) {\n\terr := set.Parse(tc.args)\n\tif tc.wantErr {\n\t\tassert.Error(t, err, \"Parse(%v) should fail.\", tc.args)\n\t\treturn\n\t}\n\tif assert.NoError(t, err, \"Parse(%v) should succeed.\", tc.args) {\n\t\tassert.Equal(t, tc.wantLevel, *actual, \"Level mismatch.\")\n\t}\n}\n\nfunc TestLevelFlag(t *testing.T) {\n\ttests := []flagTestCase{\n\t\t{\n\t\t\targs:      nil,\n\t\t\twantLevel: zapcore.InfoLevel,\n\t\t},\n\t\t{\n\t\t\targs:    []string{\"--level\", \"unknown\"},\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\targs:      []string{\"--level\", \"error\"},\n\t\t\twantLevel: zapcore.ErrorLevel,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\ttt.runExplicitSet(t)\n\t\ttt.runImplicitSet(t)\n\t}\n}\n\nfunc TestLevelFlagsAreIndependent(t *testing.T) {\n\torigCommandLine := flag.CommandLine\n\tflag.CommandLine = flag.NewFlagSet(\"test\", flag.ContinueOnError)\n\tflag.CommandLine.SetOutput(io.Discard)\n\tdefer func() { flag.CommandLine = origCommandLine }()\n\n\t// Make sure that these two flags are independent.\n\tfileLevel := LevelFlag(\"file-level\", InfoLevel, \"\")\n\tconsoleLevel := LevelFlag(\"console-level\", InfoLevel, \"\")\n\n\tassert.NoError(t, flag.CommandLine.Parse([]string{\"-file-level\", \"debug\"}), \"Unexpected flag-parsing error.\")\n\tassert.Equal(t, InfoLevel, *consoleLevel, \"Expected file logging level to remain unchanged.\")\n\tassert.Equal(t, DebugLevel, *fileLevel, \"Expected console logging level to have changed.\")\n}\n"
  },
  {
    "path": "glide.yaml",
    "content": "package: go.uber.org/zap\nlicense: MIT\nimport:\n- package: go.uber.org/atomic\n  version: ^1\n- package: go.uber.org/multierr\n  version: ^1\ntestImport:\n- package: github.com/satori/go.uuid\n- package: github.com/sirupsen/logrus\n- package: github.com/apex/log\n  subpackages:\n  - handlers/json\n- package: github.com/go-kit/kit\n  subpackages:\n  - log\n- package: github.com/stretchr/testify\n  subpackages:\n  - assert\n  - require\n- package: gopkg.in/inconshreveable/log15.v2\n- package: github.com/mattn/goveralls\n- package: github.com/pborman/uuid\n- package: github.com/pkg/errors\n- package: github.com/rs/zerolog\n- package: golang.org/x/tools\n  subpackages:\n  - cover\n- package: golang.org/x/lint\n  subpackages:\n  - golint\n- package: github.com/axw/gocov\n  subpackages:\n  - gocov\n"
  },
  {
    "path": "global.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"sync\"\n\n\t\"go.uber.org/zap/zapcore\"\n)\n\nconst (\n\t_stdLogDefaultDepth      = 1\n\t_loggerWriterDepth       = 2\n\t_programmerErrorTemplate = \"You've found a bug in zap! Please file a bug at \" +\n\t\t\"https://github.com/uber-go/zap/issues/new and reference this error: %v\"\n)\n\nvar (\n\t_globalMu sync.RWMutex\n\t_globalL  = NewNop()\n\t_globalS  = _globalL.Sugar()\n)\n\n// L returns the global Logger, which can be reconfigured with ReplaceGlobals.\n// It's safe for concurrent use.\nfunc L() *Logger {\n\t_globalMu.RLock()\n\tl := _globalL\n\t_globalMu.RUnlock()\n\treturn l\n}\n\n// S returns the global SugaredLogger, which can be reconfigured with\n// ReplaceGlobals. It's safe for concurrent use.\nfunc S() *SugaredLogger {\n\t_globalMu.RLock()\n\ts := _globalS\n\t_globalMu.RUnlock()\n\treturn s\n}\n\n// ReplaceGlobals replaces the global Logger and SugaredLogger, and returns a\n// function to restore the original values. It's safe for concurrent use.\nfunc ReplaceGlobals(logger *Logger) func() {\n\t_globalMu.Lock()\n\tprev := _globalL\n\t_globalL = logger\n\t_globalS = logger.Sugar()\n\t_globalMu.Unlock()\n\treturn func() { ReplaceGlobals(prev) }\n}\n\n// NewStdLog returns a *log.Logger which writes to the supplied zap Logger at\n// InfoLevel. To redirect the standard library's package-global logging\n// functions, use RedirectStdLog instead.\nfunc NewStdLog(l *Logger) *log.Logger {\n\tlogger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth))\n\tf := logger.Info\n\treturn log.New(&loggerWriter{f}, \"\" /* prefix */, 0 /* flags */)\n}\n\n// NewStdLogAt returns *log.Logger which writes to supplied zap logger at\n// required level.\nfunc NewStdLogAt(l *Logger, level zapcore.Level) (*log.Logger, error) {\n\tlogger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth))\n\tlogFunc, err := levelToFunc(logger, level)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn log.New(&loggerWriter{logFunc}, \"\" /* prefix */, 0 /* flags */), nil\n}\n\n// RedirectStdLog redirects output from the standard library's package-global\n// logger to the supplied logger at InfoLevel. Since zap already handles caller\n// annotations, timestamps, etc., it automatically disables the standard\n// library's annotations and prefixing.\n//\n// It returns a function to restore the original prefix and flags and reset the\n// standard library's output to os.Stderr.\nfunc RedirectStdLog(l *Logger) func() {\n\tf, err := redirectStdLogAt(l, InfoLevel)\n\tif err != nil {\n\t\t// Can't get here, since passing InfoLevel to redirectStdLogAt always\n\t\t// works.\n\t\tpanic(fmt.Sprintf(_programmerErrorTemplate, err))\n\t}\n\treturn f\n}\n\n// RedirectStdLogAt redirects output from the standard library's package-global\n// logger to the supplied logger at the specified level. Since zap already\n// handles caller annotations, timestamps, etc., it automatically disables the\n// standard library's annotations and prefixing.\n//\n// It returns a function to restore the original prefix and flags and reset the\n// standard library's output to os.Stderr.\nfunc RedirectStdLogAt(l *Logger, level zapcore.Level) (func(), error) {\n\treturn redirectStdLogAt(l, level)\n}\n\nfunc redirectStdLogAt(l *Logger, level zapcore.Level) (func(), error) {\n\tflags := log.Flags()\n\tprefix := log.Prefix()\n\tlog.SetFlags(0)\n\tlog.SetPrefix(\"\")\n\tlogger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth))\n\tlogFunc, err := levelToFunc(logger, level)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tlog.SetOutput(&loggerWriter{logFunc})\n\treturn func() {\n\t\tlog.SetFlags(flags)\n\t\tlog.SetPrefix(prefix)\n\t\tlog.SetOutput(os.Stderr)\n\t}, nil\n}\n\nfunc levelToFunc(logger *Logger, lvl zapcore.Level) (func(string, ...Field), error) {\n\tswitch lvl {\n\tcase DebugLevel:\n\t\treturn logger.Debug, nil\n\tcase InfoLevel:\n\t\treturn logger.Info, nil\n\tcase WarnLevel:\n\t\treturn logger.Warn, nil\n\tcase ErrorLevel:\n\t\treturn logger.Error, nil\n\tcase DPanicLevel:\n\t\treturn logger.DPanic, nil\n\tcase PanicLevel:\n\t\treturn logger.Panic, nil\n\tcase FatalLevel:\n\t\treturn logger.Fatal, nil\n\t}\n\treturn nil, fmt.Errorf(\"unrecognized level: %q\", lvl)\n}\n\ntype loggerWriter struct {\n\tlogFunc func(msg string, fields ...Field)\n}\n\nfunc (l *loggerWriter) Write(p []byte) (int, error) {\n\tp = bytes.TrimSpace(p)\n\tl.logFunc(string(p))\n\treturn len(p), nil\n}\n"
  },
  {
    "path": "global_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"log\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.uber.org/zap/internal/exit\"\n\t\"go.uber.org/zap/internal/ztest\"\n\n\t\"go.uber.org/zap/zapcore\"\n\t\"go.uber.org/zap/zaptest/observer\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestReplaceGlobals(t *testing.T) {\n\tinitialL := *L()\n\tinitialS := *S()\n\n\twithLogger(t, DebugLevel, nil, func(l *Logger, logs *observer.ObservedLogs) {\n\t\tL().Info(\"no-op\")\n\t\tS().Info(\"no-op\")\n\t\tassert.Equal(t, 0, logs.Len(), \"Expected initial logs to go to default no-op global.\")\n\n\t\tdefer ReplaceGlobals(l)()\n\n\t\tL().Info(\"captured\")\n\t\tS().Info(\"captured\")\n\t\texpected := observer.LoggedEntry{\n\t\t\tEntry:   zapcore.Entry{Message: \"captured\"},\n\t\t\tContext: []Field{},\n\t\t}\n\t\tassert.Equal(\n\t\t\tt,\n\t\t\t[]observer.LoggedEntry{expected, expected},\n\t\t\tlogs.AllUntimed(),\n\t\t\t\"Unexpected global log output.\",\n\t\t)\n\t})\n\n\tassert.Equal(t, initialL, *L(), \"Expected func returned from ReplaceGlobals to restore initial L.\")\n\tassert.Equal(t, initialS, *S(), \"Expected func returned from ReplaceGlobals to restore initial S.\")\n}\n\nfunc TestGlobalsConcurrentUse(t *testing.T) {\n\tvar (\n\t\tstop atomic.Bool\n\t\twg   sync.WaitGroup\n\t)\n\n\tfor i := 0; i < 100; i++ {\n\t\twg.Add(2)\n\t\tgo func() {\n\t\t\tfor !stop.Load() {\n\t\t\t\tReplaceGlobals(NewNop())\n\t\t\t}\n\t\t\twg.Done()\n\t\t}()\n\t\tgo func() {\n\t\t\tfor !stop.Load() {\n\t\t\t\tL().With(Int(\"foo\", 42)).Named(\"main\").WithOptions(Development()).Info(\"\")\n\t\t\t\tS().Info(\"\")\n\t\t\t}\n\t\t\twg.Done()\n\t\t}()\n\t}\n\n\tztest.Sleep(100 * time.Millisecond)\n\t// CAS loop to toggle the current value.\n\tfor old := stop.Load(); !stop.CompareAndSwap(old, !old); {\n\t\told = stop.Load()\n\t}\n\twg.Wait()\n}\n\nfunc TestNewStdLog(t *testing.T) {\n\twithLogger(t, DebugLevel, []Option{AddCaller()}, func(l *Logger, logs *observer.ObservedLogs) {\n\t\tstd := NewStdLog(l)\n\t\tstd.Print(\"redirected\")\n\t\tcheckStdLogMessage(t, \"redirected\", logs)\n\t})\n}\n\nfunc TestNewStdLogAt(t *testing.T) {\n\t// include DPanicLevel here, but do not include Development in options\n\tlevels := []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel}\n\tfor _, level := range levels {\n\t\twithLogger(t, DebugLevel, []Option{AddCaller()}, func(l *Logger, logs *observer.ObservedLogs) {\n\t\t\tstd, err := NewStdLogAt(l, level)\n\t\t\trequire.NoError(t, err, \"Unexpected error.\")\n\t\t\tstd.Print(\"redirected\")\n\t\t\tcheckStdLogMessage(t, \"redirected\", logs)\n\t\t})\n\t}\n}\n\nfunc TestNewStdLogAtPanics(t *testing.T) {\n\t// include DPanicLevel here and enable Development in options\n\tlevels := []zapcore.Level{DPanicLevel, PanicLevel}\n\tfor _, level := range levels {\n\t\twithLogger(t, DebugLevel, []Option{AddCaller(), Development()}, func(l *Logger, logs *observer.ObservedLogs) {\n\t\t\tstd, err := NewStdLogAt(l, level)\n\t\t\trequire.NoError(t, err, \"Unexpected error\")\n\t\t\tassert.Panics(t, func() { std.Print(\"redirected\") }, \"Expected log to panic.\")\n\t\t\tcheckStdLogMessage(t, \"redirected\", logs)\n\t\t})\n\t}\n}\n\nfunc TestNewStdLogAtFatal(t *testing.T) {\n\twithLogger(t, DebugLevel, []Option{AddCaller()}, func(l *Logger, logs *observer.ObservedLogs) {\n\t\tstub := exit.WithStub(func() {\n\t\t\tstd, err := NewStdLogAt(l, FatalLevel)\n\t\t\trequire.NoError(t, err, \"Unexpected error.\")\n\t\t\tstd.Print(\"redirected\")\n\t\t\tcheckStdLogMessage(t, \"redirected\", logs)\n\t\t})\n\t\tassert.True(t, true, stub.Exited, \"Expected Fatal logger call to terminate process.\")\n\t\tstub.Unstub()\n\t})\n}\n\nfunc TestNewStdLogAtInvalid(t *testing.T) {\n\t_, err := NewStdLogAt(NewNop(), zapcore.Level(99))\n\tassert.ErrorContains(t, err, \"99\", \"Expected level code in error message\")\n}\n\nfunc TestRedirectStdLog(t *testing.T) {\n\tinitialFlags := log.Flags()\n\tinitialPrefix := log.Prefix()\n\n\twithLogger(t, DebugLevel, nil, func(l *Logger, logs *observer.ObservedLogs) {\n\t\tdefer RedirectStdLog(l)()\n\t\tlog.Print(\"redirected\")\n\n\t\tassert.Equal(t, []observer.LoggedEntry{{\n\t\t\tEntry:   zapcore.Entry{Message: \"redirected\"},\n\t\t\tContext: []Field{},\n\t\t}}, logs.AllUntimed(), \"Unexpected global log output.\")\n\t})\n\n\tassert.Equal(t, initialFlags, log.Flags(), \"Expected to reset initial flags.\")\n\tassert.Equal(t, initialPrefix, log.Prefix(), \"Expected to reset initial prefix.\")\n}\n\nfunc TestRedirectStdLogCaller(t *testing.T) {\n\twithLogger(t, DebugLevel, []Option{AddCaller()}, func(l *Logger, logs *observer.ObservedLogs) {\n\t\tdefer RedirectStdLog(l)()\n\t\tlog.Print(\"redirected\")\n\t\tentries := logs.All()\n\t\trequire.Len(t, entries, 1, \"Unexpected number of logs.\")\n\t\tassert.Contains(t, entries[0].Caller.File, \"global_test.go\", \"Unexpected caller annotation.\")\n\t})\n}\n\nfunc TestRedirectStdLogAt(t *testing.T) {\n\tinitialFlags := log.Flags()\n\tinitialPrefix := log.Prefix()\n\n\t// include DPanicLevel here, but do not include Development in options\n\tlevels := []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel}\n\tfor _, level := range levels {\n\t\twithLogger(t, DebugLevel, nil, func(l *Logger, logs *observer.ObservedLogs) {\n\t\t\trestore, err := RedirectStdLogAt(l, level)\n\t\t\trequire.NoError(t, err, \"Unexpected error.\")\n\t\t\tdefer restore()\n\t\t\tlog.Print(\"redirected\")\n\n\t\t\tassert.Equal(t, []observer.LoggedEntry{{\n\t\t\t\tEntry:   zapcore.Entry{Level: level, Message: \"redirected\"},\n\t\t\t\tContext: []Field{},\n\t\t\t}}, logs.AllUntimed(), \"Unexpected global log output.\")\n\t\t})\n\t}\n\n\tassert.Equal(t, initialFlags, log.Flags(), \"Expected to reset initial flags.\")\n\tassert.Equal(t, initialPrefix, log.Prefix(), \"Expected to reset initial prefix.\")\n}\n\nfunc TestRedirectStdLogAtCaller(t *testing.T) {\n\t// include DPanicLevel here, but do not include Development in options\n\tlevels := []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel}\n\tfor _, level := range levels {\n\t\twithLogger(t, DebugLevel, []Option{AddCaller()}, func(l *Logger, logs *observer.ObservedLogs) {\n\t\t\trestore, err := RedirectStdLogAt(l, level)\n\t\t\trequire.NoError(t, err, \"Unexpected error.\")\n\t\t\tdefer restore()\n\t\t\tlog.Print(\"redirected\")\n\t\t\tentries := logs.All()\n\t\t\trequire.Len(t, entries, 1, \"Unexpected number of logs.\")\n\t\t\tassert.Contains(t, entries[0].Caller.File, \"global_test.go\", \"Unexpected caller annotation.\")\n\t\t})\n\t}\n}\n\nfunc TestRedirectStdLogAtPanics(t *testing.T) {\n\tinitialFlags := log.Flags()\n\tinitialPrefix := log.Prefix()\n\n\t// include DPanicLevel here and enable Development in options\n\tlevels := []zapcore.Level{DPanicLevel, PanicLevel}\n\tfor _, level := range levels {\n\t\twithLogger(t, DebugLevel, []Option{AddCaller(), Development()}, func(l *Logger, logs *observer.ObservedLogs) {\n\t\t\trestore, err := RedirectStdLogAt(l, level)\n\t\t\trequire.NoError(t, err, \"Unexpected error.\")\n\t\t\tdefer restore()\n\t\t\tassert.Panics(t, func() { log.Print(\"redirected\") }, \"Expected log to panic.\")\n\t\t\tcheckStdLogMessage(t, \"redirected\", logs)\n\t\t})\n\t}\n\n\tassert.Equal(t, initialFlags, log.Flags(), \"Expected to reset initial flags.\")\n\tassert.Equal(t, initialPrefix, log.Prefix(), \"Expected to reset initial prefix.\")\n}\n\nfunc TestRedirectStdLogAtFatal(t *testing.T) {\n\tinitialFlags := log.Flags()\n\tinitialPrefix := log.Prefix()\n\n\twithLogger(t, DebugLevel, []Option{AddCaller()}, func(l *Logger, logs *observer.ObservedLogs) {\n\t\tstub := exit.WithStub(func() {\n\t\t\trestore, err := RedirectStdLogAt(l, FatalLevel)\n\t\t\trequire.NoError(t, err, \"Unexpected error.\")\n\t\t\tdefer restore()\n\t\t\tlog.Print(\"redirected\")\n\t\t\tcheckStdLogMessage(t, \"redirected\", logs)\n\t\t})\n\t\tassert.True(t, true, stub.Exited, \"Expected Fatal logger call to terminate process.\")\n\t\tstub.Unstub()\n\t})\n\n\tassert.Equal(t, initialFlags, log.Flags(), \"Expected to reset initial flags.\")\n\tassert.Equal(t, initialPrefix, log.Prefix(), \"Expected to reset initial prefix.\")\n}\n\nfunc TestRedirectStdLogAtInvalid(t *testing.T) {\n\trestore, err := RedirectStdLogAt(NewNop(), zapcore.Level(99))\n\tdefer func() {\n\t\tif restore != nil {\n\t\t\trestore()\n\t\t}\n\t}()\n\tassert.ErrorContains(t, err, \"99\", \"Expected level code in error message\")\n}\n\nfunc checkStdLogMessage(t *testing.T, msg string, logs *observer.ObservedLogs) {\n\trequire.Equal(t, 1, logs.Len(), \"Expected exactly one entry to be logged\")\n\tentry := logs.AllUntimed()[0]\n\tassert.Equal(t, []Field{}, entry.Context, \"Unexpected entry context.\")\n\tassert.Equal(t, \"redirected\", entry.Message, \"Unexpected entry message.\")\n\tassert.Regexp(\n\t\tt,\n\t\t`/global_test.go:\\d+$`,\n\t\tentry.Caller.String(),\n\t\t\"Unexpected caller annotation.\",\n\t)\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module go.uber.org/zap\n\ngo 1.19\n\nrequire (\n\tgithub.com/stretchr/testify v1.8.1\n\tgo.uber.org/goleak v1.3.0\n\tgo.uber.org/multierr v1.10.0\n\tgo.yaml.in/yaml/v3 v3.0.4\n)\n\nrequire (\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/kr/text v0.2.0 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngo.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=\ngo.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=\ngo.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=\ngo.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=\ngo.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=\ngo.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "http_handler.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"go.uber.org/zap/zapcore\"\n)\n\n// ServeHTTP is a simple JSON endpoint that can report on or change the current\n// logging level.\n//\n// # GET\n//\n// The GET request returns a JSON description of the current logging level like:\n//\n//\t{\"level\":\"info\"}\n//\n// # PUT\n//\n// The PUT request changes the logging level. It is perfectly safe to change the\n// logging level while a program is running. Two content types are supported:\n//\n//\tContent-Type: application/x-www-form-urlencoded\n//\n// With this content type, the level can be provided through the request body or\n// a query parameter. The log level is URL encoded like:\n//\n//\tlevel=debug\n//\n// The request body takes precedence over the query parameter, if both are\n// specified.\n//\n// This content type is the default for a curl PUT request. Following are two\n// example curl requests that both set the logging level to debug.\n//\n//\tcurl -X PUT localhost:8080/log/level?level=debug\n//\tcurl -X PUT localhost:8080/log/level -d level=debug\n//\n// For any other content type, the payload is expected to be JSON encoded and\n// look like:\n//\n//\t{\"level\":\"info\"}\n//\n// An example curl request could look like this:\n//\n//\tcurl -X PUT localhost:8080/log/level -H \"Content-Type: application/json\" -d '{\"level\":\"debug\"}'\nfunc (lvl AtomicLevel) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tif err := lvl.serveHTTP(w, r); err != nil {\n\t\tw.WriteHeader(http.StatusInternalServerError)\n\t\t_, _ = fmt.Fprintf(w, \"internal error: %v\", err)\n\t}\n}\n\nfunc (lvl AtomicLevel) serveHTTP(w http.ResponseWriter, r *http.Request) error {\n\ttype errorResponse struct {\n\t\tError string `json:\"error\"`\n\t}\n\ttype payload struct {\n\t\tLevel zapcore.Level `json:\"level\"`\n\t}\n\n\tenc := json.NewEncoder(w)\n\n\tswitch r.Method {\n\tcase http.MethodGet:\n\t\treturn enc.Encode(payload{Level: lvl.Level()})\n\n\tcase http.MethodPut:\n\t\trequestedLvl, err := decodePutRequest(r.Header.Get(\"Content-Type\"), r)\n\t\tif err != nil {\n\t\t\tw.WriteHeader(http.StatusBadRequest)\n\t\t\treturn enc.Encode(errorResponse{Error: err.Error()})\n\t\t}\n\t\tlvl.SetLevel(requestedLvl)\n\t\treturn enc.Encode(payload{Level: lvl.Level()})\n\n\tdefault:\n\t\tw.WriteHeader(http.StatusMethodNotAllowed)\n\t\treturn enc.Encode(errorResponse{\n\t\t\tError: \"Only GET and PUT are supported.\",\n\t\t})\n\t}\n}\n\n// Decodes incoming PUT requests and returns the requested logging level.\nfunc decodePutRequest(contentType string, r *http.Request) (zapcore.Level, error) {\n\tif contentType == \"application/x-www-form-urlencoded\" {\n\t\treturn decodePutURL(r)\n\t}\n\treturn decodePutJSON(r.Body)\n}\n\nfunc decodePutURL(r *http.Request) (zapcore.Level, error) {\n\tlvl := r.FormValue(\"level\")\n\tif lvl == \"\" {\n\t\treturn 0, errors.New(\"must specify logging level\")\n\t}\n\tvar l zapcore.Level\n\tif err := l.UnmarshalText([]byte(lvl)); err != nil {\n\t\treturn 0, err\n\t}\n\treturn l, nil\n}\n\nfunc decodePutJSON(body io.Reader) (zapcore.Level, error) {\n\tvar pld struct {\n\t\tLevel *zapcore.Level `json:\"level\"`\n\t}\n\tif err := json.NewDecoder(body).Decode(&pld); err != nil {\n\t\treturn 0, fmt.Errorf(\"malformed request body: %v\", err)\n\t}\n\tif pld.Level == nil {\n\t\treturn 0, errors.New(\"must specify logging level\")\n\t}\n\treturn *pld.Level, nil\n}\n"
  },
  {
    "path": "http_handler_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap_test\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestAtomicLevelServeHTTP(t *testing.T) {\n\ttests := []struct {\n\t\tdesc          string\n\t\tmethod        string\n\t\tquery         string\n\t\tcontentType   string\n\t\tbody          string\n\t\texpectedCode  int\n\t\texpectedLevel zapcore.Level\n\t}{\n\t\t{\n\t\t\tdesc:          \"GET\",\n\t\t\tmethod:        http.MethodGet,\n\t\t\texpectedCode:  http.StatusOK,\n\t\t\texpectedLevel: zap.InfoLevel,\n\t\t},\n\t\t{\n\t\t\tdesc:          \"PUT JSON\",\n\t\t\tmethod:        http.MethodPut,\n\t\t\texpectedCode:  http.StatusOK,\n\t\t\texpectedLevel: zap.WarnLevel,\n\t\t\tbody:          `{\"level\":\"warn\"}`,\n\t\t},\n\t\t{\n\t\t\tdesc:          \"PUT URL encoded\",\n\t\t\tmethod:        http.MethodPut,\n\t\t\texpectedCode:  http.StatusOK,\n\t\t\texpectedLevel: zap.WarnLevel,\n\t\t\tcontentType:   \"application/x-www-form-urlencoded\",\n\t\t\tbody:          \"level=warn\",\n\t\t},\n\t\t{\n\t\t\tdesc:          \"PUT query parameters\",\n\t\t\tmethod:        http.MethodPut,\n\t\t\tquery:         \"?level=warn\",\n\t\t\texpectedCode:  http.StatusOK,\n\t\t\texpectedLevel: zap.WarnLevel,\n\t\t\tcontentType:   \"application/x-www-form-urlencoded\",\n\t\t},\n\t\t{\n\t\t\tdesc:          \"body takes precedence over query\",\n\t\t\tmethod:        http.MethodPut,\n\t\t\tquery:         \"?level=info\",\n\t\t\texpectedCode:  http.StatusOK,\n\t\t\texpectedLevel: zap.WarnLevel,\n\t\t\tcontentType:   \"application/x-www-form-urlencoded\",\n\t\t\tbody:          \"level=warn\",\n\t\t},\n\t\t{\n\t\t\tdesc:          \"JSON ignores query\",\n\t\t\tmethod:        http.MethodPut,\n\t\t\tquery:         \"?level=info\",\n\t\t\texpectedCode:  http.StatusOK,\n\t\t\texpectedLevel: zap.WarnLevel,\n\t\t\tbody:          `{\"level\":\"warn\"}`,\n\t\t},\n\t\t{\n\t\t\tdesc:         \"PUT JSON unrecognized\",\n\t\t\tmethod:       http.MethodPut,\n\t\t\texpectedCode: http.StatusBadRequest,\n\t\t\tbody:         `{\"level\":\"unrecognized\"}`,\n\t\t},\n\t\t{\n\t\t\tdesc:         \"PUT URL encoded unrecognized\",\n\t\t\tmethod:       http.MethodPut,\n\t\t\texpectedCode: http.StatusBadRequest,\n\t\t\tcontentType:  \"application/x-www-form-urlencoded\",\n\t\t\tbody:         \"level=unrecognized\",\n\t\t},\n\t\t{\n\t\t\tdesc:         \"PUT JSON malformed\",\n\t\t\tmethod:       http.MethodPut,\n\t\t\texpectedCode: http.StatusBadRequest,\n\t\t\tbody:         `{\"level\":\"warn`,\n\t\t},\n\t\t{\n\t\t\tdesc:         \"PUT URL encoded malformed\",\n\t\t\tmethod:       http.MethodPut,\n\t\t\tquery:        \"?level=%\",\n\t\t\texpectedCode: http.StatusBadRequest,\n\t\t\tcontentType:  \"application/x-www-form-urlencoded\",\n\t\t},\n\t\t{\n\t\t\tdesc:         \"PUT Query parameters malformed\",\n\t\t\tmethod:       http.MethodPut,\n\t\t\texpectedCode: http.StatusBadRequest,\n\t\t\tcontentType:  \"application/x-www-form-urlencoded\",\n\t\t\tbody:         \"level=%\",\n\t\t},\n\t\t{\n\t\t\tdesc:         \"PUT JSON unspecified\",\n\t\t\tmethod:       http.MethodPut,\n\t\t\texpectedCode: http.StatusBadRequest,\n\t\t\tbody:         `{}`,\n\t\t},\n\t\t{\n\t\t\tdesc:         \"PUT URL encoded unspecified\",\n\t\t\tmethod:       http.MethodPut,\n\t\t\texpectedCode: http.StatusBadRequest,\n\t\t\tcontentType:  \"application/x-www-form-urlencoded\",\n\t\t\tbody:         \"\",\n\t\t},\n\t\t{\n\t\t\tdesc:         \"POST JSON\",\n\t\t\tmethod:       http.MethodPost,\n\t\t\texpectedCode: http.StatusMethodNotAllowed,\n\t\t\tbody:         `{\"level\":\"warn\"}`,\n\t\t},\n\t\t{\n\t\t\tdesc:         \"POST URL\",\n\t\t\tmethod:       http.MethodPost,\n\t\t\texpectedCode: http.StatusMethodNotAllowed,\n\t\t\tcontentType:  \"application/x-www-form-urlencoded\",\n\t\t\tbody:         \"level=warn\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tlvl := zap.NewAtomicLevel()\n\t\t\tlvl.SetLevel(zapcore.InfoLevel)\n\n\t\t\tserver := httptest.NewServer(lvl)\n\t\t\tdefer server.Close()\n\n\t\t\treq, err := http.NewRequest(tt.method, server.URL+tt.query, strings.NewReader(tt.body))\n\t\t\trequire.NoError(t, err, \"Error constructing %s request.\", req.Method)\n\t\t\tif tt.contentType != \"\" {\n\t\t\t\treq.Header.Set(\"Content-Type\", tt.contentType)\n\t\t\t}\n\n\t\t\tres, err := http.DefaultClient.Do(req)\n\t\t\trequire.NoError(t, err, \"Error making %s request.\", req.Method)\n\t\t\tdefer func() {\n\t\t\t\tassert.NoError(t, res.Body.Close(), \"Error closing response body.\")\n\t\t\t}()\n\n\t\t\trequire.Equal(t, tt.expectedCode, res.StatusCode, \"Unexpected status code.\")\n\t\t\tif tt.expectedCode != http.StatusOK {\n\t\t\t\t// Don't need to test exact error message, but one should be present.\n\t\t\t\tvar pld struct {\n\t\t\t\t\tError string `json:\"error\"`\n\t\t\t\t}\n\t\t\t\trequire.NoError(t, json.NewDecoder(res.Body).Decode(&pld), \"Decoding response body\")\n\t\t\t\tassert.NotEmpty(t, pld.Error, \"Expected an error message\")\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tvar pld struct {\n\t\t\t\tLevel zapcore.Level `json:\"level\"`\n\t\t\t}\n\t\t\trequire.NoError(t, json.NewDecoder(res.Body).Decode(&pld), \"Decoding response body\")\n\t\t\tassert.Equal(t, tt.expectedLevel, pld.Level, \"Unexpected logging level returned\")\n\t\t})\n\t}\n}\n\nfunc TestAtomicLevelServeHTTPBrokenWriter(t *testing.T) {\n\tt.Parallel()\n\n\tlvl := zap.NewAtomicLevel()\n\n\trequest, err := http.NewRequest(http.MethodGet, \"http://localhost:1234/log/level\", nil)\n\trequire.NoError(t, err, \"Error constructing request.\")\n\n\trecorder := httptest.NewRecorder()\n\tlvl.ServeHTTP(&brokenHTTPResponseWriter{\n\t\tResponseWriter: recorder,\n\t}, request)\n\n\tassert.Equal(t, http.StatusInternalServerError, recorder.Code, \"Unexpected status code.\")\n}\n\ntype brokenHTTPResponseWriter struct {\n\thttp.ResponseWriter\n}\n\nfunc (w *brokenHTTPResponseWriter) Write([]byte) (int, error) {\n\treturn 0, errors.New(\"great sadness\")\n}\n\nfunc TestAtomicLevelServeHTTPBadLevel(t *testing.T) {\n\tsrv := httptest.NewServer(zap.NewAtomicLevel())\n\tdefer srv.Close()\n\n\treq, err := http.NewRequest(http.MethodPut, srv.URL, strings.NewReader(`{\"level\":\"<script>alert(\\\"malicious\\\")</script>\"}`))\n\trequire.NoError(t, err, \"Error constructing request.\")\n\n\tres, err := http.DefaultClient.Do(req)\n\trequire.NoError(t, err, \"Error making request.\")\n\tdefer func() {\n\t\tassert.NoError(t, res.Body.Close(), \"Error closing response body.\")\n\t}()\n\n\tassert.Equal(t, http.StatusBadRequest, res.StatusCode, \"Unexpected status code.\")\n\tresBody, err := io.ReadAll(res.Body)\n\trequire.NoError(t, err, \"Error reading response body.\")\n\n\tassert.Contains(t, string(resBody), \"unrecognized level\", \"Unexpected error message.\")\n\tassert.NotContains(t, string(resBody), \"<script>\", \"Unexpected error message.\")\n}\n\nfunc FuzzAtomicLevelServeHTTP(f *testing.F) {\n\tf.Add(`{\"level\":\"info\"}`)\n\tf.Add(`{\"level\":\"warn\"}`)\n\tf.Add(`{\"level\":\"<script>alert(\\\"malicious\\\")</script>\"}`)\n\tf.Fuzz(func(t *testing.T, input string) {\n\t\tlvl := zap.NewAtomicLevel()\n\n\t\tresw := httptest.NewRecorder()\n\t\treq, err := http.NewRequest(http.MethodPut, \"http://localhost:9999/log/level\", strings.NewReader(input))\n\t\trequire.NoError(t, err, \"Error constructing request.\")\n\n\t\tlvl.ServeHTTP(resw, req)\n\n\t\trequire.NotEqual(t, http.StatusInternalServerError, resw.Code, \"Unexpected status code.\")\n\n\t\t// Response body must never contain HTML tags.\n\t\tassert.NotRegexp(t, `<[^>]+>`, resw.Body.String(), \"Unexpected HTML tag in response body.\")\n\t})\n}\n"
  },
  {
    "path": "increase_level_test.go",
    "content": "// Copyright (c) 2020 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/zap/zapcore\"\n\t\"go.uber.org/zap/zaptest/observer\"\n)\n\nfunc newLoggedEntry(level zapcore.Level, msg string, fields ...zapcore.Field) observer.LoggedEntry {\n\tif len(fields) == 0 {\n\t\tfields = []zapcore.Field{}\n\t}\n\treturn observer.LoggedEntry{\n\t\tEntry:   zapcore.Entry{Level: level, Message: msg},\n\t\tContext: fields,\n\t}\n}\n\nfunc TestIncreaseLevelTryDecrease(t *testing.T) {\n\terrorOut := &bytes.Buffer{}\n\topts := []Option{\n\t\tErrorOutput(zapcore.AddSync(errorOut)),\n\t}\n\twithLogger(t, WarnLevel, opts, func(logger *Logger, logs *observer.ObservedLogs) {\n\t\tlogger.Warn(\"original warn log\")\n\n\t\tdebugLogger := logger.WithOptions(IncreaseLevel(DebugLevel))\n\t\tdebugLogger.Debug(\"ignored debug log\")\n\t\tdebugLogger.Warn(\"increase level warn log\")\n\t\tdebugLogger.Error(\"increase level error log\")\n\n\t\tassert.Equal(t, []observer.LoggedEntry{\n\t\t\tnewLoggedEntry(WarnLevel, \"original warn log\"),\n\t\t\tnewLoggedEntry(WarnLevel, \"increase level warn log\"),\n\t\t\tnewLoggedEntry(ErrorLevel, \"increase level error log\"),\n\t\t}, logs.AllUntimed(), \"unexpected logs\")\n\t\tassert.Equal(t,\n\t\t\t\"failed to IncreaseLevel: invalid increase level, as level \\\"info\\\" is allowed by increased level, but not by existing core\\n\",\n\t\t\terrorOut.String(),\n\t\t\t\"unexpected error output\",\n\t\t)\n\t})\n}\n\nfunc TestIncreaseLevel(t *testing.T) {\n\terrorOut := &bytes.Buffer{}\n\topts := []Option{\n\t\tErrorOutput(zapcore.AddSync(errorOut)),\n\t}\n\twithLogger(t, WarnLevel, opts, func(logger *Logger, logs *observer.ObservedLogs) {\n\t\tlogger.Warn(\"original warn log\")\n\n\t\terrorLogger := logger.WithOptions(IncreaseLevel(ErrorLevel))\n\t\terrorLogger.Debug(\"ignored debug log\")\n\t\terrorLogger.Warn(\"ignored warn log\")\n\t\terrorLogger.Error(\"increase level error log\")\n\n\t\twithFields := errorLogger.With(String(\"k\", \"v\"))\n\t\twithFields.Debug(\"ignored debug log with fields\")\n\t\twithFields.Warn(\"ignored warn log with fields\")\n\t\twithFields.Error(\"increase level error log with fields\")\n\n\t\tassert.Equal(t, []observer.LoggedEntry{\n\t\t\tnewLoggedEntry(WarnLevel, \"original warn log\"),\n\t\t\tnewLoggedEntry(ErrorLevel, \"increase level error log\"),\n\t\t\tnewLoggedEntry(ErrorLevel, \"increase level error log with fields\", String(\"k\", \"v\")),\n\t\t}, logs.AllUntimed(), \"unexpected logs\")\n\n\t\tassert.Empty(t, errorOut.String(), \"expect no error output\")\n\t})\n}\n"
  },
  {
    "path": "internal/bufferpool/bufferpool.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// Package bufferpool houses zap's shared internal buffer pool. Third-party\n// packages can recreate the same functionality with buffers.NewPool.\npackage bufferpool\n\nimport \"go.uber.org/zap/buffer\"\n\nvar (\n\t_pool = buffer.NewPool()\n\t// Get retrieves a buffer from the pool, creating one if necessary.\n\tGet = _pool.Get\n)\n"
  },
  {
    "path": "internal/color/color.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// Package color adds coloring functionality for TTY output.\npackage color\n\nimport \"fmt\"\n\n// Foreground colors.\nconst (\n\tBlack Color = iota + 30\n\tRed\n\tGreen\n\tYellow\n\tBlue\n\tMagenta\n\tCyan\n\tWhite\n)\n\n// Color represents a text color.\ntype Color uint8\n\n// Add adds the coloring to the given string.\nfunc (c Color) Add(s string) string {\n\treturn fmt.Sprintf(\"\\x1b[%dm%s\\x1b[0m\", uint8(c), s)\n}\n"
  },
  {
    "path": "internal/color/color_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage color\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestColorFormatting(t *testing.T) {\n\tassert.Equal(\n\t\tt,\n\t\t\"\\x1b[31mfoo\\x1b[0m\",\n\t\tRed.Add(\"foo\"),\n\t\t\"Unexpected colored output.\",\n\t)\n}\n"
  },
  {
    "path": "internal/exit/exit.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// Package exit provides stubs so that unit tests can exercise code that calls\n// os.Exit(1).\npackage exit\n\nimport \"os\"\n\nvar _exit = os.Exit\n\n// With terminates the process by calling os.Exit(code). If the package is\n// stubbed, it instead records a call in the testing spy.\nfunc With(code int) {\n\t_exit(code)\n}\n\n// A StubbedExit is a testing fake for os.Exit.\ntype StubbedExit struct {\n\tExited bool\n\tCode   int\n\tprev   func(code int)\n}\n\n// Stub substitutes a fake for the call to os.Exit(1).\nfunc Stub() *StubbedExit {\n\ts := &StubbedExit{prev: _exit}\n\t_exit = s.exit\n\treturn s\n}\n\n// WithStub runs the supplied function with Exit stubbed. It returns the stub\n// used, so that users can test whether the process would have crashed.\nfunc WithStub(f func()) *StubbedExit {\n\ts := Stub()\n\tdefer s.Unstub()\n\tf()\n\treturn s\n}\n\n// Unstub restores the previous exit function.\nfunc (se *StubbedExit) Unstub() {\n\t_exit = se.prev\n}\n\nfunc (se *StubbedExit) exit(code int) {\n\tse.Exited = true\n\tse.Code = code\n}\n"
  },
  {
    "path": "internal/exit/exit_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage exit_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/zap/internal/exit\"\n)\n\nfunc TestStub(t *testing.T) {\n\ttype want struct {\n\t\texit bool\n\t\tcode int\n\t}\n\ttests := []struct {\n\t\tf    func()\n\t\twant want\n\t}{\n\t\t{func() { exit.With(42) }, want{exit: true, code: 42}},\n\t\t{func() {}, want{}},\n\t}\n\n\tfor _, tt := range tests {\n\t\ts := exit.WithStub(tt.f)\n\t\tassert.Equal(t, tt.want.exit, s.Exited, \"Stub captured unexpected exit value.\")\n\t\tassert.Equal(t, tt.want.code, s.Code, \"Stub captured unexpected exit value.\")\n\t}\n}\n"
  },
  {
    "path": "internal/level_enabler.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// Package internal and its subpackages hold types and functionality\n// that are not part of Zap's public API.\npackage internal\n\nimport \"go.uber.org/zap/zapcore\"\n\n// LeveledEnabler is an interface satisfied by LevelEnablers that are able to\n// report their own level.\n//\n// This interface is defined to use more conveniently in tests and non-zapcore\n// packages.\n// This cannot be imported from zapcore because of the cyclic dependency.\ntype LeveledEnabler interface {\n\tzapcore.LevelEnabler\n\n\tLevel() zapcore.Level\n}\n"
  },
  {
    "path": "internal/pool/pool.go",
    "content": "// Copyright (c) 2023 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// Package pool provides internal pool utilities.\npackage pool\n\nimport (\n\t\"sync\"\n)\n\n// A Pool is a generic wrapper around [sync.Pool] to provide strongly-typed\n// object pooling.\n//\n// Note that SA6002 (ref: https://staticcheck.io/docs/checks/#SA6002) will\n// not be detected, so all internal pool use must take care to only store\n// pointer types.\ntype Pool[T any] struct {\n\tpool sync.Pool\n}\n\n// New returns a new [Pool] for T, and will use fn to construct new Ts when\n// the pool is empty.\nfunc New[T any](fn func() T) *Pool[T] {\n\treturn &Pool[T]{\n\t\tpool: sync.Pool{\n\t\t\tNew: func() any {\n\t\t\t\treturn fn()\n\t\t\t},\n\t\t},\n\t}\n}\n\n// Get gets a T from the pool, or creates a new one if the pool is empty.\nfunc (p *Pool[T]) Get() T {\n\treturn p.pool.Get().(T)\n}\n\n// Put returns x into the pool.\nfunc (p *Pool[T]) Put(x T) {\n\tp.pool.Put(x)\n}\n"
  },
  {
    "path": "internal/pool/pool_test.go",
    "content": "// Copyright (c) 2023 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage pool_test\n\nimport (\n\t\"runtime/debug\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/zap/internal/pool\"\n)\n\ntype pooledValue[T any] struct {\n\tvalue T\n}\n\nfunc TestNew(t *testing.T) {\n\t// Disable GC to avoid the victim cache during the test.\n\tdefer debug.SetGCPercent(debug.SetGCPercent(-1))\n\n\tp := pool.New(func() *pooledValue[string] {\n\t\treturn &pooledValue[string]{\n\t\t\tvalue: \"new\",\n\t\t}\n\t})\n\n\t// Probabilistically, 75% of sync.Pool.Put calls will succeed when -race\n\t// is enabled (see ref below); attempt to make this quasi-deterministic by\n\t// brute force (i.e., put significantly more objects in the pool than we\n\t// will need for the test) in order to avoid testing without race enabled.\n\t//\n\t// ref: https://cs.opensource.google/go/go/+/refs/tags/go1.20.2:src/sync/pool.go;l=100-103\n\tfor i := 0; i < 1_000; i++ {\n\t\tp.Put(&pooledValue[string]{\n\t\t\tvalue: t.Name(),\n\t\t})\n\t}\n\n\t// Ensure that we always get the expected value. Note that this must only\n\t// run a fraction of the number of times that Put is called above.\n\tfor i := 0; i < 10; i++ {\n\t\tfunc() {\n\t\t\tx := p.Get()\n\t\t\tdefer p.Put(x)\n\t\t\trequire.Equal(t, t.Name(), x.value)\n\t\t}()\n\t}\n\n\t// Depool all objects that might be in the pool to ensure that it's empty.\n\tfor i := 0; i < 1_000; i++ {\n\t\tp.Get()\n\t}\n\n\t// Now that the pool is empty, it should use the value specified in the\n\t// underlying sync.Pool.New func.\n\trequire.Equal(t, \"new\", p.Get().value)\n}\n\nfunc TestNew_Race(t *testing.T) {\n\tp := pool.New(func() *pooledValue[int] {\n\t\treturn &pooledValue[int]{\n\t\t\tvalue: -1,\n\t\t}\n\t})\n\n\tvar wg sync.WaitGroup\n\tdefer wg.Wait()\n\n\t// Run a number of goroutines that read and write pool object fields to\n\t// tease out races.\n\tfor i := 0; i < 1_000; i++ {\n\t\ti := i\n\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\n\t\t\tx := p.Get()\n\t\t\tdefer p.Put(x)\n\n\t\t\t// Must both read and write the field.\n\t\t\tif n := x.value; n >= -1 {\n\t\t\t\tx.value = i\n\t\t\t}\n\t\t}()\n\t}\n}\n"
  },
  {
    "path": "internal/readme/readme.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// readme generates Zap's README from a template.\npackage main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"os/exec\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"text/template\"\n\t\"time\"\n)\n\nvar libraryNameToMarkdownName = map[string]string{\n\t\"Zap\":                   \":zap: zap\",\n\t\"Zap.Sugar\":             \":zap: zap (sugared)\",\n\t\"stdlib.Println\":        \"standard library\",\n\t\"sirupsen/logrus\":       \"logrus\",\n\t\"go-kit/kit/log\":        \"go-kit\",\n\t\"inconshreveable/log15\": \"log15\",\n\t\"apex/log\":              \"apex/log\",\n\t\"rs/zerolog\":            \"zerolog\",\n\t\"slog\":                  \"slog\",\n\t\"slog.LogAttrs\":         \"slog (LogAttrs)\",\n}\n\nfunc main() {\n\tflag.Parse()\n\tif err := do(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n\nfunc do() error {\n\ttmplData, err := getTmplData()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdata, err := io.ReadAll(os.Stdin)\n\tif err != nil {\n\t\treturn err\n\t}\n\tt, err := template.New(\"tmpl\").Parse(string(data))\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn t.Execute(os.Stdout, tmplData)\n}\n\nfunc getTmplData() (*tmplData, error) {\n\ttmplData := &tmplData{}\n\trows, err := getBenchmarkRows(\"BenchmarkAddingFields\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\ttmplData.BenchmarkAddingFields = rows\n\trows, err = getBenchmarkRows(\"BenchmarkAccumulatedContext\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\ttmplData.BenchmarkAccumulatedContext = rows\n\trows, err = getBenchmarkRows(\"BenchmarkWithoutFields\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\ttmplData.BenchmarkWithoutFields = rows\n\treturn tmplData, nil\n}\n\nfunc getBenchmarkRows(benchmarkName string) (string, error) {\n\tbenchmarkOutput, err := getBenchmarkOutput(benchmarkName)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\t// get the Zap time (unsugared) as baseline to compare with other loggers\n\tbaseline, err := getBenchmarkRow(benchmarkOutput, benchmarkName, \"Zap\", nil)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tvar benchmarkRows []*benchmarkRow\n\tfor libraryName := range libraryNameToMarkdownName {\n\t\tbenchmarkRow, err := getBenchmarkRow(\n\t\t\tbenchmarkOutput, benchmarkName, libraryName, baseline,\n\t\t)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tif benchmarkRow == nil {\n\t\t\tcontinue\n\t\t}\n\t\tbenchmarkRows = append(benchmarkRows, benchmarkRow)\n\t}\n\tsort.Sort(benchmarkRowsByTime(benchmarkRows))\n\trows := []string{\n\t\t\"| Package | Time | Time % to zap | Objects Allocated |\",\n\t\t\"| :------ | :--: | :-----------: | :---------------: |\",\n\t}\n\tfor _, benchmarkRow := range benchmarkRows {\n\t\trows = append(rows, benchmarkRow.String())\n\t}\n\treturn strings.Join(rows, \"\\n\"), nil\n}\n\nfunc getBenchmarkRow(\n\tinput []string, benchmarkName string, libraryName string, baseline *benchmarkRow,\n) (*benchmarkRow, error) {\n\tline, err := findUniqueSubstring(input, fmt.Sprintf(\"%s/%s-\", benchmarkName, libraryName))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif line == \"\" {\n\t\treturn nil, nil\n\t}\n\tsplit := strings.Split(line, \"\\t\")\n\tif len(split) < 5 {\n\t\treturn nil, fmt.Errorf(\"unknown benchmark line: %s\", line)\n\t}\n\tduration, err := time.ParseDuration(strings.ReplaceAll(strings.TrimSuffix(strings.TrimSpace(split[2]), \"/op\"), \" \", \"\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tallocatedBytes, err := strconv.Atoi(strings.TrimSuffix(strings.TrimSpace(split[3]), \" B/op\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tallocatedObjects, err := strconv.Atoi(strings.TrimSuffix(strings.TrimSpace(split[4]), \" allocs/op\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tr := &benchmarkRow{\n\t\tName:             libraryNameToMarkdownName[libraryName],\n\t\tTime:             duration,\n\t\tAllocatedBytes:   allocatedBytes,\n\t\tAllocatedObjects: allocatedObjects,\n\t}\n\n\tif baseline != nil {\n\t\tr.ZapTime = baseline.Time\n\t\tr.ZapAllocatedBytes = baseline.AllocatedBytes\n\t\tr.ZapAllocatedObjects = baseline.AllocatedObjects\n\t}\n\n\treturn r, nil\n}\n\nfunc findUniqueSubstring(input []string, substring string) (string, error) {\n\tvar output string\n\tfor _, line := range input {\n\t\tif strings.Contains(line, substring) {\n\t\t\tif output != \"\" {\n\t\t\t\treturn \"\", fmt.Errorf(\"input has duplicate substring %s\", substring)\n\t\t\t}\n\t\t\toutput = line\n\t\t}\n\t}\n\treturn output, nil\n}\n\nfunc getBenchmarkOutput(benchmarkName string) ([]string, error) {\n\tcmd := exec.Command(\"go\", \"test\", fmt.Sprintf(\"-bench=%s\", benchmarkName), \"-benchmem\")\n\tcmd.Dir = \"benchmarks\"\n\toutput, err := cmd.CombinedOutput()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error running 'go test -bench=%q': %v\\n%s\", benchmarkName, err, string(output))\n\t}\n\treturn strings.Split(string(output), \"\\n\"), nil\n}\n\ntype tmplData struct {\n\tBenchmarkAddingFields       string\n\tBenchmarkAccumulatedContext string\n\tBenchmarkWithoutFields      string\n}\n\ntype benchmarkRow struct {\n\tName string\n\n\tTime             time.Duration\n\tAllocatedBytes   int\n\tAllocatedObjects int\n\n\tZapTime             time.Duration\n\tZapAllocatedBytes   int\n\tZapAllocatedObjects int\n}\n\nfunc (b *benchmarkRow) String() string {\n\tpct := func(val, baseline int64) string {\n\t\treturn fmt.Sprintf(\n\t\t\t\"%+0.f%%\",\n\t\t\t((float64(val)/float64(baseline))*100)-100,\n\t\t)\n\t}\n\tt := b.Time.Nanoseconds()\n\ttp := pct(t, b.ZapTime.Nanoseconds())\n\n\treturn fmt.Sprintf(\n\t\t\"| %s | %d ns/op | %s | %d allocs/op\", b.Name,\n\t\tt, tp, b.AllocatedObjects,\n\t)\n}\n\ntype benchmarkRowsByTime []*benchmarkRow\n\nfunc (b benchmarkRowsByTime) Len() int      { return len(b) }\nfunc (b benchmarkRowsByTime) Swap(i, j int) { b[i], b[j] = b[j], b[i] }\nfunc (b benchmarkRowsByTime) Less(i, j int) bool {\n\tleft, right := b[i], b[j]\n\tleftZap, rightZap := strings.Contains(left.Name, \"zap\"), strings.Contains(right.Name, \"zap\")\n\n\t// If neither benchmark is for zap or both are, sort by time.\n\tif leftZap == rightZap {\n\t\treturn left.Time.Nanoseconds() < right.Time.Nanoseconds()\n\t}\n\t// Sort zap benchmark first.\n\treturn leftZap\n}\n"
  },
  {
    "path": "internal/stacktrace/stack.go",
    "content": "// Copyright (c) 2023 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// Package stacktrace provides support for gathering stack traces\n// efficiently.\npackage stacktrace\n\nimport (\n\t\"runtime\"\n\n\t\"go.uber.org/zap/buffer\"\n\t\"go.uber.org/zap/internal/bufferpool\"\n\t\"go.uber.org/zap/internal/pool\"\n)\n\nvar _stackPool = pool.New(func() *Stack {\n\treturn &Stack{\n\t\tstorage: make([]uintptr, 64),\n\t}\n})\n\n// Stack is a captured stack trace.\ntype Stack struct {\n\tpcs    []uintptr // program counters; always a subslice of storage\n\tframes *runtime.Frames\n\n\t// The size of pcs varies depending on requirements:\n\t// it will be one if the only the first frame was requested,\n\t// and otherwise it will reflect the depth of the call stack.\n\t//\n\t// storage decouples the slice we need (pcs) from the slice we pool.\n\t// We will always allocate a reasonably large storage, but we'll use\n\t// only as much of it as we need.\n\tstorage []uintptr\n}\n\n// Depth specifies how deep of a stack trace should be captured.\ntype Depth int\n\nconst (\n\t// First captures only the first frame.\n\tFirst Depth = iota\n\n\t// Full captures the entire call stack, allocating more\n\t// storage for it if needed.\n\tFull\n)\n\n// Capture captures a stack trace of the specified depth, skipping\n// the provided number of frames. skip=0 identifies the caller of\n// Capture.\n//\n// The caller must call Free on the returned stacktrace after using it.\nfunc Capture(skip int, depth Depth) *Stack {\n\tstack := _stackPool.Get()\n\n\tswitch depth {\n\tcase First:\n\t\tstack.pcs = stack.storage[:1]\n\tcase Full:\n\t\tstack.pcs = stack.storage\n\t}\n\n\t// Unlike other \"skip\"-based APIs, skip=0 identifies runtime.Callers\n\t// itself. +2 to skip captureStacktrace and runtime.Callers.\n\tnumFrames := runtime.Callers(\n\t\tskip+2,\n\t\tstack.pcs,\n\t)\n\n\t// runtime.Callers truncates the recorded stacktrace if there is no\n\t// room in the provided slice. For the full stack trace, keep expanding\n\t// storage until there are fewer frames than there is room.\n\tif depth == Full {\n\t\tpcs := stack.pcs\n\t\tfor numFrames == len(pcs) {\n\t\t\tpcs = make([]uintptr, len(pcs)*2)\n\t\t\tnumFrames = runtime.Callers(skip+2, pcs)\n\t\t}\n\n\t\t// Discard old storage instead of returning it to the pool.\n\t\t// This will adjust the pool size over time if stack traces are\n\t\t// consistently very deep.\n\t\tstack.storage = pcs\n\t\tstack.pcs = pcs[:numFrames]\n\t} else {\n\t\tstack.pcs = stack.pcs[:numFrames]\n\t}\n\n\tstack.frames = runtime.CallersFrames(stack.pcs)\n\treturn stack\n}\n\n// Free releases resources associated with this stacktrace\n// and returns it back to the pool.\nfunc (st *Stack) Free() {\n\tst.frames = nil\n\tst.pcs = nil\n\t_stackPool.Put(st)\n}\n\n// Count reports the total number of frames in this stacktrace.\n// Count DOES NOT change as Next is called.\nfunc (st *Stack) Count() int {\n\treturn len(st.pcs)\n}\n\n// Next returns the next frame in the stack trace,\n// and a boolean indicating whether there are more after it.\nfunc (st *Stack) Next() (_ runtime.Frame, more bool) {\n\treturn st.frames.Next()\n}\n\n// Take returns a string representation of the current stacktrace.\n//\n// skip is the number of frames to skip before recording the stack trace.\n// skip=0 identifies the caller of Take.\nfunc Take(skip int) string {\n\tstack := Capture(skip+1, Full)\n\tdefer stack.Free()\n\n\tbuffer := bufferpool.Get()\n\tdefer buffer.Free()\n\n\tstackfmt := NewFormatter(buffer)\n\tstackfmt.FormatStack(stack)\n\treturn buffer.String()\n}\n\n// Formatter formats a stack trace into a readable string representation.\ntype Formatter struct {\n\tb        *buffer.Buffer\n\tnonEmpty bool // whehther we've written at least one frame already\n}\n\n// NewFormatter builds a new Formatter.\nfunc NewFormatter(b *buffer.Buffer) Formatter {\n\treturn Formatter{b: b}\n}\n\n// FormatStack formats all remaining frames in the provided stacktrace -- minus\n// the final runtime.main/runtime.goexit frame.\nfunc (sf *Formatter) FormatStack(stack *Stack) {\n\t// Note: On the last iteration, frames.Next() returns false, with a valid\n\t// frame, but we ignore this frame. The last frame is a runtime frame which\n\t// adds noise, since it's only either runtime.main or runtime.goexit.\n\tfor frame, more := stack.Next(); more; frame, more = stack.Next() {\n\t\tsf.FormatFrame(frame)\n\t}\n}\n\n// FormatFrame formats the given frame.\nfunc (sf *Formatter) FormatFrame(frame runtime.Frame) {\n\tif sf.nonEmpty {\n\t\tsf.b.AppendByte('\\n')\n\t}\n\tsf.nonEmpty = true\n\tsf.b.AppendString(frame.Function)\n\tsf.b.AppendByte('\\n')\n\tsf.b.AppendByte('\\t')\n\tsf.b.AppendString(frame.File)\n\tsf.b.AppendByte(':')\n\tsf.b.AppendInt(int64(frame.Line))\n}\n"
  },
  {
    "path": "internal/stacktrace/stack_test.go",
    "content": "// Copyright (c) 2023 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage stacktrace\n\nimport (\n\t\"bytes\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestTake(t *testing.T) {\n\ttrace := Take(0)\n\tlines := strings.Split(trace, \"\\n\")\n\trequire.NotEmpty(t, lines, \"Expected stacktrace to have at least one frame.\")\n\tassert.Contains(\n\t\tt,\n\t\tlines[0],\n\t\t\"go.uber.org/zap/internal/stacktrace.TestTake\",\n\t\t\"Expected stacktrace to start with the test.\",\n\t)\n}\n\nfunc TestTakeWithSkip(t *testing.T) {\n\ttrace := Take(1)\n\tlines := strings.Split(trace, \"\\n\")\n\trequire.NotEmpty(t, lines, \"Expected stacktrace to have at least one frame.\")\n\tassert.Contains(\n\t\tt,\n\t\tlines[0],\n\t\t\"testing.\",\n\t\t\"Expected stacktrace to start with the test runner (skipping our own frame).\",\n\t)\n}\n\nfunc TestTakeWithSkipInnerFunc(t *testing.T) {\n\tvar trace string\n\tfunc() {\n\t\ttrace = Take(2)\n\t}()\n\tlines := strings.Split(trace, \"\\n\")\n\trequire.NotEmpty(t, lines, \"Expected stacktrace to have at least one frame.\")\n\tassert.Contains(\n\t\tt,\n\t\tlines[0],\n\t\t\"testing.\",\n\t\t\"Expected stacktrace to start with the test function (skipping the test function).\",\n\t)\n}\n\nfunc TestTakeDeepStack(t *testing.T) {\n\tconst (\n\t\tN                  = 500\n\t\twithStackDepthName = \"go.uber.org/zap/internal/stacktrace.withStackDepth\"\n\t)\n\twithStackDepth(N, func() {\n\t\ttrace := Take(0)\n\t\tfor found := 0; found < N; found++ {\n\t\t\ti := strings.Index(trace, withStackDepthName)\n\t\t\tif i < 0 {\n\t\t\t\tt.Fatalf(`expected %v occurrences of %q, found %d`,\n\t\t\t\t\tN, withStackDepthName, found)\n\t\t\t}\n\t\t\ttrace = trace[i+len(withStackDepthName):]\n\t\t}\n\t})\n}\n\nfunc BenchmarkTake(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\tTake(0)\n\t}\n}\n\nfunc withStackDepth(depth int, f func()) {\n\tvar recurse func(rune) rune\n\trecurse = func(r rune) rune {\n\t\tif r > 0 {\n\t\t\tbytes.Map(recurse, []byte(string([]rune{r - 1})))\n\t\t} else {\n\t\t\tf()\n\t\t}\n\t\treturn 0\n\t}\n\trecurse(rune(depth))\n}\n"
  },
  {
    "path": "internal/ztest/clock.go",
    "content": "// Copyright (c) 2023 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage ztest\n\nimport (\n\t\"sort\"\n\t\"sync\"\n\t\"time\"\n)\n\n// MockClock is a fake source of time.\n// It implements standard time operations,\n// but allows the user to control the passage of time.\n//\n// Use the [Add] method to progress time.\ntype MockClock struct {\n\tmu  sync.RWMutex\n\tnow time.Time\n\n\t// The MockClock works by maintaining a list of waiters.\n\t// Each waiter knows the time at which it should be resolved.\n\t// When the clock advances, all waiters that are in range are resolved\n\t// in chronological order.\n\twaiters []waiter\n}\n\n// NewMockClock builds a new mock clock\n// using the current actual time as the initial time.\nfunc NewMockClock() *MockClock {\n\treturn &MockClock{\n\t\tnow: time.Now(),\n\t}\n}\n\n// Now reports the current time.\nfunc (c *MockClock) Now() time.Time {\n\tc.mu.RLock()\n\tdefer c.mu.RUnlock()\n\treturn c.now\n}\n\n// NewTicker returns a time.Ticker that ticks at the specified frequency.\n//\n// As with [time.NewTicker],\n// the ticker will drop ticks if the receiver is slow,\n// and the channel is never closed.\n//\n// Calling Stop on the returned ticker is a no-op.\n// The ticker only runs when the clock is advanced.\nfunc (c *MockClock) NewTicker(d time.Duration) *time.Ticker {\n\tch := make(chan time.Time, 1)\n\n\tvar tick func(time.Time)\n\ttick = func(now time.Time) {\n\t\tnext := now.Add(d)\n\t\tc.runAt(next, func() {\n\t\t\tdefer tick(next)\n\n\t\t\tselect {\n\t\t\tcase ch <- next:\n\t\t\t\t// ok\n\t\t\tdefault:\n\t\t\t\t// The receiver is slow.\n\t\t\t\t// Drop the tick and continue.\n\t\t\t}\n\t\t})\n\t}\n\ttick(c.Now())\n\n\treturn &time.Ticker{C: ch}\n}\n\n// runAt schedules the given function to be run at the given time.\n// The function runs without a lock held, so it may schedule more work.\nfunc (c *MockClock) runAt(t time.Time, fn func()) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\tc.waiters = append(c.waiters, waiter{until: t, fn: fn})\n}\n\ntype waiter struct {\n\tuntil time.Time\n\tfn    func()\n}\n\n// Add progresses time by the given duration.\n// Other operations waiting for the time to advance\n// will be resolved if they are within range.\n//\n// Side effects of operations waiting for the time to advance\n// will take effect on a best-effort basis.\n// Avoid racing with operations that have side effects.\n//\n// Panics if the duration is negative.\nfunc (c *MockClock) Add(d time.Duration) {\n\tif d < 0 {\n\t\tpanic(\"cannot add negative duration\")\n\t}\n\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\tsort.Slice(c.waiters, func(i, j int) bool {\n\t\treturn c.waiters[i].until.Before(c.waiters[j].until)\n\t})\n\n\tnewTime := c.now.Add(d)\n\t// newTime won't be recorded until the end of this method.\n\t// This ensures that any waiters that are resolved\n\t// are resolved at the time they were expecting.\n\n\tfor len(c.waiters) > 0 {\n\t\tw := c.waiters[0]\n\t\tif w.until.After(newTime) {\n\t\t\tbreak\n\t\t}\n\t\tc.waiters[0] = waiter{} // avoid memory leak\n\t\tc.waiters = c.waiters[1:]\n\n\t\t// The waiter is within range.\n\t\t// Travel to the time of the waiter and resolve it.\n\t\tc.now = w.until\n\n\t\t// The waiter may schedule more work\n\t\t// so we must release the lock.\n\t\tc.mu.Unlock()\n\t\tw.fn()\n\t\t// Sleeping here is necessary to let the side effects of waiters\n\t\t// take effect before we continue.\n\t\ttime.Sleep(1 * time.Millisecond)\n\t\tc.mu.Lock()\n\t}\n\n\tc.now = newTime\n}\n"
  },
  {
    "path": "internal/ztest/clock_test.go",
    "content": "// Copyright (c) 2023 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage ztest\n\nimport (\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestMockClock_NewTicker(t *testing.T) {\n\tvar n atomic.Int32\n\tclock := NewMockClock()\n\n\tdone := make(chan struct{})\n\tdefer func() { <-done }() // wait for end\n\n\tquit := make(chan struct{})\n\t// Create a channel to increment every microsecond.\n\tgo func(ticker *time.Ticker) {\n\t\tdefer close(done)\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase <-quit:\n\t\t\t\tticker.Stop()\n\t\t\t\treturn\n\t\t\tcase <-ticker.C:\n\t\t\t\tn.Add(1)\n\t\t\t}\n\t\t}\n\t}(clock.NewTicker(time.Microsecond))\n\n\t// Move clock forward.\n\tclock.Add(2 * time.Microsecond)\n\tassert.Equal(t, int32(2), n.Load())\n\tclose(quit)\n}\n\nfunc TestMockClock_NewTicker_slowConsumer(t *testing.T) {\n\tclock := NewMockClock()\n\n\tticker := clock.NewTicker(time.Microsecond)\n\tdefer ticker.Stop()\n\n\t// Two ticks, only one consumed.\n\tclock.Add(2 * time.Microsecond)\n\t<-ticker.C\n\n\tselect {\n\tcase <-ticker.C:\n\t\tt.Fatal(\"unexpected tick\")\n\tdefault:\n\t\t// ok\n\t}\n}\n\nfunc TestMockClock_Add_negative(t *testing.T) {\n\tclock := NewMockClock()\n\tassert.Panics(t, func() { clock.Add(-1) })\n}\n"
  },
  {
    "path": "internal/ztest/doc.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// Package ztest provides low-level helpers for testing log output. These\n// utilities are helpful in zap's own unit tests, but any assertions using\n// them are strongly coupled to a single encoding.\npackage ztest // import \"go.uber.org/zap/internal/ztest\"\n"
  },
  {
    "path": "internal/ztest/timeout.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage ztest\n\nimport (\n\t\"log\"\n\t\"os\"\n\t\"strconv\"\n\t\"time\"\n)\n\nvar _timeoutScale = 1.0\n\n// Timeout scales the provided duration by $TEST_TIMEOUT_SCALE.\nfunc Timeout(base time.Duration) time.Duration {\n\treturn time.Duration(float64(base) * _timeoutScale)\n}\n\n// Sleep scales the sleep duration by $TEST_TIMEOUT_SCALE.\nfunc Sleep(base time.Duration) {\n\ttime.Sleep(Timeout(base))\n}\n\n// Initialize checks the environment and alters the timeout scale accordingly.\n// It returns a function to undo the scaling.\nfunc Initialize(factor string) func() {\n\tfv, err := strconv.ParseFloat(factor, 64)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\toriginal := _timeoutScale\n\t_timeoutScale = fv\n\treturn func() { _timeoutScale = original }\n}\n\nfunc init() {\n\tif v := os.Getenv(\"TEST_TIMEOUT_SCALE\"); v != \"\" {\n\t\tInitialize(v)\n\t\tlog.Printf(\"Scaling timeouts by %vx.\\n\", _timeoutScale)\n\t}\n}\n"
  },
  {
    "path": "internal/ztest/writer.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage ztest\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"io\"\n\t\"strings\"\n)\n\n// A Syncer is a spy for the Sync portion of zapcore.WriteSyncer.\ntype Syncer struct {\n\terr    error\n\tcalled bool\n}\n\n// SetError sets the error that the Sync method will return.\nfunc (s *Syncer) SetError(err error) {\n\ts.err = err\n}\n\n// Sync records that it was called, then returns the user-supplied error (if\n// any).\nfunc (s *Syncer) Sync() error {\n\ts.called = true\n\treturn s.err\n}\n\n// Called reports whether the Sync method was called.\nfunc (s *Syncer) Called() bool {\n\treturn s.called\n}\n\n// A Discarder sends all writes to io.Discard.\ntype Discarder struct{ Syncer }\n\n// Write implements io.Writer.\nfunc (d *Discarder) Write(b []byte) (int, error) {\n\treturn io.Discard.Write(b)\n}\n\n// FailWriter is a WriteSyncer that always returns an error on writes.\ntype FailWriter struct{ Syncer }\n\n// Write implements io.Writer.\nfunc (w FailWriter) Write(b []byte) (int, error) {\n\treturn len(b), errors.New(\"failed\")\n}\n\n// ShortWriter is a WriteSyncer whose write method never fails, but\n// nevertheless fails to the last byte of the input.\ntype ShortWriter struct{ Syncer }\n\n// Write implements io.Writer.\nfunc (w ShortWriter) Write(b []byte) (int, error) {\n\treturn len(b) - 1, nil\n}\n\n// Buffer is an implementation of zapcore.WriteSyncer that sends all writes to\n// a bytes.Buffer. It has convenience methods to split the accumulated buffer\n// on newlines.\ntype Buffer struct {\n\tbytes.Buffer\n\tSyncer\n}\n\n// Lines returns the current buffer contents, split on newlines.\nfunc (b *Buffer) Lines() []string {\n\toutput := strings.Split(b.String(), \"\\n\")\n\treturn output[:len(output)-1]\n}\n\n// Stripped returns the current buffer contents with the last trailing newline\n// stripped.\nfunc (b *Buffer) Stripped() string {\n\treturn strings.TrimRight(b.String(), \"\\n\")\n}\n"
  },
  {
    "path": "leak_test.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"testing\"\n\n\t\"go.uber.org/goleak\"\n)\n\nfunc TestMain(m *testing.M) {\n\tgoleak.VerifyTestMain(m)\n}\n"
  },
  {
    "path": "level.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"sync/atomic\"\n\n\t\"go.uber.org/zap/internal\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\nconst (\n\t// DebugLevel logs are typically voluminous, and are usually disabled in\n\t// production.\n\tDebugLevel = zapcore.DebugLevel\n\t// InfoLevel is the default logging priority.\n\tInfoLevel = zapcore.InfoLevel\n\t// WarnLevel logs are more important than Info, but don't need individual\n\t// human review.\n\tWarnLevel = zapcore.WarnLevel\n\t// ErrorLevel logs are high-priority. If an application is running smoothly,\n\t// it shouldn't generate any error-level logs.\n\tErrorLevel = zapcore.ErrorLevel\n\t// DPanicLevel logs are particularly important errors. In development the\n\t// logger panics after writing the message.\n\tDPanicLevel = zapcore.DPanicLevel\n\t// PanicLevel logs a message, then panics.\n\tPanicLevel = zapcore.PanicLevel\n\t// FatalLevel logs a message, then calls os.Exit(1).\n\tFatalLevel = zapcore.FatalLevel\n)\n\n// LevelEnablerFunc is a convenient way to implement zapcore.LevelEnabler with\n// an anonymous function.\n//\n// It's particularly useful when splitting log output between different\n// outputs (e.g., standard error and standard out). For sample code, see the\n// package-level AdvancedConfiguration example.\ntype LevelEnablerFunc func(zapcore.Level) bool\n\n// Enabled calls the wrapped function.\nfunc (f LevelEnablerFunc) Enabled(lvl zapcore.Level) bool { return f(lvl) }\n\n// An AtomicLevel is an atomically changeable, dynamic logging level. It lets\n// you safely change the log level of a tree of loggers (the root logger and\n// any children created by adding context) at runtime.\n//\n// The AtomicLevel itself is an http.Handler that serves a JSON endpoint to\n// alter its level.\n//\n// AtomicLevels must be created with the NewAtomicLevel constructor to allocate\n// their internal atomic pointer.\ntype AtomicLevel struct {\n\tl *atomic.Int32\n}\n\nvar _ internal.LeveledEnabler = AtomicLevel{}\n\n// NewAtomicLevel creates an AtomicLevel with InfoLevel and above logging\n// enabled.\nfunc NewAtomicLevel() AtomicLevel {\n\tlvl := AtomicLevel{l: new(atomic.Int32)}\n\tlvl.l.Store(int32(InfoLevel))\n\treturn lvl\n}\n\n// NewAtomicLevelAt is a convenience function that creates an AtomicLevel\n// and then calls SetLevel with the given level.\nfunc NewAtomicLevelAt(l zapcore.Level) AtomicLevel {\n\ta := NewAtomicLevel()\n\ta.SetLevel(l)\n\treturn a\n}\n\n// ParseAtomicLevel parses an AtomicLevel based on a lowercase or all-caps ASCII\n// representation of the log level. If the provided ASCII representation is\n// invalid an error is returned.\n//\n// This is particularly useful when dealing with text input to configure log\n// levels.\nfunc ParseAtomicLevel(text string) (AtomicLevel, error) {\n\ta := NewAtomicLevel()\n\tl, err := zapcore.ParseLevel(text)\n\tif err != nil {\n\t\treturn a, err\n\t}\n\n\ta.SetLevel(l)\n\treturn a, nil\n}\n\n// Enabled implements the zapcore.LevelEnabler interface, which allows the\n// AtomicLevel to be used in place of traditional static levels.\nfunc (lvl AtomicLevel) Enabled(l zapcore.Level) bool {\n\treturn lvl.Level().Enabled(l)\n}\n\n// Level returns the minimum enabled log level.\nfunc (lvl AtomicLevel) Level() zapcore.Level {\n\treturn zapcore.Level(int8(lvl.l.Load()))\n}\n\n// SetLevel alters the logging level.\nfunc (lvl AtomicLevel) SetLevel(l zapcore.Level) {\n\tlvl.l.Store(int32(l))\n}\n\n// String returns the string representation of the underlying Level.\nfunc (lvl AtomicLevel) String() string {\n\treturn lvl.Level().String()\n}\n\n// UnmarshalText unmarshals the text to an AtomicLevel. It uses the same text\n// representations as the static zapcore.Levels (\"debug\", \"info\", \"warn\",\n// \"error\", \"dpanic\", \"panic\", and \"fatal\").\nfunc (lvl *AtomicLevel) UnmarshalText(text []byte) error {\n\tif lvl.l == nil {\n\t\tlvl.l = &atomic.Int32{}\n\t}\n\n\tvar l zapcore.Level\n\tif err := l.UnmarshalText(text); err != nil {\n\t\treturn err\n\t}\n\n\tlvl.SetLevel(l)\n\treturn nil\n}\n\n// MarshalText marshals the AtomicLevel to a byte slice. It uses the same\n// text representation as the static zapcore.Levels (\"debug\", \"info\", \"warn\",\n// \"error\", \"dpanic\", \"panic\", and \"fatal\").\nfunc (lvl AtomicLevel) MarshalText() (text []byte, err error) {\n\treturn lvl.Level().MarshalText()\n}\n"
  },
  {
    "path": "level_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"sync\"\n\t\"testing\"\n\n\t\"go.uber.org/zap/zapcore\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestLevelEnablerFunc(t *testing.T) {\n\tenab := LevelEnablerFunc(func(l zapcore.Level) bool { return l == zapcore.InfoLevel })\n\ttests := []struct {\n\t\tlevel   zapcore.Level\n\t\tenabled bool\n\t}{\n\t\t{DebugLevel, false},\n\t\t{InfoLevel, true},\n\t\t{WarnLevel, false},\n\t\t{ErrorLevel, false},\n\t\t{DPanicLevel, false},\n\t\t{PanicLevel, false},\n\t\t{FatalLevel, false},\n\t}\n\tfor _, tt := range tests {\n\t\tassert.Equal(t, tt.enabled, enab.Enabled(tt.level), \"Unexpected result applying LevelEnablerFunc to %s\", tt.level)\n\t}\n}\n\nfunc TestNewAtomicLevel(t *testing.T) {\n\tlvl := NewAtomicLevel()\n\tassert.Equal(t, InfoLevel, lvl.Level(), \"Unexpected initial level.\")\n\tlvl.SetLevel(ErrorLevel)\n\tassert.Equal(t, ErrorLevel, lvl.Level(), \"Unexpected level after SetLevel.\")\n\tlvl = NewAtomicLevelAt(WarnLevel)\n\tassert.Equal(t, WarnLevel, lvl.Level(), \"Unexpected level after SetLevel.\")\n}\n\nfunc TestParseAtomicLevel(t *testing.T) {\n\ttests := []struct {\n\t\ttext  string\n\t\tlevel AtomicLevel\n\t\terr   string\n\t}{\n\t\t{\"info\", NewAtomicLevel(), \"\"},\n\t\t{\"DEBUG\", NewAtomicLevelAt(DebugLevel), \"\"},\n\t\t{\"FOO\", NewAtomicLevel(), `unrecognized level: \"FOO\"`},\n\t}\n\n\tfor _, tt := range tests {\n\t\tparsedAtomicLevel, err := ParseAtomicLevel(tt.text)\n\t\tif len(tt.err) == 0 {\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, tt.level, parsedAtomicLevel)\n\t\t} else {\n\t\t\tassert.ErrorContains(t, err, tt.err)\n\t\t}\n\t}\n}\n\nfunc TestAtomicLevelMutation(t *testing.T) {\n\tlvl := NewAtomicLevel()\n\tlvl.SetLevel(WarnLevel)\n\t// Trigger races for non-atomic level mutations.\n\tproceed := make(chan struct{})\n\twg := &sync.WaitGroup{}\n\trunConcurrently(10, 100, wg, func() {\n\t\t<-proceed\n\t\tassert.Equal(t, WarnLevel, lvl.Level())\n\t})\n\trunConcurrently(10, 100, wg, func() {\n\t\t<-proceed\n\t\tlvl.SetLevel(WarnLevel)\n\t})\n\tclose(proceed)\n\twg.Wait()\n}\n\nfunc TestAtomicLevelText(t *testing.T) {\n\ttests := []struct {\n\t\ttext   string\n\t\texpect zapcore.Level\n\t\terr    bool\n\t}{\n\t\t{\"debug\", DebugLevel, false},\n\t\t{\"info\", InfoLevel, false},\n\t\t{\"\", InfoLevel, false},\n\t\t{\"warn\", WarnLevel, false},\n\t\t{\"error\", ErrorLevel, false},\n\t\t{\"dpanic\", DPanicLevel, false},\n\t\t{\"panic\", PanicLevel, false},\n\t\t{\"fatal\", FatalLevel, false},\n\t\t{\"foobar\", InfoLevel, true},\n\t}\n\n\tfor _, tt := range tests {\n\t\tvar lvl AtomicLevel\n\t\t// Test both initial unmarshaling and overwriting existing value.\n\t\tfor i := 0; i < 2; i++ {\n\t\t\tif tt.err {\n\t\t\t\tassert.Error(t, lvl.UnmarshalText([]byte(tt.text)), \"Expected unmarshaling %q to fail.\", tt.text)\n\t\t\t} else {\n\t\t\t\tassert.NoError(t, lvl.UnmarshalText([]byte(tt.text)), \"Expected unmarshaling %q to succeed.\", tt.text)\n\t\t\t}\n\t\t\tassert.Equal(t, tt.expect, lvl.Level(), \"Unexpected level after unmarshaling.\")\n\t\t\tlvl.SetLevel(InfoLevel)\n\t\t}\n\n\t\t// Test marshalling\n\t\tif tt.text != \"\" && !tt.err {\n\t\t\tlvl.SetLevel(tt.expect)\n\t\t\tmarshaled, err := lvl.MarshalText()\n\t\t\tassert.NoError(t, err, `Unexpected error marshalling level \"%v\" to text.`, tt.expect)\n\t\t\tassert.Equal(t, tt.text, string(marshaled), \"Expected marshaled text to match\")\n\t\t\tassert.Equal(t, tt.text, lvl.String(), \"Expected Stringer call to match\")\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "logger.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n\n\t\"go.uber.org/zap/internal/bufferpool\"\n\t\"go.uber.org/zap/internal/stacktrace\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\n// A Logger provides fast, leveled, structured logging. All methods are safe\n// for concurrent use.\n//\n// The Logger is designed for contexts in which every microsecond and every\n// allocation matters, so its API intentionally favors performance and type\n// safety over brevity. For most applications, the SugaredLogger strikes a\n// better balance between performance and ergonomics.\ntype Logger struct {\n\tcore zapcore.Core\n\n\tdevelopment bool\n\taddCaller   bool\n\tonPanic     zapcore.CheckWriteHook // default is WriteThenPanic\n\tonFatal     zapcore.CheckWriteHook // default is WriteThenFatal\n\n\tname        string\n\terrorOutput zapcore.WriteSyncer\n\n\taddStack zapcore.LevelEnabler\n\n\tcallerSkip int\n\n\tclock zapcore.Clock\n}\n\n// New constructs a new Logger from the provided zapcore.Core and Options. If\n// the passed zapcore.Core is nil, it falls back to using a no-op\n// implementation.\n//\n// This is the most flexible way to construct a Logger, but also the most\n// verbose. For typical use cases, the highly-opinionated presets\n// (NewProduction, NewDevelopment, and NewExample) or the Config struct are\n// more convenient.\n//\n// For sample code, see the package-level AdvancedConfiguration example.\nfunc New(core zapcore.Core, options ...Option) *Logger {\n\tif core == nil {\n\t\treturn NewNop()\n\t}\n\tlog := &Logger{\n\t\tcore:        core,\n\t\terrorOutput: zapcore.Lock(os.Stderr),\n\t\taddStack:    zapcore.FatalLevel + 1,\n\t\tclock:       zapcore.DefaultClock,\n\t}\n\treturn log.WithOptions(options...)\n}\n\n// NewNop returns a no-op Logger. It never writes out logs or internal errors,\n// and it never runs user-defined hooks.\n//\n// Using WithOptions to replace the Core or error output of a no-op Logger can\n// re-enable logging.\nfunc NewNop() *Logger {\n\treturn &Logger{\n\t\tcore:        zapcore.NewNopCore(),\n\t\terrorOutput: zapcore.AddSync(io.Discard),\n\t\taddStack:    zapcore.FatalLevel + 1,\n\t\tclock:       zapcore.DefaultClock,\n\t}\n}\n\n// NewProduction builds a sensible production Logger that writes InfoLevel and\n// above logs to standard error as JSON.\n//\n// It's a shortcut for NewProductionConfig().Build(...Option).\nfunc NewProduction(options ...Option) (*Logger, error) {\n\treturn NewProductionConfig().Build(options...)\n}\n\n// NewDevelopment builds a development Logger that writes DebugLevel and above\n// logs to standard error in a human-friendly format.\n//\n// It's a shortcut for NewDevelopmentConfig().Build(...Option).\nfunc NewDevelopment(options ...Option) (*Logger, error) {\n\treturn NewDevelopmentConfig().Build(options...)\n}\n\n// Must is a helper that wraps a call to a function returning (*Logger, error)\n// and panics if the error is non-nil. It is intended for use in variable\n// initialization such as:\n//\n//\tvar logger = zap.Must(zap.NewProduction())\nfunc Must(logger *Logger, err error) *Logger {\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn logger\n}\n\n// NewExample builds a Logger that's designed for use in zap's testable\n// examples. It writes DebugLevel and above logs to standard out as JSON, but\n// omits the timestamp and calling function to keep example output\n// short and deterministic.\nfunc NewExample(options ...Option) *Logger {\n\tencoderCfg := zapcore.EncoderConfig{\n\t\tMessageKey:     \"msg\",\n\t\tLevelKey:       \"level\",\n\t\tNameKey:        \"logger\",\n\t\tEncodeLevel:    zapcore.LowercaseLevelEncoder,\n\t\tEncodeTime:     zapcore.ISO8601TimeEncoder,\n\t\tEncodeDuration: zapcore.StringDurationEncoder,\n\t}\n\tcore := zapcore.NewCore(zapcore.NewJSONEncoder(encoderCfg), os.Stdout, DebugLevel)\n\treturn New(core).WithOptions(options...)\n}\n\n// Sugar wraps the Logger to provide a more ergonomic, but slightly slower,\n// API. Sugaring a Logger is quite inexpensive, so it's reasonable for a\n// single application to use both Loggers and SugaredLoggers, converting\n// between them on the boundaries of performance-sensitive code.\nfunc (log *Logger) Sugar() *SugaredLogger {\n\tcore := log.clone()\n\tcore.callerSkip += 2\n\treturn &SugaredLogger{core}\n}\n\n// Named adds a new path segment to the logger's name. Segments are joined by\n// periods. By default, Loggers are unnamed.\nfunc (log *Logger) Named(s string) *Logger {\n\tif s == \"\" {\n\t\treturn log\n\t}\n\tl := log.clone()\n\tif log.name == \"\" {\n\t\tl.name = s\n\t} else {\n\t\tl.name = strings.Join([]string{l.name, s}, \".\")\n\t}\n\treturn l\n}\n\n// WithOptions clones the current Logger, applies the supplied Options, and\n// returns the resulting Logger. It's safe to use concurrently.\nfunc (log *Logger) WithOptions(opts ...Option) *Logger {\n\tc := log.clone()\n\tfor _, opt := range opts {\n\t\topt.apply(c)\n\t}\n\treturn c\n}\n\n// With creates a child logger and adds structured context to it. Fields added\n// to the child don't affect the parent, and vice versa. Any fields that\n// require evaluation (such as Objects) are evaluated upon invocation of With.\nfunc (log *Logger) With(fields ...Field) *Logger {\n\tif len(fields) == 0 {\n\t\treturn log\n\t}\n\tl := log.clone()\n\tl.core = l.core.With(fields)\n\treturn l\n}\n\n// WithLazy creates a child logger and adds structured context to it lazily.\n//\n// The fields are evaluated only if the logger is further chained with [With]\n// or is written to with any of the log level methods.\n// Until that occurs, the logger may retain references to objects inside the fields,\n// and logging will reflect the state of an object at the time of logging,\n// not the time of WithLazy().\n//\n// WithLazy provides a worthwhile performance optimization for contextual loggers\n// when the likelihood of using the child logger is low,\n// such as error paths and rarely taken branches.\n//\n// Similar to [With], fields added to the child don't affect the parent, and vice versa.\nfunc (log *Logger) WithLazy(fields ...Field) *Logger {\n\tif len(fields) == 0 {\n\t\treturn log\n\t}\n\treturn log.WithOptions(WrapCore(func(core zapcore.Core) zapcore.Core {\n\t\treturn zapcore.NewLazyWith(core, fields)\n\t}))\n}\n\n// Level reports the minimum enabled level for this logger.\n//\n// For NopLoggers, this is [zapcore.InvalidLevel].\nfunc (log *Logger) Level() zapcore.Level {\n\treturn zapcore.LevelOf(log.core)\n}\n\n// Check returns a CheckedEntry if logging a message at the specified level\n// is enabled. It's a completely optional optimization; in high-performance\n// applications, Check can help avoid allocating a slice to hold fields.\nfunc (log *Logger) Check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry {\n\treturn log.check(lvl, msg)\n}\n\n// Log logs a message at the specified level. The message includes any fields\n// passed at the log site, as well as any fields accumulated on the logger.\n// Any Fields that require  evaluation (such as Objects) are evaluated upon\n// invocation of Log.\nfunc (log *Logger) Log(lvl zapcore.Level, msg string, fields ...Field) {\n\tif ce := log.check(lvl, msg); ce != nil {\n\t\tce.Write(fields...)\n\t}\n}\n\n// Debug logs a message at DebugLevel. The message includes any fields passed\n// at the log site, as well as any fields accumulated on the logger.\nfunc (log *Logger) Debug(msg string, fields ...Field) {\n\tif ce := log.check(DebugLevel, msg); ce != nil {\n\t\tce.Write(fields...)\n\t}\n}\n\n// Info logs a message at InfoLevel. The message includes any fields passed\n// at the log site, as well as any fields accumulated on the logger.\nfunc (log *Logger) Info(msg string, fields ...Field) {\n\tif ce := log.check(InfoLevel, msg); ce != nil {\n\t\tce.Write(fields...)\n\t}\n}\n\n// Warn logs a message at WarnLevel. The message includes any fields passed\n// at the log site, as well as any fields accumulated on the logger.\nfunc (log *Logger) Warn(msg string, fields ...Field) {\n\tif ce := log.check(WarnLevel, msg); ce != nil {\n\t\tce.Write(fields...)\n\t}\n}\n\n// Error logs a message at ErrorLevel. The message includes any fields passed\n// at the log site, as well as any fields accumulated on the logger.\nfunc (log *Logger) Error(msg string, fields ...Field) {\n\tif ce := log.check(ErrorLevel, msg); ce != nil {\n\t\tce.Write(fields...)\n\t}\n}\n\n// DPanic logs a message at DPanicLevel. The message includes any fields\n// passed at the log site, as well as any fields accumulated on the logger.\n//\n// If the logger is in development mode, it then panics (DPanic means\n// \"development panic\"). This is useful for catching errors that are\n// recoverable, but shouldn't ever happen.\nfunc (log *Logger) DPanic(msg string, fields ...Field) {\n\tif ce := log.check(DPanicLevel, msg); ce != nil {\n\t\tce.Write(fields...)\n\t}\n}\n\n// Panic logs a message at PanicLevel. The message includes any fields passed\n// at the log site, as well as any fields accumulated on the logger.\n//\n// The logger then panics, even if logging at PanicLevel is disabled.\nfunc (log *Logger) Panic(msg string, fields ...Field) {\n\tif ce := log.check(PanicLevel, msg); ce != nil {\n\t\tce.Write(fields...)\n\t}\n}\n\n// Fatal logs a message at FatalLevel. The message includes any fields passed\n// at the log site, as well as any fields accumulated on the logger.\n//\n// The logger then calls os.Exit(1), even if logging at FatalLevel is\n// disabled.\nfunc (log *Logger) Fatal(msg string, fields ...Field) {\n\tif ce := log.check(FatalLevel, msg); ce != nil {\n\t\tce.Write(fields...)\n\t}\n}\n\n// Sync calls the underlying Core's Sync method, flushing any buffered log\n// entries. Applications should take care to call Sync before exiting.\nfunc (log *Logger) Sync() error {\n\treturn log.core.Sync()\n}\n\n// Core returns the Logger's underlying zapcore.Core.\nfunc (log *Logger) Core() zapcore.Core {\n\treturn log.core\n}\n\n// Name returns the Logger's underlying name,\n// or an empty string if the logger is unnamed.\nfunc (log *Logger) Name() string {\n\treturn log.name\n}\n\nfunc (log *Logger) clone() *Logger {\n\tclone := *log\n\treturn &clone\n}\n\nfunc (log *Logger) check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry {\n\t// Logger.check must always be called directly by a method in the\n\t// Logger interface (e.g., Check, Info, Fatal).\n\t// This skips Logger.check and the Info/Fatal/Check/etc. method that\n\t// called it.\n\tconst callerSkipOffset = 2\n\n\t// Check the level first to reduce the cost of disabled log calls.\n\t// Since Panic and higher may exit, we skip the optimization for those levels.\n\tif lvl < zapcore.DPanicLevel && !log.core.Enabled(lvl) {\n\t\treturn nil\n\t}\n\n\t// Create basic checked entry thru the core; this will be non-nil if the\n\t// log message will actually be written somewhere.\n\tent := zapcore.Entry{\n\t\tLoggerName: log.name,\n\t\tTime:       log.clock.Now(),\n\t\tLevel:      lvl,\n\t\tMessage:    msg,\n\t}\n\tce := log.core.Check(ent, nil)\n\twillWrite := ce != nil\n\n\t// Set up any required terminal behavior.\n\tswitch ent.Level {\n\tcase zapcore.PanicLevel:\n\t\tce = ce.After(ent, terminalHookOverride(zapcore.WriteThenPanic, log.onPanic))\n\tcase zapcore.FatalLevel:\n\t\tce = ce.After(ent, terminalHookOverride(zapcore.WriteThenFatal, log.onFatal))\n\tcase zapcore.DPanicLevel:\n\t\tif log.development {\n\t\t\tce = ce.After(ent, terminalHookOverride(zapcore.WriteThenPanic, log.onPanic))\n\t\t}\n\t}\n\n\t// Only do further annotation if we're going to write this message; checked\n\t// entries that exist only for terminal behavior don't benefit from\n\t// annotation.\n\tif !willWrite {\n\t\treturn ce\n\t}\n\n\t// Thread the error output through to the CheckedEntry.\n\tce.ErrorOutput = log.errorOutput\n\n\taddStack := log.addStack.Enabled(ce.Level)\n\tif !log.addCaller && !addStack {\n\t\treturn ce\n\t}\n\n\t// Adding the caller or stack trace requires capturing the callers of\n\t// this function. We'll share information between these two.\n\tstackDepth := stacktrace.First\n\tif addStack {\n\t\tstackDepth = stacktrace.Full\n\t}\n\tstack := stacktrace.Capture(log.callerSkip+callerSkipOffset, stackDepth)\n\tdefer stack.Free()\n\n\tif stack.Count() == 0 {\n\t\tif log.addCaller {\n\t\t\t_, _ = fmt.Fprintf(\n\t\t\t\tlog.errorOutput,\n\t\t\t\t\"%v Logger.check error: failed to get caller\\n\",\n\t\t\t\tent.Time.UTC(),\n\t\t\t)\n\t\t\t_ = log.errorOutput.Sync()\n\t\t}\n\t\treturn ce\n\t}\n\n\tframe, more := stack.Next()\n\n\tif log.addCaller {\n\t\tce.Caller = zapcore.EntryCaller{\n\t\t\tDefined:  frame.PC != 0,\n\t\t\tPC:       frame.PC,\n\t\t\tFile:     frame.File,\n\t\t\tLine:     frame.Line,\n\t\t\tFunction: frame.Function,\n\t\t}\n\t}\n\n\tif addStack {\n\t\tbuffer := bufferpool.Get()\n\t\tdefer buffer.Free()\n\n\t\tstackfmt := stacktrace.NewFormatter(buffer)\n\n\t\t// We've already extracted the first frame, so format that\n\t\t// separately and defer to stackfmt for the rest.\n\t\tstackfmt.FormatFrame(frame)\n\t\tif more {\n\t\t\tstackfmt.FormatStack(stack)\n\t\t}\n\t\tce.Stack = buffer.String()\n\t}\n\n\treturn ce\n}\n\nfunc terminalHookOverride(defaultHook, override zapcore.CheckWriteHook) zapcore.CheckWriteHook {\n\t// A nil or WriteThenNoop hook will lead to continued execution after\n\t// a Panic or Fatal log entry, which is unexpected. For example,\n\t//\n\t//   f, err := os.Open(..)\n\t//   if err != nil {\n\t//     log.Fatal(\"cannot open\", zap.Error(err))\n\t//   }\n\t//   fmt.Println(f.Name())\n\t//\n\t// The f.Name() will panic if we continue execution after the log.Fatal.\n\tif override == nil || override == zapcore.WriteThenNoop {\n\t\treturn defaultHook\n\t}\n\treturn override\n}\n"
  },
  {
    "path": "logger_bench_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"errors\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.uber.org/zap/internal/ztest\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\ntype user struct {\n\tName      string\n\tEmail     string\n\tCreatedAt time.Time\n}\n\nfunc (u *user) MarshalLogObject(enc zapcore.ObjectEncoder) error {\n\tenc.AddString(\"name\", u.Name)\n\tenc.AddString(\"email\", u.Email)\n\tenc.AddInt64(\"created_at\", u.CreatedAt.UnixNano())\n\treturn nil\n}\n\nvar _jane = &user{\n\tName:      \"Jane Doe\",\n\tEmail:     \"jane@test.com\",\n\tCreatedAt: time.Date(1980, 1, 1, 12, 0, 0, 0, time.UTC),\n}\n\nfunc withBenchedLogger(b *testing.B, f func(*Logger)) {\n\tlogger := New(\n\t\tzapcore.NewCore(\n\t\t\tzapcore.NewJSONEncoder(NewProductionConfig().EncoderConfig),\n\t\t\t&ztest.Discarder{},\n\t\t\tDebugLevel,\n\t\t))\n\tb.ResetTimer()\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tf(logger)\n\t\t}\n\t})\n}\n\nfunc BenchmarkNoContext(b *testing.B) {\n\twithBenchedLogger(b, func(log *Logger) {\n\t\tlog.Info(\"No context.\")\n\t})\n}\n\nfunc BenchmarkBoolField(b *testing.B) {\n\twithBenchedLogger(b, func(log *Logger) {\n\t\tlog.Info(\"Boolean.\", Bool(\"foo\", true))\n\t})\n}\n\nfunc BenchmarkByteStringField(b *testing.B) {\n\tval := []byte(\"bar\")\n\twithBenchedLogger(b, func(log *Logger) {\n\t\tlog.Info(\"ByteString.\", ByteString(\"foo\", val))\n\t})\n}\n\nfunc BenchmarkFloat64Field(b *testing.B) {\n\twithBenchedLogger(b, func(log *Logger) {\n\t\tlog.Info(\"Floating point.\", Float64(\"foo\", 3.14))\n\t})\n}\n\nfunc BenchmarkIntField(b *testing.B) {\n\twithBenchedLogger(b, func(log *Logger) {\n\t\tlog.Info(\"Integer.\", Int(\"foo\", 42))\n\t})\n}\n\nfunc BenchmarkInt64Field(b *testing.B) {\n\twithBenchedLogger(b, func(log *Logger) {\n\t\tlog.Info(\"64-bit integer.\", Int64(\"foo\", 42))\n\t})\n}\n\nfunc BenchmarkStringField(b *testing.B) {\n\twithBenchedLogger(b, func(log *Logger) {\n\t\tlog.Info(\"Strings.\", String(\"foo\", \"bar\"))\n\t})\n}\n\nfunc BenchmarkStringerField(b *testing.B) {\n\twithBenchedLogger(b, func(log *Logger) {\n\t\tlog.Info(\"Level.\", Stringer(\"foo\", InfoLevel))\n\t})\n}\n\nfunc BenchmarkTimeField(b *testing.B) {\n\tt := time.Unix(0, 0)\n\twithBenchedLogger(b, func(log *Logger) {\n\t\tlog.Info(\"Time.\", Time(\"foo\", t))\n\t})\n}\n\nfunc BenchmarkDurationField(b *testing.B) {\n\twithBenchedLogger(b, func(log *Logger) {\n\t\tlog.Info(\"Duration\", Duration(\"foo\", time.Second))\n\t})\n}\n\nfunc BenchmarkErrorField(b *testing.B) {\n\terr := errors.New(\"egad\")\n\twithBenchedLogger(b, func(log *Logger) {\n\t\tlog.Info(\"Error.\", Error(err))\n\t})\n}\n\nfunc BenchmarkErrorsField(b *testing.B) {\n\terrs := []error{\n\t\terrors.New(\"egad\"),\n\t\terrors.New(\"oh no\"),\n\t\terrors.New(\"dear me\"),\n\t\terrors.New(\"such fail\"),\n\t}\n\twithBenchedLogger(b, func(log *Logger) {\n\t\tlog.Info(\"Errors.\", Errors(\"errors\", errs))\n\t})\n}\n\nfunc BenchmarkStackField(b *testing.B) {\n\twithBenchedLogger(b, func(log *Logger) {\n\t\tlog.Info(\"Error.\", Stack(\"stacktrace\"))\n\t})\n}\n\nfunc BenchmarkObjectField(b *testing.B) {\n\twithBenchedLogger(b, func(log *Logger) {\n\t\tlog.Info(\"Arbitrary ObjectMarshaler.\", Object(\"user\", _jane))\n\t})\n}\n\nfunc BenchmarkReflectField(b *testing.B) {\n\twithBenchedLogger(b, func(log *Logger) {\n\t\tlog.Info(\"Reflection-based serialization.\", Reflect(\"user\", _jane))\n\t})\n}\n\nfunc BenchmarkAddCallerHook(b *testing.B) {\n\tlogger := New(\n\t\tzapcore.NewCore(\n\t\t\tzapcore.NewJSONEncoder(NewProductionConfig().EncoderConfig),\n\t\t\t&ztest.Discarder{},\n\t\t\tInfoLevel,\n\t\t),\n\t\tAddCaller(),\n\t)\n\tb.ResetTimer()\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tlogger.Info(\"Caller.\")\n\t\t}\n\t})\n}\n\nfunc BenchmarkAddCallerAndStacktrace(b *testing.B) {\n\tlogger := New(\n\t\tzapcore.NewCore(\n\t\t\tzapcore.NewJSONEncoder(NewProductionConfig().EncoderConfig),\n\t\t\t&ztest.Discarder{},\n\t\t\tInfoLevel,\n\t\t),\n\t\tAddCaller(),\n\t\tAddStacktrace(WarnLevel),\n\t)\n\tb.ResetTimer()\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tlogger.Warn(\"Caller and stacktrace.\")\n\t\t}\n\t})\n}\n\nfunc Benchmark5WithsUsed(b *testing.B) {\n\tbenchmarkWithUsed(b, (*Logger).With, 5, true)\n}\n\n// This benchmark will be used in future as a\n// baseline for improving\nfunc Benchmark5WithsNotUsed(b *testing.B) {\n\tbenchmarkWithUsed(b, (*Logger).With, 5, false)\n}\n\nfunc Benchmark5WithLazysUsed(b *testing.B) {\n\tbenchmarkWithUsed(b, (*Logger).WithLazy, 5, true)\n}\n\n// This benchmark will be used in future as a\n// baseline for improving\nfunc Benchmark5WithLazysNotUsed(b *testing.B) {\n\tbenchmarkWithUsed(b, (*Logger).WithLazy, 5, false)\n}\n\nfunc benchmarkWithUsed(b *testing.B, withMethod func(*Logger, ...zapcore.Field) *Logger, N int, use bool) {\n\tkeys := make([]string, N)\n\tvalues := make([]string, N)\n\tfor i := 0; i < N; i++ {\n\t\tkeys[i] = \"k\" + strconv.Itoa(i)\n\t\tvalues[i] = \"v\" + strconv.Itoa(i)\n\t}\n\n\tb.ResetTimer()\n\n\twithBenchedLogger(b, func(log *Logger) {\n\t\tfor i := 0; i < N; i++ {\n\t\t\tlog = withMethod(log, String(keys[i], values[i]))\n\t\t}\n\t\tif use {\n\t\t\tlog.Info(\"used\")\n\t\t\treturn\n\t\t}\n\t\truntime.KeepAlive(log)\n\t})\n}\n\nfunc Benchmark10Fields(b *testing.B) {\n\twithBenchedLogger(b, func(log *Logger) {\n\t\tlog.Info(\"Ten fields, passed at the log site.\",\n\t\t\tInt(\"one\", 1),\n\t\t\tInt(\"two\", 2),\n\t\t\tInt(\"three\", 3),\n\t\t\tInt(\"four\", 4),\n\t\t\tInt(\"five\", 5),\n\t\t\tInt(\"six\", 6),\n\t\t\tInt(\"seven\", 7),\n\t\t\tInt(\"eight\", 8),\n\t\t\tInt(\"nine\", 9),\n\t\t\tInt(\"ten\", 10),\n\t\t)\n\t})\n}\n\nfunc Benchmark100Fields(b *testing.B) {\n\tconst batchSize = 50\n\tlogger := New(zapcore.NewCore(\n\t\tzapcore.NewJSONEncoder(NewProductionConfig().EncoderConfig),\n\t\t&ztest.Discarder{},\n\t\tDebugLevel,\n\t))\n\n\t// Don't include allocating these helper slices in the benchmark. Since\n\t// access to them isn't synchronized, we can't run the benchmark in\n\t// parallel.\n\tfirst := make([]Field, batchSize)\n\tsecond := make([]Field, batchSize)\n\tb.ResetTimer()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tfor i := 0; i < batchSize; i++ {\n\t\t\t// We're duplicating keys, but that doesn't affect performance.\n\t\t\tfirst[i] = Int(\"foo\", i)\n\t\t\tsecond[i] = Int(\"foo\", i+batchSize)\n\t\t}\n\t\tlogger.With(first...).Info(\"Child loggers with lots of context.\", second...)\n\t}\n}\n\nfunc BenchmarkAny(b *testing.B) {\n\tkey := \"some-long-string-longer-than-16\"\n\n\ttests := []struct {\n\t\tname   string\n\t\ttyped  func() Field\n\t\tanyArg any\n\t}{\n\t\t{\n\t\t\tname:   \"string\",\n\t\t\ttyped:  func() Field { return String(key, \"yet-another-long-string\") },\n\t\t\tanyArg: \"yet-another-long-string\",\n\t\t},\n\t\t{\n\t\t\tname:   \"stringer\",\n\t\t\ttyped:  func() Field { return Stringer(key, InfoLevel) },\n\t\t\tanyArg: InfoLevel,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tb.Run(tt.name, func(b *testing.B) {\n\t\t\tb.Run(\"field-only\", func(b *testing.B) {\n\t\t\t\tb.Run(\"typed\", func(b *testing.B) {\n\t\t\t\t\twithBenchedLogger(b, func(log *Logger) {\n\t\t\t\t\t\tf := tt.typed()\n\t\t\t\t\t\truntime.KeepAlive(f)\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t\tb.Run(\"any\", func(b *testing.B) {\n\t\t\t\t\twithBenchedLogger(b, func(log *Logger) {\n\t\t\t\t\t\tf := Any(key, tt.anyArg)\n\t\t\t\t\t\truntime.KeepAlive(f)\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t})\n\t\t\tb.Run(\"log\", func(b *testing.B) {\n\t\t\t\tb.Run(\"typed\", func(b *testing.B) {\n\t\t\t\t\twithBenchedLogger(b, func(log *Logger) {\n\t\t\t\t\t\tlog.Info(\"\", tt.typed())\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t\tb.Run(\"any\", func(b *testing.B) {\n\t\t\t\t\twithBenchedLogger(b, func(log *Logger) {\n\t\t\t\t\t\tlog.Info(\"\", Any(key, tt.anyArg))\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t})\n\t\t\tb.Run(\"log-go\", func(b *testing.B) {\n\t\t\t\tb.Run(\"typed\", func(b *testing.B) {\n\t\t\t\t\twithBenchedLogger(b, func(log *Logger) {\n\t\t\t\t\t\tvar wg sync.WaitGroup\n\t\t\t\t\t\twg.Add(1)\n\t\t\t\t\t\tgo func() {\n\t\t\t\t\t\t\tlog.Info(\"\", tt.typed())\n\t\t\t\t\t\t\twg.Done()\n\t\t\t\t\t\t}()\n\t\t\t\t\t\twg.Wait()\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t\tb.Run(\"any\", func(b *testing.B) {\n\t\t\t\t\twithBenchedLogger(b, func(log *Logger) {\n\t\t\t\t\t\tvar wg sync.WaitGroup\n\t\t\t\t\t\twg.Add(1)\n\t\t\t\t\t\tgo func() {\n\t\t\t\t\t\t\tlog.Info(\"\", Any(key, tt.anyArg))\n\t\t\t\t\t\t\twg.Done()\n\t\t\t\t\t\t}()\n\t\t\t\t\t\twg.Wait()\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "logger_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\n\t\"go.uber.org/zap/internal/exit\"\n\t\"go.uber.org/zap/internal/ztest\"\n\t\"go.uber.org/zap/zapcore\"\n\t\"go.uber.org/zap/zaptest/observer\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc makeCountingHook() (func(zapcore.Entry) error, *atomic.Int64) {\n\tcount := &atomic.Int64{}\n\th := func(zapcore.Entry) error {\n\t\tcount.Add(1)\n\t\treturn nil\n\t}\n\treturn h, count\n}\n\nfunc TestLoggerAtomicLevel(t *testing.T) {\n\t// Test that the dynamic level applies to all ancestors and descendants.\n\tdl := NewAtomicLevel()\n\n\twithLogger(t, dl, nil, func(grandparent *Logger, _ *observer.ObservedLogs) {\n\t\tparent := grandparent.With(Int(\"generation\", 1))\n\t\tchild := parent.With(Int(\"generation\", 2))\n\n\t\ttests := []struct {\n\t\t\tsetLevel  zapcore.Level\n\t\t\ttestLevel zapcore.Level\n\t\t\tenabled   bool\n\t\t}{\n\t\t\t{DebugLevel, DebugLevel, true},\n\t\t\t{InfoLevel, DebugLevel, false},\n\t\t\t{WarnLevel, PanicLevel, true},\n\t\t}\n\n\t\tfor _, tt := range tests {\n\t\t\tdl.SetLevel(tt.setLevel)\n\t\t\tfor _, logger := range []*Logger{grandparent, parent, child} {\n\t\t\t\tif tt.enabled {\n\t\t\t\t\tassert.NotNil(\n\t\t\t\t\t\tt,\n\t\t\t\t\t\tlogger.Check(tt.testLevel, \"\"),\n\t\t\t\t\t\t\"Expected level %s to be enabled after setting level %s.\", tt.testLevel, tt.setLevel,\n\t\t\t\t\t)\n\t\t\t\t} else {\n\t\t\t\t\tassert.Nil(\n\t\t\t\t\t\tt,\n\t\t\t\t\t\tlogger.Check(tt.testLevel, \"\"),\n\t\t\t\t\t\t\"Expected level %s to be enabled after setting level %s.\", tt.testLevel, tt.setLevel,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestLoggerLevel(t *testing.T) {\n\tlevels := []zapcore.Level{\n\t\tDebugLevel,\n\t\tInfoLevel,\n\t\tWarnLevel,\n\t\tErrorLevel,\n\t\tDPanicLevel,\n\t\tPanicLevel,\n\t\tFatalLevel,\n\t}\n\n\tfor _, lvl := range levels {\n\t\tlvl := lvl\n\t\tt.Run(lvl.String(), func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tcore, _ := observer.New(lvl)\n\t\t\tlog := New(core)\n\t\t\tassert.Equal(t, lvl, log.Level())\n\t\t})\n\t}\n\n\tt.Run(\"Nop\", func(t *testing.T) {\n\t\tassert.Equal(t, zapcore.InvalidLevel, NewNop().Level())\n\t})\n}\n\nfunc TestLoggerInitialFields(t *testing.T) {\n\tfieldOpts := opts(Fields(Int(\"foo\", 42), String(\"bar\", \"baz\")))\n\twithLogger(t, DebugLevel, fieldOpts, func(logger *Logger, logs *observer.ObservedLogs) {\n\t\tlogger.Info(\"\")\n\t\tassert.Equal(\n\t\t\tt,\n\t\t\tobserver.LoggedEntry{Context: []Field{Int(\"foo\", 42), String(\"bar\", \"baz\")}},\n\t\t\tlogs.AllUntimed()[0],\n\t\t\t\"Unexpected output with initial fields set.\",\n\t\t)\n\t})\n}\n\nfunc TestLoggerWith(t *testing.T) {\n\ttests := []struct {\n\t\tname          string\n\t\tinitialFields []Field\n\t\twithMethod    func(*Logger, ...Field) *Logger\n\t}{\n\t\t{\n\t\t\t\"regular non lazy logger\",\n\t\t\t[]Field{Int(\"foo\", 42)},\n\t\t\t(*Logger).With,\n\t\t},\n\t\t{\n\t\t\t\"regular non lazy logger no initial fields\",\n\t\t\t[]Field{},\n\t\t\t(*Logger).With,\n\t\t},\n\t\t{\n\t\t\t\"lazy with logger\",\n\t\t\t[]Field{Int(\"foo\", 42)},\n\t\t\t(*Logger).WithLazy,\n\t\t},\n\t\t{\n\t\t\t\"lazy with logger no initial fields\",\n\t\t\t[]Field{},\n\t\t\t(*Logger).WithLazy,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\twithLogger(t, DebugLevel, opts(Fields(tt.initialFields...)), func(logger *Logger, logs *observer.ObservedLogs) {\n\t\t\t\t// Child loggers should have copy-on-write semantics, so two children\n\t\t\t\t// shouldn't stomp on each other's fields or affect the parent's fields.\n\t\t\t\ttt.withMethod(logger).Info(\"\")\n\t\t\t\ttt.withMethod(logger, String(\"one\", \"two\")).Info(\"\")\n\t\t\t\ttt.withMethod(logger, String(\"three\", \"four\")).Info(\"\")\n\t\t\t\ttt.withMethod(logger, String(\"five\", \"six\")).With(String(\"seven\", \"eight\")).Info(\"\")\n\t\t\t\tlogger.Info(\"\")\n\n\t\t\t\tassert.Equal(t, []observer.LoggedEntry{\n\t\t\t\t\t{Context: tt.initialFields},\n\t\t\t\t\t{Context: append(tt.initialFields, String(\"one\", \"two\"))},\n\t\t\t\t\t{Context: append(tt.initialFields, String(\"three\", \"four\"))},\n\t\t\t\t\t{Context: append(tt.initialFields, String(\"five\", \"six\"), String(\"seven\", \"eight\"))},\n\t\t\t\t\t{Context: tt.initialFields},\n\t\t\t\t}, logs.AllUntimed(), \"Unexpected cross-talk between child loggers.\")\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestLoggerWithCaptures(t *testing.T) {\n\ttype withF func(*Logger, ...Field) *Logger\n\ttests := []struct {\n\t\tname        string\n\t\twithMethods []withF\n\t\twantJSON    []string\n\t}{\n\t\t{\n\t\t\tname:        \"regular with captures arguments at time of With\",\n\t\t\twithMethods: []withF{(*Logger).With},\n\t\t\twantJSON: []string{\n\t\t\t\t`{\n\t\t\t\t\t\"m\": \"hello 0\",\n\t\t\t\t\t\"a0\": [0],\n\t\t\t\t\t\"b0\": [1]\n\t\t\t\t}`,\n\t\t\t\t`{\n\t\t\t\t\t\"m\": \"world 0\",\n\t\t\t\t\t\"a0\": [0],\n\t\t\t\t\t\"c0\": [2]\n\t\t\t\t}`,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:        \"lazy with captures arguments at time of With or Logging\",\n\t\t\twithMethods: []withF{(*Logger).WithLazy},\n\t\t\twantJSON: []string{\n\t\t\t\t`{\n\t\t\t\t\t\"m\": \"hello 0\",\n\t\t\t\t\t\"a0\": [1],\n\t\t\t\t\t\"b0\": [1]\n\t\t\t\t}`,\n\t\t\t\t`{\n\t\t\t\t\t\"m\": \"world 0\",\n\t\t\t\t\t\"a0\": [1],\n\t\t\t\t\t\"c0\": [2]\n\t\t\t\t}`,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:        \"2x With captures arguments at time of each With\",\n\t\t\twithMethods: []withF{(*Logger).With, (*Logger).With},\n\t\t\twantJSON: []string{\n\t\t\t\t`{\n\t\t\t\t\t\"m\": \"hello 0\",\n\t\t\t\t\t\"a0\": [0],\n\t\t\t\t\t\"b0\": [1]\n\t\t\t\t}`,\n\t\t\t\t`{\n\t\t\t\t\t\"m\": \"world 0\",\n\t\t\t\t\t\"a0\": [0],\n\t\t\t\t\t\"c0\": [2]\n\t\t\t\t}`,\n\t\t\t\t`{\n\t\t\t\t\t\"m\": \"hello 1\",\n\t\t\t\t\t\"a0\": [0],\n\t\t\t\t\t\"c0\": [2],\n\t\t\t\t\t\"a1\": [10],\n\t\t\t\t\t\"b1\": [11]\n\t\t\t\t}`,\n\t\t\t\t`{\n\t\t\t\t\t\"m\": \"world 1\",\n\t\t\t\t\t\"a0\": [0],\n\t\t\t\t\t\"c0\": [2],\n\t\t\t\t\t\"a1\": [10],\n\t\t\t\t\t\"c1\": [12]\n\t\t\t\t}`,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:        \"2x WithLazy. Captures arguments only at logging time.\",\n\t\t\twithMethods: []withF{(*Logger).WithLazy, (*Logger).WithLazy},\n\t\t\twantJSON: []string{\n\t\t\t\t`{\n\t\t\t\t\t\"m\": \"hello 0\",\n\t\t\t\t\t\"a0\": [1],\n\t\t\t\t\t\"b0\": [1]\n\t\t\t\t}`,\n\t\t\t\t`{\n\t\t\t\t\t\"m\": \"world 0\",\n\t\t\t\t\t\"a0\": [1],\n\t\t\t\t\t\"c0\": [2]\n\t\t\t\t}`,\n\t\t\t\t`{\n\t\t\t\t\t\"m\": \"hello 1\",\n\t\t\t\t\t\"a0\": [1],\n\t\t\t\t\t\"c0\": [2],\n\t\t\t\t\t\"a1\": [11],\n\t\t\t\t\t\"b1\": [11]\n\t\t\t\t}`,\n\t\t\t\t`{\n\t\t\t\t\t\"m\": \"world 1\",\n\t\t\t\t\t\"a0\": [1],\n\t\t\t\t\t\"c0\": [2],\n\t\t\t\t\t\"a1\": [11],\n\t\t\t\t\t\"c1\": [12]\n\t\t\t\t}`,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:        \"WithLazy then With\",\n\t\t\twithMethods: []withF{(*Logger).WithLazy, (*Logger).With},\n\t\t\twantJSON: []string{\n\t\t\t\t`{\n\t\t\t\t\t\"m\": \"hello 0\",\n\t\t\t\t\t\"a0\": [1],\n\t\t\t\t\t\"b0\": [1]\n\t\t\t\t}`,\n\t\t\t\t`{\n\t\t\t\t\t\"m\": \"world 0\",\n\t\t\t\t\t\"a0\": [1],\n\t\t\t\t\t\"c0\": [2]\n\t\t\t\t}`,\n\t\t\t\t`{\n\t\t\t\t\t\"m\": \"hello 1\",\n\t\t\t\t\t\"a0\": [1],\n\t\t\t\t\t\"c0\": [2],\n\t\t\t\t\t\"a1\": [10],\n\t\t\t\t\t\"b1\": [11]\n\t\t\t\t}`,\n\t\t\t\t`{\n\t\t\t\t\t\"m\": \"world 1\",\n\t\t\t\t\t\"a0\": [1],\n\t\t\t\t\t\"c0\": [2],\n\t\t\t\t\t\"a1\": [10],\n\t\t\t\t\t\"c1\": [12]\n\t\t\t\t}`,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:        \"With then WithLazy\",\n\t\t\twithMethods: []withF{(*Logger).With, (*Logger).WithLazy},\n\t\t\twantJSON: []string{\n\t\t\t\t`{\n\t\t\t\t\t\"m\": \"hello 0\",\n\t\t\t\t\t\"a0\": [0],\n\t\t\t\t\t\"b0\": [1]\n\t\t\t\t}`,\n\t\t\t\t`{\n\t\t\t\t\t\"m\": \"world 0\",\n\t\t\t\t\t\"a0\": [0],\n\t\t\t\t\t\"c0\": [2]\n\t\t\t\t}`,\n\t\t\t\t`{\n\t\t\t\t\t\"m\": \"hello 1\",\n\t\t\t\t\t\"a0\": [0],\n\t\t\t\t\t\"c0\": [2],\n\t\t\t\t\t\"a1\": [11],\n\t\t\t\t\t\"b1\": [11]\n\t\t\t\t}`,\n\t\t\t\t`{\n\t\t\t\t\t\"m\": \"world 1\",\n\t\t\t\t\t\"a0\": [0],\n\t\t\t\t\t\"c0\": [2],\n\t\t\t\t\t\"a1\": [11],\n\t\t\t\t\t\"c1\": [12]\n\t\t\t\t}`,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tenc := zapcore.NewJSONEncoder(zapcore.EncoderConfig{\n\t\t\t\tMessageKey: \"m\",\n\t\t\t})\n\n\t\t\tvar bs ztest.Buffer\n\t\t\tlogger := New(zapcore.NewCore(enc, &bs, DebugLevel))\n\n\t\t\tfor i, withMethod := range tt.withMethods {\n\n\t\t\t\tiStr := strconv.Itoa(i)\n\t\t\t\tx := 10 * i\n\t\t\t\tarr := zapcore.ArrayMarshalerFunc(func(enc zapcore.ArrayEncoder) error {\n\t\t\t\t\tenc.AppendInt(x)\n\t\t\t\t\treturn nil\n\t\t\t\t})\n\n\t\t\t\t// Demonstrate the arguments are captured when With() and Info() are invoked.\n\t\t\t\tlogger = withMethod(logger, Array(\"a\"+iStr, arr))\n\t\t\t\tx++\n\t\t\t\tlogger.Info(fmt.Sprintf(\"hello %d\", i), Array(\"b\"+iStr, arr))\n\t\t\t\tx++\n\t\t\t\tlogger = withMethod(logger, Array(\"c\"+iStr, arr))\n\t\t\t\tlogger.Info(fmt.Sprintf(\"world %d\", i))\n\t\t\t}\n\n\t\t\tif lines := bs.Lines(); assert.Len(t, lines, len(tt.wantJSON)) {\n\t\t\t\tfor i, want := range tt.wantJSON {\n\t\t\t\t\tassert.JSONEq(t, want, lines[i], \"Unexpected output from the %d'th log.\", i)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestLoggerLogPanic(t *testing.T) {\n\tfor _, tt := range []struct {\n\t\tdo       func(*Logger)\n\t\tshould   bool\n\t\texpected string\n\t}{\n\t\t{func(logger *Logger) { logger.Check(PanicLevel, \"foo\").Write() }, true, \"foo\"},\n\t\t{func(logger *Logger) { logger.Log(PanicLevel, \"bar\") }, true, \"bar\"},\n\t\t{func(logger *Logger) { logger.Panic(\"baz\") }, true, \"baz\"},\n\t} {\n\t\twithLogger(t, DebugLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) {\n\t\t\tif tt.should {\n\t\t\t\tassert.Panics(t, func() { tt.do(logger) }, \"Expected panic\")\n\t\t\t} else {\n\t\t\t\tassert.NotPanics(t, func() { tt.do(logger) }, \"Expected no panic\")\n\t\t\t}\n\n\t\t\toutput := logs.AllUntimed()\n\t\t\tassert.Equal(t, 1, len(output), \"Unexpected number of logs.\")\n\t\t\tassert.Equal(t, 0, len(output[0].Context), \"Unexpected context on first log.\")\n\t\t\tassert.Equal(\n\t\t\t\tt,\n\t\t\t\tzapcore.Entry{Message: tt.expected, Level: PanicLevel},\n\t\t\t\toutput[0].Entry,\n\t\t\t\t\"Unexpected output from panic-level Log.\",\n\t\t\t)\n\t\t})\n\t}\n}\n\nfunc TestLoggerLogFatal(t *testing.T) {\n\tfor _, tt := range []struct {\n\t\tdo       func(*Logger)\n\t\texpected string\n\t}{\n\t\t{func(logger *Logger) { logger.Check(FatalLevel, \"foo\").Write() }, \"foo\"},\n\t\t{func(logger *Logger) { logger.Log(FatalLevel, \"bar\") }, \"bar\"},\n\t\t{func(logger *Logger) { logger.Fatal(\"baz\") }, \"baz\"},\n\t} {\n\t\twithLogger(t, DebugLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) {\n\t\t\tstub := exit.WithStub(func() {\n\t\t\t\ttt.do(logger)\n\t\t\t})\n\t\t\tassert.True(t, stub.Exited, \"Expected Fatal logger call to terminate process.\")\n\t\t\toutput := logs.AllUntimed()\n\t\t\tassert.Equal(t, 1, len(output), \"Unexpected number of logs.\")\n\t\t\tassert.Equal(t, 0, len(output[0].Context), \"Unexpected context on first log.\")\n\t\t\tassert.Equal(\n\t\t\t\tt,\n\t\t\t\tzapcore.Entry{Message: tt.expected, Level: FatalLevel},\n\t\t\t\toutput[0].Entry,\n\t\t\t\t\"Unexpected output from fatal-level Log.\",\n\t\t\t)\n\t\t})\n\t}\n}\n\nfunc TestLoggerLeveledMethods(t *testing.T) {\n\twithLogger(t, DebugLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) {\n\t\ttests := []struct {\n\t\t\tmethod        func(string, ...Field)\n\t\t\texpectedLevel zapcore.Level\n\t\t}{\n\t\t\t{logger.Debug, DebugLevel},\n\t\t\t{logger.Info, InfoLevel},\n\t\t\t{logger.Warn, WarnLevel},\n\t\t\t{logger.Error, ErrorLevel},\n\t\t\t{logger.DPanic, DPanicLevel},\n\t\t}\n\t\tfor i, tt := range tests {\n\t\t\ttt.method(\"\")\n\t\t\toutput := logs.AllUntimed()\n\t\t\tassert.Equal(t, i+1, len(output), \"Unexpected number of logs.\")\n\t\t\tassert.Equal(t, 0, len(output[i].Context), \"Unexpected context on first log.\")\n\t\t\tassert.Equal(\n\t\t\t\tt,\n\t\t\t\tzapcore.Entry{Level: tt.expectedLevel},\n\t\t\t\toutput[i].Entry,\n\t\t\t\t\"Unexpected output from %s-level logger method.\", tt.expectedLevel)\n\t\t}\n\t})\n}\n\nfunc TestLoggerLogLevels(t *testing.T) {\n\twithLogger(t, DebugLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) {\n\t\tlevels := []zapcore.Level{\n\t\t\tDebugLevel,\n\t\t\tInfoLevel,\n\t\t\tWarnLevel,\n\t\t\tErrorLevel,\n\t\t\tDPanicLevel,\n\t\t}\n\t\tfor i, level := range levels {\n\t\t\tlogger.Log(level, \"\")\n\t\t\toutput := logs.AllUntimed()\n\t\t\tassert.Equal(t, i+1, len(output), \"Unexpected number of logs.\")\n\t\t\tassert.Equal(t, 0, len(output[i].Context), \"Unexpected context on first log.\")\n\t\t\tassert.Equal(\n\t\t\t\tt,\n\t\t\t\tzapcore.Entry{Level: level},\n\t\t\t\toutput[i].Entry,\n\t\t\t\t\"Unexpected output from %s-level logger method.\", level)\n\t\t}\n\t})\n}\n\nfunc TestLoggerAlwaysPanics(t *testing.T) {\n\t// Users can disable writing out panic-level logs, but calls to logger.Panic()\n\t// should still call panic().\n\twithLogger(t, FatalLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) {\n\t\tmsg := \"Even if output is disabled, logger.Panic should always panic.\"\n\t\tassert.Panics(t, func() { logger.Panic(\"foo\") }, msg)\n\t\tassert.Panics(t, func() { logger.Log(PanicLevel, \"foo\") }, msg)\n\t\tassert.Panics(t, func() {\n\t\t\tif ce := logger.Check(PanicLevel, \"foo\"); ce != nil {\n\t\t\t\tce.Write()\n\t\t\t}\n\t\t}, msg)\n\t\tassert.Equal(t, 0, logs.Len(), \"Panics shouldn't be written out if PanicLevel is disabled.\")\n\t})\n}\n\nfunc TestLoggerAlwaysFatals(t *testing.T) {\n\t// Users can disable writing out fatal-level logs, but calls to logger.Fatal()\n\t// should still terminate the process.\n\twithLogger(t, FatalLevel+1, nil, func(logger *Logger, logs *observer.ObservedLogs) {\n\t\tstub := exit.WithStub(func() { logger.Fatal(\"\") })\n\t\tassert.True(t, stub.Exited, \"Expected calls to logger.Fatal to terminate process.\")\n\n\t\tstub = exit.WithStub(func() { logger.Log(FatalLevel, \"\") })\n\t\tassert.True(t, stub.Exited, \"Expected calls to logger.Fatal to terminate process.\")\n\n\t\tstub = exit.WithStub(func() {\n\t\t\tif ce := logger.Check(FatalLevel, \"\"); ce != nil {\n\t\t\t\tce.Write()\n\t\t\t}\n\t\t})\n\t\tassert.True(t, stub.Exited, \"Expected calls to logger.Check(FatalLevel, ...) to terminate process.\")\n\n\t\tassert.Equal(t, 0, logs.Len(), \"Shouldn't write out logs when fatal-level logging is disabled.\")\n\t})\n}\n\nfunc TestLoggerDPanic(t *testing.T) {\n\twithLogger(t, DebugLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) {\n\t\tassert.NotPanics(t, func() { logger.DPanic(\"\") })\n\t\tassert.Equal(\n\t\t\tt,\n\t\t\t[]observer.LoggedEntry{{Entry: zapcore.Entry{Level: DPanicLevel}, Context: []Field{}}},\n\t\t\tlogs.AllUntimed(),\n\t\t\t\"Unexpected log output from DPanic in production mode.\",\n\t\t)\n\t})\n\twithLogger(t, DebugLevel, opts(Development()), func(logger *Logger, logs *observer.ObservedLogs) {\n\t\tassert.Panics(t, func() { logger.DPanic(\"\") })\n\t\tassert.Equal(\n\t\t\tt,\n\t\t\t[]observer.LoggedEntry{{Entry: zapcore.Entry{Level: DPanicLevel}, Context: []Field{}}},\n\t\t\tlogs.AllUntimed(),\n\t\t\t\"Unexpected log output from DPanic in development mode.\",\n\t\t)\n\t})\n}\n\nfunc TestLoggerNoOpsDisabledLevels(t *testing.T) {\n\twithLogger(t, WarnLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) {\n\t\tlogger.Info(\"silence!\")\n\t\tassert.Equal(\n\t\t\tt,\n\t\t\t[]observer.LoggedEntry{},\n\t\t\tlogs.AllUntimed(),\n\t\t\t\"Expected logging at a disabled level to produce no output.\",\n\t\t)\n\t})\n}\n\nfunc TestLoggerNames(t *testing.T) {\n\ttests := []struct {\n\t\tnames    []string\n\t\texpected string\n\t}{\n\t\t{nil, \"\"},\n\t\t{[]string{\"\"}, \"\"},\n\t\t{[]string{\"foo\"}, \"foo\"},\n\t\t{[]string{\"foo\", \"\"}, \"foo\"},\n\t\t{[]string{\"foo\", \"bar\"}, \"foo.bar\"},\n\t\t{[]string{\"foo.bar\", \"baz\"}, \"foo.bar.baz\"},\n\t\t// Garbage in, garbage out.\n\t\t{[]string{\"foo.\", \"bar\"}, \"foo..bar\"},\n\t\t{[]string{\"foo\", \".bar\"}, \"foo..bar\"},\n\t\t{[]string{\"foo.\", \".bar\"}, \"foo...bar\"},\n\t}\n\n\tfor _, tt := range tests {\n\t\twithLogger(t, DebugLevel, nil, func(log *Logger, logs *observer.ObservedLogs) {\n\t\t\tfor _, n := range tt.names {\n\t\t\t\tlog = log.Named(n)\n\t\t\t}\n\t\t\tlog.Info(\"\")\n\t\t\trequire.Equal(t, 1, logs.Len(), \"Expected only one log entry to be written.\")\n\t\t\tassert.Equal(t, tt.expected, logs.AllUntimed()[0].LoggerName, \"Unexpected logger name from entry.\")\n\t\t\tassert.Equal(t, tt.expected, log.Name(), \"Unexpected logger name.\")\n\t\t})\n\t\twithSugar(t, DebugLevel, nil, func(log *SugaredLogger, logs *observer.ObservedLogs) {\n\t\t\tfor _, n := range tt.names {\n\t\t\t\tlog = log.Named(n)\n\t\t\t}\n\t\t\tlog.Infow(\"\")\n\t\t\trequire.Equal(t, 1, logs.Len(), \"Expected only one log entry to be written.\")\n\t\t\tassert.Equal(t, tt.expected, logs.AllUntimed()[0].LoggerName, \"Unexpected logger name from entry.\")\n\t\t\tassert.Equal(t, tt.expected, log.base.Name(), \"Unexpected logger name.\")\n\t\t})\n\t}\n}\n\nfunc TestLoggerWriteFailure(t *testing.T) {\n\terrSink := &ztest.Buffer{}\n\tlogger := New(\n\t\tzapcore.NewCore(\n\t\t\tzapcore.NewJSONEncoder(NewProductionConfig().EncoderConfig),\n\t\t\tzapcore.Lock(zapcore.AddSync(ztest.FailWriter{})),\n\t\t\tDebugLevel,\n\t\t),\n\t\tErrorOutput(errSink),\n\t)\n\n\tlogger.Info(\"foo\")\n\t// Should log the error.\n\tassert.Regexp(t, `write error: failed`, errSink.Stripped(), \"Expected to log the error to the error output.\")\n\tassert.True(t, errSink.Called(), \"Expected logging an internal error to call Sync the error sink.\")\n}\n\nfunc TestLoggerSync(t *testing.T) {\n\twithLogger(t, DebugLevel, nil, func(logger *Logger, _ *observer.ObservedLogs) {\n\t\tassert.NoError(t, logger.Sync(), \"Expected syncing a test logger to succeed.\")\n\t\tassert.NoError(t, logger.Sugar().Sync(), \"Expected syncing a sugared logger to succeed.\")\n\t})\n}\n\nfunc TestLoggerSyncFail(t *testing.T) {\n\tnoSync := &ztest.Buffer{}\n\terr := errors.New(\"fail\")\n\tnoSync.SetError(err)\n\tlogger := New(zapcore.NewCore(\n\t\tzapcore.NewJSONEncoder(zapcore.EncoderConfig{}),\n\t\tnoSync,\n\t\tDebugLevel,\n\t))\n\tassert.Equal(t, err, logger.Sync(), \"Expected Logger.Sync to propagate errors.\")\n\tassert.Equal(t, err, logger.Sugar().Sync(), \"Expected SugaredLogger.Sync to propagate errors.\")\n}\n\nfunc TestLoggerAddCaller(t *testing.T) {\n\ttests := []struct {\n\t\toptions []Option\n\t\tpat     string\n\t}{\n\t\t{opts(), `^undefined$`},\n\t\t{opts(WithCaller(false)), `^undefined$`},\n\t\t{opts(AddCaller()), `.+/logger_test.go:[\\d]+$`},\n\t\t{opts(AddCaller(), WithCaller(false)), `^undefined$`},\n\t\t{opts(WithCaller(true)), `.+/logger_test.go:[\\d]+$`},\n\t\t{opts(WithCaller(true), WithCaller(false)), `^undefined$`},\n\t\t{opts(AddCaller(), AddCallerSkip(1), AddCallerSkip(-1)), `.+/logger_test.go:[\\d]+$`},\n\t\t{opts(AddCaller(), AddCallerSkip(1)), `.+/common_test.go:[\\d]+$`},\n\t\t{opts(AddCaller(), AddCallerSkip(1), AddCallerSkip(3)), `.+/src/runtime/.*:[\\d]+$`},\n\t}\n\tfor _, tt := range tests {\n\t\twithLogger(t, DebugLevel, tt.options, func(logger *Logger, logs *observer.ObservedLogs) {\n\t\t\t// Make sure that sugaring and desugaring resets caller skip properly.\n\t\t\tlogger = logger.Sugar().Desugar()\n\t\t\tlogger.Info(\"\")\n\t\t\toutput := logs.AllUntimed()\n\t\t\tassert.Equal(t, 1, len(output), \"Unexpected number of logs written out.\")\n\t\t\tassert.Regexp(\n\t\t\t\tt,\n\t\t\t\ttt.pat,\n\t\t\t\toutput[0].Caller,\n\t\t\t\t\"Expected to find package name and file name in output.\",\n\t\t\t)\n\t\t})\n\t}\n}\n\nfunc TestLoggerAddCallerFunction(t *testing.T) {\n\ttests := []struct {\n\t\toptions         []Option\n\t\tloggerFunction  string\n\t\tsugaredFunction string\n\t}{\n\t\t{\n\t\t\toptions:         opts(),\n\t\t\tloggerFunction:  \"\",\n\t\t\tsugaredFunction: \"\",\n\t\t},\n\t\t{\n\t\t\toptions:         opts(WithCaller(false)),\n\t\t\tloggerFunction:  \"\",\n\t\t\tsugaredFunction: \"\",\n\t\t},\n\t\t{\n\t\t\toptions:         opts(AddCaller()),\n\t\t\tloggerFunction:  \"go.uber.org/zap.infoLog\",\n\t\t\tsugaredFunction: \"go.uber.org/zap.infoLogSugared\",\n\t\t},\n\t\t{\n\t\t\toptions:         opts(AddCaller(), WithCaller(false)),\n\t\t\tloggerFunction:  \"\",\n\t\t\tsugaredFunction: \"\",\n\t\t},\n\t\t{\n\t\t\toptions:         opts(WithCaller(true)),\n\t\t\tloggerFunction:  \"go.uber.org/zap.infoLog\",\n\t\t\tsugaredFunction: \"go.uber.org/zap.infoLogSugared\",\n\t\t},\n\t\t{\n\t\t\toptions:         opts(WithCaller(true), WithCaller(false)),\n\t\t\tloggerFunction:  \"\",\n\t\t\tsugaredFunction: \"\",\n\t\t},\n\t\t{\n\t\t\toptions:         opts(AddCaller(), AddCallerSkip(1), AddCallerSkip(-1)),\n\t\t\tloggerFunction:  \"go.uber.org/zap.infoLog\",\n\t\t\tsugaredFunction: \"go.uber.org/zap.infoLogSugared\",\n\t\t},\n\t\t{\n\t\t\toptions:         opts(AddCaller(), AddCallerSkip(2)),\n\t\t\tloggerFunction:  \"go.uber.org/zap.withLogger\",\n\t\t\tsugaredFunction: \"go.uber.org/zap.withLogger\",\n\t\t},\n\t\t{\n\t\t\toptions:         opts(AddCaller(), AddCallerSkip(2), AddCallerSkip(3)),\n\t\t\tloggerFunction:  \"runtime.goexit\",\n\t\t\tsugaredFunction: \"runtime.goexit\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\twithLogger(t, DebugLevel, tt.options, func(logger *Logger, logs *observer.ObservedLogs) {\n\t\t\t// Make sure that sugaring and desugaring resets caller skip properly.\n\t\t\tlogger = logger.Sugar().Desugar()\n\t\t\tinfoLog(logger, \"\")\n\t\t\tinfoLogSugared(logger.Sugar(), \"\")\n\t\t\tinfoLog(logger.Sugar().Desugar(), \"\")\n\n\t\t\tentries := logs.AllUntimed()\n\t\t\tassert.Equal(t, 3, len(entries), \"Unexpected number of logs written out.\")\n\t\t\tfor _, entry := range []observer.LoggedEntry{entries[0], entries[2]} {\n\t\t\t\tassert.Regexp(\n\t\t\t\t\tt,\n\t\t\t\t\ttt.loggerFunction,\n\t\t\t\t\tentry.Caller.Function,\n\t\t\t\t\t\"Expected to find function name in output.\",\n\t\t\t\t)\n\t\t\t}\n\t\t\tassert.Regexp(\n\t\t\t\tt,\n\t\t\t\ttt.sugaredFunction,\n\t\t\t\tentries[1].Caller.Function,\n\t\t\t\t\"Expected to find function name in output.\",\n\t\t\t)\n\t\t})\n\t}\n}\n\nfunc TestLoggerAddCallerFail(t *testing.T) {\n\terrBuf := &ztest.Buffer{}\n\twithLogger(t, DebugLevel, opts(AddCaller(), AddCallerSkip(1e3), ErrorOutput(errBuf)), func(log *Logger, logs *observer.ObservedLogs) {\n\t\tlog.Info(\"Failure.\")\n\t\tassert.Regexp(\n\t\t\tt,\n\t\t\t`Logger.check error: failed to get caller`,\n\t\t\terrBuf.String(),\n\t\t\t\"Didn't find expected failure message.\",\n\t\t)\n\t\tassert.Equal(\n\t\t\tt,\n\t\t\tlogs.AllUntimed()[0].Message,\n\t\t\t\"Failure.\",\n\t\t\t\"Expected original message to survive failures in runtime.Caller.\")\n\t\tassert.Equal(\n\t\t\tt,\n\t\t\tlogs.AllUntimed()[0].Caller.Function,\n\t\t\t\"\",\n\t\t\t\"Expected function name to be empty string.\")\n\t})\n}\n\nfunc TestLoggerReplaceCore(t *testing.T) {\n\treplace := WrapCore(func(zapcore.Core) zapcore.Core {\n\t\treturn zapcore.NewNopCore()\n\t})\n\twithLogger(t, DebugLevel, opts(replace), func(logger *Logger, logs *observer.ObservedLogs) {\n\t\tlogger.Debug(\"\")\n\t\tlogger.Info(\"\")\n\t\tlogger.Warn(\"\")\n\t\tassert.Equal(t, 0, logs.Len(), \"Expected no-op core to write no logs.\")\n\t})\n}\n\nfunc TestLoggerIncreaseLevel(t *testing.T) {\n\twithLogger(t, DebugLevel, opts(IncreaseLevel(WarnLevel)), func(logger *Logger, logs *observer.ObservedLogs) {\n\t\tlogger.Info(\"logger.Info\")\n\t\tlogger.Warn(\"logger.Warn\")\n\t\tlogger.Error(\"logger.Error\")\n\t\trequire.Equal(t, 2, logs.Len(), \"expected only warn + error logs due to IncreaseLevel.\")\n\t\tassert.Equal(\n\t\t\tt,\n\t\t\tlogs.AllUntimed()[0].Message,\n\t\t\t\"logger.Warn\",\n\t\t\t\"Expected first logged message to be warn level message\",\n\t\t)\n\t})\n}\n\nfunc TestLoggerHooks(t *testing.T) {\n\thook, seen := makeCountingHook()\n\twithLogger(t, DebugLevel, opts(Hooks(hook)), func(logger *Logger, logs *observer.ObservedLogs) {\n\t\tlogger.Debug(\"\")\n\t\tlogger.Info(\"\")\n\t})\n\tassert.Equal(t, int64(2), seen.Load(), \"Hook saw an unexpected number of logs.\")\n}\n\nfunc TestLoggerConcurrent(t *testing.T) {\n\twithLogger(t, DebugLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) {\n\t\tchild := logger.With(String(\"foo\", \"bar\"))\n\n\t\twg := &sync.WaitGroup{}\n\t\trunConcurrently(5, 10, wg, func() {\n\t\t\tlogger.Info(\"\", String(\"foo\", \"bar\"))\n\t\t})\n\t\trunConcurrently(5, 10, wg, func() {\n\t\t\tchild.Info(\"\")\n\t\t})\n\n\t\twg.Wait()\n\n\t\t// Make sure the output doesn't contain interspersed entries.\n\t\tassert.Equal(t, 100, logs.Len(), \"Unexpected number of logs written out.\")\n\t\tfor _, obs := range logs.AllUntimed() {\n\t\t\tassert.Equal(\n\t\t\t\tt,\n\t\t\t\tobserver.LoggedEntry{\n\t\t\t\t\tEntry:   zapcore.Entry{Level: InfoLevel},\n\t\t\t\t\tContext: []Field{String(\"foo\", \"bar\")},\n\t\t\t\t},\n\t\t\t\tobs,\n\t\t\t\t\"Unexpected log output.\",\n\t\t\t)\n\t\t}\n\t})\n}\n\nfunc TestLoggerFatalOnNoop(t *testing.T) {\n\texitStub := exit.Stub()\n\tdefer exitStub.Unstub()\n\tcore, _ := observer.New(InfoLevel)\n\n\t// We don't allow a no-op fatal hook.\n\tNew(core, WithFatalHook(zapcore.WriteThenNoop)).Fatal(\"great sadness\")\n\tassert.True(t, exitStub.Exited, \"must exit for WriteThenNoop\")\n\tassert.Equal(t, 1, exitStub.Code, \"must exit with status 1 for WriteThenNoop\")\n}\n\nfunc TestLoggerCustomOnPanic(t *testing.T) {\n\ttests := []struct {\n\t\tmsg          string\n\t\tlevel        zapcore.Level\n\t\topts         []Option\n\t\tfinished     bool\n\t\twant         []observer.LoggedEntry\n\t\trecoverValue any\n\t}{\n\t\t{\n\t\t\tmsg:      \"panic with nil hook\",\n\t\t\tlevel:    PanicLevel,\n\t\t\topts:     opts(WithPanicHook(nil)),\n\t\t\tfinished: false,\n\t\t\twant: []observer.LoggedEntry{\n\t\t\t\t{\n\t\t\t\t\tEntry:   zapcore.Entry{Level: PanicLevel, Message: \"foobar\"},\n\t\t\t\t\tContext: []Field{},\n\t\t\t\t},\n\t\t\t},\n\t\t\trecoverValue: \"foobar\",\n\t\t},\n\t\t{\n\t\t\tmsg:      \"panic with noop hook\",\n\t\t\tlevel:    PanicLevel,\n\t\t\topts:     opts(WithPanicHook(zapcore.WriteThenNoop)),\n\t\t\tfinished: false,\n\t\t\twant: []observer.LoggedEntry{\n\t\t\t\t{\n\t\t\t\t\tEntry:   zapcore.Entry{Level: PanicLevel, Message: \"foobar\"},\n\t\t\t\t\tContext: []Field{},\n\t\t\t\t},\n\t\t\t},\n\t\t\trecoverValue: \"foobar\",\n\t\t},\n\t\t{\n\t\t\tmsg:      \"no panic with goexit hook\",\n\t\t\tlevel:    PanicLevel,\n\t\t\topts:     opts(WithPanicHook(zapcore.WriteThenGoexit)),\n\t\t\tfinished: false,\n\t\t\twant: []observer.LoggedEntry{\n\t\t\t\t{\n\t\t\t\t\tEntry:   zapcore.Entry{Level: PanicLevel, Message: \"foobar\"},\n\t\t\t\t\tContext: []Field{},\n\t\t\t\t},\n\t\t\t},\n\t\t\trecoverValue: nil,\n\t\t},\n\t\t{\n\t\t\tmsg:      \"dpanic no panic in development mode with goexit hook\",\n\t\t\tlevel:    DPanicLevel,\n\t\t\topts:     opts(WithPanicHook(zapcore.WriteThenGoexit), Development()),\n\t\t\tfinished: false,\n\t\t\twant: []observer.LoggedEntry{\n\t\t\t\t{\n\t\t\t\t\tEntry:   zapcore.Entry{Level: DPanicLevel, Message: \"foobar\"},\n\t\t\t\t\tContext: []Field{},\n\t\t\t\t},\n\t\t\t},\n\t\t\trecoverValue: nil,\n\t\t},\n\t\t{\n\t\t\tmsg:      \"dpanic panic in development mode with noop hook\",\n\t\t\tlevel:    DPanicLevel,\n\t\t\topts:     opts(WithPanicHook(zapcore.WriteThenNoop), Development()),\n\t\t\tfinished: false,\n\t\t\twant: []observer.LoggedEntry{\n\t\t\t\t{\n\t\t\t\t\tEntry:   zapcore.Entry{Level: DPanicLevel, Message: \"foobar\"},\n\t\t\t\t\tContext: []Field{},\n\t\t\t\t},\n\t\t\t},\n\t\t\trecoverValue: \"foobar\",\n\t\t},\n\t\t{\n\t\t\tmsg:      \"dpanic no exit in production mode with goexit hook\",\n\t\t\tlevel:    DPanicLevel,\n\t\t\topts:     opts(WithPanicHook(zapcore.WriteThenPanic)),\n\t\t\tfinished: true,\n\t\t\twant: []observer.LoggedEntry{\n\t\t\t\t{\n\t\t\t\t\tEntry:   zapcore.Entry{Level: DPanicLevel, Message: \"foobar\"},\n\t\t\t\t\tContext: []Field{},\n\t\t\t\t},\n\t\t\t},\n\t\t\trecoverValue: nil,\n\t\t},\n\t\t{\n\t\t\tmsg:      \"dpanic no panic in production mode with panic hook\",\n\t\t\tlevel:    DPanicLevel,\n\t\t\topts:     opts(WithPanicHook(zapcore.WriteThenPanic)),\n\t\t\tfinished: true,\n\t\t\twant: []observer.LoggedEntry{\n\t\t\t\t{\n\t\t\t\t\tEntry:   zapcore.Entry{Level: DPanicLevel, Message: \"foobar\"},\n\t\t\t\t\tContext: []Field{},\n\t\t\t\t},\n\t\t\t},\n\t\t\trecoverValue: nil,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.msg, func(t *testing.T) {\n\t\t\twithLogger(t, InfoLevel, tt.opts, func(logger *Logger, logs *observer.ObservedLogs) {\n\t\t\t\tvar finished bool\n\t\t\t\trecovered := make(chan any)\n\t\t\t\tgo func() {\n\t\t\t\t\tdefer func() {\n\t\t\t\t\t\trecovered <- recover()\n\t\t\t\t\t}()\n\n\t\t\t\t\tlogger.Log(tt.level, \"foobar\")\n\t\t\t\t\tfinished = true\n\t\t\t\t}()\n\n\t\t\t\tassert.Equal(t, tt.recoverValue, <-recovered, \"unexpected value from recover()\")\n\t\t\t\tassert.Equal(t, tt.finished, finished, \"expect goroutine finished state doesn't match\")\n\t\t\t\tassert.Equal(t, tt.want, logs.AllUntimed(), \"unexpected logs\")\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestLoggerCustomOnFatal(t *testing.T) {\n\ttests := []struct {\n\t\tmsg          string\n\t\tonFatal      zapcore.CheckWriteAction\n\t\trecoverValue interface{}\n\t}{\n\t\t{\n\t\t\tmsg:          \"panic\",\n\t\t\tonFatal:      zapcore.WriteThenPanic,\n\t\t\trecoverValue: \"fatal\",\n\t\t},\n\t\t{\n\t\t\tmsg:          \"goexit\",\n\t\t\tonFatal:      zapcore.WriteThenGoexit,\n\t\t\trecoverValue: nil,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.msg, func(t *testing.T) {\n\t\t\twithLogger(t, InfoLevel, opts(OnFatal(tt.onFatal)), func(logger *Logger, logs *observer.ObservedLogs) {\n\t\t\t\tvar finished bool\n\t\t\t\trecovered := make(chan interface{})\n\t\t\t\tgo func() {\n\t\t\t\t\tdefer func() {\n\t\t\t\t\t\trecovered <- recover()\n\t\t\t\t\t}()\n\n\t\t\t\t\tlogger.Fatal(\"fatal\")\n\t\t\t\t\tfinished = true\n\t\t\t\t}()\n\n\t\t\t\tassert.Equal(t, tt.recoverValue, <-recovered, \"unexpected value from recover()\")\n\t\t\t\tassert.False(t, finished, \"expect goroutine to not finish after Fatal\")\n\n\t\t\t\tassert.Equal(t, []observer.LoggedEntry{{\n\t\t\t\t\tEntry:   zapcore.Entry{Level: FatalLevel, Message: \"fatal\"},\n\t\t\t\t\tContext: []Field{},\n\t\t\t\t}}, logs.AllUntimed(), \"unexpected logs\")\n\t\t\t})\n\t\t})\n\t}\n}\n\ntype customWriteHook struct {\n\tcalled bool\n}\n\nfunc (h *customWriteHook) OnWrite(_ *zapcore.CheckedEntry, _ []Field) {\n\th.called = true\n}\n\nfunc TestLoggerWithFatalHook(t *testing.T) {\n\tvar h customWriteHook\n\twithLogger(t, InfoLevel, opts(WithFatalHook(&h)), func(logger *Logger, logs *observer.ObservedLogs) {\n\t\tlogger.Fatal(\"great sadness\")\n\t\tassert.True(t, h.called)\n\t\tassert.Equal(t, 1, logs.FilterLevelExact(FatalLevel).Len())\n\t})\n}\n\nfunc TestNopLogger(t *testing.T) {\n\tlogger := NewNop()\n\n\tt.Run(\"basic levels\", func(t *testing.T) {\n\t\tlogger.Debug(\"foo\", String(\"k\", \"v\"))\n\t\tlogger.Info(\"bar\", Int(\"x\", 42))\n\t\tlogger.Warn(\"baz\", Strings(\"ks\", []string{\"a\", \"b\"}))\n\t\tlogger.Error(\"qux\", Error(errors.New(\"great sadness\")))\n\t})\n\n\tt.Run(\"DPanic\", func(t *testing.T) {\n\t\tlogger.With(String(\"component\", \"whatever\")).DPanic(\"stuff\")\n\t})\n\n\tt.Run(\"Panic\", func(t *testing.T) {\n\t\tassert.Panics(t, func() {\n\t\t\tlogger.Panic(\"great sadness\")\n\t\t}, \"Nop logger should still cause panics.\")\n\t})\n}\n\nfunc TestMust(t *testing.T) {\n\tt.Run(\"must without an error does not panic\", func(t *testing.T) {\n\t\tassert.NotPanics(t, func() { Must(NewNop(), nil) }, \"must paniced with no error\")\n\t})\n\n\tt.Run(\"must with an error panics\", func(t *testing.T) {\n\t\tassert.Panics(t, func() { Must(nil, errors.New(\"an error\")) }, \"must did not panic with an error\")\n\t})\n}\n\nfunc infoLog(logger *Logger, msg string, fields ...Field) {\n\tlogger.Info(msg, fields...)\n}\n\nfunc infoLogSugared(logger *SugaredLogger, args ...interface{}) {\n\tlogger.Info(args...)\n}\n"
  },
  {
    "path": "options.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"fmt\"\n\n\t\"go.uber.org/zap/zapcore\"\n)\n\n// An Option configures a Logger.\ntype Option interface {\n\tapply(*Logger)\n}\n\n// optionFunc wraps a func so it satisfies the Option interface.\ntype optionFunc func(*Logger)\n\nfunc (f optionFunc) apply(log *Logger) {\n\tf(log)\n}\n\n// WrapCore wraps or replaces the Logger's underlying zapcore.Core.\nfunc WrapCore(f func(zapcore.Core) zapcore.Core) Option {\n\treturn optionFunc(func(log *Logger) {\n\t\tlog.core = f(log.core)\n\t})\n}\n\n// Hooks registers functions which will be called each time the Logger writes\n// out an Entry. Repeated use of Hooks is additive.\n//\n// Hooks are useful for simple side effects, like capturing metrics for the\n// number of emitted logs. More complex side effects, including anything that\n// requires access to the Entry's structured fields, should be implemented as\n// a zapcore.Core instead. See zapcore.RegisterHooks for details.\nfunc Hooks(hooks ...func(zapcore.Entry) error) Option {\n\treturn optionFunc(func(log *Logger) {\n\t\tlog.core = zapcore.RegisterHooks(log.core, hooks...)\n\t})\n}\n\n// Fields adds fields to the Logger.\nfunc Fields(fs ...Field) Option {\n\treturn optionFunc(func(log *Logger) {\n\t\tlog.core = log.core.With(fs)\n\t})\n}\n\n// ErrorOutput sets the destination for errors generated by the Logger. Note\n// that this option only affects internal errors; for sample code that sends\n// error-level logs to a different location from info- and debug-level logs,\n// see the package-level AdvancedConfiguration example.\n//\n// The supplied WriteSyncer must be safe for concurrent use. The Open and\n// zapcore.Lock functions are the simplest ways to protect files with a mutex.\nfunc ErrorOutput(w zapcore.WriteSyncer) Option {\n\treturn optionFunc(func(log *Logger) {\n\t\tlog.errorOutput = w\n\t})\n}\n\n// Development puts the logger in development mode, which makes DPanic-level\n// logs panic instead of simply logging an error.\nfunc Development() Option {\n\treturn optionFunc(func(log *Logger) {\n\t\tlog.development = true\n\t})\n}\n\n// AddCaller configures the Logger to annotate each message with the filename,\n// line number, and function name of zap's caller. See also WithCaller.\nfunc AddCaller() Option {\n\treturn WithCaller(true)\n}\n\n// WithCaller configures the Logger to annotate each message with the filename,\n// line number, and function name of zap's caller, or not, depending on the\n// value of enabled. This is a generalized form of AddCaller.\nfunc WithCaller(enabled bool) Option {\n\treturn optionFunc(func(log *Logger) {\n\t\tlog.addCaller = enabled\n\t})\n}\n\n// AddCallerSkip increases the number of callers skipped by caller annotation\n// (as enabled by the AddCaller option). When building wrappers around the\n// Logger and SugaredLogger, supplying this Option prevents zap from always\n// reporting the wrapper code as the caller.\nfunc AddCallerSkip(skip int) Option {\n\treturn optionFunc(func(log *Logger) {\n\t\tlog.callerSkip += skip\n\t})\n}\n\n// AddStacktrace configures the Logger to record a stack trace for all messages at\n// or above a given level.\nfunc AddStacktrace(lvl zapcore.LevelEnabler) Option {\n\treturn optionFunc(func(log *Logger) {\n\t\tlog.addStack = lvl\n\t})\n}\n\n// IncreaseLevel increase the level of the logger. It has no effect if\n// the passed in level tries to decrease the level of the logger.\nfunc IncreaseLevel(lvl zapcore.LevelEnabler) Option {\n\treturn optionFunc(func(log *Logger) {\n\t\tcore, err := zapcore.NewIncreaseLevelCore(log.core, lvl)\n\t\tif err != nil {\n\t\t\t_, _ = fmt.Fprintf(\n\t\t\t\tlog.errorOutput,\n\t\t\t\t\"failed to IncreaseLevel: %v\\n\",\n\t\t\t\terr,\n\t\t\t)\n\t\t} else {\n\t\t\tlog.core = core\n\t\t}\n\t})\n}\n\n// WithPanicHook sets a CheckWriteHook to run on Panic/DPanic logs.\n// Zap will call this hook after writing a log statement with a Panic/DPanic level.\n//\n// For example, the following builds a logger that will exit the current\n// goroutine after writing a Panic/DPanic log message, but it will not start a panic.\n//\n//\tzap.New(core, zap.WithPanicHook(zapcore.WriteThenGoexit))\n//\n// This is useful for testing Panic/DPanic log output.\nfunc WithPanicHook(hook zapcore.CheckWriteHook) Option {\n\treturn optionFunc(func(log *Logger) {\n\t\tlog.onPanic = hook\n\t})\n}\n\n// OnFatal sets the action to take on fatal logs.\n//\n// Deprecated: Use [WithFatalHook] instead.\nfunc OnFatal(action zapcore.CheckWriteAction) Option {\n\treturn WithFatalHook(action)\n}\n\n// WithFatalHook sets a CheckWriteHook to run on fatal logs.\n// Zap will call this hook after writing a log statement with a Fatal level.\n//\n// For example, the following builds a logger that will exit the current\n// goroutine after writing a fatal log message, but it will not exit the\n// program.\n//\n//\tzap.New(core, zap.WithFatalHook(zapcore.WriteThenGoexit))\n//\n// It is important that the provided CheckWriteHook stops the control flow at\n// the current statement to meet expectations of callers of the logger.\n// We recommend calling os.Exit or runtime.Goexit inside custom hooks at\n// minimum.\nfunc WithFatalHook(hook zapcore.CheckWriteHook) Option {\n\treturn optionFunc(func(log *Logger) {\n\t\tlog.onFatal = hook\n\t})\n}\n\n// WithClock specifies the clock used by the logger to determine the current\n// time for logged entries. Defaults to the system clock with time.Now.\nfunc WithClock(clock zapcore.Clock) Option {\n\treturn optionFunc(func(log *Logger) {\n\t\tlog.clock = clock\n\t})\n}\n"
  },
  {
    "path": "sink.go",
    "content": "// Copyright (c) 2016-2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/url\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"go.uber.org/zap/zapcore\"\n)\n\nconst schemeFile = \"file\"\n\nvar _sinkRegistry = newSinkRegistry()\n\n// Sink defines the interface to write to and close logger destinations.\ntype Sink interface {\n\tzapcore.WriteSyncer\n\tio.Closer\n}\n\ntype errSinkNotFound struct {\n\tscheme string\n}\n\nfunc (e *errSinkNotFound) Error() string {\n\treturn fmt.Sprintf(\"no sink found for scheme %q\", e.scheme)\n}\n\ntype nopCloserSink struct{ zapcore.WriteSyncer }\n\nfunc (nopCloserSink) Close() error { return nil }\n\ntype sinkRegistry struct {\n\tmu        sync.Mutex\n\tfactories map[string]func(*url.URL) (Sink, error)          // keyed by scheme\n\topenFile  func(string, int, os.FileMode) (*os.File, error) // type matches os.OpenFile\n}\n\nfunc newSinkRegistry() *sinkRegistry {\n\tsr := &sinkRegistry{\n\t\tfactories: make(map[string]func(*url.URL) (Sink, error)),\n\t\topenFile:  os.OpenFile,\n\t}\n\t// Infallible operation: the registry is empty, so we can't have a conflict.\n\t_ = sr.RegisterSink(schemeFile, sr.newFileSinkFromURL)\n\treturn sr\n}\n\n// RegisterSink registers the given factory for the specific scheme.\nfunc (sr *sinkRegistry) RegisterSink(scheme string, factory func(*url.URL) (Sink, error)) error {\n\tsr.mu.Lock()\n\tdefer sr.mu.Unlock()\n\n\tif scheme == \"\" {\n\t\treturn errors.New(\"can't register a sink factory for empty string\")\n\t}\n\tnormalized, err := normalizeScheme(scheme)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"%q is not a valid scheme: %v\", scheme, err)\n\t}\n\tif _, ok := sr.factories[normalized]; ok {\n\t\treturn fmt.Errorf(\"sink factory already registered for scheme %q\", normalized)\n\t}\n\tsr.factories[normalized] = factory\n\treturn nil\n}\n\nfunc (sr *sinkRegistry) newSink(rawURL string) (Sink, error) {\n\t// URL parsing doesn't work well for Windows paths such as `c:\\log.txt`, as scheme is set to\n\t// the drive, and path is unset unless `c:/log.txt` is used.\n\t// To avoid Windows-specific URL handling, we instead check IsAbs to open as a file.\n\t// filepath.IsAbs is OS-specific, so IsAbs('c:/log.txt') is false outside of Windows.\n\tif filepath.IsAbs(rawURL) {\n\t\treturn sr.newFileSinkFromPath(rawURL)\n\t}\n\n\tu, err := url.Parse(rawURL)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't parse %q as a URL: %v\", rawURL, err)\n\t}\n\tif u.Scheme == \"\" {\n\t\tu.Scheme = schemeFile\n\t}\n\n\tsr.mu.Lock()\n\tfactory, ok := sr.factories[u.Scheme]\n\tsr.mu.Unlock()\n\tif !ok {\n\t\treturn nil, &errSinkNotFound{u.Scheme}\n\t}\n\treturn factory(u)\n}\n\n// RegisterSink registers a user-supplied factory for all sinks with a\n// particular scheme.\n//\n// All schemes must be ASCII, valid under section 0.1 of RFC 3986\n// (https://tools.ietf.org/html/rfc3983#section-3.1), and must not already\n// have a factory registered. Zap automatically registers a factory for the\n// \"file\" scheme.\nfunc RegisterSink(scheme string, factory func(*url.URL) (Sink, error)) error {\n\treturn _sinkRegistry.RegisterSink(scheme, factory)\n}\n\nfunc (sr *sinkRegistry) newFileSinkFromURL(u *url.URL) (Sink, error) {\n\tif u.User != nil {\n\t\treturn nil, fmt.Errorf(\"user and password not allowed with file URLs: got %v\", u)\n\t}\n\tif u.Fragment != \"\" {\n\t\treturn nil, fmt.Errorf(\"fragments not allowed with file URLs: got %v\", u)\n\t}\n\tif u.RawQuery != \"\" {\n\t\treturn nil, fmt.Errorf(\"query parameters not allowed with file URLs: got %v\", u)\n\t}\n\t// Error messages are better if we check hostname and port separately.\n\tif u.Port() != \"\" {\n\t\treturn nil, fmt.Errorf(\"ports not allowed with file URLs: got %v\", u)\n\t}\n\tif hn := u.Hostname(); hn != \"\" && hn != \"localhost\" {\n\t\treturn nil, fmt.Errorf(\"file URLs must leave host empty or use localhost: got %v\", u)\n\t}\n\n\treturn sr.newFileSinkFromPath(u.Path)\n}\n\nfunc (sr *sinkRegistry) newFileSinkFromPath(path string) (Sink, error) {\n\tswitch path {\n\tcase \"stdout\":\n\t\treturn nopCloserSink{os.Stdout}, nil\n\tcase \"stderr\":\n\t\treturn nopCloserSink{os.Stderr}, nil\n\t}\n\treturn sr.openFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0o666)\n}\n\nfunc normalizeScheme(s string) (string, error) {\n\t// https://tools.ietf.org/html/rfc3986#section-3.1\n\ts = strings.ToLower(s)\n\tif first := s[0]; 'a' > first || 'z' < first {\n\t\treturn \"\", errors.New(\"must start with a letter\")\n\t}\n\tfor i := 1; i < len(s); i++ { // iterate over bytes, not runes\n\t\tc := s[i]\n\t\tswitch {\n\t\tcase 'a' <= c && c <= 'z':\n\t\t\tcontinue\n\t\tcase '0' <= c && c <= '9':\n\t\t\tcontinue\n\t\tcase c == '.' || c == '+' || c == '-':\n\t\t\tcontinue\n\t\t}\n\t\treturn \"\", fmt.Errorf(\"may not contain %q\", c)\n\t}\n\treturn s, nil\n}\n"
  },
  {
    "path": "sink_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"net/url\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"go.uber.org/zap/zapcore\"\n)\n\nfunc stubSinkRegistry(t testing.TB) *sinkRegistry {\n\torigSinkRegistry := _sinkRegistry\n\tt.Cleanup(func() {\n\t\t_sinkRegistry = origSinkRegistry\n\t})\n\n\tr := newSinkRegistry()\n\t_sinkRegistry = r\n\treturn r\n}\n\nfunc TestRegisterSink(t *testing.T) {\n\tstubSinkRegistry(t)\n\n\tconst (\n\t\tmemScheme = \"mem\"\n\t\tnopScheme = \"no-op.1234\"\n\t)\n\tvar memCalls, nopCalls int\n\n\tbuf := bytes.NewBuffer(nil)\n\tmemFactory := func(u *url.URL) (Sink, error) {\n\t\tassert.Equal(t, u.Scheme, memScheme, \"Scheme didn't match registration.\")\n\t\tmemCalls++\n\t\treturn nopCloserSink{zapcore.AddSync(buf)}, nil\n\t}\n\tnopFactory := func(u *url.URL) (Sink, error) {\n\t\tassert.Equal(t, u.Scheme, nopScheme, \"Scheme didn't match registration.\")\n\t\tnopCalls++\n\t\treturn nopCloserSink{zapcore.AddSync(io.Discard)}, nil\n\t}\n\n\trequire.NoError(t, RegisterSink(strings.ToUpper(memScheme), memFactory), \"Failed to register scheme %q.\", memScheme)\n\trequire.NoError(t, RegisterSink(nopScheme, nopFactory), \"Failed to register scheme %q.\", nopScheme)\n\n\tsink, closeSink, err := Open(\n\t\tmemScheme+\"://somewhere\",\n\t\tnopScheme+\"://somewhere-else\",\n\t)\n\trequire.NoError(t, err, \"Unexpected error opening URLs with registered schemes.\")\n\tdefer closeSink()\n\n\tassert.Equal(t, 1, memCalls, \"Unexpected number of calls to memory factory.\")\n\tassert.Equal(t, 1, nopCalls, \"Unexpected number of calls to no-op factory.\")\n\n\t_, err = sink.Write([]byte(\"foo\"))\n\tassert.NoError(t, err, \"Failed to write to combined WriteSyncer.\")\n\tassert.Equal(t, \"foo\", buf.String(), \"Unexpected buffer contents.\")\n}\n\nfunc TestRegisterSinkErrors(t *testing.T) {\n\tnopFactory := func(_ *url.URL) (Sink, error) {\n\t\treturn nopCloserSink{zapcore.AddSync(io.Discard)}, nil\n\t}\n\ttests := []struct {\n\t\tscheme string\n\t\terr    string\n\t}{\n\t\t{\"\", \"empty string\"},\n\t\t{\"FILE\", \"already registered\"},\n\t\t{\"42\", \"not a valid scheme\"},\n\t\t{\"http*\", \"not a valid scheme\"},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(\"scheme-\"+tt.scheme, func(t *testing.T) {\n\t\t\tr := newSinkRegistry()\n\t\t\terr := r.RegisterSink(tt.scheme, nopFactory)\n\t\t\tassert.ErrorContains(t, err, tt.err)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "sink_windows_test.go",
    "content": "// Copyright (c) 2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n//go:build windows\n\npackage zap\n\nimport (\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestWindowsPaths(t *testing.T) {\n\t// See https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats\n\ttests := []struct {\n\t\tmsg  string\n\t\tpath string\n\t}{\n\t\t{\n\t\t\tmsg:  \"local path with drive\",\n\t\t\tpath: `c:\\log.json`,\n\t\t},\n\t\t{\n\t\t\tmsg:  \"local path with drive using forward slash\",\n\t\t\tpath: `c:/log.json`,\n\t\t},\n\t\t{\n\t\t\tmsg:  \"local path without drive\",\n\t\t\tpath: `\\Temp\\log.json`,\n\t\t},\n\t\t{\n\t\t\tmsg:  \"unc path\",\n\t\t\tpath: `\\\\Server2\\Logs\\log.json`,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.msg, func(t *testing.T) {\n\t\t\tsr := newSinkRegistry()\n\n\t\t\topenFilename := \"<not called>\"\n\t\t\tsr.openFile = func(filename string, _ int, _ os.FileMode) (*os.File, error) {\n\t\t\t\topenFilename = filename\n\t\t\t\treturn nil, assert.AnError\n\t\t\t}\n\n\t\t\t_, err := sr.newSink(tt.path)\n\t\t\tassert.Equal(t, assert.AnError, err, \"expect stub error from OpenFile\")\n\t\t\tassert.Equal(t, tt.path, openFilename, \"unexpected path opened\")\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "stacktrace_ext_test.go",
    "content": "// Copyright (c) 2016, 2017 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap_test\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\n// _zapPackages are packages that we search for in the logging output to match a\n// zap stack frame. It is different from _zapStacktracePrefixes which  is only\n// intended to match on the function name, while this is on the full output\n// which includes filenames.\nvar _zapPackages = []string{\n\t\"go.uber.org/zap.\",\n\t\"go.uber.org/zap/zapcore.\",\n}\n\nfunc TestStacktraceFiltersZapLog(t *testing.T) {\n\twithLogger(t, func(logger *zap.Logger, out *bytes.Buffer) {\n\t\tlogger.Error(\"test log\")\n\t\tlogger.Sugar().Error(\"sugar test log\")\n\n\t\trequire.Contains(t, out.String(), \"TestStacktraceFiltersZapLog\", \"Should not strip out non-zap import\")\n\t\tverifyNoZap(t, out.String())\n\t})\n}\n\nfunc TestStacktraceFiltersZapMarshal(t *testing.T) {\n\twithLogger(t, func(logger *zap.Logger, out *bytes.Buffer) {\n\t\tmarshal := func(enc zapcore.ObjectEncoder) error {\n\t\t\tlogger.Warn(\"marshal caused warn\")\n\t\t\tenc.AddString(\"f\", \"v\")\n\t\t\treturn nil\n\t\t}\n\t\tlogger.Error(\"test log\", zap.Object(\"obj\", zapcore.ObjectMarshalerFunc(marshal)))\n\n\t\tlogs := out.String()\n\n\t\t// The marshal function (which will be under the test function) should not be stripped.\n\t\tconst marshalFnPrefix = \"TestStacktraceFiltersZapMarshal.\"\n\t\trequire.Contains(t, logs, marshalFnPrefix, \"Should not strip out marshal call\")\n\n\t\t// There should be no zap stack traces before that point.\n\t\tmarshalIndex := strings.Index(logs, marshalFnPrefix)\n\t\tverifyNoZap(t, logs[:marshalIndex])\n\n\t\t// After that point, there should be zap stack traces - we don't want to strip out\n\t\t// the Marshal caller information.\n\t\tfor _, fnPrefix := range _zapPackages {\n\t\t\trequire.Contains(t, logs[marshalIndex:], fnPrefix, \"Missing zap caller stack for Marshal\")\n\t\t}\n\t})\n}\n\nfunc TestStacktraceFiltersVendorZap(t *testing.T) {\n\t// We already have the dependencies downloaded so this should be\n\t// instant.\n\tdeps := downloadDependencies(t)\n\n\t// We need to simulate a zap as a vendor library, so we're going to\n\t// create a fake GOPATH and run the above test which will contain zap\n\t// in the vendor directory.\n\twithGoPath(t, func(goPath string) {\n\t\tzapDir, err := os.Getwd()\n\t\trequire.NoError(t, err, \"Failed to get current directory\")\n\n\t\ttestDir := filepath.Join(goPath, \"src/go.uber.org/zap_test/\")\n\t\tvendorDir := filepath.Join(testDir, \"vendor\")\n\t\trequire.NoError(t, os.MkdirAll(testDir, 0o777), \"Failed to create source director\")\n\n\t\tcurFile := getSelfFilename(t)\n\t\tsetupSymlink(t, curFile, filepath.Join(testDir, curFile))\n\n\t\t// Set up symlinks for zap, and for any test dependencies.\n\t\tsetupSymlink(t, zapDir, filepath.Join(vendorDir, \"go.uber.org/zap\"))\n\t\tfor _, dep := range deps {\n\t\t\tsetupSymlink(t, dep.Dir, filepath.Join(vendorDir, dep.ImportPath))\n\t\t}\n\n\t\t// Now run the above test which ensures we filter out zap\n\t\t// stacktraces, but this time zap is in a vendor\n\t\tcmd := exec.Command(\"go\", \"test\", \"-v\", \"-run\", \"TestStacktraceFiltersZap\")\n\t\tcmd.Dir = testDir\n\t\tcmd.Env = append(os.Environ(), \"GO111MODULE=off\")\n\t\tout, err := cmd.CombinedOutput()\n\t\trequire.NoError(t, err, \"Failed to run test in vendor directory, output: %s\", out)\n\t\tassert.Contains(t, string(out), \"PASS\")\n\t})\n}\n\nfunc TestStacktraceWithoutCallerSkip(t *testing.T) {\n\twithLogger(t, func(logger *zap.Logger, out *bytes.Buffer) {\n\t\tfunc() {\n\t\t\tlogger.Error(\"test log\")\n\t\t}()\n\n\t\trequire.Contains(t, out.String(), \"TestStacktraceWithoutCallerSkip.\", \"Should not skip too much\")\n\t\tverifyNoZap(t, out.String())\n\t})\n}\n\nfunc TestStacktraceWithCallerSkip(t *testing.T) {\n\twithLogger(t, func(logger *zap.Logger, out *bytes.Buffer) {\n\t\tlogger = logger.WithOptions(zap.AddCallerSkip(2))\n\t\tfunc() {\n\t\t\tlogger.Error(\"test log\")\n\t\t}()\n\n\t\trequire.NotContains(t, out.String(), \"TestStacktraceWithCallerSkip.\", \"Should skip as requested by caller skip\")\n\t\trequire.Contains(t, out.String(), \"TestStacktraceWithCallerSkip\", \"Should not skip too much\")\n\t\tverifyNoZap(t, out.String())\n\t})\n}\n\n// withLogger sets up a logger with a real encoder set up, so that any marshal functions are called.\n// The inbuilt observer does not call Marshal for objects/arrays, which we need for some tests.\nfunc withLogger(t *testing.T, fn func(logger *zap.Logger, out *bytes.Buffer)) {\n\tbuf := &bytes.Buffer{}\n\tencoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())\n\tcore := zapcore.NewCore(encoder, zapcore.AddSync(buf), zapcore.DebugLevel)\n\tlogger := zap.New(core, zap.AddStacktrace(zap.DebugLevel))\n\tfn(logger, buf)\n}\n\nfunc verifyNoZap(t *testing.T, logs string) {\n\tfor _, fnPrefix := range _zapPackages {\n\t\trequire.NotContains(t, logs, fnPrefix, \"Should not strip out marshal call\")\n\t}\n}\n\nfunc withGoPath(t *testing.T, f func(goPath string)) {\n\tgoPath := filepath.Join(t.TempDir(), \"gopath\")\n\tt.Setenv(\"GOPATH\", goPath)\n\n\tf(goPath)\n}\n\nfunc getSelfFilename(t *testing.T) string {\n\t_, file, _, ok := runtime.Caller(0)\n\trequire.True(t, ok, \"Failed to get caller information to identify local file\")\n\n\treturn filepath.Base(file)\n}\n\nfunc setupSymlink(t *testing.T, src, dst string) {\n\t// Make sure the destination directory exists.\n\trequire.NoError(t, os.MkdirAll(filepath.Dir(dst), 0o777))\n\n\t// Get absolute path of the source for the symlink, otherwise we can create a symlink\n\t// that uses relative paths.\n\tsrcAbs, err := filepath.Abs(src)\n\trequire.NoError(t, err, \"Failed to get absolute path\")\n\n\trequire.NoError(t, os.Symlink(srcAbs, dst), \"Failed to set up symlink\")\n}\n\ntype dependency struct {\n\tImportPath string `json:\"Path\"` // import path of the dependency\n\tDir        string `json:\"Dir\"`  // location on disk\n}\n\n// Downloads all dependencies for the current Go module and reports their\n// module paths and locations on disk.\nfunc downloadDependencies(t *testing.T) []dependency {\n\tcmd := exec.Command(\"go\", \"mod\", \"download\", \"-json\")\n\n\tstdout, err := cmd.Output()\n\trequire.NoError(t, err, \"Failed to run 'go mod download'\")\n\n\tvar deps []dependency\n\tdec := json.NewDecoder(bytes.NewBuffer(stdout))\n\tfor dec.More() {\n\t\tvar d dependency\n\t\trequire.NoError(t, dec.Decode(&d), \"Failed to decode dependency\")\n\t\tdeps = append(deps, d)\n\t}\n\n\treturn deps\n}\n"
  },
  {
    "path": "sugar.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"fmt\"\n\n\t\"go.uber.org/zap/zapcore\"\n\n\t\"go.uber.org/multierr\"\n)\n\nconst (\n\t_oddNumberErrMsg    = \"Ignored key without a value.\"\n\t_nonStringKeyErrMsg = \"Ignored key-value pairs with non-string keys.\"\n\t_multipleErrMsg     = \"Multiple errors without a key.\"\n)\n\n// A SugaredLogger wraps the base Logger functionality in a slower, but less\n// verbose, API. Any Logger can be converted to a SugaredLogger with its Sugar\n// method.\n//\n// Unlike the Logger, the SugaredLogger doesn't insist on structured logging.\n// For each log level, it exposes four methods:\n//\n//   - methods named after the log level for log.Print-style logging\n//   - methods ending in \"w\" for loosely-typed structured logging\n//   - methods ending in \"f\" for log.Printf-style logging\n//   - methods ending in \"ln\" for log.Println-style logging\n//\n// For example, the methods for InfoLevel are:\n//\n//\tInfo(...any)           Print-style logging\n//\tInfow(...any)          Structured logging (read as \"info with\")\n//\tInfof(string, ...any)  Printf-style logging\n//\tInfoln(...any)         Println-style logging\ntype SugaredLogger struct {\n\tbase *Logger\n}\n\n// Desugar unwraps a SugaredLogger, exposing the original Logger. Desugaring\n// is quite inexpensive, so it's reasonable for a single application to use\n// both Loggers and SugaredLoggers, converting between them on the boundaries\n// of performance-sensitive code.\nfunc (s *SugaredLogger) Desugar() *Logger {\n\tbase := s.base.clone()\n\tbase.callerSkip -= 2\n\treturn base\n}\n\n// Named adds a sub-scope to the logger's name. See Logger.Named for details.\nfunc (s *SugaredLogger) Named(name string) *SugaredLogger {\n\treturn &SugaredLogger{base: s.base.Named(name)}\n}\n\n// WithOptions clones the current SugaredLogger, applies the supplied Options,\n// and returns the result. It's safe to use concurrently.\nfunc (s *SugaredLogger) WithOptions(opts ...Option) *SugaredLogger {\n\tbase := s.base.clone()\n\tfor _, opt := range opts {\n\t\topt.apply(base)\n\t}\n\treturn &SugaredLogger{base: base}\n}\n\n// With adds a variadic number of fields to the logging context. It accepts a\n// mix of strongly-typed Field objects and loosely-typed key-value pairs. When\n// processing pairs, the first element of the pair is used as the field key\n// and the second as the field value.\n//\n// For example,\n//\n//\t sugaredLogger.With(\n//\t   \"hello\", \"world\",\n//\t   \"failure\", errors.New(\"oh no\"),\n//\t   Stack(),\n//\t   \"count\", 42,\n//\t   \"user\", User{Name: \"alice\"},\n//\t)\n//\n// is the equivalent of\n//\n//\tunsugared.With(\n//\t  String(\"hello\", \"world\"),\n//\t  String(\"failure\", \"oh no\"),\n//\t  Stack(),\n//\t  Int(\"count\", 42),\n//\t  Object(\"user\", User{Name: \"alice\"}),\n//\t)\n//\n// Note that the keys in key-value pairs should be strings. In development,\n// passing a non-string key panics. In production, the logger is more\n// forgiving: a separate error is logged, but the key-value pair is skipped\n// and execution continues. Passing an orphaned key triggers similar behavior:\n// panics in development and errors in production.\nfunc (s *SugaredLogger) With(args ...interface{}) *SugaredLogger {\n\treturn &SugaredLogger{base: s.base.With(s.sweetenFields(args)...)}\n}\n\n// WithLazy adds a variadic number of fields to the logging context lazily.\n// The fields are evaluated only if the logger is further chained with [With]\n// or is written to with any of the log level methods.\n// Until that occurs, the logger may retain references to objects inside the fields,\n// and logging will reflect the state of an object at the time of logging,\n// not the time of WithLazy().\n//\n// Similar to [With], fields added to the child don't affect the parent,\n// and vice versa. Also, the keys in key-value pairs should be strings. In development,\n// passing a non-string key panics, while in production it logs an error and skips the pair.\n// Passing an orphaned key has the same behavior.\nfunc (s *SugaredLogger) WithLazy(args ...interface{}) *SugaredLogger {\n\treturn &SugaredLogger{base: s.base.WithLazy(s.sweetenFields(args)...)}\n}\n\n// Level reports the minimum enabled level for this logger.\n//\n// For NopLoggers, this is [zapcore.InvalidLevel].\nfunc (s *SugaredLogger) Level() zapcore.Level {\n\treturn zapcore.LevelOf(s.base.core)\n}\n\n// Log logs the provided arguments at provided level.\n// Spaces are added between arguments when neither is a string.\nfunc (s *SugaredLogger) Log(lvl zapcore.Level, args ...interface{}) {\n\ts.log(lvl, \"\", args, nil)\n}\n\n// Debug logs the provided arguments at [DebugLevel].\n// Spaces are added between arguments when neither is a string.\nfunc (s *SugaredLogger) Debug(args ...interface{}) {\n\ts.log(DebugLevel, \"\", args, nil)\n}\n\n// Info logs the provided arguments at [InfoLevel].\n// Spaces are added between arguments when neither is a string.\nfunc (s *SugaredLogger) Info(args ...interface{}) {\n\ts.log(InfoLevel, \"\", args, nil)\n}\n\n// Warn logs the provided arguments at [WarnLevel].\n// Spaces are added between arguments when neither is a string.\nfunc (s *SugaredLogger) Warn(args ...interface{}) {\n\ts.log(WarnLevel, \"\", args, nil)\n}\n\n// Error logs the provided arguments at [ErrorLevel].\n// Spaces are added between arguments when neither is a string.\nfunc (s *SugaredLogger) Error(args ...interface{}) {\n\ts.log(ErrorLevel, \"\", args, nil)\n}\n\n// DPanic logs the provided arguments at [DPanicLevel].\n// In development, the logger then panics. (See [DPanicLevel] for details.)\n// Spaces are added between arguments when neither is a string.\nfunc (s *SugaredLogger) DPanic(args ...interface{}) {\n\ts.log(DPanicLevel, \"\", args, nil)\n}\n\n// Panic constructs a message with the provided arguments and panics.\n// Spaces are added between arguments when neither is a string.\nfunc (s *SugaredLogger) Panic(args ...interface{}) {\n\ts.log(PanicLevel, \"\", args, nil)\n}\n\n// Fatal constructs a message with the provided arguments and calls os.Exit.\n// Spaces are added between arguments when neither is a string.\nfunc (s *SugaredLogger) Fatal(args ...interface{}) {\n\ts.log(FatalLevel, \"\", args, nil)\n}\n\n// Logf formats the message according to the format specifier\n// and logs it at provided level.\nfunc (s *SugaredLogger) Logf(lvl zapcore.Level, template string, args ...interface{}) {\n\ts.log(lvl, template, args, nil)\n}\n\n// Debugf formats the message according to the format specifier\n// and logs it at [DebugLevel].\nfunc (s *SugaredLogger) Debugf(template string, args ...interface{}) {\n\ts.log(DebugLevel, template, args, nil)\n}\n\n// Infof formats the message according to the format specifier\n// and logs it at [InfoLevel].\nfunc (s *SugaredLogger) Infof(template string, args ...interface{}) {\n\ts.log(InfoLevel, template, args, nil)\n}\n\n// Warnf formats the message according to the format specifier\n// and logs it at [WarnLevel].\nfunc (s *SugaredLogger) Warnf(template string, args ...interface{}) {\n\ts.log(WarnLevel, template, args, nil)\n}\n\n// Errorf formats the message according to the format specifier\n// and logs it at [ErrorLevel].\nfunc (s *SugaredLogger) Errorf(template string, args ...interface{}) {\n\ts.log(ErrorLevel, template, args, nil)\n}\n\n// DPanicf formats the message according to the format specifier\n// and logs it at [DPanicLevel].\n// In development, the logger then panics. (See [DPanicLevel] for details.)\nfunc (s *SugaredLogger) DPanicf(template string, args ...interface{}) {\n\ts.log(DPanicLevel, template, args, nil)\n}\n\n// Panicf formats the message according to the format specifier\n// and panics.\nfunc (s *SugaredLogger) Panicf(template string, args ...interface{}) {\n\ts.log(PanicLevel, template, args, nil)\n}\n\n// Fatalf formats the message according to the format specifier\n// and calls os.Exit.\nfunc (s *SugaredLogger) Fatalf(template string, args ...interface{}) {\n\ts.log(FatalLevel, template, args, nil)\n}\n\n// Logw logs a message with some additional context. The variadic key-value\n// pairs are treated as they are in With.\nfunc (s *SugaredLogger) Logw(lvl zapcore.Level, msg string, keysAndValues ...interface{}) {\n\ts.log(lvl, msg, nil, keysAndValues)\n}\n\n// Debugw logs a message with some additional context. The variadic key-value\n// pairs are treated as they are in With.\n//\n// When debug-level logging is disabled, this is much faster than\n//\n//\ts.With(keysAndValues).Debug(msg)\nfunc (s *SugaredLogger) Debugw(msg string, keysAndValues ...interface{}) {\n\ts.log(DebugLevel, msg, nil, keysAndValues)\n}\n\n// Infow logs a message with some additional context. The variadic key-value\n// pairs are treated as they are in With.\nfunc (s *SugaredLogger) Infow(msg string, keysAndValues ...interface{}) {\n\ts.log(InfoLevel, msg, nil, keysAndValues)\n}\n\n// Warnw logs a message with some additional context. The variadic key-value\n// pairs are treated as they are in With.\nfunc (s *SugaredLogger) Warnw(msg string, keysAndValues ...interface{}) {\n\ts.log(WarnLevel, msg, nil, keysAndValues)\n}\n\n// Errorw logs a message with some additional context. The variadic key-value\n// pairs are treated as they are in With.\nfunc (s *SugaredLogger) Errorw(msg string, keysAndValues ...interface{}) {\n\ts.log(ErrorLevel, msg, nil, keysAndValues)\n}\n\n// DPanicw logs a message with some additional context. In development, the\n// logger then panics. (See DPanicLevel for details.) The variadic key-value\n// pairs are treated as they are in With.\nfunc (s *SugaredLogger) DPanicw(msg string, keysAndValues ...interface{}) {\n\ts.log(DPanicLevel, msg, nil, keysAndValues)\n}\n\n// Panicw logs a message with some additional context, then panics. The\n// variadic key-value pairs are treated as they are in With.\nfunc (s *SugaredLogger) Panicw(msg string, keysAndValues ...interface{}) {\n\ts.log(PanicLevel, msg, nil, keysAndValues)\n}\n\n// Fatalw logs a message with some additional context, then calls os.Exit. The\n// variadic key-value pairs are treated as they are in With.\nfunc (s *SugaredLogger) Fatalw(msg string, keysAndValues ...interface{}) {\n\ts.log(FatalLevel, msg, nil, keysAndValues)\n}\n\n// Logln logs a message at provided level.\n// Spaces are always added between arguments.\nfunc (s *SugaredLogger) Logln(lvl zapcore.Level, args ...interface{}) {\n\ts.logln(lvl, args, nil)\n}\n\n// Debugln logs a message at [DebugLevel].\n// Spaces are always added between arguments.\nfunc (s *SugaredLogger) Debugln(args ...interface{}) {\n\ts.logln(DebugLevel, args, nil)\n}\n\n// Infoln logs a message at [InfoLevel].\n// Spaces are always added between arguments.\nfunc (s *SugaredLogger) Infoln(args ...interface{}) {\n\ts.logln(InfoLevel, args, nil)\n}\n\n// Warnln logs a message at [WarnLevel].\n// Spaces are always added between arguments.\nfunc (s *SugaredLogger) Warnln(args ...interface{}) {\n\ts.logln(WarnLevel, args, nil)\n}\n\n// Errorln logs a message at [ErrorLevel].\n// Spaces are always added between arguments.\nfunc (s *SugaredLogger) Errorln(args ...interface{}) {\n\ts.logln(ErrorLevel, args, nil)\n}\n\n// DPanicln logs a message at [DPanicLevel].\n// In development, the logger then panics. (See [DPanicLevel] for details.)\n// Spaces are always added between arguments.\nfunc (s *SugaredLogger) DPanicln(args ...interface{}) {\n\ts.logln(DPanicLevel, args, nil)\n}\n\n// Panicln logs a message at [PanicLevel] and panics.\n// Spaces are always added between arguments.\nfunc (s *SugaredLogger) Panicln(args ...interface{}) {\n\ts.logln(PanicLevel, args, nil)\n}\n\n// Fatalln logs a message at [FatalLevel] and calls os.Exit.\n// Spaces are always added between arguments.\nfunc (s *SugaredLogger) Fatalln(args ...interface{}) {\n\ts.logln(FatalLevel, args, nil)\n}\n\n// Sync flushes any buffered log entries.\nfunc (s *SugaredLogger) Sync() error {\n\treturn s.base.Sync()\n}\n\n// log message with Sprint, Sprintf, or neither.\nfunc (s *SugaredLogger) log(lvl zapcore.Level, template string, fmtArgs []interface{}, context []interface{}) {\n\t// If logging at this level is completely disabled, skip the overhead of\n\t// string formatting.\n\tif lvl < DPanicLevel && !s.base.Core().Enabled(lvl) {\n\t\treturn\n\t}\n\n\tmsg := getMessage(template, fmtArgs)\n\tif ce := s.base.Check(lvl, msg); ce != nil {\n\t\tce.Write(s.sweetenFields(context)...)\n\t}\n}\n\n// logln message with Sprintln\nfunc (s *SugaredLogger) logln(lvl zapcore.Level, fmtArgs []interface{}, context []interface{}) {\n\tif lvl < DPanicLevel && !s.base.Core().Enabled(lvl) {\n\t\treturn\n\t}\n\n\tmsg := getMessageln(fmtArgs)\n\tif ce := s.base.Check(lvl, msg); ce != nil {\n\t\tce.Write(s.sweetenFields(context)...)\n\t}\n}\n\n// getMessage format with Sprint, Sprintf, or neither.\nfunc getMessage(template string, fmtArgs []interface{}) string {\n\tif len(fmtArgs) == 0 {\n\t\treturn template\n\t}\n\n\tif template != \"\" {\n\t\treturn fmt.Sprintf(template, fmtArgs...)\n\t}\n\n\tif len(fmtArgs) == 1 {\n\t\tif str, ok := fmtArgs[0].(string); ok {\n\t\t\treturn str\n\t\t}\n\t}\n\treturn fmt.Sprint(fmtArgs...)\n}\n\n// getMessageln format with Sprintln.\nfunc getMessageln(fmtArgs []interface{}) string {\n\tmsg := fmt.Sprintln(fmtArgs...)\n\treturn msg[:len(msg)-1]\n}\n\nfunc (s *SugaredLogger) sweetenFields(args []interface{}) []Field {\n\tif len(args) == 0 {\n\t\treturn nil\n\t}\n\n\tvar (\n\t\t// Allocate enough space for the worst case; if users pass only structured\n\t\t// fields, we shouldn't penalize them with extra allocations.\n\t\tfields    = make([]Field, 0, len(args))\n\t\tinvalid   invalidPairs\n\t\tseenError bool\n\t)\n\n\tfor i := 0; i < len(args); {\n\t\t// This is a strongly-typed field. Consume it and move on.\n\t\tif f, ok := args[i].(Field); ok {\n\t\t\tfields = append(fields, f)\n\t\t\ti++\n\t\t\tcontinue\n\t\t}\n\n\t\t// If it is an error, consume it and move on.\n\t\tif err, ok := args[i].(error); ok {\n\t\t\tif !seenError {\n\t\t\t\tseenError = true\n\t\t\t\tfields = append(fields, Error(err))\n\t\t\t} else {\n\t\t\t\ts.base.Error(_multipleErrMsg, Error(err))\n\t\t\t}\n\t\t\ti++\n\t\t\tcontinue\n\t\t}\n\n\t\t// Make sure this element isn't a dangling key.\n\t\tif i == len(args)-1 {\n\t\t\ts.base.Error(_oddNumberErrMsg, Any(\"ignored\", args[i]))\n\t\t\tbreak\n\t\t}\n\n\t\t// Consume this value and the next, treating them as a key-value pair. If the\n\t\t// key isn't a string, add this pair to the slice of invalid pairs.\n\t\tkey, val := args[i], args[i+1]\n\t\tif keyStr, ok := key.(string); !ok {\n\t\t\t// Subsequent errors are likely, so allocate once up front.\n\t\t\tif cap(invalid) == 0 {\n\t\t\t\tinvalid = make(invalidPairs, 0, len(args)/2)\n\t\t\t}\n\t\t\tinvalid = append(invalid, invalidPair{i, key, val})\n\t\t} else {\n\t\t\tfields = append(fields, Any(keyStr, val))\n\t\t}\n\t\ti += 2\n\t}\n\n\t// If we encountered any invalid key-value pairs, log an error.\n\tif len(invalid) > 0 {\n\t\ts.base.Error(_nonStringKeyErrMsg, Array(\"invalid\", invalid))\n\t}\n\treturn fields\n}\n\ntype invalidPair struct {\n\tposition   int\n\tkey, value interface{}\n}\n\nfunc (p invalidPair) MarshalLogObject(enc zapcore.ObjectEncoder) error {\n\tenc.AddInt64(\"position\", int64(p.position))\n\tAny(\"key\", p.key).AddTo(enc)\n\tAny(\"value\", p.value).AddTo(enc)\n\treturn nil\n}\n\ntype invalidPairs []invalidPair\n\nfunc (ps invalidPairs) MarshalLogArray(enc zapcore.ArrayEncoder) error {\n\tvar err error\n\tfor i := range ps {\n\t\terr = multierr.Append(err, enc.AppendObject(ps[i]))\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "sugar_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"testing\"\n\n\t\"go.uber.org/zap/internal/exit\"\n\t\"go.uber.org/zap/internal/ztest\"\n\t\"go.uber.org/zap/zapcore\"\n\t\"go.uber.org/zap/zaptest/observer\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestSugarWith(t *testing.T) {\n\t// Convenience functions to create expected error logs.\n\tignored := func(msg interface{}) observer.LoggedEntry {\n\t\treturn observer.LoggedEntry{\n\t\t\tEntry:   zapcore.Entry{Level: ErrorLevel, Message: _oddNumberErrMsg},\n\t\t\tContext: []Field{Any(\"ignored\", msg)},\n\t\t}\n\t}\n\tnonString := func(pairs ...invalidPair) observer.LoggedEntry {\n\t\treturn observer.LoggedEntry{\n\t\t\tEntry:   zapcore.Entry{Level: ErrorLevel, Message: _nonStringKeyErrMsg},\n\t\t\tContext: []Field{Array(\"invalid\", invalidPairs(pairs))},\n\t\t}\n\t}\n\tignoredError := func(err error) observer.LoggedEntry {\n\t\treturn observer.LoggedEntry{\n\t\t\tEntry:   zapcore.Entry{Level: ErrorLevel, Message: _multipleErrMsg},\n\t\t\tContext: []Field{Error(err)},\n\t\t}\n\t}\n\n\ttype withAny func(*SugaredLogger, ...interface{}) *SugaredLogger\n\twithMethods := []withAny{(*SugaredLogger).With, (*SugaredLogger).WithLazy}\n\n\ttests := []struct {\n\t\tdesc     string\n\t\targs     []interface{}\n\t\texpected []Field\n\t\terrLogs  []observer.LoggedEntry\n\t}{\n\t\t{\n\t\t\tdesc:     \"nil args\",\n\t\t\targs:     nil,\n\t\t\texpected: []Field{},\n\t\t\terrLogs:  nil,\n\t\t},\n\t\t{\n\t\t\tdesc:     \"empty slice of args\",\n\t\t\targs:     []interface{}{},\n\t\t\texpected: []Field{},\n\t\t\terrLogs:  nil,\n\t\t},\n\t\t{\n\t\t\tdesc:     \"just a dangling key\",\n\t\t\targs:     []interface{}{\"should ignore\"},\n\t\t\texpected: []Field{},\n\t\t\terrLogs:  []observer.LoggedEntry{ignored(\"should ignore\")},\n\t\t},\n\t\t{\n\t\t\tdesc:     \"well-formed key-value pairs\",\n\t\t\targs:     []interface{}{\"foo\", 42, \"true\", \"bar\"},\n\t\t\texpected: []Field{Int(\"foo\", 42), String(\"true\", \"bar\")},\n\t\t\terrLogs:  nil,\n\t\t},\n\t\t{\n\t\t\tdesc:     \"just a structured field\",\n\t\t\targs:     []interface{}{Int(\"foo\", 42)},\n\t\t\texpected: []Field{Int(\"foo\", 42)},\n\t\t\terrLogs:  nil,\n\t\t},\n\t\t{\n\t\t\tdesc:     \"structured field and a dangling key\",\n\t\t\targs:     []interface{}{Int(\"foo\", 42), \"dangling\"},\n\t\t\texpected: []Field{Int(\"foo\", 42)},\n\t\t\terrLogs:  []observer.LoggedEntry{ignored(\"dangling\")},\n\t\t},\n\t\t{\n\t\t\tdesc:     \"structured field and a dangling non-string key\",\n\t\t\targs:     []interface{}{Int(\"foo\", 42), 13},\n\t\t\texpected: []Field{Int(\"foo\", 42)},\n\t\t\terrLogs:  []observer.LoggedEntry{ignored(13)},\n\t\t},\n\t\t{\n\t\t\tdesc:     \"key-value pair and a dangling key\",\n\t\t\targs:     []interface{}{\"foo\", 42, \"dangling\"},\n\t\t\texpected: []Field{Int(\"foo\", 42)},\n\t\t\terrLogs:  []observer.LoggedEntry{ignored(\"dangling\")},\n\t\t},\n\t\t{\n\t\t\tdesc:     \"pairs, a structured field, and a dangling key\",\n\t\t\targs:     []interface{}{\"first\", \"field\", Int(\"foo\", 42), \"baz\", \"quux\", \"dangling\"},\n\t\t\texpected: []Field{String(\"first\", \"field\"), Int(\"foo\", 42), String(\"baz\", \"quux\")},\n\t\t\terrLogs:  []observer.LoggedEntry{ignored(\"dangling\")},\n\t\t},\n\t\t{\n\t\t\tdesc:     \"one non-string key\",\n\t\t\targs:     []interface{}{\"foo\", 42, true, \"bar\"},\n\t\t\texpected: []Field{Int(\"foo\", 42)},\n\t\t\terrLogs:  []observer.LoggedEntry{nonString(invalidPair{2, true, \"bar\"})},\n\t\t},\n\t\t{\n\t\t\tdesc:     \"pairs, structured fields, non-string keys, and a dangling key\",\n\t\t\targs:     []interface{}{\"foo\", 42, true, \"bar\", Int(\"structure\", 11), 42, \"reversed\", \"baz\", \"quux\", \"dangling\"},\n\t\t\texpected: []Field{Int(\"foo\", 42), Int(\"structure\", 11), String(\"baz\", \"quux\")},\n\t\t\terrLogs: []observer.LoggedEntry{\n\t\t\t\tignored(\"dangling\"),\n\t\t\t\tnonString(invalidPair{2, true, \"bar\"}, invalidPair{5, 42, \"reversed\"}),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc:     \"multiple errors\",\n\t\t\targs:     []interface{}{errors.New(\"first\"), errors.New(\"second\"), errors.New(\"third\")},\n\t\t\texpected: []Field{Error(errors.New(\"first\"))},\n\t\t\terrLogs: []observer.LoggedEntry{\n\t\t\t\tignoredError(errors.New(\"second\")),\n\t\t\t\tignoredError(errors.New(\"third\")),\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tfor _, withMethod := range withMethods {\n\t\t\twithSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) {\n\t\t\t\twithMethod(logger, tt.args...).Info(\"\")\n\t\t\t\toutput := logs.AllUntimed()\n\t\t\t\tif len(tt.errLogs) > 0 {\n\t\t\t\t\tfor i := range tt.errLogs {\n\t\t\t\t\t\tassert.Equal(t, tt.errLogs[i], output[i], \"Unexpected error log at position %d for scenario %s.\", i, tt.desc)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tassert.Equal(t, len(tt.errLogs)+1, len(output), \"Expected only one non-error message to be logged in scenario %s.\", tt.desc)\n\t\t\t\tassert.Equal(t, tt.expected, output[len(tt.errLogs)].Context, \"Unexpected message context in scenario %s.\", tt.desc)\n\t\t\t})\n\t\t}\n\t}\n}\n\nfunc TestSugarWithCaptures(t *testing.T) {\n\ttype withAny func(*SugaredLogger, ...interface{}) *SugaredLogger\n\n\ttests := []struct {\n\t\tname        string\n\t\twithMethods []withAny\n\t\twantJSON    []string\n\t}{\n\t\t{\n\t\t\tname:        \"with captures arguments at time of With\",\n\t\t\twithMethods: []withAny{(*SugaredLogger).With},\n\t\t\twantJSON: []string{\n\t\t\t\t`{\n\t\t\t\t\t\"m\": \"hello 0\",\n\t\t\t\t\t\"a0\": [0],\n\t\t\t\t\t\"b0\": [1]\n\t\t\t\t}`,\n\t\t\t\t`{\n\t\t\t\t\t\"m\": \"world 0\",\n\t\t\t\t\t\"a0\": [0],\n\t\t\t\t\t\"c0\": [2]\n\t\t\t\t}`,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:        \"lazy with captures arguments at time of Logging\",\n\t\t\twithMethods: []withAny{(*SugaredLogger).WithLazy},\n\t\t\twantJSON: []string{\n\t\t\t\t`{\n\t\t\t\t\t\"m\": \"hello 0\",\n\t\t\t\t\t\"a0\": [1],\n\t\t\t\t\t\"b0\": [1]\n\t\t\t\t}`,\n\t\t\t\t`{\n\t\t\t\t\t\"m\": \"world 0\",\n\t\t\t\t\t\"a0\": [1],\n\t\t\t\t\t\"c0\": [2]\n\t\t\t\t}`,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tenc := zapcore.NewJSONEncoder(zapcore.EncoderConfig{\n\t\t\t\tMessageKey: \"m\",\n\t\t\t})\n\n\t\t\tvar bs ztest.Buffer\n\t\t\tlogger := New(zapcore.NewCore(enc, &bs, DebugLevel)).Sugar()\n\n\t\t\tfor i, withMethod := range tt.withMethods {\n\t\t\t\tiStr := strconv.Itoa(i)\n\t\t\t\tx := 10 * i\n\t\t\t\tarr := zapcore.ArrayMarshalerFunc(func(enc zapcore.ArrayEncoder) error {\n\t\t\t\t\tenc.AppendInt(x)\n\t\t\t\t\treturn nil\n\t\t\t\t})\n\n\t\t\t\tlogger = withMethod(logger, Array(\"a\"+iStr, arr))\n\t\t\t\tx++\n\t\t\t\tlogger.Infow(fmt.Sprintf(\"hello %d\", i), Array(\"b\"+iStr, arr))\n\t\t\t\tx++\n\t\t\t\tlogger = withMethod(logger, Array(\"c\"+iStr, arr))\n\t\t\t\tlogger.Infow(fmt.Sprintf(\"world %d\", i))\n\t\t\t}\n\n\t\t\tif lines := bs.Lines(); assert.Len(t, lines, len(tt.wantJSON)) {\n\t\t\t\tfor i, want := range tt.wantJSON {\n\t\t\t\t\tassert.JSONEq(t, want, lines[i], \"Unexpected output from the %d'th log.\", i)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSugaredLoggerLevel(t *testing.T) {\n\tlevels := []zapcore.Level{\n\t\tDebugLevel,\n\t\tInfoLevel,\n\t\tWarnLevel,\n\t\tErrorLevel,\n\t\tDPanicLevel,\n\t\tPanicLevel,\n\t\tFatalLevel,\n\t}\n\n\tfor _, lvl := range levels {\n\t\tlvl := lvl\n\t\tt.Run(lvl.String(), func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tcore, _ := observer.New(lvl)\n\t\t\tlog := New(core).Sugar()\n\t\t\tassert.Equal(t, lvl, log.Level())\n\t\t})\n\t}\n\n\tt.Run(\"Nop\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tassert.Equal(t, zapcore.InvalidLevel, NewNop().Sugar().Level())\n\t})\n}\n\nfunc TestSugarFieldsInvalidPairs(t *testing.T) {\n\twithSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) {\n\t\tlogger.With(42, \"foo\", []string{\"bar\"}, \"baz\").Info(\"\")\n\t\toutput := logs.AllUntimed()\n\n\t\t// Double-check that the actual message was logged.\n\t\trequire.Equal(t, 2, len(output), \"Unexpected number of entries logged.\")\n\t\trequire.Equal(t, observer.LoggedEntry{Context: []Field{}}, output[1], \"Unexpected non-error log entry.\")\n\n\t\t// Assert that the error message's structured fields serialize properly.\n\t\trequire.Equal(t, 1, len(output[0].Context), \"Expected one field in error entry context.\")\n\t\tenc := zapcore.NewMapObjectEncoder()\n\t\toutput[0].Context[0].AddTo(enc)\n\t\tassert.Equal(t, []interface{}{\n\t\t\tmap[string]interface{}{\"position\": int64(0), \"key\": int64(42), \"value\": \"foo\"},\n\t\t\tmap[string]interface{}{\"position\": int64(2), \"key\": []interface{}{\"bar\"}, \"value\": \"baz\"},\n\t\t}, enc.Fields[\"invalid\"], \"Unexpected output when logging invalid key-value pairs.\")\n\t})\n}\n\nfunc TestSugarStructuredLogging(t *testing.T) {\n\ttests := []struct {\n\t\tmsg       string\n\t\texpectMsg string\n\t}{\n\t\t{\"foo\", \"foo\"},\n\t\t{\"\", \"\"},\n\t}\n\n\t// Common to all test cases.\n\tvar (\n\t\terr            = errors.New(\"qux\")\n\t\tcontext        = []interface{}{\"foo\", \"bar\"}\n\t\textra          = []interface{}{err, \"baz\", false}\n\t\texpectedFields = []Field{String(\"foo\", \"bar\"), Error(err), Bool(\"baz\", false)}\n\t)\n\n\tfor _, tt := range tests {\n\t\twithSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) {\n\t\t\tlogger.With(context...).Debugw(tt.msg, extra...)\n\t\t\tlogger.With(context...).Infow(tt.msg, extra...)\n\t\t\tlogger.With(context...).Warnw(tt.msg, extra...)\n\t\t\tlogger.With(context...).Errorw(tt.msg, extra...)\n\t\t\tlogger.With(context...).DPanicw(tt.msg, extra...)\n\t\t\tlogger.With(context...).Logw(WarnLevel, tt.msg, extra...)\n\n\t\t\texpected := make([]observer.LoggedEntry, 6)\n\t\t\tfor i, lvl := range []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel, WarnLevel} {\n\t\t\t\texpected[i] = observer.LoggedEntry{\n\t\t\t\t\tEntry:   zapcore.Entry{Message: tt.expectMsg, Level: lvl},\n\t\t\t\t\tContext: expectedFields,\n\t\t\t\t}\n\t\t\t}\n\t\t\tassert.Equal(t, expected, logs.AllUntimed(), \"Unexpected log output.\")\n\t\t})\n\t}\n}\n\nfunc TestSugarConcatenatingLogging(t *testing.T) {\n\ttests := []struct {\n\t\targs   []interface{}\n\t\texpect string\n\t}{\n\t\t{[]interface{}{nil}, \"<nil>\"},\n\t}\n\n\t// Common to all test cases.\n\tcontext := []interface{}{\"foo\", \"bar\"}\n\texpectedFields := []Field{String(\"foo\", \"bar\")}\n\n\tfor _, tt := range tests {\n\t\twithSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) {\n\t\t\tlogger.With(context...).Debug(tt.args...)\n\t\t\tlogger.With(context...).Info(tt.args...)\n\t\t\tlogger.With(context...).Warn(tt.args...)\n\t\t\tlogger.With(context...).Error(tt.args...)\n\t\t\tlogger.With(context...).DPanic(tt.args...)\n\t\t\tlogger.With(context...).Log(InfoLevel, tt.args...)\n\n\t\t\texpected := make([]observer.LoggedEntry, 6)\n\t\t\tfor i, lvl := range []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel, InfoLevel} {\n\t\t\t\texpected[i] = observer.LoggedEntry{\n\t\t\t\t\tEntry:   zapcore.Entry{Message: tt.expect, Level: lvl},\n\t\t\t\t\tContext: expectedFields,\n\t\t\t\t}\n\t\t\t}\n\t\t\tassert.Equal(t, expected, logs.AllUntimed(), \"Unexpected log output.\")\n\t\t})\n\t}\n}\n\nfunc TestSugarTemplatedLogging(t *testing.T) {\n\ttests := []struct {\n\t\tformat string\n\t\targs   []interface{}\n\t\texpect string\n\t}{\n\t\t{\"\", nil, \"\"},\n\t\t{\"foo\", nil, \"foo\"},\n\t\t// If the user fails to pass a template, degrade to fmt.Sprint.\n\t\t{\"\", []interface{}{\"foo\"}, \"foo\"},\n\t}\n\n\t// Common to all test cases.\n\tcontext := []interface{}{\"foo\", \"bar\"}\n\texpectedFields := []Field{String(\"foo\", \"bar\")}\n\n\tfor _, tt := range tests {\n\t\twithSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) {\n\t\t\tlogger.With(context...).Debugf(tt.format, tt.args...)\n\t\t\tlogger.With(context...).Infof(tt.format, tt.args...)\n\t\t\tlogger.With(context...).Warnf(tt.format, tt.args...)\n\t\t\tlogger.With(context...).Errorf(tt.format, tt.args...)\n\t\t\tlogger.With(context...).DPanicf(tt.format, tt.args...)\n\t\t\tlogger.With(context...).Logf(ErrorLevel, tt.format, tt.args...)\n\n\t\t\texpected := make([]observer.LoggedEntry, 6)\n\t\t\tfor i, lvl := range []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel, ErrorLevel} {\n\t\t\t\texpected[i] = observer.LoggedEntry{\n\t\t\t\t\tEntry:   zapcore.Entry{Message: tt.expect, Level: lvl},\n\t\t\t\t\tContext: expectedFields,\n\t\t\t\t}\n\t\t\t}\n\t\t\tassert.Equal(t, expected, logs.AllUntimed(), \"Unexpected log output.\")\n\t\t})\n\t}\n}\n\nfunc TestSugarLnLogging(t *testing.T) {\n\ttests := []struct {\n\t\targs   []interface{}\n\t\texpect string\n\t}{\n\t\t{nil, \"\"},\n\t\t{[]interface{}{}, \"\"},\n\t\t{[]interface{}{\"\"}, \"\"},\n\t\t{[]interface{}{\"foo\"}, \"foo\"},\n\t\t{[]interface{}{\"foo\", \"bar\"}, \"foo bar\"},\n\t}\n\n\t// Common to all test cases.\n\tcontext := []interface{}{\"foo\", \"bar\"}\n\texpectedFields := []Field{String(\"foo\", \"bar\")}\n\n\tfor _, tt := range tests {\n\t\twithSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) {\n\t\t\tlogger.With(context...).Debugln(tt.args...)\n\t\t\tlogger.With(context...).Infoln(tt.args...)\n\t\t\tlogger.With(context...).Warnln(tt.args...)\n\t\t\tlogger.With(context...).Errorln(tt.args...)\n\t\t\tlogger.With(context...).DPanicln(tt.args...)\n\t\t\tlogger.With(context...).Logln(InfoLevel, tt.args...)\n\n\t\t\texpected := make([]observer.LoggedEntry, 6)\n\t\t\tfor i, lvl := range []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel, InfoLevel} {\n\t\t\t\texpected[i] = observer.LoggedEntry{\n\t\t\t\t\tEntry:   zapcore.Entry{Message: tt.expect, Level: lvl},\n\t\t\t\t\tContext: expectedFields,\n\t\t\t\t}\n\t\t\t}\n\t\t\tassert.Equal(t, expected, logs.AllUntimed(), \"Unexpected log output.\")\n\t\t})\n\t}\n}\n\nfunc TestSugarLnLoggingIgnored(t *testing.T) {\n\twithSugar(t, WarnLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) {\n\t\tlogger.Infoln(\"hello\")\n\t\tassert.Zero(t, logs.Len(), \"Expected zero log statements.\")\n\t})\n}\n\nfunc TestSugarPanicLogging(t *testing.T) {\n\ttests := []struct {\n\t\tloggerLevel zapcore.Level\n\t\tf           func(*SugaredLogger)\n\t\texpectedMsg string\n\t}{\n\t\t{FatalLevel, func(s *SugaredLogger) { s.Panic(\"foo\") }, \"\"},\n\t\t{PanicLevel, func(s *SugaredLogger) { s.Panic(\"foo\") }, \"foo\"},\n\t\t{DebugLevel, func(s *SugaredLogger) { s.Panic(\"foo\") }, \"foo\"},\n\t\t{FatalLevel, func(s *SugaredLogger) { s.Panicf(\"%s\", \"foo\") }, \"\"},\n\t\t{PanicLevel, func(s *SugaredLogger) { s.Panicf(\"%s\", \"foo\") }, \"foo\"},\n\t\t{DebugLevel, func(s *SugaredLogger) { s.Panicf(\"%s\", \"foo\") }, \"foo\"},\n\t\t{FatalLevel, func(s *SugaredLogger) { s.Panicw(\"foo\") }, \"\"},\n\t\t{PanicLevel, func(s *SugaredLogger) { s.Panicw(\"foo\") }, \"foo\"},\n\t\t{DebugLevel, func(s *SugaredLogger) { s.Panicw(\"foo\") }, \"foo\"},\n\t\t{FatalLevel, func(s *SugaredLogger) { s.Panicln(\"foo\") }, \"\"},\n\t\t{PanicLevel, func(s *SugaredLogger) { s.Panicln(\"foo\") }, \"foo\"},\n\t\t{DebugLevel, func(s *SugaredLogger) { s.Panicln(\"foo\") }, \"foo\"},\n\t}\n\n\tfor _, tt := range tests {\n\t\twithSugar(t, tt.loggerLevel, nil, func(sugar *SugaredLogger, logs *observer.ObservedLogs) {\n\t\t\tassert.Panics(t, func() { tt.f(sugar) }, \"Expected panic-level logger calls to panic.\")\n\t\t\tif tt.expectedMsg != \"\" {\n\t\t\t\tassert.Equal(t, []observer.LoggedEntry{{\n\t\t\t\t\tContext: []Field{},\n\t\t\t\t\tEntry:   zapcore.Entry{Message: tt.expectedMsg, Level: PanicLevel},\n\t\t\t\t}}, logs.AllUntimed(), \"Unexpected log output.\")\n\t\t\t} else {\n\t\t\t\tassert.Equal(t, 0, logs.Len(), \"Didn't expect any log output.\")\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSugarFatalLogging(t *testing.T) {\n\ttests := []struct {\n\t\tloggerLevel zapcore.Level\n\t\tf           func(*SugaredLogger)\n\t\texpectedMsg string\n\t}{\n\t\t{FatalLevel + 1, func(s *SugaredLogger) { s.Fatal(\"foo\") }, \"\"},\n\t\t{FatalLevel, func(s *SugaredLogger) { s.Fatal(\"foo\") }, \"foo\"},\n\t\t{DebugLevel, func(s *SugaredLogger) { s.Fatal(\"foo\") }, \"foo\"},\n\t\t{FatalLevel + 1, func(s *SugaredLogger) { s.Fatalf(\"%s\", \"foo\") }, \"\"},\n\t\t{FatalLevel, func(s *SugaredLogger) { s.Fatalf(\"%s\", \"foo\") }, \"foo\"},\n\t\t{DebugLevel, func(s *SugaredLogger) { s.Fatalf(\"%s\", \"foo\") }, \"foo\"},\n\t\t{FatalLevel + 1, func(s *SugaredLogger) { s.Fatalw(\"foo\") }, \"\"},\n\t\t{FatalLevel, func(s *SugaredLogger) { s.Fatalw(\"foo\") }, \"foo\"},\n\t\t{DebugLevel, func(s *SugaredLogger) { s.Fatalw(\"foo\") }, \"foo\"},\n\t\t{FatalLevel + 1, func(s *SugaredLogger) { s.Fatalln(\"foo\") }, \"\"},\n\t\t{FatalLevel, func(s *SugaredLogger) { s.Fatalln(\"foo\") }, \"foo\"},\n\t\t{DebugLevel, func(s *SugaredLogger) { s.Fatalln(\"foo\") }, \"foo\"},\n\t}\n\n\tfor _, tt := range tests {\n\t\twithSugar(t, tt.loggerLevel, nil, func(sugar *SugaredLogger, logs *observer.ObservedLogs) {\n\t\t\tstub := exit.WithStub(func() { tt.f(sugar) })\n\t\t\tassert.True(t, stub.Exited, \"Expected all calls to fatal logger methods to exit process.\")\n\t\t\tif tt.expectedMsg != \"\" {\n\t\t\t\tassert.Equal(t, []observer.LoggedEntry{{\n\t\t\t\t\tContext: []Field{},\n\t\t\t\t\tEntry:   zapcore.Entry{Message: tt.expectedMsg, Level: FatalLevel},\n\t\t\t\t}}, logs.AllUntimed(), \"Unexpected log output.\")\n\t\t\t} else {\n\t\t\t\tassert.Equal(t, 0, logs.Len(), \"Didn't expect any log output.\")\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSugarAddCaller(t *testing.T) {\n\ttests := []struct {\n\t\toptions []Option\n\t\tpat     string\n\t}{\n\t\t{opts(AddCaller()), `.+/sugar_test.go:[\\d]+$`},\n\t\t{opts(AddCaller(), AddCallerSkip(1), AddCallerSkip(-1)), `.+/sugar_test.go:[\\d]+$`},\n\t\t{opts(AddCaller(), AddCallerSkip(1)), `.+/common_test.go:[\\d]+$`},\n\t\t{opts(AddCaller(), AddCallerSkip(1), AddCallerSkip(5)), `.+/src/runtime/.*:[\\d]+$`},\n\t}\n\tfor _, tt := range tests {\n\t\twithSugar(t, DebugLevel, tt.options, func(logger *SugaredLogger, logs *observer.ObservedLogs) {\n\t\t\tlogger.Info(\"\")\n\t\t\toutput := logs.AllUntimed()\n\t\t\tassert.Equal(t, 1, len(output), \"Unexpected number of logs written out.\")\n\t\t\tassert.Regexp(\n\t\t\t\tt,\n\t\t\t\ttt.pat,\n\t\t\t\toutput[0].Caller,\n\t\t\t\t\"Expected to find package name and file name in output.\",\n\t\t\t)\n\t\t})\n\t}\n}\n\nfunc TestSugarAddCallerFail(t *testing.T) {\n\terrBuf := &ztest.Buffer{}\n\twithSugar(t, DebugLevel, opts(AddCaller(), AddCallerSkip(1e3), ErrorOutput(errBuf)), func(log *SugaredLogger, logs *observer.ObservedLogs) {\n\t\tlog.Info(\"Failure.\")\n\t\tassert.Regexp(\n\t\t\tt,\n\t\t\t`Logger.check error: failed to get caller`,\n\t\t\terrBuf.String(),\n\t\t\t\"Didn't find expected failure message.\",\n\t\t)\n\t\tassert.Equal(\n\t\t\tt,\n\t\t\tlogs.AllUntimed()[0].Message,\n\t\t\t\"Failure.\",\n\t\t\t\"Expected original message to survive failures in runtime.Caller.\")\n\t})\n}\n\nfunc TestSugarWithOptionsIncreaseLevel(t *testing.T) {\n\twithSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) {\n\t\tlogger = logger.WithOptions(IncreaseLevel(WarnLevel))\n\t\tlogger.Info(\"logger.Info\")\n\t\tlogger.Warn(\"logger.Warn\")\n\t\tlogger.Error(\"logger.Error\")\n\t\trequire.Equal(t, 2, logs.Len(), \"expected only warn + error logs due to IncreaseLevel.\")\n\t\tassert.Equal(\n\t\t\tt,\n\t\t\tlogs.AllUntimed()[0].Message,\n\t\t\t\"logger.Warn\",\n\t\t\t\"Expected first logged message to be warn level message\",\n\t\t)\n\t})\n}\n\nfunc TestSugarLnWithOptionsIncreaseLevel(t *testing.T) {\n\twithSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) {\n\t\tlogger = logger.WithOptions(IncreaseLevel(WarnLevel))\n\t\tlogger.Infoln(\"logger.Infoln\")\n\t\tlogger.Warnln(\"logger.Warnln\")\n\t\tlogger.Errorln(\"logger.Errorln\")\n\t\trequire.Equal(t, 2, logs.Len(), \"expected only warn + error logs due to IncreaseLevel.\")\n\t\tassert.Equal(\n\t\t\tt,\n\t\t\tlogs.AllUntimed()[0].Message,\n\t\t\t\"logger.Warnln\",\n\t\t\t\"Expected first logged message to be warn level message\",\n\t\t)\n\t})\n}\n\nfunc BenchmarkSugarSingleStrArg(b *testing.B) {\n\twithSugar(b, InfoLevel, nil /* opts* */, func(log *SugaredLogger, logs *observer.ObservedLogs) {\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\tlog.Info(\"hello world\")\n\t\t}\n\t})\n}\n\nfunc BenchmarkLnSugarSingleStrArg(b *testing.B) {\n\twithSugar(b, InfoLevel, nil /* opts* */, func(log *SugaredLogger, logs *observer.ObservedLogs) {\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\tlog.Infoln(\"hello world\")\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "time.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport \"time\"\n\nfunc timeToMillis(t time.Time) int64 {\n\treturn t.UnixNano() / int64(time.Millisecond)\n}\n"
  },
  {
    "path": "time_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestTimeToMillis(t *testing.T) {\n\ttests := []struct {\n\t\tt     time.Time\n\t\tstamp int64\n\t}{\n\t\t{t: time.Unix(0, 0), stamp: 0},\n\t\t{t: time.Unix(1, 0), stamp: 1000},\n\t\t{t: time.Unix(1, int64(500*time.Millisecond)), stamp: 1500},\n\t}\n\tfor _, tt := range tests {\n\t\tassert.Equal(t, tt.stamp, timeToMillis(tt.t), \"Unexpected timestamp for time %v.\", tt.t)\n\t}\n}\n"
  },
  {
    "path": "tools/go.mod",
    "content": "module go.uber.org/zap/tools\n\nrequire golang.org/x/vuln v1.1.4\n\nrequire (\n\tgolang.org/x/mod v0.22.0 // indirect\n\tgolang.org/x/sync v0.10.0 // indirect\n\tgolang.org/x/sys v0.29.0 // indirect\n\tgolang.org/x/telemetry v0.0.0-20240522233618-39ace7a40ae7 // indirect\n\tgolang.org/x/tools v0.29.0 // indirect\n)\n\ngo 1.22.0\n\ntoolchain go1.24.0\n"
  },
  {
    "path": "tools/go.sum",
    "content": "github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786 h1:rcv+Ippz6RAtvaGgKxc+8FQIpxHgsF+HBzPyYL2cyVU=\ngithub.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786/go.mod h1:apVn/GCasLZUVpAJ6oWAuyP7Ne7CEsQbTnc0plM3m+o=\ngithub.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngolang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=\ngolang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=\ngolang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=\ngolang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=\ngolang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/telemetry v0.0.0-20240522233618-39ace7a40ae7 h1:FemxDzfMUcK2f3YY4H+05K9CDzbSVr2+q/JKN45pey0=\ngolang.org/x/telemetry v0.0.0-20240522233618-39ace7a40ae7/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0=\ngolang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=\ngolang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=\ngolang.org/x/vuln v1.1.4 h1:Ju8QsuyhX3Hk8ma3CesTbO8vfJD9EvUBgHvkxHBzj0I=\ngolang.org/x/vuln v1.1.4/go.mod h1:F+45wmU18ym/ca5PLTPLsSzr2KppzswxPP603ldA67s=\n"
  },
  {
    "path": "tools/tools.go",
    "content": "// Copyright (c) 2019 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n//go:build tools\n// +build tools\n\npackage tools\n\nimport (\n\t// Tools we use during development.\n\t_ \"golang.org/x/vuln/cmd/govulncheck\"\n)\n"
  },
  {
    "path": "writer.go",
    "content": "// Copyright (c) 2016-2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\n\t\"go.uber.org/zap/zapcore\"\n\n\t\"go.uber.org/multierr\"\n)\n\n// Open is a high-level wrapper that takes a variadic number of URLs, opens or\n// creates each of the specified resources, and combines them into a locked\n// WriteSyncer. It also returns any error encountered and a function to close\n// any opened files.\n//\n// Passing no URLs returns a no-op WriteSyncer. Zap handles URLs without a\n// scheme and URLs with the \"file\" scheme. Third-party code may register\n// factories for other schemes using RegisterSink.\n//\n// URLs with the \"file\" scheme must use absolute paths on the local\n// filesystem. No user, password, port, fragments, or query parameters are\n// allowed, and the hostname must be empty or \"localhost\".\n//\n// Since it's common to write logs to the local filesystem, URLs without a\n// scheme (e.g., \"/var/log/foo.log\") are treated as local file paths. Without\n// a scheme, the special paths \"stdout\" and \"stderr\" are interpreted as\n// os.Stdout and os.Stderr. When specified without a scheme, relative file\n// paths also work.\nfunc Open(paths ...string) (zapcore.WriteSyncer, func(), error) {\n\twriters, closeAll, err := open(paths)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\twriter := CombineWriteSyncers(writers...)\n\treturn writer, closeAll, nil\n}\n\nfunc open(paths []string) ([]zapcore.WriteSyncer, func(), error) {\n\twriters := make([]zapcore.WriteSyncer, 0, len(paths))\n\tclosers := make([]io.Closer, 0, len(paths))\n\tcloseAll := func() {\n\t\tfor _, c := range closers {\n\t\t\t_ = c.Close()\n\t\t}\n\t}\n\n\tvar openErr error\n\tfor _, path := range paths {\n\t\tsink, err := _sinkRegistry.newSink(path)\n\t\tif err != nil {\n\t\t\topenErr = multierr.Append(openErr, fmt.Errorf(\"open sink %q: %w\", path, err))\n\t\t\tcontinue\n\t\t}\n\t\twriters = append(writers, sink)\n\t\tclosers = append(closers, sink)\n\t}\n\tif openErr != nil {\n\t\tcloseAll()\n\t\treturn nil, nil, openErr\n\t}\n\n\treturn writers, closeAll, nil\n}\n\n// CombineWriteSyncers is a utility that combines multiple WriteSyncers into a\n// single, locked WriteSyncer. If no inputs are supplied, it returns a no-op\n// WriteSyncer.\n//\n// It's provided purely as a convenience; the result is no different from\n// using zapcore.NewMultiWriteSyncer and zapcore.Lock individually.\nfunc CombineWriteSyncers(writers ...zapcore.WriteSyncer) zapcore.WriteSyncer {\n\tif len(writers) == 0 {\n\t\treturn zapcore.AddSync(io.Discard)\n\t}\n\treturn zapcore.Lock(zapcore.NewMultiWriteSyncer(writers...))\n}\n"
  },
  {
    "path": "writer_test.go",
    "content": "// Copyright (c) 2016-2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zap\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"io/fs\"\n\t\"net/url\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/multierr\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\nfunc TestOpenNoPaths(t *testing.T) {\n\tws, cleanup, err := Open()\n\tdefer cleanup()\n\n\tassert.NoError(t, err, \"Expected opening no paths to succeed.\")\n\tassert.Equal(\n\t\tt,\n\t\tzapcore.AddSync(io.Discard),\n\t\tws,\n\t\t\"Expected opening no paths to return a no-op WriteSyncer.\",\n\t)\n}\n\nfunc TestOpen(t *testing.T) {\n\ttempName := filepath.Join(t.TempDir(), \"test.log\")\n\tassert.False(t, fileExists(tempName))\n\trequire.True(t, filepath.IsAbs(tempName), \"Expected absolute temp file path.\")\n\n\ttests := []struct {\n\t\tmsg   string\n\t\tpaths []string\n\t}{\n\t\t{\n\t\t\tmsg:   \"stdout\",\n\t\t\tpaths: []string{\"stdout\"},\n\t\t},\n\t\t{\n\t\t\tmsg:   \"stderr\",\n\t\t\tpaths: []string{\"stderr\"},\n\t\t},\n\t\t{\n\t\t\tmsg:   \"temp file path only\",\n\t\t\tpaths: []string{tempName},\n\t\t},\n\t\t{\n\t\t\tmsg:   \"temp file file scheme\",\n\t\t\tpaths: []string{\"file://\" + tempName},\n\t\t},\n\t\t{\n\t\t\tmsg:   \"temp file with file scheme and host localhost\",\n\t\t\tpaths: []string{\"file://localhost\" + tempName},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.msg, func(t *testing.T) {\n\t\t\t_, cleanup, err := Open(tt.paths...)\n\t\t\tif err == nil {\n\t\t\t\tdefer cleanup()\n\t\t\t}\n\n\t\t\tassert.NoError(t, err, \"Unexpected error opening paths %v.\", tt.paths)\n\t\t})\n\t}\n\n\tassert.True(t, fileExists(tempName))\n}\n\nfunc TestOpenPathsNotFound(t *testing.T) {\n\ttempName := filepath.Join(t.TempDir(), \"test.log\")\n\n\ttests := []struct {\n\t\tmsg               string\n\t\tpaths             []string\n\t\twantNotFoundPaths []string\n\t}{\n\t\t{\n\t\t\tmsg:               \"missing path\",\n\t\t\tpaths:             []string{\"/foo/bar/baz\"},\n\t\t\twantNotFoundPaths: []string{\"/foo/bar/baz\"},\n\t\t},\n\t\t{\n\t\t\tmsg:               \"missing file scheme url with host localhost\",\n\t\t\tpaths:             []string{\"file://localhost/foo/bar/baz\"},\n\t\t\twantNotFoundPaths: []string{\"/foo/bar/baz\"},\n\t\t},\n\t\t{\n\t\t\tmsg:   \"multiple paths\",\n\t\t\tpaths: []string{\"stdout\", \"/foo/bar/baz\", tempName, \"file:///baz/quux\"},\n\t\t\twantNotFoundPaths: []string{\n\t\t\t\t\"/foo/bar/baz\",\n\t\t\t\t\"/baz/quux\",\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.msg, func(t *testing.T) {\n\t\t\t_, cleanup, err := Open(tt.paths...)\n\t\t\tif !assert.Error(t, err, \"Open must fail.\") {\n\t\t\t\tcleanup()\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\terrs := multierr.Errors(err)\n\t\t\trequire.Len(t, errs, len(tt.wantNotFoundPaths))\n\t\t\tfor i, err := range errs {\n\t\t\t\tassert.ErrorIs(t, err, fs.ErrNotExist)\n\t\t\t\tassert.ErrorContains(t, err, tt.wantNotFoundPaths[i], \"missing path in error\")\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestOpenRelativePath(t *testing.T) {\n\tconst name = \"test-relative-path.txt\"\n\n\trequire.False(t, fileExists(name), \"Test file already exists.\")\n\ts, cleanup, err := Open(name)\n\trequire.NoError(t, err, \"Open failed.\")\n\tdefer func() {\n\t\terr := os.Remove(name)\n\t\tif !t.Failed() {\n\t\t\t// If the test has already failed, we probably didn't create this file.\n\t\t\trequire.NoError(t, err, \"Deleting test file failed.\")\n\t\t}\n\t}()\n\tdefer cleanup()\n\n\t_, err = s.Write([]byte(\"test\"))\n\tassert.NoError(t, err, \"Write failed.\")\n\tassert.True(t, fileExists(name), \"Didn't create file for relative path.\")\n}\n\nfunc TestOpenFails(t *testing.T) {\n\ttests := []struct {\n\t\tpaths []string\n\t}{\n\t\t{paths: []string{\"./non-existent-dir/file\"}},           // directory doesn't exist\n\t\t{paths: []string{\"stdout\", \"./non-existent-dir/file\"}}, // directory doesn't exist\n\t\t{paths: []string{\"://foo.log\"}},                        // invalid URL, scheme can't begin with colon\n\t\t{paths: []string{\"mem://somewhere\"}},                   // scheme not registered\n\t}\n\n\tfor _, tt := range tests {\n\t\t_, cleanup, err := Open(tt.paths...)\n\t\trequire.Nil(t, cleanup, \"Cleanup function should be nil\")\n\t\tassert.Error(t, err, \"Open with invalid URL should fail.\")\n\t}\n}\n\nfunc TestOpenOtherErrors(t *testing.T) {\n\ttempName := filepath.Join(t.TempDir(), \"test.log\")\n\n\ttests := []struct {\n\t\tmsg     string\n\t\tpaths   []string\n\t\twantErr string\n\t}{\n\t\t{\n\t\t\tmsg:     \"file with unexpected host\",\n\t\t\tpaths:   []string{\"file://host01.test.com\" + tempName},\n\t\t\twantErr: \"empty or use localhost\",\n\t\t},\n\t\t{\n\t\t\tmsg:     \"file with user on localhost\",\n\t\t\tpaths:   []string{\"file://rms@localhost\" + tempName},\n\t\t\twantErr: \"user and password not allowed\",\n\t\t},\n\t\t{\n\t\t\tmsg:     \"file url with fragment\",\n\t\t\tpaths:   []string{\"file://localhost\" + tempName + \"#foo\"},\n\t\t\twantErr: \"fragments not allowed\",\n\t\t},\n\t\t{\n\t\t\tmsg:     \"file url with query\",\n\t\t\tpaths:   []string{\"file://localhost\" + tempName + \"?foo=bar\"},\n\t\t\twantErr: \"query parameters not allowed\",\n\t\t},\n\t\t{\n\t\t\tmsg:     \"file with port\",\n\t\t\tpaths:   []string{\"file://localhost:8080\" + tempName},\n\t\t\twantErr: \"ports not allowed\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.msg, func(t *testing.T) {\n\t\t\t_, cleanup, err := Open(tt.paths...)\n\t\t\tif !assert.Error(t, err, \"Open must fail.\") {\n\t\t\t\tcleanup()\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tassert.ErrorContains(t, err, tt.wantErr, \"Unexpected error opening paths %v.\", tt.paths)\n\t\t})\n\t}\n}\n\ntype testWriter struct {\n\texpected string\n\tt        testing.TB\n}\n\nfunc (w *testWriter) Write(actual []byte) (int, error) {\n\tassert.Equal(w.t, []byte(w.expected), actual, \"Unexpected write error.\")\n\treturn len(actual), nil\n}\n\nfunc (w *testWriter) Sync() error {\n\treturn nil\n}\n\nfunc TestOpenWithErroringSinkFactory(t *testing.T) {\n\tstubSinkRegistry(t)\n\n\tmsg := \"expected factory error\"\n\tfactory := func(_ *url.URL) (Sink, error) {\n\t\treturn nil, errors.New(msg)\n\t}\n\n\tassert.NoError(t, RegisterSink(\"test\", factory), \"Failed to register sink factory.\")\n\t_, _, err := Open(\"test://some/path\")\n\tassert.ErrorContains(t, err, msg)\n}\n\nfunc TestCombineWriteSyncers(t *testing.T) {\n\ttw := &testWriter{\"test\", t}\n\tw := CombineWriteSyncers(tw)\n\t_, err := w.Write([]byte(\"test\"))\n\tassert.NoError(t, err, \"Unexpected write error.\")\n}\n\nfunc fileExists(name string) bool {\n\tif _, err := os.Stat(name); os.IsNotExist(err) {\n\t\treturn false\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "zapcore/buffered_write_syncer.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport (\n\t\"bufio\"\n\t\"sync\"\n\t\"time\"\n\n\t\"go.uber.org/multierr\"\n)\n\nconst (\n\t// _defaultBufferSize specifies the default size used by Buffer.\n\t_defaultBufferSize = 256 * 1024 // 256 kB\n\n\t// _defaultFlushInterval specifies the default flush interval for\n\t// Buffer.\n\t_defaultFlushInterval = 30 * time.Second\n)\n\n// A BufferedWriteSyncer is a WriteSyncer that buffers writes in-memory before\n// flushing them to a wrapped WriteSyncer after reaching some limit, or at some\n// fixed interval--whichever comes first.\n//\n// BufferedWriteSyncer is safe for concurrent use. You don't need to use\n// zapcore.Lock for WriteSyncers with BufferedWriteSyncer.\n//\n// To set up a BufferedWriteSyncer, construct a WriteSyncer for your log\n// destination (*os.File is a valid WriteSyncer), wrap it with\n// BufferedWriteSyncer, and defer a Stop() call for when you no longer need the\n// object.\n//\n//\t func main() {\n//\t   ws := ... // your log destination\n//\t   bws := &zapcore.BufferedWriteSyncer{WS: ws}\n//\t   defer bws.Stop()\n//\n//\t   // ...\n//\t   core := zapcore.NewCore(enc, bws, lvl)\n//\t   logger := zap.New(core)\n//\n//\t   // ...\n//\t}\n//\n// By default, a BufferedWriteSyncer will buffer up to 256 kilobytes of logs,\n// waiting at most 30 seconds between flushes.\n// You can customize these parameters by setting the Size or FlushInterval\n// fields.\n// For example, the following buffers up to 512 kB of logs before flushing them\n// to Stderr, with a maximum of one minute between each flush.\n//\n//\tws := &BufferedWriteSyncer{\n//\t  WS:            os.Stderr,\n//\t  Size:          512 * 1024, // 512 kB\n//\t  FlushInterval: time.Minute,\n//\t}\n//\tdefer ws.Stop()\ntype BufferedWriteSyncer struct {\n\t// WS is the WriteSyncer around which BufferedWriteSyncer will buffer\n\t// writes.\n\t//\n\t// This field is required.\n\tWS WriteSyncer\n\n\t// Size specifies the maximum amount of data the writer will buffered\n\t// before flushing.\n\t//\n\t// Defaults to 256 kB if unspecified.\n\tSize int\n\n\t// FlushInterval specifies how often the writer should flush data if\n\t// there have been no writes.\n\t//\n\t// Defaults to 30 seconds if unspecified.\n\tFlushInterval time.Duration\n\n\t// Clock, if specified, provides control of the source of time for the\n\t// writer.\n\t//\n\t// Defaults to the system clock.\n\tClock Clock\n\n\t// unexported fields for state\n\tmu          sync.Mutex\n\tinitialized bool // whether initialize() has run\n\tstopped     bool // whether Stop() has run\n\twriter      *bufio.Writer\n\tticker      *time.Ticker\n\tstop        chan struct{} // closed when flushLoop should stop\n\tdone        chan struct{} // closed when flushLoop has stopped\n}\n\nfunc (s *BufferedWriteSyncer) initialize() {\n\tsize := s.Size\n\tif size == 0 {\n\t\tsize = _defaultBufferSize\n\t}\n\n\tflushInterval := s.FlushInterval\n\tif flushInterval == 0 {\n\t\tflushInterval = _defaultFlushInterval\n\t}\n\n\tif s.Clock == nil {\n\t\ts.Clock = DefaultClock\n\t}\n\n\ts.ticker = s.Clock.NewTicker(flushInterval)\n\ts.writer = bufio.NewWriterSize(s.WS, size)\n\ts.stop = make(chan struct{})\n\ts.done = make(chan struct{})\n\ts.initialized = true\n\tgo s.flushLoop()\n}\n\n// Write writes log data into buffer syncer directly, multiple Write calls will be batched,\n// and log data will be flushed to disk when the buffer is full or periodically.\nfunc (s *BufferedWriteSyncer) Write(bs []byte) (int, error) {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\n\tif !s.initialized {\n\t\ts.initialize()\n\t}\n\n\t// To avoid partial writes from being flushed, we manually flush the existing buffer if:\n\t// * The current write doesn't fit into the buffer fully, and\n\t// * The buffer is not empty (since bufio will not split large writes when the buffer is empty)\n\tif len(bs) > s.writer.Available() && s.writer.Buffered() > 0 {\n\t\tif err := s.writer.Flush(); err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t}\n\n\treturn s.writer.Write(bs)\n}\n\n// Sync flushes buffered log data into disk directly.\nfunc (s *BufferedWriteSyncer) Sync() error {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\n\tvar err error\n\tif s.initialized {\n\t\terr = s.writer.Flush()\n\t}\n\n\treturn multierr.Append(err, s.WS.Sync())\n}\n\n// flushLoop flushes the buffer at the configured interval until Stop is\n// called.\nfunc (s *BufferedWriteSyncer) flushLoop() {\n\tdefer close(s.done)\n\n\tfor {\n\t\tselect {\n\t\tcase <-s.ticker.C:\n\t\t\t// we just simply ignore error here\n\t\t\t// because the underlying bufio writer stores any errors\n\t\t\t// and we return any error from Sync() as part of the close\n\t\t\t_ = s.Sync()\n\t\tcase <-s.stop:\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// Stop closes the buffer, cleans up background goroutines, and flushes\n// remaining unwritten data.\nfunc (s *BufferedWriteSyncer) Stop() (err error) {\n\t// Critical section.\n\tstopped := func() bool {\n\t\ts.mu.Lock()\n\t\tdefer s.mu.Unlock()\n\n\t\tif !s.initialized {\n\t\t\treturn false\n\t\t}\n\n\t\tif s.stopped {\n\t\t\treturn false\n\t\t}\n\t\ts.stopped = true\n\n\t\ts.ticker.Stop()\n\t\tclose(s.stop) // tell flushLoop to stop\n\t\treturn true\n\t}()\n\n\t// Not initialized, or already stopped, no need for any cleanup.\n\tif !stopped {\n\t\treturn\n\t}\n\n\t// Wait for flushLoop to end outside of the lock, as it may need the lock to complete.\n\t// See https://github.com/uber-go/zap/issues/1428 for details.\n\t<-s.done\n\n\treturn s.Sync()\n}\n"
  },
  {
    "path": "zapcore/buffered_write_syncer_bench_test.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport (\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc BenchmarkBufferedWriteSyncer(b *testing.B) {\n\tb.Run(\"write file with buffer\", func(b *testing.B) {\n\t\tfile, err := os.CreateTemp(b.TempDir(), \"test.log\")\n\t\trequire.NoError(b, err)\n\n\t\tdefer func() {\n\t\t\tassert.NoError(b, file.Close())\n\t\t}()\n\n\t\tw := &BufferedWriteSyncer{\n\t\t\tWS: AddSync(file),\n\t\t}\n\t\tdefer func() {\n\t\t\tassert.NoError(b, w.Stop(), \"failed to stop buffered write syncer\")\n\t\t}()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tif _, err := w.Write([]byte(\"foobarbazbabble\")); err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "zapcore/buffered_write_syncer_test.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/zap/internal/ztest\"\n)\n\nfunc TestBufferWriter(t *testing.T) {\n\t// If we pass a plain io.Writer, make sure that we still get a WriteSyncer\n\t// with a no-op Sync.\n\tt.Run(\"sync\", func(t *testing.T) {\n\t\tbuf := &bytes.Buffer{}\n\t\tws := &BufferedWriteSyncer{WS: AddSync(buf)}\n\n\t\trequireWriteWorks(t, ws)\n\t\tassert.Empty(t, buf.String(), \"Unexpected log calling a no-op Write method.\")\n\t\tassert.NoError(t, ws.Sync(), \"Unexpected error calling a no-op Sync method.\")\n\t\tassert.Equal(t, \"foo\", buf.String(), \"Unexpected log string\")\n\t\tassert.NoError(t, ws.Stop())\n\t})\n\n\tt.Run(\"stop\", func(t *testing.T) {\n\t\tbuf := &bytes.Buffer{}\n\t\tws := &BufferedWriteSyncer{WS: AddSync(buf)}\n\t\trequireWriteWorks(t, ws)\n\t\tassert.Empty(t, buf.String(), \"Unexpected log calling a no-op Write method.\")\n\t\tassert.NoError(t, ws.Stop())\n\t\tassert.Equal(t, \"foo\", buf.String(), \"Unexpected log string\")\n\t})\n\n\tt.Run(\"stop race with flush\", func(t *testing.T) {\n\t\tbuf := &bytes.Buffer{}\n\t\tws := &BufferedWriteSyncer{WS: AddSync(buf), FlushInterval: 1}\n\t\trequireWriteWorks(t, ws)\n\t\tassert.NoError(t, ws.Stop())\n\t\tassert.Equal(t, \"foo\", buf.String(), \"Unexpected log string\")\n\t})\n\n\tt.Run(\"stop twice\", func(t *testing.T) {\n\t\tws := &BufferedWriteSyncer{WS: &ztest.FailWriter{}}\n\t\t_, err := ws.Write([]byte(\"foo\"))\n\t\trequire.NoError(t, err, \"Unexpected error writing to WriteSyncer.\")\n\t\tassert.Error(t, ws.Stop(), \"Expected stop to fail.\")\n\t\tassert.NoError(t, ws.Stop(), \"Expected stop to not fail.\")\n\t})\n\n\tt.Run(\"wrap twice\", func(t *testing.T) {\n\t\tbuf := &bytes.Buffer{}\n\t\tbufsync := &BufferedWriteSyncer{WS: AddSync(buf)}\n\t\tws := &BufferedWriteSyncer{WS: bufsync}\n\t\trequireWriteWorks(t, ws)\n\t\tassert.Empty(t, buf.String(), \"Unexpected log calling a no-op Write method.\")\n\t\trequire.NoError(t, ws.Sync())\n\t\tassert.Equal(t, \"foo\", buf.String())\n\t\tassert.NoError(t, ws.Stop())\n\t\tassert.NoError(t, bufsync.Stop())\n\t\tassert.Equal(t, \"foo\", buf.String(), \"Unexpected log string\")\n\t})\n\n\tt.Run(\"small buffer\", func(t *testing.T) {\n\t\tbuf := &bytes.Buffer{}\n\t\tws := &BufferedWriteSyncer{WS: AddSync(buf), Size: 5}\n\n\t\trequireWriteWorks(t, ws)\n\t\tassert.Equal(t, \"\", buf.String(), \"Unexpected log calling a no-op Write method.\")\n\t\trequireWriteWorks(t, ws)\n\t\tassert.Equal(t, \"foo\", buf.String(), \"Unexpected log string\")\n\t\tassert.NoError(t, ws.Stop())\n\t})\n\n\tt.Run(\"with lockedWriteSyncer\", func(t *testing.T) {\n\t\tbuf := &bytes.Buffer{}\n\t\tws := &BufferedWriteSyncer{WS: Lock(AddSync(buf)), Size: 5}\n\n\t\trequireWriteWorks(t, ws)\n\t\tassert.Equal(t, \"\", buf.String(), \"Unexpected log calling a no-op Write method.\")\n\t\trequireWriteWorks(t, ws)\n\t\tassert.Equal(t, \"foo\", buf.String(), \"Unexpected log string\")\n\t\tassert.NoError(t, ws.Stop())\n\t})\n\n\tt.Run(\"flush error\", func(t *testing.T) {\n\t\tws := &BufferedWriteSyncer{WS: &ztest.FailWriter{}, Size: 4}\n\t\tn, err := ws.Write([]byte(\"foo\"))\n\t\trequire.NoError(t, err, \"Unexpected error writing to WriteSyncer.\")\n\t\trequire.Equal(t, 3, n, \"Wrote an unexpected number of bytes.\")\n\t\t_, err = ws.Write([]byte(\"foo\"))\n\t\tassert.Error(t, err, \"Expected error writing to WriteSyncer.\")\n\t\tassert.Error(t, ws.Stop(), \"Expected stop to fail.\")\n\t})\n\n\tt.Run(\"flush timer\", func(t *testing.T) {\n\t\tbuf := &bytes.Buffer{}\n\t\tclock := ztest.NewMockClock()\n\t\tws := &BufferedWriteSyncer{\n\t\t\tWS:            AddSync(buf),\n\t\t\tSize:          6,\n\t\t\tFlushInterval: time.Microsecond,\n\t\t\tClock:         clock,\n\t\t}\n\t\trequireWriteWorks(t, ws)\n\t\tclock.Add(10 * time.Microsecond)\n\t\tassert.Equal(t, \"foo\", buf.String(), \"Unexpected log string\")\n\n\t\t// flush twice to validate loop logic\n\t\trequireWriteWorks(t, ws)\n\t\tclock.Add(10 * time.Microsecond)\n\t\tassert.Equal(t, \"foofoo\", buf.String(), \"Unexpected log string\")\n\t\tassert.NoError(t, ws.Stop())\n\t})\n}\n\nfunc TestBufferWriterWithoutStart(t *testing.T) {\n\tt.Run(\"stop\", func(t *testing.T) {\n\t\tws := &BufferedWriteSyncer{WS: AddSync(new(bytes.Buffer))}\n\t\tassert.NoError(t, ws.Stop(), \"Stop must not fail\")\n\t})\n\n\tt.Run(\"Sync\", func(t *testing.T) {\n\t\tws := &BufferedWriteSyncer{WS: AddSync(new(bytes.Buffer))}\n\t\tassert.NoError(t, ws.Sync(), \"Sync must not fail\")\n\t})\n}\n"
  },
  {
    "path": "zapcore/clock.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport \"time\"\n\n// DefaultClock is the default clock used by Zap in operations that require\n// time. This clock uses the system clock for all operations.\nvar DefaultClock = systemClock{}\n\n// Clock is a source of time for logged entries.\ntype Clock interface {\n\t// Now returns the current local time.\n\tNow() time.Time\n\n\t// NewTicker returns *time.Ticker that holds a channel\n\t// that delivers \"ticks\" of a clock.\n\tNewTicker(time.Duration) *time.Ticker\n}\n\n// systemClock implements default Clock that uses system time.\ntype systemClock struct{}\n\nfunc (systemClock) Now() time.Time {\n\treturn time.Now()\n}\n\nfunc (systemClock) NewTicker(duration time.Duration) *time.Ticker {\n\treturn time.NewTicker(duration)\n}\n"
  },
  {
    "path": "zapcore/clock_test.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"go.uber.org/zap/internal/ztest\"\n)\n\n// Verify that the mock clock satisfies the Clock interface.\nvar _ Clock = (*ztest.MockClock)(nil)\n\nfunc TestSystemClock_NewTicker(t *testing.T) {\n\twant := 3\n\n\tvar n int\n\ttimer := DefaultClock.NewTicker(time.Millisecond)\n\tfor range timer.C {\n\t\tn++\n\t\tif n == want {\n\t\t\treturn\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "zapcore/console_encoder.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport (\n\t\"fmt\"\n\n\t\"go.uber.org/zap/buffer\"\n\t\"go.uber.org/zap/internal/bufferpool\"\n\t\"go.uber.org/zap/internal/pool\"\n)\n\nvar _sliceEncoderPool = pool.New(func() *sliceArrayEncoder {\n\treturn &sliceArrayEncoder{\n\t\telems: make([]interface{}, 0, 2),\n\t}\n})\n\nfunc getSliceEncoder() *sliceArrayEncoder {\n\treturn _sliceEncoderPool.Get()\n}\n\nfunc putSliceEncoder(e *sliceArrayEncoder) {\n\te.elems = e.elems[:0]\n\t_sliceEncoderPool.Put(e)\n}\n\ntype consoleEncoder struct {\n\t*jsonEncoder\n}\n\n// NewConsoleEncoder creates an encoder whose output is designed for human -\n// rather than machine - consumption. It serializes the core log entry data\n// (message, level, timestamp, etc.) in a plain-text format and leaves the\n// structured context as JSON.\n//\n// Note that although the console encoder doesn't use the keys specified in the\n// encoder configuration, it will omit any element whose key is set to the empty\n// string.\nfunc NewConsoleEncoder(cfg EncoderConfig) Encoder {\n\tif cfg.ConsoleSeparator == \"\" {\n\t\t// Use a default delimiter of '\\t' for backwards compatibility\n\t\tcfg.ConsoleSeparator = \"\\t\"\n\t}\n\treturn consoleEncoder{newJSONEncoder(cfg, true)}\n}\n\nfunc (c consoleEncoder) Clone() Encoder {\n\treturn consoleEncoder{c.jsonEncoder.Clone().(*jsonEncoder)}\n}\n\nfunc (c consoleEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, error) {\n\tline := bufferpool.Get()\n\n\t// We don't want the entry's metadata to be quoted and escaped (if it's\n\t// encoded as strings), which means that we can't use the JSON encoder. The\n\t// simplest option is to use the memory encoder and fmt.Fprint.\n\t//\n\t// If this ever becomes a performance bottleneck, we can implement\n\t// ArrayEncoder for our plain-text format.\n\tarr := getSliceEncoder()\n\tif c.TimeKey != \"\" && c.EncodeTime != nil && !ent.Time.IsZero() {\n\t\tc.EncodeTime(ent.Time, arr)\n\t}\n\tif c.LevelKey != \"\" && c.EncodeLevel != nil {\n\t\tc.EncodeLevel(ent.Level, arr)\n\t}\n\tif ent.LoggerName != \"\" && c.NameKey != \"\" {\n\t\tnameEncoder := c.EncodeName\n\n\t\tif nameEncoder == nil {\n\t\t\t// Fall back to FullNameEncoder for backward compatibility.\n\t\t\tnameEncoder = FullNameEncoder\n\t\t}\n\n\t\tnameEncoder(ent.LoggerName, arr)\n\t}\n\tif ent.Caller.Defined {\n\t\tif c.CallerKey != \"\" && c.EncodeCaller != nil {\n\t\t\tc.EncodeCaller(ent.Caller, arr)\n\t\t}\n\t\tif c.FunctionKey != \"\" {\n\t\t\tarr.AppendString(ent.Caller.Function)\n\t\t}\n\t}\n\tfor i := range arr.elems {\n\t\tif i > 0 {\n\t\t\tline.AppendString(c.ConsoleSeparator)\n\t\t}\n\t\t_, _ = fmt.Fprint(line, arr.elems[i])\n\t}\n\tputSliceEncoder(arr)\n\n\t// Add the message itself.\n\tif c.MessageKey != \"\" {\n\t\tc.addSeparatorIfNecessary(line)\n\t\tline.AppendString(ent.Message)\n\t}\n\n\t// Add any structured context.\n\tc.writeContext(line, fields)\n\n\t// If there's no stacktrace key, honor that; this allows users to force\n\t// single-line output.\n\tif ent.Stack != \"\" && c.StacktraceKey != \"\" {\n\t\tline.AppendByte('\\n')\n\t\tline.AppendString(ent.Stack)\n\t}\n\n\tline.AppendString(c.LineEnding)\n\treturn line, nil\n}\n\nfunc (c consoleEncoder) writeContext(line *buffer.Buffer, extra []Field) {\n\tcontext := c.jsonEncoder.Clone().(*jsonEncoder)\n\tdefer func() {\n\t\t// putJSONEncoder assumes the buffer is still used, but we write out the buffer so\n\t\t// we can free it.\n\t\tcontext.buf.Free()\n\t\tputJSONEncoder(context)\n\t}()\n\n\taddFields(context, extra)\n\tcontext.closeOpenNamespaces()\n\tif context.buf.Len() == 0 {\n\t\treturn\n\t}\n\n\tc.addSeparatorIfNecessary(line)\n\tline.AppendByte('{')\n\tline.Write(context.buf.Bytes())\n\tline.AppendByte('}')\n}\n\nfunc (c consoleEncoder) addSeparatorIfNecessary(line *buffer.Buffer) {\n\tif line.Len() > 0 {\n\t\tline.AppendString(c.ConsoleSeparator)\n\t}\n}\n"
  },
  {
    "path": "zapcore/console_encoder_bench_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore_test\n\nimport (\n\t\"testing\"\n\n\t//revive:disable:dot-imports\n\t. \"go.uber.org/zap/zapcore\"\n)\n\nfunc BenchmarkZapConsole(b *testing.B) {\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tenc := NewConsoleEncoder(humanEncoderConfig())\n\t\t\tenc.AddString(\"str\", \"foo\")\n\t\t\tenc.AddInt64(\"int64-1\", 1)\n\t\t\tenc.AddInt64(\"int64-2\", 2)\n\t\t\tenc.AddFloat64(\"float64\", 1.0)\n\t\t\tenc.AddString(\"string1\", \"\\n\")\n\t\t\tenc.AddString(\"string2\", \"💩\")\n\t\t\tenc.AddString(\"string3\", \"🤔\")\n\t\t\tenc.AddString(\"string4\", \"🙊\")\n\t\t\tenc.AddBool(\"bool\", true)\n\t\t\tbuf, _ := enc.EncodeEntry(Entry{\n\t\t\t\tMessage: \"fake\",\n\t\t\t\tLevel:   DebugLevel,\n\t\t\t}, nil)\n\t\t\tbuf.Free()\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "zapcore/console_encoder_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\npackage zapcore_test\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t//revive:disable:dot-imports\n\t. \"go.uber.org/zap/zapcore\"\n)\n\nvar testEntry = Entry{\n\tLoggerName: \"main\",\n\tLevel:      InfoLevel,\n\tMessage:    `hello`,\n\tTime:       _epoch,\n\tStack:      \"fake-stack\",\n\tCaller:     EntryCaller{Defined: true, File: \"foo.go\", Line: 42, Function: \"foo.Foo\"},\n}\n\nfunc TestConsoleEncodeEntry(t *testing.T) {\n\ttests := []struct {\n\t\tdesc     string\n\t\texpected string\n\t\tent      Entry\n\t\tfields   []Field\n\t}{\n\t\t{\n\t\t\tdesc:     \"info no fields\",\n\t\t\texpected: \"2018-06-19T16:33:42Z\\tinfo\\tbob\\tlob law\\n\",\n\t\t\tent: Entry{\n\t\t\t\tLevel:      InfoLevel,\n\t\t\t\tTime:       time.Date(2018, 6, 19, 16, 33, 42, 99, time.UTC),\n\t\t\t\tLoggerName: \"bob\",\n\t\t\t\tMessage:    \"lob law\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc:     \"zero_time_omitted\",\n\t\t\texpected: \"info\\tname\\tmessage\\n\",\n\t\t\tent: Entry{\n\t\t\t\tLevel:      InfoLevel,\n\t\t\t\tTime:       time.Time{},\n\t\t\t\tLoggerName: \"name\",\n\t\t\t\tMessage:    \"message\",\n\t\t\t},\n\t\t},\n\t}\n\n\tcfg := testEncoderConfig()\n\tcfg.EncodeTime = RFC3339TimeEncoder\n\tenc := NewConsoleEncoder(cfg)\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tbuf, err := enc.EncodeEntry(tt.ent, tt.fields)\n\t\t\tif assert.NoError(t, err, \"Unexpected console encoding error.\") {\n\t\t\t\tassert.Equal(t, tt.expected, buf.String(), \"Incorrect encoded entry.\")\n\t\t\t}\n\t\t\tbuf.Free()\n\t\t})\n\t}\n}\n\nfunc TestConsoleSeparator(t *testing.T) {\n\ttests := []struct {\n\t\tdesc        string\n\t\tseparator   string\n\t\twantConsole string\n\t}{\n\t\t{\n\t\t\tdesc:        \"space console separator\",\n\t\t\tseparator:   \" \",\n\t\t\twantConsole: \"0 info main foo.go:42 foo.Foo hello\\nfake-stack\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc:        \"default console separator\",\n\t\t\tseparator:   \"\",\n\t\t\twantConsole: \"0\\tinfo\\tmain\\tfoo.go:42\\tfoo.Foo\\thello\\nfake-stack\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc:        \"tag console separator\",\n\t\t\tseparator:   \"\\t\",\n\t\t\twantConsole: \"0\\tinfo\\tmain\\tfoo.go:42\\tfoo.Foo\\thello\\nfake-stack\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc:        \"dash console separator\",\n\t\t\tseparator:   \"--\",\n\t\t\twantConsole: \"0--info--main--foo.go:42--foo.Foo--hello\\nfake-stack\\n\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tconsole := NewConsoleEncoder(encoderTestEncoderConfig(tt.separator))\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tentry := testEntry\n\t\t\tconsoleOut, err := console.EncodeEntry(entry, nil)\n\t\t\tif !assert.NoError(t, err) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tassert.Equal(\n\t\t\t\tt,\n\t\t\t\ttt.wantConsole,\n\t\t\t\tconsoleOut.String(),\n\t\t\t\t\"Unexpected console output\",\n\t\t\t)\n\t\t})\n\n\t}\n}\n\nfunc encoderTestEncoderConfig(separator string) EncoderConfig {\n\ttestEncoder := testEncoderConfig()\n\ttestEncoder.ConsoleSeparator = separator\n\treturn testEncoder\n}\n"
  },
  {
    "path": "zapcore/core.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\n// Core is a minimal, fast logger interface. It's designed for library authors\n// to wrap in a more user-friendly API.\ntype Core interface {\n\tLevelEnabler\n\n\t// With adds structured context to the Core.\n\tWith([]Field) Core\n\t// Check determines whether the supplied Entry should be logged (using the\n\t// embedded LevelEnabler and possibly some extra logic). If the entry\n\t// should be logged, the Core adds itself to the CheckedEntry and returns\n\t// the result.\n\t//\n\t// Callers must use Check before calling Write.\n\tCheck(Entry, *CheckedEntry) *CheckedEntry\n\t// Write serializes the Entry and any Fields supplied at the log site and\n\t// writes them to their destination.\n\t//\n\t// If called, Write should always log the Entry and Fields; it should not\n\t// replicate the logic of Check.\n\tWrite(Entry, []Field) error\n\t// Sync flushes buffered logs (if any).\n\tSync() error\n}\n\ntype nopCore struct{}\n\n// NewNopCore returns a no-op Core.\nfunc NewNopCore() Core                                        { return nopCore{} }\nfunc (nopCore) Enabled(Level) bool                            { return false }\nfunc (n nopCore) With([]Field) Core                           { return n }\nfunc (nopCore) Check(_ Entry, ce *CheckedEntry) *CheckedEntry { return ce }\nfunc (nopCore) Write(Entry, []Field) error                    { return nil }\nfunc (nopCore) Sync() error                                   { return nil }\n\n// NewCore creates a Core that writes logs to a WriteSyncer.\nfunc NewCore(enc Encoder, ws WriteSyncer, enab LevelEnabler) Core {\n\treturn &ioCore{\n\t\tLevelEnabler: enab,\n\t\tenc:          enc,\n\t\tout:          ws,\n\t}\n}\n\ntype ioCore struct {\n\tLevelEnabler\n\tenc Encoder\n\tout WriteSyncer\n}\n\nvar (\n\t_ Core           = (*ioCore)(nil)\n\t_ leveledEnabler = (*ioCore)(nil)\n)\n\nfunc (c *ioCore) Level() Level {\n\treturn LevelOf(c.LevelEnabler)\n}\n\nfunc (c *ioCore) With(fields []Field) Core {\n\tclone := c.clone()\n\taddFields(clone.enc, fields)\n\treturn clone\n}\n\nfunc (c *ioCore) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {\n\tif c.Enabled(ent.Level) {\n\t\treturn ce.AddCore(ent, c)\n\t}\n\treturn ce\n}\n\nfunc (c *ioCore) Write(ent Entry, fields []Field) error {\n\tbuf, err := c.enc.EncodeEntry(ent, fields)\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = c.out.Write(buf.Bytes())\n\tbuf.Free()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif ent.Level > ErrorLevel {\n\t\t// Since we may be crashing the program, sync the output.\n\t\t// Ignore Sync errors, pending a clean solution to issue #370.\n\t\t_ = c.Sync()\n\t}\n\treturn nil\n}\n\nfunc (c *ioCore) Sync() error {\n\treturn c.out.Sync()\n}\n\nfunc (c *ioCore) clone() *ioCore {\n\treturn &ioCore{\n\t\tLevelEnabler: c.LevelEnabler,\n\t\tenc:          c.enc.Clone(),\n\t\tout:          c.out,\n\t}\n}\n"
  },
  {
    "path": "zapcore/core_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore_test\n\nimport (\n\t\"errors\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.uber.org/zap/internal/ztest\"\n\t//revive:disable:dot-imports\n\t. \"go.uber.org/zap/zapcore\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc makeInt64Field(key string, val int) Field {\n\treturn Field{Type: Int64Type, Integer: int64(val), Key: key}\n}\n\nfunc TestNopCore(t *testing.T) {\n\tentry := Entry{\n\t\tMessage:    \"test\",\n\t\tLevel:      InfoLevel,\n\t\tTime:       time.Now(),\n\t\tLoggerName: \"main\",\n\t\tStack:      \"fake-stack\",\n\t}\n\tce := &CheckedEntry{}\n\n\tallLevels := []Level{\n\t\tDebugLevel,\n\t\tInfoLevel,\n\t\tWarnLevel,\n\t\tErrorLevel,\n\t\tDPanicLevel,\n\t\tPanicLevel,\n\t\tFatalLevel,\n\t}\n\tcore := NewNopCore()\n\tassert.Equal(t, core, core.With([]Field{makeInt64Field(\"k\", 42)}), \"Expected no-op With.\")\n\tfor _, level := range allLevels {\n\t\tassert.False(t, core.Enabled(level), \"Expected all levels to be disabled in no-op core.\")\n\t\tassert.Equal(t, ce, core.Check(entry, ce), \"Expected no-op Check to return checked entry unchanged.\")\n\t\tassert.NoError(t, core.Write(entry, nil), \"Expected no-op Writes to always succeed.\")\n\t\tassert.NoError(t, core.Sync(), \"Expected no-op Syncs to always succeed.\")\n\t}\n}\n\nfunc TestIOCore(t *testing.T) {\n\ttemp, err := os.CreateTemp(t.TempDir(), \"test.log\")\n\trequire.NoError(t, err)\n\n\t// Drop timestamps for simpler assertions (timestamp encoding is tested\n\t// elsewhere).\n\tcfg := testEncoderConfig()\n\tcfg.TimeKey = \"\"\n\n\tcore := NewCore(\n\t\tNewJSONEncoder(cfg),\n\t\ttemp,\n\t\tInfoLevel,\n\t).With([]Field{makeInt64Field(\"k\", 1)})\n\tdefer assert.NoError(t, core.Sync(), \"Expected Syncing a temp file to succeed.\")\n\n\tt.Run(\"LevelOf\", func(t *testing.T) {\n\t\tassert.Equal(t, InfoLevel, LevelOf(core), \"Incorrect Core Level\")\n\t})\n\n\tif ce := core.Check(Entry{Level: DebugLevel, Message: \"debug\"}, nil); ce != nil {\n\t\tce.Write(makeInt64Field(\"k\", 2))\n\t}\n\tif ce := core.Check(Entry{Level: InfoLevel, Message: \"info\"}, nil); ce != nil {\n\t\tce.Write(makeInt64Field(\"k\", 3))\n\t}\n\tif ce := core.Check(Entry{Level: WarnLevel, Message: \"warn\"}, nil); ce != nil {\n\t\tce.Write(makeInt64Field(\"k\", 4))\n\t}\n\n\tlogged, err := os.ReadFile(temp.Name())\n\trequire.NoError(t, err, \"Failed to read from temp file.\")\n\trequire.Equal(\n\t\tt,\n\t\t`{\"level\":\"info\",\"msg\":\"info\",\"k\":1,\"k\":3}`+\"\\n\"+\n\t\t\t`{\"level\":\"warn\",\"msg\":\"warn\",\"k\":1,\"k\":4}`+\"\\n\",\n\t\tstring(logged),\n\t\t\"Unexpected log output.\",\n\t)\n}\n\nfunc TestIOCoreSyncFail(t *testing.T) {\n\tsink := &ztest.Discarder{}\n\terr := errors.New(\"failed\")\n\tsink.SetError(err)\n\n\tcore := NewCore(\n\t\tNewJSONEncoder(testEncoderConfig()),\n\t\tsink,\n\t\tDebugLevel,\n\t)\n\n\tassert.Equal(\n\t\tt,\n\t\terr,\n\t\tcore.Sync(),\n\t\t\"Expected core.Sync to return errors from underlying WriteSyncer.\",\n\t)\n}\n\nfunc TestIOCoreSyncsOutput(t *testing.T) {\n\ttests := []struct {\n\t\tentry      Entry\n\t\tshouldSync bool\n\t}{\n\t\t{Entry{Level: DebugLevel}, false},\n\t\t{Entry{Level: InfoLevel}, false},\n\t\t{Entry{Level: WarnLevel}, false},\n\t\t{Entry{Level: ErrorLevel}, false},\n\t\t{Entry{Level: DPanicLevel}, true},\n\t\t{Entry{Level: PanicLevel}, true},\n\t\t{Entry{Level: FatalLevel}, true},\n\t}\n\n\tfor _, tt := range tests {\n\t\tsink := &ztest.Discarder{}\n\t\tcore := NewCore(\n\t\t\tNewJSONEncoder(testEncoderConfig()),\n\t\t\tsink,\n\t\t\tDebugLevel,\n\t\t)\n\n\t\tassert.NoError(t, core.Write(tt.entry, nil), \"Unexpected error writing entry.\")\n\t\tassert.Equal(t, tt.shouldSync, sink.Called(), \"Incorrect Sync behavior.\")\n\t}\n}\n\nfunc TestIOCoreWriteFailure(t *testing.T) {\n\tcore := NewCore(\n\t\tNewJSONEncoder(testEncoderConfig()),\n\t\tLock(&ztest.FailWriter{}),\n\t\tDebugLevel,\n\t)\n\terr := core.Write(Entry{}, nil)\n\t// Should log the error.\n\tassert.Error(t, err, \"Expected writing Entry to fail.\")\n}\n"
  },
  {
    "path": "zapcore/doc.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// Package zapcore defines and implements the low-level interfaces upon which\n// zap is built. By providing alternate implementations of these interfaces,\n// external packages can extend zap's capabilities.\npackage zapcore // import \"go.uber.org/zap/zapcore\"\n"
  },
  {
    "path": "zapcore/encoder.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n\t\"time\"\n\n\t\"go.uber.org/zap/buffer\"\n)\n\n// DefaultLineEnding defines the default line ending when writing logs.\n// Alternate line endings specified in EncoderConfig can override this\n// behavior.\nconst DefaultLineEnding = \"\\n\"\n\n// OmitKey defines the key to use when callers want to remove a key from log output.\nconst OmitKey = \"\"\n\n// A LevelEncoder serializes a Level to a primitive type.\n//\n// This function must make exactly one call\n// to a PrimitiveArrayEncoder's Append* method.\ntype LevelEncoder func(Level, PrimitiveArrayEncoder)\n\n// LowercaseLevelEncoder serializes a Level to a lowercase string. For example,\n// InfoLevel is serialized to \"info\".\nfunc LowercaseLevelEncoder(l Level, enc PrimitiveArrayEncoder) {\n\tenc.AppendString(l.String())\n}\n\n// LowercaseColorLevelEncoder serializes a Level to a lowercase string and adds coloring.\n// For example, InfoLevel is serialized to \"info\" and colored blue.\nfunc LowercaseColorLevelEncoder(l Level, enc PrimitiveArrayEncoder) {\n\ts, ok := _levelToLowercaseColorString[l]\n\tif !ok {\n\t\ts = _unknownLevelColor.Add(l.String())\n\t}\n\tenc.AppendString(s)\n}\n\n// CapitalLevelEncoder serializes a Level to an all-caps string. For example,\n// InfoLevel is serialized to \"INFO\".\nfunc CapitalLevelEncoder(l Level, enc PrimitiveArrayEncoder) {\n\tenc.AppendString(l.CapitalString())\n}\n\n// CapitalColorLevelEncoder serializes a Level to an all-caps string and adds color.\n// For example, InfoLevel is serialized to \"INFO\" and colored blue.\nfunc CapitalColorLevelEncoder(l Level, enc PrimitiveArrayEncoder) {\n\ts, ok := _levelToCapitalColorString[l]\n\tif !ok {\n\t\ts = _unknownLevelColor.Add(l.CapitalString())\n\t}\n\tenc.AppendString(s)\n}\n\n// UnmarshalText unmarshals text to a LevelEncoder. \"capital\" is unmarshaled to\n// CapitalLevelEncoder, \"coloredCapital\" is unmarshaled to CapitalColorLevelEncoder,\n// \"colored\" is unmarshaled to LowercaseColorLevelEncoder, and anything else\n// is unmarshaled to LowercaseLevelEncoder.\nfunc (e *LevelEncoder) UnmarshalText(text []byte) error {\n\tswitch string(text) {\n\tcase \"capital\":\n\t\t*e = CapitalLevelEncoder\n\tcase \"capitalColor\":\n\t\t*e = CapitalColorLevelEncoder\n\tcase \"color\":\n\t\t*e = LowercaseColorLevelEncoder\n\tdefault:\n\t\t*e = LowercaseLevelEncoder\n\t}\n\treturn nil\n}\n\n// A TimeEncoder serializes a time.Time to a primitive type.\n//\n// This function must make exactly one call\n// to a PrimitiveArrayEncoder's Append* method.\ntype TimeEncoder func(time.Time, PrimitiveArrayEncoder)\n\n// EpochTimeEncoder serializes a time.Time to a floating-point number of seconds\n// since the Unix epoch.\nfunc EpochTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {\n\tnanos := t.UnixNano()\n\tsec := float64(nanos) / float64(time.Second)\n\tenc.AppendFloat64(sec)\n}\n\n// EpochMillisTimeEncoder serializes a time.Time to a floating-point number of\n// milliseconds since the Unix epoch.\nfunc EpochMillisTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {\n\tnanos := t.UnixNano()\n\tmillis := float64(nanos) / float64(time.Millisecond)\n\tenc.AppendFloat64(millis)\n}\n\n// EpochNanosTimeEncoder serializes a time.Time to an integer number of\n// nanoseconds since the Unix epoch.\nfunc EpochNanosTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {\n\tenc.AppendInt64(t.UnixNano())\n}\n\nfunc encodeTimeLayout(t time.Time, layout string, enc PrimitiveArrayEncoder) {\n\ttype appendTimeEncoder interface {\n\t\tAppendTimeLayout(time.Time, string)\n\t}\n\n\tif enc, ok := enc.(appendTimeEncoder); ok {\n\t\tenc.AppendTimeLayout(t, layout)\n\t\treturn\n\t}\n\n\tenc.AppendString(t.Format(layout))\n}\n\n// ISO8601TimeEncoder serializes a time.Time to an ISO8601-formatted string\n// with millisecond precision.\n//\n// If enc supports AppendTimeLayout(t time.Time,layout string), it's used\n// instead of appending a pre-formatted string value.\nfunc ISO8601TimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {\n\tencodeTimeLayout(t, \"2006-01-02T15:04:05.000Z0700\", enc)\n}\n\n// RFC3339TimeEncoder serializes a time.Time to an RFC3339-formatted string.\n//\n// If enc supports AppendTimeLayout(t time.Time,layout string), it's used\n// instead of appending a pre-formatted string value.\nfunc RFC3339TimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {\n\tencodeTimeLayout(t, time.RFC3339, enc)\n}\n\n// RFC3339NanoTimeEncoder serializes a time.Time to an RFC3339-formatted string\n// with nanosecond precision.\n//\n// If enc supports AppendTimeLayout(t time.Time,layout string), it's used\n// instead of appending a pre-formatted string value.\nfunc RFC3339NanoTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {\n\tencodeTimeLayout(t, time.RFC3339Nano, enc)\n}\n\n// TimeEncoderOfLayout returns TimeEncoder which serializes a time.Time using\n// given layout.\nfunc TimeEncoderOfLayout(layout string) TimeEncoder {\n\treturn func(t time.Time, enc PrimitiveArrayEncoder) {\n\t\tencodeTimeLayout(t, layout, enc)\n\t}\n}\n\n// UnmarshalText unmarshals text to a TimeEncoder.\n// \"rfc3339nano\" and \"RFC3339Nano\" are unmarshaled to RFC3339NanoTimeEncoder.\n// \"rfc3339\" and \"RFC3339\" are unmarshaled to RFC3339TimeEncoder.\n// \"iso8601\" and \"ISO8601\" are unmarshaled to ISO8601TimeEncoder.\n// \"millis\" is unmarshaled to EpochMillisTimeEncoder.\n// \"nanos\" is unmarshaled to EpochNanosEncoder.\n// Anything else is unmarshaled to EpochTimeEncoder.\nfunc (e *TimeEncoder) UnmarshalText(text []byte) error {\n\tswitch string(text) {\n\tcase \"rfc3339nano\", \"RFC3339Nano\":\n\t\t*e = RFC3339NanoTimeEncoder\n\tcase \"rfc3339\", \"RFC3339\":\n\t\t*e = RFC3339TimeEncoder\n\tcase \"iso8601\", \"ISO8601\":\n\t\t*e = ISO8601TimeEncoder\n\tcase \"millis\":\n\t\t*e = EpochMillisTimeEncoder\n\tcase \"nanos\":\n\t\t*e = EpochNanosTimeEncoder\n\tdefault:\n\t\t*e = EpochTimeEncoder\n\t}\n\treturn nil\n}\n\n// UnmarshalYAML unmarshals YAML to a TimeEncoder.\n// If value is an object with a \"layout\" field, it will be unmarshaled to  TimeEncoder with given layout.\n//\n//\ttimeEncoder:\n//\t  layout: 06/01/02 03:04pm\n//\n// If value is string, it uses UnmarshalText.\n//\n//\ttimeEncoder: iso8601\nfunc (e *TimeEncoder) UnmarshalYAML(unmarshal func(interface{}) error) error {\n\tvar o struct {\n\t\tLayout string `json:\"layout\" yaml:\"layout\"`\n\t}\n\tif err := unmarshal(&o); err == nil {\n\t\t*e = TimeEncoderOfLayout(o.Layout)\n\t\treturn nil\n\t}\n\n\tvar s string\n\tif err := unmarshal(&s); err != nil {\n\t\treturn err\n\t}\n\treturn e.UnmarshalText([]byte(s))\n}\n\n// UnmarshalJSON unmarshals JSON to a TimeEncoder as same way UnmarshalYAML does.\nfunc (e *TimeEncoder) UnmarshalJSON(data []byte) error {\n\treturn e.UnmarshalYAML(func(v interface{}) error {\n\t\treturn json.Unmarshal(data, v)\n\t})\n}\n\n// A DurationEncoder serializes a time.Duration to a primitive type.\n//\n// This function must make exactly one call\n// to a PrimitiveArrayEncoder's Append* method.\ntype DurationEncoder func(time.Duration, PrimitiveArrayEncoder)\n\n// SecondsDurationEncoder serializes a time.Duration to a floating-point number of seconds elapsed.\nfunc SecondsDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) {\n\tenc.AppendFloat64(float64(d) / float64(time.Second))\n}\n\n// NanosDurationEncoder serializes a time.Duration to an integer number of\n// nanoseconds elapsed.\nfunc NanosDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) {\n\tenc.AppendInt64(int64(d))\n}\n\n// MillisDurationEncoder serializes a time.Duration to an integer number of\n// milliseconds elapsed.\nfunc MillisDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) {\n\tenc.AppendInt64(d.Nanoseconds() / 1e6)\n}\n\n// StringDurationEncoder serializes a time.Duration using its built-in String\n// method.\nfunc StringDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) {\n\tenc.AppendString(d.String())\n}\n\n// UnmarshalText unmarshals text to a DurationEncoder. \"string\" is unmarshaled\n// to StringDurationEncoder, and anything else is unmarshaled to\n// NanosDurationEncoder.\nfunc (e *DurationEncoder) UnmarshalText(text []byte) error {\n\tswitch string(text) {\n\tcase \"string\":\n\t\t*e = StringDurationEncoder\n\tcase \"nanos\":\n\t\t*e = NanosDurationEncoder\n\tcase \"ms\":\n\t\t*e = MillisDurationEncoder\n\tdefault:\n\t\t*e = SecondsDurationEncoder\n\t}\n\treturn nil\n}\n\n// A CallerEncoder serializes an EntryCaller to a primitive type.\n//\n// This function must make exactly one call\n// to a PrimitiveArrayEncoder's Append* method.\ntype CallerEncoder func(EntryCaller, PrimitiveArrayEncoder)\n\n// FullCallerEncoder serializes a caller in /full/path/to/package/file:line\n// format.\nfunc FullCallerEncoder(caller EntryCaller, enc PrimitiveArrayEncoder) {\n\t// TODO: consider using a byte-oriented API to save an allocation.\n\tenc.AppendString(caller.String())\n}\n\n// ShortCallerEncoder serializes a caller in package/file:line format, trimming\n// all but the final directory from the full path.\nfunc ShortCallerEncoder(caller EntryCaller, enc PrimitiveArrayEncoder) {\n\t// TODO: consider using a byte-oriented API to save an allocation.\n\tenc.AppendString(caller.TrimmedPath())\n}\n\n// UnmarshalText unmarshals text to a CallerEncoder. \"full\" is unmarshaled to\n// FullCallerEncoder and anything else is unmarshaled to ShortCallerEncoder.\nfunc (e *CallerEncoder) UnmarshalText(text []byte) error {\n\tswitch string(text) {\n\tcase \"full\":\n\t\t*e = FullCallerEncoder\n\tdefault:\n\t\t*e = ShortCallerEncoder\n\t}\n\treturn nil\n}\n\n// A NameEncoder serializes a period-separated logger name to a primitive\n// type.\n//\n// This function must make exactly one call\n// to a PrimitiveArrayEncoder's Append* method.\ntype NameEncoder func(string, PrimitiveArrayEncoder)\n\n// FullNameEncoder serializes the logger name as-is.\nfunc FullNameEncoder(loggerName string, enc PrimitiveArrayEncoder) {\n\tenc.AppendString(loggerName)\n}\n\n// UnmarshalText unmarshals text to a NameEncoder. Currently, everything is\n// unmarshaled to FullNameEncoder.\nfunc (e *NameEncoder) UnmarshalText(text []byte) error {\n\tswitch string(text) {\n\tcase \"full\":\n\t\t*e = FullNameEncoder\n\tdefault:\n\t\t*e = FullNameEncoder\n\t}\n\treturn nil\n}\n\n// An EncoderConfig allows users to configure the concrete encoders supplied by\n// zapcore.\ntype EncoderConfig struct {\n\t// Set the keys used for each log entry. If any key is empty, that portion\n\t// of the entry is omitted.\n\tMessageKey     string `json:\"messageKey\" yaml:\"messageKey\"`\n\tLevelKey       string `json:\"levelKey\" yaml:\"levelKey\"`\n\tTimeKey        string `json:\"timeKey\" yaml:\"timeKey\"`\n\tNameKey        string `json:\"nameKey\" yaml:\"nameKey\"`\n\tCallerKey      string `json:\"callerKey\" yaml:\"callerKey\"`\n\tFunctionKey    string `json:\"functionKey\" yaml:\"functionKey\"`\n\tStacktraceKey  string `json:\"stacktraceKey\" yaml:\"stacktraceKey\"`\n\tSkipLineEnding bool   `json:\"skipLineEnding\" yaml:\"skipLineEnding\"`\n\tLineEnding     string `json:\"lineEnding\" yaml:\"lineEnding\"`\n\t// Configure the primitive representations of common complex types. For\n\t// example, some users may want all time.Times serialized as floating-point\n\t// seconds since epoch, while others may prefer ISO8601 strings.\n\tEncodeLevel    LevelEncoder    `json:\"levelEncoder\" yaml:\"levelEncoder\"`\n\tEncodeTime     TimeEncoder     `json:\"timeEncoder\" yaml:\"timeEncoder\"`\n\tEncodeDuration DurationEncoder `json:\"durationEncoder\" yaml:\"durationEncoder\"`\n\tEncodeCaller   CallerEncoder   `json:\"callerEncoder\" yaml:\"callerEncoder\"`\n\t// Unlike the other primitive type encoders, EncodeName is optional. The\n\t// zero value falls back to FullNameEncoder.\n\tEncodeName NameEncoder `json:\"nameEncoder\" yaml:\"nameEncoder\"`\n\t// Configure the encoder for interface{} type objects.\n\t// If not provided, objects are encoded using json.Encoder\n\tNewReflectedEncoder func(io.Writer) ReflectedEncoder `json:\"-\" yaml:\"-\"`\n\t// Configures the field separator used by the console encoder. Defaults\n\t// to tab.\n\tConsoleSeparator string `json:\"consoleSeparator\" yaml:\"consoleSeparator\"`\n}\n\n// ObjectEncoder is a strongly-typed, encoding-agnostic interface for adding a\n// map- or struct-like object to the logging context. Like maps, ObjectEncoders\n// aren't safe for concurrent use (though typical use shouldn't require locks).\ntype ObjectEncoder interface {\n\t// Logging-specific marshalers.\n\tAddArray(key string, marshaler ArrayMarshaler) error\n\tAddObject(key string, marshaler ObjectMarshaler) error\n\n\t// Built-in types.\n\tAddBinary(key string, value []byte)     // for arbitrary bytes\n\tAddByteString(key string, value []byte) // for UTF-8 encoded bytes\n\tAddBool(key string, value bool)\n\tAddComplex128(key string, value complex128)\n\tAddComplex64(key string, value complex64)\n\tAddDuration(key string, value time.Duration)\n\tAddFloat64(key string, value float64)\n\tAddFloat32(key string, value float32)\n\tAddInt(key string, value int)\n\tAddInt64(key string, value int64)\n\tAddInt32(key string, value int32)\n\tAddInt16(key string, value int16)\n\tAddInt8(key string, value int8)\n\tAddString(key, value string)\n\tAddTime(key string, value time.Time)\n\tAddUint(key string, value uint)\n\tAddUint64(key string, value uint64)\n\tAddUint32(key string, value uint32)\n\tAddUint16(key string, value uint16)\n\tAddUint8(key string, value uint8)\n\tAddUintptr(key string, value uintptr)\n\n\t// AddReflected uses reflection to serialize arbitrary objects, so it can be\n\t// slow and allocation-heavy.\n\tAddReflected(key string, value interface{}) error\n\t// OpenNamespace opens an isolated namespace where all subsequent fields will\n\t// be added. Applications can use namespaces to prevent key collisions when\n\t// injecting loggers into sub-components or third-party libraries.\n\tOpenNamespace(key string)\n}\n\n// ArrayEncoder is a strongly-typed, encoding-agnostic interface for adding\n// array-like objects to the logging context. Of note, it supports mixed-type\n// arrays even though they aren't typical in Go. Like slices, ArrayEncoders\n// aren't safe for concurrent use (though typical use shouldn't require locks).\ntype ArrayEncoder interface {\n\t// Built-in types.\n\tPrimitiveArrayEncoder\n\n\t// Time-related types.\n\tAppendDuration(time.Duration)\n\tAppendTime(time.Time)\n\n\t// Logging-specific marshalers.\n\tAppendArray(ArrayMarshaler) error\n\tAppendObject(ObjectMarshaler) error\n\n\t// AppendReflected uses reflection to serialize arbitrary objects, so it's\n\t// slow and allocation-heavy.\n\tAppendReflected(value interface{}) error\n}\n\n// PrimitiveArrayEncoder is the subset of the ArrayEncoder interface that deals\n// only in Go's built-in types. It's included only so that Duration- and\n// TimeEncoders cannot trigger infinite recursion.\ntype PrimitiveArrayEncoder interface {\n\t// Built-in types.\n\tAppendBool(bool)\n\tAppendByteString([]byte) // for UTF-8 encoded bytes\n\tAppendComplex128(complex128)\n\tAppendComplex64(complex64)\n\tAppendFloat64(float64)\n\tAppendFloat32(float32)\n\tAppendInt(int)\n\tAppendInt64(int64)\n\tAppendInt32(int32)\n\tAppendInt16(int16)\n\tAppendInt8(int8)\n\tAppendString(string)\n\tAppendUint(uint)\n\tAppendUint64(uint64)\n\tAppendUint32(uint32)\n\tAppendUint16(uint16)\n\tAppendUint8(uint8)\n\tAppendUintptr(uintptr)\n}\n\n// Encoder is a format-agnostic interface for all log entry marshalers. Since\n// log encoders don't need to support the same wide range of use cases as\n// general-purpose marshalers, it's possible to make them faster and\n// lower-allocation.\n//\n// Implementations of the ObjectEncoder interface's methods can, of course,\n// freely modify the receiver. However, the Clone and EncodeEntry methods will\n// be called concurrently and shouldn't modify the receiver.\ntype Encoder interface {\n\tObjectEncoder\n\n\t// Clone copies the encoder, ensuring that adding fields to the copy doesn't\n\t// affect the original.\n\tClone() Encoder\n\n\t// EncodeEntry encodes an entry and fields, along with any accumulated\n\t// context, into a byte buffer and returns it. Any fields that are empty,\n\t// including fields on the `Entry` type, should be omitted.\n\tEncodeEntry(Entry, []Field) (*buffer.Buffer, error)\n}\n"
  },
  {
    "path": "zapcore/encoder_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore_test\n\nimport (\n\t\"encoding/json\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.yaml.in/yaml/v3\"\n\n\t//revive:disable:dot-imports\n\t. \"go.uber.org/zap/zapcore\"\n)\n\nvar (\n\t_epoch     = time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC)\n\t_testEntry = Entry{\n\t\tLoggerName: \"main\",\n\t\tLevel:      InfoLevel,\n\t\tMessage:    `hello`,\n\t\tTime:       _epoch,\n\t\tStack:      \"fake-stack\",\n\t\tCaller:     EntryCaller{Defined: true, File: \"foo.go\", Line: 42, Function: \"foo.Foo\"},\n\t}\n)\n\nfunc testEncoderConfig() EncoderConfig {\n\treturn EncoderConfig{\n\t\tMessageKey:     \"msg\",\n\t\tLevelKey:       \"level\",\n\t\tNameKey:        \"name\",\n\t\tTimeKey:        \"ts\",\n\t\tCallerKey:      \"caller\",\n\t\tFunctionKey:    \"func\",\n\t\tStacktraceKey:  \"stacktrace\",\n\t\tLineEnding:     \"\\n\",\n\t\tEncodeTime:     EpochTimeEncoder,\n\t\tEncodeLevel:    LowercaseLevelEncoder,\n\t\tEncodeDuration: SecondsDurationEncoder,\n\t\tEncodeCaller:   ShortCallerEncoder,\n\t}\n}\n\nfunc humanEncoderConfig() EncoderConfig {\n\tcfg := testEncoderConfig()\n\tcfg.EncodeTime = ISO8601TimeEncoder\n\tcfg.EncodeLevel = CapitalLevelEncoder\n\tcfg.EncodeDuration = StringDurationEncoder\n\treturn cfg\n}\n\nfunc capitalNameEncoder(loggerName string, enc PrimitiveArrayEncoder) {\n\tenc.AppendString(strings.ToUpper(loggerName))\n}\n\nfunc TestEncoderConfiguration(t *testing.T) {\n\tbase := testEncoderConfig()\n\n\ttests := []struct {\n\t\tdesc            string\n\t\tcfg             EncoderConfig\n\t\tamendEntry      func(Entry) Entry\n\t\textra           func(Encoder)\n\t\texpectedJSON    string\n\t\texpectedConsole string\n\t}{\n\t\t{\n\t\t\tdesc: \"messages to be escaped\",\n\t\t\tcfg:  base,\n\t\t\tamendEntry: func(ent Entry) Entry {\n\t\t\t\tent.Message = `hello\\`\n\t\t\t\treturn ent\n\t\t\t},\n\t\t\texpectedJSON:    `{\"level\":\"info\",\"ts\":0,\"name\":\"main\",\"caller\":\"foo.go:42\",\"func\":\"foo.Foo\",\"msg\":\"hello\\\\\",\"stacktrace\":\"fake-stack\"}` + \"\\n\",\n\t\t\texpectedConsole: \"0\\tinfo\\tmain\\tfoo.go:42\\tfoo.Foo\\thello\\\\\\nfake-stack\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"use custom entry keys in JSON output and ignore them in console output\",\n\t\t\tcfg: EncoderConfig{\n\t\t\t\tLevelKey:       \"L\",\n\t\t\t\tTimeKey:        \"T\",\n\t\t\t\tMessageKey:     \"M\",\n\t\t\t\tNameKey:        \"N\",\n\t\t\t\tCallerKey:      \"C\",\n\t\t\t\tFunctionKey:    \"F\",\n\t\t\t\tStacktraceKey:  \"S\",\n\t\t\t\tLineEnding:     base.LineEnding,\n\t\t\t\tEncodeTime:     base.EncodeTime,\n\t\t\t\tEncodeDuration: base.EncodeDuration,\n\t\t\t\tEncodeLevel:    base.EncodeLevel,\n\t\t\t\tEncodeCaller:   base.EncodeCaller,\n\t\t\t},\n\t\t\texpectedJSON:    `{\"L\":\"info\",\"T\":0,\"N\":\"main\",\"C\":\"foo.go:42\",\"F\":\"foo.Foo\",\"M\":\"hello\",\"S\":\"fake-stack\"}` + \"\\n\",\n\t\t\texpectedConsole: \"0\\tinfo\\tmain\\tfoo.go:42\\tfoo.Foo\\thello\\nfake-stack\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"skip line ending if SkipLineEnding is 'true'\",\n\t\t\tcfg: EncoderConfig{\n\t\t\t\tLevelKey:       \"L\",\n\t\t\t\tTimeKey:        \"T\",\n\t\t\t\tMessageKey:     \"M\",\n\t\t\t\tNameKey:        \"N\",\n\t\t\t\tCallerKey:      \"C\",\n\t\t\t\tFunctionKey:    \"F\",\n\t\t\t\tStacktraceKey:  \"S\",\n\t\t\t\tLineEnding:     base.LineEnding,\n\t\t\t\tSkipLineEnding: true,\n\t\t\t\tEncodeTime:     base.EncodeTime,\n\t\t\t\tEncodeDuration: base.EncodeDuration,\n\t\t\t\tEncodeLevel:    base.EncodeLevel,\n\t\t\t\tEncodeCaller:   base.EncodeCaller,\n\t\t\t},\n\t\t\texpectedJSON:    `{\"L\":\"info\",\"T\":0,\"N\":\"main\",\"C\":\"foo.go:42\",\"F\":\"foo.Foo\",\"M\":\"hello\",\"S\":\"fake-stack\"}`,\n\t\t\texpectedConsole: \"0\\tinfo\\tmain\\tfoo.go:42\\tfoo.Foo\\thello\\nfake-stack\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"skip level if LevelKey is omitted\",\n\t\t\tcfg: EncoderConfig{\n\t\t\t\tLevelKey:       OmitKey,\n\t\t\t\tTimeKey:        \"T\",\n\t\t\t\tMessageKey:     \"M\",\n\t\t\t\tNameKey:        \"N\",\n\t\t\t\tCallerKey:      \"C\",\n\t\t\t\tFunctionKey:    \"F\",\n\t\t\t\tStacktraceKey:  \"S\",\n\t\t\t\tLineEnding:     base.LineEnding,\n\t\t\t\tEncodeTime:     base.EncodeTime,\n\t\t\t\tEncodeDuration: base.EncodeDuration,\n\t\t\t\tEncodeLevel:    base.EncodeLevel,\n\t\t\t\tEncodeCaller:   base.EncodeCaller,\n\t\t\t},\n\t\t\texpectedJSON:    `{\"T\":0,\"N\":\"main\",\"C\":\"foo.go:42\",\"F\":\"foo.Foo\",\"M\":\"hello\",\"S\":\"fake-stack\"}` + \"\\n\",\n\t\t\texpectedConsole: \"0\\tmain\\tfoo.go:42\\tfoo.Foo\\thello\\nfake-stack\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"skip timestamp if TimeKey is omitted\",\n\t\t\tcfg: EncoderConfig{\n\t\t\t\tLevelKey:       \"L\",\n\t\t\t\tTimeKey:        OmitKey,\n\t\t\t\tMessageKey:     \"M\",\n\t\t\t\tNameKey:        \"N\",\n\t\t\t\tCallerKey:      \"C\",\n\t\t\t\tFunctionKey:    \"F\",\n\t\t\t\tStacktraceKey:  \"S\",\n\t\t\t\tLineEnding:     base.LineEnding,\n\t\t\t\tEncodeTime:     base.EncodeTime,\n\t\t\t\tEncodeDuration: base.EncodeDuration,\n\t\t\t\tEncodeLevel:    base.EncodeLevel,\n\t\t\t\tEncodeCaller:   base.EncodeCaller,\n\t\t\t},\n\t\t\texpectedJSON:    `{\"L\":\"info\",\"N\":\"main\",\"C\":\"foo.go:42\",\"F\":\"foo.Foo\",\"M\":\"hello\",\"S\":\"fake-stack\"}` + \"\\n\",\n\t\t\texpectedConsole: \"info\\tmain\\tfoo.go:42\\tfoo.Foo\\thello\\nfake-stack\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"skip message if MessageKey is omitted\",\n\t\t\tcfg: EncoderConfig{\n\t\t\t\tLevelKey:       \"L\",\n\t\t\t\tTimeKey:        \"T\",\n\t\t\t\tMessageKey:     OmitKey,\n\t\t\t\tNameKey:        \"N\",\n\t\t\t\tCallerKey:      \"C\",\n\t\t\t\tFunctionKey:    \"F\",\n\t\t\t\tStacktraceKey:  \"S\",\n\t\t\t\tLineEnding:     base.LineEnding,\n\t\t\t\tEncodeTime:     base.EncodeTime,\n\t\t\t\tEncodeDuration: base.EncodeDuration,\n\t\t\t\tEncodeLevel:    base.EncodeLevel,\n\t\t\t\tEncodeCaller:   base.EncodeCaller,\n\t\t\t},\n\t\t\texpectedJSON:    `{\"L\":\"info\",\"T\":0,\"N\":\"main\",\"C\":\"foo.go:42\",\"F\":\"foo.Foo\",\"S\":\"fake-stack\"}` + \"\\n\",\n\t\t\texpectedConsole: \"0\\tinfo\\tmain\\tfoo.go:42\\tfoo.Foo\\nfake-stack\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"skip name if NameKey is omitted\",\n\t\t\tcfg: EncoderConfig{\n\t\t\t\tLevelKey:       \"L\",\n\t\t\t\tTimeKey:        \"T\",\n\t\t\t\tMessageKey:     \"M\",\n\t\t\t\tNameKey:        OmitKey,\n\t\t\t\tCallerKey:      \"C\",\n\t\t\t\tFunctionKey:    \"F\",\n\t\t\t\tStacktraceKey:  \"S\",\n\t\t\t\tLineEnding:     base.LineEnding,\n\t\t\t\tEncodeTime:     base.EncodeTime,\n\t\t\t\tEncodeDuration: base.EncodeDuration,\n\t\t\t\tEncodeLevel:    base.EncodeLevel,\n\t\t\t\tEncodeCaller:   base.EncodeCaller,\n\t\t\t},\n\t\t\texpectedJSON:    `{\"L\":\"info\",\"T\":0,\"C\":\"foo.go:42\",\"F\":\"foo.Foo\",\"M\":\"hello\",\"S\":\"fake-stack\"}` + \"\\n\",\n\t\t\texpectedConsole: \"0\\tinfo\\tfoo.go:42\\tfoo.Foo\\thello\\nfake-stack\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"skip caller if CallerKey is omitted\",\n\t\t\tcfg: EncoderConfig{\n\t\t\t\tLevelKey:       \"L\",\n\t\t\t\tTimeKey:        \"T\",\n\t\t\t\tMessageKey:     \"M\",\n\t\t\t\tNameKey:        \"N\",\n\t\t\t\tCallerKey:      OmitKey,\n\t\t\t\tFunctionKey:    \"F\",\n\t\t\t\tStacktraceKey:  \"S\",\n\t\t\t\tLineEnding:     base.LineEnding,\n\t\t\t\tEncodeTime:     base.EncodeTime,\n\t\t\t\tEncodeDuration: base.EncodeDuration,\n\t\t\t\tEncodeLevel:    base.EncodeLevel,\n\t\t\t\tEncodeCaller:   base.EncodeCaller,\n\t\t\t},\n\t\t\texpectedJSON:    `{\"L\":\"info\",\"T\":0,\"N\":\"main\",\"F\":\"foo.Foo\",\"M\":\"hello\",\"S\":\"fake-stack\"}` + \"\\n\",\n\t\t\texpectedConsole: \"0\\tinfo\\tmain\\tfoo.Foo\\thello\\nfake-stack\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"skip function if FunctionKey is omitted\",\n\t\t\tcfg: EncoderConfig{\n\t\t\t\tLevelKey:       \"L\",\n\t\t\t\tTimeKey:        \"T\",\n\t\t\t\tMessageKey:     \"M\",\n\t\t\t\tNameKey:        \"N\",\n\t\t\t\tCallerKey:      \"C\",\n\t\t\t\tFunctionKey:    OmitKey,\n\t\t\t\tStacktraceKey:  \"S\",\n\t\t\t\tLineEnding:     base.LineEnding,\n\t\t\t\tEncodeTime:     base.EncodeTime,\n\t\t\t\tEncodeDuration: base.EncodeDuration,\n\t\t\t\tEncodeLevel:    base.EncodeLevel,\n\t\t\t\tEncodeCaller:   base.EncodeCaller,\n\t\t\t},\n\t\t\texpectedJSON:    `{\"L\":\"info\",\"T\":0,\"N\":\"main\",\"C\":\"foo.go:42\",\"M\":\"hello\",\"S\":\"fake-stack\"}` + \"\\n\",\n\t\t\texpectedConsole: \"0\\tinfo\\tmain\\tfoo.go:42\\thello\\nfake-stack\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"skip stacktrace if StacktraceKey is omitted\",\n\t\t\tcfg: EncoderConfig{\n\t\t\t\tLevelKey:       \"L\",\n\t\t\t\tTimeKey:        \"T\",\n\t\t\t\tMessageKey:     \"M\",\n\t\t\t\tNameKey:        \"N\",\n\t\t\t\tCallerKey:      \"C\",\n\t\t\t\tFunctionKey:    \"F\",\n\t\t\t\tStacktraceKey:  OmitKey,\n\t\t\t\tLineEnding:     base.LineEnding,\n\t\t\t\tEncodeTime:     base.EncodeTime,\n\t\t\t\tEncodeDuration: base.EncodeDuration,\n\t\t\t\tEncodeLevel:    base.EncodeLevel,\n\t\t\t\tEncodeCaller:   base.EncodeCaller,\n\t\t\t},\n\t\t\texpectedJSON:    `{\"L\":\"info\",\"T\":0,\"N\":\"main\",\"C\":\"foo.go:42\",\"F\":\"foo.Foo\",\"M\":\"hello\"}` + \"\\n\",\n\t\t\texpectedConsole: \"0\\tinfo\\tmain\\tfoo.go:42\\tfoo.Foo\\thello\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"use the supplied EncodeTime, for both the entry and any times added\",\n\t\t\tcfg: EncoderConfig{\n\t\t\t\tLevelKey:       \"L\",\n\t\t\t\tTimeKey:        \"T\",\n\t\t\t\tMessageKey:     \"M\",\n\t\t\t\tNameKey:        \"N\",\n\t\t\t\tCallerKey:      \"C\",\n\t\t\t\tFunctionKey:    \"F\",\n\t\t\t\tStacktraceKey:  \"S\",\n\t\t\t\tLineEnding:     base.LineEnding,\n\t\t\t\tEncodeTime:     func(t time.Time, enc PrimitiveArrayEncoder) { enc.AppendString(t.String()) },\n\t\t\t\tEncodeDuration: base.EncodeDuration,\n\t\t\t\tEncodeLevel:    base.EncodeLevel,\n\t\t\t\tEncodeCaller:   base.EncodeCaller,\n\t\t\t},\n\t\t\textra: func(enc Encoder) {\n\t\t\t\tenc.AddTime(\"extra\", _epoch)\n\t\t\t\terr := enc.AddArray(\"extras\", ArrayMarshalerFunc(func(enc ArrayEncoder) error {\n\t\t\t\t\tenc.AppendTime(_epoch)\n\t\t\t\t\treturn nil\n\t\t\t\t}))\n\t\t\t\tassert.NoError(t, err)\n\t\t\t},\n\t\t\texpectedJSON: `{\"L\":\"info\",\"T\":\"1970-01-01 00:00:00 +0000 UTC\",\"N\":\"main\",\"C\":\"foo.go:42\",\"F\":\"foo.Foo\",\"M\":\"hello\",\"extra\":\"1970-01-01 00:00:00 +0000 UTC\",\"extras\":[\"1970-01-01 00:00:00 +0000 UTC\"],\"S\":\"fake-stack\"}` + \"\\n\",\n\t\t\texpectedConsole: \"1970-01-01 00:00:00 +0000 UTC\\tinfo\\tmain\\tfoo.go:42\\tfoo.Foo\\thello\\t\" + // plain-text preamble\n\t\t\t\t`{\"extra\": \"1970-01-01 00:00:00 +0000 UTC\", \"extras\": [\"1970-01-01 00:00:00 +0000 UTC\"]}` + // JSON context\n\t\t\t\t\"\\nfake-stack\\n\", // stacktrace after newline\n\t\t},\n\t\t{\n\t\t\tdesc: \"use the supplied EncodeDuration for any durations added\",\n\t\t\tcfg: EncoderConfig{\n\t\t\t\tLevelKey:       \"L\",\n\t\t\t\tTimeKey:        \"T\",\n\t\t\t\tMessageKey:     \"M\",\n\t\t\t\tNameKey:        \"N\",\n\t\t\t\tCallerKey:      \"C\",\n\t\t\t\tFunctionKey:    \"F\",\n\t\t\t\tStacktraceKey:  \"S\",\n\t\t\t\tLineEnding:     base.LineEnding,\n\t\t\t\tEncodeTime:     base.EncodeTime,\n\t\t\t\tEncodeDuration: StringDurationEncoder,\n\t\t\t\tEncodeLevel:    base.EncodeLevel,\n\t\t\t\tEncodeCaller:   base.EncodeCaller,\n\t\t\t},\n\t\t\textra: func(enc Encoder) {\n\t\t\t\tenc.AddDuration(\"extra\", time.Second)\n\t\t\t\terr := enc.AddArray(\"extras\", ArrayMarshalerFunc(func(enc ArrayEncoder) error {\n\t\t\t\t\tenc.AppendDuration(time.Minute)\n\t\t\t\t\treturn nil\n\t\t\t\t}))\n\t\t\t\tassert.NoError(t, err)\n\t\t\t},\n\t\t\texpectedJSON: `{\"L\":\"info\",\"T\":0,\"N\":\"main\",\"C\":\"foo.go:42\",\"F\":\"foo.Foo\",\"M\":\"hello\",\"extra\":\"1s\",\"extras\":[\"1m0s\"],\"S\":\"fake-stack\"}` + \"\\n\",\n\t\t\texpectedConsole: \"0\\tinfo\\tmain\\tfoo.go:42\\tfoo.Foo\\thello\\t\" + // preamble\n\t\t\t\t`{\"extra\": \"1s\", \"extras\": [\"1m0s\"]}` + // context\n\t\t\t\t\"\\nfake-stack\\n\", // stacktrace\n\t\t},\n\t\t{\n\t\t\tdesc: \"use the supplied EncodeLevel\",\n\t\t\tcfg: EncoderConfig{\n\t\t\t\tLevelKey:       \"L\",\n\t\t\t\tTimeKey:        \"T\",\n\t\t\t\tMessageKey:     \"M\",\n\t\t\t\tNameKey:        \"N\",\n\t\t\t\tCallerKey:      \"C\",\n\t\t\t\tFunctionKey:    \"F\",\n\t\t\t\tStacktraceKey:  \"S\",\n\t\t\t\tLineEnding:     base.LineEnding,\n\t\t\t\tEncodeTime:     base.EncodeTime,\n\t\t\t\tEncodeDuration: base.EncodeDuration,\n\t\t\t\tEncodeLevel:    CapitalLevelEncoder,\n\t\t\t\tEncodeCaller:   base.EncodeCaller,\n\t\t\t},\n\t\t\texpectedJSON:    `{\"L\":\"INFO\",\"T\":0,\"N\":\"main\",\"C\":\"foo.go:42\",\"F\":\"foo.Foo\",\"M\":\"hello\",\"S\":\"fake-stack\"}` + \"\\n\",\n\t\t\texpectedConsole: \"0\\tINFO\\tmain\\tfoo.go:42\\tfoo.Foo\\thello\\nfake-stack\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"use the supplied EncodeName\",\n\t\t\tcfg: EncoderConfig{\n\t\t\t\tLevelKey:       \"L\",\n\t\t\t\tTimeKey:        \"T\",\n\t\t\t\tMessageKey:     \"M\",\n\t\t\t\tNameKey:        \"N\",\n\t\t\t\tCallerKey:      \"C\",\n\t\t\t\tFunctionKey:    \"F\",\n\t\t\t\tStacktraceKey:  \"S\",\n\t\t\t\tLineEnding:     base.LineEnding,\n\t\t\t\tEncodeTime:     base.EncodeTime,\n\t\t\t\tEncodeDuration: base.EncodeDuration,\n\t\t\t\tEncodeLevel:    base.EncodeLevel,\n\t\t\t\tEncodeCaller:   base.EncodeCaller,\n\t\t\t\tEncodeName:     capitalNameEncoder,\n\t\t\t},\n\t\t\texpectedJSON:    `{\"L\":\"info\",\"T\":0,\"N\":\"MAIN\",\"C\":\"foo.go:42\",\"F\":\"foo.Foo\",\"M\":\"hello\",\"S\":\"fake-stack\"}` + \"\\n\",\n\t\t\texpectedConsole: \"0\\tinfo\\tMAIN\\tfoo.go:42\\tfoo.Foo\\thello\\nfake-stack\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"close all open namespaces\",\n\t\t\tcfg: EncoderConfig{\n\t\t\t\tLevelKey:       \"L\",\n\t\t\t\tTimeKey:        \"T\",\n\t\t\t\tMessageKey:     \"M\",\n\t\t\t\tNameKey:        \"N\",\n\t\t\t\tCallerKey:      \"C\",\n\t\t\t\tFunctionKey:    \"F\",\n\t\t\t\tStacktraceKey:  \"S\",\n\t\t\t\tLineEnding:     base.LineEnding,\n\t\t\t\tEncodeTime:     base.EncodeTime,\n\t\t\t\tEncodeDuration: base.EncodeDuration,\n\t\t\t\tEncodeLevel:    base.EncodeLevel,\n\t\t\t\tEncodeCaller:   base.EncodeCaller,\n\t\t\t},\n\t\t\textra: func(enc Encoder) {\n\t\t\t\tenc.OpenNamespace(\"outer\")\n\t\t\t\tenc.OpenNamespace(\"inner\")\n\t\t\t\tenc.AddString(\"foo\", \"bar\")\n\t\t\t\tenc.OpenNamespace(\"innermost\")\n\t\t\t},\n\t\t\texpectedJSON: `{\"L\":\"info\",\"T\":0,\"N\":\"main\",\"C\":\"foo.go:42\",\"F\":\"foo.Foo\",\"M\":\"hello\",\"outer\":{\"inner\":{\"foo\":\"bar\",\"innermost\":{}}},\"S\":\"fake-stack\"}` + \"\\n\",\n\t\t\texpectedConsole: \"0\\tinfo\\tmain\\tfoo.go:42\\tfoo.Foo\\thello\\t\" +\n\t\t\t\t`{\"outer\": {\"inner\": {\"foo\": \"bar\", \"innermost\": {}}}}` +\n\t\t\t\t\"\\nfake-stack\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"handle no-op EncodeTime\",\n\t\t\tcfg: EncoderConfig{\n\t\t\t\tLevelKey:       \"L\",\n\t\t\t\tTimeKey:        \"T\",\n\t\t\t\tMessageKey:     \"M\",\n\t\t\t\tNameKey:        \"N\",\n\t\t\t\tCallerKey:      \"C\",\n\t\t\t\tFunctionKey:    \"F\",\n\t\t\t\tStacktraceKey:  \"S\",\n\t\t\t\tLineEnding:     base.LineEnding,\n\t\t\t\tEncodeTime:     func(time.Time, PrimitiveArrayEncoder) {},\n\t\t\t\tEncodeDuration: base.EncodeDuration,\n\t\t\t\tEncodeLevel:    base.EncodeLevel,\n\t\t\t\tEncodeCaller:   base.EncodeCaller,\n\t\t\t},\n\t\t\textra:           func(enc Encoder) { enc.AddTime(\"sometime\", time.Unix(0, 100)) },\n\t\t\texpectedJSON:    `{\"L\":\"info\",\"T\":0,\"N\":\"main\",\"C\":\"foo.go:42\",\"F\":\"foo.Foo\",\"M\":\"hello\",\"sometime\":100,\"S\":\"fake-stack\"}` + \"\\n\",\n\t\t\texpectedConsole: \"info\\tmain\\tfoo.go:42\\tfoo.Foo\\thello\\t\" + `{\"sometime\": 100}` + \"\\nfake-stack\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"handle no-op EncodeDuration\",\n\t\t\tcfg: EncoderConfig{\n\t\t\t\tLevelKey:       \"L\",\n\t\t\t\tTimeKey:        \"T\",\n\t\t\t\tMessageKey:     \"M\",\n\t\t\t\tNameKey:        \"N\",\n\t\t\t\tCallerKey:      \"C\",\n\t\t\t\tFunctionKey:    \"F\",\n\t\t\t\tStacktraceKey:  \"S\",\n\t\t\t\tLineEnding:     base.LineEnding,\n\t\t\t\tEncodeTime:     base.EncodeTime,\n\t\t\t\tEncodeDuration: func(time.Duration, PrimitiveArrayEncoder) {},\n\t\t\t\tEncodeLevel:    base.EncodeLevel,\n\t\t\t\tEncodeCaller:   base.EncodeCaller,\n\t\t\t},\n\t\t\textra:           func(enc Encoder) { enc.AddDuration(\"someduration\", time.Microsecond) },\n\t\t\texpectedJSON:    `{\"L\":\"info\",\"T\":0,\"N\":\"main\",\"C\":\"foo.go:42\",\"F\":\"foo.Foo\",\"M\":\"hello\",\"someduration\":1000,\"S\":\"fake-stack\"}` + \"\\n\",\n\t\t\texpectedConsole: \"0\\tinfo\\tmain\\tfoo.go:42\\tfoo.Foo\\thello\\t\" + `{\"someduration\": 1000}` + \"\\nfake-stack\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"handle no-op EncodeLevel\",\n\t\t\tcfg: EncoderConfig{\n\t\t\t\tLevelKey:       \"L\",\n\t\t\t\tTimeKey:        \"T\",\n\t\t\t\tMessageKey:     \"M\",\n\t\t\t\tNameKey:        \"N\",\n\t\t\t\tCallerKey:      \"C\",\n\t\t\t\tFunctionKey:    \"F\",\n\t\t\t\tStacktraceKey:  \"S\",\n\t\t\t\tLineEnding:     base.LineEnding,\n\t\t\t\tEncodeTime:     base.EncodeTime,\n\t\t\t\tEncodeDuration: base.EncodeDuration,\n\t\t\t\tEncodeLevel:    func(Level, PrimitiveArrayEncoder) {},\n\t\t\t\tEncodeCaller:   base.EncodeCaller,\n\t\t\t},\n\t\t\texpectedJSON:    `{\"L\":\"info\",\"T\":0,\"N\":\"main\",\"C\":\"foo.go:42\",\"F\":\"foo.Foo\",\"M\":\"hello\",\"S\":\"fake-stack\"}` + \"\\n\",\n\t\t\texpectedConsole: \"0\\tmain\\tfoo.go:42\\tfoo.Foo\\thello\\nfake-stack\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"handle no-op EncodeCaller\",\n\t\t\tcfg: EncoderConfig{\n\t\t\t\tLevelKey:       \"L\",\n\t\t\t\tTimeKey:        \"T\",\n\t\t\t\tMessageKey:     \"M\",\n\t\t\t\tNameKey:        \"N\",\n\t\t\t\tCallerKey:      \"C\",\n\t\t\t\tFunctionKey:    \"F\",\n\t\t\t\tStacktraceKey:  \"S\",\n\t\t\t\tLineEnding:     base.LineEnding,\n\t\t\t\tEncodeTime:     base.EncodeTime,\n\t\t\t\tEncodeDuration: base.EncodeDuration,\n\t\t\t\tEncodeLevel:    base.EncodeLevel,\n\t\t\t\tEncodeCaller:   func(EntryCaller, PrimitiveArrayEncoder) {},\n\t\t\t},\n\t\t\texpectedJSON:    `{\"L\":\"info\",\"T\":0,\"N\":\"main\",\"C\":\"foo.go:42\",\"F\":\"foo.Foo\",\"M\":\"hello\",\"S\":\"fake-stack\"}` + \"\\n\",\n\t\t\texpectedConsole: \"0\\tinfo\\tmain\\tfoo.Foo\\thello\\nfake-stack\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"handle no-op EncodeName\",\n\t\t\tcfg: EncoderConfig{\n\t\t\t\tLevelKey:       \"L\",\n\t\t\t\tTimeKey:        \"T\",\n\t\t\t\tMessageKey:     \"M\",\n\t\t\t\tNameKey:        \"N\",\n\t\t\t\tCallerKey:      \"C\",\n\t\t\t\tFunctionKey:    \"F\",\n\t\t\t\tStacktraceKey:  \"S\",\n\t\t\t\tLineEnding:     base.LineEnding,\n\t\t\t\tEncodeTime:     base.EncodeTime,\n\t\t\t\tEncodeDuration: base.EncodeDuration,\n\t\t\t\tEncodeLevel:    base.EncodeLevel,\n\t\t\t\tEncodeCaller:   base.EncodeCaller,\n\t\t\t\tEncodeName:     func(string, PrimitiveArrayEncoder) {},\n\t\t\t},\n\t\t\texpectedJSON:    `{\"L\":\"info\",\"T\":0,\"N\":\"main\",\"C\":\"foo.go:42\",\"F\":\"foo.Foo\",\"M\":\"hello\",\"S\":\"fake-stack\"}` + \"\\n\",\n\t\t\texpectedConsole: \"0\\tinfo\\tfoo.go:42\\tfoo.Foo\\thello\\nfake-stack\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"use custom line separator\",\n\t\t\tcfg: EncoderConfig{\n\t\t\t\tLevelKey:       \"L\",\n\t\t\t\tTimeKey:        \"T\",\n\t\t\t\tMessageKey:     \"M\",\n\t\t\t\tNameKey:        \"N\",\n\t\t\t\tCallerKey:      \"C\",\n\t\t\t\tFunctionKey:    \"F\",\n\t\t\t\tStacktraceKey:  \"S\",\n\t\t\t\tLineEnding:     \"\\r\\n\",\n\t\t\t\tEncodeTime:     base.EncodeTime,\n\t\t\t\tEncodeDuration: base.EncodeDuration,\n\t\t\t\tEncodeLevel:    base.EncodeLevel,\n\t\t\t\tEncodeCaller:   base.EncodeCaller,\n\t\t\t},\n\t\t\texpectedJSON:    `{\"L\":\"info\",\"T\":0,\"N\":\"main\",\"C\":\"foo.go:42\",\"F\":\"foo.Foo\",\"M\":\"hello\",\"S\":\"fake-stack\"}` + \"\\r\\n\",\n\t\t\texpectedConsole: \"0\\tinfo\\tmain\\tfoo.go:42\\tfoo.Foo\\thello\\nfake-stack\\r\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"omit line separator definition - fall back to default\",\n\t\t\tcfg: EncoderConfig{\n\t\t\t\tLevelKey:       \"L\",\n\t\t\t\tTimeKey:        \"T\",\n\t\t\t\tMessageKey:     \"M\",\n\t\t\t\tNameKey:        \"N\",\n\t\t\t\tCallerKey:      \"C\",\n\t\t\t\tFunctionKey:    \"F\",\n\t\t\t\tStacktraceKey:  \"S\",\n\t\t\t\tEncodeTime:     base.EncodeTime,\n\t\t\t\tEncodeDuration: base.EncodeDuration,\n\t\t\t\tEncodeLevel:    base.EncodeLevel,\n\t\t\t\tEncodeCaller:   base.EncodeCaller,\n\t\t\t},\n\t\t\texpectedJSON:    `{\"L\":\"info\",\"T\":0,\"N\":\"main\",\"C\":\"foo.go:42\",\"F\":\"foo.Foo\",\"M\":\"hello\",\"S\":\"fake-stack\"}` + DefaultLineEnding,\n\t\t\texpectedConsole: \"0\\tinfo\\tmain\\tfoo.go:42\\tfoo.Foo\\thello\\nfake-stack\" + DefaultLineEnding,\n\t\t},\n\t}\n\n\tfor i, tt := range tests {\n\t\tjson := NewJSONEncoder(tt.cfg)\n\t\tconsole := NewConsoleEncoder(tt.cfg)\n\t\tif tt.extra != nil {\n\t\t\ttt.extra(json)\n\t\t\ttt.extra(console)\n\t\t}\n\t\tentry := _testEntry\n\t\tif tt.amendEntry != nil {\n\t\t\tentry = tt.amendEntry(_testEntry)\n\t\t}\n\t\tjsonOut, jsonErr := json.EncodeEntry(entry, nil)\n\t\tif assert.NoError(t, jsonErr, \"Unexpected error JSON-encoding entry in case #%d.\", i) {\n\t\t\tassert.Equal(\n\t\t\t\tt,\n\t\t\t\ttt.expectedJSON,\n\t\t\t\tjsonOut.String(),\n\t\t\t\t\"Unexpected JSON output: expected to %v.\", tt.desc,\n\t\t\t)\n\t\t}\n\t\tconsoleOut, consoleErr := console.EncodeEntry(entry, nil)\n\t\tif assert.NoError(t, consoleErr, \"Unexpected error console-encoding entry in case #%d.\", i) {\n\t\t\tassert.Equal(\n\t\t\t\tt,\n\t\t\t\ttt.expectedConsole,\n\t\t\t\tconsoleOut.String(),\n\t\t\t\t\"Unexpected console output: expected to %v.\", tt.desc,\n\t\t\t)\n\t\t}\n\t}\n}\n\nfunc TestLevelEncoders(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\texpected interface{} // output of encoding InfoLevel\n\t}{\n\t\t{\"capital\", \"INFO\"},\n\t\t{\"lower\", \"info\"},\n\t\t{\"\", \"info\"},\n\t\t{\"something-random\", \"info\"},\n\t}\n\n\tfor _, tt := range tests {\n\t\tvar le LevelEncoder\n\t\trequire.NoError(t, le.UnmarshalText([]byte(tt.name)), \"Unexpected error unmarshaling %q.\", tt.name)\n\t\tassertAppended(\n\t\t\tt,\n\t\t\ttt.expected,\n\t\t\tfunc(arr ArrayEncoder) { le(InfoLevel, arr) },\n\t\t\t\"Unexpected output serializing InfoLevel with %q.\", tt.name,\n\t\t)\n\t}\n}\n\nfunc TestTimeEncoders(t *testing.T) {\n\tmoment := time.Unix(100, 50005000).UTC()\n\ttests := []struct {\n\t\tyamlDoc  string\n\t\texpected interface{} // output of serializing moment\n\t}{\n\t\t{\"timeEncoder: iso8601\", \"1970-01-01T00:01:40.050Z\"},\n\t\t{\"timeEncoder: ISO8601\", \"1970-01-01T00:01:40.050Z\"},\n\t\t{\"timeEncoder: millis\", 100050.005},\n\t\t{\"timeEncoder: nanos\", int64(100050005000)},\n\t\t{\"timeEncoder: {layout: 06/01/02 03:04pm}\", \"70/01/01 12:01am\"},\n\t\t{\"timeEncoder: ''\", 100.050005},\n\t\t{\"timeEncoder: something-random\", 100.050005},\n\t\t{\"timeEncoder: rfc3339\", \"1970-01-01T00:01:40Z\"},\n\t\t{\"timeEncoder: RFC3339\", \"1970-01-01T00:01:40Z\"},\n\t\t{\"timeEncoder: rfc3339nano\", \"1970-01-01T00:01:40.050005Z\"},\n\t\t{\"timeEncoder: RFC3339Nano\", \"1970-01-01T00:01:40.050005Z\"},\n\t}\n\n\tfor _, tt := range tests {\n\t\tcfg := EncoderConfig{}\n\t\trequire.NoError(t, yaml.Unmarshal([]byte(tt.yamlDoc), &cfg), \"Unexpected error unmarshaling %q.\", tt.yamlDoc)\n\t\trequire.NotNil(t, cfg.EncodeTime, \"Unmashalled timeEncoder is nil for %q.\", tt.yamlDoc)\n\t\tassertAppended(\n\t\t\tt,\n\t\t\ttt.expected,\n\t\t\tfunc(arr ArrayEncoder) { cfg.EncodeTime(moment, arr) },\n\t\t\t\"Unexpected output serializing %v with %q.\", moment, tt.yamlDoc,\n\t\t)\n\t}\n}\n\nfunc TestTimeEncodersWrongYAML(t *testing.T) {\n\ttests := []string{\n\t\t\"timeEncoder: [1, 2, 3]\", // wrong type\n\t\t\"timeEncoder: {foo:bar\",  // broken yaml\n\t}\n\tfor _, tt := range tests {\n\t\tcfg := EncoderConfig{}\n\t\tassert.Error(t, yaml.Unmarshal([]byte(tt), &cfg), \"Expected unmarshaling %q to become error, but not.\", tt)\n\t}\n}\n\nfunc TestTimeEncodersParseFromJSON(t *testing.T) {\n\tmoment := time.Unix(100, 50005000).UTC()\n\ttests := []struct {\n\t\tjsonDoc  string\n\t\texpected interface{} // output of serializing moment\n\t}{\n\t\t{`{\"timeEncoder\": \"iso8601\"}`, \"1970-01-01T00:01:40.050Z\"},\n\t\t{`{\"timeEncoder\": {\"layout\": \"06/01/02 03:04pm\"}}`, \"70/01/01 12:01am\"},\n\t}\n\n\tfor _, tt := range tests {\n\t\tcfg := EncoderConfig{}\n\t\trequire.NoError(t, json.Unmarshal([]byte(tt.jsonDoc), &cfg), \"Unexpected error unmarshaling %q.\", tt.jsonDoc)\n\t\trequire.NotNil(t, cfg.EncodeTime, \"Unmashalled timeEncoder is nil for %q.\", tt.jsonDoc)\n\t\tassertAppended(\n\t\t\tt,\n\t\t\ttt.expected,\n\t\t\tfunc(arr ArrayEncoder) { cfg.EncodeTime(moment, arr) },\n\t\t\t\"Unexpected output serializing %v with %q.\", moment, tt.jsonDoc,\n\t\t)\n\t}\n}\n\nfunc TestDurationEncoders(t *testing.T) {\n\telapsed := time.Second + 500*time.Nanosecond\n\ttests := []struct {\n\t\tname     string\n\t\texpected interface{} // output of serializing elapsed\n\t}{\n\t\t{\"string\", \"1.0000005s\"},\n\t\t{\"nanos\", int64(1000000500)},\n\t\t{\"ms\", int64(1000)},\n\t\t{\"\", 1.0000005},\n\t\t{\"something-random\", 1.0000005},\n\t}\n\n\tfor _, tt := range tests {\n\t\tvar de DurationEncoder\n\t\trequire.NoError(t, de.UnmarshalText([]byte(tt.name)), \"Unexpected error unmarshaling %q.\", tt.name)\n\t\tassertAppended(\n\t\t\tt,\n\t\t\ttt.expected,\n\t\t\tfunc(arr ArrayEncoder) { de(elapsed, arr) },\n\t\t\t\"Unexpected output serializing %v with %q.\", elapsed, tt.name,\n\t\t)\n\t}\n}\n\nfunc TestCallerEncoders(t *testing.T) {\n\tcaller := EntryCaller{Defined: true, File: \"/home/jack/src/github.com/foo/foo.go\", Line: 42}\n\ttests := []struct {\n\t\tname     string\n\t\texpected interface{} // output of serializing caller\n\t}{\n\t\t{\"\", \"foo/foo.go:42\"},\n\t\t{\"something-random\", \"foo/foo.go:42\"},\n\t\t{\"short\", \"foo/foo.go:42\"},\n\t\t{\"full\", \"/home/jack/src/github.com/foo/foo.go:42\"},\n\t}\n\n\tfor _, tt := range tests {\n\t\tvar ce CallerEncoder\n\t\trequire.NoError(t, ce.UnmarshalText([]byte(tt.name)), \"Unexpected error unmarshaling %q.\", tt.name)\n\t\tassertAppended(\n\t\t\tt,\n\t\t\ttt.expected,\n\t\t\tfunc(arr ArrayEncoder) { ce(caller, arr) },\n\t\t\t\"Unexpected output serializing file name as %v with %q.\", tt.expected, tt.name,\n\t\t)\n\t}\n}\n\nfunc TestNameEncoders(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\texpected interface{} // output of encoding InfoLevel\n\t}{\n\t\t{\"\", \"main\"},\n\t\t{\"full\", \"main\"},\n\t\t{\"something-random\", \"main\"},\n\t}\n\n\tfor _, tt := range tests {\n\t\tvar ne NameEncoder\n\t\trequire.NoError(t, ne.UnmarshalText([]byte(tt.name)), \"Unexpected error unmarshaling %q.\", tt.name)\n\t\tassertAppended(\n\t\t\tt,\n\t\t\ttt.expected,\n\t\t\tfunc(arr ArrayEncoder) { ne(\"main\", arr) },\n\t\t\t\"Unexpected output serializing logger name with %q.\", tt.name,\n\t\t)\n\t}\n}\n\nfunc assertAppended(t testing.TB, expected interface{}, f func(ArrayEncoder), msgAndArgs ...interface{}) {\n\tmem := NewMapObjectEncoder()\n\terr := mem.AddArray(\"k\", ArrayMarshalerFunc(func(arr ArrayEncoder) error {\n\t\tf(arr)\n\t\treturn nil\n\t}))\n\tassert.NoError(t, err, msgAndArgs...)\n\tarr := mem.Fields[\"k\"].([]interface{})\n\trequire.Equal(t, 1, len(arr), \"Expected to append exactly one element to array.\")\n\tassert.Equal(t, expected, arr[0], msgAndArgs...)\n}\n"
  },
  {
    "path": "zapcore/entry.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n\t\"strings\"\n\t\"time\"\n\n\t\"go.uber.org/multierr\"\n\t\"go.uber.org/zap/internal/bufferpool\"\n\t\"go.uber.org/zap/internal/exit\"\n\t\"go.uber.org/zap/internal/pool\"\n)\n\nvar _cePool = pool.New(func() *CheckedEntry {\n\t// Pre-allocate some space for cores.\n\treturn &CheckedEntry{\n\t\tcores: make([]Core, 4),\n\t}\n})\n\nfunc getCheckedEntry() *CheckedEntry {\n\tce := _cePool.Get()\n\tce.reset()\n\treturn ce\n}\n\nfunc putCheckedEntry(ce *CheckedEntry) {\n\tif ce == nil {\n\t\treturn\n\t}\n\t_cePool.Put(ce)\n}\n\n// NewEntryCaller makes an EntryCaller from the return signature of\n// runtime.Caller.\nfunc NewEntryCaller(pc uintptr, file string, line int, ok bool) EntryCaller {\n\tif !ok {\n\t\treturn EntryCaller{}\n\t}\n\treturn EntryCaller{\n\t\tPC:      pc,\n\t\tFile:    file,\n\t\tLine:    line,\n\t\tDefined: true,\n\t}\n}\n\n// EntryCaller represents the caller of a logging function.\ntype EntryCaller struct {\n\tDefined  bool\n\tPC       uintptr\n\tFile     string\n\tLine     int\n\tFunction string\n}\n\n// String returns the full path and line number of the caller.\nfunc (ec EntryCaller) String() string {\n\treturn ec.FullPath()\n}\n\n// FullPath returns a /full/path/to/package/file:line description of the\n// caller.\nfunc (ec EntryCaller) FullPath() string {\n\tif !ec.Defined {\n\t\treturn \"undefined\"\n\t}\n\tbuf := bufferpool.Get()\n\tbuf.AppendString(ec.File)\n\tbuf.AppendByte(':')\n\tbuf.AppendInt(int64(ec.Line))\n\tcaller := buf.String()\n\tbuf.Free()\n\treturn caller\n}\n\n// TrimmedPath returns a package/file:line description of the caller,\n// preserving only the leaf directory name and file name.\nfunc (ec EntryCaller) TrimmedPath() string {\n\tif !ec.Defined {\n\t\treturn \"undefined\"\n\t}\n\t// nb. To make sure we trim the path correctly on Windows too, we\n\t// counter-intuitively need to use '/' and *not* os.PathSeparator here,\n\t// because the path given originates from Go stdlib, specifically\n\t// runtime.Caller() which (as of Mar/17) returns forward slashes even on\n\t// Windows.\n\t//\n\t// See https://github.com/golang/go/issues/3335\n\t// and https://github.com/golang/go/issues/18151\n\t//\n\t// for discussion on the issue on Go side.\n\t//\n\t// Find the last separator.\n\t//\n\tidx := strings.LastIndexByte(ec.File, '/')\n\tif idx == -1 {\n\t\treturn ec.FullPath()\n\t}\n\t// Find the penultimate separator.\n\tidx = strings.LastIndexByte(ec.File[:idx], '/')\n\tif idx == -1 {\n\t\treturn ec.FullPath()\n\t}\n\tbuf := bufferpool.Get()\n\t// Keep everything after the penultimate separator.\n\tbuf.AppendString(ec.File[idx+1:])\n\tbuf.AppendByte(':')\n\tbuf.AppendInt(int64(ec.Line))\n\tcaller := buf.String()\n\tbuf.Free()\n\treturn caller\n}\n\n// An Entry represents a complete log message. The entry's structured context\n// is already serialized, but the log level, time, message, and call site\n// information are available for inspection and modification. Any fields left\n// empty will be omitted when encoding.\n//\n// Entries are pooled, so any functions that accept them MUST be careful not to\n// retain references to them.\ntype Entry struct {\n\tLevel      Level\n\tTime       time.Time\n\tLoggerName string\n\tMessage    string\n\tCaller     EntryCaller\n\tStack      string\n}\n\n// CheckWriteHook is a custom action that may be executed after an entry is\n// written.\n//\n// Register one on a CheckedEntry with the After method.\n//\n//\tif ce := logger.Check(...); ce != nil {\n//\t  ce = ce.After(hook)\n//\t  ce.Write(...)\n//\t}\n//\n// You can configure the hook for Fatal log statements at the logger level with\n// the zap.WithFatalHook option.\ntype CheckWriteHook interface {\n\t// OnWrite is invoked with the CheckedEntry that was written and a list\n\t// of fields added with that entry.\n\t//\n\t// The list of fields DOES NOT include fields that were already added\n\t// to the logger with the With method.\n\tOnWrite(*CheckedEntry, []Field)\n}\n\n// CheckWriteAction indicates what action to take after a log entry is\n// processed. Actions are ordered in increasing severity.\ntype CheckWriteAction uint8\n\nconst (\n\t// WriteThenNoop indicates that nothing special needs to be done. It's the\n\t// default behavior.\n\tWriteThenNoop CheckWriteAction = iota\n\t// WriteThenGoexit runs runtime.Goexit after Write.\n\tWriteThenGoexit\n\t// WriteThenPanic causes a panic after Write.\n\tWriteThenPanic\n\t// WriteThenFatal causes an os.Exit(1) after Write.\n\tWriteThenFatal\n)\n\n// OnWrite implements the OnWrite method to keep CheckWriteAction compatible\n// with the new CheckWriteHook interface which deprecates CheckWriteAction.\nfunc (a CheckWriteAction) OnWrite(ce *CheckedEntry, _ []Field) {\n\tswitch a {\n\tcase WriteThenGoexit:\n\t\truntime.Goexit()\n\tcase WriteThenPanic:\n\t\tpanic(ce.Message)\n\tcase WriteThenFatal:\n\t\texit.With(1)\n\t}\n}\n\nvar _ CheckWriteHook = CheckWriteAction(0)\n\n// CheckPreWriteHook is a function that transforms an Entry and its Fields\n// before they are written to cores. Register one on a CheckedEntry with the\n// Before method.\n//\n// Pre-write hooks run in the order they were added, before any Core's Write\n// method is called. They may modify the Entry and Fields freely.\ntype CheckPreWriteHook func(Entry, []Field) (Entry, []Field)\n\n// CheckedEntry is an Entry together with a collection of Cores that have\n// already agreed to log it.\n//\n// CheckedEntry references should be created by calling AddCore or After on a\n// nil *CheckedEntry. References are returned to a pool after Write, and MUST\n// NOT be retained after calling their Write method.\ntype CheckedEntry struct {\n\tEntry\n\tErrorOutput WriteSyncer\n\tdirty       bool // best-effort detection of pool misuse\n\tafter       CheckWriteHook\n\tcores       []Core\n\tbefore      []CheckPreWriteHook\n}\n\nfunc (ce *CheckedEntry) reset() {\n\tce.Entry = Entry{}\n\tce.ErrorOutput = nil\n\tce.dirty = false\n\tce.after = nil\n\tfor i := range ce.cores {\n\t\t// don't keep references to cores\n\t\tce.cores[i] = nil\n\t}\n\tce.cores = ce.cores[:0]\n\tfor i := range ce.before {\n\t\tce.before[i] = nil\n\t}\n\tce.before = ce.before[:0]\n}\n\n// Write writes the entry to the stored Cores, returns any errors, and returns\n// the CheckedEntry reference to a pool for immediate re-use. Finally, it\n// executes any required CheckWriteAction.\nfunc (ce *CheckedEntry) Write(fields ...Field) {\n\tif ce == nil {\n\t\treturn\n\t}\n\n\tif ce.dirty {\n\t\tif ce.ErrorOutput != nil {\n\t\t\t// Make a best effort to detect unsafe re-use of this CheckedEntry.\n\t\t\t// If the entry is dirty, log an internal error; because the\n\t\t\t// CheckedEntry is being used after it was returned to the pool,\n\t\t\t// the message may be an amalgamation from multiple call sites.\n\t\t\t_, _ = fmt.Fprintf(\n\t\t\t\tce.ErrorOutput,\n\t\t\t\t\"%v Unsafe CheckedEntry re-use near Entry %+v.\\n\",\n\t\t\t\tce.Time,\n\t\t\t\tce.Entry,\n\t\t\t)\n\t\t\t_ = ce.ErrorOutput.Sync() // ignore error\n\t\t}\n\t\treturn\n\t}\n\tce.dirty = true\n\n\tent := ce.Entry\n\tfor i := range ce.before {\n\t\tent, fields = ce.before[i](ent, fields)\n\t}\n\n\tvar err error\n\tfor i := range ce.cores {\n\t\terr = multierr.Append(err, ce.cores[i].Write(ent, fields))\n\t}\n\tif err != nil && ce.ErrorOutput != nil {\n\t\t_, _ = fmt.Fprintf(\n\t\t\tce.ErrorOutput,\n\t\t\t\"%v write error: %v\\n\",\n\t\t\tce.Time,\n\t\t\terr,\n\t\t)\n\t\t_ = ce.ErrorOutput.Sync() // ignore error\n\t}\n\n\thook := ce.after\n\tif hook != nil {\n\t\thook.OnWrite(ce, fields)\n\t}\n\tputCheckedEntry(ce)\n}\n\n// AddCore adds a Core that has agreed to log this CheckedEntry. It's intended to be\n// used by Core.Check implementations, and is safe to call on nil CheckedEntry\n// references.\nfunc (ce *CheckedEntry) AddCore(ent Entry, core Core) *CheckedEntry {\n\tif ce == nil {\n\t\tce = getCheckedEntry()\n\t\tce.Entry = ent\n\t}\n\tce.cores = append(ce.cores, core)\n\treturn ce\n}\n\n// Should sets this CheckedEntry's CheckWriteAction, which controls whether a\n// Core will panic or fatal after writing this log entry. Like AddCore, it's\n// safe to call on nil CheckedEntry references.\n//\n// Deprecated: Use [CheckedEntry.After] instead.\nfunc (ce *CheckedEntry) Should(ent Entry, should CheckWriteAction) *CheckedEntry {\n\treturn ce.After(ent, should)\n}\n\n// Before adds a pre-write hook that transforms the Entry and Fields before\n// they are written to any registered Cores. Multiple hooks run in the order\n// they were added. It's safe to call this on nil CheckedEntry references.\nfunc (ce *CheckedEntry) Before(ent Entry, hook CheckPreWriteHook) *CheckedEntry {\n\tif ce == nil {\n\t\tce = getCheckedEntry()\n\t\tce.Entry = ent\n\t}\n\tce.before = append(ce.before, hook)\n\treturn ce\n}\n\n// After sets this CheckEntry's CheckWriteHook, which will be called after this\n// log entry has been written. It's safe to call this on nil CheckedEntry\n// references.\nfunc (ce *CheckedEntry) After(ent Entry, hook CheckWriteHook) *CheckedEntry {\n\tif ce == nil {\n\t\tce = getCheckedEntry()\n\t\tce.Entry = ent\n\t}\n\tce.after = hook\n\treturn ce\n}\n"
  },
  {
    "path": "zapcore/entry_ext_test.go",
    "content": "// Copyright (c) 2023 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore_test\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n\t\"go.uber.org/zap/zaptest\"\n)\n\nfunc TestCheckedEntryIllegalReuse(t *testing.T) {\n\tt.Parallel()\n\n\tvar errOut bytes.Buffer\n\n\ttestCore := zaptest.NewLogger(t).Core()\n\tce := testCore.Check(zapcore.Entry{\n\t\tLevel:   zapcore.InfoLevel,\n\t\tTime:    time.Now(),\n\t\tMessage: \"hello\",\n\t}, nil)\n\tce.ErrorOutput = zapcore.AddSync(&errOut)\n\n\t// The first write should succeed.\n\tce.Write(zap.String(\"k\", \"v\"), zap.Int(\"n\", 42))\n\tassert.Empty(t, errOut.String(), \"Expected no errors on first write.\")\n\n\t// The second write should fail.\n\tce.Write(zap.String(\"foo\", \"bar\"), zap.Int(\"x\", 1))\n\tassert.Contains(t, errOut.String(), \"Unsafe CheckedEntry re-use near Entry\",\n\t\t\"Expected error logged on second write.\")\n}\n"
  },
  {
    "path": "zapcore/entry_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport (\n\t\"sync\"\n\t\"testing\"\n\n\t\"go.uber.org/zap/internal/exit\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc assertGoexit(t *testing.T, f func()) {\n\tvar finished bool\n\trecovered := make(chan interface{})\n\tgo func() {\n\t\tdefer func() {\n\t\t\trecovered <- recover()\n\t\t}()\n\n\t\tf()\n\t\tfinished = true\n\t}()\n\n\tassert.Nil(t, <-recovered, \"Goexit should cause recover to return nil\")\n\tassert.False(t, finished, \"Goroutine should not finish after Goexit\")\n}\n\nfunc TestPutNilEntry(t *testing.T) {\n\t// Pooling nil entries defeats the purpose.\n\tvar wg sync.WaitGroup\n\twg.Add(2)\n\n\tgo func() {\n\t\tdefer wg.Done()\n\t\tfor i := 0; i < 1000; i++ {\n\t\t\tputCheckedEntry(nil)\n\t\t}\n\t}()\n\n\tgo func() {\n\t\tdefer wg.Done()\n\t\tfor i := 0; i < 1000; i++ {\n\t\t\tce := getCheckedEntry()\n\t\t\tassert.NotNil(t, ce, \"Expected only non-nil CheckedEntries in pool.\")\n\t\t\tassert.False(t, ce.dirty, \"Unexpected dirty bit set.\")\n\t\t\tassert.Nil(t, ce.ErrorOutput, \"Non-nil ErrorOutput.\")\n\t\t\tassert.Nil(t, ce.after, \"Unexpected terminal behavior.\")\n\t\t\tassert.Equal(t, 0, len(ce.cores), \"Expected empty slice of cores.\")\n\t\t\tassert.True(t, cap(ce.cores) > 0, \"Expected pooled CheckedEntries to pre-allocate slice of Cores.\")\n\t\t}\n\t}()\n\n\twg.Wait()\n}\n\nfunc TestEntryCaller(t *testing.T) {\n\ttests := []struct {\n\t\tcaller EntryCaller\n\t\tfull   string\n\t\tshort  string\n\t}{\n\t\t{\n\t\t\tcaller: NewEntryCaller(100, \"/path/to/foo.go\", 42, false),\n\t\t\tfull:   \"undefined\",\n\t\t\tshort:  \"undefined\",\n\t\t},\n\t\t{\n\t\t\tcaller: NewEntryCaller(100, \"/path/to/foo.go\", 42, true),\n\t\t\tfull:   \"/path/to/foo.go:42\",\n\t\t\tshort:  \"to/foo.go:42\",\n\t\t},\n\t\t{\n\t\t\tcaller: NewEntryCaller(100, \"to/foo.go\", 42, true),\n\t\t\tfull:   \"to/foo.go:42\",\n\t\t\tshort:  \"to/foo.go:42\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tassert.Equal(t, tt.full, tt.caller.String(), \"Unexpected string from EntryCaller.\")\n\t\tassert.Equal(t, tt.full, tt.caller.FullPath(), \"Unexpected FullPath from EntryCaller.\")\n\t\tassert.Equal(t, tt.short, tt.caller.TrimmedPath(), \"Unexpected TrimmedPath from EntryCaller.\")\n\t}\n}\n\nfunc TestCheckedEntryWrite(t *testing.T) {\n\tt.Run(\"nil is safe\", func(t *testing.T) {\n\t\tvar ce *CheckedEntry\n\t\tassert.NotPanics(t, func() { ce.Write() }, \"Unexpected panic writing nil CheckedEntry.\")\n\t})\n\n\tt.Run(\"WriteThenPanic\", func(t *testing.T) {\n\t\tvar ce *CheckedEntry\n\t\tce = ce.After(Entry{}, WriteThenPanic)\n\t\tassert.Panics(t, func() { ce.Write() }, \"Expected to panic when WriteThenPanic is set.\")\n\t})\n\n\tt.Run(\"WriteThenGoexit\", func(t *testing.T) {\n\t\tvar ce *CheckedEntry\n\t\tce = ce.After(Entry{}, WriteThenGoexit)\n\t\tassertGoexit(t, func() { ce.Write() })\n\t})\n\n\tt.Run(\"WriteThenFatal\", func(t *testing.T) {\n\t\tvar ce *CheckedEntry\n\t\tce = ce.After(Entry{}, WriteThenFatal)\n\t\tstub := exit.WithStub(func() {\n\t\t\tce.Write()\n\t\t})\n\t\tassert.True(t, stub.Exited, \"Expected to exit when WriteThenFatal is set.\")\n\t\tassert.Equal(t, 1, stub.Code, \"Expected to exit when WriteThenFatal is set.\")\n\t})\n\n\tt.Run(\"After\", func(t *testing.T) {\n\t\tvar ce *CheckedEntry\n\t\thook := &customHook{}\n\t\tce = ce.After(Entry{}, hook)\n\t\tce.Write()\n\t\tassert.True(t, hook.called, \"Expected to call custom action after Write.\")\n\t})\n}\n\ntype customHook struct {\n\tcalled bool\n}\n\nfunc (c *customHook) OnWrite(_ *CheckedEntry, _ []Field) {\n\tc.called = true\n}\n\nfunc TestCheckedEntryBefore(t *testing.T) {\n\tt.Run(\"nil is safe\", func(t *testing.T) {\n\t\tvar ce *CheckedEntry\n\t\tce = ce.Before(Entry{Message: \"hello\"}, func(ent Entry, fields []Field) (Entry, []Field) {\n\t\t\tent.Message = \"modified\"\n\t\t\treturn ent, fields\n\t\t})\n\t\tassert.NotNil(t, ce)\n\t\tassert.Equal(t, \"hello\", ce.Entry.Message)\n\t})\n\n\tt.Run(\"modifies entry and fields\", func(t *testing.T) {\n\t\tcore := &recordingCore{}\n\t\tvar ce *CheckedEntry\n\t\tce = ce.AddCore(Entry{Message: \"original\"}, core)\n\t\tce = ce.Before(Entry{}, func(ent Entry, fields []Field) (Entry, []Field) {\n\t\t\tent.Message = \"modified\"\n\t\t\tfields = append(fields, Field{Key: \"added\", Type: StringType, String: \"value\"})\n\t\t\treturn ent, fields\n\t\t})\n\t\tce.Write(Field{Key: \"initial\", Type: StringType, String: \"v\"})\n\n\t\tassert.Equal(t, \"modified\", core.entry.Message)\n\t\tassert.Len(t, core.fields, 2)\n\t\tassert.Equal(t, \"initial\", core.fields[0].Key)\n\t\tassert.Equal(t, \"added\", core.fields[1].Key)\n\t})\n\n\tt.Run(\"multiple hooks chain in order\", func(t *testing.T) {\n\t\tcore := &recordingCore{}\n\t\tvar ce *CheckedEntry\n\t\tce = ce.AddCore(Entry{Message: \"start\"}, core)\n\t\tce = ce.Before(Entry{}, func(ent Entry, fields []Field) (Entry, []Field) {\n\t\t\tent.Message = ent.Message + \"-first\"\n\t\t\treturn ent, fields\n\t\t})\n\t\tce = ce.Before(Entry{}, func(ent Entry, fields []Field) (Entry, []Field) {\n\t\t\tent.Message = ent.Message + \"-second\"\n\t\t\treturn ent, fields\n\t\t})\n\t\tce.Write()\n\n\t\tassert.Equal(t, \"start-first-second\", core.entry.Message)\n\t})\n\n\tt.Run(\"hooks reset on pool reuse\", func(t *testing.T) {\n\t\tcore := &recordingCore{}\n\t\tvar ce *CheckedEntry\n\t\tce = ce.AddCore(Entry{Message: \"first\"}, core)\n\t\tce = ce.Before(Entry{}, func(ent Entry, fields []Field) (Entry, []Field) {\n\t\t\tent.Message = \"hooked\"\n\t\t\treturn ent, fields\n\t\t})\n\t\tce.Write()\n\t\tassert.Equal(t, \"hooked\", core.entry.Message)\n\n\t\t// Get a new entry from the pool — hooks should be cleared.\n\t\tce2 := getCheckedEntry()\n\t\tassert.Empty(t, ce2.before)\n\t\tputCheckedEntry(ce2)\n\t})\n}\n\n// recordingCore captures the last entry and fields written to it.\ntype recordingCore struct {\n\tentry  Entry\n\tfields []Field\n}\n\nfunc (c *recordingCore) Enabled(Level) bool                              { return true }\nfunc (c *recordingCore) With([]Field) Core                               { return c }\nfunc (c *recordingCore) Check(ent Entry, ce *CheckedEntry) *CheckedEntry { return ce.AddCore(ent, c) }\nfunc (c *recordingCore) Sync() error                                     { return nil }\nfunc (c *recordingCore) Write(ent Entry, fields []Field) error {\n\tc.entry = ent\n\tc.fields = fields\n\treturn nil\n}\n"
  },
  {
    "path": "zapcore/error.go",
    "content": "// Copyright (c) 2017 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\n\t\"go.uber.org/zap/internal/pool\"\n)\n\n// Encodes the given error into fields of an object. A field with the given\n// name is added for the error message.\n//\n// If the error implements fmt.Formatter, a field with the name ${key}Verbose\n// is also added with the full verbose error message.\n//\n// Finally, if the error implements errorGroup (from go.uber.org/multierr) or\n// causer (from github.com/pkg/errors), a ${key}Causes field is added with an\n// array of objects containing the errors this error was comprised of.\n//\n//\t{\n//\t  \"error\": err.Error(),\n//\t  \"errorVerbose\": fmt.Sprintf(\"%+v\", err),\n//\t  \"errorCauses\": [\n//\t    ...\n//\t  ],\n//\t}\nfunc encodeError(key string, err error, enc ObjectEncoder) (retErr error) {\n\t// Try to capture panics (from nil references or otherwise) when calling\n\t// the Error() method\n\tdefer func() {\n\t\tif rerr := recover(); rerr != nil {\n\t\t\t// If it's a nil pointer, just say \"<nil>\". The likeliest causes are a\n\t\t\t// error that fails to guard against nil or a nil pointer for a\n\t\t\t// value receiver, and in either case, \"<nil>\" is a nice result.\n\t\t\tif v := reflect.ValueOf(err); v.Kind() == reflect.Ptr && v.IsNil() {\n\t\t\t\tenc.AddString(key, \"<nil>\")\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tretErr = fmt.Errorf(\"PANIC=%v\", rerr)\n\t\t}\n\t}()\n\n\tbasic := err.Error()\n\tenc.AddString(key, basic)\n\n\tswitch e := err.(type) {\n\tcase errorGroup:\n\t\treturn enc.AddArray(key+\"Causes\", errArray(e.Errors()))\n\tcase fmt.Formatter:\n\t\tverbose := fmt.Sprintf(\"%+v\", e)\n\t\tif verbose != basic {\n\t\t\t// This is a rich error type, like those produced by\n\t\t\t// github.com/pkg/errors.\n\t\t\tenc.AddString(key+\"Verbose\", verbose)\n\t\t}\n\t}\n\treturn nil\n}\n\ntype errorGroup interface {\n\t// Provides read-only access to the underlying list of errors, preferably\n\t// without causing any allocs.\n\tErrors() []error\n}\n\n// Note that errArray and errArrayElem are very similar to the version\n// implemented in the top-level error.go file. We can't re-use this because\n// that would require exporting errArray as part of the zapcore API.\n\n// Encodes a list of errors using the standard error encoding logic.\ntype errArray []error\n\nfunc (errs errArray) MarshalLogArray(arr ArrayEncoder) error {\n\tfor i := range errs {\n\t\tif errs[i] == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tel := newErrArrayElem(errs[i])\n\t\terr := arr.AppendObject(el)\n\t\tel.Free()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nvar _errArrayElemPool = pool.New(func() *errArrayElem {\n\treturn &errArrayElem{}\n})\n\n// Encodes any error into a {\"error\": ...} re-using the same errors logic.\n//\n// May be passed in place of an array to build a single-element array.\ntype errArrayElem struct{ err error }\n\nfunc newErrArrayElem(err error) *errArrayElem {\n\te := _errArrayElemPool.Get()\n\te.err = err\n\treturn e\n}\n\nfunc (e *errArrayElem) MarshalLogArray(arr ArrayEncoder) error {\n\treturn arr.AppendObject(e)\n}\n\nfunc (e *errArrayElem) MarshalLogObject(enc ObjectEncoder) error {\n\treturn encodeError(\"error\", e.err, enc)\n}\n\nfunc (e *errArrayElem) Free() {\n\te.err = nil\n\t_errArrayElemPool.Put(e)\n}\n"
  },
  {
    "path": "zapcore/error_test.go",
    "content": "// Copyright (c) 2017 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore_test\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"go.uber.org/multierr\"\n\t//revive:disable:dot-imports\n\t. \"go.uber.org/zap/zapcore\"\n)\n\ntype errTooManyUsers int\n\nfunc (e errTooManyUsers) Error() string {\n\treturn fmt.Sprintf(\"%d too many users\", int(e))\n}\n\nfunc (e errTooManyUsers) Format(s fmt.State, verb rune) {\n\t// Implement fmt.Formatter, but don't add any information beyond the basic\n\t// Error method.\n\tif verb == 'v' && s.Flag('+') {\n\t\t_, _ = io.WriteString(s, e.Error())\n\t}\n}\n\ntype errTooFewUsers int\n\nfunc (e errTooFewUsers) Error() string {\n\treturn fmt.Sprintf(\"%d too few users\", int(e))\n}\n\nfunc (e errTooFewUsers) Format(s fmt.State, verb rune) {\n\tif verb == 'v' && s.Flag('+') {\n\t\t_, _ = io.WriteString(s, \"verbose: \")\n\t\t_, _ = io.WriteString(s, e.Error())\n\t}\n}\n\ntype customMultierr struct{}\n\nfunc (e customMultierr) Error() string {\n\treturn \"great sadness\"\n}\n\nfunc (e customMultierr) Errors() []error {\n\treturn []error{\n\t\terrors.New(\"foo\"),\n\t\tnil,\n\t\tmultierr.Append(\n\t\t\terrors.New(\"bar\"),\n\t\t\terrors.New(\"baz\"),\n\t\t),\n\t}\n}\n\nfunc TestErrorEncoding(t *testing.T) {\n\ttests := []struct {\n\t\tkey   string\n\t\tiface any\n\t\twant  map[string]any\n\t}{\n\t\t{\n\t\t\tkey:   \"k\",\n\t\t\tiface: errTooManyUsers(2),\n\t\t\twant: map[string]any{\n\t\t\t\t\"k\": \"2 too many users\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tkey:   \"k\",\n\t\t\tiface: errTooFewUsers(2),\n\t\t\twant: map[string]any{\n\t\t\t\t\"k\":        \"2 too few users\",\n\t\t\t\t\"kVerbose\": \"verbose: 2 too few users\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tkey: \"err\",\n\t\t\tiface: multierr.Combine(\n\t\t\t\terrors.New(\"foo\"),\n\t\t\t\terrors.New(\"bar\"),\n\t\t\t\terrors.New(\"baz\"),\n\t\t\t),\n\t\t\twant: map[string]any{\n\t\t\t\t\"err\": \"foo; bar; baz\",\n\t\t\t\t\"errCauses\": []any{\n\t\t\t\t\tmap[string]any{\"error\": \"foo\"},\n\t\t\t\t\tmap[string]any{\"error\": \"bar\"},\n\t\t\t\t\tmap[string]any{\"error\": \"baz\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tkey:   \"e\",\n\t\t\tiface: customMultierr{},\n\t\t\twant: map[string]any{\n\t\t\t\t\"e\": \"great sadness\",\n\t\t\t\t\"eCauses\": []any{\n\t\t\t\t\tmap[string]any{\"error\": \"foo\"},\n\t\t\t\t\tmap[string]any{\n\t\t\t\t\t\t\"error\": \"bar; baz\",\n\t\t\t\t\t\t\"errorCauses\": []any{\n\t\t\t\t\t\t\tmap[string]any{\"error\": \"bar\"},\n\t\t\t\t\t\t\tmap[string]any{\"error\": \"baz\"},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tkey:   \"k\",\n\t\t\tiface: fmt.Errorf(\"failed: %w\", errors.New(\"egad\")),\n\t\t\twant: map[string]any{\n\t\t\t\t\"k\": \"failed: egad\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tkey: \"error\",\n\t\t\tiface: multierr.Combine(\n\t\t\t\tfmt.Errorf(\"hello: %w\",\n\t\t\t\t\tmultierr.Combine(errors.New(\"foo\"), errors.New(\"bar\")),\n\t\t\t\t),\n\t\t\t\terrors.New(\"baz\"),\n\t\t\t\tfmt.Errorf(\"world: %w\", errors.New(\"qux\")),\n\t\t\t),\n\t\t\twant: map[string]any{\n\t\t\t\t\"error\": \"hello: foo; bar; baz; world: qux\",\n\t\t\t\t\"errorCauses\": []any{\n\t\t\t\t\tmap[string]any{\n\t\t\t\t\t\t\"error\": \"hello: foo; bar\",\n\t\t\t\t\t},\n\t\t\t\t\tmap[string]any{\"error\": \"baz\"},\n\t\t\t\t\tmap[string]any{\"error\": \"world: qux\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tenc := NewMapObjectEncoder()\n\t\tf := Field{Key: tt.key, Type: ErrorType, Interface: tt.iface}\n\t\tf.AddTo(enc)\n\t\tassert.Equal(t, tt.want, enc.Fields, \"Unexpected output from field %+v.\", f)\n\t}\n}\n\nfunc TestRichErrorSupport(t *testing.T) {\n\tf := Field{\n\t\tType:      ErrorType,\n\t\tInterface: fmt.Errorf(\"failed: %w\", errors.New(\"egad\")),\n\t\tKey:       \"k\",\n\t}\n\tenc := NewMapObjectEncoder()\n\tf.AddTo(enc)\n\tassert.Equal(t, \"failed: egad\", enc.Fields[\"k\"], \"Unexpected basic error message.\")\n}\n\nfunc TestErrArrayBrokenEncoder(t *testing.T) {\n\tt.Parallel()\n\n\tf := Field{\n\t\tKey:  \"foo\",\n\t\tType: ErrorType,\n\t\tInterface: multierr.Combine(\n\t\t\terrors.New(\"foo\"),\n\t\t\terrors.New(\"bar\"),\n\t\t),\n\t}\n\n\tfailWith := errors.New(\"great sadness\")\n\tenc := NewMapObjectEncoder()\n\tf.AddTo(brokenArrayObjectEncoder{\n\t\tErr:           failWith,\n\t\tObjectEncoder: enc,\n\t})\n\n\t// Failure to add the field to the encoder\n\t// causes the error to be added as a string field.\n\tassert.Equal(t, \"great sadness\", enc.Fields[\"fooError\"],\n\t\t\"Unexpected error message.\")\n}\n\n// brokenArrayObjectEncoder is an ObjectEncoder\n// that builds a broken ArrayEncoder.\ntype brokenArrayObjectEncoder struct {\n\tObjectEncoder\n\tArrayEncoder\n\n\tErr error // error to return\n}\n\nfunc (enc brokenArrayObjectEncoder) AddArray(key string, marshaler ArrayMarshaler) error {\n\treturn enc.ObjectEncoder.AddArray(key,\n\t\tArrayMarshalerFunc(func(ae ArrayEncoder) error {\n\t\t\tenc.ArrayEncoder = ae\n\t\t\treturn marshaler.MarshalLogArray(enc)\n\t\t}))\n}\n\nfunc (enc brokenArrayObjectEncoder) AppendObject(ObjectMarshaler) error {\n\treturn enc.Err\n}\n"
  },
  {
    "path": "zapcore/field.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"math\"\n\t\"reflect\"\n\t\"time\"\n)\n\n// A FieldType indicates which member of the Field union struct should be used\n// and how it should be serialized.\ntype FieldType uint8\n\nconst (\n\t// UnknownType is the default field type. Attempting to add it to an encoder will panic.\n\tUnknownType FieldType = iota\n\t// ArrayMarshalerType indicates that the field carries an ArrayMarshaler.\n\tArrayMarshalerType\n\t// ObjectMarshalerType indicates that the field carries an ObjectMarshaler.\n\tObjectMarshalerType\n\t// BinaryType indicates that the field carries an opaque binary blob.\n\tBinaryType\n\t// BoolType indicates that the field carries a bool.\n\tBoolType\n\t// ByteStringType indicates that the field carries UTF-8 encoded bytes.\n\tByteStringType\n\t// Complex128Type indicates that the field carries a complex128.\n\tComplex128Type\n\t// Complex64Type indicates that the field carries a complex64.\n\tComplex64Type\n\t// DurationType indicates that the field carries a time.Duration.\n\tDurationType\n\t// Float64Type indicates that the field carries a float64.\n\tFloat64Type\n\t// Float32Type indicates that the field carries a float32.\n\tFloat32Type\n\t// Int64Type indicates that the field carries an int64.\n\tInt64Type\n\t// Int32Type indicates that the field carries an int32.\n\tInt32Type\n\t// Int16Type indicates that the field carries an int16.\n\tInt16Type\n\t// Int8Type indicates that the field carries an int8.\n\tInt8Type\n\t// StringType indicates that the field carries a string.\n\tStringType\n\t// TimeType indicates that the field carries a time.Time that is\n\t// representable by a UnixNano() stored as an int64.\n\tTimeType\n\t// TimeFullType indicates that the field carries a time.Time stored as-is.\n\tTimeFullType\n\t// Uint64Type indicates that the field carries a uint64.\n\tUint64Type\n\t// Uint32Type indicates that the field carries a uint32.\n\tUint32Type\n\t// Uint16Type indicates that the field carries a uint16.\n\tUint16Type\n\t// Uint8Type indicates that the field carries a uint8.\n\tUint8Type\n\t// UintptrType indicates that the field carries a uintptr.\n\tUintptrType\n\t// ReflectType indicates that the field carries an interface{}, which should\n\t// be serialized using reflection.\n\tReflectType\n\t// NamespaceType signals the beginning of an isolated namespace. All\n\t// subsequent fields should be added to the new namespace.\n\tNamespaceType\n\t// StringerType indicates that the field carries a fmt.Stringer.\n\tStringerType\n\t// ErrorType indicates that the field carries an error.\n\tErrorType\n\t// SkipType indicates that the field is a no-op.\n\tSkipType\n\n\t// InlineMarshalerType indicates that the field carries an ObjectMarshaler\n\t// that should be inlined.\n\tInlineMarshalerType\n)\n\n// A Field is a marshaling operation used to add a key-value pair to a logger's\n// context. Most fields are lazily marshaled, so it's inexpensive to add fields\n// to disabled debug-level log statements.\ntype Field struct {\n\tKey       string\n\tType      FieldType\n\tInteger   int64\n\tString    string\n\tInterface interface{}\n}\n\n// AddTo exports a field through the ObjectEncoder interface. It's primarily\n// useful to library authors, and shouldn't be necessary in most applications.\nfunc (f Field) AddTo(enc ObjectEncoder) {\n\tvar err error\n\n\tswitch f.Type {\n\tcase ArrayMarshalerType:\n\t\terr = enc.AddArray(f.Key, f.Interface.(ArrayMarshaler))\n\tcase ObjectMarshalerType:\n\t\terr = enc.AddObject(f.Key, f.Interface.(ObjectMarshaler))\n\tcase InlineMarshalerType:\n\t\terr = f.Interface.(ObjectMarshaler).MarshalLogObject(enc)\n\tcase BinaryType:\n\t\tenc.AddBinary(f.Key, f.Interface.([]byte))\n\tcase BoolType:\n\t\tenc.AddBool(f.Key, f.Integer == 1)\n\tcase ByteStringType:\n\t\tenc.AddByteString(f.Key, f.Interface.([]byte))\n\tcase Complex128Type:\n\t\tenc.AddComplex128(f.Key, f.Interface.(complex128))\n\tcase Complex64Type:\n\t\tenc.AddComplex64(f.Key, f.Interface.(complex64))\n\tcase DurationType:\n\t\tenc.AddDuration(f.Key, time.Duration(f.Integer))\n\tcase Float64Type:\n\t\tenc.AddFloat64(f.Key, math.Float64frombits(uint64(f.Integer)))\n\tcase Float32Type:\n\t\tenc.AddFloat32(f.Key, math.Float32frombits(uint32(f.Integer)))\n\tcase Int64Type:\n\t\tenc.AddInt64(f.Key, f.Integer)\n\tcase Int32Type:\n\t\tenc.AddInt32(f.Key, int32(f.Integer))\n\tcase Int16Type:\n\t\tenc.AddInt16(f.Key, int16(f.Integer))\n\tcase Int8Type:\n\t\tenc.AddInt8(f.Key, int8(f.Integer))\n\tcase StringType:\n\t\tenc.AddString(f.Key, f.String)\n\tcase TimeType:\n\t\tif f.Interface != nil {\n\t\t\tenc.AddTime(f.Key, time.Unix(0, f.Integer).In(f.Interface.(*time.Location)))\n\t\t} else {\n\t\t\t// Fall back to UTC if location is nil.\n\t\t\tenc.AddTime(f.Key, time.Unix(0, f.Integer))\n\t\t}\n\tcase TimeFullType:\n\t\tenc.AddTime(f.Key, f.Interface.(time.Time))\n\tcase Uint64Type:\n\t\tenc.AddUint64(f.Key, uint64(f.Integer))\n\tcase Uint32Type:\n\t\tenc.AddUint32(f.Key, uint32(f.Integer))\n\tcase Uint16Type:\n\t\tenc.AddUint16(f.Key, uint16(f.Integer))\n\tcase Uint8Type:\n\t\tenc.AddUint8(f.Key, uint8(f.Integer))\n\tcase UintptrType:\n\t\tenc.AddUintptr(f.Key, uintptr(f.Integer))\n\tcase ReflectType:\n\t\terr = enc.AddReflected(f.Key, f.Interface)\n\tcase NamespaceType:\n\t\tenc.OpenNamespace(f.Key)\n\tcase StringerType:\n\t\terr = encodeStringer(f.Key, f.Interface, enc)\n\tcase ErrorType:\n\t\terr = encodeError(f.Key, f.Interface.(error), enc)\n\tcase SkipType:\n\t\tbreak\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"unknown field type: %v\", f))\n\t}\n\n\tif err != nil {\n\t\tenc.AddString(fmt.Sprintf(\"%sError\", f.Key), err.Error())\n\t}\n}\n\n// Equals returns whether two fields are equal. For non-primitive types such as\n// errors, marshalers, or reflect types, it uses reflect.DeepEqual.\nfunc (f Field) Equals(other Field) bool {\n\tif f.Type != other.Type {\n\t\treturn false\n\t}\n\tif f.Key != other.Key {\n\t\treturn false\n\t}\n\n\tswitch f.Type {\n\tcase BinaryType, ByteStringType:\n\t\treturn bytes.Equal(f.Interface.([]byte), other.Interface.([]byte))\n\tcase ArrayMarshalerType, ObjectMarshalerType, ErrorType, ReflectType:\n\t\treturn reflect.DeepEqual(f.Interface, other.Interface)\n\tdefault:\n\t\treturn f == other\n\t}\n}\n\nfunc addFields(enc ObjectEncoder, fields []Field) {\n\tfor i := range fields {\n\t\tfields[i].AddTo(enc)\n\t}\n}\n\nfunc encodeStringer(key string, stringer interface{}, enc ObjectEncoder) (retErr error) {\n\t// Try to capture panics (from nil references or otherwise) when calling\n\t// the String() method, similar to https://golang.org/src/fmt/print.go#L540\n\tdefer func() {\n\t\tif err := recover(); err != nil {\n\t\t\t// If it's a nil pointer, just say \"<nil>\". The likeliest causes are a\n\t\t\t// Stringer that fails to guard against nil or a nil pointer for a\n\t\t\t// value receiver, and in either case, \"<nil>\" is a nice result.\n\t\t\tif v := reflect.ValueOf(stringer); v.Kind() == reflect.Ptr && v.IsNil() {\n\t\t\t\tenc.AddString(key, \"<nil>\")\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tretErr = fmt.Errorf(\"PANIC=%v\", err)\n\t\t}\n\t}()\n\n\tenc.AddString(key, stringer.(fmt.Stringer).String())\n\treturn nil\n}\n"
  },
  {
    "path": "zapcore/field_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore_test\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/zap\"\n\n\t//revive:disable:dot-imports\n\t. \"go.uber.org/zap/zapcore\"\n)\n\ntype users int\n\nfunc (u users) String() string {\n\treturn fmt.Sprintf(\"%d users\", int(u))\n}\n\nfunc (u users) MarshalLogObject(enc ObjectEncoder) error {\n\tif int(u) < 0 {\n\t\treturn errors.New(\"too few users\")\n\t}\n\tenc.AddInt(\"users\", int(u))\n\treturn nil\n}\n\nfunc (u users) MarshalLogArray(enc ArrayEncoder) error {\n\tif int(u) < 0 {\n\t\treturn errors.New(\"too few users\")\n\t}\n\tfor i := 0; i < int(u); i++ {\n\t\tenc.AppendString(\"user\")\n\t}\n\treturn nil\n}\n\ntype obj struct {\n\tkind int\n}\n\nfunc (o *obj) String() string {\n\tif o == nil {\n\t\treturn \"nil obj\"\n\t}\n\n\tif o.kind == 1 {\n\t\tpanic(\"panic with string\")\n\t} else if o.kind == 2 {\n\t\tpanic(errors.New(\"panic with error\"))\n\t} else if o.kind == 3 {\n\t\t// panic with an arbitrary object that causes a panic itself\n\t\t// when being converted to a string\n\t\tpanic((*url.URL)(nil))\n\t}\n\n\treturn \"obj\"\n}\n\ntype errObj struct {\n\tkind   int\n\terrMsg string\n}\n\nfunc (eobj *errObj) Error() string {\n\tif eobj.kind == 1 {\n\t\tpanic(\"panic in Error() method\")\n\t}\n\treturn eobj.errMsg\n}\n\nfunc TestUnknownFieldType(t *testing.T) {\n\tunknown := Field{Key: \"k\", String: \"foo\"}\n\tassert.Equal(t, UnknownType, unknown.Type, \"Expected zero value of FieldType to be UnknownType.\")\n\tassert.Panics(t, func() {\n\t\tunknown.AddTo(NewMapObjectEncoder())\n\t}, \"Expected using a field with unknown type to panic.\")\n}\n\nfunc TestFieldAddingError(t *testing.T) {\n\tvar empty interface{}\n\ttests := []struct {\n\t\tt     FieldType\n\t\tiface interface{}\n\t\twant  interface{}\n\t\terr   string\n\t}{\n\t\t{t: ArrayMarshalerType, iface: users(-1), want: []interface{}{}, err: \"too few users\"},\n\t\t{t: ObjectMarshalerType, iface: users(-1), want: map[string]interface{}{}, err: \"too few users\"},\n\t\t{t: InlineMarshalerType, iface: users(-1), want: nil, err: \"too few users\"},\n\t\t{t: StringerType, iface: obj{}, want: empty, err: \"PANIC=interface conversion: zapcore_test.obj is not fmt.Stringer: missing method String\"},\n\t\t{t: StringerType, iface: &obj{1}, want: empty, err: \"PANIC=panic with string\"},\n\t\t{t: StringerType, iface: &obj{2}, want: empty, err: \"PANIC=panic with error\"},\n\t\t{t: StringerType, iface: &obj{3}, want: empty, err: \"PANIC=<nil>\"},\n\t\t{t: ErrorType, iface: &errObj{kind: 1}, want: empty, err: \"PANIC=panic in Error() method\"},\n\t}\n\tfor _, tt := range tests {\n\t\tf := Field{Key: \"k\", Interface: tt.iface, Type: tt.t}\n\t\tenc := NewMapObjectEncoder()\n\t\tassert.NotPanics(t, func() { f.AddTo(enc) }, \"Unexpected panic when adding fields returns an error.\")\n\t\tassert.Equal(t, tt.want, enc.Fields[\"k\"], \"On error, expected zero value in field.Key.\")\n\t\tassert.Equal(t, tt.err, enc.Fields[\"kError\"], \"Expected error message in log context.\")\n\t}\n}\n\nfunc TestFields(t *testing.T) {\n\ttests := []struct {\n\t\tt     FieldType\n\t\ti     int64\n\t\ts     string\n\t\tiface interface{}\n\t\twant  interface{}\n\t}{\n\t\t{t: ArrayMarshalerType, iface: users(2), want: []interface{}{\"user\", \"user\"}},\n\t\t{t: ObjectMarshalerType, iface: users(2), want: map[string]interface{}{\"users\": 2}},\n\t\t{t: BoolType, i: 0, want: false},\n\t\t{t: ByteStringType, iface: []byte(\"foo\"), want: \"foo\"},\n\t\t{t: Complex128Type, iface: 1 + 2i, want: 1 + 2i},\n\t\t{t: Complex64Type, iface: complex64(1 + 2i), want: complex64(1 + 2i)},\n\t\t{t: DurationType, i: 1000, want: time.Microsecond},\n\t\t{t: Float64Type, i: int64(math.Float64bits(3.14)), want: 3.14},\n\t\t{t: Float32Type, i: int64(math.Float32bits(3.14)), want: float32(3.14)},\n\t\t{t: Int64Type, i: 42, want: int64(42)},\n\t\t{t: Int32Type, i: 42, want: int32(42)},\n\t\t{t: Int16Type, i: 42, want: int16(42)},\n\t\t{t: Int8Type, i: 42, want: int8(42)},\n\t\t{t: StringType, s: \"foo\", want: \"foo\"},\n\t\t{t: TimeType, i: 1000, iface: time.UTC, want: time.Unix(0, 1000).In(time.UTC)},\n\t\t{t: TimeType, i: 1000, want: time.Unix(0, 1000)},\n\t\t{t: Uint64Type, i: 42, want: uint64(42)},\n\t\t{t: Uint32Type, i: 42, want: uint32(42)},\n\t\t{t: Uint16Type, i: 42, want: uint16(42)},\n\t\t{t: Uint8Type, i: 42, want: uint8(42)},\n\t\t{t: UintptrType, i: 42, want: uintptr(42)},\n\t\t{t: ReflectType, iface: users(2), want: users(2)},\n\t\t{t: ReflectType, iface: nil, want: nil},\n\t\t{t: NamespaceType, want: map[string]interface{}{}},\n\t\t{t: StringerType, iface: users(2), want: \"2 users\"},\n\t\t{t: StringerType, iface: &obj{}, want: \"obj\"},\n\t\t{t: StringerType, iface: (*obj)(nil), want: \"nil obj\"},\n\t\t{t: SkipType, want: interface{}(nil)},\n\t\t{t: StringerType, iface: (*url.URL)(nil), want: \"<nil>\"},\n\t\t{t: StringerType, iface: (*users)(nil), want: \"<nil>\"},\n\t\t{t: ErrorType, iface: (*errObj)(nil), want: \"<nil>\"},\n\t}\n\n\tfor _, tt := range tests {\n\t\tenc := NewMapObjectEncoder()\n\t\tf := Field{Key: \"k\", Type: tt.t, Integer: tt.i, Interface: tt.iface, String: tt.s}\n\t\tf.AddTo(enc)\n\t\tassert.Equal(t, tt.want, enc.Fields[\"k\"], \"Unexpected output from field %+v.\", f)\n\n\t\tdelete(enc.Fields, \"k\")\n\t\tassert.Equal(t, 0, len(enc.Fields), \"Unexpected extra fields present.\")\n\n\t\tassert.True(t, f.Equals(f), \"Field does not equal itself\")\n\t}\n}\n\nfunc TestInlineMarshaler(t *testing.T) {\n\tenc := NewMapObjectEncoder()\n\n\ttopLevelStr := Field{Key: \"k\", Type: StringType, String: \"s\"}\n\ttopLevelStr.AddTo(enc)\n\n\tinlineObj := Field{Key: \"ignored\", Type: InlineMarshalerType, Interface: users(10)}\n\tinlineObj.AddTo(enc)\n\n\tnestedObj := Field{Key: \"nested\", Type: ObjectMarshalerType, Interface: users(11)}\n\tnestedObj.AddTo(enc)\n\n\tassert.Equal(t, map[string]interface{}{\n\t\t\"k\":     \"s\",\n\t\t\"users\": 10,\n\t\t\"nested\": map[string]interface{}{\n\t\t\t\"users\": 11,\n\t\t},\n\t}, enc.Fields)\n}\n\nfunc TestEquals(t *testing.T) {\n\t// Values outside the UnixNano range were encoded incorrectly (#737, #803).\n\ttimeOutOfRangeHigh := time.Unix(0, math.MaxInt64).Add(time.Nanosecond)\n\ttimeOutOfRangeLow := time.Unix(0, math.MinInt64).Add(-time.Nanosecond)\n\ttimeOutOfRangeHighNano := time.Unix(0, timeOutOfRangeHigh.UnixNano())\n\ttimeOutOfRangeLowNano := time.Unix(0, timeOutOfRangeLow.UnixNano())\n\trequire.False(t, timeOutOfRangeHigh.Equal(timeOutOfRangeHighNano), \"should be different as value is >  UnixNano range\")\n\trequire.False(t, timeOutOfRangeHigh.Equal(timeOutOfRangeHighNano), \"should be different as value is <  UnixNano range\")\n\n\ttests := []struct {\n\t\ta, b Field\n\t\twant bool\n\t}{\n\t\t{\n\t\t\ta:    zap.Int16(\"a\", 1),\n\t\t\tb:    zap.Int32(\"a\", 1),\n\t\t\twant: false,\n\t\t},\n\t\t{\n\t\t\ta:    zap.String(\"k\", \"a\"),\n\t\t\tb:    zap.String(\"k\", \"a\"),\n\t\t\twant: true,\n\t\t},\n\t\t{\n\t\t\ta:    zap.String(\"k\", \"a\"),\n\t\t\tb:    zap.String(\"k2\", \"a\"),\n\t\t\twant: false,\n\t\t},\n\t\t{\n\t\t\ta:    zap.String(\"k\", \"a\"),\n\t\t\tb:    zap.String(\"k\", \"b\"),\n\t\t\twant: false,\n\t\t},\n\t\t{\n\t\t\ta:    zap.Time(\"k\", time.Unix(1000, 1000)),\n\t\t\tb:    zap.Time(\"k\", time.Unix(1000, 1000)),\n\t\t\twant: true,\n\t\t},\n\t\t{\n\t\t\ta:    zap.Time(\"k\", time.Unix(1000, 1000).In(time.UTC)),\n\t\t\tb:    zap.Time(\"k\", time.Unix(1000, 1000).In(time.FixedZone(\"TEST\", -8))),\n\t\t\twant: false,\n\t\t},\n\t\t{\n\t\t\ta:    zap.Time(\"k\", timeOutOfRangeLow),\n\t\t\tb:    zap.Time(\"k\", timeOutOfRangeLowNano),\n\t\t\twant: false,\n\t\t},\n\t\t{\n\t\t\ta:    zap.Time(\"k\", timeOutOfRangeHigh),\n\t\t\tb:    zap.Time(\"k\", timeOutOfRangeHighNano),\n\t\t\twant: false,\n\t\t},\n\t\t{\n\t\t\ta:    zap.Time(\"k\", time.Unix(1000, 1000)),\n\t\t\tb:    zap.Time(\"k\", time.Unix(1000, 2000)),\n\t\t\twant: false,\n\t\t},\n\t\t{\n\t\t\ta:    zap.Binary(\"k\", []byte{1, 2}),\n\t\t\tb:    zap.Binary(\"k\", []byte{1, 2}),\n\t\t\twant: true,\n\t\t},\n\t\t{\n\t\t\ta:    zap.Binary(\"k\", []byte{1, 2}),\n\t\t\tb:    zap.Binary(\"k\", []byte{1, 3}),\n\t\t\twant: false,\n\t\t},\n\t\t{\n\t\t\ta:    zap.ByteString(\"k\", []byte(\"abc\")),\n\t\t\tb:    zap.ByteString(\"k\", []byte(\"abc\")),\n\t\t\twant: true,\n\t\t},\n\t\t{\n\t\t\ta:    zap.ByteString(\"k\", []byte(\"abc\")),\n\t\t\tb:    zap.ByteString(\"k\", []byte(\"abd\")),\n\t\t\twant: false,\n\t\t},\n\t\t{\n\t\t\ta:    zap.Ints(\"k\", []int{1, 2}),\n\t\t\tb:    zap.Ints(\"k\", []int{1, 2}),\n\t\t\twant: true,\n\t\t},\n\t\t{\n\t\t\ta:    zap.Ints(\"k\", []int{1, 2}),\n\t\t\tb:    zap.Ints(\"k\", []int{1, 3}),\n\t\t\twant: false,\n\t\t},\n\t\t{\n\t\t\ta:    zap.Object(\"k\", users(10)),\n\t\t\tb:    zap.Object(\"k\", users(10)),\n\t\t\twant: true,\n\t\t},\n\t\t{\n\t\t\ta:    zap.Object(\"k\", users(10)),\n\t\t\tb:    zap.Object(\"k\", users(20)),\n\t\t\twant: false,\n\t\t},\n\t\t{\n\t\t\ta:    zap.Any(\"k\", map[string]string{\"a\": \"b\"}),\n\t\t\tb:    zap.Any(\"k\", map[string]string{\"a\": \"b\"}),\n\t\t\twant: true,\n\t\t},\n\t\t{\n\t\t\ta:    zap.Any(\"k\", map[string]string{\"a\": \"b\"}),\n\t\t\tb:    zap.Any(\"k\", map[string]string{\"a\": \"d\"}),\n\t\t\twant: false,\n\t\t},\n\t\t{\n\t\t\ta:    zap.Dict(\"k\", zap.String(\"a\", \"b\")),\n\t\t\tb:    zap.Dict(\"k\", zap.String(\"a\", \"b\")),\n\t\t\twant: true,\n\t\t},\n\t\t{\n\t\t\ta:    zap.Dict(\"k\", zap.String(\"a\", \"b\")),\n\t\t\tb:    zap.Dict(\"k\", zap.String(\"a\", \"d\")),\n\t\t\twant: false,\n\t\t},\n\t\t{\n\t\t\ta:    zap.Object(\"k\", zap.DictObject(zap.String(\"a\", \"b\"))),\n\t\t\tb:    zap.Object(\"k\", zap.DictObject(zap.String(\"a\", \"b\"))),\n\t\t\twant: true,\n\t\t},\n\t\t{\n\t\t\ta:    zap.Object(\"k\", zap.DictObject(zap.String(\"a\", \"b\"))),\n\t\t\tb:    zap.Object(\"k\", zap.DictObject(zap.String(\"a\", \"d\"))),\n\t\t\twant: false,\n\t\t},\n\t\t{\n\t\t\ta:    zap.Object(\"k\", nil),\n\t\t\tb:    zap.Object(\"k\", nil),\n\t\t\twant: true,\n\t\t},\n\t\t{\n\t\t\ta:    zap.Object(\"k\", users(10)),\n\t\t\tb:    zap.Object(\"k\", nil),\n\t\t\twant: false,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tassert.Equal(t, tt.want, tt.a.Equals(tt.b), \"a.Equals(b) a: %#v b: %#v\", tt.a, tt.b)\n\t\tassert.Equal(t, tt.want, tt.b.Equals(tt.a), \"b.Equals(a) a: %#v b: %#v\", tt.a, tt.b)\n\t}\n}\n"
  },
  {
    "path": "zapcore/hook.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport \"go.uber.org/multierr\"\n\ntype hooked struct {\n\tCore\n\tfuncs []func(Entry) error\n}\n\nvar (\n\t_ Core           = (*hooked)(nil)\n\t_ leveledEnabler = (*hooked)(nil)\n)\n\n// RegisterHooks wraps a Core and runs a collection of user-defined callback\n// hooks each time a message is logged. Execution of the callbacks is blocking.\n//\n// This offers users an easy way to register simple callbacks (e.g., metrics\n// collection) without implementing the full Core interface.\nfunc RegisterHooks(core Core, hooks ...func(Entry) error) Core {\n\tfuncs := append([]func(Entry) error{}, hooks...)\n\treturn &hooked{\n\t\tCore:  core,\n\t\tfuncs: funcs,\n\t}\n}\n\nfunc (h *hooked) Level() Level {\n\treturn LevelOf(h.Core)\n}\n\nfunc (h *hooked) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {\n\t// Let the wrapped Core decide whether to log this message or not. This\n\t// also gives the downstream a chance to register itself directly with the\n\t// CheckedEntry.\n\tif downstream := h.Core.Check(ent, ce); downstream != nil {\n\t\treturn downstream.AddCore(ent, h)\n\t}\n\treturn ce\n}\n\nfunc (h *hooked) With(fields []Field) Core {\n\treturn &hooked{\n\t\tCore:  h.Core.With(fields),\n\t\tfuncs: h.funcs,\n\t}\n}\n\nfunc (h *hooked) Write(ent Entry, _ []Field) error {\n\t// Since our downstream had a chance to register itself directly with the\n\t// CheckedMessage, we don't need to call it here.\n\tvar err error\n\tfor i := range h.funcs {\n\t\terr = multierr.Append(err, h.funcs[i](ent))\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "zapcore/hook_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore_test\n\nimport (\n\t\"testing\"\n\n\t//revive:disable:dot-imports\n\t. \"go.uber.org/zap/zapcore\"\n\t\"go.uber.org/zap/zaptest/observer\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestHooks(t *testing.T) {\n\ttests := []struct {\n\t\tentryLevel Level\n\t\tcoreLevel  Level\n\t\texpectCall bool\n\t}{\n\t\t{DebugLevel, InfoLevel, false},\n\t\t{InfoLevel, InfoLevel, true},\n\t\t{WarnLevel, InfoLevel, true},\n\t}\n\n\tfor _, tt := range tests {\n\t\tfac, logs := observer.New(tt.coreLevel)\n\n\t\t// sanity check\n\t\trequire.Equal(t, tt.coreLevel, LevelOf(fac), \"Original logger has the wrong level\")\n\n\t\tintField := makeInt64Field(\"foo\", 42)\n\t\tent := Entry{Message: \"bar\", Level: tt.entryLevel}\n\n\t\tvar called int\n\t\tf := func(e Entry) error {\n\t\t\tcalled++\n\t\t\tassert.Equal(t, ent, e, \"Hook called with unexpected Entry.\")\n\t\t\treturn nil\n\t\t}\n\n\t\th := RegisterHooks(fac, f)\n\t\tif ce := h.With([]Field{intField}).Check(ent, nil); ce != nil {\n\t\t\tce.Write()\n\t\t}\n\n\t\tt.Run(\"LevelOf\", func(t *testing.T) {\n\t\t\tassert.Equal(t, tt.coreLevel, LevelOf(h), \"Wrapped logger has the wrong log level\")\n\t\t})\n\n\t\tif tt.expectCall {\n\t\t\tassert.Equal(t, 1, called, \"Expected to call hook once.\")\n\t\t\tassert.Equal(\n\t\t\t\tt,\n\t\t\t\t[]observer.LoggedEntry{{Entry: ent, Context: []Field{intField}}},\n\t\t\t\tlogs.AllUntimed(),\n\t\t\t\t\"Unexpected logs written out.\",\n\t\t\t)\n\t\t} else {\n\t\t\tassert.Equal(t, 0, called, \"Didn't expect to call hook.\")\n\t\t\tassert.Equal(t, 0, logs.Len(), \"Unexpected logs written out.\")\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "zapcore/increase_level.go",
    "content": "// Copyright (c) 2020 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport \"fmt\"\n\ntype levelFilterCore struct {\n\tcore  Core\n\tlevel LevelEnabler\n}\n\nvar (\n\t_ Core           = (*levelFilterCore)(nil)\n\t_ leveledEnabler = (*levelFilterCore)(nil)\n)\n\n// NewIncreaseLevelCore creates a core that can be used to increase the level of\n// an existing Core. It cannot be used to decrease the logging level, as it acts\n// as a filter before calling the underlying core. If level decreases the log level,\n// an error is returned.\nfunc NewIncreaseLevelCore(core Core, level LevelEnabler) (Core, error) {\n\tfor l := _maxLevel; l >= _minLevel; l-- {\n\t\tif !core.Enabled(l) && level.Enabled(l) {\n\t\t\treturn nil, fmt.Errorf(\"invalid increase level, as level %q is allowed by increased level, but not by existing core\", l)\n\t\t}\n\t}\n\n\treturn &levelFilterCore{core, level}, nil\n}\n\nfunc (c *levelFilterCore) Enabled(lvl Level) bool {\n\treturn c.level.Enabled(lvl)\n}\n\nfunc (c *levelFilterCore) Level() Level {\n\treturn LevelOf(c.level)\n}\n\nfunc (c *levelFilterCore) With(fields []Field) Core {\n\treturn &levelFilterCore{c.core.With(fields), c.level}\n}\n\nfunc (c *levelFilterCore) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {\n\tif !c.Enabled(ent.Level) {\n\t\treturn ce\n\t}\n\n\treturn c.core.Check(ent, ce)\n}\n\nfunc (c *levelFilterCore) Write(ent Entry, fields []Field) error {\n\treturn c.core.Write(ent, fields)\n}\n\nfunc (c *levelFilterCore) Sync() error {\n\treturn c.core.Sync()\n}\n"
  },
  {
    "path": "zapcore/increase_level_test.go",
    "content": "// Copyright (c) 2020 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/zap\"\n\n\t//revive:disable:dot-imports\n\t. \"go.uber.org/zap/zapcore\"\n\t\"go.uber.org/zap/zaptest/observer\"\n)\n\nfunc TestIncreaseLevel(t *testing.T) {\n\ttests := []struct {\n\t\tcoreLevel     Level\n\t\tincreaseLevel Level\n\t\twantErr       bool\n\t\twith          []Field\n\t}{\n\t\t{\n\t\t\tcoreLevel:     InfoLevel,\n\t\t\tincreaseLevel: DebugLevel,\n\t\t\twantErr:       true,\n\t\t},\n\t\t{\n\t\t\tcoreLevel:     InfoLevel,\n\t\t\tincreaseLevel: InfoLevel,\n\t\t},\n\t\t{\n\t\t\tcoreLevel:     InfoLevel,\n\t\t\tincreaseLevel: ErrorLevel,\n\t\t},\n\t\t{\n\t\t\tcoreLevel:     InfoLevel,\n\t\t\tincreaseLevel: ErrorLevel,\n\t\t\twith:          []Field{zap.String(\"k\", \"v\")},\n\t\t},\n\t\t{\n\t\t\tcoreLevel:     ErrorLevel,\n\t\t\tincreaseLevel: DebugLevel,\n\t\t\twantErr:       true,\n\t\t},\n\t\t{\n\t\t\tcoreLevel:     ErrorLevel,\n\t\t\tincreaseLevel: InfoLevel,\n\t\t\twantErr:       true,\n\t\t},\n\t\t{\n\t\t\tcoreLevel:     ErrorLevel,\n\t\t\tincreaseLevel: WarnLevel,\n\t\t\twantErr:       true,\n\t\t},\n\t\t{\n\t\t\tcoreLevel:     ErrorLevel,\n\t\t\tincreaseLevel: PanicLevel,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tmsg := fmt.Sprintf(\"increase %v to %v\", tt.coreLevel, tt.increaseLevel)\n\t\tt.Run(msg, func(t *testing.T) {\n\t\t\tlogger, logs := observer.New(tt.coreLevel)\n\n\t\t\t// sanity check\n\t\t\trequire.Equal(t, tt.coreLevel, LevelOf(logger), \"Original logger has the wrong level\")\n\n\t\t\tfilteredLogger, err := NewIncreaseLevelCore(logger, tt.increaseLevel)\n\t\t\tif tt.wantErr {\n\t\t\t\tassert.ErrorContains(t, err, \"invalid increase level\")\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif len(tt.with) > 0 {\n\t\t\t\tfilteredLogger = filteredLogger.With(tt.with)\n\t\t\t}\n\n\t\t\trequire.NoError(t, err)\n\n\t\t\tt.Run(\"LevelOf\", func(t *testing.T) {\n\t\t\t\tassert.Equal(t, tt.increaseLevel, LevelOf(filteredLogger), \"Filtered logger has the wrong level\")\n\t\t\t})\n\n\t\t\tfor l := DebugLevel; l <= FatalLevel; l++ {\n\t\t\t\tenabled := filteredLogger.Enabled(l)\n\t\t\t\tentry := Entry{Level: l}\n\t\t\t\tce := filteredLogger.Check(entry, nil)\n\t\t\t\tce.Write()\n\t\t\t\tentries := logs.TakeAll()\n\n\t\t\t\tif l >= tt.increaseLevel {\n\t\t\t\t\tassert.True(t, enabled, \"expect %v to be enabled\", l)\n\t\t\t\t\tassert.NotNil(t, ce, \"expect non-nil Check\")\n\t\t\t\t\tassert.NotEmpty(t, entries, \"Expect log to be written\")\n\t\t\t\t} else {\n\t\t\t\t\tassert.False(t, enabled, \"expect %v to be disabled\", l)\n\t\t\t\t\tassert.Nil(t, ce, \"expect nil Check\")\n\t\t\t\t\tassert.Empty(t, entries, \"No logs should have been written\")\n\t\t\t\t}\n\n\t\t\t\t// Write should always log the entry as per the Core interface\n\t\t\t\trequire.NoError(t, filteredLogger.Write(entry, nil), \"Write failed\")\n\t\t\t\trequire.NoError(t, filteredLogger.Sync(), \"Sync failed\")\n\t\t\t\tassert.NotEmpty(t, logs.TakeAll(), \"Write should always log\")\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "zapcore/json_encoder.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport (\n\t\"encoding/base64\"\n\t\"math\"\n\t\"time\"\n\t\"unicode/utf8\"\n\n\t\"go.uber.org/zap/buffer\"\n\t\"go.uber.org/zap/internal/bufferpool\"\n\t\"go.uber.org/zap/internal/pool\"\n)\n\n// For JSON-escaping; see jsonEncoder.safeAddString below.\nconst _hex = \"0123456789abcdef\"\n\nvar _jsonPool = pool.New(func() *jsonEncoder {\n\treturn &jsonEncoder{}\n})\n\nfunc putJSONEncoder(enc *jsonEncoder) {\n\tif enc.reflectBuf != nil {\n\t\tenc.reflectBuf.Free()\n\t}\n\tenc.EncoderConfig = nil\n\tenc.buf = nil\n\tenc.spaced = false\n\tenc.openNamespaces = 0\n\tenc.reflectBuf = nil\n\tenc.reflectEnc = nil\n\t_jsonPool.Put(enc)\n}\n\ntype jsonEncoder struct {\n\t*EncoderConfig\n\tbuf            *buffer.Buffer\n\tspaced         bool // include spaces after colons and commas\n\topenNamespaces int\n\n\t// for encoding generic values by reflection\n\treflectBuf *buffer.Buffer\n\treflectEnc ReflectedEncoder\n}\n\n// NewJSONEncoder creates a fast, low-allocation JSON encoder. The encoder\n// appropriately escapes all field keys and values.\n//\n// Note that the encoder doesn't deduplicate keys, so it's possible to produce\n// a message like\n//\n//\t{\"foo\":\"bar\",\"foo\":\"baz\"}\n//\n// This is permitted by the JSON specification, but not encouraged. Many\n// libraries will ignore duplicate key-value pairs (typically keeping the last\n// pair) when unmarshaling, but users should attempt to avoid adding duplicate\n// keys.\nfunc NewJSONEncoder(cfg EncoderConfig) Encoder {\n\treturn newJSONEncoder(cfg, false)\n}\n\nfunc newJSONEncoder(cfg EncoderConfig, spaced bool) *jsonEncoder {\n\tif cfg.SkipLineEnding {\n\t\tcfg.LineEnding = \"\"\n\t} else if cfg.LineEnding == \"\" {\n\t\tcfg.LineEnding = DefaultLineEnding\n\t}\n\n\t// If no EncoderConfig.NewReflectedEncoder is provided by the user, then use default\n\tif cfg.NewReflectedEncoder == nil {\n\t\tcfg.NewReflectedEncoder = defaultReflectedEncoder\n\t}\n\n\treturn &jsonEncoder{\n\t\tEncoderConfig: &cfg,\n\t\tbuf:           bufferpool.Get(),\n\t\tspaced:        spaced,\n\t}\n}\n\nfunc (enc *jsonEncoder) AddArray(key string, arr ArrayMarshaler) error {\n\tenc.addKey(key)\n\treturn enc.AppendArray(arr)\n}\n\nfunc (enc *jsonEncoder) AddObject(key string, obj ObjectMarshaler) error {\n\tenc.addKey(key)\n\treturn enc.AppendObject(obj)\n}\n\nfunc (enc *jsonEncoder) AddBinary(key string, val []byte) {\n\tenc.AddString(key, base64.StdEncoding.EncodeToString(val))\n}\n\nfunc (enc *jsonEncoder) AddByteString(key string, val []byte) {\n\tenc.addKey(key)\n\tenc.AppendByteString(val)\n}\n\nfunc (enc *jsonEncoder) AddBool(key string, val bool) {\n\tenc.addKey(key)\n\tenc.AppendBool(val)\n}\n\nfunc (enc *jsonEncoder) AddComplex128(key string, val complex128) {\n\tenc.addKey(key)\n\tenc.AppendComplex128(val)\n}\n\nfunc (enc *jsonEncoder) AddComplex64(key string, val complex64) {\n\tenc.addKey(key)\n\tenc.AppendComplex64(val)\n}\n\nfunc (enc *jsonEncoder) AddDuration(key string, val time.Duration) {\n\tenc.addKey(key)\n\tenc.AppendDuration(val)\n}\n\nfunc (enc *jsonEncoder) AddFloat64(key string, val float64) {\n\tenc.addKey(key)\n\tenc.AppendFloat64(val)\n}\n\nfunc (enc *jsonEncoder) AddFloat32(key string, val float32) {\n\tenc.addKey(key)\n\tenc.AppendFloat32(val)\n}\n\nfunc (enc *jsonEncoder) AddInt64(key string, val int64) {\n\tenc.addKey(key)\n\tenc.AppendInt64(val)\n}\n\nfunc (enc *jsonEncoder) resetReflectBuf() {\n\tif enc.reflectBuf == nil {\n\t\tenc.reflectBuf = bufferpool.Get()\n\t\tenc.reflectEnc = enc.NewReflectedEncoder(enc.reflectBuf)\n\t} else {\n\t\tenc.reflectBuf.Reset()\n\t}\n}\n\nvar nullLiteralBytes = []byte(\"null\")\n\n// Only invoke the standard JSON encoder if there is actually something to\n// encode; otherwise write JSON null literal directly.\nfunc (enc *jsonEncoder) encodeReflected(obj interface{}) ([]byte, error) {\n\tif obj == nil {\n\t\treturn nullLiteralBytes, nil\n\t}\n\tenc.resetReflectBuf()\n\tif err := enc.reflectEnc.Encode(obj); err != nil {\n\t\treturn nil, err\n\t}\n\tenc.reflectBuf.TrimNewline()\n\treturn enc.reflectBuf.Bytes(), nil\n}\n\nfunc (enc *jsonEncoder) AddReflected(key string, obj interface{}) error {\n\tvalueBytes, err := enc.encodeReflected(obj)\n\tif err != nil {\n\t\treturn err\n\t}\n\tenc.addKey(key)\n\t_, err = enc.buf.Write(valueBytes)\n\treturn err\n}\n\nfunc (enc *jsonEncoder) OpenNamespace(key string) {\n\tenc.addKey(key)\n\tenc.buf.AppendByte('{')\n\tenc.openNamespaces++\n}\n\nfunc (enc *jsonEncoder) AddString(key, val string) {\n\tenc.addKey(key)\n\tenc.AppendString(val)\n}\n\nfunc (enc *jsonEncoder) AddTime(key string, val time.Time) {\n\tenc.addKey(key)\n\tenc.AppendTime(val)\n}\n\nfunc (enc *jsonEncoder) AddUint64(key string, val uint64) {\n\tenc.addKey(key)\n\tenc.AppendUint64(val)\n}\n\nfunc (enc *jsonEncoder) AppendArray(arr ArrayMarshaler) error {\n\tenc.addElementSeparator()\n\tenc.buf.AppendByte('[')\n\terr := arr.MarshalLogArray(enc)\n\tenc.buf.AppendByte(']')\n\treturn err\n}\n\nfunc (enc *jsonEncoder) AppendObject(obj ObjectMarshaler) error {\n\t// Close ONLY new openNamespaces that are created during\n\t// AppendObject().\n\told := enc.openNamespaces\n\tenc.openNamespaces = 0\n\tenc.addElementSeparator()\n\tenc.buf.AppendByte('{')\n\terr := obj.MarshalLogObject(enc)\n\tenc.buf.AppendByte('}')\n\tenc.closeOpenNamespaces()\n\tenc.openNamespaces = old\n\treturn err\n}\n\nfunc (enc *jsonEncoder) AppendBool(val bool) {\n\tenc.addElementSeparator()\n\tenc.buf.AppendBool(val)\n}\n\nfunc (enc *jsonEncoder) AppendByteString(val []byte) {\n\tenc.addElementSeparator()\n\tenc.buf.AppendByte('\"')\n\tenc.safeAddByteString(val)\n\tenc.buf.AppendByte('\"')\n}\n\n// appendComplex appends the encoded form of the provided complex128 value.\n// precision specifies the encoding precision for the real and imaginary\n// components of the complex number.\nfunc (enc *jsonEncoder) appendComplex(val complex128, precision int) {\n\tenc.addElementSeparator()\n\t// Cast to a platform-independent, fixed-size type.\n\tr, i := float64(real(val)), float64(imag(val))\n\tenc.buf.AppendByte('\"')\n\t// Because we're always in a quoted string, we can use strconv without\n\t// special-casing NaN and +/-Inf.\n\tenc.buf.AppendFloat(r, precision)\n\t// If imaginary part is less than 0, minus (-) sign is added by default\n\t// by AppendFloat.\n\tif i >= 0 {\n\t\tenc.buf.AppendByte('+')\n\t}\n\tenc.buf.AppendFloat(i, precision)\n\tenc.buf.AppendByte('i')\n\tenc.buf.AppendByte('\"')\n}\n\nfunc (enc *jsonEncoder) AppendDuration(val time.Duration) {\n\tcur := enc.buf.Len()\n\tif e := enc.EncodeDuration; e != nil {\n\t\te(val, enc)\n\t}\n\tif cur == enc.buf.Len() {\n\t\t// User-supplied EncodeDuration is a no-op. Fall back to nanoseconds to keep\n\t\t// JSON valid.\n\t\tenc.AppendInt64(int64(val))\n\t}\n}\n\nfunc (enc *jsonEncoder) AppendInt64(val int64) {\n\tenc.addElementSeparator()\n\tenc.buf.AppendInt(val)\n}\n\nfunc (enc *jsonEncoder) AppendReflected(val interface{}) error {\n\tvalueBytes, err := enc.encodeReflected(val)\n\tif err != nil {\n\t\treturn err\n\t}\n\tenc.addElementSeparator()\n\t_, err = enc.buf.Write(valueBytes)\n\treturn err\n}\n\nfunc (enc *jsonEncoder) AppendString(val string) {\n\tenc.addElementSeparator()\n\tenc.buf.AppendByte('\"')\n\tenc.safeAddString(val)\n\tenc.buf.AppendByte('\"')\n}\n\nfunc (enc *jsonEncoder) AppendTimeLayout(time time.Time, layout string) {\n\tenc.addElementSeparator()\n\tenc.buf.AppendByte('\"')\n\tenc.buf.AppendTime(time, layout)\n\tenc.buf.AppendByte('\"')\n}\n\nfunc (enc *jsonEncoder) AppendTime(val time.Time) {\n\tcur := enc.buf.Len()\n\tif e := enc.EncodeTime; e != nil {\n\t\te(val, enc)\n\t}\n\tif cur == enc.buf.Len() {\n\t\t// User-supplied EncodeTime is a no-op. Fall back to nanos since epoch to keep\n\t\t// output JSON valid.\n\t\tenc.AppendInt64(val.UnixNano())\n\t}\n}\n\nfunc (enc *jsonEncoder) AppendUint64(val uint64) {\n\tenc.addElementSeparator()\n\tenc.buf.AppendUint(val)\n}\n\nfunc (enc *jsonEncoder) AddInt(k string, v int)         { enc.AddInt64(k, int64(v)) }\nfunc (enc *jsonEncoder) AddInt32(k string, v int32)     { enc.AddInt64(k, int64(v)) }\nfunc (enc *jsonEncoder) AddInt16(k string, v int16)     { enc.AddInt64(k, int64(v)) }\nfunc (enc *jsonEncoder) AddInt8(k string, v int8)       { enc.AddInt64(k, int64(v)) }\nfunc (enc *jsonEncoder) AddUint(k string, v uint)       { enc.AddUint64(k, uint64(v)) }\nfunc (enc *jsonEncoder) AddUint32(k string, v uint32)   { enc.AddUint64(k, uint64(v)) }\nfunc (enc *jsonEncoder) AddUint16(k string, v uint16)   { enc.AddUint64(k, uint64(v)) }\nfunc (enc *jsonEncoder) AddUint8(k string, v uint8)     { enc.AddUint64(k, uint64(v)) }\nfunc (enc *jsonEncoder) AddUintptr(k string, v uintptr) { enc.AddUint64(k, uint64(v)) }\nfunc (enc *jsonEncoder) AppendComplex64(v complex64)    { enc.appendComplex(complex128(v), 32) }\nfunc (enc *jsonEncoder) AppendComplex128(v complex128)  { enc.appendComplex(complex128(v), 64) }\nfunc (enc *jsonEncoder) AppendFloat64(v float64)        { enc.appendFloat(v, 64) }\nfunc (enc *jsonEncoder) AppendFloat32(v float32)        { enc.appendFloat(float64(v), 32) }\nfunc (enc *jsonEncoder) AppendInt(v int)                { enc.AppendInt64(int64(v)) }\nfunc (enc *jsonEncoder) AppendInt32(v int32)            { enc.AppendInt64(int64(v)) }\nfunc (enc *jsonEncoder) AppendInt16(v int16)            { enc.AppendInt64(int64(v)) }\nfunc (enc *jsonEncoder) AppendInt8(v int8)              { enc.AppendInt64(int64(v)) }\nfunc (enc *jsonEncoder) AppendUint(v uint)              { enc.AppendUint64(uint64(v)) }\nfunc (enc *jsonEncoder) AppendUint32(v uint32)          { enc.AppendUint64(uint64(v)) }\nfunc (enc *jsonEncoder) AppendUint16(v uint16)          { enc.AppendUint64(uint64(v)) }\nfunc (enc *jsonEncoder) AppendUint8(v uint8)            { enc.AppendUint64(uint64(v)) }\nfunc (enc *jsonEncoder) AppendUintptr(v uintptr)        { enc.AppendUint64(uint64(v)) }\n\nfunc (enc *jsonEncoder) Clone() Encoder {\n\tclone := enc.clone()\n\tclone.buf.Write(enc.buf.Bytes())\n\treturn clone\n}\n\nfunc (enc *jsonEncoder) clone() *jsonEncoder {\n\tclone := _jsonPool.Get()\n\tclone.EncoderConfig = enc.EncoderConfig\n\tclone.spaced = enc.spaced\n\tclone.openNamespaces = enc.openNamespaces\n\tclone.buf = bufferpool.Get()\n\treturn clone\n}\n\nfunc (enc *jsonEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, error) {\n\tfinal := enc.clone()\n\tfinal.buf.AppendByte('{')\n\n\tif final.LevelKey != \"\" && final.EncodeLevel != nil {\n\t\tfinal.addKey(final.LevelKey)\n\t\tcur := final.buf.Len()\n\t\tfinal.EncodeLevel(ent.Level, final)\n\t\tif cur == final.buf.Len() {\n\t\t\t// User-supplied EncodeLevel was a no-op. Fall back to strings to keep\n\t\t\t// output JSON valid.\n\t\t\tfinal.AppendString(ent.Level.String())\n\t\t}\n\t}\n\tif final.TimeKey != \"\" && !ent.Time.IsZero() {\n\t\tfinal.AddTime(final.TimeKey, ent.Time)\n\t}\n\tif ent.LoggerName != \"\" && final.NameKey != \"\" {\n\t\tfinal.addKey(final.NameKey)\n\t\tcur := final.buf.Len()\n\t\tnameEncoder := final.EncodeName\n\n\t\t// if no name encoder provided, fall back to FullNameEncoder for backwards\n\t\t// compatibility\n\t\tif nameEncoder == nil {\n\t\t\tnameEncoder = FullNameEncoder\n\t\t}\n\n\t\tnameEncoder(ent.LoggerName, final)\n\t\tif cur == final.buf.Len() {\n\t\t\t// User-supplied EncodeName was a no-op. Fall back to strings to\n\t\t\t// keep output JSON valid.\n\t\t\tfinal.AppendString(ent.LoggerName)\n\t\t}\n\t}\n\tif ent.Caller.Defined {\n\t\tif final.CallerKey != \"\" {\n\t\t\tfinal.addKey(final.CallerKey)\n\t\t\tcur := final.buf.Len()\n\t\t\tfinal.EncodeCaller(ent.Caller, final)\n\t\t\tif cur == final.buf.Len() {\n\t\t\t\t// User-supplied EncodeCaller was a no-op. Fall back to strings to\n\t\t\t\t// keep output JSON valid.\n\t\t\t\tfinal.AppendString(ent.Caller.String())\n\t\t\t}\n\t\t}\n\t\tif final.FunctionKey != \"\" {\n\t\t\tfinal.addKey(final.FunctionKey)\n\t\t\tfinal.AppendString(ent.Caller.Function)\n\t\t}\n\t}\n\tif final.MessageKey != \"\" {\n\t\tfinal.addKey(enc.MessageKey)\n\t\tfinal.AppendString(ent.Message)\n\t}\n\tif enc.buf.Len() > 0 {\n\t\tfinal.addElementSeparator()\n\t\tfinal.buf.Write(enc.buf.Bytes())\n\t}\n\taddFields(final, fields)\n\tfinal.closeOpenNamespaces()\n\tif ent.Stack != \"\" && final.StacktraceKey != \"\" {\n\t\tfinal.AddString(final.StacktraceKey, ent.Stack)\n\t}\n\tfinal.buf.AppendByte('}')\n\tfinal.buf.AppendString(final.LineEnding)\n\n\tret := final.buf\n\tputJSONEncoder(final)\n\treturn ret, nil\n}\n\nfunc (enc *jsonEncoder) truncate() {\n\tenc.buf.Reset()\n}\n\nfunc (enc *jsonEncoder) closeOpenNamespaces() {\n\tfor i := 0; i < enc.openNamespaces; i++ {\n\t\tenc.buf.AppendByte('}')\n\t}\n\tenc.openNamespaces = 0\n}\n\nfunc (enc *jsonEncoder) addKey(key string) {\n\tenc.addElementSeparator()\n\tenc.buf.AppendByte('\"')\n\tenc.safeAddString(key)\n\tenc.buf.AppendByte('\"')\n\tenc.buf.AppendByte(':')\n\tif enc.spaced {\n\t\tenc.buf.AppendByte(' ')\n\t}\n}\n\nfunc (enc *jsonEncoder) addElementSeparator() {\n\tlast := enc.buf.Len() - 1\n\tif last < 0 {\n\t\treturn\n\t}\n\tswitch enc.buf.Bytes()[last] {\n\tcase '{', '[', ':', ',', ' ':\n\t\treturn\n\tdefault:\n\t\tenc.buf.AppendByte(',')\n\t\tif enc.spaced {\n\t\t\tenc.buf.AppendByte(' ')\n\t\t}\n\t}\n}\n\nfunc (enc *jsonEncoder) appendFloat(val float64, bitSize int) {\n\tenc.addElementSeparator()\n\tswitch {\n\tcase math.IsNaN(val):\n\t\tenc.buf.AppendString(`\"NaN\"`)\n\tcase math.IsInf(val, 1):\n\t\tenc.buf.AppendString(`\"+Inf\"`)\n\tcase math.IsInf(val, -1):\n\t\tenc.buf.AppendString(`\"-Inf\"`)\n\tdefault:\n\t\tenc.buf.AppendFloat(val, bitSize)\n\t}\n}\n\n// safeAddString JSON-escapes a string and appends it to the internal buffer.\n// Unlike the standard library's encoder, it doesn't attempt to protect the\n// user from browser vulnerabilities or JSONP-related problems.\nfunc (enc *jsonEncoder) safeAddString(s string) {\n\tsafeAppendStringLike(\n\t\t(*buffer.Buffer).AppendString,\n\t\tutf8.DecodeRuneInString,\n\t\tenc.buf,\n\t\ts,\n\t)\n}\n\n// safeAddByteString is no-alloc equivalent of safeAddString(string(s)) for s []byte.\nfunc (enc *jsonEncoder) safeAddByteString(s []byte) {\n\tsafeAppendStringLike(\n\t\t(*buffer.Buffer).AppendBytes,\n\t\tutf8.DecodeRune,\n\t\tenc.buf,\n\t\ts,\n\t)\n}\n\n// safeAppendStringLike is a generic implementation of safeAddString and safeAddByteString.\n// It appends a string or byte slice to the buffer, escaping all special characters.\nfunc safeAppendStringLike[S []byte | string](\n\t// appendTo appends this string-like object to the buffer.\n\tappendTo func(*buffer.Buffer, S),\n\t// decodeRune decodes the next rune from the string-like object\n\t// and returns its value and width in bytes.\n\tdecodeRune func(S) (rune, int),\n\tbuf *buffer.Buffer,\n\ts S,\n) {\n\t// The encoding logic below works by skipping over characters\n\t// that can be safely copied as-is,\n\t// until a character is found that needs special handling.\n\t// At that point, we copy everything we've seen so far,\n\t// and then handle that special character.\n\t//\n\t// last is the index of the last byte that was copied to the buffer.\n\tlast := 0\n\tfor i := 0; i < len(s); {\n\t\tif s[i] >= utf8.RuneSelf {\n\t\t\t// Character >= RuneSelf may be part of a multi-byte rune.\n\t\t\t// They need to be decoded before we can decide how to handle them.\n\t\t\tr, size := decodeRune(s[i:])\n\t\t\tif r != utf8.RuneError || size != 1 {\n\t\t\t\t// No special handling required.\n\t\t\t\t// Skip over this rune and continue.\n\t\t\t\ti += size\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Invalid UTF-8 sequence.\n\t\t\t// Replace it with the Unicode replacement character.\n\t\t\tappendTo(buf, s[last:i])\n\t\t\tbuf.AppendString(`\\ufffd`)\n\n\t\t\ti++\n\t\t\tlast = i\n\t\t} else {\n\t\t\t// Character < RuneSelf is a single-byte UTF-8 rune.\n\t\t\tif s[i] >= 0x20 && s[i] != '\\\\' && s[i] != '\"' {\n\t\t\t\t// No escaping necessary.\n\t\t\t\t// Skip over this character and continue.\n\t\t\t\ti++\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// This character needs to be escaped.\n\t\t\tappendTo(buf, s[last:i])\n\t\t\tswitch s[i] {\n\t\t\tcase '\\\\', '\"':\n\t\t\t\tbuf.AppendByte('\\\\')\n\t\t\t\tbuf.AppendByte(s[i])\n\t\t\tcase '\\n':\n\t\t\t\tbuf.AppendByte('\\\\')\n\t\t\t\tbuf.AppendByte('n')\n\t\t\tcase '\\r':\n\t\t\t\tbuf.AppendByte('\\\\')\n\t\t\t\tbuf.AppendByte('r')\n\t\t\tcase '\\t':\n\t\t\t\tbuf.AppendByte('\\\\')\n\t\t\t\tbuf.AppendByte('t')\n\t\t\tdefault:\n\t\t\t\t// Encode bytes < 0x20, except for the escape sequences above.\n\t\t\t\tbuf.AppendString(`\\u00`)\n\t\t\t\tbuf.AppendByte(_hex[s[i]>>4])\n\t\t\t\tbuf.AppendByte(_hex[s[i]&0xF])\n\t\t\t}\n\n\t\t\ti++\n\t\t\tlast = i\n\t\t}\n\t}\n\n\t// add remaining\n\tappendTo(buf, s[last:])\n}\n"
  },
  {
    "path": "zapcore/json_encoder_bench_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore_test\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t//revive:disable:dot-imports\n\t. \"go.uber.org/zap/zapcore\"\n)\n\nfunc BenchmarkJSONLogMarshalerFunc(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\tenc := NewJSONEncoder(testEncoderConfig())\n\t\terr := enc.AddObject(\"nested\", ObjectMarshalerFunc(func(enc ObjectEncoder) error {\n\t\t\tenc.AddInt64(\"i\", int64(i))\n\t\t\treturn nil\n\t\t}))\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkZapJSONFloat32AndComplex64(b *testing.B) {\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tenc := NewJSONEncoder(testEncoderConfig())\n\t\t\tenc.AddFloat32(\"float32\", 3.14)\n\t\t\tenc.AddComplex64(\"complex64\", 2.71+3.14i)\n\t\t}\n\t})\n}\n\nconst _sliceSize = 5000\n\ntype StringSlice []string\n\nfunc (s StringSlice) MarshalLogArray(encoder ArrayEncoder) error {\n\tfor _, str := range s {\n\t\tencoder.AppendString(str)\n\t}\n\treturn nil\n}\n\nfunc generateStringSlice(n int) StringSlice {\n\toutput := make(StringSlice, 0, n)\n\tfor i := 0; i < n; i++ {\n\t\toutput = append(output, fmt.Sprint(\"00000000-0000-0000-0000-0000000000\", i))\n\t}\n\treturn output\n}\n\nfunc BenchmarkZapJSON(b *testing.B) {\n\tadditional := generateStringSlice(_sliceSize)\n\tb.ResetTimer()\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tenc := NewJSONEncoder(testEncoderConfig())\n\t\t\tenc.AddString(\"str\", \"foo\")\n\t\t\tenc.AddInt64(\"int64-1\", 1)\n\t\t\tenc.AddInt64(\"int64-2\", 2)\n\t\t\tenc.AddFloat64(\"float64\", 1.0)\n\t\t\tenc.AddString(\"string1\", \"\\n\")\n\t\t\tenc.AddString(\"string2\", \"💩\")\n\t\t\tenc.AddString(\"string3\", \"🤔\")\n\t\t\tenc.AddString(\"string4\", \"🙊\")\n\t\t\tenc.AddBool(\"bool\", true)\n\t\t\t_ = enc.AddArray(\"test\", additional)\n\t\t\tbuf, _ := enc.EncodeEntry(Entry{\n\t\t\t\tMessage: \"fake\",\n\t\t\t\tLevel:   DebugLevel,\n\t\t\t}, nil)\n\t\t\tbuf.Free()\n\t\t}\n\t})\n}\n\nfunc BenchmarkStandardJSON(b *testing.B) {\n\trecord := struct {\n\t\tLevel      string                 `json:\"level\"`\n\t\tMessage    string                 `json:\"msg\"`\n\t\tTime       time.Time              `json:\"ts\"`\n\t\tFields     map[string]interface{} `json:\"fields\"`\n\t\tAdditional StringSlice\n\t}{\n\t\tLevel:   \"debug\",\n\t\tMessage: \"fake\",\n\t\tTime:    time.Unix(0, 0),\n\t\tFields: map[string]interface{}{\n\t\t\t\"str\":     \"foo\",\n\t\t\t\"int64-1\": int64(1),\n\t\t\t\"int64-2\": int64(1),\n\t\t\t\"float64\": float64(1.0),\n\t\t\t\"string1\": \"\\n\",\n\t\t\t\"string2\": \"💩\",\n\t\t\t\"string3\": \"🤔\",\n\t\t\t\"string4\": \"🙊\",\n\t\t\t\"bool\":    true,\n\t\t},\n\t\tAdditional: generateStringSlice(_sliceSize),\n\t}\n\tb.ResetTimer()\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tif _, err := json.Marshal(record); err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "zapcore/json_encoder_impl_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"math\"\n\t\"math/rand\"\n\t\"reflect\"\n\t\"testing\"\n\t\"testing/quick\"\n\t\"time\"\n\t\"unicode/utf8\"\n\n\t\"go.uber.org/zap/buffer\"\n\t\"go.uber.org/zap/internal/bufferpool\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/multierr\"\n)\n\nvar _defaultEncoderConfig = EncoderConfig{\n\tEncodeTime:     EpochTimeEncoder,\n\tEncodeDuration: SecondsDurationEncoder,\n}\n\nfunc TestJSONClone(t *testing.T) {\n\t// The parent encoder is created with plenty of excess capacity.\n\tparent := &jsonEncoder{buf: bufferpool.Get()}\n\tclone := parent.Clone()\n\n\t// Adding to the parent shouldn't affect the clone, and vice versa.\n\tparent.AddString(\"foo\", \"bar\")\n\tclone.AddString(\"baz\", \"bing\")\n\n\tassertJSON(t, `\"foo\":\"bar\"`, parent)\n\tassertJSON(t, `\"baz\":\"bing\"`, clone.(*jsonEncoder))\n}\n\nfunc TestJSONEscaping(t *testing.T) {\n\tenc := &jsonEncoder{buf: bufferpool.Get()}\n\t// Test all the edge cases of JSON escaping directly.\n\tcases := map[string]string{\n\t\t// ASCII.\n\t\t`foo`: `foo`,\n\t\t// Special-cased characters.\n\t\t`\"`: `\\\"`,\n\t\t`\\`: `\\\\`,\n\t\t// Special-cased characters within everyday ASCII.\n\t\t`foo\"foo`: `foo\\\"foo`,\n\t\t\"foo\\n\":   `foo\\n`,\n\t\t// Special-cased control characters.\n\t\t\"\\n\": `\\n`,\n\t\t\"\\r\": `\\r`,\n\t\t\"\\t\": `\\t`,\n\t\t// \\b and \\f are sometimes backslash-escaped, but this representation is also\n\t\t// conformant.\n\t\t\"\\b\": `\\u0008`,\n\t\t\"\\f\": `\\u000c`,\n\t\t// The standard lib special-cases angle brackets and ampersands by default,\n\t\t// because it wants to protect users from browser exploits. In a logging\n\t\t// context, we shouldn't special-case these characters.\n\t\t\"<\": \"<\",\n\t\t\">\": \">\",\n\t\t\"&\": \"&\",\n\t\t// ASCII bell - not special-cased.\n\t\tstring(byte(0x07)): `\\u0007`,\n\t\t// Astral-plane unicode.\n\t\t`☃`: `☃`,\n\t\t// Decodes to (RuneError, 1)\n\t\t\"\\xed\\xa0\\x80\":    `\\ufffd\\ufffd\\ufffd`,\n\t\t\"foo\\xed\\xa0\\x80\": `foo\\ufffd\\ufffd\\ufffd`,\n\t}\n\n\tt.Run(\"String\", func(t *testing.T) {\n\t\tfor input, output := range cases {\n\t\t\tenc.truncate()\n\t\t\tenc.safeAddString(input)\n\t\t\tassertJSON(t, output, enc)\n\t\t}\n\t})\n\n\tt.Run(\"ByteString\", func(t *testing.T) {\n\t\tfor input, output := range cases {\n\t\t\tenc.truncate()\n\t\t\tenc.safeAddByteString([]byte(input))\n\t\t\tassertJSON(t, output, enc)\n\t\t}\n\t})\n}\n\nfunc TestJSONEncoderObjectFields(t *testing.T) {\n\ttests := []struct {\n\t\tdesc     string\n\t\texpected string\n\t\tf        func(Encoder)\n\t}{\n\t\t{\"binary\", `\"k\":\"YWIxMg==\"`, func(e Encoder) { e.AddBinary(\"k\", []byte(\"ab12\")) }},\n\t\t{\"bool\", `\"k\\\\\":true`, func(e Encoder) { e.AddBool(`k\\`, true) }}, // test key escaping once\n\t\t{\"bool\", `\"k\":true`, func(e Encoder) { e.AddBool(\"k\", true) }},\n\t\t{\"bool\", `\"k\":false`, func(e Encoder) { e.AddBool(\"k\", false) }},\n\t\t{\"byteString\", `\"k\":\"v\\\\\"`, func(e Encoder) { e.AddByteString(`k`, []byte(`v\\`)) }},\n\t\t{\"byteString\", `\"k\":\"v\"`, func(e Encoder) { e.AddByteString(\"k\", []byte(\"v\")) }},\n\t\t{\"byteString\", `\"k\":\"\"`, func(e Encoder) { e.AddByteString(\"k\", []byte{}) }},\n\t\t{\"byteString\", `\"k\":\"\"`, func(e Encoder) { e.AddByteString(\"k\", nil) }},\n\t\t{\"complex128\", `\"k\":\"1+2i\"`, func(e Encoder) { e.AddComplex128(\"k\", 1+2i) }},\n\t\t{\"complex128/negative_i\", `\"k\":\"1-2i\"`, func(e Encoder) { e.AddComplex128(\"k\", 1-2i) }},\n\t\t{\"complex64\", `\"k\":\"1+2i\"`, func(e Encoder) { e.AddComplex64(\"k\", 1+2i) }},\n\t\t{\"complex64/negative_i\", `\"k\":\"1-2i\"`, func(e Encoder) { e.AddComplex64(\"k\", 1-2i) }},\n\t\t{\"complex64\", `\"k\":\"2.71+3.14i\"`, func(e Encoder) { e.AddComplex64(\"k\", 2.71+3.14i) }},\n\t\t{\"duration\", `\"k\":0.000000001`, func(e Encoder) { e.AddDuration(\"k\", 1) }},\n\t\t{\"duration/negative\", `\"k\":-0.000000001`, func(e Encoder) { e.AddDuration(\"k\", -1) }},\n\t\t{\"float64\", `\"k\":1`, func(e Encoder) { e.AddFloat64(\"k\", 1.0) }},\n\t\t{\"float64\", `\"k\":10000000000`, func(e Encoder) { e.AddFloat64(\"k\", 1e10) }},\n\t\t{\"float64\", `\"k\":\"NaN\"`, func(e Encoder) { e.AddFloat64(\"k\", math.NaN()) }},\n\t\t{\"float64\", `\"k\":\"+Inf\"`, func(e Encoder) { e.AddFloat64(\"k\", math.Inf(1)) }},\n\t\t{\"float64\", `\"k\":\"-Inf\"`, func(e Encoder) { e.AddFloat64(\"k\", math.Inf(-1)) }},\n\t\t{\"float64/pi\", `\"k\":3.141592653589793`, func(e Encoder) { e.AddFloat64(\"k\", math.Pi) }},\n\t\t{\"float32\", `\"k\":1`, func(e Encoder) { e.AddFloat32(\"k\", 1.0) }},\n\t\t{\"float32\", `\"k\":2.71`, func(e Encoder) { e.AddFloat32(\"k\", 2.71) }},\n\t\t{\"float32\", `\"k\":0.1`, func(e Encoder) { e.AddFloat32(\"k\", 0.1) }},\n\t\t{\"float32\", `\"k\":10000000000`, func(e Encoder) { e.AddFloat32(\"k\", 1e10) }},\n\t\t{\"float32\", `\"k\":\"NaN\"`, func(e Encoder) { e.AddFloat32(\"k\", float32(math.NaN())) }},\n\t\t{\"float32\", `\"k\":\"+Inf\"`, func(e Encoder) { e.AddFloat32(\"k\", float32(math.Inf(1))) }},\n\t\t{\"float32\", `\"k\":\"-Inf\"`, func(e Encoder) { e.AddFloat32(\"k\", float32(math.Inf(-1))) }},\n\t\t{\"float32/pi\", `\"k\":3.1415927`, func(e Encoder) { e.AddFloat32(\"k\", math.Pi) }},\n\t\t{\"int\", `\"k\":42`, func(e Encoder) { e.AddInt(\"k\", 42) }},\n\t\t{\"int64\", `\"k\":42`, func(e Encoder) { e.AddInt64(\"k\", 42) }},\n\t\t{\"int64/min\", `\"k\":-9223372036854775808`, func(e Encoder) { e.AddInt64(\"k\", math.MinInt64) }},\n\t\t{\"int64/max\", `\"k\":9223372036854775807`, func(e Encoder) { e.AddInt64(\"k\", math.MaxInt64) }},\n\t\t{\"int32\", `\"k\":42`, func(e Encoder) { e.AddInt32(\"k\", 42) }},\n\t\t{\"int32/min\", `\"k\":-2147483648`, func(e Encoder) { e.AddInt32(\"k\", math.MinInt32) }},\n\t\t{\"int32/max\", `\"k\":2147483647`, func(e Encoder) { e.AddInt32(\"k\", math.MaxInt32) }},\n\t\t{\"int16\", `\"k\":42`, func(e Encoder) { e.AddInt16(\"k\", 42) }},\n\t\t{\"int16/min\", `\"k\":-32768`, func(e Encoder) { e.AddInt16(\"k\", math.MinInt16) }},\n\t\t{\"int16/max\", `\"k\":32767`, func(e Encoder) { e.AddInt16(\"k\", math.MaxInt16) }},\n\t\t{\"int8\", `\"k\":42`, func(e Encoder) { e.AddInt8(\"k\", 42) }},\n\t\t{\"int8/min\", `\"k\":-128`, func(e Encoder) { e.AddInt8(\"k\", math.MinInt8) }},\n\t\t{\"int8/max\", `\"k\":127`, func(e Encoder) { e.AddInt8(\"k\", math.MaxInt8) }},\n\t\t{\"string\", `\"k\":\"v\\\\\"`, func(e Encoder) { e.AddString(`k`, `v\\`) }},\n\t\t{\"string\", `\"k\":\"v\"`, func(e Encoder) { e.AddString(\"k\", \"v\") }},\n\t\t{\"string\", `\"k\":\"\"`, func(e Encoder) { e.AddString(\"k\", \"\") }},\n\t\t{\"time\", `\"k\":1`, func(e Encoder) { e.AddTime(\"k\", time.Unix(1, 0)) }},\n\t\t{\"uint\", `\"k\":42`, func(e Encoder) { e.AddUint(\"k\", 42) }},\n\t\t{\"uint64\", `\"k\":42`, func(e Encoder) { e.AddUint64(\"k\", 42) }},\n\t\t{\"uint64/max\", `\"k\":18446744073709551615`, func(e Encoder) { e.AddUint64(\"k\", math.MaxUint64) }},\n\t\t{\"uint32\", `\"k\":42`, func(e Encoder) { e.AddUint32(\"k\", 42) }},\n\t\t{\"uint32/max\", `\"k\":4294967295`, func(e Encoder) { e.AddUint32(\"k\", math.MaxUint32) }},\n\t\t{\"uint16\", `\"k\":42`, func(e Encoder) { e.AddUint16(\"k\", 42) }},\n\t\t{\"uint16/max\", `\"k\":65535`, func(e Encoder) { e.AddUint16(\"k\", math.MaxUint16) }},\n\t\t{\"uint8\", `\"k\":42`, func(e Encoder) { e.AddUint8(\"k\", 42) }},\n\t\t{\"uint8/max\", `\"k\":255`, func(e Encoder) { e.AddUint8(\"k\", math.MaxUint8) }},\n\t\t{\"uintptr\", `\"k\":42`, func(e Encoder) { e.AddUintptr(\"k\", 42) }},\n\t\t{\n\t\t\tdesc:     \"object (success)\",\n\t\t\texpected: `\"k\":{\"loggable\":\"yes\"}`,\n\t\t\tf: func(e Encoder) {\n\t\t\t\tassert.NoError(t, e.AddObject(\"k\", loggable{true}), \"Unexpected error calling MarshalLogObject.\")\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc:     \"object (error)\",\n\t\t\texpected: `\"k\":{}`,\n\t\t\tf: func(e Encoder) {\n\t\t\t\tassert.Error(t, e.AddObject(\"k\", loggable{false}), \"Expected an error calling MarshalLogObject.\")\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc:     \"object (with nested array)\",\n\t\t\texpected: `\"turducken\":{\"ducks\":[{\"in\":\"chicken\"},{\"in\":\"chicken\"}]}`,\n\t\t\tf: func(e Encoder) {\n\t\t\t\tassert.NoError(\n\t\t\t\t\tt,\n\t\t\t\t\te.AddObject(\"turducken\", turducken{}),\n\t\t\t\t\t\"Unexpected error calling MarshalLogObject with nested ObjectMarshalers and ArrayMarshalers.\",\n\t\t\t\t)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc:     \"array (with nested object)\",\n\t\t\texpected: `\"turduckens\":[{\"ducks\":[{\"in\":\"chicken\"},{\"in\":\"chicken\"}]},{\"ducks\":[{\"in\":\"chicken\"},{\"in\":\"chicken\"}]}]`,\n\t\t\tf: func(e Encoder) {\n\t\t\t\tassert.NoError(\n\t\t\t\t\tt,\n\t\t\t\t\te.AddArray(\"turduckens\", turduckens(2)),\n\t\t\t\t\t\"Unexpected error calling MarshalLogObject with nested ObjectMarshalers and ArrayMarshalers.\",\n\t\t\t\t)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc:     \"array (success)\",\n\t\t\texpected: `\"k\":[true]`,\n\t\t\tf: func(e Encoder) {\n\t\t\t\tassert.NoError(t, e.AddArray(`k`, loggable{true}), \"Unexpected error calling MarshalLogArray.\")\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc:     \"array (error)\",\n\t\t\texpected: `\"k\":[]`,\n\t\t\tf: func(e Encoder) {\n\t\t\t\tassert.Error(t, e.AddArray(\"k\", loggable{false}), \"Expected an error calling MarshalLogArray.\")\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc:     \"reflect (success)\",\n\t\t\texpected: `\"k\":{\"escape\":\"<&>\",\"loggable\":\"yes\"}`,\n\t\t\tf: func(e Encoder) {\n\t\t\t\tassert.NoError(t, e.AddReflected(\"k\", map[string]string{\"escape\": \"<&>\", \"loggable\": \"yes\"}), \"Unexpected error JSON-serializing a map.\")\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc:     \"reflect (failure)\",\n\t\t\texpected: \"\",\n\t\t\tf: func(e Encoder) {\n\t\t\t\tassert.Error(t, e.AddReflected(\"k\", noJSON{}), \"Unexpected success JSON-serializing a noJSON.\")\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"namespace\",\n\t\t\t// EncodeEntry is responsible for closing all open namespaces.\n\t\t\texpected: `\"outermost\":{\"outer\":{\"foo\":1,\"inner\":{\"foo\":2,\"innermost\":{`,\n\t\t\tf: func(e Encoder) {\n\t\t\t\te.OpenNamespace(\"outermost\")\n\t\t\t\te.OpenNamespace(\"outer\")\n\t\t\t\te.AddInt(\"foo\", 1)\n\t\t\t\te.OpenNamespace(\"inner\")\n\t\t\t\te.AddInt(\"foo\", 2)\n\t\t\t\te.OpenNamespace(\"innermost\")\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc:     \"object (no nested namespace)\",\n\t\t\texpected: `\"obj\":{\"obj-out\":\"obj-outside-namespace\"},\"not-obj\":\"should-be-outside-obj\"`,\n\t\t\tf: func(e Encoder) {\n\t\t\t\tassert.NoError(t, e.AddObject(\"obj\", maybeNamespace{false}))\n\t\t\t\te.AddString(\"not-obj\", \"should-be-outside-obj\")\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc:     \"object (with nested namespace)\",\n\t\t\texpected: `\"obj\":{\"obj-out\":\"obj-outside-namespace\",\"obj-namespace\":{\"obj-in\":\"obj-inside-namespace\"}},\"not-obj\":\"should-be-outside-obj\"`,\n\t\t\tf: func(e Encoder) {\n\t\t\t\tassert.NoError(t, e.AddObject(\"obj\", maybeNamespace{true}))\n\t\t\t\te.AddString(\"not-obj\", \"should-be-outside-obj\")\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc:     \"multiple open namespaces\",\n\t\t\texpected: `\"k\":{\"foo\":1,\"middle\":{\"foo\":2,\"inner\":{\"foo\":3}}}`,\n\t\t\tf: func(e Encoder) {\n\t\t\t\terr := e.AddObject(\"k\", ObjectMarshalerFunc(func(enc ObjectEncoder) error {\n\t\t\t\t\te.AddInt(\"foo\", 1)\n\t\t\t\t\te.OpenNamespace(\"middle\")\n\t\t\t\t\te.AddInt(\"foo\", 2)\n\t\t\t\t\te.OpenNamespace(\"inner\")\n\t\t\t\t\te.AddInt(\"foo\", 3)\n\t\t\t\t\treturn nil\n\t\t\t\t}))\n\t\t\t\tassert.NoError(t, err)\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tassertOutput(t, _defaultEncoderConfig, tt.expected, tt.f)\n\t\t})\n\t}\n}\n\nfunc TestJSONEncoderTimeFormats(t *testing.T) {\n\tdate := time.Date(2000, time.January, 2, 3, 4, 5, 6, time.UTC)\n\n\tf := func(e Encoder) {\n\t\te.AddTime(\"k\", date)\n\t\terr := e.AddArray(\"a\", ArrayMarshalerFunc(func(enc ArrayEncoder) error {\n\t\t\tenc.AppendTime(date)\n\t\t\treturn nil\n\t\t}))\n\t\tassert.NoError(t, err)\n\t}\n\ttests := []struct {\n\t\tdesc     string\n\t\tcfg      EncoderConfig\n\t\texpected string\n\t}{\n\t\t{\n\t\t\tdesc: \"time.Time ISO8601\",\n\t\t\tcfg: EncoderConfig{\n\t\t\t\tEncodeDuration: NanosDurationEncoder,\n\t\t\t\tEncodeTime:     ISO8601TimeEncoder,\n\t\t\t},\n\t\t\texpected: `\"k\":\"2000-01-02T03:04:05.000Z\",\"a\":[\"2000-01-02T03:04:05.000Z\"]`,\n\t\t},\n\t\t{\n\t\t\tdesc: \"time.Time RFC3339\",\n\t\t\tcfg: EncoderConfig{\n\t\t\t\tEncodeDuration: NanosDurationEncoder,\n\t\t\t\tEncodeTime:     RFC3339TimeEncoder,\n\t\t\t},\n\t\t\texpected: `\"k\":\"2000-01-02T03:04:05Z\",\"a\":[\"2000-01-02T03:04:05Z\"]`,\n\t\t},\n\t\t{\n\t\t\tdesc: \"time.Time RFC3339Nano\",\n\t\t\tcfg: EncoderConfig{\n\t\t\t\tEncodeDuration: NanosDurationEncoder,\n\t\t\t\tEncodeTime:     RFC3339NanoTimeEncoder,\n\t\t\t},\n\t\t\texpected: `\"k\":\"2000-01-02T03:04:05.000000006Z\",\"a\":[\"2000-01-02T03:04:05.000000006Z\"]`,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tassertOutput(t, tt.cfg, tt.expected, f)\n\t\t})\n\t}\n}\n\nfunc TestJSONEncoderArrays(t *testing.T) {\n\ttests := []struct {\n\t\tdesc     string\n\t\texpected string // expect f to be called twice\n\t\tf        func(ArrayEncoder)\n\t}{\n\t\t{\"bool\", `[true,true]`, func(e ArrayEncoder) { e.AppendBool(true) }},\n\t\t{\"byteString\", `[\"k\",\"k\"]`, func(e ArrayEncoder) { e.AppendByteString([]byte(\"k\")) }},\n\t\t{\"byteString\", `[\"k\\\\\",\"k\\\\\"]`, func(e ArrayEncoder) { e.AppendByteString([]byte(`k\\`)) }},\n\t\t{\"complex128\", `[\"1+2i\",\"1+2i\"]`, func(e ArrayEncoder) { e.AppendComplex128(1 + 2i) }},\n\t\t{\"complex64\", `[\"1+2i\",\"1+2i\"]`, func(e ArrayEncoder) { e.AppendComplex64(1 + 2i) }},\n\t\t{\"durations\", `[0.000000002,0.000000002]`, func(e ArrayEncoder) { e.AppendDuration(2) }},\n\t\t{\"float64\", `[3.14,3.14]`, func(e ArrayEncoder) { e.AppendFloat64(3.14) }},\n\t\t{\"float32\", `[3.14,3.14]`, func(e ArrayEncoder) { e.AppendFloat32(3.14) }},\n\t\t{\"int\", `[42,42]`, func(e ArrayEncoder) { e.AppendInt(42) }},\n\t\t{\"int64\", `[42,42]`, func(e ArrayEncoder) { e.AppendInt64(42) }},\n\t\t{\"int32\", `[42,42]`, func(e ArrayEncoder) { e.AppendInt32(42) }},\n\t\t{\"int16\", `[42,42]`, func(e ArrayEncoder) { e.AppendInt16(42) }},\n\t\t{\"int8\", `[42,42]`, func(e ArrayEncoder) { e.AppendInt8(42) }},\n\t\t{\"string\", `[\"k\",\"k\"]`, func(e ArrayEncoder) { e.AppendString(\"k\") }},\n\t\t{\"string\", `[\"k\\\\\",\"k\\\\\"]`, func(e ArrayEncoder) { e.AppendString(`k\\`) }},\n\t\t{\"times\", `[1,1]`, func(e ArrayEncoder) { e.AppendTime(time.Unix(1, 0)) }},\n\t\t{\"uint\", `[42,42]`, func(e ArrayEncoder) { e.AppendUint(42) }},\n\t\t{\"uint64\", `[42,42]`, func(e ArrayEncoder) { e.AppendUint64(42) }},\n\t\t{\"uint32\", `[42,42]`, func(e ArrayEncoder) { e.AppendUint32(42) }},\n\t\t{\"uint16\", `[42,42]`, func(e ArrayEncoder) { e.AppendUint16(42) }},\n\t\t{\"uint8\", `[42,42]`, func(e ArrayEncoder) { e.AppendUint8(42) }},\n\t\t{\"uintptr\", `[42,42]`, func(e ArrayEncoder) { e.AppendUintptr(42) }},\n\t\t{\n\t\t\tdesc:     \"arrays (success)\",\n\t\t\texpected: `[[true],[true]]`,\n\t\t\tf: func(arr ArrayEncoder) {\n\t\t\t\tassert.NoError(t, arr.AppendArray(ArrayMarshalerFunc(func(inner ArrayEncoder) error {\n\t\t\t\t\tinner.AppendBool(true)\n\t\t\t\t\treturn nil\n\t\t\t\t})), \"Unexpected error appending an array.\")\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc:     \"arrays (error)\",\n\t\t\texpected: `[[true],[true]]`,\n\t\t\tf: func(arr ArrayEncoder) {\n\t\t\t\tassert.Error(t, arr.AppendArray(ArrayMarshalerFunc(func(inner ArrayEncoder) error {\n\t\t\t\t\tinner.AppendBool(true)\n\t\t\t\t\treturn errors.New(\"fail\")\n\t\t\t\t})), \"Expected an error appending an array.\")\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc:     \"objects (success)\",\n\t\t\texpected: `[{\"loggable\":\"yes\"},{\"loggable\":\"yes\"}]`,\n\t\t\tf: func(arr ArrayEncoder) {\n\t\t\t\tassert.NoError(t, arr.AppendObject(loggable{true}), \"Unexpected error appending an object.\")\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc:     \"objects (error)\",\n\t\t\texpected: `[{},{}]`,\n\t\t\tf: func(arr ArrayEncoder) {\n\t\t\t\tassert.Error(t, arr.AppendObject(loggable{false}), \"Expected an error appending an object.\")\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc:     \"reflect (success)\",\n\t\t\texpected: `[{\"foo\":5},{\"foo\":5}]`,\n\t\t\tf: func(arr ArrayEncoder) {\n\t\t\t\tassert.NoError(\n\t\t\t\t\tt,\n\t\t\t\t\tarr.AppendReflected(map[string]int{\"foo\": 5}),\n\t\t\t\t\t\"Unexpected an error appending an object with reflection.\",\n\t\t\t\t)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc:     \"reflect (error)\",\n\t\t\texpected: `[]`,\n\t\t\tf: func(arr ArrayEncoder) {\n\t\t\t\tassert.Error(\n\t\t\t\t\tt,\n\t\t\t\t\tarr.AppendReflected(noJSON{}),\n\t\t\t\t\t\"Unexpected an error appending an object with reflection.\",\n\t\t\t\t)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc:     \"object (no nested namespace) then string\",\n\t\t\texpected: `[{\"obj-out\":\"obj-outside-namespace\"},\"should-be-outside-obj\",{\"obj-out\":\"obj-outside-namespace\"},\"should-be-outside-obj\"]`,\n\t\t\tf: func(arr ArrayEncoder) {\n\t\t\t\tassert.NoError(t, arr.AppendObject(maybeNamespace{false}))\n\t\t\t\tarr.AppendString(\"should-be-outside-obj\")\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc:     \"object (with nested namespace) then string\",\n\t\t\texpected: `[{\"obj-out\":\"obj-outside-namespace\",\"obj-namespace\":{\"obj-in\":\"obj-inside-namespace\"}},\"should-be-outside-obj\",{\"obj-out\":\"obj-outside-namespace\",\"obj-namespace\":{\"obj-in\":\"obj-inside-namespace\"}},\"should-be-outside-obj\"]`,\n\t\t\tf: func(arr ArrayEncoder) {\n\t\t\t\tassert.NoError(t, arr.AppendObject(maybeNamespace{true}))\n\t\t\t\tarr.AppendString(\"should-be-outside-obj\")\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tf := func(enc Encoder) error {\n\t\t\t\treturn enc.AddArray(\"array\", ArrayMarshalerFunc(func(arr ArrayEncoder) error {\n\t\t\t\t\ttt.f(arr)\n\t\t\t\t\ttt.f(arr)\n\t\t\t\t\treturn nil\n\t\t\t\t}))\n\t\t\t}\n\t\t\tassertOutput(t, _defaultEncoderConfig, `\"array\":`+tt.expected, func(enc Encoder) {\n\t\t\t\terr := f(enc)\n\t\t\t\tassert.NoError(t, err, \"Unexpected error adding array to JSON encoder.\")\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestJSONEncoderTimeArrays(t *testing.T) {\n\ttimes := []time.Time{\n\t\ttime.Unix(1008720000, 0).UTC(), // 2001-12-19\n\t\ttime.Unix(1040169600, 0).UTC(), // 2002-12-18\n\t\ttime.Unix(1071619200, 0).UTC(), // 2003-12-17\n\t}\n\n\ttests := []struct {\n\t\tdesc    string\n\t\tencoder TimeEncoder\n\t\twant    string\n\t}{\n\t\t{\n\t\t\tdesc:    \"epoch\",\n\t\t\tencoder: EpochTimeEncoder,\n\t\t\twant:    `[1008720000,1040169600,1071619200]`,\n\t\t},\n\t\t{\n\t\t\tdesc:    \"epoch millis\",\n\t\t\tencoder: EpochMillisTimeEncoder,\n\t\t\twant:    `[1008720000000,1040169600000,1071619200000]`,\n\t\t},\n\t\t{\n\t\t\tdesc:    \"iso8601\",\n\t\t\tencoder: ISO8601TimeEncoder,\n\t\t\twant:    `[\"2001-12-19T00:00:00.000Z\",\"2002-12-18T00:00:00.000Z\",\"2003-12-17T00:00:00.000Z\"]`,\n\t\t},\n\t\t{\n\t\t\tdesc:    \"rfc3339\",\n\t\t\tencoder: RFC3339TimeEncoder,\n\t\t\twant:    `[\"2001-12-19T00:00:00Z\",\"2002-12-18T00:00:00Z\",\"2003-12-17T00:00:00Z\"]`,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tcfg := _defaultEncoderConfig\n\t\t\tcfg.EncodeTime = tt.encoder\n\n\t\t\tenc := &jsonEncoder{buf: bufferpool.Get(), EncoderConfig: &cfg}\n\t\t\terr := enc.AddArray(\"array\", ArrayMarshalerFunc(func(arr ArrayEncoder) error {\n\t\t\t\tfor _, time := range times {\n\t\t\t\t\tarr.AppendTime(time)\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t}))\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, `\"array\":`+tt.want, enc.buf.String())\n\t\t})\n\t}\n}\n\nfunc assertJSON(t *testing.T, expected string, enc *jsonEncoder) {\n\tassert.Equal(t, expected, enc.buf.String(), \"Encoded JSON didn't match expectations.\")\n}\n\nfunc assertOutput(t testing.TB, cfg EncoderConfig, expected string, f func(Encoder)) {\n\tenc := NewJSONEncoder(cfg).(*jsonEncoder)\n\tf(enc)\n\tassert.Equal(t, expected, enc.buf.String(), \"Unexpected encoder output after adding.\")\n\n\tenc.truncate()\n\tenc.AddString(\"foo\", \"bar\")\n\tf(enc)\n\texpectedPrefix := `\"foo\":\"bar\"`\n\tif expected != \"\" {\n\t\t// If we expect output, it should be comma-separated from the previous\n\t\t// field.\n\t\texpectedPrefix += \",\"\n\t}\n\tassert.Equal(t, expectedPrefix+expected, enc.buf.String(), \"Unexpected encoder output after adding as a second field.\")\n}\n\n// Nested Array- and ObjectMarshalers.\ntype turducken struct{}\n\nfunc (t turducken) MarshalLogObject(enc ObjectEncoder) error {\n\treturn enc.AddArray(\"ducks\", ArrayMarshalerFunc(func(arr ArrayEncoder) error {\n\t\tfor i := 0; i < 2; i++ {\n\t\t\terr := arr.AppendObject(ObjectMarshalerFunc(func(inner ObjectEncoder) error {\n\t\t\t\tinner.AddString(\"in\", \"chicken\")\n\t\t\t\treturn nil\n\t\t\t}))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}))\n}\n\ntype turduckens int\n\nfunc (t turduckens) MarshalLogArray(enc ArrayEncoder) error {\n\tvar err error\n\ttur := turducken{}\n\tfor i := 0; i < int(t); i++ {\n\t\terr = multierr.Append(err, enc.AppendObject(tur))\n\t}\n\treturn err\n}\n\ntype loggable struct{ bool }\n\nfunc (l loggable) MarshalLogObject(enc ObjectEncoder) error {\n\tif !l.bool {\n\t\treturn errors.New(\"can't marshal\")\n\t}\n\tenc.AddString(\"loggable\", \"yes\")\n\treturn nil\n}\n\nfunc (l loggable) MarshalLogArray(enc ArrayEncoder) error {\n\tif !l.bool {\n\t\treturn errors.New(\"can't marshal\")\n\t}\n\tenc.AppendBool(true)\n\treturn nil\n}\n\n// maybeNamespace is an ObjectMarshaler that sometimes opens a namespace\ntype maybeNamespace struct{ bool }\n\nfunc (m maybeNamespace) MarshalLogObject(enc ObjectEncoder) error {\n\tenc.AddString(\"obj-out\", \"obj-outside-namespace\")\n\tif m.bool {\n\t\tenc.OpenNamespace(\"obj-namespace\")\n\t\tenc.AddString(\"obj-in\", \"obj-inside-namespace\")\n\t}\n\treturn nil\n}\n\ntype noJSON struct{}\n\nfunc (nj noJSON) MarshalJSON() ([]byte, error) {\n\treturn nil, errors.New(\"no\")\n}\n\nfunc zapEncode(encode func(*jsonEncoder, string)) func(s string) []byte {\n\treturn func(s string) []byte {\n\t\tenc := &jsonEncoder{buf: bufferpool.Get()}\n\t\t// Escape and quote a string using our encoder.\n\t\tvar ret []byte\n\t\tencode(enc, s)\n\t\tret = make([]byte, 0, enc.buf.Len()+2)\n\t\tret = append(ret, '\"')\n\t\tret = append(ret, enc.buf.Bytes()...)\n\t\tret = append(ret, '\"')\n\t\treturn ret\n\t}\n}\n\nfunc roundTripsCorrectly(encode func(string) []byte, original string) bool {\n\t// Encode using our encoder, decode using the standard library, and assert\n\t// that we haven't lost any information.\n\tencoded := encode(original)\n\n\tvar decoded string\n\terr := json.Unmarshal(encoded, &decoded)\n\tif err != nil {\n\t\treturn false\n\t}\n\treturn original == decoded\n}\n\nfunc roundTripsCorrectlyString(original string) bool {\n\treturn roundTripsCorrectly(zapEncode((*jsonEncoder).safeAddString), original)\n}\n\nfunc roundTripsCorrectlyByteString(original string) bool {\n\treturn roundTripsCorrectly(\n\t\tzapEncode(func(enc *jsonEncoder, s string) {\n\t\t\tenc.safeAddByteString([]byte(s))\n\t\t}),\n\t\toriginal)\n}\n\ntype ASCII string\n\nfunc (s ASCII) Generate(r *rand.Rand, size int) reflect.Value {\n\tbs := make([]byte, size)\n\tfor i := range bs {\n\t\tbs[i] = byte(r.Intn(128))\n\t}\n\ta := ASCII(bs)\n\treturn reflect.ValueOf(a)\n}\n\nfunc asciiRoundTripsCorrectlyString(s ASCII) bool {\n\treturn roundTripsCorrectlyString(string(s))\n}\n\nfunc asciiRoundTripsCorrectlyByteString(s ASCII) bool {\n\treturn roundTripsCorrectlyByteString(string(s))\n}\n\nfunc TestJSONQuick(t *testing.T) {\n\tcheck := func(f interface{}) {\n\t\terr := quick.Check(f, &quick.Config{MaxCountScale: 100.0})\n\t\tassert.NoError(t, err)\n\t}\n\t// Test the full range of UTF-8 strings.\n\tcheck(roundTripsCorrectlyString)\n\tcheck(roundTripsCorrectlyByteString)\n\n\t// Focus on ASCII strings.\n\tcheck(asciiRoundTripsCorrectlyString)\n\tcheck(asciiRoundTripsCorrectlyByteString)\n}\n\nvar _stringLikeCorpus = []string{\n\t\"\",\n\t\"foo\",\n\t\"bar\",\n\t\"a\\nb\",\n\t\"a\\tb\",\n\t\"a\\\\b\",\n\t`a\"b`,\n}\n\nfunc FuzzSafeAppendStringLike_bytes(f *testing.F) {\n\tfor _, s := range _stringLikeCorpus {\n\t\tf.Add([]byte(s))\n\t}\n\tf.Fuzz(func(t *testing.T, b []byte) {\n\t\tif !utf8.Valid(b) {\n\t\t\tt.Skip()\n\t\t}\n\n\t\tfuzzSafeAppendStringLike(t, string(b), func(buf *buffer.Buffer) {\n\t\t\tsafeAppendStringLike(\n\t\t\t\t(*buffer.Buffer).AppendBytes,\n\t\t\t\tutf8.DecodeRune,\n\t\t\t\tbuf,\n\t\t\t\tb,\n\t\t\t)\n\t\t})\n\t})\n}\n\nfunc FuzzSafeAppendStringLike_string(f *testing.F) {\n\tfor _, s := range _stringLikeCorpus {\n\t\tf.Add(s)\n\t}\n\tf.Fuzz(func(t *testing.T, s string) {\n\t\tif !utf8.ValidString(s) {\n\t\t\tt.Skip()\n\t\t}\n\n\t\tfuzzSafeAppendStringLike(t, s, func(buf *buffer.Buffer) {\n\t\t\tsafeAppendStringLike(\n\t\t\t\t(*buffer.Buffer).AppendString,\n\t\t\t\tutf8.DecodeRuneInString,\n\t\t\t\tbuf,\n\t\t\t\ts,\n\t\t\t)\n\t\t})\n\t})\n}\n\nfunc fuzzSafeAppendStringLike(\n\tt *testing.T,\n\twant string,\n\twriteString func(*buffer.Buffer),\n) {\n\tt.Helper()\n\n\tbuf := bufferpool.Get()\n\tdefer buf.Free()\n\n\tbuf.AppendByte('\"')\n\twriteString(buf)\n\tbuf.AppendByte('\"')\n\n\tvar got string\n\trequire.NoError(t, json.Unmarshal(buf.Bytes(), &got))\n\tassert.Equal(t, want, got)\n}\n"
  },
  {
    "path": "zapcore/json_encoder_test.go",
    "content": "// Copyright (c) 2018 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore_test\n\nimport (\n\t\"io\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\n// TestJSONEncodeEntry is an more \"integrated\" test that makes it easier to get\n// coverage on the json encoder (e.g. putJSONEncoder, let alone EncodeEntry\n// itself) than the tests in json_encoder_impl_test.go; it needs to be in the\n// zapcore_test package, so that it can import the toplevel zap package for\n// field constructor convenience.\nfunc TestJSONEncodeEntry(t *testing.T) {\n\ttype bar struct {\n\t\tKey string  `json:\"key\"`\n\t\tVal float64 `json:\"val\"`\n\t}\n\n\ttype foo struct {\n\t\tA string  `json:\"aee\"`\n\t\tB int     `json:\"bee\"`\n\t\tC float64 `json:\"cee\"`\n\t\tD []bar   `json:\"dee\"`\n\t}\n\n\ttests := []struct {\n\t\tdesc     string\n\t\texpected string\n\t\tent      zapcore.Entry\n\t\tfields   []zapcore.Field\n\t}{\n\t\t{\n\t\t\tdesc: \"info entry with some fields\",\n\t\t\texpected: `{\n\t\t\t\t\"L\": \"info\",\n\t\t\t\t\"T\": \"2018-06-19T16:33:42.000Z\",\n\t\t\t\t\"N\": \"bob\",\n\t\t\t\t\"M\": \"lob law\",\n\t\t\t\t\"so\": \"passes\",\n\t\t\t\t\"answer\": 42,\n\t\t\t\t\"a_float32\": 2.71,\n\t\t\t\t\"common_pie\": 3.14,\n\t\t\t\t\"complex_value\": \"3.14-2.71i\",\n\t\t\t\t\"null_value\": null,\n\t\t\t\t\"array_with_null_elements\": [{}, null, null, 2],\n\t\t\t\t\"such\": {\n\t\t\t\t\t\"aee\": \"lol\",\n\t\t\t\t\t\"bee\": 123,\n\t\t\t\t\t\"cee\": 0.9999,\n\t\t\t\t\t\"dee\": [\n\t\t\t\t\t\t{\"key\": \"pi\", \"val\": 3.141592653589793},\n\t\t\t\t\t\t{\"key\": \"tau\", \"val\": 6.283185307179586}\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t}`,\n\t\t\tent: zapcore.Entry{\n\t\t\t\tLevel:      zapcore.InfoLevel,\n\t\t\t\tTime:       time.Date(2018, 6, 19, 16, 33, 42, 99, time.UTC),\n\t\t\t\tLoggerName: \"bob\",\n\t\t\t\tMessage:    \"lob law\",\n\t\t\t},\n\t\t\tfields: []zapcore.Field{\n\t\t\t\tzap.String(\"so\", \"passes\"),\n\t\t\t\tzap.Int(\"answer\", 42),\n\t\t\t\tzap.Float64(\"common_pie\", 3.14),\n\t\t\t\tzap.Float32(\"a_float32\", 2.71),\n\t\t\t\tzap.Complex128(\"complex_value\", 3.14-2.71i),\n\t\t\t\t// Cover special-cased handling of nil in AddReflect() and\n\t\t\t\t// AppendReflect(). Note that for the latter, we explicitly test\n\t\t\t\t// correct results for both the nil static interface{} value\n\t\t\t\t// (`nil`), as well as the non-nil interface value with a\n\t\t\t\t// dynamic type and nil value (`(*struct{})(nil)`).\n\t\t\t\tzap.Reflect(\"null_value\", nil),\n\t\t\t\tzap.Reflect(\"array_with_null_elements\", []interface{}{&struct{}{}, nil, (*struct{})(nil), 2}),\n\t\t\t\tzap.Reflect(\"such\", foo{\n\t\t\t\t\tA: \"lol\",\n\t\t\t\t\tB: 123,\n\t\t\t\t\tC: 0.9999,\n\t\t\t\t\tD: []bar{\n\t\t\t\t\t\t{\"pi\", 3.141592653589793},\n\t\t\t\t\t\t{\"tau\", 6.283185307179586},\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"zero_time_omitted\",\n\t\t\texpected: `{\n\t\t\t\t\"L\": \"info\",\n\t\t\t\t\"N\": \"name\",\n\t\t\t\t\"M\": \"message\"\n\t\t\t}`,\n\t\t\tent: zapcore.Entry{\n\t\t\t\tLevel:      zapcore.InfoLevel,\n\t\t\t\tTime:       time.Time{},\n\t\t\t\tLoggerName: \"name\",\n\t\t\t\tMessage:    \"message\",\n\t\t\t},\n\t\t},\n\t}\n\n\tenc := zapcore.NewJSONEncoder(zapcore.EncoderConfig{\n\t\tMessageKey:     \"M\",\n\t\tLevelKey:       \"L\",\n\t\tTimeKey:        \"T\",\n\t\tNameKey:        \"N\",\n\t\tCallerKey:      \"C\",\n\t\tFunctionKey:    \"F\",\n\t\tStacktraceKey:  \"S\",\n\t\tEncodeLevel:    zapcore.LowercaseLevelEncoder,\n\t\tEncodeTime:     zapcore.ISO8601TimeEncoder,\n\t\tEncodeDuration: zapcore.SecondsDurationEncoder,\n\t\tEncodeCaller:   zapcore.ShortCallerEncoder,\n\t})\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tbuf, err := enc.EncodeEntry(tt.ent, tt.fields)\n\t\t\tif assert.NoError(t, err, \"Unexpected JSON encoding error.\") {\n\t\t\t\tassert.JSONEq(t, tt.expected, buf.String(), \"Incorrect encoded JSON entry.\")\n\t\t\t}\n\t\t\tbuf.Free()\n\t\t})\n\t}\n}\n\nfunc TestNoEncodeLevelSupplied(t *testing.T) {\n\tenc := zapcore.NewJSONEncoder(zapcore.EncoderConfig{\n\t\tMessageKey:     \"M\",\n\t\tLevelKey:       \"L\",\n\t\tTimeKey:        \"T\",\n\t\tNameKey:        \"N\",\n\t\tCallerKey:      \"C\",\n\t\tFunctionKey:    \"F\",\n\t\tStacktraceKey:  \"S\",\n\t\tEncodeTime:     zapcore.ISO8601TimeEncoder,\n\t\tEncodeDuration: zapcore.SecondsDurationEncoder,\n\t\tEncodeCaller:   zapcore.ShortCallerEncoder,\n\t})\n\n\tent := zapcore.Entry{\n\t\tLevel:      zapcore.InfoLevel,\n\t\tTime:       time.Date(2018, 6, 19, 16, 33, 42, 99, time.UTC),\n\t\tLoggerName: \"bob\",\n\t\tMessage:    \"lob law\",\n\t}\n\n\tfields := []zapcore.Field{\n\t\tzap.Int(\"answer\", 42),\n\t}\n\n\t_, err := enc.EncodeEntry(ent, fields)\n\tassert.NoError(t, err, \"Unexpected JSON encoding error.\")\n}\n\nfunc TestJSONEmptyConfig(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\tfield    zapcore.Field\n\t\texpected string\n\t}{\n\t\t{\n\t\t\tname:     \"time\",\n\t\t\tfield:    zap.Time(\"foo\", time.Unix(1591287718, 0)), // 2020-06-04 09:21:58 -0700 PDT\n\t\t\texpected: `{\"foo\": 1591287718000000000}`,\n\t\t},\n\t\t{\n\t\t\tname:     \"duration\",\n\t\t\tfield:    zap.Duration(\"bar\", time.Microsecond),\n\t\t\texpected: `{\"bar\": 1000}`,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tenc := zapcore.NewJSONEncoder(zapcore.EncoderConfig{})\n\n\t\t\tbuf, err := enc.EncodeEntry(zapcore.Entry{\n\t\t\t\tLevel:      zapcore.DebugLevel,\n\t\t\t\tTime:       time.Now(),\n\t\t\t\tLoggerName: \"mylogger\",\n\t\t\t\tMessage:    \"things happened\",\n\t\t\t}, []zapcore.Field{tt.field})\n\t\t\tif assert.NoError(t, err, \"Unexpected JSON encoding error.\") {\n\t\t\t\tassert.JSONEq(t, tt.expected, buf.String(), \"Incorrect encoded JSON entry.\")\n\t\t\t}\n\n\t\t\tbuf.Free()\n\t\t})\n\t}\n}\n\n// Encodes any object into empty json '{}'\ntype emptyReflectedEncoder struct {\n\twriter io.Writer\n}\n\nfunc (enc *emptyReflectedEncoder) Encode(obj interface{}) error {\n\t_, err := enc.writer.Write([]byte(\"{}\"))\n\treturn err\n}\n\nfunc TestJSONCustomReflectedEncoder(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\tfield    zapcore.Field\n\t\texpected string\n\t}{\n\t\t{\n\t\t\tname: \"encode custom map object\",\n\t\t\tfield: zapcore.Field{\n\t\t\t\tKey:  \"data\",\n\t\t\t\tType: zapcore.ReflectType,\n\t\t\t\tInterface: map[string]interface{}{\n\t\t\t\t\t\"foo\": \"hello\",\n\t\t\t\t\t\"bar\": 1111,\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: `{\"data\":{}}`,\n\t\t},\n\t\t{\n\t\t\tname: \"encode nil object\",\n\t\t\tfield: zapcore.Field{\n\t\t\t\tKey:  \"data\",\n\t\t\t\tType: zapcore.ReflectType,\n\t\t\t},\n\t\t\texpected: `{\"data\":null}`,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tenc := zapcore.NewJSONEncoder(zapcore.EncoderConfig{\n\t\t\t\tNewReflectedEncoder: func(writer io.Writer) zapcore.ReflectedEncoder {\n\t\t\t\t\treturn &emptyReflectedEncoder{\n\t\t\t\t\t\twriter: writer,\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t})\n\n\t\t\tbuf, err := enc.EncodeEntry(zapcore.Entry{\n\t\t\t\tLevel:      zapcore.DebugLevel,\n\t\t\t\tTime:       time.Now(),\n\t\t\t\tLoggerName: \"logger\",\n\t\t\t\tMessage:    \"things happened\",\n\t\t\t}, []zapcore.Field{tt.field})\n\t\t\tif assert.NoError(t, err, \"Unexpected JSON encoding error.\") {\n\t\t\t\tassert.JSONEq(t, tt.expected, buf.String(), \"Incorrect encoded JSON entry.\")\n\t\t\t}\n\t\t\tbuf.Free()\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "zapcore/lazy_with.go",
    "content": "// Copyright (c) 2023 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport \"sync\"\n\ntype lazyWithCore struct {\n\tcore         Core\n\toriginalCore Core\n\tsync.Once\n\tfields []Field\n}\n\n// NewLazyWith wraps a Core with a \"lazy\" Core that will only encode fields if\n// the logger is written to (or is further chained in a lon-lazy manner).\nfunc NewLazyWith(core Core, fields []Field) Core {\n\treturn &lazyWithCore{\n\t\tcore:         nil, // core is allocated once `initOnce` is called.\n\t\toriginalCore: core,\n\t\tfields:       fields,\n\t}\n}\n\nfunc (d *lazyWithCore) initOnce() {\n\td.Once.Do(func() {\n\t\td.core = d.originalCore.With(d.fields)\n\t})\n}\n\nfunc (d *lazyWithCore) With(fields []Field) Core {\n\td.initOnce()\n\treturn d.core.With(fields)\n}\n\nfunc (d *lazyWithCore) Check(e Entry, ce *CheckedEntry) *CheckedEntry {\n\t// This is safe because `lazyWithCore` doesn't change the level.\n\t// So we can delagate the level check, any not `initOnce`\n\t// just for the check.\n\tif !d.originalCore.Enabled(e.Level) {\n\t\treturn ce\n\t}\n\td.initOnce()\n\treturn d.core.Check(e, ce)\n}\n\nfunc (d *lazyWithCore) Enabled(level Level) bool {\n\t// Like above, this is safe because `lazyWithCore` doesn't change the level.\n\treturn d.originalCore.Enabled(level)\n}\n\nfunc (d *lazyWithCore) Write(e Entry, fields []Field) error {\n\td.initOnce()\n\treturn d.core.Write(e, fields)\n}\n\nfunc (d *lazyWithCore) Sync() error {\n\td.initOnce()\n\treturn d.core.Sync()\n}\n"
  },
  {
    "path": "zapcore/lazy_with_test.go",
    "content": "// Copyright (c) 2023 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore_test\n\nimport (\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/zap/zapcore\"\n\t\"go.uber.org/zap/zaptest/observer\"\n)\n\ntype proxyCore struct {\n\tzapcore.Core\n\n\twithCount  atomic.Int64\n\tcheckCount atomic.Int64\n}\n\nfunc newProxyCore(inner zapcore.Core) *proxyCore {\n\treturn &proxyCore{Core: inner}\n}\n\nfunc (p *proxyCore) With(fields []zapcore.Field) zapcore.Core {\n\tp.withCount.Add(1)\n\treturn p.Core.With(fields)\n}\n\nfunc (p *proxyCore) Check(e zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {\n\tp.checkCount.Add(1)\n\treturn p.Core.Check(e, ce)\n}\n\nfunc withLazyCore(f func(zapcore.Core, *proxyCore, *observer.ObservedLogs), initialFields ...zapcore.Field) {\n\tinfoLogger, infoLogs := observer.New(zapcore.InfoLevel)\n\tproxyCore := newProxyCore(infoLogger)\n\tlazyCore := zapcore.NewLazyWith(proxyCore, initialFields)\n\tf(lazyCore, proxyCore, infoLogs)\n}\n\nfunc TestLazyCore(t *testing.T) {\n\ttests := []struct {\n\t\tname          string\n\t\tentries       []zapcore.Entry\n\t\tinitialFields []zapcore.Field\n\t\twithChains    [][]zapcore.Field\n\t\twantLogs      []observer.LoggedEntry\n\t\twantSkipInit  bool\n\t}{\n\t\t{\n\t\t\tname:     \"no logging, no with, inner core with never called, inner core check never called\",\n\t\t\twantLogs: []observer.LoggedEntry{},\n\t\t},\n\t\t{\n\t\t\tname: \"2 logs, 1 dropped, no with, inner core with called once, inner core check never called\",\n\t\t\tentries: []zapcore.Entry{\n\t\t\t\t{Level: zapcore.DebugLevel, Message: \"log-at-debug\"},\n\t\t\t\t{Level: zapcore.WarnLevel, Message: \"log-at-warn\"},\n\t\t\t},\n\t\t\twantLogs: []observer.LoggedEntry{\n\t\t\t\t{\n\t\t\t\t\tEntry: zapcore.Entry{\n\t\t\t\t\t\tLevel:   zapcore.WarnLevel,\n\t\t\t\t\t\tMessage: \"log-at-warn\",\n\t\t\t\t\t},\n\t\t\t\t\tContext: []zapcore.Field{},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"no logs, 2-chained with, inner core with called once, inner core check never called\",\n\t\t\twithChains: [][]zapcore.Field{\n\t\t\t\t{makeInt64Field(\"a\", 11), makeInt64Field(\"b\", 22)},\n\t\t\t\t{makeInt64Field(\"c\", 33), makeInt64Field(\"d\", 44)},\n\t\t\t},\n\t\t\twantLogs: []observer.LoggedEntry{},\n\t\t},\n\t\t{\n\t\t\tname: \"1 log, discarded, no-chains, inner core with never called\",\n\t\t\tentries: []zapcore.Entry{\n\t\t\t\t{Level: zapcore.DebugLevel, Message: \"log-at-debug\"},\n\t\t\t},\n\t\t\tinitialFields: []zapcore.Field{\n\t\t\t\tmakeInt64Field(\"a\", 11), makeInt64Field(\"b\", 22),\n\t\t\t},\n\t\t\twantLogs:     []observer.LoggedEntry{},\n\t\t\twantSkipInit: true,\n\t\t},\n\t\t{\n\t\t\tname: \"1 log, logged, no-chains, inner core with called\",\n\t\t\tentries: []zapcore.Entry{\n\t\t\t\t{Level: zapcore.WarnLevel, Message: \"log-at-warn\"},\n\t\t\t},\n\t\t\tinitialFields: []zapcore.Field{\n\t\t\t\tmakeInt64Field(\"a\", 11), makeInt64Field(\"b\", 22),\n\t\t\t},\n\t\t\twantLogs: []observer.LoggedEntry{\n\t\t\t\t{\n\t\t\t\t\tEntry: zapcore.Entry{\n\t\t\t\t\t\tLevel:   zapcore.WarnLevel,\n\t\t\t\t\t\tMessage: \"log-at-warn\",\n\t\t\t\t\t},\n\t\t\t\t\tContext: []zapcore.Field{\n\t\t\t\t\t\tmakeInt64Field(\"a\", 11),\n\t\t\t\t\t\tmakeInt64Field(\"b\", 22),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"2 logs, 1 dropped, 2-chained with, inner core with called once, inner core check never called\",\n\t\t\tentries: []zapcore.Entry{\n\t\t\t\t{Level: zapcore.DebugLevel, Message: \"log-at-debug\"},\n\t\t\t\t{Level: zapcore.WarnLevel, Message: \"log-at-warn\"},\n\t\t\t},\n\t\t\twithChains: [][]zapcore.Field{\n\t\t\t\t{makeInt64Field(\"a\", 11), makeInt64Field(\"b\", 22)},\n\t\t\t\t{makeInt64Field(\"c\", 33), makeInt64Field(\"d\", 44)},\n\t\t\t},\n\t\t\twantLogs: []observer.LoggedEntry{\n\t\t\t\t{\n\t\t\t\t\tEntry: zapcore.Entry{\n\t\t\t\t\t\tLevel:   zapcore.WarnLevel,\n\t\t\t\t\t\tMessage: \"log-at-warn\",\n\t\t\t\t\t},\n\t\t\t\t\tContext: []zapcore.Field{\n\t\t\t\t\t\tmakeInt64Field(\"a\", 11),\n\t\t\t\t\t\tmakeInt64Field(\"b\", 22),\n\t\t\t\t\t\tmakeInt64Field(\"c\", 33),\n\t\t\t\t\t\tmakeInt64Field(\"d\", 44),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\twithLazyCore(func(lazy zapcore.Core, proxy *proxyCore, logs *observer.ObservedLogs) {\n\t\t\t\tcheckCounts := func(withCount int64, msg string) {\n\t\t\t\t\tassert.Equal(t, withCount, proxy.withCount.Load(), msg)\n\t\t\t\t}\n\t\t\t\tcheckCounts(0, \"expected no with calls because the logger is not used yet\")\n\n\t\t\t\tfor _, chain := range tt.withChains {\n\t\t\t\t\tlazy = lazy.With(chain)\n\t\t\t\t}\n\t\t\t\tif len(tt.withChains) > 0 {\n\t\t\t\t\tcheckCounts(1, \"expected with calls because the logger was with-chained\")\n\t\t\t\t} else {\n\t\t\t\t\tcheckCounts(0, \"expected no with calls because the logger is not used yet\")\n\t\t\t\t}\n\n\t\t\t\tfor _, ent := range tt.entries {\n\t\t\t\t\tif ce := lazy.Check(ent, nil); ce != nil {\n\t\t\t\t\t\tce.Write()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif !tt.wantSkipInit && (len(tt.entries) > 0 || len(tt.withChains) > 0) {\n\t\t\t\t\tcheckCounts(1, \"expected with calls because the logger had entries or with chains\")\n\t\t\t\t} else {\n\t\t\t\t\tcheckCounts(0, \"expected no with calls because the logger is not used yet\")\n\t\t\t\t}\n\t\t\t\tassert.Zero(t, proxy.checkCount.Load(), \"expected no check calls because the inner core is copied\")\n\t\t\t\tassert.Equal(t, tt.wantLogs, logs.AllUntimed())\n\t\t\t}, tt.initialFields...)\n\t\t})\n\t}\n}\n\n// TestLazyCoreRace tests concurrent access to lazyWithCore methods\n// This is a regression test for issue #1426\nfunc TestLazyCoreRace(t *testing.T) {\n\tcore, _ := observer.New(zapcore.InfoLevel)\n\tlazyCore := zapcore.NewLazyWith(core, []zapcore.Field{\n\t\tmakeInt64Field(\"lazy\", 42),\n\t})\n\n\tvar wg sync.WaitGroup\n\tconst numGoroutines = 50\n\n\t// Test concurrent access to Enabled() method which was the source of the race\n\tfor i := 0; i < numGoroutines; i++ {\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\n\t\t\t// These operations should not race\n\t\t\t_ = lazyCore.Enabled(zapcore.InfoLevel)\n\t\t\t_ = lazyCore.Enabled(zapcore.DebugLevel)\n\n\t\t\t// Also test other methods for good measure\n\t\t\tif ce := lazyCore.Check(zapcore.Entry{Level: zapcore.InfoLevel, Message: \"test\"}, nil); ce != nil {\n\t\t\t\t_ = lazyCore.Write(zapcore.Entry{Level: zapcore.InfoLevel, Message: \"test\"}, nil)\n\t\t\t}\n\t\t}()\n\t}\n\n\twg.Wait()\n}\n"
  },
  {
    "path": "zapcore/leak_test.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport (\n\t\"testing\"\n\n\t\"go.uber.org/goleak\"\n)\n\nfunc TestMain(m *testing.M) {\n\tgoleak.VerifyTestMain(m)\n}\n"
  },
  {
    "path": "zapcore/level.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n)\n\nvar errUnmarshalNilLevel = errors.New(\"can't unmarshal a nil *Level\")\n\n// A Level is a logging priority. Higher levels are more important.\ntype Level int8\n\nconst (\n\t// DebugLevel logs are typically voluminous, and are usually disabled in\n\t// production.\n\tDebugLevel Level = iota - 1\n\t// InfoLevel is the default logging priority.\n\tInfoLevel\n\t// WarnLevel logs are more important than Info, but don't need individual\n\t// human review.\n\tWarnLevel\n\t// ErrorLevel logs are high-priority. If an application is running smoothly,\n\t// it shouldn't generate any error-level logs.\n\tErrorLevel\n\t// DPanicLevel logs are particularly important errors. In development the\n\t// logger panics after writing the message.\n\tDPanicLevel\n\t// PanicLevel logs a message, then panics.\n\tPanicLevel\n\t// FatalLevel logs a message, then calls os.Exit(1).\n\tFatalLevel\n\n\t_minLevel = DebugLevel\n\t_maxLevel = FatalLevel\n\n\t// InvalidLevel is an invalid value for Level.\n\t//\n\t// Core implementations may panic if they see messages of this level.\n\tInvalidLevel = _maxLevel + 1\n)\n\n// ParseLevel parses a level based on the lower-case or all-caps ASCII\n// representation of the log level. If the provided ASCII representation is\n// invalid an error is returned.\n//\n// This is particularly useful when dealing with text input to configure log\n// levels.\nfunc ParseLevel(text string) (Level, error) {\n\tvar level Level\n\terr := level.UnmarshalText([]byte(text))\n\treturn level, err\n}\n\ntype leveledEnabler interface {\n\tLevelEnabler\n\n\tLevel() Level\n}\n\n// LevelOf reports the minimum enabled log level for the given LevelEnabler\n// from Zap's supported log levels, or [InvalidLevel] if none of them are\n// enabled.\n//\n// A LevelEnabler may implement a 'Level() Level' method to override the\n// behavior of this function.\n//\n//\tfunc (c *core) Level() Level {\n//\t\treturn c.currentLevel\n//\t}\n//\n// It is recommended that [Core] implementations that wrap other cores use\n// LevelOf to retrieve the level of the wrapped core. For example,\n//\n//\tfunc (c *coreWrapper) Level() Level {\n//\t\treturn zapcore.LevelOf(c.wrappedCore)\n//\t}\nfunc LevelOf(enab LevelEnabler) Level {\n\tif lvler, ok := enab.(leveledEnabler); ok {\n\t\treturn lvler.Level()\n\t}\n\n\tfor lvl := _minLevel; lvl <= _maxLevel; lvl++ {\n\t\tif enab.Enabled(lvl) {\n\t\t\treturn lvl\n\t\t}\n\t}\n\n\treturn InvalidLevel\n}\n\n// String returns a lower-case ASCII representation of the log level.\nfunc (l Level) String() string {\n\tswitch l {\n\tcase DebugLevel:\n\t\treturn \"debug\"\n\tcase InfoLevel:\n\t\treturn \"info\"\n\tcase WarnLevel:\n\t\treturn \"warn\"\n\tcase ErrorLevel:\n\t\treturn \"error\"\n\tcase DPanicLevel:\n\t\treturn \"dpanic\"\n\tcase PanicLevel:\n\t\treturn \"panic\"\n\tcase FatalLevel:\n\t\treturn \"fatal\"\n\tdefault:\n\t\treturn fmt.Sprintf(\"Level(%d)\", l)\n\t}\n}\n\n// CapitalString returns an all-caps ASCII representation of the log level.\nfunc (l Level) CapitalString() string {\n\t// Printing levels in all-caps is common enough that we should export this\n\t// functionality.\n\tswitch l {\n\tcase DebugLevel:\n\t\treturn \"DEBUG\"\n\tcase InfoLevel:\n\t\treturn \"INFO\"\n\tcase WarnLevel:\n\t\treturn \"WARN\"\n\tcase ErrorLevel:\n\t\treturn \"ERROR\"\n\tcase DPanicLevel:\n\t\treturn \"DPANIC\"\n\tcase PanicLevel:\n\t\treturn \"PANIC\"\n\tcase FatalLevel:\n\t\treturn \"FATAL\"\n\tdefault:\n\t\treturn fmt.Sprintf(\"LEVEL(%d)\", l)\n\t}\n}\n\n// MarshalText marshals the Level to text. Note that the text representation\n// drops the -Level suffix (see example).\nfunc (l Level) MarshalText() ([]byte, error) {\n\treturn []byte(l.String()), nil\n}\n\n// UnmarshalText unmarshals text to a level. Like MarshalText, UnmarshalText\n// expects the text representation of a Level to drop the -Level suffix (see\n// example).\n//\n// In particular, this makes it easy to configure logging levels using YAML,\n// TOML, or JSON files.\nfunc (l *Level) UnmarshalText(text []byte) error {\n\tif l == nil {\n\t\treturn errUnmarshalNilLevel\n\t}\n\tif !l.unmarshalText(text) && !l.unmarshalText(bytes.ToLower(text)) {\n\t\treturn fmt.Errorf(\"unrecognized level: %q\", text)\n\t}\n\treturn nil\n}\n\nfunc (l *Level) unmarshalText(text []byte) bool {\n\tswitch string(text) {\n\tcase \"debug\":\n\t\t*l = DebugLevel\n\tcase \"info\", \"\": // make the zero value useful\n\t\t*l = InfoLevel\n\tcase \"warn\", \"warning\":\n\t\t*l = WarnLevel\n\tcase \"error\":\n\t\t*l = ErrorLevel\n\tcase \"dpanic\":\n\t\t*l = DPanicLevel\n\tcase \"panic\":\n\t\t*l = PanicLevel\n\tcase \"fatal\":\n\t\t*l = FatalLevel\n\tdefault:\n\t\treturn false\n\t}\n\treturn true\n}\n\n// Set sets the level for the flag.Value interface.\nfunc (l *Level) Set(s string) error {\n\treturn l.UnmarshalText([]byte(s))\n}\n\n// Get gets the level for the flag.Getter interface.\nfunc (l *Level) Get() interface{} {\n\treturn *l\n}\n\n// Enabled returns true if the given level is at or above this level.\nfunc (l Level) Enabled(lvl Level) bool {\n\treturn lvl >= l\n}\n\n// LevelEnabler decides whether a given logging level is enabled when logging a\n// message.\n//\n// Enablers are intended to be used to implement deterministic filters;\n// concerns like sampling are better implemented as a Core.\n//\n// Each concrete Level value implements a static LevelEnabler which returns\n// true for itself and all higher logging levels. For example WarnLevel.Enabled()\n// will return true for WarnLevel, ErrorLevel, DPanicLevel, PanicLevel, and\n// FatalLevel, but return false for InfoLevel and DebugLevel.\ntype LevelEnabler interface {\n\tEnabled(Level) bool\n}\n"
  },
  {
    "path": "zapcore/level_strings.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport \"go.uber.org/zap/internal/color\"\n\nvar (\n\t_levelToColor = map[Level]color.Color{\n\t\tDebugLevel:  color.Magenta,\n\t\tInfoLevel:   color.Blue,\n\t\tWarnLevel:   color.Yellow,\n\t\tErrorLevel:  color.Red,\n\t\tDPanicLevel: color.Red,\n\t\tPanicLevel:  color.Red,\n\t\tFatalLevel:  color.Red,\n\t}\n\t_unknownLevelColor = color.Red\n\n\t_levelToLowercaseColorString = make(map[Level]string, len(_levelToColor))\n\t_levelToCapitalColorString   = make(map[Level]string, len(_levelToColor))\n)\n\nfunc init() {\n\tfor level, color := range _levelToColor {\n\t\t_levelToLowercaseColorString[level] = color.Add(level.String())\n\t\t_levelToCapitalColorString[level] = color.Add(level.CapitalString())\n\t}\n}\n"
  },
  {
    "path": "zapcore/level_strings_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestAllLevelsCoveredByLevelString(t *testing.T) {\n\tnumLevels := int((_maxLevel - _minLevel) + 1)\n\n\tisComplete := func(m map[Level]string) bool {\n\t\treturn len(m) == numLevels\n\t}\n\n\tassert.True(t, isComplete(_levelToLowercaseColorString), \"Colored lowercase strings don't cover all levels.\")\n\tassert.True(t, isComplete(_levelToCapitalColorString), \"Colored capital strings don't cover all levels.\")\n}\n"
  },
  {
    "path": "zapcore/level_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport (\n\t\"bytes\"\n\t\"flag\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestLevelString(t *testing.T) {\n\ttests := map[Level]string{\n\t\tDebugLevel:   \"debug\",\n\t\tInfoLevel:    \"info\",\n\t\tWarnLevel:    \"warn\",\n\t\tErrorLevel:   \"error\",\n\t\tDPanicLevel:  \"dpanic\",\n\t\tPanicLevel:   \"panic\",\n\t\tFatalLevel:   \"fatal\",\n\t\tLevel(-42):   \"Level(-42)\",\n\t\tInvalidLevel: \"Level(6)\", // InvalidLevel does not have a name\n\t}\n\n\tfor lvl, stringLevel := range tests {\n\t\tassert.Equal(t, stringLevel, lvl.String(), \"Unexpected lowercase level string.\")\n\t\tassert.Equal(t, strings.ToUpper(stringLevel), lvl.CapitalString(), \"Unexpected all-caps level string.\")\n\t}\n}\n\nfunc TestLevelText(t *testing.T) {\n\ttests := []struct {\n\t\ttext  string\n\t\tlevel Level\n\t}{\n\t\t{\"debug\", DebugLevel},\n\t\t{\"info\", InfoLevel},\n\t\t{\"\", InfoLevel}, // make the zero value useful\n\t\t{\"warn\", WarnLevel},\n\t\t{\"error\", ErrorLevel},\n\t\t{\"dpanic\", DPanicLevel},\n\t\t{\"panic\", PanicLevel},\n\t\t{\"fatal\", FatalLevel},\n\t}\n\tfor _, tt := range tests {\n\t\tif tt.text != \"\" {\n\t\t\tlvl := tt.level\n\t\t\tmarshaled, err := lvl.MarshalText()\n\t\t\tassert.NoError(t, err, \"Unexpected error marshaling level %v to text.\", &lvl)\n\t\t\tassert.Equal(t, tt.text, string(marshaled), \"Marshaling level %v to text yielded unexpected result.\", &lvl)\n\t\t}\n\n\t\tvar unmarshaled Level\n\t\terr := unmarshaled.UnmarshalText([]byte(tt.text))\n\t\tassert.NoError(t, err, `Unexpected error unmarshaling text %q to level.`, tt.text)\n\t\tassert.Equal(t, tt.level, unmarshaled, `Text %q unmarshaled to an unexpected level.`, tt.text)\n\t}\n\n\t// Some logging libraries are using \"warning\" instead of \"warn\" as level indicator. Handle this case\n\t// for cross compatibility.\n\tt.Run(\"unmarshal warning compatibility\", func(t *testing.T) {\n\t\tvar unmarshaled Level\n\t\tinput := []byte(\"warning\")\n\t\terr := unmarshaled.UnmarshalText(input)\n\t\tassert.NoError(t, err, `Unexpected error unmarshaling text %q to level.`, string(input))\n\t\tassert.Equal(t, WarnLevel, unmarshaled, `Text %q unmarshaled to an unexpected level.`, string(input))\n\t})\n}\n\nfunc TestParseLevel(t *testing.T) {\n\ttests := []struct {\n\t\ttext  string\n\t\tlevel Level\n\t\terr   string\n\t}{\n\t\t{\"info\", InfoLevel, \"\"},\n\t\t{\"DEBUG\", DebugLevel, \"\"},\n\t\t{\"FOO\", 0, `unrecognized level: \"FOO\"`},\n\t\t{\"WARNING\", WarnLevel, \"\"},\n\t}\n\tfor _, tt := range tests {\n\t\tparsedLevel, err := ParseLevel(tt.text)\n\t\tif len(tt.err) == 0 {\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, tt.level, parsedLevel)\n\t\t} else {\n\t\t\tassert.ErrorContains(t, err, tt.err)\n\t\t}\n\t}\n}\n\nfunc TestCapitalLevelsParse(t *testing.T) {\n\ttests := []struct {\n\t\ttext  string\n\t\tlevel Level\n\t}{\n\t\t{\"DEBUG\", DebugLevel},\n\t\t{\"INFO\", InfoLevel},\n\t\t{\"WARN\", WarnLevel},\n\t\t{\"ERROR\", ErrorLevel},\n\t\t{\"DPANIC\", DPanicLevel},\n\t\t{\"PANIC\", PanicLevel},\n\t\t{\"FATAL\", FatalLevel},\n\t}\n\tfor _, tt := range tests {\n\t\tvar unmarshaled Level\n\t\terr := unmarshaled.UnmarshalText([]byte(tt.text))\n\t\tassert.NoError(t, err, `Unexpected error unmarshaling text %q to level.`, tt.text)\n\t\tassert.Equal(t, tt.level, unmarshaled, `Text %q unmarshaled to an unexpected level.`, tt.text)\n\t}\n}\n\nfunc TestWeirdLevelsParse(t *testing.T) {\n\ttests := []struct {\n\t\ttext  string\n\t\tlevel Level\n\t}{\n\t\t// I guess...\n\t\t{\"Debug\", DebugLevel},\n\t\t{\"Info\", InfoLevel},\n\t\t{\"Warn\", WarnLevel},\n\t\t{\"Error\", ErrorLevel},\n\t\t{\"Dpanic\", DPanicLevel},\n\t\t{\"Panic\", PanicLevel},\n\t\t{\"Fatal\", FatalLevel},\n\n\t\t// What even is...\n\t\t{\"DeBuG\", DebugLevel},\n\t\t{\"InFo\", InfoLevel},\n\t\t{\"WaRn\", WarnLevel},\n\t\t{\"ErRor\", ErrorLevel},\n\t\t{\"DpAnIc\", DPanicLevel},\n\t\t{\"PaNiC\", PanicLevel},\n\t\t{\"FaTaL\", FatalLevel},\n\t}\n\tfor _, tt := range tests {\n\t\tvar unmarshaled Level\n\t\terr := unmarshaled.UnmarshalText([]byte(tt.text))\n\t\tassert.NoError(t, err, `Unexpected error unmarshaling text %q to level.`, tt.text)\n\t\tassert.Equal(t, tt.level, unmarshaled, `Text %q unmarshaled to an unexpected level.`, tt.text)\n\t}\n}\n\nfunc TestLevelNils(t *testing.T) {\n\tvar l *Level\n\n\t// The String() method will not handle nil level properly.\n\tassert.Panics(t, func() {\n\t\tassert.Equal(t, \"Level(nil)\", l.String(), \"Unexpected result stringifying nil *Level.\")\n\t}, \"Level(nil).String() should panic\")\n\n\tassert.Panics(t, func() {\n\t\t_, _ = l.MarshalText() // should panic\n\t}, \"Expected to panic when marshalling a nil level.\")\n\n\terr := l.UnmarshalText([]byte(\"debug\"))\n\tassert.Equal(t, errUnmarshalNilLevel, err, \"Expected to error unmarshalling into a nil Level.\")\n}\n\nfunc TestLevelUnmarshalUnknownText(t *testing.T) {\n\tvar l Level\n\terr := l.UnmarshalText([]byte(\"foo\"))\n\tassert.ErrorContains(t, err, \"unrecognized level\", \"Expected unmarshaling arbitrary text to fail.\")\n}\n\nfunc TestLevelAsFlagValue(t *testing.T) {\n\tvar (\n\t\tbuf bytes.Buffer\n\t\tlvl Level\n\t)\n\tfs := flag.NewFlagSet(\"levelTest\", flag.ContinueOnError)\n\tfs.SetOutput(&buf)\n\tfs.Var(&lvl, \"level\", \"log level\")\n\n\tfor _, expected := range []Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel, PanicLevel, FatalLevel} {\n\t\tassert.NoError(t, fs.Parse([]string{\"-level\", expected.String()}))\n\t\tassert.Equal(t, expected, lvl, \"Unexpected level after parsing flag.\")\n\t\tassert.Equal(t, expected, lvl.Get(), \"Unexpected output using flag.Getter API.\")\n\t\tassert.Empty(t, buf.String(), \"Unexpected error output parsing level flag.\")\n\t\tbuf.Reset()\n\t}\n\n\tassert.Error(t, fs.Parse([]string{\"-level\", \"nope\"}))\n\tassert.Equal(\n\t\tt,\n\t\t`invalid value \"nope\" for flag -level: unrecognized level: \"nope\"`,\n\t\tstrings.Split(buf.String(), \"\\n\")[0], // second line is help message\n\t\t\"Unexpected error output from invalid flag input.\",\n\t)\n}\n\n// enablerWithCustomLevel is a LevelEnabler that implements a custom Level\n// method.\ntype enablerWithCustomLevel struct{ lvl Level }\n\nvar _ leveledEnabler = (*enablerWithCustomLevel)(nil)\n\nfunc (l *enablerWithCustomLevel) Enabled(lvl Level) bool {\n\treturn l.lvl.Enabled(lvl)\n}\n\nfunc (l *enablerWithCustomLevel) Level() Level {\n\treturn l.lvl\n}\n\nfunc TestLevelOf(t *testing.T) {\n\ttests := []struct {\n\t\tdesc string\n\t\tgive LevelEnabler\n\t\twant Level\n\t}{\n\t\t{desc: \"debug\", give: DebugLevel, want: DebugLevel},\n\t\t{desc: \"info\", give: InfoLevel, want: InfoLevel},\n\t\t{desc: \"warn\", give: WarnLevel, want: WarnLevel},\n\t\t{desc: \"error\", give: ErrorLevel, want: ErrorLevel},\n\t\t{desc: \"dpanic\", give: DPanicLevel, want: DPanicLevel},\n\t\t{desc: \"panic\", give: PanicLevel, want: PanicLevel},\n\t\t{desc: \"fatal\", give: FatalLevel, want: FatalLevel},\n\t\t{\n\t\t\tdesc: \"leveledEnabler\",\n\t\t\tgive: &enablerWithCustomLevel{lvl: InfoLevel},\n\t\t\twant: InfoLevel,\n\t\t},\n\t\t{\n\t\t\tdesc: \"noop\",\n\t\t\tgive: NewNopCore(), // always disabled\n\t\t\twant: InvalidLevel,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tassert.Equal(t, tt.want, LevelOf(tt.give), \"Reported level did not match.\")\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "zapcore/marshaler.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\n// ObjectMarshaler allows user-defined types to efficiently add themselves to the\n// logging context, and to selectively omit information which shouldn't be\n// included in logs (e.g., passwords).\n//\n// Note: ObjectMarshaler is only used when zap.Object is used or when\n// passed directly to zap.Any. It is not used when reflection-based\n// encoding is used.\ntype ObjectMarshaler interface {\n\tMarshalLogObject(ObjectEncoder) error\n}\n\n// ObjectMarshalerFunc is a type adapter that turns a function into an\n// ObjectMarshaler.\ntype ObjectMarshalerFunc func(ObjectEncoder) error\n\n// MarshalLogObject calls the underlying function.\nfunc (f ObjectMarshalerFunc) MarshalLogObject(enc ObjectEncoder) error {\n\treturn f(enc)\n}\n\n// ArrayMarshaler allows user-defined types to efficiently add themselves to the\n// logging context, and to selectively omit information which shouldn't be\n// included in logs (e.g., passwords).\n//\n// Note: ArrayMarshaler is only used when zap.Array is used or when\n// passed directly to zap.Any. It is not used when reflection-based\n// encoding is used.\ntype ArrayMarshaler interface {\n\tMarshalLogArray(ArrayEncoder) error\n}\n\n// ArrayMarshalerFunc is a type adapter that turns a function into an\n// ArrayMarshaler.\ntype ArrayMarshalerFunc func(ArrayEncoder) error\n\n// MarshalLogArray calls the underlying function.\nfunc (f ArrayMarshalerFunc) MarshalLogArray(enc ArrayEncoder) error {\n\treturn f(enc)\n}\n"
  },
  {
    "path": "zapcore/memory_encoder.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport \"time\"\n\n// MapObjectEncoder is an ObjectEncoder backed by a simple\n// map[string]interface{}. It's not fast enough for production use, but it's\n// helpful in tests.\ntype MapObjectEncoder struct {\n\t// Fields contains the entire encoded log context.\n\tFields map[string]interface{}\n\t// cur is a pointer to the namespace we're currently writing to.\n\tcur map[string]interface{}\n}\n\n// NewMapObjectEncoder creates a new map-backed ObjectEncoder.\nfunc NewMapObjectEncoder() *MapObjectEncoder {\n\tm := make(map[string]interface{})\n\treturn &MapObjectEncoder{\n\t\tFields: m,\n\t\tcur:    m,\n\t}\n}\n\n// AddArray implements ObjectEncoder.\nfunc (m *MapObjectEncoder) AddArray(key string, v ArrayMarshaler) error {\n\tarr := &sliceArrayEncoder{elems: make([]interface{}, 0)}\n\terr := v.MarshalLogArray(arr)\n\tm.cur[key] = arr.elems\n\treturn err\n}\n\n// AddObject implements ObjectEncoder.\nfunc (m *MapObjectEncoder) AddObject(k string, v ObjectMarshaler) error {\n\tnewMap := NewMapObjectEncoder()\n\tm.cur[k] = newMap.Fields\n\treturn v.MarshalLogObject(newMap)\n}\n\n// AddBinary implements ObjectEncoder.\nfunc (m *MapObjectEncoder) AddBinary(k string, v []byte) { m.cur[k] = v }\n\n// AddByteString implements ObjectEncoder.\nfunc (m *MapObjectEncoder) AddByteString(k string, v []byte) { m.cur[k] = string(v) }\n\n// AddBool implements ObjectEncoder.\nfunc (m *MapObjectEncoder) AddBool(k string, v bool) { m.cur[k] = v }\n\n// AddDuration implements ObjectEncoder.\nfunc (m MapObjectEncoder) AddDuration(k string, v time.Duration) { m.cur[k] = v }\n\n// AddComplex128 implements ObjectEncoder.\nfunc (m *MapObjectEncoder) AddComplex128(k string, v complex128) { m.cur[k] = v }\n\n// AddComplex64 implements ObjectEncoder.\nfunc (m *MapObjectEncoder) AddComplex64(k string, v complex64) { m.cur[k] = v }\n\n// AddFloat64 implements ObjectEncoder.\nfunc (m *MapObjectEncoder) AddFloat64(k string, v float64) { m.cur[k] = v }\n\n// AddFloat32 implements ObjectEncoder.\nfunc (m *MapObjectEncoder) AddFloat32(k string, v float32) { m.cur[k] = v }\n\n// AddInt implements ObjectEncoder.\nfunc (m *MapObjectEncoder) AddInt(k string, v int) { m.cur[k] = v }\n\n// AddInt64 implements ObjectEncoder.\nfunc (m *MapObjectEncoder) AddInt64(k string, v int64) { m.cur[k] = v }\n\n// AddInt32 implements ObjectEncoder.\nfunc (m *MapObjectEncoder) AddInt32(k string, v int32) { m.cur[k] = v }\n\n// AddInt16 implements ObjectEncoder.\nfunc (m *MapObjectEncoder) AddInt16(k string, v int16) { m.cur[k] = v }\n\n// AddInt8 implements ObjectEncoder.\nfunc (m *MapObjectEncoder) AddInt8(k string, v int8) { m.cur[k] = v }\n\n// AddString implements ObjectEncoder.\nfunc (m *MapObjectEncoder) AddString(k string, v string) { m.cur[k] = v }\n\n// AddTime implements ObjectEncoder.\nfunc (m MapObjectEncoder) AddTime(k string, v time.Time) { m.cur[k] = v }\n\n// AddUint implements ObjectEncoder.\nfunc (m *MapObjectEncoder) AddUint(k string, v uint) { m.cur[k] = v }\n\n// AddUint64 implements ObjectEncoder.\nfunc (m *MapObjectEncoder) AddUint64(k string, v uint64) { m.cur[k] = v }\n\n// AddUint32 implements ObjectEncoder.\nfunc (m *MapObjectEncoder) AddUint32(k string, v uint32) { m.cur[k] = v }\n\n// AddUint16 implements ObjectEncoder.\nfunc (m *MapObjectEncoder) AddUint16(k string, v uint16) { m.cur[k] = v }\n\n// AddUint8 implements ObjectEncoder.\nfunc (m *MapObjectEncoder) AddUint8(k string, v uint8) { m.cur[k] = v }\n\n// AddUintptr implements ObjectEncoder.\nfunc (m *MapObjectEncoder) AddUintptr(k string, v uintptr) { m.cur[k] = v }\n\n// AddReflected implements ObjectEncoder.\nfunc (m *MapObjectEncoder) AddReflected(k string, v interface{}) error {\n\tm.cur[k] = v\n\treturn nil\n}\n\n// OpenNamespace implements ObjectEncoder.\nfunc (m *MapObjectEncoder) OpenNamespace(k string) {\n\tns := make(map[string]interface{})\n\tm.cur[k] = ns\n\tm.cur = ns\n}\n\n// sliceArrayEncoder is an ArrayEncoder backed by a simple []interface{}. Like\n// the MapObjectEncoder, it's not designed for production use.\ntype sliceArrayEncoder struct {\n\telems []interface{}\n}\n\nfunc (s *sliceArrayEncoder) AppendArray(v ArrayMarshaler) error {\n\tenc := &sliceArrayEncoder{}\n\terr := v.MarshalLogArray(enc)\n\ts.elems = append(s.elems, enc.elems)\n\treturn err\n}\n\nfunc (s *sliceArrayEncoder) AppendObject(v ObjectMarshaler) error {\n\tm := NewMapObjectEncoder()\n\terr := v.MarshalLogObject(m)\n\ts.elems = append(s.elems, m.Fields)\n\treturn err\n}\n\nfunc (s *sliceArrayEncoder) AppendReflected(v interface{}) error {\n\ts.elems = append(s.elems, v)\n\treturn nil\n}\n\nfunc (s *sliceArrayEncoder) AppendBool(v bool)              { s.elems = append(s.elems, v) }\nfunc (s *sliceArrayEncoder) AppendByteString(v []byte)      { s.elems = append(s.elems, string(v)) }\nfunc (s *sliceArrayEncoder) AppendComplex128(v complex128)  { s.elems = append(s.elems, v) }\nfunc (s *sliceArrayEncoder) AppendComplex64(v complex64)    { s.elems = append(s.elems, v) }\nfunc (s *sliceArrayEncoder) AppendDuration(v time.Duration) { s.elems = append(s.elems, v) }\nfunc (s *sliceArrayEncoder) AppendFloat64(v float64)        { s.elems = append(s.elems, v) }\nfunc (s *sliceArrayEncoder) AppendFloat32(v float32)        { s.elems = append(s.elems, v) }\nfunc (s *sliceArrayEncoder) AppendInt(v int)                { s.elems = append(s.elems, v) }\nfunc (s *sliceArrayEncoder) AppendInt64(v int64)            { s.elems = append(s.elems, v) }\nfunc (s *sliceArrayEncoder) AppendInt32(v int32)            { s.elems = append(s.elems, v) }\nfunc (s *sliceArrayEncoder) AppendInt16(v int16)            { s.elems = append(s.elems, v) }\nfunc (s *sliceArrayEncoder) AppendInt8(v int8)              { s.elems = append(s.elems, v) }\nfunc (s *sliceArrayEncoder) AppendString(v string)          { s.elems = append(s.elems, v) }\nfunc (s *sliceArrayEncoder) AppendTime(v time.Time)         { s.elems = append(s.elems, v) }\nfunc (s *sliceArrayEncoder) AppendUint(v uint)              { s.elems = append(s.elems, v) }\nfunc (s *sliceArrayEncoder) AppendUint64(v uint64)          { s.elems = append(s.elems, v) }\nfunc (s *sliceArrayEncoder) AppendUint32(v uint32)          { s.elems = append(s.elems, v) }\nfunc (s *sliceArrayEncoder) AppendUint16(v uint16)          { s.elems = append(s.elems, v) }\nfunc (s *sliceArrayEncoder) AppendUint8(v uint8)            { s.elems = append(s.elems, v) }\nfunc (s *sliceArrayEncoder) AppendUintptr(v uintptr)        { s.elems = append(s.elems, v) }\n"
  },
  {
    "path": "zapcore/memory_encoder_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestMapObjectEncoderAdd(t *testing.T) {\n\t// Expected output of a turducken.\n\twantTurducken := map[string]interface{}{\n\t\t\"ducks\": []interface{}{\n\t\t\tmap[string]interface{}{\"in\": \"chicken\"},\n\t\t\tmap[string]interface{}{\"in\": \"chicken\"},\n\t\t},\n\t}\n\n\ttests := []struct {\n\t\tdesc     string\n\t\tf        func(ObjectEncoder)\n\t\texpected interface{}\n\t}{\n\t\t{\n\t\t\tdesc: \"AddObject\",\n\t\t\tf: func(e ObjectEncoder) {\n\t\t\t\tassert.NoError(t, e.AddObject(\"k\", loggable{true}), \"Expected AddObject to succeed.\")\n\t\t\t},\n\t\t\texpected: map[string]interface{}{\"loggable\": \"yes\"},\n\t\t},\n\t\t{\n\t\t\tdesc: \"AddObject (nested)\",\n\t\t\tf: func(e ObjectEncoder) {\n\t\t\t\tassert.NoError(t, e.AddObject(\"k\", turducken{}), \"Expected AddObject to succeed.\")\n\t\t\t},\n\t\t\texpected: wantTurducken,\n\t\t},\n\t\t{\n\t\t\tdesc: \"AddArray\",\n\t\t\tf: func(e ObjectEncoder) {\n\t\t\t\tassert.NoError(t, e.AddArray(\"k\", ArrayMarshalerFunc(func(arr ArrayEncoder) error {\n\t\t\t\t\tarr.AppendBool(true)\n\t\t\t\t\tarr.AppendBool(false)\n\t\t\t\t\tarr.AppendBool(true)\n\t\t\t\t\treturn nil\n\t\t\t\t})), \"Expected AddArray to succeed.\")\n\t\t\t},\n\t\t\texpected: []interface{}{true, false, true},\n\t\t},\n\t\t{\n\t\t\tdesc: \"AddArray (nested)\",\n\t\t\tf: func(e ObjectEncoder) {\n\t\t\t\tassert.NoError(t, e.AddArray(\"k\", turduckens(2)), \"Expected AddArray to succeed.\")\n\t\t\t},\n\t\t\texpected: []interface{}{wantTurducken, wantTurducken},\n\t\t},\n\t\t{\n\t\t\tdesc: \"AddArray (empty)\",\n\t\t\tf: func(e ObjectEncoder) {\n\t\t\t\tassert.NoError(t, e.AddArray(\"k\", turduckens(0)), \"Expected AddArray to succeed.\")\n\t\t\t},\n\t\t\texpected: []interface{}{},\n\t\t},\n\t\t{\n\t\t\tdesc:     \"AddBinary\",\n\t\t\tf:        func(e ObjectEncoder) { e.AddBinary(\"k\", []byte(\"foo\")) },\n\t\t\texpected: []byte(\"foo\"),\n\t\t},\n\t\t{\n\t\t\tdesc:     \"AddByteString\",\n\t\t\tf:        func(e ObjectEncoder) { e.AddByteString(\"k\", []byte(\"foo\")) },\n\t\t\texpected: \"foo\",\n\t\t},\n\t\t{\n\t\t\tdesc:     \"AddBool\",\n\t\t\tf:        func(e ObjectEncoder) { e.AddBool(\"k\", true) },\n\t\t\texpected: true,\n\t\t},\n\t\t{\n\t\t\tdesc:     \"AddComplex128\",\n\t\t\tf:        func(e ObjectEncoder) { e.AddComplex128(\"k\", 1+2i) },\n\t\t\texpected: 1 + 2i,\n\t\t},\n\t\t{\n\t\t\tdesc:     \"AddComplex64\",\n\t\t\tf:        func(e ObjectEncoder) { e.AddComplex64(\"k\", 1+2i) },\n\t\t\texpected: complex64(1 + 2i),\n\t\t},\n\t\t{\n\t\t\tdesc:     \"AddDuration\",\n\t\t\tf:        func(e ObjectEncoder) { e.AddDuration(\"k\", time.Millisecond) },\n\t\t\texpected: time.Millisecond,\n\t\t},\n\t\t{\n\t\t\tdesc:     \"AddFloat64\",\n\t\t\tf:        func(e ObjectEncoder) { e.AddFloat64(\"k\", 3.14) },\n\t\t\texpected: 3.14,\n\t\t},\n\t\t{\n\t\t\tdesc:     \"AddFloat32\",\n\t\t\tf:        func(e ObjectEncoder) { e.AddFloat32(\"k\", 3.14) },\n\t\t\texpected: float32(3.14),\n\t\t},\n\t\t{\n\t\t\tdesc:     \"AddInt\",\n\t\t\tf:        func(e ObjectEncoder) { e.AddInt(\"k\", 42) },\n\t\t\texpected: 42,\n\t\t},\n\t\t{\n\t\t\tdesc:     \"AddInt64\",\n\t\t\tf:        func(e ObjectEncoder) { e.AddInt64(\"k\", 42) },\n\t\t\texpected: int64(42),\n\t\t},\n\t\t{\n\t\t\tdesc:     \"AddInt32\",\n\t\t\tf:        func(e ObjectEncoder) { e.AddInt32(\"k\", 42) },\n\t\t\texpected: int32(42),\n\t\t},\n\t\t{\n\t\t\tdesc:     \"AddInt16\",\n\t\t\tf:        func(e ObjectEncoder) { e.AddInt16(\"k\", 42) },\n\t\t\texpected: int16(42),\n\t\t},\n\t\t{\n\t\t\tdesc:     \"AddInt8\",\n\t\t\tf:        func(e ObjectEncoder) { e.AddInt8(\"k\", 42) },\n\t\t\texpected: int8(42),\n\t\t},\n\t\t{\n\t\t\tdesc:     \"AddString\",\n\t\t\tf:        func(e ObjectEncoder) { e.AddString(\"k\", \"v\") },\n\t\t\texpected: \"v\",\n\t\t},\n\t\t{\n\t\t\tdesc:     \"AddTime\",\n\t\t\tf:        func(e ObjectEncoder) { e.AddTime(\"k\", time.Unix(0, 100)) },\n\t\t\texpected: time.Unix(0, 100),\n\t\t},\n\t\t{\n\t\t\tdesc:     \"AddUint\",\n\t\t\tf:        func(e ObjectEncoder) { e.AddUint(\"k\", 42) },\n\t\t\texpected: uint(42),\n\t\t},\n\t\t{\n\t\t\tdesc:     \"AddUint64\",\n\t\t\tf:        func(e ObjectEncoder) { e.AddUint64(\"k\", 42) },\n\t\t\texpected: uint64(42),\n\t\t},\n\t\t{\n\t\t\tdesc:     \"AddUint32\",\n\t\t\tf:        func(e ObjectEncoder) { e.AddUint32(\"k\", 42) },\n\t\t\texpected: uint32(42),\n\t\t},\n\t\t{\n\t\t\tdesc:     \"AddUint16\",\n\t\t\tf:        func(e ObjectEncoder) { e.AddUint16(\"k\", 42) },\n\t\t\texpected: uint16(42),\n\t\t},\n\t\t{\n\t\t\tdesc:     \"AddUint8\",\n\t\t\tf:        func(e ObjectEncoder) { e.AddUint8(\"k\", 42) },\n\t\t\texpected: uint8(42),\n\t\t},\n\t\t{\n\t\t\tdesc:     \"AddUintptr\",\n\t\t\tf:        func(e ObjectEncoder) { e.AddUintptr(\"k\", 42) },\n\t\t\texpected: uintptr(42),\n\t\t},\n\t\t{\n\t\t\tdesc: \"AddReflected\",\n\t\t\tf: func(e ObjectEncoder) {\n\t\t\t\tassert.NoError(t, e.AddReflected(\"k\", map[string]interface{}{\"foo\": 5}), \"Expected AddReflected to succeed.\")\n\t\t\t},\n\t\t\texpected: map[string]interface{}{\"foo\": 5},\n\t\t},\n\t\t{\n\t\t\tdesc: \"OpenNamespace\",\n\t\t\tf: func(e ObjectEncoder) {\n\t\t\t\te.OpenNamespace(\"k\")\n\t\t\t\te.AddInt(\"foo\", 1)\n\t\t\t\te.OpenNamespace(\"middle\")\n\t\t\t\te.AddInt(\"foo\", 2)\n\t\t\t\te.OpenNamespace(\"inner\")\n\t\t\t\te.AddInt(\"foo\", 3)\n\t\t\t},\n\t\t\texpected: map[string]interface{}{\n\t\t\t\t\"foo\": 1,\n\t\t\t\t\"middle\": map[string]interface{}{\n\t\t\t\t\t\"foo\": 2,\n\t\t\t\t\t\"inner\": map[string]interface{}{\n\t\t\t\t\t\t\"foo\": 3,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"object (no nested namespace) then string\",\n\t\t\tf: func(e ObjectEncoder) {\n\t\t\t\te.OpenNamespace(\"k\")\n\t\t\t\tassert.NoError(t, e.AddObject(\"obj\", maybeNamespace{false}))\n\t\t\t\te.AddString(\"not-obj\", \"should-be-outside-obj\")\n\t\t\t},\n\t\t\texpected: map[string]interface{}{\n\t\t\t\t\"obj\": map[string]interface{}{\n\t\t\t\t\t\"obj-out\": \"obj-outside-namespace\",\n\t\t\t\t},\n\t\t\t\t\"not-obj\": \"should-be-outside-obj\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"object (with nested namespace) then string\",\n\t\t\tf: func(e ObjectEncoder) {\n\t\t\t\te.OpenNamespace(\"k\")\n\t\t\t\tassert.NoError(t, e.AddObject(\"obj\", maybeNamespace{true}))\n\t\t\t\te.AddString(\"not-obj\", \"should-be-outside-obj\")\n\t\t\t},\n\t\t\texpected: map[string]interface{}{\n\t\t\t\t\"obj\": map[string]interface{}{\n\t\t\t\t\t\"obj-out\": \"obj-outside-namespace\",\n\t\t\t\t\t\"obj-namespace\": map[string]interface{}{\n\t\t\t\t\t\t\"obj-in\": \"obj-inside-namespace\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t\"not-obj\": \"should-be-outside-obj\",\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tenc := NewMapObjectEncoder()\n\t\t\ttt.f(enc)\n\t\t\tassert.Equal(t, tt.expected, enc.Fields[\"k\"], \"Unexpected encoder output.\")\n\t\t})\n\t}\n}\n\nfunc TestSliceArrayEncoderAppend(t *testing.T) {\n\ttests := []struct {\n\t\tdesc     string\n\t\tf        func(ArrayEncoder)\n\t\texpected interface{}\n\t}{\n\t\t// AppendObject and AppendArray are covered by the AddObject (nested) and\n\t\t// AddArray (nested) cases above.\n\t\t{\"AppendBool\", func(e ArrayEncoder) { e.AppendBool(true) }, true},\n\t\t{\"AppendByteString\", func(e ArrayEncoder) { e.AppendByteString([]byte(\"foo\")) }, \"foo\"},\n\t\t{\"AppendComplex128\", func(e ArrayEncoder) { e.AppendComplex128(1 + 2i) }, 1 + 2i},\n\t\t{\"AppendComplex64\", func(e ArrayEncoder) { e.AppendComplex64(1 + 2i) }, complex64(1 + 2i)},\n\t\t{\"AppendDuration\", func(e ArrayEncoder) { e.AppendDuration(time.Second) }, time.Second},\n\t\t{\"AppendFloat64\", func(e ArrayEncoder) { e.AppendFloat64(3.14) }, 3.14},\n\t\t{\"AppendFloat32\", func(e ArrayEncoder) { e.AppendFloat32(3.14) }, float32(3.14)},\n\t\t{\"AppendInt\", func(e ArrayEncoder) { e.AppendInt(42) }, 42},\n\t\t{\"AppendInt64\", func(e ArrayEncoder) { e.AppendInt64(42) }, int64(42)},\n\t\t{\"AppendInt32\", func(e ArrayEncoder) { e.AppendInt32(42) }, int32(42)},\n\t\t{\"AppendInt16\", func(e ArrayEncoder) { e.AppendInt16(42) }, int16(42)},\n\t\t{\"AppendInt8\", func(e ArrayEncoder) { e.AppendInt8(42) }, int8(42)},\n\t\t{\"AppendString\", func(e ArrayEncoder) { e.AppendString(\"foo\") }, \"foo\"},\n\t\t{\"AppendTime\", func(e ArrayEncoder) { e.AppendTime(time.Unix(0, 100)) }, time.Unix(0, 100)},\n\t\t{\"AppendUint\", func(e ArrayEncoder) { e.AppendUint(42) }, uint(42)},\n\t\t{\"AppendUint64\", func(e ArrayEncoder) { e.AppendUint64(42) }, uint64(42)},\n\t\t{\"AppendUint32\", func(e ArrayEncoder) { e.AppendUint32(42) }, uint32(42)},\n\t\t{\"AppendUint16\", func(e ArrayEncoder) { e.AppendUint16(42) }, uint16(42)},\n\t\t{\"AppendUint8\", func(e ArrayEncoder) { e.AppendUint8(42) }, uint8(42)},\n\t\t{\"AppendUintptr\", func(e ArrayEncoder) { e.AppendUintptr(42) }, uintptr(42)},\n\t\t{\n\t\t\tdesc: \"AppendReflected\",\n\t\t\tf: func(e ArrayEncoder) {\n\t\t\t\tassert.NoError(t, e.AppendReflected(map[string]interface{}{\"foo\": 5}))\n\t\t\t},\n\t\t\texpected: map[string]interface{}{\"foo\": 5},\n\t\t},\n\t\t{\n\t\t\tdesc: \"AppendArray (arrays of arrays)\",\n\t\t\tf: func(e ArrayEncoder) {\n\t\t\t\terr := e.AppendArray(ArrayMarshalerFunc(func(inner ArrayEncoder) error {\n\t\t\t\t\tinner.AppendBool(true)\n\t\t\t\t\tinner.AppendBool(false)\n\t\t\t\t\treturn nil\n\t\t\t\t}))\n\t\t\t\tassert.NoError(t, err)\n\t\t\t},\n\t\t\texpected: []interface{}{true, false},\n\t\t},\n\t\t{\n\t\t\tdesc: \"object (no nested namespace) then string\",\n\t\t\tf: func(e ArrayEncoder) {\n\t\t\t\terr := e.AppendArray(ArrayMarshalerFunc(func(inner ArrayEncoder) error {\n\t\t\t\t\terr := inner.AppendObject(maybeNamespace{false})\n\t\t\t\t\tinner.AppendString(\"should-be-outside-obj\")\n\t\t\t\t\treturn err\n\t\t\t\t}))\n\t\t\t\tassert.NoError(t, err)\n\t\t\t},\n\t\t\texpected: []interface{}{\n\t\t\t\tmap[string]interface{}{\n\t\t\t\t\t\"obj-out\": \"obj-outside-namespace\",\n\t\t\t\t},\n\t\t\t\t\"should-be-outside-obj\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"object (with nested namespace) then string\",\n\t\t\tf: func(e ArrayEncoder) {\n\t\t\t\terr := e.AppendArray(ArrayMarshalerFunc(func(inner ArrayEncoder) error {\n\t\t\t\t\terr := inner.AppendObject(maybeNamespace{true})\n\t\t\t\t\tinner.AppendString(\"should-be-outside-obj\")\n\t\t\t\t\treturn err\n\t\t\t\t}))\n\t\t\t\tassert.NoError(t, err)\n\t\t\t},\n\t\t\texpected: []interface{}{\n\t\t\t\tmap[string]interface{}{\n\t\t\t\t\t\"obj-out\": \"obj-outside-namespace\",\n\t\t\t\t\t\"obj-namespace\": map[string]interface{}{\n\t\t\t\t\t\t\"obj-in\": \"obj-inside-namespace\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t\"should-be-outside-obj\",\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tenc := NewMapObjectEncoder()\n\t\t\tassert.NoError(t, enc.AddArray(\"k\", ArrayMarshalerFunc(func(arr ArrayEncoder) error {\n\t\t\t\ttt.f(arr)\n\t\t\t\ttt.f(arr)\n\t\t\t\treturn nil\n\t\t\t})), \"Expected AddArray to succeed.\")\n\n\t\t\tarr, ok := enc.Fields[\"k\"].([]interface{})\n\t\t\trequire.True(t, ok, \"Test case %s didn't encode an array.\", tt.desc)\n\t\t\tassert.Equal(t, []interface{}{tt.expected, tt.expected}, arr, \"Unexpected encoder output.\")\n\t\t})\n\t}\n}\n\nfunc TestMapObjectEncoderReflectionFailures(t *testing.T) {\n\tenc := NewMapObjectEncoder()\n\tassert.Error(t, enc.AddObject(\"object\", loggable{false}), \"Expected AddObject to fail.\")\n\tassert.Equal(\n\t\tt,\n\t\tmap[string]interface{}{\"object\": map[string]interface{}{}},\n\t\tenc.Fields,\n\t\t\"Expected encoder to use empty values on errors.\",\n\t)\n}\n"
  },
  {
    "path": "zapcore/reflected_encoder.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n)\n\n// ReflectedEncoder serializes log fields that can't be serialized with Zap's\n// JSON encoder. These have the ReflectType field type.\n// Use EncoderConfig.NewReflectedEncoder to set this.\ntype ReflectedEncoder interface {\n\t// Encode encodes and writes to the underlying data stream.\n\tEncode(interface{}) error\n}\n\nfunc defaultReflectedEncoder(w io.Writer) ReflectedEncoder {\n\tenc := json.NewEncoder(w)\n\t// For consistency with our custom JSON encoder.\n\tenc.SetEscapeHTML(false)\n\treturn enc\n}\n"
  },
  {
    "path": "zapcore/sampler.go",
    "content": "// Copyright (c) 2016-2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport (\n\t\"sync/atomic\"\n\t\"time\"\n)\n\nconst (\n\t_numLevels        = _maxLevel - _minLevel + 1\n\t_countersPerLevel = 4096\n)\n\ntype counter struct {\n\tresetAt atomic.Int64\n\tcounter atomic.Uint64\n}\n\ntype counters [_numLevels][_countersPerLevel]counter\n\nfunc newCounters() *counters {\n\treturn &counters{}\n}\n\nfunc (cs *counters) get(lvl Level, key string) *counter {\n\ti := lvl - _minLevel\n\tj := fnv32a(key) % _countersPerLevel\n\treturn &cs[i][j]\n}\n\n// fnv32a, adapted from \"hash/fnv\", but without a []byte(string) alloc\nfunc fnv32a(s string) uint32 {\n\tconst (\n\t\toffset32 = 2166136261\n\t\tprime32  = 16777619\n\t)\n\thash := uint32(offset32)\n\tfor i := 0; i < len(s); i++ {\n\t\thash ^= uint32(s[i])\n\t\thash *= prime32\n\t}\n\treturn hash\n}\n\nfunc (c *counter) IncCheckReset(t time.Time, tick time.Duration) uint64 {\n\ttn := t.UnixNano()\n\tresetAfter := c.resetAt.Load()\n\tif resetAfter > tn {\n\t\treturn c.counter.Add(1)\n\t}\n\n\tc.counter.Store(1)\n\n\tnewResetAfter := tn + tick.Nanoseconds()\n\tif !c.resetAt.CompareAndSwap(resetAfter, newResetAfter) {\n\t\t// We raced with another goroutine trying to reset, and it also reset\n\t\t// the counter to 1, so we need to reincrement the counter.\n\t\treturn c.counter.Add(1)\n\t}\n\n\treturn 1\n}\n\n// SamplingDecision is a decision represented as a bit field made by sampler.\n// More decisions may be added in the future.\ntype SamplingDecision uint32\n\nconst (\n\t// LogDropped indicates that the Sampler dropped a log entry.\n\tLogDropped SamplingDecision = 1 << iota\n\t// LogSampled indicates that the Sampler sampled a log entry.\n\tLogSampled\n)\n\n// optionFunc wraps a func so it satisfies the SamplerOption interface.\ntype optionFunc func(*sampler)\n\nfunc (f optionFunc) apply(s *sampler) {\n\tf(s)\n}\n\n// SamplerOption configures a Sampler.\ntype SamplerOption interface {\n\tapply(*sampler)\n}\n\n// nopSamplingHook is the default hook used by sampler.\nfunc nopSamplingHook(Entry, SamplingDecision) {}\n\n// SamplerHook registers a function  which will be called when Sampler makes a\n// decision.\n//\n// This hook may be used to get visibility into the performance of the sampler.\n// For example, use it to track metrics of dropped versus sampled logs.\n//\n//\tvar dropped atomic.Int64\n//\tzapcore.SamplerHook(func(ent zapcore.Entry, dec zapcore.SamplingDecision) {\n//\t  if dec&zapcore.LogDropped > 0 {\n//\t    dropped.Inc()\n//\t  }\n//\t})\nfunc SamplerHook(hook func(entry Entry, dec SamplingDecision)) SamplerOption {\n\treturn optionFunc(func(s *sampler) {\n\t\ts.hook = hook\n\t})\n}\n\n// NewSamplerWithOptions creates a Core that samples incoming entries, which\n// caps the CPU and I/O load of logging while attempting to preserve a\n// representative subset of your logs.\n//\n// Zap samples by logging the first N entries with a given level and message\n// each tick. If more Entries with the same level and message are seen during\n// the same interval, every Mth message is logged and the rest are dropped.\n//\n// For example,\n//\n//\tcore = NewSamplerWithOptions(core, time.Second, 10, 5)\n//\n// This will log the first 10 log entries with the same level and message\n// in a one second interval as-is. Following that, it will allow through\n// every 5th log entry with the same level and message in that interval.\n//\n// If thereafter is zero, the Core will drop all log entries after the first N\n// in that interval.\n//\n// Sampler can be configured to report sampling decisions with the SamplerHook\n// option.\n//\n// Keep in mind that Zap's sampling implementation is optimized for speed over\n// absolute precision; under load, each tick may be slightly over- or\n// under-sampled.\nfunc NewSamplerWithOptions(core Core, tick time.Duration, first, thereafter int, opts ...SamplerOption) Core {\n\ts := &sampler{\n\t\tCore:       core,\n\t\ttick:       tick,\n\t\tcounts:     newCounters(),\n\t\tfirst:      uint64(first),\n\t\tthereafter: uint64(thereafter),\n\t\thook:       nopSamplingHook,\n\t}\n\tfor _, opt := range opts {\n\t\topt.apply(s)\n\t}\n\n\treturn s\n}\n\ntype sampler struct {\n\tCore\n\n\tcounts            *counters\n\ttick              time.Duration\n\tfirst, thereafter uint64\n\thook              func(Entry, SamplingDecision)\n}\n\nvar (\n\t_ Core           = (*sampler)(nil)\n\t_ leveledEnabler = (*sampler)(nil)\n)\n\n// NewSampler creates a Core that samples incoming entries, which\n// caps the CPU and I/O load of logging while attempting to preserve a\n// representative subset of your logs.\n//\n// Zap samples by logging the first N entries with a given level and message\n// each tick. If more Entries with the same level and message are seen during\n// the same interval, every Mth message is logged and the rest are dropped.\n//\n// Keep in mind that zap's sampling implementation is optimized for speed over\n// absolute precision; under load, each tick may be slightly over- or\n// under-sampled.\n//\n// Deprecated: use NewSamplerWithOptions.\nfunc NewSampler(core Core, tick time.Duration, first, thereafter int) Core {\n\treturn NewSamplerWithOptions(core, tick, first, thereafter)\n}\n\nfunc (s *sampler) Level() Level {\n\treturn LevelOf(s.Core)\n}\n\nfunc (s *sampler) With(fields []Field) Core {\n\treturn &sampler{\n\t\tCore:       s.Core.With(fields),\n\t\ttick:       s.tick,\n\t\tcounts:     s.counts,\n\t\tfirst:      s.first,\n\t\tthereafter: s.thereafter,\n\t\thook:       s.hook,\n\t}\n}\n\nfunc (s *sampler) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {\n\tif !s.Enabled(ent.Level) {\n\t\treturn ce\n\t}\n\n\tif ent.Level >= _minLevel && ent.Level <= _maxLevel {\n\t\tcounter := s.counts.get(ent.Level, ent.Message)\n\t\tn := counter.IncCheckReset(ent.Time, s.tick)\n\t\tif n > s.first && (s.thereafter == 0 || (n-s.first)%s.thereafter != 0) {\n\t\t\ts.hook(ent, LogDropped)\n\t\t\treturn ce\n\t\t}\n\t\ts.hook(ent, LogSampled)\n\t}\n\treturn s.Core.Check(ent, ce)\n}\n"
  },
  {
    "path": "zapcore/sampler_bench_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore_test\n\nimport (\n\t\"fmt\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/zap/internal/ztest\"\n\n\t//revive:disable:dot-imports\n\t. \"go.uber.org/zap/zapcore\"\n)\n\nvar counterTestCases = [][]string{\n\t// some stuff I made up\n\t{\n\t\t\"foo\",\n\t\t\"bar\",\n\t\t\"baz\",\n\t\t\"alpha\",\n\t\t\"bravo\",\n\t\t\"charlie\",\n\t\t\"delta\",\n\t},\n\n\t// shuf -n50 /usr/share/dict/words\n\t{\n\t\t\"unbracing\",\n\t\t\"stereotomy\",\n\t\t\"supranervian\",\n\t\t\"moaning\",\n\t\t\"exchangeability\",\n\t\t\"gunyang\",\n\t\t\"sulcation\",\n\t\t\"dariole\",\n\t\t\"archheresy\",\n\t\t\"synchronistically\",\n\t\t\"clips\",\n\t\t\"unsanctioned\",\n\t\t\"Argoan\",\n\t\t\"liparomphalus\",\n\t\t\"layship\",\n\t\t\"Fregatae\",\n\t\t\"microzoology\",\n\t\t\"glaciaria\",\n\t\t\"Frugivora\",\n\t\t\"patterist\",\n\t\t\"Grossulariaceae\",\n\t\t\"lithotint\",\n\t\t\"bargander\",\n\t\t\"opisthographical\",\n\t\t\"cacography\",\n\t\t\"chalkstone\",\n\t\t\"nonsubstantialism\",\n\t\t\"sardonicism\",\n\t\t\"calamiform\",\n\t\t\"lodginghouse\",\n\t\t\"predisposedly\",\n\t\t\"topotypic\",\n\t\t\"broideress\",\n\t\t\"outrange\",\n\t\t\"gingivolabial\",\n\t\t\"monoazo\",\n\t\t\"sparlike\",\n\t\t\"concameration\",\n\t\t\"untoothed\",\n\t\t\"Camorrism\",\n\t\t\"reissuer\",\n\t\t\"soap\",\n\t\t\"palaiotype\",\n\t\t\"countercharm\",\n\t\t\"yellowbird\",\n\t\t\"palterly\",\n\t\t\"writinger\",\n\t\t\"boatfalls\",\n\t\t\"tuglike\",\n\t\t\"underbitten\",\n\t},\n\n\t// shuf -n100 /usr/share/dict/words\n\t{\n\t\t\"rooty\",\n\t\t\"malcultivation\",\n\t\t\"degrade\",\n\t\t\"pseudoindependent\",\n\t\t\"stillatory\",\n\t\t\"antiseptize\",\n\t\t\"protoamphibian\",\n\t\t\"antiar\",\n\t\t\"Esther\",\n\t\t\"pseudelminth\",\n\t\t\"superfluitance\",\n\t\t\"teallite\",\n\t\t\"disunity\",\n\t\t\"spirignathous\",\n\t\t\"vergency\",\n\t\t\"myliobatid\",\n\t\t\"inosic\",\n\t\t\"overabstemious\",\n\t\t\"patriarchally\",\n\t\t\"foreimagine\",\n\t\t\"coetaneity\",\n\t\t\"hemimellitene\",\n\t\t\"hyperspatial\",\n\t\t\"aulophyte\",\n\t\t\"electropoion\",\n\t\t\"antitrope\",\n\t\t\"Amarantus\",\n\t\t\"smaltine\",\n\t\t\"lighthead\",\n\t\t\"syntonically\",\n\t\t\"incubous\",\n\t\t\"versation\",\n\t\t\"cirsophthalmia\",\n\t\t\"Ulidian\",\n\t\t\"homoeography\",\n\t\t\"Velella\",\n\t\t\"Hecatean\",\n\t\t\"serfage\",\n\t\t\"Spermaphyta\",\n\t\t\"palatoplasty\",\n\t\t\"electroextraction\",\n\t\t\"aconite\",\n\t\t\"avirulence\",\n\t\t\"initiator\",\n\t\t\"besmear\",\n\t\t\"unrecognizably\",\n\t\t\"euphoniousness\",\n\t\t\"balbuties\",\n\t\t\"pascuage\",\n\t\t\"quebracho\",\n\t\t\"Yakala\",\n\t\t\"auriform\",\n\t\t\"sevenbark\",\n\t\t\"superorganism\",\n\t\t\"telesterion\",\n\t\t\"ensand\",\n\t\t\"nagaika\",\n\t\t\"anisuria\",\n\t\t\"etching\",\n\t\t\"soundingly\",\n\t\t\"grumpish\",\n\t\t\"drillmaster\",\n\t\t\"perfumed\",\n\t\t\"dealkylate\",\n\t\t\"anthracitiferous\",\n\t\t\"predefiance\",\n\t\t\"sulphoxylate\",\n\t\t\"freeness\",\n\t\t\"untucking\",\n\t\t\"misworshiper\",\n\t\t\"Nestorianize\",\n\t\t\"nonegoistical\",\n\t\t\"construe\",\n\t\t\"upstroke\",\n\t\t\"teated\",\n\t\t\"nasolachrymal\",\n\t\t\"Mastodontidae\",\n\t\t\"gallows\",\n\t\t\"radioluminescent\",\n\t\t\"uncourtierlike\",\n\t\t\"phasmatrope\",\n\t\t\"Clunisian\",\n\t\t\"drainage\",\n\t\t\"sootless\",\n\t\t\"brachyfacial\",\n\t\t\"antiheroism\",\n\t\t\"irreligionize\",\n\t\t\"ked\",\n\t\t\"unfact\",\n\t\t\"nonprofessed\",\n\t\t\"milady\",\n\t\t\"conjecture\",\n\t\t\"Arctomys\",\n\t\t\"guapilla\",\n\t\t\"Sassenach\",\n\t\t\"emmetrope\",\n\t\t\"rosewort\",\n\t\t\"raphidiferous\",\n\t\t\"pooh\",\n\t\t\"Tyndallize\",\n\t},\n}\n\nfunc BenchmarkSampler_Check(b *testing.B) {\n\tfor _, keys := range counterTestCases {\n\t\tb.Run(fmt.Sprintf(\"%v keys\", len(keys)), func(b *testing.B) {\n\t\t\tfac := NewSamplerWithOptions(\n\t\t\t\tNewCore(\n\t\t\t\t\tNewJSONEncoder(testEncoderConfig()),\n\t\t\t\t\t&ztest.Discarder{},\n\t\t\t\t\tDebugLevel,\n\t\t\t\t),\n\t\t\t\ttime.Millisecond, 1, 1000)\n\t\t\tb.ResetTimer()\n\t\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\t\ti := 0\n\t\t\t\tfor pb.Next() {\n\t\t\t\t\tent := Entry{\n\t\t\t\t\t\tLevel:   DebugLevel + Level(i%4),\n\t\t\t\t\t\tMessage: keys[i],\n\t\t\t\t\t}\n\t\t\t\t\t_ = fac.Check(ent, nil)\n\t\t\t\t\ti++\n\t\t\t\t\tif n := len(keys); i >= n {\n\t\t\t\t\t\ti -= n\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc makeSamplerCountingHook() (func(_ Entry, dec SamplingDecision), *atomic.Int64, *atomic.Int64) {\n\tdroppedCount := new(atomic.Int64)\n\tsampledCount := new(atomic.Int64)\n\th := func(_ Entry, dec SamplingDecision) {\n\t\tif dec&LogDropped > 0 {\n\t\t\tdroppedCount.Add(1)\n\t\t} else if dec&LogSampled > 0 {\n\t\t\tsampledCount.Add(1)\n\t\t}\n\t}\n\treturn h, droppedCount, sampledCount\n}\n\nfunc BenchmarkSampler_CheckWithHook(b *testing.B) {\n\thook, dropped, sampled := makeSamplerCountingHook()\n\tfor _, keys := range counterTestCases {\n\t\tb.Run(fmt.Sprintf(\"%v keys\", len(keys)), func(b *testing.B) {\n\t\t\tfac := NewSamplerWithOptions(\n\t\t\t\tNewCore(\n\t\t\t\t\tNewJSONEncoder(testEncoderConfig()),\n\t\t\t\t\t&ztest.Discarder{},\n\t\t\t\t\tDebugLevel,\n\t\t\t\t),\n\t\t\t\ttime.Millisecond,\n\t\t\t\t1,\n\t\t\t\t1000,\n\t\t\t\tSamplerHook(hook),\n\t\t\t)\n\t\t\tb.ResetTimer()\n\t\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\t\ti := 0\n\t\t\t\tfor pb.Next() {\n\t\t\t\t\tent := Entry{\n\t\t\t\t\t\tLevel:   DebugLevel + Level(i%4),\n\t\t\t\t\t\tMessage: keys[i],\n\t\t\t\t\t}\n\t\t\t\t\t_ = fac.Check(ent, nil)\n\t\t\t\t\ti++\n\t\t\t\t\tif n := len(keys); i >= n {\n\t\t\t\t\t\ti -= n\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t\t// We expect to see 1000 dropped messages for every sampled per settings,\n\t\t\t// with a delta due to less 1000 messages getting dropped after initial one\n\t\t\t// is sampled.\n\t\t\tassert.Greater(b, dropped.Load()/1000, sampled.Load()-1000)\n\t\t\tdropped.Store(0)\n\t\t\tsampled.Store(0)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "zapcore/sampler_test.go",
    "content": "// Copyright (c) 2016-2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore_test\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.uber.org/zap/internal/ztest\"\n\t//revive:disable:dot-imports\n\t. \"go.uber.org/zap/zapcore\"\n\t\"go.uber.org/zap/zaptest/observer\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc fakeSampler(lvl LevelEnabler, tick time.Duration, first, thereafter int) (Core, *observer.ObservedLogs) {\n\tcore, logs := observer.New(lvl)\n\t// Keep using deprecated constructor for cc.\n\tcore = NewSampler(core, tick, first, thereafter)\n\treturn core, logs\n}\n\nfunc assertSequence(t testing.TB, logs []observer.LoggedEntry, lvl Level, seq ...int64) {\n\tseen := make([]int64, len(logs))\n\tfor i, entry := range logs {\n\t\trequire.Equal(t, \"\", entry.Message, \"Message wasn't created by writeSequence.\")\n\t\trequire.Equal(t, 1, len(entry.Context), \"Unexpected number of fields.\")\n\t\trequire.Equal(t, lvl, entry.Level, \"Unexpected level.\")\n\t\tf := entry.Context[0]\n\t\trequire.Equal(t, \"iter\", f.Key, \"Unexpected field key.\")\n\t\trequire.Equal(t, Int64Type, f.Type, \"Unexpected field type\")\n\t\tseen[i] = f.Integer\n\t}\n\tassert.Equal(t, seq, seen, \"Unexpected sequence logged at level %v.\", lvl)\n}\n\nfunc writeSequence(core Core, n int, lvl Level) {\n\t// All tests using writeSequence verify that counters are shared between\n\t// parent and child cores.\n\tcore = core.With([]Field{makeInt64Field(\"iter\", n)})\n\tif ce := core.Check(Entry{Level: lvl, Time: time.Now()}, nil); ce != nil {\n\t\tce.Write()\n\t}\n}\n\nfunc TestSampler(t *testing.T) {\n\tfor _, lvl := range []Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel, PanicLevel, FatalLevel} {\n\t\tsampler, logs := fakeSampler(DebugLevel, time.Minute, 2, 3)\n\n\t\t// Ensure that counts aren't shared between levels.\n\t\tprobeLevel := DebugLevel\n\t\tif lvl == DebugLevel {\n\t\t\tprobeLevel = InfoLevel\n\t\t}\n\t\tfor i := 0; i < 10; i++ {\n\t\t\twriteSequence(sampler, 1, probeLevel)\n\t\t}\n\t\t// Clear any output.\n\t\tlogs.TakeAll()\n\n\t\tfor i := 1; i < 10; i++ {\n\t\t\twriteSequence(sampler, i, lvl)\n\t\t}\n\t\tassertSequence(t, logs.TakeAll(), lvl, 1, 2, 5, 8)\n\t}\n}\n\nfunc TestLevelOfSampler(t *testing.T) {\n\tlevels := []Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel, PanicLevel, FatalLevel}\n\tfor _, lvl := range levels {\n\t\tlvl := lvl\n\t\tt.Run(lvl.String(), func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tsampler, _ := fakeSampler(lvl, time.Minute, 2, 3)\n\t\t\tassert.Equal(t, lvl, LevelOf(sampler), \"Sampler level did not match.\")\n\t\t})\n\t}\n}\n\nfunc TestSamplerDisabledLevels(t *testing.T) {\n\tsampler, logs := fakeSampler(InfoLevel, time.Minute, 1, 100)\n\n\t// Shouldn't be counted, because debug logging isn't enabled.\n\twriteSequence(sampler, 1, DebugLevel)\n\twriteSequence(sampler, 2, InfoLevel)\n\tassertSequence(t, logs.TakeAll(), InfoLevel, 2)\n}\n\nfunc TestSamplerTicking(t *testing.T) {\n\t// Ensure that we're resetting the sampler's counter every tick.\n\tsampler, logs := fakeSampler(DebugLevel, 10*time.Millisecond, 5, 10)\n\n\t// If we log five or fewer messages every tick, none of them should be\n\t// dropped.\n\tfor tick := 0; tick < 2; tick++ {\n\t\tfor i := 1; i <= 5; i++ {\n\t\t\twriteSequence(sampler, i, InfoLevel)\n\t\t}\n\t\tztest.Sleep(15 * time.Millisecond)\n\t}\n\tassertSequence(\n\t\tt,\n\t\tlogs.TakeAll(),\n\t\tInfoLevel,\n\t\t1, 2, 3, 4, 5, // first tick\n\t\t1, 2, 3, 4, 5, // second tick\n\t)\n\n\t// If we log quickly, we should drop some logs. The first five statements\n\t// each tick should be logged, then every tenth.\n\tfor tick := 0; tick < 3; tick++ {\n\t\tfor i := 1; i < 18; i++ {\n\t\t\twriteSequence(sampler, i, InfoLevel)\n\t\t}\n\t\tztest.Sleep(10 * time.Millisecond)\n\t}\n\n\tassertSequence(\n\t\tt,\n\t\tlogs.TakeAll(),\n\t\tInfoLevel,\n\t\t1, 2, 3, 4, 5, 15, // first tick\n\t\t1, 2, 3, 4, 5, 15, // second tick\n\t\t1, 2, 3, 4, 5, 15, // third tick\n\t)\n}\n\ntype countingCore struct {\n\tlogs atomic.Uint32\n}\n\nfunc (c *countingCore) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {\n\treturn ce.AddCore(ent, c)\n}\n\nfunc (c *countingCore) Write(Entry, []Field) error {\n\tc.logs.Add(1)\n\treturn nil\n}\n\nfunc (c *countingCore) With([]Field) Core { return c }\nfunc (*countingCore) Enabled(Level) bool  { return true }\nfunc (*countingCore) Sync() error         { return nil }\n\nfunc TestSamplerConcurrent(t *testing.T) {\n\tconst (\n\t\tlogsPerTick   = 10\n\t\tnumMessages   = 5\n\t\tnumTicks      = 25\n\t\tnumGoroutines = 10\n\t\ttick          = 10 * time.Millisecond\n\n\t\t// We'll make a total of,\n\t\t// (numGoroutines * numTicks * logsPerTick * 2) log attempts\n\t\t// with numMessages unique messages.\n\t\tnumLogAttempts = numGoroutines * logsPerTick * numTicks * 2\n\t\t// Of those, we'll accept (logsPerTick * numTicks) entries\n\t\t// for each unique message.\n\t\texpectedCount = numMessages * logsPerTick * numTicks\n\t\t// The rest will be dropped.\n\t\texpectedDropped = numLogAttempts - expectedCount\n\t)\n\n\tclock := ztest.NewMockClock()\n\n\tcc := &countingCore{}\n\n\thook, dropped, sampled := makeSamplerCountingHook()\n\tsampler := NewSamplerWithOptions(cc, tick, logsPerTick, 100000, SamplerHook(hook))\n\n\tstop := make(chan struct{})\n\tvar wg sync.WaitGroup\n\tfor i := 0; i < numGoroutines; i++ {\n\t\twg.Add(1)\n\t\tgo func(i int, ticker *time.Ticker) {\n\t\t\tdefer wg.Done()\n\t\t\tdefer ticker.Stop()\n\n\t\t\tfor {\n\t\t\t\tselect {\n\t\t\t\tcase <-stop:\n\t\t\t\t\treturn\n\n\t\t\t\tcase <-ticker.C:\n\t\t\t\t\tfor j := 0; j < logsPerTick*2; j++ {\n\t\t\t\t\t\tmsg := fmt.Sprintf(\"msg%v\", i%numMessages)\n\t\t\t\t\t\tent := Entry{\n\t\t\t\t\t\t\tLevel:   DebugLevel,\n\t\t\t\t\t\t\tMessage: msg,\n\t\t\t\t\t\t\tTime:    clock.Now(),\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ce := sampler.Check(ent, nil); ce != nil {\n\t\t\t\t\t\t\tce.Write()\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Give a chance for other goroutines to run.\n\t\t\t\t\t\truntime.Gosched()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}(i, clock.NewTicker(tick))\n\t}\n\n\tclock.Add(tick * numTicks)\n\tclose(stop)\n\twg.Wait()\n\n\tassert.Equal(\n\t\tt,\n\t\texpectedCount,\n\t\tint(cc.logs.Load()),\n\t\t\"Unexpected number of logs\",\n\t)\n\tassert.Equal(t,\n\t\texpectedCount,\n\t\tint(sampled.Load()),\n\t\t\"Unexpected number of logs sampled\",\n\t)\n\tassert.Equal(t,\n\t\texpectedDropped,\n\t\tint(dropped.Load()),\n\t\t\"Unexpected number of logs dropped\",\n\t)\n}\n\nfunc TestSamplerRaces(t *testing.T) {\n\tsampler, _ := fakeSampler(DebugLevel, time.Minute, 1, 1000)\n\n\tvar wg sync.WaitGroup\n\tstart := make(chan struct{})\n\n\tfor i := 0; i < 100; i++ {\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\t<-start\n\t\t\tfor j := 0; j < 100; j++ {\n\t\t\t\twriteSequence(sampler, j, InfoLevel)\n\t\t\t}\n\t\t\twg.Done()\n\t\t}()\n\t}\n\n\tclose(start)\n\twg.Wait()\n}\n\nfunc TestSamplerUnknownLevels(t *testing.T) {\n\t// Prove that out-of-bounds levels don't panic.\n\tunknownLevels := []Level{\n\t\tDebugLevel - 1,\n\t\tFatalLevel + 1,\n\t}\n\n\tfor _, lvl := range unknownLevels {\n\t\tt.Run(lvl.String(), func(t *testing.T) {\n\t\t\tsampler, logs := fakeSampler(lvl, time.Minute, 2, 3)\n\t\t\tfor i := 1; i < 10; i++ {\n\t\t\t\twriteSequence(sampler, i, lvl)\n\t\t\t}\n\n\t\t\t// Expect no sampling for unknown levels.\n\t\t\tassertSequence(t, logs.TakeAll(), lvl, 1, 2, 3, 4, 5, 6, 7, 8, 9)\n\t\t})\n\t}\n}\n\nfunc TestSamplerWithZeroThereafter(t *testing.T) {\n\tvar counter countingCore\n\n\t// Logs two messages per second.\n\tsampler := NewSamplerWithOptions(&counter, time.Second, 2, 0)\n\n\tnow := time.Now()\n\n\tfor i := 0; i < 1000; i++ {\n\t\tent := Entry{\n\t\t\tLevel:   InfoLevel,\n\t\t\tMessage: \"msg\",\n\t\t\tTime:    now,\n\t\t}\n\t\tif ce := sampler.Check(ent, nil); ce != nil {\n\t\t\tce.Write()\n\t\t}\n\t}\n\n\tassert.Equal(t, 2, int(counter.logs.Load()),\n\t\t\"Unexpected number of logs\")\n\n\tnow = now.Add(time.Second)\n\n\tfor i := 0; i < 1000; i++ {\n\t\tent := Entry{\n\t\t\tLevel:   InfoLevel,\n\t\t\tMessage: \"msg\",\n\t\t\tTime:    now,\n\t\t}\n\t\tif ce := sampler.Check(ent, nil); ce != nil {\n\t\t\tce.Write()\n\t\t}\n\t}\n\n\tassert.Equal(t, 4, int(counter.logs.Load()),\n\t\t\"Unexpected number of logs\")\n}\n"
  },
  {
    "path": "zapcore/tee.go",
    "content": "// Copyright (c) 2016-2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport \"go.uber.org/multierr\"\n\ntype multiCore []Core\n\nvar (\n\t_ leveledEnabler = multiCore(nil)\n\t_ Core           = multiCore(nil)\n)\n\n// NewTee creates a Core that duplicates log entries into two or more\n// underlying Cores.\n//\n// Calling it with a single Core returns the input unchanged, and calling\n// it with no input returns a no-op Core.\nfunc NewTee(cores ...Core) Core {\n\tswitch len(cores) {\n\tcase 0:\n\t\treturn NewNopCore()\n\tcase 1:\n\t\treturn cores[0]\n\tdefault:\n\t\treturn multiCore(cores)\n\t}\n}\n\nfunc (mc multiCore) With(fields []Field) Core {\n\tclone := make(multiCore, len(mc))\n\tfor i := range mc {\n\t\tclone[i] = mc[i].With(fields)\n\t}\n\treturn clone\n}\n\nfunc (mc multiCore) Level() Level {\n\tminLvl := _maxLevel // mc is never empty\n\tfor i := range mc {\n\t\tif lvl := LevelOf(mc[i]); lvl < minLvl {\n\t\t\tminLvl = lvl\n\t\t}\n\t}\n\treturn minLvl\n}\n\nfunc (mc multiCore) Enabled(lvl Level) bool {\n\tfor i := range mc {\n\t\tif mc[i].Enabled(lvl) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (mc multiCore) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {\n\tfor i := range mc {\n\t\tce = mc[i].Check(ent, ce)\n\t}\n\treturn ce\n}\n\nfunc (mc multiCore) Write(ent Entry, fields []Field) error {\n\tvar err error\n\tfor i := range mc {\n\t\terr = multierr.Append(err, mc[i].Write(ent, fields))\n\t}\n\treturn err\n}\n\nfunc (mc multiCore) Sync() error {\n\tvar err error\n\tfor i := range mc {\n\t\terr = multierr.Append(err, mc[i].Sync())\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "zapcore/tee_logger_bench_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore_test\n\nimport (\n\t\"testing\"\n\n\t\"go.uber.org/zap/internal/ztest\"\n\t//revive:disable:dot-imports\n\t. \"go.uber.org/zap/zapcore\"\n)\n\nfunc withBenchedTee(b *testing.B, f func(Core)) {\n\tfac := NewTee(\n\t\tNewCore(NewJSONEncoder(testEncoderConfig()), &ztest.Discarder{}, DebugLevel),\n\t\tNewCore(NewJSONEncoder(testEncoderConfig()), &ztest.Discarder{}, InfoLevel),\n\t)\n\tb.ResetTimer()\n\tf(fac)\n}\n\nfunc BenchmarkTeeCheck(b *testing.B) {\n\tcases := []struct {\n\t\tlvl Level\n\t\tmsg string\n\t}{\n\t\t{DebugLevel, \"foo\"},\n\t\t{InfoLevel, \"bar\"},\n\t\t{WarnLevel, \"baz\"},\n\t\t{ErrorLevel, \"babble\"},\n\t}\n\twithBenchedTee(b, func(core Core) {\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\ti := 0\n\t\t\tfor pb.Next() {\n\t\t\t\ttt := cases[i]\n\t\t\t\tentry := Entry{Level: tt.lvl, Message: tt.msg}\n\t\t\t\tif cm := core.Check(entry, nil); cm != nil {\n\t\t\t\t\tcm.Write(Field{Key: \"i\", Integer: int64(i), Type: Int64Type})\n\t\t\t\t}\n\t\t\t\ti = (i + 1) % len(cases)\n\t\t\t}\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "zapcore/tee_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore_test\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"go.uber.org/zap/internal/ztest\"\n\t//revive:disable:dot-imports\n\t. \"go.uber.org/zap/zapcore\"\n\t\"go.uber.org/zap/zaptest/observer\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc withTee(f func(core Core, debugLogs, warnLogs *observer.ObservedLogs)) {\n\tdebugLogger, debugLogs := observer.New(DebugLevel)\n\twarnLogger, warnLogs := observer.New(WarnLevel)\n\ttee := NewTee(debugLogger, warnLogger)\n\tf(tee, debugLogs, warnLogs)\n}\n\nfunc TestTeeUnusualInput(t *testing.T) {\n\t// Verify that Tee handles receiving one and no inputs correctly.\n\tt.Run(\"one input\", func(t *testing.T) {\n\t\tobs, _ := observer.New(DebugLevel)\n\t\tassert.Equal(t, obs, NewTee(obs), \"Expected to return single inputs unchanged.\")\n\t})\n\tt.Run(\"no input\", func(t *testing.T) {\n\t\tassert.Equal(t, NewNopCore(), NewTee(), \"Expected to return NopCore.\")\n\t})\n}\n\nfunc TestLevelOfTee(t *testing.T) {\n\tdebugLogger, _ := observer.New(DebugLevel)\n\twarnLogger, _ := observer.New(WarnLevel)\n\n\ttests := []struct {\n\t\tdesc string\n\t\tgive []Core\n\t\twant Level\n\t}{\n\t\t{desc: \"empty\", want: InvalidLevel},\n\t\t{\n\t\t\tdesc: \"debug\",\n\t\t\tgive: []Core{debugLogger},\n\t\t\twant: DebugLevel,\n\t\t},\n\t\t{\n\t\t\tdesc: \"warn\",\n\t\t\tgive: []Core{warnLogger},\n\t\t\twant: WarnLevel,\n\t\t},\n\t\t{\n\t\t\tdesc: \"debug and warn\",\n\t\t\tgive: []Core{warnLogger, debugLogger},\n\t\t\twant: DebugLevel,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tcore := NewTee(tt.give...)\n\t\t\tassert.Equal(t, tt.want, LevelOf(core), \"Level of Tee core did not match.\")\n\t\t})\n\t}\n}\n\nfunc TestTeeCheck(t *testing.T) {\n\twithTee(func(tee Core, debugLogs, warnLogs *observer.ObservedLogs) {\n\t\tdebugEntry := Entry{Level: DebugLevel, Message: \"log-at-debug\"}\n\t\tinfoEntry := Entry{Level: InfoLevel, Message: \"log-at-info\"}\n\t\twarnEntry := Entry{Level: WarnLevel, Message: \"log-at-warn\"}\n\t\terrorEntry := Entry{Level: ErrorLevel, Message: \"log-at-error\"}\n\t\tfor _, ent := range []Entry{debugEntry, infoEntry, warnEntry, errorEntry} {\n\t\t\tif ce := tee.Check(ent, nil); ce != nil {\n\t\t\t\tce.Write()\n\t\t\t}\n\t\t}\n\n\t\tassert.Equal(t, []observer.LoggedEntry{\n\t\t\t{Entry: debugEntry, Context: []Field{}},\n\t\t\t{Entry: infoEntry, Context: []Field{}},\n\t\t\t{Entry: warnEntry, Context: []Field{}},\n\t\t\t{Entry: errorEntry, Context: []Field{}},\n\t\t}, debugLogs.All())\n\n\t\tassert.Equal(t, []observer.LoggedEntry{\n\t\t\t{Entry: warnEntry, Context: []Field{}},\n\t\t\t{Entry: errorEntry, Context: []Field{}},\n\t\t}, warnLogs.All())\n\t})\n}\n\nfunc TestTeeWrite(t *testing.T) {\n\t// Calling the tee's Write method directly should always log, regardless of\n\t// the configured level.\n\twithTee(func(tee Core, debugLogs, warnLogs *observer.ObservedLogs) {\n\t\tdebugEntry := Entry{Level: DebugLevel, Message: \"log-at-debug\"}\n\t\twarnEntry := Entry{Level: WarnLevel, Message: \"log-at-warn\"}\n\t\tfor _, ent := range []Entry{debugEntry, warnEntry} {\n\t\t\tassert.NoError(t, tee.Write(ent, nil))\n\t\t}\n\n\t\tfor _, logs := range []*observer.ObservedLogs{debugLogs, warnLogs} {\n\t\t\tassert.Equal(t, []observer.LoggedEntry{\n\t\t\t\t{Entry: debugEntry, Context: []Field{}},\n\t\t\t\t{Entry: warnEntry, Context: []Field{}},\n\t\t\t}, logs.All())\n\t\t}\n\t})\n}\n\nfunc TestTeeWith(t *testing.T) {\n\twithTee(func(tee Core, debugLogs, warnLogs *observer.ObservedLogs) {\n\t\tf := makeInt64Field(\"k\", 42)\n\t\ttee = tee.With([]Field{f})\n\t\tent := Entry{Level: WarnLevel, Message: \"log-at-warn\"}\n\t\tif ce := tee.Check(ent, nil); ce != nil {\n\t\t\tce.Write()\n\t\t}\n\n\t\tfor _, logs := range []*observer.ObservedLogs{debugLogs, warnLogs} {\n\t\t\tassert.Equal(t, []observer.LoggedEntry{\n\t\t\t\t{Entry: ent, Context: []Field{f}},\n\t\t\t}, logs.All())\n\t\t}\n\t})\n}\n\nfunc TestTeeEnabled(t *testing.T) {\n\tinfoLogger, _ := observer.New(InfoLevel)\n\twarnLogger, _ := observer.New(WarnLevel)\n\ttee := NewTee(infoLogger, warnLogger)\n\ttests := []struct {\n\t\tlvl     Level\n\t\tenabled bool\n\t}{\n\t\t{DebugLevel, false},\n\t\t{InfoLevel, true},\n\t\t{WarnLevel, true},\n\t\t{ErrorLevel, true},\n\t\t{DPanicLevel, true},\n\t\t{PanicLevel, true},\n\t\t{FatalLevel, true},\n\t}\n\n\tfor _, tt := range tests {\n\t\tassert.Equal(t, tt.enabled, tee.Enabled(tt.lvl), \"Unexpected Enabled result for level %s.\", tt.lvl)\n\t}\n}\n\nfunc TestTeeSync(t *testing.T) {\n\tinfoLogger, _ := observer.New(InfoLevel)\n\twarnLogger, _ := observer.New(WarnLevel)\n\ttee := NewTee(infoLogger, warnLogger)\n\tassert.NoError(t, tee.Sync(), \"Unexpected error from Syncing a tee.\")\n\n\tsink := &ztest.Discarder{}\n\terr := errors.New(\"failed\")\n\tsink.SetError(err)\n\n\tnoSync := NewCore(\n\t\tNewJSONEncoder(testEncoderConfig()),\n\t\tsink,\n\t\tDebugLevel,\n\t)\n\ttee = NewTee(tee, noSync)\n\tassert.Equal(t, err, tee.Sync(), \"Expected an error when part of tee can't Sync.\")\n}\n"
  },
  {
    "path": "zapcore/write_syncer.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport (\n\t\"io\"\n\t\"sync\"\n\n\t\"go.uber.org/multierr\"\n)\n\n// A WriteSyncer is an io.Writer that can also flush any buffered data. Note\n// that *os.File (and thus, os.Stderr and os.Stdout) implement WriteSyncer.\ntype WriteSyncer interface {\n\tio.Writer\n\tSync() error\n}\n\n// AddSync converts an io.Writer to a WriteSyncer. It attempts to be\n// intelligent: if the concrete type of the io.Writer implements WriteSyncer,\n// we'll use the existing Sync method. If it doesn't, we'll add a no-op Sync.\nfunc AddSync(w io.Writer) WriteSyncer {\n\tswitch w := w.(type) {\n\tcase WriteSyncer:\n\t\treturn w\n\tdefault:\n\t\treturn writerWrapper{w}\n\t}\n}\n\ntype lockedWriteSyncer struct {\n\tsync.Mutex\n\tws WriteSyncer\n}\n\n// Lock wraps a WriteSyncer in a mutex to make it safe for concurrent use. In\n// particular, *os.Files must be locked before use.\nfunc Lock(ws WriteSyncer) WriteSyncer {\n\tif _, ok := ws.(*lockedWriteSyncer); ok {\n\t\t// no need to layer on another lock\n\t\treturn ws\n\t}\n\treturn &lockedWriteSyncer{ws: ws}\n}\n\nfunc (s *lockedWriteSyncer) Write(bs []byte) (int, error) {\n\ts.Lock()\n\tn, err := s.ws.Write(bs)\n\ts.Unlock()\n\treturn n, err\n}\n\nfunc (s *lockedWriteSyncer) Sync() error {\n\ts.Lock()\n\terr := s.ws.Sync()\n\ts.Unlock()\n\treturn err\n}\n\ntype writerWrapper struct {\n\tio.Writer\n}\n\nfunc (w writerWrapper) Sync() error {\n\treturn nil\n}\n\ntype multiWriteSyncer []WriteSyncer\n\n// NewMultiWriteSyncer creates a WriteSyncer that duplicates its writes\n// and sync calls, much like io.MultiWriter.\nfunc NewMultiWriteSyncer(ws ...WriteSyncer) WriteSyncer {\n\tif len(ws) == 1 {\n\t\treturn ws[0]\n\t}\n\treturn multiWriteSyncer(ws)\n}\n\n// See https://golang.org/src/io/multi.go\n// When not all underlying syncers write the same number of bytes,\n// the smallest number is returned even though Write() is called on\n// all of them.\nfunc (ws multiWriteSyncer) Write(p []byte) (int, error) {\n\tvar writeErr error\n\tnWritten := 0\n\tfor _, w := range ws {\n\t\tn, err := w.Write(p)\n\t\twriteErr = multierr.Append(writeErr, err)\n\t\tif nWritten == 0 && n != 0 {\n\t\t\tnWritten = n\n\t\t} else if n < nWritten {\n\t\t\tnWritten = n\n\t\t}\n\t}\n\treturn nWritten, writeErr\n}\n\nfunc (ws multiWriteSyncer) Sync() error {\n\tvar err error\n\tfor _, w := range ws {\n\t\terr = multierr.Append(err, w.Sync())\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "zapcore/write_syncer_bench_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport (\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/zap/internal/ztest\"\n)\n\nfunc BenchmarkMultiWriteSyncer(b *testing.B) {\n\tb.Run(\"2 discarder\", func(b *testing.B) {\n\t\tw := NewMultiWriteSyncer(\n\t\t\t&ztest.Discarder{},\n\t\t\t&ztest.Discarder{},\n\t\t)\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tif _, err := w.Write([]byte(\"foobarbazbabble\")); err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"4 discarder\", func(b *testing.B) {\n\t\tw := NewMultiWriteSyncer(\n\t\t\t&ztest.Discarder{},\n\t\t\t&ztest.Discarder{},\n\t\t\t&ztest.Discarder{},\n\t\t\t&ztest.Discarder{},\n\t\t)\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tif _, err := w.Write([]byte(\"foobarbazbabble\")); err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"4 discarder with buffer\", func(b *testing.B) {\n\t\tw := &BufferedWriteSyncer{\n\t\t\tWS: NewMultiWriteSyncer(\n\t\t\t\t&ztest.Discarder{},\n\t\t\t\t&ztest.Discarder{},\n\t\t\t\t&ztest.Discarder{},\n\t\t\t\t&ztest.Discarder{},\n\t\t\t),\n\t\t}\n\t\tdefer func() {\n\t\t\tassert.NoError(b, w.Stop(), \"Unexpected error stopping buffered write syncer.\")\n\t\t}()\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tif _, err := w.Write([]byte(\"foobarbazbabble\")); err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t})\n}\n\nfunc BenchmarkWriteSyncer(b *testing.B) {\n\tb.Run(\"write file with no buffer\", func(b *testing.B) {\n\t\tfile, err := os.CreateTemp(b.TempDir(), \"test.log\")\n\t\trequire.NoError(b, err)\n\n\t\tw := AddSync(file)\n\t\tb.ResetTimer()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tif _, err := w.Write([]byte(\"foobarbazbabble\")); err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "zapcore/write_syncer_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapcore\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"io\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/zap/internal/ztest\"\n)\n\ntype writeSyncSpy struct {\n\tio.Writer\n\tztest.Syncer\n}\n\nfunc requireWriteWorks(t testing.TB, ws WriteSyncer) {\n\tn, err := ws.Write([]byte(\"foo\"))\n\trequire.NoError(t, err, \"Unexpected error writing to WriteSyncer.\")\n\trequire.Equal(t, 3, n, \"Wrote an unexpected number of bytes.\")\n}\n\nfunc TestAddSyncWriteSyncer(t *testing.T) {\n\tbuf := &bytes.Buffer{}\n\tconcrete := &writeSyncSpy{Writer: buf}\n\tws := AddSync(concrete)\n\trequireWriteWorks(t, ws)\n\n\trequire.NoError(t, ws.Sync(), \"Unexpected error syncing a WriteSyncer.\")\n\trequire.True(t, concrete.Called(), \"Expected to dispatch to concrete type's Sync method.\")\n\n\tconcrete.SetError(errors.New(\"fail\"))\n\tassert.Error(t, ws.Sync(), \"Expected to propagate errors from concrete type's Sync method.\")\n}\n\nfunc TestAddSyncWriter(t *testing.T) {\n\t// If we pass a plain io.Writer, make sure that we still get a WriteSyncer\n\t// with a no-op Sync.\n\tbuf := &bytes.Buffer{}\n\tws := AddSync(buf)\n\trequireWriteWorks(t, ws)\n\tassert.NoError(t, ws.Sync(), \"Unexpected error calling a no-op Sync method.\")\n}\n\nfunc TestNewMultiWriteSyncerWorksForSingleWriter(t *testing.T) {\n\tw := &ztest.Buffer{}\n\n\tws := NewMultiWriteSyncer(w)\n\tassert.Equal(t, w, ws, \"Expected NewMultiWriteSyncer to return the same WriteSyncer object for a single argument.\")\n\n\tassert.NoError(t, ws.Sync(), \"Expected Sync to succeed.\")\n\tassert.True(t, w.Called(), \"Expected Sync to be called on the created WriteSyncer\")\n}\n\nfunc TestMultiWriteSyncerWritesBoth(t *testing.T) {\n\tfirst := &bytes.Buffer{}\n\tsecond := &bytes.Buffer{}\n\tws := NewMultiWriteSyncer(AddSync(first), AddSync(second))\n\n\tmsg := []byte(\"dumbledore\")\n\tn, err := ws.Write(msg)\n\trequire.NoError(t, err, \"Expected successful buffer write\")\n\tassert.Equal(t, len(msg), n)\n\n\tassert.Equal(t, msg, first.Bytes())\n\tassert.Equal(t, msg, second.Bytes())\n}\n\nfunc TestMultiWriteSyncerFailsWrite(t *testing.T) {\n\tws := NewMultiWriteSyncer(AddSync(&ztest.FailWriter{}))\n\t_, err := ws.Write([]byte(\"test\"))\n\tassert.Error(t, err, \"Write error should propagate\")\n}\n\nfunc TestMultiWriteSyncerFailsShortWrite(t *testing.T) {\n\tws := NewMultiWriteSyncer(AddSync(&ztest.ShortWriter{}))\n\tn, err := ws.Write([]byte(\"test\"))\n\tassert.NoError(t, err, \"Expected fake-success from short write\")\n\tassert.Equal(t, 3, n, \"Expected byte count to return from underlying writer\")\n}\n\nfunc TestWritestoAllSyncs_EvenIfFirstErrors(t *testing.T) {\n\tfailer := &ztest.FailWriter{}\n\tsecond := &bytes.Buffer{}\n\tws := NewMultiWriteSyncer(AddSync(failer), AddSync(second))\n\n\t_, err := ws.Write([]byte(\"fail\"))\n\tassert.Error(t, err, \"Expected error from call to a writer that failed\")\n\tassert.Equal(t, []byte(\"fail\"), second.Bytes(), \"Expected second sink to be written after first error\")\n}\n\nfunc TestMultiWriteSyncerSync_PropagatesErrors(t *testing.T) {\n\tbadsink := &ztest.Buffer{}\n\tbadsink.SetError(errors.New(\"sink is full\"))\n\tws := NewMultiWriteSyncer(&ztest.Discarder{}, badsink)\n\n\tassert.Error(t, ws.Sync(), \"Expected sync error to propagate\")\n}\n\nfunc TestMultiWriteSyncerSync_NoErrorsOnDiscard(t *testing.T) {\n\tws := NewMultiWriteSyncer(&ztest.Discarder{})\n\tassert.NoError(t, ws.Sync(), \"Expected error-free sync to /dev/null\")\n}\n\nfunc TestMultiWriteSyncerSync_AllCalled(t *testing.T) {\n\tfailed, second := &ztest.Buffer{}, &ztest.Buffer{}\n\n\tfailed.SetError(errors.New(\"disposal broken\"))\n\tws := NewMultiWriteSyncer(failed, second)\n\n\tassert.Error(t, ws.Sync(), \"Expected first sink to fail\")\n\tassert.True(t, failed.Called(), \"Expected first sink to have Sync method called.\")\n\tassert.True(t, second.Called(), \"Expected call to Sync even with first failure.\")\n}\n"
  },
  {
    "path": "zapgrpc/internal/test/README.md",
    "content": "This submodule exists to test zapgrpc against grpc-go without adding a\ndependency on grpc-go to Zap.\n"
  },
  {
    "path": "zapgrpc/internal/test/go.mod",
    "content": "module go.uber.org/zap/zapgrpc/internal/test\n\ngo 1.18\n\nrequire (\n\tgithub.com/stretchr/testify v1.8.1\n\tgo.uber.org/zap v1.16.0\n\tgoogle.golang.org/grpc v1.56.3\n)\n\nrequire (\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/kr/pretty v0.3.0 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgithub.com/rogpeppe/go-internal v1.8.0 // indirect\n\tgo.uber.org/multierr v1.10.0 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n\nreplace go.uber.org/zap => ../../..\n"
  },
  {
    "path": "zapgrpc/internal/test/go.sum",
    "content": "github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=\ngithub.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=\ngithub.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=\ngithub.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngo.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=\ngo.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=\ngo.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=\ngo.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=\ngoogle.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc=\ngoogle.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "zapgrpc/internal/test/grpc.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// Package grpc tests Zap's zapgrpc package without requiring a dependency on\n// grpc from Zap itself.\npackage grpc\n\n// This file exists to treat this directory as a valid package with at least\n// one non-test file.\n"
  },
  {
    "path": "zapgrpc/internal/test/grpc_test.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage grpc\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n\t\"go.uber.org/zap/zapgrpc\"\n\t\"go.uber.org/zap/zaptest/observer\"\n\t\"google.golang.org/grpc/grpclog\"\n)\n\nfunc TestLoggerV2(t *testing.T) {\n\tcore, observedLogs := observer.New(zapcore.InfoLevel)\n\tzlog := zap.New(core)\n\n\tgrpclog.SetLoggerV2(zapgrpc.NewLogger(zlog))\n\n\tgrpclog.Info(\"hello from grpc\")\n\n\tlogs := observedLogs.TakeAll()\n\trequire.Len(t, logs, 1, \"Expected one log entry.\")\n\tentry := logs[0]\n\n\tassert.Equal(t, zapcore.InfoLevel, entry.Level,\n\t\t\"Log entry level did not match.\")\n\tassert.Equal(t, \"hello from grpc\", entry.Message,\n\t\t\"Log entry message did not match.\")\n}\n"
  },
  {
    "path": "zapgrpc/zapgrpc.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// Package zapgrpc provides a logger that is compatible with grpclog.\npackage zapgrpc // import \"go.uber.org/zap/zapgrpc\"\n\nimport (\n\t\"fmt\"\n\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\n// See https://github.com/grpc/grpc-go/blob/v1.35.0/grpclog/loggerv2.go#L77-L86\nconst (\n\tgrpcLvlInfo int = iota\n\tgrpcLvlWarn\n\tgrpcLvlError\n\tgrpcLvlFatal\n)\n\n// _grpcToZapLevel maps gRPC log levels to zap log levels.\n// See https://pkg.go.dev/go.uber.org/zap@v1.16.0/zapcore#Level\nvar _grpcToZapLevel = map[int]zapcore.Level{\n\tgrpcLvlInfo:  zapcore.InfoLevel,\n\tgrpcLvlWarn:  zapcore.WarnLevel,\n\tgrpcLvlError: zapcore.ErrorLevel,\n\tgrpcLvlFatal: zapcore.FatalLevel,\n}\n\n// An Option overrides a Logger's default configuration.\ntype Option interface {\n\tapply(*Logger)\n}\n\ntype optionFunc func(*Logger)\n\nfunc (f optionFunc) apply(log *Logger) {\n\tf(log)\n}\n\n// WithDebug configures a Logger to print at zap's DebugLevel instead of\n// InfoLevel.\n// It only affects the Printf, Println and Print methods, which are only used in the gRPC v1 grpclog.Logger API.\n//\n// Deprecated: use grpclog.SetLoggerV2() for v2 API.\nfunc WithDebug() Option {\n\treturn optionFunc(func(logger *Logger) {\n\t\tlogger.print = &printer{\n\t\t\tenab:   logger.levelEnabler,\n\t\t\tlevel:  zapcore.DebugLevel,\n\t\t\tprint:  logger.delegate.Debug,\n\t\t\tprintf: logger.delegate.Debugf,\n\t\t}\n\t})\n}\n\n// withWarn redirects the fatal level to the warn level, which makes testing\n// easier. This is intentionally unexported.\nfunc withWarn() Option {\n\treturn optionFunc(func(logger *Logger) {\n\t\tlogger.fatal = &printer{\n\t\t\tenab:   logger.levelEnabler,\n\t\t\tlevel:  zapcore.WarnLevel,\n\t\t\tprint:  logger.delegate.Warn,\n\t\t\tprintf: logger.delegate.Warnf,\n\t\t}\n\t})\n}\n\n// NewLogger returns a new Logger.\nfunc NewLogger(l *zap.Logger, options ...Option) *Logger {\n\tlogger := &Logger{\n\t\tdelegate:     l.Sugar(),\n\t\tlevelEnabler: l.Core(),\n\t}\n\tlogger.print = &printer{\n\t\tenab:   logger.levelEnabler,\n\t\tlevel:  zapcore.InfoLevel,\n\t\tprint:  logger.delegate.Info,\n\t\tprintf: logger.delegate.Infof,\n\t}\n\tlogger.fatal = &printer{\n\t\tenab:   logger.levelEnabler,\n\t\tlevel:  zapcore.FatalLevel,\n\t\tprint:  logger.delegate.Fatal,\n\t\tprintf: logger.delegate.Fatalf,\n\t}\n\tfor _, option := range options {\n\t\toption.apply(logger)\n\t}\n\treturn logger\n}\n\n// printer implements Print, Printf, and Println operations for a Zap level.\n//\n// We use it to customize Debug vs Info, and Warn vs Fatal for Print and Fatal\n// respectively.\ntype printer struct {\n\tenab   zapcore.LevelEnabler\n\tlevel  zapcore.Level\n\tprint  func(...interface{})\n\tprintf func(string, ...interface{})\n}\n\nfunc (v *printer) Print(args ...interface{}) {\n\tv.print(args...)\n}\n\nfunc (v *printer) Printf(format string, args ...interface{}) {\n\tv.printf(format, args...)\n}\n\nfunc (v *printer) Println(args ...interface{}) {\n\tif v.enab.Enabled(v.level) {\n\t\tv.print(sprintln(args))\n\t}\n}\n\n// Logger adapts zap's Logger to be compatible with grpclog.LoggerV2 and the deprecated grpclog.Logger.\ntype Logger struct {\n\tdelegate     *zap.SugaredLogger\n\tlevelEnabler zapcore.LevelEnabler\n\tprint        *printer\n\tfatal        *printer\n\t// printToDebug bool\n\t// fatalToWarn  bool\n}\n\n// Print implements grpclog.Logger.\n//\n// Deprecated: use [Logger.Info].\nfunc (l *Logger) Print(args ...interface{}) {\n\tl.print.Print(args...)\n}\n\n// Printf implements grpclog.Logger.\n//\n// Deprecated: use [Logger.Infof].\nfunc (l *Logger) Printf(format string, args ...interface{}) {\n\tl.print.Printf(format, args...)\n}\n\n// Println implements grpclog.Logger.\n//\n// Deprecated: use [Logger.Info].\nfunc (l *Logger) Println(args ...interface{}) {\n\tl.print.Println(args...)\n}\n\n// Info implements grpclog.LoggerV2.\nfunc (l *Logger) Info(args ...interface{}) {\n\tl.delegate.Info(args...)\n}\n\n// Infoln implements grpclog.LoggerV2.\nfunc (l *Logger) Infoln(args ...interface{}) {\n\tif l.levelEnabler.Enabled(zapcore.InfoLevel) {\n\t\tl.delegate.Info(sprintln(args))\n\t}\n}\n\n// Infof implements grpclog.LoggerV2.\nfunc (l *Logger) Infof(format string, args ...interface{}) {\n\tl.delegate.Infof(format, args...)\n}\n\n// Warning implements grpclog.LoggerV2.\nfunc (l *Logger) Warning(args ...interface{}) {\n\tl.delegate.Warn(args...)\n}\n\n// Warningln implements grpclog.LoggerV2.\nfunc (l *Logger) Warningln(args ...interface{}) {\n\tif l.levelEnabler.Enabled(zapcore.WarnLevel) {\n\t\tl.delegate.Warn(sprintln(args))\n\t}\n}\n\n// Warningf implements grpclog.LoggerV2.\nfunc (l *Logger) Warningf(format string, args ...interface{}) {\n\tl.delegate.Warnf(format, args...)\n}\n\n// Error implements grpclog.LoggerV2.\nfunc (l *Logger) Error(args ...interface{}) {\n\tl.delegate.Error(args...)\n}\n\n// Errorln implements grpclog.LoggerV2.\nfunc (l *Logger) Errorln(args ...interface{}) {\n\tif l.levelEnabler.Enabled(zapcore.ErrorLevel) {\n\t\tl.delegate.Error(sprintln(args))\n\t}\n}\n\n// Errorf implements grpclog.LoggerV2.\nfunc (l *Logger) Errorf(format string, args ...interface{}) {\n\tl.delegate.Errorf(format, args...)\n}\n\n// Fatal implements grpclog.LoggerV2.\nfunc (l *Logger) Fatal(args ...interface{}) {\n\tl.fatal.Print(args...)\n}\n\n// Fatalln implements grpclog.LoggerV2.\nfunc (l *Logger) Fatalln(args ...interface{}) {\n\tl.fatal.Println(args...)\n}\n\n// Fatalf implements grpclog.LoggerV2.\nfunc (l *Logger) Fatalf(format string, args ...interface{}) {\n\tl.fatal.Printf(format, args...)\n}\n\n// V implements grpclog.LoggerV2.\nfunc (l *Logger) V(level int) bool {\n\treturn l.levelEnabler.Enabled(_grpcToZapLevel[level])\n}\n\nfunc sprintln(args []interface{}) string {\n\ts := fmt.Sprintln(args...)\n\t// Drop the new line character added by Sprintln\n\treturn s[:len(s)-1]\n}\n"
  },
  {
    "path": "zapgrpc/zapgrpc_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapgrpc\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n\t\"go.uber.org/zap/zaptest/observer\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestLoggerInfoExpected(t *testing.T) {\n\tcheckMessages(t, zapcore.DebugLevel, nil, zapcore.InfoLevel, []string{\n\t\t\"hello\",\n\t\t\"s1s21 2 3s34s56\",\n\t\t\"hello world\",\n\t\t\"\",\n\t\t\"foo\",\n\t\t\"foo bar\",\n\t\t\"s1 s2 1 2 3 s3 4 s5 6\",\n\t\t\"hello\",\n\t\t\"s1s21 2 3s34s56\",\n\t\t\"hello world\",\n\t\t\"\",\n\t\t\"foo\",\n\t\t\"foo bar\",\n\t\t\"s1 s2 1 2 3 s3 4 s5 6\",\n\t}, func(logger *Logger) {\n\t\tlogger.Info(\"hello\")\n\t\tlogger.Info(\"s1\", \"s2\", 1, 2, 3, \"s3\", 4, \"s5\", 6)\n\t\tlogger.Infof(\"%s world\", \"hello\")\n\t\tlogger.Infoln()\n\t\tlogger.Infoln(\"foo\")\n\t\tlogger.Infoln(\"foo\", \"bar\")\n\t\tlogger.Infoln(\"s1\", \"s2\", 1, 2, 3, \"s3\", 4, \"s5\", 6)\n\t\tlogger.Print(\"hello\")\n\t\tlogger.Print(\"s1\", \"s2\", 1, 2, 3, \"s3\", 4, \"s5\", 6)\n\t\tlogger.Printf(\"%s world\", \"hello\")\n\t\tlogger.Println()\n\t\tlogger.Println(\"foo\")\n\t\tlogger.Println(\"foo\", \"bar\")\n\t\tlogger.Println(\"s1\", \"s2\", 1, 2, 3, \"s3\", 4, \"s5\", 6)\n\t})\n}\n\nfunc TestLoggerDebugExpected(t *testing.T) {\n\tcheckMessages(t, zapcore.DebugLevel, []Option{WithDebug()}, zapcore.DebugLevel, []string{\n\t\t\"hello\",\n\t\t\"s1s21 2 3s34s56\",\n\t\t\"hello world\",\n\t\t\"\",\n\t\t\"foo\",\n\t\t\"foo bar\",\n\t\t\"s1 s2 1 2 3 s3 4 s5 6\",\n\t}, func(logger *Logger) {\n\t\tlogger.Print(\"hello\")\n\t\tlogger.Print(\"s1\", \"s2\", 1, 2, 3, \"s3\", 4, \"s5\", 6)\n\t\tlogger.Printf(\"%s world\", \"hello\")\n\t\tlogger.Println()\n\t\tlogger.Println(\"foo\")\n\t\tlogger.Println(\"foo\", \"bar\")\n\t\tlogger.Println(\"s1\", \"s2\", 1, 2, 3, \"s3\", 4, \"s5\", 6)\n\t})\n}\n\nfunc TestLoggerDebugSuppressed(t *testing.T) {\n\tcheckMessages(t, zapcore.InfoLevel, []Option{WithDebug()}, zapcore.DebugLevel, nil, func(logger *Logger) {\n\t\tlogger.Print(\"hello\")\n\t\tlogger.Printf(\"%s world\", \"hello\")\n\t\tlogger.Println()\n\t\tlogger.Println(\"foo\")\n\t\tlogger.Println(\"foo\", \"bar\")\n\t})\n}\n\nfunc TestLoggerWarningExpected(t *testing.T) {\n\tcheckMessages(t, zapcore.DebugLevel, nil, zapcore.WarnLevel, []string{\n\t\t\"hello\",\n\t\t\"s1s21 2 3s34s56\",\n\t\t\"hello world\",\n\t\t\"\",\n\t\t\"foo\",\n\t\t\"foo bar\",\n\t\t\"s1 s2 1 2 3 s3 4 s5 6\",\n\t}, func(logger *Logger) {\n\t\tlogger.Warning(\"hello\")\n\t\tlogger.Warning(\"s1\", \"s2\", 1, 2, 3, \"s3\", 4, \"s5\", 6)\n\t\tlogger.Warningf(\"%s world\", \"hello\")\n\t\tlogger.Warningln()\n\t\tlogger.Warningln(\"foo\")\n\t\tlogger.Warningln(\"foo\", \"bar\")\n\t\tlogger.Warningln(\"s1\", \"s2\", 1, 2, 3, \"s3\", 4, \"s5\", 6)\n\t})\n}\n\nfunc TestLoggerErrorExpected(t *testing.T) {\n\tcheckMessages(t, zapcore.DebugLevel, nil, zapcore.ErrorLevel, []string{\n\t\t\"hello\",\n\t\t\"s1s21 2 3s34s56\",\n\t\t\"hello world\",\n\t\t\"\",\n\t\t\"foo\",\n\t\t\"foo bar\",\n\t\t\"s1 s2 1 2 3 s3 4 s5 6\",\n\t}, func(logger *Logger) {\n\t\tlogger.Error(\"hello\")\n\t\tlogger.Error(\"s1\", \"s2\", 1, 2, 3, \"s3\", 4, \"s5\", 6)\n\t\tlogger.Errorf(\"%s world\", \"hello\")\n\t\tlogger.Errorln()\n\t\tlogger.Errorln(\"foo\")\n\t\tlogger.Errorln(\"foo\", \"bar\")\n\t\tlogger.Errorln(\"s1\", \"s2\", 1, 2, 3, \"s3\", 4, \"s5\", 6)\n\t})\n}\n\nfunc TestLoggerFatalExpected(t *testing.T) {\n\tcheckMessages(t, zapcore.DebugLevel, nil, zapcore.FatalLevel, []string{\n\t\t\"hello\",\n\t\t\"s1s21 2 3s34s56\",\n\t\t\"hello world\",\n\t\t\"\",\n\t\t\"foo\",\n\t\t\"foo bar\",\n\t\t\"s1 s2 1 2 3 s3 4 s5 6\",\n\t}, func(logger *Logger) {\n\t\tlogger.Fatal(\"hello\")\n\t\tlogger.Fatal(\"s1\", \"s2\", 1, 2, 3, \"s3\", 4, \"s5\", 6)\n\t\tlogger.Fatalf(\"%s world\", \"hello\")\n\t\tlogger.Fatalln()\n\t\tlogger.Fatalln(\"foo\")\n\t\tlogger.Fatalln(\"foo\", \"bar\")\n\t\tlogger.Fatalln(\"s1\", \"s2\", 1, 2, 3, \"s3\", 4, \"s5\", 6)\n\t})\n}\n\nfunc TestLoggerV(t *testing.T) {\n\ttests := []struct {\n\t\tzapLevel     zapcore.Level\n\t\tgrpcEnabled  []int\n\t\tgrpcDisabled []int\n\t}{\n\t\t{\n\t\t\tzapLevel:     zapcore.DebugLevel,\n\t\t\tgrpcEnabled:  []int{grpcLvlInfo, grpcLvlWarn, grpcLvlError, grpcLvlFatal},\n\t\t\tgrpcDisabled: []int{}, // everything is enabled, nothing is disabled\n\t\t},\n\t\t{\n\t\t\tzapLevel:     zapcore.InfoLevel,\n\t\t\tgrpcEnabled:  []int{grpcLvlInfo, grpcLvlWarn, grpcLvlError, grpcLvlFatal},\n\t\t\tgrpcDisabled: []int{}, // everything is enabled, nothing is disabled\n\t\t},\n\t\t{\n\t\t\tzapLevel:     zapcore.WarnLevel,\n\t\t\tgrpcEnabled:  []int{grpcLvlWarn, grpcLvlError, grpcLvlFatal},\n\t\t\tgrpcDisabled: []int{grpcLvlInfo},\n\t\t},\n\t\t{\n\t\t\tzapLevel:     zapcore.ErrorLevel,\n\t\t\tgrpcEnabled:  []int{grpcLvlError, grpcLvlFatal},\n\t\t\tgrpcDisabled: []int{grpcLvlInfo, grpcLvlWarn},\n\t\t},\n\t\t{\n\t\t\tzapLevel:     zapcore.DPanicLevel,\n\t\t\tgrpcEnabled:  []int{grpcLvlFatal},\n\t\t\tgrpcDisabled: []int{grpcLvlInfo, grpcLvlWarn, grpcLvlError},\n\t\t},\n\t\t{\n\t\t\tzapLevel:     zapcore.PanicLevel,\n\t\t\tgrpcEnabled:  []int{grpcLvlFatal},\n\t\t\tgrpcDisabled: []int{grpcLvlInfo, grpcLvlWarn, grpcLvlError},\n\t\t},\n\t\t{\n\t\t\tzapLevel:     zapcore.FatalLevel,\n\t\t\tgrpcEnabled:  []int{grpcLvlFatal},\n\t\t\tgrpcDisabled: []int{grpcLvlInfo, grpcLvlWarn, grpcLvlError},\n\t\t},\n\t}\n\tfor _, tst := range tests {\n\t\tfor _, grpcLvl := range tst.grpcEnabled {\n\t\t\tt.Run(fmt.Sprintf(\"enabled %s %d\", tst.zapLevel, grpcLvl), func(t *testing.T) {\n\t\t\t\tcheckLevel(t, tst.zapLevel, true, func(logger *Logger) bool {\n\t\t\t\t\treturn logger.V(grpcLvl)\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t\tfor _, grpcLvl := range tst.grpcDisabled {\n\t\t\tt.Run(fmt.Sprintf(\"disabled %s %d\", tst.zapLevel, grpcLvl), func(t *testing.T) {\n\t\t\t\tcheckLevel(t, tst.zapLevel, false, func(logger *Logger) bool {\n\t\t\t\t\treturn logger.V(grpcLvl)\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t}\n}\n\nfunc checkLevel(\n\tt testing.TB,\n\tenab zapcore.LevelEnabler,\n\texpectedBool bool,\n\tf func(*Logger) bool,\n) {\n\twithLogger(enab, nil, func(logger *Logger, observedLogs *observer.ObservedLogs) {\n\t\tactualBool := f(logger)\n\t\tif expectedBool {\n\t\t\trequire.True(t, actualBool)\n\t\t} else {\n\t\t\trequire.False(t, actualBool)\n\t\t}\n\t})\n}\n\nfunc checkMessages(\n\tt testing.TB,\n\tenab zapcore.LevelEnabler,\n\topts []Option,\n\texpectedLevel zapcore.Level,\n\texpectedMessages []string,\n\tf func(*Logger),\n) {\n\tif expectedLevel == zapcore.FatalLevel {\n\t\texpectedLevel = zapcore.WarnLevel\n\t}\n\twithLogger(enab, opts, func(logger *Logger, observedLogs *observer.ObservedLogs) {\n\t\tf(logger)\n\t\tlogEntries := observedLogs.All()\n\t\trequire.Equal(t, len(expectedMessages), len(logEntries))\n\t\tfor i, logEntry := range logEntries {\n\t\t\trequire.Equal(t, expectedLevel, logEntry.Level)\n\t\t\trequire.Equal(t, expectedMessages[i], logEntry.Message)\n\t\t}\n\t})\n}\n\nfunc withLogger(\n\tenab zapcore.LevelEnabler,\n\topts []Option,\n\tf func(*Logger, *observer.ObservedLogs),\n) {\n\tcore, observedLogs := observer.New(enab)\n\tf(NewLogger(zap.New(core), append(opts, withWarn())...), observedLogs)\n}\n"
  },
  {
    "path": "zapio/example_test.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapio_test\n\nimport (\n\t\"io\"\n\t\"log\"\n\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapio\"\n)\n\nfunc ExampleWriter() {\n\tlogger := zap.NewExample()\n\tw := &zapio.Writer{Log: logger}\n\n\tio.WriteString(w, \"starting up\\n\")\n\tio.WriteString(w, \"running\\n\")\n\tio.WriteString(w, \"shutting down\\n\")\n\n\tif err := w.Close(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Output:\n\t// {\"level\":\"info\",\"msg\":\"starting up\"}\n\t// {\"level\":\"info\",\"msg\":\"running\"}\n\t// {\"level\":\"info\",\"msg\":\"shutting down\"}\n}\n"
  },
  {
    "path": "zapio/writer.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// Package zapio provides tools for interacting with IO streams through Zap.\npackage zapio\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\n// Writer is an io.Writer that writes to the provided Zap logger, splitting log\n// messages on line boundaries. The Writer will buffer writes in memory until\n// it encounters a newline, or the caller calls Sync or Close.\n//\n// Use the Writer with packages like os/exec where an io.Writer is required,\n// and you want to log the output using your existing logger configuration. For\n// example,\n//\n//\twriter := &zapio.Writer{Log: logger, Level: zap.DebugLevel}\n//\tdefer writer.Close()\n//\n//\tcmd := exec.CommandContext(ctx, ...)\n//\tcmd.Stdout = writer\n//\tcmd.Stderr = writer\n//\tif err := cmd.Run(); err != nil {\n//\t    return err\n//\t}\n//\n// Writer must be closed when finished to flush buffered data to the logger.\ntype Writer struct {\n\t// Log specifies the logger to which the Writer will write messages.\n\t//\n\t// The Writer will panic if Log is unspecified.\n\tLog *zap.Logger\n\n\t// Log level for the messages written to the provided logger.\n\t//\n\t// If unspecified, defaults to Info.\n\tLevel zapcore.Level\n\n\tbuff bytes.Buffer\n}\n\nvar (\n\t_ zapcore.WriteSyncer = (*Writer)(nil)\n\t_ io.Closer           = (*Writer)(nil)\n)\n\n// Write writes the provided bytes to the underlying logger at the configured\n// log level and returns the length of the bytes.\n//\n// Write will split the input on newlines and post each line as a new log entry\n// to the logger.\nfunc (w *Writer) Write(bs []byte) (n int, err error) {\n\t// Skip all checks if the level isn't enabled.\n\tif !w.Log.Core().Enabled(w.Level) {\n\t\treturn len(bs), nil\n\t}\n\n\tn = len(bs)\n\tfor len(bs) > 0 {\n\t\tbs = w.writeLine(bs)\n\t}\n\n\treturn n, nil\n}\n\n// writeLine writes a single line from the input, returning the remaining,\n// unconsumed bytes.\nfunc (w *Writer) writeLine(line []byte) (remaining []byte) {\n\tidx := bytes.IndexByte(line, '\\n')\n\tif idx < 0 {\n\t\t// If there are no newlines, buffer the entire string.\n\t\tw.buff.Write(line)\n\t\treturn nil\n\t}\n\n\t// Split on the newline, buffer and flush the left.\n\tline, remaining = line[:idx], line[idx+1:]\n\n\t// Fast path: if we don't have a partial message from a previous write\n\t// in the buffer, skip the buffer and log directly.\n\tif w.buff.Len() == 0 {\n\t\tw.log(line)\n\t\treturn\n\t}\n\n\tw.buff.Write(line)\n\n\t// Log empty messages in the middle of the stream so that we don't lose\n\t// information when the user writes \"foo\\n\\nbar\".\n\tw.flush(true /* allowEmpty */)\n\n\treturn remaining\n}\n\n// Close closes the writer, flushing any buffered data in the process.\n//\n// Always call Close once you're done with the Writer to ensure that it flushes\n// all data.\nfunc (w *Writer) Close() error {\n\treturn w.Sync()\n}\n\n// Sync flushes buffered data to the logger as a new log entry even if it\n// doesn't contain a newline.\nfunc (w *Writer) Sync() error {\n\t// Don't allow empty messages on explicit Sync calls or on Close\n\t// because we don't want an extraneous empty message at the end of the\n\t// stream -- it's common for files to end with a newline.\n\tw.flush(false /* allowEmpty */)\n\treturn nil\n}\n\n// flush flushes the buffered data to the logger, allowing empty messages only\n// if the bool is set.\nfunc (w *Writer) flush(allowEmpty bool) {\n\tif allowEmpty || w.buff.Len() > 0 {\n\t\tw.log(w.buff.Bytes())\n\t}\n\tw.buff.Reset()\n}\n\nfunc (w *Writer) log(b []byte) {\n\tif ce := w.Log.Check(w.Level, string(b)); ce != nil {\n\t\tce.Write()\n\t}\n}\n"
  },
  {
    "path": "zapio/writer_test.go",
    "content": "// Copyright (c) 2021 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zapio\n\nimport (\n\t\"io\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n\t\"go.uber.org/zap/zaptest/observer\"\n)\n\nfunc TestWriter(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tdesc   string\n\t\tlevel  zapcore.Level // defaults to info\n\t\twrites []string\n\t\twant   []zapcore.Entry\n\t}{\n\t\t{\n\t\t\tdesc: \"simple\",\n\t\t\twrites: []string{\n\t\t\t\t\"foo\\n\",\n\t\t\t\t\"bar\\n\",\n\t\t\t\t\"baz\\n\",\n\t\t\t},\n\t\t\twant: []zapcore.Entry{\n\t\t\t\t{Level: zap.InfoLevel, Message: \"foo\"},\n\t\t\t\t{Level: zap.InfoLevel, Message: \"bar\"},\n\t\t\t\t{Level: zap.InfoLevel, Message: \"baz\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc:  \"level too low\",\n\t\t\tlevel: zap.DebugLevel,\n\t\t\twrites: []string{\n\t\t\t\t\"foo\\n\",\n\t\t\t\t\"bar\\n\",\n\t\t\t},\n\t\t\twant: []zapcore.Entry{},\n\t\t},\n\t\t{\n\t\t\tdesc:  \"multiple newlines in a message\",\n\t\t\tlevel: zap.WarnLevel,\n\t\t\twrites: []string{\n\t\t\t\t\"foo\\nbar\\n\",\n\t\t\t\t\"baz\\n\",\n\t\t\t\t\"qux\\nquux\\n\",\n\t\t\t},\n\t\t\twant: []zapcore.Entry{\n\t\t\t\t{Level: zap.WarnLevel, Message: \"foo\"},\n\t\t\t\t{Level: zap.WarnLevel, Message: \"bar\"},\n\t\t\t\t{Level: zap.WarnLevel, Message: \"baz\"},\n\t\t\t\t{Level: zap.WarnLevel, Message: \"qux\"},\n\t\t\t\t{Level: zap.WarnLevel, Message: \"quux\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc:  \"message split across multiple writes\",\n\t\t\tlevel: zap.ErrorLevel,\n\t\t\twrites: []string{\n\t\t\t\t\"foo\",\n\t\t\t\t\"bar\\nbaz\",\n\t\t\t\t\"qux\",\n\t\t\t},\n\t\t\twant: []zapcore.Entry{\n\t\t\t\t{Level: zap.ErrorLevel, Message: \"foobar\"},\n\t\t\t\t{Level: zap.ErrorLevel, Message: \"bazqux\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"blank lines in the middle\",\n\t\t\twrites: []string{\n\t\t\t\t\"foo\\n\\nbar\\nbaz\",\n\t\t\t},\n\t\t\twant: []zapcore.Entry{\n\t\t\t\t{Level: zap.InfoLevel, Message: \"foo\"},\n\t\t\t\t{Level: zap.InfoLevel, Message: \"\"},\n\t\t\t\t{Level: zap.InfoLevel, Message: \"bar\"},\n\t\t\t\t{Level: zap.InfoLevel, Message: \"baz\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"blank line at the end\",\n\t\t\twrites: []string{\n\t\t\t\t\"foo\\nbar\\nbaz\\n\",\n\t\t\t},\n\t\t\twant: []zapcore.Entry{\n\t\t\t\t{Level: zap.InfoLevel, Message: \"foo\"},\n\t\t\t\t{Level: zap.InfoLevel, Message: \"bar\"},\n\t\t\t\t{Level: zap.InfoLevel, Message: \"baz\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"multiple blank line at the end\",\n\t\t\twrites: []string{\n\t\t\t\t\"foo\\nbar\\nbaz\\n\\n\",\n\t\t\t},\n\t\t\twant: []zapcore.Entry{\n\t\t\t\t{Level: zap.InfoLevel, Message: \"foo\"},\n\t\t\t\t{Level: zap.InfoLevel, Message: \"bar\"},\n\t\t\t\t{Level: zap.InfoLevel, Message: \"baz\"},\n\t\t\t\t{Level: zap.InfoLevel, Message: \"\"},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\ttt := tt // for t.Parallel\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tcore, observed := observer.New(zap.InfoLevel)\n\n\t\t\tw := Writer{\n\t\t\t\tLog:   zap.New(core),\n\t\t\t\tLevel: tt.level,\n\t\t\t}\n\n\t\t\tfor _, s := range tt.writes {\n\t\t\t\t_, err := io.WriteString(&w, s)\n\t\t\t\trequire.NoError(t, err, \"Writer.Write failed.\")\n\t\t\t}\n\n\t\t\tassert.NoError(t, w.Close(), \"Writer.Close failed.\")\n\n\t\t\t// Turn []observer.LoggedEntry => []zapcore.Entry\n\t\t\tgot := make([]zapcore.Entry, observed.Len())\n\t\t\tfor i, ent := range observed.AllUntimed() {\n\t\t\t\tgot[i] = ent.Entry\n\t\t\t}\n\t\t\tassert.Equal(t, tt.want, got, \"Logged entries do not match.\")\n\t\t})\n\t}\n}\n\nfunc TestWrite_Sync(t *testing.T) {\n\tt.Parallel()\n\n\tcore, observed := observer.New(zap.InfoLevel)\n\n\tw := Writer{\n\t\tLog:   zap.New(core),\n\t\tLevel: zap.InfoLevel,\n\t}\n\n\tio.WriteString(&w, \"foo\")\n\tio.WriteString(&w, \"bar\")\n\n\tt.Run(\"no sync\", func(t *testing.T) {\n\t\tassert.Zero(t, observed.Len(), \"Expected no logs yet\")\n\t})\n\n\tt.Run(\"sync\", func(t *testing.T) {\n\t\tdefer observed.TakeAll()\n\n\t\trequire.NoError(t, w.Sync(), \"Sync must not fail\")\n\n\t\tassert.Equal(t, []observer.LoggedEntry{\n\t\t\t{Entry: zapcore.Entry{Message: \"foobar\"}, Context: []zapcore.Field{}},\n\t\t}, observed.AllUntimed(), \"Log messages did not match\")\n\t})\n\n\tt.Run(\"sync on empty\", func(t *testing.T) {\n\t\trequire.NoError(t, w.Sync(), \"Sync must not fail\")\n\t\tassert.Zero(t, observed.Len(), \"Expected no logs yet\")\n\t})\n}\n\nfunc BenchmarkWriter(b *testing.B) {\n\ttests := []struct {\n\t\tname   string\n\t\twrites [][]byte\n\t}{\n\t\t{\n\t\t\tname: \"single\",\n\t\t\twrites: [][]byte{\n\t\t\t\t[]byte(\"foobar\\n\"),\n\t\t\t\t[]byte(\"bazqux\\n\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"splits\",\n\t\t\twrites: [][]byte{\n\t\t\t\t[]byte(\"foo\"),\n\t\t\t\t[]byte(\"bar\\nbaz\"),\n\t\t\t\t[]byte(\"qux\\n\"),\n\t\t\t},\n\t\t},\n\t}\n\n\twriter := Writer{\n\t\tLog:   zap.New(new(partiallyNopCore)),\n\t\tLevel: zapcore.DebugLevel,\n\t}\n\n\tfor _, tt := range tests {\n\t\tb.Run(tt.name, func(b *testing.B) {\n\t\t\tb.ResetTimer()\n\n\t\t\tfor i := 0; i < b.N; i++ {\n\t\t\t\tfor _, bs := range tt.writes {\n\t\t\t\t\twriter.Write(bs)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\n// partiallyNopCore behaves exactly like NopCore except it always returns true\n// for whether the provided level is enabled, and accepts all Check requests.\n//\n// This lets us measure the overhead of the writer without measuring the cost\n// of logging.\ntype partiallyNopCore struct{}\n\nfunc (*partiallyNopCore) Enabled(zapcore.Level) bool { return true }\n\nfunc (c *partiallyNopCore) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {\n\treturn ce.AddCore(ent, c)\n}\n\nfunc (c *partiallyNopCore) With([]zapcore.Field) zapcore.Core        { return c }\nfunc (*partiallyNopCore) Write(zapcore.Entry, []zapcore.Field) error { return nil }\nfunc (*partiallyNopCore) Sync() error                                { return nil }\n"
  },
  {
    "path": "zaptest/doc.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// Package zaptest provides a variety of helpers for testing log output.\npackage zaptest // import \"go.uber.org/zap/zaptest\"\n"
  },
  {
    "path": "zaptest/logger.go",
    "content": "// Copyright (c) 2017 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zaptest\n\nimport (\n\t\"bytes\"\n\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\n// LoggerOption configures the test logger built by NewLogger.\ntype LoggerOption interface {\n\tapplyLoggerOption(*loggerOptions)\n}\n\ntype loggerOptions struct {\n\tLevel      zapcore.LevelEnabler\n\tzapOptions []zap.Option\n}\n\ntype loggerOptionFunc func(*loggerOptions)\n\nfunc (f loggerOptionFunc) applyLoggerOption(opts *loggerOptions) {\n\tf(opts)\n}\n\n// Level controls which messages are logged by a test Logger built by\n// NewLogger.\nfunc Level(enab zapcore.LevelEnabler) LoggerOption {\n\treturn loggerOptionFunc(func(opts *loggerOptions) {\n\t\topts.Level = enab\n\t})\n}\n\n// WrapOptions adds zap.Option's to a test Logger built by NewLogger.\nfunc WrapOptions(zapOpts ...zap.Option) LoggerOption {\n\treturn loggerOptionFunc(func(opts *loggerOptions) {\n\t\topts.zapOptions = zapOpts\n\t})\n}\n\n// NewLogger builds a new Logger that logs all messages to the given\n// testing.TB.\n//\n//\tlogger := zaptest.NewLogger(t)\n//\n// Use this with a *testing.T or *testing.B to get logs which get printed only\n// if a test fails or if you ran go test -v.\n//\n// The returned logger defaults to logging debug level messages and above.\n// This may be changed by passing a zaptest.Level during construction.\n//\n//\tlogger := zaptest.NewLogger(t, zaptest.Level(zap.WarnLevel))\n//\n// You may also pass zap.Option's to customize test logger.\n//\n//\tlogger := zaptest.NewLogger(t, zaptest.WrapOptions(zap.AddCaller()))\nfunc NewLogger(t TestingT, opts ...LoggerOption) *zap.Logger {\n\tcfg := loggerOptions{\n\t\tLevel: zapcore.DebugLevel,\n\t}\n\tfor _, o := range opts {\n\t\to.applyLoggerOption(&cfg)\n\t}\n\n\twriter := NewTestingWriter(t)\n\tzapOptions := []zap.Option{\n\t\t// Send zap errors to the same writer and mark the test as failed if\n\t\t// that happens.\n\t\tzap.ErrorOutput(writer.WithMarkFailed(true)),\n\t}\n\tzapOptions = append(zapOptions, cfg.zapOptions...)\n\n\treturn zap.New(\n\t\tzapcore.NewCore(\n\t\t\tzapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig()),\n\t\t\twriter,\n\t\t\tcfg.Level,\n\t\t),\n\t\tzapOptions...,\n\t)\n}\n\n// TestingWriter is a WriteSyncer that writes to the given testing.TB.\ntype TestingWriter struct {\n\tt TestingT\n\n\t// If true, the test will be marked as failed if this TestingWriter is\n\t// ever used.\n\tmarkFailed bool\n}\n\n// NewTestingWriter builds a new TestingWriter that writes to the given\n// testing.TB.\n//\n// Use this if you need more flexibility when creating *zap.Logger\n// than zaptest.NewLogger() provides.\n//\n// E.g., if you want to use custom core with zaptest.TestingWriter:\n//\n//\tencoder := newCustomEncoder()\n//\twriter := zaptest.NewTestingWriter(t)\n//\tlevel := zap.NewAtomicLevelAt(zapcore.DebugLevel)\n//\n//\tcore := newCustomCore(encoder, writer, level)\n//\n//\tlogger := zap.New(core, zap.AddCaller())\nfunc NewTestingWriter(t TestingT) TestingWriter {\n\treturn TestingWriter{t: t}\n}\n\n// WithMarkFailed returns a copy of this TestingWriter with markFailed set to\n// the provided value.\nfunc (w TestingWriter) WithMarkFailed(v bool) TestingWriter {\n\tw.markFailed = v\n\treturn w\n}\n\n// Write writes bytes from p to the underlying testing.TB.\nfunc (w TestingWriter) Write(p []byte) (n int, err error) {\n\tn = len(p)\n\n\t// Strip trailing newline because t.Log always adds one.\n\tp = bytes.TrimRight(p, \"\\n\")\n\n\t// Note: t.Log is safe for concurrent use.\n\tw.t.Logf(\"%s\", p)\n\tif w.markFailed {\n\t\tw.t.Fail()\n\t}\n\n\treturn n, nil\n}\n\n// Sync commits the current contents (a no-op for TestingWriter).\nfunc (w TestingWriter) Sync() error {\n\treturn nil\n}\n"
  },
  {
    "path": "zaptest/logger_test.go",
    "content": "// Copyright (c) 2017 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zaptest\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/internal/ztest\"\n\t\"go.uber.org/zap/zapcore\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestTestLogger(t *testing.T) {\n\tts := newTestLogSpy(t)\n\tdefer ts.AssertPassed()\n\n\tlog := NewLogger(ts)\n\n\tlog.Info(\"received work order\")\n\tlog.Debug(\"starting work\")\n\tlog.Warn(\"work may fail\")\n\tlog.Error(\"work failed\", zap.Error(errors.New(\"great sadness\")))\n\n\tassert.Panics(t, func() {\n\t\tlog.Panic(\"failed to do work\")\n\t}, \"log.Panic should panic\")\n\n\tts.AssertMessages(\n\t\t\"INFO\treceived work order\",\n\t\t\"DEBUG\tstarting work\",\n\t\t\"WARN\twork may fail\",\n\t\t`ERROR\twork failed\t{\"error\": \"great sadness\"}`,\n\t\t\"PANIC\tfailed to do work\",\n\t)\n}\n\nfunc TestTestLoggerSupportsLevels(t *testing.T) {\n\tts := newTestLogSpy(t)\n\tdefer ts.AssertPassed()\n\n\tlog := NewLogger(ts, Level(zap.WarnLevel))\n\n\tlog.Info(\"received work order\")\n\tlog.Debug(\"starting work\")\n\tlog.Warn(\"work may fail\")\n\tlog.Error(\"work failed\", zap.Error(errors.New(\"great sadness\")))\n\n\tassert.Panics(t, func() {\n\t\tlog.Panic(\"failed to do work\")\n\t}, \"log.Panic should panic\")\n\n\tts.AssertMessages(\n\t\t\"WARN\twork may fail\",\n\t\t`ERROR\twork failed\t{\"error\": \"great sadness\"}`,\n\t\t\"PANIC\tfailed to do work\",\n\t)\n}\n\nfunc TestTestLoggerSupportsWrappedZapOptions(t *testing.T) {\n\tts := newTestLogSpy(t)\n\tdefer ts.AssertPassed()\n\n\tlog := NewLogger(ts, WrapOptions(zap.AddCaller(), zap.Fields(zap.String(\"k1\", \"v1\"))))\n\n\tlog.Info(\"received work order\")\n\tlog.Debug(\"starting work\")\n\tlog.Warn(\"work may fail\")\n\tlog.Error(\"work failed\", zap.Error(errors.New(\"great sadness\")))\n\n\tassert.Panics(t, func() {\n\t\tlog.Panic(\"failed to do work\")\n\t}, \"log.Panic should panic\")\n\n\tts.AssertMessages(\n\t\t`INFO\tzaptest/logger_test.go:89\treceived work order\t{\"k1\": \"v1\"}`,\n\t\t`DEBUG\tzaptest/logger_test.go:90\tstarting work\t{\"k1\": \"v1\"}`,\n\t\t`WARN\tzaptest/logger_test.go:91\twork may fail\t{\"k1\": \"v1\"}`,\n\t\t`ERROR\tzaptest/logger_test.go:92\twork failed\t{\"k1\": \"v1\", \"error\": \"great sadness\"}`,\n\t\t`PANIC\tzaptest/logger_test.go:95\tfailed to do work\t{\"k1\": \"v1\"}`,\n\t)\n}\n\nfunc TestTestingWriter(t *testing.T) {\n\tts := newTestLogSpy(t)\n\tw := NewTestingWriter(ts)\n\n\tn, err := io.WriteString(w, \"hello\\n\\n\")\n\tassert.NoError(t, err, \"WriteString must not fail\")\n\tassert.Equal(t, 7, n)\n}\n\nfunc TestTestLoggerErrorOutput(t *testing.T) {\n\t// This test verifies that the test logger logs internal messages to the\n\t// testing.T and marks the test as failed.\n\n\tts := newTestLogSpy(t)\n\tdefer ts.AssertFailed()\n\n\tlog := NewLogger(ts)\n\n\t// Replace with a core that fails.\n\tlog = log.WithOptions(zap.WrapCore(func(zapcore.Core) zapcore.Core {\n\t\treturn zapcore.NewCore(\n\t\t\tzapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig()),\n\t\t\tzapcore.Lock(zapcore.AddSync(ztest.FailWriter{})),\n\t\t\tzapcore.DebugLevel,\n\t\t)\n\t}))\n\n\tlog.Info(\"foo\") // this fails\n\n\tif assert.Len(t, ts.Messages, 1, \"expected a log message\") {\n\t\tassert.Regexp(t, `write error: failed`, ts.Messages[0])\n\t}\n}\n\n// testLogSpy is a testing.TB that captures logged messages.\ntype testLogSpy struct {\n\ttesting.TB\n\n\tfailed   bool\n\tMessages []string\n}\n\nfunc newTestLogSpy(t testing.TB) *testLogSpy {\n\treturn &testLogSpy{TB: t}\n}\n\nfunc (t *testLogSpy) Fail() {\n\tt.failed = true\n}\n\nfunc (t *testLogSpy) Failed() bool {\n\treturn t.failed\n}\n\nfunc (t *testLogSpy) FailNow() {\n\tt.Fail()\n\tt.TB.FailNow()\n}\n\nfunc (t *testLogSpy) Logf(format string, args ...interface{}) {\n\t// Log messages are in the format,\n\t//\n\t//   2017-10-27T13:03:01.000-0700\tDEBUG\tyour message here\t{data here}\n\t//\n\t// We strip the first part of these messages because we can't really test\n\t// for the timestamp from these tests.\n\tm := fmt.Sprintf(format, args...)\n\tm = m[strings.IndexByte(m, '\\t')+1:]\n\tt.Messages = append(t.Messages, m)\n\tt.TB.Log(m)\n}\n\nfunc (t *testLogSpy) AssertMessages(msgs ...string) {\n\tassert.Equal(t.TB, msgs, t.Messages, \"logged messages did not match\")\n}\n\nfunc (t *testLogSpy) AssertPassed() {\n\tt.assertFailed(false, \"expected test to pass\")\n}\n\nfunc (t *testLogSpy) AssertFailed() {\n\tt.assertFailed(true, \"expected test to fail\")\n}\n\nfunc (t *testLogSpy) assertFailed(v bool, msg string) {\n\tassert.Equal(t.TB, v, t.failed, msg)\n}\n"
  },
  {
    "path": "zaptest/observer/logged_entry.go",
    "content": "// Copyright (c) 2017 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage observer\n\nimport \"go.uber.org/zap/zapcore\"\n\n// A LoggedEntry is an encoding-agnostic representation of a log message.\n// Field availability is context dependent.\ntype LoggedEntry struct {\n\tzapcore.Entry\n\tContext []zapcore.Field\n}\n\n// ContextMap returns a map for all fields in Context.\nfunc (e LoggedEntry) ContextMap() map[string]interface{} {\n\tencoder := zapcore.NewMapObjectEncoder()\n\tfor _, f := range e.Context {\n\t\tf.AddTo(encoder)\n\t}\n\treturn encoder.Fields\n}\n"
  },
  {
    "path": "zaptest/observer/logged_entry_test.go",
    "content": "// Copyright (c) 2017 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage observer\n\nimport (\n\t\"testing\"\n\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestLoggedEntryContextMap(t *testing.T) {\n\ttests := []struct {\n\t\tmsg    string\n\t\tfields []zapcore.Field\n\t\twant   map[string]interface{}\n\t}{\n\t\t{\n\t\t\tmsg:    \"no fields\",\n\t\t\tfields: nil,\n\t\t\twant:   map[string]interface{}{},\n\t\t},\n\t\t{\n\t\t\tmsg: \"simple\",\n\t\t\tfields: []zapcore.Field{\n\t\t\t\tzap.String(\"k1\", \"v\"),\n\t\t\t\tzap.Int64(\"k2\", 10),\n\t\t\t},\n\t\t\twant: map[string]interface{}{\n\t\t\t\t\"k1\": \"v\",\n\t\t\t\t\"k2\": int64(10),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmsg: \"overwrite\",\n\t\t\tfields: []zapcore.Field{\n\t\t\t\tzap.String(\"k1\", \"v1\"),\n\t\t\t\tzap.String(\"k1\", \"v2\"),\n\t\t\t},\n\t\t\twant: map[string]interface{}{\n\t\t\t\t\"k1\": \"v2\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmsg: \"nested\",\n\t\t\tfields: []zapcore.Field{\n\t\t\t\tzap.String(\"k1\", \"v1\"),\n\t\t\t\tzap.Namespace(\"nested\"),\n\t\t\t\tzap.String(\"k2\", \"v2\"),\n\t\t\t},\n\t\t\twant: map[string]interface{}{\n\t\t\t\t\"k1\": \"v1\",\n\t\t\t\t\"nested\": map[string]interface{}{\n\t\t\t\t\t\"k2\": \"v2\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.msg, func(t *testing.T) {\n\t\t\tentry := LoggedEntry{\n\t\t\t\tContext: tt.fields,\n\t\t\t}\n\t\t\tassert.Equal(t, tt.want, entry.ContextMap())\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "zaptest/observer/observer.go",
    "content": "// Copyright (c) 2016-2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\n// Package observer provides a zapcore.Core that keeps an in-memory,\n// encoding-agnostic representation of log entries. It's useful for\n// applications that want to unit test their log output without tying their\n// tests to a particular output encoding.\npackage observer // import \"go.uber.org/zap/zaptest/observer\"\n\nimport (\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"go.uber.org/zap/internal\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\n// ObservedLogs is a concurrency-safe, ordered collection of observed logs.\ntype ObservedLogs struct {\n\tmu   sync.RWMutex\n\tlogs []LoggedEntry\n}\n\n// Len returns the number of items in the collection.\nfunc (o *ObservedLogs) Len() int {\n\to.mu.RLock()\n\tn := len(o.logs)\n\to.mu.RUnlock()\n\treturn n\n}\n\n// All returns a copy of all the observed logs.\nfunc (o *ObservedLogs) All() []LoggedEntry {\n\to.mu.RLock()\n\tret := make([]LoggedEntry, len(o.logs))\n\tcopy(ret, o.logs)\n\to.mu.RUnlock()\n\treturn ret\n}\n\n// TakeAll returns a copy of all the observed logs, and truncates the observed\n// slice.\nfunc (o *ObservedLogs) TakeAll() []LoggedEntry {\n\to.mu.Lock()\n\tret := o.logs\n\to.logs = nil\n\to.mu.Unlock()\n\treturn ret\n}\n\n// AllUntimed returns a copy of all the observed logs, but overwrites the\n// observed timestamps with time.Time's zero value. This is useful when making\n// assertions in tests.\nfunc (o *ObservedLogs) AllUntimed() []LoggedEntry {\n\tret := o.All()\n\tfor i := range ret {\n\t\tret[i].Time = time.Time{}\n\t}\n\treturn ret\n}\n\n// FilterLevelExact filters entries to those logged at exactly the given level.\nfunc (o *ObservedLogs) FilterLevelExact(level zapcore.Level) *ObservedLogs {\n\treturn o.Filter(func(e LoggedEntry) bool {\n\t\treturn e.Level == level\n\t})\n}\n\n// FilterMessage filters entries to those that have the specified message.\nfunc (o *ObservedLogs) FilterMessage(msg string) *ObservedLogs {\n\treturn o.Filter(func(e LoggedEntry) bool {\n\t\treturn e.Message == msg\n\t})\n}\n\n// FilterLoggerName filters entries to those logged through logger with the specified logger name.\nfunc (o *ObservedLogs) FilterLoggerName(name string) *ObservedLogs {\n\treturn o.Filter(func(e LoggedEntry) bool {\n\t\treturn e.LoggerName == name\n\t})\n}\n\n// FilterMessageSnippet filters entries to those that have a message containing the specified snippet.\nfunc (o *ObservedLogs) FilterMessageSnippet(snippet string) *ObservedLogs {\n\treturn o.Filter(func(e LoggedEntry) bool {\n\t\treturn strings.Contains(e.Message, snippet)\n\t})\n}\n\n// FilterField filters entries to those that have the specified field.\nfunc (o *ObservedLogs) FilterField(field zapcore.Field) *ObservedLogs {\n\treturn o.Filter(func(e LoggedEntry) bool {\n\t\tfor _, ctxField := range e.Context {\n\t\t\tif ctxField.Equals(field) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\treturn false\n\t})\n}\n\n// FilterFieldKey filters entries to those that have the specified key.\nfunc (o *ObservedLogs) FilterFieldKey(key string) *ObservedLogs {\n\treturn o.Filter(func(e LoggedEntry) bool {\n\t\tfor _, ctxField := range e.Context {\n\t\t\tif ctxField.Key == key {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\treturn false\n\t})\n}\n\n// Filter returns a copy of this ObservedLogs containing only those entries\n// for which the provided function returns true.\nfunc (o *ObservedLogs) Filter(keep func(LoggedEntry) bool) *ObservedLogs {\n\to.mu.RLock()\n\tdefer o.mu.RUnlock()\n\n\tvar filtered []LoggedEntry\n\tfor _, entry := range o.logs {\n\t\tif keep(entry) {\n\t\t\tfiltered = append(filtered, entry)\n\t\t}\n\t}\n\treturn &ObservedLogs{logs: filtered}\n}\n\nfunc (o *ObservedLogs) add(log LoggedEntry) {\n\to.mu.Lock()\n\to.logs = append(o.logs, log)\n\to.mu.Unlock()\n}\n\n// New creates a new Core that buffers logs in memory (without any encoding).\n// It's particularly useful in tests.\nfunc New(enab zapcore.LevelEnabler) (zapcore.Core, *ObservedLogs) {\n\tol := &ObservedLogs{}\n\treturn &contextObserver{\n\t\tLevelEnabler: enab,\n\t\tlogs:         ol,\n\t}, ol\n}\n\ntype contextObserver struct {\n\tzapcore.LevelEnabler\n\tlogs    *ObservedLogs\n\tcontext []zapcore.Field\n}\n\nvar (\n\t_ zapcore.Core            = (*contextObserver)(nil)\n\t_ internal.LeveledEnabler = (*contextObserver)(nil)\n)\n\nfunc (co *contextObserver) Level() zapcore.Level {\n\treturn zapcore.LevelOf(co.LevelEnabler)\n}\n\nfunc (co *contextObserver) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {\n\tif co.Enabled(ent.Level) {\n\t\treturn ce.AddCore(ent, co)\n\t}\n\treturn ce\n}\n\nfunc (co *contextObserver) With(fields []zapcore.Field) zapcore.Core {\n\treturn &contextObserver{\n\t\tLevelEnabler: co.LevelEnabler,\n\t\tlogs:         co.logs,\n\t\tcontext:      append(co.context[:len(co.context):len(co.context)], fields...),\n\t}\n}\n\nfunc (co *contextObserver) Write(ent zapcore.Entry, fields []zapcore.Field) error {\n\tall := make([]zapcore.Field, 0, len(fields)+len(co.context))\n\tall = append(all, co.context...)\n\tall = append(all, fields...)\n\tco.logs.add(LoggedEntry{ent, all})\n\treturn nil\n}\n\nfunc (co *contextObserver) Sync() error {\n\treturn nil\n}\n"
  },
  {
    "path": "zaptest/observer/observer_test.go",
    "content": "// Copyright (c) 2016-2022 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage observer_test\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n\n\t//revive:disable:dot-imports\n\t. \"go.uber.org/zap/zaptest/observer\"\n)\n\nfunc assertEmpty(t testing.TB, logs *ObservedLogs) {\n\tassert.Equal(t, 0, logs.Len(), \"Expected empty ObservedLogs to have zero length.\")\n\tassert.Equal(t, []LoggedEntry{}, logs.All(), \"Unexpected LoggedEntries in empty ObservedLogs.\")\n}\n\nfunc TestObserver(t *testing.T) {\n\tobserver, logs := New(zap.InfoLevel)\n\tassertEmpty(t, logs)\n\n\tt.Run(\"LevelOf\", func(t *testing.T) {\n\t\tassert.Equal(t, zap.InfoLevel, zapcore.LevelOf(observer), \"Observer reported the wrong log level.\")\n\t})\n\n\tassert.NoError(t, observer.Sync(), \"Unexpected failure in no-op Sync\")\n\n\tobs := zap.New(observer).With(zap.Int(\"i\", 1))\n\tobs.Info(\"foo\")\n\tobs.Debug(\"bar\")\n\twant := []LoggedEntry{{\n\t\tEntry:   zapcore.Entry{Level: zap.InfoLevel, Message: \"foo\"},\n\t\tContext: []zapcore.Field{zap.Int(\"i\", 1)},\n\t}}\n\n\tassert.Equal(t, 1, logs.Len(), \"Unexpected observed logs Len.\")\n\tassert.Equal(t, want, logs.AllUntimed(), \"Unexpected contents from AllUntimed.\")\n\n\tall := logs.All()\n\trequire.Equal(t, 1, len(all), \"Unexpected number of LoggedEntries returned from All.\")\n\tassert.NotEqual(t, time.Time{}, all[0].Time, \"Expected non-zero time on LoggedEntry.\")\n\n\t// copy & zero time for stable assertions\n\tuntimed := append([]LoggedEntry{}, all...)\n\tuntimed[0].Time = time.Time{}\n\tassert.Equal(t, want, untimed, \"Unexpected LoggedEntries from All.\")\n\n\tassert.Equal(t, all, logs.TakeAll(), \"Expected All and TakeAll to return identical results.\")\n\tassertEmpty(t, logs)\n}\n\nfunc TestObserverWith(t *testing.T) {\n\tsf1, logs := New(zap.InfoLevel)\n\n\t// need to pad out enough initial fields so that the underlying slice cap()\n\t// gets ahead of its len() so that the sf3/4 With append's could choose\n\t// not to copy (if the implementation doesn't force them)\n\tsf1 = sf1.With([]zapcore.Field{zap.Int(\"a\", 1), zap.Int(\"b\", 2)})\n\n\tsf2 := sf1.With([]zapcore.Field{zap.Int(\"c\", 3)})\n\tsf3 := sf2.With([]zapcore.Field{zap.Int(\"d\", 4)})\n\tsf4 := sf2.With([]zapcore.Field{zap.Int(\"e\", 5)})\n\tent := zapcore.Entry{Level: zap.InfoLevel, Message: \"hello\"}\n\n\tfor i, core := range []zapcore.Core{sf2, sf3, sf4} {\n\t\tif ce := core.Check(ent, nil); ce != nil {\n\t\t\tce.Write(zap.Int(\"i\", i))\n\t\t}\n\t}\n\n\tassert.Equal(t, []LoggedEntry{\n\t\t{\n\t\t\tEntry: ent,\n\t\t\tContext: []zapcore.Field{\n\t\t\t\tzap.Int(\"a\", 1),\n\t\t\t\tzap.Int(\"b\", 2),\n\t\t\t\tzap.Int(\"c\", 3),\n\t\t\t\tzap.Int(\"i\", 0),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tEntry: ent,\n\t\t\tContext: []zapcore.Field{\n\t\t\t\tzap.Int(\"a\", 1),\n\t\t\t\tzap.Int(\"b\", 2),\n\t\t\t\tzap.Int(\"c\", 3),\n\t\t\t\tzap.Int(\"d\", 4),\n\t\t\t\tzap.Int(\"i\", 1),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tEntry: ent,\n\t\t\tContext: []zapcore.Field{\n\t\t\t\tzap.Int(\"a\", 1),\n\t\t\t\tzap.Int(\"b\", 2),\n\t\t\t\tzap.Int(\"c\", 3),\n\t\t\t\tzap.Int(\"e\", 5),\n\t\t\t\tzap.Int(\"i\", 2),\n\t\t\t},\n\t\t},\n\t}, logs.All(), \"expected no field sharing between With siblings\")\n}\n\nfunc TestFilters(t *testing.T) {\n\tlogs := []LoggedEntry{\n\t\t{\n\t\t\tEntry:   zapcore.Entry{Level: zap.InfoLevel, Message: \"log a\"},\n\t\t\tContext: []zapcore.Field{zap.String(\"fStr\", \"1\"), zap.Int(\"a\", 1)},\n\t\t},\n\t\t{\n\t\t\tEntry:   zapcore.Entry{Level: zap.InfoLevel, Message: \"log a\"},\n\t\t\tContext: []zapcore.Field{zap.String(\"fStr\", \"2\"), zap.Int(\"b\", 2)},\n\t\t},\n\t\t{\n\t\t\tEntry:   zapcore.Entry{Level: zap.InfoLevel, Message: \"log b\"},\n\t\t\tContext: []zapcore.Field{zap.Int(\"a\", 1), zap.Int(\"b\", 2)},\n\t\t},\n\t\t{\n\t\t\tEntry:   zapcore.Entry{Level: zap.InfoLevel, Message: \"log c\"},\n\t\t\tContext: []zapcore.Field{zap.Int(\"a\", 1), zap.Namespace(\"ns\"), zap.Int(\"a\", 2)},\n\t\t},\n\t\t{\n\t\t\tEntry:   zapcore.Entry{Level: zap.InfoLevel, Message: \"msg 1\"},\n\t\t\tContext: []zapcore.Field{zap.Int(\"a\", 1), zap.Namespace(\"ns\")},\n\t\t},\n\t\t{\n\t\t\tEntry:   zapcore.Entry{Level: zap.InfoLevel, Message: \"any map\"},\n\t\t\tContext: []zapcore.Field{zap.Any(\"map\", map[string]string{\"a\": \"b\"})},\n\t\t},\n\t\t{\n\t\t\tEntry:   zapcore.Entry{Level: zap.InfoLevel, Message: \"any slice\"},\n\t\t\tContext: []zapcore.Field{zap.Any(\"slice\", []string{\"a\"})},\n\t\t},\n\t\t{\n\t\t\tEntry:   zapcore.Entry{Level: zap.InfoLevel, Message: \"msg 2\"},\n\t\t\tContext: []zapcore.Field{zap.Int(\"b\", 2), zap.Namespace(\"filterMe\")},\n\t\t},\n\t\t{\n\t\t\tEntry:   zapcore.Entry{Level: zap.InfoLevel, Message: \"any slice\"},\n\t\t\tContext: []zapcore.Field{zap.Any(\"filterMe\", []string{\"b\"})},\n\t\t},\n\t\t{\n\t\t\tEntry:   zapcore.Entry{Level: zap.WarnLevel, Message: \"danger will robinson\"},\n\t\t\tContext: []zapcore.Field{zap.Int(\"b\", 42)},\n\t\t},\n\t\t{\n\t\t\tEntry:   zapcore.Entry{Level: zap.ErrorLevel, Message: \"warp core breach\"},\n\t\t\tContext: []zapcore.Field{zap.Int(\"b\", 42)},\n\t\t},\n\t\t{\n\t\t\tEntry:   zapcore.Entry{Level: zap.ErrorLevel, Message: \"msg\", LoggerName: \"my.logger\"},\n\t\t\tContext: []zapcore.Field{zap.Int(\"b\", 42)},\n\t\t},\n\t}\n\n\tlogger, sink := New(zap.InfoLevel)\n\tfor _, log := range logs {\n\t\tassert.NoError(t, logger.Write(log.Entry, log.Context), \"Unexpected error writing log entry.\")\n\t}\n\n\ttests := []struct {\n\t\tmsg      string\n\t\tfiltered *ObservedLogs\n\t\twant     []LoggedEntry\n\t}{\n\t\t{\n\t\t\tmsg:      \"filter by message\",\n\t\t\tfiltered: sink.FilterMessage(\"log a\"),\n\t\t\twant:     logs[0:2],\n\t\t},\n\t\t{\n\t\t\tmsg:      \"filter by field\",\n\t\t\tfiltered: sink.FilterField(zap.String(\"fStr\", \"1\")),\n\t\t\twant:     logs[0:1],\n\t\t},\n\t\t{\n\t\t\tmsg:      \"filter by message and field\",\n\t\t\tfiltered: sink.FilterMessage(\"log a\").FilterField(zap.Int(\"b\", 2)),\n\t\t\twant:     logs[1:2],\n\t\t},\n\t\t{\n\t\t\tmsg:      \"filter by field with duplicate fields\",\n\t\t\tfiltered: sink.FilterField(zap.Int(\"a\", 2)),\n\t\t\twant:     logs[3:4],\n\t\t},\n\t\t{\n\t\t\tmsg:      \"filter doesn't match any messages\",\n\t\t\tfiltered: sink.FilterMessage(\"no match\"),\n\t\t\twant:     []LoggedEntry{},\n\t\t},\n\t\t{\n\t\t\tmsg:      \"filter by snippet\",\n\t\t\tfiltered: sink.FilterMessageSnippet(\"log\"),\n\t\t\twant:     logs[0:4],\n\t\t},\n\t\t{\n\t\t\tmsg:      \"filter by snippet and field\",\n\t\t\tfiltered: sink.FilterMessageSnippet(\"a\").FilterField(zap.Int(\"b\", 2)),\n\t\t\twant:     logs[1:2],\n\t\t},\n\t\t{\n\t\t\tmsg:      \"filter for map\",\n\t\t\tfiltered: sink.FilterField(zap.Any(\"map\", map[string]string{\"a\": \"b\"})),\n\t\t\twant:     logs[5:6],\n\t\t},\n\t\t{\n\t\t\tmsg:      \"filter for slice\",\n\t\t\tfiltered: sink.FilterField(zap.Any(\"slice\", []string{\"a\"})),\n\t\t\twant:     logs[6:7],\n\t\t},\n\t\t{\n\t\t\tmsg:      \"filter field key\",\n\t\t\tfiltered: sink.FilterFieldKey(\"filterMe\"),\n\t\t\twant:     logs[7:9],\n\t\t},\n\t\t{\n\t\t\tmsg: \"filter by arbitrary function\",\n\t\t\tfiltered: sink.Filter(func(e LoggedEntry) bool {\n\t\t\t\treturn len(e.Context) > 1\n\t\t\t}),\n\t\t\twant: func() []LoggedEntry {\n\t\t\t\t// Do not modify logs slice.\n\t\t\t\tw := make([]LoggedEntry, 0, len(logs))\n\t\t\t\tw = append(w, logs[0:5]...)\n\t\t\t\tw = append(w, logs[7])\n\t\t\t\treturn w\n\t\t\t}(),\n\t\t},\n\t\t{\n\t\t\tmsg:      \"filter level\",\n\t\t\tfiltered: sink.FilterLevelExact(zap.WarnLevel),\n\t\t\twant:     logs[9:10],\n\t\t},\n\t\t{\n\t\t\tmsg:      \"filter logger name\",\n\t\t\tfiltered: sink.FilterLoggerName(\"my.logger\"),\n\t\t\twant:     logs[11:12],\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tgot := tt.filtered.AllUntimed()\n\t\tassert.Equal(t, tt.want, got, tt.msg)\n\t}\n}\n"
  },
  {
    "path": "zaptest/testingt.go",
    "content": "// Copyright (c) 2017 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zaptest\n\n// TestingT is a subset of the API provided by all *testing.T and *testing.B\n// objects.\ntype TestingT interface {\n\t// Logs the given message without failing the test.\n\tLogf(string, ...interface{})\n\n\t// Logs the given message and marks the test as failed.\n\tErrorf(string, ...interface{})\n\n\t// Marks the test as failed.\n\tFail()\n\n\t// Returns true if the test has been marked as failed.\n\tFailed() bool\n\n\t// Returns the name of the test.\n\tName() string\n\n\t// Marks the test as failed and stops execution of that test.\n\tFailNow()\n}\n\n// Note: We currently only rely on Logf. We are including Errorf and FailNow\n// in the interface in anticipation of future need since we can't extend the\n// interface without a breaking change.\n"
  },
  {
    "path": "zaptest/testingt_test.go",
    "content": "// Copyright (c) 2017 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zaptest\n\nimport \"testing\"\n\n// Just a compile-time test to ensure that TestingT matches the testing.TB\n// interface. We could do this in testingt.go but that would put a dependency\n// on the \"testing\" package from zaptest.\n\nvar _ TestingT = (testing.TB)(nil)\n"
  },
  {
    "path": "zaptest/timeout.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zaptest\n\nimport (\n\t\"time\"\n\n\t\"go.uber.org/zap/internal/ztest\"\n)\n\n// Timeout scales the provided duration by $TEST_TIMEOUT_SCALE.\n//\n// Deprecated: This function is intended for internal testing and shouldn't be\n// used outside zap itself. It was introduced before Go supported internal\n// packages.\nfunc Timeout(base time.Duration) time.Duration {\n\treturn ztest.Timeout(base)\n}\n\n// Sleep scales the sleep duration by $TEST_TIMEOUT_SCALE.\n//\n// Deprecated: This function is intended for internal testing and shouldn't be\n// used outside zap itself. It was introduced before Go supported internal\n// packages.\nfunc Sleep(base time.Duration) {\n\tztest.Sleep(base)\n}\n"
  },
  {
    "path": "zaptest/timeout_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zaptest\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/zap/internal/ztest\"\n)\n\nfunc TestTimeout(t *testing.T) {\n\tdefer ztest.Initialize(\"2\")()\n\tassert.Equal(t, time.Duration(100), Timeout(50), \"Expected to scale up timeout.\")\n}\n\nfunc TestSleep(t *testing.T) {\n\tdefer ztest.Initialize(\"2\")()\n\tconst sleepFor = 50 * time.Millisecond\n\tnow := time.Now()\n\tSleep(sleepFor)\n\telapsed := time.Since(now)\n\tassert.True(t, 2*sleepFor <= elapsed, \"Expected to scale up timeout.\")\n}\n"
  },
  {
    "path": "zaptest/writer.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zaptest\n\nimport \"go.uber.org/zap/internal/ztest\"\n\ntype (\n\t// A Syncer is a spy for the Sync portion of zapcore.WriteSyncer.\n\tSyncer = ztest.Syncer\n\n\t// A Discarder sends all writes to io.Discard.\n\tDiscarder = ztest.Discarder\n\n\t// FailWriter is a WriteSyncer that always returns an error on writes.\n\tFailWriter = ztest.FailWriter\n\n\t// ShortWriter is a WriteSyncer whose write method never returns an error,\n\t// but always reports that it wrote one byte less than the input slice's\n\t// length (thus, a \"short write\").\n\tShortWriter = ztest.ShortWriter\n\n\t// Buffer is an implementation of zapcore.WriteSyncer that sends all writes to\n\t// a bytes.Buffer. It has convenience methods to split the accumulated buffer\n\t// on newlines.\n\tBuffer = ztest.Buffer\n)\n"
  },
  {
    "path": "zaptest/writer_test.go",
    "content": "// Copyright (c) 2016 Uber Technologies, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n\npackage zaptest\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestSyncer(t *testing.T) {\n\terr := errors.New(\"sentinel\")\n\ts := &Syncer{}\n\ts.SetError(err)\n\tassert.Equal(t, err, s.Sync(), \"Expected Sync to fail with provided error.\")\n\tassert.True(t, s.Called(), \"Expected to record that Sync was called.\")\n}\n\nfunc TestDiscarder(t *testing.T) {\n\td := &Discarder{}\n\tpayload := []byte(\"foo\")\n\tn, err := d.Write(payload)\n\tassert.NoError(t, err, \"Unexpected error writing to Discarder.\")\n\tassert.Equal(t, len(payload), n, \"Wrong number of bytes written.\")\n}\n\nfunc TestFailWriter(t *testing.T) {\n\tw := &FailWriter{}\n\tpayload := []byte(\"foo\")\n\tn, err := w.Write(payload)\n\tassert.Error(t, err, \"Expected an error writing to FailWriter.\")\n\tassert.Equal(t, len(payload), n, \"Wrong number of bytes written.\")\n}\n\nfunc TestShortWriter(t *testing.T) {\n\tw := &ShortWriter{}\n\tpayload := []byte(\"foo\")\n\tn, err := w.Write(payload)\n\tassert.NoError(t, err, \"Unexpected error writing to ShortWriter.\")\n\tassert.Equal(t, len(payload)-1, n, \"Wrong number of bytes written.\")\n}\n\nfunc TestBuffer(t *testing.T) {\n\tbuf := &Buffer{}\n\tbuf.WriteString(\"foo\\n\")\n\tbuf.WriteString(\"bar\\n\")\n\tassert.Equal(t, []string{\"foo\", \"bar\"}, buf.Lines(), \"Unexpected output from Lines.\")\n\tassert.Equal(t, \"foo\\nbar\", buf.Stripped(), \"Unexpected output from Stripped.\")\n}\n"
  }
]