[
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: github-actions\n    directory: /\n    schedule:\n      interval: weekly\n  - package-ecosystem: gomod\n    directory: /\n    schedule:\n      interval: weekly\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "on: [push, pull_request]\nname: Test\njobs:\n  test:\n    strategy:\n      matrix:\n        go-version: [1.21.x, 1.24.x]\n        os: [ubuntu-latest, macos-latest]\n    runs-on: ${{ matrix.os }}\n    steps:\n    - name: Install Go\n      uses: actions/setup-go@v5\n      with:\n        go-version: ${{ matrix.go-version }}\n    - name: Checkout code\n      uses: actions/checkout@v6\n    - uses: actions/cache@v4\n      with:\n        path: ~/go/pkg/mod\n        key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}\n        restore-keys: |\n          ${{ runner.os }}-go-\n    - name: Test\n      run: go test -race -bench . -benchmem ./...\n    - name: Test CBOR\n      run: go test -tags binary_log ./...\n  coverage:\n    runs-on: ubuntu-latest\n    steps:\n    - name: Update coverage report\n      uses: ncruces/go-coverage-report@main\n      with:\n        report: 'true'\n        chart: 'true'\n        amend: 'true'\n      continue-on-error: true\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\ntmp\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\r\ncoverage.out\r\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Zerolog\n\nThank you for your interest in contributing to **Zerolog**!\n\nZerolog is a **feature-complete**, high-performance logging library designed to be **lean** and **non-bloated**. The focus of ongoing development is on **bug fixes**, **performance improvements**, and **modernization efforts** (such as keeping up with Go best practices and compatibility with newer Go versions).\n\n## What We're Looking For\n\nWe welcome contributions in the following areas:\n\n- **Bug Fixes**: If you find an issue or unexpected behavior, please open an issue and/or submit a fix.\n- **Performance Optimizations**: Improvements that reduce memory usage, allocation count, or CPU cycles without introducing complexity are appreciated.\n- **Modernization**: Compatibility updates for newer Go versions or idiomatic improvements that do not increase library size or complexity.\n- **Documentation Enhancements**: Corrections, clarifications, and improvements to documentation or code comments.\n\n## What We're *Not* Looking For\n\nZerolog is intended to remain **minimalistic and efficient**. Therefore, we are **not accepting**:\n\n- New features that add optional behaviors or extend API surface area.\n- Built-in support for frameworks or external systems (e.g., bindings, integrations).\n- General-purpose abstractions or configuration helpers.\n\nIf you're unsure whether a change aligns with the project's philosophy, feel free to open an issue for discussion before submitting a PR.\n\n## Contributing Guidelines\n\n1. **Fork the repository**\n2. **Create a branch** for your fix or improvement\n3. **Write tests** to cover your changes\n4. Ensure `go test ./...` passes\n5. Run `go fmt` and `go vet` to ensure code consistency\n6. **Submit a pull request** with a clear explanation of the motivation and impact\n\n## Code Style\n\n- Keep the code simple, efficient, and idiomatic.\n- Avoid introducing new dependencies.\n- Preserve backwards compatibility unless explicitly discussed.\n\n---\n\nWe appreciate your effort in helping us keep Zerolog fast, minimal, and reliable!\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2017 Olivier Poitrey\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 all\ncopies 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 THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Zero Allocation JSON Logger\n\n[![godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/rs/zerolog) [![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://raw.githubusercontent.com/rs/zerolog/master/LICENSE) [![Build Status](https://github.com/rs/zerolog/actions/workflows/test.yml/badge.svg)](https://github.com/rs/zerolog/actions/workflows/test.yml) [![Go Coverage](https://github.com/rs/zerolog/wiki/coverage.svg)](https://raw.githack.com/wiki/rs/zerolog/coverage.html)\n\nThe zerolog package provides a fast and simple logger dedicated to JSON output.\n\nZerolog's API is designed to provide both a great developer experience and stunning [performance](#benchmarks). Its unique chaining API allows zerolog to write JSON (or CBOR) log events by avoiding allocations and reflection.\n\nUber's [zap](https://godoc.org/go.uber.org/zap) library pioneered this approach. Zerolog is taking this concept to the next level with a simpler to use API and even better performance.\n\nTo keep the code base and the API simple, zerolog focuses on efficient structured logging only. Pretty logging on the console is made possible using the provided (but inefficient) [`zerolog.ConsoleWriter`](#pretty-logging).\n\n![Pretty Logging Image](pretty.png)\n\n## Who uses zerolog\n\nFind out [who uses zerolog](https://github.com/rs/zerolog/wiki/Who-uses-zerolog) and add your company / project to the list.\n\n## Features\n\n- [Blazing fast](#benchmarks)\n- [Low to zero allocation](#benchmarks)\n- [Leveled logging](#leveled-logging)\n- [Sampling](#log-sampling)\n- [Hooks](#hooks)\n- [Contextual fields](#contextual-logging)\n- [`context.Context` integration](#contextcontext-integration)\n- [Integration with `net/http`](#integration-with-nethttp)\n- [JSON and CBOR encoding formats](#binary-encoding)\n- [Pretty logging for development](#pretty-logging)\n- [Error Logging (with optional Stacktrace)](#error-logging)\n- [`log/slog` integration](#integration-with-logslog)\n\n## Installation\n\n```bash\ngo get -u github.com/rs/zerolog/log\n```\n\n## Getting Started\n\n### Simple Logging Example\n\nFor simple logging, import the global logger package **github.com/rs/zerolog/log**\n\n```go\npackage main\n\nimport (\n    \"github.com/rs/zerolog\"\n    \"github.com/rs/zerolog/log\"\n)\n\nfunc main() {\n    // UNIX Time is faster and smaller than most timestamps\n    zerolog.TimeFieldFormat = zerolog.TimeFormatUnix\n\n    log.Print(\"hello world\")\n}\n\n// Output: {\"time\":1516134303,\"level\":\"debug\",\"message\":\"hello world\"}\n```\n\n> Note: By default log writes to `os.Stderr`\n> Note: The default log level for `log.Print` is _trace_\n\n### Contextual Logging\n\n**zerolog** allows data to be added to log messages in the form of key:value pairs. The data added to the message adds \"context\" about the log event that can be critical for debugging as well as myriad other purposes. An example of this is below:\n\n```go\npackage main\n\nimport (\n    \"github.com/rs/zerolog\"\n    \"github.com/rs/zerolog/log\"\n)\n\nfunc main() {\n    zerolog.TimeFieldFormat = zerolog.TimeFormatUnix\n\n    log.Debug().\n        Str(\"Scale\", \"833 cents\").\n        Float64(\"Interval\", 833.09).\n        Msg(\"Fibonacci is everywhere\")\n\n    log.Debug().\n        Str(\"Name\", \"Tom\").\n        Send()\n}\n\n// Output: {\"level\":\"debug\",\"Scale\":\"833 cents\",\"Interval\":833.09,\"time\":1562212768,\"message\":\"Fibonacci is everywhere\"}\n// Output: {\"level\":\"debug\",\"Name\":\"Tom\",\"time\":1562212768}\n```\n\n> You'll note in the above example that when adding contextual fields, the fields are strongly typed. You can find the full list of supported fields [here](#standard-types)\n\n### Leveled Logging\n\n#### Simple Leveled Logging Example\n\n```go\npackage main\n\nimport (\n    \"github.com/rs/zerolog\"\n    \"github.com/rs/zerolog/log\"\n)\n\nfunc main() {\n    zerolog.TimeFieldFormat = zerolog.TimeFormatUnix\n\n    log.Info().Msg(\"hello world\")\n}\n\n// Output: {\"time\":1516134303,\"level\":\"info\",\"message\":\"hello world\"}\n```\n\n> It is very important to note that when using the **zerolog** chaining API, as shown above (`log.Info().Msg(\"hello world\"`), the chain must have either the `Msg` or `Msgf` method call. If you forget to add either of these, the log will not occur and there is no compile time error to alert you of this.\n\n**zerolog** allows for logging at the following levels (from highest to lowest):\n\n- panic (`zerolog.PanicLevel`, 5)\n- fatal (`zerolog.FatalLevel`, 4)\n- error (`zerolog.ErrorLevel`, 3)\n- warn (`zerolog.WarnLevel`, 2)\n- info (`zerolog.InfoLevel`, 1)\n- debug (`zerolog.DebugLevel`, 0)\n- trace (`zerolog.TraceLevel`, -1)\n\nYou can set the Global logging level to any of these options using the `SetGlobalLevel` function in the zerolog package, passing in one of the given constants above, e.g. `zerolog.InfoLevel` would be the \"info\" level. Whichever level is chosen, all logs with a level greater than or equal to that level will be written. To turn off logging entirely, pass the `zerolog.Disabled` constant.\n\n#### Setting Global Log Level\n\nThis example uses command-line flags to demonstrate various outputs depending on the chosen log level.\n\n```go\npackage main\n\nimport (\n    \"flag\"\n\n    \"github.com/rs/zerolog\"\n    \"github.com/rs/zerolog/log\"\n)\n\nfunc main() {\n    zerolog.TimeFieldFormat = zerolog.TimeFormatUnix\n    debug := flag.Bool(\"debug\", false, \"sets log level to debug\")\n\n    flag.Parse()\n\n    // Default level for this example is info, unless debug flag is present\n    zerolog.SetGlobalLevel(zerolog.InfoLevel)\n    if *debug {\n        zerolog.SetGlobalLevel(zerolog.DebugLevel)\n    }\n\n    log.Debug().Msg(\"This message appears only when log level set to Debug\")\n    log.Info().Msg(\"This message appears when log level set to Debug or Info\")\n\n    if e := log.Debug(); e.Enabled() {\n        // Compute log output only if enabled.\n        value := \"bar\"\n        e.Str(\"foo\", value).Msg(\"some debug message\")\n    }\n}\n```\n\nInfo Output (no flag)\n\n```bash\n$ ./logLevelExample\n{\"time\":1516387492,\"level\":\"info\",\"message\":\"This message appears when log level set to Debug or Info\"}\n```\n\nDebug Output (debug flag set)\n\n```bash\n$ ./logLevelExample -debug\n{\"time\":1516387573,\"level\":\"debug\",\"message\":\"This message appears only when log level set to Debug\"}\n{\"time\":1516387573,\"level\":\"info\",\"message\":\"This message appears when log level set to Debug or Info\"}\n{\"time\":1516387573,\"level\":\"debug\",\"foo\":\"bar\",\"message\":\"some debug message\"}\n```\n\n#### Logging without Level or Message\n\nYou may choose to log without a specific level by using the `Log` method. You may also write without a message by setting an empty string in the `msg string` parameter of the `Msg` method. Both are demonstrated in the example below.\n\n```go\npackage main\n\nimport (\n    \"github.com/rs/zerolog\"\n    \"github.com/rs/zerolog/log\"\n)\n\nfunc main() {\n    zerolog.TimeFieldFormat = zerolog.TimeFormatUnix\n\n    log.Log().\n        Str(\"foo\", \"bar\").\n        Msg(\"\")\n}\n\n// Output: {\"time\":1494567715,\"foo\":\"bar\"}\n```\n\n### Error Logging\n\nYou can log errors using the `Err` method\n\n```go\npackage main\n\nimport (\n    \"errors\"\n\n    \"github.com/rs/zerolog\"\n    \"github.com/rs/zerolog/log\"\n)\n\nfunc main() {\n    zerolog.TimeFieldFormat = zerolog.TimeFormatUnix\n\n    err := errors.New(\"seems we have an error here\")\n    log.Error().Err(err).Msg(\"\")\n}\n\n// Output: {\"level\":\"error\",\"error\":\"seems we have an error here\",\"time\":1609085256}\n```\n\n> The default field name for errors is `error`, you can change this by setting `zerolog.ErrorFieldName` to meet your needs.\n\n#### Error Logging with Stacktrace\n\nUsing `github.com/pkg/errors`, you can add a formatted stacktrace to your errors.\n\n```go\npackage main\n\nimport (\n    \"github.com/pkg/errors\"\n    \"github.com/rs/zerolog/pkgerrors\"\n\n    \"github.com/rs/zerolog\"\n    \"github.com/rs/zerolog/log\"\n)\n\nfunc main() {\n    zerolog.TimeFieldFormat = zerolog.TimeFormatUnix\n    zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack\n\n    err := outer()\n    log.Error().Stack().Err(err).Msg(\"\")\n}\n\nfunc inner() error {\n    return errors.New(\"seems we have an error here\")\n}\n\nfunc middle() error {\n    err := inner()\n    if err != nil {\n        return err\n    }\n    return nil\n}\n\nfunc outer() error {\n    err := middle()\n    if err != nil {\n        return err\n    }\n    return nil\n}\n\n// Output: {\"level\":\"error\",\"stack\":[{\"func\":\"inner\",\"line\":\"20\",\"source\":\"errors.go\"},{\"func\":\"middle\",\"line\":\"24\",\"source\":\"errors.go\"},{\"func\":\"outer\",\"line\":\"32\",\"source\":\"errors.go\"},{\"func\":\"main\",\"line\":\"15\",\"source\":\"errors.go\"},{\"func\":\"main\",\"line\":\"204\",\"source\":\"proc.go\"},{\"func\":\"goexit\",\"line\":\"1374\",\"source\":\"asm_amd64.s\"}],\"error\":\"seems we have an error here\",\"time\":1609086683}\n```\n\n> zerolog.ErrorStackMarshaler must be set in order for the stack to output anything.\n\n#### Logging Fatal Messages\n\n```go\npackage main\n\nimport (\n    \"errors\"\n\n    \"github.com/rs/zerolog\"\n    \"github.com/rs/zerolog/log\"\n)\n\nfunc main() {\n    err := errors.New(\"A repo man spends his life getting into tense situations\")\n    service := \"myservice\"\n\n    zerolog.TimeFieldFormat = zerolog.TimeFormatUnix\n\n    log.Fatal().\n        Err(err).\n        Str(\"service\", service).\n        Msgf(\"Cannot start %s\", service)\n}\n\n// Output: {\"time\":1516133263,\"level\":\"fatal\",\"error\":\"A repo man spends his life getting into tense situations\",\"service\":\"myservice\",\"message\":\"Cannot start myservice\"}\n//         exit status 1\n```\n\n> NOTE: Using `Msgf` generates one allocation even when the logger is disabled.\n\n### Create logger instance to manage different outputs\n\n```go\nlogger := zerolog.New(os.Stderr).With().Timestamp().Logger()\n\nlogger.Info().Str(\"foo\", \"bar\").Msg(\"hello world\")\n\n// Output: {\"level\":\"info\",\"time\":1494567715,\"message\":\"hello world\",\"foo\":\"bar\"}\n```\n\n### Sub-loggers let you chain loggers with additional context\n\n```go\nsublogger := log.With().\n                 Str(\"component\", \"foo\").\n                 Logger()\nsublogger.Info().Msg(\"hello world\")\n\n// Output: {\"level\":\"info\",\"time\":1494567715,\"message\":\"hello world\",\"component\":\"foo\"}\n```\n\n### Pretty logging\n\nTo log a human-friendly, colorized output, use `zerolog.ConsoleWriter`:\n\n```go\nlog.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})\n\nlog.Info().Str(\"foo\", \"bar\").Msg(\"Hello world\")\n\n// Output: 3:04PM INF Hello World foo=bar\n```\n\nTo customize the configuration and formatting:\n\n```go\noutput := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}\noutput.FormatLevel = func(i interface{}) string {\n    return strings.ToUpper(fmt.Sprintf(\"| %-6s|\", i))\n}\noutput.FormatMessage = func(i interface{}) string {\n    return fmt.Sprintf(\"***%s****\", i)\n}\noutput.FormatFieldName = func(i interface{}) string {\n    return fmt.Sprintf(\"%s:\", i)\n}\noutput.FormatFieldValue = func(i interface{}) string {\n    return strings.ToUpper(fmt.Sprintf(\"%s\", i))\n}\n\nlog := zerolog.New(output).With().Timestamp().Logger()\n\nlog.Info().Str(\"foo\", \"bar\").Msg(\"Hello World\")\n\n// Output: 2006-01-02T15:04:05Z07:00 | INFO  | ***Hello World**** foo:BAR\n```\n\nTo use custom advanced formatting:\n\n```go\noutput := zerolog.ConsoleWriter{Out: os.Stdout, NoColor: true,\n    PartsOrder:    []string{\"level\", \"one\", \"two\", \"three\", \"message\"},\n    FieldsExclude: []string{\"one\", \"two\", \"three\"}}\noutput.FormatLevel = func(i interface{}) string { return strings.ToUpper(fmt.Sprintf(\"%-6s\", i)) }\noutput.FormatFieldName = func(i interface{}) string { return fmt.Sprintf(\"%s:\", i) }\noutput.FormatPartValueByName = func(i interface{}, s string) string {\n    var ret string\n    switch s {\n    case \"one\":\n        ret = strings.ToUpper(fmt.Sprintf(\"%s\", i))\n    case \"two\":\n        ret = strings.ToLower(fmt.Sprintf(\"%s\", i))\n    case \"three\":\n        ret = strings.ToLower(fmt.Sprintf(\"(%s)\", i))\n    }\n    return ret\n}\nlog := zerolog.New(output)\n\nlog.Info().Str(\"foo\", \"bar\").\n    Str(\"two\", \"TEST_TWO\").\n    Str(\"one\", \"test_one\").\n    Str(\"three\", \"test_three\").\n    Msg(\"Hello World\")\n\n// Output: INFO   TEST_ONE test_two (test_three) Hello World foo:bar\n```\n\n### Sub dictionary\n\n```go\nlog.Info().\n    Str(\"foo\", \"bar\").\n    Dict(\"dict\", zerolog.Dict().\n        Str(\"bar\", \"baz\").\n        Int(\"n\", 1),\n    ).Msg(\"hello world\")\n\n// Output: {\"level\":\"info\",\"time\":1494567715,\"foo\":\"bar\",\"dict\":{\"bar\":\"baz\",\"n\":1},\"message\":\"hello world\"}\n```\n\n### Customize automatic field names\n\n```go\nzerolog.TimestampFieldName = \"t\"\nzerolog.LevelFieldName = \"l\"\nzerolog.MessageFieldName = \"m\"\n\nlog.Info().Msg(\"hello world\")\n\n// Output: {\"l\":\"info\",\"t\":1494567715,\"m\":\"hello world\"}\n```\n\n### Add contextual fields to the global logger\n\n```go\nlog.Logger = log.With().Str(\"foo\", \"bar\").Logger()\n```\n\n### Add file and line number to log\n\nEquivalent of `Llongfile`:\n\n```go\nlog.Logger = log.With().Caller().Logger()\nlog.Info().Msg(\"hello world\")\n\n// Output: {\"level\": \"info\", \"message\": \"hello world\", \"caller\": \"/go/src/your_project/some_file:21\"}\n```\n\nEquivalent of `Lshortfile`:\n\n```go\nzerolog.CallerMarshalFunc = func(pc uintptr, file string, line int) string {\n    return filepath.Base(file) + \":\" + strconv.Itoa(line)\n}\nlog.Logger = log.With().Caller().Logger()\nlog.Info().Msg(\"hello world\")\n\n// Output: {\"level\": \"info\", \"message\": \"hello world\", \"caller\": \"some_file:21\"}\n```\n\n### Thread-safe, lock-free, non-blocking writer\n\nIf your writer might be slow or not thread-safe and you need your log producers to never get slowed down by a slow writer, you can use a `diode.Writer` as follows:\n\n```go\nwr := diode.NewWriter(os.Stdout, 1000, 10*time.Millisecond, func(missed int) {\n        fmt.Printf(\"Logger Dropped %d messages\", missed)\n    })\nlog := zerolog.New(wr)\nlog.Print(\"test\")\n```\n\nYou will need to install `code.cloudfoundry.org/go-diodes` to use this feature.\n\n### Log Sampling\n\n```go\nsampled := log.Sample(&zerolog.BasicSampler{N: 10})\nsampled.Info().Msg(\"will be logged every 10 messages\")\n\n// Output: {\"time\":1494567715,\"level\":\"info\",\"message\":\"will be logged every 10 messages\"}\n```\n\nMore advanced sampling:\n\n```go\n// Will let 5 debug messages per period of 1 second.\n// Over 5 debug message, 1 every 100 debug messages are logged.\n// Other levels are not sampled.\nsampled := log.Sample(zerolog.LevelSampler{\n    DebugSampler: &zerolog.BurstSampler{\n        Burst: 5,\n        Period: 1*time.Second,\n        NextSampler: &zerolog.BasicSampler{N: 100},\n    },\n})\nsampled.Debug().Msg(\"hello world\")\n\n// Output: {\"time\":1494567715,\"level\":\"debug\",\"message\":\"hello world\"}\n```\n\n### Hooks\n\n```go\ntype SeverityHook struct{}\n\nfunc (h SeverityHook) Run(e *zerolog.Event, level zerolog.Level, msg string) {\n    if level != zerolog.NoLevel {\n        e.Str(\"severity\", level.String())\n    }\n}\n\nhooked := log.Hook(SeverityHook{})\nhooked.Warn().Msg(\"\")\n\n// Output: {\"level\":\"warn\",\"severity\":\"warn\"}\n```\n\n### Pass a sub-logger by context\n\n```go\nctx := log.With().Str(\"component\", \"module\").Logger().WithContext(ctx)\n\nlog.Ctx(ctx).Info().Msg(\"hello world\")\n\n// Output: {\"component\":\"module\",\"level\":\"info\",\"message\":\"hello world\"}\n```\n\n### Set as standard logger output\n\n```go\nlog := zerolog.New(os.Stdout).With().\n    Str(\"foo\", \"bar\").\n    Logger()\n\nstdlog.SetFlags(0)\nstdlog.SetOutput(log)\n\nstdlog.Print(\"hello world\")\n\n// Output: {\"foo\":\"bar\",\"message\":\"hello world\"}\n```\n\n### context.Context integration\n\nGo contexts are commonly passed throughout Go code, and this can help you pass\nyour Logger into places it might otherwise be hard to inject. The `Logger`\ninstance may be attached to Go context (`context.Context`) using\n`Logger.WithContext(ctx)` and extracted from it using `zerolog.Ctx(ctx)`.\nFor example:\n\n```go\nfunc f() {\n    logger := zerolog.New(os.Stdout)\n    ctx := context.Background()\n\n    // Attach the Logger to the context.Context\n    ctx = logger.WithContext(ctx)\n    someFunc(ctx)\n}\n\nfunc someFunc(ctx context.Context) {\n    // Get Logger from the go Context. if it's nil, then\n    // `zerolog.DefaultContextLogger` is returned, if\n    // `DefaultContextLogger` is nil, then a disabled logger is returned.\n    logger := zerolog.Ctx(ctx)\n    logger.Info().Msg(\"Hello\")\n}\n```\n\nA second form of `context.Context` integration allows you to pass the current\n`context.Context` into the logged event, and retrieve it from hooks. This can be\nuseful to log trace and span IDs or other information stored in the go context,\nand facilitates the unification of logging and tracing in some systems:\n\n```go\ntype TracingHook struct{}\n\nfunc (h TracingHook) Run(e *zerolog.Event, level zerolog.Level, msg string) {\n    ctx := e.GetCtx()\n    spanId := getSpanIdFromContext(ctx) // as per your tracing framework\n    e.Str(\"span-id\", spanId)\n}\n\nfunc f() {\n    // Setup the logger\n    logger := zerolog.New(os.Stdout)\n    logger = logger.Hook(TracingHook{})\n\n    ctx := context.Background()\n    // Use the Ctx function to make the context available to the hook\n    logger.Info().Ctx(ctx).Msg(\"Hello\")\n}\n```\n\n### Integration with `net/http`\n\nThe `github.com/rs/zerolog/hlog` package provides some helpers to integrate zerolog with `http.Handler`.\n\nIn this example we use [alice](https://github.com/justinas/alice) to install logger for better readability.\n\n```go\nlog := zerolog.New(os.Stdout).With().\n    Timestamp().\n    Str(\"role\", \"my-service\").\n    Str(\"host\", host).\n    Logger()\n\nc := alice.New()\n\n// Install the logger handler with default output on the console\nc = c.Append(hlog.NewHandler(log))\n\n// Install some provided extra handler to set some request's context fields.\n// Thanks to that handler, all our logs will come with some prepopulated fields.\nc = c.Append(hlog.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) {\n    hlog.FromRequest(r).Info().\n        Str(\"method\", r.Method).\n        Stringer(\"url\", r.URL).\n        Int(\"status\", status).\n        Int(\"size\", size).\n        Dur(\"duration\", duration).\n        Msg(\"\")\n}))\nc = c.Append(hlog.RemoteAddrHandler(\"ip\"))\nc = c.Append(hlog.UserAgentHandler(\"user_agent\"))\nc = c.Append(hlog.RefererHandler(\"referer\"))\nc = c.Append(hlog.RequestIDHandler(\"req_id\", \"Request-Id\"))\n\n// Here is your final handler\nh := c.Then(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n    // Get the logger from the request's context. You can safely assume it\n    // will be always there: if the handler is removed, hlog.FromRequest\n    // will return a no-op logger.\n    hlog.FromRequest(r).Info().\n        Str(\"user\", \"current user\").\n        Str(\"status\", \"ok\").\n        Msg(\"Something happened\")\n\n    // Output: {\"level\":\"info\",\"time\":\"2001-02-03T04:05:06Z\",\"role\":\"my-service\",\"host\":\"local-hostname\",\"req_id\":\"b4g0l5t6tfid6dtrapu0\",\"user\":\"current user\",\"status\":\"ok\",\"message\":\"Something happened\"}\n}))\nhttp.Handle(\"/\", h)\n\nif err := http.ListenAndServe(\":8080\", nil); err != nil {\n    log.Fatal().Err(err).Msg(\"Startup failed\")\n}\n```\n\n## Multiple Log Output\n\n`zerolog.MultiLevelWriter` may be used to send the log message to multiple outputs.\n\nIn this example, we send the log message to both `os.Stdout` and the in-built `ConsoleWriter`.\n\n```go\nfunc main() {\n    consoleWriter := zerolog.ConsoleWriter{Out: os.Stdout}\n    multi := zerolog.MultiLevelWriter(consoleWriter, os.Stdout)\n    logger := zerolog.New(multi).With().Timestamp().Logger()\n    logger.Info().Msg(\"Hello World!\")\n}\n\n// Output (Line 1: Console; Line 2: Stdout)\n// 12:36PM INF Hello World!\n// {\"level\":\"info\",\"time\":\"2019-11-07T12:36:38+03:00\",\"message\":\"Hello World!\"}\n```\n\n## Global Settings\n\nSome settings can be changed and will be applied to all loggers:\n\n- `log.Logger`: You can set this value to customize the global logger (the one used by package level methods).\n- `zerolog.SetGlobalLevel`: Can raise the minimum level of all loggers. Call this with `zerolog.Disabled` to disable logging altogether (quiet mode).\n- `zerolog.DisableSampling`: If argument is `true`, all sampled loggers will stop sampling and issue 100% of their log events.\n- `zerolog.TimestampFieldName`: Can be set to customize `Timestamp` field name.\n- `zerolog.LevelFieldName`: Can be set to customize level field name.\n- `zerolog.MessageFieldName`: Can be set to customize message field name.\n- `zerolog.ErrorFieldName`: Can be set to customize `Err` field name.\n- `zerolog.TimeFieldFormat`: Can be set to customize `Time` field value formatting. If set with `zerolog.TimeFormatUnix`, `zerolog.TimeFormatUnixMs` or `zerolog.TimeFormatUnixMicro`, times are formatted as UNIX timestamp.\n- `zerolog.DurationFieldUnit`: Can be set to customize the unit for time.Duration type fields added by `Dur` (default: `time.Millisecond`).\n- `zerolog.DurationFieldFormat`: Can be set to `DurationFormatFloat`, `DurationFormatInt`, or `DurationFormatString` (default: `DurationFormatFloat`) to append the `Duration` as a `Float64`, `Int64`, or by calling `String()` (respectively).\n- `zerolog.DurationFieldInteger`: If set to `true`, `Dur` fields are formatted as integers instead of floats (default: `false`). Deprecated: Use `zerolog.DurationFieldFormat = DurationFormatInt` instead.\n- `zerolog.ErrorHandler`: Called whenever zerolog fails to write an event on its output. If not set, an error is printed on the stderr. This handler must be thread safe and non-blocking.\n- `zerolog.FloatingPointPrecision`: If set to a value other than -1, controls the number of digits when formatting float numbers in JSON. See [strconv.FormatFloat](https://pkg.go.dev/strconv#FormatFloat)\n  for more details.\n\n## Field Types\n\n### Standard Types\n\n- `Str`\n- `Bool`\n- `Int`, `Int8`, `Int16`, `Int32`, `Int64`\n- `Uint`, `Uint8`, `Uint16`, `Uint32`, `Uint64`\n- `Float32`, `Float64`\n\n### Advanced Fields\n\n- `Err`: Takes an `error` and renders it as a string using the `zerolog.ErrorFieldName` field name.\n- `Func`: Run a `func` only if the level is enabled.\n- `Timestamp`: Inserts a timestamp field with `zerolog.TimestampFieldName` field name, formatted using `zerolog.TimeFieldFormat`.\n- `Time`: Adds a field with time formatted with `zerolog.TimeFieldFormat`.\n- `Dur`: Adds a field with `time.Duration`.\n- `Dict`: Adds a sub-key/value as a field of the event.\n- `RawJSON`: Adds a field with an already encoded JSON (`[]byte`)\n- `Hex`: Adds a field with value formatted as a hexadecimal string (`[]byte`)\n- `Interface`: Uses reflection to marshal the type.\n- `IPAddr`: Adds a field with `net.IP`.\n- `IPPrefix`: Adds a field with `net.IPNet`.\n- `MACAddr`: Adds a field with `net.HardwareAddr`\n\nMost fields are also available in the slice format (`Strs` for `[]string`, `Errs` for `[]error` etc.)\n\n## Binary Encoding\n\nIn addition to the default JSON encoding, `zerolog` can produce binary logs using [CBOR](https://cbor.io) encoding. The choice of encoding can be decided at compile time using the build tag `binary_log` as follows:\n\n```bash\ngo build -tags binary_log .\n```\n\nTo decode binary encoded log files you can use any CBOR decoder. One has been tested to work\nwith zerolog library is [CSD](https://github.com/toravir/csd/).\n\n## Integration with `log/slog`\n\nzerolog provides a `slog.Handler` implementation that routes `log/slog` records through a zerolog logger. This lets you use the standard library's `slog` API while keeping zerolog's performance and encoding:\n\n```go\npackage main\n\nimport (\n    \"log/slog\"\n\n    \"github.com/rs/zerolog\"\n    \"github.com/rs/zerolog/log\"\n)\n\nfunc main() {\n    zl := log.Logger\n    handler := zerolog.NewSlogHandler(zl)\n    logger := slog.New(handler)\n\n    logger.Info(\"user logged in\", \"user\", \"alice\", \"role\", \"admin\")\n}\n\n// Output: {\"level\":\"info\",\"user\":\"alice\",\"role\":\"admin\",\"time\":\"...\",\"message\":\"user logged in\"}\n```\n\nThe handler supports all `slog` features including `WithAttrs`, `WithGroup`, nested groups, and `LogValuer` resolution. slog levels are mapped to zerolog levels (e.g. `slog.LevelDebug` to `zerolog.DebugLevel`).\n\n## Related Projects\n\n- [grpc-zerolog](https://github.com/cheapRoc/grpc-zerolog): Implementation of `grpclog.LoggerV2` interface using `zerolog`\n- [overlog](https://github.com/Trendyol/overlog): Implementation of `Mapped Diagnostic Context` interface using `zerolog`\n- [zerologr](https://github.com/go-logr/zerologr): Implementation of `logr.LogSink` interface using `zerolog`\n- [logze](https://github.com/maxbolgarin/logze): Implementation of `log/slog` interface using `zerolog`\n\n## Benchmarks\n\nSee [logbench](http://bench.zerolog.io/) for more comprehensive and up-to-date benchmarks.\n\nAll operations are allocation free (those numbers _include_ JSON encoding):\n\n```text\nBenchmarkLogEmpty-8        100000000    19.1 ns/op     0 B/op       0 allocs/op\nBenchmarkDisabled-8        500000000    4.07 ns/op     0 B/op       0 allocs/op\nBenchmarkInfo-8            30000000     42.5 ns/op     0 B/op       0 allocs/op\nBenchmarkContextFields-8   30000000     44.9 ns/op     0 B/op       0 allocs/op\nBenchmarkLogFields-8       10000000     184 ns/op      0 B/op       0 allocs/op\n```\n\nThere are a few Go logging benchmarks and comparisons that include zerolog.\n\n- [imkira/go-loggers-bench](https://github.com/imkira/go-loggers-bench)\n- [uber-common/zap](https://github.com/uber-go/zap#performance)\n\nUsing Uber's zap comparison benchmark:\n\nLog a message and 10 fields:\n\n| Library             |    Time     | Bytes Allocated | Objects Allocated |\n| :------------------ | :---------: | :-------------: | :---------------: |\n| zerolog             |  767 ns/op  |    552 B/op     |    6 allocs/op    |\n| :zap: zap           |  848 ns/op  |    704 B/op     |    2 allocs/op    |\n| :zap: zap (sugared) | 1363 ns/op  |    1610 B/op    |   20 allocs/op    |\n| go-kit              | 3614 ns/op  |    2895 B/op    |   66 allocs/op    |\n| lion                | 5392 ns/op  |    5807 B/op    |   63 allocs/op    |\n| logrus              | 5661 ns/op  |    6092 B/op    |   78 allocs/op    |\n| apex/log            | 15332 ns/op |    3832 B/op    |   65 allocs/op    |\n| log15               | 20657 ns/op |    5632 B/op    |   93 allocs/op    |\n\nLog a message with a logger that already has 10 fields of context:\n\n| Library             |    Time     | Bytes Allocated | Objects Allocated |\n| :------------------ | :---------: | :-------------: | :---------------: |\n| zerolog             |  52 ns/op   |     0 B/op      |    0 allocs/op    |\n| :zap: zap           |  283 ns/op  |     0 B/op      |    0 allocs/op    |\n| :zap: zap (sugared) |  337 ns/op  |     80 B/op     |    2 allocs/op    |\n| lion                | 2702 ns/op  |    4074 B/op    |   38 allocs/op    |\n| go-kit              | 3378 ns/op  |    3046 B/op    |   52 allocs/op    |\n| logrus              | 4309 ns/op  |    4564 B/op    |   63 allocs/op    |\n| apex/log            | 13456 ns/op |    2898 B/op    |   51 allocs/op    |\n| log15               | 14179 ns/op |    2642 B/op    |   44 allocs/op    |\n\nLog a static string, without any context or `printf`-style templating:\n\n| Library             |    Time    | Bytes Allocated | Objects Allocated |\n| :------------------ | :--------: | :-------------: | :---------------: |\n| zerolog             |  50 ns/op  |     0 B/op      |    0 allocs/op    |\n| :zap: zap           | 236 ns/op  |     0 B/op      |    0 allocs/op    |\n| standard library    | 453 ns/op  |     80 B/op     |    2 allocs/op    |\n| :zap: zap (sugared) | 337 ns/op  |     80 B/op     |    2 allocs/op    |\n| go-kit              | 508 ns/op  |    656 B/op     |   13 allocs/op    |\n| lion                | 771 ns/op  |    1224 B/op    |   10 allocs/op    |\n| logrus              | 1244 ns/op |    1505 B/op    |   27 allocs/op    |\n| apex/log            | 2751 ns/op |    584 B/op     |   11 allocs/op    |\n| log15               | 5181 ns/op |    1592 B/op    |   26 allocs/op    |\n\n## Caveats\n\n### Field duplication\n\nNote that zerolog does no de-duplication of fields. Using the same key multiple times creates multiple keys in final JSON:\n\n```go\nlogger := zerolog.New(os.Stderr).With().Timestamp().Logger()\nlogger.Info().\n       Timestamp().\n       Msg(\"dup\")\n// Output: {\"level\":\"info\",\"time\":1494567715,\"time\":1494567715,\"message\":\"dup\"}\n```\n\nIn this case, many consumers will take the last value, but this is not guaranteed; check yours if in doubt.\n\n### Concurrency safety\n\nBe careful when calling `UpdateContext`. It is not concurrency safe. Use the `With()` method to create a child logger:\n\n```go\nfunc handler(w http.ResponseWriter, r *http.Request) {\n    // Create a child logger for concurrency safety\n    logger := log.Logger.With().Logger()\n\n    // Add context fields, for example User-Agent from HTTP headers\n    logger.UpdateContext(func(c zerolog.Context) zerolog.Context {\n        ...\n    })\n}\n```\n\nThe `Event` object returned from the `Logger` level-specific message functions (e.g. `Log()`, `Trace()`, `Debug()`, etc.)\nis allocated in `sync.Pool` memory that will be returned to the pool as soon as the `Msg()`, `Msgf()`, `Send()`,\nor `MsgFunc()` writes the message and **must not** be accessed afterwards.\n\n**Do not** hold a reference to the `*Event` while in callback functions or your own code. This is especially important in\n`Hook.Run()` and `HookFunc` functions or `MarshalZerologObject(e *Event)` callback (e.g. `LogObjectMarshaler` implementations).\n\nAny `Array` objects returned from `Context.CreateArray()` or `Event.CreateArray()` are from a `sync.Pool` so **do not** hold\nreferences to them from within any `MarshalZerologArray(a *Array)` callback (e.g. `LogArrayMarshaler` implementations) or your\nown code as they will be cleared and returned to the pool after being buffered by a call to `Context.Array()` or `Event.Array()`.\n\nAny _dictionary_ `Event` returned from `Context.CreateDict()` or `Event.CreateDict()` **must not** be referenced after being\nbuffered by a call to `Array.Dict()`, `Context.Dict()`, or `Event.Dict()` as they will be cleared and returned to the pool.\n"
  },
  {
    "path": "array.go",
    "content": "package zerolog\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n)\n\nvar arrayPool = &sync.Pool{\n\tNew: func() interface{} {\n\t\treturn &Array{\n\t\t\tbuf: make([]byte, 0, 500),\n\t\t}\n\t},\n}\n\n// Array is used to prepopulate an array of items\n// which can be re-used to add to log messages.\ntype Array struct {\n\tbuf   []byte\n\tstack bool            // enable error stack trace\n\tctx   context.Context // Optional Go context\n\tch    []Hook          // hooks\n}\n\nfunc putArray(a *Array) {\n\t// prevent any subsequent use of the Array contextual state and truncate the buffer\n\ta.stack = false\n\ta.ctx = nil\n\ta.ch = nil\n\ta.buf = a.buf[:0]\n\n\t// Proper usage of a sync.Pool requires each entry to have approximately\n\t// the same memory cost. To obtain this property when the stored type\n\t// contains a variably-sized buffer, we add a hard limit on the maximum buffer\n\t// to place back in the pool.\n\t//\n\t// See https://golang.org/issue/23199\n\tconst maxSize = 1 << 16 // 64KiB\n\tif cap(a.buf) <= maxSize {\n\t\tarrayPool.Put(a)\n\t}\n}\n\n// Arr creates an array to be added to an Event or Context.\n// WARNING: This function is deprecated because it does not preserve\n// the stack, hooks, and context from the parent event.\n// Deprecated: Use Event.CreateArray or Context.CreateArray instead.\nfunc Arr() *Array {\n\ta := arrayPool.Get().(*Array)\n\ta.buf = a.buf[:0]\n\ta.stack = false\n\ta.ctx = nil\n\ta.ch = nil\n\treturn a\n}\n\n// MarshalZerologArray method here is no-op - since data is\n// already in the needed format.\nfunc (*Array) MarshalZerologArray(*Array) {\n\t// untestable: there's no code to be covered\n}\n\nfunc (a *Array) write(dst []byte) []byte {\n\tdst = enc.AppendArrayStart(dst)\n\tif len(a.buf) > 0 {\n\t\tdst = append(dst, a.buf...)\n\t}\n\tdst = enc.AppendArrayEnd(dst)\n\tputArray(a)\n\treturn dst\n}\n\n// Object marshals an object that implement the LogObjectMarshaler\n// interface and appends it to the array.\nfunc (a *Array) Object(obj LogObjectMarshaler) *Array {\n\ta.buf = appendObject(enc.AppendArrayDelim(a.buf), obj, a.stack, a.ctx, a.ch)\n\treturn a\n}\n\n// Str appends the val as a string to the array.\nfunc (a *Array) Str(val string) *Array {\n\ta.buf = enc.AppendString(enc.AppendArrayDelim(a.buf), val)\n\treturn a\n}\n\n// Bytes appends the val as a string to the array.\nfunc (a *Array) Bytes(val []byte) *Array {\n\ta.buf = enc.AppendBytes(enc.AppendArrayDelim(a.buf), val)\n\treturn a\n}\n\n// Hex appends the val as a hex string to the array.\nfunc (a *Array) Hex(val []byte) *Array {\n\ta.buf = enc.AppendHex(enc.AppendArrayDelim(a.buf), val)\n\treturn a\n}\n\n// RawJSON adds already encoded JSON to the array.\nfunc (a *Array) RawJSON(val []byte) *Array {\n\ta.buf = appendJSON(enc.AppendArrayDelim(a.buf), val)\n\treturn a\n}\n\n// Err serializes and appends the err to the array.\nfunc (a *Array) Err(err error) *Array {\n\tswitch m := ErrorMarshalFunc(err).(type) {\n\tcase nil:\n\t\ta.buf = enc.AppendNil(enc.AppendArrayDelim(a.buf))\n\tcase LogObjectMarshaler:\n\t\ta = a.Object(m)\n\tcase error:\n\t\tif !isNilValue(m) {\n\t\t\ta.buf = enc.AppendString(enc.AppendArrayDelim(a.buf), m.Error())\n\t\t}\n\tcase string:\n\t\ta.buf = enc.AppendString(enc.AppendArrayDelim(a.buf), m)\n\tdefault:\n\t\ta.buf = enc.AppendInterface(enc.AppendArrayDelim(a.buf), m)\n\t}\n\n\treturn a\n}\n\n// Errs serializes and appends errors to the array.\nfunc (a *Array) Errs(errs []error) *Array {\n\tfor _, err := range errs {\n\t\tswitch m := ErrorMarshalFunc(err).(type) {\n\t\tcase nil:\n\t\t\ta = a.Interface(nil)\n\t\tcase LogObjectMarshaler:\n\t\t\ta = a.Object(m)\n\t\tcase error:\n\t\t\tif !isNilValue(m) {\n\t\t\t\ta = a.Str(m.Error())\n\t\t\t}\n\t\tcase string:\n\t\t\ta = a.Str(m)\n\t\tdefault:\n\t\t\ta = a.Interface(m)\n\t\t}\n\t}\n\treturn a\n}\n\n// Bool appends the val as a bool to the array.\nfunc (a *Array) Bool(b bool) *Array {\n\ta.buf = enc.AppendBool(enc.AppendArrayDelim(a.buf), b)\n\treturn a\n}\n\n// Int appends i as a int to the array.\nfunc (a *Array) Int(i int) *Array {\n\ta.buf = enc.AppendInt(enc.AppendArrayDelim(a.buf), i)\n\treturn a\n}\n\n// Int8 appends i as a int8 to the array.\nfunc (a *Array) Int8(i int8) *Array {\n\ta.buf = enc.AppendInt8(enc.AppendArrayDelim(a.buf), i)\n\treturn a\n}\n\n// Int16 appends i as a int16 to the array.\nfunc (a *Array) Int16(i int16) *Array {\n\ta.buf = enc.AppendInt16(enc.AppendArrayDelim(a.buf), i)\n\treturn a\n}\n\n// Int32 appends i as a int32 to the array.\nfunc (a *Array) Int32(i int32) *Array {\n\ta.buf = enc.AppendInt32(enc.AppendArrayDelim(a.buf), i)\n\treturn a\n}\n\n// Int64 appends i as a int64 to the array.\nfunc (a *Array) Int64(i int64) *Array {\n\ta.buf = enc.AppendInt64(enc.AppendArrayDelim(a.buf), i)\n\treturn a\n}\n\n// Uint appends i as a uint to the array.\nfunc (a *Array) Uint(i uint) *Array {\n\ta.buf = enc.AppendUint(enc.AppendArrayDelim(a.buf), i)\n\treturn a\n}\n\n// Uint8 appends i as a uint8 to the array.\nfunc (a *Array) Uint8(i uint8) *Array {\n\ta.buf = enc.AppendUint8(enc.AppendArrayDelim(a.buf), i)\n\treturn a\n}\n\n// Uint16 appends i as a uint16 to the array.\nfunc (a *Array) Uint16(i uint16) *Array {\n\ta.buf = enc.AppendUint16(enc.AppendArrayDelim(a.buf), i)\n\treturn a\n}\n\n// Uint32 appends i as a uint32 to the array.\nfunc (a *Array) Uint32(i uint32) *Array {\n\ta.buf = enc.AppendUint32(enc.AppendArrayDelim(a.buf), i)\n\treturn a\n}\n\n// Uint64 appends i as a uint64 to the array.\nfunc (a *Array) Uint64(i uint64) *Array {\n\ta.buf = enc.AppendUint64(enc.AppendArrayDelim(a.buf), i)\n\treturn a\n}\n\n// Float32 appends f as a float32 to the array.\nfunc (a *Array) Float32(f float32) *Array {\n\ta.buf = enc.AppendFloat32(enc.AppendArrayDelim(a.buf), f, FloatingPointPrecision)\n\treturn a\n}\n\n// Float64 appends f as a float64 to the array.\nfunc (a *Array) Float64(f float64) *Array {\n\ta.buf = enc.AppendFloat64(enc.AppendArrayDelim(a.buf), f, FloatingPointPrecision)\n\treturn a\n}\n\n// Time appends t formatted as string using zerolog.TimeFieldFormat.\nfunc (a *Array) Time(t time.Time) *Array {\n\ta.buf = enc.AppendTime(enc.AppendArrayDelim(a.buf), t, TimeFieldFormat)\n\treturn a\n}\n\n// Dur appends d to the array.\nfunc (a *Array) Dur(d time.Duration) *Array {\n\ta.buf = enc.AppendDuration(enc.AppendArrayDelim(a.buf), d, DurationFieldUnit, DurationFieldFormat, DurationFieldInteger, FloatingPointPrecision)\n\treturn a\n}\n\n// Interface appends i marshaled using reflection.\nfunc (a *Array) Interface(i interface{}) *Array {\n\tif obj, ok := i.(LogObjectMarshaler); ok {\n\t\treturn a.Object(obj)\n\t}\n\ta.buf = enc.AppendInterface(enc.AppendArrayDelim(a.buf), i)\n\treturn a\n}\n\n// IPAddr adds a net.IP IPv4 or IPv6 address to the array\nfunc (a *Array) IPAddr(ip net.IP) *Array {\n\ta.buf = enc.AppendIPAddr(enc.AppendArrayDelim(a.buf), ip)\n\treturn a\n}\n\n// IPPrefix adds a net.IPNet IPv4 or IPv6 Prefix (IP + mask) to the array\nfunc (a *Array) IPPrefix(pfx net.IPNet) *Array {\n\ta.buf = enc.AppendIPPrefix(enc.AppendArrayDelim(a.buf), pfx)\n\treturn a\n}\n\n// MACAddr adds a net.HardwareAddr MAC (Ethernet) address to the array\nfunc (a *Array) MACAddr(ha net.HardwareAddr) *Array {\n\ta.buf = enc.AppendMACAddr(enc.AppendArrayDelim(a.buf), ha)\n\treturn a\n}\n\n// Dict adds the dict Event to the array\nfunc (a *Array) Dict(dict *Event) *Array {\n\tdict.buf = enc.AppendEndMarker(dict.buf)\n\ta.buf = append(enc.AppendArrayDelim(a.buf), dict.buf...)\n\tputEvent(dict)\n\treturn a\n}\n\n// Type adds the val's type using reflection to the array.\nfunc (a *Array) Type(val interface{}) *Array {\n\ta.buf = enc.AppendType(enc.AppendArrayDelim(a.buf), val)\n\treturn a\n}\n"
  },
  {
    "path": "array_test.go",
    "content": "package zerolog\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestArray(t *testing.T) {\n\ta := Arr().\n\t\tBool(true).\n\t\tInt(1).\n\t\tInt8(2).\n\t\tInt16(3).\n\t\tInt32(4).\n\t\tInt64(5).\n\t\tUint(6).\n\t\tUint8(7).\n\t\tUint16(8).\n\t\tUint32(9).\n\t\tUint64(10).\n\t\tFloat32(11.98122).\n\t\tFloat64(12.987654321).\n\t\tStr(\"a\").\n\t\tBytes([]byte(\"b\")).\n\t\tHex([]byte{0x1f}).\n\t\tRawJSON([]byte(`{\"some\":\"json\"}`)).\n\t\tRawJSON([]byte(`{\"longer\":[1111,2222,3333,4444,5555]}`)).\n\t\tTime(time.Time{}).\n\t\tIPAddr(net.IP{192, 168, 0, 10}).\n\t\tIPPrefix(net.IPNet{IP: net.IP{127, 0, 0, 0}, Mask: net.CIDRMask(24, 32)}).\n\t\tMACAddr(net.HardwareAddr{0x01, 0x23, 0x45, 0x67, 0x89, 0xab}).\n\t\tInterface(struct {\n\t\t\tPub  string\n\t\t\tTag  string `json:\"tag\"`\n\t\t\tpriv int\n\t\t}{\"A\", \"j\", -5}).\n\t\tInterface(logObjectMarshalerImpl{\n\t\t\tname: \"ZOT\",\n\t\t\tage:  35,\n\t\t}).\n\t\tDur(0).\n\t\tDict(Dict().\n\t\t\tStr(\"bar\", \"baz\").\n\t\t\tInt(\"n\", 1),\n\t\t).\n\t\tErr(nil).\n\t\tErr(fmt.Errorf(\"failure\")).\n\t\tErr(loggableError{fmt.Errorf(\"oops\")}).\n\t\tObject(logObjectMarshalerImpl{\n\t\t\tname: \"ZIT\",\n\t\t\tage:  22,\n\t\t}).\n\t\tType(3.14)\n\n\twant := `[true,1,2,3,4,5,6,7,8,9,10,11.98122,12.987654321,\"a\",\"b\",\"1f\",{\"some\":\"json\"},{\"longer\":[1111,2222,3333,4444,5555]},\"0001-01-01T00:00:00Z\",\"192.168.0.10\",\"127.0.0.0/24\",\"01:23:45:67:89:ab\",{\"Pub\":\"A\",\"tag\":\"j\"},{\"name\":\"zot\",\"age\":-35},0,{\"bar\":\"baz\",\"n\":1},null,\"failure\",{\"l\":\"OOPS\"},{\"name\":\"zit\",\"age\":-22},\"float64\"]`\n\tif got := decodeObjectToStr(a.write([]byte{})); got != want {\n\t\tt.Errorf(\"Array.write()\\ngot:  %s\\nwant: %s\", got, want)\n\t}\n}\n\nfunc TestArray_MarshalZerologArray(t *testing.T) {\n\ta := Arr()\n\ta.MarshalZerologArray(nil) // no-op method, should not panic\n}\n"
  },
  {
    "path": "benchmark_test.go",
    "content": "package zerolog\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"testing\"\n\t\"time\"\n)\n\nvar (\n\terrExample  = errors.New(\"fail\")\n\tfakeMessage = \"Test logging, but use a somewhat realistic message length.\"\n)\n\nfunc BenchmarkLogEmpty(b *testing.B) {\n\tlogger := New(io.Discard)\n\tb.ResetTimer()\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tlogger.Log().Msg(\"\")\n\t\t}\n\t})\n}\n\nfunc BenchmarkDisabled(b *testing.B) {\n\tlogger := New(io.Discard).Level(Disabled)\n\tb.ResetTimer()\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tlogger.Info().Msg(fakeMessage)\n\t\t}\n\t})\n}\n\nfunc BenchmarkInfo(b *testing.B) {\n\tlogger := New(io.Discard)\n\tb.ResetTimer()\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tlogger.Info().Msg(fakeMessage)\n\t\t}\n\t})\n}\n\nfunc BenchmarkContextFields(b *testing.B) {\n\tlogger := New(io.Discard).With().\n\t\tStr(\"string\", \"four!\").\n\t\tTime(\"time\", time.Time{}).\n\t\tInt(\"int\", 123).\n\t\tFloat32(\"float\", -2.203230293249593).\n\t\tLogger()\n\tb.ResetTimer()\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tlogger.Info().Msg(fakeMessage)\n\t\t}\n\t})\n}\n\nfunc BenchmarkContextAppend(b *testing.B) {\n\tlogger := New(io.Discard).With().\n\t\tStr(\"foo\", \"bar\").\n\t\tLogger()\n\tb.ResetTimer()\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tlogger.With().Str(\"bar\", \"baz\")\n\t\t}\n\t})\n}\n\nfunc BenchmarkLogFields(b *testing.B) {\n\tlogger := New(io.Discard)\n\tb.ResetTimer()\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tlogger.Info().\n\t\t\t\tStr(\"string\", \"four!\").\n\t\t\t\tTime(\"time\", time.Time{}).\n\t\t\t\tInt(\"int\", 123).\n\t\t\t\tFloat32(\"float\", -2.203230293249593).\n\t\t\t\tMsg(fakeMessage)\n\t\t}\n\t})\n}\n\nfunc BenchmarkLogArrayObject(b *testing.B) {\n\tobj1 := fixtureObj{\"a\", \"b\", 2}\n\tobj2 := fixtureObj{\"c\", \"d\", 3}\n\tobj3 := fixtureObj{\"e\", \"f\", 4}\n\tlogger := New(io.Discard)\n\tb.ResetTimer()\n\tb.ReportAllocs()\n\tfor i := 0; i < b.N; i++ {\n\t\tarr := Arr()\n\t\tarr.Object(&obj1)\n\t\tarr.Object(&obj2)\n\t\tarr.Object(&obj3)\n\t\tlogger.Info().Array(\"objects\", arr).Msg(\"test\")\n\t}\n}\n\nfunc BenchmarkLogFieldType(b *testing.B) {\n\tfixtures := makeFieldFixtures()\n\ttypes := map[string]func(e *Event) *Event{\n\t\t\"Any\": func(e *Event) *Event {\n\t\t\treturn e.Any(\"k\", fixtures.Interfaces[0])\n\t\t},\n\t\t\"Bool\": func(e *Event) *Event {\n\t\t\treturn e.Bool(\"k\", fixtures.Bools[0])\n\t\t},\n\t\t\"Bools\": func(e *Event) *Event {\n\t\t\treturn e.Bools(\"k\", fixtures.Bools)\n\t\t},\n\t\t\"Bytes\": func(e *Event) *Event {\n\t\t\treturn e.Bytes(\"k\", fixtures.Bytes)\n\t\t},\n\t\t\"Hex\": func(e *Event) *Event {\n\t\t\treturn e.Hex(\"k\", fixtures.Bytes)\n\t\t},\n\t\t\"Int\": func(e *Event) *Event {\n\t\t\treturn e.Int(\"k\", fixtures.Ints[0])\n\t\t},\n\t\t\"Ints\": func(e *Event) *Event {\n\t\t\treturn e.Ints(\"k\", fixtures.Ints)\n\t\t},\n\t\t\"Float32\": func(e *Event) *Event {\n\t\t\treturn e.Float32(\"k\", fixtures.Floats32[0])\n\t\t},\n\t\t\"Floats32\": func(e *Event) *Event {\n\t\t\treturn e.Floats32(\"k\", fixtures.Floats32)\n\t\t},\n\t\t\"Float64\": func(e *Event) *Event {\n\t\t\treturn e.Float64(\"k\", fixtures.Floats64[0])\n\t\t},\n\t\t\"Floats64\": func(e *Event) *Event {\n\t\t\treturn e.Floats64(\"k\", fixtures.Floats64)\n\t\t},\n\t\t\"Str\": func(e *Event) *Event {\n\t\t\treturn e.Str(\"k\", fixtures.Strings[0])\n\t\t},\n\t\t\"Strs\": func(e *Event) *Event {\n\t\t\treturn e.Strs(\"k\", fixtures.Strings)\n\t\t},\n\t\t\"Stringer\": func(e *Event) *Event {\n\t\t\treturn e.Stringer(\"k\", fixtures.Stringers[0])\n\t\t},\n\t\t\"Stringers\": func(e *Event) *Event {\n\t\t\treturn e.Stringers(\"k\", fixtures.Stringers)\n\t\t},\n\t\t\"Err\": func(e *Event) *Event {\n\t\t\treturn e.Err(fixtures.Errs[0])\n\t\t},\n\t\t\"Errs\": func(e *Event) *Event {\n\t\t\treturn e.Errs(\"k\", fixtures.Errs)\n\t\t},\n\t\t\"Ctx\": func(e *Event) *Event {\n\t\t\treturn e.Ctx(fixtures.Ctx)\n\t\t},\n\t\t\"Time\": func(e *Event) *Event {\n\t\t\treturn e.Time(\"k\", fixtures.Times[0])\n\t\t},\n\t\t\"Times\": func(e *Event) *Event {\n\t\t\treturn e.Times(\"k\", fixtures.Times)\n\t\t},\n\t\t\"Dur\": func(e *Event) *Event {\n\t\t\treturn e.Dur(\"k\", fixtures.Durations[0])\n\t\t},\n\t\t\"Durs\": func(e *Event) *Event {\n\t\t\treturn e.Durs(\"k\", fixtures.Durations)\n\t\t},\n\t\t\"Interface\": func(e *Event) *Event {\n\t\t\treturn e.Interface(\"k\", fixtures.Interfaces[0])\n\t\t},\n\t\t\"Interfaces\": func(e *Event) *Event {\n\t\t\treturn e.Interface(\"k\", fixtures.Interfaces)\n\t\t},\n\t\t\"Interface(Object)\": func(e *Event) *Event {\n\t\t\treturn e.Interface(\"k\", fixtures.Objects[0])\n\t\t},\n\t\t\"Interface(Objects)\": func(e *Event) *Event {\n\t\t\treturn e.Interface(\"k\", fixtures.Objects)\n\t\t},\n\t\t\"Object\": func(e *Event) *Event {\n\t\t\treturn e.Object(\"k\", fixtures.Objects[0])\n\t\t},\n\t\t\"Objects\": func(e *Event) *Event {\n\t\t\treturn e.Objects(\"k\", fixtures.Objects)\n\t\t},\n\t\t\"Timestamp\": func(e *Event) *Event {\n\t\t\treturn e.Timestamp()\n\t\t},\n\t\t\"IPAddr\": func(e *Event) *Event {\n\t\t\treturn e.IPAddr(\"k\", fixtures.IPAddrs[0])\n\t\t},\n\t\t\"IPAddrs\": func(e *Event) *Event {\n\t\t\treturn e.IPAddrs(\"k\", fixtures.IPAddrs)\n\t\t},\n\t\t\"IPPrefix\": func(e *Event) *Event {\n\t\t\treturn e.IPPrefix(\"k\", fixtures.IPPfxs[0])\n\t\t},\n\t\t\"IPPrefixes\": func(e *Event) *Event {\n\t\t\treturn e.IPPrefixes(\"k\", fixtures.IPPfxs)\n\t\t},\n\t\t\"MACAddr\": func(e *Event) *Event {\n\t\t\treturn e.MACAddr(\"k\", fixtures.MACAddr)\n\t\t},\n\t\t\"Type\": func(e *Event) *Event {\n\t\t\treturn e.Type(\"k\", fixtures.Type)\n\t\t},\n\t}\n\n\tlogger := New(io.Discard)\n\tb.ResetTimer()\n\tfor name := range types {\n\t\tf := types[name]\n\t\tb.Run(name, func(b *testing.B) {\n\t\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\t\tfor pb.Next() {\n\t\t\t\t\tf(logger.Info()).Msg(\"\")\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc BenchmarkContextFieldType(b *testing.B) {\n\toldFormat := TimeFieldFormat\n\tTimeFieldFormat = TimeFormatUnix\n\tdefer func() { TimeFieldFormat = oldFormat }()\n\n\tfixtures := makeFieldFixtures()\n\ttypes := map[string]func(c Context) Context{\n\t\t\"Any\": func(c Context) Context {\n\t\t\treturn c.Any(\"k\", fixtures.Interfaces[0])\n\t\t},\n\t\t\"Bool\": func(c Context) Context {\n\t\t\treturn c.Bool(\"k\", fixtures.Bools[0])\n\t\t},\n\t\t\"Bools\": func(c Context) Context {\n\t\t\treturn c.Bools(\"k\", fixtures.Bools)\n\t\t},\n\t\t\"Bytes\": func(c Context) Context {\n\t\t\treturn c.Bytes(\"k\", fixtures.Bytes)\n\t\t},\n\t\t\"Hex\": func(c Context) Context {\n\t\t\treturn c.Hex(\"k\", fixtures.Bytes)\n\t\t},\n\t\t\"Int\": func(c Context) Context {\n\t\t\treturn c.Int(\"k\", fixtures.Ints[0])\n\t\t},\n\t\t\"Ints\": func(c Context) Context {\n\t\t\treturn c.Ints(\"k\", fixtures.Ints)\n\t\t},\n\t\t\"Float32\": func(c Context) Context {\n\t\t\treturn c.Float32(\"k\", fixtures.Floats32[0])\n\t\t},\n\t\t\"Floats32\": func(c Context) Context {\n\t\t\treturn c.Floats32(\"k\", fixtures.Floats32)\n\t\t},\n\t\t\"Float64\": func(c Context) Context {\n\t\t\treturn c.Float64(\"k\", fixtures.Floats64[0])\n\t\t},\n\t\t\"Floats64\": func(c Context) Context {\n\t\t\treturn c.Floats64(\"k\", fixtures.Floats64)\n\t\t},\n\t\t\"Str\": func(c Context) Context {\n\t\t\treturn c.Str(\"k\", fixtures.Strings[0])\n\t\t},\n\t\t\"Strs\": func(c Context) Context {\n\t\t\treturn c.Strs(\"k\", fixtures.Strings)\n\t\t},\n\t\t\"Stringer\": func(c Context) Context {\n\t\t\treturn c.Stringer(\"k\", fixtures.Stringers[0])\n\t\t},\n\t\t\"Stringers\": func(c Context) Context {\n\t\t\treturn c.Stringers(\"k\", fixtures.Stringers)\n\t\t},\n\t\t\"Err\": func(c Context) Context {\n\t\t\treturn c.Err(fixtures.Errs[0])\n\t\t},\n\t\t\"Errs\": func(c Context) Context {\n\t\t\treturn c.Errs(\"k\", fixtures.Errs)\n\t\t},\n\t\t\"Ctx\": func(c Context) Context {\n\t\t\treturn c.Ctx(fixtures.Ctx)\n\t\t},\n\t\t\"Time\": func(c Context) Context {\n\t\t\treturn c.Time(\"k\", fixtures.Times[0])\n\t\t},\n\t\t\"Times\": func(c Context) Context {\n\t\t\treturn c.Times(\"k\", fixtures.Times)\n\t\t},\n\t\t\"Dur\": func(c Context) Context {\n\t\t\treturn c.Dur(\"k\", fixtures.Durations[0])\n\t\t},\n\t\t\"Durs\": func(c Context) Context {\n\t\t\treturn c.Durs(\"k\", fixtures.Durations)\n\t\t},\n\t\t\"Interface\": func(c Context) Context {\n\t\t\treturn c.Interface(\"k\", fixtures.Interfaces[0])\n\t\t},\n\t\t\"Interfaces\": func(c Context) Context {\n\t\t\treturn c.Interface(\"k\", fixtures.Interfaces)\n\t\t},\n\t\t\"Interface(Object)\": func(c Context) Context {\n\t\t\treturn c.Interface(\"k\", fixtures.Objects[0])\n\t\t},\n\t\t\"Interface(Objects)\": func(c Context) Context {\n\t\t\treturn c.Interface(\"k\", fixtures.Objects)\n\t\t},\n\t\t\"Object\": func(c Context) Context {\n\t\t\treturn c.Object(\"k\", fixtures.Objects[0])\n\t\t},\n\t\t\"Objects\": func(c Context) Context {\n\t\t\treturn c.Objects(\"k\", fixtures.Objects)\n\t\t},\n\t\t\"Timestamp\": func(c Context) Context {\n\t\t\treturn c.Timestamp()\n\t\t},\n\t\t\"IPAddr\": func(c Context) Context {\n\t\t\treturn c.IPAddr(\"k\", fixtures.IPAddrs[0])\n\t\t},\n\t\t\"IPAddrs\": func(c Context) Context {\n\t\t\treturn c.IPAddrs(\"k\", fixtures.IPAddrs)\n\t\t},\n\t\t\"IPPrefix\": func(c Context) Context {\n\t\t\treturn c.IPPrefix(\"k\", fixtures.IPPfxs[0])\n\t\t},\n\t\t\"IPPrefixes\": func(c Context) Context {\n\t\t\treturn c.IPPrefixes(\"k\", fixtures.IPPfxs)\n\t\t},\n\t\t\"MACAddr\": func(c Context) Context {\n\t\t\treturn c.MACAddr(\"k\", fixtures.MACAddr)\n\t\t},\n\t\t\"Type\": func(c Context) Context {\n\t\t\treturn c.Type(\"k\", fixtures.Type)\n\t\t},\n\t}\n\n\tlogger := New(io.Discard)\n\tb.ResetTimer()\n\tfor name := range types {\n\t\tf := types[name]\n\t\tb.Run(name, func(b *testing.B) {\n\t\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\t\tfor pb.Next() {\n\t\t\t\t\tl := f(logger.With()).Logger()\n\t\t\t\t\tl.Info().Msg(\"\")\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "binary_test.go",
    "content": "//go:build binary_log\n// +build binary_log\n\npackage zerolog\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\n\tstdlog \"log\"\n\t\"time\"\n)\n\nfunc ExampleLogger_With() {\n\tdst := bytes.Buffer{}\n\tlog := New(&dst).\n\t\tWith().\n\t\tStr(\"foo\", \"bar\").\n\t\tLogger()\n\n\tlog.Info().Msg(\"hello world\")\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\n\t// Output: {\"level\":\"info\",\"foo\":\"bar\",\"message\":\"hello world\"}\n}\n\nfunc ExampleLogger_Level() {\n\tdst := bytes.Buffer{}\n\tlog := New(&dst).Level(WarnLevel)\n\n\tlog.Info().Msg(\"filtered out message\")\n\tlog.Error().Msg(\"kept message\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"level\":\"error\",\"message\":\"kept message\"}\n}\n\nfunc ExampleLogger_Sample() {\n\tdst := bytes.Buffer{}\n\tlog := New(&dst).Sample(&BasicSampler{N: 2})\n\n\tlog.Info().Msg(\"message 1\")\n\tlog.Info().Msg(\"message 2\")\n\tlog.Info().Msg(\"message 3\")\n\tlog.Info().Msg(\"message 4\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"level\":\"info\",\"message\":\"message 1\"}\n\t// {\"level\":\"info\",\"message\":\"message 3\"}\n}\n\ntype LevelNameHook1 struct{}\n\nfunc (h LevelNameHook1) Run(e *Event, l Level, msg string) {\n\tif l != NoLevel {\n\t\te.Str(\"level_name\", l.String())\n\t} else {\n\t\te.Str(\"level_name\", \"NoLevel\")\n\t}\n}\n\ntype MessageHook string\n\nfunc (h MessageHook) Run(e *Event, l Level, msg string) {\n\te.Str(\"the_message\", msg)\n}\n\nfunc ExampleLogger_Hook() {\n\tvar levelNameHook LevelNameHook1\n\tvar messageHook MessageHook = \"The message\"\n\n\tdst := bytes.Buffer{}\n\tlog := New(&dst).Hook(levelNameHook).Hook(messageHook)\n\n\tlog.Info().Msg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"level\":\"info\",\"level_name\":\"info\",\"the_message\":\"hello world\",\"message\":\"hello world\"}\n}\n\nfunc ExampleLogger_Print() {\n\tdst := bytes.Buffer{}\n\tlog := New(&dst)\n\n\tlog.Print(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"level\":\"debug\",\"message\":\"hello world\"}\n}\n\nfunc ExampleLogger_Printf() {\n\tdst := bytes.Buffer{}\n\tlog := New(&dst)\n\n\tlog.Printf(\"hello %s\", \"world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"level\":\"debug\",\"message\":\"hello world\"}\n}\n\nfunc ExampleLogger_Trace() {\n\tdst := bytes.Buffer{}\n\tlog := New(&dst)\n\n\tlog.Trace().\n\t\tStr(\"foo\", \"bar\").\n\t\tInt(\"n\", 123).\n\t\tMsg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"level\":\"trace\",\"foo\":\"bar\",\"n\":123,\"message\":\"hello world\"}\n}\n\nfunc ExampleLogger_Debug() {\n\tdst := bytes.Buffer{}\n\tlog := New(&dst)\n\n\tlog.Debug().\n\t\tStr(\"foo\", \"bar\").\n\t\tInt(\"n\", 123).\n\t\tMsg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"level\":\"debug\",\"foo\":\"bar\",\"n\":123,\"message\":\"hello world\"}\n}\n\nfunc ExampleLogger_Info() {\n\tdst := bytes.Buffer{}\n\tlog := New(&dst)\n\n\tlog.Info().\n\t\tStr(\"foo\", \"bar\").\n\t\tInt(\"n\", 123).\n\t\tMsg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"level\":\"info\",\"foo\":\"bar\",\"n\":123,\"message\":\"hello world\"}\n}\n\nfunc ExampleLogger_Warn() {\n\tdst := bytes.Buffer{}\n\tlog := New(&dst)\n\n\tlog.Warn().\n\t\tStr(\"foo\", \"bar\").\n\t\tMsg(\"a warning message\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"level\":\"warn\",\"foo\":\"bar\",\"message\":\"a warning message\"}\n}\n\nfunc ExampleLogger_Error() {\n\tdst := bytes.Buffer{}\n\tlog := New(&dst)\n\n\tlog.Error().\n\t\tErr(errors.New(\"some error\")).\n\t\tMsg(\"error doing something\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"level\":\"error\",\"error\":\"some error\",\"message\":\"error doing something\"}\n}\n\nfunc ExampleLogger_WithLevel() {\n\tdst := bytes.Buffer{}\n\tlog := New(&dst)\n\n\tlog.WithLevel(InfoLevel).\n\t\tMsg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"level\":\"info\",\"message\":\"hello world\"}\n}\n\nfunc ExampleLogger_Write() {\n\tdst := bytes.Buffer{}\n\tlog := New(&dst).With().\n\t\tStr(\"foo\", \"bar\").\n\t\tLogger()\n\n\tstdlog.SetFlags(0)\n\tstdlog.SetOutput(log)\n\n\tstdlog.Print(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"foo\":\"bar\",\"message\":\"hello world\"}\n}\n\nfunc ExampleLogger_Log() {\n\tdst := bytes.Buffer{}\n\tlog := New(&dst)\n\n\tlog.Log().\n\t\tStr(\"foo\", \"bar\").\n\t\tStr(\"bar\", \"baz\").\n\t\tMsg(\"\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"foo\":\"bar\",\"bar\":\"baz\"}\n}\n\nfunc ExampleEvent_Dict() {\n\tdst := bytes.Buffer{}\n\tlog := New(&dst)\n\n\te := log.Log().\n\t\tStr(\"foo\", \"bar\")\n\n\te.Dict(\"dict\", e.CreateDict().\n\t\tStr(\"bar\", \"baz\").\n\t\tInt(\"n\", 1),\n\t).\n\t\tMsg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"foo\":\"bar\",\"dict\":{\"bar\":\"baz\",\"n\":1},\"message\":\"hello world\"}\n}\n\ntype User struct {\n\tName    string\n\tAge     int\n\tCreated time.Time\n}\n\nfunc (u User) MarshalZerologObject(e *Event) {\n\te.Str(\"name\", u.Name).\n\t\tInt(\"age\", u.Age).\n\t\tTime(\"created\", u.Created)\n}\n\ntype Users []User\n\n// User implements LogObjectMarshaler\nfunc (uu Users) MarshalZerologArray(a *Array) {\n\tfor _, u := range uu {\n\t\ta.Object(u)\n\t}\n}\n\nfunc ExampleEvent_Array() {\n\tdst := bytes.Buffer{}\n\tlog := New(&dst)\n\n\te := log.Log().\n\t\tStr(\"foo\", \"bar\")\n\n\te.Array(\"array\", e.CreateArray().\n\t\tStr(\"baz\").\n\t\tInt(1),\n\t).\n\t\tMsg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"foo\":\"bar\",\"array\":[\"baz\",1],\"message\":\"hello world\"}\n}\n\nfunc ExampleEvent_Array_object() {\n\tdst := bytes.Buffer{}\n\tlog := New(&dst)\n\n\t// Users implements LogArrayMarshaler\n\tu := Users{\n\t\tUser{\"John\", 35, time.Time{}},\n\t\tUser{\"Bob\", 55, time.Time{}},\n\t}\n\n\tlog.Log().\n\t\tStr(\"foo\", \"bar\").\n\t\tArray(\"users\", u).\n\t\tMsg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"foo\":\"bar\",\"users\":[{\"name\":\"John\",\"age\":35,\"created\":\"0001-01-01T00:00:00Z\"},{\"name\":\"Bob\",\"age\":55,\"created\":\"0001-01-01T00:00:00Z\"}],\"message\":\"hello world\"}\n}\n\nfunc ExampleEvent_Object() {\n\tdst := bytes.Buffer{}\n\tlog := New(&dst)\n\n\t// User implements LogObjectMarshaler\n\tu := User{\"John\", 35, time.Time{}}\n\n\tlog.Log().\n\t\tStr(\"foo\", \"bar\").\n\t\tObject(\"user\", u).\n\t\tMsg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"foo\":\"bar\",\"user\":{\"name\":\"John\",\"age\":35,\"created\":\"0001-01-01T00:00:00Z\"},\"message\":\"hello world\"}\n}\n\nfunc ExampleContext_Objects() {\n\t// In go, arrays are type invariant so even if you have a variable u of type []User array and User implements\n\t// the LogObjectMarshaler interface, you cannot pass that to func that takes an []LogObjectMarshaler array in the\n\t// Objects call. In 1.24+ it allows passing the variadic covariant slice (e.g. u...) but the unit test needs to\n\t// work in earlier versions so we'll declare the array as []LogObjectMarshaler here.\n\tu := []LogObjectMarshaler{User{\"John\", 35, time.Time{}}, User{\"Bob\", 55, time.Time{}}}\n\n\tdst := bytes.Buffer{}\n\tlog := New(&dst).With().\n\t\tStr(\"foo\", \"bar\").\n\t\tObjects(\"users\", u).\n\t\tLogger()\n\n\tlog.Log().Msg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"foo\":\"bar\",\"users\":[{\"name\":\"John\",\"age\":35,\"created\":\"0001-01-01T00:00:00Z\"},{\"name\":\"Bob\",\"age\":55,\"created\":\"0001-01-01T00:00:00Z\"}],\"message\":\"hello world\"}\n}\n\nfunc ExampleEvent_EmbedObject() {\n\tprice := Price{val: 6449, prec: 2, unit: \"$\"}\n\n\tdst := bytes.Buffer{}\n\tlog := New(&dst)\n\n\tlog.Log().\n\t\tStr(\"foo\", \"bar\").\n\t\tEmbedObject(price).\n\t\tMsg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"foo\":\"bar\",\"price\":\"$64.49\",\"message\":\"hello world\"}\n}\n\nfunc ExampleEvent_Interface() {\n\tdst := bytes.Buffer{}\n\tlog := New(&dst)\n\n\tobj := struct {\n\t\tName string `json:\"name\"`\n\t}{\n\t\tName: \"john\",\n\t}\n\n\tlog.Log().\n\t\tStr(\"foo\", \"bar\").\n\t\tInterface(\"obj\", obj).\n\t\tMsg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"foo\":\"bar\",\"obj\":{\"name\":\"john\"},\"message\":\"hello world\"}\n}\n\nfunc ExampleEvent_Dur() {\n\td := time.Duration(10 * time.Second)\n\n\tdst := bytes.Buffer{}\n\tlog := New(&dst)\n\n\tlog.Log().\n\t\tStr(\"foo\", \"bar\").\n\t\tDur(\"dur\", d).\n\t\tMsg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"foo\":\"bar\",\"dur\":10000,\"message\":\"hello world\"}\n}\n\nfunc ExampleEvent_Durs() {\n\td := []time.Duration{\n\t\ttime.Duration(10 * time.Second),\n\t\ttime.Duration(20 * time.Second),\n\t}\n\n\tdst := bytes.Buffer{}\n\tlog := New(&dst)\n\n\tlog.Log().\n\t\tStr(\"foo\", \"bar\").\n\t\tDurs(\"durs\", d).\n\t\tMsg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"foo\":\"bar\",\"durs\":[10000,20000],\"message\":\"hello world\"}\n}\n\nfunc ExampleEvent_Fields_map() {\n\tfields := map[string]interface{}{\n\t\t\"bar\": \"baz\",\n\t\t\"n\":   1,\n\t}\n\n\tdst := bytes.Buffer{}\n\tlog := New(&dst)\n\n\tlog.Log().\n\t\tStr(\"foo\", \"bar\").\n\t\tFields(fields).\n\t\tMsg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"foo\":\"bar\",\"bar\":\"baz\",\"n\":1,\"message\":\"hello world\"}\n}\n\nfunc ExampleEvent_Fields_slice() {\n\tfields := []interface{}{\n\t\t\"bar\", \"baz\",\n\t\t\"n\", 1,\n\t}\n\n\tdst := bytes.Buffer{}\n\tlog := New(&dst)\n\n\tlog.Log().\n\t\tStr(\"foo\", \"bar\").\n\t\tFields(fields).\n\t\tMsg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"foo\":\"bar\",\"bar\":\"baz\",\"n\":1,\"message\":\"hello world\"}\n}\n\nfunc ExampleContext_Dict() {\n\tdst := bytes.Buffer{}\n\tctx := New(&dst).With().\n\t\tStr(\"foo\", \"bar\")\n\n\tlogger := ctx.Dict(\"dict\", ctx.CreateDict().\n\t\tStr(\"bar\", \"baz\").\n\t\tInt(\"n\", 1),\n\t).Logger()\n\n\tlogger.Log().Msg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"foo\":\"bar\",\"dict\":{\"bar\":\"baz\",\"n\":1},\"message\":\"hello world\"}\n}\n\nfunc ExampleContext_Array() {\n\tdst := bytes.Buffer{}\n\tctx := New(&dst).With().\n\t\tStr(\"foo\", \"bar\")\n\n\tlogger := ctx.Array(\"array\", ctx.CreateArray().\n\t\tStr(\"baz\").\n\t\tInt(1),\n\t).Logger()\n\n\tlogger.Log().Msg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"foo\":\"bar\",\"array\":[\"baz\",1],\"message\":\"hello world\"}\n}\n\nfunc ExampleContext_Array_object() {\n\t// Users implements LogArrayMarshaler\n\tu := Users{\n\t\tUser{\"John\", 35, time.Time{}},\n\t\tUser{\"Bob\", 55, time.Time{}},\n\t}\n\n\tdst := bytes.Buffer{}\n\tlog := New(&dst).With().\n\t\tStr(\"foo\", \"bar\").\n\t\tArray(\"users\", u).\n\t\tLogger()\n\n\tlog.Log().Msg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"foo\":\"bar\",\"users\":[{\"name\":\"John\",\"age\":35,\"created\":\"0001-01-01T00:00:00Z\"},{\"name\":\"Bob\",\"age\":55,\"created\":\"0001-01-01T00:00:00Z\"}],\"message\":\"hello world\"}\n}\n\ntype Price struct {\n\tval  uint64\n\tprec int\n\tunit string\n}\n\nfunc (p Price) MarshalZerologObject(e *Event) {\n\tdenom := uint64(1)\n\tfor i := 0; i < p.prec; i++ {\n\t\tdenom *= 10\n\t}\n\tresult := []byte(p.unit)\n\tresult = append(result, fmt.Sprintf(\"%d.%d\", p.val/denom, p.val%denom)...)\n\te.Str(\"price\", string(result))\n}\n\nfunc ExampleContext_EmbedObject() {\n\tprice := Price{val: 6449, prec: 2, unit: \"$\"}\n\n\tdst := bytes.Buffer{}\n\tlog := New(&dst).With().\n\t\tStr(\"foo\", \"bar\").\n\t\tEmbedObject(price).\n\t\tLogger()\n\n\tlog.Log().Msg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"foo\":\"bar\",\"price\":\"$64.49\",\"message\":\"hello world\"}\n}\nfunc ExampleContext_Object() {\n\t// User implements LogObjectMarshaler\n\tu := User{\"John\", 35, time.Time{}}\n\n\tdst := bytes.Buffer{}\n\tlog := New(&dst).With().\n\t\tStr(\"foo\", \"bar\").\n\t\tObject(\"user\", u).\n\t\tLogger()\n\n\tlog.Log().Msg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"foo\":\"bar\",\"user\":{\"name\":\"John\",\"age\":35,\"created\":\"0001-01-01T00:00:00Z\"},\"message\":\"hello world\"}\n}\n\nfunc ExampleContext_Interface() {\n\tobj := struct {\n\t\tName string `json:\"name\"`\n\t}{\n\t\tName: \"john\",\n\t}\n\n\tdst := bytes.Buffer{}\n\tlog := New(&dst).With().\n\t\tStr(\"foo\", \"bar\").\n\t\tInterface(\"obj\", obj).\n\t\tLogger()\n\n\tlog.Log().Msg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"foo\":\"bar\",\"obj\":{\"name\":\"john\"},\"message\":\"hello world\"}\n}\n\nfunc ExampleContext_Dur() {\n\td := time.Duration(10 * time.Second)\n\n\tdst := bytes.Buffer{}\n\tlog := New(&dst).With().\n\t\tStr(\"foo\", \"bar\").\n\t\tDur(\"dur\", d).\n\t\tLogger()\n\n\tlog.Log().Msg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"foo\":\"bar\",\"dur\":10000,\"message\":\"hello world\"}\n}\n\nfunc ExampleContext_Durs() {\n\td := []time.Duration{\n\t\ttime.Duration(10 * time.Second),\n\t\ttime.Duration(20 * time.Second),\n\t}\n\n\tdst := bytes.Buffer{}\n\tlog := New(&dst).With().\n\t\tStr(\"foo\", \"bar\").\n\t\tDurs(\"durs\", d).\n\t\tLogger()\n\n\tlog.Log().Msg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"foo\":\"bar\",\"durs\":[10000,20000],\"message\":\"hello world\"}\n}\n\nfunc ExampleContext_Fields_map() {\n\tfields := map[string]interface{}{\n\t\t\"bar\": \"baz\",\n\t\t\"n\":   1,\n\t}\n\n\tdst := bytes.Buffer{}\n\tlog := New(&dst).With().\n\t\tStr(\"foo\", \"bar\").\n\t\tFields(fields).\n\t\tLogger()\n\n\tlog.Log().Msg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"foo\":\"bar\",\"bar\":\"baz\",\"n\":1,\"message\":\"hello world\"}\n}\n\nfunc ExampleContext_Fields_slice() {\n\tfields := []interface{}{\n\t\t\"bar\", \"baz\",\n\t\t\"n\", 1,\n\t}\n\n\tdst := bytes.Buffer{}\n\tlog := New(&dst).With().\n\t\tStr(\"foo\", \"bar\").\n\t\tFields(fields).\n\t\tLogger()\n\n\tlog.Log().Msg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"foo\":\"bar\",\"bar\":\"baz\",\"n\":1,\"message\":\"hello world\"}\n}\n\nfunc ExampleContext_IPAddr() {\n\tipV4 := net.IP{192, 168, 0, 1}\n\tipV6 := net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34}\n\n\tdst := bytes.Buffer{}\n\tlog := New(&dst).With().\n\t\tStr(\"foo\", \"bar\").\n\t\tIPAddr(\"v4\", ipV4).\n\t\tIPAddr(\"v6\", ipV6).\n\t\tLogger()\n\n\tlog.Log().Msg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"foo\":\"bar\",\"v4\":\"192.168.0.1\",\"v6\":\"2001:db8:85a3::8a2e:370:7334\",\"message\":\"hello world\"}\n}\nfunc ExampleContext_IPPrefix() {\n\tpfxV4 := net.IPNet{IP: net.IP{192, 168, 0, 100}, Mask: net.CIDRMask(24, 32)}\n\tpfxV6 := net.IPNet{IP: net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x00}, Mask: net.CIDRMask(64, 128)}\n\n\tdst := bytes.Buffer{}\n\tlog := New(&dst).With().\n\t\tStr(\"foo\", \"bar\").\n\t\tIPPrefix(\"v4\", pfxV4).\n\t\tIPPrefix(\"v6\", pfxV6).\n\t\tLogger()\n\n\tlog.Log().Msg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"foo\":\"bar\",\"v4\":\"192.168.0.100/24\",\"v6\":\"2001:db8:85a3::8a2e:370:7300/64\",\"message\":\"hello world\"}\n}\nfunc ExampleContext_MACAddr() {\n\tmac := net.HardwareAddr{0x12, 0x34, 0x56, 0x78, 0x90, 0xab}\n\n\tdst := bytes.Buffer{}\n\tlog := New(&dst).With().\n\t\tStr(\"foo\", \"bar\").\n\t\tMACAddr(\"mac\", mac).\n\t\tLogger()\n\n\tlog.Log().Msg(\"hello world\")\n\n\tfmt.Println(decodeIfBinaryToString(dst.Bytes()))\n\t// Output: {\"foo\":\"bar\",\"mac\":\"12:34:56:78:90:ab\",\"message\":\"hello world\"}\n}\n"
  },
  {
    "path": "cmd/lint/README.md",
    "content": "# Zerolog Lint\n\n**DEPRECATED: In favor of https://github.com/ykadowak/zerologlint which is integrated with `go vet` and [golangci-lint](https://golangci-lint.run/).**\n\nThis is a basic linter that checks for missing log event finishers. Finds errors like: `log.Error().Int64(\"userID\": 5)` - missing the `Msg`/`Msgf` finishers.\n\n## Problem\n\nWhen using zerolog it's easy to forget to finish the log event chain by calling a finisher - the `Msg` or `Msgf` function that will schedule the event for writing. The problem with this is that it doesn't warn/panic during compilation and it's not easily found by grep or other general tools. It's even prominently mentioned in the project's readme, that:\n\n> It is very important to note that when using the **zerolog** chaining API, as shown above (`log.Info().Msg(\"hello world\"`), the chain must have either the `Msg` or `Msgf` method call. If you forget to add either of these, the log will not occur and there is no compile time error to alert you of this.\n\n## Solution\n\nA basic linter like this one here that looks for method invocations on `zerolog.Event` can examine the last call in a method call chain and check if it is a finisher, thus pointing out these errors.\n\n## Usage\n\nJust compile this and then run it. Or just run it via `go run` command via something like `go run cmd/lint/lint.go`.\n\nThe command accepts only one argument - the package to be inspected - and 4 optional flags, all of which can occur multiple times. The standard synopsis of the command is:\n\n`lint [-finisher value] [-ignoreFile value] [-ignorePkg value] [-ignorePkgRecursively value] package`\n\n#### Flags\n\n- finisher\n    - specify which finishers to accept, defaults to `Msg` and `Msgf`\n- ignoreFile\n    - which files to ignore, either by full path or by go path (package/file.go)\n- ignorePkg\n    - do not inspect the specified package if found in the dependency tree\n- ignorePkgRecursively\n    - do not inspect the specified package or its subpackages if found in the dependency tree\n\n## Drawbacks\n\nAs it is, linter can generate a false positives in a specific case. These false positives come from the fact that if you have a method that returns a `zerolog.Event` the linter will flag it because you are obviously not finishing the event. This will be solved in later release.\n\n"
  },
  {
    "path": "cmd/lint/go.mod",
    "content": "module github.com/rs/zerolog/cmd/lint\n\ngo 1.15\n\nrequire golang.org/x/tools v0.1.8\n"
  },
  {
    "path": "cmd/lint/go.sum",
    "content": "github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=\ngolang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=\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/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w=\ngolang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\n"
  },
  {
    "path": "cmd/lint/lint.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/token\"\n\t\"go/types\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"golang.org/x/tools/go/loader\"\n)\n\nvar (\n\trecursivelyIgnoredPkgs arrayFlag\n\tignoredPkgs            arrayFlag\n\tignoredFiles           arrayFlag\n\tallowedFinishers       arrayFlag = []string{\"Msg\", \"Msgf\"}\n\trootPkg                string\n)\n\n// parse input flags and args\nfunc init() {\n\tflag.Var(&recursivelyIgnoredPkgs, \"ignorePkgRecursively\", \"ignore the specified package and all subpackages recursively\")\n\tflag.Var(&ignoredPkgs, \"ignorePkg\", \"ignore the specified package\")\n\tflag.Var(&ignoredFiles, \"ignoreFile\", \"ignore the specified file by its path and/or go path (package/file.go)\")\n\tflag.Var(&allowedFinishers, \"finisher\", \"allowed finisher for the event chain\")\n\tflag.Parse()\n\n\t// add zerolog to recursively ignored packages\n\trecursivelyIgnoredPkgs = append(recursivelyIgnoredPkgs, \"github.com/rs/zerolog\")\n\targs := flag.Args()\n\tif len(args) != 1 {\n\t\tfmt.Fprintln(os.Stderr, \"you must provide exactly one package path\")\n\t\tos.Exit(1)\n\t}\n\trootPkg = args[0]\n}\n\nfunc main() {\n\t// load the package and all its dependencies\n\tconf := loader.Config{}\n\tconf.Import(rootPkg)\n\tp, err := conf.Load()\n\tif err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"Error: unable to load the root package. %s\\n\", err.Error())\n\t\tos.Exit(1)\n\t}\n\n\t// get the github.com/rs/zerolog.Event type\n\tevent := getEvent(p)\n\tif event == nil {\n\t\tfmt.Fprintln(os.Stderr, \"Error: github.com/rs/zerolog.Event declaration not found, maybe zerolog is not imported in the scanned package?\")\n\t\tos.Exit(1)\n\t}\n\n\t// get all selections (function calls) with the github.com/rs/zerolog.Event (or pointer) receiver\n\tselections := getSelectionsWithReceiverType(p, event)\n\n\t// print the violations (if any)\n\thasViolations := false\n\tfor _, s := range selections {\n\t\tif hasBadFinisher(p, s) {\n\t\t\thasViolations = true\n\t\t\tfmt.Printf(\"Error: missing or bad finisher for log chain, last call: %q at: %s:%v\\n\", s.fn.Name(), p.Fset.File(s.Pos()).Name(), p.Fset.Position(s.Pos()).Line)\n\t\t}\n\t}\n\n\t// if no violations detected, return normally\n\tif !hasViolations {\n\t\tfmt.Println(\"No violations found\")\n\t\treturn\n\t}\n\n\t// if violations were detected, return error code\n\tos.Exit(1)\n}\n\nfunc getEvent(p *loader.Program) types.Type {\n\tfor _, pkg := range p.AllPackages {\n\t\tif strings.HasSuffix(pkg.Pkg.Path(), \"github.com/rs/zerolog\") {\n\t\t\tfor _, d := range pkg.Defs {\n\t\t\t\tif d != nil && d.Name() == \"Event\" {\n\t\t\t\t\treturn d.Type()\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc getSelectionsWithReceiverType(p *loader.Program, targetType types.Type) map[token.Pos]selection {\n\tselections := map[token.Pos]selection{}\n\n\tfor _, z := range p.AllPackages {\n\t\tfor i, t := range z.Selections {\n\t\t\tswitch o := t.Obj().(type) {\n\t\t\tcase *types.Func:\n\t\t\t\t// this is not a bug, o.Type() is always *types.Signature, see docs\n\t\t\t\tif vt := o.Type().(*types.Signature).Recv(); vt != nil {\n\t\t\t\t\ttyp := vt.Type()\n\t\t\t\t\tif pointer, ok := typ.(*types.Pointer); ok {\n\t\t\t\t\t\ttyp = pointer.Elem()\n\t\t\t\t\t}\n\n\t\t\t\t\tif typ == targetType {\n\t\t\t\t\t\tif s, ok := selections[i.Pos()]; !ok || i.End() > s.End() {\n\t\t\t\t\t\t\tselections[i.Pos()] = selection{i, o, z.Pkg}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\t// skip\n\t\t\t}\n\t\t}\n\t}\n\n\treturn selections\n}\n\nfunc hasBadFinisher(p *loader.Program, s selection) bool {\n\tpkgPath := strings.TrimPrefix(s.pkg.Path(), rootPkg+\"/vendor/\")\n\tabsoluteFilePath := strings.TrimPrefix(p.Fset.File(s.Pos()).Name(), rootPkg+\"/vendor/\")\n\tgoFilePath := pkgPath + \"/\" + filepath.Base(p.Fset.Position(s.Pos()).Filename)\n\n\tfor _, f := range allowedFinishers {\n\t\tif f == s.fn.Name() {\n\t\t\treturn false\n\t\t}\n\t}\n\n\tfor _, ignoredPkg := range recursivelyIgnoredPkgs {\n\t\tif strings.HasPrefix(pkgPath, ignoredPkg) {\n\t\t\treturn false\n\t\t}\n\t}\n\n\tfor _, ignoredPkg := range ignoredPkgs {\n\t\tif pkgPath == ignoredPkg {\n\t\t\treturn false\n\t\t}\n\t}\n\n\tfor _, ignoredFile := range ignoredFiles {\n\t\tif absoluteFilePath == ignoredFile {\n\t\t\treturn false\n\t\t}\n\n\t\tif goFilePath == ignoredFile {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n\ntype arrayFlag []string\n\nfunc (i *arrayFlag) String() string {\n\treturn fmt.Sprintf(\"%v\", []string(*i))\n}\n\nfunc (i *arrayFlag) Set(value string) error {\n\t*i = append(*i, value)\n\treturn nil\n}\n\ntype selection struct {\n\t*ast.SelectorExpr\n\tfn  *types.Func\n\tpkg *types.Package\n}\n"
  },
  {
    "path": "cmd/prettylog/README.md",
    "content": "# Zerolog PrettyLog\n\nThis is a basic CLI utility that will colorize and pretty print your structured JSON logs.\n\n## Usage\n\nYou can compile it or run it directly. The only issue is that by default Zerolog does not output to `stdout`\nbut rather to `stderr` so we must pipe `stderr` stream to this CLI tool.\n\n### Linux\n\nThese commands will redirect `stderr` to our `prettylog` tool and `stdout` will remain unaffected.\n\n1. Compiled version\n\n```shell\nsome_program_with_zerolog 2> >(prettylog)\n```\n\n2. Run it directly with `go run`\n\n```shell\nsome_program_with_zerolog 2> >(go run cmd/prettylog/prettylog.go)\n```\n\n### Windows\n\nThese commands will redirect `stderr` to `stdout` and then pipe it to our `prettylog` tool.\n\n1. Compiled version\n\n```shell\nsome_program_with_zerolog 2>&1 | prettylog\n```\n\n2. Run it directly with `go run`\n\n```shell\nsome_program_with_zerolog 2>&1 | go run cmd/prettylog/prettylog.go\n```\n"
  },
  {
    "path": "cmd/prettylog/prettylog.go",
    "content": "package main\n\nimport (\n\t\"bufio\"\n\t\"errors\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/rs/zerolog\"\n)\n\nfunc isInputFromPipe() bool {\n\tfileInfo, _ := os.Stdin.Stat()\n\treturn fileInfo.Mode()&os.ModeCharDevice == 0\n}\n\nfunc processInput(reader io.Reader, writer io.Writer) error {\n\tscanner := bufio.NewScanner(reader)\n\tfor scanner.Scan() {\n\t\tbytesToWrite := scanner.Bytes()\n\t\t_, err := writer.Write(bytesToWrite)\n\t\tif err != nil {\n\t\t\tif errors.Is(err, io.EOF) {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tfmt.Printf(\"%s\\n\", bytesToWrite)\n\t\t}\n\t}\n\n\treturn scanner.Err()\n}\n\nfunc main() {\n\ttimeFormats := map[string]string{\n\t\t\"default\": time.Kitchen,\n\t\t\"full\":    time.RFC1123,\n\t}\n\n\ttimeFormatFlag := flag.String(\n\t\t\"time-format\",\n\t\t\"default\",\n\t\t\"Time format, either 'default' or 'full'\",\n\t)\n\n\tflag.Parse()\n\n\ttimeFormat, ok := timeFormats[*timeFormatFlag]\n\tif !ok {\n\t\tpanic(\"Invalid time-format provided\")\n\t}\n\n\twriter := zerolog.NewConsoleWriter()\n\twriter.TimeFormat = timeFormat\n\n\tif isInputFromPipe() {\n\t\t_ = processInput(os.Stdin, writer)\n\t} else if flag.NArg() >= 1 {\n\t\tfor _, filename := range flag.Args() {\n\t\t\t// Scan each line from filename and write it into writer\n\t\t\treader, err := os.Open(filename)\n\t\t\tif err != nil {\n\t\t\t\tfmt.Printf(\"%s open: %v\", filename, err)\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\n\t\t\tif err := processInput(reader, writer); err != nil {\n\t\t\t\tfmt.Printf(\"%s scan: %v\", filename, err)\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t}\n\t} else {\n\t\tfmt.Println(\"Usage:\")\n\t\tfmt.Println(\"  app_with_zerolog | 2> >(prettylog)\")\n\t\tfmt.Println(\"  prettylog zerolog_output.jsonl\")\n\t\tos.Exit(1)\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "console.go",
    "content": "package zerolog\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/mattn/go-colorable\"\n)\n\nconst (\n\tcolorBlack = iota + 30\n\tcolorRed\n\tcolorGreen\n\tcolorYellow\n\tcolorBlue\n\tcolorMagenta\n\tcolorCyan\n\tcolorWhite\n\n\tcolorBold     = 1\n\tcolorDarkGray = 90\n\n\tunknownLevel = \"???\"\n)\n\nvar (\n\tconsoleBufPool = sync.Pool{\n\t\tNew: func() interface{} {\n\t\t\treturn bytes.NewBuffer(make([]byte, 0, 100))\n\t\t},\n\t}\n)\n\nconst (\n\tconsoleDefaultTimeFormat = time.Kitchen\n)\n\n// Formatter transforms the input into a formatted string.\ntype Formatter func(interface{}) string\n\n// FormatterByFieldName transforms the input into a formatted string,\n// being able to differentiate formatting based on field name.\ntype FormatterByFieldName func(interface{}, string) string\n\n// ConsoleWriter parses the JSON input and writes it in an\n// (optionally) colorized, human-friendly format to Out.\ntype ConsoleWriter struct {\n\t// Out is the output destination.\n\tOut io.Writer\n\n\t// NoColor disables the colorized output.\n\tNoColor bool\n\n\t// TimeFormat specifies the format for timestamp in output.\n\tTimeFormat string\n\n\t// TimeLocation tells ConsoleWriter’s default FormatTimestamp\n\t// how to localize the time.\n\tTimeLocation *time.Location\n\n\t// PartsOrder defines the order of parts in output.\n\tPartsOrder []string\n\n\t// PartsExclude defines parts to not display in output.\n\tPartsExclude []string\n\n\t// FieldsOrder defines the order of contextual fields in output.\n\tFieldsOrder []string\n\n\tfieldIsOrdered map[string]int\n\n\t// FieldsExclude defines contextual fields to not display in output.\n\tFieldsExclude []string\n\n\tFormatTimestamp     Formatter\n\tFormatLevel         Formatter\n\tFormatCaller        Formatter\n\tFormatMessage       Formatter\n\tFormatFieldName     Formatter\n\tFormatFieldValue    Formatter\n\tFormatErrFieldName  Formatter\n\tFormatErrFieldValue Formatter\n\t// If this is configured it is used for \"part\" values and\n\t// has precedence on FormatFieldValue\n\tFormatPartValueByName FormatterByFieldName\n\n\tFormatExtra func(map[string]interface{}, *bytes.Buffer) error\n\n\tFormatPrepare func(map[string]interface{}) error\n}\n\n// NewConsoleWriter creates and initializes a new ConsoleWriter.\nfunc NewConsoleWriter(options ...func(w *ConsoleWriter)) ConsoleWriter {\n\tw := ConsoleWriter{\n\t\tOut:        os.Stdout,\n\t\tTimeFormat: consoleDefaultTimeFormat,\n\t\tPartsOrder: consoleDefaultPartsOrder(),\n\t}\n\n\tfor _, opt := range options {\n\t\topt(&w)\n\t}\n\n\t// Fix color on Windows\n\tif w.Out == os.Stdout || w.Out == os.Stderr {\n\t\tw.Out = colorable.NewColorable(w.Out.(*os.File))\n\t}\n\n\treturn w\n}\n\n// Write transforms the JSON input with formatters and appends to w.Out.\nfunc (w ConsoleWriter) Write(p []byte) (n int, err error) {\n\t// Fix color on Windows\n\tif w.Out == os.Stdout || w.Out == os.Stderr {\n\t\tw.Out = colorable.NewColorable(w.Out.(*os.File))\n\t}\n\n\tif w.PartsOrder == nil {\n\t\tw.PartsOrder = consoleDefaultPartsOrder()\n\t}\n\n\tvar buf = consoleBufPool.Get().(*bytes.Buffer)\n\tdefer func() {\n\t\tbuf.Reset()\n\t\tconsoleBufPool.Put(buf)\n\t}()\n\n\tvar evt map[string]interface{}\n\tp = decodeIfBinaryToBytes(p)\n\td := json.NewDecoder(bytes.NewReader(p))\n\td.UseNumber()\n\terr = d.Decode(&evt)\n\tif err != nil {\n\t\treturn n, fmt.Errorf(\"cannot decode event: %s\", err)\n\t}\n\n\tif w.FormatPrepare != nil {\n\t\terr = w.FormatPrepare(evt)\n\t\tif err != nil {\n\t\t\treturn n, err\n\t\t}\n\t}\n\n\tfor _, p := range w.PartsOrder {\n\t\tw.writePart(buf, evt, p)\n\t}\n\n\tw.writeFields(evt, buf)\n\n\tif w.FormatExtra != nil {\n\t\terr = w.FormatExtra(evt, buf)\n\t\tif err != nil {\n\t\t\treturn n, err\n\t\t}\n\t}\n\n\terr = buf.WriteByte('\\n')\n\tif err != nil {\n\t\treturn n, err\n\t}\n\n\t_, err = buf.WriteTo(w.Out)\n\treturn len(p), err\n}\n\n// Call the underlying writer's Close method if it is an io.Closer. Otherwise\n// does nothing.\nfunc (w ConsoleWriter) Close() error {\n\tif closer, ok := w.Out.(io.Closer); ok {\n\t\treturn closer.Close()\n\t}\n\treturn nil\n}\n\n// writeFields appends formatted key-value pairs to buf.\nfunc (w ConsoleWriter) writeFields(evt map[string]interface{}, buf *bytes.Buffer) {\n\tvar fields = make([]string, 0, len(evt))\n\tfor field := range evt {\n\t\tvar isExcluded bool\n\t\tfor _, excluded := range w.FieldsExclude {\n\t\t\tif field == excluded {\n\t\t\t\tisExcluded = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif isExcluded {\n\t\t\tcontinue\n\t\t}\n\n\t\tswitch field {\n\t\tcase LevelFieldName, TimestampFieldName, MessageFieldName, CallerFieldName:\n\t\t\tcontinue\n\t\t}\n\t\tfields = append(fields, field)\n\t}\n\n\tif len(w.FieldsOrder) > 0 {\n\t\tw.orderFields(fields)\n\t} else {\n\t\tsort.Strings(fields)\n\t}\n\n\t// Write space only if something has already been written to the buffer, and if there are fields.\n\tif buf.Len() > 0 && len(fields) > 0 {\n\t\tbuf.WriteByte(' ')\n\t}\n\n\t// Move the \"error\" field to the front\n\tei := sort.Search(len(fields), func(i int) bool { return fields[i] >= ErrorFieldName })\n\tif ei < len(fields) && fields[ei] == ErrorFieldName {\n\t\tfields[ei] = \"\"\n\t\tfields = append([]string{ErrorFieldName}, fields...)\n\t\tvar xfields = make([]string, 0, len(fields))\n\t\tfor _, field := range fields {\n\t\t\tif field == \"\" { // Skip empty fields\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\txfields = append(xfields, field)\n\t\t}\n\t\tfields = xfields\n\t}\n\n\tfor i, field := range fields {\n\t\tvar fn Formatter\n\t\tvar fv Formatter\n\n\t\tif field == ErrorFieldName {\n\t\t\tif w.FormatErrFieldName == nil {\n\t\t\t\tfn = consoleDefaultFormatErrFieldName(w.NoColor)\n\t\t\t} else {\n\t\t\t\tfn = w.FormatErrFieldName\n\t\t\t}\n\n\t\t\tif w.FormatErrFieldValue == nil {\n\t\t\t\tfv = consoleDefaultFormatErrFieldValue(w.NoColor)\n\t\t\t} else {\n\t\t\t\tfv = w.FormatErrFieldValue\n\t\t\t}\n\t\t} else {\n\t\t\tif w.FormatFieldName == nil {\n\t\t\t\tfn = consoleDefaultFormatFieldName(w.NoColor)\n\t\t\t} else {\n\t\t\t\tfn = w.FormatFieldName\n\t\t\t}\n\n\t\t\tif w.FormatFieldValue == nil {\n\t\t\t\tfv = consoleDefaultFormatFieldValue\n\t\t\t} else {\n\t\t\t\tfv = w.FormatFieldValue\n\t\t\t}\n\t\t}\n\n\t\tbuf.WriteString(fn(field))\n\n\t\tswitch fValue := evt[field].(type) {\n\t\tcase string:\n\t\t\tif needsQuote(fValue) {\n\t\t\t\tbuf.WriteString(fv(strconv.Quote(fValue)))\n\t\t\t} else {\n\t\t\t\tbuf.WriteString(fv(fValue))\n\t\t\t}\n\t\tcase json.Number:\n\t\t\tbuf.WriteString(fv(fValue))\n\t\tdefault:\n\t\t\tb, err := InterfaceMarshalFunc(fValue)\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintf(buf, colorize(\"[error: %v]\", colorRed, w.NoColor), err)\n\t\t\t} else {\n\t\t\t\tfmt.Fprint(buf, fv(b))\n\t\t\t}\n\t\t}\n\n\t\tif i < len(fields)-1 { // Skip space for last field\n\t\t\tbuf.WriteByte(' ')\n\t\t}\n\t}\n}\n\n// writePart appends a formatted part to buf.\nfunc (w ConsoleWriter) writePart(buf *bytes.Buffer, evt map[string]interface{}, p string) {\n\tvar f Formatter\n\tvar fvn FormatterByFieldName\n\n\tif len(w.PartsExclude) > 0 {\n\t\tfor _, exclude := range w.PartsExclude {\n\t\t\tif exclude == p {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\tswitch p {\n\tcase LevelFieldName:\n\t\tif w.FormatLevel == nil {\n\t\t\tf = consoleDefaultFormatLevel(w.NoColor)\n\t\t} else {\n\t\t\tf = w.FormatLevel\n\t\t}\n\tcase TimestampFieldName:\n\t\tif w.FormatTimestamp == nil {\n\t\t\tf = consoleDefaultFormatTimestamp(w.TimeFormat, w.TimeLocation, w.NoColor)\n\t\t} else {\n\t\t\tf = w.FormatTimestamp\n\t\t}\n\tcase MessageFieldName:\n\t\tif w.FormatMessage == nil {\n\t\t\tf = consoleDefaultFormatMessage(w.NoColor, evt[LevelFieldName])\n\t\t} else {\n\t\t\tf = w.FormatMessage\n\t\t}\n\tcase CallerFieldName:\n\t\tif w.FormatCaller == nil {\n\t\t\tf = consoleDefaultFormatCaller(w.NoColor)\n\t\t} else {\n\t\t\tf = w.FormatCaller\n\t\t}\n\tdefault:\n\t\tif w.FormatPartValueByName != nil {\n\t\t\tfvn = w.FormatPartValueByName\n\t\t} else if w.FormatFieldValue != nil {\n\t\t\tf = w.FormatFieldValue\n\t\t} else {\n\t\t\tf = consoleDefaultFormatFieldValue\n\t\t}\n\t}\n\n\tvar s string\n\tif f == nil {\n\t\ts = fvn(evt[p], p)\n\t} else {\n\t\ts = f(evt[p])\n\t}\n\n\tif len(s) > 0 {\n\t\tif buf.Len() > 0 {\n\t\t\tbuf.WriteByte(' ') // Write space only if not the first part\n\t\t}\n\t\tbuf.WriteString(s)\n\t}\n}\n\n// orderFields takes an array of field names and an array representing field order\n// and returns an array with any ordered fields at the beginning, in order,\n// and the remaining fields after in their original order.\nfunc (w ConsoleWriter) orderFields(fields []string) {\n\tif w.fieldIsOrdered == nil {\n\t\tw.fieldIsOrdered = make(map[string]int)\n\t\tfor i, fieldName := range w.FieldsOrder {\n\t\t\tw.fieldIsOrdered[fieldName] = i\n\t\t}\n\t}\n\tsort.Slice(fields, func(i, j int) bool {\n\t\tii, iOrdered := w.fieldIsOrdered[fields[i]]\n\t\tjj, jOrdered := w.fieldIsOrdered[fields[j]]\n\t\tif iOrdered && jOrdered {\n\t\t\treturn ii < jj\n\t\t}\n\t\tif iOrdered {\n\t\t\treturn true\n\t\t}\n\t\tif jOrdered {\n\t\t\treturn false\n\t\t}\n\t\treturn fields[i] < fields[j]\n\t})\n}\n\n// needsQuote returns true when the string s should be quoted in output.\nfunc needsQuote(s string) bool {\n\tfor i := range s {\n\t\tif s[i] < 0x20 || s[i] > 0x7e || s[i] == ' ' || s[i] == '\\\\' || s[i] == '\"' {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// colorize returns the string s wrapped in ANSI code c, unless disabled is true or c is 0.\nfunc colorize(s interface{}, c int, disabled bool) string {\n\te := os.Getenv(\"NO_COLOR\")\n\tif e != \"\" || c == 0 {\n\t\tdisabled = true\n\t}\n\n\tif disabled {\n\t\treturn fmt.Sprintf(\"%s\", s)\n\t}\n\treturn fmt.Sprintf(\"\\x1b[%dm%v\\x1b[0m\", c, s)\n}\n\n// ----- DEFAULT FORMATTERS ---------------------------------------------------\n\nfunc consoleDefaultPartsOrder() []string {\n\treturn []string{\n\t\tTimestampFieldName,\n\t\tLevelFieldName,\n\t\tCallerFieldName,\n\t\tMessageFieldName,\n\t}\n}\n\nfunc consoleDefaultFormatTimestamp(timeFormat string, location *time.Location, noColor bool) Formatter {\n\tif timeFormat == \"\" {\n\t\ttimeFormat = consoleDefaultTimeFormat\n\t}\n\tif location == nil {\n\t\tlocation = time.Local\n\t}\n\n\treturn func(i interface{}) string {\n\t\tt := \"<nil>\"\n\t\tswitch tt := i.(type) {\n\t\tcase string:\n\t\t\tts, err := time.ParseInLocation(TimeFieldFormat, tt, location)\n\t\t\tif err != nil {\n\t\t\t\tt = tt\n\t\t\t} else {\n\t\t\t\tt = ts.In(location).Format(timeFormat)\n\t\t\t}\n\t\tcase json.Number:\n\t\t\ti, err := tt.Int64()\n\t\t\tif err != nil {\n\t\t\t\tt = tt.String()\n\t\t\t} else {\n\t\t\t\tvar sec, nsec int64\n\n\t\t\t\tswitch TimeFieldFormat {\n\t\t\t\tcase TimeFormatUnixNano:\n\t\t\t\t\tsec, nsec = 0, i\n\t\t\t\tcase TimeFormatUnixMicro:\n\t\t\t\t\tsec, nsec = 0, int64(time.Duration(i)*time.Microsecond)\n\t\t\t\tcase TimeFormatUnixMs:\n\t\t\t\t\tsec, nsec = 0, int64(time.Duration(i)*time.Millisecond)\n\t\t\t\tdefault:\n\t\t\t\t\tsec, nsec = i, 0\n\t\t\t\t}\n\n\t\t\t\tts := time.Unix(sec, nsec)\n\t\t\t\tt = ts.In(location).Format(timeFormat)\n\t\t\t}\n\t\t}\n\t\treturn colorize(t, colorDarkGray, noColor)\n\t}\n}\n\nfunc stripLevel(ll string) string {\n\tif len(ll) == 0 {\n\t\treturn unknownLevel\n\t}\n\tif len(ll) > 3 {\n\t\tll = ll[:3]\n\t}\n\treturn strings.ToUpper(ll)\n}\n\nfunc consoleDefaultFormatLevel(noColor bool) Formatter {\n\treturn func(i interface{}) string {\n\t\tif ll, ok := i.(string); ok {\n\t\t\tlevel, _ := ParseLevel(ll)\n\t\t\tfl, ok := FormattedLevels[level]\n\t\t\tif ok {\n\t\t\t\treturn colorize(fl, LevelColors[level], noColor)\n\t\t\t}\n\t\t\treturn stripLevel(ll)\n\t\t}\n\t\tif i == nil {\n\t\t\treturn unknownLevel\n\t\t}\n\t\treturn stripLevel(fmt.Sprintf(\"%s\", i))\n\t}\n}\n\nfunc consoleDefaultFormatCaller(noColor bool) Formatter {\n\treturn func(i interface{}) string {\n\t\tvar c string\n\t\tif cc, ok := i.(string); ok {\n\t\t\tc = cc\n\t\t}\n\t\tif len(c) > 0 {\n\t\t\tif cwd, err := os.Getwd(); err == nil {\n\t\t\t\tif rel, err := filepath.Rel(cwd, c); err == nil {\n\t\t\t\t\tc = rel\n\t\t\t\t}\n\t\t\t}\n\t\t\tc = colorize(c, colorBold, noColor) + colorize(\" >\", colorCyan, noColor)\n\t\t}\n\t\treturn c\n\t}\n}\n\nfunc consoleDefaultFormatMessage(noColor bool, level interface{}) Formatter {\n\treturn func(i interface{}) string {\n\t\tif i == nil || i == \"\" {\n\t\t\treturn \"\"\n\t\t}\n\t\tswitch level {\n\t\tcase LevelInfoValue, LevelWarnValue, LevelErrorValue, LevelFatalValue, LevelPanicValue:\n\t\t\treturn colorize(fmt.Sprintf(\"%s\", i), colorBold, noColor)\n\t\tdefault:\n\t\t\treturn fmt.Sprintf(\"%s\", i)\n\t\t}\n\t}\n}\n\nfunc consoleDefaultFormatFieldName(noColor bool) Formatter {\n\treturn func(i interface{}) string {\n\t\treturn colorize(fmt.Sprintf(\"%s=\", i), colorCyan, noColor)\n\t}\n}\n\nfunc consoleDefaultFormatFieldValue(i interface{}) string {\n\treturn fmt.Sprintf(\"%s\", i)\n}\n\nfunc consoleDefaultFormatErrFieldName(noColor bool) Formatter {\n\treturn func(i interface{}) string {\n\t\treturn colorize(fmt.Sprintf(\"%s=\", i), colorCyan, noColor)\n\t}\n}\n\nfunc consoleDefaultFormatErrFieldValue(noColor bool) Formatter {\n\treturn func(i interface{}) string {\n\t\treturn colorize(colorize(fmt.Sprintf(\"%s\", i), colorBold, noColor), colorRed, noColor)\n\t}\n}\n"
  },
  {
    "path": "console_test.go",
    "content": "package zerolog_test\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/rs/zerolog\"\n)\n\nfunc ExampleConsoleWriter() {\n\tlog := zerolog.New(zerolog.ConsoleWriter{Out: os.Stdout, NoColor: true})\n\n\tlog.Info().Str(\"foo\", \"bar\").Msg(\"Hello World\")\n\t// Output: <nil> INF Hello World foo=bar\n}\n\nfunc ExampleConsoleWriter_customFormatters() {\n\tout := zerolog.ConsoleWriter{Out: os.Stdout, NoColor: true}\n\tout.FormatLevel = func(i interface{}) string { return strings.ToUpper(fmt.Sprintf(\"%-6s|\", i)) }\n\tout.FormatFieldName = func(i interface{}) string { return fmt.Sprintf(\"%s:\", i) }\n\tout.FormatFieldValue = func(i interface{}) string { return strings.ToUpper(fmt.Sprintf(\"%s\", i)) }\n\tlog := zerolog.New(out)\n\n\tlog.Info().Str(\"foo\", \"bar\").Msg(\"Hello World\")\n\t// Output: <nil> INFO  | Hello World foo:BAR\n}\n\nfunc ExampleConsoleWriter_partValueFormatter() {\n\tout := zerolog.ConsoleWriter{Out: os.Stdout, NoColor: true,\n\t\tPartsOrder:    []string{\"level\", \"one\", \"two\", \"three\", \"message\"},\n\t\tFieldsExclude: []string{\"one\", \"two\", \"three\"}}\n\tout.FormatLevel = func(i interface{}) string { return strings.ToUpper(fmt.Sprintf(\"%-6s\", i)) }\n\tout.FormatFieldName = func(i interface{}) string { return fmt.Sprintf(\"%s:\", i) }\n\tout.FormatPartValueByName = func(i interface{}, s string) string {\n\t\tvar ret string\n\t\tswitch s {\n\t\tcase \"one\":\n\t\t\tret = strings.ToUpper(fmt.Sprintf(\"%s\", i))\n\t\tcase \"two\":\n\t\t\tret = strings.ToLower(fmt.Sprintf(\"%s\", i))\n\t\tcase \"three\":\n\t\t\tret = strings.ToLower(fmt.Sprintf(\"(%s)\", i))\n\t\t}\n\t\treturn ret\n\t}\n\tlog := zerolog.New(out)\n\n\tlog.Info().Str(\"foo\", \"bar\").\n\t\tStr(\"two\", \"TEST_TWO\").\n\t\tStr(\"one\", \"test_one\").\n\t\tStr(\"three\", \"test_three\").\n\t\tMsg(\"Hello World\")\n\t// Output: INFO   TEST_ONE test_two (test_three) Hello World foo:bar\n}\n\nfunc ExampleNewConsoleWriter() {\n\tout := zerolog.NewConsoleWriter()\n\tout.NoColor = true // For testing purposes only\n\tlog := zerolog.New(out)\n\n\tlog.Debug().Str(\"foo\", \"bar\").Msg(\"Hello World\")\n\t// Output: <nil> DBG Hello World foo=bar\n}\n\nfunc ExampleNewConsoleWriter_customFormatters() {\n\tout := zerolog.NewConsoleWriter(\n\t\tfunc(w *zerolog.ConsoleWriter) {\n\t\t\t// Customize time format\n\t\t\tw.TimeFormat = time.RFC822\n\t\t\t// Customize level formatting\n\t\t\tw.FormatLevel = func(i interface{}) string { return strings.ToUpper(fmt.Sprintf(\"[%-5s]\", i)) }\n\t\t},\n\t)\n\tout.NoColor = true // For testing purposes only\n\n\tlog := zerolog.New(out)\n\n\tlog.Info().Str(\"foo\", \"bar\").Msg(\"Hello World\")\n\t// Output: <nil> [INFO ] Hello World foo=bar\n}\n\nfunc TestConsoleLogger(t *testing.T) {\n\tt.Run(\"Numbers\", func(t *testing.T) {\n\t\tbuf := &bytes.Buffer{}\n\t\tlog := zerolog.New(zerolog.ConsoleWriter{Out: buf, NoColor: true})\n\t\tlog.Info().\n\t\t\tFloat64(\"float\", 1.23).\n\t\t\tUint64(\"small\", 123).\n\t\t\tUint64(\"big\", 1152921504606846976).\n\t\t\tMsg(\"msg\")\n\t\tif got, want := strings.TrimSpace(buf.String()), \"<nil> INF msg big=1152921504606846976 float=1.23 small=123\"; got != want {\n\t\t\tt.Errorf(\"\\ngot:\\n%s\\nwant:\\n%s\", got, want)\n\t\t}\n\t})\n}\n\nfunc TestConsoleWriter(t *testing.T) {\n\tt.Run(\"Default field formatter\", func(t *testing.T) {\n\t\tbuf := &bytes.Buffer{}\n\t\tw := zerolog.ConsoleWriter{Out: buf, NoColor: true, PartsOrder: []string{\"foo\"}}\n\n\t\t_, err := w.Write([]byte(`{\"foo\": \"DEFAULT\"}`))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error when writing output: %s\", err)\n\t\t}\n\n\t\texpectedOutput := \"DEFAULT foo=DEFAULT\\n\"\n\t\tactualOutput := buf.String()\n\t\tif actualOutput != expectedOutput {\n\t\t\tt.Errorf(\"Unexpected output %q, want: %q\", actualOutput, expectedOutput)\n\t\t}\n\t})\n\n\tt.Run(\"Write colorized\", func(t *testing.T) {\n\t\tbuf := &bytes.Buffer{}\n\t\tw := zerolog.ConsoleWriter{Out: buf, NoColor: false}\n\n\t\t_, err := w.Write([]byte(`{\"level\": \"warn\", \"message\": \"Foobar\"}`))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error when writing output: %s\", err)\n\t\t}\n\n\t\texpectedOutput := \"\\x1b[90m<nil>\\x1b[0m \\x1b[33mWRN\\x1b[0m \\x1b[1mFoobar\\x1b[0m\\n\"\n\t\tactualOutput := buf.String()\n\t\tif actualOutput != expectedOutput {\n\t\t\tt.Errorf(\"Unexpected output %q, want: %q\", actualOutput, expectedOutput)\n\t\t}\n\t})\n\n\tt.Run(\"NO_COLOR = true\", func(t *testing.T) {\n\t\tos.Setenv(\"NO_COLOR\", \"anything\")\n\n\t\tbuf := &bytes.Buffer{}\n\t\tw := zerolog.ConsoleWriter{Out: buf}\n\n\t\t_, err := w.Write([]byte(`{\"level\": \"warn\", \"message\": \"Foobar\"}`))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error when writing output: %s\", err)\n\t\t}\n\n\t\texpectedOutput := \"<nil> WRN Foobar\\n\"\n\t\tactualOutput := buf.String()\n\t\tif actualOutput != expectedOutput {\n\t\t\tt.Errorf(\"Unexpected output %q, want: %q\", actualOutput, expectedOutput)\n\t\t}\n\t\tos.Unsetenv(\"NO_COLOR\")\n\t})\n\n\tt.Run(\"Write fields\", func(t *testing.T) {\n\t\tbuf := &bytes.Buffer{}\n\t\tw := zerolog.ConsoleWriter{Out: buf, NoColor: true}\n\n\t\tts := time.Unix(0, 0)\n\t\td := ts.UTC().Format(time.RFC3339)\n\t\t_, err := w.Write([]byte(`{\"time\": \"` + d + `\", \"level\": \"debug\", \"message\": \"Foobar\", \"foo\": \"bar\"}`))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error when writing output: %s\", err)\n\t\t}\n\n\t\texpectedOutput := ts.Format(time.Kitchen) + \" DBG Foobar foo=bar\\n\"\n\t\tactualOutput := buf.String()\n\t\tif actualOutput != expectedOutput {\n\t\t\tt.Errorf(\"Unexpected output %q, want: %q\", actualOutput, expectedOutput)\n\t\t}\n\t})\n\n\tt.Run(\"Unix timestamp input format\", func(t *testing.T) {\n\t\tof := zerolog.TimeFieldFormat\n\t\tdefer func() {\n\t\t\tzerolog.TimeFieldFormat = of\n\t\t}()\n\t\tzerolog.TimeFieldFormat = zerolog.TimeFormatUnix\n\n\t\tbuf := &bytes.Buffer{}\n\t\tw := zerolog.ConsoleWriter{Out: buf, TimeFormat: time.StampMilli, NoColor: true}\n\n\t\t_, err := w.Write([]byte(`{\"time\": 1234, \"level\": \"debug\", \"message\": \"Foobar\", \"foo\": \"bar\"}`))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error when writing output: %s\", err)\n\t\t}\n\n\t\texpectedOutput := time.Unix(1234, 0).Format(time.StampMilli) + \" DBG Foobar foo=bar\\n\"\n\t\tactualOutput := buf.String()\n\t\tif actualOutput != expectedOutput {\n\t\t\tt.Errorf(\"Unexpected output %q, want: %q\", actualOutput, expectedOutput)\n\t\t}\n\t})\n\n\tt.Run(\"Unix timestamp ms input format\", func(t *testing.T) {\n\t\tof := zerolog.TimeFieldFormat\n\t\tdefer func() {\n\t\t\tzerolog.TimeFieldFormat = of\n\t\t}()\n\t\tzerolog.TimeFieldFormat = zerolog.TimeFormatUnixMs\n\n\t\tbuf := &bytes.Buffer{}\n\t\tw := zerolog.ConsoleWriter{Out: buf, TimeFormat: time.StampMilli, NoColor: true}\n\n\t\t_, err := w.Write([]byte(`{\"time\": 1234567, \"level\": \"debug\", \"message\": \"Foobar\", \"foo\": \"bar\"}`))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error when writing output: %s\", err)\n\t\t}\n\n\t\texpectedOutput := time.Unix(1234, 567000000).Format(time.StampMilli) + \" DBG Foobar foo=bar\\n\"\n\t\tactualOutput := buf.String()\n\t\tif actualOutput != expectedOutput {\n\t\t\tt.Errorf(\"Unexpected output %q, want: %q\", actualOutput, expectedOutput)\n\t\t}\n\t})\n\n\tt.Run(\"Unix timestamp us input format\", func(t *testing.T) {\n\t\tof := zerolog.TimeFieldFormat\n\t\tdefer func() {\n\t\t\tzerolog.TimeFieldFormat = of\n\t\t}()\n\t\tzerolog.TimeFieldFormat = zerolog.TimeFormatUnixMicro\n\n\t\tbuf := &bytes.Buffer{}\n\t\tw := zerolog.ConsoleWriter{Out: buf, TimeFormat: time.StampMicro, NoColor: true}\n\n\t\t_, err := w.Write([]byte(`{\"time\": 1234567891, \"level\": \"debug\", \"message\": \"Foobar\", \"foo\": \"bar\"}`))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error when writing output: %s\", err)\n\t\t}\n\n\t\texpectedOutput := time.Unix(1234, 567891000).Format(time.StampMicro) + \" DBG Foobar foo=bar\\n\"\n\t\tactualOutput := buf.String()\n\t\tif actualOutput != expectedOutput {\n\t\t\tt.Errorf(\"Unexpected output %q, want: %q\", actualOutput, expectedOutput)\n\t\t}\n\t})\n\n\tt.Run(\"No message field\", func(t *testing.T) {\n\t\tbuf := &bytes.Buffer{}\n\t\tw := zerolog.ConsoleWriter{Out: buf, NoColor: true}\n\n\t\t_, err := w.Write([]byte(`{\"level\": \"debug\", \"foo\": \"bar\"}`))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error when writing output: %s\", err)\n\t\t}\n\n\t\texpectedOutput := \"<nil> DBG foo=bar\\n\"\n\t\tactualOutput := buf.String()\n\t\tif actualOutput != expectedOutput {\n\t\t\tt.Errorf(\"Unexpected output %q, want: %q\", actualOutput, expectedOutput)\n\t\t}\n\t})\n\n\tt.Run(\"No level field\", func(t *testing.T) {\n\t\tbuf := &bytes.Buffer{}\n\t\tw := zerolog.ConsoleWriter{Out: buf, NoColor: true}\n\n\t\t_, err := w.Write([]byte(`{\"message\": \"Foobar\", \"foo\": \"bar\"}`))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error when writing output: %s\", err)\n\t\t}\n\n\t\texpectedOutput := \"<nil> ??? Foobar foo=bar\\n\"\n\t\tactualOutput := buf.String()\n\t\tif actualOutput != expectedOutput {\n\t\t\tt.Errorf(\"Unexpected output %q, want: %q\", actualOutput, expectedOutput)\n\t\t}\n\t})\n\n\tt.Run(\"Write colorized fields\", func(t *testing.T) {\n\t\tbuf := &bytes.Buffer{}\n\t\tw := zerolog.ConsoleWriter{Out: buf, NoColor: false}\n\n\t\t_, err := w.Write([]byte(`{\"level\": \"warn\", \"message\": \"Foobar\", \"foo\": \"bar\"}`))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error when writing output: %s\", err)\n\t\t}\n\n\t\texpectedOutput := \"\\x1b[90m<nil>\\x1b[0m \\x1b[33mWRN\\x1b[0m \\x1b[1mFoobar\\x1b[0m \\x1b[36mfoo=\\x1b[0mbar\\n\"\n\t\tactualOutput := buf.String()\n\t\tif actualOutput != expectedOutput {\n\t\t\tt.Errorf(\"Unexpected output %q, want: %q\", actualOutput, expectedOutput)\n\t\t}\n\t})\n\n\tt.Run(\"Write error field\", func(t *testing.T) {\n\t\tbuf := &bytes.Buffer{}\n\t\tw := zerolog.ConsoleWriter{Out: buf, NoColor: true}\n\n\t\tts := time.Unix(0, 0)\n\t\td := ts.UTC().Format(time.RFC3339)\n\t\tevt := `{\"time\": \"` + d + `\", \"level\": \"error\", \"message\": \"Foobar\", \"aaa\": \"bbb\", \"error\": \"Error\"}`\n\t\t// t.Log(evt)\n\n\t\t_, err := w.Write([]byte(evt))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error when writing output: %s\", err)\n\t\t}\n\n\t\texpectedOutput := ts.Format(time.Kitchen) + \" ERR Foobar error=Error aaa=bbb\\n\"\n\t\tactualOutput := buf.String()\n\t\tif actualOutput != expectedOutput {\n\t\t\tt.Errorf(\"Unexpected output %q, want: %q\", actualOutput, expectedOutput)\n\t\t}\n\t})\n\n\tt.Run(\"Write caller field\", func(t *testing.T) {\n\t\tbuf := &bytes.Buffer{}\n\t\tw := zerolog.ConsoleWriter{Out: buf, NoColor: true}\n\n\t\tcwd, err := os.Getwd()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Cannot get working directory: %s\", err)\n\t\t}\n\n\t\tts := time.Unix(0, 0)\n\t\td := ts.UTC().Format(time.RFC3339)\n\n\t\tfields := map[string]interface{}{\n\t\t\t\"time\":    d,\n\t\t\t\"level\":   \"debug\",\n\t\t\t\"message\": \"Foobar\",\n\t\t\t\"foo\":     \"bar\",\n\t\t\t\"caller\":  filepath.Join(cwd, \"foo\", \"bar.go\"),\n\t\t}\n\n\t\tevt, err := json.Marshal(fields)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Cannot marshal fields: %s\", err)\n\t\t}\n\n\t\t_, err = w.Write(evt)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error when writing output: %s\", err)\n\t\t}\n\n\t\t// Define the expected output with forward slashes\n\t\texpectedOutput := ts.Format(time.Kitchen) + \" DBG foo/bar.go > Foobar foo=bar\\n\"\n\n\t\t// Get the actual output and normalize path separators to forward slashes\n\t\tactualOutput := buf.String()\n\t\tactualOutput = strings.ReplaceAll(actualOutput, string(os.PathSeparator), \"/\")\n\n\t\t// Compare the normalized actual output to the expected output\n\t\tif actualOutput != expectedOutput {\n\t\t\tt.Errorf(\"Unexpected output %q, want: %q\", actualOutput, expectedOutput)\n\t\t}\n\t})\n\n\tt.Run(\"Write JSON field\", func(t *testing.T) {\n\t\tbuf := &bytes.Buffer{}\n\t\tw := zerolog.ConsoleWriter{Out: buf, NoColor: true}\n\n\t\tevt := `{\"level\": \"debug\", \"message\": \"Foobar\", \"foo\": [1, 2, 3], \"bar\": true}`\n\t\t// t.Log(evt)\n\n\t\t_, err := w.Write([]byte(evt))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error when writing output: %s\", err)\n\t\t}\n\n\t\texpectedOutput := \"<nil> DBG Foobar bar=true foo=[1,2,3]\\n\"\n\t\tactualOutput := buf.String()\n\t\tif actualOutput != expectedOutput {\n\t\t\tt.Errorf(\"Unexpected output %q, want: %q\", actualOutput, expectedOutput)\n\t\t}\n\t})\n\n\tt.Run(\"With an extra 'level' field\", func(t *testing.T) {\n\t\tt.Run(\"malformed string\", func(t *testing.T) {\n\t\t\tcases := []struct {\n\t\t\t\tfield  string\n\t\t\t\toutput string\n\t\t\t}{\n\t\t\t\t{\"\", \"<nil> ??? Hello World foo=bar\\n\"},\n\t\t\t\t{\"-\", \"<nil> - Hello World foo=bar\\n\"},\n\t\t\t\t{\"1\", \"<nil> \" + zerolog.FormattedLevels[1] + \" Hello World foo=bar\\n\"},\n\t\t\t\t{\"a\", \"<nil> A Hello World foo=bar\\n\"},\n\t\t\t\t{\"12\", \"<nil> 12 Hello World foo=bar\\n\"},\n\t\t\t\t{\"a2\", \"<nil> A2 Hello World foo=bar\\n\"},\n\t\t\t\t{\"2a\", \"<nil> 2A Hello World foo=bar\\n\"},\n\t\t\t\t{\"ab\", \"<nil> AB Hello World foo=bar\\n\"},\n\t\t\t\t{\"12a\", \"<nil> 12A Hello World foo=bar\\n\"},\n\t\t\t\t{\"a12\", \"<nil> A12 Hello World foo=bar\\n\"},\n\t\t\t\t{\"abc\", \"<nil> ABC Hello World foo=bar\\n\"},\n\t\t\t\t{\"123\", \"<nil> 123 Hello World foo=bar\\n\"},\n\t\t\t\t{\"abcd\", \"<nil> ABC Hello World foo=bar\\n\"},\n\t\t\t\t{\"1234\", \"<nil> 123 Hello World foo=bar\\n\"},\n\t\t\t\t{\"123d\", \"<nil> 123 Hello World foo=bar\\n\"},\n\t\t\t\t{\"01\", \"<nil> \" + zerolog.FormattedLevels[1] + \" Hello World foo=bar\\n\"},\n\t\t\t\t{\"001\", \"<nil> \" + zerolog.FormattedLevels[1] + \" Hello World foo=bar\\n\"},\n\t\t\t\t{\"0001\", \"<nil> \" + zerolog.FormattedLevels[1] + \" Hello World foo=bar\\n\"},\n\t\t\t}\n\t\t\tfor i, c := range cases {\n\t\t\t\tc := c\n\t\t\t\tt.Run(fmt.Sprintf(\"case %d\", i), func(t *testing.T) {\n\t\t\t\t\tbuf := &bytes.Buffer{}\n\t\t\t\t\tout := zerolog.NewConsoleWriter()\n\t\t\t\t\tout.NoColor = true\n\t\t\t\t\tout.Out = buf\n\t\t\t\t\tlog := zerolog.New(out)\n\n\t\t\t\t\tlog.Debug().Str(\"level\", c.field).Str(\"foo\", \"bar\").Msg(\"Hello World\")\n\n\t\t\t\t\tactualOutput := buf.String()\n\t\t\t\t\tif actualOutput != c.output {\n\t\t\t\t\t\tt.Errorf(\"Unexpected output %q, want: %q\", actualOutput, c.output)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"weird value\", func(t *testing.T) {\n\t\t\tcases := []struct {\n\t\t\t\tfield  interface{}\n\t\t\t\toutput string\n\t\t\t}{\n\t\t\t\t{0, \"<nil> 0 Hello World foo=bar\\n\"},\n\t\t\t\t{1, \"<nil> 1 Hello World foo=bar\\n\"},\n\t\t\t\t{-1, \"<nil> -1 Hello World foo=bar\\n\"},\n\t\t\t\t{-3, \"<nil> -3 Hello World foo=bar\\n\"},\n\t\t\t\t{-32, \"<nil> -32 Hello World foo=bar\\n\"},\n\t\t\t\t{-321, \"<nil> -32 Hello World foo=bar\\n\"},\n\t\t\t\t{12, \"<nil> 12 Hello World foo=bar\\n\"},\n\t\t\t\t{123, \"<nil> 123 Hello World foo=bar\\n\"},\n\t\t\t\t{1234, \"<nil> 123 Hello World foo=bar\\n\"},\n\t\t\t}\n\t\t\tfor i, c := range cases {\n\t\t\t\tc := c\n\t\t\t\tt.Run(fmt.Sprintf(\"case %d\", i), func(t *testing.T) {\n\t\t\t\t\tbuf := &bytes.Buffer{}\n\t\t\t\t\tout := zerolog.NewConsoleWriter()\n\t\t\t\t\tout.NoColor = true\n\t\t\t\t\tout.Out = buf\n\t\t\t\t\tlog := zerolog.New(out)\n\n\t\t\t\t\tlog.Debug().Interface(\"level\", c.field).Str(\"foo\", \"bar\").Msg(\"Hello World\")\n\n\t\t\t\t\tactualOutput := buf.String()\n\t\t\t\t\tif actualOutput != c.output {\n\t\t\t\t\t\tt.Errorf(\"Unexpected output %q, want: %q\", actualOutput, c.output)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t})\n}\n\nfunc TestConsoleWriterConfiguration(t *testing.T) {\n\tt.Run(\"Sets TimeFormat\", func(t *testing.T) {\n\t\tbuf := &bytes.Buffer{}\n\t\tw := zerolog.ConsoleWriter{Out: buf, NoColor: true, TimeFormat: time.RFC3339}\n\n\t\tts := time.Unix(0, 0)\n\t\td := ts.UTC().Format(time.RFC3339)\n\t\tevt := `{\"time\": \"` + d + `\", \"level\": \"info\", \"message\": \"Foobar\"}`\n\n\t\t_, err := w.Write([]byte(evt))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error when writing output: %s\", err)\n\t\t}\n\n\t\texpectedOutput := ts.Format(time.RFC3339) + \" INF Foobar\\n\"\n\t\tactualOutput := buf.String()\n\t\tif actualOutput != expectedOutput {\n\t\t\tt.Errorf(\"Unexpected output %q, want: %q\", actualOutput, expectedOutput)\n\t\t}\n\t})\n\n\tt.Run(\"Sets TimeFormat and TimeLocation\", func(t *testing.T) {\n\t\tlocs := []*time.Location{time.Local, time.UTC}\n\n\t\tfor _, location := range locs {\n\t\t\tbuf := &bytes.Buffer{}\n\t\t\tw := zerolog.ConsoleWriter{\n\t\t\t\tOut:          buf,\n\t\t\t\tNoColor:      true,\n\t\t\t\tTimeFormat:   time.RFC3339,\n\t\t\t\tTimeLocation: location,\n\t\t\t}\n\n\t\t\tts := time.Unix(0, 0)\n\t\t\td := ts.UTC().Format(time.RFC3339)\n\t\t\tevt := `{\"time\": \"` + d + `\", \"level\": \"info\", \"message\": \"Foobar\"}`\n\n\t\t\t_, err := w.Write([]byte(evt))\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"Unexpected error when writing output: %s\", err)\n\t\t\t}\n\n\t\t\texpectedOutput := ts.In(location).Format(time.RFC3339) + \" INF Foobar\\n\"\n\t\t\tactualOutput := buf.String()\n\t\t\tif actualOutput != expectedOutput {\n\t\t\t\tt.Errorf(\"Unexpected output %q, want: %q (location=%s)\", actualOutput, expectedOutput, location)\n\t\t\t}\n\t\t}\n\t})\n\n\tt.Run(\"Sets PartsOrder\", func(t *testing.T) {\n\t\tbuf := &bytes.Buffer{}\n\t\tw := zerolog.ConsoleWriter{Out: buf, NoColor: true, PartsOrder: []string{\"message\", \"level\"}}\n\n\t\tevt := `{\"level\": \"info\", \"message\": \"Foobar\"}`\n\t\t_, err := w.Write([]byte(evt))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error when writing output: %s\", err)\n\t\t}\n\n\t\texpectedOutput := \"Foobar INF\\n\"\n\t\tactualOutput := buf.String()\n\t\tif actualOutput != expectedOutput {\n\t\t\tt.Errorf(\"Unexpected output %q, want: %q\", actualOutput, expectedOutput)\n\t\t}\n\t})\n\n\tt.Run(\"Sets PartsExclude\", func(t *testing.T) {\n\t\tbuf := &bytes.Buffer{}\n\t\tw := zerolog.ConsoleWriter{Out: buf, NoColor: true, PartsExclude: []string{\"time\"}}\n\n\t\td := time.Unix(0, 0).UTC().Format(time.RFC3339)\n\t\tevt := `{\"time\": \"` + d + `\", \"level\": \"info\", \"message\": \"Foobar\"}`\n\t\t_, err := w.Write([]byte(evt))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error when writing output: %s\", err)\n\t\t}\n\n\t\texpectedOutput := \"INF Foobar\\n\"\n\t\tactualOutput := buf.String()\n\t\tif actualOutput != expectedOutput {\n\t\t\tt.Errorf(\"Unexpected output %q, want: %q\", actualOutput, expectedOutput)\n\t\t}\n\t})\n\n\tt.Run(\"Sets FieldsOrder\", func(t *testing.T) {\n\t\tbuf := &bytes.Buffer{}\n\t\tw := zerolog.ConsoleWriter{Out: buf, NoColor: true, FieldsOrder: []string{\"zebra\", \"aardvark\"}}\n\n\t\tevt := `{\"level\": \"info\", \"message\": \"Zoo\", \"aardvark\": \"Able\", \"mussel\": \"Mountain\", \"zebra\": \"Zulu\"}`\n\t\t_, err := w.Write([]byte(evt))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error when writing output: %s\", err)\n\t\t}\n\n\t\texpectedOutput := \"<nil> INF Zoo zebra=Zulu aardvark=Able mussel=Mountain\\n\"\n\t\tactualOutput := buf.String()\n\t\tif actualOutput != expectedOutput {\n\t\t\tt.Errorf(\"Unexpected output %q, want: %q\", actualOutput, expectedOutput)\n\t\t}\n\t})\n\n\tt.Run(\"Sets FieldsExclude\", func(t *testing.T) {\n\t\tbuf := &bytes.Buffer{}\n\t\tw := zerolog.ConsoleWriter{Out: buf, NoColor: true, FieldsExclude: []string{\"foo\"}}\n\n\t\tevt := `{\"level\": \"info\", \"message\": \"Foobar\", \"foo\":\"bar\", \"baz\":\"quux\"}`\n\t\t_, err := w.Write([]byte(evt))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error when writing output: %s\", err)\n\t\t}\n\n\t\texpectedOutput := \"<nil> INF Foobar baz=quux\\n\"\n\t\tactualOutput := buf.String()\n\t\tif actualOutput != expectedOutput {\n\t\t\tt.Errorf(\"Unexpected output %q, want: %q\", actualOutput, expectedOutput)\n\t\t}\n\t})\n\n\tt.Run(\"Sets FormatExtra\", func(t *testing.T) {\n\t\tbuf := &bytes.Buffer{}\n\t\tw := zerolog.ConsoleWriter{\n\t\t\tOut: buf, NoColor: true, PartsOrder: []string{\"level\", \"message\"},\n\t\t\tFormatExtra: func(evt map[string]interface{}, buf *bytes.Buffer) error {\n\t\t\t\tbuf.WriteString(\"\\nAdditional stacktrace\")\n\t\t\t\treturn nil\n\t\t\t},\n\t\t}\n\n\t\tevt := `{\"level\": \"info\", \"message\": \"Foobar\"}`\n\t\t_, err := w.Write([]byte(evt))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error when writing output: %s\", err)\n\t\t}\n\n\t\texpectedOutput := \"INF Foobar\\nAdditional stacktrace\\n\"\n\t\tactualOutput := buf.String()\n\t\tif actualOutput != expectedOutput {\n\t\t\tt.Errorf(\"Unexpected output %q, want: %q\", actualOutput, expectedOutput)\n\t\t}\n\t})\n\n\tt.Run(\"Sets FormatPrepare\", func(t *testing.T) {\n\t\tbuf := &bytes.Buffer{}\n\t\tw := zerolog.ConsoleWriter{\n\t\t\tOut: buf, NoColor: true, PartsOrder: []string{\"level\", \"message\"},\n\t\t\tFormatPrepare: func(evt map[string]interface{}) error {\n\t\t\t\tevt[\"message\"] = fmt.Sprintf(\"msg=%s\", evt[\"message\"])\n\t\t\t\treturn nil\n\t\t\t},\n\t\t}\n\n\t\tevt := `{\"level\": \"info\", \"message\": \"Foobar\"}`\n\t\t_, err := w.Write([]byte(evt))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error when writing output: %s\", err)\n\t\t}\n\n\t\texpectedOutput := \"INF msg=Foobar\\n\"\n\t\tactualOutput := buf.String()\n\t\tif actualOutput != expectedOutput {\n\t\t\tt.Errorf(\"Unexpected output %q, want: %q\", actualOutput, expectedOutput)\n\t\t}\n\t})\n\n\tt.Run(\"Uses local time for console writer without time zone\", func(t *testing.T) {\n\t\t// Regression test for issue #483 (check there for more details)\n\n\t\ttimeFormat := \"2006-01-02 15:04:05\"\n\t\texpectedOutput := \"2022-10-20 20:24:50 INF Foobar\\n\"\n\t\tevt := `{\"time\": \"2022-10-20 20:24:50\", \"level\": \"info\", \"message\": \"Foobar\"}`\n\n\t\tof := zerolog.TimeFieldFormat\n\t\tdefer func() {\n\t\t\tzerolog.TimeFieldFormat = of\n\t\t}()\n\t\tzerolog.TimeFieldFormat = timeFormat\n\n\t\tbuf := &bytes.Buffer{}\n\t\tw := zerolog.ConsoleWriter{Out: buf, NoColor: true, TimeFormat: timeFormat}\n\t\t_, err := w.Write([]byte(evt))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error when writing output: %s\", err)\n\t\t}\n\n\t\tactualOutput := buf.String()\n\t\tif actualOutput != expectedOutput {\n\t\t\tt.Errorf(\"Unexpected output %q, want: %q\", actualOutput, expectedOutput)\n\t\t}\n\t})\n}\n\nfunc BenchmarkConsoleWriter(b *testing.B) {\n\tb.ResetTimer()\n\tb.ReportAllocs()\n\n\tvar msg = []byte(`{\"level\": \"info\", \"foo\": \"bar\", \"message\": \"HELLO\", \"time\": \"1990-01-01\"}`)\n\n\tw := zerolog.ConsoleWriter{Out: io.Discard, NoColor: false}\n\n\tfor i := 0; i < b.N; i++ {\n\t\tw.Write(msg)\n\t}\n}\n"
  },
  {
    "path": "context.go",
    "content": "package zerolog\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"math\"\n\t\"net\"\n\t\"time\"\n)\n\n// Context configures a new sub-logger with contextual fields.\ntype Context struct {\n\tl Logger\n}\n\n// Logger returns the logger with the context previously set.\nfunc (c Context) Logger() Logger {\n\treturn c.l\n}\n\n// Fields is a helper function to use a map or slice to set fields using type assertion.\n// Only map[string]interface{} and []interface{} are accepted. []interface{} must\n// alternate string keys and arbitrary values, and extraneous ones are ignored.\nfunc (c Context) Fields(fields interface{}) Context {\n\tc.l.context = appendFields(c.l.context, fields, c.l.stack, c.l.ctx, c.l.hooks)\n\treturn c\n}\n\n// Dict adds the field key with the dict to the logger context.\nfunc (c Context) Dict(key string, dict *Event) Context {\n\tdict.buf = enc.AppendEndMarker(dict.buf)\n\tc.l.context = append(enc.AppendKey(c.l.context, key), dict.buf...)\n\tputEvent(dict)\n\treturn c\n}\n\n// CreateDict creates an Event to be used with the Context.Dict method.\n// It preserves the stack, hooks, and context from the logger.\n// Call usual field methods like Str, Int etc to add fields to this\n// event and give it as argument the Context.Dict method.\nfunc (c Context) CreateDict() *Event {\n\treturn newEvent(nil, DebugLevel, c.l.stack, c.l.ctx, c.l.hooks)\n}\n\n// CreateArray creates an Array to be used with the Context.Array method.\n// It preserves the stack, hooks, and context from the logger.\n// Call usual field methods like Str, Int etc to add elements to this\n// array and give it as argument the Context.Array method.\nfunc (c Context) CreateArray() *Array {\n\ta := Arr()\n\ta.stack = c.l.stack\n\ta.ctx = c.l.ctx\n\ta.ch = c.l.hooks\n\treturn a\n}\n\n// Array adds the field key with an array to the event context.\n// Use c.CreateArray() to create the array or pass a type that\n// implement the LogArrayMarshaler interface.\nfunc (c Context) Array(key string, arr LogArrayMarshaler) Context {\n\tc.l.context = enc.AppendKey(c.l.context, key)\n\tif arr, ok := arr.(*Array); ok {\n\t\tc.l.context = arr.write(c.l.context)\n\t\treturn c\n\t}\n\ta := c.CreateArray()\n\tarr.MarshalZerologArray(a)\n\tc.l.context = a.write(c.l.context)\n\treturn c\n}\n\n// Object marshals an object that implement the LogObjectMarshaler interface.\nfunc (c Context) Object(key string, obj LogObjectMarshaler) Context {\n\te := c.l.scratchEvent()\n\te.Object(key, obj)\n\tc.l.context = enc.AppendObjectData(c.l.context, e.buf)\n\tputEvent(e)\n\treturn c\n}\n\n// Object marshals an object that implement the LogObjectMarshaler interface.\nfunc (c Context) Objects(key string, objs []LogObjectMarshaler) Context {\n\te := c.l.scratchEvent()\n\te.Objects(key, objs)\n\tc.l.context = enc.AppendObjectData(c.l.context, e.buf)\n\tputEvent(e)\n\treturn c\n}\n\n// EmbedObject marshals and Embeds an object that implement the LogObjectMarshaler interface.\nfunc (c Context) EmbedObject(obj LogObjectMarshaler) Context {\n\te := c.l.scratchEvent()\n\te.EmbedObject(obj)\n\tc.l.context = enc.AppendObjectData(c.l.context, e.buf)\n\tputEvent(e)\n\treturn c\n}\n\n// Str adds the field key with val as a string to the logger context.\nfunc (c Context) Str(key, val string) Context {\n\tc.l.context = enc.AppendString(enc.AppendKey(c.l.context, key), val)\n\treturn c\n}\n\n// Strs adds the field key with val as a string to the logger context.\nfunc (c Context) Strs(key string, vals []string) Context {\n\tc.l.context = enc.AppendStrings(enc.AppendKey(c.l.context, key), vals)\n\treturn c\n}\n\n// Stringer adds the field key with val.String() (or null if val is nil) to the logger context.\nfunc (c Context) Stringer(key string, val fmt.Stringer) Context {\n\tif val != nil {\n\t\tc.l.context = enc.AppendString(enc.AppendKey(c.l.context, key), val.String())\n\t\treturn c\n\t}\n\n\tc.l.context = enc.AppendInterface(enc.AppendKey(c.l.context, key), nil)\n\treturn c\n}\n\n// Stringers adds the field key with vals as an array of strings by calling .String() on each entry\n// to the logger context.\nfunc (c Context) Stringers(key string, vals []fmt.Stringer) Context {\n\tif vals != nil {\n\t\tc.l.context = enc.AppendStringers(enc.AppendKey(c.l.context, key), vals)\n\t\treturn c\n\t}\n\n\tc.l.context = enc.AppendInterface(enc.AppendKey(c.l.context, key), nil)\n\treturn c\n}\n\n// Bytes adds the field key with val as a []byte to the logger context.\nfunc (c Context) Bytes(key string, val []byte) Context {\n\tc.l.context = enc.AppendBytes(enc.AppendKey(c.l.context, key), val)\n\treturn c\n}\n\n// Hex adds the field key with val as a hex string to the logger context.\nfunc (c Context) Hex(key string, val []byte) Context {\n\tc.l.context = enc.AppendHex(enc.AppendKey(c.l.context, key), val)\n\treturn c\n}\n\n// RawJSON adds already encoded JSON to context.\n//\n// No sanity check is performed on b; it must not contain carriage returns and\n// be valid JSON.\nfunc (c Context) RawJSON(key string, b []byte) Context {\n\tc.l.context = appendJSON(enc.AppendKey(c.l.context, key), b)\n\treturn c\n}\n\n// AnErr adds the field key with serialized err to the logger context.\n// If err is nil, no field is added.\nfunc (c Context) AnErr(key string, err error) Context {\n\tswitch m := ErrorMarshalFunc(err).(type) {\n\tcase nil:\n\t\treturn c\n\tcase LogObjectMarshaler:\n\t\treturn c.Object(key, m)\n\tcase error:\n\t\tif isNilValue(m) {\n\t\t\treturn c\n\t\t}\n\t\treturn c.Str(key, m.Error())\n\tcase string:\n\t\treturn c.Str(key, m)\n\tdefault:\n\t\treturn c.Interface(key, m)\n\t}\n}\n\n// Errs adds the field key with errs as an array of serialized errors to the\n// logger context.\nfunc (c Context) Errs(key string, errs []error) Context {\n\tarr := c.CreateArray().Errs(errs)\n\treturn c.Array(key, arr)\n}\n\n// Err adds the field \"error\" with serialized err to the logger context.\nfunc (c Context) Err(err error) Context {\n\tif c.l.stack && ErrorStackMarshaler != nil {\n\t\tswitch m := ErrorStackMarshaler(err).(type) {\n\t\tcase nil:\n\t\t\treturn c // do nothing with nil errors\n\t\tcase LogObjectMarshaler:\n\t\t\tc = c.Object(ErrorStackFieldName, m)\n\t\tcase error:\n\t\t\tc = c.Str(ErrorStackFieldName, m.Error())\n\t\tcase string:\n\t\t\tc = c.Str(ErrorStackFieldName, m)\n\t\tdefault:\n\t\t\tc = c.Interface(ErrorStackFieldName, m)\n\t\t}\n\t}\n\n\treturn c.AnErr(ErrorFieldName, err)\n}\n\n// Ctx adds the context.Context to the logger context. The context.Context is\n// not rendered in the error message, but is made available for hooks to use.\n// A typical use case is to extract tracing information from the\n// context.Context.\nfunc (c Context) Ctx(ctx context.Context) Context {\n\tc.l.ctx = ctx\n\treturn c\n}\n\n// Bool adds the field key with val as a bool to the logger context.\nfunc (c Context) Bool(key string, b bool) Context {\n\tc.l.context = enc.AppendBool(enc.AppendKey(c.l.context, key), b)\n\treturn c\n}\n\n// Bools adds the field key with val as a []bool to the logger context.\nfunc (c Context) Bools(key string, b []bool) Context {\n\tc.l.context = enc.AppendBools(enc.AppendKey(c.l.context, key), b)\n\treturn c\n}\n\n// Int adds the field key with i as a int to the logger context.\nfunc (c Context) Int(key string, i int) Context {\n\tc.l.context = enc.AppendInt(enc.AppendKey(c.l.context, key), i)\n\treturn c\n}\n\n// Ints adds the field key with i as a []int to the logger context.\nfunc (c Context) Ints(key string, i []int) Context {\n\tc.l.context = enc.AppendInts(enc.AppendKey(c.l.context, key), i)\n\treturn c\n}\n\n// Int8 adds the field key with i as a int8 to the logger context.\nfunc (c Context) Int8(key string, i int8) Context {\n\tc.l.context = enc.AppendInt8(enc.AppendKey(c.l.context, key), i)\n\treturn c\n}\n\n// Ints8 adds the field key with i as a []int8 to the logger context.\nfunc (c Context) Ints8(key string, i []int8) Context {\n\tc.l.context = enc.AppendInts8(enc.AppendKey(c.l.context, key), i)\n\treturn c\n}\n\n// Int16 adds the field key with i as a int16 to the logger context.\nfunc (c Context) Int16(key string, i int16) Context {\n\tc.l.context = enc.AppendInt16(enc.AppendKey(c.l.context, key), i)\n\treturn c\n}\n\n// Ints16 adds the field key with i as a []int16 to the logger context.\nfunc (c Context) Ints16(key string, i []int16) Context {\n\tc.l.context = enc.AppendInts16(enc.AppendKey(c.l.context, key), i)\n\treturn c\n}\n\n// Int32 adds the field key with i as a int32 to the logger context.\nfunc (c Context) Int32(key string, i int32) Context {\n\tc.l.context = enc.AppendInt32(enc.AppendKey(c.l.context, key), i)\n\treturn c\n}\n\n// Ints32 adds the field key with i as a []int32 to the logger context.\nfunc (c Context) Ints32(key string, i []int32) Context {\n\tc.l.context = enc.AppendInts32(enc.AppendKey(c.l.context, key), i)\n\treturn c\n}\n\n// Int64 adds the field key with i as a int64 to the logger context.\nfunc (c Context) Int64(key string, i int64) Context {\n\tc.l.context = enc.AppendInt64(enc.AppendKey(c.l.context, key), i)\n\treturn c\n}\n\n// Ints64 adds the field key with i as a []int64 to the logger context.\nfunc (c Context) Ints64(key string, i []int64) Context {\n\tc.l.context = enc.AppendInts64(enc.AppendKey(c.l.context, key), i)\n\treturn c\n}\n\n// Uint adds the field key with i as a uint to the logger context.\nfunc (c Context) Uint(key string, i uint) Context {\n\tc.l.context = enc.AppendUint(enc.AppendKey(c.l.context, key), i)\n\treturn c\n}\n\n// Uints adds the field key with i as a []uint to the logger context.\nfunc (c Context) Uints(key string, i []uint) Context {\n\tc.l.context = enc.AppendUints(enc.AppendKey(c.l.context, key), i)\n\treturn c\n}\n\n// Uint8 adds the field key with i as a uint8 to the logger context.\nfunc (c Context) Uint8(key string, i uint8) Context {\n\tc.l.context = enc.AppendUint8(enc.AppendKey(c.l.context, key), i)\n\treturn c\n}\n\n// Uints8 adds the field key with i as a []uint8 to the logger context.\nfunc (c Context) Uints8(key string, i []uint8) Context {\n\tc.l.context = enc.AppendUints8(enc.AppendKey(c.l.context, key), i)\n\treturn c\n}\n\n// Uint16 adds the field key with i as a uint16 to the logger context.\nfunc (c Context) Uint16(key string, i uint16) Context {\n\tc.l.context = enc.AppendUint16(enc.AppendKey(c.l.context, key), i)\n\treturn c\n}\n\n// Uints16 adds the field key with i as a []uint16 to the logger context.\nfunc (c Context) Uints16(key string, i []uint16) Context {\n\tc.l.context = enc.AppendUints16(enc.AppendKey(c.l.context, key), i)\n\treturn c\n}\n\n// Uint32 adds the field key with i as a uint32 to the logger context.\nfunc (c Context) Uint32(key string, i uint32) Context {\n\tc.l.context = enc.AppendUint32(enc.AppendKey(c.l.context, key), i)\n\treturn c\n}\n\n// Uints32 adds the field key with i as a []uint32 to the logger context.\nfunc (c Context) Uints32(key string, i []uint32) Context {\n\tc.l.context = enc.AppendUints32(enc.AppendKey(c.l.context, key), i)\n\treturn c\n}\n\n// Uint64 adds the field key with i as a uint64 to the logger context.\nfunc (c Context) Uint64(key string, i uint64) Context {\n\tc.l.context = enc.AppendUint64(enc.AppendKey(c.l.context, key), i)\n\treturn c\n}\n\n// Uints64 adds the field key with i as a []uint64 to the logger context.\nfunc (c Context) Uints64(key string, i []uint64) Context {\n\tc.l.context = enc.AppendUints64(enc.AppendKey(c.l.context, key), i)\n\treturn c\n}\n\n// Float32 adds the field key with f as a float32 to the logger context.\nfunc (c Context) Float32(key string, f float32) Context {\n\tc.l.context = enc.AppendFloat32(enc.AppendKey(c.l.context, key), f, FloatingPointPrecision)\n\treturn c\n}\n\n// Floats32 adds the field key with f as a []float32 to the logger context.\nfunc (c Context) Floats32(key string, f []float32) Context {\n\tc.l.context = enc.AppendFloats32(enc.AppendKey(c.l.context, key), f, FloatingPointPrecision)\n\treturn c\n}\n\n// Float64 adds the field key with f as a float64 to the logger context.\nfunc (c Context) Float64(key string, f float64) Context {\n\tc.l.context = enc.AppendFloat64(enc.AppendKey(c.l.context, key), f, FloatingPointPrecision)\n\treturn c\n}\n\n// Floats64 adds the field key with f as a []float64 to the logger context.\nfunc (c Context) Floats64(key string, f []float64) Context {\n\tc.l.context = enc.AppendFloats64(enc.AppendKey(c.l.context, key), f, FloatingPointPrecision)\n\treturn c\n}\n\ntype timestampHook struct{}\n\nfunc (ts timestampHook) Run(e *Event, level Level, msg string) {\n\te.Timestamp()\n}\n\nvar th = timestampHook{}\n\n// Timestamp adds the current local time to the logger context with the \"time\" key, formatted using zerolog.TimeFieldFormat.\n// To customize the key name, change zerolog.TimestampFieldName.\n// To customize the time format, change zerolog.TimeFieldFormat.\n//\n// NOTE: It won't dedupe the \"time\" key if the *Context has one already.\nfunc (c Context) Timestamp() Context {\n\tc.l = c.l.Hook(th)\n\treturn c\n}\n\n// Time adds the field key with t formatted as string using zerolog.TimeFieldFormat.\nfunc (c Context) Time(key string, t time.Time) Context {\n\tc.l.context = enc.AppendTime(enc.AppendKey(c.l.context, key), t, TimeFieldFormat)\n\treturn c\n}\n\n// Times adds the field key with t formatted as string using zerolog.TimeFieldFormat.\nfunc (c Context) Times(key string, t []time.Time) Context {\n\tc.l.context = enc.AppendTimes(enc.AppendKey(c.l.context, key), t, TimeFieldFormat)\n\treturn c\n}\n\n// Dur adds the field key with d divided by unit and stored as a float.\nfunc (c Context) Dur(key string, d time.Duration) Context {\n\tc.l.context = enc.AppendDuration(enc.AppendKey(c.l.context, key), d, DurationFieldUnit, DurationFieldFormat, DurationFieldInteger, FloatingPointPrecision)\n\treturn c\n}\n\n// Durs adds the field key with d divided by unit and stored as a float.\nfunc (c Context) Durs(key string, d []time.Duration) Context {\n\tc.l.context = enc.AppendDurations(enc.AppendKey(c.l.context, key), d, DurationFieldUnit, DurationFieldFormat, DurationFieldInteger, FloatingPointPrecision)\n\treturn c\n}\n\n// Interface adds the field key with obj marshaled using reflection.\nfunc (c Context) Interface(key string, i interface{}) Context {\n\tif obj, ok := i.(LogObjectMarshaler); ok {\n\t\treturn c.Object(key, obj)\n\t}\n\tc.l.context = enc.AppendInterface(enc.AppendKey(c.l.context, key), i)\n\treturn c\n}\n\n// Type adds the field key with val's type using reflection.\nfunc (c Context) Type(key string, val interface{}) Context {\n\tc.l.context = enc.AppendType(enc.AppendKey(c.l.context, key), val)\n\treturn c\n}\n\n// Any is a wrapper around Context.Interface.\nfunc (c Context) Any(key string, i interface{}) Context {\n\treturn c.Interface(key, i)\n}\n\n// Reset removes all the context fields.\nfunc (c Context) Reset() Context {\n\tc.l.context = enc.AppendBeginMarker(make([]byte, 0, 500))\n\treturn c\n}\n\ntype callerHook struct {\n\tcallerSkipFrameCount int\n}\n\nfunc newCallerHook(skipFrameCount int) callerHook {\n\treturn callerHook{callerSkipFrameCount: skipFrameCount}\n}\n\nfunc (ch callerHook) Run(e *Event, level Level, msg string) {\n\tswitch ch.callerSkipFrameCount {\n\tcase useGlobalSkipFrameCount:\n\t\t// Extra frames to skip (added by hook infra).\n\t\te.caller(CallerSkipFrameCount + contextCallerSkipFrameCount)\n\tdefault:\n\t\t// Extra frames to skip (added by hook infra).\n\t\te.caller(ch.callerSkipFrameCount + contextCallerSkipFrameCount)\n\t}\n}\n\n// useGlobalSkipFrameCount acts as a flag to informat callerHook.Run\n// to use the global CallerSkipFrameCount.\nconst useGlobalSkipFrameCount = math.MinInt32\n\n// ch is the default caller hook using the global CallerSkipFrameCount.\nvar ch = newCallerHook(useGlobalSkipFrameCount)\n\n// Caller adds the file:line of the caller with the zerolog.CallerFieldName key.\nfunc (c Context) Caller() Context {\n\tc.l = c.l.Hook(ch)\n\treturn c\n}\n\n// CallerWithSkipFrameCount adds the file:line of the caller with the zerolog.CallerFieldName key.\n// The specified skipFrameCount int will override the global CallerSkipFrameCount for this context's respective logger.\n// If set to -1 the global CallerSkipFrameCount will be used.\nfunc (c Context) CallerWithSkipFrameCount(skipFrameCount int) Context {\n\tc.l = c.l.Hook(newCallerHook(skipFrameCount))\n\treturn c\n}\n\n// Stack enables stack trace printing for the error passed to Err().\nfunc (c Context) Stack() Context {\n\tc.l.stack = true\n\treturn c\n}\n\n// IPAddr adds adds the field key with ip as a net.IP IPv4 or IPv6 Address to the context\nfunc (c Context) IPAddr(key string, ip net.IP) Context {\n\tc.l.context = enc.AppendIPAddr(enc.AppendKey(c.l.context, key), ip)\n\treturn c\n}\n\n// IPAddrs adds the field key with ip as a []net.IP array of IPv4 or IPv6 Address to the context\nfunc (c Context) IPAddrs(key string, ip []net.IP) Context {\n\tc.l.context = enc.AppendIPAddrs(enc.AppendKey(c.l.context, key), ip)\n\treturn c\n}\n\n// IPPrefix adds adds the field key with pfx as a []net.IPNet IPv4 or IPv6 Prefix (address and mask) to the context\nfunc (c Context) IPPrefix(key string, pfx net.IPNet) Context {\n\tc.l.context = enc.AppendIPPrefix(enc.AppendKey(c.l.context, key), pfx)\n\treturn c\n}\n\n// IPPrefix adds adds the field key with pfx as a []net.IPNet array of IPv4 or IPv6 Prefix (address and mask) to the context\nfunc (c Context) IPPrefixes(key string, pfx []net.IPNet) Context {\n\tc.l.context = enc.AppendIPPrefixes(enc.AppendKey(c.l.context, key), pfx)\n\treturn c\n}\n\n// MACAddr adds adds the field key with ha as a net.HardwareAddr MAC address to the context\nfunc (c Context) MACAddr(key string, ha net.HardwareAddr) Context {\n\tc.l.context = enc.AppendMACAddr(enc.AppendKey(c.l.context, key), ha)\n\treturn c\n}\n"
  },
  {
    "path": "context_test.go",
    "content": "package zerolog\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"testing\"\n)\n\ntype myError struct{}\n\nfunc (e *myError) Error() string { return \"test\" }\n\nfunc TestContext_ErrWithStackMarshaler(t *testing.T) {\n\t// Save original\n\toriginal := ErrorStackMarshaler\n\tdefer func() { ErrorStackMarshaler = original }()\n\n\t// Set a mock marshaler\n\tErrorStackMarshaler = func(err error) interface{} {\n\t\treturn \"stack-trace\"\n\t}\n\n\tvar buf bytes.Buffer\n\tlog := New(&buf).With().Stack().Err(errors.New(\"test error\")).Logger()\n\n\tlog.Info().Msg(\"test message\")\n\n\tgot := decodeIfBinaryToString(buf.Bytes())\n\twant := `{\"level\":\"info\",\"stack\":\"stack-trace\",\"error\":\"test error\",\"message\":\"test message\"}` + \"\\n\"\n\tif got != want {\n\t\tt.Errorf(\"Context.Err() with stack marshaler = %q, want %q\", got, want)\n\t}\n}\n\nfunc TestContext_AnErrWithNilErrorMarshal(t *testing.T) {\n\t// Save original\n\toriginal := ErrorMarshalFunc\n\tdefer func() { ErrorMarshalFunc = original }()\n\n\t// Set marshaler to return a nil error pointer\n\tErrorMarshalFunc = func(err error) interface{} {\n\t\treturn (*myError)(nil) // nil pointer of error type\n\t}\n\n\tvar buf bytes.Buffer\n\tlog := New(&buf).With().AnErr(\"test\", errors.New(\"some error\")).Logger()\n\n\tlog.Info().Msg(\"test message\")\n\n\tgot := decodeIfBinaryToString(buf.Bytes())\n\twant := `{\"level\":\"info\",\"message\":\"test message\"}` + \"\\n\" // No \"test\" field because isNilValue returned true\n\tif got != want {\n\t\tt.Errorf(\"Context.AnErr() with nil error marshal = %q, want %q\", got, want)\n\t}\n}\n\nfunc TestContext_ErrWithNilStackMarshaler(t *testing.T) {\n\t// Save original\n\toriginal := ErrorStackMarshaler\n\tdefer func() { ErrorStackMarshaler = original }()\n\n\t// Set marshaler to return nil\n\tErrorStackMarshaler = func(err error) interface{} {\n\t\treturn nil\n\t}\n\n\tvar buf bytes.Buffer\n\tlog := New(&buf).With().Stack().Err(errors.New(\"test error\")).Logger()\n\n\tlog.Info().Msg(\"test message\")\n\n\tgot := decodeIfBinaryToString(buf.Bytes())\n\twant := `{\"level\":\"info\",\"message\":\"test message\"}` + \"\\n\" // No stack or error field because stack marshaler returned nil\n\tif got != want {\n\t\tt.Errorf(\"Context.Err() with nil stack marshaler = %q, want %q\", got, want)\n\t}\n}\n\nfunc TestContext_ErrWithStackMarshalerObject(t *testing.T) {\n\t// Save original\n\toriginal := ErrorStackMarshaler\n\tdefer func() { ErrorStackMarshaler = original }()\n\n\t// Set a mock marshaler that returns LogObjectMarshaler\n\tErrorStackMarshaler = func(err error) interface{} {\n\t\treturn logObjectMarshalerImpl{name: \"user\", age: 30}\n\t}\n\n\tvar buf bytes.Buffer\n\tlog := New(&buf).With().Stack().Err(errors.New(\"test error\")).Logger()\n\n\tlog.Info().Msg(\"test message\")\n\n\tgot := decodeIfBinaryToString(buf.Bytes())\n\twant := `{\"level\":\"info\",\"stack\":{\"name\":\"user\",\"age\":-30},\"error\":\"test error\",\"message\":\"test message\"}` + \"\\n\"\n\tif got != want {\n\t\tt.Errorf(\"Context.Err() with stack marshaler object = %q, want %q\", got, want)\n\t}\n}\n\nfunc TestContext_ErrWithStackMarshalerError(t *testing.T) {\n\t// Save original\n\toriginal := ErrorStackMarshaler\n\tdefer func() { ErrorStackMarshaler = original }()\n\n\t// Set a mock marshaler that returns an error\n\tErrorStackMarshaler = func(err error) interface{} {\n\t\treturn errors.New(\"stack error\")\n\t}\n\n\tvar buf bytes.Buffer\n\tlog := New(&buf).With().Stack().Err(errors.New(\"test error\")).Logger()\n\n\tlog.Info().Msg(\"test message\")\n\n\tgot := decodeIfBinaryToString(buf.Bytes())\n\twant := `{\"level\":\"info\",\"stack\":\"stack error\",\"error\":\"test error\",\"message\":\"test message\"}` + \"\\n\"\n\tif got != want {\n\t\tt.Errorf(\"Context.Err() with stack marshaler error = %q, want %q\", got, want)\n\t}\n}\n\nfunc TestContext_ErrWithStackMarshalerInterface(t *testing.T) {\n\t// Save original\n\toriginal := ErrorStackMarshaler\n\tdefer func() { ErrorStackMarshaler = original }()\n\n\t// Set a mock marshaler that returns an int\n\tErrorStackMarshaler = func(err error) interface{} {\n\t\treturn 42\n\t}\n\n\tvar buf bytes.Buffer\n\tlog := New(&buf).With().Stack().Err(errors.New(\"test error\")).Logger()\n\n\tlog.Info().Msg(\"test message\")\n\n\tgot := decodeIfBinaryToString(buf.Bytes())\n\twant := `{\"level\":\"info\",\"stack\":42,\"error\":\"test error\",\"message\":\"test message\"}` + \"\\n\"\n\tif got != want {\n\t\tt.Errorf(\"Context.Err() with stack marshaler interface = %q, want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "ctx.go",
    "content": "package zerolog\n\nimport (\n\t\"context\"\n)\n\nvar disabledLogger *Logger\n\nfunc init() {\n\tSetGlobalLevel(TraceLevel)\n\tl := Nop()\n\tdisabledLogger = &l\n}\n\ntype ctxKey struct{}\n\n// WithContext returns a copy of ctx with the receiver attached. The Logger\n// attached to the provided Context (if any) will not be effected.  If the\n// receiver's log level is Disabled it will only be attached to the returned\n// Context if the provided Context has a previously attached Logger. If the\n// provided Context has no attached Logger, a Disabled Logger will not be\n// attached.\n//\n// Note: to modify the existing Logger attached to a Context (instead of\n// replacing it in a new Context), use UpdateContext with the following\n// notation:\n//\n//\tctx := r.Context()\n//\tl := zerolog.Ctx(ctx)\n//\tl.UpdateContext(func(c Context) Context {\n//\t    return c.Str(\"bar\", \"baz\")\n//\t})\nfunc (l Logger) WithContext(ctx context.Context) context.Context {\n\tif _, ok := ctx.Value(ctxKey{}).(*Logger); !ok && l.level == Disabled {\n\t\t// Do not store disabled logger.\n\t\treturn ctx\n\t}\n\treturn context.WithValue(ctx, ctxKey{}, &l)\n}\n\n// Ctx returns the Logger associated with the ctx. If no logger\n// is associated, DefaultContextLogger is returned, unless DefaultContextLogger\n// is nil, in which case a disabled logger is returned.\nfunc Ctx(ctx context.Context) *Logger {\n\tif l, ok := ctx.Value(ctxKey{}).(*Logger); ok {\n\t\treturn l\n\t} else if l = DefaultContextLogger; l != nil {\n\t\treturn l\n\t}\n\treturn disabledLogger\n}\n"
  },
  {
    "path": "ctx_test.go",
    "content": "package zerolog\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"io\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/rs/zerolog/internal/cbor\"\n)\n\nfunc TestCtx(t *testing.T) {\n\tlog := New(io.Discard)\n\tctx := log.WithContext(context.Background())\n\tlog2 := Ctx(ctx)\n\tif !reflect.DeepEqual(log, *log2) {\n\t\tt.Error(\"Ctx did not return the expected logger\")\n\t}\n\n\t// update\n\tlog = log.Level(InfoLevel)\n\tctx = log.WithContext(ctx)\n\tlog2 = Ctx(ctx)\n\tif !reflect.DeepEqual(log, *log2) {\n\t\tt.Error(\"Ctx did not return the expected logger\")\n\t}\n\n\tlog2 = Ctx(context.Background())\n\tif log2 != disabledLogger {\n\t\tt.Error(\"Ctx did not return the expected logger\")\n\t}\n\n\tDefaultContextLogger = &log\n\tt.Cleanup(func() { DefaultContextLogger = nil })\n\tlog2 = Ctx(context.Background())\n\tif log2 != &log {\n\t\tt.Error(\"Ctx did not return the expected logger\")\n\t}\n}\n\nfunc TestCtxDisabled(t *testing.T) {\n\tdl := New(io.Discard).Level(Disabled)\n\tctx := dl.WithContext(context.Background())\n\tif ctx != context.Background() {\n\t\tt.Error(\"WithContext stored a disabled logger\")\n\t}\n\n\tl := New(io.Discard).With().Str(\"foo\", \"bar\").Logger()\n\tctx = l.WithContext(ctx)\n\tif !reflect.DeepEqual(Ctx(ctx), &l) {\n\t\tt.Error(\"WithContext did not store logger\")\n\t}\n\n\tl.UpdateContext(func(c Context) Context {\n\t\treturn c.Str(\"bar\", \"baz\")\n\t})\n\tctx = l.WithContext(ctx)\n\tif !reflect.DeepEqual(Ctx(ctx), &l) {\n\t\tt.Error(\"WithContext did not store updated logger\")\n\t}\n\n\tl = l.Level(DebugLevel)\n\tctx = l.WithContext(ctx)\n\tif !reflect.DeepEqual(Ctx(ctx), &l) {\n\t\tt.Error(\"WithContext did not store copied logger\")\n\t}\n\n\tctx = dl.WithContext(ctx)\n\tif !reflect.DeepEqual(Ctx(ctx), &dl) {\n\t\tt.Error(\"WithContext did not override logger with a disabled logger\")\n\t}\n}\n\ntype logObjectMarshalerImpl struct {\n\tname string\n\tage  int\n}\n\nfunc (t logObjectMarshalerImpl) MarshalZerologObject(e *Event) {\n\te.Str(\"name\", strings.ToLower(t.name)).Int(\"age\", -t.age)\n}\n\nfunc Test_InterfaceLogObjectMarshaler(t *testing.T) {\n\tvar buf bytes.Buffer\n\tlog := New(&buf)\n\tctx := log.WithContext(context.Background())\n\n\tlog2 := Ctx(ctx)\n\n\twithLog := log2.With().Interface(\"obj\", &logObjectMarshalerImpl{\n\t\tname: \"FOO\",\n\t\tage:  29,\n\t}).Logger()\n\n\twithLog.Info().Msg(\"test\")\n\n\tif got, want := cbor.DecodeIfBinaryToString(buf.Bytes()), `{\"level\":\"info\",\"obj\":{\"name\":\"foo\",\"age\":-29},\"message\":\"test\"}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"got %q, want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "diode/diode.go",
    "content": "// Package diode provides a thread-safe, lock-free, non-blocking io.Writer\n// wrapper.\npackage diode\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/rs/zerolog/diode/internal/diodes\"\n)\n\nvar bufPool = &sync.Pool{\n\tNew: func() interface{} {\n\t\treturn make([]byte, 0, 500)\n\t},\n}\n\ntype Alerter func(missed int)\n\ntype diodeFetcher interface {\n\tdiodes.Diode\n\tNext() diodes.GenericDataType\n}\n\n// Writer is a io.Writer wrapper that uses a diode to make Write lock-free,\n// non-blocking and thread safe.\ntype Writer struct {\n\tw    io.Writer\n\td    diodeFetcher\n\tc    context.CancelFunc\n\tdone chan struct{}\n}\n\n// NewWriter creates a writer wrapping w with a many-to-one diode in order to\n// never block log producers and drop events if the writer can't keep up with\n// the flow of data.\n//\n// Use a diode.Writer when\n//\n//     wr := diode.NewWriter(w, 1000, 0, func(missed int) {\n//         log.Printf(\"Dropped %d messages\", missed)\n//     })\n//     log := zerolog.New(wr)\n//\n// If pollInterval is greater than 0, a poller is used otherwise a waiter is\n// used.\n//\n// See code.cloudfoundry.org/go-diodes for more info on diode.\nfunc NewWriter(w io.Writer, size int, pollInterval time.Duration, f Alerter) Writer {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdw := Writer{\n\t\tw:    w,\n\t\tc:    cancel,\n\t\tdone: make(chan struct{}),\n\t}\n\tif f == nil {\n\t\tf = func(int) {}\n\t}\n\td := diodes.NewManyToOne(size, diodes.AlertFunc(f))\n\tif pollInterval > 0 {\n\t\tdw.d = diodes.NewPoller(d,\n\t\t\tdiodes.WithPollingInterval(pollInterval),\n\t\t\tdiodes.WithPollingContext(ctx))\n\t} else {\n\t\tdw.d = diodes.NewWaiter(d,\n\t\t\tdiodes.WithWaiterContext(ctx))\n\t}\n\tgo dw.poll()\n\treturn dw\n}\n\nfunc (dw Writer) Write(p []byte) (n int, err error) {\n\t// p is pooled in zerolog so we can't hold it passed this call, hence the\n\t// copy.\n\tp = append(bufPool.Get().([]byte), p...)\n\tdw.d.Set(diodes.GenericDataType(&p))\n\treturn len(p), nil\n}\n\n// Close releases the diode poller and call Close on the wrapped writer if\n// io.Closer is implemented.\nfunc (dw Writer) Close() error {\n\tdw.c()\n\t<-dw.done\n\tif w, ok := dw.w.(io.Closer); ok {\n\t\treturn w.Close()\n\t}\n\treturn nil\n}\n\nfunc (dw Writer) poll() {\n\tdefer close(dw.done)\n\tfor {\n\t\td := dw.d.Next()\n\t\tif d == nil {\n\t\t\treturn\n\t\t}\n\t\tp := *(*[]byte)(d)\n\t\tdw.w.Write(p)\n\n\t\t// Proper usage of a sync.Pool requires each entry to have approximately\n\t\t// the same memory cost. To obtain this property when the stored type\n\t\t// contains a variably-sized buffer, we add a hard limit on the maximum buffer\n\t\t// to place back in the pool.\n\t\t//\n\t\t// See https://golang.org/issue/23199\n\t\tconst maxSize = 1 << 16 // 64KiB\n\t\tif cap(p) <= maxSize {\n\t\t\tbufPool.Put(p[:0])\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "diode/diode_example_test.go",
    "content": "// +build !binary_log\n\npackage diode_test\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/rs/zerolog\"\n\t\"github.com/rs/zerolog/diode\"\n)\n\nfunc ExampleNewWriter() {\n\tw := diode.NewWriter(os.Stdout, 1000, 0, func(missed int) {\n\t\tfmt.Printf(\"Dropped %d messages\\n\", missed)\n\t})\n\tlog := zerolog.New(w)\n\tlog.Print(\"test\")\n\n\tw.Close()\n\n\t// Output: {\"level\":\"debug\",\"message\":\"test\"}\n}\n"
  },
  {
    "path": "diode/diode_test.go",
    "content": "package diode_test\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"os/exec\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/rs/zerolog\"\n\t\"github.com/rs/zerolog/diode\"\n\t\"github.com/rs/zerolog/internal/cbor\"\n)\n\nfunc TestNewWriter(t *testing.T) {\n\tbuf := bytes.Buffer{}\n\tw := diode.NewWriter(&buf, 1000, 0, func(missed int) {\n\t\tfmt.Printf(\"Dropped %d messages\\n\", missed)\n\t})\n\tlog := zerolog.New(w)\n\tlog.Print(\"test\")\n\n\tw.Close()\n\twant := \"{\\\"level\\\":\\\"debug\\\",\\\"message\\\":\\\"test\\\"}\\n\"\n\tgot := cbor.DecodeIfBinaryToString(buf.Bytes())\n\tif got != want {\n\t\tt.Errorf(\"Diode New Writer Test failed. got:%s, want:%s!\", got, want)\n\t}\n}\n\nfunc TestClose(t *testing.T) {\n\tbuf := bytes.Buffer{}\n\tw := diode.NewWriter(&buf, 1000, 0, func(missed int) {})\n\tlog := zerolog.New(w)\n\tlog.Print(\"test\")\n\tw.Close()\n}\n\nfunc TestFatal(t *testing.T) {\n\tif os.Getenv(\"TEST_FATAL\") == \"1\" {\n\t\tw := diode.NewWriter(os.Stderr, 1000, 0, func(missed int) {\n\t\t\tfmt.Printf(\"Dropped %d messages\\n\", missed)\n\t\t})\n\t\tdefer w.Close()\n\t\tlog := zerolog.New(w)\n\t\tlog.Fatal().Msg(\"test\")\n\t\treturn\n\t}\n\n\tcmd := exec.Command(os.Args[0], \"-test.run=TestFatal\")\n\tcmd.Env = append(os.Environ(), \"TEST_FATAL=1\")\n\tstderr, err := cmd.StderrPipe()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\terr = cmd.Start()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tvar stderrBuf bytes.Buffer\n\tvar wg sync.WaitGroup\n\twg.Add(1)\n\tgo func() {\n\t\tdefer wg.Done()\n\t\tif _, err := io.Copy(&stderrBuf, stderr); err != nil {\n\t\t\tt.Errorf(\"failed to copy stderr: %v\", err)\n\t\t}\n\t}()\n\n\terr = cmd.Wait()\n\tif err == nil {\n\t\tt.Error(\"Expected log.Fatal to exit with non-zero status\")\n\t}\n\n\twg.Wait() // Wait for the goroutine to finish copying\n\tslurp := stderrBuf.Bytes()\n\n\twant := \"{\\\"level\\\":\\\"fatal\\\",\\\"message\\\":\\\"test\\\"}\\n\"\n\tgot := cbor.DecodeIfBinaryToString(slurp)\n\tif got != want {\n\t\tt.Errorf(\"Diode Fatal Test failed. got:%s, want:%s!\", got, want)\n\t}\n}\n\ntype SlowWriter struct{}\n\nfunc (rw *SlowWriter) Write(p []byte) (n int, err error) {\n\ttime.Sleep(200 * time.Millisecond)\n\tfmt.Print(string(p))\n\treturn len(p), nil\n}\n\nfunc TestFatalWithFilteredLevelWriter(t *testing.T) {\n\tif os.Getenv(\"TEST_FATAL_SLOW\") == \"1\" {\n\t\tslowWriter := SlowWriter{}\n\t\tdiodeWriter := diode.NewWriter(&slowWriter, 500, 0, func(missed int) {\n\t\t\tfmt.Printf(\"Missed %d logs\\n\", missed)\n\t\t})\n\t\tleveledDiodeWriter := zerolog.LevelWriterAdapter{\n\t\t\tWriter: &diodeWriter,\n\t\t}\n\t\tfilteredDiodeWriter := zerolog.FilteredLevelWriter{\n\t\t\tWriter: &leveledDiodeWriter,\n\t\t\tLevel:  zerolog.InfoLevel,\n\t\t}\n\t\tlogger := zerolog.New(&filteredDiodeWriter)\n\t\tlogger.Fatal().Msg(\"test\")\n\t\treturn\n\t}\n\n\tcmd := exec.Command(os.Args[0], \"-test.run=TestFatalWithFilteredLevelWriter\")\n\tcmd.Env = append(os.Environ(), \"TEST_FATAL_SLOW=1\")\n\tstdout, err := cmd.StdoutPipe()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\terr = cmd.Start()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tvar stdoutBuf bytes.Buffer\n\tvar wg sync.WaitGroup\n\twg.Add(1)\n\tgo func() {\n\t\tdefer wg.Done()\n\t\t_, _ = io.Copy(&stdoutBuf, stdout)\n\t}()\n\n\terr = cmd.Wait()\n\tif err == nil {\n\t\tt.Error(\"Expected log.Fatal to exit with non-zero status\")\n\t}\n\n\twg.Wait() // Wait for the goroutine to finish copying\n\tslurp := stdoutBuf.Bytes()\n\n\tgot := cbor.DecodeIfBinaryToString(slurp)\n\twant := \"{\\\"level\\\":\\\"fatal\\\",\\\"message\\\":\\\"test\\\"}\\n\"\n\tif got != want {\n\t\tt.Errorf(\"Expected output %q, got: %q\", want, got)\n\t}\n}\n\nfunc Benchmark(b *testing.B) {\n\tlog.SetOutput(io.Discard)\n\tdefer log.SetOutput(os.Stderr)\n\tbenchs := map[string]time.Duration{\n\t\t\"Waiter\": 0,\n\t\t\"Pooler\": 10 * time.Millisecond,\n\t}\n\tfor name, interval := range benchs {\n\t\tb.Run(name, func(b *testing.B) {\n\t\t\tw := diode.NewWriter(io.Discard, 100000, interval, nil)\n\t\t\tlog := zerolog.New(w)\n\t\t\tdefer w.Close()\n\n\t\t\tb.SetParallelism(1000)\n\t\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\t\tfor pb.Next() {\n\t\t\t\t\tlog.Print(\"test\")\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "diode/internal/diodes/README",
    "content": "Copied from https://github.com/cloudfoundry/go-diodes to avoid test dependencies.\n"
  },
  {
    "path": "diode/internal/diodes/many_to_one.go",
    "content": "package diodes\n\nimport (\n\t\"log\"\n\t\"sync/atomic\"\n\t\"unsafe\"\n)\n\n// ManyToOne diode is optimal for many writers (go-routines B-n) and a single\n// reader (go-routine A). It is not thread safe for multiple readers.\ntype ManyToOne struct {\n\twriteIndex uint64\n\treadIndex  uint64\n\tbuffer     []unsafe.Pointer\n\talerter    Alerter\n}\n\n// NewManyToOne creates a new diode (ring buffer). The ManyToOne diode\n// is optimized for many writers (on go-routines B-n) and a single reader\n// (on go-routine A). The alerter is invoked on the read's go-routine. It is\n// called when it notices that the writer go-routine has passed it and wrote\n// over data. A nil can be used to ignore alerts.\nfunc NewManyToOne(size int, alerter Alerter) *ManyToOne {\n\tif alerter == nil {\n\t\talerter = AlertFunc(func(int) {})\n\t}\n\n\td := &ManyToOne{\n\t\tbuffer:  make([]unsafe.Pointer, size),\n\t\talerter: alerter,\n\t}\n\n\t// Start write index at the value before 0\n\t// to allow the first write to use AddUint64\n\t// and still have a beginning index of 0\n\td.writeIndex = ^d.writeIndex\n\treturn d\n}\n\n// Set sets the data in the next slot of the ring buffer.\nfunc (d *ManyToOne) Set(data GenericDataType) {\n\tfor {\n\t\twriteIndex := atomic.AddUint64(&d.writeIndex, 1)\n\t\tidx := writeIndex % uint64(len(d.buffer))\n\t\told := atomic.LoadPointer(&d.buffer[idx])\n\n\t\tif old != nil &&\n\t\t\t(*bucket)(old) != nil &&\n\t\t\t(*bucket)(old).seq > writeIndex-uint64(len(d.buffer)) {\n\t\t\tlog.Println(\"Diode set collision: consider using a larger diode\")\n\t\t\tcontinue\n\t\t}\n\n\t\tnewBucket := &bucket{\n\t\t\tdata: data,\n\t\t\tseq:  writeIndex,\n\t\t}\n\n\t\tif !atomic.CompareAndSwapPointer(&d.buffer[idx], old, unsafe.Pointer(newBucket)) {\n\t\t\tlog.Println(\"Diode set collision: consider using a larger diode\")\n\t\t\tcontinue\n\t\t}\n\n\t\treturn\n\t}\n}\n\n// TryNext will attempt to read from the next slot of the ring buffer.\n// If there is no data available, it will return (nil, false).\nfunc (d *ManyToOne) TryNext() (data GenericDataType, ok bool) {\n\t// Read a value from the ring buffer based on the readIndex.\n\tidx := d.readIndex % uint64(len(d.buffer))\n\tresult := (*bucket)(atomic.SwapPointer(&d.buffer[idx], nil))\n\n\t// When the result is nil that means the writer has not had the\n\t// opportunity to write a value into the diode. This value must be ignored\n\t// and the read head must not increment.\n\tif result == nil {\n\t\treturn nil, false\n\t}\n\n\t// When the seq value is less than the current read index that means a\n\t// value was read from idx that was previously written but since has\n\t// been dropped. This value must be ignored and the read head must not\n\t// increment.\n\t//\n\t// The simulation for this scenario assumes the fast forward occurred as\n\t// detailed below.\n\t//\n\t// 5. The reader reads again getting seq 5. It then reads again expecting\n\t//    seq 6 but gets seq 2. This is a read of a stale value that was\n\t//    effectively \"dropped\" so the read fails and the read head stays put.\n\t//    `| 4 | 5 | 2 | 3 |` r: 7, w: 6\n\t//\n\tif result.seq < d.readIndex {\n\t\treturn nil, false\n\t}\n\n\t// When the seq value is greater than the current read index that means a\n\t// value was read from idx that overwrote the value that was expected to\n\t// be at this idx. This happens when the writer has lapped the reader. The\n\t// reader needs to catch up to the writer so it moves its write head to\n\t// the new seq, effectively dropping the messages that were not read in\n\t// between the two values.\n\t//\n\t// Here is a simulation of this scenario:\n\t//\n\t// 1. Both the read and write heads start at 0.\n\t//    `| nil | nil | nil | nil |` r: 0, w: 0\n\t// 2. The writer fills the buffer.\n\t//    `| 0 | 1 | 2 | 3 |` r: 0, w: 4\n\t// 3. The writer laps the read head.\n\t//    `| 4 | 5 | 2 | 3 |` r: 0, w: 6\n\t// 4. The reader reads the first value, expecting a seq of 0 but reads 4,\n\t//    this forces the reader to fast forward to 5.\n\t//    `| 4 | 5 | 2 | 3 |` r: 5, w: 6\n\t//\n\tif result.seq > d.readIndex {\n\t\tdropped := result.seq - d.readIndex\n\t\td.readIndex = result.seq\n\t\td.alerter.Alert(int(dropped))\n\t}\n\n\t// Only increment read index if a regular read occurred (where seq was\n\t// equal to readIndex) or a value was read that caused a fast forward\n\t// (where seq was greater than readIndex).\n\t//\n\td.readIndex++\n\treturn result.data, true\n}\n"
  },
  {
    "path": "diode/internal/diodes/one_to_one.go",
    "content": "package diodes\n\nimport (\n\t\"sync/atomic\"\n\t\"unsafe\"\n)\n\n// GenericDataType is the data type the diodes operate on.\ntype GenericDataType unsafe.Pointer\n\n// Alerter is used to report how many values were overwritten since the\n// last write.\ntype Alerter interface {\n\tAlert(missed int)\n}\n\n// AlertFunc type is an adapter to allow the use of ordinary functions as\n// Alert handlers.\ntype AlertFunc func(missed int)\n\n// Alert calls f(missed)\nfunc (f AlertFunc) Alert(missed int) {\n\tf(missed)\n}\n\ntype bucket struct {\n\tdata GenericDataType\n\tseq  uint64 // seq is the recorded write index at the time of writing\n}\n\n// OneToOne diode is meant to be used by a single reader and a single writer.\n// It is not thread safe if used otherwise.\ntype OneToOne struct {\n\twriteIndex uint64\n\treadIndex  uint64\n\tbuffer     []unsafe.Pointer\n\talerter    Alerter\n}\n\n// NewOneToOne creates a new diode is meant to be used by a single reader and\n// a single writer. The alerter is invoked on the read's go-routine. It is\n// called when it notices that the writer go-routine has passed it and wrote\n// over data. A nil can be used to ignore alerts.\nfunc NewOneToOne(size int, alerter Alerter) *OneToOne {\n\tif alerter == nil {\n\t\talerter = AlertFunc(func(int) {})\n\t}\n\n\treturn &OneToOne{\n\t\tbuffer:  make([]unsafe.Pointer, size),\n\t\talerter: alerter,\n\t}\n}\n\n// Set sets the data in the next slot of the ring buffer.\nfunc (d *OneToOne) Set(data GenericDataType) {\n\tidx := d.writeIndex % uint64(len(d.buffer))\n\n\tnewBucket := &bucket{\n\t\tdata: data,\n\t\tseq:  d.writeIndex,\n\t}\n\td.writeIndex++\n\n\tatomic.StorePointer(&d.buffer[idx], unsafe.Pointer(newBucket))\n}\n\n// TryNext will attempt to read from the next slot of the ring buffer.\n// If there is no data available, it will return (nil, false).\nfunc (d *OneToOne) TryNext() (data GenericDataType, ok bool) {\n\t// Read a value from the ring buffer based on the readIndex.\n\tidx := d.readIndex % uint64(len(d.buffer))\n\tresult := (*bucket)(atomic.SwapPointer(&d.buffer[idx], nil))\n\n\t// When the result is nil that means the writer has not had the\n\t// opportunity to write a value into the diode. This value must be ignored\n\t// and the read head must not increment.\n\tif result == nil {\n\t\treturn nil, false\n\t}\n\n\t// When the seq value is less than the current read index that means a\n\t// value was read from idx that was previously written but since has\n\t// been dropped. This value must be ignored and the read head must not\n\t// increment.\n\t//\n\t// The simulation for this scenario assumes the fast forward occurred as\n\t// detailed below.\n\t//\n\t// 5. The reader reads again getting seq 5. It then reads again expecting\n\t//    seq 6 but gets seq 2. This is a read of a stale value that was\n\t//    effectively \"dropped\" so the read fails and the read head stays put.\n\t//    `| 4 | 5 | 2 | 3 |` r: 7, w: 6\n\t//\n\tif result.seq < d.readIndex {\n\t\treturn nil, false\n\t}\n\n\t// When the seq value is greater than the current read index that means a\n\t// value was read from idx that overwrote the value that was expected to\n\t// be at this idx. This happens when the writer has lapped the reader. The\n\t// reader needs to catch up to the writer so it moves its write head to\n\t// the new seq, effectively dropping the messages that were not read in\n\t// between the two values.\n\t//\n\t// Here is a simulation of this scenario:\n\t//\n\t// 1. Both the read and write heads start at 0.\n\t//    `| nil | nil | nil | nil |` r: 0, w: 0\n\t// 2. The writer fills the buffer.\n\t//    `| 0 | 1 | 2 | 3 |` r: 0, w: 4\n\t// 3. The writer laps the read head.\n\t//    `| 4 | 5 | 2 | 3 |` r: 0, w: 6\n\t// 4. The reader reads the first value, expecting a seq of 0 but reads 4,\n\t//    this forces the reader to fast forward to 5.\n\t//    `| 4 | 5 | 2 | 3 |` r: 5, w: 6\n\t//\n\tif result.seq > d.readIndex {\n\t\tdropped := result.seq - d.readIndex\n\t\td.readIndex = result.seq\n\t\td.alerter.Alert(int(dropped))\n\t}\n\n\t// Only increment read index if a regular read occurred (where seq was\n\t// equal to readIndex) or a value was read that caused a fast forward\n\t// (where seq was greater than readIndex).\n\td.readIndex++\n\treturn result.data, true\n}\n"
  },
  {
    "path": "diode/internal/diodes/poller.go",
    "content": "package diodes\n\nimport (\n\t\"context\"\n\t\"time\"\n)\n\n// Diode is any implementation of a diode.\ntype Diode interface {\n\tSet(GenericDataType)\n\tTryNext() (GenericDataType, bool)\n}\n\n// Poller will poll a diode until a value is available.\ntype Poller struct {\n\tDiode\n\tinterval time.Duration\n\tctx      context.Context\n}\n\n// PollerConfigOption can be used to setup the poller.\ntype PollerConfigOption func(*Poller)\n\n// WithPollingInterval sets the interval at which the diode is queried\n// for new data. The default is 10ms.\nfunc WithPollingInterval(interval time.Duration) PollerConfigOption {\n\treturn func(c *Poller) {\n\t\tc.interval = interval\n\t}\n}\n\n// WithPollingContext sets the context to cancel any retrieval (Next()). It\n// will not change any results for adding data (Set()). Default is\n// context.Background().\nfunc WithPollingContext(ctx context.Context) PollerConfigOption {\n\treturn func(c *Poller) {\n\t\tc.ctx = ctx\n\t}\n}\n\n// NewPoller returns a new Poller that wraps the given diode.\nfunc NewPoller(d Diode, opts ...PollerConfigOption) *Poller {\n\tp := &Poller{\n\t\tDiode:    d,\n\t\tinterval: 10 * time.Millisecond,\n\t\tctx:      context.Background(),\n\t}\n\n\tfor _, o := range opts {\n\t\to(p)\n\t}\n\n\treturn p\n}\n\n// Next polls the diode until data is available or until the context is done.\n// If the context is done, then nil will be returned.\nfunc (p *Poller) Next() GenericDataType {\n\tfor {\n\t\tdata, ok := p.Diode.TryNext()\n\t\tif !ok {\n\t\t\tif p.isDone() {\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\ttime.Sleep(p.interval)\n\t\t\tcontinue\n\t\t}\n\t\treturn data\n\t}\n}\n\nfunc (p *Poller) isDone() bool {\n\tselect {\n\tcase <-p.ctx.Done():\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n"
  },
  {
    "path": "diode/internal/diodes/waiter.go",
    "content": "package diodes\n\nimport (\n\t\"context\"\n\t\"sync\"\n)\n\n// Waiter will use a conditional mutex to alert the reader to when data is\n// available.\ntype Waiter struct {\n\tDiode\n\tmu  sync.Mutex\n\tc   *sync.Cond\n\tctx context.Context\n}\n\n// WaiterConfigOption can be used to setup the waiter.\ntype WaiterConfigOption func(*Waiter)\n\n// WithWaiterContext sets the context to cancel any retrieval (Next()). It\n// will not change any results for adding data (Set()). Default is\n// context.Background().\nfunc WithWaiterContext(ctx context.Context) WaiterConfigOption {\n\treturn func(c *Waiter) {\n\t\tc.ctx = ctx\n\t}\n}\n\n// NewWaiter returns a new Waiter that wraps the given diode.\nfunc NewWaiter(d Diode, opts ...WaiterConfigOption) *Waiter {\n\tw := new(Waiter)\n\tw.Diode = d\n\tw.c = sync.NewCond(&w.mu)\n\tw.ctx = context.Background()\n\n\tfor _, opt := range opts {\n\t\topt(w)\n\t}\n\n\tgo func() {\n\t\t<-w.ctx.Done()\n\n\t\t// Mutex is strictly necessary here to avoid a race in Next() (between\n\t\t// w.isDone() and w.c.Wait()) and w.c.Broadcast() here.\n\t\tw.mu.Lock()\n\t\tw.c.Broadcast()\n\t\tw.mu.Unlock()\n\t}()\n\n\treturn w\n}\n\n// Set invokes the wrapped diode's Set with the given data and uses Broadcast\n// to wake up any readers.\nfunc (w *Waiter) Set(data GenericDataType) {\n\tw.Diode.Set(data)\n\tw.c.Broadcast()\n}\n\n// Next returns the next data point on the wrapped diode. If there is not any\n// new data, it will Wait for set to be called or the context to be done.\n// If the context is done, then nil will be returned.\nfunc (w *Waiter) Next() GenericDataType {\n\tw.mu.Lock()\n\tdefer w.mu.Unlock()\n\n\tfor {\n\t\tdata, ok := w.Diode.TryNext()\n\t\tif !ok {\n\t\t\tif w.isDone() {\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tw.c.Wait()\n\t\t\tcontinue\n\t\t}\n\t\treturn data\n\t}\n}\n\nfunc (w *Waiter) isDone() bool {\n\tselect {\n\tcase <-w.ctx.Done():\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n"
  },
  {
    "path": "encoder.go",
    "content": "package zerolog\n\nimport (\n\t\"net\"\n\t\"time\"\n)\n\ntype encoder interface {\n\tAppendArrayDelim(dst []byte) []byte\n\tAppendArrayEnd(dst []byte) []byte\n\tAppendArrayStart(dst []byte) []byte\n\tAppendBeginMarker(dst []byte) []byte\n\tAppendBool(dst []byte, val bool) []byte\n\tAppendBools(dst []byte, vals []bool) []byte\n\tAppendBytes(dst, s []byte) []byte\n\tAppendDuration(dst []byte, d time.Duration, unit time.Duration, format string, useInt bool, precision int) []byte\n\tAppendDurations(dst []byte, vals []time.Duration, unit time.Duration, format string, useInt bool, precision int) []byte\n\tAppendEndMarker(dst []byte) []byte\n\tAppendFloat32(dst []byte, val float32, precision int) []byte\n\tAppendFloat64(dst []byte, val float64, precision int) []byte\n\tAppendFloats32(dst []byte, vals []float32, precision int) []byte\n\tAppendFloats64(dst []byte, vals []float64, precision int) []byte\n\tAppendHex(dst, s []byte) []byte\n\tAppendIPAddr(dst []byte, ip net.IP) []byte\n\tAppendIPPrefix(dst []byte, pfx net.IPNet) []byte\n\tAppendInt(dst []byte, val int) []byte\n\tAppendInt16(dst []byte, val int16) []byte\n\tAppendInt32(dst []byte, val int32) []byte\n\tAppendInt64(dst []byte, val int64) []byte\n\tAppendInt8(dst []byte, val int8) []byte\n\tAppendInterface(dst []byte, i interface{}) []byte\n\tAppendInts(dst []byte, vals []int) []byte\n\tAppendInts16(dst []byte, vals []int16) []byte\n\tAppendInts32(dst []byte, vals []int32) []byte\n\tAppendInts64(dst []byte, vals []int64) []byte\n\tAppendInts8(dst []byte, vals []int8) []byte\n\tAppendKey(dst []byte, key string) []byte\n\tAppendLineBreak(dst []byte) []byte\n\tAppendMACAddr(dst []byte, ha net.HardwareAddr) []byte\n\tAppendNil(dst []byte) []byte\n\tAppendObjectData(dst []byte, o []byte) []byte\n\tAppendString(dst []byte, s string) []byte\n\tAppendStrings(dst []byte, vals []string) []byte\n\tAppendTime(dst []byte, t time.Time, format string) []byte\n\tAppendTimes(dst []byte, vals []time.Time, format string) []byte\n\tAppendUint(dst []byte, val uint) []byte\n\tAppendUint16(dst []byte, val uint16) []byte\n\tAppendUint32(dst []byte, val uint32) []byte\n\tAppendUint64(dst []byte, val uint64) []byte\n\tAppendUint8(dst []byte, val uint8) []byte\n\tAppendUints(dst []byte, vals []uint) []byte\n\tAppendUints16(dst []byte, vals []uint16) []byte\n\tAppendUints32(dst []byte, vals []uint32) []byte\n\tAppendUints64(dst []byte, vals []uint64) []byte\n\tAppendUints8(dst []byte, vals []uint8) []byte\n}\n"
  },
  {
    "path": "encoder_cbor.go",
    "content": "// +build binary_log\n\npackage zerolog\n\n// This file contains bindings to do binary encoding.\n\nimport (\n\t\"github.com/rs/zerolog/internal/cbor\"\n)\n\nvar (\n\t_ encoder = (*cbor.Encoder)(nil)\n\n\tenc = cbor.Encoder{}\n)\n\nfunc init() {\n\t// using closure to reflect the changes at runtime.\n\tcbor.JSONMarshalFunc = func(v interface{}) ([]byte, error) {\n\t\treturn InterfaceMarshalFunc(v)\n\t}\n}\n\nfunc appendJSON(dst []byte, j []byte) []byte {\n\treturn cbor.AppendEmbeddedJSON(dst, j)\n}\nfunc appendCBOR(dst []byte, c []byte) []byte {\n\treturn cbor.AppendEmbeddedCBOR(dst, c)\n}\n\n// decodeIfBinaryToString - converts a binary formatted log msg to a\n// JSON formatted String Log message.\nfunc decodeIfBinaryToString(in []byte) string {\n\treturn cbor.DecodeIfBinaryToString(in)\n}\n\nfunc decodeObjectToStr(in []byte) string {\n\treturn cbor.DecodeObjectToStr(in)\n}\n\n// decodeIfBinaryToBytes - converts a binary formatted log msg to a\n// JSON formatted Bytes Log message.\nfunc decodeIfBinaryToBytes(in []byte) []byte {\n\treturn cbor.DecodeIfBinaryToBytes(in)\n}\n"
  },
  {
    "path": "encoder_json.go",
    "content": "// +build !binary_log\n\npackage zerolog\n\n// encoder_json.go file contains bindings to generate\n// JSON encoded byte stream.\n\nimport (\n\t\"encoding/base64\"\n\t\"github.com/rs/zerolog/internal/json\"\n)\n\nvar (\n\t_ encoder = (*json.Encoder)(nil)\n\n\tenc = json.Encoder{}\n)\n\nfunc init() {\n\t// using closure to reflect the changes at runtime.\n\tjson.JSONMarshalFunc = func(v interface{}) ([]byte, error) {\n\t\treturn InterfaceMarshalFunc(v)\n\t}\n}\n\nfunc appendJSON(dst []byte, j []byte) []byte {\n\treturn append(dst, j...)\n}\nfunc appendCBOR(dst []byte, cbor []byte) []byte {\n\tdst = append(dst, []byte(\"\\\"data:application/cbor;base64,\")...)\n\tl := len(dst)\n\tenc := base64.StdEncoding\n\tn := enc.EncodedLen(len(cbor))\n\tfor i := 0; i < n; i++ {\n\t\tdst = append(dst, '.')\n\t}\n\tenc.Encode(dst[l:], cbor)\n\treturn append(dst, '\"')\n}\n\nfunc decodeIfBinaryToString(in []byte) string {\n\treturn string(in)\n}\n\nfunc decodeObjectToStr(in []byte) string {\n\treturn string(in)\n}\n\nfunc decodeIfBinaryToBytes(in []byte) []byte {\n\treturn in\n}\n"
  },
  {
    "path": "error_marshal_test.go",
    "content": "package zerolog\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"strings\"\n\t\"testing\"\n)\n\ntype loggableError struct {\n\terror\n}\n\nfunc (l loggableError) MarshalZerologObject(e *Event) {\n\tif l.error == nil {\n\t\treturn\n\t}\n\te.Str(\"l\", strings.ToUpper(l.error.Error()))\n}\n\ntype nonLoggableError struct {\n\terror\n\tline int\n}\n\ntype wrappedError struct {\n\terror\n\tmsg string\n}\n\nfunc (w wrappedError) Error() string {\n\tif w.error == nil {\n\t\treturn w.msg\n\t}\n\treturn w.error.Error() + \": \" + w.msg\n}\n\ntype interfaceError struct {\n\tval string\n}\n\nfunc TestArrayErrorMarshalFunc(t *testing.T) {\n\tprefixed := func(s, prefix string) string {\n\t\tif s == \"null\" {\n\t\t\treturn \"\"\n\t\t}\n\t\treturn prefix + s + `,`\n\t}\n\terrs := []error{\n\t\tnil,\n\t\tfmt.Errorf(\"failure\"),\n\t\tloggableError{fmt.Errorf(\"whoops\")},\n\t\tnonLoggableError{fmt.Errorf(\"oops\"), 402},\n\t}\n\ttype testCase struct {\n\t\tname    string\n\t\tmarshal func(err error) interface{}\n\t\twant    []string\n\t}\n\ttestCases := []testCase{\n\t\t{\n\t\t\tname:    \"default\",\n\t\t\tmarshal: nil,\n\t\t\twant:    []string{`null`, `\"failure\"`, `{\"l\":\"WHOOPS\"}`, `\"oops\"`},\n\t\t},\n\t\t{\n\t\t\tname: \"string\",\n\t\t\tmarshal: func(err error) interface{} {\n\t\t\t\tif err == nil {\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\treturn err.Error()\n\t\t\t},\n\t\t\twant: []string{`null`, `\"failure\"`, `\"whoops\"`, `\"oops\"`},\n\t\t},\n\t\t{\n\t\t\tname: \"loggable\",\n\t\t\tmarshal: func(err error) interface{} {\n\t\t\t\tif err == nil {\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\treturn loggableError{err}\n\t\t\t},\n\t\t\twant: []string{`null`, `{\"l\":\"FAILURE\"}`, `{\"l\":\"WHOOPS\"}`, `{\"l\":\"OOPS\"}`},\n\t\t},\n\t\t{\n\t\t\tname: \"non-loggable\",\n\t\t\tmarshal: func(err error) interface{} {\n\t\t\t\tif err == nil {\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\treturn nonLoggableError{err, 404}\n\t\t\t},\n\t\t\twant: []string{`null`, `\"failure\"`, `\"whoops\"`, `\"oops\"`},\n\t\t},\n\t\t{\n\t\t\tname: \"interface\",\n\t\t\tmarshal: func(err error) interface{} {\n\t\t\t\tvar some interfaceError\n\t\t\t\tif err != nil {\n\t\t\t\t\tsome.val = err.Error()\n\t\t\t\t}\n\t\t\t\tvar interfaceErr interface{} = some\n\t\t\t\treturn interfaceErr\n\t\t\t},\n\t\t\twant: []string{`{}`, `{}`, `{}`, `{}`},\n\t\t},\n\t\t{\n\t\t\tname: \"nilError\",\n\t\t\tmarshal: func(err error) interface{} {\n\t\t\t\tvar errNil error = nil\n\t\t\t\treturn errNil\n\t\t\t},\n\t\t\twant: []string{`null`, `null`, `null`, `null`},\n\t\t},\n\t\t{\n\t\t\tname: \"wrapped error\",\n\t\t\tmarshal: func(err error) interface{} {\n\t\t\t\tif err == nil {\n\t\t\t\t\treturn nil\n\t\t\t\t} else if we, ok := err.(wrappedError); ok {\n\t\t\t\t\treturn we\n\t\t\t\t} else {\n\t\t\t\t\treturn wrappedError{err, \"addendum\"}\n\t\t\t\t}\n\t\t\t},\n\t\t\twant: []string{`null`, `\"failure: addendum\"`, `\"whoops: addendum\"`, `\"oops: addendum\"`},\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\toriginalErrorMarshalFunc := ErrorMarshalFunc\n\t\t\tdefer func() {\n\t\t\t\tErrorMarshalFunc = originalErrorMarshalFunc\n\t\t\t}()\n\n\t\t\tif tc.marshal != nil {\n\t\t\t\tErrorMarshalFunc = tc.marshal\n\t\t\t}\n\n\t\t\tt.Run(\"Err\", func(t *testing.T) {\n\t\t\t\tfor i, err := range errs {\n\t\t\t\t\twant := tc.want[i]\n\t\t\t\t\tt.Run(\"Arr\", func(t *testing.T) {\n\t\t\t\t\t\twants := `[` + want + `]`\n\t\t\t\t\t\ta := Arr().Err(err)\n\t\t\t\t\t\tif got := decodeObjectToStr(a.write([]byte{})); got != wants {\n\t\t\t\t\t\t\tt.Errorf(\"%s %d Array.Err(%v)\\ngot:  %s\\nwant: %s\", tc.name, i, err, got, wants)\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\tt.Run(\"Ctx\", func(t *testing.T) {\n\t\t\t\t\t\twants := `{` + prefixed(want, `\"error\":`) + `\"message\":\"msg\"}` + \"\\n\"\n\t\t\t\t\t\tout := &bytes.Buffer{}\n\t\t\t\t\t\tlogger := New(out).With().Err(err).Logger()\n\t\t\t\t\t\tlogger.Log().Msg(\"msg\")\n\t\t\t\t\t\tif got := decodeIfBinaryToString(out.Bytes()); got != wants {\n\t\t\t\t\t\t\tt.Errorf(\"%s %d Ctx.Err(%v)\\ngot:  %v\\nwant: %v\", tc.name, i, err, got, wants)\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\tt.Run(\"Event\", func(t *testing.T) {\n\t\t\t\t\t\twants := `{` + prefixed(want, `\"error\":`) + `\"message\":\"msg\"}` + \"\\n\"\n\t\t\t\t\t\tout := &bytes.Buffer{}\n\t\t\t\t\t\tlogger := New(out)\n\t\t\t\t\t\tlogger.Log().Err(err).Msg(\"msg\")\n\t\t\t\t\t\tif got := decodeIfBinaryToString(out.Bytes()); got != wants {\n\t\t\t\t\t\t\tt.Errorf(\"%s %d Event.Err(%v)\\ngot:  %v\\nwant: %v\", tc.name, i, err, got, wants)\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\tt.Run(\"Fields\", func(t *testing.T) {\n\t\t\t\t\t\tif i == 0 && tc.want[i] == \"{}\" {\n\t\t\t\t\t\t\twant = `null`\n\t\t\t\t\t\t}\n\t\t\t\t\t\twants := `{\"err\":` + want + `,\"message\":\"msg\"}` + \"\\n\"\n\t\t\t\t\t\tout := &bytes.Buffer{}\n\t\t\t\t\t\tlogger := New(out)\n\t\t\t\t\t\tlogger.Log().Fields(map[string]interface{}{\"err\": err}).Msg(\"msg\")\n\t\t\t\t\t\tif got := decodeIfBinaryToString(out.Bytes()); got != wants {\n\t\t\t\t\t\t\tt.Errorf(\"%s %d Event.Fields(%v)\\ngot:  %v\\nwant: %v\", tc.name, i, err, got, wants)\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"Errs\", func(t *testing.T) {\n\t\t\t\twant := `[` + strings.Join(tc.want, \",\") + `]`\n\t\t\t\tt.Run(\"Arr\", func(t *testing.T) {\n\t\t\t\t\ta := Arr().Errs(errs)\n\t\t\t\t\tif got := decodeObjectToStr(a.write([]byte{})); got != want {\n\t\t\t\t\t\tt.Errorf(\"%s Array.Errs()\\ngot:  %s\\nwant: %s\", tc.name, got, want)\n\t\t\t\t\t}\n\t\t\t\t})\n\n\t\t\t\tt.Run(\"Ctx\", func(t *testing.T) {\n\t\t\t\t\twants := `{\"e\":` + want + `,\"message\":\"msg\"}` + \"\\n\"\n\t\t\t\t\tout := &bytes.Buffer{}\n\t\t\t\t\tlogger := New(out).With().Errs(\"e\", errs).Logger()\n\t\t\t\t\tlogger.Log().Msg(\"msg\")\n\t\t\t\t\tif got := decodeIfBinaryToString(out.Bytes()); got != wants {\n\t\t\t\t\t\tt.Errorf(\"%s Ctx.Errs()\\ngot:  %v\\nwant: %v\", tc.name, got, wants)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\tt.Run(\"Event\", func(t *testing.T) {\n\t\t\t\t\twants := `{\"e\":` + want + `,\"message\":\"msg\"}` + \"\\n\"\n\t\t\t\t\tout := &bytes.Buffer{}\n\t\t\t\t\tlogger := New(out)\n\t\t\t\t\tlogger.Log().Errs(\"e\", errs).Msg(\"msg\")\n\t\t\t\t\tif got := decodeIfBinaryToString(out.Bytes()); got != wants {\n\t\t\t\t\t\tt.Errorf(\"%s Ctx.Errs()\\ngot:  %v\\nwant: %v\", tc.name, got, wants)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\tt.Run(\"Fields\", func(t *testing.T) {\n\t\t\t\t\twants := `{\"e\":` + want + `,\"message\":\"msg\"}` + \"\\n\"\n\t\t\t\t\tout := &bytes.Buffer{}\n\t\t\t\t\tlogger := New(out)\n\t\t\t\t\tlogger.Log().Fields(map[string]interface{}{\"e\": errs}).Msg(\"msg\")\n\t\t\t\t\tif got := decodeIfBinaryToString(out.Bytes()); got != wants {\n\t\t\t\t\t\tt.Errorf(\"%s Ctx.Errs()\\ngot:  %v\\nwant: %v\", tc.name, got, wants)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "event.go",
    "content": "package zerolog\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"runtime\"\n\t\"sync\"\n\t\"time\"\n)\n\nvar eventPool = &sync.Pool{\n\tNew: func() interface{} {\n\t\treturn &Event{\n\t\t\tbuf: make([]byte, 0, 500),\n\t\t}\n\t},\n}\n\n// Event represents a log event. It is instanced by one of the level method of\n// Logger and finalized by the Msg or Msgf method.\ntype Event struct {\n\tbuf       []byte\n\tw         LevelWriter\n\tlevel     Level\n\tdone      func(msg string)\n\tstack     bool            // enable error stack trace\n\tch        []Hook          // hooks from context\n\tskipFrame int             // The number of additional frames to skip when printing the caller.\n\tctx       context.Context // Optional Go context for event\n}\n\nfunc putEvent(e *Event) {\n\t// prevent any subsequent use of the Event contextual state and truncate the buffer\n\te.w = nil\n\te.done = nil\n\te.stack = false\n\te.ch = nil\n\te.skipFrame = 0\n\te.ctx = nil\n\te.buf = e.buf[:0]\n\n\t// Proper usage of a sync.Pool requires each entry to have approximately\n\t// the same memory cost. To obtain this property when the stored type\n\t// contains a variably-sized buffer, we add a hard limit on the maximum buffer\n\t// to place back in the pool.\n\t//\n\t// See https://golang.org/issue/23199\n\tconst maxSize = 1 << 16 // 64KiB\n\tif cap(e.buf) <= maxSize {\n\t\teventPool.Put(e)\n\t}\n}\n\n// LogObjectMarshaler provides a strongly-typed and encoding-agnostic interface\n// to be implemented by types used with Event/Context's Object methods.\ntype LogObjectMarshaler interface {\n\tMarshalZerologObject(e *Event)\n}\n\n// LogArrayMarshaler provides a strongly-typed and encoding-agnostic interface\n// to be implemented by types used with Event/Context's Array methods.\ntype LogArrayMarshaler interface {\n\tMarshalZerologArray(a *Array)\n}\n\nfunc newEvent(w LevelWriter, level Level, stack bool, ctx context.Context, hooks []Hook) *Event {\n\te := eventPool.Get().(*Event)\n\te.buf = e.buf[:0]\n\te.stack = stack\n\te.ctx = ctx\n\te.ch = hooks\n\te.buf = enc.AppendBeginMarker(e.buf)\n\te.w = w\n\te.level = level\n\te.skipFrame = 0\n\treturn e\n}\n\nfunc (e *Event) write() (err error) {\n\tif e == nil {\n\t\treturn nil\n\t}\n\tif e.level != Disabled {\n\t\te.buf = enc.AppendEndMarker(e.buf)\n\t\te.buf = enc.AppendLineBreak(e.buf)\n\t\tif e.w != nil {\n\t\t\t_, err = e.w.WriteLevel(e.level, e.buf)\n\t\t}\n\t}\n\tputEvent(e)\n\treturn\n}\n\n// Enabled return false if the *Event is going to be filtered out by\n// log level or sampling.\nfunc (e *Event) Enabled() bool {\n\treturn e != nil && e.level != Disabled\n}\n\n// Discard disables the event so Msg(f) won't print it.\nfunc (e *Event) Discard() *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.level = Disabled\n\treturn nil\n}\n\n// Msg sends the *Event with msg added as the message field if not empty.\n//\n// NOTICE: once this method is called, the *Event should be disposed.\n// Calling Msg twice can have unexpected result.\nfunc (e *Event) Msg(msg string) {\n\tif e == nil {\n\t\treturn\n\t}\n\te.msg(msg)\n}\n\n// Send is equivalent to calling Msg(\"\").\n//\n// NOTICE: once this method is called, the *Event should be disposed.\nfunc (e *Event) Send() {\n\tif e == nil {\n\t\treturn\n\t}\n\te.msg(\"\")\n}\n\n// Msgf sends the event with formatted msg added as the message field if not empty.\n//\n// NOTICE: once this method is called, the *Event should be disposed.\n// Calling Msgf twice can have unexpected result.\nfunc (e *Event) Msgf(format string, v ...interface{}) {\n\tif e == nil {\n\t\treturn\n\t}\n\te.msg(fmt.Sprintf(format, v...))\n}\n\nfunc (e *Event) MsgFunc(createMsg func() string) {\n\tif e == nil {\n\t\treturn\n\t}\n\te.msg(createMsg())\n}\n\nfunc (e *Event) msg(msg string) {\n\tfor _, hook := range e.ch {\n\t\thook.Run(e, e.level, msg)\n\t}\n\tif msg != \"\" {\n\t\te.buf = enc.AppendString(enc.AppendKey(e.buf, MessageFieldName), msg)\n\t}\n\tif e.done != nil {\n\t\tdefer e.done(msg)\n\t}\n\tif err := e.write(); err != nil {\n\t\tif ErrorHandler != nil {\n\t\t\tErrorHandler(err)\n\t\t} else {\n\t\t\tfmt.Fprintf(os.Stderr, \"zerolog: could not write event: %v\\n\", err)\n\t\t}\n\t}\n}\n\n// Fields is a helper function to use a map or slice to set fields using type assertion.\n// Only map[string]interface{} and []interface{} are accepted. []interface{} must\n// alternate string keys and arbitrary values, and extraneous ones are ignored.\nfunc (e *Event) Fields(fields interface{}) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = appendFields(e.buf, fields, e.stack, e.ctx, e.ch)\n\treturn e\n}\n\n// Dict adds the field key with a dict to the event context.\n// Use e.CreateDict() to create the dictionary.\nfunc (e *Event) Dict(key string, dict *Event) *Event {\n\tif e != nil {\n\t\tdict.buf = enc.AppendEndMarker(dict.buf)\n\t\te.buf = append(enc.AppendKey(e.buf, key), dict.buf...)\n\t}\n\tputEvent(dict)\n\treturn e\n}\n\n// CreateDict creates an Event to be used with the *Event.Dict method.\n// It preserves the stack, hooks, and context from the parent event.\n// Call usual field methods like Str, Int etc to add fields to this\n// event and give it as argument the *Event.Dict method.\nfunc (e *Event) CreateDict() *Event {\n\tif e == nil {\n\t\treturn newEvent(nil, DebugLevel, false, nil, nil)\n\t}\n\treturn newEvent(nil, DebugLevel, e.stack, e.ctx, e.ch)\n}\n\n// Dict creates an Event to be used with the *Event.Dict method.\n// Call usual field methods like Str, Int etc to add fields to this\n// event and give it as argument the *Event.Dict method.\n// NOTE: This function is deprecated because it does not preserve\n// the stack, hooks, and context from the parent event.\n// Deprecated: Use Event.CreateDict instead.\nfunc Dict() *Event {\n\treturn newEvent(nil, DebugLevel, false, nil, nil)\n}\n\n// CreateArray creates an Array to be used with the *Event.Array method.\n// It preserves the stack, hooks, and context from the parent event.\n// Call usual field methods like Str, Int etc to add elements to this\n// array and give it as argument the *Event.Array method.\nfunc (e *Event) CreateArray() *Array {\n\ta := Arr()\n\tif e != nil {\n\t\ta.stack = e.stack\n\t\ta.ctx = e.ctx\n\t\ta.ch = e.ch\n\t}\n\treturn a\n}\n\n// Array adds the field key with an array to the event context.\n// Use e.CreateArray() to create the array or pass a type that\n// implement the LogArrayMarshaler interface.\nfunc (e *Event) Array(key string, arr LogArrayMarshaler) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendKey(e.buf, key)\n\tvar a *Array\n\tif aa, ok := arr.(*Array); ok {\n\t\ta = aa\n\t} else {\n\t\ta = e.CreateArray()\n\t\tarr.MarshalZerologArray(a)\n\t}\n\te.buf = a.write(e.buf)\n\treturn e\n}\n\nfunc (e *Event) appendObject(obj LogObjectMarshaler) {\n\te.buf = enc.AppendBeginMarker(e.buf)\n\tobj.MarshalZerologObject(e)\n\te.buf = enc.AppendEndMarker(e.buf)\n}\n\n// Object marshals an object that implement the LogObjectMarshaler interface.\nfunc (e *Event) Object(key string, obj LogObjectMarshaler) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendKey(e.buf, key)\n\tif obj == nil {\n\t\te.buf = enc.AppendNil(e.buf)\n\n\t\treturn e\n\t}\n\n\te.appendObject(obj)\n\treturn e\n}\n\n// Objects adds the field key with obj as an array of objects that implement the LogObjectMarshaler interface.\nfunc (e *Event) Objects(key string, objs []LogObjectMarshaler) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendArrayStart(enc.AppendKey(e.buf, key))\n\tfor i, obj := range objs {\n\t\te.buf = appendObject(e.buf, obj, e.stack, e.ctx, e.ch)\n\t\tif i < (len(objs) - 1) {\n\t\t\te.buf = enc.AppendArrayDelim(e.buf)\n\t\t}\n\t}\n\te.buf = enc.AppendArrayEnd(e.buf)\n\treturn e\n}\n\n// Func allows an anonymous func to run only if the event is enabled.\nfunc (e *Event) Func(f func(e *Event)) *Event {\n\tif e != nil && e.Enabled() {\n\t\tf(e)\n\t}\n\treturn e\n}\n\n// EmbedObject marshals an object that implement the LogObjectMarshaler interface.\nfunc (e *Event) EmbedObject(obj LogObjectMarshaler) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\tif obj == nil {\n\t\treturn e\n\t}\n\tobj.MarshalZerologObject(e)\n\treturn e\n}\n\n// Str adds the field key with val as a string to the *Event context.\nfunc (e *Event) Str(key, val string) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendString(enc.AppendKey(e.buf, key), val)\n\treturn e\n}\n\n// Strs adds the field key with vals as a []string to the *Event context.\nfunc (e *Event) Strs(key string, vals []string) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendStrings(enc.AppendKey(e.buf, key), vals)\n\treturn e\n}\n\n// Stringer adds the field key with val.String() (or null if val is nil)\n// to the *Event context.\nfunc (e *Event) Stringer(key string, val fmt.Stringer) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendStringer(enc.AppendKey(e.buf, key), val)\n\treturn e\n}\n\n// Stringers adds the field key with vals where each individual val\n// is used as val.String() (or null if val is empty) to the *Event\n// context.\nfunc (e *Event) Stringers(key string, vals []fmt.Stringer) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendStringers(enc.AppendKey(e.buf, key), vals)\n\treturn e\n}\n\n// Bytes adds the field key with val as a string to the *Event context.\n//\n// Runes outside of normal ASCII ranges will be hex-encoded in the resulting\n// JSON.\nfunc (e *Event) Bytes(key string, val []byte) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendBytes(enc.AppendKey(e.buf, key), val)\n\treturn e\n}\n\n// Hex adds the field key with val as a hex string to the *Event context.\nfunc (e *Event) Hex(key string, val []byte) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendHex(enc.AppendKey(e.buf, key), val)\n\treturn e\n}\n\n// RawJSON adds already encoded JSON to the log line under key.\n//\n// No sanity check is performed on b; it must not contain carriage returns and\n// be valid JSON.\nfunc (e *Event) RawJSON(key string, b []byte) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = appendJSON(enc.AppendKey(e.buf, key), b)\n\treturn e\n}\n\n// RawCBOR adds already encoded CBOR to the log line under key.\n//\n// No sanity check is performed on b\n// Note: The full featureset of CBOR is supported as data will not be mapped to json but stored as data-url\nfunc (e *Event) RawCBOR(key string, b []byte) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = appendCBOR(enc.AppendKey(e.buf, key), b)\n\treturn e\n}\n\n// AnErr adds the field key with serialized err to the *Event context.\n// If err is nil, no field is added.\nfunc (e *Event) AnErr(key string, err error) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\tswitch m := ErrorMarshalFunc(err).(type) {\n\tcase nil:\n\t\treturn e\n\tcase LogObjectMarshaler:\n\t\treturn e.Object(key, m)\n\tcase error:\n\t\tif isNilValue(m) {\n\t\t\treturn e\n\t\t}\n\t\treturn e.Str(key, m.Error())\n\tcase string:\n\t\treturn e.Str(key, m)\n\tdefault:\n\t\treturn e.Interface(key, m)\n\t}\n}\n\n// Errs adds the field key with errs as an array of serialized errors to the\n// *Event context.\nfunc (e *Event) Errs(key string, errs []error) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\tarr := e.CreateArray().Errs(errs)\n\treturn e.Array(key, arr)\n}\n\n// Err adds the field \"error\" with serialized err to the *Event context.\n// If err is nil, no field is added.\n//\n// To customize the key name, change zerolog.ErrorFieldName.\n//\n// If Stack() has been called before and zerolog.ErrorStackMarshaler is defined,\n// the err is passed to ErrorStackMarshaler and the result is appended to the\n// zerolog.ErrorStackFieldName.\nfunc (e *Event) Err(err error) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\n\tif e.stack && ErrorStackMarshaler != nil {\n\t\tswitch m := ErrorStackMarshaler(err).(type) {\n\t\tcase nil:\n\t\t\treturn e\n\t\tcase LogObjectMarshaler:\n\t\t\te = e.Object(ErrorStackFieldName, m)\n\t\tcase error:\n\t\t\te = e.Str(ErrorStackFieldName, m.Error())\n\t\tcase string:\n\t\t\te = e.Str(ErrorStackFieldName, m)\n\t\tdefault:\n\t\t\te = e.Interface(ErrorStackFieldName, m)\n\t\t}\n\t}\n\n\treturn e.AnErr(ErrorFieldName, err)\n}\n\n// Stack enables stack trace printing for the error passed to Err().\n//\n// ErrorStackMarshaler must be set for this method to do something.\nfunc (e *Event) Stack() *Event {\n\tif e != nil {\n\t\te.stack = true\n\t}\n\treturn e\n}\n\n// Ctx adds the Go Context to the *Event context.  The context is not rendered\n// in the output message, but is available to hooks and to Func() calls via the\n// GetCtx() accessor. A typical use case is to extract tracing information from\n// the Go Ctx.\nfunc (e *Event) Ctx(ctx context.Context) *Event {\n\tif e != nil {\n\t\te.ctx = ctx\n\t}\n\treturn e\n}\n\n// GetCtx retrieves the Go context.Context which is optionally stored in the\n// Event. This allows Hooks and functions passed to Func() to retrieve values\n// which are stored in the context.Context. This can be useful in tracing,\n// where span information is commonly propagated in the context.Context.\nfunc (e *Event) GetCtx() context.Context {\n\tif e == nil || e.ctx == nil {\n\t\treturn context.Background()\n\t}\n\treturn e.ctx\n}\n\n// Bool adds the field key with val as a bool to the *Event context.\nfunc (e *Event) Bool(key string, b bool) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendBool(enc.AppendKey(e.buf, key), b)\n\treturn e\n}\n\n// Bools adds the field key with val as a []bool to the *Event context.\nfunc (e *Event) Bools(key string, b []bool) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendBools(enc.AppendKey(e.buf, key), b)\n\treturn e\n}\n\n// Int adds the field key with i as a int to the *Event context.\nfunc (e *Event) Int(key string, i int) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendInt(enc.AppendKey(e.buf, key), i)\n\treturn e\n}\n\n// Ints adds the field key with i as a []int to the *Event context.\nfunc (e *Event) Ints(key string, i []int) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendInts(enc.AppendKey(e.buf, key), i)\n\treturn e\n}\n\n// Int8 adds the field key with i as a int8 to the *Event context.\nfunc (e *Event) Int8(key string, i int8) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendInt8(enc.AppendKey(e.buf, key), i)\n\treturn e\n}\n\n// Ints8 adds the field key with i as a []int8 to the *Event context.\nfunc (e *Event) Ints8(key string, i []int8) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendInts8(enc.AppendKey(e.buf, key), i)\n\treturn e\n}\n\n// Int16 adds the field key with i as a int16 to the *Event context.\nfunc (e *Event) Int16(key string, i int16) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendInt16(enc.AppendKey(e.buf, key), i)\n\treturn e\n}\n\n// Ints16 adds the field key with i as a []int16 to the *Event context.\nfunc (e *Event) Ints16(key string, i []int16) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendInts16(enc.AppendKey(e.buf, key), i)\n\treturn e\n}\n\n// Int32 adds the field key with i as a int32 to the *Event context.\nfunc (e *Event) Int32(key string, i int32) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendInt32(enc.AppendKey(e.buf, key), i)\n\treturn e\n}\n\n// Ints32 adds the field key with i as a []int32 to the *Event context.\nfunc (e *Event) Ints32(key string, i []int32) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendInts32(enc.AppendKey(e.buf, key), i)\n\treturn e\n}\n\n// Int64 adds the field key with i as a int64 to the *Event context.\nfunc (e *Event) Int64(key string, i int64) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendInt64(enc.AppendKey(e.buf, key), i)\n\treturn e\n}\n\n// Ints64 adds the field key with i as a []int64 to the *Event context.\nfunc (e *Event) Ints64(key string, i []int64) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendInts64(enc.AppendKey(e.buf, key), i)\n\treturn e\n}\n\n// Uint adds the field key with i as a uint to the *Event context.\nfunc (e *Event) Uint(key string, i uint) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendUint(enc.AppendKey(e.buf, key), i)\n\treturn e\n}\n\n// Uints adds the field key with i as a []int to the *Event context.\nfunc (e *Event) Uints(key string, i []uint) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendUints(enc.AppendKey(e.buf, key), i)\n\treturn e\n}\n\n// Uint8 adds the field key with i as a uint8 to the *Event context.\nfunc (e *Event) Uint8(key string, i uint8) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendUint8(enc.AppendKey(e.buf, key), i)\n\treturn e\n}\n\n// Uints8 adds the field key with i as a []int8 to the *Event context.\nfunc (e *Event) Uints8(key string, i []uint8) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendUints8(enc.AppendKey(e.buf, key), i)\n\treturn e\n}\n\n// Uint16 adds the field key with i as a uint16 to the *Event context.\nfunc (e *Event) Uint16(key string, i uint16) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendUint16(enc.AppendKey(e.buf, key), i)\n\treturn e\n}\n\n// Uints16 adds the field key with i as a []int16 to the *Event context.\nfunc (e *Event) Uints16(key string, i []uint16) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendUints16(enc.AppendKey(e.buf, key), i)\n\treturn e\n}\n\n// Uint32 adds the field key with i as a uint32 to the *Event context.\nfunc (e *Event) Uint32(key string, i uint32) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendUint32(enc.AppendKey(e.buf, key), i)\n\treturn e\n}\n\n// Uints32 adds the field key with i as a []int32 to the *Event context.\nfunc (e *Event) Uints32(key string, i []uint32) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendUints32(enc.AppendKey(e.buf, key), i)\n\treturn e\n}\n\n// Uint64 adds the field key with i as a uint64 to the *Event context.\nfunc (e *Event) Uint64(key string, i uint64) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendUint64(enc.AppendKey(e.buf, key), i)\n\treturn e\n}\n\n// Uints64 adds the field key with i as a []int64 to the *Event context.\nfunc (e *Event) Uints64(key string, i []uint64) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendUints64(enc.AppendKey(e.buf, key), i)\n\treturn e\n}\n\n// Float32 adds the field key with f as a float32 to the *Event context.\nfunc (e *Event) Float32(key string, f float32) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendFloat32(enc.AppendKey(e.buf, key), f, FloatingPointPrecision)\n\treturn e\n}\n\n// Floats32 adds the field key with f as a []float32 to the *Event context.\nfunc (e *Event) Floats32(key string, f []float32) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendFloats32(enc.AppendKey(e.buf, key), f, FloatingPointPrecision)\n\treturn e\n}\n\n// Float64 adds the field key with f as a float64 to the *Event context.\nfunc (e *Event) Float64(key string, f float64) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendFloat64(enc.AppendKey(e.buf, key), f, FloatingPointPrecision)\n\treturn e\n}\n\n// Floats64 adds the field key with f as a []float64 to the *Event context.\nfunc (e *Event) Floats64(key string, f []float64) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendFloats64(enc.AppendKey(e.buf, key), f, FloatingPointPrecision)\n\treturn e\n}\n\n// Timestamp adds the current local time as UNIX timestamp to the *Event context with the \"time\" key.\n// To customize the key name, change zerolog.TimestampFieldName.\n//\n// NOTE: It won't dedupe the \"time\" key if the *Event (or *Context) has one\n// already.\nfunc (e *Event) Timestamp() *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendTime(enc.AppendKey(e.buf, TimestampFieldName), TimestampFunc(), TimeFieldFormat)\n\treturn e\n}\n\n// Time adds the field key with t formatted as string using zerolog.TimeFieldFormat.\nfunc (e *Event) Time(key string, t time.Time) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendTime(enc.AppendKey(e.buf, key), t, TimeFieldFormat)\n\treturn e\n}\n\n// Times adds the field key with t formatted as string using zerolog.TimeFieldFormat.\nfunc (e *Event) Times(key string, t []time.Time) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendTimes(enc.AppendKey(e.buf, key), t, TimeFieldFormat)\n\treturn e\n}\n\n// Dur adds the field key with duration d stored as zerolog.DurationFieldUnit.\n// If zerolog.DurationFieldInteger is true, durations are rendered as integer\n// instead of float.\nfunc (e *Event) Dur(key string, d time.Duration) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendDuration(enc.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldFormat, DurationFieldInteger, FloatingPointPrecision)\n\treturn e\n}\n\n// Durs adds the field key with duration d stored as zerolog.DurationFieldUnit.\n// If zerolog.DurationFieldInteger is true, durations are rendered as integer\n// instead of float.\nfunc (e *Event) Durs(key string, d []time.Duration) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendDurations(enc.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldFormat, DurationFieldInteger, FloatingPointPrecision)\n\treturn e\n}\n\n// TimeDiff adds the field key with positive duration between time t and start.\n// If time t is not greater than start, duration will be 0.\n// Duration format follows the same principle as Dur().\nfunc (e *Event) TimeDiff(key string, t time.Time, start time.Time) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\tvar d time.Duration\n\tif t.After(start) {\n\t\td = t.Sub(start)\n\t}\n\te.buf = enc.AppendDuration(enc.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldFormat, DurationFieldInteger, FloatingPointPrecision)\n\treturn e\n}\n\n// Any is a wrapper around Event.Interface.\nfunc (e *Event) Any(key string, i interface{}) *Event {\n\treturn e.Interface(key, i)\n}\n\n// Interface adds the field key with i marshaled using reflection.\nfunc (e *Event) Interface(key string, i interface{}) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\tif obj, ok := i.(LogObjectMarshaler); ok {\n\t\treturn e.Object(key, obj)\n\t}\n\te.buf = enc.AppendInterface(enc.AppendKey(e.buf, key), i)\n\treturn e\n}\n\n// Type adds the field key with val's type using reflection.\nfunc (e *Event) Type(key string, val interface{}) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendType(enc.AppendKey(e.buf, key), val)\n\treturn e\n}\n\n// CallerSkipFrame instructs any future Caller calls to skip the specified number of frames.\n// This includes those added via hooks from the context.\nfunc (e *Event) CallerSkipFrame(skip int) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.skipFrame += skip\n\treturn e\n}\n\n// Caller adds the file:line of the caller with the zerolog.CallerFieldName key.\n// The argument skip is the number of stack frames to ascend\n// Skip If not passed, use the global variable CallerSkipFrameCount\nfunc (e *Event) Caller(skip ...int) *Event {\n\tsk := CallerSkipFrameCount\n\tif len(skip) > 0 {\n\t\tsk = skip[0] + CallerSkipFrameCount\n\t}\n\treturn e.caller(sk)\n}\n\nfunc (e *Event) caller(skip int) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\tif pc, file, line, ok := runtime.Caller(skip + e.skipFrame); ok {\n\t\te.buf = enc.AppendString(enc.AppendKey(e.buf, CallerFieldName), CallerMarshalFunc(pc, file, line))\n\t}\n\treturn e\n}\n\n// IPAddr adds the field key with ip as a net.IP IPv4 or IPv6 Address to the event\nfunc (e *Event) IPAddr(key string, ip net.IP) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendIPAddr(enc.AppendKey(e.buf, key), ip)\n\treturn e\n}\n\n// IPAddrs adds the field key with ip as a net.IP array of IPv4 or IPv6 Address to the event\nfunc (e *Event) IPAddrs(key string, ip []net.IP) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendIPAddrs(enc.AppendKey(e.buf, key), ip)\n\treturn e\n}\n\n// IPPrefix adds the field key with pfx as a net.IPNet IPv4 or IPv6 Prefix (address and mask) to the event\nfunc (e *Event) IPPrefix(key string, pfx net.IPNet) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendIPPrefix(enc.AppendKey(e.buf, key), pfx)\n\treturn e\n}\n\n// IPPrefixes the field key with pfx as a net.IPNet array of IPv4 or IPv6 Prefixes (address and mask) to the event\nfunc (e *Event) IPPrefixes(key string, pfx []net.IPNet) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendIPPrefixes(enc.AppendKey(e.buf, key), pfx)\n\treturn e\n}\n\n// MACAddr the field key with ha as a net.HardwareAddr MAC address to the event\nfunc (e *Event) MACAddr(key string, ha net.HardwareAddr) *Event {\n\tif e == nil {\n\t\treturn e\n\t}\n\te.buf = enc.AppendMACAddr(enc.AppendKey(e.buf, key), ha)\n\treturn e\n}\n"
  },
  {
    "path": "event_test.go",
    "content": "//go:build !binary_log\n// +build !binary_log\n\npackage zerolog\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n)\n\ntype nilError struct{}\n\nfunc (nilError) Error() string {\n\treturn \"nope\"\n}\n\nfunc TestEvent_AnErr(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\terr  error\n\t\twant string\n\t}{\n\t\t{\"nil\", nil, `{}`},\n\t\t{\"error\", errors.New(\"test\"), `{\"err\":\"test\"}`},\n\t\t{\"nil interface\", func() *nilError { return nil }(), `{}`},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tvar buf bytes.Buffer\n\t\t\te := newEvent(LevelWriterAdapter{&buf}, DebugLevel, false, nil, nil)\n\t\t\te = e.AnErr(\"err\", tt.err)\n\t\t\terr := e.write()\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"Event.AnErr() error: %v\", err)\n\t\t\t}\n\n\t\t\tif got, want := strings.TrimSpace(buf.String()), tt.want; got != want {\n\t\t\t\tt.Errorf(\"Event.AnErr() = %v, want %v\", got, want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestEvent_writeWithNil(t *testing.T) {\n\tvar e *Event = nil\n\tgot := e.write()\n\n\tvar want *Event = nil\n\tif got != nil {\n\t\tt.Errorf(\"Event.write() = %v, want %v\", got, want)\n\t}\n}\n\ntype loggableObject struct {\n\tmember string\n}\n\nfunc (o loggableObject) MarshalZerologObject(e *Event) {\n\te.Str(\"member\", o.member)\n}\n\nfunc TestEvent_Object(t *testing.T) {\n\tt.Run(\"ObjectWithNil\", func(t *testing.T) {\n\t\tvar buf bytes.Buffer\n\t\te := newEvent(LevelWriterAdapter{&buf}, DebugLevel, false, nil, nil)\n\t\te = e.Object(\"obj\", nil)\n\t\terr := e.write()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Event.Object() error: %v\", err)\n\t\t}\n\n\t\twant := `{\"obj\":null}`\n\t\tgot := strings.TrimSpace(buf.String())\n\t\tif got != want {\n\t\t\tt.Errorf(\"Event.Object()\\ngot:  %s\\nwant: %s\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"EmbedObjectWithNil\", func(t *testing.T) {\n\t\tvar buf bytes.Buffer\n\t\te := newEvent(LevelWriterAdapter{&buf}, DebugLevel, false, nil, nil)\n\t\te = e.EmbedObject(nil)\n\t\terr := e.write()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Event.EmbedObject() error: %v\", err)\n\t\t}\n\n\t\twant := \"{}\"\n\t\tgot := strings.TrimSpace(buf.String())\n\t\tif got != want {\n\t\t\tt.Errorf(\"Event.EmbedObject()\\ngot:  %s\\nwant: %s\", got, want)\n\t\t}\n\t})\n\n\ttype contextKeyType struct{}\n\tvar contextKey = contextKeyType{}\n\n\tcalled := false\n\tctxHook := HookFunc(func(e *Event, level Level, message string) {\n\t\tcalled = true\n\t\tctx := e.GetCtx()\n\t\tif ctx == nil {\n\t\t\tt.Errorf(\"expected context to be set in Event\")\n\t\t}\n\t\tval := ctx.Value(contextKey)\n\t\tif val == nil {\n\t\t\tt.Errorf(\"expected context value, got %v\", val)\n\t\t}\n\t\te.Str(\"ctxValue\", val.(string))\n\t\te.Bool(\"stackValue\", e.stack)\n\t})\n\n\tt.Run(\"ObjectWithFullContext\", func(t *testing.T) {\n\t\tcalled = false\n\t\tctx := context.WithValue(context.Background(), contextKey, \"ctx-object\")\n\n\t\tvar buf bytes.Buffer\n\t\te := newEvent(LevelWriterAdapter{&buf}, DebugLevel, true, ctx, []Hook{ctxHook})\n\t\te = e.Object(\"obj\", loggableObject{member: \"object-value\"})\n\t\te.Msg(\"hello\")\n\n\t\tif !called {\n\t\t\tt.Errorf(\"hook was not called\")\n\t\t}\n\n\t\twant := `{\"obj\":{\"member\":\"object-value\"},\"ctxValue\":\"ctx-object\",\"stackValue\":true,\"message\":\"hello\"}`\n\t\tgot := strings.TrimSpace(buf.String())\n\t\tif got != want {\n\t\t\tt.Errorf(\"Event.EmbedObject()\\ngot:  %s\\nwant: %s\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"EmbedObjectWithFullContext\", func(t *testing.T) {\n\t\tcalled = false\n\t\tctx := context.WithValue(context.Background(), contextKey, \"ctx-embed\")\n\n\t\tvar buf bytes.Buffer\n\t\te := newEvent(LevelWriterAdapter{&buf}, DebugLevel, false, ctx, []Hook{ctxHook})\n\t\te = e.EmbedObject(loggableObject{member: \"embedded-value\"})\n\t\te.Msg(\"hello\")\n\n\t\tif !called {\n\t\t\tt.Errorf(\"hook was not called\")\n\t\t}\n\n\t\twant := `{\"member\":\"embedded-value\",\"ctxValue\":\"ctx-embed\",\"stackValue\":false,\"message\":\"hello\"}`\n\t\tgot := strings.TrimSpace(buf.String())\n\t\tif got != want {\n\t\t\tt.Errorf(\"Event.EmbedObject()\\ngot:  %s\\nwant: %s\", got, want)\n\t\t}\n\t})\n}\n\nfunc TestEvent_WithNilEvent(t *testing.T) {\n\t// coverage for nil Event receiver for all types\n\tvar e *Event = nil\n\n\tfixtures := makeFieldFixtures()\n\ttypes := map[string]func() *Event{\n\t\t\"Array\": func() *Event {\n\t\t\tarr := e.CreateArray()\n\t\t\treturn e.Array(\"k\", arr)\n\t\t},\n\t\t\"Bool\": func() *Event {\n\t\t\treturn e.Bool(\"k\", fixtures.Bools[0])\n\t\t},\n\t\t\"Bools\": func() *Event {\n\t\t\treturn e.Bools(\"k\", fixtures.Bools)\n\t\t},\n\t\t\"Fields\": func() *Event {\n\t\t\treturn e.Fields(fixtures)\n\t\t},\n\t\t\"Int\": func() *Event {\n\t\t\treturn e.Int(\"k\", fixtures.Ints[0])\n\t\t},\n\t\t\"Ints\": func() *Event {\n\t\t\treturn e.Ints(\"k\", fixtures.Ints)\n\t\t},\n\t\t\"Int8\": func() *Event {\n\t\t\treturn e.Int8(\"k\", fixtures.Ints8[0])\n\t\t},\n\t\t\"Ints8\": func() *Event {\n\t\t\treturn e.Ints8(\"k\", fixtures.Ints8)\n\t\t},\n\t\t\"Int16\": func() *Event {\n\t\t\treturn e.Int16(\"k\", fixtures.Ints16[0])\n\t\t},\n\t\t\"Ints16\": func() *Event {\n\t\t\treturn e.Ints16(\"k\", fixtures.Ints16)\n\t\t},\n\t\t\"Int32\": func() *Event {\n\t\t\treturn e.Int32(\"k\", fixtures.Ints32[0])\n\t\t},\n\t\t\"Ints32\": func() *Event {\n\t\t\treturn e.Ints32(\"k\", fixtures.Ints32)\n\t\t},\n\t\t\"Int64\": func() *Event {\n\t\t\treturn e.Int64(\"k\", fixtures.Ints64[0])\n\t\t},\n\t\t\"Ints64\": func() *Event {\n\t\t\treturn e.Ints64(\"k\", fixtures.Ints64)\n\t\t},\n\t\t\"Uint\": func() *Event {\n\t\t\treturn e.Uint(\"k\", fixtures.Uints[0])\n\t\t},\n\t\t\"Uints\": func() *Event {\n\t\t\treturn e.Uints(\"k\", fixtures.Uints)\n\t\t},\n\t\t\"Uint8\": func() *Event {\n\t\t\treturn e.Uint8(\"k\", fixtures.Uints8[0])\n\t\t},\n\t\t\"Uints8\": func() *Event {\n\t\t\treturn e.Uints8(\"k\", fixtures.Uints8)\n\t\t},\n\t\t\"Uint16\": func() *Event {\n\t\t\treturn e.Uint16(\"k\", fixtures.Uints16[0])\n\t\t},\n\t\t\"Uints16\": func() *Event {\n\t\t\treturn e.Uints16(\"k\", fixtures.Uints16)\n\t\t},\n\t\t\"Uint32\": func() *Event {\n\t\t\treturn e.Uint32(\"k\", fixtures.Uints32[0])\n\t\t},\n\t\t\"Uints32\": func() *Event {\n\t\t\treturn e.Uints32(\"k\", fixtures.Uints32)\n\t\t},\n\t\t\"Uint64\": func() *Event {\n\t\t\treturn e.Uint64(\"k\", fixtures.Uints64[0])\n\t\t},\n\t\t\"Uints64\": func() *Event {\n\t\t\treturn e.Uints64(\"k\", fixtures.Uints64)\n\t\t},\n\t\t\"Float64\": func() *Event {\n\t\t\treturn e.Float64(\"k\", fixtures.Floats64[0])\n\t\t},\n\t\t\"Floats64\": func() *Event {\n\t\t\treturn e.Floats64(\"k\", fixtures.Floats64)\n\t\t},\n\t\t\"Float32\": func() *Event {\n\t\t\treturn e.Float32(\"k\", fixtures.Floats32[0])\n\t\t},\n\t\t\"Floats32\": func() *Event {\n\t\t\treturn e.Floats32(\"k\", fixtures.Floats32)\n\t\t},\n\t\t\"RawCBOR\": func() *Event {\n\t\t\treturn e.RawCBOR(\"k\", fixtures.RawCBOR)\n\t\t},\n\t\t\"RawJSON\": func() *Event {\n\t\t\treturn e.RawJSON(\"k\", fixtures.RawJSONs[0])\n\t\t},\n\t\t\"Str\": func() *Event {\n\t\t\treturn e.Str(\"k\", fixtures.Strings[0])\n\t\t},\n\t\t\"Strs\": func() *Event {\n\t\t\treturn e.Strs(\"k\", fixtures.Strings)\n\t\t},\n\t\t\"Stringers\": func() *Event {\n\t\t\treturn e.Stringers(\"k\", fixtures.Stringers)\n\t\t},\n\t\t\"Err\": func() *Event {\n\t\t\treturn e.Err(fixtures.Errs[0])\n\t\t},\n\t\t\"Errs\": func() *Event {\n\t\t\treturn e.Errs(\"k\", fixtures.Errs)\n\t\t},\n\t\t\"Ctx\": func() *Event {\n\t\t\treturn e.Ctx(fixtures.Ctx)\n\t\t},\n\t\t\"Time\": func() *Event {\n\t\t\treturn e.Time(\"k\", fixtures.Times[0])\n\t\t},\n\t\t\"Times\": func() *Event {\n\t\t\treturn e.Times(\"k\", fixtures.Times)\n\t\t},\n\t\t\"Dict\": func() *Event {\n\t\t\td := e.CreateDict()\n\t\t\td.Str(\"greeting\", \"hello\")\n\t\t\treturn e.Dict(\"k\", d)\n\t\t},\n\t\t\"Dur\": func() *Event {\n\t\t\treturn e.Dur(\"k\", fixtures.Durations[0])\n\t\t},\n\t\t\"Durs\": func() *Event {\n\t\t\treturn e.Durs(\"k\", fixtures.Durations)\n\t\t},\n\t\t\"Interface\": func() *Event {\n\t\t\treturn e.Interface(\"k\", fixtures.Interfaces[0])\n\t\t},\n\t\t\"Interfaces\": func() *Event {\n\t\t\treturn e.Interface(\"k\", fixtures.Interfaces)\n\t\t},\n\t\t\"Interface(Object)\": func() *Event {\n\t\t\treturn e.Interface(\"k\", fixtures.Objects[0])\n\t\t},\n\t\t\"Interface(Objects)\": func() *Event {\n\t\t\treturn e.Interface(\"k\", fixtures.Objects)\n\t\t},\n\t\t\"Object\": func() *Event {\n\t\t\treturn e.Object(\"k\", fixtures.Objects[0])\n\t\t},\n\t\t\"Objects\": func() *Event {\n\t\t\treturn e.Objects(\"k\", fixtures.Objects)\n\t\t},\n\t\t\"EmbedObject\": func() *Event {\n\t\t\treturn e.EmbedObject(fixtures.Objects[0])\n\t\t},\n\t\t\"Timestamp\": func() *Event {\n\t\t\treturn e.Timestamp()\n\t\t},\n\t\t\"IPAddr\": func() *Event {\n\t\t\treturn e.IPAddr(\"k\", fixtures.IPAddrs[0])\n\t\t},\n\t\t\"IPAddrs\": func() *Event {\n\t\t\treturn e.IPAddrs(\"k\", fixtures.IPAddrs)\n\t\t},\n\t\t\"IPPrefix\": func() *Event {\n\t\t\treturn e.IPPrefix(\"k\", fixtures.IPPfxs[0])\n\t\t},\n\t\t\"IPPrefixes\": func() *Event {\n\t\t\treturn e.IPPrefixes(\"k\", fixtures.IPPfxs)\n\t\t},\n\t\t\"MACAddr\": func() *Event {\n\t\t\treturn e.MACAddr(\"k\", fixtures.MACAddr)\n\t\t},\n\t\t\"Type\": func() *Event {\n\t\t\treturn e.Type(\"k\", fixtures.Type)\n\t\t},\n\t\t\"Caller\": func() *Event {\n\t\t\treturn e.Caller(1)\n\t\t},\n\t\t\"CallerSkip\": func() *Event {\n\t\t\treturn e.CallerSkipFrame(2)\n\t\t},\n\t\t\"Stack\": func() *Event {\n\t\t\treturn e.Stack()\n\t\t},\n\t}\n\n\tfor name := range types {\n\t\tf := types[name]\n\t\tif got := f(); got != nil {\n\t\t\tt.Errorf(\"Event.Bool() = %v, want %v\", got, nil)\n\t\t}\n\t}\n\n\te.Send()\n\te.Msg(\"nothing\")\n\te.Msgf(\"what %s\", \"nothing\")\n\n\tgot := e.write()\n\tif got != nil {\n\t\tt.Errorf(\"Event.write() = %v, want %v\", got, e)\n\t}\n\n\tcalled := false\n\te.MsgFunc(func() string {\n\t\tcalled = true\n\t\treturn \"called\"\n\t})\n\tif called {\n\t\tt.Errorf(\"Event.MsgFunc() should not be called on nil Event\")\n\t}\n}\n\nfunc TestEvent_MsgFunc(t *testing.T) {\n\tvar buf bytes.Buffer\n\te := newEvent(LevelWriterAdapter{&buf}, DebugLevel, false, nil, nil)\n\n\tcalled := false\n\te.MsgFunc(func() string {\n\t\tcalled = true\n\t\treturn \"called\"\n\t})\n\tif !called {\n\t\tt.Errorf(\"Event.MsgFunc() was not called on non-nil Event\")\n\t}\n\n\twant := `{\"message\":\"called\"}`\n\tgot := strings.TrimSpace(buf.String())\n\tif got != want {\n\t\tt.Errorf(\"Event.MsgFunc() = %q, want %q\", got, want)\n\t}\n}\n\nfunc TestEvent_CallerRuntimeFail(t *testing.T) {\n\tvar buf bytes.Buffer\n\te := newEvent(LevelWriterAdapter{&buf}, DebugLevel, false, nil, nil)\n\n\t// Set a very large skipFrame to make runtime.Caller fail\n\te.CallerSkipFrame(1000)\n\te.Caller()\n\n\te.Msg(\"test\")\n\n\tgot := strings.TrimSpace(buf.String())\n\twant := `{\"message\":\"test\"}` // No caller field because runtime.Caller failed\n\tif got != want {\n\t\tt.Errorf(\"Event.Caller() with failed runtime.Caller = %q, want %q\", got, want)\n\t}\n}\n\nfunc TestEvent_DoneHandler(t *testing.T) {\n\te := newEvent(nil, InfoLevel, false, nil, nil)\n\n\t// Set up a done handler to capture calls\n\tvar called bool\n\tvar capturedMsg string\n\te.done = func(msg string) {\n\t\tcalled = true\n\t\tcapturedMsg = msg\n\t}\n\n\t// Trigger msg via Msg\n\te.Msg(\"test message\")\n\n\t// Assert the handler was called with the correct message\n\tif !called {\n\t\tt.Error(\"Done handler was not called\")\n\t}\n\tif capturedMsg != \"test message\" {\n\t\tt.Errorf(\"Expected message 'test message', got '%s'\", capturedMsg)\n\t}\n}\n\ntype badLevelWriter struct {\n\terr error\n}\n\nfunc (w *badLevelWriter) WriteLevel(level Level, p []byte) (n int, err error) {\n\treturn 0, w.err\n}\n\nfunc (w *badLevelWriter) Write(p []byte) (n int, err error) {\n\treturn 0, w.err\n}\n\nfunc TestEvent_Msg_ErrorHandlerNil(t *testing.T) {\n\t// Save original ErrorHandler and restore after test\n\toriginalErrorHandler := ErrorHandler\n\tErrorHandler = nil\n\tdefer func() { ErrorHandler = originalErrorHandler }()\n\n\t// Create a LevelWriter that always returns an error\n\tmockWriter := &badLevelWriter{err: errors.New(\"write error\")}\n\n\te := newEvent(mockWriter, InfoLevel, false, nil, nil)\n\tif e == nil {\n\t\tt.Fatal(\"Event should not be nil\")\n\t}\n\n\t// Capture stderr\n\toldStderr := os.Stderr\n\tr, w, err := os.Pipe()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tos.Stderr = w\n\n\t// Call Msg to trigger write error\n\te.Msg(\"test message\")\n\n\t// Restore stderr and read captured output\n\tw.Close()\n\tos.Stderr = oldStderr\n\tcaptured, err := io.ReadAll(r)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// Assert the error message was printed to stderr\n\texpected := \"zerolog: could not write event: write error\\n\"\n\tif string(captured) != expected {\n\t\tt.Errorf(\"Expected stderr output %q, got %q\", expected, string(captured))\n\t}\n}\n\ntype mockLogObjectMarshaler struct {\n\tdata string\n}\n\nfunc (m mockLogObjectMarshaler) MarshalZerologObject(e *Event) {\n\te.Str(\"stack_func\", m.data)\n}\n\nfunc TestEvent_ErrWithStackMarshaler(t *testing.T) {\n\t// Save original\n\toriginal := ErrorStackMarshaler\n\tdefer func() { ErrorStackMarshaler = original }()\n\n\t// Set a mock marshaler\n\tErrorStackMarshaler = func(err error) interface{} {\n\t\treturn \"stack-trace\"\n\t}\n\n\tvar buf bytes.Buffer\n\tlog := New(&buf)\n\n\terr := errors.New(\"test error\")\n\tlog.Log().Stack().Err(err).Msg(\"test message\")\n\n\tgot := buf.String()\n\twant := `{\"stack\":\"stack-trace\",\"error\":\"test error\",\"message\":\"test message\"}` + \"\\n\"\n\tif got != want {\n\t\tt.Errorf(\"Event.Err() with stack marshaler = %q, want %q\", got, want)\n\t}\n}\n\nfunc TestEvent_FieldsWithErrorAndStackMarshaler(t *testing.T) {\n\t// Save original\n\toriginal := ErrorStackMarshaler\n\tdefer func() { ErrorStackMarshaler = original }()\n\n\t// Set a mock marshaler\n\tErrorStackMarshaler = func(err error) interface{} {\n\t\treturn \"stack-trace\"\n\t}\n\n\tvar buf bytes.Buffer\n\tlog := New(&buf)\n\n\terr := errors.New(\"test error\")\n\tlog.Log().Stack().Fields([]interface{}{\"error\", err}).Msg(\"test message\")\n\n\tgot := buf.String()\n\twant := `{\"error\":\"test error\",\"stack\":\"stack-trace\",\"message\":\"test message\"}` + \"\\n\"\n\tif got != want {\n\t\tt.Errorf(\"Event.Fields() with error and stack marshaler = %q, want %q\", got, want)\n\t}\n}\n\nfunc TestEvent_FieldsWithErrorAndStackMarshalerObject(t *testing.T) {\n\t// Save original\n\toriginal := ErrorStackMarshaler\n\tdefer func() { ErrorStackMarshaler = original }()\n\n\t// Set a mock marshaler that returns LogObjectMarshaler\n\tErrorStackMarshaler = func(err error) interface{} {\n\t\treturn mockLogObjectMarshaler{data: \"stack-data\"}\n\t}\n\n\tvar buf bytes.Buffer\n\tlog := New(&buf)\n\n\terr := errors.New(\"test error\")\n\tlog.Log().Stack().Fields([]interface{}{\"error\", err}).Msg(\"test message\")\n\n\tgot := buf.String()\n\twant := `{\"error\":\"test error\",\"stack\":{\"stack_func\":\"stack-data\"},\"message\":\"test message\"}` + \"\\n\"\n\tif got != want {\n\t\tt.Errorf(\"Event.Fields() with error and stack marshaler object = %q, want %q\", got, want)\n\t}\n}\n\nfunc TestEvent_FieldsWithErrorAndStackMarshalerError(t *testing.T) {\n\t// Save original\n\toriginal := ErrorStackMarshaler\n\tdefer func() { ErrorStackMarshaler = original }()\n\n\t// Set a mock marshaler that returns an error\n\tErrorStackMarshaler = func(err error) interface{} {\n\t\treturn errors.New(\"stack error\")\n\t}\n\n\tvar buf bytes.Buffer\n\tlog := New(&buf)\n\n\terr := errors.New(\"test error\")\n\tlog.Log().Stack().Fields([]interface{}{\"error\", err}).Msg(\"test message\")\n\n\tgot := buf.String()\n\twant := `{\"error\":\"test error\",\"stack\":\"stack error\",\"message\":\"test message\"}` + \"\\n\"\n\tif got != want {\n\t\tt.Errorf(\"Event.Fields() with error and stack marshaler error = %q, want %q\", got, want)\n\t}\n}\n\nfunc TestEvent_FieldsWithErrorAndStackMarshalerInterface(t *testing.T) {\n\t// Save original\n\toriginal := ErrorStackMarshaler\n\tdefer func() { ErrorStackMarshaler = original }()\n\n\t// Set a mock marshaler that returns an int\n\tErrorStackMarshaler = func(err error) interface{} {\n\t\treturn 42\n\t}\n\n\tvar buf bytes.Buffer\n\tlog := New(&buf)\n\n\terr := errors.New(\"test error\")\n\tlog.Log().Stack().Fields([]interface{}{\"error\", err}).Msg(\"test message\")\n\n\tgot := buf.String()\n\twant := `{\"error\":\"test error\",\"stack\":42,\"message\":\"test message\"}` + \"\\n\"\n\tif got != want {\n\t\tt.Errorf(\"Event.Fields() with error and stack marshaler interface = %q, want %q\", got, want)\n\t}\n}\n\nfunc TestEvent_FieldsWithErrorAndStackMarshalerNil(t *testing.T) {\n\t// Save original\n\toriginal := ErrorStackMarshaler\n\tdefer func() { ErrorStackMarshaler = original }()\n\n\t// Set marshaler to return nil\n\tErrorStackMarshaler = func(err error) interface{} {\n\t\treturn nil\n\t}\n\n\tvar buf bytes.Buffer\n\tlog := New(&buf)\n\n\terr := errors.New(\"test error\")\n\tlog.Log().Stack().Fields([]interface{}{\"error\", err}).Msg(\"test message\")\n\n\tgot := buf.String()\n\twant := `{\"error\":\"test error\",\"message\":\"test message\"}` + \"\\n\" // No stack field because marshaler returned nil\n\tif got != want {\n\t\tt.Errorf(\"Event.Fields() with error and nil stack marshaler = %q, want %q\", got, want)\n\t}\n}\n\nfunc TestEvent_ErrWithStackMarshalerObject(t *testing.T) {\n\t// Save original\n\toriginal := ErrorStackMarshaler\n\tdefer func() { ErrorStackMarshaler = original }()\n\n\t// Set a mock marshaler that returns LogObjectMarshaler\n\tErrorStackMarshaler = func(err error) interface{} {\n\t\treturn mockLogObjectMarshaler{data: \"stack-data\"}\n\t}\n\n\tvar buf bytes.Buffer\n\tlog := New(&buf)\n\n\terr := errors.New(\"test error\")\n\tlog.Log().Stack().Err(err).Msg(\"test message\")\n\n\tgot := buf.String()\n\twant := `{\"stack\":{\"stack_func\":\"stack-data\"},\"error\":\"test error\",\"message\":\"test message\"}` + \"\\n\"\n\tif got != want {\n\t\tt.Errorf(\"Event.Err() with stack marshaler object = %q, want %q\", got, want)\n\t}\n}\n\nfunc TestEvent_ErrWithStackMarshalerError(t *testing.T) {\n\t// Save original\n\toriginal := ErrorStackMarshaler\n\tdefer func() { ErrorStackMarshaler = original }()\n\n\t// Set a mock marshaler that returns an error\n\tErrorStackMarshaler = func(err error) interface{} {\n\t\treturn errors.New(\"stack error\")\n\t}\n\n\tvar buf bytes.Buffer\n\tlog := New(&buf)\n\n\terr := errors.New(\"test error\")\n\tlog.Log().Stack().Err(err).Msg(\"test message\")\n\n\tgot := buf.String()\n\twant := `{\"stack\":\"stack error\",\"error\":\"test error\",\"message\":\"test message\"}` + \"\\n\"\n\tif got != want {\n\t\tt.Errorf(\"Event.Err() with stack marshaler error = %q, want %q\", got, want)\n\t}\n}\n\nfunc TestEvent_ErrWithStackMarshalerInterface(t *testing.T) {\n\t// Save original\n\toriginal := ErrorStackMarshaler\n\tdefer func() { ErrorStackMarshaler = original }()\n\n\t// Set a mock marshaler that returns an int\n\tErrorStackMarshaler = func(err error) interface{} {\n\t\treturn 42\n\t}\n\n\tvar buf bytes.Buffer\n\tlog := New(&buf)\n\n\terr := errors.New(\"test error\")\n\tlog.Log().Stack().Err(err).Msg(\"test message\")\n\n\tgot := buf.String()\n\twant := `{\"stack\":42,\"error\":\"test error\",\"message\":\"test message\"}` + \"\\n\"\n\tif got != want {\n\t\tt.Errorf(\"Event.Err() with stack marshaler interface = %q, want %q\", got, want)\n\t}\n}\n\nfunc TestEvent_ErrWithStackMarshalerNil(t *testing.T) {\n\t// Save original\n\toriginal := ErrorStackMarshaler\n\tdefer func() { ErrorStackMarshaler = original }()\n\n\t// Set marshaler to return nil\n\tErrorStackMarshaler = func(err error) interface{} {\n\t\treturn nil\n\t}\n\n\tvar buf bytes.Buffer\n\tlog := New(&buf)\n\n\terr := errors.New(\"test error\")\n\tlog.Log().Stack().Err(err).Msg(\"test message\")\n\n\tgot := buf.String()\n\twant := `{\"message\":\"test message\"}` + \"\\n\" // No fields because stack marshaler returned nil\n\tif got != want {\n\t\tt.Errorf(\"Event.Err() with nil stack marshaler = %q, want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "example.jsonl",
    "content": "{\"time\":\"5:41PM\",\"level\":\"info\",\"message\":\"Starting listener\",\"listen\":\":8080\",\"pid\":37556}\n{\"time\":\"5:41PM\",\"level\":\"debug\",\"message\":\"Access\",\"database\":\"myapp\",\"host\":\"localhost:4962\",\"pid\":37556}\n{\"time\":\"5:41PM\",\"level\":\"info\",\"message\":\"Access\",\"method\":\"GET\",\"path\":\"/users\",\"pid\":37556,\"resp_time\":23}\n{\"time\":\"5:41PM\",\"level\":\"info\",\"message\":\"Access\",\"method\":\"POST\",\"path\":\"/posts\",\"pid\":37556,\"resp_time\":532}\n{\"time\":\"5:41PM\",\"level\":\"warn\",\"message\":\"Slow request\",\"method\":\"POST\",\"path\":\"/posts\",\"pid\":37556,\"resp_time\":532}\n{\"time\":\"5:41PM\",\"level\":\"info\",\"message\":\"Access\",\"method\":\"GET\",\"path\":\"/users\",\"pid\":37556,\"resp_time\":10}\n{\"time\":\"5:41PM\",\"level\":\"error\",\"message\":\"Database connection lost\",\"database\":\"myapp\",\"pid\":37556,\"error\":\"connection reset by peer\"}\n"
  },
  {
    "path": "fields.go",
    "content": "package zerolog\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"net\"\n\t\"reflect\"\n\t\"sort\"\n\t\"time\"\n)\n\nfunc isNilValue(e error) bool {\n\tswitch reflect.TypeOf(e).Kind() {\n\tcase reflect.Ptr:\n\t\treturn reflect.ValueOf(e).IsNil()\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc appendFields(dst []byte, fields interface{}, stack bool, ctx context.Context, hooks []Hook) []byte {\n\tswitch fields := fields.(type) {\n\tcase []interface{}:\n\t\tif n := len(fields); n&0x1 == 1 { // odd number\n\t\t\tfields = fields[:n-1]\n\t\t}\n\t\tdst = appendFieldList(dst, fields, stack, ctx, hooks)\n\tcase map[string]interface{}:\n\t\tkeys := make([]string, 0, len(fields))\n\t\tfor key := range fields {\n\t\t\tkeys = append(keys, key)\n\t\t}\n\t\tsort.Strings(keys)\n\t\tkv := make([]interface{}, 2)\n\t\tfor _, key := range keys {\n\t\t\tkv[0], kv[1] = key, fields[key]\n\t\t\tdst = appendFieldList(dst, kv, stack, ctx, hooks)\n\t\t}\n\t}\n\treturn dst\n}\n\nfunc appendObject(dst []byte, obj LogObjectMarshaler, stack bool, ctx context.Context, hooks []Hook) []byte {\n\te := newEvent(LevelWriterAdapter{io.Discard}, DebugLevel, stack, ctx, hooks)\n\te.buf = e.buf[:0] // discard the beginning marker added by newEvent\n\te.appendObject(obj)\n\tdst = append(dst, e.buf...)\n\tputEvent(e)\n\treturn dst\n}\n\nfunc appendFieldList(dst []byte, kvList []interface{}, stack bool, ctx context.Context, hooks []Hook) []byte {\n\tfor i, n := 0, len(kvList); i < n; i += 2 {\n\t\tkey, val := kvList[i], kvList[i+1]\n\t\tif key, ok := key.(string); ok {\n\t\t\tdst = enc.AppendKey(dst, key)\n\t\t} else {\n\t\t\tcontinue\n\t\t}\n\t\tswitch val := val.(type) {\n\t\tcase string:\n\t\t\tdst = enc.AppendString(dst, val)\n\t\tcase []byte:\n\t\t\tdst = enc.AppendBytes(dst, val)\n\t\tcase error:\n\t\t\tswitch m := ErrorMarshalFunc(val).(type) {\n\t\t\tcase nil:\n\t\t\t\tdst = enc.AppendNil(dst)\n\t\t\tcase LogObjectMarshaler:\n\t\t\t\tdst = appendObject(dst, m, stack, ctx, hooks)\n\t\t\tcase error:\n\t\t\t\tif !isNilValue(m) {\n\t\t\t\t\tdst = enc.AppendString(dst, m.Error())\n\t\t\t\t}\n\t\t\tcase string:\n\t\t\t\tdst = enc.AppendString(dst, m)\n\t\t\tdefault:\n\t\t\t\tdst = enc.AppendInterface(dst, m)\n\t\t\t}\n\n\t\t\tif stack && ErrorStackMarshaler != nil {\n\t\t\t\tswitch m := ErrorStackMarshaler(val).(type) {\n\t\t\t\tcase nil:\n\t\t\t\t\treturn dst // do nothing with nil errors\n\t\t\t\tcase LogObjectMarshaler:\n\t\t\t\t\tdst = enc.AppendKey(dst, ErrorStackFieldName)\n\t\t\t\t\tdst = appendObject(dst, m, stack, ctx, hooks)\n\t\t\t\tcase error:\n\t\t\t\t\tdst = enc.AppendKey(dst, ErrorStackFieldName)\n\t\t\t\t\tdst = enc.AppendString(dst, m.Error())\n\t\t\t\tcase string:\n\t\t\t\t\tdst = enc.AppendKey(dst, ErrorStackFieldName)\n\t\t\t\t\tdst = enc.AppendString(dst, m)\n\t\t\t\tdefault:\n\t\t\t\t\tdst = enc.AppendKey(dst, ErrorStackFieldName)\n\t\t\t\t\tdst = enc.AppendInterface(dst, m)\n\t\t\t\t}\n\t\t\t}\n\t\tcase []error:\n\t\t\tdst = enc.AppendArrayStart(dst)\n\t\t\tfor i, err := range val {\n\t\t\t\tswitch m := ErrorMarshalFunc(err).(type) {\n\t\t\t\tcase nil:\n\t\t\t\t\tdst = enc.AppendNil(dst)\n\t\t\t\tcase LogObjectMarshaler:\n\t\t\t\t\tdst = appendObject(dst, m, stack, ctx, hooks)\n\t\t\t\tcase error:\n\t\t\t\t\tif !isNilValue(m) {\n\t\t\t\t\t\tdst = enc.AppendString(dst, m.Error())\n\t\t\t\t\t}\n\t\t\t\tcase string:\n\t\t\t\t\tdst = enc.AppendString(dst, m)\n\t\t\t\tdefault:\n\t\t\t\t\tdst = enc.AppendInterface(dst, m)\n\t\t\t\t}\n\n\t\t\t\tif i < (len(val) - 1) {\n\t\t\t\t\tdst = enc.AppendArrayDelim(dst)\n\t\t\t\t}\n\t\t\t}\n\t\t\tdst = enc.AppendArrayEnd(dst)\n\t\tcase []LogObjectMarshaler:\n\t\t\tdst = enc.AppendArrayStart(dst)\n\t\t\tfor i, obj := range val {\n\t\t\t\tdst = appendObject(dst, obj, stack, ctx, hooks)\n\t\t\t\tif i < (len(val) - 1) {\n\t\t\t\t\tdst = enc.AppendArrayDelim(dst)\n\t\t\t\t}\n\t\t\t}\n\t\t\tdst = enc.AppendArrayEnd(dst)\n\t\tcase bool:\n\t\t\tdst = enc.AppendBool(dst, val)\n\t\tcase int:\n\t\t\tdst = enc.AppendInt(dst, val)\n\t\tcase int8:\n\t\t\tdst = enc.AppendInt8(dst, val)\n\t\tcase int16:\n\t\t\tdst = enc.AppendInt16(dst, val)\n\t\tcase int32:\n\t\t\tdst = enc.AppendInt32(dst, val)\n\t\tcase int64:\n\t\t\tdst = enc.AppendInt64(dst, val)\n\t\tcase uint:\n\t\t\tdst = enc.AppendUint(dst, val)\n\t\tcase uint8:\n\t\t\tdst = enc.AppendUint8(dst, val)\n\t\tcase uint16:\n\t\t\tdst = enc.AppendUint16(dst, val)\n\t\tcase uint32:\n\t\t\tdst = enc.AppendUint32(dst, val)\n\t\tcase uint64:\n\t\t\tdst = enc.AppendUint64(dst, val)\n\t\tcase float32:\n\t\t\tdst = enc.AppendFloat32(dst, val, FloatingPointPrecision)\n\t\tcase float64:\n\t\t\tdst = enc.AppendFloat64(dst, val, FloatingPointPrecision)\n\t\tcase time.Time:\n\t\t\tdst = enc.AppendTime(dst, val, TimeFieldFormat)\n\t\tcase time.Duration:\n\t\t\tdst = enc.AppendDuration(dst, val, DurationFieldUnit, DurationFieldFormat, DurationFieldInteger, FloatingPointPrecision)\n\t\tcase *string:\n\t\t\tif val != nil {\n\t\t\t\tdst = enc.AppendString(dst, *val)\n\t\t\t} else {\n\t\t\t\tdst = enc.AppendNil(dst)\n\t\t\t}\n\t\tcase *bool:\n\t\t\tif val != nil {\n\t\t\t\tdst = enc.AppendBool(dst, *val)\n\t\t\t} else {\n\t\t\t\tdst = enc.AppendNil(dst)\n\t\t\t}\n\t\tcase *int:\n\t\t\tif val != nil {\n\t\t\t\tdst = enc.AppendInt(dst, *val)\n\t\t\t} else {\n\t\t\t\tdst = enc.AppendNil(dst)\n\t\t\t}\n\t\tcase *int8:\n\t\t\tif val != nil {\n\t\t\t\tdst = enc.AppendInt8(dst, *val)\n\t\t\t} else {\n\t\t\t\tdst = enc.AppendNil(dst)\n\t\t\t}\n\t\tcase *int16:\n\t\t\tif val != nil {\n\t\t\t\tdst = enc.AppendInt16(dst, *val)\n\t\t\t} else {\n\t\t\t\tdst = enc.AppendNil(dst)\n\t\t\t}\n\t\tcase *int32:\n\t\t\tif val != nil {\n\t\t\t\tdst = enc.AppendInt32(dst, *val)\n\t\t\t} else {\n\t\t\t\tdst = enc.AppendNil(dst)\n\t\t\t}\n\t\tcase *int64:\n\t\t\tif val != nil {\n\t\t\t\tdst = enc.AppendInt64(dst, *val)\n\t\t\t} else {\n\t\t\t\tdst = enc.AppendNil(dst)\n\t\t\t}\n\t\tcase *uint:\n\t\t\tif val != nil {\n\t\t\t\tdst = enc.AppendUint(dst, *val)\n\t\t\t} else {\n\t\t\t\tdst = enc.AppendNil(dst)\n\t\t\t}\n\t\tcase *uint8:\n\t\t\tif val != nil {\n\t\t\t\tdst = enc.AppendUint8(dst, *val)\n\t\t\t} else {\n\t\t\t\tdst = enc.AppendNil(dst)\n\t\t\t}\n\t\tcase *uint16:\n\t\t\tif val != nil {\n\t\t\t\tdst = enc.AppendUint16(dst, *val)\n\t\t\t} else {\n\t\t\t\tdst = enc.AppendNil(dst)\n\t\t\t}\n\t\tcase *uint32:\n\t\t\tif val != nil {\n\t\t\t\tdst = enc.AppendUint32(dst, *val)\n\t\t\t} else {\n\t\t\t\tdst = enc.AppendNil(dst)\n\t\t\t}\n\t\tcase *uint64:\n\t\t\tif val != nil {\n\t\t\t\tdst = enc.AppendUint64(dst, *val)\n\t\t\t} else {\n\t\t\t\tdst = enc.AppendNil(dst)\n\t\t\t}\n\t\tcase *float32:\n\t\t\tif val != nil {\n\t\t\t\tdst = enc.AppendFloat32(dst, *val, FloatingPointPrecision)\n\t\t\t} else {\n\t\t\t\tdst = enc.AppendNil(dst)\n\t\t\t}\n\t\tcase *float64:\n\t\t\tif val != nil {\n\t\t\t\tdst = enc.AppendFloat64(dst, *val, FloatingPointPrecision)\n\t\t\t} else {\n\t\t\t\tdst = enc.AppendNil(dst)\n\t\t\t}\n\t\tcase *time.Time:\n\t\t\tif val != nil {\n\t\t\t\tdst = enc.AppendTime(dst, *val, TimeFieldFormat)\n\t\t\t} else {\n\t\t\t\tdst = enc.AppendNil(dst)\n\t\t\t}\n\t\tcase *time.Duration:\n\t\t\tif val != nil {\n\t\t\t\tdst = enc.AppendDuration(dst, *val, DurationFieldUnit, DurationFieldFormat, DurationFieldInteger, FloatingPointPrecision)\n\t\t\t} else {\n\t\t\t\tdst = enc.AppendNil(dst)\n\t\t\t}\n\t\tcase []string:\n\t\t\tdst = enc.AppendStrings(dst, val)\n\t\tcase []bool:\n\t\t\tdst = enc.AppendBools(dst, val)\n\t\tcase []int:\n\t\t\tdst = enc.AppendInts(dst, val)\n\t\tcase []int8:\n\t\t\tdst = enc.AppendInts8(dst, val)\n\t\tcase []int16:\n\t\t\tdst = enc.AppendInts16(dst, val)\n\t\tcase []int32:\n\t\t\tdst = enc.AppendInts32(dst, val)\n\t\tcase []int64:\n\t\t\tdst = enc.AppendInts64(dst, val)\n\t\tcase []uint:\n\t\t\tdst = enc.AppendUints(dst, val)\n\t\t// case []uint8: is handled as []byte above\n\t\tcase []uint16:\n\t\t\tdst = enc.AppendUints16(dst, val)\n\t\tcase []uint32:\n\t\t\tdst = enc.AppendUints32(dst, val)\n\t\tcase []uint64:\n\t\t\tdst = enc.AppendUints64(dst, val)\n\t\tcase []float32:\n\t\t\tdst = enc.AppendFloats32(dst, val, FloatingPointPrecision)\n\t\tcase []float64:\n\t\t\tdst = enc.AppendFloats64(dst, val, FloatingPointPrecision)\n\t\tcase []time.Time:\n\t\t\tdst = enc.AppendTimes(dst, val, TimeFieldFormat)\n\t\tcase []time.Duration:\n\t\t\tdst = enc.AppendDurations(dst, val, DurationFieldUnit, DurationFieldFormat, DurationFieldInteger, FloatingPointPrecision)\n\t\tcase nil:\n\t\t\tdst = enc.AppendNil(dst)\n\t\tcase net.IP:\n\t\t\tdst = enc.AppendIPAddr(dst, val)\n\t\tcase []net.IP:\n\t\t\tdst = enc.AppendIPAddrs(dst, val)\n\t\tcase net.IPNet:\n\t\t\tdst = enc.AppendIPPrefix(dst, val)\n\t\tcase []net.IPNet:\n\t\t\tdst = enc.AppendIPPrefixes(dst, val)\n\t\tcase net.HardwareAddr:\n\t\t\tdst = enc.AppendMACAddr(dst, val)\n\t\tcase json.RawMessage:\n\t\t\tdst = appendJSON(dst, val)\n\t\tdefault:\n\t\t\tif lom, ok := val.(LogObjectMarshaler); ok {\n\t\t\t\tdst = appendObject(dst, lom, stack, ctx, hooks)\n\t\t\t} else {\n\t\t\t\tdst = enc.AppendInterface(dst, val)\n\t\t\t}\n\t\t}\n\t}\n\treturn dst\n}\n"
  },
  {
    "path": "fixtures_test.go",
    "content": "package zerolog\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"reflect\"\n\t\"time\"\n)\n\ntype fixtureObj struct {\n\tPub  string\n\tTag  string `json:\"tag\"`\n\tpriv int\n}\n\nfunc (o fixtureObj) MarshalZerologObject(e *Event) {\n\te.Str(\"Pub\", o.Pub).\n\t\tStr(\"Tag\", o.Tag).\n\t\tInt(\"priv\", o.priv)\n}\n\ntype fieldFixtures struct {\n\tBools      []bool\n\tBytes      []byte\n\tCtx        context.Context\n\tDurations  []time.Duration\n\tErrs       []error\n\tFloats32   []float32\n\tFloats64   []float64\n\tInterfaces []struct {\n\t\tPub  string\n\t\tTag  string `json:\"tag\"`\n\t\tpriv int\n\t}\n\tInts      []int\n\tInts8     []int8\n\tInts16    []int16\n\tInts32    []int32\n\tInts64    []int64\n\tUints     []uint\n\tUints8    []uint8\n\tUints16   []uint16\n\tUints32   []uint32\n\tUints64   []uint64\n\tIPAddrs   []net.IP\n\tIPPfxs    []net.IPNet\n\tMACAddr   net.HardwareAddr\n\tObjects   []LogObjectMarshaler\n\tRawCBOR   []byte\n\tRawJSONs  [][]byte\n\tStringers []fmt.Stringer\n\tStrings   []string\n\tTimes     []time.Time\n\tType      reflect.Type\n}\n\nfunc makeFieldFixtures() *fieldFixtures {\n\tbools := []bool{true, false, true, false, true, false, true, false, true, false}\n\tbytes := []byte(`abcdef`)\n\tints := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}\n\tints8 := []int8{-8, 8}\n\tints16 := []int16{-16, 16}\n\tints32 := []int32{-32, 32}\n\tints64 := []int64{-64, 64}\n\tuints := []uint{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}\n\tuints8 := []uint8{8, uint8(^uint8(0))}\n\tuints16 := []uint16{16, uint16(^uint16(0))}\n\tuints32 := []uint32{32, uint32(^uint32(0))}\n\tuints64 := []uint64{64, uint64(^uint64(0))}\n\tfloats32 := []float32{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}\n\tfloats64 := []float64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}\n\tstrings := []string{\"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\", \"i\", \"j\"}\n\tdurations := []time.Duration{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}\n\ttimes := []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\tinterfaces := []struct {\n\t\tPub  string\n\t\tTag  string `json:\"tag\"`\n\t\tpriv int\n\t}{\n\t\t{\"A\", \"j\", -5},\n\t\t{\"B\", \"i\", -4},\n\t\t{\"C\", \"h\", -3},\n\t\t{\"D\", \"g\", -2},\n\t\t{\"E\", \"f\", -1},\n\t\t{\"F\", \"e\", 0},\n\t\t{\"G\", \"d\", 1},\n\t\t{\"H\", \"c\", 2},\n\t\t{\"I\", \"b\", 3},\n\t\t{\"J\", \"a\", 4},\n\t}\n\tobjects := []LogObjectMarshaler{\n\t\tfixtureObj{\"a\", \"z\", 1},\n\t\tfixtureObj{\"b\", \"y\", 2},\n\t\tfixtureObj{\"c\", \"x\", 3},\n\t\tfixtureObj{\"d\", \"w\", 4},\n\t\tfixtureObj{\"e\", \"v\", 5},\n\t\tfixtureObj{\"f\", \"u\", 6},\n\t\tfixtureObj{\"g\", \"t\", 7},\n\t\tfixtureObj{\"h\", \"s\", 8},\n\t\tfixtureObj{\"i\", \"r\", 9},\n\t\tfixtureObj{\"j\", \"q\", 10},\n\t}\n\tipAddrV4 := net.IP{192, 168, 0, 1}\n\tipAddrV6 := net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34}\n\tipAddrs := []net.IP{ipAddrV4, ipAddrV6, ipAddrV4, ipAddrV6, ipAddrV4, ipAddrV6, ipAddrV4, ipAddrV6, ipAddrV4, ipAddrV6}\n\tipPfxV4 := net.IPNet{IP: net.IP{192, 168, 0, 0}, Mask: net.CIDRMask(24, 32)}\n\tipPfxV6 := net.IPNet{IP: net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x00}, Mask: net.CIDRMask(64, 128)}\n\tipPfxs := []net.IPNet{ipPfxV4, ipPfxV6, ipPfxV4, ipPfxV6, ipPfxV4, ipPfxV6, ipPfxV4, ipPfxV6, ipPfxV4, ipPfxV6}\n\tmacAddr := net.HardwareAddr{0x00, 0x1A, 0x2B, 0x3C, 0x4D, 0x5E}\n\terrs := []error{errors.New(\"a\"), errors.New(\"b\"), errors.New(\"c\"), errors.New(\"d\"), errors.New(\"e\"), nil, loggableError{fmt.Errorf(\"oops\")}}\n\tctx := context.Background()\n\tstringers := []fmt.Stringer{ipAddrs[0], durations[0]}\n\trawJSONs := [][]byte{[]byte(`{\"some\":\"json\"}`), []byte(`{\"longer\":[1111,2222,3333,4444,5555]}`)}\n\trawCBOR := []byte{0xA1, 0x64, 0x73, 0x6F, 0x6D, 0x65, 0x64, 0x61, 0x74, 0x61} // {\"some\":\"data\"}\n\n\treturn &fieldFixtures{\n\t\tBools:      bools,\n\t\tBytes:      bytes,\n\t\tCtx:        ctx,\n\t\tDurations:  durations,\n\t\tErrs:       errs,\n\t\tFloats32:   floats32,\n\t\tFloats64:   floats64,\n\t\tInterfaces: interfaces,\n\t\tInts:       ints,\n\t\tInts8:      ints8,\n\t\tInts16:     ints16,\n\t\tInts32:     ints32,\n\t\tInts64:     ints64,\n\t\tUints:      uints,\n\t\tUints8:     uints8,\n\t\tUints16:    uints16,\n\t\tUints32:    uints32,\n\t\tUints64:    uints64,\n\t\tIPAddrs:    ipAddrs,\n\t\tIPPfxs:     ipPfxs,\n\t\tMACAddr:    macAddr,\n\t\tObjects:    objects,\n\t\tRawCBOR:    rawCBOR,\n\t\tRawJSONs:   rawJSONs,\n\t\tStringers:  stringers,\n\t\tStrings:    strings,\n\t\tTimes:      times,\n\t\tType:       reflect.TypeOf(12345),\n\t}\n}\n"
  },
  {
    "path": "globals.go",
    "content": "package zerolog\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"strconv\"\n\t\"sync/atomic\"\n\t\"time\"\n)\n\nconst (\n\t// TimeFormatUnix defines a time format that makes time fields to be\n\t// serialized as Unix timestamp integers.\n\tTimeFormatUnix = \"\"\n\n\t// TimeFormatUnixMs defines a time format that makes time fields to be\n\t// serialized as Unix timestamp integers in milliseconds.\n\tTimeFormatUnixMs = \"UNIXMS\"\n\n\t// TimeFormatUnixMicro defines a time format that makes time fields to be\n\t// serialized as Unix timestamp integers in microseconds.\n\tTimeFormatUnixMicro = \"UNIXMICRO\"\n\n\t// TimeFormatUnixNano defines a time format that makes time fields to be\n\t// serialized as Unix timestamp integers in nanoseconds.\n\tTimeFormatUnixNano = \"UNIXNANO\"\n\n\t// DurationFormatFloat defines a format for Duration fields that makes duration fields to be\n\t// serialized as floating point numbers.\n\tDurationFormatFloat = \"float\"\n\t// DurationFormatInt defines a format for Duration fields that makes duration fields to be\n\t// serialized as integers.\n\tDurationFormatInt = \"int\"\n\t// DurationFormatString defines a format for Duration fields that makes duration fields to be\n\t// serialized as string.\n\tDurationFormatString = \"string\"\n)\n\nvar (\n\t// TimestampFieldName is the field name used for the timestamp field.\n\tTimestampFieldName = \"time\"\n\n\t// LevelFieldName is the field name used for the level field.\n\tLevelFieldName = \"level\"\n\n\t// LevelTraceValue is the value used for the trace level field.\n\tLevelTraceValue = \"trace\"\n\t// LevelDebugValue is the value used for the debug level field.\n\tLevelDebugValue = \"debug\"\n\t// LevelInfoValue is the value used for the info level field.\n\tLevelInfoValue = \"info\"\n\t// LevelWarnValue is the value used for the warn level field.\n\tLevelWarnValue = \"warn\"\n\t// LevelErrorValue is the value used for the error level field.\n\tLevelErrorValue = \"error\"\n\t// LevelFatalValue is the value used for the fatal level field.\n\tLevelFatalValue = \"fatal\"\n\t// LevelPanicValue is the value used for the panic level field.\n\tLevelPanicValue = \"panic\"\n\n\t// LevelFieldMarshalFunc allows customization of global level field marshaling.\n\tLevelFieldMarshalFunc = func(l Level) string {\n\t\treturn l.String()\n\t}\n\n\t// MessageFieldName is the field name used for the message field.\n\tMessageFieldName = \"message\"\n\n\t// ErrorFieldName is the field name used for error fields.\n\tErrorFieldName = \"error\"\n\n\t// CallerFieldName is the field name used for caller field.\n\tCallerFieldName = \"caller\"\n\n\t// CallerSkipFrameCount is the number of stack frames to skip to find the caller.\n\tCallerSkipFrameCount = 2\n\n\t// CallerMarshalFunc allows customization of global caller marshaling\n\tCallerMarshalFunc = func(pc uintptr, file string, line int) string {\n\t\treturn file + \":\" + strconv.Itoa(line)\n\t}\n\n\t// ErrorStackFieldName is the field name used for error stacks.\n\tErrorStackFieldName = \"stack\"\n\n\t// ErrorStackMarshaler extract the stack from err if any.\n\tErrorStackMarshaler func(err error) interface{}\n\n\t// ErrorMarshalFunc allows customization of global error marshaling\n\tErrorMarshalFunc = func(err error) interface{} {\n\t\treturn err\n\t}\n\n\t// InterfaceMarshalFunc allows customization of interface marshaling.\n\t// Default: \"encoding/json.Marshal\" with disabled HTML escaping\n\tInterfaceMarshalFunc = func(v interface{}) ([]byte, error) {\n\t\tvar buf bytes.Buffer\n\t\tencoder := json.NewEncoder(&buf)\n\t\tencoder.SetEscapeHTML(false)\n\t\terr := encoder.Encode(v)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tb := buf.Bytes()\n\t\tif len(b) > 0 {\n\t\t\t// Remove trailing \\n which is added by Encode.\n\t\t\treturn b[:len(b)-1], nil\n\t\t}\n\t\treturn b, nil\n\t}\n\n\t// TimeFieldFormat defines the time format of the Time field type. If set to\n\t// TimeFormatUnix, TimeFormatUnixMs, TimeFormatUnixMicro or TimeFormatUnixNano, the time is formatted as a UNIX\n\t// timestamp as integer.\n\tTimeFieldFormat = time.RFC3339\n\n\t// TimestampFunc defines the function called to generate a timestamp.\n\tTimestampFunc = time.Now\n\n\t// DurationFieldFormat defines the format of the Duration field type.\n\tDurationFieldFormat = DurationFormatFloat\n\n\t// DurationFieldUnit defines the unit for time.Duration type fields added\n\t// using the Dur method.\n\tDurationFieldUnit = time.Millisecond\n\n\t// DurationFieldInteger renders Dur fields as integer instead of float if\n\t// set to true.\n\t// Deprecated: use DurationFieldFormat with DurationFormatInt instead.\n\tDurationFieldInteger = false\n\n\t// ErrorHandler is called whenever zerolog fails to write an event on its\n\t// output. If not set, an error is printed on the stderr. This handler must\n\t// be thread safe and non-blocking.\n\tErrorHandler func(err error)\n\n\t// FatalExitFunc is called by log.Fatal() instead of os.Exit(1). If not set,\n\t// os.Exit(1) is called.\n\tFatalExitFunc func()\n\n\t// DefaultContextLogger is returned from Ctx() if there is no logger associated\n\t// with the context.\n\tDefaultContextLogger *Logger\n\n\t// LevelColors are used by ConsoleWriter's consoleDefaultFormatLevel to color\n\t// log levels.\n\tLevelColors = map[Level]int{\n\t\tTraceLevel: colorBlue,\n\t\tDebugLevel: 0,\n\t\tInfoLevel:  colorGreen,\n\t\tWarnLevel:  colorYellow,\n\t\tErrorLevel: colorRed,\n\t\tFatalLevel: colorRed,\n\t\tPanicLevel: colorRed,\n\t}\n\n\t// FormattedLevels are used by ConsoleWriter's consoleDefaultFormatLevel\n\t// for a short level name.\n\tFormattedLevels = map[Level]string{\n\t\tTraceLevel: \"TRC\",\n\t\tDebugLevel: \"DBG\",\n\t\tInfoLevel:  \"INF\",\n\t\tWarnLevel:  \"WRN\",\n\t\tErrorLevel: \"ERR\",\n\t\tFatalLevel: \"FTL\",\n\t\tPanicLevel: \"PNC\",\n\t}\n\n\t// TriggerLevelWriterBufferReuseLimit is a limit in bytes that a buffer is dropped\n\t// from the TriggerLevelWriter buffer pool if the buffer grows above the limit.\n\tTriggerLevelWriterBufferReuseLimit = 64 * 1024\n\n\t// FloatingPointPrecision, if set to a value other than -1, controls the number\n\t// of digits when formatting float numbers in JSON. See strconv.FormatFloat for\n\t// more details.\n\tFloatingPointPrecision = -1\n)\n\nvar (\n\tgLevel          = new(int32)\n\tdisableSampling = new(int32)\n)\n\n// SetGlobalLevel sets the global override for log level. If this\n// values is raised, all Loggers will use at least this value.\n//\n// To globally disable logs, set GlobalLevel to Disabled.\nfunc SetGlobalLevel(l Level) {\n\tatomic.StoreInt32(gLevel, int32(l))\n}\n\n// GlobalLevel returns the current global log level\nfunc GlobalLevel() Level {\n\treturn Level(atomic.LoadInt32(gLevel))\n}\n\n// DisableSampling will disable sampling in all Loggers if true.\nfunc DisableSampling(v bool) {\n\tvar i int32\n\tif v {\n\t\ti = 1\n\t}\n\tatomic.StoreInt32(disableSampling, i)\n}\n\nfunc samplingDisabled() bool {\n\treturn atomic.LoadInt32(disableSampling) == 1\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/rs/zerolog\n\ngo 1.23\n\nrequire (\n\tgithub.com/coreos/go-systemd/v22 v22.6.0\n\tgithub.com/mattn/go-colorable v0.1.14\n\tgithub.com/pkg/errors v0.9.1\n\tgithub.com/rs/xid v1.6.0\n)\n\nrequire (\n\tgithub.com/mattn/go-isatty v0.0.20 // indirect\n\tgolang.org/x/sys v0.29.0 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/coreos/go-systemd/v22 v22.6.0 h1:aGVa/v8B7hpb0TKl0MWoAavPDmHvobFe5R5zn0bCJWo=\ngithub.com/coreos/go-systemd/v22 v22.6.0/go.mod h1:iG+pp635Fo7ZmV/j14KUcmEyWF+0X7Lua8rrTWzYgWU=\ngithub.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=\ngithub.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=\ngithub.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=\ngithub.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=\ngithub.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=\ngolang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\n"
  },
  {
    "path": "go112.go",
    "content": "// +build go1.12\n\npackage zerolog\n\n// Since go 1.12, some auto generated init functions are hidden from\n// runtime.Caller.\nconst contextCallerSkipFrameCount = 2\n"
  },
  {
    "path": "hlog/hlog.go",
    "content": "// Package hlog provides a set of http.Handler helpers for zerolog.\npackage hlog\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/rs/xid\"\n\t\"github.com/rs/zerolog\"\n\t\"github.com/rs/zerolog/hlog/internal/mutil\"\n\t\"github.com/rs/zerolog/log\"\n)\n\n// FromRequest gets the logger in the request's context.\n// This is a shortcut for log.Ctx(r.Context())\nfunc FromRequest(r *http.Request) *zerolog.Logger {\n\treturn log.Ctx(r.Context())\n}\n\n// NewHandler injects log into requests context.\nfunc NewHandler(log zerolog.Logger) func(http.Handler) http.Handler {\n\treturn func(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\t// Create a copy of the logger (including internal context slice)\n\t\t\t// to prevent data race when using UpdateContext.\n\t\t\tl := log.With().Logger()\n\t\t\tr = r.WithContext(l.WithContext(r.Context()))\n\t\t\tnext.ServeHTTP(w, r)\n\t\t})\n\t}\n}\n\n// URLHandler adds the requested URL as a field to the context's logger\n// using fieldKey as field key.\nfunc URLHandler(fieldKey string) func(next http.Handler) http.Handler {\n\treturn func(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tlog := zerolog.Ctx(r.Context())\n\t\t\tlog.UpdateContext(func(c zerolog.Context) zerolog.Context {\n\t\t\t\treturn c.Str(fieldKey, r.URL.String())\n\t\t\t})\n\t\t\tnext.ServeHTTP(w, r)\n\t\t})\n\t}\n}\n\n// MethodHandler adds the request method as a field to the context's logger\n// using fieldKey as field key.\nfunc MethodHandler(fieldKey string) func(next http.Handler) http.Handler {\n\treturn func(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tlog := zerolog.Ctx(r.Context())\n\t\t\tlog.UpdateContext(func(c zerolog.Context) zerolog.Context {\n\t\t\t\treturn c.Str(fieldKey, r.Method)\n\t\t\t})\n\t\t\tnext.ServeHTTP(w, r)\n\t\t})\n\t}\n}\n\n// RequestHandler adds the request method and URL as a field to the context's logger\n// using fieldKey as field key.\nfunc RequestHandler(fieldKey string) func(next http.Handler) http.Handler {\n\treturn func(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tlog := zerolog.Ctx(r.Context())\n\t\t\tlog.UpdateContext(func(c zerolog.Context) zerolog.Context {\n\t\t\t\treturn c.Str(fieldKey, r.Method+\" \"+r.URL.String())\n\t\t\t})\n\t\t\tnext.ServeHTTP(w, r)\n\t\t})\n\t}\n}\n\n// RemoteAddrHandler adds the request's remote address as a field to the context's logger\n// using fieldKey as field key.\nfunc RemoteAddrHandler(fieldKey string) func(next http.Handler) http.Handler {\n\treturn func(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tif r.RemoteAddr != \"\" {\n\t\t\t\tlog := zerolog.Ctx(r.Context())\n\t\t\t\tlog.UpdateContext(func(c zerolog.Context) zerolog.Context {\n\t\t\t\t\treturn c.Str(fieldKey, r.RemoteAddr)\n\t\t\t\t})\n\t\t\t}\n\t\t\tnext.ServeHTTP(w, r)\n\t\t})\n\t}\n}\n\nfunc getHost(hostPort string) string {\n\tif hostPort == \"\" {\n\t\treturn \"\"\n\t}\n\n\thost, _, err := net.SplitHostPort(hostPort)\n\tif err != nil {\n\t\treturn hostPort\n\t}\n\treturn host\n}\n\n// RemoteIPHandler is similar to RemoteAddrHandler, but logs only\n// an IP, not a port.\nfunc RemoteIPHandler(fieldKey string) func(next http.Handler) http.Handler {\n\treturn func(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tip := getHost(r.RemoteAddr)\n\t\t\tif ip != \"\" {\n\t\t\t\tlog := zerolog.Ctx(r.Context())\n\t\t\t\tlog.UpdateContext(func(c zerolog.Context) zerolog.Context {\n\t\t\t\t\treturn c.Str(fieldKey, ip)\n\t\t\t\t})\n\t\t\t}\n\t\t\tnext.ServeHTTP(w, r)\n\t\t})\n\t}\n}\n\n// UserAgentHandler adds the request's user-agent as a field to the context's logger\n// using fieldKey as field key.\nfunc UserAgentHandler(fieldKey string) func(next http.Handler) http.Handler {\n\treturn func(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tif ua := r.Header.Get(\"User-Agent\"); ua != \"\" {\n\t\t\t\tlog := zerolog.Ctx(r.Context())\n\t\t\t\tlog.UpdateContext(func(c zerolog.Context) zerolog.Context {\n\t\t\t\t\treturn c.Str(fieldKey, ua)\n\t\t\t\t})\n\t\t\t}\n\t\t\tnext.ServeHTTP(w, r)\n\t\t})\n\t}\n}\n\n// RefererHandler adds the request's referer as a field to the context's logger\n// using fieldKey as field key.\nfunc RefererHandler(fieldKey string) func(next http.Handler) http.Handler {\n\treturn func(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tif ref := r.Header.Get(\"Referer\"); ref != \"\" {\n\t\t\t\tlog := zerolog.Ctx(r.Context())\n\t\t\t\tlog.UpdateContext(func(c zerolog.Context) zerolog.Context {\n\t\t\t\t\treturn c.Str(fieldKey, ref)\n\t\t\t\t})\n\t\t\t}\n\t\t\tnext.ServeHTTP(w, r)\n\t\t})\n\t}\n}\n\n// ProtoHandler adds the requests protocol version as a field to the context logger\n// using fieldKey as field Key.\nfunc ProtoHandler(fieldKey string) func(next http.Handler) http.Handler {\n\treturn func(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tlog := zerolog.Ctx(r.Context())\n\t\t\tlog.UpdateContext(func(c zerolog.Context) zerolog.Context {\n\t\t\t\treturn c.Str(fieldKey, r.Proto)\n\t\t\t})\n\t\t\tnext.ServeHTTP(w, r)\n\t\t})\n\t}\n}\n\n// HTTPVersionHandler is similar to ProtoHandler, but it does not store the \"HTTP/\"\n// prefix in the protocol name.\nfunc HTTPVersionHandler(fieldKey string) func(next http.Handler) http.Handler {\n\treturn func(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tproto := strings.TrimPrefix(r.Proto, \"HTTP/\")\n\t\t\tlog := zerolog.Ctx(r.Context())\n\t\t\tlog.UpdateContext(func(c zerolog.Context) zerolog.Context {\n\t\t\t\treturn c.Str(fieldKey, proto)\n\t\t\t})\n\t\t\tnext.ServeHTTP(w, r)\n\t\t})\n\t}\n}\n\ntype idKey struct{}\n\n// IDFromRequest returns the unique id associated to the request if any.\nfunc IDFromRequest(r *http.Request) (id xid.ID, ok bool) {\n\tif r == nil {\n\t\treturn\n\t}\n\treturn IDFromCtx(r.Context())\n}\n\n// IDFromCtx returns the unique id associated to the context if any.\nfunc IDFromCtx(ctx context.Context) (id xid.ID, ok bool) {\n\tid, ok = ctx.Value(idKey{}).(xid.ID)\n\treturn\n}\n\n// CtxWithID adds the given xid.ID to the context\nfunc CtxWithID(ctx context.Context, id xid.ID) context.Context {\n\treturn context.WithValue(ctx, idKey{}, id)\n}\n\n// RequestIDHandler returns a handler setting a unique id to the request which can\n// be gathered using IDFromRequest(req). This generated id is added as a field to the\n// logger using the passed fieldKey as field name. The id is also added as a response\n// header if the headerName is not empty.\n//\n// The generated id is a URL safe base64 encoded mongo object-id-like unique id.\n// Mongo unique id generation algorithm has been selected as a trade-off between\n// size and ease of use: UUID is less space efficient and snowflake requires machine\n// configuration.\nfunc RequestIDHandler(fieldKey, headerName string) func(next http.Handler) http.Handler {\n\treturn func(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tctx := r.Context()\n\t\t\tid, ok := IDFromRequest(r)\n\t\t\tif !ok {\n\t\t\t\tid = xid.New()\n\t\t\t\tctx = CtxWithID(ctx, id)\n\t\t\t\tr = r.WithContext(ctx)\n\t\t\t}\n\t\t\tif fieldKey != \"\" {\n\t\t\t\tlog := zerolog.Ctx(ctx)\n\t\t\t\tlog.UpdateContext(func(c zerolog.Context) zerolog.Context {\n\t\t\t\t\treturn c.Str(fieldKey, id.String())\n\t\t\t\t})\n\t\t\t}\n\t\t\tif headerName != \"\" {\n\t\t\t\tw.Header().Set(headerName, id.String())\n\t\t\t}\n\t\t\tnext.ServeHTTP(w, r)\n\t\t})\n\t}\n}\n\n// CustomHeaderHandler adds given header from request's header as a field to\n// the context's logger using fieldKey as field key.\nfunc CustomHeaderHandler(fieldKey, header string) func(next http.Handler) http.Handler {\n\treturn func(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tif val := r.Header.Get(header); val != \"\" {\n\t\t\t\tlog := zerolog.Ctx(r.Context())\n\t\t\t\tlog.UpdateContext(func(c zerolog.Context) zerolog.Context {\n\t\t\t\t\treturn c.Str(fieldKey, val)\n\t\t\t\t})\n\t\t\t}\n\t\t\tnext.ServeHTTP(w, r)\n\t\t})\n\t}\n}\n\n// EtagHandler adds Etag header from response's header as a field to\n// the context's logger using fieldKey as field key.\nfunc EtagHandler(fieldKey string) func(next http.Handler) http.Handler {\n\treturn func(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tdefer func() {\n\t\t\t\tetag := w.Header().Get(\"Etag\")\n\t\t\t\tif etag != \"\" {\n\t\t\t\t\tetag = strings.ReplaceAll(etag, `\"`, \"\")\n\t\t\t\t\tlog := zerolog.Ctx(r.Context())\n\t\t\t\t\tlog.UpdateContext(func(c zerolog.Context) zerolog.Context {\n\t\t\t\t\t\treturn c.Str(fieldKey, etag)\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}()\n\t\t\tnext.ServeHTTP(w, r)\n\t\t})\n\t}\n}\n\nfunc ResponseHeaderHandler(fieldKey, headerName string) func(next http.Handler) http.Handler {\n\treturn func(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tdefer func() {\n\t\t\t\tvalue := w.Header().Get(headerName)\n\t\t\t\tif value != \"\" {\n\t\t\t\t\tlog := zerolog.Ctx(r.Context())\n\t\t\t\t\tlog.UpdateContext(func(c zerolog.Context) zerolog.Context {\n\t\t\t\t\t\treturn c.Str(fieldKey, value)\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}()\n\t\t\tnext.ServeHTTP(w, r)\n\t\t})\n\t}\n}\n\n// AccessHandler returns a handler that call f after each request.\nfunc AccessHandler(f func(r *http.Request, status, size int, duration time.Duration)) func(next http.Handler) http.Handler {\n\treturn func(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tstart := time.Now()\n\t\t\tlw := mutil.WrapWriter(w)\n\t\t\tdefer func() {\n\t\t\t\tf(r, lw.Status(), lw.BytesWritten(), time.Since(start))\n\t\t\t}()\n\t\t\tnext.ServeHTTP(lw, r)\n\t\t})\n\t}\n}\n\n// HostHandler adds the request's host as a field to the context's logger\n// using fieldKey as field key. If trimPort is set to true, then port is\n// removed from the host.\nfunc HostHandler(fieldKey string, trimPort ...bool) func(next http.Handler) http.Handler {\n\treturn func(next http.Handler) http.Handler {\n\t\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tvar host string\n\t\t\tif len(trimPort) > 0 && trimPort[0] {\n\t\t\t\thost = getHost(r.Host)\n\t\t\t} else {\n\t\t\t\thost = r.Host\n\t\t\t}\n\t\t\tif host != \"\" {\n\t\t\t\tlog := zerolog.Ctx(r.Context())\n\t\t\t\tlog.UpdateContext(func(c zerolog.Context) zerolog.Context {\n\t\t\t\t\treturn c.Str(fieldKey, host)\n\t\t\t\t})\n\t\t\t}\n\t\t\tnext.ServeHTTP(w, r)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "hlog/hlog_example_test.go",
    "content": "// +build !binary_log\n\npackage hlog_test\n\nimport (\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"net/http/httptest\"\n\n\t\"github.com/rs/zerolog\"\n\t\"github.com/rs/zerolog/hlog\"\n)\n\n// fake alice to avoid dep\ntype middleware func(http.Handler) http.Handler\ntype alice struct {\n\tm []middleware\n}\n\nfunc (a alice) Append(m middleware) alice {\n\ta.m = append(a.m, m)\n\treturn a\n}\nfunc (a alice) Then(h http.Handler) http.Handler {\n\tfor i := range a.m {\n\t\th = a.m[len(a.m)-1-i](h)\n\t}\n\treturn h\n}\n\nfunc init() {\n\tzerolog.TimestampFunc = func() time.Time {\n\t\treturn time.Date(2001, time.February, 3, 4, 5, 6, 7, time.UTC)\n\t}\n}\n\nfunc Example_handler() {\n\tlog := zerolog.New(os.Stdout).With().\n\t\tTimestamp().\n\t\tStr(\"role\", \"my-service\").\n\t\tStr(\"host\", \"local-hostname\").\n\t\tLogger()\n\n\tc := alice{}\n\n\t// Install the logger handler with default output on the console\n\tc = c.Append(hlog.NewHandler(log))\n\n\t// Install some provided extra handlers to set some request's context fields.\n\t// Thanks to those handlers, all our logs will come with some pre-populated fields.\n\tc = c.Append(hlog.RemoteAddrHandler(\"ip\"))\n\tc = c.Append(hlog.UserAgentHandler(\"user_agent\"))\n\tc = c.Append(hlog.RefererHandler(\"referer\"))\n\t//c = c.Append(hlog.RequestIDHandler(\"req_id\", \"Request-Id\"))\n\n\t// Here is your final handler\n\th := c.Then(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t// Get the logger from the request's context. You can safely assume it\n\t\t// will be always there: if the handler is removed, hlog.FromRequest\n\t\t// will return a no-op logger.\n\t\thlog.FromRequest(r).Info().\n\t\t\tStr(\"user\", \"current user\").\n\t\t\tStr(\"status\", \"ok\").\n\t\t\tMsg(\"Something happened\")\n\t}))\n\thttp.Handle(\"/\", h)\n\n\th.ServeHTTP(httptest.NewRecorder(), &http.Request{})\n\n\t// Output: {\"level\":\"info\",\"role\":\"my-service\",\"host\":\"local-hostname\",\"user\":\"current user\",\"status\":\"ok\",\"time\":\"2001-02-03T04:05:06Z\",\"message\":\"Something happened\"}\n}\n"
  },
  {
    "path": "hlog/hlog_test.go",
    "content": "//go:build go1.7\n// +build go1.7\n\npackage hlog\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/rs/xid\"\n\t\"github.com/rs/zerolog\"\n\t\"github.com/rs/zerolog/internal/cbor\"\n)\n\nfunc decodeIfBinary(out *bytes.Buffer) string {\n\tp := out.Bytes()\n\tif len(p) == 0 || p[0] < 0x7F {\n\t\treturn out.String()\n\t}\n\treturn cbor.DecodeObjectToStr(p) + \"\\n\"\n}\n\nfunc TestNewHandler(t *testing.T) {\n\tlog := zerolog.New(nil).With().\n\t\tStr(\"foo\", \"bar\").\n\t\tLogger()\n\tlh := NewHandler(log)\n\th := lh(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tl := FromRequest(r)\n\t\tif !reflect.DeepEqual(*l, log) {\n\t\t\tt.Fail()\n\t\t}\n\t}))\n\th.ServeHTTP(nil, &http.Request{})\n}\n\nfunc TestURLHandler(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tr := &http.Request{\n\t\tURL: &url.URL{Path: \"/path\", RawQuery: \"foo=bar\"},\n\t}\n\th := URLHandler(\"url\")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tl := FromRequest(r)\n\t\tl.Log().Msg(\"\")\n\t}))\n\th = NewHandler(zerolog.New(out))(h)\n\th.ServeHTTP(nil, r)\n\tif want, got := `{\"url\":\"/path?foo=bar\"}`+\"\\n\", decodeIfBinary(out); want != got {\n\t\tt.Errorf(\"Invalid log output, got: %s, want: %s\", got, want)\n\t}\n}\n\nfunc TestMethodHandler(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tr := &http.Request{\n\t\tMethod: \"POST\",\n\t}\n\th := MethodHandler(\"method\")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tl := FromRequest(r)\n\t\tl.Log().Msg(\"\")\n\t}))\n\th = NewHandler(zerolog.New(out))(h)\n\th.ServeHTTP(nil, r)\n\tif want, got := `{\"method\":\"POST\"}`+\"\\n\", decodeIfBinary(out); want != got {\n\t\tt.Errorf(\"Invalid log output, got: %s, want: %s\", got, want)\n\t}\n}\n\nfunc TestRequestHandler(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tr := &http.Request{\n\t\tMethod: \"POST\",\n\t\tURL:    &url.URL{Path: \"/path\", RawQuery: \"foo=bar\"},\n\t}\n\th := RequestHandler(\"request\")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tl := FromRequest(r)\n\t\tl.Log().Msg(\"\")\n\t}))\n\th = NewHandler(zerolog.New(out))(h)\n\th.ServeHTTP(nil, r)\n\tif want, got := `{\"request\":\"POST /path?foo=bar\"}`+\"\\n\", decodeIfBinary(out); want != got {\n\t\tt.Errorf(\"Invalid log output, got: %s, want: %s\", got, want)\n\t}\n}\n\nfunc TestRemoteAddrHandler(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tr := &http.Request{\n\t\tRemoteAddr: \"1.2.3.4:1234\",\n\t}\n\th := RemoteAddrHandler(\"ip\")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tl := FromRequest(r)\n\t\tl.Log().Msg(\"\")\n\t}))\n\th = NewHandler(zerolog.New(out))(h)\n\th.ServeHTTP(nil, r)\n\tif want, got := `{\"ip\":\"1.2.3.4:1234\"}`+\"\\n\", decodeIfBinary(out); want != got {\n\t\tt.Errorf(\"Invalid log output, got: %s, want: %s\", got, want)\n\t}\n}\n\nfunc TestRemoteAddrHandlerIPv6(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tr := &http.Request{\n\t\tRemoteAddr: \"[2001:db8:a0b:12f0::1]:1234\",\n\t}\n\th := RemoteAddrHandler(\"ip\")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tl := FromRequest(r)\n\t\tl.Log().Msg(\"\")\n\t}))\n\th = NewHandler(zerolog.New(out))(h)\n\th.ServeHTTP(nil, r)\n\tif want, got := `{\"ip\":\"[2001:db8:a0b:12f0::1]:1234\"}`+\"\\n\", decodeIfBinary(out); want != got {\n\t\tt.Errorf(\"Invalid log output, got: %s, want: %s\", got, want)\n\t}\n}\n\nfunc TestRemoteIPHandler(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tr := &http.Request{\n\t\tRemoteAddr: \"1.2.3.4:1234\",\n\t}\n\th := RemoteIPHandler(\"ip\")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tl := FromRequest(r)\n\t\tl.Log().Msg(\"\")\n\t}))\n\th = NewHandler(zerolog.New(out))(h)\n\th.ServeHTTP(nil, r)\n\tif want, got := `{\"ip\":\"1.2.3.4\"}`+\"\\n\", decodeIfBinary(out); want != got {\n\t\tt.Errorf(\"Invalid log output, got: %s, want: %s\", got, want)\n\t}\n}\n\nfunc TestRemoteIPHandlerIPv6(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tr := &http.Request{\n\t\tRemoteAddr: \"[2001:db8:a0b:12f0::1]:1234\",\n\t}\n\th := RemoteIPHandler(\"ip\")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tl := FromRequest(r)\n\t\tl.Log().Msg(\"\")\n\t}))\n\th = NewHandler(zerolog.New(out))(h)\n\th.ServeHTTP(nil, r)\n\tif want, got := `{\"ip\":\"2001:db8:a0b:12f0::1\"}`+\"\\n\", decodeIfBinary(out); want != got {\n\t\tt.Errorf(\"Invalid log output, got: %s, want: %s\", got, want)\n\t}\n}\n\nfunc TestUserAgentHandler(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tr := &http.Request{\n\t\tHeader: http.Header{\n\t\t\t\"User-Agent\": []string{\"some user agent string\"},\n\t\t},\n\t}\n\th := UserAgentHandler(\"ua\")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tl := FromRequest(r)\n\t\tl.Log().Msg(\"\")\n\t}))\n\th = NewHandler(zerolog.New(out))(h)\n\th.ServeHTTP(nil, r)\n\tif want, got := `{\"ua\":\"some user agent string\"}`+\"\\n\", decodeIfBinary(out); want != got {\n\t\tt.Errorf(\"Invalid log output, got: %s, want: %s\", got, want)\n\t}\n}\n\nfunc TestRefererHandler(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tr := &http.Request{\n\t\tHeader: http.Header{\n\t\t\t\"Referer\": []string{\"http://foo.com/bar\"},\n\t\t},\n\t}\n\th := RefererHandler(\"referer\")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tl := FromRequest(r)\n\t\tl.Log().Msg(\"\")\n\t}))\n\th = NewHandler(zerolog.New(out))(h)\n\th.ServeHTTP(nil, r)\n\tif want, got := `{\"referer\":\"http://foo.com/bar\"}`+\"\\n\", decodeIfBinary(out); want != got {\n\t\tt.Errorf(\"Invalid log output, got: %s, want: %s\", got, want)\n\t}\n}\n\nfunc TestRequestIDHandler(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tr := &http.Request{\n\t\tHeader: http.Header{\n\t\t\t\"Referer\": []string{\"http://foo.com/bar\"},\n\t\t},\n\t}\n\th := RequestIDHandler(\"id\", \"Request-Id\")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tid, ok := IDFromRequest(r)\n\t\tif !ok {\n\t\t\tt.Fatal(\"Missing id in request\")\n\t\t}\n\t\tif want, got := id.String(), w.Header().Get(\"Request-Id\"); got != want {\n\t\t\tt.Errorf(\"Invalid Request-Id header, got: %s, want: %s\", got, want)\n\t\t}\n\t\tl := FromRequest(r)\n\t\tl.Log().Msg(\"\")\n\t\tif want, got := fmt.Sprintf(`{\"id\":\"%s\"}`+\"\\n\", id), decodeIfBinary(out); want != got {\n\t\t\tt.Errorf(\"Invalid log output, got: %s, want: %s\", got, want)\n\t\t}\n\t}))\n\th = NewHandler(zerolog.New(out))(h)\n\th.ServeHTTP(httptest.NewRecorder(), r)\n}\n\nfunc TestCustomHeaderHandler(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tr := &http.Request{\n\t\tHeader: http.Header{\n\t\t\t\"X-Request-Id\": []string{\"514bbe5bb5251c92bd07a9846f4a1ab6\"},\n\t\t},\n\t}\n\th := CustomHeaderHandler(\"reqID\", \"X-Request-Id\")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tl := FromRequest(r)\n\t\tl.Log().Msg(\"\")\n\t}))\n\th = NewHandler(zerolog.New(out))(h)\n\th.ServeHTTP(nil, r)\n\tif want, got := `{\"reqID\":\"514bbe5bb5251c92bd07a9846f4a1ab6\"}`+\"\\n\", decodeIfBinary(out); want != got {\n\t\tt.Errorf(\"Invalid log output, got: %s, want: %s\", got, want)\n\t}\n}\n\nfunc TestEtagHandler(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tw := httptest.NewRecorder()\n\tr := &http.Request{}\n\th := EtagHandler(\"etag\")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tw.Header().Set(\"Etag\", `\"abcdef\"`)\n\t\tw.WriteHeader(http.StatusOK)\n\t}))\n\th2 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\th.ServeHTTP(w, r)\n\t\tl := FromRequest(r)\n\t\tl.Log().Msg(\"\")\n\t})\n\th3 := NewHandler(zerolog.New(out))(h2)\n\th3.ServeHTTP(w, r)\n\tif want, got := `{\"etag\":\"abcdef\"}`+\"\\n\", decodeIfBinary(out); want != got {\n\t\tt.Errorf(\"Invalid log output, got: %s, want: %s\", got, want)\n\t}\n}\n\nfunc TestResponseHeaderHandler(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tw := httptest.NewRecorder()\n\tr := &http.Request{}\n\th := ResponseHeaderHandler(\"encoding\", \"Content-Encoding\")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tw.Header().Set(\"Content-Encoding\", `gzip`)\n\t\tw.WriteHeader(http.StatusOK)\n\t}))\n\th2 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\th.ServeHTTP(w, r)\n\t\tl := FromRequest(r)\n\t\tl.Log().Msg(\"\")\n\t})\n\th3 := NewHandler(zerolog.New(out))(h2)\n\th3.ServeHTTP(w, r)\n\tif want, got := `{\"encoding\":\"gzip\"}`+\"\\n\", decodeIfBinary(out); want != got {\n\t\tt.Errorf(\"Invalid log output, got: %s, want: %s\", got, want)\n\t}\n}\n\nfunc TestProtoHandler(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tr := &http.Request{\n\t\tProto: \"test\",\n\t}\n\th := ProtoHandler(\"proto\")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tl := FromRequest(r)\n\t\tl.Log().Msg(\"\")\n\t}))\n\th = NewHandler(zerolog.New(out))(h)\n\th.ServeHTTP(nil, r)\n\tif want, got := `{\"proto\":\"test\"}`+\"\\n\", decodeIfBinary(out); want != got {\n\t\tt.Errorf(\"Invalid log output, got: %s, want: %s\", got, want)\n\t}\n}\n\nfunc TestHTTPVersionHandler(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tr := &http.Request{\n\t\tProto: \"HTTP/1.1\",\n\t}\n\th := HTTPVersionHandler(\"proto\")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tl := FromRequest(r)\n\t\tl.Log().Msg(\"\")\n\t}))\n\th = NewHandler(zerolog.New(out))(h)\n\th.ServeHTTP(nil, r)\n\tif want, got := `{\"proto\":\"1.1\"}`+\"\\n\", decodeIfBinary(out); want != got {\n\t\tt.Errorf(\"Invalid log output, got: %s, want: %s\", got, want)\n\t}\n}\n\nfunc TestCombinedHandlers(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tr := &http.Request{\n\t\tMethod: \"POST\",\n\t\tURL:    &url.URL{Path: \"/path\", RawQuery: \"foo=bar\"},\n\t}\n\th := MethodHandler(\"method\")(RequestHandler(\"request\")(URLHandler(\"url\")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tl := FromRequest(r)\n\t\tl.Log().Msg(\"\")\n\t}))))\n\th = NewHandler(zerolog.New(out))(h)\n\th.ServeHTTP(nil, r)\n\tif want, got := `{\"method\":\"POST\",\"request\":\"POST /path?foo=bar\",\"url\":\"/path?foo=bar\"}`+\"\\n\", decodeIfBinary(out); want != got {\n\t\tt.Errorf(\"Invalid log output, got: %s, want: %s\", got, want)\n\t}\n}\n\nfunc BenchmarkHandlers(b *testing.B) {\n\tr := &http.Request{\n\t\tMethod: \"POST\",\n\t\tURL:    &url.URL{Path: \"/path\", RawQuery: \"foo=bar\"},\n\t}\n\th1 := URLHandler(\"url\")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tl := FromRequest(r)\n\t\tl.Log().Msg(\"\")\n\t}))\n\th2 := MethodHandler(\"method\")(RequestHandler(\"request\")(h1))\n\thandlers := map[string]http.Handler{\n\t\t\"Single\":           NewHandler(zerolog.New(io.Discard))(h1),\n\t\t\"Combined\":         NewHandler(zerolog.New(io.Discard))(h2),\n\t\t\"SingleDisabled\":   NewHandler(zerolog.New(io.Discard).Level(zerolog.Disabled))(h1),\n\t\t\"CombinedDisabled\": NewHandler(zerolog.New(io.Discard).Level(zerolog.Disabled))(h2),\n\t}\n\tfor name := range handlers {\n\t\th := handlers[name]\n\t\tb.Run(name, func(b *testing.B) {\n\t\t\tfor i := 0; i < b.N; i++ {\n\t\t\t\th.ServeHTTP(nil, r)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkDataRace(b *testing.B) {\n\tlog := zerolog.New(nil).With().\n\t\tStr(\"foo\", \"bar\").\n\t\tLogger()\n\tlh := NewHandler(log)\n\th := lh(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tl := FromRequest(r)\n\t\tl.UpdateContext(func(c zerolog.Context) zerolog.Context {\n\t\t\treturn c.Str(\"bar\", \"baz\")\n\t\t})\n\t\tl.Log().Msg(\"\")\n\t}))\n\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\th.ServeHTTP(nil, &http.Request{})\n\t\t}\n\t})\n}\n\nfunc TestCtxWithID(t *testing.T) {\n\tctx := context.Background()\n\n\tid, _ := xid.FromString(`c0umremcie6smuu506pg`)\n\n\twant := context.Background()\n\twant = context.WithValue(want, idKey{}, id)\n\n\tif got := CtxWithID(ctx, id); !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"CtxWithID() = %v, want %v\", got, want)\n\t}\n}\n\nfunc TestHostHandler(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tr := &http.Request{Host: \"example.com:8080\"}\n\th := HostHandler(\"host\")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tl := FromRequest(r)\n\t\tl.Log().Msg(\"\")\n\t}))\n\th = NewHandler(zerolog.New(out))(h)\n\th.ServeHTTP(nil, r)\n\tif want, got := `{\"host\":\"example.com:8080\"}`+\"\\n\", decodeIfBinary(out); want != got {\n\t\tt.Errorf(\"Invalid log output, got: %s, want: %s\", got, want)\n\t}\n}\n\nfunc TestHostHandlerWithoutPort(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tr := &http.Request{Host: \"example.com:8080\"}\n\th := HostHandler(\"host\", true)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tl := FromRequest(r)\n\t\tl.Log().Msg(\"\")\n\t}))\n\th = NewHandler(zerolog.New(out))(h)\n\th.ServeHTTP(nil, r)\n\tif want, got := `{\"host\":\"example.com\"}`+\"\\n\", decodeIfBinary(out); want != got {\n\t\tt.Errorf(\"Invalid log output, got: %s, want: %s\", got, want)\n\t}\n}\n\nfunc TestGetHost(t *testing.T) {\n\ttests := []struct {\n\t\tinput    string\n\t\texpected string\n\t}{\n\t\t{\"\", \"\"},\n\t\t{\"example.com:8080\", \"example.com\"},\n\t\t{\"example.com\", \"example.com\"},\n\t\t{\"invalid\", \"invalid\"},\n\t\t{\"192.168.0.1:8080\", \"192.168.0.1\"},\n\t\t{\"[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8080\", \"2001:0db8:85a3:0000:0000:8a2e:0370:7334\"},\n\t\t{\"こんにちは.com:8080\", \"こんにちは.com\"},\n\t}\n\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.input, func(t *testing.T) {\n\t\t\tresult := getHost(tt.input)\n\t\t\tif tt.expected != result {\n\t\t\t\tt.Errorf(\"Invalid log output, got: %s, want: %s\", result, tt.expected)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "hlog/internal/mutil/LICENSE",
    "content": "Copyright (c) 2014, 2015, 2016 Carl Jackson (carl@avtok.com)\n\nMIT License\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies 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, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "hlog/internal/mutil/mutil.go",
    "content": "// Package mutil contains various functions that are helpful when writing http\n// middleware.\n//\n// It has been vendored from Goji v1.0, with the exception of the code for Go 1.8:\n// https://github.com/zenazn/goji/\npackage mutil\n"
  },
  {
    "path": "hlog/internal/mutil/writer_proxy.go",
    "content": "package mutil\n\nimport (\n\t\"bufio\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n)\n\n// WriterProxy is a proxy around an http.ResponseWriter that allows you to hook\n// into various parts of the response process.\ntype WriterProxy interface {\n\thttp.ResponseWriter\n\t// Status returns the HTTP status of the request, or 0 if one has not\n\t// yet been sent.\n\tStatus() int\n\t// BytesWritten returns the total number of bytes sent to the client.\n\tBytesWritten() int\n\t// Tee causes the response body to be written to the given io.Writer in\n\t// addition to proxying the writes through. Only one io.Writer can be\n\t// tee'd to at once: setting a second one will overwrite the first.\n\t// Writes will be sent to the proxy before being written to this\n\t// io.Writer. It is illegal for the tee'd writer to be modified\n\t// concurrently with writes.\n\tTee(io.Writer)\n\t// Unwrap returns the original proxied target.\n\tUnwrap() http.ResponseWriter\n}\n\n// WrapWriter wraps an http.ResponseWriter, returning a proxy that allows you to\n// hook into various parts of the response process.\nfunc WrapWriter(w http.ResponseWriter) WriterProxy {\n\t_, cn := w.(http.CloseNotifier)\n\t_, fl := w.(http.Flusher)\n\t_, hj := w.(http.Hijacker)\n\t_, rf := w.(io.ReaderFrom)\n\n\tbw := basicWriter{ResponseWriter: w}\n\tif cn && fl && hj && rf {\n\t\treturn &fancyWriter{bw}\n\t}\n\tif fl {\n\t\treturn &flushWriter{bw}\n\t}\n\treturn &bw\n}\n\n// basicWriter wraps a http.ResponseWriter that implements the minimal\n// http.ResponseWriter interface.\ntype basicWriter struct {\n\thttp.ResponseWriter\n\twroteHeader bool\n\tcode        int\n\tbytes       int\n\ttee         io.Writer\n}\n\nfunc (b *basicWriter) WriteHeader(code int) {\n\tif !b.wroteHeader {\n\t\tb.code = code\n\t\tb.wroteHeader = true\n\t\tb.ResponseWriter.WriteHeader(code)\n\t}\n}\n\nfunc (b *basicWriter) Write(buf []byte) (int, error) {\n\tb.WriteHeader(http.StatusOK)\n\tn, err := b.ResponseWriter.Write(buf)\n\tif b.tee != nil {\n\t\t_, err2 := b.tee.Write(buf[:n])\n\t\t// Prefer errors generated by the proxied writer.\n\t\tif err == nil {\n\t\t\terr = err2\n\t\t}\n\t}\n\tb.bytes += n\n\treturn n, err\n}\n\nfunc (b *basicWriter) maybeWriteHeader() {\n\tif !b.wroteHeader {\n\t\tb.WriteHeader(http.StatusOK)\n\t}\n}\n\nfunc (b *basicWriter) Status() int {\n\treturn b.code\n}\n\nfunc (b *basicWriter) BytesWritten() int {\n\treturn b.bytes\n}\n\nfunc (b *basicWriter) Tee(w io.Writer) {\n\tb.tee = w\n}\n\nfunc (b *basicWriter) Unwrap() http.ResponseWriter {\n\treturn b.ResponseWriter\n}\n\n// fancyWriter is a writer that additionally satisfies http.CloseNotifier,\n// http.Flusher, http.Hijacker, and io.ReaderFrom. It exists for the common case\n// of wrapping the http.ResponseWriter that package http gives you, in order to\n// make the proxied object support the full method set of the proxied object.\ntype fancyWriter struct {\n\tbasicWriter\n}\n\nfunc (f *fancyWriter) CloseNotify() <-chan bool {\n\tcn := f.basicWriter.ResponseWriter.(http.CloseNotifier)\n\treturn cn.CloseNotify()\n}\n\nfunc (f *fancyWriter) Flush() {\n\tfl := f.basicWriter.ResponseWriter.(http.Flusher)\n\tfl.Flush()\n}\n\nfunc (f *fancyWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {\n\thj := f.basicWriter.ResponseWriter.(http.Hijacker)\n\treturn hj.Hijack()\n}\n\nfunc (f *fancyWriter) ReadFrom(r io.Reader) (int64, error) {\n\tif f.basicWriter.tee != nil {\n\t\tn, err := io.Copy(&f.basicWriter, r)\n\t\tf.bytes += int(n)\n\t\treturn n, err\n\t}\n\trf := f.basicWriter.ResponseWriter.(io.ReaderFrom)\n\tf.basicWriter.maybeWriteHeader()\n\n\tn, err := rf.ReadFrom(r)\n\tf.bytes += int(n)\n\treturn n, err\n}\n\ntype flushWriter struct {\n\tbasicWriter\n}\n\nfunc (f *flushWriter) Flush() {\n\tfl := f.basicWriter.ResponseWriter.(http.Flusher)\n\tfl.Flush()\n}\n\nvar (\n\t_ http.CloseNotifier = &fancyWriter{}\n\t_ http.Flusher       = &fancyWriter{}\n\t_ http.Hijacker      = &fancyWriter{}\n\t_ io.ReaderFrom      = &fancyWriter{}\n\t_ http.Flusher       = &flushWriter{}\n)\n"
  },
  {
    "path": "hook.go",
    "content": "package zerolog\n\n// Hook defines an interface to a log hook.\ntype Hook interface {\n\t// Run runs the hook with the event.\n\tRun(e *Event, level Level, message string)\n}\n\n// HookFunc is an adaptor to allow the use of an ordinary function\n// as a Hook.\ntype HookFunc func(e *Event, level Level, message string)\n\n// Run implements the Hook interface.\nfunc (h HookFunc) Run(e *Event, level Level, message string) {\n\th(e, level, message)\n}\n\n// LevelHook applies a different hook for each level.\ntype LevelHook struct {\n\tNoLevelHook, TraceHook, DebugHook, InfoHook, WarnHook, ErrorHook, FatalHook, PanicHook Hook\n}\n\n// Run implements the Hook interface.\nfunc (h LevelHook) Run(e *Event, level Level, message string) {\n\tswitch level {\n\tcase TraceLevel:\n\t\tif h.TraceHook != nil {\n\t\t\th.TraceHook.Run(e, level, message)\n\t\t}\n\tcase DebugLevel:\n\t\tif h.DebugHook != nil {\n\t\t\th.DebugHook.Run(e, level, message)\n\t\t}\n\tcase InfoLevel:\n\t\tif h.InfoHook != nil {\n\t\t\th.InfoHook.Run(e, level, message)\n\t\t}\n\tcase WarnLevel:\n\t\tif h.WarnHook != nil {\n\t\t\th.WarnHook.Run(e, level, message)\n\t\t}\n\tcase ErrorLevel:\n\t\tif h.ErrorHook != nil {\n\t\t\th.ErrorHook.Run(e, level, message)\n\t\t}\n\tcase FatalLevel:\n\t\tif h.FatalHook != nil {\n\t\t\th.FatalHook.Run(e, level, message)\n\t\t}\n\tcase PanicLevel:\n\t\tif h.PanicHook != nil {\n\t\t\th.PanicHook.Run(e, level, message)\n\t\t}\n\tcase NoLevel:\n\t\tif h.NoLevelHook != nil {\n\t\t\th.NoLevelHook.Run(e, level, message)\n\t\t}\n\t}\n}\n\n// NewLevelHook returns a new LevelHook.\nfunc NewLevelHook() LevelHook {\n\treturn LevelHook{}\n}\n"
  },
  {
    "path": "hook_test.go",
    "content": "package zerolog\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"io\"\n\t\"testing\"\n)\n\ntype contextKeyType int\n\nvar contextKey contextKeyType\n\nvar (\n\tlevelNameHook = HookFunc(func(e *Event, level Level, msg string) {\n\t\tlevelName := level.String()\n\t\tif level == NoLevel {\n\t\t\tlevelName = \"nolevel\"\n\t\t}\n\t\te.Str(\"level_name\", levelName)\n\t})\n\tsimpleHook = HookFunc(func(e *Event, level Level, msg string) {\n\t\te.Bool(\"has_level\", level != NoLevel)\n\t\te.Str(\"test\", \"logged\")\n\t})\n\tcopyHook = HookFunc(func(e *Event, level Level, msg string) {\n\t\thasLevel := level != NoLevel\n\t\te.Bool(\"copy_has_level\", hasLevel)\n\t\tif hasLevel {\n\t\t\te.Str(\"copy_level\", level.String())\n\t\t}\n\t\te.Str(\"copy_msg\", msg)\n\t})\n\tnopHook = HookFunc(func(e *Event, level Level, message string) {\n\t})\n\tdiscardHook = HookFunc(func(e *Event, level Level, message string) {\n\t\te.Discard()\n\t})\n\tcontextHook = HookFunc(func(e *Event, level Level, message string) {\n\t\tcontextData, ok := e.GetCtx().Value(contextKey).(string)\n\t\tif ok {\n\t\t\te.Str(\"context-data\", contextData)\n\t\t}\n\t})\n)\n\nfunc TestHook(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\twant string\n\t\ttest func(log Logger)\n\t}{\n\t\t{\"Message\", `{\"message\":\"test message\"}` + \"\\n\", func(log Logger) {\n\t\t\tlog = log.Hook()\n\t\t\tlog.Log().Msg(\"test message\")\n\t\t}},\n\t\t{\"Message\", `{\"level_name\":\"nolevel\",\"message\":\"test message\"}` + \"\\n\", func(log Logger) {\n\t\t\tlog = log.Hook(levelNameHook)\n\t\t\tlog.Log().Msg(\"test message\")\n\t\t}},\n\t\t{\"NoLevel\", `{\"level_name\":\"nolevel\"}` + \"\\n\", func(log Logger) {\n\t\t\tlog = log.Hook(levelNameHook)\n\t\t\tlog.Log().Msg(\"\")\n\t\t}},\n\t\t{\"Print\", `{\"level\":\"debug\",\"level_name\":\"debug\"}` + \"\\n\", func(log Logger) {\n\t\t\tlog = log.Hook(levelNameHook)\n\t\t\tlog.Print(\"\")\n\t\t}},\n\t\t{\"Error\", `{\"level\":\"error\",\"level_name\":\"error\"}` + \"\\n\", func(log Logger) {\n\t\t\tlog = log.Hook(levelNameHook)\n\t\t\tlog.Error().Msg(\"\")\n\t\t}},\n\t\t{\"Copy/1\", `{\"copy_has_level\":false,\"copy_msg\":\"\"}` + \"\\n\", func(log Logger) {\n\t\t\tlog = log.Hook(copyHook)\n\t\t\tlog.Log().Msg(\"\")\n\t\t}},\n\t\t{\"Copy/2\", `{\"level\":\"info\",\"copy_has_level\":true,\"copy_level\":\"info\",\"copy_msg\":\"a message\",\"message\":\"a message\"}` + \"\\n\", func(log Logger) {\n\t\t\tlog = log.Hook(copyHook)\n\t\t\tlog.Info().Msg(\"a message\")\n\t\t}},\n\t\t{\"Multi\", `{\"level\":\"error\",\"level_name\":\"error\",\"has_level\":true,\"test\":\"logged\"}` + \"\\n\", func(log Logger) {\n\t\t\tlog = log.Hook(levelNameHook).Hook(simpleHook)\n\t\t\tlog.Error().Msg(\"\")\n\t\t}},\n\t\t{\"Multi/Message\", `{\"level\":\"error\",\"level_name\":\"error\",\"has_level\":true,\"test\":\"logged\",\"message\":\"a message\"}` + \"\\n\", func(log Logger) {\n\t\t\tlog = log.Hook(levelNameHook).Hook(simpleHook)\n\t\t\tlog.Error().Msg(\"a message\")\n\t\t}},\n\t\t{\"Output/single/pre\", `{\"level\":\"error\",\"level_name\":\"error\"}` + \"\\n\", func(log Logger) {\n\t\t\tignored := &bytes.Buffer{}\n\t\t\tlog = New(ignored).Hook(levelNameHook).Output(log.w)\n\t\t\tlog.Error().Msg(\"\")\n\t\t}},\n\t\t{\"Output/single/post\", `{\"level\":\"error\",\"level_name\":\"error\"}` + \"\\n\", func(log Logger) {\n\t\t\tignored := &bytes.Buffer{}\n\t\t\tlog = New(ignored).Output(log.w).Hook(levelNameHook)\n\t\t\tlog.Error().Msg(\"\")\n\t\t}},\n\t\t{\"Output/multi/pre\", `{\"level\":\"error\",\"level_name\":\"error\",\"has_level\":true,\"test\":\"logged\"}` + \"\\n\", func(log Logger) {\n\t\t\tignored := &bytes.Buffer{}\n\t\t\tlog = New(ignored).Hook(levelNameHook).Hook(simpleHook).Output(log.w)\n\t\t\tlog.Error().Msg(\"\")\n\t\t}},\n\t\t{\"Output/multi/post\", `{\"level\":\"error\",\"level_name\":\"error\",\"has_level\":true,\"test\":\"logged\"}` + \"\\n\", func(log Logger) {\n\t\t\tignored := &bytes.Buffer{}\n\t\t\tlog = New(ignored).Output(log.w).Hook(levelNameHook).Hook(simpleHook)\n\t\t\tlog.Error().Msg(\"\")\n\t\t}},\n\t\t{\"Output/mixed\", `{\"level\":\"error\",\"level_name\":\"error\",\"has_level\":true,\"test\":\"logged\"}` + \"\\n\", func(log Logger) {\n\t\t\tignored := &bytes.Buffer{}\n\t\t\tlog = New(ignored).Hook(levelNameHook).Output(log.w).Hook(simpleHook)\n\t\t\tlog.Error().Msg(\"\")\n\t\t}},\n\t\t{\"With/single/pre\", `{\"level\":\"error\",\"with\":\"pre\",\"level_name\":\"error\"}` + \"\\n\", func(log Logger) {\n\t\t\tlog = log.Hook(levelNameHook).With().Str(\"with\", \"pre\").Logger()\n\t\t\tlog.Error().Msg(\"\")\n\t\t}},\n\t\t{\"With/single/post\", `{\"level\":\"error\",\"with\":\"post\",\"level_name\":\"error\"}` + \"\\n\", func(log Logger) {\n\t\t\tlog = log.With().Str(\"with\", \"post\").Logger().Hook(levelNameHook)\n\t\t\tlog.Error().Msg(\"\")\n\t\t}},\n\t\t{\"With/multi/pre\", `{\"level\":\"error\",\"with\":\"pre\",\"level_name\":\"error\",\"has_level\":true,\"test\":\"logged\"}` + \"\\n\", func(log Logger) {\n\t\t\tlog = log.Hook(levelNameHook).Hook(simpleHook).With().Str(\"with\", \"pre\").Logger()\n\t\t\tlog.Error().Msg(\"\")\n\t\t}},\n\t\t{\"With/multi/post\", `{\"level\":\"error\",\"with\":\"post\",\"level_name\":\"error\",\"has_level\":true,\"test\":\"logged\"}` + \"\\n\", func(log Logger) {\n\t\t\tlog = log.With().Str(\"with\", \"post\").Logger().Hook(levelNameHook).Hook(simpleHook)\n\t\t\tlog.Error().Msg(\"\")\n\t\t}},\n\t\t{\"With/mixed\", `{\"level\":\"error\",\"with\":\"mixed\",\"level_name\":\"error\",\"has_level\":true,\"test\":\"logged\"}` + \"\\n\", func(log Logger) {\n\t\t\tlog = log.Hook(levelNameHook).With().Str(\"with\", \"mixed\").Logger().Hook(simpleHook)\n\t\t\tlog.Error().Msg(\"\")\n\t\t}},\n\t\t{\"Discard\", \"\", func(log Logger) {\n\t\t\tlog = log.Hook(discardHook)\n\t\t\tlog.Log().Msg(\"test message\")\n\t\t}},\n\t\t{\"Context/Background\", `{\"level\":\"info\",\"message\":\"test message\"}` + \"\\n\", func(log Logger) {\n\t\t\tlog = log.Hook(contextHook)\n\t\t\tlog.Info().Ctx(context.Background()).Msg(\"test message\")\n\t\t}},\n\t\t{\"Context/nil\", `{\"level\":\"info\",\"message\":\"test message\"}` + \"\\n\", func(log Logger) {\n\t\t\t// passing `nil` where a context is wanted is against\n\t\t\t// the rules, but people still do it.\n\t\t\tlog = log.Hook(contextHook)\n\t\t\tlog.Info().Ctx(nil).Msg(\"test message\") // nolint\n\t\t}},\n\t\t{\"Context/valid\", `{\"level\":\"info\",\"context-data\":\"12345abcdef\",\"message\":\"test message\"}` + \"\\n\", func(log Logger) {\n\t\t\tctx := context.Background()\n\t\t\tctx = context.WithValue(ctx, contextKey, \"12345abcdef\")\n\t\t\tlog = log.Hook(contextHook)\n\t\t\tlog.Info().Ctx(ctx).Msg(\"test message\")\n\t\t}},\n\t\t{\"Context/With/valid\", `{\"level\":\"info\",\"context-data\":\"12345abcdef\",\"message\":\"test message\"}` + \"\\n\", func(log Logger) {\n\t\t\tctx := context.Background()\n\t\t\tctx = context.WithValue(ctx, contextKey, \"12345abcdef\")\n\t\t\tlog = log.Hook(contextHook)\n\t\t\tlog = log.With().Ctx(ctx).Logger()\n\t\t\tlog.Info().Msg(\"test message\")\n\t\t}},\n\t\t{\"None\", `{\"level\":\"error\"}` + \"\\n\", func(log Logger) {\n\t\t\tlog.Error().Msg(\"\")\n\t\t}},\n\t}\n\tfor _, tt := range tests {\n\t\ttt := tt\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tout := &bytes.Buffer{}\n\t\t\tlog := New(out)\n\t\t\ttt.test(log)\n\t\t\tif got, want := decodeIfBinaryToString(out.Bytes()), tt.want; got != want {\n\t\t\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestLevelHook(t *testing.T) {\n\tvar called []string\n\n\ttraceHook := HookFunc(func(e *Event, level Level, msg string) {\n\t\tcalled = append(called, \"trace\")\n\t})\n\tdebugHook := HookFunc(func(e *Event, level Level, msg string) {\n\t\tcalled = append(called, \"debug\")\n\t})\n\tinfoHook := HookFunc(func(e *Event, level Level, msg string) {\n\t\tcalled = append(called, \"info\")\n\t})\n\twarnHook := HookFunc(func(e *Event, level Level, msg string) {\n\t\tcalled = append(called, \"warn\")\n\t})\n\terrorHook := HookFunc(func(e *Event, level Level, msg string) {\n\t\tcalled = append(called, \"error\")\n\t})\n\tfatalHook := HookFunc(func(e *Event, level Level, msg string) {\n\t\tcalled = append(called, \"fatal\")\n\t})\n\tpanicHook := HookFunc(func(e *Event, level Level, msg string) {\n\t\tcalled = append(called, \"panic\")\n\t})\n\tnoLevelHook := HookFunc(func(e *Event, level Level, msg string) {\n\t\tcalled = append(called, \"nolevel\")\n\t})\n\n\thook := LevelHook{\n\t\tTraceHook:   traceHook,\n\t\tDebugHook:   debugHook,\n\t\tInfoHook:    infoHook,\n\t\tWarnHook:    warnHook,\n\t\tErrorHook:   errorHook,\n\t\tFatalHook:   fatalHook,\n\t\tPanicHook:   panicHook,\n\t\tNoLevelHook: noLevelHook,\n\t}\n\n\te := &Event{}\n\n\t// Test each level\n\thook.Run(e, TraceLevel, \"\")\n\tif len(called) != 1 || called[0] != \"trace\" {\n\t\tt.Errorf(\"TraceLevel hook not called correctly: %v\", called)\n\t}\n\n\tcalled = nil\n\thook.Run(e, DebugLevel, \"\")\n\tif len(called) != 1 || called[0] != \"debug\" {\n\t\tt.Errorf(\"DebugLevel hook not called correctly: %v\", called)\n\t}\n\n\tcalled = nil\n\thook.Run(e, InfoLevel, \"\")\n\tif len(called) != 1 || called[0] != \"info\" {\n\t\tt.Errorf(\"InfoLevel hook not called correctly: %v\", called)\n\t}\n\n\tcalled = nil\n\thook.Run(e, WarnLevel, \"\")\n\tif len(called) != 1 || called[0] != \"warn\" {\n\t\tt.Errorf(\"WarnLevel hook not called correctly: %v\", called)\n\t}\n\n\tcalled = nil\n\thook.Run(e, ErrorLevel, \"\")\n\tif len(called) != 1 || called[0] != \"error\" {\n\t\tt.Errorf(\"ErrorLevel hook not called correctly: %v\", called)\n\t}\n\n\tcalled = nil\n\thook.Run(e, FatalLevel, \"\")\n\tif len(called) != 1 || called[0] != \"fatal\" {\n\t\tt.Errorf(\"FatalLevel hook not called correctly: %v\", called)\n\t}\n\n\tcalled = nil\n\thook.Run(e, PanicLevel, \"\")\n\tif len(called) != 1 || called[0] != \"panic\" {\n\t\tt.Errorf(\"PanicLevel hook not called correctly: %v\", called)\n\t}\n\n\tcalled = nil\n\thook.Run(e, NoLevel, \"\")\n\tif len(called) != 1 || called[0] != \"nolevel\" {\n\t\tt.Errorf(\"NoLevel hook not called correctly: %v\", called)\n\t}\n\n\t// Test NewLevelHook\n\t_ = NewLevelHook()\n}\n\nfunc BenchmarkHooks(b *testing.B) {\n\tlogger := New(io.Discard)\n\tb.ResetTimer()\n\tb.Run(\"Nop/Single\", func(b *testing.B) {\n\t\tlog := logger.Hook(nopHook)\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlog.Log().Msg(\"\")\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"Nop/Multi\", func(b *testing.B) {\n\t\tlog := logger.Hook(nopHook).Hook(nopHook)\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlog.Log().Msg(\"\")\n\t\t\t}\n\t\t})\n\t})\n\tb.Run(\"Simple\", func(b *testing.B) {\n\t\tlog := logger.Hook(simpleHook)\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlog.Log().Msg(\"\")\n\t\t\t}\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "internal/cbor/README.md",
    "content": "## Reference:\n   CBOR Encoding is described in [RFC7049](https://tools.ietf.org/html/rfc7049)\n\n## Comparison of JSON vs CBOR\n\nTwo main areas of reduction are:\n\n1. CPU usage to write a log msg \n2. Size (in bytes) of log messages.\n\n\nCPU Usage savings are below:\n```\nname                                    JSON time/op    CBOR time/op   delta\nInfo-32                                   15.3ns ± 1%    11.7ns ± 3%  -23.78%  (p=0.000 n=9+10)      \nContextFields-32                          16.2ns ± 2%    12.3ns ± 3%  -23.97%  (p=0.000 n=9+9)       \nContextAppend-32                          6.70ns ± 0%    6.20ns ± 0%   -7.44%  (p=0.000 n=9+9)       \nLogFields-32                              66.4ns ± 0%    24.6ns ± 2%  -62.89%  (p=0.000 n=10+9)      \nLogArrayObject-32                          911ns ±11%     768ns ± 6%  -15.64%  (p=0.000 n=10+10)     \nLogFieldType/Floats-32                    70.3ns ± 2%    29.5ns ± 1%  -57.98%  (p=0.000 n=10+10)     \nLogFieldType/Err-32                       14.0ns ± 3%    12.1ns ± 8%  -13.20%  (p=0.000 n=8+10)      \nLogFieldType/Dur-32                       17.2ns ± 2%    13.1ns ± 1%  -24.27%  (p=0.000 n=10+9)      \nLogFieldType/Object-32                    54.3ns ±11%    52.3ns ± 7%     ~     (p=0.239 n=10+10)     \nLogFieldType/Ints-32                      20.3ns ± 2%    15.1ns ± 2%  -25.50%  (p=0.000 n=9+10)      \nLogFieldType/Interfaces-32                 642ns ±11%     621ns ± 9%     ~     (p=0.118 n=10+10)     \nLogFieldType/Interface(Objects)-32         635ns ±13%     632ns ± 9%     ~     (p=0.592 n=10+10)     \nLogFieldType/Times-32                      294ns ± 0%      27ns ± 1%  -90.71%  (p=0.000 n=10+9)      \nLogFieldType/Durs-32                       121ns ± 0%      33ns ± 2%  -72.44%  (p=0.000 n=9+9)       \nLogFieldType/Interface(Object)-32         56.6ns ± 8%    52.3ns ± 8%   -7.54%  (p=0.007 n=10+10)     \nLogFieldType/Errs-32                      17.8ns ± 3%    16.1ns ± 2%   -9.71%  (p=0.000 n=10+9)      \nLogFieldType/Time-32                      40.5ns ± 1%    12.7ns ± 6%  -68.66%  (p=0.000 n=8+9)       \nLogFieldType/Bool-32                      12.0ns ± 5%    10.2ns ± 2%  -15.18%  (p=0.000 n=10+8)      \nLogFieldType/Bools-32                     17.2ns ± 2%    12.6ns ± 4%  -26.63%  (p=0.000 n=10+10)     \nLogFieldType/Int-32                       12.3ns ± 2%    11.2ns ± 4%   -9.27%  (p=0.000 n=9+10)      \nLogFieldType/Float-32                     16.7ns ± 1%    12.6ns ± 2%  -24.42%  (p=0.000 n=7+9)       \nLogFieldType/Str-32                       12.7ns ± 7%    11.3ns ± 7%  -10.88%  (p=0.000 n=10+9)      \nLogFieldType/Strs-32                      20.3ns ± 3%    18.2ns ± 3%  -10.25%  (p=0.000 n=9+10)      \nLogFieldType/Interface-32                  183ns ±12%     175ns ± 9%     ~     (p=0.078 n=10+10)     \n```\n\nLog message size savings is greatly dependent on the number and type of fields in the log message.\nAssuming this log message (with an Integer, timestamp and string, in addition to level).\n\n`{\"level\":\"error\",\"Fault\":41650,\"time\":\"2018-04-01T15:18:19-07:00\",\"message\":\"Some Message\"}`\n\nTwo measurements were done for the log file sizes - one without any compression, second\nusing [compress/zlib](https://golang.org/pkg/compress/zlib/). \n\nResults for 10,000 log messages:\n\n| Log Format |  Plain File Size (in KB) | Compressed File Size (in KB) |\n| :--- | :---: | :---: |\n| JSON | 920 | 28 |\n| CBOR | 550 | 28 |\n\nThe example used to calculate the above data is available in [Examples](examples).\n"
  },
  {
    "path": "internal/cbor/base.go",
    "content": "package cbor\n\n// JSONMarshalFunc is used to marshal interface to JSON encoded byte slice.\n// Making it package level instead of embedded in Encoder brings\n// some extra efforts at importing, but avoids value copy when the functions\n// of Encoder being invoked.\n// DO REMEMBER to set this variable at importing, or\n// you might get a nil pointer dereference panic at runtime.\nvar JSONMarshalFunc func(v interface{}) ([]byte, error)\n\ntype Encoder struct{}\n\n// AppendKey adds a key (string) to the binary encoded log message\nfunc (e Encoder) AppendKey(dst []byte, key string) []byte {\n\tif len(dst) < 1 {\n\t\tdst = e.AppendBeginMarker(dst)\n\t}\n\treturn e.AppendString(dst, key)\n}\n"
  },
  {
    "path": "internal/cbor/base_test.go",
    "content": "package cbor\n\nimport (\n\t\"bytes\"\n\t\"encoding/hex\"\n\t\"testing\"\n)\n\nfunc TestAppendKey(t *testing.T) {\n\twant := make([]byte, 0)\n\twant = append(want, 0xbf) // start string\n\twant = append(want, 0x63) // length 3\n\twant = append(want, []byte(\"key\")...)\n\n\tgot := enc.AppendKey([]byte{}, \"key\")\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendKey(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\t\"key\",\n\t\t\thex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n}\n"
  },
  {
    "path": "internal/cbor/cbor.go",
    "content": "// Package cbor provides primitives for storing different data\n// in the CBOR (binary) format. CBOR is defined in RFC7049.\npackage cbor\n\nimport \"time\"\n\nconst (\n\tmajorOffset   = 5\n\tadditionalMax = 23\n\n\t// Non Values.\n\tadditionalTypeBoolFalse byte = 20\n\tadditionalTypeBoolTrue  byte = 21\n\tadditionalTypeNull      byte = 22\n\n\t// Integer (+ve and -ve) Sub-types.\n\tadditionalTypeIntUint8  byte = 24\n\tadditionalTypeIntUint16 byte = 25\n\tadditionalTypeIntUint32 byte = 26\n\tadditionalTypeIntUint64 byte = 27\n\n\t// Float Sub-types.\n\tadditionalTypeFloat16 byte = 25\n\tadditionalTypeFloat32 byte = 26\n\tadditionalTypeFloat64 byte = 27\n\tadditionalTypeBreak   byte = 31\n\n\t// Tag Sub-types.\n\tadditionalTypeTimestamp    byte = 01\n\tadditionalTypeEmbeddedCBOR byte = 63\n\n\t// Extended Tags - from https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml\n\tadditionalTypeTagNetworkAddr   uint16 = 260\n\tadditionalTypeTagNetworkPrefix uint16 = 261\n\tadditionalTypeEmbeddedJSON     uint16 = 262\n\tadditionalTypeTagHexString     uint16 = 263\n\n\t// Unspecified number of elements.\n\tadditionalTypeInfiniteCount byte = 31\n)\nconst (\n\tmajorTypeUnsignedInt    byte = iota << majorOffset // Major type 0\n\tmajorTypeNegativeInt                               // Major type 1\n\tmajorTypeByteString                                // Major type 2\n\tmajorTypeUtf8String                                // Major type 3\n\tmajorTypeArray                                     // Major type 4\n\tmajorTypeMap                                       // Major type 5\n\tmajorTypeTags                                      // Major type 6\n\tmajorTypeSimpleAndFloat                            // Major type 7\n)\n\nconst (\n\tmaskOutAdditionalType byte = (7 << majorOffset)\n\tmaskOutMajorType      byte = 31\n)\n\nconst (\n\tfloat32Nan         = \"\\x7f\\xc0\\x00\\x00\"\n\tfloat32PosInfinity = \"\\x7f\\x80\\x00\\x00\"\n\tfloat32NegInfinity = \"\\xff\\x80\\x00\\x00\"\n\tfloat64Nan         = \"\\x7f\\xf8\\x00\\x00\\x00\\x00\\x00\\x00\"\n\tfloat64PosInfinity = \"\\x7f\\xf0\\x00\\x00\\x00\\x00\\x00\\x00\"\n\tfloat64NegInfinity = \"\\xff\\xf0\\x00\\x00\\x00\\x00\\x00\\x00\"\n)\n\n// IntegerTimeFieldFormat indicates the format of timestamp decoded\n// from an integer (time in seconds).\nvar IntegerTimeFieldFormat = time.RFC3339\n\n// NanoTimeFieldFormat indicates the format of timestamp decoded\n// from a float value (time in seconds and nanoseconds).\nvar NanoTimeFieldFormat = time.RFC3339Nano\n\nfunc appendCborTypePrefix(dst []byte, major byte, number uint64) []byte {\n\tvar byteCount int\n\tvar minor byte\n\tswitch {\n\tcase number < 256:\n\t\tbyteCount = 1\n\t\tminor = additionalTypeIntUint8\n\n\tcase number < 65536:\n\t\tbyteCount = 2\n\t\tminor = additionalTypeIntUint16\n\n\tcase number < 4294967296:\n\t\tbyteCount = 4\n\t\tminor = additionalTypeIntUint32\n\n\tdefault:\n\t\tbyteCount = 8\n\t\tminor = additionalTypeIntUint64\n\n\t}\n\n\tdst = append(dst, major|minor)\n\tbyteCount--\n\tfor ; byteCount >= 0; byteCount-- {\n\t\tdst = append(dst, byte(number>>(uint(byteCount)*8)))\n\t}\n\treturn dst\n}\n"
  },
  {
    "path": "internal/cbor/decode_stream.go",
    "content": "package cbor\n\n// This file contains code to decode a stream of CBOR Data into JSON.\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"net\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\t\"unicode/utf8\"\n)\n\nvar decodeTimeZone *time.Location\n\nconst hexTable = \"0123456789abcdef\"\n\nconst isFloat32 = 4\nconst isFloat64 = 8\n\nfunc readNBytes(src *bufio.Reader, n int) []byte {\n\tret := make([]byte, n)\n\tfor i := 0; i < n; i++ {\n\t\tch, e := src.ReadByte()\n\t\tif e != nil {\n\t\t\tpanic(fmt.Errorf(\"Tried to Read %d Bytes.. But hit end of file\", n))\n\t\t}\n\t\tret[i] = ch\n\t}\n\treturn ret\n}\n\nfunc readByte(src *bufio.Reader) byte {\n\tb, e := src.ReadByte()\n\tif e != nil {\n\t\tpanic(fmt.Errorf(\"Tried to Read 1 Byte.. But hit end of file\"))\n\t}\n\treturn b\n}\n\nfunc decodeIntAdditionalType(src *bufio.Reader, minor byte) int64 {\n\tval := int64(0)\n\tif minor <= 23 {\n\t\tval = int64(minor)\n\t} else {\n\t\tbytesToRead := 0\n\t\tswitch minor {\n\t\tcase additionalTypeIntUint8:\n\t\t\tbytesToRead = 1\n\t\tcase additionalTypeIntUint16:\n\t\t\tbytesToRead = 2\n\t\tcase additionalTypeIntUint32:\n\t\t\tbytesToRead = 4\n\t\tcase additionalTypeIntUint64:\n\t\t\tbytesToRead = 8\n\t\tdefault:\n\t\t\tpanic(fmt.Errorf(\"Invalid Additional Type: %d in decodeInteger (expected <28)\", minor))\n\t\t}\n\t\tpb := readNBytes(src, bytesToRead)\n\t\tfor i := 0; i < bytesToRead; i++ {\n\t\t\tval = val * 256\n\t\t\tval += int64(pb[i])\n\t\t}\n\t}\n\treturn val\n}\n\nfunc decodeInteger(src *bufio.Reader) int64 {\n\tpb := readByte(src)\n\tmajor := pb & maskOutAdditionalType\n\tminor := pb & maskOutMajorType\n\tif major != majorTypeUnsignedInt && major != majorTypeNegativeInt {\n\t\tpanic(fmt.Errorf(\"Major type is: %d in decodeInteger!! (expected 0 or 1)\", major))\n\t}\n\tval := decodeIntAdditionalType(src, minor)\n\tif major == 0 {\n\t\treturn val\n\t}\n\treturn (-1 - val)\n}\n\nfunc decodeFloat(src *bufio.Reader) (float64, int) {\n\tpb := readByte(src)\n\tmajor := pb & maskOutAdditionalType\n\tminor := pb & maskOutMajorType\n\tif major != majorTypeSimpleAndFloat {\n\t\tpanic(fmt.Errorf(\"Incorrect Major type is: %d in decodeFloat\", major))\n\t}\n\n\tswitch minor {\n\tcase additionalTypeFloat16:\n\t\tpanic(fmt.Errorf(\"float16 is not supported in decodeFloat\"))\n\n\tcase additionalTypeFloat32:\n\t\tpb := readNBytes(src, 4)\n\t\tswitch string(pb) {\n\t\tcase float32Nan:\n\t\t\treturn math.NaN(), isFloat32\n\t\tcase float32PosInfinity:\n\t\t\treturn math.Inf(0), isFloat32\n\t\tcase float32NegInfinity:\n\t\t\treturn math.Inf(-1), isFloat32\n\t\t}\n\t\tn := uint32(0)\n\t\tfor i := 0; i < 4; i++ {\n\t\t\tn = n * 256\n\t\t\tn += uint32(pb[i])\n\t\t}\n\t\tval := math.Float32frombits(n)\n\t\treturn float64(val), isFloat32\n\tcase additionalTypeFloat64:\n\t\tpb := readNBytes(src, 8)\n\t\tswitch string(pb) {\n\t\tcase float64Nan:\n\t\t\treturn math.NaN(), isFloat64\n\t\tcase float64PosInfinity:\n\t\t\treturn math.Inf(0), isFloat64\n\t\tcase float64NegInfinity:\n\t\t\treturn math.Inf(-1), isFloat64\n\t\t}\n\t\tn := uint64(0)\n\t\tfor i := 0; i < 8; i++ {\n\t\t\tn = n * 256\n\t\t\tn += uint64(pb[i])\n\t\t}\n\t\tval := math.Float64frombits(n)\n\t\treturn val, isFloat64\n\t}\n\tpanic(fmt.Errorf(\"Invalid Additional Type: %d in decodeFloat\", minor))\n}\n\nfunc decodeStringComplex(dst []byte, s string, pos uint) []byte {\n\ti := int(pos)\n\tstart := 0\n\n\tfor i < len(s) {\n\t\tb := s[i]\n\t\tif b >= utf8.RuneSelf {\n\t\t\tr, size := utf8.DecodeRuneInString(s[i:])\n\t\t\tif r == utf8.RuneError && size == 1 {\n\t\t\t\t// In case of error, first append previous simple characters to\n\t\t\t\t// the byte slice if any and append a replacement character code\n\t\t\t\t// in place of the invalid sequence.\n\t\t\t\tif start < i {\n\t\t\t\t\tdst = append(dst, s[start:i]...)\n\t\t\t\t}\n\t\t\t\tdst = append(dst, `\\ufffd`...)\n\t\t\t\ti += size\n\t\t\t\tstart = i\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ti += size\n\t\t\tcontinue\n\t\t}\n\t\tif b >= 0x20 && b <= 0x7e && b != '\\\\' && b != '\"' {\n\t\t\ti++\n\t\t\tcontinue\n\t\t}\n\t\t// We encountered a character that needs to be encoded.\n\t\t// Let's append the previous simple characters to the byte slice\n\t\t// and switch our operation to read and encode the remainder\n\t\t// characters byte-by-byte.\n\t\tif start < i {\n\t\t\tdst = append(dst, s[start:i]...)\n\t\t}\n\t\tswitch b {\n\t\tcase '\"', '\\\\':\n\t\t\tdst = append(dst, '\\\\', b)\n\t\tcase '\\b':\n\t\t\tdst = append(dst, '\\\\', 'b')\n\t\tcase '\\f':\n\t\t\tdst = append(dst, '\\\\', 'f')\n\t\tcase '\\n':\n\t\t\tdst = append(dst, '\\\\', 'n')\n\t\tcase '\\r':\n\t\t\tdst = append(dst, '\\\\', 'r')\n\t\tcase '\\t':\n\t\t\tdst = append(dst, '\\\\', 't')\n\t\tdefault:\n\t\t\tdst = append(dst, '\\\\', 'u', '0', '0', hexTable[b>>4], hexTable[b&0xF])\n\t\t}\n\t\ti++\n\t\tstart = i\n\t}\n\tif start < len(s) {\n\t\tdst = append(dst, s[start:]...)\n\t}\n\treturn dst\n}\n\nfunc decodeString(src *bufio.Reader, noQuotes bool) []byte {\n\tpb := readByte(src)\n\tmajor := pb & maskOutAdditionalType\n\tminor := pb & maskOutMajorType\n\tif major != majorTypeByteString {\n\t\tpanic(fmt.Errorf(\"Major type is: %d in decodeString\", major))\n\t}\n\tresult := []byte{}\n\tif !noQuotes {\n\t\tresult = append(result, '\"')\n\t}\n\tlength := decodeIntAdditionalType(src, minor)\n\tlen := int(length)\n\tpbs := readNBytes(src, len)\n\tresult = append(result, pbs...)\n\tif noQuotes {\n\t\treturn result\n\t}\n\treturn append(result, '\"')\n}\nfunc decodeStringToDataUrl(src *bufio.Reader, mimeType string) []byte {\n\tpb := readByte(src)\n\tmajor := pb & maskOutAdditionalType\n\tminor := pb & maskOutMajorType\n\tif major != majorTypeByteString {\n\t\tpanic(fmt.Errorf(\"Major type is: %d in decodeString\", major))\n\t}\n\tlength := decodeIntAdditionalType(src, minor)\n\tl := int(length)\n\tenc := base64.StdEncoding\n\tlEnc := enc.EncodedLen(l)\n\tresult := make([]byte, len(\"\\\"data:;base64,\\\"\")+len(mimeType)+lEnc)\n\tdest := result\n\tu := copy(dest, \"\\\"data:\")\n\tdest = dest[u:]\n\tu = copy(dest, mimeType)\n\tdest = dest[u:]\n\tu = copy(dest, \";base64,\")\n\tdest = dest[u:]\n\tpbs := readNBytes(src, l)\n\tenc.Encode(dest, pbs)\n\tdest = dest[lEnc:]\n\tdest[0] = '\"'\n\treturn result\n}\n\nfunc decodeUTF8String(src *bufio.Reader) []byte {\n\tpb := readByte(src)\n\tmajor := pb & maskOutAdditionalType\n\tminor := pb & maskOutMajorType\n\tif major != majorTypeUtf8String {\n\t\tpanic(fmt.Errorf(\"Major type is: %d in decodeUTF8String\", major))\n\t}\n\tresult := []byte{'\"'}\n\tlength := decodeIntAdditionalType(src, minor)\n\tlen := int(length)\n\tpbs := readNBytes(src, len)\n\n\tfor i := 0; i < len; i++ {\n\t\t// Check if the character needs encoding. Control characters, slashes,\n\t\t// and the double quote need json encoding. Bytes above the ascii\n\t\t// boundary needs utf8 encoding.\n\t\tif pbs[i] < 0x20 || pbs[i] > 0x7e || pbs[i] == '\\\\' || pbs[i] == '\"' {\n\t\t\t// We encountered a character that needs to be encoded. Switch\n\t\t\t// to complex version of the algorithm.\n\t\t\tdst := []byte{'\"'}\n\t\t\tdst = decodeStringComplex(dst, string(pbs), uint(i))\n\t\t\treturn append(dst, '\"')\n\t\t}\n\t}\n\t// The string has no need for encoding and therefore is directly\n\t// appended to the byte slice.\n\tresult = append(result, pbs...)\n\treturn append(result, '\"')\n}\n\nfunc array2Json(src *bufio.Reader, dst io.Writer) {\n\tdst.Write([]byte{'['})\n\tpb := readByte(src)\n\tmajor := pb & maskOutAdditionalType\n\tminor := pb & maskOutMajorType\n\tif major != majorTypeArray {\n\t\tpanic(fmt.Errorf(\"Major type is: %d in array2Json\", major))\n\t}\n\tlen := 0\n\tunSpecifiedCount := false\n\tif minor == additionalTypeInfiniteCount {\n\t\tunSpecifiedCount = true\n\t} else {\n\t\tlength := decodeIntAdditionalType(src, minor)\n\t\tlen = int(length)\n\t}\n\tfor i := 0; unSpecifiedCount || i < len; i++ {\n\t\tif unSpecifiedCount {\n\t\t\tpb, e := src.Peek(1)\n\t\t\tif e != nil {\n\t\t\t\tpanic(e)\n\t\t\t}\n\t\t\tif pb[0] == majorTypeSimpleAndFloat|additionalTypeBreak {\n\t\t\t\treadByte(src)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tcbor2JsonOneObject(src, dst)\n\t\tif unSpecifiedCount {\n\t\t\tpb, e := src.Peek(1)\n\t\t\tif e != nil {\n\t\t\t\tpanic(e)\n\t\t\t}\n\t\t\tif pb[0] == majorTypeSimpleAndFloat|additionalTypeBreak {\n\t\t\t\treadByte(src)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tdst.Write([]byte{','})\n\t\t} else if i+1 < len {\n\t\t\tdst.Write([]byte{','})\n\t\t}\n\t}\n\tdst.Write([]byte{']'})\n}\n\nfunc map2Json(src *bufio.Reader, dst io.Writer) {\n\tpb := readByte(src)\n\tmajor := pb & maskOutAdditionalType\n\tminor := pb & maskOutMajorType\n\tif major != majorTypeMap {\n\t\tpanic(fmt.Errorf(\"Major type is: %d in map2Json\", major))\n\t}\n\tlen := 0\n\tunSpecifiedCount := false\n\tif minor == additionalTypeInfiniteCount {\n\t\tunSpecifiedCount = true\n\t} else {\n\t\tlength := decodeIntAdditionalType(src, minor)\n\t\tlen = int(length)\n\t}\n\tdst.Write([]byte{'{'})\n\tfor i := 0; unSpecifiedCount || i < len; i++ {\n\t\tif unSpecifiedCount {\n\t\t\tpb, e := src.Peek(1)\n\t\t\tif e != nil {\n\t\t\t\tpanic(e)\n\t\t\t}\n\t\t\tif pb[0] == majorTypeSimpleAndFloat|additionalTypeBreak {\n\t\t\t\treadByte(src)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tcbor2JsonOneObject(src, dst)\n\t\tif i%2 == 0 {\n\t\t\t// Even position values are keys.\n\t\t\tdst.Write([]byte{':'})\n\t\t} else {\n\t\t\tif unSpecifiedCount {\n\t\t\t\tpb, e := src.Peek(1)\n\t\t\t\tif e != nil {\n\t\t\t\t\tpanic(e)\n\t\t\t\t}\n\t\t\t\tif pb[0] == majorTypeSimpleAndFloat|additionalTypeBreak {\n\t\t\t\t\treadByte(src)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tdst.Write([]byte{','})\n\t\t\t} else if i+1 < len {\n\t\t\t\tdst.Write([]byte{','})\n\t\t\t}\n\t\t}\n\t}\n\tdst.Write([]byte{'}'})\n}\n\nfunc decodeTagData(src *bufio.Reader) []byte {\n\tpb := readByte(src)\n\tmajor := pb & maskOutAdditionalType\n\tminor := pb & maskOutMajorType\n\tif major != majorTypeTags {\n\t\tpanic(fmt.Errorf(\"Major type is: %d in decodeTagData\", major))\n\t}\n\tswitch minor {\n\tcase additionalTypeTimestamp:\n\t\treturn decodeTimeStamp(src)\n\tcase additionalTypeIntUint8:\n\t\tval := decodeIntAdditionalType(src, minor)\n\t\tswitch byte(val) {\n\t\tcase additionalTypeEmbeddedCBOR:\n\t\t\tpb := readByte(src)\n\t\t\tdataMajor := pb & maskOutAdditionalType\n\t\t\tif dataMajor != majorTypeByteString {\n\t\t\t\tpanic(fmt.Errorf(\"Unsupported embedded Type: %d in decodeEmbeddedCBOR\", dataMajor))\n\t\t\t}\n\t\t\tsrc.UnreadByte()\n\t\t\treturn decodeStringToDataUrl(src, \"application/cbor\")\n\t\tdefault:\n\t\t\tpanic(fmt.Errorf(\"Unsupported Additional Tag Type: %d in decodeTagData\", val))\n\t\t}\n\n\t// Tag value is larger than 256 (so uint16).\n\tcase additionalTypeIntUint16:\n\t\tval := decodeIntAdditionalType(src, minor)\n\n\t\tswitch uint16(val) {\n\t\tcase additionalTypeEmbeddedJSON:\n\t\t\tpb := readByte(src)\n\t\t\tdataMajor := pb & maskOutAdditionalType\n\t\t\tif dataMajor != majorTypeByteString {\n\t\t\t\tpanic(fmt.Errorf(\"Unsupported embedded Type: %d in decodeEmbeddedJSON\", dataMajor))\n\t\t\t}\n\t\t\tsrc.UnreadByte()\n\t\t\treturn decodeString(src, true)\n\n\t\tcase additionalTypeTagNetworkAddr:\n\t\t\toctets := decodeString(src, true)\n\t\t\tss := []byte{'\"'}\n\t\t\tswitch len(octets) {\n\t\t\tcase 6: // MAC address.\n\t\t\t\tha := net.HardwareAddr(octets)\n\t\t\t\tss = append(append(ss, ha.String()...), '\"')\n\t\t\tcase 4: // IPv4 address.\n\t\t\t\tfallthrough\n\t\t\tcase 16: // IPv6 address.\n\t\t\t\tip := net.IP(octets)\n\t\t\t\tss = append(append(ss, ip.String()...), '\"')\n\t\t\tdefault:\n\t\t\t\tpanic(fmt.Errorf(\"Unexpected Network Address length: %d (expected 4,6,16)\", len(octets)))\n\t\t\t}\n\t\t\treturn ss\n\n\t\tcase additionalTypeTagNetworkPrefix:\n\t\t\tpb := readByte(src)\n\t\t\tif pb != majorTypeMap|0x1 {\n\t\t\t\tpanic(fmt.Errorf(\"IP Prefix is NOT of MAP of 1 elements as expected\"))\n\t\t\t}\n\t\t\toctets := decodeString(src, true)\n\t\t\tval := decodeInteger(src)\n\t\t\tip := net.IP(octets)\n\t\t\tvar mask net.IPMask\n\t\t\tpfxLen := int(val)\n\t\t\tif len(octets) == 4 {\n\t\t\t\tmask = net.CIDRMask(pfxLen, 32)\n\t\t\t} else {\n\t\t\t\tmask = net.CIDRMask(pfxLen, 128)\n\t\t\t}\n\t\t\tipPfx := net.IPNet{IP: ip, Mask: mask}\n\t\t\tss := []byte{'\"'}\n\t\t\tss = append(append(ss, ipPfx.String()...), '\"')\n\t\t\treturn ss\n\n\t\tcase additionalTypeTagHexString:\n\t\t\toctets := decodeString(src, true)\n\t\t\tss := []byte{'\"'}\n\t\t\tfor _, v := range octets {\n\t\t\t\tss = append(ss, hexTable[v>>4], hexTable[v&0x0f])\n\t\t\t}\n\t\t\treturn append(ss, '\"')\n\n\t\tdefault:\n\t\t\tpanic(fmt.Errorf(\"Unsupported Additional Tag Type: %d in decodeTagData\", val))\n\t\t}\n\t}\n\tpanic(fmt.Errorf(\"Unsupported Additional Type: %d in decodeTagData\", minor))\n}\n\nfunc decodeTimeStamp(src *bufio.Reader) []byte {\n\tpb := readByte(src)\n\tsrc.UnreadByte()\n\ttsMajor := pb & maskOutAdditionalType\n\tif tsMajor == majorTypeUnsignedInt || tsMajor == majorTypeNegativeInt {\n\t\tn := decodeInteger(src)\n\t\tt := time.Unix(n, 0)\n\t\tif decodeTimeZone != nil {\n\t\t\tt = t.In(decodeTimeZone)\n\t\t} else {\n\t\t\tt = t.In(time.UTC)\n\t\t}\n\t\ttsb := []byte{}\n\t\ttsb = append(tsb, '\"')\n\t\ttsb = t.AppendFormat(tsb, IntegerTimeFieldFormat)\n\t\ttsb = append(tsb, '\"')\n\t\treturn tsb\n\t} else if tsMajor == majorTypeSimpleAndFloat {\n\t\tn, _ := decodeFloat(src)\n\t\tsecs := int64(n)\n\t\tn -= float64(secs)\n\t\tn *= float64(1e9)\n\t\tt := time.Unix(secs, int64(n))\n\t\tif decodeTimeZone != nil {\n\t\t\tt = t.In(decodeTimeZone)\n\t\t} else {\n\t\t\tt = t.In(time.UTC)\n\t\t}\n\t\ttsb := []byte{}\n\t\ttsb = append(tsb, '\"')\n\t\ttsb = t.AppendFormat(tsb, NanoTimeFieldFormat)\n\t\ttsb = append(tsb, '\"')\n\t\treturn tsb\n\t}\n\tpanic(fmt.Errorf(\"TS format is neither int nor float: %d\", tsMajor))\n}\n\nfunc decodeSimpleFloat(src *bufio.Reader) []byte {\n\tpb := readByte(src)\n\tmajor := pb & maskOutAdditionalType\n\tminor := pb & maskOutMajorType\n\tif major != majorTypeSimpleAndFloat {\n\t\tpanic(fmt.Errorf(\"Major type is: %d in decodeSimpleFloat\", major))\n\t}\n\tswitch minor {\n\tcase additionalTypeBoolTrue:\n\t\treturn []byte(\"true\")\n\tcase additionalTypeBoolFalse:\n\t\treturn []byte(\"false\")\n\tcase additionalTypeNull:\n\t\treturn []byte(\"null\")\n\tcase additionalTypeFloat16:\n\t\tfallthrough\n\tcase additionalTypeFloat32:\n\t\tfallthrough\n\tcase additionalTypeFloat64:\n\t\tsrc.UnreadByte()\n\t\tv, bc := decodeFloat(src)\n\t\tba := []byte{}\n\t\tswitch {\n\t\tcase math.IsNaN(v):\n\t\t\treturn []byte(\"\\\"NaN\\\"\")\n\t\tcase math.IsInf(v, 1):\n\t\t\treturn []byte(\"\\\"+Inf\\\"\")\n\t\tcase math.IsInf(v, -1):\n\t\t\treturn []byte(\"\\\"-Inf\\\"\")\n\t\t}\n\t\tif bc == isFloat32 {\n\t\t\tba = strconv.AppendFloat(ba, v, 'f', -1, 32)\n\t\t} else if bc == isFloat64 {\n\t\t\tba = strconv.AppendFloat(ba, v, 'f', -1, 64)\n\t\t} else {\n\t\t\tpanic(fmt.Errorf(\"Invalid Float precision from decodeFloat: %d\", bc))\n\t\t}\n\t\treturn ba\n\tdefault:\n\t\tpanic(fmt.Errorf(\"Invalid Additional Type: %d in decodeSimpleFloat\", minor))\n\t}\n}\n\nfunc cbor2JsonOneObject(src *bufio.Reader, dst io.Writer) {\n\tpb, e := src.Peek(1)\n\tif e != nil {\n\t\tpanic(e)\n\t}\n\tmajor := (pb[0] & maskOutAdditionalType)\n\n\tswitch major {\n\tcase majorTypeUnsignedInt:\n\t\tfallthrough\n\tcase majorTypeNegativeInt:\n\t\tn := decodeInteger(src)\n\t\tdst.Write([]byte(strconv.Itoa(int(n))))\n\n\tcase majorTypeByteString:\n\t\ts := decodeString(src, false)\n\t\tdst.Write(s)\n\n\tcase majorTypeUtf8String:\n\t\ts := decodeUTF8String(src)\n\t\tdst.Write(s)\n\n\tcase majorTypeArray:\n\t\tarray2Json(src, dst)\n\n\tcase majorTypeMap:\n\t\tmap2Json(src, dst)\n\n\tcase majorTypeTags:\n\t\ts := decodeTagData(src)\n\t\tdst.Write(s)\n\n\tcase majorTypeSimpleAndFloat:\n\t\ts := decodeSimpleFloat(src)\n\t\tdst.Write(s)\n\t}\n}\n\nfunc moreBytesToRead(src *bufio.Reader) bool {\n\t_, e := src.ReadByte()\n\tif e == nil {\n\t\tsrc.UnreadByte()\n\t\treturn true\n\t}\n\treturn false\n}\n\n// Cbor2JsonManyObjects decodes all the CBOR Objects read from src\n// reader. It keeps on decoding until reader returns EOF (error when reading).\n// Decoded string is written to the dst. At the end of every CBOR Object\n// newline is written to the output stream.\n//\n// Returns error (if any) that was encountered during decode.\n// The child functions will generate a panic when error is encountered and\n// this function will recover non-runtime Errors and return the reason as error.\nfunc Cbor2JsonManyObjects(src io.Reader, dst io.Writer) (err error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tif _, ok := r.(runtime.Error); ok {\n\t\t\t\tpanic(r)\n\t\t\t}\n\t\t\terr = r.(error)\n\t\t}\n\t}()\n\tbufRdr := bufio.NewReader(src)\n\tfor moreBytesToRead(bufRdr) {\n\t\tcbor2JsonOneObject(bufRdr, dst)\n\t\tdst.Write([]byte(\"\\n\"))\n\t}\n\treturn nil\n}\n\n// Detect if the bytes to be printed is Binary or not.\nfunc binaryFmt(p []byte) bool {\n\tif len(p) > 0 && p[0] > 0x7F {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc getReader(str string) *bufio.Reader {\n\treturn bufio.NewReader(strings.NewReader(str))\n}\n\n// DecodeIfBinaryToString converts a binary formatted log msg to a\n// JSON formatted String Log message - suitable for printing to Console/Syslog.\nfunc DecodeIfBinaryToString(in []byte) string {\n\tif binaryFmt(in) {\n\t\tvar b bytes.Buffer\n\t\tCbor2JsonManyObjects(strings.NewReader(string(in)), &b)\n\t\treturn b.String()\n\t}\n\treturn string(in)\n}\n\n// DecodeObjectToStr checks if the input is a binary format, if so,\n// it will decode a single Object and return the decoded string.\nfunc DecodeObjectToStr(in []byte) string {\n\tif binaryFmt(in) {\n\t\tvar b bytes.Buffer\n\t\tcbor2JsonOneObject(getReader(string(in)), &b)\n\t\treturn b.String()\n\t}\n\treturn string(in)\n}\n\n// DecodeIfBinaryToBytes checks if the input is a binary format, if so,\n// it will decode all Objects and return the decoded string as byte array.\nfunc DecodeIfBinaryToBytes(in []byte) []byte {\n\tif binaryFmt(in) {\n\t\tvar b bytes.Buffer\n\t\tCbor2JsonManyObjects(bytes.NewReader(in), &b)\n\t\treturn b.Bytes()\n\t}\n\treturn in\n}\n"
  },
  {
    "path": "internal/cbor/decoder_test.go",
    "content": "package cbor\n\nimport (\n\t\"bytes\"\n\t\"encoding/hex\"\n\t\"math\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/rs/zerolog/internal\"\n)\n\nfunc TestDecodeInteger(t *testing.T) {\n\tfor _, tc := range internal.IntegerTestCases {\n\t\tgotv := decodeInteger(getReader(tc.Binary))\n\t\tif gotv != int64(tc.Val) {\n\t\t\tt.Errorf(\"decodeInteger(0x%s)=0x%d, want: 0x%d\",\n\t\t\t\thex.EncodeToString([]byte(tc.Binary)), gotv, tc.Val)\n\t\t}\n\t}\n}\n\nfunc TestDecodeString(t *testing.T) {\n\tfor _, tt := range encodeStringTests {\n\t\tgot := decodeUTF8String(getReader(tt.binary))\n\t\tif string(got) != \"\\\"\"+tt.json+\"\\\"\" {\n\t\t\tt.Errorf(\"DecodeString(0x%s)=%s, want:\\\"%s\\\"\\n\", hex.EncodeToString([]byte(tt.binary)), string(got),\n\t\t\t\thex.EncodeToString([]byte(tt.json)))\n\t\t}\n\t}\n}\n\nfunc TestDecodeArray(t *testing.T) {\n\tfor _, tc := range internal.IntegerArrayTestCases {\n\t\tbuf := bytes.NewBuffer([]byte{})\n\t\tarray2Json(getReader(tc.Binary), buf)\n\t\tif buf.String() != tc.Json {\n\t\t\tt.Errorf(\"array2Json(0x%s)=%s, want: %s\", hex.EncodeToString([]byte(tc.Binary)), buf.String(), tc.Json)\n\t\t}\n\t}\n\t//Unspecified Length Array\n\tvar infiniteArrayTestCases = []struct {\n\t\tin  string\n\t\tout string\n\t}{\n\t\t{\"\\x9f\\x20\\x00\\x18\\xc8\\x14\\xff\", \"[-1,0,200,20]\"},\n\t\t{\"\\x9f\\x38\\xc7\\x29\\x18\\xc8\\x19\\x01\\x90\\xff\", \"[-200,-10,200,400]\"},\n\t\t{\"\\x9f\\x01\\x02\\x03\\xff\", \"[1,2,3]\"},\n\t\t{\"\\x9f\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x18\\x18\\x19\\xff\",\n\t\t\t\"[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]\"},\n\t}\n\tfor _, tc := range infiniteArrayTestCases {\n\t\tbuf := bytes.NewBuffer([]byte{})\n\t\tarray2Json(getReader(tc.in), buf)\n\t\tif buf.String() != tc.out {\n\t\t\tt.Errorf(\"array2Json(0x%s)=%s, want: %s\", hex.EncodeToString([]byte(tc.out)), buf.String(), tc.out)\n\t\t}\n\t}\n\tfor _, tc := range internal.BooleanArrayTestCases {\n\t\tbuf := bytes.NewBuffer([]byte{})\n\t\tarray2Json(getReader(tc.Binary), buf)\n\t\tif buf.String() != tc.Json {\n\t\t\tt.Errorf(\"array2Json(0x%s)=%s, want: %s\", hex.EncodeToString([]byte(tc.Binary)), buf.String(), tc.Json)\n\t\t}\n\t}\n\t//TODO add cases for arrays of other types\n}\n\nvar infiniteMapDecodeTestCases = []struct {\n\tBin  []byte\n\tJson string\n}{\n\t{[]byte(\"\\xbf\\x64IETF\\x20\\xff\"), \"{\\\"IETF\\\":-1}\"},\n\t{[]byte(\"\\xbf\\x65Array\\x84\\x20\\x00\\x18\\xc8\\x14\\xff\"), \"{\\\"Array\\\":[-1,0,200,20]}\"},\n}\n\nvar mapDecodeTestCases = []struct {\n\tBin  []byte\n\tJson string\n}{\n\t{[]byte(\"\\xa2\\x64IETF\\x20\"), \"{\\\"IETF\\\":-1}\"},\n\t{[]byte(\"\\xa2\\x65Array\\x84\\x20\\x00\\x18\\xc8\\x14\"), \"{\\\"Array\\\":[-1,0,200,20]}\"},\n\t{[]byte(\"\\xa6\\x61\\x61\\x01\\x61\\x62\\x02\\x61\\x63\\x03\"), \"{\\\"a\\\":1,\\\"b\\\":2,\\\"c\\\":3}\"},\n\t{[]byte(\"\\xbf\\x61a\\x01\\x61b\\x02\\xff\"), \"{\\\"a\\\":1,\\\"b\\\":2}\"},\n}\n\nfunc TestDecodeMap(t *testing.T) {\n\tfor _, tc := range mapDecodeTestCases {\n\t\tbuf := bytes.NewBuffer([]byte{})\n\t\tmap2Json(getReader(string(tc.Bin)), buf)\n\t\tif buf.String() != tc.Json {\n\t\t\tt.Errorf(\"map2Json(0x%s)=%s, want: %s\", hex.EncodeToString(tc.Bin), buf.String(), tc.Json)\n\t\t}\n\t}\n\tfor _, tc := range infiniteMapDecodeTestCases {\n\t\tbuf := bytes.NewBuffer([]byte{})\n\t\tmap2Json(getReader(string(tc.Bin)), buf)\n\t\tif buf.String() != tc.Json {\n\t\t\tt.Errorf(\"map2Json(0x%s)=%s, want: %s\", hex.EncodeToString(tc.Bin), buf.String(), tc.Json)\n\t\t}\n\t}\n}\n\nfunc TestDecodeBool(t *testing.T) {\n\tfor _, tc := range internal.BooleanTestCases {\n\t\tgot := decodeSimpleFloat(getReader(tc.Binary))\n\t\tif string(got) != tc.Json {\n\t\t\tt.Errorf(\"decodeSimpleFloat(0x%s)=%s, want:%s\", hex.EncodeToString([]byte(tc.Binary)), string(got), tc.Json)\n\t\t}\n\t}\n}\n\nfunc TestDecodeFloat(t *testing.T) {\n\tfor _, tc := range internal.Float32TestCases {\n\t\tgot, _ := decodeFloat(getReader(tc.Binary))\n\t\tif got != float64(tc.Val) && math.IsNaN(got) != math.IsNaN(float64(tc.Val)) {\n\t\t\tt.Errorf(\"decodeFloat(0x%s)=%f, want:%f\", hex.EncodeToString([]byte(tc.Binary)), got, tc.Val)\n\t\t}\n\t}\n\tfor _, tc := range internal.Float64TestCases {\n\t\tgot, _ := decodeFloat(getReader(tc.Binary))\n\t\tif got != tc.Val && math.IsNaN(got) != math.IsNaN(tc.Val) {\n\t\t\tt.Errorf(\"decodeFloat(0x%s)=%f, want:%f\", hex.EncodeToString([]byte(tc.Binary)), got, tc.Val)\n\t\t}\n\t}\n\n\t// Test float64 special values with correct CBOR encoding\n\tfloat64Tests := []struct {\n\t\tname  string\n\t\tinput string\n\t\twant  float64\n\t}{\n\t\t{\"float64 NaN\", \"\\xfb\\x7f\\xf8\\x00\\x00\\x00\\x00\\x00\\x00\", math.NaN()},\n\t\t{\"float64 +Inf\", \"\\xfb\\x7f\\xf0\\x00\\x00\\x00\\x00\\x00\\x00\", math.Inf(0)},\n\t\t{\"float64 -Inf\", \"\\xfb\\xff\\xf0\\x00\\x00\\x00\\x00\\x00\\x00\", math.Inf(-1)},\n\t\t{\"float64 1.0\", \"\\xfb\\x3f\\xf0\\x00\\x00\\x00\\x00\\x00\\x00\", 1.0},\n\t}\n\n\tfor _, tt := range float64Tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot, _ := decodeFloat(getReader(tt.input))\n\t\t\tif math.IsNaN(tt.want) {\n\t\t\t\tif !math.IsNaN(got) {\n\t\t\t\t\tt.Errorf(\"decodeFloat(%q) = %f, want NaN\", tt.input, got)\n\t\t\t\t}\n\t\t\t} else if math.IsInf(tt.want, 0) {\n\t\t\t\tif !math.IsInf(got, 0) {\n\t\t\t\t\tt.Errorf(\"decodeFloat(%q) = %f, want +Inf\", tt.input, got)\n\t\t\t\t}\n\t\t\t} else if math.IsInf(tt.want, -1) {\n\t\t\t\tif !math.IsInf(got, -1) {\n\t\t\t\t\tt.Errorf(\"decodeFloat(%q) = %f, want -Inf\", tt.input, got)\n\t\t\t\t}\n\t\t\t} else if got != tt.want {\n\t\t\t\tt.Errorf(\"decodeFloat(%q) = %f, want %f\", tt.input, got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestDecodeTimestamp(t *testing.T) {\n\tdecodeTimeZone, _ = time.LoadLocation(\"UTC\")\n\tfor _, tc := range internal.TimeIntegerTestcases {\n\t\ttm := decodeTagData(getReader(tc.Binary))\n\t\tif string(tm) != \"\\\"\"+tc.RfcStr+\"\\\"\" {\n\t\t\tt.Errorf(\"decodeFloat(0x%s)=%s, want:%s\", hex.EncodeToString([]byte(tc.Binary)), tm, tc.RfcStr)\n\t\t}\n\t}\n\tfor _, tc := range internal.TimeFloatTestcases {\n\t\ttm := decodeTagData(getReader(tc.Out))\n\t\t//Since we convert to float and back - it may be slightly off - so\n\t\t//we cannot check for exact equality instead, we'll check it is\n\t\t//very close to each other Less than a Microsecond (lets not yet do nanosec)\n\n\t\tgot, _ := time.Parse(string(tm), string(tm))\n\t\twant, _ := time.Parse(tc.RfcStr, tc.RfcStr)\n\t\tif got.Sub(want) > time.Microsecond {\n\t\t\tt.Errorf(\"decodeFloat(0x%s)=%s, want:%s\", hex.EncodeToString([]byte(tc.Out)), tm, tc.RfcStr)\n\t\t}\n\t}\n\n\t// Test with decodeTimeZone = nil to cover the else branches\n\toldTimeZone := decodeTimeZone\n\tdecodeTimeZone = nil\n\tdefer func() { decodeTimeZone = oldTimeZone }()\n\n\tfor _, tc := range internal.TimeIntegerTestcases {\n\t\ttm := decodeTagData(getReader(tc.Binary))\n\t\tif string(tm) != \"\\\"\"+tc.RfcStr+\"\\\"\" {\n\t\t\tt.Errorf(\"decodeFloat(0x%s)=%s, want:%s\", hex.EncodeToString([]byte(tc.Binary)), tm, tc.RfcStr)\n\t\t}\n\t}\n\tfor _, tc := range internal.TimeFloatTestcases {\n\t\ttm := decodeTagData(getReader(tc.Out))\n\t\tgot, _ := time.Parse(string(tm), string(tm))\n\t\twant, _ := time.Parse(tc.RfcStr, tc.RfcStr)\n\t\tif got.Sub(want) > time.Microsecond {\n\t\t\tt.Errorf(\"decodeFloat(0x%s)=%s, want:%s\", hex.EncodeToString([]byte(tc.Out)), tm, tc.RfcStr)\n\t\t}\n\t}\n}\n\nfunc TestDecodeNetworkAddr(t *testing.T) {\n\tfor _, tc := range internal.IpAddrTestCases {\n\t\td1 := decodeTagData(getReader(tc.Binary))\n\t\tif string(d1) != tc.Text {\n\t\t\tt.Errorf(\"decodeNetworkAddr(0x%s)=%s, want:%s\", hex.EncodeToString([]byte(tc.Binary)), d1, tc.Text)\n\t\t}\n\t}\n}\n\nfunc TestDecodeMACAddr(t *testing.T) {\n\tfor _, tc := range internal.MacAddrTestCases {\n\t\td1 := decodeTagData(getReader(tc.Binary))\n\t\tif string(d1) != tc.Text {\n\t\t\tt.Errorf(\"decodeNetworkAddr(0x%s)=%s, want:%s\", hex.EncodeToString([]byte(tc.Binary)), d1, tc.Text)\n\t\t}\n\t}\n}\n\nfunc TestDecodeIPPrefix(t *testing.T) {\n\tfor _, tc := range internal.IPPrefixTestCases {\n\t\td1 := decodeTagData(getReader(tc.Binary))\n\t\tif string(d1) != tc.Text {\n\t\t\tt.Errorf(\"decodeIPPrefix(0x%s)=%s, want:%s\", hex.EncodeToString([]byte(tc.Binary)), d1, tc.Text)\n\t\t}\n\t}\n}\n\nvar compositeCborTestCases = []struct {\n\tBinary []byte\n\tJson   string\n}{\n\t{[]byte(\"\\xbf\\x64IETF\\x20\\x65Array\\x9f\\x20\\x00\\x18\\xc8\\x14\\xff\\xff\"), \"{\\\"IETF\\\":-1,\\\"Array\\\":[-1,0,200,20]}\\n\"},\n\t{[]byte(\"\\xbf\\x64IETF\\x64YES!\\x65Array\\x9f\\x20\\x00\\x18\\xc8\\x14\\xff\\xff\"), \"{\\\"IETF\\\":\\\"YES!\\\",\\\"Array\\\":[-1,0,200,20]}\\n\"},\n\t{[]byte(\"\\xbf\\x61a\\x01\\x61b\\x02\\x61c\\x03\\xff\"), \"{\\\"a\\\":1,\\\"b\\\":2,\\\"c\\\":3}\\n\"},\n\t{[]byte(\"\\xc1\\x1a\\x51\\x0f\\x30\\xd8\"), \"\\\"2013-02-04T03:54:00Z\\\"\\n\"},\n}\n\nfunc TestDecodeCbor2Json(t *testing.T) {\n\tfor _, tc := range compositeCborTestCases {\n\t\tbuf := bytes.NewBuffer([]byte{})\n\t\terr := Cbor2JsonManyObjects(getReader(string(tc.Binary)), buf)\n\t\tif buf.String() != tc.Json || err != nil {\n\t\t\tt.Errorf(\"cbor2JsonManyObjects(0x%s)=%s, want: %s, err:%s\", hex.EncodeToString(tc.Binary), buf.String(), tc.Json, err.Error())\n\t\t}\n\t}\n}\n\nvar negativeCborTestCases = []struct {\n\tBinary []byte\n\terrStr string\n}{\n\t{[]byte(\"\\xb9\\x64IETF\\x20\\x65Array\\x9f\\x20\\x00\\x18\\xc8\\x14\"), \"Tried to Read 18 Bytes.. But hit end of file\"},\n\t{[]byte(\"\\xbf\\x64IETF\\x20\\x65Array\\x9f\\x20\\x00\\x18\\xc8\\x14\"), \"EOF\"},\n\t{[]byte(\"\\xbf\\x14IETF\\x20\\x65Array\\x9f\\x20\\x00\\x18\\xc8\\x14\"), \"Tried to Read 40736 Bytes.. But hit end of file\"},\n\t{[]byte(\"\\xbf\\x64IETF\"), \"EOF\"},\n\t{[]byte(\"\\xbf\\x64IETF\\x20\\x65Array\\x9f\\x20\\x00\\x18\\xc8\\xff\\xff\\xff\"), \"Invalid Additional Type: 31 in decodeSimpleFloat\"},\n\t{[]byte(\"\\xbf\\x64IETF\\x20\\x65Array\"), \"EOF\"},\n\t{[]byte(\"\\xbf\\x64\"), \"Tried to Read 4 Bytes.. But hit end of file\"},\n}\n\nfunc TestDecodeNegativeCbor2Json(t *testing.T) {\n\tfor _, tc := range negativeCborTestCases {\n\t\tbuf := bytes.NewBuffer([]byte{})\n\t\terr := Cbor2JsonManyObjects(getReader(string(tc.Binary)), buf)\n\t\tif err == nil || err.Error() != tc.errStr {\n\t\t\tt.Errorf(\"Expected error got:%s, want:%s\", err, tc.errStr)\n\t\t}\n\t}\n}\n\nfunc TestBinaryFmt(t *testing.T) {\n\ttests := []struct {\n\t\tinput []byte\n\t\twant  bool\n\t}{\n\t\t{[]byte{}, false},\n\t\t{[]byte{0x00}, false},\n\t\t{[]byte{0x7F}, false},\n\t\t{[]byte{0x80}, true},\n\t\t{[]byte{0xFF}, true},\n\t\t{[]byte{0x00, 0x80}, false}, // Only checks first byte\n\t}\n\n\tfor _, tt := range tests {\n\t\tgot := binaryFmt(tt.input)\n\t\tif got != tt.want {\n\t\t\tt.Errorf(\"binaryFmt(%v) = %v, want %v\", tt.input, got, tt.want)\n\t\t}\n\t}\n}\n\nfunc TestDecodeIfBinaryToString(t *testing.T) {\n\ttests := []struct {\n\t\tname  string\n\t\tinput []byte\n\t\twant  string\n\t}{\n\t\t{\n\t\t\tname:  \"non-binary input\",\n\t\t\tinput: []byte(`{\"key\":\"value\"}`),\n\t\t\twant:  `{\"key\":\"value\"}`,\n\t\t},\n\t\t{\n\t\t\tname:  \"binary input - simple object\",\n\t\t\tinput: []byte(\"\\xbf\\x64IETF\\x20\\xff\"), // {\"IETF\": -1} in indefinite length CBOR\n\t\t\twant:  \"{\\\"IETF\\\":-1}\\n\",\n\t\t},\n\t\t{\n\t\t\tname:  \"binary input - multiple objects\",\n\t\t\tinput: []byte(\"\\xbf\\x64IETF\\x20\\xff\\xbf\\x65Array\\x84\\x20\\x00\\x18\\xc8\\x14\\xff\"), // Two objects\n\t\t\twant:  \"{\\\"IETF\\\":-1}\\n{\\\"Array\\\":[-1,0,200,20]}\\n\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := DecodeIfBinaryToString(tt.input)\n\t\t\tif got != tt.want {\n\t\t\t\tt.Errorf(\"DecodeIfBinaryToString() = %q, want %q\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestDecodeObjectToStr(t *testing.T) {\n\ttests := []struct {\n\t\tname  string\n\t\tinput []byte\n\t\twant  string\n\t}{\n\t\t{\n\t\t\tname:  \"non-binary input\",\n\t\t\tinput: []byte(`{\"key\":\"value\"}`),\n\t\t\twant:  `{\"key\":\"value\"}`,\n\t\t},\n\t\t{\n\t\t\tname:  \"binary input - simple object\",\n\t\t\tinput: []byte(\"\\xbf\\x64IETF\\x20\\xff\"), // {\"IETF\": -1} in indefinite length CBOR\n\t\t\twant:  \"{\\\"IETF\\\":-1}\",\n\t\t},\n\t\t{\n\t\t\tname:  \"binary input - array\",\n\t\t\tinput: []byte(\"\\x84\\x20\\x00\\x18\\xc8\\x14\"), // [-1, 0, 200, 20]\n\t\t\twant:  \"[-1,0,200,20]\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := DecodeObjectToStr(tt.input)\n\t\t\tif got != tt.want {\n\t\t\t\tt.Errorf(\"DecodeObjectToStr() = %q, want %q\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestDecodeIfBinaryToBytes(t *testing.T) {\n\ttests := []struct {\n\t\tname  string\n\t\tinput []byte\n\t\twant  []byte\n\t}{\n\t\t{\n\t\t\tname:  \"non-binary input\",\n\t\t\tinput: []byte(`{\"key\":\"value\"}`),\n\t\t\twant:  []byte(`{\"key\":\"value\"}`),\n\t\t},\n\t\t{\n\t\t\tname:  \"binary input - simple object\",\n\t\t\tinput: []byte(\"\\xbf\\x64IETF\\x20\\xff\"), // {\"IETF\": -1} in indefinite length CBOR\n\t\t\twant:  []byte(\"{\\\"IETF\\\":-1}\\n\"),\n\t\t},\n\t\t{\n\t\t\tname:  \"binary input - multiple objects\",\n\t\t\tinput: []byte(\"\\xbf\\x64IETF\\x20\\xff\\xbf\\x65Array\\x84\\x20\\x00\\x18\\xc8\\x14\\xff\"), // Two objects\n\t\t\twant:  []byte(\"{\\\"IETF\\\":-1}\\n{\\\"Array\\\":[-1,0,200,20]}\\n\"),\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := DecodeIfBinaryToBytes(tt.input)\n\t\t\tif !bytes.Equal(got, tt.want) {\n\t\t\t\tt.Errorf(\"DecodeIfBinaryToBytes() = %q, want %q\", string(got), string(tt.want))\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestDecodeEmbeddedCBOR(t *testing.T) {\n\t// Test embedded CBOR tag: 0xD8 0x3F (tag 63) followed by byte string\n\t// 0xD8 = major type 6 (tags) + additional type 24 (uint8 follows)\n\t// 0x3F = 63 (additionalTypeEmbeddedCBOR)\n\t// 0x43 = major type 2 (byte string) + length 3\n\t// 0x01 0x02 0x03 = the embedded CBOR data\n\n\tembeddedCBOR := []byte(\"\\xd8\\x3f\\x43\\x01\\x02\\x03\")\n\texpected := \"\\\"data:application/cbor;base64,AQID\\\"\"\n\n\tgot := decodeTagData(getReader(string(embeddedCBOR)))\n\tif string(got) != expected {\n\t\tt.Errorf(\"decodeTagData(embedded CBOR) = %q, want %q\", string(got), expected)\n\t}\n}\n\nfunc TestDecodeEmbeddedJSON(t *testing.T) {\n\tt.Run(\"valid embedded JSON\", func(t *testing.T) {\n\t\t// Test embedded JSON tag: 0xD9 0x01 0x06 (tag 262) followed by byte string.\n\t\t// 0xD9 = major type 6 (tags) + additional type 25 (uint16 follows)\n\t\t// 0x01 0x06 = 262 (additionalTypeEmbeddedJSON)\n\t\t// 0x47 = major type 2 (byte string) + length 7\n\t\t// {\"a\":1} = embedded JSON payload (no surrounding quotes expected)\n\t\tembeddedJSON := []byte(\"\\xd9\\x01\\x06\\x47{\\\"a\\\":1}\")\n\t\texpected := \"{\\\"a\\\":1}\"\n\n\t\tgot := decodeTagData(getReader(string(embeddedJSON)))\n\t\tif string(got) != expected {\n\t\t\tt.Errorf(\"decodeTagData(embedded JSON) = %q, want %q\", string(got), expected)\n\t\t}\n\t})\n\n\tt.Run(\"unsupported embedded type panics\", func(t *testing.T) {\n\t\t// Same embedded JSON tag, but followed by a UTF-8 string instead of a byte string.\n\t\t// This should hit the \"Unsupported embedded Type\" panic branch.\n\t\tbad := []byte(\"\\xd9\\x01\\x06\\x61x\")\n\n\t\tdefer func() {\n\t\t\tif r := recover(); r == nil {\n\t\t\t\tt.Fatalf(\"expected panic, got none\")\n\t\t\t}\n\t\t}()\n\n\t\t_ = decodeTagData(getReader(string(bad)))\n\t})\n}\n\nfunc TestDecodeHexString(t *testing.T) {\n\t// Test hex string tag: 0xD9 0x01 0x07 (tag 263) followed by byte string\n\t// 0xD9 = major type 6 (tags) + additional type 25 (uint16 follows)\n\t// 0x01 0x07 = 263 (additionalTypeTagHexString)\n\t// 0x43 = major type 2 (byte string) + length 3\n\t// 0x01 0x02 0x03 = the byte data to hex encode\n\n\thexString := []byte(\"\\xd9\\x01\\x07\\x43\\x01\\x02\\x03\")\n\texpected := \"\\\"010203\\\"\"\n\n\tgot := decodeTagData(getReader(string(hexString)))\n\tif string(got) != expected {\n\t\tt.Errorf(\"decodeTagData(hex string) = %q, want %q\", string(got), expected)\n\t}\n}\n\nfunc TestDecodeSimpleFloat(t *testing.T) {\n\ttests := []struct {\n\t\tname  string\n\t\tinput string\n\t\twant  string\n\t}{\n\t\t// Boolean and null cases (already covered)\n\t\t{\"true\", \"\\xf5\", \"true\"},\n\t\t{\"false\", \"\\xf4\", \"false\"},\n\t\t{\"null\", \"\\xf6\", \"null\"},\n\n\t\t// Float32 cases\n\t\t{\"float32 1.0\", \"\\xfa\\x3f\\x80\\x00\\x00\", \"1\"},\n\t\t{\"float32 1.5\", \"\\xfa\\x3f\\xc0\\x00\\x00\", \"1.5\"},\n\t\t{\"float32 +Inf\", \"\\xfa\\x7f\\x80\\x00\\x00\", \"\\\"+Inf\\\"\"},\n\t\t{\"float32 -Inf\", \"\\xfa\\xff\\x80\\x00\\x00\", \"\\\"-Inf\\\"\"},\n\t\t{\"float32 NaN\", \"\\xfa\\x7f\\xc0\\x00\\x00\", \"\\\"NaN\\\"\"},\n\n\t\t// Float64 cases\n\t\t{\"float64 1.0\", \"\\xfb\\x3f\\xf0\\x00\\x00\\x00\\x00\\x00\\x00\", \"1\"},\n\t\t{\"float64 +Inf\", \"\\xfb\\x7f\\xf0\\x00\\x00\\x00\\x00\\x00\\x00\", \"\\\"+Inf\\\"\"},\n\t\t{\"float64 -Inf\", \"\\xfb\\xff\\xf0\\x00\\x00\\x00\\x00\\x00\\x00\", \"\\\"-Inf\\\"\"},\n\t\t{\"float64 NaN\", \"\\xfb\\x7f\\xf8\\x00\\x00\\x00\\x00\\x00\\x00\", \"\\\"NaN\\\"\"},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := decodeSimpleFloat(getReader(tt.input))\n\t\t\tif string(got) != tt.want {\n\t\t\t\tt.Errorf(\"decodeSimpleFloat(%q) = %q, want %q\", tt.input, string(got), tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/cbor/examples/genLog.go",
    "content": "package main\n\nimport (\n\t\"compress/zlib\"\n\t\"flag\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/rs/zerolog\"\n)\n\nfunc writeLog(fname string, count int, useCompress bool) {\n\topFile := os.Stdout\n\tif fname != \"<stdout>\" {\n\t\tfil, _ := os.Create(fname)\n\t\topFile = fil\n\t\tdefer func() {\n\t\t\tif err := fil.Close(); err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\t\t}()\n\t}\n\n\tvar f io.WriteCloser = opFile\n\tif useCompress {\n\t\tf = zlib.NewWriter(f)\n\t\tdefer func() {\n\t\t\tif err := f.Close(); err != nil {\n\t\t\t\tlog.Fatal(err)\n\t\t\t}\n\t\t}()\n\n\t}\n\n\tzerolog.TimestampFunc = func() time.Time { return time.Now().Round(time.Second) }\n\tlog := zerolog.New(f).With().\n\t\tTimestamp().\n\t\tLogger()\n\tfor i := 0; i < count; i++ {\n\t\tlog.Error().\n\t\t\tInt(\"Fault\", 41650+i).Msg(\"Some Message\")\n\t}\n}\n\nfunc main() {\n\toutFile := flag.String(\"out\", \"<stdout>\", \"Output File to which logs will be written to (WILL overwrite if already present).\")\n\tnumLogs := flag.Int(\"num\", 10, \"Number of log messages to generate.\")\n\tdoCompress := flag.Bool(\"compress\", false, \"Enable inline compressed writer\")\n\n\tflag.Parse()\n\n\twriteLog(*outFile, *numLogs, *doCompress)\n}\n"
  },
  {
    "path": "internal/cbor/examples/makefile",
    "content": "all: genLogJSON genLogCBOR\n\ngenLogJSON: genLog.go\n\tgo build -o genLogJSON genLog.go \n\ngenLogCBOR: genLog.go\n\tgo build -tags binary_log -o genLogCBOR genLog.go \n\nclean:\n\trm -f genLogJSON genLogCBOR\n"
  },
  {
    "path": "internal/cbor/string.go",
    "content": "package cbor\n\nimport \"fmt\"\n\n// AppendStrings encodes and adds an array of strings to the dst byte array.\nfunc (e Encoder) AppendStrings(dst []byte, vals []string) []byte {\n\tmajor := majorTypeArray\n\tl := len(vals)\n\tif l <= additionalMax {\n\t\tlb := byte(l)\n\t\tdst = append(dst, major|lb)\n\t} else {\n\t\tdst = appendCborTypePrefix(dst, major, uint64(l))\n\t}\n\tfor _, v := range vals {\n\t\tdst = e.AppendString(dst, v)\n\t}\n\treturn dst\n}\n\n// AppendString encodes and adds a string to the dst byte array.\nfunc (Encoder) AppendString(dst []byte, s string) []byte {\n\tmajor := majorTypeUtf8String\n\n\tl := len(s)\n\tif l <= additionalMax {\n\t\tlb := byte(l)\n\t\tdst = append(dst, major|lb)\n\t} else {\n\t\tdst = appendCborTypePrefix(dst, majorTypeUtf8String, uint64(l))\n\t}\n\treturn append(dst, s...)\n}\n\n// AppendStringers encodes and adds an array of Stringer values\n// to the dst byte array.\nfunc (e Encoder) AppendStringers(dst []byte, vals []fmt.Stringer) []byte {\n\tif len(vals) == 0 {\n\t\treturn e.AppendArrayEnd(e.AppendArrayStart(dst))\n\t}\n\tdst = e.AppendArrayStart(dst)\n\tdst = e.AppendStringer(dst, vals[0])\n\tif len(vals) > 1 {\n\t\tfor _, val := range vals[1:] {\n\t\t\tdst = e.AppendStringer(dst, val)\n\t\t}\n\t}\n\treturn e.AppendArrayEnd(dst)\n}\n\n// AppendStringer encodes and adds the Stringer value to the dst\n// byte array.\nfunc (e Encoder) AppendStringer(dst []byte, val fmt.Stringer) []byte {\n\tif val == nil {\n\t\treturn e.AppendNil(dst)\n\t}\n\treturn e.AppendString(dst, val.String())\n}\n\n// AppendBytes encodes and adds an array of bytes to the dst byte array.\nfunc (Encoder) AppendBytes(dst, s []byte) []byte {\n\tmajor := majorTypeByteString\n\n\tl := len(s)\n\tif l <= additionalMax {\n\t\tlb := byte(l)\n\t\tdst = append(dst, major|lb)\n\t} else {\n\t\tdst = appendCborTypePrefix(dst, major, uint64(l))\n\t}\n\treturn append(dst, s...)\n}\n\n// AppendEmbeddedJSON adds a tag and embeds input JSON as such.\nfunc AppendEmbeddedJSON(dst, s []byte) []byte {\n\tmajor := majorTypeTags\n\tminor := additionalTypeEmbeddedJSON\n\n\t// Append the TAG to indicate this is Embedded JSON.\n\tdst = append(dst, major|additionalTypeIntUint16)\n\tdst = append(dst, byte(minor>>8))\n\tdst = append(dst, byte(minor&0xff))\n\n\t// Append the JSON Object as Byte String.\n\tmajor = majorTypeByteString\n\n\tl := len(s)\n\tif l <= additionalMax {\n\t\tlb := byte(l)\n\t\tdst = append(dst, major|lb)\n\t} else {\n\t\tdst = appendCborTypePrefix(dst, major, uint64(l))\n\t}\n\treturn append(dst, s...)\n}\n\n// AppendEmbeddedCBOR adds a tag and embeds input CBOR as such.\nfunc AppendEmbeddedCBOR(dst, s []byte) []byte {\n\tmajor := majorTypeTags\n\tminor := additionalTypeEmbeddedCBOR\n\n\t// Append the TAG to indicate this is Embedded JSON.\n\tdst = append(dst, major|additionalTypeIntUint8)\n\tdst = append(dst, minor)\n\n\t// Append the CBOR Object as Byte String.\n\tmajor = majorTypeByteString\n\n\tl := len(s)\n\tif l <= additionalMax {\n\t\tlb := byte(l)\n\t\tdst = append(dst, major|lb)\n\t} else {\n\t\tdst = appendCborTypePrefix(dst, major, uint64(l))\n\t}\n\treturn append(dst, s...)\n}\n"
  },
  {
    "path": "internal/cbor/string_test.go",
    "content": "package cbor\n\nimport (\n\t\"bytes\"\n\t\"encoding/hex\"\n\t\"testing\"\n\n\t\"github.com/rs/zerolog/internal\"\n)\n\nvar encodeStringTests = []struct {\n\tplain  string\n\tbinary string\n\tjson   string //begin and end quotes are implied\n}{\n\t{\"\", \"\\x60\", \"\"},\n\t{\"\\\\\", \"\\x61\\x5c\", \"\\\\\\\\\"},\n\t{\"\\\"\", \"\\x61\\x22\", \"\\\\\\\"\"},\n\t{\"\\b\", \"\\x61\\x08\", \"\\\\b\"},\n\t{\"\\f\", \"\\x61\\x0c\", \"\\\\f\"},\n\t{\"\\n\", \"\\x61\\x0a\", \"\\\\n\"},\n\t{\"\\r\", \"\\x61\\x0d\", \"\\\\r\"},\n\t{\"\\t\", \"\\x61\\x09\", \"\\\\t\"},\n\t{\"Hi\\t\", \"\\x63Hi\\x09\", \"Hi\\\\t\"},\n\t{\"\\x00\", \"\\x61\\x00\", \"\\\\u0000\"},\n\t{\"\\x01\", \"\\x61\\x01\", \"\\\\u0001\"},\n\t{\"\\x02\", \"\\x61\\x02\", \"\\\\u0002\"},\n\t{\"\\x03\", \"\\x61\\x03\", \"\\\\u0003\"},\n\t{\"\\x04\", \"\\x61\\x04\", \"\\\\u0004\"},\n\t{\"*\", \"\\x61*\", \"*\"},\n\t{\"a\", \"\\x61a\", \"a\"},\n\t{\"IETF\", \"\\x64IETF\", \"IETF\"},\n\t{\"abcdefghijklmnopqrstuvwxyzABCD\", \"\\x78\\x1eabcdefghijklmnopqrstuvwxyzABCD\", \"abcdefghijklmnopqrstuvwxyzABCD\"},\n\t{\"<------------------------------------  This is a 100 character string ----------------------------->\" +\n\t\t\"<------------------------------------  This is a 100 character string ----------------------------->\" +\n\t\t\"<------------------------------------  This is a 100 character string ----------------------------->\",\n\t\t\"\\x79\\x01\\x2c<------------------------------------  This is a 100 character string ----------------------------->\" +\n\t\t\t\"<------------------------------------  This is a 100 character string ----------------------------->\" +\n\t\t\t\"<------------------------------------  This is a 100 character string ----------------------------->\",\n\t\t\"<------------------------------------  This is a 100 character string ----------------------------->\" +\n\t\t\t\"<------------------------------------  This is a 100 character string ----------------------------->\" +\n\t\t\t\"<------------------------------------  This is a 100 character string ----------------------------->\"},\n\t{\"emoji \\u2764\\ufe0f!\", \"\\x6demoji ❤️!\", \"emoji \\u2764\\ufe0f!\"},\n\t{\"invalid utf8 \\xff\", \"\\x6einvalid utf8 \\xff\", \"invalid utf8 \\\\ufffd\"},\n}\n\nvar encodeByteTests = []struct {\n\tplain  []byte\n\tbinary string\n}{\n\t{[]byte{}, \"\\x40\"},\n\t{[]byte(\"\\\\\"), \"\\x41\\x5c\"},\n\t{[]byte(\"\\x00\"), \"\\x41\\x00\"},\n\t{[]byte(\"\\x01\"), \"\\x41\\x01\"},\n\t{[]byte(\"\\x02\"), \"\\x41\\x02\"},\n\t{[]byte(\"\\x03\"), \"\\x41\\x03\"},\n\t{[]byte(\"\\x04\"), \"\\x41\\x04\"},\n\t{[]byte(\"\\f\"), \"\\x41\\x0C\"},\n\t{[]byte(\"\\n\"), \"\\x41\\x0A\"},\n\t{[]byte(\"\\r\"), \"\\x41\\x0D\"},\n\t{[]byte(\"*\"), \"\\x41*\"},\n\t{[]byte(\"a\"), \"\\x41a\"},\n\t{[]byte(\"IETF\"), \"\\x44IETF\"},\n\t{[]byte(\"abcdefghijklmnopqrstuvwxyzABCD\"), \"\\x58\\x1eabcdefghijklmnopqrstuvwxyzABCD\"},\n\t{[]byte(\"<------------------------------------  This is a 100 character string ----------------------------->\" +\n\t\t\"<------------------------------------  This is a 100 character string ----------------------------->\" +\n\t\t\"<------------------------------------  This is a 100 character string ----------------------------->\"),\n\t\t\"\\x59\\x01\\x2c<------------------------------------  This is a 100 character string ----------------------------->\" +\n\t\t\t\"<------------------------------------  This is a 100 character string ----------------------------->\" +\n\t\t\t\"<------------------------------------  This is a 100 character string ----------------------------->\"},\n\t{[]byte(\"emoji \\u2764\\ufe0f!\"), \"\\x4demoji ❤️!\"},\n}\n\nfunc TestAppendString(t *testing.T) {\n\tfor _, tt := range encodeStringTests {\n\t\tb := enc.AppendString([]byte{}, tt.plain)\n\t\tif got, want := string(b), tt.binary; got != want {\n\t\t\tt.Errorf(\"appendString(%q) = %#q, want %#q\", tt.plain, got, want)\n\t\t}\n\t}\n\t//Test a large string > 65535 length\n\n\tvar buffer bytes.Buffer\n\tfor i := 0; i < 0x00011170; i++ { //70,000 character string\n\t\tbuffer.WriteString(\"a\")\n\t}\n\tinp := buffer.String()\n\twant := \"\\x7a\\x00\\x01\\x11\\x70\" + inp\n\tb := enc.AppendString([]byte{}, inp)\n\tif got := string(b); got != want {\n\t\tt.Errorf(\"appendString(%q) = %#q, want %#q\", inp, got, want)\n\t}\n}\nfunc TestAppendStrings(t *testing.T) {\n\tarray := []string{}\n\tfor _, tt := range encodeStringTests {\n\t\tarray = append(array, tt.plain)\n\t}\n\twant := make([]byte, 0)\n\twant = append(want, 0x95) // start array\n\tfor _, tt := range encodeStringTests {\n\t\twant = append(want, []byte(tt.binary)...)\n\t}\n\n\tgot := enc.AppendStrings([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendStrings(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray,\n\t\t\thex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now empty array case\n\tarray = make([]string, 0)\n\twant = make([]byte, 0)\n\twant = append(want, 0x80) // start an empty string array\n\tgot = enc.AppendStrings([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendStrings(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now large array case\n\tarray = make([]string, 24)\n\twant = make([]byte, 0)\n\twant = append(want, 0x98) // start a large array\n\twant = append(want, 0x18) // of length 24\n\tfor i := 0; i < len(array); i++ {\n\t\tarray[i] = \"test\"\n\t\twant = append(want, []byte(\"\\x64test\")...)\n\t}\n\tgot = enc.AppendStrings([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendStrings(%v)\\ngot:  %s\\nwant: %s\",\n\t\t\tarray,\n\t\t\thex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n}\n\nfunc TestAppendStringer(t *testing.T) {\n\toldJSONMarshalFunc := JSONMarshalFunc\n\tdefer func() {\n\t\tJSONMarshalFunc = oldJSONMarshalFunc\n\t}()\n\n\tJSONMarshalFunc = func(v interface{}) ([]byte, error) {\n\t\treturn internal.InterfaceMarshalFunc(v)\n\t}\n\n\tfor _, tt := range internal.EncodeStringerTests {\n\t\tgot := enc.AppendStringer([]byte{}, tt.In)\n\t\twant := []byte(tt.Binary)\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"AppendStrings(%v)\\ngot:  %s\\nwant: %s\",\n\t\t\t\ttt.In,\n\t\t\t\thex.EncodeToString(got),\n\t\t\t\thex.EncodeToString(want))\n\t\t}\n\t}\n}\n\nfunc TestAppendStringers(t *testing.T) {\n\tfor _, tt := range internal.EncodeStringersTests {\n\t\twant := make([]byte, 0)\n\t\twant = append(want, []byte(tt.Binary)...)\n\n\t\tgot := enc.AppendStringers([]byte{}, tt.In)\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"AppendStrings(%v)\\ngot:  %s\\nwant: %s\",\n\t\t\t\ttt,\n\t\t\t\thex.EncodeToString(got),\n\t\t\t\thex.EncodeToString(want))\n\t\t}\n\t}\n}\n\nfunc TestAppendBytes(t *testing.T) {\n\tfor _, tt := range encodeByteTests {\n\t\tb := enc.AppendBytes([]byte{}, tt.plain)\n\t\tif got, want := string(b), tt.binary; got != want {\n\t\t\tt.Errorf(\"appendString(%q) = %#q, want %#q\", tt.plain, got, want)\n\t\t}\n\t}\n\t//Test a large string > 65535 length\n\n\tinp := []byte{}\n\tfor i := 0; i < 0x00011170; i++ { //70,000 character string\n\t\tinp = append(inp, byte('a'))\n\t}\n\twant := \"\\x5a\\x00\\x01\\x11\\x70\" + string(inp)\n\tb := enc.AppendBytes([]byte{}, inp)\n\tif got := string(b); got != want {\n\t\tt.Errorf(\"appendString(%q) = %#q, want %#q\", inp, got, want)\n\t}\n}\n\nfunc BenchmarkAppendString(b *testing.B) {\n\ttests := map[string]string{\n\t\t\"NoEncoding\":       `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`,\n\t\t\"EncodingFirst\":    `\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`,\n\t\t\"EncodingMiddle\":   `aaaaaaaaaaaaaaaaaaaaaaaaa\"aaaaaaaaaaaaaaaaaaaaaaaa`,\n\t\t\"EncodingLast\":     `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"`,\n\t\t\"MultiBytesFirst\":  `❤️aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`,\n\t\t\"MultiBytesMiddle\": `aaaaaaaaaaaaaaaaaaaaaaaaa❤️aaaaaaaaaaaaaaaaaaaaaaaa`,\n\t\t\"MultiBytesLast\":   `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa❤️`,\n\t}\n\tfor name, str := range tests {\n\t\tb.Run(name, func(b *testing.B) {\n\t\t\tbuf := make([]byte, 0, 120)\n\t\t\tfor i := 0; i < b.N; i++ {\n\t\t\t\t_ = enc.AppendString(buf, str)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAppendEmbeddedJSON(t *testing.T) {\n\ttests := []struct {\n\t\tname  string\n\t\tinput []byte\n\t\twant  string\n\t}{\n\t\t{\n\t\t\tname:  \"empty JSON\",\n\t\t\tinput: []byte{},\n\t\t\twant:  \"\\xd9\\x01\\x06@\", // tag 0xd9 + empty byte string\n\t\t},\n\t\t{\n\t\t\tname:  \"small JSON\",\n\t\t\tinput: []byte(`{\"key\":\"value\"}`),\n\t\t\twant:  \"\\xd9\\x01\\x06O{\\\"key\\\":\\\"value\\\"}\", // tag 0xd9 + byte string with content\n\t\t},\n\t\t{\n\t\t\tname:  \"large JSON (>23 bytes)\",\n\t\t\tinput: []byte(`{\"key\":\"this is a very long value that exceeds the 23 byte limit for direct encoding\"}`),\n\t\t\twant:  \"\\xd9\\x01\\x06XV{\\\"key\\\":\\\"this is a very long value that exceeds the 23 byte limit for direct encoding\\\"}\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := AppendEmbeddedJSON([]byte{}, tt.input)\n\t\t\tif string(got) != tt.want {\n\t\t\t\tt.Errorf(\"AppendEmbeddedJSON() = %q, want %q\", string(got), tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAppendEmbeddedCBOR(t *testing.T) {\n\ttests := []struct {\n\t\tname  string\n\t\tinput []byte\n\t\twant  string\n\t}{\n\t\t{\n\t\t\tname:  \"empty CBOR\",\n\t\t\tinput: []byte{},\n\t\t\twant:  \"\\xd8?@\", // tag 0xd8 + empty byte string\n\t\t},\n\t\t{\n\t\t\tname:  \"small CBOR\",\n\t\t\tinput: []byte{0x01, 0x02, 0x03},\n\t\t\twant:  \"\\xd8?C\\x01\\x02\\x03\", // tag 0xd8 + byte string with 3 bytes\n\t\t},\n\t\t{\n\t\t\tname:  \"large CBOR (>23 bytes)\",\n\t\t\tinput: make([]byte, 30),                        // 30 bytes of zeros\n\t\t\twant:  \"\\xd8?X\\x1e\" + string(make([]byte, 30)), // tag 0xd8 + byte string with length prefix\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := AppendEmbeddedCBOR([]byte{}, tt.input)\n\t\t\tif string(got) != tt.want {\n\t\t\t\tt.Errorf(\"AppendEmbeddedCBOR() = %q, want %q\", string(got), tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/cbor/time.go",
    "content": "package cbor\n\nimport (\n\t\"time\"\n)\n\nconst (\n\t// Import from zerolog/global.go\n\ttimeFormatUnix       = \"\"\n\ttimeFormatUnixMs     = \"UNIXMS\"\n\ttimeFormatUnixMicro  = \"UNIXMICRO\"\n\ttimeFormatUnixNano   = \"UNIXNANO\"\n\tdurationFormatFloat  = \"float\"\n\tdurationFormatInt    = \"int\"\n\tdurationFormatString = \"string\"\n)\n\nfunc appendIntegerTimestamp(dst []byte, t time.Time) []byte {\n\tmajor := majorTypeTags\n\tminor := additionalTypeTimestamp\n\tdst = append(dst, major|minor)\n\tsecs := t.Unix()\n\tvar val uint64\n\tif secs < 0 {\n\t\tmajor = majorTypeNegativeInt\n\t\tval = uint64(-secs - 1)\n\t} else {\n\t\tmajor = majorTypeUnsignedInt\n\t\tval = uint64(secs)\n\t}\n\tdst = appendCborTypePrefix(dst, major, val)\n\treturn dst\n}\n\nfunc (e Encoder) appendFloatTimestamp(dst []byte, t time.Time) []byte {\n\tmajor := majorTypeTags\n\tminor := additionalTypeTimestamp\n\tdst = append(dst, major|minor)\n\tsecs := t.Unix()\n\tnanos := t.Nanosecond()\n\tval := float64(secs)*1.0 + float64(nanos)*1e-9\n\treturn e.AppendFloat64(dst, val, -1)\n}\n\n// AppendTime encodes and adds a timestamp to the dst byte array.\nfunc (e Encoder) AppendTime(dst []byte, t time.Time, unused string) []byte {\n\tutc := t.UTC()\n\tif utc.Nanosecond() == 0 {\n\t\treturn appendIntegerTimestamp(dst, utc)\n\t}\n\treturn e.appendFloatTimestamp(dst, utc)\n}\n\n// AppendTimes encodes and adds an array of timestamps to the dst byte array.\nfunc (e Encoder) AppendTimes(dst []byte, vals []time.Time, unused string) []byte {\n\tmajor := majorTypeArray\n\tl := len(vals)\n\tif l == 0 {\n\t\treturn e.AppendArrayEnd(e.AppendArrayStart(dst))\n\t}\n\tif l <= additionalMax {\n\t\tlb := byte(l)\n\t\tdst = append(dst, major|lb)\n\t} else {\n\t\tdst = appendCborTypePrefix(dst, major, uint64(l))\n\t}\n\n\tfor _, t := range vals {\n\t\tdst = e.AppendTime(dst, t, unused)\n\t}\n\treturn dst\n}\n\n// AppendDuration encodes and adds a duration to the dst byte array.\n// useInt field indicates whether to store the duration as seconds (integer) or\n// as seconds+nanoseconds (float).\nfunc (e Encoder) AppendDuration(dst []byte, d time.Duration, unit time.Duration, format string, useInt bool, unused int) []byte {\n\tif useInt {\n\t\treturn e.AppendInt64(dst, int64(d/unit))\n\t}\n\tswitch format {\n\tcase durationFormatFloat:\n\t\treturn e.AppendFloat64(dst, float64(d)/float64(unit), unused)\n\tcase durationFormatInt:\n\t\treturn e.AppendInt64(dst, int64(d/unit))\n\tcase durationFormatString:\n\t\treturn e.AppendString(dst, d.String())\n\t}\n\treturn e.AppendFloat64(dst, float64(d)/float64(unit), unused)\n}\n\n// AppendDurations encodes and adds an array of durations to the dst byte array.\n// useInt field indicates whether to store the duration as seconds (integer) or\n// as seconds+nanoseconds (float).\nfunc (e Encoder) AppendDurations(dst []byte, vals []time.Duration, unit time.Duration, format string, useInt bool, unused int) []byte {\n\tmajor := majorTypeArray\n\tl := len(vals)\n\tif l == 0 {\n\t\treturn e.AppendArrayEnd(e.AppendArrayStart(dst))\n\t}\n\tif l <= additionalMax {\n\t\tlb := byte(l)\n\t\tdst = append(dst, major|lb)\n\t} else {\n\t\tdst = appendCborTypePrefix(dst, major, uint64(l))\n\t}\n\tfor _, d := range vals {\n\t\tdst = e.AppendDuration(dst, d, unit, format, useInt, unused)\n\t}\n\treturn dst\n}\n"
  },
  {
    "path": "internal/cbor/time_test.go",
    "content": "package cbor\n\nimport (\n\t\"bytes\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"math\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/rs/zerolog/internal\"\n)\n\nfunc TestEncoder_AppendDuration(t *testing.T) {\n\ttype args struct {\n\t\tdst    []byte\n\t\td      time.Duration\n\t\tunit   time.Duration\n\t\tformat string\n\t\tuseInt bool\n\t\tunused int\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\twant []byte\n\t}{\n\t\t{\n\t\t\tname: \"useInt\",\n\t\t\targs: args{\n\t\t\t\td:      1234567890,\n\t\t\t\tunit:   time.Second,\n\t\t\t\tuseInt: true,\n\t\t\t},\n\t\t\twant: []byte{1},\n\t\t},\n\t\t{\n\t\t\tname: \"formatFloat\",\n\t\t\targs: args{\n\t\t\t\td:      1234567890,\n\t\t\t\tunit:   time.Second,\n\t\t\t\tformat: durationFormatFloat,\n\t\t\t},\n\t\t\twant: []byte{251, 63, 243, 192, 202, 66, 131, 222, 27},\n\t\t},\n\t\t{\n\t\t\tname: \"formatInt\",\n\t\t\targs: args{\n\t\t\t\td:      1234567890,\n\t\t\t\tunit:   time.Second,\n\t\t\t\tformat: durationFormatInt,\n\t\t\t},\n\t\t\twant: []byte{1},\n\t\t},\n\t\t{\n\t\t\tname: \"formatString\",\n\t\t\targs: args{\n\t\t\t\td:      1234567890,\n\t\t\t\tunit:   time.Second,\n\t\t\t\tformat: durationFormatString,\n\t\t\t},\n\t\t\twant: []byte{107, 49, 46, 50, 51, 52, 53, 54, 55, 56, 57, 115},\n\t\t},\n\t\t{\n\t\t\tname: \"formatBlank\",\n\t\t\targs: args{\n\t\t\t\td:    1234567890,\n\t\t\t\tunit: time.Second,\n\t\t\t},\n\t\t\twant: []byte{251, 63, 243, 192, 202, 66, 131, 222, 27},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\te := Encoder{}\n\t\t\tif got := e.AppendDuration(tt.args.dst, tt.args.d, tt.args.unit, tt.args.format, tt.args.useInt, tt.args.unused); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"AppendDuration() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestEncoder_AppendDurations(t *testing.T) {\n\ttype args struct {\n\t\tdst    []byte\n\t\tvals   []time.Duration\n\t\tunit   time.Duration\n\t\tformat string\n\t\tuseInt bool\n\t\tunused int\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\twant []byte\n\t}{\n\t\t{\n\t\t\tname: \"useInt\",\n\t\t\targs: args{\n\t\t\t\tvals:   []time.Duration{1234567890},\n\t\t\t\tunit:   time.Second,\n\t\t\t\tuseInt: true,\n\t\t\t},\n\t\t\twant: []byte{129, 1},\n\t\t},\n\t\t{\n\t\t\tname: \"formatFloat\",\n\t\t\targs: args{\n\t\t\t\tvals:   []time.Duration{1234567890},\n\t\t\t\tunit:   time.Second,\n\t\t\t\tformat: durationFormatFloat,\n\t\t\t},\n\t\t\twant: []byte{129, 251, 63, 243, 192, 202, 66, 131, 222, 27},\n\t\t},\n\t\t{\n\t\t\tname: \"formatInt\",\n\t\t\targs: args{\n\t\t\t\tvals:   []time.Duration{1234567890},\n\t\t\t\tunit:   time.Second,\n\t\t\t\tformat: durationFormatInt,\n\t\t\t},\n\t\t\twant: []byte{129, 1},\n\t\t},\n\t\t{\n\t\t\tname: \"formatString\",\n\t\t\targs: args{\n\t\t\t\tvals:   []time.Duration{1234567890},\n\t\t\t\tunit:   time.Second,\n\t\t\t\tformat: durationFormatString,\n\t\t\t},\n\t\t\twant: []byte{129, 107, 49, 46, 50, 51, 52, 53, 54, 55, 56, 57, 115},\n\t\t},\n\t\t{\n\t\t\tname: \"formatBlank\",\n\t\t\targs: args{\n\t\t\t\tvals: []time.Duration{1234567890},\n\t\t\t\tunit: time.Second,\n\t\t\t},\n\t\t\twant: []byte{129, 251, 63, 243, 192, 202, 66, 131, 222, 27},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\te := Encoder{}\n\t\t\tif got := e.AppendDurations(tt.args.dst, tt.args.vals, tt.args.unit, tt.args.format, tt.args.useInt, tt.args.unused); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"AppendDurations() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAppendTimeNow(t *testing.T) {\n\ttm := time.Now()\n\ts := enc.AppendTime([]byte{}, tm, \"unused\")\n\tgot := string(s)\n\n\ttm1 := float64(tm.Unix()) + float64(tm.Nanosecond())*1e-9\n\ttm2 := math.Float64bits(tm1)\n\tvar tm3 [8]byte\n\tfor i := uint(0); i < 8; i++ {\n\t\ttm3[i] = byte(tm2 >> ((8 - i - 1) * 8))\n\t}\n\twant := append([]byte{0xc1, 0xfb}, tm3[:]...)\n\tif got != string(want) {\n\t\tt.Errorf(\"Appendtime(%s)=0x%s, want: 0x%s\",\n\t\t\t\"time.Now()\", hex.EncodeToString(s),\n\t\t\thex.EncodeToString(want))\n\t}\n}\n\nfunc TestAppendTimePastPresentInteger(t *testing.T) {\n\tfor _, tt := range internal.TimeIntegerTestcases {\n\t\ttin, err := time.Parse(time.RFC3339, tt.Txt)\n\t\tif err != nil {\n\t\t\tfmt.Println(\"Cannot parse input\", tt.Txt, \".. Skipping!\", err)\n\t\t\tcontinue\n\t\t}\n\t\tb := enc.AppendTime([]byte{}, tin, \"unused\")\n\t\tif got, want := string(b), tt.Binary; got != want {\n\t\t\tt.Errorf(\"appendString(%s) = 0x%s, want 0x%s\", tt.Txt,\n\t\t\t\thex.EncodeToString(b),\n\t\t\t\thex.EncodeToString([]byte(want)))\n\t\t}\n\t}\n}\n\nfunc TestAppendTimePastPresentFloat(t *testing.T) {\n\tconst timeFloatFmt = \"2006-01-02T15:04:05.999999-07:00\"\n\tfor _, tt := range internal.TimeFloatTestcases {\n\t\ttin, err := time.Parse(timeFloatFmt, tt.RfcStr)\n\t\tif err != nil {\n\t\t\tfmt.Println(\"Cannot parse input\", tt.RfcStr, \".. Skipping!\")\n\t\t\tcontinue\n\t\t}\n\t\tb := enc.AppendTime([]byte{}, tin, \"unused\")\n\t\tif got, want := string(b), tt.Out; got != want {\n\t\t\tt.Errorf(\"appendString(%s) = 0x%s, want 0x%s\", tt.RfcStr,\n\t\t\t\thex.EncodeToString(b),\n\t\t\t\thex.EncodeToString([]byte(want)))\n\t\t}\n\t}\n}\nfunc TestAppendTimes(t *testing.T) {\n\tconst timeFloatFmt = \"2006-01-02T15:04:05.999999-07:00\"\n\tarray := make([]time.Time, len(internal.TimeFloatTestcases))\n\twant := make([]byte, 0)\n\twant = append(want, 0x82) // start small array\n\tfor i, tt := range internal.TimeFloatTestcases {\n\t\tarray[i], _ = time.Parse(timeFloatFmt, tt.RfcStr)\n\t\twant = append(want, []byte(tt.Out)...)\n\t}\n\n\tgot := enc.AppendTimes([]byte{}, array, \"unused\")\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendTimes(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now empty array case\n\tarray = make([]time.Time, 0)\n\twant = make([]byte, 0)\n\twant = append(want, 0x9f) // start and end array\n\twant = append(want, 0xff) // for empty array\n\tgot = enc.AppendTimes([]byte{}, array, \"unused\")\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendTimes(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now large array case\n\ttesttime, _ := time.Parse(timeFloatFmt, internal.TimeFloatTestcases[0].RfcStr)\n\toutbytes := internal.TimeFloatTestcases[0].Out\n\tarray = make([]time.Time, 24)\n\twant = make([]byte, 0)\n\twant = append(want, 0x98) // start a large array\n\twant = append(want, 0x18) // of length 24\n\tfor i := 0; i < len(array); i++ {\n\t\tarray[i] = testtime\n\t\twant = append(want, []byte(outbytes)...)\n\t}\n\tgot = enc.AppendTimes([]byte{}, array, \"unused\")\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendTimes(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray,\n\t\t\thex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n}\n\nfunc TestAppendDurationFloat(t *testing.T) {\n\tfor _, tt := range internal.DurTestcases {\n\t\tdur := tt.Duration\n\t\twant := []byte{}\n\t\twant = append(want, []byte(tt.FloatOut)...)\n\t\tgot := enc.AppendDuration([]byte{}, dur, time.Microsecond, \"\", false, -1)\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"AppendDuration(%v)=\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\t\tdur,\n\t\t\t\thex.EncodeToString(got),\n\t\t\t\thex.EncodeToString(want))\n\t\t}\n\t}\n}\nfunc TestAppendDurationInteger(t *testing.T) {\n\tfor _, tt := range internal.DurTestcases {\n\t\tdur := tt.Duration\n\t\twant := []byte{}\n\t\twant = append(want, []byte(tt.IntegerOut)...)\n\t\tgot := enc.AppendDuration([]byte{}, dur, time.Microsecond, \"\", true, -1)\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"AppendDuration(%v)=\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\t\tdur,\n\t\t\t\thex.EncodeToString(got),\n\t\t\t\thex.EncodeToString(want))\n\t\t}\n\t}\n}\nfunc TestAppendDurations(t *testing.T) {\n\tarray := make([]time.Duration, len(internal.DurTestcases))\n\twant := make([]byte, 0)\n\twant = append(want, 0x83) // start 3 element array\n\tfor i, tt := range internal.DurTestcases {\n\t\tarray[i] = tt.Duration\n\t\twant = append(want, []byte(tt.FloatOut)...)\n\t}\n\n\tgot := enc.AppendDurations([]byte{}, array, time.Microsecond, \"\", false, -1)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendDurations(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now empty array case\n\tarray = make([]time.Duration, 0)\n\twant = make([]byte, 0)\n\twant = append(want, 0x9f) // start and end array\n\twant = append(want, 0xff) // for empty array\n\tgot = enc.AppendDurations([]byte{}, array, time.Microsecond, \"\", false, -1)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendDurations(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now large array case\n\ttesttime := internal.DurTestcases[0].Duration\n\toutbytes := internal.DurTestcases[0].FloatOut\n\tarray = make([]time.Duration, 24)\n\twant = make([]byte, 0)\n\twant = append(want, 0x98) // start a large array\n\twant = append(want, 0x18) // of length 24\n\tfor i := 0; i < len(array); i++ {\n\t\tarray[i] = testtime\n\t\twant = append(want, []byte(outbytes)...)\n\t}\n\tgot = enc.AppendDurations([]byte{}, array, time.Microsecond, \"\", false, -1)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendDurations(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray,\n\t\t\thex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n}\n\nfunc BenchmarkAppendTime(b *testing.B) {\n\ttests := map[string]string{\n\t\t\"Integer\": \"Feb 3, 2013 at 7:54pm (PST)\",\n\t\t\"Float\":   \"2006-01-02T15:04:05.999999-08:00\",\n\t}\n\tconst timeFloatFmt = \"2006-01-02T15:04:05.999999-07:00\"\n\n\tfor name, str := range tests {\n\t\tt, err := time.Parse(time.RFC3339, str)\n\t\tif err != nil {\n\t\t\tt, _ = time.Parse(timeFloatFmt, str)\n\t\t}\n\t\tb.Run(name, func(b *testing.B) {\n\t\t\tbuf := make([]byte, 0, 100)\n\t\t\tfor i := 0; i < b.N; i++ {\n\t\t\t\t_ = enc.AppendTime(buf, t, \"unused\")\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/cbor/types.go",
    "content": "package cbor\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"net\"\n\t\"reflect\"\n)\n\n// AppendNil inserts a 'Nil' object into the dst byte array.\nfunc (Encoder) AppendNil(dst []byte) []byte {\n\treturn append(dst, majorTypeSimpleAndFloat|additionalTypeNull)\n}\n\n// AppendBeginMarker inserts a map start into the dst byte array.\nfunc (Encoder) AppendBeginMarker(dst []byte) []byte {\n\treturn append(dst, majorTypeMap|additionalTypeInfiniteCount)\n}\n\n// AppendEndMarker inserts a map end into the dst byte array.\nfunc (Encoder) AppendEndMarker(dst []byte) []byte {\n\treturn append(dst, majorTypeSimpleAndFloat|additionalTypeBreak)\n}\n\n// AppendObjectData takes an object in form of a byte array and appends to dst.\nfunc (Encoder) AppendObjectData(dst []byte, o []byte) []byte {\n\t// BeginMarker is present in the dst, which\n\t// should not be copied when appending to existing data.\n\treturn append(dst, o[1:]...)\n}\n\n// AppendArrayStart adds markers to indicate the start of an array.\nfunc (Encoder) AppendArrayStart(dst []byte) []byte {\n\treturn append(dst, majorTypeArray|additionalTypeInfiniteCount)\n}\n\n// AppendArrayEnd adds markers to indicate the end of an array.\nfunc (Encoder) AppendArrayEnd(dst []byte) []byte {\n\treturn append(dst, majorTypeSimpleAndFloat|additionalTypeBreak)\n}\n\n// AppendArrayDelim adds markers to indicate end of a particular array element.\nfunc (Encoder) AppendArrayDelim(dst []byte) []byte {\n\t//No delimiters needed in cbor\n\treturn dst\n}\n\n// AppendLineBreak is a noop that keep API compat with json encoder.\nfunc (Encoder) AppendLineBreak(dst []byte) []byte {\n\t// No line breaks needed in binary format.\n\treturn dst\n}\n\n// AppendBool encodes and inserts a boolean value into the dst byte array.\nfunc (Encoder) AppendBool(dst []byte, val bool) []byte {\n\tb := additionalTypeBoolFalse\n\tif val {\n\t\tb = additionalTypeBoolTrue\n\t}\n\treturn append(dst, majorTypeSimpleAndFloat|b)\n}\n\n// AppendBools encodes and inserts an array of boolean values into the dst byte array.\nfunc (e Encoder) AppendBools(dst []byte, vals []bool) []byte {\n\tmajor := majorTypeArray\n\tl := len(vals)\n\tif l == 0 {\n\t\treturn e.AppendArrayEnd(e.AppendArrayStart(dst))\n\t}\n\tif l <= additionalMax {\n\t\tlb := byte(l)\n\t\tdst = append(dst, major|lb)\n\t} else {\n\t\tdst = appendCborTypePrefix(dst, major, uint64(l))\n\t}\n\tfor _, v := range vals {\n\t\tdst = e.AppendBool(dst, v)\n\t}\n\treturn dst\n}\n\n// AppendInt encodes and inserts an integer value into the dst byte array.\nfunc (Encoder) AppendInt(dst []byte, val int) []byte {\n\tmajor := majorTypeUnsignedInt\n\tcontentVal := val\n\tif val < 0 {\n\t\tmajor = majorTypeNegativeInt\n\t\tcontentVal = -val - 1\n\t}\n\tif contentVal <= additionalMax {\n\t\tlb := byte(contentVal)\n\t\tdst = append(dst, major|lb)\n\t} else {\n\t\tdst = appendCborTypePrefix(dst, major, uint64(contentVal))\n\t}\n\treturn dst\n}\n\n// AppendInts encodes and inserts an array of integer values into the dst byte array.\nfunc (e Encoder) AppendInts(dst []byte, vals []int) []byte {\n\tmajor := majorTypeArray\n\tl := len(vals)\n\tif l == 0 {\n\t\treturn e.AppendArrayEnd(e.AppendArrayStart(dst))\n\t}\n\tif l <= additionalMax {\n\t\tlb := byte(l)\n\t\tdst = append(dst, major|lb)\n\t} else {\n\t\tdst = appendCborTypePrefix(dst, major, uint64(l))\n\t}\n\tfor _, v := range vals {\n\t\tdst = e.AppendInt(dst, v)\n\t}\n\treturn dst\n}\n\n// AppendInt8 encodes and inserts an int8 value into the dst byte array.\nfunc (e Encoder) AppendInt8(dst []byte, val int8) []byte {\n\treturn e.AppendInt(dst, int(val))\n}\n\n// AppendInts8 encodes and inserts an array of integer values into the dst byte array.\nfunc (e Encoder) AppendInts8(dst []byte, vals []int8) []byte {\n\tmajor := majorTypeArray\n\tl := len(vals)\n\tif l == 0 {\n\t\treturn e.AppendArrayEnd(e.AppendArrayStart(dst))\n\t}\n\tif l <= additionalMax {\n\t\tlb := byte(l)\n\t\tdst = append(dst, major|lb)\n\t} else {\n\t\tdst = appendCborTypePrefix(dst, major, uint64(l))\n\t}\n\tfor _, v := range vals {\n\t\tdst = e.AppendInt(dst, int(v))\n\t}\n\treturn dst\n}\n\n// AppendInt16 encodes and inserts a int16 value into the dst byte array.\nfunc (e Encoder) AppendInt16(dst []byte, val int16) []byte {\n\treturn e.AppendInt(dst, int(val))\n}\n\n// AppendInts16 encodes and inserts an array of int16 values into the dst byte array.\nfunc (e Encoder) AppendInts16(dst []byte, vals []int16) []byte {\n\tmajor := majorTypeArray\n\tl := len(vals)\n\tif l == 0 {\n\t\treturn e.AppendArrayEnd(e.AppendArrayStart(dst))\n\t}\n\tif l <= additionalMax {\n\t\tlb := byte(l)\n\t\tdst = append(dst, major|lb)\n\t} else {\n\t\tdst = appendCborTypePrefix(dst, major, uint64(l))\n\t}\n\tfor _, v := range vals {\n\t\tdst = e.AppendInt(dst, int(v))\n\t}\n\treturn dst\n}\n\n// AppendInt32 encodes and inserts a int32 value into the dst byte array.\nfunc (e Encoder) AppendInt32(dst []byte, val int32) []byte {\n\treturn e.AppendInt(dst, int(val))\n}\n\n// AppendInts32 encodes and inserts an array of int32 values into the dst byte array.\nfunc (e Encoder) AppendInts32(dst []byte, vals []int32) []byte {\n\tmajor := majorTypeArray\n\tl := len(vals)\n\tif l == 0 {\n\t\treturn e.AppendArrayEnd(e.AppendArrayStart(dst))\n\t}\n\tif l <= additionalMax {\n\t\tlb := byte(l)\n\t\tdst = append(dst, major|lb)\n\t} else {\n\t\tdst = appendCborTypePrefix(dst, major, uint64(l))\n\t}\n\tfor _, v := range vals {\n\t\tdst = e.AppendInt(dst, int(v))\n\t}\n\treturn dst\n}\n\n// AppendInt64 encodes and inserts a int64 value into the dst byte array.\nfunc (Encoder) AppendInt64(dst []byte, val int64) []byte {\n\tmajor := majorTypeUnsignedInt\n\tcontentVal := val\n\tif val < 0 {\n\t\tmajor = majorTypeNegativeInt\n\t\tcontentVal = -val - 1\n\t}\n\tif contentVal <= additionalMax {\n\t\tlb := byte(contentVal)\n\t\tdst = append(dst, major|lb)\n\t} else {\n\t\tdst = appendCborTypePrefix(dst, major, uint64(contentVal))\n\t}\n\treturn dst\n}\n\n// AppendInts64 encodes and inserts an array of int64 values into the dst byte array.\nfunc (e Encoder) AppendInts64(dst []byte, vals []int64) []byte {\n\tmajor := majorTypeArray\n\tl := len(vals)\n\tif l == 0 {\n\t\treturn e.AppendArrayEnd(e.AppendArrayStart(dst))\n\t}\n\tif l <= additionalMax {\n\t\tlb := byte(l)\n\t\tdst = append(dst, major|lb)\n\t} else {\n\t\tdst = appendCborTypePrefix(dst, major, uint64(l))\n\t}\n\tfor _, v := range vals {\n\t\tdst = e.AppendInt64(dst, v)\n\t}\n\treturn dst\n}\n\n// AppendUint encodes and inserts an unsigned integer value into the dst byte array.\nfunc (e Encoder) AppendUint(dst []byte, val uint) []byte {\n\treturn e.AppendInt64(dst, int64(val))\n}\n\n// AppendUints encodes and inserts an array of unsigned integer values into the dst byte array.\nfunc (e Encoder) AppendUints(dst []byte, vals []uint) []byte {\n\tmajor := majorTypeArray\n\tl := len(vals)\n\tif l == 0 {\n\t\treturn e.AppendArrayEnd(e.AppendArrayStart(dst))\n\t}\n\tif l <= additionalMax {\n\t\tlb := byte(l)\n\t\tdst = append(dst, major|lb)\n\t} else {\n\t\tdst = appendCborTypePrefix(dst, major, uint64(l))\n\t}\n\tfor _, v := range vals {\n\t\tdst = e.AppendUint(dst, v)\n\t}\n\treturn dst\n}\n\n// AppendUint8 encodes and inserts a unsigned int8 value into the dst byte array.\nfunc (e Encoder) AppendUint8(dst []byte, val uint8) []byte {\n\treturn e.AppendUint(dst, uint(val))\n}\n\n// AppendUints8 encodes and inserts an array of uint8 values into the dst byte array.\nfunc (e Encoder) AppendUints8(dst []byte, vals []uint8) []byte {\n\tmajor := majorTypeArray\n\tl := len(vals)\n\tif l == 0 {\n\t\treturn e.AppendArrayEnd(e.AppendArrayStart(dst))\n\t}\n\tif l <= additionalMax {\n\t\tlb := byte(l)\n\t\tdst = append(dst, major|lb)\n\t} else {\n\t\tdst = appendCborTypePrefix(dst, major, uint64(l))\n\t}\n\tfor _, v := range vals {\n\t\tdst = e.AppendUint8(dst, v)\n\t}\n\treturn dst\n}\n\n// AppendUint16 encodes and inserts a uint16 value into the dst byte array.\nfunc (e Encoder) AppendUint16(dst []byte, val uint16) []byte {\n\treturn e.AppendUint(dst, uint(val))\n}\n\n// AppendUints16 encodes and inserts an array of uint16 values into the dst byte array.\nfunc (e Encoder) AppendUints16(dst []byte, vals []uint16) []byte {\n\tmajor := majorTypeArray\n\tl := len(vals)\n\tif l == 0 {\n\t\treturn e.AppendArrayEnd(e.AppendArrayStart(dst))\n\t}\n\tif l <= additionalMax {\n\t\tlb := byte(l)\n\t\tdst = append(dst, major|lb)\n\t} else {\n\t\tdst = appendCborTypePrefix(dst, major, uint64(l))\n\t}\n\tfor _, v := range vals {\n\t\tdst = e.AppendUint16(dst, v)\n\t}\n\treturn dst\n}\n\n// AppendUint32 encodes and inserts a uint32 value into the dst byte array.\nfunc (e Encoder) AppendUint32(dst []byte, val uint32) []byte {\n\treturn e.AppendUint(dst, uint(val))\n}\n\n// AppendUints32 encodes and inserts an array of uint32 values into the dst byte array.\nfunc (e Encoder) AppendUints32(dst []byte, vals []uint32) []byte {\n\tmajor := majorTypeArray\n\tl := len(vals)\n\tif l == 0 {\n\t\treturn e.AppendArrayEnd(e.AppendArrayStart(dst))\n\t}\n\tif l <= additionalMax {\n\t\tlb := byte(l)\n\t\tdst = append(dst, major|lb)\n\t} else {\n\t\tdst = appendCborTypePrefix(dst, major, uint64(l))\n\t}\n\tfor _, v := range vals {\n\t\tdst = e.AppendUint32(dst, v)\n\t}\n\treturn dst\n}\n\n// AppendUint64 encodes and inserts a uint64 value into the dst byte array.\nfunc (Encoder) AppendUint64(dst []byte, val uint64) []byte {\n\tmajor := majorTypeUnsignedInt\n\tcontentVal := val\n\tif contentVal <= additionalMax {\n\t\tlb := byte(contentVal)\n\t\tdst = append(dst, major|lb)\n\t} else {\n\t\tdst = appendCborTypePrefix(dst, major, contentVal)\n\t}\n\treturn dst\n}\n\n// AppendUints64 encodes and inserts an array of uint64 values into the dst byte array.\nfunc (e Encoder) AppendUints64(dst []byte, vals []uint64) []byte {\n\tmajor := majorTypeArray\n\tl := len(vals)\n\tif l == 0 {\n\t\treturn e.AppendArrayEnd(e.AppendArrayStart(dst))\n\t}\n\tif l <= additionalMax {\n\t\tlb := byte(l)\n\t\tdst = append(dst, major|lb)\n\t} else {\n\t\tdst = appendCborTypePrefix(dst, major, uint64(l))\n\t}\n\tfor _, v := range vals {\n\t\tdst = e.AppendUint64(dst, v)\n\t}\n\treturn dst\n}\n\n// AppendFloat32 encodes and inserts a single precision float value into the dst byte array.\nfunc (Encoder) AppendFloat32(dst []byte, val float32, unused int) []byte {\n\tswitch {\n\tcase math.IsNaN(float64(val)):\n\t\treturn append(dst, \"\\xfa\\x7f\\xc0\\x00\\x00\"...)\n\tcase math.IsInf(float64(val), 1):\n\t\treturn append(dst, \"\\xfa\\x7f\\x80\\x00\\x00\"...)\n\tcase math.IsInf(float64(val), -1):\n\t\treturn append(dst, \"\\xfa\\xff\\x80\\x00\\x00\"...)\n\t}\n\tmajor := majorTypeSimpleAndFloat\n\tsubType := additionalTypeFloat32\n\tn := math.Float32bits(val)\n\tvar buf [4]byte\n\tfor i := uint(0); i < 4; i++ {\n\t\tbuf[i] = byte(n >> ((3 - i) * 8))\n\t}\n\treturn append(append(dst, major|subType), buf[0], buf[1], buf[2], buf[3])\n}\n\n// AppendFloats32 encodes and inserts an array of single precision float value into the dst byte array.\nfunc (e Encoder) AppendFloats32(dst []byte, vals []float32, unused int) []byte {\n\tmajor := majorTypeArray\n\tl := len(vals)\n\tif l == 0 {\n\t\treturn e.AppendArrayEnd(e.AppendArrayStart(dst))\n\t}\n\tif l <= additionalMax {\n\t\tlb := byte(l)\n\t\tdst = append(dst, major|lb)\n\t} else {\n\t\tdst = appendCborTypePrefix(dst, major, uint64(l))\n\t}\n\tfor _, v := range vals {\n\t\tdst = e.AppendFloat32(dst, v, unused)\n\t}\n\treturn dst\n}\n\n// AppendFloat64 encodes and inserts a double precision float value into the dst byte array.\nfunc (Encoder) AppendFloat64(dst []byte, val float64, unused int) []byte {\n\tswitch {\n\tcase math.IsNaN(val):\n\t\treturn append(dst, \"\\xfb\\x7f\\xf8\\x00\\x00\\x00\\x00\\x00\\x00\"...)\n\tcase math.IsInf(val, 1):\n\t\treturn append(dst, \"\\xfb\\x7f\\xf0\\x00\\x00\\x00\\x00\\x00\\x00\"...)\n\tcase math.IsInf(val, -1):\n\t\treturn append(dst, \"\\xfb\\xff\\xf0\\x00\\x00\\x00\\x00\\x00\\x00\"...)\n\t}\n\tmajor := majorTypeSimpleAndFloat\n\tsubType := additionalTypeFloat64\n\tn := math.Float64bits(val)\n\tdst = append(dst, major|subType)\n\tfor i := uint(1); i <= 8; i++ {\n\t\tb := byte(n >> ((8 - i) * 8))\n\t\tdst = append(dst, b)\n\t}\n\treturn dst\n}\n\n// AppendFloats64 encodes and inserts an array of double precision float values into the dst byte array.\nfunc (e Encoder) AppendFloats64(dst []byte, vals []float64, unused int) []byte {\n\tmajor := majorTypeArray\n\tl := len(vals)\n\tif l == 0 {\n\t\treturn e.AppendArrayEnd(e.AppendArrayStart(dst))\n\t}\n\tif l <= additionalMax {\n\t\tlb := byte(l)\n\t\tdst = append(dst, major|lb)\n\t} else {\n\t\tdst = appendCborTypePrefix(dst, major, uint64(l))\n\t}\n\tfor _, v := range vals {\n\t\tdst = e.AppendFloat64(dst, v, unused)\n\t}\n\treturn dst\n}\n\n// AppendInterface takes an arbitrary object and converts it to JSON and embeds it dst.\nfunc (e Encoder) AppendInterface(dst []byte, i interface{}) []byte {\n\tmarshaled, err := JSONMarshalFunc(i)\n\tif err != nil {\n\t\treturn e.AppendString(dst, fmt.Sprintf(\"marshaling error: %v\", err))\n\t}\n\treturn AppendEmbeddedJSON(dst, marshaled)\n}\n\n// AppendType appends the parameter type (as a string) to the input byte slice.\nfunc (e Encoder) AppendType(dst []byte, i interface{}) []byte {\n\tif i == nil {\n\t\treturn e.AppendString(dst, \"<nil>\")\n\t}\n\treturn e.AppendString(dst, reflect.TypeOf(i).String())\n}\n\n// AppendIPAddr adds a net.IP IPv4 or IPv6 address into the dst byte array.\nfunc (e Encoder) AppendIPAddr(dst []byte, ip net.IP) []byte {\n\tdst = append(dst, majorTypeTags|additionalTypeIntUint16)\n\tdst = append(dst, byte(additionalTypeTagNetworkAddr>>8))\n\tdst = append(dst, byte(additionalTypeTagNetworkAddr&0xff))\n\treturn e.AppendBytes(dst, ip)\n}\n\n// AppendIPAddrs adds a []net.IP array of IPv4 or IPv6 address into the dst byte array.\nfunc (e Encoder) AppendIPAddrs(dst []byte, ips []net.IP) []byte {\n\tmajor := majorTypeArray\n\tl := len(ips)\n\tif l == 0 {\n\t\treturn e.AppendArrayEnd(e.AppendArrayStart(dst))\n\t}\n\tif l <= additionalMax {\n\t\tlb := byte(l)\n\t\tdst = append(dst, major|lb)\n\t} else {\n\t\tdst = appendCborTypePrefix(dst, major, uint64(l))\n\t}\n\tfor _, v := range ips {\n\t\tdst = e.AppendIPAddr(dst, v)\n\t}\n\treturn dst\n}\n\n// AppendIPPrefix adds a net.IPNet IPv4 or IPv6 Prefix (address & mask) into the dst byte array.\nfunc (e Encoder) AppendIPPrefix(dst []byte, pfx net.IPNet) []byte {\n\tdst = append(dst, majorTypeTags|additionalTypeIntUint16)\n\tdst = append(dst, byte(additionalTypeTagNetworkPrefix>>8))\n\tdst = append(dst, byte(additionalTypeTagNetworkPrefix&0xff))\n\n\t// Prefix is a tuple (aka MAP of 1 pair of elements) -\n\t// first element is prefix, second is mask length.\n\tdst = append(dst, majorTypeMap|0x1)\n\tdst = e.AppendBytes(dst, pfx.IP)\n\tmaskLen, _ := pfx.Mask.Size()\n\treturn e.AppendUint8(dst, uint8(maskLen))\n}\n\n// AppendIPPrefixes adds a []net.IPNet array of IPv4 or IPv6 Prefix (address & mask) into the dst byte array.\nfunc (e Encoder) AppendIPPrefixes(dst []byte, pfxs []net.IPNet) []byte {\n\tmajor := majorTypeArray\n\tl := len(pfxs)\n\tif l == 0 {\n\t\treturn e.AppendArrayEnd(e.AppendArrayStart(dst))\n\t}\n\tif l <= additionalMax {\n\t\tlb := byte(l)\n\t\tdst = append(dst, major|lb)\n\t} else {\n\t\tdst = appendCborTypePrefix(dst, major, uint64(l))\n\t}\n\tfor _, v := range pfxs {\n\t\tdst = e.AppendIPPrefix(dst, v)\n\t}\n\treturn dst\n}\n\n// AppendMACAddr encodes and inserts a Hardware (MAC) address.\nfunc (e Encoder) AppendMACAddr(dst []byte, ha net.HardwareAddr) []byte {\n\tdst = append(dst, majorTypeTags|additionalTypeIntUint16)\n\tdst = append(dst, byte(additionalTypeTagNetworkAddr>>8))\n\tdst = append(dst, byte(additionalTypeTagNetworkAddr&0xff))\n\treturn e.AppendBytes(dst, ha)\n}\n\n// AppendHex adds a TAG and inserts a hex bytes as a string.\nfunc (e Encoder) AppendHex(dst []byte, val []byte) []byte {\n\tdst = append(dst, majorTypeTags|additionalTypeIntUint16)\n\tdst = append(dst, byte(additionalTypeTagHexString>>8))\n\tdst = append(dst, byte(additionalTypeTagHexString&0xff))\n\treturn e.AppendBytes(dst, val)\n}\n"
  },
  {
    "path": "internal/cbor/types_64_test.go",
    "content": "// +build !386\n\npackage cbor\n\nimport (\n\t\"encoding/hex\"\n\t\"testing\"\n)\n\nvar enc2 = Encoder{}\n\nvar integerTestCases_64bit = []struct {\n\tval    int\n\tbinary string\n}{\n\t// Value in 8 bytes.\n\t{0xabcd100000000, \"\\x1b\\x00\\x0a\\xbc\\xd1\\x00\\x00\\x00\\x00\"},\n\t{1000000000000, \"\\x1b\\x00\\x00\\x00\\xe8\\xd4\\xa5\\x10\\x00\"},\n\t// Value in 8 bytes.\n\t{-0xabcd100000001, \"\\x3b\\x00\\x0a\\xbc\\xd1\\x00\\x00\\x00\\x00\"},\n\t{-1000000000001, \"\\x3b\\x00\\x00\\x00\\xe8\\xd4\\xa5\\x10\\x00\"},\n}\n\nfunc TestAppendInt_64bit(t *testing.T) {\n\tfor _, tc := range integerTestCases_64bit {\n\t\ts := enc2.AppendInt([]byte{}, tc.val)\n\t\tgot := string(s)\n\t\tif got != tc.binary {\n\t\t\tt.Errorf(\"AppendInt(0x%x)=0x%s, want: 0x%s\",\n\t\t\t\ttc.val, hex.EncodeToString(s),\n\t\t\t\thex.EncodeToString([]byte(tc.binary)))\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "internal/cbor/types_test.go",
    "content": "package cbor\n\nimport (\n\t\"bytes\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"math\"\n\t\"net\"\n\t\"testing\"\n\n\t\"github.com/rs/zerolog/internal\"\n)\n\nvar enc = Encoder{}\n\nfunc TestAppendNil(t *testing.T) {\n\ts := enc.AppendNil([]byte{})\n\tgot := string(s)\n\twant := \"\\xf6\"\n\tif got != want {\n\t\tt.Errorf(\"AppendNil() = 0x%s, want: 0x%s\", hex.EncodeToString(s),\n\t\t\thex.EncodeToString([]byte(want)))\n\t}\n}\nfunc TestAppendBeginMarker(t *testing.T) {\n\ts := enc.AppendBeginMarker([]byte{})\n\tgot := string(s)\n\twant := \"\\xbf\"\n\tif got != want {\n\t\tt.Errorf(\"AppendBeginMarker() = 0x%s, want: 0x%s\", hex.EncodeToString(s),\n\t\t\thex.EncodeToString([]byte(want)))\n\t}\n}\nfunc TestAppendEndMarker(t *testing.T) {\n\ts := enc.AppendEndMarker([]byte{})\n\tgot := string(s)\n\twant := \"\\xff\"\n\tif got != want {\n\t\tt.Errorf(\"AppendEndMarker() = 0x%s, want: 0x%s\", hex.EncodeToString(s),\n\t\t\thex.EncodeToString([]byte(want)))\n\t}\n}\nfunc TestAppendObjectData(t *testing.T) {\n\tdata := []byte{0xbf, 0x02, 0x03}\n\ts := enc.AppendObjectData([]byte{}, data)\n\tgot := string(s)\n\twant := \"\\x02\\x03\" // the begin marker is not copied\n\tif got != want {\n\t\tt.Errorf(\"AppendObjectData() = 0x%s, want: 0x%s\", hex.EncodeToString(s),\n\t\t\thex.EncodeToString([]byte(want)))\n\t}\n}\nfunc TestAppendArrayDelim(t *testing.T) {\n\ts := enc.AppendArrayDelim([]byte{})\n\tgot := string(s)\n\twant := \"\"\n\tif got != want {\n\t\tt.Errorf(\"AppendArrayDelim() = 0x%s, want: 0x%s\", hex.EncodeToString(s),\n\t\t\thex.EncodeToString([]byte(want)))\n\t}\n}\nfunc TestAppendLineBreak(t *testing.T) {\n\ts := enc.AppendLineBreak([]byte{})\n\tgot := string(s)\n\twant := \"\"\n\tif got != want {\n\t\tt.Errorf(\"AppendLineBreak() = 0x%s, want: 0x%s\", hex.EncodeToString(s),\n\t\t\thex.EncodeToString([]byte(want)))\n\t}\n}\n\nfunc TestAppendInterface(t *testing.T) {\n\toldJSONMarshalFunc := JSONMarshalFunc\n\tdefer func() {\n\t\tJSONMarshalFunc = oldJSONMarshalFunc\n\t}()\n\n\tJSONMarshalFunc = func(v interface{}) ([]byte, error) {\n\t\treturn internal.InterfaceMarshalFunc(v)\n\t}\n\n\tvar i int = 17\n\tgot := enc.AppendInterface([]byte{}, i)\n\twant := make([]byte, 0)\n\twant = append(want, 0xd9, 0x01) // start an array\n\twant = append(want, 0x06, 0x42) // of type interface, two characters\n\twant = append(want, 0x31, 0x37) // with literal int 17\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendInterface\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\thex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\tJSONMarshalFunc = func(v interface{}) ([]byte, error) {\n\t\treturn nil, errors.New(\"test\")\n\t}\n\n\tgot = enc.AppendInterface([]byte{}, nil)\n\twant = make([]byte, 0)\n\twant = append(want, 0x76)                                // string\n\twant = append(want, []byte(\"marshaling error: test\")...) // of type interface, two characters\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendInterface\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\thex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n}\n\nfunc TestAppendType(t *testing.T) {\n\ts := enc.AppendType([]byte{}, \"\")\n\tgot := string(s)\n\twant := \"\\x66string\"\n\tif got != want {\n\t\tt.Errorf(\"AppendType() = 0x%s, want: 0x%s\", hex.EncodeToString(s),\n\t\t\thex.EncodeToString([]byte(want)))\n\t}\n\n\ts = enc.AppendType([]byte{}, nil)\n\tgot = string(s)\n\twant = \"\\x65<nil>\"\n\tif got != want {\n\t\tt.Errorf(\"AppendType() = 0x%s, want: 0x%s\", hex.EncodeToString(s),\n\t\t\thex.EncodeToString([]byte(want)))\n\t}\n\n\tvar n *int = nil\n\ts = enc.AppendType([]byte{}, n)\n\tgot = string(s)\n\twant = \"\\x64*int\"\n\tif got != want {\n\t\tt.Errorf(\"AppendType() = 0x%s, want: 0x%s\", hex.EncodeToString(s),\n\t\t\thex.EncodeToString([]byte(want)))\n\t}\n}\n\nfunc TestAppendBool(t *testing.T) {\n\tfor _, tc := range internal.BooleanTestCases {\n\t\ts := enc.AppendBool([]byte{}, tc.Val)\n\t\tgot := string(s)\n\t\tif got != tc.Binary {\n\t\t\tt.Errorf(\"AppendBool(%s)=0x%s, want: 0x%s\",\n\t\t\t\ttc.Json, hex.EncodeToString(s),\n\t\t\t\thex.EncodeToString([]byte(tc.Binary)))\n\t\t}\n\t}\n}\n\nfunc TestAppendBoolArray(t *testing.T) {\n\tfor _, tc := range internal.BooleanArrayTestCases {\n\t\ts := enc.AppendBools([]byte{}, tc.Val)\n\t\tgot := string(s)\n\t\tif got != tc.Binary {\n\t\t\tt.Errorf(\"AppendBools(%s)=0x%s, want: 0x%s\",\n\t\t\t\ttc.Json, hex.EncodeToString(s),\n\t\t\t\thex.EncodeToString([]byte(tc.Binary)))\n\t\t}\n\t}\n\n\t// now empty array case\n\tarray := make([]bool, 0)\n\twant := make([]byte, 0)\n\twant = append(want, 0x9f) // start and end array\n\twant = append(want, 0xff) // for empty array\n\tgot := enc.AppendBools([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendBools(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now a large array case\n\tarray = make([]bool, 24)\n\twant = make([]byte, 0)\n\twant = append(want, 0x98) // start a large array\n\twant = append(want, 0x18) // for 24 elements\n\tfor i := 0; i < 24; i++ {\n\t\tarray[i] = bool(i%2 == 1)\n\t\twant = append(want, 0xf4|byte(i&0x01)) // 0xf4 is false, 0xf5 is true\n\t}\n\tgot = enc.AppendBools([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendBools(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n}\n\nfunc TestAppendInt8(t *testing.T) {\n\tfor _, tc := range internal.IntegerTestCases {\n\t\tif (tc.Val < math.MinInt8) || (tc.Val > math.MaxInt8) {\n\t\t\tcontinue\n\t\t}\n\t\ts := enc.AppendInt8([]byte{}, int8(tc.Val))\n\t\tgot := string(s)\n\t\tif got != tc.Binary {\n\t\t\tt.Errorf(\"AppendInt8(0x%x)=0x%s, want: 0x%s\",\n\t\t\t\ttc.Val, hex.EncodeToString(s),\n\t\t\t\thex.EncodeToString([]byte(tc.Binary)))\n\t\t}\n\t}\n}\n\nfunc TestAppendInts8(t *testing.T) {\n\tarray := make([]int8, 0)\n\twant := make([]byte, 0)\n\twant = append(want, 0x98) // start array\n\twant = append(want, 0x1b) // for signed 8-bit elements\n\tfor _, tc := range internal.IntegerTestCases {\n\t\tif (tc.Val < math.MinInt8) || (tc.Val > math.MaxInt8) {\n\t\t\tcontinue\n\t\t}\n\t\tarray = append(array, int8(tc.Val))\n\t\twant = append(want, tc.Binary...)\n\t}\n\n\tgot := enc.AppendInts8([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendInts8(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now empty array case\n\tarray = make([]int8, 0)\n\twant = make([]byte, 0)\n\twant = append(want, 0x9f) // start and end array\n\twant = append(want, 0xff) // for empty array\n\tgot = enc.AppendInts8([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendInts8(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now small array case\n\tarray = make([]int8, 21)\n\twant = make([]byte, 0)\n\twant = append(want, 0x95) // start a small array\n\tfor i := 0; i < 21; i++ {\n\t\tarray[i] = int8(i)\n\t\twant = append(want, byte(i))\n\t}\n\tgot = enc.AppendInts8([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendInts8(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n}\n\nfunc TestAppendInt16(t *testing.T) {\n\tfor _, tc := range internal.IntegerTestCases {\n\t\tif (tc.Val < math.MinInt16) || (tc.Val > math.MaxInt16) {\n\t\t\tcontinue\n\t\t}\n\t\ts := enc.AppendInt16([]byte{}, int16(tc.Val))\n\t\tgot := string(s)\n\t\tif got != tc.Binary {\n\t\t\tt.Errorf(\"AppendInt16(0x%x)=0x%s, want: 0x%s\",\n\t\t\t\ttc.Val, hex.EncodeToString(s),\n\t\t\t\thex.EncodeToString([]byte(tc.Binary)))\n\t\t}\n\t}\n}\n\nfunc TestAppendInts16(t *testing.T) {\n\tarray := make([]int16, 0)\n\twant := make([]byte, 0)\n\twant = append(want, 0x98) // start array\n\twant = append(want, 0x28) // for signed 16-bit elements\n\tfor _, tc := range internal.IntegerTestCases {\n\t\tif (tc.Val < math.MinInt16) || (tc.Val > math.MaxInt16) {\n\t\t\tcontinue\n\t\t}\n\t\tarray = append(array, int16(tc.Val))\n\t\twant = append(want, tc.Binary...)\n\t}\n\n\tgot := enc.AppendInts16([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendInts16(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now empty array case\n\tarray = make([]int16, 0)\n\twant = make([]byte, 0)\n\twant = append(want, 0x9f) // start and end array\n\twant = append(want, 0xff) // for empty array\n\tgot = enc.AppendInts16([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendInts16(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now a small array case\n\tarray = make([]int16, 21)\n\twant = make([]byte, 0)\n\twant = append(want, 0x95) // start a smaller array\n\tfor i := 0; i < 21; i++ {\n\t\tarray[i] = int16(i)\n\t\twant = append(want, byte(i))\n\t}\n\tgot = enc.AppendInts16([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendInts16(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n}\n\nfunc TestAppendInt32(t *testing.T) {\n\tfor _, tc := range internal.IntegerTestCases {\n\t\tif (tc.Val < math.MinInt32) || (tc.Val > math.MaxInt32) {\n\t\t\tcontinue\n\t\t}\n\t\ts := enc.AppendInt32([]byte{}, int32(tc.Val))\n\t\tgot := string(s)\n\t\tif got != tc.Binary {\n\t\t\tt.Errorf(\"AppendInt32(0x%x)=0x%s, want: 0x%s\",\n\t\t\t\ttc.Val, hex.EncodeToString(s),\n\t\t\t\thex.EncodeToString([]byte(tc.Binary)))\n\t\t}\n\t}\n}\n\nfunc TestAppendInts32(t *testing.T) {\n\tarray := make([]int32, 0)\n\twant := make([]byte, 0)\n\twant = append(want, 0x98) // start array\n\twant = append(want, 0x31) // for signed 32-bit elements\n\tfor _, tc := range internal.IntegerTestCases {\n\t\tif (tc.Val < math.MinInt32) || (tc.Val > math.MaxInt32) {\n\t\t\tcontinue\n\t\t}\n\t\tarray = append(array, int32(tc.Val))\n\t\twant = append(want, tc.Binary...)\n\t}\n\n\tgot := enc.AppendInts32([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendInts32(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now empty array case\n\tarray = make([]int32, 0)\n\twant = make([]byte, 0)\n\twant = append(want, 0x9f) // start and end array\n\twant = append(want, 0xff) // for empty array\n\tgot = enc.AppendInts32([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendInts32(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now a small array case\n\tarray = make([]int32, 21)\n\twant = make([]byte, 0)\n\twant = append(want, 0x95) // start a smaller array\n\tfor i := 0; i < 21; i++ {\n\t\tarray[i] = int32(i)\n\t\twant = append(want, byte(i))\n\t}\n\tgot = enc.AppendInts32([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendInts32(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n}\n\nfunc TestAppendInt64(t *testing.T) {\n\tfor _, tc := range internal.IntegerTestCases {\n\t\ts := enc.AppendInt64([]byte{}, int64(tc.Val))\n\t\tgot := string(s)\n\t\tif got != tc.Binary {\n\t\t\tt.Errorf(\"AppendInt64(0x%x)=0x%s, want: 0x%s\",\n\t\t\t\ttc.Val, hex.EncodeToString(s),\n\t\t\t\thex.EncodeToString([]byte(tc.Binary)))\n\t\t}\n\t}\n}\n\nfunc TestAppendInts64(t *testing.T) {\n\tarray := make([]int64, 0)\n\twant := make([]byte, 0)\n\twant = append(want, 0x98) // start array\n\twant = append(want, 0x33) // for signed 64-bit elements\n\tfor _, tc := range internal.IntegerTestCases {\n\t\tarray = append(array, int64(tc.Val))\n\t\twant = append(want, tc.Binary...)\n\t}\n\n\tgot := enc.AppendInts64([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendInts64(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now empty array case\n\tarray = make([]int64, 0)\n\twant = make([]byte, 0)\n\twant = append(want, 0x9f) // start and end array\n\twant = append(want, 0xff) // for empty array\n\tgot = enc.AppendInts64([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendInts64(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now a small array case\n\tarray = make([]int64, 21)\n\twant = make([]byte, 0)\n\twant = append(want, 0x95) // start a smaller array\n\tfor i := 0; i < 21; i++ {\n\t\tarray[i] = int64(i)\n\t\twant = append(want, byte(i))\n\t}\n\tgot = enc.AppendInts64([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendInts64(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n}\n\nfunc TestAppendInt(t *testing.T) {\n\tfor _, tc := range internal.IntegerTestCases {\n\t\ts := enc.AppendInt([]byte{}, tc.Val)\n\t\tgot := string(s)\n\t\tif got != tc.Binary {\n\t\t\tt.Errorf(\"AppendInt(0x%x)=0x%s, want: 0x%s\",\n\t\t\t\ttc.Val, hex.EncodeToString(s),\n\t\t\t\thex.EncodeToString([]byte(tc.Binary)))\n\t\t}\n\t}\n}\n\nfunc TestAppendInts(t *testing.T) {\n\tarray := make([]int, 0)\n\twant := make([]byte, 0)\n\twant = append(want, 0x98) // start array\n\twant = append(want, 0x33) // for signed int elements\n\tfor _, tc := range internal.IntegerTestCases {\n\t\tarray = append(array, int(tc.Val))\n\t\twant = append(want, tc.Binary...)\n\t}\n\n\tgot := enc.AppendInts([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendInts(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now empty array case\n\tarray = make([]int, 0)\n\twant = make([]byte, 0)\n\twant = append(want, 0x9f) // start and end array\n\twant = append(want, 0xff) // for empty array\n\tgot = enc.AppendInts([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendInts(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now a small array case\n\tarray = make([]int, 21)\n\twant = make([]byte, 0)\n\twant = append(want, 0x95) // start a smaller array\n\tfor i := 0; i < 21; i++ {\n\t\tarray[i] = int(i)\n\t\twant = append(want, byte(i))\n\t}\n\tgot = enc.AppendInts([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendInts(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n}\n\nfunc TestAppendUint8(t *testing.T) {\n\tfor _, tc := range internal.UnsignedIntegerTestCases {\n\t\tif tc.Val > math.MaxUint8 {\n\t\t\tcontinue\n\t\t}\n\t\ts := enc.AppendUint8([]byte{}, uint8(tc.Val))\n\t\tgot := string(s)\n\t\tif got != tc.Binary {\n\t\t\tt.Errorf(\"AppendUint8(0x%x)=0x%s, want: 0x%s\",\n\t\t\t\ttc.Val, hex.EncodeToString(s),\n\t\t\t\thex.EncodeToString([]byte(tc.Binary)))\n\t\t}\n\t}\n}\n\nfunc TestAppendUints8(t *testing.T) {\n\tarray := make([]uint8, 0)\n\twant := make([]byte, 0)\n\twant = append(want, 0x91) // start array for unsigned 8-bit elements\n\tfor _, tc := range internal.UnsignedIntegerTestCases {\n\t\tif tc.Val > math.MaxUint8 {\n\t\t\tcontinue\n\t\t}\n\t\tarray = append(array, uint8(tc.Val))\n\t\twant = append(want, tc.Binary...)\n\t}\n\n\tgot := enc.AppendUints8([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendUints8(%v)=0x%s, want: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now empty array case\n\tarray = make([]uint8, 0)\n\twant = make([]byte, 0)\n\twant = append(want, 0x9f) // start and end array\n\twant = append(want, 0xff) // for empty array\n\tgot = enc.AppendUints8([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendUints8(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now a large array case\n\tarray = make([]uint8, 24)\n\twant = make([]byte, 0)\n\twant = append(want, 0x98) // start a large array\n\twant = append(want, 0x18) // for 24 elements\n\tfor i := 0; i < 24; i++ {\n\t\tarray[i] = uint8(i)\n\t\twant = append(want, byte(i))\n\t}\n\tgot = enc.AppendUints8([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendUints8(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n}\n\nfunc TestAppendUint16(t *testing.T) {\n\tfor _, tc := range internal.UnsignedIntegerTestCases {\n\t\tif tc.Val > math.MaxUint16 {\n\t\t\tcontinue\n\t\t}\n\t\ts := enc.AppendUint16([]byte{}, uint16(tc.Val))\n\t\tgot := string(s)\n\t\tif got != tc.Binary {\n\t\t\tt.Errorf(\"AppendUint16(0x%x)=0x%s, want: 0x%s\",\n\t\t\t\ttc.Val, hex.EncodeToString(s),\n\t\t\t\thex.EncodeToString([]byte(tc.Binary)))\n\t\t}\n\n\t}\n}\n\nfunc TestAppendUints16(t *testing.T) {\n\tarray := make([]uint16, 0)\n\twant := make([]byte, 0)\n\twant = append(want, 0x97) // start array for unsigned 16-bit elements\n\tfor _, tc := range internal.UnsignedIntegerTestCases {\n\t\tif tc.Val > math.MaxUint16 {\n\t\t\tcontinue\n\t\t}\n\t\tarray = append(array, uint16(tc.Val))\n\t\twant = append(want, tc.Binary...)\n\t}\n\n\tgot := enc.AppendUints16([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendUints16(%v)=0x%s, want: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now empty array case\n\tarray = make([]uint16, 0)\n\twant = make([]byte, 0)\n\twant = append(want, 0x9f) // start and end array\n\twant = append(want, 0xff) // for empty array\n\tgot = enc.AppendUints16([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendUints8(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now a large array case\n\tarray = make([]uint16, 24)\n\twant = make([]byte, 0)\n\twant = append(want, 0x98) // start a larger array\n\twant = append(want, 0x18) // for 24 elements\n\tfor i := 0; i < 24; i++ {\n\t\tarray[i] = uint16(i)\n\t\twant = append(want, byte(i))\n\t}\n\tgot = enc.AppendUints16([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendUints16(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n}\n\nfunc TestAppendUint32(t *testing.T) {\n\tfor _, tc := range internal.UnsignedIntegerTestCases {\n\t\tif tc.Val > math.MaxUint32 {\n\t\t\tcontinue\n\t\t}\n\t\ts := enc.AppendUint32([]byte{}, uint32(tc.Val))\n\t\tgot := string(s)\n\t\twant := tc.Bigbinary\n\t\tif got != want {\n\t\t\tt.Errorf(\"AppendUint32(0x%x)=0x%s, want: 0x%s\",\n\t\t\t\ttc.Val, hex.EncodeToString(s),\n\t\t\t\thex.EncodeToString([]byte(want)))\n\t\t}\n\t}\n}\n\nfunc TestAppendUints32(t *testing.T) {\n\tarray := make([]uint32, 0)\n\twant := make([]byte, 0)\n\twant = append(want, 0x98) // start array\n\twant = append(want, 0x1f) // for unsigned  32-bit elements\n\tfor _, tc := range internal.UnsignedIntegerTestCases {\n\t\tif tc.Val > math.MaxUint32 {\n\t\t\tcontinue\n\t\t}\n\t\tarray = append(array, uint32(tc.Val))\n\t\twant = append(want, tc.Bigbinary...)\n\t}\n\n\tgot := enc.AppendUints32([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendUints32(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now empty array case\n\tarray = make([]uint32, 0)\n\twant = make([]byte, 0)\n\twant = append(want, 0x9f) // start and end array\n\twant = append(want, 0xff) // for empty array\n\tgot = enc.AppendUints32([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendUints32(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now a small array case\n\tarray = make([]uint32, 21)\n\twant = make([]byte, 0)\n\twant = append(want, 0x95) // start a smaller array\n\tfor i := 0; i < 21; i++ {\n\t\tarray[i] = uint32(i)\n\t\twant = append(want, byte(i))\n\t}\n\tgot = enc.AppendUints32([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendUints32(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n}\n\nfunc TestAppendUint64(t *testing.T) {\n\tfor _, tc := range internal.UnsignedIntegerTestCases {\n\t\ts := enc.AppendUint64([]byte{}, uint64(tc.Val))\n\t\tgot := string(s)\n\t\twant := tc.Bigbinary\n\t\tif got != want {\n\t\t\tt.Errorf(\"AppendUint64(0x%x)=0x%s, want: 0x%s\",\n\t\t\t\ttc.Val, hex.EncodeToString(s),\n\t\t\t\thex.EncodeToString([]byte(want)))\n\t\t}\n\t}\n}\n\nfunc TestAppendUints64(t *testing.T) {\n\tarray := make([]uint64, 0)\n\twant := make([]byte, 0)\n\twant = append(want, 0x98) // start array\n\twant = append(want, 0x21) // for unsigned 64-bit elements\n\tfor _, tc := range internal.UnsignedIntegerTestCases {\n\t\tarray = append(array, uint64(tc.Val))\n\t\twant = append(want, tc.Bigbinary...)\n\t}\n\n\tgot := enc.AppendUints64([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendUints64(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now empty array case\n\tarray = make([]uint64, 0)\n\twant = make([]byte, 0)\n\twant = append(want, 0x9f) // start and end array\n\twant = append(want, 0xff) // for empty array\n\tgot = enc.AppendUints64([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendUints64(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now a small array case\n\tarray = make([]uint64, 21)\n\twant = make([]byte, 0)\n\twant = append(want, 0x95) // start a smaller array\n\tfor i := 0; i < 21; i++ {\n\t\tarray[i] = uint64(i)\n\t\twant = append(want, byte(i))\n\t}\n\tgot = enc.AppendUints64([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendUints64(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n}\n\nfunc TestAppendUint(t *testing.T) {\n\tfor _, tc := range internal.UnsignedIntegerTestCases {\n\t\ts := enc.AppendUint([]byte{}, tc.Val)\n\t\tgot := string(s)\n\t\twant := tc.Bigbinary\n\t\tif tc.Val == math.MaxUint64 {\n\t\t\twant = \"\\x20\" // this is special case for uint max value when using AppendUint\n\t\t}\n\t\tif got != want {\n\t\t\tt.Errorf(\"AppendUint(0x%x)=0x%s, want: 0x%s\",\n\t\t\t\ttc.Val, hex.EncodeToString(s),\n\t\t\t\thex.EncodeToString([]byte(want)))\n\t\t}\n\t}\n}\n\nfunc TestAppendUints(t *testing.T) {\n\tarray := make([]uint, 0)\n\twant := make([]byte, 0)\n\twant = append(want, 0x98) // start array\n\twant = append(want, 0x21) // for unsigned int elements\n\tfor _, tc := range internal.UnsignedIntegerTestCases {\n\t\tarray = append(array, uint(tc.Val))\n\t\texpected := tc.Bigbinary\n\t\tif tc.Val == math.MaxUint64 {\n\t\t\texpected = \"\\x20\" // this is special case for uint max value when using AppendUint\n\t\t}\n\t\twant = append(want, expected...)\n\t}\n\n\tgot := enc.AppendUints([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendUints(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now empty array case\n\tarray = make([]uint, 0)\n\twant = make([]byte, 0)\n\twant = append(want, 0x9f) // start and end array\n\twant = append(want, 0xff) // for empty array\n\tgot = enc.AppendUints([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendUints(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now a small array case\n\tarray = make([]uint, 21)\n\twant = make([]byte, 0)\n\twant = append(want, 0x95) // start a smaller array\n\tfor i := 0; i < 21; i++ {\n\t\tarray[i] = uint(i)\n\t\twant = append(want, byte(i))\n\t}\n\tgot = enc.AppendUints([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendUints(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n}\n\nfunc TestAppendIntArray(t *testing.T) {\n\tfor _, tc := range internal.IntegerArrayTestCases {\n\t\ts := enc.AppendInts([]byte{}, tc.Val)\n\t\tgot := string(s)\n\t\tif got != tc.Binary {\n\t\t\tt.Errorf(\"AppendInts(%s)=0x%s, want: 0x%s\",\n\t\t\t\ttc.Json, hex.EncodeToString(s),\n\t\t\t\thex.EncodeToString([]byte(tc.Binary)))\n\t\t}\n\t}\n}\n\nfunc TestAppendFloat32(t *testing.T) {\n\tfor _, tc := range internal.Float32TestCases {\n\t\ts := enc.AppendFloat32([]byte{}, tc.Val, -1)\n\t\tgot := string(s)\n\t\twant := tc.Binary\n\t\tif got != want {\n\t\t\tt.Errorf(\"AppendFloat32(0x%x)=0x%s, want: 0x%s\",\n\t\t\t\ttc.Val, hex.EncodeToString(s),\n\t\t\t\thex.EncodeToString([]byte(want)))\n\t\t}\n\t}\n}\n\nfunc TestAppendFloats32(t *testing.T) {\n\tarray := []float32{1.0, 1.5}\n\twant := make([]byte, 0)\n\twant = append(want, 0x82)                         // start array for float elements\n\twant = append(want, 0xfa, 0x3f, 0x80, 0x00, 0x00) // 32 bit 1.0\n\twant = append(want, 0xfa, 0x3f, 0xc0, 0x00, 0x00) // 32 bit 1.5\n\n\tgot := enc.AppendFloats32([]byte{}, array, -1)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendFloats32(%v)=0x%s, want: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now empty array case\n\tarray = []float32{}\n\twant = make([]byte, 0)\n\twant = append(want, 0x9f, 0xff) // start and end array\n\tgot = enc.AppendFloats32([]byte{}, array, -1)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendFloats32(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now a large array case\n\tarray = make([]float32, 24)\n\twant = make([]byte, 0)\n\twant = append(want, 0x98) // start a larger array\n\twant = append(want, 0x18) // for 24 elements\n\tfor i := 0; i < 24; i++ {\n\t\twant = append(want, 0xfa, 0x00, 0x00, 0x00, 0x00)\n\t}\n\tgot = enc.AppendFloats32([]byte{}, array, -1)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendFloats32(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n}\n\nfunc TestAppendFloat64(t *testing.T) {\n\tfor _, tc := range internal.Float64TestCases {\n\t\ts := enc.AppendFloat64([]byte{}, tc.Val, -1)\n\t\tgot := string(s)\n\t\tif got != tc.Binary && ((got == \"NaN\") != math.IsNaN(tc.Val)) {\n\t\t\tt.Errorf(\"AppendFloat64(%f)=0x%s, want: 0x%s\",\n\t\t\t\ttc.Val, hex.EncodeToString(s),\n\t\t\t\thex.EncodeToString([]byte(tc.Binary)))\n\t\t}\n\t}\n}\n\nfunc TestAppendFloats64(t *testing.T) {\n\tarray := []float64{1.0, 1.5}\n\twant := make([]byte, 0)\n\twant = append(want, 0x82)                                                 // start array for float elements\n\twant = append(want, 0xfb, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) // 64 bit 1.0\n\twant = append(want, 0xfb, 0x3f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) // 64 bit 1.5\n\n\tgot := enc.AppendFloats64([]byte{}, array, -1)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendFloats64(%v)=0x%s, want: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now empty array case\n\tarray = []float64{}\n\twant = make([]byte, 0)\n\twant = append(want, 0x9f, 0xff) // start and end array\n\tgot = enc.AppendFloats64([]byte{}, array, -1)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendFloats64(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now a large array case\n\tarray = make([]float64, 24)\n\twant = make([]byte, 0)\n\twant = append(want, 0x98) // start a larger array\n\twant = append(want, 0x18) // for 24 elements\n\tfor i := 0; i < 24; i++ {\n\t\twant = append(want, 0xfb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)\n\t}\n\tgot = enc.AppendFloats64([]byte{}, array, -1)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendFloats64(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n}\n\nfunc TestAppendNetworkAddr(t *testing.T) {\n\tfor _, tc := range internal.IpAddrTestCases {\n\t\ts := enc.AppendIPAddr([]byte{}, tc.Ipaddr)\n\t\tgot := string(s)\n\t\tif got != tc.Binary {\n\t\t\tt.Errorf(\"AppendIPAddr(%s)=0x%s, want: 0x%s\",\n\t\t\t\ttc.Ipaddr, hex.EncodeToString(s),\n\t\t\t\thex.EncodeToString([]byte(tc.Binary)))\n\t\t}\n\t}\n}\n\nfunc TestAppendIPAddrArray(t *testing.T) {\n\tfor _, tc := range internal.IPAddrArrayTestCases {\n\t\ts := enc.AppendIPAddrs([]byte{}, tc.Val)\n\t\tgot := string(s)\n\t\tif got != tc.Binary {\n\t\t\tt.Errorf(\"AppendIPAddr(%s)=0x%s, want: 0x%s\",\n\t\t\t\ttc.Json, hex.EncodeToString(s),\n\t\t\t\thex.EncodeToString([]byte(tc.Binary)))\n\t\t}\n\t}\n\n\t// now a large array case\n\tarray := make([]net.IP, 24)\n\twant := make([]byte, 0)\n\twant = append(want, 0x98) // start a larger array\n\twant = append(want, 0x18) // for 24 elements\n\tfor i := 0; i < 24; i++ {\n\t\tarray[i] = net.IP{0, 0, 0, byte(i)}\n\t\twant = append(want, 0xd9, 0x01, 0x04, 0x44, 0x00, 0x00, 0x00, byte(i))\n\t}\n\tgot := enc.AppendIPAddrs([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendIPAddrs(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n}\n\nfunc TestAppendMACAddr(t *testing.T) {\n\tfor _, tc := range internal.MacAddrTestCases {\n\t\ts := enc.AppendMACAddr([]byte{}, tc.Macaddr)\n\t\tgot := string(s)\n\t\tif got != tc.Binary {\n\t\t\tt.Errorf(\"AppendMACAddr(%s)=0x%s, want: 0x%s\",\n\t\t\t\ttc.Macaddr.String(), hex.EncodeToString(s),\n\t\t\t\thex.EncodeToString([]byte(tc.Binary)))\n\t\t}\n\t}\n}\n\nfunc TestAppendIPPrefix(t *testing.T) {\n\tfor _, tc := range internal.IPPrefixTestCases {\n\t\ts := enc.AppendIPPrefix([]byte{}, tc.Pfx)\n\t\tgot := string(s)\n\t\tif got != tc.Binary {\n\t\t\tt.Errorf(\"AppendIPPrefix(%s)=0x%s, want: 0x%s\",\n\t\t\t\ttc.Pfx.String(), hex.EncodeToString(s),\n\t\t\t\thex.EncodeToString([]byte(tc.Binary)))\n\t\t}\n\t}\n}\n\nfunc TestAppendIPPrefixArray(t *testing.T) {\n\tfor _, tc := range internal.IPPrefixArrayTestCases {\n\t\ts := enc.AppendIPPrefixes([]byte{}, tc.Val)\n\t\tgot := string(s)\n\t\tif got != tc.Binary {\n\t\t\tt.Errorf(\"AppendIPPrefix(%s)=0x%s, want: 0x%s\",\n\t\t\t\ttc.Json, hex.EncodeToString(s),\n\t\t\t\thex.EncodeToString([]byte(tc.Binary)))\n\t\t}\n\t}\n\n\t// now a large array case\n\tarray := make([]net.IPNet, 24)\n\twant := make([]byte, 0)\n\twant = append(want, 0x98) // start a larger array\n\twant = append(want, 0x18) // for 24 elements\n\tfor i := 0; i < 24; i++ {\n\t\tarray[i] = net.IPNet{IP: net.IP{0, 0, 0, byte(i)}, Mask: net.CIDRMask(24, 32)}\n\t\twant = append(want, 0xd9, 0x01, 0x05, 0xa1, 0x44, 0x00, 0x00, 0x00, byte(i), 0x18, 0x18)\n\t}\n\tgot := enc.AppendIPPrefixes([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendIPPrefixes(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n}\n\nfunc TestAppendHex(t *testing.T) {\n\tarray := []byte{0x01, 0x02}\n\twant := make([]byte, 0)\n\twant = append(want, 0xd9, 0x01) // start array\n\twant = append(want, 0x07, 0x42) // array of two elements\n\twant = append(want, 0x01, 0x02) // 0x01, 0x02\n\tgot := enc.AppendHex([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendHex(%v)=0x%s, want: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now empty array case\n\tarray = make([]byte, 0)\n\twant = make([]byte, 0)\n\twant = append(want, 0xd9, 0x01) // start an array\n\twant = append(want, 0x07, 0x40) // array of zero elements\n\tgot = enc.AppendHex([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendHex(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray, hex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n}\n\nfunc BenchmarkAppendInt(b *testing.B) {\n\ttype st struct {\n\t\tsz  byte\n\t\tval int64\n\t}\n\ttests := map[string]st{\n\t\t\"int-Positive\": {sz: 0, val: 10000},\n\t\t\"int-Negative\": {sz: 0, val: -10000},\n\t\t\"uint8\":        {sz: 1, val: 100},\n\t\t\"uint16\":       {sz: 2, val: 0xfff},\n\t\t\"uint32\":       {sz: 4, val: 0xffffff},\n\t\t\"uint64\":       {sz: 8, val: 0xffffffffff},\n\t\t\"int8\":         {sz: 21, val: -120},\n\t\t\"int16\":        {sz: 22, val: -1200},\n\t\t\"int32\":        {sz: 23, val: 32000},\n\t\t\"int64\":        {sz: 24, val: 0xffffffffff},\n\t}\n\tfor name, str := range tests {\n\t\tb.Run(name, func(b *testing.B) {\n\t\t\tbuf := make([]byte, 0, 100)\n\t\t\tfor i := 0; i < b.N; i++ {\n\t\t\t\tswitch str.sz {\n\t\t\t\tcase 0:\n\t\t\t\t\t_ = enc.AppendInt(buf, int(str.val))\n\t\t\t\tcase 1:\n\t\t\t\t\t_ = enc.AppendUint8(buf, uint8(str.val))\n\t\t\t\tcase 2:\n\t\t\t\t\t_ = enc.AppendUint16(buf, uint16(str.val))\n\t\t\t\tcase 4:\n\t\t\t\t\t_ = enc.AppendUint32(buf, uint32(str.val))\n\t\t\t\tcase 8:\n\t\t\t\t\t_ = enc.AppendUint64(buf, uint64(str.val))\n\t\t\t\tcase 21:\n\t\t\t\t\t_ = enc.AppendInt8(buf, int8(str.val))\n\t\t\t\tcase 22:\n\t\t\t\t\t_ = enc.AppendInt16(buf, int16(str.val))\n\t\t\t\tcase 23:\n\t\t\t\t\t_ = enc.AppendInt32(buf, int32(str.val))\n\t\t\t\tcase 24:\n\t\t\t\t\t_ = enc.AppendInt64(buf, int64(str.val))\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkAppendFloat(b *testing.B) {\n\ttype st struct {\n\t\tsz  byte\n\t\tval float64\n\t}\n\ttests := map[string]st{\n\t\t\"Float32\": {sz: 4, val: 10000.12345},\n\t\t\"Float64\": {sz: 8, val: -10000.54321},\n\t}\n\tfor name, str := range tests {\n\t\tb.Run(name, func(b *testing.B) {\n\t\t\tbuf := make([]byte, 0, 100)\n\t\t\tfor i := 0; i < b.N; i++ {\n\t\t\t\tswitch str.sz {\n\t\t\t\tcase 4:\n\t\t\t\t\t_ = enc.AppendFloat32(buf, float32(str.val), -1)\n\t\t\t\tcase 8:\n\t\t\t\t\t_ = enc.AppendFloat64(buf, str.val, -1)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/json/base.go",
    "content": "package json\n\n// JSONMarshalFunc is used to marshal interface to JSON encoded byte slice.\n// Making it package level instead of embedded in Encoder brings\n// some extra efforts at importing, but avoids value copy when the functions\n// of Encoder being invoked.\n// DO REMEMBER to set this variable at importing, or\n// you might get a nil pointer dereference panic at runtime.\nvar JSONMarshalFunc func(v interface{}) ([]byte, error)\n\ntype Encoder struct{}\n\n// AppendKey appends a new key to the output JSON.\nfunc (e Encoder) AppendKey(dst []byte, key string) []byte {\n\tif dst[len(dst)-1] != '{' {\n\t\tdst = append(dst, ',')\n\t}\n\treturn append(e.AppendString(dst, key), ':')\n}\n"
  },
  {
    "path": "internal/json/base_test.go",
    "content": "package json\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n)\n\nfunc TestAppendKey(t *testing.T) {\n\twant := make([]byte, 0)\n\twant = append(want, []byte(\"{\\\"key\\\":\")...)\n\n\tgot := enc.AppendKey([]byte(\"{\"), \"key\") // test with empty object\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendKey(%v)\\ngot:  %s\\nwant: %s\",\n\t\t\t\"key\",\n\t\t\tstring(got),\n\t\t\tstring(want))\n\t}\n\n\twant = make([]byte, 0)\n\twant = append(want, []byte(\"},\\\"key\\\":\")...) // test with non-empty object\n\n\tgot = enc.AppendKey([]byte(\"}\"), \"key\")\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendKey(%v)\\ngot:  %s\\nwant: %s\",\n\t\t\t\"key\",\n\t\t\tstring(got),\n\t\t\tstring(want))\n\t}\n}\n"
  },
  {
    "path": "internal/json/bytes.go",
    "content": "package json\n\nimport \"unicode/utf8\"\n\n// AppendBytes is a mirror of appendString with []byte arg\nfunc (Encoder) AppendBytes(dst, s []byte) []byte {\n\tdst = append(dst, '\"')\n\tfor i := 0; i < len(s); i++ {\n\t\tif !noEscapeTable[s[i]] {\n\t\t\tdst = appendBytesComplex(dst, s, i)\n\t\t\treturn append(dst, '\"')\n\t\t}\n\t}\n\tdst = append(dst, s...)\n\treturn append(dst, '\"')\n}\n\n// AppendHex encodes the input bytes to a hex string and appends\n// the encoded string to the input byte slice.\n//\n// The operation loops though each byte and encodes it as hex using\n// the hex lookup table.\nfunc (Encoder) AppendHex(dst, s []byte) []byte {\n\tdst = append(dst, '\"')\n\tfor _, v := range s {\n\t\tdst = append(dst, hexCharacters[v>>4], hexCharacters[v&0x0f])\n\t}\n\treturn append(dst, '\"')\n}\n\n// appendBytesComplex is a mirror of the appendStringComplex\n// with []byte arg\nfunc appendBytesComplex(dst, s []byte, i int) []byte {\n\tstart := 0\n\tfor i < len(s) {\n\t\tb := s[i]\n\t\tif b >= utf8.RuneSelf {\n\t\t\tr, size := utf8.DecodeRune(s[i:])\n\t\t\tif r == utf8.RuneError && size == 1 {\n\t\t\t\tif start < i {\n\t\t\t\t\tdst = append(dst, s[start:i]...)\n\t\t\t\t}\n\t\t\t\tdst = append(dst, `\\ufffd`...)\n\t\t\t\ti += size\n\t\t\t\tstart = i\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ti += size\n\t\t\tcontinue\n\t\t}\n\t\tif noEscapeTable[b] {\n\t\t\ti++\n\t\t\tcontinue\n\t\t}\n\t\t// We encountered a character that needs to be encoded.\n\t\t// Let's append the previous simple characters to the byte slice\n\t\t// and switch our operation to read and encode the remainder\n\t\t// characters byte-by-byte.\n\t\tif start < i {\n\t\t\tdst = append(dst, s[start:i]...)\n\t\t}\n\t\tswitch b {\n\t\tcase '\"', '\\\\':\n\t\t\tdst = append(dst, '\\\\', b)\n\t\tcase '\\b':\n\t\t\tdst = append(dst, '\\\\', 'b')\n\t\tcase '\\f':\n\t\t\tdst = append(dst, '\\\\', 'f')\n\t\tcase '\\n':\n\t\t\tdst = append(dst, '\\\\', 'n')\n\t\tcase '\\r':\n\t\t\tdst = append(dst, '\\\\', 'r')\n\t\tcase '\\t':\n\t\t\tdst = append(dst, '\\\\', 't')\n\t\tdefault:\n\t\t\tdst = append(dst, '\\\\', 'u', '0', '0', hexCharacters[b>>4], hexCharacters[b&0xF])\n\t\t}\n\t\ti++\n\t\tstart = i\n\t}\n\tif start < len(s) {\n\t\tdst = append(dst, s[start:]...)\n\t}\n\treturn dst\n}\n"
  },
  {
    "path": "internal/json/bytes_test.go",
    "content": "package json\n\nimport (\n\t\"testing\"\n\t\"unicode\"\n\n\t\"github.com/rs/zerolog/internal\"\n)\n\nvar enc = Encoder{}\n\nfunc TestAppendBytes(t *testing.T) {\n\tfor _, tt := range internal.EncodeStringTests {\n\t\tb := enc.AppendBytes([]byte{}, []byte(tt.In))\n\t\tif got, want := string(b), tt.Out; got != want {\n\t\t\tt.Errorf(\"appendBytes(%q) = %#q, want %#q\", tt.In, got, want)\n\t\t}\n\t}\n}\n\nfunc TestAppendHex(t *testing.T) {\n\tfor _, tt := range internal.EncodeHexTests {\n\t\tb := enc.AppendHex([]byte{}, []byte{tt.In})\n\t\tif got, want := string(b), tt.Out; got != want {\n\t\t\tt.Errorf(\"appendHex(%x) = %s, want %s\", tt.In, got, want)\n\t\t}\n\t}\n}\n\nfunc TestStringBytes(t *testing.T) {\n\tt.Parallel()\n\t// Test that encodeState.stringBytes and encodeState.string use the same encoding.\n\tvar r []rune\n\tfor i := '\\u0000'; i <= unicode.MaxRune; i++ {\n\t\tr = append(r, i)\n\t}\n\ts := string(r) + \"\\xff\\xff\\xffhello\" // some invalid UTF-8 too\n\n\tencStr := string(enc.AppendString([]byte{}, s))\n\tencBytes := string(enc.AppendBytes([]byte{}, []byte(s)))\n\n\tif encStr != encBytes {\n\t\ti := 0\n\t\tfor i < len(encStr) && i < len(encBytes) && encStr[i] == encBytes[i] {\n\t\t\ti++\n\t\t}\n\t\tencStr = encStr[i:]\n\t\tencBytes = encBytes[i:]\n\t\ti = 0\n\t\tfor i < len(encStr) && i < len(encBytes) && encStr[len(encStr)-i-1] == encBytes[len(encBytes)-i-1] {\n\t\t\ti++\n\t\t}\n\t\tencStr = encStr[:len(encStr)-i]\n\t\tencBytes = encBytes[:len(encBytes)-i]\n\n\t\tif len(encStr) > 20 {\n\t\t\tencStr = encStr[:20] + \"...\"\n\t\t}\n\t\tif len(encBytes) > 20 {\n\t\t\tencBytes = encBytes[:20] + \"...\"\n\t\t}\n\n\t\tt.Errorf(\"encodings differ at %#q vs %#q\", encStr, encBytes)\n\t}\n}\n\nfunc BenchmarkAppendBytes(b *testing.B) {\n\ttests := map[string]string{\n\t\t\"NoEncoding\":       `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`,\n\t\t\"EncodingFirst\":    `\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`,\n\t\t\"EncodingMiddle\":   `aaaaaaaaaaaaaaaaaaaaaaaaa\"aaaaaaaaaaaaaaaaaaaaaaaa`,\n\t\t\"EncodingLast\":     `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"`,\n\t\t\"MultiBytesFirst\":  `❤️aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`,\n\t\t\"MultiBytesMiddle\": `aaaaaaaaaaaaaaaaaaaaaaaaa❤️aaaaaaaaaaaaaaaaaaaaaaaa`,\n\t\t\"MultiBytesLast\":   `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa❤️`,\n\t}\n\tfor name, str := range tests {\n\t\tbyt := []byte(str)\n\t\tb.Run(name, func(b *testing.B) {\n\t\t\tbuf := make([]byte, 0, 100)\n\t\t\tfor i := 0; i < b.N; i++ {\n\t\t\t\t_ = enc.AppendBytes(buf, byt)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/json/float_test.go",
    "content": "package json\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"math\"\n\t\"math/rand\"\n\t\"testing\"\n\n\t\"github.com/rs/zerolog/internal\"\n)\n\nvar float64Tests = []struct {\n\tName string\n\tVal  float64\n\tWant string\n}{\n\t{\n\t\tName: \"Positive integer\",\n\t\tVal:  1234.0,\n\t\tWant: \"1234\",\n\t},\n\t{\n\t\tName: \"Negative integer\",\n\t\tVal:  -5678.0,\n\t\tWant: \"-5678\",\n\t},\n\t{\n\t\tName: \"Positive decimal\",\n\t\tVal:  12.3456,\n\t\tWant: \"12.3456\",\n\t},\n\t{\n\t\tName: \"Negative decimal\",\n\t\tVal:  -78.9012,\n\t\tWant: \"-78.9012\",\n\t},\n\t{\n\t\tName: \"Large positive number\",\n\t\tVal:  123456789.0,\n\t\tWant: \"123456789\",\n\t},\n\t{\n\t\tName: \"Large negative number\",\n\t\tVal:  -987654321.0,\n\t\tWant: \"-987654321\",\n\t},\n\t{\n\t\tName: \"Zero\",\n\t\tVal:  0.0,\n\t\tWant: \"0\",\n\t},\n\t{\n\t\tName: \"Smallest positive value\",\n\t\tVal:  math.SmallestNonzeroFloat64,\n\t\tWant: \"5e-324\",\n\t},\n\t{\n\t\tName: \"Largest positive value\",\n\t\tVal:  math.MaxFloat64,\n\t\tWant: \"1.7976931348623157e+308\",\n\t},\n\t{\n\t\tName: \"Smallest negative value\",\n\t\tVal:  -math.SmallestNonzeroFloat64,\n\t\tWant: \"-5e-324\",\n\t},\n\t{\n\t\tName: \"Largest negative value\",\n\t\tVal:  -math.MaxFloat64,\n\t\tWant: \"-1.7976931348623157e+308\",\n\t},\n\t{\n\t\tName: \"NaN\",\n\t\tVal:  math.NaN(),\n\t\tWant: `\"NaN\"`,\n\t},\n\t{\n\t\tName: \"+Inf\",\n\t\tVal:  math.Inf(1),\n\t\tWant: `\"+Inf\"`,\n\t},\n\t{\n\t\tName: \"-Inf\",\n\t\tVal:  math.Inf(-1),\n\t\tWant: `\"-Inf\"`,\n\t},\n\t{\n\t\tName: \"Clean up e-09 to e-9 case 1\",\n\t\tVal:  1e-9,\n\t\tWant: \"1e-9\",\n\t},\n\t{\n\t\tName: \"Clean up e-09 to e-9 case 2\",\n\t\tVal:  -2.236734e-9,\n\t\tWant: \"-2.236734e-9\",\n\t},\n}\n\nfunc TestEncoder_AppendFloat64(t *testing.T) {\n\tfor _, tc := range float64Tests {\n\t\tt.Run(tc.Name, func(t *testing.T) {\n\t\t\tvar b []byte\n\t\t\tb = (Encoder{}).AppendFloat64(b, tc.Val, -1)\n\t\t\tif s := string(b); tc.Want != s {\n\t\t\t\tt.Errorf(\"%q\", s)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc FuzzEncoder_AppendFloat64(f *testing.F) {\n\tfor _, tc := range float64Tests {\n\t\tf.Add(tc.Val)\n\t}\n\tf.Fuzz(func(t *testing.T, val float64) {\n\t\tactual := (Encoder{}).AppendFloat64(nil, val, -1)\n\t\tif len(actual) == 0 {\n\t\t\tt.Fatal(\"empty buffer\")\n\t\t}\n\n\t\tif actual[0] == '\"' {\n\t\t\tswitch string(actual) {\n\t\t\tcase `\"NaN\"`:\n\t\t\t\tif !math.IsNaN(val) {\n\t\t\t\t\tt.Fatalf(\"expected %v got NaN\", val)\n\t\t\t\t}\n\t\t\tcase `\"+Inf\"`:\n\t\t\t\tif !math.IsInf(val, 1) {\n\t\t\t\t\tt.Fatalf(\"expected %v got +Inf\", val)\n\t\t\t\t}\n\t\t\tcase `\"-Inf\"`:\n\t\t\t\tif !math.IsInf(val, -1) {\n\t\t\t\t\tt.Fatalf(\"expected %v got -Inf\", val)\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\tt.Fatalf(\"unexpected string: %s\", actual)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\tif expected, err := json.Marshal(val); err != nil {\n\t\t\tt.Error(err)\n\t\t} else if string(actual) != string(expected) {\n\t\t\tt.Errorf(\"expected %s, got %s\", expected, actual)\n\t\t}\n\n\t\tvar parsed float64\n\t\tif err := json.Unmarshal(actual, &parsed); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tif parsed != val {\n\t\t\tt.Fatalf(\"expected %v, got %v\", val, parsed)\n\t\t}\n\t})\n}\n\nvar float32Tests = []struct {\n\tName string\n\tVal  float32\n\tWant string\n}{\n\t{\n\t\tName: \"Positive integer\",\n\t\tVal:  1234.0,\n\t\tWant: \"1234\",\n\t},\n\t{\n\t\tName: \"Negative integer\",\n\t\tVal:  -5678.0,\n\t\tWant: \"-5678\",\n\t},\n\t{\n\t\tName: \"Positive decimal\",\n\t\tVal:  12.3456,\n\t\tWant: \"12.3456\",\n\t},\n\t{\n\t\tName: \"Negative decimal\",\n\t\tVal:  -78.9012,\n\t\tWant: \"-78.9012\",\n\t},\n\t{\n\t\tName: \"Large positive number\",\n\t\tVal:  123456789.0,\n\t\tWant: \"123456790\",\n\t},\n\t{\n\t\tName: \"Large negative number\",\n\t\tVal:  -987654321.0,\n\t\tWant: \"-987654340\",\n\t},\n\t{\n\t\tName: \"Zero\",\n\t\tVal:  0.0,\n\t\tWant: \"0\",\n\t},\n\t{\n\t\tName: \"Smallest positive value\",\n\t\tVal:  math.SmallestNonzeroFloat32,\n\t\tWant: \"1e-45\",\n\t},\n\t{\n\t\tName: \"Largest positive value\",\n\t\tVal:  math.MaxFloat32,\n\t\tWant: \"3.4028235e+38\",\n\t},\n\t{\n\t\tName: \"Smallest negative value\",\n\t\tVal:  -math.SmallestNonzeroFloat32,\n\t\tWant: \"-1e-45\",\n\t},\n\t{\n\t\tName: \"Largest negative value\",\n\t\tVal:  -math.MaxFloat32,\n\t\tWant: \"-3.4028235e+38\",\n\t},\n\t{\n\t\tName: \"NaN\",\n\t\tVal:  float32(math.NaN()),\n\t\tWant: `\"NaN\"`,\n\t},\n\t{\n\t\tName: \"+Inf\",\n\t\tVal:  float32(math.Inf(1)),\n\t\tWant: `\"+Inf\"`,\n\t},\n\t{\n\t\tName: \"-Inf\",\n\t\tVal:  float32(math.Inf(-1)),\n\t\tWant: `\"-Inf\"`,\n\t},\n\t{\n\t\tName: \"Clean up e-09 to e-9 case 1\",\n\t\tVal:  1e-9,\n\t\tWant: \"1e-9\",\n\t},\n\t{\n\t\tName: \"Clean up e-09 to e-9 case 2\",\n\t\tVal:  -2.236734e-9,\n\t\tWant: \"-2.236734e-9\",\n\t},\n}\n\nfunc TestEncoder_AppendFloat32(t *testing.T) {\n\tfor _, tc := range float32Tests {\n\t\tt.Run(tc.Name, func(t *testing.T) {\n\t\t\tvar b []byte\n\t\t\tb = (Encoder{}).AppendFloat32(b, tc.Val, -1)\n\t\t\tif s := string(b); tc.Want != s {\n\t\t\t\tt.Errorf(\"%q\", s)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc FuzzEncoder_AppendFloat32(f *testing.F) {\n\tfor _, tc := range float32Tests {\n\t\tf.Add(tc.Val)\n\t}\n\tf.Fuzz(func(t *testing.T, val float32) {\n\t\tactual := (Encoder{}).AppendFloat32(nil, val, -1)\n\t\tif len(actual) == 0 {\n\t\t\tt.Fatal(\"empty buffer\")\n\t\t}\n\n\t\tif actual[0] == '\"' {\n\t\t\tval := float64(val)\n\t\t\tswitch string(actual) {\n\t\t\tcase `\"NaN\"`:\n\t\t\t\tif !math.IsNaN(val) {\n\t\t\t\t\tt.Fatalf(\"expected %v got NaN\", val)\n\t\t\t\t}\n\t\t\tcase `\"+Inf\"`:\n\t\t\t\tif !math.IsInf(val, 1) {\n\t\t\t\t\tt.Fatalf(\"expected %v got +Inf\", val)\n\t\t\t\t}\n\t\t\tcase `\"-Inf\"`:\n\t\t\t\tif !math.IsInf(val, -1) {\n\t\t\t\t\tt.Fatalf(\"expected %v got -Inf\", val)\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\tt.Fatalf(\"unexpected string: %s\", actual)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\tif expected, err := json.Marshal(val); err != nil {\n\t\t\tt.Error(err)\n\t\t} else if string(actual) != string(expected) {\n\t\t\tt.Errorf(\"expected %s, got %s\", expected, actual)\n\t\t}\n\n\t\tvar parsed float32\n\t\tif err := json.Unmarshal(actual, &parsed); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tif parsed != val {\n\t\t\tt.Fatalf(\"expected %v, got %v\", val, parsed)\n\t\t}\n\t})\n}\n\nfunc generateFloat32s(n int) []float32 {\n\tfloats := make([]float32, n)\n\tfor i := 0; i < n; i++ {\n\t\tfloats[i] = rand.Float32()\n\t}\n\treturn floats\n}\n\nfunc generateFloat64s(n int) []float64 {\n\tfloats := make([]float64, n)\n\tfor i := 0; i < n; i++ {\n\t\tfloats[i] = rand.Float64()\n\t}\n\treturn floats\n}\n\nfunc TestAppendFloats32(t *testing.T) {\n\tdoOne := func(vals []float32) {\n\t\twant := make([]byte, 0)\n\t\twant = append(want, '[')\n\t\tfor i, val := range vals {\n\t\t\tif math.IsNaN(float64(val)) {\n\t\t\t\twant = append(want, []byte(`\"NaN\"`)...)\n\t\t\t} else if math.IsInf(float64(val), 1) {\n\t\t\t\twant = append(want, []byte(`\"+Inf\"`)...)\n\t\t\t} else if math.IsInf(float64(val), -1) {\n\t\t\t\twant = append(want, []byte(`\"-Inf\"`)...)\n\t\t\t} else {\n\t\t\t\twant = append(want, []byte(fmt.Sprintf(\"%v\", float32(val)))...)\n\t\t\t}\n\t\t\tif i < len(vals)-1 {\n\t\t\t\twant = append(want, ',')\n\t\t\t}\n\t\t}\n\t\twant = append(want, ']')\n\n\t\tgot := enc.AppendFloats32([]byte{}, vals, -1)\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"AppendFloats32(%v)\\ngot:  %s\\nwant: %s\",\n\t\t\t\tvals,\n\t\t\t\tstring(got),\n\t\t\t\tstring(want))\n\t\t}\n\t}\n\n\tarray := make([]float32, 0)\n\tfor _, tc := range internal.Float32TestCases {\n\t\tif tc.Val > 0 && tc.Val < 1e-4 {\n\t\t\tcontinue // we want to ignore very small numbers for this test\n\t\t}\n\t\tarray = append(array, float32(tc.Val))\n\t}\n\n\tdoOne(array)\n\tdoOne(array[:1]) // single element\n\tdoOne(array[:0]) // edge case of zero length\n}\n\nfunc TestAppendFloats64(t *testing.T) {\n\tdoOne := func(vals []float64) {\n\t\twant := make([]byte, 0)\n\t\twant = append(want, '[')\n\t\tfor i, val := range vals {\n\t\t\tif math.IsNaN(val) {\n\t\t\t\twant = append(want, []byte(`\"NaN\"`)...)\n\t\t\t} else if math.IsInf(val, 1) {\n\t\t\t\twant = append(want, []byte(`\"+Inf\"`)...)\n\t\t\t} else if math.IsInf(val, -1) {\n\t\t\t\twant = append(want, []byte(`\"-Inf\"`)...)\n\t\t\t} else {\n\t\t\t\twant = append(want, []byte(fmt.Sprintf(\"%v\", val))...)\n\t\t\t}\n\t\t\tif i < len(vals)-1 {\n\t\t\t\twant = append(want, ',')\n\t\t\t}\n\t\t}\n\t\twant = append(want, ']')\n\n\t\tgot := enc.AppendFloats64([]byte{}, vals, -1)\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"AppendFloats64(%v)\\ngot:  %s\\nwant: %s\",\n\t\t\t\tvals,\n\t\t\t\tstring(got),\n\t\t\t\tstring(want))\n\t\t}\n\t}\n\n\tarray := make([]float64, 0)\n\tfor _, tc := range internal.Float64TestCases {\n\t\tif tc.Val > 0 && tc.Val < 1e-4 {\n\t\t\tcontinue // we want to ignore very small numbers for this test\n\t\t}\n\t\tarray = append(array, tc.Val)\n\t}\n\n\tdoOne(array)\n\tdoOne(array[:1]) // single element\n\tdoOne(array[:0]) // edge case of zero length\n}\n\n// this is really just for the memory allocation characteristics\nfunc BenchmarkEncoder_AppendFloat32(b *testing.B) {\n\tfloats := append(generateFloat32s(5000), float32(math.NaN()), float32(math.Inf(1)), float32(math.Inf(-1)))\n\tdst := make([]byte, 0, 128)\n\n\tb.ResetTimer()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tfor _, f := range floats {\n\t\t\tdst = (Encoder{}).AppendFloat32(dst[:0], f, -1)\n\t\t}\n\t}\n}\n\n// this is really just for the memory allocation characteristics\nfunc BenchmarkEncoder_AppendFloat64(b *testing.B) {\n\tfloats := append(generateFloat64s(5000), math.NaN(), math.Inf(1), math.Inf(-1))\n\tdst := make([]byte, 0, 128)\n\n\tb.ResetTimer()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tfor _, f := range floats {\n\t\t\tdst = (Encoder{}).AppendFloat64(dst[:0], f, -1)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "internal/json/int_test.go",
    "content": "package json\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"math\"\n\t\"testing\"\n\n\t\"github.com/rs/zerolog/internal\"\n)\n\nfunc TestAppendInts8(t *testing.T) {\n\tdoOne := func(vals []int8) {\n\t\twant := make([]byte, 0)\n\t\twant = append(want, '[')\n\t\tfor i, val := range vals {\n\t\t\twant = append(want, []byte(fmt.Sprintf(\"%d\", int8(val)))...)\n\t\t\tif i < len(vals)-1 {\n\t\t\t\twant = append(want, ',')\n\t\t\t}\n\t\t}\n\t\twant = append(want, ']')\n\n\t\tgot := enc.AppendInts8([]byte{}, vals)\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"AppendInts8(%v)\\ngot:  %s\\nwant: %s\",\n\t\t\t\tvals,\n\t\t\t\tstring(got),\n\t\t\t\tstring(want))\n\t\t}\n\t}\n\n\tarray := make([]int8, 0)\n\tfor _, tc := range internal.IntegerTestCases {\n\t\tif (tc.Val < math.MinInt8) || (tc.Val > math.MaxInt8) {\n\t\t\tcontinue\n\t\t}\n\t\tarray = append(array, int8(tc.Val))\n\t}\n\n\tdoOne(array)\n\tdoOne(array[:1]) // single element\n\tdoOne(array[:0]) // edge case of zero length\n}\nfunc TestAppendUints8(t *testing.T) {\n\tdoOne := func(vals []uint8) {\n\t\twant := make([]byte, 0)\n\t\twant = append(want, '[')\n\t\tfor i, val := range vals {\n\t\t\twant = append(want, []byte(fmt.Sprintf(\"%v\", uint8(val)))...)\n\t\t\tif i < len(vals)-1 {\n\t\t\t\twant = append(want, ',')\n\t\t\t}\n\t\t}\n\t\twant = append(want, ']')\n\n\t\tgot := enc.AppendUints8([]byte{}, vals)\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"AppendUints8(%v)\\ngot:  %s\\nwant: %s\",\n\t\t\t\tvals,\n\t\t\t\tstring(got),\n\t\t\t\tstring(want))\n\t\t}\n\t}\n\n\tarray := make([]uint8, 0)\n\tfor _, tc := range internal.UnsignedIntegerTestCases {\n\t\tif tc.Val > math.MaxUint8 {\n\t\t\tcontinue\n\t\t}\n\t\tarray = append(array, uint8(tc.Val))\n\t}\n\n\tdoOne(array)\n\tdoOne(array[:1]) // single element\n\tdoOne(array[:0]) // edge case of zero length\n}\nfunc TestAppendInts16(t *testing.T) {\n\tdoOne := func(vals []int16) {\n\t\twant := make([]byte, 0)\n\t\twant = append(want, '[')\n\t\tfor i, val := range vals {\n\t\t\twant = append(want, []byte(fmt.Sprintf(\"%d\", int16(val)))...)\n\t\t\tif i < len(vals)-1 {\n\t\t\t\twant = append(want, ',')\n\t\t\t}\n\t\t}\n\t\twant = append(want, ']')\n\n\t\tgot := enc.AppendInts16([]byte{}, vals)\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"AppendInts16(%v)\\ngot:  %s\\nwant: %s\",\n\t\t\t\tvals,\n\t\t\t\tstring(got),\n\t\t\t\tstring(want))\n\t\t}\n\t}\n\n\tarray := make([]int16, 0)\n\tfor _, tc := range internal.IntegerTestCases {\n\t\tif (tc.Val < math.MinInt16) || (tc.Val > math.MaxInt16) {\n\t\t\tcontinue\n\t\t}\n\t\tarray = append(array, int16(tc.Val))\n\t}\n\n\tdoOne(array)\n\tdoOne(array[:1]) // single element\n\tdoOne(array[:0]) // edge case of zero length\n}\nfunc TestAppendUints16(t *testing.T) {\n\tdoOne := func(vals []uint16) {\n\t\twant := make([]byte, 0)\n\t\twant = append(want, '[')\n\t\tfor i, val := range vals {\n\t\t\twant = append(want, []byte(fmt.Sprintf(\"%d\", uint16(val)))...)\n\t\t\tif i < len(vals)-1 {\n\t\t\t\twant = append(want, ',')\n\t\t\t}\n\t\t}\n\t\twant = append(want, ']')\n\n\t\tgot := enc.AppendUints16([]byte{}, vals)\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"AppendUints16(%v)\\ngot:  %s\\nwant: %s\",\n\t\t\t\tvals,\n\t\t\t\tstring(got),\n\t\t\t\tstring(want))\n\t\t}\n\t}\n\n\tarray := make([]uint16, 0)\n\tfor _, tc := range internal.UnsignedIntegerTestCases {\n\t\tif tc.Val > math.MaxUint16 {\n\t\t\tcontinue\n\t\t}\n\t\tarray = append(array, uint16(tc.Val))\n\t}\n\n\tdoOne(array)\n\tdoOne(array[:1]) // single element\n\tdoOne(array[:0]) // edge case of zero length\n}\nfunc TestAppendInts32(t *testing.T) {\n\tdoOne := func(vals []int32) {\n\t\twant := make([]byte, 0)\n\t\twant = append(want, '[')\n\t\tfor i, val := range vals {\n\t\t\twant = append(want, []byte(fmt.Sprintf(\"%d\", int32(val)))...)\n\t\t\tif i < len(vals)-1 {\n\t\t\t\twant = append(want, ',')\n\t\t\t}\n\t\t}\n\t\twant = append(want, ']')\n\n\t\tgot := enc.AppendInts32([]byte{}, vals)\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"AppendInts32(%v)\\ngot:  %s\\nwant: %s\",\n\t\t\t\tvals,\n\t\t\t\tstring(got),\n\t\t\t\tstring(want))\n\t\t}\n\t}\n\n\tarray := make([]int32, 0)\n\tfor _, tc := range internal.IntegerTestCases {\n\t\tif (tc.Val < math.MinInt32) || (tc.Val > math.MaxInt32) {\n\t\t\tcontinue\n\t\t}\n\t\tarray = append(array, int32(tc.Val))\n\t}\n\n\tdoOne(array)\n\tdoOne(array[:1]) // single element\n\tdoOne(array[:0]) // edge case of zero length\n}\nfunc TestAppendUints32(t *testing.T) {\n\tdoOne := func(vals []uint32) {\n\t\twant := make([]byte, 0)\n\t\twant = append(want, '[')\n\t\tfor i, val := range vals {\n\t\t\twant = append(want, []byte(fmt.Sprintf(\"%d\", uint32(val)))...)\n\t\t\tif i < len(vals)-1 {\n\t\t\t\twant = append(want, ',')\n\t\t\t}\n\t\t}\n\t\twant = append(want, ']')\n\n\t\tgot := enc.AppendUints32([]byte{}, vals)\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"AppendUints32(%v)\\ngot:  %s\\nwant: %s\",\n\t\t\t\tvals,\n\t\t\t\tstring(got),\n\t\t\t\tstring(want))\n\t\t}\n\t}\n\n\tarray := make([]uint32, 0)\n\tfor _, tc := range internal.UnsignedIntegerTestCases {\n\t\tif tc.Val > math.MaxUint32 {\n\t\t\tcontinue\n\t\t}\n\t\tarray = append(array, uint32(tc.Val))\n\t}\n\n\tdoOne(array)\n\tdoOne(array[:1]) // single element\n\tdoOne(array[:0]) // edge case of zero length\n}\n\nfunc TestAppendInt64(t *testing.T) {\n\tdoOne := func(vals []int64) {\n\t\twant := make([]byte, 0)\n\t\twant = append(want, '[')\n\t\tfor i, val := range vals {\n\t\t\twant = append(want, []byte(fmt.Sprintf(\"%d\", int64(val)))...)\n\t\t\tif i < len(vals)-1 {\n\t\t\t\twant = append(want, ',')\n\t\t\t}\n\t\t}\n\t\twant = append(want, ']')\n\n\t\tgot := enc.AppendInts64([]byte{}, vals)\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"AppendInts64(%v)\\ngot:  %s\\nwant: %s\",\n\t\t\t\tvals,\n\t\t\t\tstring(got),\n\t\t\t\tstring(want))\n\t\t}\n\t}\n\n\tarray := make([]int64, 0)\n\tfor _, tc := range internal.IntegerTestCases {\n\t\tarray = append(array, int64(tc.Val))\n\t}\n\n\tdoOne(array)\n\tdoOne(array[:1]) // single element\n\tdoOne(array[:0]) // edge case of zero length\n}\nfunc TestAppendUints64(t *testing.T) {\n\tdoOne := func(vals []uint64) {\n\t\twant := make([]byte, 0)\n\t\twant = append(want, '[')\n\t\tfor i, val := range vals {\n\t\t\twant = append(want, []byte(fmt.Sprintf(\"%d\", uint64(val)))...)\n\t\t\tif i < len(vals)-1 {\n\t\t\t\twant = append(want, ',')\n\t\t\t}\n\t\t}\n\t\twant = append(want, ']')\n\n\t\tgot := enc.AppendUints64([]byte{}, vals)\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"AppendUints64(%v)\\ngot:  %s\\nwant: %s\",\n\t\t\t\tvals,\n\t\t\t\tstring(got),\n\t\t\t\tstring(want))\n\t\t}\n\t}\n\n\tarray := make([]uint64, 0)\n\tfor _, tc := range internal.UnsignedIntegerTestCases {\n\t\tarray = append(array, uint64(tc.Val))\n\t}\n\n\tdoOne(array)\n\tdoOne(array[:1]) // single element\n\tdoOne(array[:0]) // edge case of zero length\n}\n\nfunc TestAppendInt(t *testing.T) {\n\tfor _, tc := range internal.IntegerTestCases {\n\t\twant := []byte(fmt.Sprintf(\"%d\", tc.Val))\n\t\tgot := enc.AppendInt([]byte{}, tc.Val)\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"AppendInt(0x%x)\\ngot:  %s\\nwant: %s\",\n\t\t\t\ttc.Val,\n\t\t\t\tstring(got),\n\t\t\t\tstring(want))\n\t\t}\n\t}\n}\nfunc TestAppendUint(t *testing.T) {\n\tfor _, tc := range internal.UnsignedIntegerTestCases {\n\t\twant := []byte(fmt.Sprintf(\"%d\", tc.Val))\n\t\tgot := enc.AppendUint([]byte{}, tc.Val)\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"AppendUint(0x%x)\\ngot:  %s\\nwant: %s\",\n\t\t\t\ttc.Val,\n\t\t\t\tstring(got),\n\t\t\t\tstring(want))\n\t\t}\n\t}\n}\n\nfunc TestAppendInts(t *testing.T) {\n\tdoOne := func(vals []int) {\n\t\twant := make([]byte, 0)\n\t\twant = append(want, '[')\n\t\tfor i, val := range vals {\n\t\t\twant = append(want, []byte(fmt.Sprintf(\"%d\", int(val)))...)\n\t\t\tif i < len(vals)-1 {\n\t\t\t\twant = append(want, ',')\n\t\t\t}\n\t\t}\n\t\twant = append(want, ']')\n\n\t\tgot := enc.AppendInts([]byte{}, vals)\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"AppendInts(%v)\\ngot:  %s\\nwant: %s\",\n\t\t\t\tvals,\n\t\t\t\tstring(got),\n\t\t\t\tstring(want))\n\t\t}\n\t}\n\n\tarray := make([]int, 0)\n\tfor _, tc := range internal.IntegerTestCases {\n\t\tarray = append(array, int(tc.Val))\n\t}\n\n\tdoOne(array)\n\tdoOne(array[:1]) // single element\n\tdoOne(array[:0]) // edge case of zero length\n}\nfunc TestAppendUints(t *testing.T) {\n\tdoOne := func(vals []uint) {\n\t\twant := make([]byte, 0)\n\t\twant = append(want, '[')\n\t\tfor i, val := range vals {\n\t\t\twant = append(want, []byte(fmt.Sprintf(\"%d\", uint(val)))...)\n\t\t\tif i < len(vals)-1 {\n\t\t\t\twant = append(want, ',')\n\t\t\t}\n\t\t}\n\t\twant = append(want, ']')\n\n\t\tgot := enc.AppendUints([]byte{}, vals)\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"AppendUints(%v)\\ngot:  %s\\nwant: %s\",\n\t\t\t\tvals,\n\t\t\t\tstring(got),\n\t\t\t\tstring(want))\n\t\t}\n\t}\n\n\tarray := make([]uint, 0)\n\tfor _, tc := range internal.UnsignedIntegerTestCases {\n\t\tarray = append(array, uint(tc.Val))\n\t}\n\n\tdoOne(array)\n\tdoOne(array[:1]) // single element\n\tdoOne(array[:0]) // edge case of zero length\n}\n"
  },
  {
    "path": "internal/json/string.go",
    "content": "package json\n\nimport (\n\t\"fmt\"\n\t\"unicode/utf8\"\n)\n\nconst hexCharacters = \"0123456789abcdef\"\n\nvar noEscapeTable = [256]bool{}\n\nfunc init() {\n\tfor i := 0; i <= 0x7e; i++ {\n\t\tnoEscapeTable[i] = i >= 0x20 && i != '\\\\' && i != '\"'\n\t}\n}\n\n// AppendStrings encodes the input strings to json and\n// appends the encoded string list to the input byte slice.\nfunc (e Encoder) AppendStrings(dst []byte, vals []string) []byte {\n\tif len(vals) == 0 {\n\t\treturn append(dst, '[', ']')\n\t}\n\tdst = append(dst, '[')\n\tdst = e.AppendString(dst, vals[0])\n\tif len(vals) > 1 {\n\t\tfor _, val := range vals[1:] {\n\t\t\tdst = e.AppendString(append(dst, ','), val)\n\t\t}\n\t}\n\tdst = append(dst, ']')\n\treturn dst\n}\n\n// AppendString encodes the input string to json and appends\n// the encoded string to the input byte slice.\n//\n// The operation loops though each byte in the string looking\n// for characters that need json or utf8 encoding. If the string\n// does not need encoding, then the string is appended in its\n// entirety to the byte slice.\n// If we encounter a byte that does need encoding, switch up\n// the operation and perform a byte-by-byte read-encode-append.\nfunc (Encoder) AppendString(dst []byte, s string) []byte {\n\t// Start with a double quote.\n\tdst = append(dst, '\"')\n\t// Loop through each character in the string.\n\tfor i := 0; i < len(s); i++ {\n\t\t// Check if the character needs encoding. Control characters, slashes,\n\t\t// and the double quote need json encoding. Bytes above the ascii\n\t\t// boundary needs utf8 encoding.\n\t\tif !noEscapeTable[s[i]] {\n\t\t\t// We encountered a character that needs to be encoded. Switch\n\t\t\t// to complex version of the algorithm.\n\t\t\tdst = appendStringComplex(dst, s, i)\n\t\t\treturn append(dst, '\"')\n\t\t}\n\t}\n\t// The string has no need for encoding and therefore is directly\n\t// appended to the byte slice.\n\tdst = append(dst, s...)\n\t// End with a double quote\n\treturn append(dst, '\"')\n}\n\n// AppendStringers encodes the provided Stringer list to json and\n// appends the encoded Stringer list to the input byte slice.\nfunc (e Encoder) AppendStringers(dst []byte, vals []fmt.Stringer) []byte {\n\tif len(vals) == 0 {\n\t\treturn append(dst, '[', ']')\n\t}\n\tdst = append(dst, '[')\n\tdst = e.AppendStringer(dst, vals[0])\n\tif len(vals) > 1 {\n\t\tfor _, val := range vals[1:] {\n\t\t\tdst = e.AppendStringer(append(dst, ','), val)\n\t\t}\n\t}\n\treturn append(dst, ']')\n}\n\n// AppendStringer encodes the input Stringer to json and appends the\n// encoded Stringer value to the input byte slice.\nfunc (e Encoder) AppendStringer(dst []byte, val fmt.Stringer) []byte {\n\tif val == nil {\n\t\treturn e.AppendInterface(dst, nil)\n\t}\n\treturn e.AppendString(dst, val.String())\n}\n\n// appendStringComplex is used by appendString to take over an in\n// progress JSON string encoding that encountered a character that needs\n// to be encoded.\nfunc appendStringComplex(dst []byte, s string, i int) []byte {\n\tstart := 0\n\tfor i < len(s) {\n\t\tb := s[i]\n\t\tif b >= utf8.RuneSelf {\n\t\t\tr, size := utf8.DecodeRuneInString(s[i:])\n\t\t\tif r == utf8.RuneError && size == 1 {\n\t\t\t\t// In case of error, first append previous simple characters to\n\t\t\t\t// the byte slice if any and append a replacement character code\n\t\t\t\t// in place of the invalid sequence.\n\t\t\t\tif start < i {\n\t\t\t\t\tdst = append(dst, s[start:i]...)\n\t\t\t\t}\n\t\t\t\tdst = append(dst, `\\ufffd`...)\n\t\t\t\ti += size\n\t\t\t\tstart = i\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ti += size\n\t\t\tcontinue\n\t\t}\n\t\tif noEscapeTable[b] {\n\t\t\ti++\n\t\t\tcontinue\n\t\t}\n\t\t// We encountered a character that needs to be encoded.\n\t\t// Let's append the previous simple characters to the byte slice\n\t\t// and switch our operation to read and encode the remainder\n\t\t// characters byte-by-byte.\n\t\tif start < i {\n\t\t\tdst = append(dst, s[start:i]...)\n\t\t}\n\t\tswitch b {\n\t\tcase '\"', '\\\\':\n\t\t\tdst = append(dst, '\\\\', b)\n\t\tcase '\\b':\n\t\t\tdst = append(dst, '\\\\', 'b')\n\t\tcase '\\f':\n\t\t\tdst = append(dst, '\\\\', 'f')\n\t\tcase '\\n':\n\t\t\tdst = append(dst, '\\\\', 'n')\n\t\tcase '\\r':\n\t\t\tdst = append(dst, '\\\\', 'r')\n\t\tcase '\\t':\n\t\t\tdst = append(dst, '\\\\', 't')\n\t\tdefault:\n\t\t\tdst = append(dst, '\\\\', 'u', '0', '0', hexCharacters[b>>4], hexCharacters[b&0xF])\n\t\t}\n\t\ti++\n\t\tstart = i\n\t}\n\tif start < len(s) {\n\t\tdst = append(dst, s[start:]...)\n\t}\n\treturn dst\n}\n"
  },
  {
    "path": "internal/json/string_test.go",
    "content": "package json\n\nimport (\n\t\"testing\"\n\n\t\"github.com/rs/zerolog/internal\"\n)\n\nfunc TestAppendString(t *testing.T) {\n\tfor _, tt := range internal.EncodeStringTests {\n\t\tb := enc.AppendString([]byte{}, tt.In)\n\t\tif got, want := string(b), tt.Out; got != want {\n\t\t\tt.Errorf(\"appendString(%q) = %#q, want %#q\", tt.In, got, want)\n\t\t}\n\t}\n}\n\nfunc TestAppendStrings(t *testing.T) {\n\tfor _, tt := range internal.EncodeStringsTests {\n\t\tb := enc.AppendStrings([]byte{}, tt.In)\n\t\tif got, want := string(b), tt.Out; got != want {\n\t\t\tt.Errorf(\"appendStrings(%q) = %#q, want %#q\", tt.In, got, want)\n\t\t}\n\t}\n}\n\nfunc TestAppendStringer(t *testing.T) {\n\toldJSONMarshalFunc := JSONMarshalFunc\n\tdefer func() {\n\t\tJSONMarshalFunc = oldJSONMarshalFunc\n\t}()\n\n\tJSONMarshalFunc = func(v interface{}) ([]byte, error) {\n\t\treturn internal.InterfaceMarshalFunc(v)\n\t}\n\n\tfor _, tt := range internal.EncodeStringerTests {\n\t\tb := enc.AppendStringer([]byte{}, tt.In)\n\t\tif got, want := string(b), tt.Out; got != want {\n\t\t\tt.Errorf(\"AppendStringer(%q)\\ngot:  %#q, want: %#q\", tt.In, got, want)\n\t\t}\n\t}\n}\n\nfunc TestAppendStringers(t *testing.T) {\n\tfor _, tt := range internal.EncodeStringersTests {\n\t\tb := enc.AppendStringers([]byte{}, tt.In)\n\t\tif got, want := string(b), tt.Out; got != want {\n\t\t\tt.Errorf(\"appendStrings(%q) = %#q, want %#q\", tt.In, got, want)\n\t\t}\n\t}\n}\n\nfunc BenchmarkAppendString(b *testing.B) {\n\ttests := map[string]string{\n\t\t\"NoEncoding\":       `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`,\n\t\t\"EncodingFirst\":    `\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`,\n\t\t\"EncodingMiddle\":   `aaaaaaaaaaaaaaaaaaaaaaaaa\"aaaaaaaaaaaaaaaaaaaaaaaa`,\n\t\t\"EncodingLast\":     `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"`,\n\t\t\"MultiBytesFirst\":  `❤️aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`,\n\t\t\"MultiBytesMiddle\": `aaaaaaaaaaaaaaaaaaaaaaaaa❤️aaaaaaaaaaaaaaaaaaaaaaaa`,\n\t\t\"MultiBytesLast\":   `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa❤️`,\n\t}\n\tfor name, str := range tests {\n\t\tb.Run(name, func(b *testing.B) {\n\t\t\tbuf := make([]byte, 0, 100)\n\t\t\tfor i := 0; i < b.N; i++ {\n\t\t\t\t_ = enc.AppendString(buf, str)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/json/time.go",
    "content": "package json\n\nimport (\n\t\"strconv\"\n\t\"time\"\n)\n\nconst (\n\t// Import from zerolog/global.go\n\ttimeFormatUnix       = \"\"\n\ttimeFormatUnixMs     = \"UNIXMS\"\n\ttimeFormatUnixMicro  = \"UNIXMICRO\"\n\ttimeFormatUnixNano   = \"UNIXNANO\"\n\tdurationFormatFloat  = \"float\"\n\tdurationFormatInt    = \"int\"\n\tdurationFormatString = \"string\"\n)\n\n// AppendTime formats the input time with the given format\n// and appends the encoded string to the input byte slice.\nfunc (e Encoder) AppendTime(dst []byte, t time.Time, format string) []byte {\n\tswitch format {\n\tcase timeFormatUnix:\n\t\treturn e.AppendInt64(dst, t.Unix())\n\tcase timeFormatUnixMs:\n\t\treturn e.AppendInt64(dst, t.UnixNano()/1000000)\n\tcase timeFormatUnixMicro:\n\t\treturn e.AppendInt64(dst, t.UnixNano()/1000)\n\tcase timeFormatUnixNano:\n\t\treturn e.AppendInt64(dst, t.UnixNano())\n\t}\n\treturn append(t.AppendFormat(append(dst, '\"'), format), '\"')\n}\n\n// AppendTimes converts the input times with the given format\n// and appends the encoded string list to the input byte slice.\nfunc (Encoder) AppendTimes(dst []byte, vals []time.Time, format string) []byte {\n\tswitch format {\n\tcase timeFormatUnix:\n\t\treturn appendUnixTimes(dst, vals)\n\tcase timeFormatUnixMs:\n\t\treturn appendUnixNanoTimes(dst, vals, 1000000)\n\tcase timeFormatUnixMicro:\n\t\treturn appendUnixNanoTimes(dst, vals, 1000)\n\tcase timeFormatUnixNano:\n\t\treturn appendUnixNanoTimes(dst, vals, 1)\n\t}\n\tif len(vals) == 0 {\n\t\treturn append(dst, '[', ']')\n\t}\n\tdst = append(dst, '[')\n\tdst = append(vals[0].AppendFormat(append(dst, '\"'), format), '\"')\n\tif len(vals) > 1 {\n\t\tfor _, t := range vals[1:] {\n\t\t\tdst = append(t.AppendFormat(append(dst, ',', '\"'), format), '\"')\n\t\t}\n\t}\n\tdst = append(dst, ']')\n\treturn dst\n}\n\nfunc appendUnixTimes(dst []byte, vals []time.Time) []byte {\n\tif len(vals) == 0 {\n\t\treturn append(dst, '[', ']')\n\t}\n\tdst = append(dst, '[')\n\tdst = strconv.AppendInt(dst, vals[0].Unix(), 10)\n\tif len(vals) > 1 {\n\t\tfor _, t := range vals[1:] {\n\t\t\tdst = strconv.AppendInt(append(dst, ','), t.Unix(), 10)\n\t\t}\n\t}\n\tdst = append(dst, ']')\n\treturn dst\n}\n\nfunc appendUnixNanoTimes(dst []byte, vals []time.Time, div int64) []byte {\n\tif len(vals) == 0 {\n\t\treturn append(dst, '[', ']')\n\t}\n\tdst = append(dst, '[')\n\tdst = strconv.AppendInt(dst, vals[0].UnixNano()/div, 10)\n\tif len(vals) > 1 {\n\t\tfor _, t := range vals[1:] {\n\t\t\tdst = strconv.AppendInt(append(dst, ','), t.UnixNano()/div, 10)\n\t\t}\n\t}\n\tdst = append(dst, ']')\n\treturn dst\n}\n\n// AppendDuration formats the input duration with the given unit & format\n// and appends the encoded string to the input byte slice.\nfunc (e Encoder) AppendDuration(dst []byte, d time.Duration, unit time.Duration, format string, useInt bool, precision int) []byte {\n\tif useInt {\n\t\treturn strconv.AppendInt(dst, int64(d/unit), 10)\n\t}\n\tswitch format {\n\tcase durationFormatFloat:\n\t\treturn e.AppendFloat64(dst, float64(d)/float64(unit), precision)\n\tcase durationFormatInt:\n\t\treturn e.AppendInt64(dst, int64(d/unit))\n\tcase durationFormatString:\n\t\treturn e.AppendString(dst, d.String())\n\t}\n\treturn e.AppendFloat64(dst, float64(d)/float64(unit), precision)\n}\n\n// AppendDurations formats the input durations with the given unit & format\n// and appends the encoded string list to the input byte slice.\nfunc (e Encoder) AppendDurations(dst []byte, vals []time.Duration, unit time.Duration, format string, useInt bool, precision int) []byte {\n\tif len(vals) == 0 {\n\t\treturn append(dst, '[', ']')\n\t}\n\tdst = append(dst, '[')\n\tdst = e.AppendDuration(dst, vals[0], unit, format, useInt, precision)\n\tif len(vals) > 1 {\n\t\tfor _, d := range vals[1:] {\n\t\t\tdst = e.AppendDuration(append(dst, ','), d, unit, format, useInt, precision)\n\t\t}\n\t}\n\tdst = append(dst, ']')\n\treturn dst\n}\n"
  },
  {
    "path": "internal/json/time_test.go",
    "content": "package json\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/rs/zerolog/internal\"\n)\n\nfunc TestEncoder_AppendDuration(t *testing.T) {\n\ttype args struct {\n\t\tdst    []byte\n\t\td      time.Duration\n\t\tunit   time.Duration\n\t\tformat string\n\t\tuseInt bool\n\t\tunused int\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\twant []byte\n\t}{\n\t\t{\n\t\t\tname: \"useInt\",\n\t\t\targs: args{\n\t\t\t\td:      1234567890,\n\t\t\t\tunit:   time.Second,\n\t\t\t\tuseInt: true,\n\t\t\t},\n\t\t\twant: []byte{49},\n\t\t},\n\t\t{\n\t\t\tname: \"formatFloat\",\n\t\t\targs: args{\n\t\t\t\td:      1234567890,\n\t\t\t\tunit:   time.Second,\n\t\t\t\tformat: durationFormatFloat,\n\t\t\t},\n\t\t\twant: []byte{49},\n\t\t},\n\t\t{\n\t\t\tname: \"formatInt\",\n\t\t\targs: args{\n\t\t\t\td:      1234567890,\n\t\t\t\tunit:   time.Second,\n\t\t\t\tformat: durationFormatInt,\n\t\t\t},\n\t\t\twant: []byte{49},\n\t\t},\n\t\t{\n\t\t\tname: \"formatString\",\n\t\t\targs: args{\n\t\t\t\td:      1234567890,\n\t\t\t\tunit:   time.Second,\n\t\t\t\tformat: durationFormatString,\n\t\t\t},\n\t\t\twant: []byte{34, 49, 46, 50, 51, 52, 53, 54, 55, 56, 57, 115, 34},\n\t\t},\n\t\t{\n\t\t\tname: \"formatBlank\",\n\t\t\targs: args{\n\t\t\t\td:    1234567890,\n\t\t\t\tunit: time.Second,\n\t\t\t},\n\t\t\twant: []byte{49},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\te := Encoder{}\n\t\t\tif got := e.AppendDuration(tt.args.dst, tt.args.d, tt.args.unit, tt.args.format, tt.args.useInt, tt.args.unused); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"AppendDuration() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestEncoder_AppendDurations(t *testing.T) {\n\ttype args struct {\n\t\tdst    []byte\n\t\tvals   []time.Duration\n\t\tunit   time.Duration\n\t\tformat string\n\t\tuseInt bool\n\t\tunused int\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\twant []byte\n\t}{\n\t\t{\n\t\t\tname: \"useInt\",\n\t\t\targs: args{\n\t\t\t\tvals:   []time.Duration{1234567890},\n\t\t\t\tunit:   time.Second,\n\t\t\t\tuseInt: true,\n\t\t\t},\n\t\t\twant: []byte{91, 49, 93},\n\t\t},\n\t\t{\n\t\t\tname: \"formatFloat\",\n\t\t\targs: args{\n\t\t\t\tvals:   []time.Duration{1234567890},\n\t\t\t\tunit:   time.Second,\n\t\t\t\tformat: durationFormatFloat,\n\t\t\t},\n\t\t\twant: []byte{91, 49, 93},\n\t\t},\n\t\t{\n\t\t\tname: \"formatInt\",\n\t\t\targs: args{\n\t\t\t\tvals:   []time.Duration{1234567890},\n\t\t\t\tunit:   time.Second,\n\t\t\t\tformat: durationFormatInt,\n\t\t\t},\n\t\t\twant: []byte{91, 49, 93},\n\t\t},\n\t\t{\n\t\t\tname: \"formatString\",\n\t\t\targs: args{\n\t\t\t\tvals:   []time.Duration{1234567890},\n\t\t\t\tunit:   time.Second,\n\t\t\t\tformat: durationFormatString,\n\t\t\t},\n\t\t\twant: []byte{91, 34, 49, 46, 50, 51, 52, 53, 54, 55, 56, 57, 115, 34, 93},\n\t\t},\n\t\t{\n\t\t\tname: \"formatBlank\",\n\t\t\targs: args{\n\t\t\t\tvals: []time.Duration{1234567890},\n\t\t\t\tunit: time.Second,\n\t\t\t},\n\t\t\twant: []byte{91, 49, 93},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\te := Encoder{}\n\t\t\tif got := e.AppendDurations(tt.args.dst, tt.args.vals, tt.args.unit, tt.args.format, tt.args.useInt, tt.args.unused); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"AppendDurations() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAppendTimeNow(t *testing.T) {\n\ttm := time.Now()\n\tgot := enc.AppendTime([]byte{}, tm, time.RFC3339)\n\twant := tm.AppendFormat([]byte{'\"'}, time.RFC3339)\n\twant = append(want, '\"')\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendTime(%s)\\ngot:  %s\\nwant: %s\",\n\t\t\t\"time.Now()\",\n\t\t\tstring(got),\n\t\t\tstring(want))\n\t}\n}\n\nfunc TestAppendTimePastPresentInteger(t *testing.T) {\n\tfor _, tt := range internal.TimeIntegerTestcases {\n\t\ttin, err := time.Parse(time.RFC3339, tt.Txt)\n\t\tif err != nil {\n\t\t\tfmt.Println(\"Cannot parse input\", tt.Txt, \".. Skipping!\", err)\n\t\t\tcontinue\n\t\t}\n\n\t\tgot := enc.AppendTime([]byte{}, tin, timeFormatUnix)\n\t\twant := []byte(fmt.Sprintf(\"%d\", tt.UnixInt))\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"appendString(%s)\\ngot:  %s\\nwant: %s\",\n\t\t\t\ttt.Txt,\n\t\t\t\tstring(got),\n\t\t\t\tstring(want))\n\t\t}\n\t\tgot = enc.AppendTime([]byte{}, tin, timeFormatUnixMs)\n\t\twant = []byte(fmt.Sprintf(\"%d\", tt.UnixInt*1000))\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"appendString(%s)\\ngot:  %s\\nwant: %s\",\n\t\t\t\ttt.Txt,\n\t\t\t\tstring(got),\n\t\t\t\tstring(want))\n\t\t}\n\t\tgot = enc.AppendTime([]byte{}, tin, timeFormatUnixMicro)\n\t\twant = []byte(fmt.Sprintf(\"%d\", tt.UnixInt*1000000))\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"appendString(%s)\\ngot:  %s\\nwant: %s\",\n\t\t\t\ttt.Txt,\n\t\t\t\tstring(got),\n\t\t\t\tstring(want))\n\t\t}\n\t\tgot = enc.AppendTime([]byte{}, tin, timeFormatUnixNano)\n\t\twant = []byte(fmt.Sprintf(\"%d\", tt.UnixInt*1000000000))\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"appendString(%s)\\ngot:  %s\\nwant: %s\",\n\t\t\t\ttt.Txt,\n\t\t\t\tstring(got),\n\t\t\t\tstring(want))\n\t\t}\n\t}\n}\n\nfunc TestAppendTimePastPresentFloat(t *testing.T) {\n\tconst timeFloatFmt = \"2006-01-02T15:04:05.999999-07:00\"\n\tfor _, tt := range internal.TimeFloatTestcases {\n\t\ttin, err := time.Parse(timeFloatFmt, tt.RfcStr)\n\t\tif err != nil {\n\t\t\tfmt.Println(\"Cannot parse input\", tt.RfcStr, \".. Skipping!\")\n\t\t\tcontinue\n\t\t}\n\t\tgot := enc.AppendTime([]byte{}, tin, timeFormatUnix)\n\t\twant := []byte(fmt.Sprintf(\"%d\", tt.UnixInt))\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"appendString(%s)\\ngot:  %s\\nwant: %s\",\n\t\t\t\ttt.RfcStr,\n\t\t\t\tstring(got),\n\t\t\t\tstring(want))\n\t\t}\n\t}\n}\nfunc TestAppendTimes(t *testing.T) {\n\tdoOne := func(multiplier int, format string) {\n\t\tarray := make([]time.Time, 0)\n\t\twant := append([]byte{}, '[')\n\t\twant = append(want, ']')\n\t\tgot := enc.AppendTimes([]byte{}, array, format)\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"AppendTimes(%v)\\ngot:  %s\\nwant: %s\",\n\t\t\t\tarray,\n\t\t\t\tstring(got),\n\t\t\t\tstring(want))\n\t\t}\n\n\t\tarray = make([]time.Time, len(internal.TimeIntegerTestcases))\n\t\twant = append([]byte{}, '[')\n\t\tfor i, tt := range internal.TimeIntegerTestcases {\n\t\t\tif tin, err := time.Parse(time.RFC3339, tt.RfcStr); err != nil {\n\t\t\t\tfmt.Println(\"Cannot parse input\", tt.RfcStr, \".. Skipping!\")\n\t\t\t\tcontinue\n\t\t\t} else {\n\t\t\t\tarray[i] = tin\n\t\t\t}\n\t\t\tif multiplier == 0 {\n\t\t\t\twant = append(want, '\"')\n\t\t\t\tformatted := array[i].Format(format)\n\t\t\t\twant = append(want, []byte(fmt.Sprintf(\"%v\", formatted))...)\n\t\t\t\twant = append(want, '\"')\n\t\t\t} else {\n\t\t\t\tscaled := tt.UnixInt * multiplier\n\t\t\t\twant = append(want, []byte(fmt.Sprintf(\"%d\", scaled))...)\n\t\t\t}\n\t\t\tif i < len(internal.TimeIntegerTestcases)-1 {\n\t\t\t\twant = append(want, ',')\n\t\t\t}\n\t\t}\n\t\twant = append(want, ']')\n\n\t\tgot = enc.AppendTimes([]byte{}, array, format)\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"AppendTimes(%v) %d %s\\ngot:  %s\\nwant: %s\",\n\t\t\t\tarray, multiplier, format,\n\t\t\t\tstring(got),\n\t\t\t\tstring(want))\n\t\t}\n\t}\n\n\tdoOne(0, time.RFC3339)\n\tdoOne(1, timeFormatUnix)\n\tdoOne(1000, timeFormatUnixMs)\n\tdoOne(1000000, timeFormatUnixMicro)\n\tdoOne(1000000000, timeFormatUnixNano)\n}\n\nfunc TestAppendDurationFloat(t *testing.T) {\n\tfor _, tt := range internal.DurTestcases {\n\t\tdur := tt.Duration\n\t\twant := []byte{}\n\t\twant = append(want, []byte(fmt.Sprintf(\"%v\", dur.Microseconds()))...)\n\t\tgot := enc.AppendDuration([]byte{}, dur, time.Microsecond, \"\", false, -1)\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"AppendDuration(%v)=\\ngot:  %s\\nwant: %s\",\n\t\t\t\tdur,\n\t\t\t\tstring(got),\n\t\t\t\tstring(want))\n\t\t}\n\n\t\twant = []byte{}\n\t\tfraction := float64(dur) / float64(time.Millisecond)\n\t\twant = append(want, []byte(fmt.Sprintf(\"%v\", fraction))...)\n\t\tgot = enc.AppendDuration([]byte{}, dur, time.Millisecond, \"\", false, -1)\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"AppendDuration(%v)=\\ngot:  %s\\nwant: %s\",\n\t\t\t\tdur,\n\t\t\t\tstring(got),\n\t\t\t\tstring(want))\n\t\t}\n\t}\n}\nfunc TestAppendDurationInteger(t *testing.T) {\n\tfor _, tt := range internal.DurTestcases {\n\t\tdur := tt.Duration\n\t\twant := []byte{}\n\t\twhole := int(dur) / int(time.Microsecond)\n\t\twant = append(want, []byte(fmt.Sprintf(\"%v\", whole))...)\n\t\tgot := enc.AppendDuration([]byte{}, dur, time.Microsecond, \"\", true, -1)\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"AppendDuration(%v)=\\ngot:  %s\\nwant: %s\",\n\t\t\t\tdur,\n\t\t\t\tstring(got),\n\t\t\t\tstring(want))\n\t\t}\n\t}\n}\nfunc TestAppendDurations(t *testing.T) {\n\tarray := make([]time.Duration, len(internal.DurTestcases))\n\twant := make([]byte, 0)\n\twant = append(want, '[')\n\tfor i, tt := range internal.DurTestcases {\n\t\tarray[i] = tt.Duration\n\t\twhole := int(tt.Duration) / int(time.Microsecond)\n\t\twant = append(want, []byte(fmt.Sprintf(\"%v\", whole))...)\n\t\tif i < len(internal.DurTestcases)-1 {\n\t\t\twant = append(want, ',')\n\t\t}\n\t}\n\twant = append(want, ']')\n\n\tgot := enc.AppendDurations([]byte{}, array, time.Microsecond, \"\", false, -1)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendDurations(%v)\\ngot:  %s\\nwant: %s\",\n\t\t\tarray,\n\t\t\tstring(got),\n\t\t\tstring(want))\n\t}\n\n\t// now empty array case\n\tarray = make([]time.Duration, 0)\n\twant = make([]byte, 0)\n\twant = append(want, '[')\n\twant = append(want, ']')\n\tgot = enc.AppendDurations([]byte{}, array, time.Microsecond, \"\", false, -1)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendDurations(%v)\\ngot:  %s\\nwant: %s\",\n\t\t\tarray,\n\t\t\tstring(got),\n\t\t\tstring(want))\n\t}\n}\n\nfunc BenchmarkAppendTime(b *testing.B) {\n\ttests := map[string]string{\n\t\t\"Integer\": \"Feb 3, 2013 at 7:54pm (PST)\",\n\t\t\"Float\":   \"2006-01-02T15:04:05.999999-08:00\",\n\t}\n\tconst timeFloatFmt = \"2006-01-02T15:04:05.999999-07:00\"\n\n\tfor name, str := range tests {\n\t\tt, err := time.Parse(time.RFC3339, str)\n\t\tif err != nil {\n\t\t\tt, _ = time.Parse(timeFloatFmt, str)\n\t\t}\n\t\tb.Run(name, func(b *testing.B) {\n\t\t\tbuf := make([]byte, 0, 100)\n\t\t\tfor i := 0; i < b.N; i++ {\n\t\t\t\t_ = enc.AppendTime(buf, t, \"unused\")\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/json/types.go",
    "content": "package json\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"net\"\n\t\"reflect\"\n\t\"strconv\"\n)\n\n// AppendNil inserts a 'Nil' object into the dst byte array.\nfunc (Encoder) AppendNil(dst []byte) []byte {\n\treturn append(dst, \"null\"...)\n}\n\n// AppendBeginMarker inserts a map start into the dst byte array.\nfunc (Encoder) AppendBeginMarker(dst []byte) []byte {\n\treturn append(dst, '{')\n}\n\n// AppendEndMarker inserts a map end into the dst byte array.\nfunc (Encoder) AppendEndMarker(dst []byte) []byte {\n\treturn append(dst, '}')\n}\n\n// AppendLineBreak appends a line break.\nfunc (Encoder) AppendLineBreak(dst []byte) []byte {\n\treturn append(dst, '\\n')\n}\n\n// AppendArrayStart adds markers to indicate the start of an array.\nfunc (Encoder) AppendArrayStart(dst []byte) []byte {\n\treturn append(dst, '[')\n}\n\n// AppendArrayEnd adds markers to indicate the end of an array.\nfunc (Encoder) AppendArrayEnd(dst []byte) []byte {\n\treturn append(dst, ']')\n}\n\n// AppendArrayDelim adds markers to indicate end of a particular array element.\nfunc (Encoder) AppendArrayDelim(dst []byte) []byte {\n\tif len(dst) > 0 {\n\t\treturn append(dst, ',')\n\t}\n\treturn dst\n}\n\n// AppendBool converts the input bool to a string and\n// appends the encoded string to the input byte slice.\nfunc (Encoder) AppendBool(dst []byte, val bool) []byte {\n\treturn strconv.AppendBool(dst, val)\n}\n\n// AppendBools encodes the input bools to json and\n// appends the encoded string list to the input byte slice.\nfunc (Encoder) AppendBools(dst []byte, vals []bool) []byte {\n\tif len(vals) == 0 {\n\t\treturn append(dst, '[', ']')\n\t}\n\tdst = append(dst, '[')\n\tdst = strconv.AppendBool(dst, vals[0])\n\tif len(vals) > 1 {\n\t\tfor _, val := range vals[1:] {\n\t\t\tdst = strconv.AppendBool(append(dst, ','), val)\n\t\t}\n\t}\n\tdst = append(dst, ']')\n\treturn dst\n}\n\n// AppendInt converts the input int to a string and\n// appends the encoded string to the input byte slice.\nfunc (Encoder) AppendInt(dst []byte, val int) []byte {\n\treturn strconv.AppendInt(dst, int64(val), 10)\n}\n\n// AppendInts encodes the input ints to json and\n// appends the encoded string list to the input byte slice.\nfunc (Encoder) AppendInts(dst []byte, vals []int) []byte {\n\tif len(vals) == 0 {\n\t\treturn append(dst, '[', ']')\n\t}\n\tdst = append(dst, '[')\n\tdst = strconv.AppendInt(dst, int64(vals[0]), 10)\n\tif len(vals) > 1 {\n\t\tfor _, val := range vals[1:] {\n\t\t\tdst = strconv.AppendInt(append(dst, ','), int64(val), 10)\n\t\t}\n\t}\n\tdst = append(dst, ']')\n\treturn dst\n}\n\n// AppendInt8 converts the input []int8 to a string and\n// appends the encoded string to the input byte slice.\nfunc (Encoder) AppendInt8(dst []byte, val int8) []byte {\n\treturn strconv.AppendInt(dst, int64(val), 10)\n}\n\n// AppendInts8 encodes the input int8s to json and\n// appends the encoded string list to the input byte slice.\nfunc (Encoder) AppendInts8(dst []byte, vals []int8) []byte {\n\tif len(vals) == 0 {\n\t\treturn append(dst, '[', ']')\n\t}\n\tdst = append(dst, '[')\n\tdst = strconv.AppendInt(dst, int64(vals[0]), 10)\n\tif len(vals) > 1 {\n\t\tfor _, val := range vals[1:] {\n\t\t\tdst = strconv.AppendInt(append(dst, ','), int64(val), 10)\n\t\t}\n\t}\n\tdst = append(dst, ']')\n\treturn dst\n}\n\n// AppendInt16 converts the input int16 to a string and\n// appends the encoded string to the input byte slice.\nfunc (Encoder) AppendInt16(dst []byte, val int16) []byte {\n\treturn strconv.AppendInt(dst, int64(val), 10)\n}\n\n// AppendInts16 encodes the input int16s to json and\n// appends the encoded string list to the input byte slice.\nfunc (Encoder) AppendInts16(dst []byte, vals []int16) []byte {\n\tif len(vals) == 0 {\n\t\treturn append(dst, '[', ']')\n\t}\n\tdst = append(dst, '[')\n\tdst = strconv.AppendInt(dst, int64(vals[0]), 10)\n\tif len(vals) > 1 {\n\t\tfor _, val := range vals[1:] {\n\t\t\tdst = strconv.AppendInt(append(dst, ','), int64(val), 10)\n\t\t}\n\t}\n\tdst = append(dst, ']')\n\treturn dst\n}\n\n// AppendInt32 converts the input int32 to a string and\n// appends the encoded string to the input byte slice.\nfunc (Encoder) AppendInt32(dst []byte, val int32) []byte {\n\treturn strconv.AppendInt(dst, int64(val), 10)\n}\n\n// AppendInts32 encodes the input int32s to json and\n// appends the encoded string list to the input byte slice.\nfunc (Encoder) AppendInts32(dst []byte, vals []int32) []byte {\n\tif len(vals) == 0 {\n\t\treturn append(dst, '[', ']')\n\t}\n\tdst = append(dst, '[')\n\tdst = strconv.AppendInt(dst, int64(vals[0]), 10)\n\tif len(vals) > 1 {\n\t\tfor _, val := range vals[1:] {\n\t\t\tdst = strconv.AppendInt(append(dst, ','), int64(val), 10)\n\t\t}\n\t}\n\tdst = append(dst, ']')\n\treturn dst\n}\n\n// AppendInt64 converts the input int64 to a string and\n// appends the encoded string to the input byte slice.\nfunc (Encoder) AppendInt64(dst []byte, val int64) []byte {\n\treturn strconv.AppendInt(dst, val, 10)\n}\n\n// AppendInts64 encodes the input int64s to json and\n// appends the encoded string list to the input byte slice.\nfunc (Encoder) AppendInts64(dst []byte, vals []int64) []byte {\n\tif len(vals) == 0 {\n\t\treturn append(dst, '[', ']')\n\t}\n\tdst = append(dst, '[')\n\tdst = strconv.AppendInt(dst, vals[0], 10)\n\tif len(vals) > 1 {\n\t\tfor _, val := range vals[1:] {\n\t\t\tdst = strconv.AppendInt(append(dst, ','), val, 10)\n\t\t}\n\t}\n\tdst = append(dst, ']')\n\treturn dst\n}\n\n// AppendUint converts the input uint to a string and\n// appends the encoded string to the input byte slice.\nfunc (Encoder) AppendUint(dst []byte, val uint) []byte {\n\treturn strconv.AppendUint(dst, uint64(val), 10)\n}\n\n// AppendUints encodes the input uints to json and\n// appends the encoded string list to the input byte slice.\nfunc (Encoder) AppendUints(dst []byte, vals []uint) []byte {\n\tif len(vals) == 0 {\n\t\treturn append(dst, '[', ']')\n\t}\n\tdst = append(dst, '[')\n\tdst = strconv.AppendUint(dst, uint64(vals[0]), 10)\n\tif len(vals) > 1 {\n\t\tfor _, val := range vals[1:] {\n\t\t\tdst = strconv.AppendUint(append(dst, ','), uint64(val), 10)\n\t\t}\n\t}\n\tdst = append(dst, ']')\n\treturn dst\n}\n\n// AppendUint8 converts the input uint8 to a string and\n// appends the encoded string to the input byte slice.\nfunc (Encoder) AppendUint8(dst []byte, val uint8) []byte {\n\treturn strconv.AppendUint(dst, uint64(val), 10)\n}\n\n// AppendUints8 encodes the input uint8s to json and\n// appends the encoded string list to the input byte slice.\nfunc (Encoder) AppendUints8(dst []byte, vals []uint8) []byte {\n\tif len(vals) == 0 {\n\t\treturn append(dst, '[', ']')\n\t}\n\tdst = append(dst, '[')\n\tdst = strconv.AppendUint(dst, uint64(vals[0]), 10)\n\tif len(vals) > 1 {\n\t\tfor _, val := range vals[1:] {\n\t\t\tdst = strconv.AppendUint(append(dst, ','), uint64(val), 10)\n\t\t}\n\t}\n\tdst = append(dst, ']')\n\treturn dst\n}\n\n// AppendUint16 converts the input uint16 to a string and\n// appends the encoded string to the input byte slice.\nfunc (Encoder) AppendUint16(dst []byte, val uint16) []byte {\n\treturn strconv.AppendUint(dst, uint64(val), 10)\n}\n\n// AppendUints16 encodes the input uint16s to json and\n// appends the encoded string list to the input byte slice.\nfunc (Encoder) AppendUints16(dst []byte, vals []uint16) []byte {\n\tif len(vals) == 0 {\n\t\treturn append(dst, '[', ']')\n\t}\n\tdst = append(dst, '[')\n\tdst = strconv.AppendUint(dst, uint64(vals[0]), 10)\n\tif len(vals) > 1 {\n\t\tfor _, val := range vals[1:] {\n\t\t\tdst = strconv.AppendUint(append(dst, ','), uint64(val), 10)\n\t\t}\n\t}\n\tdst = append(dst, ']')\n\treturn dst\n}\n\n// AppendUint32 converts the input uint32 to a string and\n// appends the encoded string to the input byte slice.\nfunc (Encoder) AppendUint32(dst []byte, val uint32) []byte {\n\treturn strconv.AppendUint(dst, uint64(val), 10)\n}\n\n// AppendUints32 encodes the input uint32s to json and\n// appends the encoded string list to the input byte slice.\nfunc (Encoder) AppendUints32(dst []byte, vals []uint32) []byte {\n\tif len(vals) == 0 {\n\t\treturn append(dst, '[', ']')\n\t}\n\tdst = append(dst, '[')\n\tdst = strconv.AppendUint(dst, uint64(vals[0]), 10)\n\tif len(vals) > 1 {\n\t\tfor _, val := range vals[1:] {\n\t\t\tdst = strconv.AppendUint(append(dst, ','), uint64(val), 10)\n\t\t}\n\t}\n\tdst = append(dst, ']')\n\treturn dst\n}\n\n// AppendUint64 converts the input uint64 to a string and\n// appends the encoded string to the input byte slice.\nfunc (Encoder) AppendUint64(dst []byte, val uint64) []byte {\n\treturn strconv.AppendUint(dst, val, 10)\n}\n\n// AppendUints64 encodes the input uint64s to json and\n// appends the encoded string list to the input byte slice.\nfunc (Encoder) AppendUints64(dst []byte, vals []uint64) []byte {\n\tif len(vals) == 0 {\n\t\treturn append(dst, '[', ']')\n\t}\n\tdst = append(dst, '[')\n\tdst = strconv.AppendUint(dst, vals[0], 10)\n\tif len(vals) > 1 {\n\t\tfor _, val := range vals[1:] {\n\t\t\tdst = strconv.AppendUint(append(dst, ','), val, 10)\n\t\t}\n\t}\n\tdst = append(dst, ']')\n\treturn dst\n}\n\nfunc appendFloat(dst []byte, val float64, bitSize, precision int) []byte {\n\t// JSON does not permit NaN or Infinity. A typical JSON encoder would fail\n\t// with an error, but a logging library wants the data to get through so we\n\t// make a tradeoff and store those types as string.\n\tswitch {\n\tcase math.IsNaN(val):\n\t\treturn append(dst, `\"NaN\"`...)\n\tcase math.IsInf(val, 1):\n\t\treturn append(dst, `\"+Inf\"`...)\n\tcase math.IsInf(val, -1):\n\t\treturn append(dst, `\"-Inf\"`...)\n\t}\n\t// convert as if by es6 number to string conversion\n\t// see also https://cs.opensource.google/go/go/+/refs/tags/go1.20.3:src/encoding/json/encode.go;l=573\n\tstrFmt := byte('f')\n\t// If precision is set to a value other than -1, we always just format the float using that precision.\n\tif precision == -1 {\n\t\t// Use float32 comparisons for underlying float32 value to get precise cutoffs right.\n\t\tif abs := math.Abs(val); abs != 0 {\n\t\t\tif bitSize == 64 && (abs < 1e-6 || abs >= 1e21) || bitSize == 32 && (float32(abs) < 1e-6 || float32(abs) >= 1e21) {\n\t\t\t\tstrFmt = 'e'\n\t\t\t}\n\t\t}\n\t}\n\tdst = strconv.AppendFloat(dst, val, strFmt, precision, bitSize)\n\tif strFmt == 'e' {\n\t\t// Clean up e-09 to e-9\n\t\tn := len(dst)\n\t\tif n >= 4 && dst[n-4] == 'e' && dst[n-3] == '-' && dst[n-2] == '0' {\n\t\t\tdst[n-2] = dst[n-1]\n\t\t\tdst = dst[:n-1]\n\t\t}\n\t}\n\treturn dst\n}\n\n// AppendFloat32 converts the input float32 to a string and\n// appends the encoded string to the input byte slice.\nfunc (Encoder) AppendFloat32(dst []byte, val float32, precision int) []byte {\n\treturn appendFloat(dst, float64(val), 32, precision)\n}\n\n// AppendFloats32 encodes the input float32s to json and\n// appends the encoded string list to the input byte slice.\nfunc (Encoder) AppendFloats32(dst []byte, vals []float32, precision int) []byte {\n\tif len(vals) == 0 {\n\t\treturn append(dst, '[', ']')\n\t}\n\tdst = append(dst, '[')\n\tdst = appendFloat(dst, float64(vals[0]), 32, precision)\n\tif len(vals) > 1 {\n\t\tfor _, val := range vals[1:] {\n\t\t\tdst = appendFloat(append(dst, ','), float64(val), 32, precision)\n\t\t}\n\t}\n\tdst = append(dst, ']')\n\treturn dst\n}\n\n// AppendFloat64 converts the input float64 to a string and\n// appends the encoded string to the input byte slice.\nfunc (Encoder) AppendFloat64(dst []byte, val float64, precision int) []byte {\n\treturn appendFloat(dst, val, 64, precision)\n}\n\n// AppendFloats64 encodes the input float64s to json and\n// appends the encoded string list to the input byte slice.\nfunc (Encoder) AppendFloats64(dst []byte, vals []float64, precision int) []byte {\n\tif len(vals) == 0 {\n\t\treturn append(dst, '[', ']')\n\t}\n\tdst = append(dst, '[')\n\tdst = appendFloat(dst, vals[0], 64, precision)\n\tif len(vals) > 1 {\n\t\tfor _, val := range vals[1:] {\n\t\t\tdst = appendFloat(append(dst, ','), val, 64, precision)\n\t\t}\n\t}\n\tdst = append(dst, ']')\n\treturn dst\n}\n\n// AppendInterface marshals the input interface to a string and\n// appends the encoded string to the input byte slice.\nfunc (e Encoder) AppendInterface(dst []byte, i interface{}) []byte {\n\tmarshaled, err := JSONMarshalFunc(i)\n\tif err != nil {\n\t\treturn e.AppendString(dst, fmt.Sprintf(\"marshaling error: %v\", err))\n\t}\n\treturn append(dst, marshaled...)\n}\n\n// AppendType appends the parameter type (as a string) to the input byte slice.\nfunc (e Encoder) AppendType(dst []byte, i interface{}) []byte {\n\tif i == nil {\n\t\treturn e.AppendString(dst, \"<nil>\")\n\t}\n\treturn e.AppendString(dst, reflect.TypeOf(i).String())\n}\n\n// AppendObjectData takes in an object that is already in a byte array\n// and adds it to the dst.\nfunc (Encoder) AppendObjectData(dst []byte, o []byte) []byte {\n\t// Three conditions apply here:\n\t// 1. new content starts with '{' - which should be dropped   OR\n\t// 2. new content starts with '{' - which should be replaced with ','\n\t//    to separate with existing content OR\n\t// 3. existing content has already other fields\n\tif o[0] == '{' {\n\t\tif len(dst) > 1 {\n\t\t\tdst = append(dst, ',')\n\t\t}\n\t\to = o[1:]\n\t} else if len(dst) > 1 {\n\t\tdst = append(dst, ',')\n\t}\n\treturn append(dst, o...)\n}\n\n// AppendIPAddr adds a net.IP IPv4 or IPv6 address to dst.\nfunc (e Encoder) AppendIPAddr(dst []byte, ip net.IP) []byte {\n\treturn e.AppendString(dst, ip.String())\n}\n\n// AppendIPAddrs adds a []net.IP array of IPv4 or IPv6 address to dst.\nfunc (e Encoder) AppendIPAddrs(dst []byte, ips []net.IP) []byte {\n\tif len(ips) == 0 {\n\t\treturn append(dst, '[', ']')\n\t}\n\tdst = append(dst, '[')\n\tdst = e.AppendString(dst, ips[0].String())\n\tif len(ips) > 1 {\n\t\tfor _, ip := range ips[1:] {\n\t\t\tdst = e.AppendString(append(dst, ','), ip.String())\n\t\t}\n\t}\n\tdst = append(dst, ']')\n\treturn dst\n}\n\n// AppendIPPrefix adds a net.IPNet IPv4 or IPv6 Prefix (address & mask) to dst.\nfunc (e Encoder) AppendIPPrefix(dst []byte, pfx net.IPNet) []byte {\n\treturn e.AppendString(dst, pfx.String())\n}\n\n// AppendIPPrefixes adds a []net.IPNet array of IPv4 or IPv6 Prefix (address & mask) to dst.\nfunc (e Encoder) AppendIPPrefixes(dst []byte, pfxs []net.IPNet) []byte {\n\tif len(pfxs) == 0 {\n\t\treturn append(dst, '[', ']')\n\t}\n\tdst = append(dst, '[')\n\tdst = e.AppendString(dst, pfxs[0].String())\n\tif len(pfxs) > 1 {\n\t\tfor _, pfx := range pfxs[1:] {\n\t\t\tdst = e.AppendString(append(dst, ','), pfx.String())\n\t\t}\n\t}\n\tdst = append(dst, ']')\n\treturn dst\n}\n\n// AppendMACAddr adds a net.HardwareAddr MAC address to dst.\nfunc (e Encoder) AppendMACAddr(dst []byte, ha net.HardwareAddr) []byte {\n\treturn e.AppendString(dst, ha.String())\n}\n"
  },
  {
    "path": "internal/json/types_test.go",
    "content": "package json\n\nimport (\n\t\"bytes\"\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"math\"\n\t\"net\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/rs/zerolog/internal\"\n)\n\nfunc TestAppendNil(t *testing.T) {\n\tgot := enc.AppendNil([]byte{})\n\twant := []byte(`null`)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendNil() = %s, want: %s\",\n\t\t\tstring(got),\n\t\t\tstring(want))\n\t}\n}\nfunc TestAppendBeginMarker(t *testing.T) {\n\tgot := enc.AppendBeginMarker([]byte{})\n\twant := []byte(`{`)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendBeginMarker()\\ngot:  %s, want: %s\",\n\t\t\tstring(got),\n\t\t\tstring(want))\n\t}\n}\nfunc TestAppendEndMarker(t *testing.T) {\n\tgot := enc.AppendEndMarker([]byte{})\n\twant := []byte(`}`)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendEndMarker()\\ngot:  %s, want: %s\",\n\t\t\tstring(got),\n\t\t\tstring(want))\n\t}\n}\nfunc TestAppendArrayStart(t *testing.T) {\n\tgot := enc.AppendArrayStart([]byte{})\n\twant := []byte(\"[\")\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendArrayStart() = %s, want: %s\",\n\t\t\tstring(got),\n\t\t\tstring(want))\n\t}\n}\nfunc TestAppendArrayEnd(t *testing.T) {\n\tgot := enc.AppendArrayEnd([]byte{})\n\twant := []byte(\"]\")\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendArrayEnd() = %s, want: %s\",\n\t\t\tstring(got),\n\t\t\tstring(want))\n\t}\n}\nfunc TestAppendArrayDelim(t *testing.T) {\n\tgot := enc.AppendArrayDelim([]byte{})\n\twant := []byte(\"\")\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendArrayDelim() = 0x%s, want: 0x%s\",\n\t\t\tstring(got),\n\t\t\tstring(want))\n\t}\n\n\tgot = enc.AppendArrayDelim([]byte(\"a\"))\n\twant = []byte(\"a,\")\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendArrayDelim() = 0x%s, want: 0x%s\",\n\t\t\tstring(got),\n\t\t\tstring(want))\n\t}\n}\nfunc TestAppendLineBreak(t *testing.T) {\n\tgot := enc.AppendLineBreak([]byte{})\n\twant := []byte(\"\\n\")\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendLineBreak() = 0x%s, want: 0x%s\",\n\t\t\tstring(got),\n\t\t\tstring(want))\n\t}\n}\n\n// inline copy from globals.go of InterfaceMarshalFunc used in tests to avoid import cycle\nfunc interfaceMarshalFunc(v interface{}) ([]byte, error) {\n\tvar buf bytes.Buffer\n\tencoder := json.NewEncoder(&buf)\n\tencoder.SetEscapeHTML(false)\n\terr := encoder.Encode(v)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tb := buf.Bytes()\n\tif len(b) > 0 {\n\t\t// Remove trailing \\n which is added by Encode.\n\t\treturn b[:len(b)-1], nil\n\t}\n\treturn b, nil\n}\n\nfunc TestAppendInterface(t *testing.T) {\n\toldJSONMarshalFunc := JSONMarshalFunc\n\tdefer func() {\n\t\tJSONMarshalFunc = oldJSONMarshalFunc\n\t}()\n\n\tJSONMarshalFunc = func(v interface{}) ([]byte, error) {\n\t\treturn interfaceMarshalFunc(v)\n\t}\n\n\tvar i int = 17\n\tgot := enc.AppendInterface([]byte{}, i)\n\twant := make([]byte, 0)\n\twant = append(want, []byte(\"17\")...) // of type interface, two characters\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendInterface\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tstring(got),\n\t\t\tstring(want))\n\t}\n\n\tJSONMarshalFunc = func(v interface{}) ([]byte, error) {\n\t\treturn nil, errors.New(\"test\")\n\t}\n\n\tgot = enc.AppendInterface([]byte{}, nil)\n\twant = make([]byte, 0)\n\twant = append(want, []byte(\"\\\"marshaling error: test\\\"\")...) // of type interface, two characters\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendInterface\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tstring(got),\n\t\t\tstring(want))\n\t}\n}\n\nfunc TestAppendType(t *testing.T) {\n\tw := map[string]func(interface{}) []byte{\n\t\t\"AppendInt\":                   func(v interface{}) []byte { return enc.AppendInt([]byte{}, v.(int)) },\n\t\t\"AppendInt8\":                  func(v interface{}) []byte { return enc.AppendInt8([]byte{}, v.(int8)) },\n\t\t\"AppendInt16\":                 func(v interface{}) []byte { return enc.AppendInt16([]byte{}, v.(int16)) },\n\t\t\"AppendInt32\":                 func(v interface{}) []byte { return enc.AppendInt32([]byte{}, v.(int32)) },\n\t\t\"AppendInt64\":                 func(v interface{}) []byte { return enc.AppendInt64([]byte{}, v.(int64)) },\n\t\t\"AppendUint\":                  func(v interface{}) []byte { return enc.AppendUint([]byte{}, v.(uint)) },\n\t\t\"AppendUint8\":                 func(v interface{}) []byte { return enc.AppendUint8([]byte{}, v.(uint8)) },\n\t\t\"AppendUint16\":                func(v interface{}) []byte { return enc.AppendUint16([]byte{}, v.(uint16)) },\n\t\t\"AppendUint32\":                func(v interface{}) []byte { return enc.AppendUint32([]byte{}, v.(uint32)) },\n\t\t\"AppendUint64\":                func(v interface{}) []byte { return enc.AppendUint64([]byte{}, v.(uint64)) },\n\t\t\"AppendFloat32\":               func(v interface{}) []byte { return enc.AppendFloat32([]byte{}, v.(float32), -1) },\n\t\t\"AppendFloat64\":               func(v interface{}) []byte { return enc.AppendFloat64([]byte{}, v.(float64), -1) },\n\t\t\"AppendFloat32SmallPrecision\": func(v interface{}) []byte { return enc.AppendFloat32([]byte{}, v.(float32), 1) },\n\t\t\"AppendFloat64SmallPrecision\": func(v interface{}) []byte { return enc.AppendFloat64([]byte{}, v.(float64), 1) },\n\t}\n\ttests := []struct {\n\t\tname  string\n\t\tfn    string\n\t\tinput interface{}\n\t\twant  []byte\n\t}{\n\t\t{\"AppendInt8(math.MaxInt8)\", \"AppendInt8\", int8(math.MaxInt8), []byte(\"127\")},\n\t\t{\"AppendInt16(math.MaxInt16)\", \"AppendInt16\", int16(math.MaxInt16), []byte(\"32767\")},\n\t\t{\"AppendInt32(math.MaxInt32)\", \"AppendInt32\", int32(math.MaxInt32), []byte(\"2147483647\")},\n\t\t{\"AppendInt64(math.MaxInt64)\", \"AppendInt64\", int64(math.MaxInt64), []byte(\"9223372036854775807\")},\n\n\t\t{\"AppendUint8(math.MaxUint8)\", \"AppendUint8\", uint8(math.MaxUint8), []byte(\"255\")},\n\t\t{\"AppendUint16(math.MaxUint16)\", \"AppendUint16\", uint16(math.MaxUint16), []byte(\"65535\")},\n\t\t{\"AppendUint32(math.MaxUint32)\", \"AppendUint32\", uint32(math.MaxUint32), []byte(\"4294967295\")},\n\t\t{\"AppendUint64(math.MaxUint64)\", \"AppendUint64\", uint64(math.MaxUint64), []byte(\"18446744073709551615\")},\n\n\t\t{\"AppendFloat32(-Inf)\", \"AppendFloat32\", float32(math.Inf(-1)), []byte(`\"-Inf\"`)},\n\t\t{\"AppendFloat32(+Inf)\", \"AppendFloat32\", float32(math.Inf(1)), []byte(`\"+Inf\"`)},\n\t\t{\"AppendFloat32(NaN)\", \"AppendFloat32\", float32(math.NaN()), []byte(`\"NaN\"`)},\n\t\t{\"AppendFloat32(0)\", \"AppendFloat32\", float32(0), []byte(`0`)},\n\t\t{\"AppendFloat32(-1.1)\", \"AppendFloat32\", float32(-1.1), []byte(`-1.1`)},\n\t\t{\"AppendFloat32(1e20)\", \"AppendFloat32\", float32(1e20), []byte(`100000000000000000000`)},\n\t\t{\"AppendFloat32(1e21)\", \"AppendFloat32\", float32(1e21), []byte(`1e+21`)},\n\n\t\t{\"AppendFloat64(-Inf)\", \"AppendFloat64\", float64(math.Inf(-1)), []byte(`\"-Inf\"`)},\n\t\t{\"AppendFloat64(+Inf)\", \"AppendFloat64\", float64(math.Inf(1)), []byte(`\"+Inf\"`)},\n\t\t{\"AppendFloat64(NaN)\", \"AppendFloat64\", float64(math.NaN()), []byte(`\"NaN\"`)},\n\t\t{\"AppendFloat64(0)\", \"AppendFloat64\", float64(0), []byte(`0`)},\n\t\t{\"AppendFloat64(-1.1)\", \"AppendFloat64\", float64(-1.1), []byte(`-1.1`)},\n\t\t{\"AppendFloat64(1e20)\", \"AppendFloat64\", float64(1e20), []byte(`100000000000000000000`)},\n\t\t{\"AppendFloat64(1e21)\", \"AppendFloat64\", float64(1e21), []byte(`1e+21`)},\n\n\t\t{\"AppendFloat32SmallPrecision(-1.123)\", \"AppendFloat32SmallPrecision\", float32(-1.123), []byte(`-1.1`)},\n\t\t{\"AppendFloat64SmallPrecision(-1.123)\", \"AppendFloat64SmallPrecision\", float64(-1.123), []byte(`-1.1`)},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := w[tt.fn](tt.input); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"got %s, want %s\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAppendMAC(t *testing.T) {\n\tMACtests := []struct {\n\t\tinput string\n\t\twant  []byte\n\t}{\n\t\t{\"01:23:45:67:89:ab\", []byte(`\"01:23:45:67:89:ab\"`)},\n\t\t{\"cd:ef:11:22:33:44\", []byte(`\"cd:ef:11:22:33:44\"`)},\n\t}\n\tfor _, tt := range MACtests {\n\t\tt.Run(\"MAC\", func(t *testing.T) {\n\t\t\tha, _ := net.ParseMAC(tt.input)\n\t\t\tif got := enc.AppendMACAddr([]byte{}, ha); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"appendMACAddr() = %s, want %s\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAppendBool(t *testing.T) {\n\tfor _, tc := range internal.BooleanTestCases {\n\t\ts := enc.AppendBool([]byte{}, tc.Val)\n\t\tgot := string(s)\n\t\tif got != tc.Json {\n\t\t\tt.Errorf(\"AppendBool(%s)=0x%s, want: 0x%s\",\n\t\t\t\ttc.Json,\n\t\t\t\tstring(s),\n\t\t\t\tstring([]byte(tc.Binary)))\n\t\t}\n\t}\n}\n\nfunc TestAppendBoolArray(t *testing.T) {\n\tfor _, tc := range internal.BooleanArrayTestCases {\n\t\ts := enc.AppendBools([]byte{}, tc.Val)\n\t\tgot := string(s)\n\t\tif got != tc.Json {\n\t\t\tt.Errorf(\"AppendBools(%s)=0x%s, want: 0x%s\",\n\t\t\t\ttc.Json,\n\t\t\t\thex.EncodeToString(s),\n\t\t\t\thex.EncodeToString([]byte(tc.Binary)))\n\t\t}\n\t}\n\n\t// now empty array case\n\tarray := make([]bool, 0)\n\twant := make([]byte, 0)\n\twant = append(want, []byte(\"[]\")...) // start and end array\n\tgot := enc.AppendBools([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendBools(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray,\n\t\t\thex.EncodeToString(got),\n\t\t\thex.EncodeToString(want))\n\t}\n\n\t// now a large array case\n\tarray = make([]bool, 24)\n\twant = make([]byte, 0)\n\twant = append(want, []byte(\"[\")...) // start a large array\n\tfor i := 0; i < 24; i++ {\n\t\tarray[i] = bool(i%2 == 1)\n\t\tif array[i] {\n\t\t\twant = append(want, []byte(\"true\")...)\n\t\t} else {\n\t\t\twant = append(want, []byte(\"false\")...)\n\t\t}\n\t\tif (i + 1) < 24 {\n\t\t\twant = append(want, []byte(\",\")...)\n\t\t}\n\t}\n\twant = append(want, []byte(\"]\")...) // end a large array\n\n\tgot = enc.AppendBools([]byte{}, array)\n\tif !bytes.Equal(got, want) {\n\t\tt.Errorf(\"AppendBools(%v)\\ngot:  0x%s\\nwant: 0x%s\",\n\t\t\tarray,\n\t\t\tstring(got),\n\t\t\tstring(want))\n\t}\n}\n\nfunc TestAppendIP(t *testing.T) {\n\tIPv4tests := []struct {\n\t\tinput net.IP\n\t\twant  []byte\n\t}{\n\t\t{net.IP{0, 0, 0, 0}, []byte(`\"0.0.0.0\"`)},\n\t\t{net.IP{192, 0, 2, 200}, []byte(`\"192.0.2.200\"`)},\n\t}\n\n\tfor _, tt := range IPv4tests {\n\t\tt.Run(\"IPv4\", func(t *testing.T) {\n\t\t\tif got := enc.AppendIPAddr([]byte{}, tt.input); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"appendIPAddr() = %s, want %s\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n\tIPv6tests := []struct {\n\t\tinput net.IP\n\t\twant  []byte\n\t}{\n\t\t{net.IPv6zero, []byte(`\"::\"`)},\n\t\t{net.IPv6linklocalallnodes, []byte(`\"ff02::1\"`)},\n\t\t{net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34}, []byte(`\"2001:db8:85a3::8a2e:370:7334\"`)},\n\t}\n\tfor _, tt := range IPv6tests {\n\t\tt.Run(\"IPv6\", func(t *testing.T) {\n\t\t\tif got := enc.AppendIPAddr([]byte{}, tt.input); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"appendIPAddr() = %s, want %s\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nvar IPAddrArrayTestCases = []struct {\n\tinput []net.IP\n\twant  []byte\n}{\n\t{[]net.IP{}, []byte(`[]`)},\n\t{[]net.IP{{127, 0, 0, 0}}, []byte(`[\"127.0.0.0\"]`)},\n\t{[]net.IP{{0, 0, 0, 0}, {192, 168, 0, 100}}, []byte(`[\"0.0.0.0\",\"192.168.0.100\"]`)},\n}\n\nfunc TestAppendIPAddrs(t *testing.T) {\n\tfor _, tt := range IPAddrArrayTestCases {\n\t\tt.Run(\"IPAddrs\", func(t *testing.T) {\n\t\t\tif got := enc.AppendIPAddrs([]byte{}, tt.input); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"appendIPAddr() = %s, want %s\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nvar IPPrefixArrayTestCases = []struct {\n\tinput []net.IPNet\n\twant  []byte\n}{\n\t{[]net.IPNet{}, []byte(`[]`)},\n\t{[]net.IPNet{{IP: net.IP{127, 0, 0, 0}, Mask: net.CIDRMask(24, 32)}}, []byte(`[\"127.0.0.0/24\"]`)},\n\t{[]net.IPNet{{IP: net.IP{0, 0, 0, 0}, Mask: net.CIDRMask(0, 32)}, {IP: net.IP{192, 168, 0, 100}, Mask: net.CIDRMask(24, 32)}}, []byte(`[\"0.0.0.0/0\",\"192.168.0.100/24\"]`)},\n}\n\nfunc TestAppendIPPrefixes(t *testing.T) {\n\tfor _, tt := range IPPrefixArrayTestCases {\n\t\tt.Run(\"IPPrefixes\", func(t *testing.T) {\n\t\t\tif got := enc.AppendIPPrefixes([]byte{}, tt.input); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"appendIPAddr() = %s, want %s\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAppendIPPrefix(t *testing.T) {\n\tIPv4Prefixtests := []struct {\n\t\tinput net.IPNet\n\t\twant  []byte\n\t}{\n\t\t{net.IPNet{IP: net.IP{0, 0, 0, 0}, Mask: net.IPv4Mask(0, 0, 0, 0)}, []byte(`\"0.0.0.0/0\"`)},\n\t\t{net.IPNet{IP: net.IP{192, 0, 2, 200}, Mask: net.IPv4Mask(255, 255, 255, 0)}, []byte(`\"192.0.2.200/24\"`)},\n\t}\n\tfor _, tt := range IPv4Prefixtests {\n\t\tt.Run(\"IPv4\", func(t *testing.T) {\n\t\t\tif got := enc.AppendIPPrefix([]byte{}, tt.input); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"appendIPPrefix() = %s, want %s\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n\tIPv6Prefixtests := []struct {\n\t\tinput net.IPNet\n\t\twant  []byte\n\t}{\n\t\t{net.IPNet{IP: net.IPv6zero, Mask: net.CIDRMask(0, 128)}, []byte(`\"::/0\"`)},\n\t\t{net.IPNet{IP: net.IPv6linklocalallnodes, Mask: net.CIDRMask(128, 128)}, []byte(`\"ff02::1/128\"`)},\n\t\t{net.IPNet{IP: net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34},\n\t\t\tMask: net.CIDRMask(64, 128)},\n\t\t\t[]byte(`\"2001:db8:85a3::8a2e:370:7334/64\"`)},\n\t}\n\tfor _, tt := range IPv6Prefixtests {\n\t\tt.Run(\"IPv6\", func(t *testing.T) {\n\t\t\tif got := enc.AppendIPPrefix([]byte{}, tt.input); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"appendIPPrefix() = %s, want %s\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAppendMACAddr(t *testing.T) {\n\tMACtests := []struct {\n\t\tinput net.HardwareAddr\n\t\twant  []byte\n\t}{\n\t\t{net.HardwareAddr{0x12, 0x34, 0x56, 0x78, 0x90, 0xab}, []byte(`\"12:34:56:78:90:ab\"`)},\n\t\t{net.HardwareAddr{0x12, 0x34, 0x00, 0x00, 0x90, 0xab}, []byte(`\"12:34:00:00:90:ab\"`)},\n\t}\n\n\tfor _, tt := range MACtests {\n\t\tt.Run(\"MAC\", func(t *testing.T) {\n\t\t\tif got := enc.AppendMACAddr([]byte{}, tt.input); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"appendMAC() = %s, want %s\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAppendType2(t *testing.T) {\n\ttypeTests := []struct {\n\t\tlabel string\n\t\tinput interface{}\n\t\twant  []byte\n\t}{\n\t\t{\"int\", 42, []byte(`\"int\"`)},\n\t\t{\"MAC\", net.HardwareAddr{0x12, 0x34, 0x00, 0x00, 0x90, 0xab}, []byte(`\"net.HardwareAddr\"`)},\n\t\t{\"float64\", float64(2.50), []byte(`\"float64\"`)},\n\t\t{\"nil\", nil, []byte(`\"<nil>\"`)},\n\t\t{\"bool\", true, []byte(`\"bool\"`)},\n\t}\n\n\tfor _, tt := range typeTests {\n\t\tt.Run(tt.label, func(t *testing.T) {\n\t\t\tif got := enc.AppendType([]byte{}, tt.input); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"appendType() = %s, want %s\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAppendObjectData(t *testing.T) {\n\ttests := []struct {\n\t\tdst  []byte\n\t\tobj  []byte\n\t\twant []byte\n\t}{\n\t\t{[]byte{}, []byte(`{\"foo\":\"bar\"}`), []byte(`\"foo\":\"bar\"}`)},\n\t\t{[]byte(`{\"qux\":\"quz\"`), []byte(`{\"foo\":\"bar\"}`), []byte(`{\"qux\":\"quz\",\"foo\":\"bar\"}`)},\n\t\t{[]byte{}, []byte(`\"foo\":\"bar\"`), []byte(`\"foo\":\"bar\"`)},\n\t\t{[]byte(`{\"qux\":\"quz\"`), []byte(`\"foo\":\"bar\"`), []byte(`{\"qux\":\"quz\",\"foo\":\"bar\"`)},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(\"ObjectData\", func(t *testing.T) {\n\t\t\tif got := enc.AppendObjectData(tt.dst, tt.obj); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"appendObjectData() = %s, want %s\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/testcases.go",
    "content": "package internal\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"math\"\n\t\"net\"\n\t\"time\"\n)\n\nvar BooleanTestCases = []struct {\n\tVal    bool\n\tBinary string\n\tJson   string\n}{\n\t{true, \"\\xf5\", \"true\"},\n\t{false, \"\\xf4\", \"false\"},\n}\n\nvar BooleanArrayTestCases = []struct {\n\tVal    []bool\n\tBinary string\n\tJson   string\n}{\n\t{[]bool{}, \"\\x9f\\xff\", \"[]\"},\n\t{[]bool{false}, \"\\x81\\xf4\", \"[false]\"},\n\t{[]bool{true, false, true}, \"\\x83\\xf5\\xf4\\xf5\", \"[true,false,true]\"},\n\t{[]bool{true, false, false, true, false, true}, \"\\x86\\xf5\\xf4\\xf4\\xf5\\xf4\\xf5\", \"[true,false,false,true,false,true]\"},\n}\n\nvar IntegerTestCases = []struct {\n\tVal    int\n\tBinary string\n}{\n\t// Value included in the type.\n\t{0, \"\\x00\"},\n\t{1, \"\\x01\"},\n\t{2, \"\\x02\"},\n\t{3, \"\\x03\"},\n\t{8, \"\\x08\"},\n\t{9, \"\\x09\"},\n\t{10, \"\\x0a\"},\n\t{22, \"\\x16\"},\n\t{23, \"\\x17\"},\n\n\t// Value in 1 byte.\n\t{24, \"\\x18\\x18\"},\n\t{25, \"\\x18\\x19\"},\n\t{26, \"\\x18\\x1a\"},\n\t{127, \"\\x18\\x7f\"},\n\t{254, \"\\x18\\xfe\"},\n\t{255, \"\\x18\\xff\"},\n\n\t// Value in 2 bytes.\n\t{256, \"\\x19\\x01\\x00\"},\n\t{257, \"\\x19\\x01\\x01\"},\n\t{1000, \"\\x19\\x03\\xe8\"},\n\t{0xFFFF, \"\\x19\\xff\\xff\"},\n\n\t// Value in 4 bytes.\n\t{0x10000, \"\\x1a\\x00\\x01\\x00\\x00\"},\n\t{0x7FFFFFFE, \"\\x1a\\x7f\\xff\\xff\\xfe\"},\n\t{1000000, \"\\x1a\\x00\\x0f\\x42\\x40\"},\n\n\t// Negative number test cases.\n\t// Value included in the type.\n\t{-1, \"\\x20\"},\n\t{-2, \"\\x21\"},\n\t{-3, \"\\x22\"},\n\t{-10, \"\\x29\"},\n\t{-21, \"\\x34\"},\n\t{-22, \"\\x35\"},\n\t{-23, \"\\x36\"},\n\t{-24, \"\\x37\"},\n\n\t// Value in 1 byte.\n\t{-25, \"\\x38\\x18\"},\n\t{-26, \"\\x38\\x19\"},\n\t{-100, \"\\x38\\x63\"},\n\t{-128, \"\\x38\\x7f\"},\n\t{-254, \"\\x38\\xfd\"},\n\t{-255, \"\\x38\\xfe\"},\n\t{-256, \"\\x38\\xff\"},\n\n\t// Value in 2 bytes.\n\t{-257, \"\\x39\\x01\\x00\"},\n\t{-258, \"\\x39\\x01\\x01\"},\n\t{-1000, \"\\x39\\x03\\xe7\"},\n\n\t// Value in 4 bytes.\n\t{-0x10001, \"\\x3a\\x00\\x01\\x00\\x00\"},\n\t{-0x7FFFFFFE, \"\\x3a\\x7f\\xff\\xff\\xfd\"},\n\t{-1000000, \"\\x3a\\x00\\x0f\\x42\\x3f\"},\n\n\t//Constants\n\t{math.MaxInt8, \"\\x18\\x7f\"},\n\t{math.MinInt8, \"\\x38\\x7f\"},\n\t{math.MaxInt16, \"\\x19\\x7f\\xff\"},\n\t{math.MinInt16, \"\\x39\\x7f\\xff\"},\n\t{math.MaxInt32, \"\\x1a\\x7f\\xff\\xff\\xff\"},\n\t{math.MinInt32, \"\\x3a\\x7f\\xff\\xff\\xff\"},\n\t{math.MaxInt64, \"\\x1b\\x7f\\xff\\xff\\xff\\xff\\xff\\xff\\xff\"},\n\t{math.MinInt64, \"\\x3b\\x7f\\xff\\xff\\xff\\xff\\xff\\xff\\xff\"},\n}\n\ntype UnsignedIntTestCase struct {\n\tVal       uint\n\tBinary    string\n\tBigbinary string\n}\n\nvar AdditionalUnsignedIntegerTestCases = []UnsignedIntTestCase{\n\t{0x7FFFFFFF, \"\\x18\\xff\", \"\\x1a\\x7f\\xff\\xff\\xff\"},\n\t{0x80000000, \"\\x19\\xff\\xff\", \"\\x1a\\x80\\x00\\x00\\x00\"},\n\t{1000000, \"\\x1b\\x80\\x00\\x00\\x00\\x00\\x00\\x00\\x00\", \"\\x1a\\x00\\x0f\\x42\\x40\"},\n\n\t//Constants\n\t{math.MaxUint8, \"\\x18\\xff\", \"\\x18\\xff\"},\n\t{math.MaxUint16, \"\\x19\\xff\\xff\", \"\\x19\\xff\\xff\"},\n\t{math.MaxUint32, \"\\x1a\\xff\\xff\\xff\\xff\", \"\\x1a\\xff\\xff\\xff\\xff\"},\n\t{math.MaxUint64, \"\\x1b\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\", \"\\x1b\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\"},\n}\n\nfunc unsignedIntegerTestCases() []UnsignedIntTestCase {\n\tsize := len(IntegerTestCases) + len(AdditionalUnsignedIntegerTestCases)\n\tcases := make([]UnsignedIntTestCase, 0, size)\n\tcases = append(cases, AdditionalUnsignedIntegerTestCases...)\n\tfor _, itc := range IntegerTestCases {\n\t\tif itc.Val < 0 {\n\t\t\tcontinue\n\t\t}\n\t\tcases = append(cases, UnsignedIntTestCase{Val: uint(itc.Val), Binary: itc.Binary, Bigbinary: itc.Binary})\n\t}\n\treturn cases\n}\n\nvar UnsignedIntegerTestCases = unsignedIntegerTestCases()\n\nvar Float32TestCases = []struct {\n\tVal    float32\n\tBinary string\n}{\n\t{0.0, \"\\xfa\\x00\\x00\\x00\\x00\"},\n\t{-0.0, \"\\xfa\\x00\\x00\\x00\\x00\"},\n\t{1.0, \"\\xfa\\x3f\\x80\\x00\\x00\"},\n\t{1.5, \"\\xfa\\x3f\\xc0\\x00\\x00\"},\n\t{65504.0, \"\\xfa\\x47\\x7f\\xe0\\x00\"},\n\t{-4.0, \"\\xfa\\xc0\\x80\\x00\\x00\"},\n\t{0.00006103515625, \"\\xfa\\x38\\x80\\x00\\x00\"},\n\t{float32(math.Inf(0)), \"\\xfa\\x7f\\x80\\x00\\x00\"},\n\t{float32(math.Inf(-1)), \"\\xfa\\xff\\x80\\x00\\x00\"},\n\t{float32(math.NaN()), \"\\xfa\\x7f\\xc0\\x00\\x00\"},\n\t{math.SmallestNonzeroFloat32, \"\\xfa\\x00\\x00\\x00\\x01\"},\n\t{math.MaxFloat32, \"\\xfa\\x7f\\x7f\\xff\\xff\"},\n}\n\nvar Float64TestCases = []struct {\n\tVal    float64\n\tBinary string\n}{\n\t{0.0, \"\\xfa\\x00\\x00\\x00\\x00\"},\n\t{-0.0, \"\\xfa\\x00\\x00\\x00\\x00\"},\n\t{1.0, \"\\xfa\\x3f\\x80\\x00\\x00\"},\n\t{1.5, \"\\xfa\\x3f\\xc0\\x00\\x00\"},\n\t{65504.0, \"\\xfa\\x47\\x7f\\xe0\\x00\"},\n\t{-4.0, \"\\xfa\\xc0\\x80\\x00\\x00\"},\n\t{0.00006103515625, \"\\xfa\\x38\\x80\\x00\\x00\"},\n\t{math.Inf(0), \"\\xfa\\x7f\\x80\\x00\\x00\\x00\\x00\\x00\\x00\"},\n\t{math.Inf(-1), \"\\xfa\\xff\\x80\\x00\\x00\\x00\\x00\\x00\\x00\"},\n\t{math.NaN(), \"\\xfb\\x7f\\xf8\\x00\\x00\\x00\\x00\\x00\\x00\"},\n\t{math.SmallestNonzeroFloat64, \"\\xfa\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\"},\n\t{math.MaxFloat64, \"\\xfa\\x7f\\x7f\\xff\\xff\"},\n}\n\nvar IntegerArrayTestCases = []struct {\n\tVal    []int\n\tBinary string\n\tJson   string\n}{\n\t{[]int{}, \"\\x9f\\xff\", \"[]\"},\n\t{[]int{32768}, \"\\x81\\x19\\x80\\x00\", \"[32768]\"},\n\t{[]int{-1, 0, 200, 20}, \"\\x84\\x20\\x00\\x18\\xc8\\x14\", \"[-1,0,200,20]\"},\n\t{[]int{-200, -10, 200, 400}, \"\\x84\\x38\\xc7\\x29\\x18\\xc8\\x19\\x01\\x90\", \"[-200,-10,200,400]\"},\n\t{[]int{1, 2, 3}, \"\\x83\\x01\\x02\\x03\", \"[1,2,3]\"},\n\t{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25},\n\t\t\"\\x98\\x19\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x18\\x18\\x19\",\n\t\t\"[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]\"},\n}\n\nvar IpAddrTestCases = []struct {\n\tIpaddr net.IP\n\tText   string\n\tBinary string\n}{\n\t{net.IP{10, 0, 0, 1}, \"\\\"10.0.0.1\\\"\", \"\\xd9\\x01\\x04\\x44\\x0a\\x00\\x00\\x01\"},\n\t{net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x0, 0x0, 0x0, 0x0, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34},\n\t\t\"\\\"2001:db8:85a3::8a2e:370:7334\\\"\",\n\t\t\"\\xd9\\x01\\x04\\x50\\x20\\x01\\x0d\\xb8\\x85\\xa3\\x00\\x00\\x00\\x00\\x8a\\x2e\\x03\\x70\\x73\\x34\"},\n}\n\nvar IPAddrArrayTestCases = []struct {\n\tVal    []net.IP\n\tBinary string\n\tJson   string\n}{\n\t{[]net.IP{}, \"\\x9f\\xff\", \"[]\"},\n\t{[]net.IP{{127, 0, 0, 0}}, \"\\x81\\xd9\\x01\\x04\\x44\\x7f\\x00\\x00\\x00\", \"[127.0.0.0]\"},\n\t{[]net.IP{{0, 0, 0, 0}, {192, 168, 0, 100}}, \"\\x82\\xd9\\x01\\x04\\x44\\x00\\x00\\x00\\x00\\xd9\\x01\\x04\\x44\\xc0\\xa8\\x00\\x64\", \"[0.0.0.0,192.168.0.100]\"},\n}\n\nvar IPPrefixTestCases = []struct {\n\tPfx    net.IPNet\n\tText   string // ASCII representation of pfx\n\tBinary string // CBOR representation of pfx\n}{\n\t{net.IPNet{IP: net.IP{0, 0, 0, 0}, Mask: net.CIDRMask(0, 32)}, \"\\\"0.0.0.0/0\\\"\", \"\\xd9\\x01\\x05\\xa1\\x44\\x00\\x00\\x00\\x00\\x00\"},\n\t{net.IPNet{IP: net.IP{192, 168, 0, 100}, Mask: net.CIDRMask(24, 32)}, \"\\\"192.168.0.100/24\\\"\",\n\t\t\"\\xd9\\x01\\x05\\xa1\\x44\\xc0\\xa8\\x00\\x64\\x18\\x18\"},\n\t{net.IPNet{IP: net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, Mask: net.CIDRMask(128, 128)}, \"\\\"::1/128\\\"\",\n\t\t\"\\xd9\\x01\\x05\\xa1\\x50\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x18\\x80\"},\n}\n\nvar IPPrefixArrayTestCases = []struct {\n\tVal    []net.IPNet\n\tBinary string\n\tJson   string\n}{\n\t{[]net.IPNet{}, \"\\x9f\\xff\", \"[]\"},\n\t{[]net.IPNet{{IP: net.IP{127, 0, 0, 0}, Mask: net.CIDRMask(24, 32)}}, \"\\x81\\xd9\\x01\\x05\\xa1\\x44\\x7f\\x00\\x00\\x00\\x18\\x18\", \"[127.0.0.0/24]\"},\n\t{[]net.IPNet{{IP: net.IP{0, 0, 0, 0}, Mask: net.CIDRMask(0, 32)}, {IP: net.IP{192, 168, 0, 100}, Mask: net.CIDRMask(24, 32)}}, \"\\x82\\xd9\\x01\\x05\\xa1\\x44\\x00\\x00\\x00\\x00\\x00\\xd9\\x01\\x05\\xa1\\x44\\xc0\\xa8\\x00\\x64\\x18\\x18\", \"[0.0.0.0/0,192.168.0.100/24]\"},\n}\n\nvar MacAddrTestCases = []struct {\n\tMacaddr net.HardwareAddr\n\tText    string // ASCII representation of macaddr\n\tBinary  string // CBOR representation of macaddr\n}{\n\t{net.HardwareAddr{0x12, 0x34, 0x56, 0x78, 0x90, 0xab}, \"\\\"12:34:56:78:90:ab\\\"\", \"\\xd9\\x01\\x04\\x46\\x12\\x34\\x56\\x78\\x90\\xab\"},\n\t{net.HardwareAddr{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3}, \"\\\"20:01:0d:b8:85:a3\\\"\", \"\\xd9\\x01\\x04\\x46\\x20\\x01\\x0d\\xb8\\x85\\xa3\"},\n}\n\nvar EncodeHexTests = []struct {\n\tIn  byte\n\tOut string\n}{\n\t{0x00, `\"00\"`},\n\t{0x0f, `\"0f\"`},\n\t{0x10, `\"10\"`},\n\t{0xf0, `\"f0\"`},\n\t{0xff, `\"ff\"`},\n}\n\nvar EncodeStringTests = []struct {\n\tIn  string\n\tOut string\n}{\n\t{\"\", `\"\"`},\n\t{\"\\\\\", `\"\\\\\"`},\n\t{\"\\x00\", `\"\\u0000\"`},\n\t{\"\\x01\", `\"\\u0001\"`},\n\t{\"\\x02\", `\"\\u0002\"`},\n\t{\"\\x03\", `\"\\u0003\"`},\n\t{\"\\x04\", `\"\\u0004\"`},\n\t{\"\\x05\", `\"\\u0005\"`},\n\t{\"\\x06\", `\"\\u0006\"`},\n\t{\"\\x07\", `\"\\u0007\"`},\n\t{\"\\x08\", `\"\\b\"`},\n\t{\"\\x09\", `\"\\t\"`},\n\t{\"\\x0a\", `\"\\n\"`},\n\t{\"\\x0b\", `\"\\u000b\"`},\n\t{\"\\x0c\", `\"\\f\"`},\n\t{\"\\x0d\", `\"\\r\"`},\n\t{\"\\x0e\", `\"\\u000e\"`},\n\t{\"\\x0f\", `\"\\u000f\"`},\n\t{\"\\x10\", `\"\\u0010\"`},\n\t{\"\\x11\", `\"\\u0011\"`},\n\t{\"\\x12\", `\"\\u0012\"`},\n\t{\"\\x13\", `\"\\u0013\"`},\n\t{\"\\x14\", `\"\\u0014\"`},\n\t{\"\\x15\", `\"\\u0015\"`},\n\t{\"\\x16\", `\"\\u0016\"`},\n\t{\"\\x17\", `\"\\u0017\"`},\n\t{\"\\x18\", `\"\\u0018\"`},\n\t{\"\\x19\", `\"\\u0019\"`},\n\t{\"\\x1a\", `\"\\u001a\"`},\n\t{\"\\x1b\", `\"\\u001b\"`},\n\t{\"\\x1c\", `\"\\u001c\"`},\n\t{\"\\x1d\", `\"\\u001d\"`},\n\t{\"\\x1e\", `\"\\u001e\"`},\n\t{\"\\x1f\", `\"\\u001f\"`},\n\t{\"✭\", `\"✭\"`},\n\t{\"foo\\xc2\\x7fbar\", `\"foo\\ufffd\\u007fbar\"`}, // invalid sequence\n\t{\"ascii\", `\"ascii\"`},\n\t{\"\\\"a\", `\"\\\"a\"`},\n\t{\"\\x1fa\", `\"\\u001fa\"`},\n\t{\"foo\\\"bar\\\"baz\", `\"foo\\\"bar\\\"baz\"`},\n\t{\"\\x1ffoo\\x1fbar\\x1fbaz\", `\"\\u001ffoo\\u001fbar\\u001fbaz\"`},\n\t{\"emoji \\u2764\\ufe0f!\", `\"emoji ❤️!\"`},\n}\n\nvar EncodeStringsTests = []struct {\n\tIn  []string\n\tOut string\n}{\n\t{nil, `[]`},\n\t{[]string{}, `[]`},\n\t{[]string{\"A\"}, `[\"A\"]`},\n\t{[]string{\"A\", \"B\"}, `[\"A\",\"B\"]`},\n}\n\nvar EncodeStringerTests = []struct {\n\tIn     fmt.Stringer\n\tOut    string\n\tBinary string\n}{\n\t{nil, `null`, \"\\xf6\"},\n\t{fmt.Stringer(nil), `null`, \"\\xf6\"},\n\t{net.IPv4bcast, `\"255.255.255.255\"`, \"\\x6f\\x32\\x35\\x35\\x2e\\x32\\x35\\x35\\x2e\\x32\\x35\\x35\\x2e\\x32\\x35\\x35\"},\n}\n\nvar EncodeStringersTests = []struct {\n\tIn     []fmt.Stringer\n\tOut    string\n\tBinary string\n}{\n\t{nil, `[]`, \"\\x9f\\xff\"},\n\t{[]fmt.Stringer{}, `[]`, \"\\x9f\\xff\"},\n\t{[]fmt.Stringer{net.IPv4bcast}, `[\"255.255.255.255\"]`, \"\\x9f\\x6f255.255.255.255\\xff\"},\n\t{[]fmt.Stringer{net.IPv4allsys, net.IPv4allrouter}, `[\"224.0.0.1\",\"224.0.0.2\"]`, \"\\x9f\\x69224.0.0.1\\x69224.0.0.2\\xff\"},\n}\n\nvar TimeIntegerTestcases = []struct {\n\tTxt     string\n\tBinary  string\n\tRfcStr  string\n\tUnixInt int\n}{\n\t{\"2013-02-03T19:54:00-08:00\", \"\\xc1\\x1a\\x51\\x0f\\x30\\xd8\", \"2013-02-04T03:54:00Z\", 1359950040},\n\t{\"1950-02-03T19:54:00-08:00\", \"\\xc1\\x3a\\x25\\x71\\x93\\xa7\", \"1950-02-04T03:54:00Z\", -628200360},\n}\n\nvar TimeFloatTestcases = []struct {\n\tRfcStr  string\n\tOut     string\n\tUnixInt int\n}{\n\t{\"2006-01-02T15:04:05.999999-08:00\", \"\\xc1\\xfb\\x41\\xd0\\xee\\x6c\\x59\\x7f\\xff\\xfc\", 1136243045},\n\t{\"1956-01-02T15:04:05.999999-08:00\", \"\\xc1\\xfb\\xc1\\xba\\x53\\x81\\x1a\\x00\\x00\\x11\", -441680155},\n}\n\nvar DurTestcases = []struct {\n\tDuration   time.Duration\n\tFloatOut   string\n\tIntegerOut string\n}{\n\t{1000, \"\\xfb\\x3f\\xf0\\x00\\x00\\x00\\x00\\x00\\x00\", \"\\x01\"},\n\t{2000, \"\\xfb\\x40\\x00\\x00\\x00\\x00\\x00\\x00\\x00\", \"\\x02\"},\n\t{200000, \"\\xfb\\x40\\x69\\x00\\x00\\x00\\x00\\x00\\x00\", \"\\x18\\xc8\"},\n}\n\n// inline copy from globals.go of InterfaceMarshalFunc used in tests to avoid import cycle\nfunc InterfaceMarshalFunc(v interface{}) ([]byte, error) {\n\tvar buf bytes.Buffer\n\tencoder := json.NewEncoder(&buf)\n\tencoder.SetEscapeHTML(false)\n\terr := encoder.Encode(v)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tb := buf.Bytes()\n\tif len(b) > 0 {\n\t\t// Remove trailing \\n which is added by Encode.\n\t\treturn b[:len(b)-1], nil\n\t}\n\treturn b, nil\n}\n"
  },
  {
    "path": "journald/journald.go",
    "content": "//go:build !windows\n// +build !windows\n\n// Package journald provides a io.Writer to send the logs\n// to journalD component of systemd.\n\npackage journald\n\n// This file provides a zerolog writer so that logs printed\n// using zerolog library can be sent to a journalD.\n\n// Zerolog's Top level key/Value Pairs are translated to\n// journald's args - all Values are sent to journald as strings.\n// And all key strings are converted to uppercase before sending\n// to journald (as required by journald).\n\n// In addition, entire log message (all Key Value Pairs), is also\n// sent to journald under the key \"JSON\".\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\n\t\"github.com/coreos/go-systemd/v22/journal\"\n\t\"github.com/rs/zerolog\"\n\t\"github.com/rs/zerolog/internal/cbor\"\n)\n\nconst defaultJournalDPrio = journal.PriNotice\n\n// NewJournalDWriter returns a zerolog log destination\n// to be used as parameter to New() calls. Writing logs\n// to this writer will send the log messages to journalD\n// running in this system.\nfunc NewJournalDWriter() io.Writer {\n\treturn journalWriter{}\n}\n\ntype journalWriter struct {\n}\n\n// levelToJPrio converts zerolog Level string into\n// journalD's priority values. JournalD has more\n// priorities than zerolog.\nfunc levelToJPrio(zLevel string) journal.Priority {\n\tlvl, _ := zerolog.ParseLevel(zLevel)\n\n\tswitch lvl {\n\tcase zerolog.TraceLevel:\n\t\treturn journal.PriDebug\n\tcase zerolog.DebugLevel:\n\t\treturn journal.PriDebug\n\tcase zerolog.InfoLevel:\n\t\treturn journal.PriInfo\n\tcase zerolog.WarnLevel:\n\t\treturn journal.PriWarning\n\tcase zerolog.ErrorLevel:\n\t\treturn journal.PriErr\n\tcase zerolog.FatalLevel:\n\t\treturn journal.PriCrit\n\tcase zerolog.PanicLevel:\n\t\treturn journal.PriEmerg\n\tcase zerolog.NoLevel:\n\t\treturn journal.PriNotice\n\t}\n\treturn defaultJournalDPrio\n}\n\nfunc (w journalWriter) Write(p []byte) (n int, err error) {\n\tvar event map[string]interface{}\n\torigPLen := len(p)\n\tp = cbor.DecodeIfBinaryToBytes(p)\n\td := json.NewDecoder(bytes.NewReader(p))\n\td.UseNumber()\n\terr = d.Decode(&event)\n\tjPrio := defaultJournalDPrio\n\targs := make(map[string]string)\n\tif err != nil {\n\t\treturn\n\t}\n\tif l, ok := event[zerolog.LevelFieldName].(string); ok {\n\t\tjPrio = levelToJPrio(l)\n\t}\n\n\tmsg := \"\"\n\tfor key, value := range event {\n\t\tjKey := strings.ToUpper(key)\n\t\tswitch key {\n\t\tcase zerolog.LevelFieldName, zerolog.TimestampFieldName:\n\t\t\tcontinue\n\t\tcase zerolog.MessageFieldName:\n\t\t\tmsg, _ = value.(string)\n\t\t\tcontinue\n\t\t}\n\n\t\tswitch v := value.(type) {\n\t\tcase string:\n\t\t\targs[jKey] = v\n\t\tcase json.Number:\n\t\t\targs[jKey] = fmt.Sprint(value)\n\t\tdefault:\n\t\t\tb, err := zerolog.InterfaceMarshalFunc(value)\n\t\t\tif err != nil {\n\t\t\t\targs[jKey] = fmt.Sprintf(\"[error: %v]\", err)\n\t\t\t} else {\n\t\t\t\targs[jKey] = string(b)\n\t\t\t}\n\t\t}\n\t}\n\targs[\"JSON\"] = string(p)\n\terr = journal.Send(msg, jPrio, args)\n\n\tif err == nil {\n\t\tn = origPLen\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "journald/journald_test.go",
    "content": "// +build linux\n\npackage journald_test\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"testing\"\n\n\t\"github.com/rs/zerolog\"\n\t\"github.com/rs/zerolog/journald\"\n)\n\nfunc ExampleNewJournalDWriter() {\n\tlog := zerolog.New(journald.NewJournalDWriter())\n\tlog.Info().Str(\"foo\", \"bar\").Uint64(\"small\", 123).Float64(\"float\", 3.14).Uint64(\"big\", 1152921504606846976).Msg(\"Journal Test\")\n\t// Output:\n}\n\n/*\n\nThere is no automated way to verify the output - since the output is sent\nto journald process and method to retrieve is journalctl. Will find a way\nto automate the process and fix this test.\n\n$ journalctl -o verbose -f\n\nThu 2018-04-26 22:30:20.768136 PDT [s=3284d695bde946e4b5017c77a399237f;i=329f0;b=98c0dca0debc4b98a5b9534e910e7dd6;m=7a702e35dd4;t=56acdccd2ed0a;x=4690034cf0348614]\n    PRIORITY=6\n    _AUDIT_LOGINUID=1000\n    _BOOT_ID=98c0dca0debc4b98a5b9534e910e7dd6\n    _MACHINE_ID=926ed67eb4744580948de70fb474975e\n    _HOSTNAME=sprint\n    _UID=1000\n    _GID=1000\n    _CAP_EFFECTIVE=0\n    _SYSTEMD_SLICE=-.slice\n    _TRANSPORT=journal\n    _SYSTEMD_CGROUP=/\n    _AUDIT_SESSION=2945\n    MESSAGE=Journal Test\n    FOO=bar\n    BIG=1152921504606846976\n    _COMM=journald.test\n    SMALL=123\n    FLOAT=3.14\n    JSON={\"level\":\"info\",\"foo\":\"bar\",\"small\":123,\"float\":3.14,\"big\":1152921504606846976,\"message\":\"Journal Test\"}\n    _PID=27103\n    _SOURCE_REALTIME_TIMESTAMP=1524807020768136\n*/\n\nfunc TestWriteReturnsNoOfWrittenBytes(t *testing.T) {\n\tinput := []byte(`{\"level\":\"info\",\"time\":1570912626,\"message\":\"Starting...\"}`)\n\twr := journald.NewJournalDWriter()\n\twant := len(input)\n\tgot, err := wr.Write(input)\n\n\tif err != nil {\n\t\tt.Errorf(\"Unexpected error %v\", err)\n\t}\n\n\tif want != got {\n\t\tt.Errorf(\"Expected %d bytes to be written got %d\", want, got)\n\t}\n}\n\nfunc TestMultiWrite(t *testing.T) {\n\tvar (\n\t\tw1 = new(bytes.Buffer)\n\t\tw2 = new(bytes.Buffer)\n\t\tw3 = journald.NewJournalDWriter()\n\t)\n\n\tzerolog.ErrorHandler = func(err error) {\n\t\tif err == io.ErrShortWrite {\n\t\t\tt.Errorf(\"Unexpected ShortWriteError\")\n\t\t\tt.FailNow()\n\t\t}\n\t}\n\n\tlog := zerolog.New(io.MultiWriter(w1, w2, w3)).With().Logger()\n\n\tfor i := 0; i < 10; i++ {\n\t\tlog.Info().Msg(\"Tick!\")\n\t}\n}\n"
  },
  {
    "path": "log/log.go",
    "content": "// Package log provides a global logger for zerolog.\npackage log\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\n\t\"github.com/rs/zerolog\"\n)\n\n// Logger is the global logger.\nvar Logger = zerolog.New(os.Stderr).With().Timestamp().Logger()\n\n// Output duplicates the global logger and sets w as its output.\nfunc Output(w io.Writer) zerolog.Logger {\n\treturn Logger.Output(w)\n}\n\n// With creates a child logger with the field added to its context.\nfunc With() zerolog.Context {\n\treturn Logger.With()\n}\n\n// Level creates a child logger with the minimum accepted level set to level.\nfunc Level(level zerolog.Level) zerolog.Logger {\n\treturn Logger.Level(level)\n}\n\n// Sample returns a logger with the s sampler.\nfunc Sample(s zerolog.Sampler) zerolog.Logger {\n\treturn Logger.Sample(s)\n}\n\n// Hook returns a logger with the h Hook.\nfunc Hook(h zerolog.Hook) zerolog.Logger {\n\treturn Logger.Hook(h)\n}\n\n// Err starts a new message with error level with err as a field if not nil or\n// with info level if err is nil.\n//\n// You must call Msg on the returned event in order to send the event.\nfunc Err(err error) *zerolog.Event {\n\treturn Logger.Err(err)\n}\n\n// Trace starts a new message with trace level.\n//\n// You must call Msg on the returned event in order to send the event.\nfunc Trace() *zerolog.Event {\n\treturn Logger.Trace()\n}\n\n// Debug starts a new message with debug level.\n//\n// You must call Msg on the returned event in order to send the event.\nfunc Debug() *zerolog.Event {\n\treturn Logger.Debug()\n}\n\n// Info starts a new message with info level.\n//\n// You must call Msg on the returned event in order to send the event.\nfunc Info() *zerolog.Event {\n\treturn Logger.Info()\n}\n\n// Warn starts a new message with warn level.\n//\n// You must call Msg on the returned event in order to send the event.\nfunc Warn() *zerolog.Event {\n\treturn Logger.Warn()\n}\n\n// Error starts a new message with error level.\n//\n// You must call Msg on the returned event in order to send the event.\nfunc Error() *zerolog.Event {\n\treturn Logger.Error()\n}\n\n// Fatal starts a new message with fatal level. The os.Exit(1) function\n// is called by the Msg method.\n//\n// You must call Msg on the returned event in order to send the event.\nfunc Fatal() *zerolog.Event {\n\treturn Logger.Fatal()\n}\n\n// Panic starts a new message with panic level. The message is also sent\n// to the panic function.\n//\n// You must call Msg on the returned event in order to send the event.\nfunc Panic() *zerolog.Event {\n\treturn Logger.Panic()\n}\n\n// WithLevel starts a new message with level.\n//\n// You must call Msg on the returned event in order to send the event.\nfunc WithLevel(level zerolog.Level) *zerolog.Event {\n\treturn Logger.WithLevel(level)\n}\n\n// Log starts a new message with no level. Setting zerolog.GlobalLevel to\n// zerolog.Disabled will still disable events produced by this method.\n//\n// You must call Msg on the returned event in order to send the event.\nfunc Log() *zerolog.Event {\n\treturn Logger.Log()\n}\n\n// Print sends a log event using debug level and no extra field.\n// Arguments are handled in the manner of fmt.Print.\nfunc Print(v ...interface{}) {\n\tLogger.Debug().CallerSkipFrame(1).Msg(fmt.Sprint(v...))\n}\n\n// Printf sends a log event using debug level and no extra field.\n// Arguments are handled in the manner of fmt.Printf.\nfunc Printf(format string, v ...interface{}) {\n\tLogger.Debug().CallerSkipFrame(1).Msgf(format, v...)\n}\n\n// Ctx returns the Logger associated with the ctx. If no logger\n// is associated, a disabled logger is returned.\nfunc Ctx(ctx context.Context) *zerolog.Logger {\n\treturn zerolog.Ctx(ctx)\n}\n"
  },
  {
    "path": "log/log_example_test.go",
    "content": "//go:build !binary_log\n// +build !binary_log\n\npackage log_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"flag\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/rs/zerolog\"\n\t\"github.com/rs/zerolog/log\"\n)\n\n// setup would normally be an init() function, however, there seems\n// to be something awry with the testing framework when we set the\n// global Logger from an init()\nfunc setup() {\n\t// UNIX Time is faster and smaller than most timestamps\n\t// If you set zerolog.TimeFieldFormat to an empty string,\n\t// logs will write with UNIX time\n\tzerolog.TimeFieldFormat = \"\"\n\t// In order to always output a static time to stdout for these\n\t// examples to pass, we need to override zerolog.TimestampFunc\n\t// and log.Logger globals -- you would not normally need to do this\n\tzerolog.TimestampFunc = func() time.Time {\n\t\treturn time.Date(2008, 1, 8, 17, 5, 05, 0, time.UTC)\n\t}\n\tlog.Logger = zerolog.New(os.Stdout).With().Timestamp().Logger()\n}\n\n// Simple logging example using the Print function in the log package\n// Note that both Print and Printf are at the debug log level by default\nfunc ExamplePrint() {\n\tsetup()\n\n\tlog.Print(\"hello world\")\n\t// Output: {\"level\":\"debug\",\"time\":1199811905,\"message\":\"hello world\"}\n}\n\n// Simple logging example using the Printf function in the log package\nfunc ExamplePrintf() {\n\tsetup()\n\n\tlog.Printf(\"hello %s\", \"world\")\n\t// Output: {\"level\":\"debug\",\"time\":1199811905,\"message\":\"hello world\"}\n}\n\n// Example of a log with no particular \"level\"\nfunc ExampleLog() {\n\tsetup()\n\tlog.Log().Msg(\"hello world\")\n\n\t// Output: {\"time\":1199811905,\"message\":\"hello world\"}\n}\n\n// Example of a conditional level based on the presence of an error.\nfunc ExampleErr() {\n\tsetup()\n\terr := errors.New(\"some error\")\n\tlog.Err(err).Msg(\"hello world\")\n\tlog.Err(nil).Msg(\"hello world\")\n\n\t// Output: {\"level\":\"error\",\"error\":\"some error\",\"time\":1199811905,\"message\":\"hello world\"}\n\t// {\"level\":\"info\",\"time\":1199811905,\"message\":\"hello world\"}\n}\n\n// Example of a log at a particular \"level\" (in this case, \"trace\")\nfunc ExampleTrace() {\n\tsetup()\n\tlog.Trace().Msg(\"hello world\")\n\n\t// Output: {\"level\":\"trace\",\"time\":1199811905,\"message\":\"hello world\"}\n}\n\n// Example of a log at a particular \"level\" (in this case, \"debug\")\nfunc ExampleDebug() {\n\tsetup()\n\tlog.Debug().Msg(\"hello world\")\n\n\t// Output: {\"level\":\"debug\",\"time\":1199811905,\"message\":\"hello world\"}\n}\n\n// Example of a log at a particular \"level\" (in this case, \"info\")\nfunc ExampleInfo() {\n\tsetup()\n\tlog.Info().Msg(\"hello world\")\n\n\t// Output: {\"level\":\"info\",\"time\":1199811905,\"message\":\"hello world\"}\n}\n\n// Example of a log at a particular \"level\" (in this case, \"warn\")\nfunc ExampleWarn() {\n\tsetup()\n\tlog.Warn().Msg(\"hello world\")\n\n\t// Output: {\"level\":\"warn\",\"time\":1199811905,\"message\":\"hello world\"}\n}\n\n// Example of a log at a particular \"level\" (in this case, \"error\")\nfunc ExampleError() {\n\tsetup()\n\tlog.Error().Msg(\"hello world\")\n\n\t// Output: {\"level\":\"error\",\"time\":1199811905,\"message\":\"hello world\"}\n}\n\n// Example of a log at a particular \"level\" (in this case, \"fatal\")\nfunc ExampleFatal() {\n\tsetup()\n\terr := errors.New(\"A repo man spends his life getting into tense situations\")\n\tservice := \"myservice\"\n\n\tlog.Fatal().\n\t\tErr(err).\n\t\tStr(\"service\", service).\n\t\tMsgf(\"Cannot start %s\", service)\n\n\t// Outputs: {\"level\":\"fatal\",\"time\":1199811905,\"error\":\"A repo man spends his life getting into tense situations\",\"service\":\"myservice\",\"message\":\"Cannot start myservice\"}\n}\n\n// Example of a log at a particular \"level\" (in this case, \"panic\")\nfunc ExamplePanic() {\n\tsetup()\n\n\tlog.Panic().Msg(\"Cannot start\")\n\t// Outputs: {\"level\":\"panic\",\"time\":1199811905,\"message\":\"Cannot start\"} then panics\n}\n\n// This example uses command-line flags to demonstrate various outputs\n// depending on the chosen log level.\nfunc Example() {\n\tsetup()\n\tdebug := flag.Bool(\"debug\", false, \"sets log level to debug\")\n\n\tflag.Parse()\n\n\t// Default level for this example is info, unless debug flag is present\n\tzerolog.SetGlobalLevel(zerolog.InfoLevel)\n\tif *debug {\n\t\tzerolog.SetGlobalLevel(zerolog.DebugLevel)\n\t}\n\n\tlog.Debug().Msg(\"This message appears only when log level set to Debug\")\n\tlog.Info().Msg(\"This message appears when log level set to Debug or Info\")\n\n\tif e := log.Debug(); e.Enabled() {\n\t\t// Compute log output only if enabled.\n\t\tvalue := \"bar\"\n\t\te.Str(\"foo\", value).Msg(\"some debug message\")\n\t}\n\n\t// Output: {\"level\":\"info\",\"time\":1199811905,\"message\":\"This message appears when log level set to Debug or Info\"}\n}\n\n// Example of using the Output function in the log package to change the output destination\nfunc ExampleOutput() {\n\tsetup()\n\n\tout := &bytes.Buffer{}\n\ttee := log.Output(out)\n\ttee.Info().Msg(\"hello world\")\n\twritten := out.Len()\n\n\tlog.Info().Int(\"bytes\", written).Msg(\"wrote\")\n\t// Output: {\"level\":\"info\",\"bytes\":59,\"time\":1199811905,\"message\":\"wrote\"}\n}\n\n// Example of using the With function to add context fields\nfunc ExampleWith() {\n\tsetup()\n\n\t// you have to assign the result of With() to a new Logger and can't inline the level calls\n\t// because they need a *Logger receiver\n\taugmented := log.With().Str(\"service\", \"myservice\").Logger()\n\taugmented.Info().Msg(\"hello world\")\n\t// Output: {\"level\":\"info\",\"service\":\"myservice\",\"time\":1199811905,\"message\":\"hello world\"}\n}\n\n// Example of using the Level function to set the log level\nfunc ExampleLevel() {\n\tsetup()\n\n\t// you have to assign the result of Level() to a new Logger and can't inline the level calls\n\t// because they need a *Logger receiver\n\tleveled := log.Level(zerolog.ErrorLevel)\n\tleveled.Info().Msg(\"hello world\")\n\tleveled.Error().Msg(\"I said HELLO\")\n\t// Output: {\"level\":\"error\",\"time\":1199811905,\"message\":\"I said HELLO\"}\n}\n\ntype valueKeyType int\n\nvar valueKey valueKeyType = 42\n\nvar captainHook = zerolog.HookFunc(func(e *zerolog.Event, l zerolog.Level, msg string) {\n\te.Interface(\"key\", e.GetCtx().Value(valueKey))\n\te.Bool(\"is_error\", l > zerolog.ErrorLevel)\n\te.Int(\"msg_len\", len(msg))\n})\n\n// Example of using the Logger Hook function to add hooks\nfunc ExampleLogger_Hook() {\n\tsetup()\n\n\thooked := log.Hook(captainHook)\n\thooked.Info().Msg(\"watch out!\")\n\t// Output: {\"level\":\"info\",\"time\":1199811905,\"key\":null,\"is_error\":false,\"msg_len\":10,\"message\":\"watch out!\"}\n}\n\n// Example of using the WithLevel function to set the log level\nfunc ExampleWithLevel() {\n\tsetup()\n\n\t// you have to assign the result of Level() to a new Logger and can't inline the level calls\n\t// because they need a *Logger receiver\n\tevent := log.WithLevel(zerolog.ErrorLevel)\n\tevent.Msg(\"taxes are due\")\n\t// Output: {\"level\":\"error\",\"time\":1199811905,\"message\":\"taxes are due\"}\n}\n\n// Example of using the Ctx function in the log package to log with context\nfunc ExampleCtx() {\n\tsetup()\n\n\thooked := log.Hook(captainHook)\n\tctx := context.WithValue(context.Background(), valueKey, \"12345\")\n\tlogger := hooked.With().Ctx(ctx).Logger()\n\tlog.Ctx(logger.WithContext(ctx)).Info().Msg(\"hello world\")\n\t// Output: {\"level\":\"info\",\"time\":1199811905,\"key\":\"12345\",\"is_error\":false,\"msg_len\":11,\"message\":\"hello world\"}\n}\n\n// Example of using the Sample function in the log package to set a sampler\nfunc ExampleSample() {\n\tsetup()\n\n\tsampled := log.Sample(&zerolog.BasicSampler{N: 2})\n\tsampled.Info().Msg(\"hello world\")\n\tsampled.Info().Msg(\"I said, hello world\")\n\tsampled.Info().Msg(\"Can you here me now world\")\n\t// Output: {\"level\":\"info\",\"time\":1199811905,\"message\":\"hello world\"}\n\t// {\"level\":\"info\",\"time\":1199811905,\"message\":\"Can you here me now world\"}\n}\n"
  },
  {
    "path": "log.go",
    "content": "// Package zerolog provides a lightweight logging library dedicated to JSON logging.\n//\n// A global Logger can be use for simple logging:\n//\n//\timport \"github.com/rs/zerolog/log\"\n//\n//\tlog.Info().Msg(\"hello world\")\n//\t// Output: {\"time\":1494567715,\"level\":\"info\",\"message\":\"hello world\"}\n//\n// NOTE: To import the global logger, import the \"log\" subpackage \"github.com/rs/zerolog/log\".\n//\n// Fields can be added to log messages:\n//\n//\tlog.Info().Str(\"foo\", \"bar\").Msg(\"hello world\")\n//\t// Output: {\"time\":1494567715,\"level\":\"info\",\"message\":\"hello world\",\"foo\":\"bar\"}\n//\n// Create logger instance to manage different outputs:\n//\n//\tlogger := zerolog.New(os.Stderr).With().Timestamp().Logger()\n//\tlogger.Info().\n//\t       Str(\"foo\", \"bar\").\n//\t       Msg(\"hello world\")\n//\t// Output: {\"time\":1494567715,\"level\":\"info\",\"message\":\"hello world\",\"foo\":\"bar\"}\n//\n// Sub-loggers let you chain loggers with additional context:\n//\n//\tsublogger := log.With().Str(\"component\", \"foo\").Logger()\n//\tsublogger.Info().Msg(\"hello world\")\n//\t// Output: {\"time\":1494567715,\"level\":\"info\",\"message\":\"hello world\",\"component\":\"foo\"}\n//\n// Level logging\n//\n//\tzerolog.SetGlobalLevel(zerolog.InfoLevel)\n//\n//\tlog.Debug().Msg(\"filtered out message\")\n//\tlog.Info().Msg(\"routed message\")\n//\n//\tif e := log.Debug(); e.Enabled() {\n//\t    // Compute log output only if enabled.\n//\t    value := compute()\n//\t    e.Str(\"foo\": value).Msg(\"some debug message\")\n//\t}\n//\t// Output: {\"level\":\"info\",\"time\":1494567715,\"routed message\"}\n//\n// Customize automatic field names:\n//\n//\tlog.TimestampFieldName = \"t\"\n//\tlog.LevelFieldName = \"p\"\n//\tlog.MessageFieldName = \"m\"\n//\n//\tlog.Info().Msg(\"hello world\")\n//\t// Output: {\"t\":1494567715,\"p\":\"info\",\"m\":\"hello world\"}\n//\n// Log with no level and message:\n//\n//\tlog.Log().Str(\"foo\",\"bar\").Msg(\"\")\n//\t// Output: {\"time\":1494567715,\"foo\":\"bar\"}\n//\n// Add contextual fields to global Logger:\n//\n//\tlog.Logger = log.With().Str(\"foo\", \"bar\").Logger()\n//\n// Sample logs:\n//\n//\tsampled := log.Sample(&zerolog.BasicSampler{N: 10})\n//\tsampled.Info().Msg(\"will be logged every 10 messages\")\n//\n// Log with contextual hooks:\n//\n//\t// Create the hook:\n//\ttype SeverityHook struct{}\n//\n//\tfunc (h SeverityHook) Run(e *zerolog.Event, level zerolog.Level, msg string) {\n//\t     if level != zerolog.NoLevel {\n//\t         e.Str(\"severity\", level.String())\n//\t     }\n//\t}\n//\n//\t// And use it:\n//\tvar h SeverityHook\n//\tlog := zerolog.New(os.Stdout).Hook(h)\n//\tlog.Warn().Msg(\"\")\n//\t// Output: {\"level\":\"warn\",\"severity\":\"warn\"}\n//\n// # Caveats\n//\n// Field duplication:\n//\n// There is no fields deduplication out-of-the-box.\n// Using the same key multiple times creates new key in final JSON each time.\n//\n//\tlogger := zerolog.New(os.Stderr).With().Timestamp().Logger()\n//\tlogger.Info().\n//\t       Timestamp().\n//\t       Msg(\"dup\")\n//\t// Output: {\"level\":\"info\",\"time\":1494567715,\"time\":1494567715,\"message\":\"dup\"}\n//\n// In this case, many consumers will take the last value,\n// but this is not guaranteed; check yours if in doubt.\n//\n// Concurrency safety:\n//\n// Be careful when calling UpdateContext. It is not concurrency safe. Use the With method to create a child logger:\n//\n//\tfunc handler(w http.ResponseWriter, r *http.Request) {\n//\t    // Create a child logger for concurrency safety\n//\t    logger := log.Logger.With().Logger()\n//\n//\t    // Add context fields, for example User-Agent from HTTP headers\n//\t    logger.UpdateContext(func(c zerolog.Context) zerolog.Context {\n//\t        ...\n//\t    })\n//\t}\npackage zerolog\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// Level defines log levels.\ntype Level int8\n\nconst (\n\t// DebugLevel defines debug log level.\n\tDebugLevel Level = iota\n\t// InfoLevel defines info log level.\n\tInfoLevel\n\t// WarnLevel defines warn log level.\n\tWarnLevel\n\t// ErrorLevel defines error log level.\n\tErrorLevel\n\t// FatalLevel defines fatal log level.\n\tFatalLevel\n\t// PanicLevel defines panic log level.\n\tPanicLevel\n\t// NoLevel defines an absent log level.\n\tNoLevel\n\t// Disabled disables the logger.\n\tDisabled\n\n\t// TraceLevel defines trace log level.\n\tTraceLevel Level = -1\n\t// Values less than TraceLevel are handled as numbers.\n)\n\nfunc (l Level) String() string {\n\tswitch l {\n\tcase TraceLevel:\n\t\treturn LevelTraceValue\n\tcase DebugLevel:\n\t\treturn LevelDebugValue\n\tcase InfoLevel:\n\t\treturn LevelInfoValue\n\tcase WarnLevel:\n\t\treturn LevelWarnValue\n\tcase ErrorLevel:\n\t\treturn LevelErrorValue\n\tcase FatalLevel:\n\t\treturn LevelFatalValue\n\tcase PanicLevel:\n\t\treturn LevelPanicValue\n\tcase Disabled:\n\t\treturn \"disabled\"\n\tcase NoLevel:\n\t\treturn \"\"\n\t}\n\treturn strconv.Itoa(int(l))\n}\n\n// ParseLevel converts a level string into a zerolog Level value.\n// returns an error if the input string does not match known values.\nfunc ParseLevel(levelStr string) (Level, error) {\n\tswitch {\n\tcase strings.EqualFold(levelStr, LevelFieldMarshalFunc(TraceLevel)):\n\t\treturn TraceLevel, nil\n\tcase strings.EqualFold(levelStr, LevelFieldMarshalFunc(DebugLevel)):\n\t\treturn DebugLevel, nil\n\tcase strings.EqualFold(levelStr, LevelFieldMarshalFunc(InfoLevel)):\n\t\treturn InfoLevel, nil\n\tcase strings.EqualFold(levelStr, LevelFieldMarshalFunc(WarnLevel)):\n\t\treturn WarnLevel, nil\n\tcase strings.EqualFold(levelStr, LevelFieldMarshalFunc(ErrorLevel)):\n\t\treturn ErrorLevel, nil\n\tcase strings.EqualFold(levelStr, LevelFieldMarshalFunc(FatalLevel)):\n\t\treturn FatalLevel, nil\n\tcase strings.EqualFold(levelStr, LevelFieldMarshalFunc(PanicLevel)):\n\t\treturn PanicLevel, nil\n\tcase strings.EqualFold(levelStr, LevelFieldMarshalFunc(Disabled)):\n\t\treturn Disabled, nil\n\tcase strings.EqualFold(levelStr, LevelFieldMarshalFunc(NoLevel)):\n\t\treturn NoLevel, nil\n\t}\n\ti, err := strconv.Atoi(levelStr)\n\tif err != nil {\n\t\treturn NoLevel, fmt.Errorf(\"Unknown Level String: '%s', defaulting to NoLevel\", levelStr)\n\t}\n\tif i > 127 || i < -128 {\n\t\treturn NoLevel, fmt.Errorf(\"Out-Of-Bounds Level: '%d', defaulting to NoLevel\", i)\n\t}\n\treturn Level(i), nil\n}\n\n// UnmarshalText implements encoding.TextUnmarshaler to allow for easy reading from toml/yaml/json formats\nfunc (l *Level) UnmarshalText(text []byte) error {\n\tif l == nil {\n\t\treturn errors.New(\"can't unmarshal a nil *Level\")\n\t}\n\tvar err error\n\t*l, err = ParseLevel(string(text))\n\treturn err\n}\n\n// MarshalText implements encoding.TextMarshaler to allow for easy writing into toml/yaml/json formats\nfunc (l Level) MarshalText() ([]byte, error) {\n\treturn []byte(LevelFieldMarshalFunc(l)), nil\n}\n\n// A Logger represents an active logging object that generates lines\n// of JSON output to an io.Writer. Each logging operation makes a single\n// call to the Writer's Write method. There is no guarantee on access\n// serialization to the Writer. If your Writer is not thread safe,\n// you may consider a sync wrapper.\ntype Logger struct {\n\tw       LevelWriter\n\tlevel   Level\n\tsampler Sampler\n\tcontext []byte\n\thooks   []Hook\n\tstack   bool\n\tctx     context.Context\n}\n\n// New creates a root logger with given output writer. If the output writer implements\n// the LevelWriter interface, the WriteLevel method will be called instead of the Write\n// one.\n//\n// Each logging operation makes a single call to the Writer's Write method. There is no\n// guarantee on access serialization to the Writer. If your Writer is not thread safe,\n// you may consider using sync wrapper.\nfunc New(w io.Writer) Logger {\n\tif w == nil {\n\t\tw = io.Discard\n\t}\n\tlw, ok := w.(LevelWriter)\n\tif !ok {\n\t\tlw = LevelWriterAdapter{w}\n\t}\n\treturn Logger{w: lw, level: TraceLevel}\n}\n\n// Nop returns a disabled logger for which all operation are no-op.\nfunc Nop() Logger {\n\treturn New(nil).Level(Disabled)\n}\n\n// Output duplicates the current logger and sets w as its output.\nfunc (l Logger) Output(w io.Writer) Logger {\n\tl2 := New(w)\n\tl2.level = l.level\n\tl2.sampler = l.sampler\n\tl2.stack = l.stack\n\tif len(l.hooks) > 0 {\n\t\tl2.hooks = append(l2.hooks, l.hooks...)\n\t}\n\tif l.context != nil {\n\t\tl2.context = make([]byte, len(l.context), cap(l.context))\n\t\tcopy(l2.context, l.context)\n\t}\n\treturn l2\n}\n\n// With creates a child logger with the field added to its context.\nfunc (l Logger) With() Context {\n\tcontext := l.context\n\tl.context = make([]byte, 0, 500)\n\tif context != nil {\n\t\tl.context = append(l.context, context...)\n\t} else {\n\t\t// This is needed for AppendKey to not check len of input\n\t\t// thus making it inlinable\n\t\tl.context = enc.AppendBeginMarker(l.context)\n\t}\n\treturn Context{l}\n}\n\n// UpdateContext updates the internal logger's context.\n//\n// Caution: This method is not concurrency safe.\n// Use the With method to create a child logger before modifying the context from concurrent goroutines.\nfunc (l *Logger) UpdateContext(update func(c Context) Context) {\n\tif l.disabled() {\n\t\treturn\n\t}\n\tif cap(l.context) == 0 {\n\t\tl.context = make([]byte, 0, 500)\n\t}\n\tif len(l.context) == 0 {\n\t\tl.context = enc.AppendBeginMarker(l.context)\n\t}\n\tc := update(Context{*l})\n\tl.context = c.l.context\n}\n\n// Level creates a child logger with the minimum accepted level set to level.\nfunc (l Logger) Level(lvl Level) Logger {\n\tl.level = lvl\n\treturn l\n}\n\n// GetLevel returns the current Level of l.\nfunc (l Logger) GetLevel() Level {\n\treturn l.level\n}\n\n// Sample returns a logger with the s sampler.\nfunc (l Logger) Sample(s Sampler) Logger {\n\tl.sampler = s\n\treturn l\n}\n\n// Hook returns a logger with the h Hook.\nfunc (l Logger) Hook(hooks ...Hook) Logger {\n\tif len(hooks) == 0 {\n\t\treturn l\n\t}\n\tnewHooks := make([]Hook, len(l.hooks), len(l.hooks)+len(hooks))\n\tcopy(newHooks, l.hooks)\n\tl.hooks = append(newHooks, hooks...)\n\treturn l\n}\n\n// Trace starts a new message with trace level.\n//\n// You must call Msg on the returned event in order to send the event.\nfunc (l *Logger) Trace() *Event {\n\treturn l.newEvent(TraceLevel, nil)\n}\n\n// Debug starts a new message with debug level.\n//\n// You must call Msg on the returned event in order to send the event.\nfunc (l *Logger) Debug() *Event {\n\treturn l.newEvent(DebugLevel, nil)\n}\n\n// Info starts a new message with info level.\n//\n// You must call Msg on the returned event in order to send the event.\nfunc (l *Logger) Info() *Event {\n\treturn l.newEvent(InfoLevel, nil)\n}\n\n// Warn starts a new message with warn level.\n//\n// You must call Msg on the returned event in order to send the event.\nfunc (l *Logger) Warn() *Event {\n\treturn l.newEvent(WarnLevel, nil)\n}\n\n// Error starts a new message with error level.\n//\n// You must call Msg on the returned event in order to send the event.\nfunc (l *Logger) Error() *Event {\n\treturn l.newEvent(ErrorLevel, nil)\n}\n\n// Err starts a new message with error level with err as a field if not nil or\n// with info level if err is nil.\n//\n// You must call Msg on the returned event in order to send the event.\nfunc (l *Logger) Err(err error) *Event {\n\tif err != nil {\n\t\treturn l.Error().Err(err)\n\t}\n\n\treturn l.Info()\n}\n\n// Fatal starts a new message with fatal level. The FatalExitFunc interceptor function\n// is called by the Msg method, which by default terminates the program immediately\n// using os.Exit(1), any desired behavior can be implemented by setting FatalExitFunc.\n//\n// You must call Msg on the returned event in order to send the event.\nfunc (l *Logger) Fatal() *Event {\n\treturn l.newEvent(FatalLevel, func(msg string) {\n\t\tif closer, ok := l.w.(io.Closer); ok {\n\t\t\t// Close the writer to flush any buffered message. Otherwise the message\n\t\t\t// could be lost if FatalExitFunc() terminates the program immediately or\n\t\t\t// os.Exit(1) is called if not FatalExitFunc isn't set (default).\n\t\t\tcloser.Close()\n\t\t}\n\t\tif FatalExitFunc != nil {\n\t\t\tFatalExitFunc()\n\t\t} else {\n\t\t\tos.Exit(1) // untestable: terminates the program, cannot be covered\n\t\t}\n\t})\n}\n\n// Panic starts a new message with panic level. The panic() function\n// is called by the Msg method, which stops the ordinary flow of a goroutine.\n//\n// You must call Msg on the returned event in order to send the event.\nfunc (l *Logger) Panic() *Event {\n\treturn l.newEvent(PanicLevel, func(msg string) { panic(msg) })\n}\n\n// WithLevel starts a new message with level. Unlike Fatal and Panic\n// methods, WithLevel does not terminate the program or stop the ordinary\n// flow of a goroutine when used with their respective levels.\n//\n// You must call Msg on the returned event in order to send the event.\nfunc (l *Logger) WithLevel(level Level) *Event {\n\tswitch level {\n\tcase TraceLevel:\n\t\treturn l.Trace()\n\tcase DebugLevel:\n\t\treturn l.Debug()\n\tcase InfoLevel:\n\t\treturn l.Info()\n\tcase WarnLevel:\n\t\treturn l.Warn()\n\tcase ErrorLevel:\n\t\treturn l.Error()\n\tcase FatalLevel:\n\t\treturn l.newEvent(FatalLevel, nil)\n\tcase PanicLevel:\n\t\treturn l.newEvent(PanicLevel, nil)\n\tcase NoLevel:\n\t\treturn l.Log()\n\tcase Disabled:\n\t\treturn nil\n\tdefault:\n\t\treturn l.newEvent(level, nil)\n\t}\n}\n\n// Log starts a new message with no level. Setting GlobalLevel to Disabled\n// will still disable events produced by this method.\n//\n// You must call Msg on the returned event in order to send the event.\nfunc (l *Logger) Log() *Event {\n\treturn l.newEvent(NoLevel, nil)\n}\n\n// Print sends a log event using debug level and no extra field.\n// Arguments are handled in the manner of fmt.Print.\nfunc (l *Logger) Print(v ...interface{}) {\n\tif e := l.Debug(); e.Enabled() {\n\t\te.CallerSkipFrame(1).Msg(fmt.Sprint(v...))\n\t}\n}\n\n// Printf sends a log event using debug level and no extra field.\n// Arguments are handled in the manner of fmt.Printf.\nfunc (l *Logger) Printf(format string, v ...interface{}) {\n\tif e := l.Debug(); e.Enabled() {\n\t\te.CallerSkipFrame(1).Msg(fmt.Sprintf(format, v...))\n\t}\n}\n\n// Println sends a log event using debug level and no extra field.\n// Arguments are handled in the manner of fmt.Println.\nfunc (l *Logger) Println(v ...interface{}) {\n\tif e := l.Debug(); e.Enabled() {\n\t\te.CallerSkipFrame(1).Msg(fmt.Sprintln(v...))\n\t}\n}\n\n// Write implements the io.Writer interface. This is useful to set as a writer\n// for the standard library log.\nfunc (l Logger) Write(p []byte) (n int, err error) {\n\tn = len(p)\n\tif n > 0 && p[n-1] == '\\n' {\n\t\t// Trim CR added by stdlog.\n\t\tp = p[0 : n-1]\n\t}\n\tl.Log().CallerSkipFrame(1).Msg(string(p))\n\treturn\n}\n\nfunc (l *Logger) newEvent(level Level, done func(string)) *Event {\n\tenabled := l.should(level)\n\tif !enabled {\n\t\tif done != nil {\n\t\t\tdone(\"\")\n\t\t}\n\t\treturn nil\n\t}\n\te := newEvent(l.w, level, l.stack, l.ctx, l.hooks)\n\te.done = done\n\tif level != NoLevel && LevelFieldName != \"\" {\n\t\te.Str(LevelFieldName, LevelFieldMarshalFunc(level))\n\t}\n\tif len(l.context) > 1 {\n\t\te.buf = enc.AppendObjectData(e.buf, l.context)\n\t}\n\treturn e\n}\n\nfunc (l *Logger) scratchEvent() *Event {\n\treturn newEvent(LevelWriterAdapter{io.Discard}, DebugLevel, l.stack, l.ctx, l.hooks)\n}\n\n// disabled returns true if the logger is a disabled or nop logger.\nfunc (l *Logger) disabled() bool {\n\treturn l.w == nil || l.level == Disabled\n}\n\n// should returns true if the log event should be logged.\nfunc (l *Logger) should(lvl Level) bool {\n\tif l.disabled() {\n\t\treturn false\n\t}\n\tif lvl < l.level || lvl < GlobalLevel() {\n\t\treturn false\n\t}\n\tif l.sampler != nil && !samplingDisabled() {\n\t\treturn l.sampler.Sample(lvl)\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "log_example_test.go",
    "content": "// +build !binary_log\n\npackage zerolog_test\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\tstdlog \"log\"\n\t\"net\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/rs/zerolog\"\n)\n\nfunc ExampleNew() {\n\tlog := zerolog.New(os.Stdout)\n\n\tlog.Info().Msg(\"hello world\")\n\t// Output: {\"level\":\"info\",\"message\":\"hello world\"}\n}\n\nfunc ExampleLogger_With() {\n\tlog := zerolog.New(os.Stdout).\n\t\tWith().\n\t\tStr(\"foo\", \"bar\").\n\t\tLogger()\n\n\tlog.Info().Msg(\"hello world\")\n\n\t// Output: {\"level\":\"info\",\"foo\":\"bar\",\"message\":\"hello world\"}\n}\n\nfunc ExampleLogger_Level() {\n\tlog := zerolog.New(os.Stdout).Level(zerolog.WarnLevel)\n\n\tlog.Info().Msg(\"filtered out message\")\n\tlog.Error().Msg(\"kept message\")\n\n\t// Output: {\"level\":\"error\",\"message\":\"kept message\"}\n}\n\nfunc ExampleLogger_Sample() {\n\tlog := zerolog.New(os.Stdout).Sample(&zerolog.BasicSampler{N: 2})\n\n\tlog.Info().Msg(\"message 1\")\n\tlog.Info().Msg(\"message 2\")\n\tlog.Info().Msg(\"message 3\")\n\tlog.Info().Msg(\"message 4\")\n\n\t// Output: {\"level\":\"info\",\"message\":\"message 1\"}\n\t// {\"level\":\"info\",\"message\":\"message 3\"}\n}\n\ntype LevelNameHook struct{}\n\nfunc (h LevelNameHook) Run(e *zerolog.Event, l zerolog.Level, msg string) {\n\tif l != zerolog.NoLevel {\n\t\te.Str(\"level_name\", l.String())\n\t} else {\n\t\te.Str(\"level_name\", \"NoLevel\")\n\t}\n}\n\ntype MessageHook string\n\nfunc (h MessageHook) Run(e *zerolog.Event, l zerolog.Level, msg string) {\n\te.Str(\"the_message\", msg)\n}\n\nfunc ExampleLogger_Hook() {\n\tvar levelNameHook LevelNameHook\n\tvar messageHook MessageHook = \"The message\"\n\n\tlog := zerolog.New(os.Stdout).Hook(levelNameHook, messageHook)\n\n\tlog.Info().Msg(\"hello world\")\n\n\t// Output: {\"level\":\"info\",\"level_name\":\"info\",\"the_message\":\"hello world\",\"message\":\"hello world\"}\n}\n\nfunc ExampleLogger_Print() {\n\tlog := zerolog.New(os.Stdout)\n\n\tlog.Print(\"hello world\")\n\n\t// Output: {\"level\":\"debug\",\"message\":\"hello world\"}\n}\n\nfunc ExampleLogger_Printf() {\n\tlog := zerolog.New(os.Stdout)\n\n\tlog.Printf(\"hello %s\", \"world\")\n\n\t// Output: {\"level\":\"debug\",\"message\":\"hello world\"}\n}\n\nfunc ExampleLogger_Println() {\n\tlog := zerolog.New(os.Stdout)\n\n\tlog.Println(\"hello world\")\n\n\t// Output: {\"level\":\"debug\",\"message\":\"hello world\\n\"}\n}\n\nfunc ExampleLogger_Trace() {\n\tlog := zerolog.New(os.Stdout)\n\n\tlog.Trace().\n\t\tStr(\"foo\", \"bar\").\n\t\tInt(\"n\", 123).\n\t\tMsg(\"hello world\")\n\n\t// Output: {\"level\":\"trace\",\"foo\":\"bar\",\"n\":123,\"message\":\"hello world\"}\n}\n\nfunc ExampleLogger_Debug() {\n\tlog := zerolog.New(os.Stdout)\n\n\tlog.Debug().\n\t\tStr(\"foo\", \"bar\").\n\t\tInt(\"n\", 123).\n\t\tMsg(\"hello world\")\n\n\t// Output: {\"level\":\"debug\",\"foo\":\"bar\",\"n\":123,\"message\":\"hello world\"}\n}\n\nfunc ExampleLogger_Info() {\n\tlog := zerolog.New(os.Stdout)\n\n\tlog.Info().\n\t\tStr(\"foo\", \"bar\").\n\t\tInt(\"n\", 123).\n\t\tMsg(\"hello world\")\n\n\t// Output: {\"level\":\"info\",\"foo\":\"bar\",\"n\":123,\"message\":\"hello world\"}\n}\n\nfunc ExampleLogger_Warn() {\n\tlog := zerolog.New(os.Stdout)\n\n\tlog.Warn().\n\t\tStr(\"foo\", \"bar\").\n\t\tMsg(\"a warning message\")\n\n\t// Output: {\"level\":\"warn\",\"foo\":\"bar\",\"message\":\"a warning message\"}\n}\n\nfunc ExampleLogger_Error() {\n\tlog := zerolog.New(os.Stdout)\n\n\tlog.Error().\n\t\tErr(errors.New(\"some error\")).\n\t\tMsg(\"error doing something\")\n\n\t// Output: {\"level\":\"error\",\"error\":\"some error\",\"message\":\"error doing something\"}\n}\n\nfunc ExampleLogger_WithLevel() {\n\tlog := zerolog.New(os.Stdout)\n\n\tlog.WithLevel(zerolog.InfoLevel).\n\t\tMsg(\"hello world\")\n\n\t// Output: {\"level\":\"info\",\"message\":\"hello world\"}\n}\n\nfunc ExampleLogger_Write() {\n\tlog := zerolog.New(os.Stdout).With().\n\t\tStr(\"foo\", \"bar\").\n\t\tLogger()\n\n\tstdlog.SetFlags(0)\n\tstdlog.SetOutput(log)\n\n\tstdlog.Print(\"hello world\")\n\n\t// Output: {\"foo\":\"bar\",\"message\":\"hello world\"}\n}\n\nfunc ExampleLogger_Log() {\n\tlog := zerolog.New(os.Stdout)\n\n\tlog.Log().\n\t\tStr(\"foo\", \"bar\").\n\t\tStr(\"bar\", \"baz\").\n\t\tMsg(\"\")\n\n\t// Output: {\"foo\":\"bar\",\"bar\":\"baz\"}\n}\n\nfunc ExampleEvent_Dict() {\n\tlog := zerolog.New(os.Stdout)\n\n\te := log.Log().\n\t\tStr(\"foo\", \"bar\")\n\n\te.Dict(\"dict\", e.CreateDict().\n\t\tStr(\"bar\", \"baz\").\n\t\tInt(\"n\", 1),\n\t).\n\t\tMsg(\"hello world\")\n\n\t// Output: {\"foo\":\"bar\",\"dict\":{\"bar\":\"baz\",\"n\":1},\"message\":\"hello world\"}\n}\n\ntype User struct {\n\tName    string\n\tAge     int\n\tCreated time.Time\n}\n\nfunc (u User) MarshalZerologObject(e *zerolog.Event) {\n\te.Str(\"name\", u.Name).\n\t\tInt(\"age\", u.Age).\n\t\tTime(\"created\", u.Created)\n}\n\ntype Price struct {\n\tval  uint64\n\tprec int\n\tunit string\n}\n\nfunc (p Price) MarshalZerologObject(e *zerolog.Event) {\n\tdenom := uint64(1)\n\tfor i := 0; i < p.prec; i++ {\n\t\tdenom *= 10\n\t}\n\tresult := []byte(p.unit)\n\tresult = append(result, fmt.Sprintf(\"%d.%d\", p.val/denom, p.val%denom)...)\n\te.Str(\"price\", string(result))\n}\n\ntype Users []User\n\nfunc (uu Users) MarshalZerologArray(a *zerolog.Array) {\n\tfor _, u := range uu {\n\t\ta.Object(u)\n\t}\n}\n\nfunc ExampleEvent_Array() {\n\tlog := zerolog.New(os.Stdout)\n\n\te := log.Log().\n\t\tStr(\"foo\", \"bar\")\n\n\te.Array(\"array\", e.CreateArray().\n\t\tStr(\"baz\").\n\t\tInt(1).\n\t\tDict(e.CreateDict().\n\t\t\tStr(\"bar\", \"baz\").\n\t\t\tInt(\"n\", 1),\n\t\t),\n\t).\n\t\tMsg(\"hello world\")\n\n\t// Output: {\"foo\":\"bar\",\"array\":[\"baz\",1,{\"bar\":\"baz\",\"n\":1}],\"message\":\"hello world\"}\n}\n\nfunc ExampleEvent_Array_object() {\n\tlog := zerolog.New(os.Stdout)\n\n\t// Users implements zerolog.LogArrayMarshaler\n\tu := Users{\n\t\tUser{\"John\", 35, time.Time{}},\n\t\tUser{\"Bob\", 55, time.Time{}},\n\t}\n\n\tlog.Log().\n\t\tStr(\"foo\", \"bar\").\n\t\tArray(\"users\", u).\n\t\tMsg(\"hello world\")\n\n\t// Output: {\"foo\":\"bar\",\"users\":[{\"name\":\"John\",\"age\":35,\"created\":\"0001-01-01T00:00:00Z\"},{\"name\":\"Bob\",\"age\":55,\"created\":\"0001-01-01T00:00:00Z\"}],\"message\":\"hello world\"}\n}\n\nfunc ExampleEvent_Object() {\n\tlog := zerolog.New(os.Stdout)\n\n\t// User implements zerolog.LogObjectMarshaler\n\tu := User{\"John\", 35, time.Time{}}\n\n\tlog.Log().\n\t\tStr(\"foo\", \"bar\").\n\t\tObject(\"user\", u).\n\t\tMsg(\"hello world\")\n\n\t// Output: {\"foo\":\"bar\",\"user\":{\"name\":\"John\",\"age\":35,\"created\":\"0001-01-01T00:00:00Z\"},\"message\":\"hello world\"}\n}\n\nfunc ExampleEvent_EmbedObject() {\n\tlog := zerolog.New(os.Stdout)\n\n\tprice := Price{val: 6449, prec: 2, unit: \"$\"}\n\n\tlog.Log().\n\t\tStr(\"foo\", \"bar\").\n\t\tEmbedObject(price).\n\t\tMsg(\"hello world\")\n\n\t// Output: {\"foo\":\"bar\",\"price\":\"$64.49\",\"message\":\"hello world\"}\n}\n\nfunc ExampleEvent_Interface() {\n\tlog := zerolog.New(os.Stdout)\n\n\tobj := struct {\n\t\tName string `json:\"name\"`\n\t}{\n\t\tName: \"john\",\n\t}\n\n\tlog.Log().\n\t\tStr(\"foo\", \"bar\").\n\t\tInterface(\"obj\", obj).\n\t\tMsg(\"hello world\")\n\n\t// Output: {\"foo\":\"bar\",\"obj\":{\"name\":\"john\"},\"message\":\"hello world\"}\n}\n\nfunc ExampleEvent_Dur() {\n\td := 10 * time.Second\n\n\tlog := zerolog.New(os.Stdout)\n\n\tlog.Log().\n\t\tStr(\"foo\", \"bar\").\n\t\tDur(\"dur\", d).\n\t\tMsg(\"hello world\")\n\n\t// Output: {\"foo\":\"bar\",\"dur\":10000,\"message\":\"hello world\"}\n}\n\nfunc ExampleEvent_Durs() {\n\td := []time.Duration{\n\t\t10 * time.Second,\n\t\t20 * time.Second,\n\t}\n\n\tlog := zerolog.New(os.Stdout)\n\n\tlog.Log().\n\t\tStr(\"foo\", \"bar\").\n\t\tDurs(\"durs\", d).\n\t\tMsg(\"hello world\")\n\n\t// Output: {\"foo\":\"bar\",\"durs\":[10000,20000],\"message\":\"hello world\"}\n}\n\nfunc ExampleEvent_Fields_map() {\n\tfields := map[string]interface{}{\n\t\t\"bar\": \"baz\",\n\t\t\"n\":   1,\n\t}\n\n\tlog := zerolog.New(os.Stdout)\n\n\tlog.Log().\n\t\tStr(\"foo\", \"bar\").\n\t\tFields(fields).\n\t\tMsg(\"hello world\")\n\n\t// Output: {\"foo\":\"bar\",\"bar\":\"baz\",\"n\":1,\"message\":\"hello world\"}\n}\n\nfunc ExampleEvent_Fields_slice() {\n\tfields := []interface{}{\n\t\t\"bar\", \"baz\",\n\t\t\"n\", 1,\n\t}\n\n\tlog := zerolog.New(os.Stdout)\n\n\tlog.Log().\n\t\tStr(\"foo\", \"bar\").\n\t\tFields(fields).\n\t\tMsg(\"hello world\")\n\n\t// Output: {\"foo\":\"bar\",\"bar\":\"baz\",\"n\":1,\"message\":\"hello world\"}\n}\n\nfunc ExampleContext_Dict() {\n\tctx := zerolog.New(os.Stdout).With().\n\t\tStr(\"foo\", \"bar\")\n\n\tlogger := ctx.Dict(\"dict\", ctx.CreateDict().\n\t\tStr(\"bar\", \"baz\").\n\t\tInt(\"n\", 1),\n\t).Logger()\n\n\tlogger.Log().Msg(\"hello world\")\n\n\t// Output: {\"foo\":\"bar\",\"dict\":{\"bar\":\"baz\",\"n\":1},\"message\":\"hello world\"}\n}\n\nfunc ExampleContext_Array() {\n\tctx := zerolog.New(os.Stdout).With().\n\t\tStr(\"foo\", \"bar\")\n\n\tlogger := ctx.Array(\"array\", ctx.CreateArray().\n\t\tStr(\"baz\").\n\t\tInt(1),\n\t).Logger()\n\n\tlogger.Log().Msg(\"hello world\")\n\n\t// Output: {\"foo\":\"bar\",\"array\":[\"baz\",1],\"message\":\"hello world\"}\n}\n\nfunc ExampleContext_Array_object() {\n\t// Users implements zerolog.LogArrayMarshaler\n\tu := Users{\n\t\tUser{\"John\", 35, time.Time{}},\n\t\tUser{\"Bob\", 55, time.Time{}},\n\t}\n\n\tlog := zerolog.New(os.Stdout).With().\n\t\tStr(\"foo\", \"bar\").\n\t\tArray(\"users\", u).\n\t\tLogger()\n\n\tlog.Log().Msg(\"hello world\")\n\n\t// Output: {\"foo\":\"bar\",\"users\":[{\"name\":\"John\",\"age\":35,\"created\":\"0001-01-01T00:00:00Z\"},{\"name\":\"Bob\",\"age\":55,\"created\":\"0001-01-01T00:00:00Z\"}],\"message\":\"hello world\"}\n}\n\nfunc ExampleContext_Object() {\n\t// User implements zerolog.LogObjectMarshaler\n\tu := User{\"John\", 35, time.Time{}}\n\n\tlog := zerolog.New(os.Stdout).With().\n\t\tStr(\"foo\", \"bar\").\n\t\tObject(\"user\", u).\n\t\tLogger()\n\n\tlog.Log().Msg(\"hello world\")\n\n\t// Output: {\"foo\":\"bar\",\"user\":{\"name\":\"John\",\"age\":35,\"created\":\"0001-01-01T00:00:00Z\"},\"message\":\"hello world\"}\n}\nfunc ExampleContext_Objects() {\n\t// User implements zerolog.LogObjectMarshaler\n\tu := User{\"John\", 35, time.Time{}}\n\tu2 := User{\"Bono\", 54, time.Time{}}\n\n\tlog := zerolog.New(os.Stdout).With().\n\t\tStr(\"foo\", \"bar\").\n\t\tObjects(\"users\", []zerolog.LogObjectMarshaler{u, u2}).\n\t\tLogger()\n\n\tlog.Log().Msg(\"hello world\")\n\n\t// Output: {\"foo\":\"bar\",\"users\":[{\"name\":\"John\",\"age\":35,\"created\":\"0001-01-01T00:00:00Z\"},{\"name\":\"Bono\",\"age\":54,\"created\":\"0001-01-01T00:00:00Z\"}],\"message\":\"hello world\"}\n}\n\nfunc ExampleContext_EmbedObject() {\n\n\tprice := Price{val: 6449, prec: 2, unit: \"$\"}\n\n\tlog := zerolog.New(os.Stdout).With().\n\t\tStr(\"foo\", \"bar\").\n\t\tEmbedObject(price).\n\t\tLogger()\n\n\tlog.Log().Msg(\"hello world\")\n\n\t// Output: {\"foo\":\"bar\",\"price\":\"$64.49\",\"message\":\"hello world\"}\n}\n\nfunc ExampleContext_Interface() {\n\tobj := struct {\n\t\tName string `json:\"name\"`\n\t}{\n\t\tName: \"john\",\n\t}\n\n\tlog := zerolog.New(os.Stdout).With().\n\t\tStr(\"foo\", \"bar\").\n\t\tInterface(\"obj\", obj).\n\t\tLogger()\n\n\tlog.Log().Msg(\"hello world\")\n\n\t// Output: {\"foo\":\"bar\",\"obj\":{\"name\":\"john\"},\"message\":\"hello world\"}\n}\n\nfunc ExampleContext_Dur() {\n\td := 10 * time.Second\n\n\tlog := zerolog.New(os.Stdout).With().\n\t\tStr(\"foo\", \"bar\").\n\t\tDur(\"dur\", d).\n\t\tLogger()\n\n\tlog.Log().Msg(\"hello world\")\n\n\t// Output: {\"foo\":\"bar\",\"dur\":10000,\"message\":\"hello world\"}\n}\n\nfunc ExampleContext_Durs() {\n\td := []time.Duration{\n\t\t10 * time.Second,\n\t\t20 * time.Second,\n\t}\n\n\tlog := zerolog.New(os.Stdout).With().\n\t\tStr(\"foo\", \"bar\").\n\t\tDurs(\"durs\", d).\n\t\tLogger()\n\n\tlog.Log().Msg(\"hello world\")\n\n\t// Output: {\"foo\":\"bar\",\"durs\":[10000,20000],\"message\":\"hello world\"}\n}\n\nfunc ExampleContext_IPAddr() {\n\thostIP := net.IP{192, 168, 0, 100}\n\tlog := zerolog.New(os.Stdout).With().\n\t\tIPAddr(\"HostIP\", hostIP).\n\t\tLogger()\n\n\tlog.Log().Msg(\"hello world\")\n\n\t// Output: {\"HostIP\":\"192.168.0.100\",\"message\":\"hello world\"}\n}\n\nfunc ExampleContext_IPAddrs() {\n\thostIP := net.IP{192, 168, 0, 100}\n\tlog := zerolog.New(os.Stdout).With().\n\t\tIPAddrs(\"HostIP\", []net.IP{hostIP}).\n\t\tLogger()\n\n\tlog.Log().Msg(\"hello world\")\n\n\t// Output: {\"HostIP\":[\"192.168.0.100\"],\"message\":\"hello world\"}\n}\n\nfunc ExampleContext_IPPrefix() {\n\troute := net.IPNet{IP: net.IP{192, 168, 0, 0}, Mask: net.CIDRMask(24, 32)}\n\tlog := zerolog.New(os.Stdout).With().\n\t\tIPPrefix(\"Route\", route).\n\t\tLogger()\n\n\tlog.Log().Msg(\"hello world\")\n\n\t// Output: {\"Route\":\"192.168.0.0/24\",\"message\":\"hello world\"}\n}\n\nfunc ExampleContext_IPPrefixes() {\n\troute := net.IPNet{IP: net.IP{192, 168, 0, 0}, Mask: net.CIDRMask(24, 32)}\n\tlog := zerolog.New(os.Stdout).With().\n\t\tIPPrefixes(\"Route\", []net.IPNet{route}).\n\t\tLogger()\n\n\tlog.Log().Msg(\"hello world\")\n\n\t// Output: {\"Route\":[\"192.168.0.0/24\"],\"message\":\"hello world\"}\n}\n\nfunc ExampleContext_MACAddr() {\n\tmac := net.HardwareAddr{0x00, 0x14, 0x22, 0x01, 0x23, 0x45}\n\tlog := zerolog.New(os.Stdout).With().\n\t\tMACAddr(\"hostMAC\", mac).\n\t\tLogger()\n\n\tlog.Log().Msg(\"hello world\")\n\n\t// Output: {\"hostMAC\":\"00:14:22:01:23:45\",\"message\":\"hello world\"}\n}\n\nfunc ExampleContext_Fields_map() {\n\tfields := map[string]interface{}{\n\t\t\"bar\": \"baz\",\n\t\t\"n\":   1,\n\t}\n\n\tlog := zerolog.New(os.Stdout).With().\n\t\tStr(\"foo\", \"bar\").\n\t\tFields(fields).\n\t\tLogger()\n\n\tlog.Log().Msg(\"hello world\")\n\n\t// Output: {\"foo\":\"bar\",\"bar\":\"baz\",\"n\":1,\"message\":\"hello world\"}\n}\n\nfunc ExampleContext_Fields_slice() {\n\tfields := []interface{}{\n\t\t\"bar\", \"baz\",\n\t\t\"n\", 1,\n\t}\n\n\tlog := zerolog.New(os.Stdout).With().\n\t\tStr(\"foo\", \"bar\").\n\t\tFields(fields).\n\t\tLogger()\n\n\tlog.Log().Msg(\"hello world\")\n\n\t// Output: {\"foo\":\"bar\",\"bar\":\"baz\",\"n\":1,\"message\":\"hello world\"}\n}\n\nfunc ExampleContext_Times() {\n\tt1 := time.Time{}\n\tt2 := t1.Add(time.Second * 10)\n\tt := []time.Time{t1, t2}\n\n\tlog := zerolog.New(os.Stdout).With().\n\t\tStr(\"foo\", \"bar\").\n\t\tTimes(\"times\", t).\n\t\tLogger()\n\n\tlog.Log().Msg(\"hello world\")\n\n\t// Output: {\"foo\":\"bar\",\"times\":[\"0001-01-01T00:00:00Z\",\"0001-01-01T00:00:10Z\"],\"message\":\"hello world\"}\n}\n"
  },
  {
    "path": "log_test.go",
    "content": "package zerolog\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"net\"\n\t\"reflect\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestLog(t *testing.T) {\n\tt.Run(\"empty\", func(t *testing.T) {\n\t\tout := &bytes.Buffer{}\n\t\tlog := New(out)\n\t\tlog.Log().Msg(\"\")\n\t\tif got, want := decodeIfBinaryToString(out.Bytes()), \"{}\\n\"; got != want {\n\t\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"one-field\", func(t *testing.T) {\n\t\tout := &bytes.Buffer{}\n\t\tlog := New(out)\n\t\tlog.Log().Str(\"foo\", \"bar\").Msg(\"\")\n\t\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"foo\":\"bar\"}`+\"\\n\"; got != want {\n\t\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"two-field\", func(t *testing.T) {\n\t\tout := &bytes.Buffer{}\n\t\tlog := New(out)\n\t\tlog.Log().\n\t\t\tStr(\"foo\", \"bar\").\n\t\t\tInt(\"n\", 123).\n\t\t\tMsg(\"\")\n\t\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"foo\":\"bar\",\"n\":123}`+\"\\n\"; got != want {\n\t\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t\t}\n\t})\n}\n\nfunc TestInfo(t *testing.T) {\n\tt.Run(\"empty\", func(t *testing.T) {\n\t\tout := &bytes.Buffer{}\n\t\tlog := New(out)\n\t\tlog.Info().Msg(\"\")\n\t\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"level\":\"info\"}`+\"\\n\"; got != want {\n\t\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"one-field\", func(t *testing.T) {\n\t\tout := &bytes.Buffer{}\n\t\tlog := New(out)\n\t\tlog.Info().Str(\"foo\", \"bar\").Msg(\"\")\n\t\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"level\":\"info\",\"foo\":\"bar\"}`+\"\\n\"; got != want {\n\t\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"two-field\", func(t *testing.T) {\n\t\tout := &bytes.Buffer{}\n\t\tlog := New(out)\n\t\tlog.Info().\n\t\t\tStr(\"foo\", \"bar\").\n\t\t\tInt(\"n\", 123).\n\t\t\tMsg(\"\")\n\t\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"level\":\"info\",\"foo\":\"bar\",\"n\":123}`+\"\\n\"; got != want {\n\t\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t\t}\n\t})\n}\n\nfunc TestEmptyLevelFieldName(t *testing.T) {\n\tfieldName := LevelFieldName\n\tLevelFieldName = \"\"\n\n\tt.Run(\"empty setting\", func(t *testing.T) {\n\t\tout := &bytes.Buffer{}\n\t\tlog := New(out)\n\t\tlog.Info().\n\t\t\tStr(\"foo\", \"bar\").\n\t\t\tInt(\"n\", 123).\n\t\t\tMsg(\"\")\n\t\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"foo\":\"bar\",\"n\":123}`+\"\\n\"; got != want {\n\t\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t\t}\n\t})\n\tLevelFieldName = fieldName\n}\n\nfunc TestWith(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tctx := New(out).With().\n\t\tStr(\"string\", \"foo\").\n\t\tStringer(\"stringer\", net.IP{127, 0, 0, 1}).\n\t\tStringer(\"stringer_nil\", nil).\n\t\tBytes(\"bytes\", []byte(\"bar\")).\n\t\tHex(\"hex\", []byte{0x12, 0xef}).\n\t\tRawJSON(\"json\", []byte(`{\"some\":\"json\"}`)).\n\t\tAnErr(\"some_err\", nil).\n\t\tErr(errors.New(\"some error\")).\n\t\tBool(\"bool\", true).\n\t\tInt(\"int\", 1).\n\t\tInt8(\"int8\", 2).\n\t\tInt16(\"int16\", 3).\n\t\tInt32(\"int32\", 4).\n\t\tInt64(\"int64\", 5).\n\t\tUint(\"uint\", 6).\n\t\tUint8(\"uint8\", 7).\n\t\tUint16(\"uint16\", 8).\n\t\tUint32(\"uint32\", 9).\n\t\tUint64(\"uint64\", 10).\n\t\tFloat32(\"float32\", 11.101).\n\t\tFloat64(\"float64\", 12.30303).\n\t\tTime(\"time\", time.Time{}).\n\t\tDur(\"dur\", 3).\n\t\tCtx(context.Background()).\n\t\tAny(\"any\", \"test\").\n\t\tInterface(\"interface\", struct {\n\t\t\tPub string\n\t\t\tTag string `json:\"tag\"`\n\t\t}{\"a\", \"b\"}).\n\t\tType(\"type\", math.Phi)\n\t_, file, line, _ := runtime.Caller(0)\n\tcaller := fmt.Sprintf(\"%s:%d\", file, line+3)\n\tlog := ctx.Caller().Logger()\n\tlog.Log().Msg(\"\")\n\tif got, want := decodeIfBinaryToString(out.Bytes()),\n\t\t`{\"string\":\"foo\",\"stringer\":\"127.0.0.1\",\"stringer_nil\":null,\"bytes\":\"bar\",\"hex\":\"12ef\",\"json\":{\"some\":\"json\"},\"error\":\"some error\",\"bool\":true,\"int\":1,\"int8\":2,\"int16\":3,\"int32\":4,\"int64\":5,\"uint\":6,\"uint8\":7,\"uint16\":8,\"uint32\":9,\"uint64\":10,\"float32\":11.101,\"float64\":12.30303,\"time\":\"0001-01-01T00:00:00Z\",\"dur\":0.000003,\"any\":\"test\",\"interface\":{\"Pub\":\"a\",\"tag\":\"b\"},\"type\":\"float64\",\"caller\":\"`+caller+`\"}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n\n\t// Validate CallerWithSkipFrameCount.\n\tout.Reset()\n\tctx = New(out).With()\n\t_, file, line, _ = runtime.Caller(0)\n\tcaller = fmt.Sprintf(\"%s:%d\", file, line+5)\n\tlog = ctx.CallerWithSkipFrameCount(3).Logger()\n\tfunc() {\n\t\tlog.Log().Msg(\"\")\n\t}()\n\t// The above line is a little contrived, but the line above should be the line due\n\t// to the extra frame skip.\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"caller\":\"`+caller+`\"}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestStackedWiths(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tctx := New(out).With().\n\t\tBool(\"bool\", true)\n\tctx = ctx.Logger().With().\n\t\tInt(\"int\", 1)\n\tlog := ctx.Logger()\n\tlog.Log().Msg(\"\")\n\tif got, want := decodeIfBinaryToString(out.Bytes()),\n\t\t`{\"bool\":true,\"int\":1}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestWithPlurals(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tctx := New(out).With().\n\t\tStrs(\"strings\", []string{\"foo\", \"bar\"}).\n\t\tStrs(\"strings_nil\", nil).\n\t\tStringers(\"stringers\", []fmt.Stringer{net.IP{127, 0, 0, 1}, nil}).\n\t\tStringers(\"stringers_nil\", nil).\n\t\tErrs(\"errs\", []error{errors.New(\"some error\"), errors.New(\"some other error\"), nil, loggableError{fmt.Errorf(\"oops\")}, nonLoggableError{fmt.Errorf(\"whoops\"), 401}}).\n\t\tBools(\"bool\", []bool{true, false}).\n\t\tInts(\"int\", []int{1, 2}).\n\t\tInts8(\"int8\", []int8{2, 3}).\n\t\tInts16(\"int16\", []int16{3, 4}).\n\t\tInts32(\"int32\", []int32{4, 5}).\n\t\tInts64(\"int64\", []int64{5, 6}).\n\t\tUints(\"uint\", []uint{6, 7}).\n\t\tUints8(\"uint8\", []uint8{7, 8}).\n\t\tUints16(\"uint16\", []uint16{8, 9}).\n\t\tUints32(\"uint32\", []uint32{9, 10}).\n\t\tUints64(\"uint64\", []uint64{10, 11}).\n\t\tFloats32(\"float32\", []float32{1.1, 2.2}).\n\t\tFloats64(\"float64\", []float64{2.2, 3.3}).\n\t\tTimes(\"time\", []time.Time{time.Time{}.AddDate(0, 1, 2), time.Time{}.AddDate(4, 5, 6)}).\n\t\tDurs(\"dur\", []time.Duration{1 * time.Second, 2 * time.Second})\n\tlog := ctx.Logger()\n\tlog.Log().Msg(\"\")\n\tif got, want := decodeIfBinaryToString(out.Bytes()),\n\t\t`{\"strings\":[\"foo\",\"bar\"],\"strings_nil\":[],\"stringers\":[\"127.0.0.1\",null],\"stringers_nil\":null,\"errs\":[\"some error\",\"some other error\",null,{\"l\":\"OOPS\"},\"whoops\"],\"bool\":[true,false],\"int\":[1,2],\"int8\":[2,3],\"int16\":[3,4],\"int32\":[4,5],\"int64\":[5,6],\"uint\":[6,7],\"uint8\":[7,8],\"uint16\":[8,9],\"uint32\":[9,10],\"uint64\":[10,11],\"float32\":[1.1,2.2],\"float64\":[2.2,3.3],\"time\":[\"0001-02-03T00:00:00Z\",\"0005-06-07T00:00:00Z\"],\"dur\":[1000,2000]}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestWithReset(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tctx := New(out).With().\n\t\tStr(\"string\", \"foo\").\n\t\tStringer(\"stringer\", net.IP{127, 0, 0, 1}).\n\t\tStringer(\"stringer_nil\", nil).\n\t\tReset().\n\t\tBytes(\"bytes\", []byte(\"bar\")).\n\t\tHex(\"hex\", []byte{0x12, 0xef}).\n\t\tUint64(\"uint64\", 10).\n\t\tFloat64(\"float64\", 12.30303).\n\t\tStack().\n\t\tCtx(context.Background())\n\tlog := ctx.Logger()\n\tlog.Log().Msg(\"\")\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"bytes\":\"bar\",\"hex\":\"12ef\",\"uint64\":10,\"float64\":12.30303}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestFieldsMap(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tlog := New(out)\n\tlog.Log().Fields(map[string]interface{}{\n\t\t\"nil\":     nil,\n\t\t\"string\":  \"foo\",\n\t\t\"bytes\":   []byte(\"bar\"),\n\t\t\"error\":   errors.New(\"some error\"),\n\t\t\"bool\":    true,\n\t\t\"int\":     int(1),\n\t\t\"int8\":    int8(2),\n\t\t\"int16\":   int16(3),\n\t\t\"int32\":   int32(4),\n\t\t\"int64\":   int64(5),\n\t\t\"uint\":    uint(6),\n\t\t\"uint8\":   uint8(7),\n\t\t\"uint16\":  uint16(8),\n\t\t\"uint32\":  uint32(9),\n\t\t\"uint64\":  uint64(10),\n\t\t\"float32\": float32(11),\n\t\t\"float64\": float64(12),\n\t\t\"ipv6\":    net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34},\n\t\t\"ipnet\":   net.IPNet{IP: net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34}, Mask: net.CIDRMask(64, 128)},\n\t\t\"macaddr\": net.HardwareAddr{0x00, 0x1A, 0x2B, 0x3C, 0x4D, 0x5E},\n\t\t\"dur\":     1 * time.Second,\n\t\t\"time\":    time.Time{},\n\t\t\"obj\":     fixtureObj{\"a\", \"b\", 1},\n\t\t\"objs\":    []LogObjectMarshaler{fixtureObj{\"a\", \"b\", 1}, fixtureObj{\"c\", \"d\", 2}},\n\t\t\"any\":     struct{ A string }{\"test\"},\n\t\t\"raw\":     json.RawMessage(`{\"some\":\"json\"}`),\n\t}).Msg(\"\")\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"any\":{\"A\":\"test\"},\"bool\":true,\"bytes\":\"bar\",\"dur\":1000,\"error\":\"some error\",\"float32\":11,\"float64\":12,\"int\":1,\"int16\":3,\"int32\":4,\"int64\":5,\"int8\":2,\"ipnet\":\"2001:db8:85a3::8a2e:370:7334/64\",\"ipv6\":\"2001:db8:85a3::8a2e:370:7334\",\"macaddr\":\"00:1a:2b:3c:4d:5e\",\"nil\":null,\"obj\":{\"Pub\":\"a\",\"Tag\":\"b\",\"priv\":1},\"objs\":[{\"Pub\":\"a\",\"Tag\":\"b\",\"priv\":1},{\"Pub\":\"c\",\"Tag\":\"d\",\"priv\":2}],\"raw\":{\"some\":\"json\"},\"string\":\"foo\",\"time\":\"0001-01-01T00:00:00Z\",\"uint\":6,\"uint16\":8,\"uint32\":9,\"uint64\":10,\"uint8\":7}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\nfunc TestFieldsMap_Arrays(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tlog := New(out)\n\tlog.Log().Fields(map[string]interface{}{\n\t\t\"strings\":  []string{\"foo\"},\n\t\t\"bools\":    []bool{true},\n\t\t\"errors\":   []error{errors.New(\"some error\")},\n\t\t\"ints\":     []int{1},\n\t\t\"ints8\":    []int8{1},\n\t\t\"ints16\":   []int16{3},\n\t\t\"ints32\":   []int32{4},\n\t\t\"ints64\":   []int64{5},\n\t\t\"uints\":    []uint{6},\n\t\t\"uint8s\":   []uint8{44},\n\t\t\"uints16\":  []uint16{8},\n\t\t\"uints32\":  []uint32{9},\n\t\t\"uints64\":  []uint64{10},\n\t\t\"floats32\": []float32{11},\n\t\t\"floats64\": []float64{12},\n\t\t\"ipv6s\":    []net.IP{{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34}},\n\t\t\"ipnets\":   []net.IPNet{{IP: net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34}, Mask: net.CIDRMask(64, 128)}},\n\t\t\"macaddrs\": []net.HardwareAddr{{0x00, 0x1A, 0x2B, 0x3C, 0x4D, 0x5E}},\n\t\t\"durs\":     []time.Duration{1 * time.Second},\n\t\t\"times\":    []time.Time{{}},\n\t\t\"objs\":     []fixtureObj{{\"a\", \"b\", 1}},\n\t}).Msg(\"\")\n\t// special case: []uint8 are logged as base64 string so we use \",\" for 44\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"bools\":[true],\"durs\":[1000],\"errors\":[\"some error\"],\"floats32\":[11],\"floats64\":[12],\"ints\":[1],\"ints16\":[3],\"ints32\":[4],\"ints64\":[5],\"ints8\":[1],\"ipnets\":[\"2001:db8:85a3::8a2e:370:7334/64\"],\"ipv6s\":[\"2001:db8:85a3::8a2e:370:7334\"],\"macaddrs\":[\"ABorPE1e\"],\"objs\":[{\"Pub\":\"a\",\"tag\":\"b\"}],\"strings\":[\"foo\"],\"times\":[\"0001-01-01T00:00:00Z\"],\"uint8s\":\",\",\"uints\":[6],\"uints16\":[8],\"uints32\":[9],\"uints64\":[10]}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestWithErr(t *testing.T) {\n\tvar err error = nil\n\tout := &bytes.Buffer{}\n\tctx := New(out).With().\n\t\tFields(map[string]interface{}{\n\t\t\t\"nil\":          nil,\n\t\t\t\"nilerror\":     err,\n\t\t\t\"error\":        errors.New(\"some error\"),\n\t\t\t\"loggable\":     loggableError{errors.New(\"loggable\")},\n\t\t\t\"non-loggable\": nonLoggableError{fmt.Errorf(\"oops\"), 401},\n\t\t})\n\tlog := ctx.Logger()\n\tlog.Log().Msg(\"\")\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"error\":\"some error\",\"loggable\":{\"l\":\"LOGGABLE\"},\"nil\":null,\"nilerror\":null,\"non-loggable\":\"oops\"}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestFieldsErr(t *testing.T) {\n\tvar err error = nil\n\tout := &bytes.Buffer{}\n\tlog := New(out)\n\tlog.Log().Fields(map[string]interface{}{\n\t\t\"nil\":          nil,\n\t\t\"nilerror\":     err,\n\t\t\"error\":        errors.New(\"some error\"),\n\t\t\"loggable\":     loggableError{errors.New(\"loggable\")},\n\t\t\"non-loggable\": nonLoggableError{fmt.Errorf(\"oops\"), 401},\n\t}).Msg(\"\")\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"error\":\"some error\",\"loggable\":{\"l\":\"LOGGABLE\"},\"nil\":null,\"nilerror\":null,\"non-loggable\":\"oops\"}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\nfunc TestFieldsErrs(t *testing.T) {\n\tvar err error = nil\n\tout := &bytes.Buffer{}\n\tlog := New(out)\n\tlog.Log().Fields(map[string]interface{}{\n\t\t\"errors\": []error{errors.New(\"some error\"), nil, err, loggableError{errors.New(\"loggable\")}, nonLoggableError{fmt.Errorf(\"oops\"), 404}},\n\t}).Msg(\"\")\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"errors\":[\"some error\",null,null,{\"l\":\"LOGGABLE\"},\"oops\"]}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestFieldsMapPnt(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tlog := New(out)\n\tlog.Log().Fields(map[string]interface{}{\n\t\t\"string\":  new(string),\n\t\t\"bool\":    new(bool),\n\t\t\"int\":     new(int),\n\t\t\"int8\":    new(int8),\n\t\t\"int16\":   new(int16),\n\t\t\"int32\":   new(int32),\n\t\t\"int64\":   new(int64),\n\t\t\"uint\":    new(uint),\n\t\t\"uint8\":   new(uint8),\n\t\t\"uint16\":  new(uint16),\n\t\t\"uint32\":  new(uint32),\n\t\t\"uint64\":  new(uint64),\n\t\t\"float32\": new(float32),\n\t\t\"float64\": new(float64),\n\t\t\"dur\":     new(time.Duration),\n\t\t\"time\":    new(time.Time),\n\t}).Msg(\"\")\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"bool\":false,\"dur\":0,\"float32\":0,\"float64\":0,\"int\":0,\"int16\":0,\"int32\":0,\"int64\":0,\"int8\":0,\"string\":\"\",\"time\":\"0001-01-01T00:00:00Z\",\"uint\":0,\"uint16\":0,\"uint32\":0,\"uint64\":0,\"uint8\":0}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestFieldsMapNilPnt(t *testing.T) {\n\tvar (\n\t\tstringPnt  *string\n\t\tboolPnt    *bool\n\t\tintPnt     *int\n\t\tint8Pnt    *int8\n\t\tint16Pnt   *int16\n\t\tint32Pnt   *int32\n\t\tint64Pnt   *int64\n\t\tuintPnt    *uint\n\t\tuint8Pnt   *uint8\n\t\tuint16Pnt  *uint16\n\t\tuint32Pnt  *uint32\n\t\tuint64Pnt  *uint64\n\t\tfloat32Pnt *float32\n\t\tfloat64Pnt *float64\n\t\tdurPnt     *time.Duration\n\t\ttimePnt    *time.Time\n\t)\n\tout := &bytes.Buffer{}\n\tlog := New(out)\n\tfields := map[string]interface{}{\n\t\t\"string\":  stringPnt,\n\t\t\"bool\":    boolPnt,\n\t\t\"int\":     intPnt,\n\t\t\"int8\":    int8Pnt,\n\t\t\"int16\":   int16Pnt,\n\t\t\"int32\":   int32Pnt,\n\t\t\"int64\":   int64Pnt,\n\t\t\"uint\":    uintPnt,\n\t\t\"uint8\":   uint8Pnt,\n\t\t\"uint16\":  uint16Pnt,\n\t\t\"uint32\":  uint32Pnt,\n\t\t\"uint64\":  uint64Pnt,\n\t\t\"float32\": float32Pnt,\n\t\t\"float64\": float64Pnt,\n\t\t\"dur\":     durPnt,\n\t\t\"time\":    timePnt,\n\t}\n\n\tlog.Log().Fields(fields).Msg(\"\")\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"bool\":null,\"dur\":null,\"float32\":null,\"float64\":null,\"int\":null,\"int16\":null,\"int32\":null,\"int64\":null,\"int8\":null,\"string\":null,\"time\":null,\"uint\":null,\"uint16\":null,\"uint32\":null,\"uint64\":null,\"uint8\":null}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestFieldsSlice(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tlog := New(out)\n\tlog.Log().Fields([]interface{}{\n\t\t\"nil\", nil,\n\t\t\"string\", \"foo\",\n\t\t\"bytes\", []byte(\"bar\"),\n\t\t\"error\", errors.New(\"some error\"),\n\t\t\"bool\", true,\n\t\t\"int\", int(1),\n\t\t\"int8\", int8(2),\n\t\t\"int16\", int16(3),\n\t\t\"int32\", int32(4),\n\t\t\"int64\", int64(5),\n\t\t\"uint\", uint(6),\n\t\t\"uint8\", uint8(7),\n\t\t\"uint16\", uint16(8),\n\t\t\"uint32\", uint32(9),\n\t\t\"uint64\", uint64(10),\n\t\t\"float32\", float32(11),\n\t\t\"float64\", float64(12),\n\t\t\"ipv6\", net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34},\n\t\t\"dur\", 1 * time.Second,\n\t\t\"time\", time.Time{},\n\t\t\"obj\", fixtureObj{\"a\", \"b\", 1},\n\t}).Msg(\"\")\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"nil\":null,\"string\":\"foo\",\"bytes\":\"bar\",\"error\":\"some error\",\"bool\":true,\"int\":1,\"int8\":2,\"int16\":3,\"int32\":4,\"int64\":5,\"uint\":6,\"uint8\":7,\"uint16\":8,\"uint32\":9,\"uint64\":10,\"float32\":11,\"float64\":12,\"ipv6\":\"2001:db8:85a3::8a2e:370:7334\",\"dur\":1000,\"time\":\"0001-01-01T00:00:00Z\",\"obj\":{\"Pub\":\"a\",\"Tag\":\"b\",\"priv\":1}}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestFieldsSliceExtraneous(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tlog := New(out)\n\tlog.Log().Fields([]interface{}{\n\t\t\"string\", \"foo\",\n\t\t\"error\", errors.New(\"some error\"),\n\t\t32, \"valueForNonStringKey\",\n\t\t\"bool\", true,\n\t\t\"int\", int(1),\n\t\t\"keyWithoutValue\",\n\t}).Msg(\"\")\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"string\":\"foo\",\"error\":\"some error\",\"bool\":true,\"int\":1}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestFieldsNotMapSlice(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tlog := New(out)\n\tlog.Log().\n\t\tFields(fixtureObj{\"a\", \"b\", 1}).\n\t\tFields(\"string\").\n\t\tFields(1).\n\t\tMsg(\"\")\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestFields(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tlog := New(out)\n\tnow := time.Now()\n\t_, file, line, _ := runtime.Caller(0)\n\tcaller := fmt.Sprintf(\"%s:%d\", file, line+3)\n\tlog.Log().\n\t\tCaller().\n\t\tStr(\"string\", \"foo\").\n\t\tStringer(\"stringer\", net.IP{127, 0, 0, 1}).\n\t\tStringer(\"stringer_nil\", nil).\n\t\tBytes(\"bytes\", []byte(\"bar\")).\n\t\tHex(\"hex\", []byte{0x12, 0xef}).\n\t\tRawJSON(\"json\", []byte(`{\"some\":\"json\"}`)).\n\t\tRawCBOR(\"cbor\", []byte{0x83, 0x01, 0x82, 0x02, 0x03, 0x82, 0x04, 0x05}).\n\t\tFunc(func(e *Event) { e.Str(\"func\", \"func_output\") }).\n\t\tAnErr(\"some_err\", nil).\n\t\tErr(errors.New(\"some error\")).\n\t\tBool(\"bool\", true).\n\t\tInt(\"int\", 1).\n\t\tInt8(\"int8\", 2).\n\t\tInt16(\"int16\", 3).\n\t\tInt32(\"int32\", 4).\n\t\tInt64(\"int64\", 5).\n\t\tUint(\"uint\", 6).\n\t\tUint8(\"uint8\", 7).\n\t\tUint16(\"uint16\", 8).\n\t\tUint32(\"uint32\", 9).\n\t\tUint64(\"uint64\", 10).\n\t\tIPAddr(\"ipv4\", net.IP{192, 168, 0, 100}).\n\t\tIPAddr(\"ipv6\", net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34}).\n\t\tMACAddr(\"mac\", net.HardwareAddr{0x00, 0x14, 0x22, 0x01, 0x23, 0x45}).\n\t\tIPPrefix(\"pfxv4\", net.IPNet{IP: net.IP{192, 168, 0, 100}, Mask: net.CIDRMask(24, 32)}).\n\t\tIPPrefix(\"pfxv6\", net.IPNet{IP: net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34}, Mask: net.CIDRMask(64, 128)}).\n\t\tFloat32(\"float32\", 11.1234).\n\t\tFloat64(\"float64\", 12.321321321).\n\t\tDur(\"dur\", 1*time.Second).\n\t\tTime(\"time\", time.Time{}).\n\t\tTimeDiff(\"diff\", now, now.Add(-10*time.Second)).\n\t\tCtx(context.Background()).\n\t\tType(\"type\", \"hello\").\n\t\tAny(\"any\", struct{ A string }{\"test\"}).\n\t\tAny(\"logobject\", fixtureObj{\"a\", \"z\", 1}).\n\t\tStack().\n\t\tMsg(\"\")\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"caller\":\"`+caller+`\",\"string\":\"foo\",\"stringer\":\"127.0.0.1\",\"stringer_nil\":null,\"bytes\":\"bar\",\"hex\":\"12ef\",\"json\":{\"some\":\"json\"},\"cbor\":\"data:application/cbor;base64,gwGCAgOCBAU=\",\"func\":\"func_output\",\"error\":\"some error\",\"bool\":true,\"int\":1,\"int8\":2,\"int16\":3,\"int32\":4,\"int64\":5,\"uint\":6,\"uint8\":7,\"uint16\":8,\"uint32\":9,\"uint64\":10,\"ipv4\":\"192.168.0.100\",\"ipv6\":\"2001:db8:85a3::8a2e:370:7334\",\"mac\":\"00:14:22:01:23:45\",\"pfxv4\":\"192.168.0.100/24\",\"pfxv6\":\"2001:db8:85a3::8a2e:370:7334/64\",\"float32\":11.1234,\"float64\":12.321321321,\"dur\":1000,\"time\":\"0001-01-01T00:00:00Z\",\"diff\":10000,\"type\":\"string\",\"any\":{\"A\":\"test\"},\"logobject\":{\"Pub\":\"a\",\"Tag\":\"z\",\"priv\":1}}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestFieldsArrayEmpty(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tlog := New(out)\n\tlog.Log().\n\t\tStrs(\"string\", []string{}).\n\t\tStringers(\"stringer\", []fmt.Stringer{}).\n\t\tErrs(\"err\", []error{}).\n\t\tBools(\"bool\", []bool{}).\n\t\tInts(\"int\", []int{}).\n\t\tInts8(\"int8\", []int8{}).\n\t\tInts16(\"int16\", []int16{}).\n\t\tInts32(\"int32\", []int32{}).\n\t\tInts64(\"int64\", []int64{}).\n\t\tUints(\"uint\", []uint{}).\n\t\tUints8(\"uint8\", []uint8{}).\n\t\tUints16(\"uint16\", []uint16{}).\n\t\tUints32(\"uint32\", []uint32{}).\n\t\tUints64(\"uint64\", []uint64{}).\n\t\tFloats32(\"float32\", []float32{}).\n\t\tFloats64(\"float64\", []float64{}).\n\t\tDurs(\"dur\", []time.Duration{}).\n\t\tTimes(\"time\", []time.Time{}).\n\t\tIPAddrs(\"ip\", []net.IP{}).\n\t\tIPPrefixes(\"pfx\", []net.IPNet{}).\n\t\tMsg(\"\")\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"string\":[],\"stringer\":[],\"err\":[],\"bool\":[],\"int\":[],\"int8\":[],\"int16\":[],\"int32\":[],\"int64\":[],\"uint\":[],\"uint8\":[],\"uint16\":[],\"uint32\":[],\"uint64\":[],\"float32\":[],\"float64\":[],\"dur\":[],\"time\":[],\"ip\":[],\"pfx\":[]}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestFieldsArraySingleElement(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tlog := New(out)\n\tlog.Log().\n\t\tStrs(\"string\", []string{\"foo\"}).\n\t\tStringers(\"stringer\", []fmt.Stringer{net.IP{127, 0, 0, 1}}).\n\t\tErrs(\"err\", []error{errors.New(\"some error\")}).\n\t\tBools(\"bool\", []bool{true}).\n\t\tInts(\"int\", []int{1}).\n\t\tInts8(\"int8\", []int8{2}).\n\t\tInts16(\"int16\", []int16{3}).\n\t\tInts32(\"int32\", []int32{4}).\n\t\tInts64(\"int64\", []int64{5}).\n\t\tUints(\"uint\", []uint{6}).\n\t\tUints8(\"uint8\", []uint8{7}).\n\t\tUints16(\"uint16\", []uint16{8}).\n\t\tUints32(\"uint32\", []uint32{9}).\n\t\tUints64(\"uint64\", []uint64{10}).\n\t\tFloats32(\"float32\", []float32{11}).\n\t\tFloats64(\"float64\", []float64{12}).\n\t\tDurs(\"dur\", []time.Duration{1 * time.Second}).\n\t\tTimes(\"time\", []time.Time{{}}).\n\t\tIPAddrs(\"ip\", []net.IP{{192, 168, 0, 100}}).\n\t\tIPPrefixes(\"pfx\", []net.IPNet{{IP: net.IP{192, 168, 0, 100}, Mask: net.CIDRMask(24, 32)}}).\n\t\tMsg(\"\")\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"string\":[\"foo\"],\"stringer\":[\"127.0.0.1\"],\"err\":[\"some error\"],\"bool\":[true],\"int\":[1],\"int8\":[2],\"int16\":[3],\"int32\":[4],\"int64\":[5],\"uint\":[6],\"uint8\":[7],\"uint16\":[8],\"uint32\":[9],\"uint64\":[10],\"float32\":[11],\"float64\":[12],\"dur\":[1000],\"time\":[\"0001-01-01T00:00:00Z\"],\"ip\":[\"192.168.0.100\"],\"pfx\":[\"192.168.0.100/24\"]}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestFieldsArrayMultipleElement(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tlog := New(out)\n\tlog.Log().\n\t\tStrs(\"string\", []string{\"foo\", \"bar\"}).\n\t\tStringers(\"stringer\", []fmt.Stringer{nil, net.IP{127, 0, 0, 1}}).\n\t\tErrs(\"err\", []error{errors.New(\"some error\"), nil}).\n\t\tBools(\"bool\", []bool{true, false}).\n\t\tInts(\"int\", []int{1, 0}).\n\t\tInts8(\"int8\", []int8{2, 0}).\n\t\tInts16(\"int16\", []int16{3, 0}).\n\t\tInts32(\"int32\", []int32{4, 0}).\n\t\tInts64(\"int64\", []int64{5, 0}).\n\t\tUints(\"uint\", []uint{6, 0}).\n\t\tUints8(\"uint8\", []uint8{7, 0}).\n\t\tUints16(\"uint16\", []uint16{8, 0}).\n\t\tUints32(\"uint32\", []uint32{9, 0}).\n\t\tUints64(\"uint64\", []uint64{10, 0}).\n\t\tFloats32(\"float32\", []float32{11, 0}).\n\t\tFloats64(\"float64\", []float64{12, 0}).\n\t\tDurs(\"dur\", []time.Duration{1 * time.Second, 0}).\n\t\tTimes(\"time\", []time.Time{{}, {}}).\n\t\tIPAddrs(\"ip\", []net.IP{{192, 168, 0, 100}, {0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34}}).\n\t\tIPPrefixes(\"pfx\", []net.IPNet{{IP: net.IP{192, 168, 0, 100}, Mask: net.CIDRMask(24, 32)}, {IP: net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34}, Mask: net.CIDRMask(64, 128)}}).\n\t\tMsg(\"\")\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"string\":[\"foo\",\"bar\"],\"stringer\":[null,\"127.0.0.1\"],\"err\":[\"some error\",null],\"bool\":[true,false],\"int\":[1,0],\"int8\":[2,0],\"int16\":[3,0],\"int32\":[4,0],\"int64\":[5,0],\"uint\":[6,0],\"uint8\":[7,0],\"uint16\":[8,0],\"uint32\":[9,0],\"uint64\":[10,0],\"float32\":[11,0],\"float64\":[12,0],\"dur\":[1000,0],\"time\":[\"0001-01-01T00:00:00Z\",\"0001-01-01T00:00:00Z\"],\"ip\":[\"192.168.0.100\",\"2001:db8:85a3::8a2e:370:7334\"],\"pfx\":[\"192.168.0.100/24\",\"2001:db8:85a3::8a2e:370:7334/64\"]}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestFieldsDisabled(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tlog := New(out).Level(InfoLevel)\n\tnow := time.Now()\n\tlog.Debug().\n\t\tStr(\"string\", \"foo\").\n\t\tStringer(\"stringer\", net.IP{127, 0, 0, 1}).\n\t\tBytes(\"bytes\", []byte(\"bar\")).\n\t\tHex(\"hex\", []byte{0x12, 0xef}).\n\t\tAnErr(\"some_err\", nil).\n\t\tErr(errors.New(\"some error\")).\n\t\tFunc(func(e *Event) { e.Str(\"func\", \"func_output\") }).\n\t\tBool(\"bool\", true).\n\t\tInt(\"int\", 1).\n\t\tInt8(\"int8\", 2).\n\t\tInt16(\"int16\", 3).\n\t\tInt32(\"int32\", 4).\n\t\tInt64(\"int64\", 5).\n\t\tUint(\"uint\", 6).\n\t\tUint8(\"uint8\", 7).\n\t\tUint16(\"uint16\", 8).\n\t\tUint32(\"uint32\", 9).\n\t\tUint64(\"uint64\", 10).\n\t\tFloat32(\"float32\", 11).\n\t\tFloat64(\"float64\", 12).\n\t\tDur(\"dur\", 1*time.Second).\n\t\tTime(\"time\", time.Time{}).\n\t\tTimeDiff(\"diff\", now, now.Add(-10*time.Second)).\n\t\tIPAddr(\"ip\", net.IP{127, 0, 0, 1}).\n\t\tIPPrefix(\"ip\", net.IPNet{IP: net.IP{127, 0, 0, 1}, Mask: net.CIDRMask(24, 32)}).\n\t\tMACAddr(\"mac\", net.HardwareAddr{0x00, 0x14, 0x22, 0x01, 0x23, 0x45}).\n\t\tCtx(context.Background()).\n\t\tAny(\"any\", struct{ A string }{\"test\"}).\n\t\tInterface(\"interface\", fixtureObj{\"a\", \"z\", 1}).\n\t\tErr(errors.New(\"some error\")).\n\t\tMsg(\"\")\n\tif got, want := decodeIfBinaryToString(out.Bytes()), \"\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestMsgf(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tlog := New(out)\n\tlog.Log().Msgf(\"one %s %.1f %d %v\", \"two\", 3.4, 5, errors.New(\"six\"))\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"message\":\"one two 3.4 5 six\"}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestWithAndFieldsCombined(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tlog := New(out).With().Str(\"f1\", \"val\").Str(\"f2\", \"val\").Logger()\n\tlog.Log().Str(\"f3\", \"val\").Msg(\"\")\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"f1\":\"val\",\"f2\":\"val\",\"f3\":\"val\"}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestLevel(t *testing.T) {\n\tt.Run(\"Disabled\", func(t *testing.T) {\n\t\tout := &bytes.Buffer{}\n\t\tlog := New(out).Level(Disabled)\n\t\tlog.Info().Msg(\"test\")\n\t\tif got, want := decodeIfBinaryToString(out.Bytes()), \"\"; got != want {\n\t\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"NoLevel/Disabled\", func(t *testing.T) {\n\t\tout := &bytes.Buffer{}\n\t\tlog := New(out).Level(Disabled)\n\t\tlog.Log().Msg(\"test\")\n\t\tif got, want := decodeIfBinaryToString(out.Bytes()), \"\"; got != want {\n\t\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"NoLevel/Info\", func(t *testing.T) {\n\t\tout := &bytes.Buffer{}\n\t\tlog := New(out).Level(InfoLevel)\n\t\tlog.Log().Msg(\"test\")\n\t\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"message\":\"test\"}`+\"\\n\"; got != want {\n\t\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"NoLevel/Panic\", func(t *testing.T) {\n\t\tout := &bytes.Buffer{}\n\t\tlog := New(out).Level(PanicLevel)\n\t\tlog.Log().Msg(\"test\")\n\t\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"message\":\"test\"}`+\"\\n\"; got != want {\n\t\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"NoLevel/WithLevel\", func(t *testing.T) {\n\t\tout := &bytes.Buffer{}\n\t\tlog := New(out).Level(InfoLevel)\n\t\tlog.WithLevel(NoLevel).Msg(\"test\")\n\t\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"message\":\"test\"}`+\"\\n\"; got != want {\n\t\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"Info\", func(t *testing.T) {\n\t\tout := &bytes.Buffer{}\n\t\tlog := New(out).Level(InfoLevel)\n\t\tlog.Info().Msg(\"test\")\n\t\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"level\":\"info\",\"message\":\"test\"}`+\"\\n\"; got != want {\n\t\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t\t}\n\t})\n}\n\nfunc TestGetLevel(t *testing.T) {\n\tlevels := []Level{\n\t\tDebugLevel,\n\t\tInfoLevel,\n\t\tWarnLevel,\n\t\tErrorLevel,\n\t\tFatalLevel,\n\t\tPanicLevel,\n\t\tNoLevel,\n\t\tDisabled,\n\t}\n\tfor _, level := range levels {\n\t\tif got, want := New(nil).Level(level).GetLevel(), level; got != want {\n\t\t\tt.Errorf(\"GetLevel() = %v, want: %v\", got, want)\n\t\t}\n\t}\n}\n\nfunc TestSampling(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tlog := New(out).Sample(&BasicSampler{N: 2})\n\tlog.Log().Int(\"i\", 1).Msg(\"\")\n\tlog.Log().Int(\"i\", 2).Msg(\"\")\n\tlog.Log().Int(\"i\", 3).Msg(\"\")\n\tlog.Log().Int(\"i\", 4).Msg(\"\")\n\tif got, want := decodeIfBinaryToString(out.Bytes()), \"{\\\"i\\\":1}\\n{\\\"i\\\":3}\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestDisableSampling(t *testing.T) {\n\t// Save original state\n\toriginal := samplingDisabled()\n\tdefer DisableSampling(original)\n\n\tout := &bytes.Buffer{}\n\tlog := New(out).Sample(&BasicSampler{N: 2})\n\n\t// Enable sampling disable\n\tDisableSampling(true)\n\n\tlog.Log().Int(\"i\", 1).Msg(\"\")\n\tlog.Log().Int(\"i\", 2).Msg(\"\")\n\tlog.Log().Int(\"i\", 3).Msg(\"\")\n\tlog.Log().Int(\"i\", 4).Msg(\"\")\n\n\t// All messages should be logged since sampling is disabled\n\tif got, want := decodeIfBinaryToString(out.Bytes()), \"{\\\"i\\\":1}\\n{\\\"i\\\":2}\\n{\\\"i\\\":3}\\n{\\\"i\\\":4}\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestDiscard(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tlog := New(out)\n\tlog.Log().Discard().Str(\"a\", \"b\").Msgf(\"one %s %.1f %d %v\", \"two\", 3.4, 5, errors.New(\"six\"))\n\tif got, want := decodeIfBinaryToString(out.Bytes()), \"\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n\n\t// Double call\n\tlog.Log().Discard().Discard().Str(\"a\", \"b\").Msgf(\"one %s %.1f %d %v\", \"two\", 3.4, 5, errors.New(\"six\"))\n\tif got, want := decodeIfBinaryToString(out.Bytes()), \"\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\ntype levelWriter struct {\n\tops []struct {\n\t\tl Level\n\t\tp string\n\t}\n}\n\nfunc (lw *levelWriter) Write(p []byte) (int, error) {\n\treturn len(p), nil\n}\n\nfunc (lw *levelWriter) WriteLevel(lvl Level, p []byte) (int, error) {\n\tp = decodeIfBinaryToBytes(p)\n\tlw.ops = append(lw.ops, struct {\n\t\tl Level\n\t\tp string\n\t}{lvl, string(p)})\n\treturn len(p), nil\n}\n\nfunc (lw *levelWriter) Close() error {\n\treturn nil\n}\n\nfunc TestLevelWriter(t *testing.T) {\n\tlw := &levelWriter{\n\t\tops: []struct {\n\t\t\tl Level\n\t\t\tp string\n\t\t}{},\n\t}\n\n\t// Allow extra-verbose logs.\n\tSetGlobalLevel(TraceLevel - 1)\n\tlog := New(lw).Level(TraceLevel - 1)\n\n\tlog.Trace().Msg(\"0\")\n\tlog.Debug().Msg(\"1\")\n\tlog.Info().Msg(\"2\")\n\tlog.Warn().Msg(\"3\")\n\tlog.Error().Msg(\"4\")\n\tlog.Log().Msg(\"nolevel-1\")\n\tlog.WithLevel(TraceLevel).Msg(\"5\")\n\tlog.WithLevel(DebugLevel).Msg(\"6\")\n\tlog.WithLevel(InfoLevel).Msg(\"7\")\n\tlog.WithLevel(WarnLevel).Msg(\"8\")\n\tlog.WithLevel(ErrorLevel).Msg(\"9\")\n\tlog.WithLevel(FatalLevel).Msg(\"10\")\n\tlog.WithLevel(PanicLevel).Msg(\"11\")\n\tlog.WithLevel(NoLevel).Msg(\"nolevel-2\")\n\tlog.WithLevel(-1).Msg(\"-1\")                  // Same as TraceLevel\n\tlog.WithLevel(-2).Msg(\"-2\")                  // Will log\n\tlog.WithLevel(-3).Msg(\"-3\")                  // Will not log\n\tlog.WithLevel(Disabled).Msg(\"Disabled\")      // Will not log\n\tlog.Err(nil).Msg(\"e-1\")                      // Will log at InfoLevel\n\tlog.Err(errors.New(\"some error\")).Msg(\"e-2\") // Will log at ErrorLevel\n\n\twant := []struct {\n\t\tl Level\n\t\tp string\n\t}{\n\t\t{TraceLevel, `{\"level\":\"trace\",\"message\":\"0\"}` + \"\\n\"},\n\t\t{DebugLevel, `{\"level\":\"debug\",\"message\":\"1\"}` + \"\\n\"},\n\t\t{InfoLevel, `{\"level\":\"info\",\"message\":\"2\"}` + \"\\n\"},\n\t\t{WarnLevel, `{\"level\":\"warn\",\"message\":\"3\"}` + \"\\n\"},\n\t\t{ErrorLevel, `{\"level\":\"error\",\"message\":\"4\"}` + \"\\n\"},\n\t\t{NoLevel, `{\"message\":\"nolevel-1\"}` + \"\\n\"},\n\t\t{TraceLevel, `{\"level\":\"trace\",\"message\":\"5\"}` + \"\\n\"},\n\t\t{DebugLevel, `{\"level\":\"debug\",\"message\":\"6\"}` + \"\\n\"},\n\t\t{InfoLevel, `{\"level\":\"info\",\"message\":\"7\"}` + \"\\n\"},\n\t\t{WarnLevel, `{\"level\":\"warn\",\"message\":\"8\"}` + \"\\n\"},\n\t\t{ErrorLevel, `{\"level\":\"error\",\"message\":\"9\"}` + \"\\n\"},\n\t\t{FatalLevel, `{\"level\":\"fatal\",\"message\":\"10\"}` + \"\\n\"},\n\t\t{PanicLevel, `{\"level\":\"panic\",\"message\":\"11\"}` + \"\\n\"},\n\t\t{NoLevel, `{\"message\":\"nolevel-2\"}` + \"\\n\"},\n\t\t{Level(-1), `{\"level\":\"trace\",\"message\":\"-1\"}` + \"\\n\"},\n\t\t{Level(-2), `{\"level\":\"-2\",\"message\":\"-2\"}` + \"\\n\"},\n\t\t{InfoLevel, `{\"level\":\"info\",\"message\":\"e-1\"}` + \"\\n\"},\n\t\t{ErrorLevel, `{\"level\":\"error\",\"error\":\"some error\",\"message\":\"e-2\"}` + \"\\n\"},\n\t}\n\tif got := lw.ops; !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"invalid ops:\\ngot:\\n%v\\nwant:\\n%v\", got, want)\n\t}\n}\n\nfunc TestDisabledLevel(t *testing.T) {\n\tlw := &levelWriter{\n\t\tops: []struct {\n\t\t\tl Level\n\t\t\tp string\n\t\t}{},\n\t}\n\n\t// Allow extra-verbose logs.\n\tSetGlobalLevel(TraceLevel - 1)\n\tlog := New(lw).Level(Disabled)\n\tlog.Error().Msg(\"0\")                    // will not log\n\tlog.Log().Msg(\"nolevel-1\")              // will not log\n\tlog.WithLevel(ErrorLevel).Msg(\"3\")      // will not log\n\tlog.WithLevel(NoLevel).Msg(\"nolevel-2\") // will not log\n\tlog.WithLevel(Disabled).Msg(\"Disabled\") // will not log\n\n\twant := []struct {\n\t\tl Level\n\t\tp string\n\t}{}\n\tif got := lw.ops; !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"invalid ops:\\ngot:\\n%v\\nwant:\\n%v\", got, want)\n\t}\n}\n\nfunc TestPanicLevel(t *testing.T) {\n\tlw := &levelWriter{\n\t\tops: []struct {\n\t\t\tl Level\n\t\t\tp string\n\t\t}{},\n\t}\n\n\t// Allow extra-verbose logs.\n\tSetGlobalLevel(TraceLevel - 1)\n\tlog := New(lw).Level(TraceLevel - 1)\n\n\t// Catch the panic from log.Panic().Msg(\"1\")\n\tdefer func() {\n\t\tif r := recover(); r == nil {\n\t\t\tt.Error(\"expected panic from log.Panic()\")\n\t\t}\n\t}()\n\tlog.Panic().Msg(\"1\")\n\tlog.WithLevel(PanicLevel).Msg(\"2\")\n\n\twant := []struct {\n\t\tl Level\n\t\tp string\n\t}{\n\t\t{PanicLevel, `{\"level\":\"panic\",\"message\":\"1\"}` + \"\\n\"},\n\t\t{PanicLevel, `{\"level\":\"panic\",\"message\":\"2\"}` + \"\\n\"},\n\t}\n\tif got := lw.ops; !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"invalid ops:\\ngot:\\n%v\\nwant:\\n%v\", got, want)\n\t}\n}\n\nfunc TestFatalLevel(t *testing.T) {\n\tlw := &levelWriter{\n\t\tops: []struct {\n\t\t\tl Level\n\t\t\tp string\n\t\t}{},\n\t}\n\n\t// Allow extra-verbose logs.\n\tSetGlobalLevel(TraceLevel - 1)\n\tlog := New(lw).Level(TraceLevel - 1)\n\n\t// Set FatalExitFunc to panic so we can catch it\n\toldFatalExitFunc := FatalExitFunc\n\tFatalExitFunc = func() { panic(\"fatal exit\") }\n\tdefer func() { FatalExitFunc = oldFatalExitFunc }()\n\n\t// Catch the panic from log.Fatal().Msg(\"1\")\n\tdefer func() {\n\t\tif r := recover(); r == nil || r != \"fatal exit\" {\n\t\t\tt.Errorf(\"expected panic 'fatal exit' from log.Fatal(), got %v\", r)\n\t\t}\n\t}()\n\tlog.Fatal().Msg(\"1\")\n\tlog.WithLevel(FatalLevel).Msg(\"2\")\n\n\twant := []struct {\n\t\tl Level\n\t\tp string\n\t}{\n\t\t{FatalLevel, `{\"level\":\"fatal\",\"message\":\"1\"}` + \"\\n\"},\n\t\t{FatalLevel, `{\"level\":\"fatal\",\"message\":\"2\"}` + \"\\n\"},\n\t}\n\tif got := lw.ops; !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"invalid ops:\\ngot:\\n%v\\nwant:\\n%v\", got, want)\n\t}\n}\n\nfunc TestFatalDisabled(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tlog := New(out).Level(PanicLevel) // Disable FatalLevel\n\n\t// Set FatalExitFunc to set a flag\n\tvar fatalCalled bool\n\toldFatalExitFunc := FatalExitFunc\n\tFatalExitFunc = func() { fatalCalled = true }\n\tdefer func() { FatalExitFunc = oldFatalExitFunc }()\n\n\t// Call Fatal, which should be disabled, call done, and return nil\n\te := log.Fatal()\n\tif e != nil {\n\t\tt.Error(\"Expected nil event when Fatal is disabled\")\n\t}\n\tif !fatalCalled {\n\t\tt.Error(\"Expected FatalExitFunc to be called when Fatal is disabled\")\n\t}\n\tif out.Len() > 0 {\n\t\tt.Errorf(\"Expected no output when Fatal is disabled, got: %s\", out.String())\n\t}\n}\n\nfunc TestPanicDisabled(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tlog := New(out).Level(Disabled) // Disable all levels\n\n\t// Call Panic, which should be disabled, call done, and panic with \"\"\n\tdefer func() {\n\t\tif r := recover(); r == nil || r != \"\" {\n\t\t\tt.Errorf(\"Expected panic with empty string when Panic is disabled, got %v\", r)\n\t\t}\n\t}()\n\te := log.Panic()\n\tif e != nil {\n\t\tt.Error(\"Expected nil event when Panic is disabled\")\n\t}\n\tif out.Len() > 0 {\n\t\tt.Errorf(\"Expected no output when Panic is disabled, got: %s\", out.String())\n\t}\n}\n\nfunc TestLoggerShouldWithNilWriter(t *testing.T) {\n\t// Create a logger with nil writer to test the should method's nil check\n\tlog := Logger{w: nil, level: TraceLevel}\n\n\te := log.Info()\n\tif e != nil {\n\t\tt.Error(\"Expected nil event when writer is nil\")\n\t}\n}\n\nfunc TestContextTimestamp(t *testing.T) {\n\tTimestampFunc = func() time.Time {\n\t\treturn time.Date(2001, time.February, 3, 4, 5, 6, 7, time.UTC)\n\t}\n\tdefer func() {\n\t\tTimestampFunc = time.Now\n\t}()\n\tout := &bytes.Buffer{}\n\tlog := New(out).With().Timestamp().Str(\"foo\", \"bar\").Logger()\n\tlog.Log().Msg(\"hello world\")\n\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"foo\":\"bar\",\"time\":\"2001-02-03T04:05:06Z\",\"message\":\"hello world\"}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestEventTimestamp(t *testing.T) {\n\tTimestampFunc = func() time.Time {\n\t\treturn time.Date(2001, time.February, 3, 4, 5, 6, 7, time.UTC)\n\t}\n\tdefer func() {\n\t\tTimestampFunc = time.Now\n\t}()\n\tout := &bytes.Buffer{}\n\tlog := New(out).With().Str(\"foo\", \"bar\").Logger()\n\tlog.Log().Timestamp().Msg(\"hello world\")\n\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"foo\":\"bar\",\"time\":\"2001-02-03T04:05:06Z\",\"message\":\"hello world\"}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestOutputWithoutTimestamp(t *testing.T) {\n\tignoredOut := &bytes.Buffer{}\n\tout := &bytes.Buffer{}\n\tlog := New(ignoredOut).Output(out).With().Str(\"foo\", \"bar\").Logger()\n\tlog.Log().Msg(\"hello world\")\n\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"foo\":\"bar\",\"message\":\"hello world\"}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestOutputWithTimestamp(t *testing.T) {\n\tTimestampFunc = func() time.Time {\n\t\treturn time.Date(2001, time.February, 3, 4, 5, 6, 7, time.UTC)\n\t}\n\tdefer func() {\n\t\tTimestampFunc = time.Now\n\t}()\n\tignoredOut := &bytes.Buffer{}\n\tout := &bytes.Buffer{}\n\tlog := New(ignoredOut).Output(out).With().Timestamp().Str(\"foo\", \"bar\").Logger()\n\tlog.Log().Msg(\"hello world\")\n\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"foo\":\"bar\",\"time\":\"2001-02-03T04:05:06Z\",\"message\":\"hello world\"}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestOutputWithContext(t *testing.T) {\n\tignoredOut := &bytes.Buffer{}\n\tout := &bytes.Buffer{}\n\tlog := New(ignoredOut).With().Str(\"foo\", \"bar\").Logger().Output(out)\n\tlog.Log().Msg(\"hello world\")\n\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"foo\":\"bar\",\"message\":\"hello world\"}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestCallerMarshalFunc(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tlog := New(out)\n\n\t// test default behaviour this is really brittle due to the line numbers\n\t// actually mattering for validation\n\tpc, file, line, _ := runtime.Caller(0)\n\tcaller := fmt.Sprintf(\"%s:%d\", file, line+2)\n\tlog.Log().Caller().Msg(\"msg\")\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"caller\":\"`+caller+`\",\"message\":\"msg\"}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n\tout.Reset()\n\n\t// test custom behavior. In this case we'll take just the last directory\n\torigCallerMarshalFunc := CallerMarshalFunc\n\tdefer func() { CallerMarshalFunc = origCallerMarshalFunc }()\n\tCallerMarshalFunc = func(pc uintptr, file string, line int) string {\n\t\tparts := strings.Split(file, \"/\")\n\t\tif len(parts) > 1 {\n\t\t\treturn strings.Join(parts[len(parts)-2:], \"/\") + \":\" + strconv.Itoa(line)\n\t\t}\n\n\t\treturn runtime.FuncForPC(pc).Name() + \":\" + file + \":\" + strconv.Itoa(line)\n\t}\n\tpc, file, line, _ = runtime.Caller(0)\n\tcaller = CallerMarshalFunc(pc, file, line+2)\n\tlog.Log().Caller().Msg(\"msg\")\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"caller\":\"`+caller+`\",\"message\":\"msg\"}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestLevelFieldMarshalFunc(t *testing.T) {\n\torigLevelFieldMarshalFunc := LevelFieldMarshalFunc\n\tLevelFieldMarshalFunc = func(l Level) string {\n\t\treturn strings.ToUpper(l.String())\n\t}\n\tdefer func() {\n\t\tLevelFieldMarshalFunc = origLevelFieldMarshalFunc\n\t}()\n\tout := &bytes.Buffer{}\n\tlog := New(out)\n\n\tlog.Debug().Msg(\"test\")\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"level\":\"DEBUG\",\"message\":\"test\"}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n\tout.Reset()\n\n\tlog.Info().Msg(\"test\")\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"level\":\"INFO\",\"message\":\"test\"}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n\tout.Reset()\n\n\tlog.Warn().Msg(\"test\")\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"level\":\"WARN\",\"message\":\"test\"}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n\tout.Reset()\n\n\tlog.Error().Msg(\"test\")\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"level\":\"ERROR\",\"message\":\"test\"}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n\tout.Reset()\n}\n\ntype errWriter struct {\n\terror\n}\n\nfunc (w errWriter) Write(p []byte) (n int, err error) {\n\treturn 0, w.error\n}\n\nfunc TestErrorHandler(t *testing.T) {\n\tvar got error\n\twant := errors.New(\"write error\")\n\tErrorHandler = func(err error) {\n\t\tgot = err\n\t}\n\tlog := New(errWriter{want})\n\tlog.Log().Msg(\"test\")\n\tif got != want {\n\t\tt.Errorf(\"ErrorHandler err = %#v, want %#v\", got, want)\n\t}\n}\n\nfunc TestUpdateEmptyContext(t *testing.T) {\n\tvar buf bytes.Buffer\n\tlog := New(&buf)\n\n\tlog.UpdateContext(func(c Context) Context {\n\t\treturn c.Str(\"foo\", \"bar\")\n\t})\n\tlog.Info().Msg(\"no panic\")\n\n\twant := `{\"level\":\"info\",\"foo\":\"bar\",\"message\":\"no panic\"}` + \"\\n\"\n\n\tif got := decodeIfBinaryToString(buf.Bytes()); got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %q\\nwant: %q\", got, want)\n\t}\n}\n\nfunc TestUpdateContextOnDisabledLogger(t *testing.T) {\n\tvar buf bytes.Buffer\n\tlog := disabledLogger\n\n\tlog.UpdateContext(func(c Context) Context {\n\t\treturn c.Str(\"foo\", \"bar\")\n\t})\n\tlog.Info().Msg(\"no panic\")\n\n\twant := \"\"\n\n\tif got := decodeIfBinaryToString(buf.Bytes()); got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %q\\nwant: %q\", got, want)\n\t}\n}\n\nfunc TestUpdateContextOnNopLogger(t *testing.T) {\n\t// Nop() creates a new disabled logger that is a different pointer than\n\t// the package-level disabledLogger. UpdateContext should still treat it\n\t// as disabled and skip the update function. See issue #643.\n\tlog := Nop()\n\n\tcalled := false\n\tlog.UpdateContext(func(c Context) Context {\n\t\tcalled = true\n\t\treturn c.Str(\"foo\", \"bar\")\n\t})\n\n\tif called {\n\t\tt.Error(\"UpdateContext should not call update func on a Nop logger\")\n\t}\n}\n\nfunc TestUpdateContextOnZeroValueLogger(t *testing.T) {\n\t// A zero-value Logger has a nil writer and should be treated as\n\t// disabled by UpdateContext. See issue #643.\n\tvar log Logger\n\n\tcalled := false\n\tlog.UpdateContext(func(c Context) Context {\n\t\tcalled = true\n\t\treturn c.Str(\"foo\", \"bar\")\n\t})\n\n\tif called {\n\t\tt.Error(\"UpdateContext should not call update func on a zero-value logger\")\n\t}\n}\n\nfunc TestLevel_String(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tl    Level\n\t\twant string\n\t}{\n\t\t{\"trace\", TraceLevel, \"trace\"},\n\t\t{\"debug\", DebugLevel, \"debug\"},\n\t\t{\"info\", InfoLevel, \"info\"},\n\t\t{\"warn\", WarnLevel, \"warn\"},\n\t\t{\"error\", ErrorLevel, \"error\"},\n\t\t{\"fatal\", FatalLevel, \"fatal\"},\n\t\t{\"panic\", PanicLevel, \"panic\"},\n\t\t{\"disabled\", Disabled, \"disabled\"},\n\t\t{\"nolevel\", NoLevel, \"\"},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := tt.l.String(); got != tt.want {\n\t\t\t\tt.Errorf(\"String() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestLevel_MarshalText(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tl    Level\n\t\twant string\n\t}{\n\t\t{\"trace\", TraceLevel, \"trace\"},\n\t\t{\"debug\", DebugLevel, \"debug\"},\n\t\t{\"info\", InfoLevel, \"info\"},\n\t\t{\"warn\", WarnLevel, \"warn\"},\n\t\t{\"error\", ErrorLevel, \"error\"},\n\t\t{\"fatal\", FatalLevel, \"fatal\"},\n\t\t{\"panic\", PanicLevel, \"panic\"},\n\t\t{\"disabled\", Disabled, \"disabled\"},\n\t\t{\"nolevel\", NoLevel, \"\"},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got, err := tt.l.MarshalText(); err != nil {\n\t\t\t\tt.Errorf(\"MarshalText couldn't marshal: %v\", tt.l)\n\t\t\t} else if string(got) != tt.want {\n\t\t\t\tt.Errorf(\"String() = %v, want %v\", string(got), tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestParseLevel(t *testing.T) {\n\ttype args struct {\n\t\tlevelStr string\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\twant    Level\n\t\twantErr bool\n\t}{\n\t\t{\"trace\", args{\"trace\"}, TraceLevel, false},\n\t\t{\"debug\", args{\"debug\"}, DebugLevel, false},\n\t\t{\"info\", args{\"info\"}, InfoLevel, false},\n\t\t{\"warn\", args{\"warn\"}, WarnLevel, false},\n\t\t{\"error\", args{\"error\"}, ErrorLevel, false},\n\t\t{\"fatal\", args{\"fatal\"}, FatalLevel, false},\n\t\t{\"panic\", args{\"panic\"}, PanicLevel, false},\n\t\t{\"disabled\", args{\"disabled\"}, Disabled, false},\n\t\t{\"nolevel\", args{\"\"}, NoLevel, false},\n\t\t{\"-1\", args{\"-1\"}, TraceLevel, false},\n\t\t{\"-2\", args{\"-2\"}, Level(-2), false},\n\t\t{\"-3\", args{\"-3\"}, Level(-3), false},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot, err := ParseLevel(tt.args.levelStr)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"ParseLevel() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif got != tt.want {\n\t\t\t\tt.Errorf(\"ParseLevel() got = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestUnmarshalTextLevel(t *testing.T) {\n\ttype args struct {\n\t\tlevelStr string\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\twant    Level\n\t\twantErr bool\n\t}{\n\t\t{\"trace\", args{\"trace\"}, TraceLevel, false},\n\t\t{\"debug\", args{\"debug\"}, DebugLevel, false},\n\t\t{\"info\", args{\"info\"}, InfoLevel, false},\n\t\t{\"warn\", args{\"warn\"}, WarnLevel, false},\n\t\t{\"error\", args{\"error\"}, ErrorLevel, false},\n\t\t{\"fatal\", args{\"fatal\"}, FatalLevel, false},\n\t\t{\"panic\", args{\"panic\"}, PanicLevel, false},\n\t\t{\"disabled\", args{\"disabled\"}, Disabled, false},\n\t\t{\"nolevel\", args{\"\"}, NoLevel, false},\n\t\t{\"-1\", args{\"-1\"}, TraceLevel, false},\n\t\t{\"-2\", args{\"-2\"}, Level(-2), false},\n\t\t{\"-3\", args{\"-3\"}, Level(-3), false},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tvar l Level\n\t\t\terr := l.UnmarshalText([]byte(tt.args.levelStr))\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"UnmarshalText() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif l != tt.want {\n\t\t\t\tt.Errorf(\"UnmarshalText() got = %v, want %v\", l, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestUnmarshalTextLevelNil(t *testing.T) {\n\tvar l *Level\n\terr := l.UnmarshalText([]byte(\"info\"))\n\tif err == nil || err.Error() != \"can't unmarshal a nil *Level\" {\n\t\tt.Errorf(\"UnmarshalText() on nil *Level error = %v, want 'can't unmarshal a nil *Level'\", err)\n\t}\n}\n\nfunc TestHTMLNoEscaping(t *testing.T) {\n\tout := &bytes.Buffer{}\n\tlog := New(out)\n\tlog.Log().Interface(\"head\", \"<test>\").Send()\n\tif got, want := decodeIfBinaryToString(out.Bytes()), `{\"head\":\"<test>\"}`+\"\\n\"; got != want {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n"
  },
  {
    "path": "not_go112.go",
    "content": "// +build !go1.12\n\npackage zerolog\n\nconst contextCallerSkipFrameCount = 3\n"
  },
  {
    "path": "pkgerrors/stacktrace.go",
    "content": "package pkgerrors\n\nimport (\n\t\"github.com/pkg/errors\"\n)\n\nvar (\n\tStackSourceFileName     = \"source\"\n\tStackSourceLineName     = \"line\"\n\tStackSourceFunctionName = \"func\"\n)\n\ntype state struct {\n\tb []byte\n}\n\n// Write implement fmt.Formatter interface.\nfunc (s *state) Write(b []byte) (n int, err error) {\n\ts.b = b\n\treturn len(b), nil\n}\n\n// Width implement fmt.Formatter interface.\nfunc (s *state) Width() (wid int, ok bool) {\n\treturn 0, false\n}\n\n// Precision implement fmt.Formatter interface.\nfunc (s *state) Precision() (prec int, ok bool) {\n\treturn 0, false\n}\n\n// Flag implement fmt.Formatter interface.\nfunc (s *state) Flag(c int) bool {\n\treturn false\n}\n\nfunc frameField(f errors.Frame, s *state, c rune) string {\n\tf.Format(s, c)\n\treturn string(s.b)\n}\n\n// MarshalStack implements pkg/errors stack trace marshaling.\n//\n// zerolog.ErrorStackMarshaler = MarshalStack\nfunc MarshalStack(err error) interface{} {\n\ttype stackTracer interface {\n\t\tStackTrace() errors.StackTrace\n\t}\n\tvar sterr stackTracer\n\tvar ok bool\n\tfor err != nil {\n\t\tsterr, ok = err.(stackTracer)\n\t\tif ok {\n\t\t\tbreak\n\t\t}\n\n\t\tu, ok := err.(interface {\n\t\t\tUnwrap() error\n\t\t})\n\t\tif !ok {\n\t\t\treturn nil\n\t\t}\n\n\t\terr = u.Unwrap()\n\t}\n\tif sterr == nil {\n\t\treturn nil\n\t}\n\n\tst := sterr.StackTrace()\n\ts := &state{}\n\tout := make([]map[string]string, 0, len(st))\n\tfor _, frame := range st {\n\t\tout = append(out, map[string]string{\n\t\t\tStackSourceFileName:     frameField(frame, s, 's'),\n\t\t\tStackSourceLineName:     frameField(frame, s, 'd'),\n\t\t\tStackSourceFunctionName: frameField(frame, s, 'n'),\n\t\t})\n\t}\n\treturn out\n}\n"
  },
  {
    "path": "pkgerrors/stacktrace_test.go",
    "content": "// +build !binary_log\n\npackage pkgerrors\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"regexp\"\n\t\"testing\"\n\n\t\"github.com/pkg/errors\"\n\t\"github.com/rs/zerolog\"\n)\n\nfunc TestLogStack(t *testing.T) {\n\tzerolog.ErrorStackMarshaler = MarshalStack\n\n\tout := &bytes.Buffer{}\n\tlog := zerolog.New(out)\n\n\terr := fmt.Errorf(\"from error: %w\", errors.New(\"error message\"))\n\tlog.Log().Stack().Err(err).Msg(\"\")\n\n\tgot := out.String()\n\twant := `\\{\"stack\":\\[\\{\"func\":\"TestLogStack\",\"line\":\"21\",\"source\":\"stacktrace_test.go\"\\},.*\\],\"error\":\"from error: error message\"\\}\\n`\n\tif ok, _ := regexp.MatchString(want, got); !ok {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestLogStackFields(t *testing.T) {\n\tzerolog.ErrorStackMarshaler = MarshalStack\n\n\tout := &bytes.Buffer{}\n\tlog := zerolog.New(out)\n\n\terr := fmt.Errorf(\"from error: %w\", errors.New(\"error message\"))\n\tlog.Log().Stack().Fields([]interface{}{\"error\", err}).Msg(\"\")\n\n\tgot := out.String()\n\twant := `\\{\"error\":\"from error: error message\",\"stack\":\\[\\{\"func\":\"TestLogStackFields\",\"line\":\"37\",\"source\":\"stacktrace_test.go\"\\},.*\\]\\}\\n`\n\tif ok, _ := regexp.MatchString(want, got); !ok {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestLogStackFromContext(t *testing.T) {\n\tzerolog.ErrorStackMarshaler = MarshalStack\n\n\tout := &bytes.Buffer{}\n\tlog := zerolog.New(out).With().Stack().Logger() // calling Stack() on log context instead of event\n\n\terr := fmt.Errorf(\"from error: %w\", errors.New(\"error message\"))\n\tlog.Log().Err(err).Msg(\"\") // not explicitly calling Stack()\n\n\tgot := out.String()\n\twant := `\\{\"stack\":\\[\\{\"func\":\"TestLogStackFromContext\",\"line\":\"53\",\"source\":\"stacktrace_test.go\"\\},.*\\],\"error\":\"from error: error message\"\\}\\n`\n\tif ok, _ := regexp.MatchString(want, got); !ok {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc TestLogStackFromContextWith(t *testing.T) {\n\tzerolog.ErrorStackMarshaler = MarshalStack\n\n\terr := fmt.Errorf(\"from error: %w\", errors.New(\"error message\"))\n\tout := &bytes.Buffer{}\n\tlog := zerolog.New(out).With().Stack().Err(err).Logger() // calling Stack() on log context instead of event\n\n\tlog.Error().Msg(\"\")\n\n\tgot := out.String()\n\twant := `\\{\"level\":\"error\",\"stack\":\\[\\{\"func\":\"TestLogStackFromContextWith\",\"line\":\"66\",\"source\":\"stacktrace_test.go\"\\},.*\\],\"error\":\"from error: error message\"\\}\\n`\n\tif ok, _ := regexp.MatchString(want, got); !ok {\n\t\tt.Errorf(\"invalid log output:\\ngot:  %v\\nwant: %v\", got, want)\n\t}\n}\n\nfunc BenchmarkLogStack(b *testing.B) {\n\tzerolog.ErrorStackMarshaler = MarshalStack\n\tout := &bytes.Buffer{}\n\tlog := zerolog.New(out)\n\terr := errors.Wrap(errors.New(\"error message\"), \"from error\")\n\tb.ReportAllocs()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tlog.Log().Stack().Err(err).Msg(\"\")\n\t\tout.Reset()\n\t}\n}\n"
  },
  {
    "path": "sampler.go",
    "content": "package zerolog\n\nimport (\n\t\"math/rand\"\n\t\"sync/atomic\"\n\t\"time\"\n)\n\nvar (\n\t// Often samples log every ~ 10 events.\n\tOften = RandomSampler(10)\n\t// Sometimes samples log every ~ 100 events.\n\tSometimes = RandomSampler(100)\n\t// Rarely samples log every ~ 1000 events.\n\tRarely = RandomSampler(1000)\n)\n\n// Sampler defines an interface to a log sampler.\ntype Sampler interface {\n\t// Sample returns true if the event should be part of the sample, false if\n\t// the event should be dropped.\n\tSample(lvl Level) bool\n}\n\n// RandomSampler use a PRNG to randomly sample an event out of N events,\n// regardless of their level.\ntype RandomSampler uint32\n\n// Sample implements the Sampler interface.\nfunc (s RandomSampler) Sample(lvl Level) bool {\n\tif s <= 0 {\n\t\treturn false\n\t}\n\tif rand.Intn(int(s)) != 0 {\n\t\treturn false\n\t}\n\treturn true\n}\n\n// BasicSampler is a sampler that will send every Nth events, regardless of\n// their level.\ntype BasicSampler struct {\n\tN       uint32\n\tcounter uint32\n}\n\n// Sample implements the Sampler interface.\nfunc (s *BasicSampler) Sample(lvl Level) bool {\n\tn := s.N\n\tif n == 0 {\n\t\treturn false\n\t}\n\tif n == 1 {\n\t\treturn true\n\t}\n\tc := atomic.AddUint32(&s.counter, 1)\n\treturn c%n == 1\n}\n\n// BurstSampler lets Burst events pass per Period then pass the decision to\n// NextSampler. If Sampler is not set, all subsequent events are rejected.\ntype BurstSampler struct {\n\t// Burst is the maximum number of event per period allowed before calling\n\t// NextSampler.\n\tBurst uint32\n\t// Period defines the burst period. If 0, NextSampler is always called.\n\tPeriod time.Duration\n\t// NextSampler is the sampler used after the burst is reached. If nil,\n\t// events are always rejected after the burst.\n\tNextSampler Sampler\n\n\tcounter uint32\n\tresetAt int64\n}\n\n// Sample implements the Sampler interface.\nfunc (s *BurstSampler) Sample(lvl Level) bool {\n\tif s.Burst > 0 && s.Period > 0 {\n\t\tif s.inc() <= s.Burst {\n\t\t\treturn true\n\t\t}\n\t}\n\tif s.NextSampler == nil {\n\t\treturn false\n\t}\n\treturn s.NextSampler.Sample(lvl)\n}\n\nfunc (s *BurstSampler) inc() uint32 {\n\tnow := TimestampFunc().UnixNano()\n\tresetAt := atomic.LoadInt64(&s.resetAt)\n\tvar c uint32\n\tif now >= resetAt {\n\t\tc = 1\n\t\tatomic.StoreUint32(&s.counter, c)\n\t\tnewResetAt := now + s.Period.Nanoseconds()\n\t\treset := atomic.CompareAndSwapInt64(&s.resetAt, resetAt, newResetAt)\n\t\tif !reset {\n\t\t\t// Lost the race with another goroutine trying to reset.\n\t\t\tc = atomic.AddUint32(&s.counter, 1)\n\t\t}\n\t} else {\n\t\tc = atomic.AddUint32(&s.counter, 1)\n\t}\n\treturn c\n}\n\n// LevelSampler applies a different sampler for each level.\ntype LevelSampler struct {\n\tTraceSampler, DebugSampler, InfoSampler, WarnSampler, ErrorSampler Sampler\n}\n\nfunc (s LevelSampler) Sample(lvl Level) bool {\n\tswitch lvl {\n\tcase TraceLevel:\n\t\tif s.TraceSampler != nil {\n\t\t\treturn s.TraceSampler.Sample(lvl)\n\t\t}\n\tcase DebugLevel:\n\t\tif s.DebugSampler != nil {\n\t\t\treturn s.DebugSampler.Sample(lvl)\n\t\t}\n\tcase InfoLevel:\n\t\tif s.InfoSampler != nil {\n\t\t\treturn s.InfoSampler.Sample(lvl)\n\t\t}\n\tcase WarnLevel:\n\t\tif s.WarnSampler != nil {\n\t\t\treturn s.WarnSampler.Sample(lvl)\n\t\t}\n\tcase ErrorLevel:\n\t\tif s.ErrorSampler != nil {\n\t\t\treturn s.ErrorSampler.Sample(lvl)\n\t\t}\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "sampler_test.go",
    "content": "//go:build !binary_log\n// +build !binary_log\n\npackage zerolog\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\nvar samplers = []struct {\n\tname    string\n\tsampler func() Sampler\n\ttotal   int\n\twantMin int\n\twantMax int\n}{\n\t{\n\t\t\"BasicSampler_1\",\n\t\tfunc() Sampler {\n\t\t\treturn &BasicSampler{N: 1}\n\t\t},\n\t\t100, 100, 100,\n\t},\n\t{\n\t\t\"BasicSampler_5\",\n\t\tfunc() Sampler {\n\t\t\treturn &BasicSampler{N: 5}\n\t\t},\n\t\t100, 20, 20,\n\t},\n\t{\n\t\t\"BasicSampler_0\",\n\t\tfunc() Sampler {\n\t\t\treturn &BasicSampler{N: 0}\n\t\t},\n\t\t100, 0, 0,\n\t},\n\t{\n\t\t\"RandomSampler\",\n\t\tfunc() Sampler {\n\t\t\treturn RandomSampler(5)\n\t\t},\n\t\t100, 10, 30,\n\t},\n\t{\n\t\t\"RandomSampler_0\",\n\t\tfunc() Sampler {\n\t\t\treturn RandomSampler(0)\n\t\t},\n\t\t100, 0, 0,\n\t},\n\t{\n\t\t\"BurstSampler\",\n\t\tfunc() Sampler {\n\t\t\treturn &BurstSampler{Burst: 20, Period: time.Second}\n\t\t},\n\t\t100, 20, 20,\n\t},\n\t{\n\t\t\"BurstSampler_0\",\n\t\tfunc() Sampler {\n\t\t\treturn &BurstSampler{Burst: 0, Period: time.Second}\n\t\t},\n\t\t100, 0, 0,\n\t},\n\t{\n\t\t\"BurstSamplerNext\",\n\t\tfunc() Sampler {\n\t\t\treturn &BurstSampler{Burst: 20, Period: time.Second, NextSampler: &BasicSampler{N: 5}}\n\t\t},\n\t\t120, 40, 40,\n\t},\n}\n\nfunc TestSamplers(t *testing.T) {\n\tfor i := range samplers {\n\t\ts := samplers[i]\n\t\tt.Run(s.name, func(t *testing.T) {\n\t\t\tsampler := s.sampler()\n\t\t\tgot := 0\n\t\t\tfor t := s.total; t > 0; t-- {\n\t\t\t\tif sampler.Sample(0) {\n\t\t\t\t\tgot++\n\t\t\t\t}\n\t\t\t}\n\t\t\tif got < s.wantMin || got > s.wantMax {\n\t\t\t\tt.Errorf(\"%s.Sample(0) == true %d on %d, want [%d, %d]\", s.name, got, s.total, s.wantMin, s.wantMax)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkSamplers(b *testing.B) {\n\tfor i := range samplers {\n\t\ts := samplers[i]\n\t\tb.Run(s.name, func(b *testing.B) {\n\t\t\tsampler := s.sampler()\n\t\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\t\tfor pb.Next() {\n\t\t\t\t\tsampler.Sample(0)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestBurst(t *testing.T) {\n\tsampler := &BurstSampler{Burst: 1, Period: time.Second}\n\n\tt0 := time.Now()\n\tnow := t0\n\tmockedTime := func() time.Time {\n\t\treturn now\n\t}\n\n\tTimestampFunc = mockedTime\n\tdefer func() { TimestampFunc = time.Now }()\n\n\tscenario := []struct {\n\t\ttm   time.Time\n\t\twant bool\n\t}{\n\t\t{t0, true},\n\t\t{t0.Add(time.Second - time.Nanosecond), false},\n\t\t{t0.Add(time.Second), true},\n\t\t{t0.Add(time.Second + time.Nanosecond), false},\n\t}\n\n\tfor i, step := range scenario {\n\t\tnow = step.tm\n\t\tgot := sampler.Sample(NoLevel)\n\t\tif got != step.want {\n\t\t\tt.Errorf(\"step %d (t=%s): expect %t got %t\", i, step.tm, step.want, got)\n\t\t}\n\t}\n}\n\nfunc TestLevelSampler(t *testing.T) {\n\t// Create mock samplers that return true for specific levels\n\ttraceSampler := &BasicSampler{N: 1} // Always sample\n\tdebugSampler := &BasicSampler{N: 0} // Never sample\n\tinfoSampler := &BasicSampler{N: 1}  // Always sample\n\twarnSampler := &BasicSampler{N: 0}  // Never sample\n\terrorSampler := &BasicSampler{N: 1} // Always sample\n\n\tsampler := LevelSampler{\n\t\tTraceSampler: traceSampler,\n\t\tDebugSampler: debugSampler,\n\t\tInfoSampler:  infoSampler,\n\t\tWarnSampler:  warnSampler,\n\t\tErrorSampler: errorSampler,\n\t}\n\n\t// Test each level\n\tif !sampler.Sample(TraceLevel) {\n\t\tt.Error(\"TraceLevel should be sampled\")\n\t}\n\tif sampler.Sample(DebugLevel) {\n\t\tt.Error(\"DebugLevel should not be sampled\")\n\t}\n\tif !sampler.Sample(InfoLevel) {\n\t\tt.Error(\"InfoLevel should be sampled\")\n\t}\n\tif sampler.Sample(WarnLevel) {\n\t\tt.Error(\"WarnLevel should not be sampled\")\n\t}\n\tif !sampler.Sample(ErrorLevel) {\n\t\tt.Error(\"ErrorLevel should be sampled\")\n\t}\n\n\t// Test levels not covered by the LevelSampler sampler (FatalLevel, PanicLevel, NoLevel) - should return true\n\tif !sampler.Sample(FatalLevel) {\n\t\tt.Error(\"FatalLevel should return true when no sampler is set\")\n\t}\n\tif !sampler.Sample(PanicLevel) {\n\t\tt.Error(\"PanicLevel should return true when no sampler is set\")\n\t}\n\tif !sampler.Sample(NoLevel) {\n\t\tt.Error(\"NoLevel should return true when no sampler is set\")\n\t}\n}\n"
  },
  {
    "path": "slog.go",
    "content": "package zerolog\n\nimport (\n\t\"context\"\n\t\"log/slog\"\n\t\"time\"\n)\n\n// SlogHandler implements the slog.Handler interface using a zerolog.Logger\n// as the underlying log backend. This allows code that uses the standard\n// library's slog package to route log output through zerolog.\ntype SlogHandler struct {\n\tlogger Logger\n\tprefix string // group prefix for nested groups\n\tattrs  []slog.Attr\n}\n\n// NewSlogHandler creates a new slog.Handler that writes log records to the\n// given zerolog.Logger. The handler maps slog levels to zerolog levels and\n// converts slog attributes to zerolog fields.\nfunc NewSlogHandler(logger Logger) *SlogHandler {\n\treturn &SlogHandler{logger: logger}\n}\n\n// Enabled reports whether the handler handles records at the given level.\n// It mirrors Logger.should's level and writer checks (without sampling).\nfunc (h *SlogHandler) Enabled(_ context.Context, level slog.Level) bool {\n\tif h.logger.w == nil {\n\t\treturn false\n\t}\n\tzl := slogToZerologLevel(level)\n\tif zl < GlobalLevel() {\n\t\treturn false\n\t}\n\treturn zl >= h.logger.level\n}\n\n// Handle handles the Record. It converts the slog.Record into a zerolog event\n// and writes it using the underlying zerolog.Logger.\nfunc (h *SlogHandler) Handle(ctx context.Context, record slog.Record) error {\n\tzlevel := slogToZerologLevel(record.Level)\n\tevent := h.logger.WithLevel(zlevel)\n\tif event == nil {\n\t\treturn nil\n\t}\n\n\t// Propagate slog context to the zerolog event so that hooks\n\t// relying on Event.GetCtx() (e.g. tracing) can access it.\n\tif ctx != nil {\n\t\tevent = event.Ctx(ctx)\n\t}\n\n\t// Add pre-attached attrs from WithAttrs\n\tfor _, a := range h.attrs {\n\t\tevent = appendSlogAttr(event, a, h.prefix)\n\t}\n\n\t// Add attrs from the record itself\n\trecord.Attrs(func(a slog.Attr) bool {\n\t\tevent = appendSlogAttr(event, a, h.prefix)\n\t\treturn true\n\t})\n\n\t// Add timestamp from the slog record, but only if the logger doesn't\n\t// already have a timestampHook (added via .With().Timestamp()) to\n\t// avoid duplicate timestamp keys in the output.\n\tif !record.Time.IsZero() && !h.hasTimestampHook() {\n\t\tevent.Time(TimestampFieldName, record.Time)\n\t}\n\n\tevent.Msg(record.Message)\n\treturn nil\n}\n\n// hasTimestampHook reports whether the logger has a timestampHook installed,\n// which would cause duplicate timestamp fields if we also emit record.Time.\nfunc (h *SlogHandler) hasTimestampHook() bool {\n\tfor _, hook := range h.logger.hooks {\n\t\tif _, ok := hook.(timestampHook); ok {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// WithAttrs returns a new Handler with the given attributes pre-attached.\n// These attributes will be included in every subsequent log record.\nfunc (h *SlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {\n\tif len(attrs) == 0 {\n\t\treturn h\n\t}\n\th2 := h.clone()\n\th2.attrs = append(h2.attrs, attrs...)\n\treturn h2\n}\n\n// WithGroup returns a new Handler with the given group name. All subsequent\n// attributes will be nested under this group name in the output.\nfunc (h *SlogHandler) WithGroup(name string) slog.Handler {\n\tif name == \"\" {\n\t\treturn h\n\t}\n\th2 := h.clone()\n\tif h2.prefix != \"\" {\n\t\th2.prefix = h2.prefix + \".\" + name\n\t} else {\n\t\th2.prefix = name\n\t}\n\treturn h2\n}\n\nfunc (h *SlogHandler) clone() *SlogHandler {\n\th2 := &SlogHandler{\n\t\tlogger: h.logger,\n\t\tprefix: h.prefix,\n\t}\n\tif len(h.attrs) > 0 {\n\t\th2.attrs = make([]slog.Attr, len(h.attrs))\n\t\tcopy(h2.attrs, h.attrs)\n\t}\n\treturn h2\n}\n\n// slogToZerologLevel maps slog levels to zerolog levels.\n//\n// slog levels:  Debug=-4, Info=0, Warn=4, Error=8\n// zerolog levels: Trace=-1, Debug=0, Info=1, Warn=2, Error=3, Fatal=4, Panic=5\nfunc slogToZerologLevel(level slog.Level) Level {\n\tswitch {\n\tcase level < slog.LevelDebug:\n\t\treturn TraceLevel\n\tcase level < slog.LevelInfo:\n\t\treturn DebugLevel\n\tcase level < slog.LevelWarn:\n\t\treturn InfoLevel\n\tcase level < slog.LevelError:\n\t\treturn WarnLevel\n\tdefault:\n\t\treturn ErrorLevel\n\t}\n}\n\n// zerologToSlogLevel maps zerolog levels to slog levels.\nfunc zerologToSlogLevel(level Level) slog.Level {\n\tswitch level {\n\tcase TraceLevel:\n\t\treturn slog.LevelDebug - 4\n\tcase DebugLevel:\n\t\treturn slog.LevelDebug\n\tcase InfoLevel:\n\t\treturn slog.LevelInfo\n\tcase WarnLevel:\n\t\treturn slog.LevelWarn\n\tcase ErrorLevel:\n\t\treturn slog.LevelError\n\tcase FatalLevel:\n\t\treturn slog.LevelError + 4\n\tcase PanicLevel:\n\t\treturn slog.LevelError + 8\n\tdefault:\n\t\treturn slog.LevelInfo\n\t}\n}\n\n// joinPrefix concatenates a prefix and key with a dot separator.\n// It avoids allocations when either prefix or key is empty.\nfunc joinPrefix(prefix, key string) string {\n\tif prefix == \"\" {\n\t\treturn key\n\t}\n\tif key == \"\" {\n\t\treturn prefix\n\t}\n\treturn prefix + \".\" + key\n}\n\n// appendSlogAttr appends a single slog.Attr to the zerolog event, handling\n// type-specific encoding to avoid reflection where possible.\nfunc appendSlogAttr(event *Event, attr slog.Attr, prefix string) *Event {\n\tif event == nil {\n\t\treturn event\n\t}\n\n\t// Resolve the attribute to handle LogValuer types.\n\t// This handles slog.KindLogValuer implicitly by unwrapping\n\t// any values that implement slog.LogValuer to their resolved form.\n\tattr.Value = attr.Value.Resolve()\n\n\t// For group kinds, handle grouping before key concatenation\n\tif attr.Value.Kind() == slog.KindGroup {\n\t\tattrs := attr.Value.Group()\n\t\tif len(attrs) == 0 {\n\t\t\treturn event\n\t\t}\n\t\tgroupPrefix := joinPrefix(prefix, attr.Key)\n\t\tfor _, ga := range attrs {\n\t\t\tevent = appendSlogAttr(event, ga, groupPrefix)\n\t\t}\n\t\treturn event\n\t}\n\n\t// Skip empty keys for non-group attributes\n\tif attr.Key == \"\" {\n\t\treturn event\n\t}\n\n\tkey := joinPrefix(prefix, attr.Key)\n\tval := attr.Value\n\n\tswitch val.Kind() {\n\tcase slog.KindString:\n\t\tevent = event.Str(key, val.String())\n\tcase slog.KindInt64:\n\t\tevent = event.Int64(key, val.Int64())\n\tcase slog.KindUint64:\n\t\tevent = event.Uint64(key, val.Uint64())\n\tcase slog.KindFloat64:\n\t\tevent = event.Float64(key, val.Float64())\n\tcase slog.KindBool:\n\t\tevent = event.Bool(key, val.Bool())\n\tcase slog.KindDuration:\n\t\tevent = event.Dur(key, val.Duration())\n\tcase slog.KindTime:\n\t\tevent = event.Time(key, val.Time())\n\tcase slog.KindAny:\n\t\tv := val.Any()\n\t\tswitch cv := v.(type) {\n\t\tcase error:\n\t\t\tevent = event.AnErr(key, cv)\n\t\tcase time.Duration:\n\t\t\tevent = event.Dur(key, cv)\n\t\tcase time.Time:\n\t\t\tevent = event.Time(key, cv)\n\t\tcase []byte:\n\t\t\tevent = event.Bytes(key, cv)\n\t\tdefault:\n\t\t\tevent = event.Interface(key, v)\n\t\t}\n\tdefault:\n\t\tevent = event.Interface(key, val.Any())\n\t}\n\n\treturn event\n}\n\n// Verify at compile time that SlogHandler satisfies the slog.Handler interface.\nvar _ slog.Handler = (*SlogHandler)(nil)\n"
  },
  {
    "path": "slog_test.go",
    "content": "package zerolog_test\n\nimport (\n\t\"context\"\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"log/slog\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/rs/zerolog\"\n\t\"github.com/rs/zerolog/internal/cbor\"\n)\n\nfunc newSlogLogger(buf *bytes.Buffer) *slog.Logger {\n\tzl := zerolog.New(buf)\n\treturn slog.New(zerolog.NewSlogHandler(zl))\n}\n\n// decodeOutput converts the buffer contents to a JSON string,\n// handling CBOR-encoded output when built with the binary_log tag.\nfunc decodeOutput(buf *bytes.Buffer) string {\n\tp := buf.Bytes()\n\tif len(p) == 0 || p[0] < 0x7F {\n\t\treturn buf.String()\n\t}\n\treturn cbor.DecodeObjectToStr(p) + \"\\n\"\n}\n\nfunc decodeJSON(t *testing.T, buf *bytes.Buffer) map[string]interface{} {\n\tt.Helper()\n\tvar m map[string]interface{}\n\ts := decodeOutput(buf)\n\tif err := json.Unmarshal([]byte(s), &m); err != nil {\n\t\tt.Fatalf(\"failed to decode JSON %q: %v\", s, err)\n\t}\n\treturn m\n}\n\nfunc TestSlogHandler_BasicInfo(t *testing.T) {\n\tvar buf bytes.Buffer\n\tlogger := newSlogLogger(&buf)\n\n\tlogger.Info(\"hello world\")\n\n\tm := decodeJSON(t, &buf)\n\tif m[\"level\"] != \"info\" {\n\t\tt.Errorf(\"expected level info, got %v\", m[\"level\"])\n\t}\n\tif m[\"message\"] != \"hello world\" {\n\t\tt.Errorf(\"expected message 'hello world', got %v\", m[\"message\"])\n\t}\n}\n\nfunc TestSlogHandler_Debug(t *testing.T) {\n\tvar buf bytes.Buffer\n\tzl := zerolog.New(&buf).Level(zerolog.DebugLevel)\n\tlogger := slog.New(zerolog.NewSlogHandler(zl))\n\n\tlogger.Debug(\"debug msg\")\n\n\tm := decodeJSON(t, &buf)\n\tif m[\"level\"] != \"debug\" {\n\t\tt.Errorf(\"expected level debug, got %v\", m[\"level\"])\n\t}\n\tif m[\"message\"] != \"debug msg\" {\n\t\tt.Errorf(\"expected message 'debug msg', got %v\", m[\"message\"])\n\t}\n}\n\nfunc TestSlogHandler_Warn(t *testing.T) {\n\tvar buf bytes.Buffer\n\tlogger := newSlogLogger(&buf)\n\n\tlogger.Warn(\"warn msg\")\n\n\tm := decodeJSON(t, &buf)\n\tif m[\"level\"] != \"warn\" {\n\t\tt.Errorf(\"expected level warn, got %v\", m[\"level\"])\n\t}\n}\n\nfunc TestSlogHandler_Error(t *testing.T) {\n\tvar buf bytes.Buffer\n\tlogger := newSlogLogger(&buf)\n\n\tlogger.Error(\"error msg\")\n\n\tm := decodeJSON(t, &buf)\n\tif m[\"level\"] != \"error\" {\n\t\tt.Errorf(\"expected level error, got %v\", m[\"level\"])\n\t}\n}\n\nfunc TestSlogHandler_WithStringAttr(t *testing.T) {\n\tvar buf bytes.Buffer\n\tlogger := newSlogLogger(&buf)\n\n\tlogger.Info(\"test\", \"key\", \"value\")\n\n\tm := decodeJSON(t, &buf)\n\tif m[\"key\"] != \"value\" {\n\t\tt.Errorf(\"expected key=value, got %v\", m[\"key\"])\n\t}\n}\n\nfunc TestSlogHandler_WithIntAttr(t *testing.T) {\n\tvar buf bytes.Buffer\n\tlogger := newSlogLogger(&buf)\n\n\tlogger.Info(\"test\", slog.Int(\"count\", 42))\n\n\tm := decodeJSON(t, &buf)\n\tif m[\"count\"] != float64(42) {\n\t\tt.Errorf(\"expected count=42, got %v\", m[\"count\"])\n\t}\n}\n\nfunc TestSlogHandler_WithBoolAttr(t *testing.T) {\n\tvar buf bytes.Buffer\n\tlogger := newSlogLogger(&buf)\n\n\tlogger.Info(\"test\", slog.Bool(\"flag\", true))\n\n\tm := decodeJSON(t, &buf)\n\tif m[\"flag\"] != true {\n\t\tt.Errorf(\"expected flag=true, got %v\", m[\"flag\"])\n\t}\n}\n\nfunc TestSlogHandler_WithFloat64Attr(t *testing.T) {\n\tvar buf bytes.Buffer\n\tlogger := newSlogLogger(&buf)\n\n\tlogger.Info(\"test\", slog.Float64(\"pi\", 3.14))\n\n\tm := decodeJSON(t, &buf)\n\tif m[\"pi\"] != 3.14 {\n\t\tt.Errorf(\"expected pi=3.14, got %v\", m[\"pi\"])\n\t}\n}\n\nfunc TestSlogHandler_WithTimeAttr(t *testing.T) {\n\tvar buf bytes.Buffer\n\tlogger := newSlogLogger(&buf)\n\n\tts := time.Date(2024, 1, 15, 12, 0, 0, 0, time.UTC)\n\tlogger.Info(\"test\", slog.Time(\"created\", ts))\n\n\tm := decodeJSON(t, &buf)\n\tif m[\"created\"] == nil {\n\t\tt.Error(\"expected created field to be present\")\n\t}\n}\n\nfunc TestSlogHandler_WithDurationAttr(t *testing.T) {\n\tvar buf bytes.Buffer\n\tlogger := newSlogLogger(&buf)\n\n\tlogger.Info(\"test\", slog.Duration(\"elapsed\", 5*time.Second))\n\n\tm := decodeJSON(t, &buf)\n\tif m[\"elapsed\"] == nil {\n\t\tt.Error(\"expected elapsed field to be present\")\n\t}\n}\n\nfunc TestSlogHandler_WithErrorAttr(t *testing.T) {\n\tvar buf bytes.Buffer\n\tlogger := newSlogLogger(&buf)\n\n\tlogger.Info(\"test\", slog.Any(\"err\", errors.New(\"something failed\")))\n\n\tm := decodeJSON(t, &buf)\n\tif m[\"err\"] != \"something failed\" {\n\t\tt.Errorf(\"expected err='something failed', got %v\", m[\"err\"])\n\t}\n}\n\nfunc TestSlogHandler_WithAttrs(t *testing.T) {\n\tvar buf bytes.Buffer\n\tzl := zerolog.New(&buf)\n\thandler := zerolog.NewSlogHandler(zl)\n\n\tchild := handler.WithAttrs([]slog.Attr{\n\t\tslog.String(\"component\", \"auth\"),\n\t\tslog.Int(\"version\", 2),\n\t})\n\tlogger := slog.New(child)\n\n\tlogger.Info(\"request handled\")\n\n\tm := decodeJSON(t, &buf)\n\tif m[\"component\"] != \"auth\" {\n\t\tt.Errorf(\"expected component=auth, got %v\", m[\"component\"])\n\t}\n\tif m[\"version\"] != float64(2) {\n\t\tt.Errorf(\"expected version=2, got %v\", m[\"version\"])\n\t}\n\tif m[\"message\"] != \"request handled\" {\n\t\tt.Errorf(\"expected message 'request handled', got %v\", m[\"message\"])\n\t}\n}\n\nfunc TestSlogHandler_WithAttrsEmpty(t *testing.T) {\n\tvar buf bytes.Buffer\n\tzl := zerolog.New(&buf)\n\thandler := zerolog.NewSlogHandler(zl)\n\n\t// WithAttrs with empty slice should return same handler\n\tchild := handler.WithAttrs(nil)\n\tif child != handler {\n\t\tt.Error(\"expected WithAttrs(nil) to return same handler\")\n\t}\n}\n\nfunc TestSlogHandler_WithGroup(t *testing.T) {\n\tvar buf bytes.Buffer\n\tzl := zerolog.New(&buf)\n\thandler := zerolog.NewSlogHandler(zl)\n\n\tchild := handler.WithGroup(\"request\")\n\tlogger := slog.New(child)\n\n\tlogger.Info(\"handled\", \"method\", \"GET\", \"status\", 200)\n\n\tm := decodeJSON(t, &buf)\n\tif m[\"request.method\"] != \"GET\" {\n\t\tt.Errorf(\"expected request.method=GET, got %v\", m[\"request.method\"])\n\t}\n\tif m[\"request.status\"] != float64(200) {\n\t\tt.Errorf(\"expected request.status=200, got %v\", m[\"request.status\"])\n\t}\n}\n\nfunc TestSlogHandler_WithGroupEmpty(t *testing.T) {\n\tvar buf bytes.Buffer\n\tzl := zerolog.New(&buf)\n\thandler := zerolog.NewSlogHandler(zl)\n\n\t// WithGroup with empty name should return same handler\n\tchild := handler.WithGroup(\"\")\n\tif child != handler {\n\t\tt.Error(\"expected WithGroup('') to return same handler\")\n\t}\n}\n\nfunc TestSlogHandler_WithNestedGroups(t *testing.T) {\n\tvar buf bytes.Buffer\n\tzl := zerolog.New(&buf)\n\thandler := zerolog.NewSlogHandler(zl)\n\n\tchild := handler.WithGroup(\"http\").WithGroup(\"request\")\n\tlogger := slog.New(child)\n\n\tlogger.Info(\"handled\", \"method\", \"POST\")\n\n\tm := decodeJSON(t, &buf)\n\tif m[\"http.request.method\"] != \"POST\" {\n\t\tt.Errorf(\"expected http.request.method=POST, got %v\", m[\"http.request.method\"])\n\t}\n}\n\nfunc TestSlogHandler_WithGroupAndAttrs(t *testing.T) {\n\tvar buf bytes.Buffer\n\tzl := zerolog.New(&buf)\n\thandler := zerolog.NewSlogHandler(zl)\n\n\tchild := handler.WithGroup(\"server\").WithAttrs([]slog.Attr{\n\t\tslog.String(\"host\", \"localhost\"),\n\t})\n\tlogger := slog.New(child)\n\n\tlogger.Info(\"started\", \"port\", 8080)\n\n\tm := decodeJSON(t, &buf)\n\tif m[\"server.host\"] != \"localhost\" {\n\t\tt.Errorf(\"expected server.host=localhost, got %v\", m[\"server.host\"])\n\t}\n\tif m[\"server.port\"] != float64(8080) {\n\t\tt.Errorf(\"expected server.port=8080, got %v\", m[\"server.port\"])\n\t}\n}\n\nfunc TestSlogHandler_GroupAttrInRecord(t *testing.T) {\n\tvar buf bytes.Buffer\n\tlogger := newSlogLogger(&buf)\n\n\tlogger.Info(\"test\", slog.Group(\"user\",\n\t\tslog.String(\"name\", \"alice\"),\n\t\tslog.Int(\"age\", 30),\n\t))\n\n\tm := decodeJSON(t, &buf)\n\tif m[\"user.name\"] != \"alice\" {\n\t\tt.Errorf(\"expected user.name=alice, got %v\", m[\"user.name\"])\n\t}\n\tif m[\"user.age\"] != float64(30) {\n\t\tt.Errorf(\"expected user.age=30, got %v\", m[\"user.age\"])\n\t}\n}\n\nfunc TestSlogHandler_LevelFiltering(t *testing.T) {\n\tvar buf bytes.Buffer\n\tzl := zerolog.New(&buf).Level(zerolog.WarnLevel)\n\thandler := zerolog.NewSlogHandler(zl)\n\n\t// Debug should be filtered\n\tif handler.Enabled(nil, slog.LevelDebug) {\n\t\tt.Error(\"expected debug to be filtered at warn level\")\n\t}\n\t// Info should be filtered\n\tif handler.Enabled(nil, slog.LevelInfo) {\n\t\tt.Error(\"expected info to be filtered at warn level\")\n\t}\n\t// Warn should pass\n\tif !handler.Enabled(nil, slog.LevelWarn) {\n\t\tt.Error(\"expected warn to be enabled at warn level\")\n\t}\n\t// Error should pass\n\tif !handler.Enabled(nil, slog.LevelError) {\n\t\tt.Error(\"expected error to be enabled at warn level\")\n\t}\n}\n\nfunc TestSlogHandler_FilteredMessageNotWritten(t *testing.T) {\n\tvar buf bytes.Buffer\n\tzl := zerolog.New(&buf).Level(zerolog.ErrorLevel)\n\tlogger := slog.New(zerolog.NewSlogHandler(zl))\n\n\tlogger.Info(\"should not appear\")\n\n\tif buf.Len() != 0 {\n\t\tt.Errorf(\"expected no output for filtered message, got %q\", buf.String())\n\t}\n}\n\nfunc TestSlogHandler_MultipleAttrs(t *testing.T) {\n\tvar buf bytes.Buffer\n\tlogger := newSlogLogger(&buf)\n\n\tlogger.Info(\"multi\",\n\t\tslog.String(\"a\", \"1\"),\n\t\tslog.Int(\"b\", 2),\n\t\tslog.Bool(\"c\", true),\n\t\tslog.Float64(\"d\", 3.5),\n\t)\n\n\tm := decodeJSON(t, &buf)\n\tif m[\"a\"] != \"1\" {\n\t\tt.Errorf(\"expected a=1, got %v\", m[\"a\"])\n\t}\n\tif m[\"b\"] != float64(2) {\n\t\tt.Errorf(\"expected b=2, got %v\", m[\"b\"])\n\t}\n\tif m[\"c\"] != true {\n\t\tt.Errorf(\"expected c=true, got %v\", m[\"c\"])\n\t}\n\tif m[\"d\"] != 3.5 {\n\t\tt.Errorf(\"expected d=3.5, got %v\", m[\"d\"])\n\t}\n}\n\nfunc TestSlogHandler_LogValuer(t *testing.T) {\n\tvar buf bytes.Buffer\n\tlogger := newSlogLogger(&buf)\n\n\tlogger.Info(\"test\", \"addr\", testLogValuer{host: \"example.com\", port: 443})\n\n\tm := decodeJSON(t, &buf)\n\t// LogValuer resolves to a group\n\tif m[\"addr.host\"] != \"example.com\" {\n\t\tt.Errorf(\"expected addr.host=example.com, got %v\", m[\"addr.host\"])\n\t}\n\tif m[\"addr.port\"] != float64(443) {\n\t\tt.Errorf(\"expected addr.port=443, got %v\", m[\"addr.port\"])\n\t}\n}\n\ntype testLogValuer struct {\n\thost string\n\tport int\n}\n\nfunc (v testLogValuer) LogValue() slog.Value {\n\treturn slog.GroupValue(\n\t\tslog.String(\"host\", v.host),\n\t\tslog.Int(\"port\", v.port),\n\t)\n}\n\nfunc TestSlogHandler_WithAttrsImmutability(t *testing.T) {\n\tvar buf1, buf2 bytes.Buffer\n\tzl1 := zerolog.New(&buf1)\n\tzl2 := zerolog.New(&buf2)\n\n\thandler := zerolog.NewSlogHandler(zl1)\n\tchild1 := handler.WithAttrs([]slog.Attr{slog.String(\"from\", \"child1\")})\n\t_ = zerolog.NewSlogHandler(zl2).WithAttrs([]slog.Attr{slog.String(\"from\", \"child2\")})\n\n\tslog.New(child1).Info(\"test\")\n\n\tm := decodeJSON(t, &buf1)\n\tif m[\"from\"] != \"child1\" {\n\t\tt.Errorf(\"expected from=child1, got %v\", m[\"from\"])\n\t}\n}\n\nfunc TestSlogHandler_LevelMapping(t *testing.T) {\n\ttests := []struct {\n\t\tslogLevel slog.Level\n\t\twantLevel string\n\t}{\n\t\t{slog.LevelDebug - 4, \"trace\"},\n\t\t{slog.LevelDebug, \"debug\"},\n\t\t{slog.LevelInfo, \"info\"},\n\t\t{slog.LevelWarn, \"warn\"},\n\t\t{slog.LevelError, \"error\"},\n\t}\n\n\tfor _, tt := range tests {\n\t\tvar buf bytes.Buffer\n\t\tzl := zerolog.New(&buf).Level(zerolog.TraceLevel)\n\t\tlogger := slog.New(zerolog.NewSlogHandler(zl))\n\n\t\tlogger.Log(nil, tt.slogLevel, \"test\")\n\n\t\tm := decodeJSON(t, &buf)\n\t\tif m[\"level\"] != tt.wantLevel {\n\t\t\tt.Errorf(\"slog level %d: expected zerolog level %q, got %q\",\n\t\t\t\ttt.slogLevel, tt.wantLevel, m[\"level\"])\n\t\t}\n\t\tbuf.Reset()\n\t}\n}\n\nfunc TestSlogHandler_EmptyMessage(t *testing.T) {\n\tvar buf bytes.Buffer\n\tlogger := newSlogLogger(&buf)\n\n\tlogger.Info(\"\", \"key\", \"val\")\n\n\tm := decodeJSON(t, &buf)\n\tif m[\"key\"] != \"val\" {\n\t\tt.Errorf(\"expected key=val, got %v\", m[\"key\"])\n\t}\n}\n\nfunc TestSlogHandler_WithContext(t *testing.T) {\n\tvar buf bytes.Buffer\n\tzl := zerolog.New(&buf).With().Str(\"service\", \"api\").Logger()\n\tlogger := slog.New(zerolog.NewSlogHandler(zl))\n\n\tlogger.Info(\"request\")\n\n\tm := decodeJSON(t, &buf)\n\tif m[\"service\"] != \"api\" {\n\t\tt.Errorf(\"expected service=api, got %v\", m[\"service\"])\n\t}\n\tif m[\"message\"] != \"request\" {\n\t\tt.Errorf(\"expected message 'request', got %v\", m[\"message\"])\n\t}\n}\n\nfunc TestSlogHandler_EnabledRespectsGlobalLevel(t *testing.T) {\n\tvar buf bytes.Buffer\n\tzl := zerolog.New(&buf).Level(zerolog.DebugLevel)\n\thandler := zerolog.NewSlogHandler(zl)\n\n\t// Logger level is debug, so info should be enabled\n\tif !handler.Enabled(nil, slog.LevelInfo) {\n\t\tt.Fatal(\"expected info to be enabled before setting global level\")\n\t}\n\n\t// Set global level to error\n\tzerolog.SetGlobalLevel(zerolog.ErrorLevel)\n\tdefer zerolog.SetGlobalLevel(zerolog.TraceLevel)\n\n\t// Now info should be disabled even though logger level allows it\n\tif handler.Enabled(nil, slog.LevelInfo) {\n\t\tt.Error(\"expected info to be disabled when GlobalLevel is error\")\n\t}\n\t// Error should still be enabled\n\tif !handler.Enabled(nil, slog.LevelError) {\n\t\tt.Error(\"expected error to be enabled when GlobalLevel is error\")\n\t}\n}\n\nfunc TestSlogHandler_EnabledNilWriter(t *testing.T) {\n\tzl := zerolog.Nop()\n\thandler := zerolog.NewSlogHandler(zl)\n\n\tif handler.Enabled(nil, slog.LevelError) {\n\t\tt.Error(\"expected disabled for nop logger\")\n\t}\n}\n\nfunc TestSlogHandler_HandlePropagatesContext(t *testing.T) {\n\tvar buf bytes.Buffer\n\ttype ctxKey struct{}\n\tctx := context.WithValue(context.Background(), ctxKey{}, \"test-value\")\n\n\tvar gotCtx context.Context\n\thook := zerolog.HookFunc(func(e *zerolog.Event, level zerolog.Level, msg string) {\n\t\tgotCtx = e.GetCtx()\n\t})\n\n\tzl := zerolog.New(&buf).Hook(hook)\n\thandler := zerolog.NewSlogHandler(zl)\n\n\trecord := slog.NewRecord(time.Now(), slog.LevelInfo, \"test\", 0)\n\t_ = handler.Handle(ctx, record)\n\n\tif gotCtx == nil {\n\t\tt.Fatal(\"expected context to be propagated to event\")\n\t}\n\tif gotCtx.Value(ctxKey{}) != \"test-value\" {\n\t\tt.Error(\"expected context value to be preserved\")\n\t}\n}\n\nfunc TestSlogHandler_NoDuplicateTimestamp(t *testing.T) {\n\tvar buf bytes.Buffer\n\t// Create logger with Timestamp() hook - this adds \"time\" automatically\n\tzl := zerolog.New(&buf).With().Timestamp().Logger()\n\thandler := zerolog.NewSlogHandler(zl)\n\n\trecord := slog.NewRecord(time.Now(), slog.LevelInfo, \"test\", 0)\n\t_ = handler.Handle(context.Background(), record)\n\n\toutput := decodeOutput(&buf)\n\t// Count occurrences of the timestamp field name - should appear exactly once\n\tcount := 0\n\tfor i := 0; i < len(output); i++ {\n\t\tif i+4 <= len(output) && output[i:i+4] == \"time\" {\n\t\t\tcount++\n\t\t}\n\t}\n\tif count > 1 {\n\t\tt.Errorf(\"expected at most 1 timestamp field, got %d in output: %s\", count, output)\n\t}\n}\n\nfunc TestSlogHandler_TimestampWithoutHook(t *testing.T) {\n\tvar buf bytes.Buffer\n\t// Logger without Timestamp() hook - Handle should add the timestamp\n\tzl := zerolog.New(&buf)\n\thandler := zerolog.NewSlogHandler(zl)\n\n\tts := time.Date(2024, 6, 15, 12, 0, 0, 0, time.UTC)\n\trecord := slog.NewRecord(ts, slog.LevelInfo, \"test\", 0)\n\t_ = handler.Handle(context.Background(), record)\n\n\tm := decodeJSON(t, &buf)\n\tif m[zerolog.TimestampFieldName] == nil {\n\t\tt.Error(\"expected timestamp field when logger has no timestamp hook\")\n\t}\n}\n"
  },
  {
    "path": "syslog.go",
    "content": "// +build !windows\n// +build !binary_log\n\npackage zerolog\n\nimport (\n\t\"io\"\n)\n\n// See http://cee.mitre.org/language/1.0-beta1/clt.html#syslog\n// or https://www.rsyslog.com/json-elasticsearch/\nconst ceePrefix = \"@cee:\"\n\n// SyslogWriter is an interface matching a syslog.Writer struct.\ntype SyslogWriter interface {\n\tio.Writer\n\tDebug(m string) error\n\tInfo(m string) error\n\tWarning(m string) error\n\tErr(m string) error\n\tEmerg(m string) error\n\tCrit(m string) error\n}\n\ntype syslogWriter struct {\n\tw      SyslogWriter\n\tprefix string\n}\n\n// SyslogLevelWriter wraps a SyslogWriter and call the right syslog level\n// method matching the zerolog level.\nfunc SyslogLevelWriter(w SyslogWriter) LevelWriter {\n\treturn syslogWriter{w, \"\"}\n}\n\n// SyslogCEEWriter wraps a SyslogWriter with a SyslogLevelWriter that adds a\n// MITRE CEE prefix for JSON syslog entries, compatible with rsyslog \n// and syslog-ng JSON logging support. \n// See https://www.rsyslog.com/json-elasticsearch/\nfunc SyslogCEEWriter(w SyslogWriter) LevelWriter {\n\treturn syslogWriter{w, ceePrefix}\n}\n\nfunc (sw syslogWriter) Write(p []byte) (n int, err error) {\n\tvar pn int\n\tif sw.prefix != \"\" {\n\t\tpn, err = sw.w.Write([]byte(sw.prefix))\n\t\tif err != nil {\n\t\t\treturn pn, err\n\t\t}\n\t}\n\tn, err = sw.w.Write(p)\n\treturn pn + n, err\n}\n\n// WriteLevel implements LevelWriter interface.\nfunc (sw syslogWriter) WriteLevel(level Level, p []byte) (n int, err error) {\n\tswitch level {\n\tcase TraceLevel:\n\tcase DebugLevel:\n\t\terr = sw.w.Debug(sw.prefix + string(p))\n\tcase InfoLevel:\n\t\terr = sw.w.Info(sw.prefix + string(p))\n\tcase WarnLevel:\n\t\terr = sw.w.Warning(sw.prefix + string(p))\n\tcase ErrorLevel:\n\t\terr = sw.w.Err(sw.prefix + string(p))\n\tcase FatalLevel:\n\t\terr = sw.w.Emerg(sw.prefix + string(p))\n\tcase PanicLevel:\n\t\terr = sw.w.Crit(sw.prefix + string(p))\n\tcase NoLevel:\n\t\terr = sw.w.Info(sw.prefix + string(p))\n\tdefault:\n\t\tpanic(\"invalid level\")\n\t}\n\t// Any CEE prefix is not part of the message, so we don't include its length\n\tn = len(p)\n\treturn\n}\n\n// Call the underlying writer's Close method if it is an io.Closer. Otherwise\n// does nothing.\nfunc (sw syslogWriter) Close() error {\n\tif c, ok := sw.w.(io.Closer); ok {\n\t\treturn c.Close()\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "syslog_test.go",
    "content": "// +build !binary_log\n// +build !windows\n\npackage zerolog\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n)\n\ntype syslogEvent struct {\n\tlevel string\n\tmsg   string\n}\ntype syslogTestWriter struct {\n\tevents []syslogEvent\n}\n\nfunc (w *syslogTestWriter) Write(p []byte) (int, error) {\n\treturn len(p), nil\n}\nfunc (w *syslogTestWriter) Trace(m string) error {\n\tw.events = append(w.events, syslogEvent{\"Trace\", m})\n\treturn nil\n}\nfunc (w *syslogTestWriter) Debug(m string) error {\n\tw.events = append(w.events, syslogEvent{\"Debug\", m})\n\treturn nil\n}\nfunc (w *syslogTestWriter) Info(m string) error {\n\tw.events = append(w.events, syslogEvent{\"Info\", m})\n\treturn nil\n}\nfunc (w *syslogTestWriter) Warning(m string) error {\n\tw.events = append(w.events, syslogEvent{\"Warning\", m})\n\treturn nil\n}\nfunc (w *syslogTestWriter) Err(m string) error {\n\tw.events = append(w.events, syslogEvent{\"Err\", m})\n\treturn nil\n}\nfunc (w *syslogTestWriter) Emerg(m string) error {\n\tw.events = append(w.events, syslogEvent{\"Emerg\", m})\n\treturn nil\n}\nfunc (w *syslogTestWriter) Crit(m string) error {\n\tw.events = append(w.events, syslogEvent{\"Crit\", m})\n\treturn nil\n}\n\nfunc TestSyslogWriter(t *testing.T) {\n\tsw := &syslogTestWriter{}\n\tlog := New(SyslogLevelWriter(sw))\n\tlog.Trace().Msg(\"trace\")\n\tlog.Debug().Msg(\"debug\")\n\tlog.Info().Msg(\"info\")\n\tlog.Warn().Msg(\"warn\")\n\tlog.Error().Msg(\"error\")\n\tlog.Log().Msg(\"nolevel\")\n\twant := []syslogEvent{\n\t\t{\"Debug\", `{\"level\":\"debug\",\"message\":\"debug\"}` + \"\\n\"},\n\t\t{\"Info\", `{\"level\":\"info\",\"message\":\"info\"}` + \"\\n\"},\n\t\t{\"Warning\", `{\"level\":\"warn\",\"message\":\"warn\"}` + \"\\n\"},\n\t\t{\"Err\", `{\"level\":\"error\",\"message\":\"error\"}` + \"\\n\"},\n\t\t{\"Info\", `{\"message\":\"nolevel\"}` + \"\\n\"},\n\t}\n\tif got := sw.events; !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"Invalid syslog message routing: want %v, got %v\", want, got)\n\t}\n}\n\ntype testCEEwriter struct {\n\tbuf *bytes.Buffer\n}\n\n// Only implement one method as we're just testing the prefixing\nfunc (c testCEEwriter) Debug(m string) error { return nil }\n\nfunc (c testCEEwriter) Info(m string) error {\n\t_, err := c.buf.Write([]byte(m))\n\treturn err\n}\n\nfunc (c testCEEwriter) Warning(m string) error { return nil }\n\nfunc (c testCEEwriter) Err(m string) error { return nil }\n\nfunc (c testCEEwriter) Emerg(m string) error { return nil }\n\nfunc (c testCEEwriter) Crit(m string) error { return nil }\n\nfunc (c testCEEwriter) Write(b []byte) (int, error) {\n\treturn c.buf.Write(b)\n}\n\nfunc TestSyslogWriter_WithCEE(t *testing.T) {\n\tvar buf bytes.Buffer\n\tsw := testCEEwriter{&buf}\n\tlog := New(SyslogCEEWriter(sw))\n\tlog.Info().Str(\"key\", \"value\").Msg(\"message string\")\n\tgot := buf.String()\n\twant := \"@cee:{\"\n\tif !strings.HasPrefix(got, want) {\n\t\tt.Errorf(\"Bad CEE message start: want %v, got %v\", want, got)\n\t}\n}\n\ntype errorSyslogWriter struct {\n\t*syslogTestWriter\n\twriteError error\n}\n\nfunc (w *errorSyslogWriter) Write(p []byte) (int, error) {\n\tif w.writeError != nil {\n\t\treturn 0, w.writeError\n\t}\n\treturn len(p), nil\n}\n\nfunc TestSyslogWriter_Write(t *testing.T) {\n\t// Test Write method without prefix\n\tsw := &syslogTestWriter{}\n\twriter := SyslogLevelWriter(sw)\n\n\tdata := []byte(\"test message\")\n\tn, err := writer.Write(data)\n\tif err != nil {\n\t\tt.Errorf(\"Write failed: %v\", err)\n\t}\n\tif n != len(data) {\n\t\tt.Errorf(\"Write returned wrong length: got %d, want %d\", n, len(data))\n\t}\n\n\t// Test Write method with CEE prefix\n\tsw2 := &syslogTestWriter{}\n\twriter2 := SyslogCEEWriter(sw2)\n\n\tdata2 := []byte(\"test message\")\n\tn2, err2 := writer2.Write(data2)\n\tif err2 != nil {\n\t\tt.Errorf(\"Write with CEE failed: %v\", err2)\n\t}\n\texpectedLen := len(ceePrefix) + len(data2)\n\tif n2 != expectedLen {\n\t\tt.Errorf(\"Write with CEE returned wrong length: got %d, want %d\", n2, expectedLen)\n\t}\n\n\t// Test Write method with CEE prefix and error on prefix write\n\tsw3 := &errorSyslogWriter{syslogTestWriter: &syslogTestWriter{}, writeError: io.EOF}\n\twriter3 := SyslogCEEWriter(sw3)\n\n\t_, err3 := writer3.Write(data2)\n\tif err3 != io.EOF {\n\t\tt.Errorf(\"Write with CEE error failed: got %v, want %v\", err3, io.EOF)\n\t}\n}\n\nfunc TestSyslogWriter_WriteLevel_AllLevels(t *testing.T) {\n\tsw := &syslogTestWriter{}\n\twriter := SyslogLevelWriter(sw)\n\n\t// Test all levels to ensure full coverage\n\twriter.WriteLevel(TraceLevel, []byte(`{\"level\":\"trace\",\"message\":\"trace\"}`+\"\\n\"))\n\twriter.WriteLevel(DebugLevel, []byte(`{\"level\":\"debug\",\"message\":\"debug\"}`+\"\\n\"))\n\twriter.WriteLevel(InfoLevel, []byte(`{\"level\":\"info\",\"message\":\"info\"}`+\"\\n\"))\n\twriter.WriteLevel(WarnLevel, []byte(`{\"level\":\"warn\",\"message\":\"warn\"}`+\"\\n\"))\n\twriter.WriteLevel(ErrorLevel, []byte(`{\"level\":\"error\",\"message\":\"error\"}`+\"\\n\"))\n\twriter.WriteLevel(FatalLevel, []byte(`{\"level\":\"fatal\",\"message\":\"fatal\"}`+\"\\n\"))\n\twriter.WriteLevel(PanicLevel, []byte(`{\"level\":\"panic\",\"message\":\"panic\"}`+\"\\n\"))\n\twriter.WriteLevel(NoLevel, []byte(`{\"message\":\"nolevel\"}`+\"\\n\"))\n\n\twant := []syslogEvent{\n\t\t{\"Debug\", `{\"level\":\"debug\",\"message\":\"debug\"}` + \"\\n\"},\n\t\t{\"Info\", `{\"level\":\"info\",\"message\":\"info\"}` + \"\\n\"},\n\t\t{\"Warning\", `{\"level\":\"warn\",\"message\":\"warn\"}` + \"\\n\"},\n\t\t{\"Err\", `{\"level\":\"error\",\"message\":\"error\"}` + \"\\n\"},\n\t\t{\"Emerg\", `{\"level\":\"fatal\",\"message\":\"fatal\"}` + \"\\n\"},\n\t\t{\"Crit\", `{\"level\":\"panic\",\"message\":\"panic\"}` + \"\\n\"},\n\t\t{\"Info\", `{\"message\":\"nolevel\"}` + \"\\n\"},\n\t}\n\tif got := sw.events; !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"Invalid syslog message routing: want %v, got %v\", want, got)\n\t}\n}\n\ntype closableSyslogWriter struct {\n\t*syslogTestWriter\n\tclosed bool\n}\n\nfunc (w *closableSyslogWriter) Close() error {\n\tw.closed = true\n\treturn nil\n}\n\nfunc TestSyslogWriter_Close(t *testing.T) {\n\t// Test with closable writer\n\tsw := &closableSyslogWriter{syslogTestWriter: &syslogTestWriter{}}\n\twriter := SyslogLevelWriter(sw).(syslogWriter) // Cast to concrete type to access Close\n\n\terr := writer.Close()\n\tif err != nil {\n\t\tt.Errorf(\"Close failed: %v\", err)\n\t}\n\tif !sw.closed {\n\t\tt.Error(\"Close was not called on underlying writer\")\n\t}\n\n\t// Test with non-closable writer\n\tsw2 := &syslogTestWriter{}\n\twriter2 := SyslogLevelWriter(sw2).(syslogWriter) // Cast to concrete type to access Close\n\n\terr = writer2.Close()\n\tif err != nil {\n\t\tt.Errorf(\"Close failed for non-closable writer: %v\", err)\n\t}\n}\n\nfunc TestSyslogWriter_WriteLevel_InvalidLevel(t *testing.T) {\n\tsw := &syslogTestWriter{}\n\twriter := SyslogLevelWriter(sw)\n\n\t// Test invalid level - should panic\n\tdefer func() {\n\t\tif r := recover(); r == nil {\n\t\t\tt.Error(\"Expected panic for invalid level\")\n\t\t} else if r != \"invalid level\" {\n\t\t\tt.Errorf(\"Expected panic 'invalid level', got %v\", r)\n\t\t}\n\t}()\n\n\twriter.WriteLevel(Level(100), []byte(\"test\"))\n}\n"
  },
  {
    "path": "writer.go",
    "content": "package zerolog\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"path\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n)\n\n// LevelWriter defines as interface a writer may implement in order\n// to receive level information with payload.\ntype LevelWriter interface {\n\tio.Writer\n\tWriteLevel(level Level, p []byte) (n int, err error)\n}\n\n// LevelWriterAdapter adapts an io.Writer to support the LevelWriter interface.\ntype LevelWriterAdapter struct {\n\tio.Writer\n}\n\n// WriteLevel simply writes everything to the adapted writer, ignoring the level.\nfunc (lw LevelWriterAdapter) WriteLevel(l Level, p []byte) (n int, err error) {\n\treturn lw.Write(p)\n}\n\n// Call the underlying writer's Close method if it is an io.Closer. Otherwise\n// does nothing.\nfunc (lw LevelWriterAdapter) Close() error {\n\tif closer, ok := lw.Writer.(io.Closer); ok {\n\t\treturn closer.Close()\n\t}\n\treturn nil\n}\n\ntype syncWriter struct {\n\tmu sync.Mutex\n\tlw LevelWriter\n}\n\n// SyncWriter wraps w so that each call to Write is synchronized with a mutex.\n// This syncer can be used to wrap the call to writer's Write method if it is\n// not thread safe. Note that you do not need this wrapper for os.File Write\n// operations on POSIX and Windows systems as they are already thread-safe.\nfunc SyncWriter(w io.Writer) io.Writer {\n\tif lw, ok := w.(LevelWriter); ok {\n\t\treturn &syncWriter{lw: lw}\n\t}\n\treturn &syncWriter{lw: LevelWriterAdapter{w}}\n}\n\n// Write implements the io.Writer interface.\nfunc (s *syncWriter) Write(p []byte) (n int, err error) {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\treturn s.lw.Write(p)\n}\n\n// WriteLevel implements the LevelWriter interface.\nfunc (s *syncWriter) WriteLevel(l Level, p []byte) (n int, err error) {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\treturn s.lw.WriteLevel(l, p)\n}\n\nfunc (s *syncWriter) Close() error {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\tif closer, ok := s.lw.(io.Closer); ok {\n\t\treturn closer.Close()\n\t}\n\treturn nil\n}\n\ntype multiLevelWriter struct {\n\twriters []LevelWriter\n}\n\nfunc (t multiLevelWriter) Write(p []byte) (n int, err error) {\n\tfor _, w := range t.writers {\n\t\tif _n, _err := w.Write(p); err == nil {\n\t\t\tn = _n\n\t\t\tif _err != nil {\n\t\t\t\terr = _err\n\t\t\t} else if _n != len(p) {\n\t\t\t\terr = io.ErrShortWrite\n\t\t\t}\n\t\t}\n\t}\n\treturn n, err\n}\n\nfunc (t multiLevelWriter) WriteLevel(l Level, p []byte) (n int, err error) {\n\tfor _, w := range t.writers {\n\t\tif _n, _err := w.WriteLevel(l, p); err == nil {\n\t\t\tn = _n\n\t\t\tif _err != nil {\n\t\t\t\terr = _err\n\t\t\t} else if _n != len(p) {\n\t\t\t\terr = io.ErrShortWrite\n\t\t\t}\n\t\t}\n\t}\n\treturn n, err\n}\n\n// Calls close on all the underlying writers that are io.Closers. If any of the\n// Close methods return an error, the remainder of the closers are not closed\n// and the error is returned.\nfunc (t multiLevelWriter) Close() error {\n\tfor _, w := range t.writers {\n\t\tif closer, ok := w.(io.Closer); ok {\n\t\t\tif err := closer.Close(); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// MultiLevelWriter creates a writer that duplicates its writes to all the\n// provided writers, similar to the Unix tee(1) command. If some writers\n// implement LevelWriter, their WriteLevel method will be used instead of Write.\nfunc MultiLevelWriter(writers ...io.Writer) LevelWriter {\n\tlwriters := make([]LevelWriter, 0, len(writers))\n\tfor _, w := range writers {\n\t\tif lw, ok := w.(LevelWriter); ok {\n\t\t\tlwriters = append(lwriters, lw)\n\t\t} else {\n\t\t\tlwriters = append(lwriters, LevelWriterAdapter{w})\n\t\t}\n\t}\n\treturn multiLevelWriter{lwriters}\n}\n\n// TestingLog is the logging interface of testing.TB.\ntype TestingLog interface {\n\tLog(args ...interface{})\n\tLogf(format string, args ...interface{})\n\tHelper()\n}\n\n// TestWriter is a writer that writes to testing.TB.\ntype TestWriter struct {\n\tT TestingLog\n\n\t// Frame skips caller frames to capture the original file and line numbers.\n\tFrame int\n}\n\n// NewTestWriter creates a writer that logs to the testing.TB.\nfunc NewTestWriter(t TestingLog) TestWriter {\n\treturn TestWriter{T: t}\n}\n\n// Write to testing.TB.\nfunc (t TestWriter) Write(p []byte) (n int, err error) {\n\tt.T.Helper()\n\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// Try to correct the log file and line number to the caller.\n\tif t.Frame > 0 {\n\t\t_, origFile, origLine, _ := runtime.Caller(1)\n\t\t_, frameFile, frameLine, ok := runtime.Caller(1 + t.Frame)\n\t\tif ok {\n\t\t\terase := strings.Repeat(\"\\b\", len(path.Base(origFile))+len(strconv.Itoa(origLine))+3)\n\t\t\tt.T.Logf(\"%s%s:%d: %s\", erase, path.Base(frameFile), frameLine, p)\n\t\t\treturn n, err\n\t\t}\n\t}\n\tt.T.Log(string(p))\n\n\treturn n, err\n}\n\n// ConsoleTestWriter creates an option that correctly sets the file frame depth for testing.TB log.\nfunc ConsoleTestWriter(t TestingLog) func(w *ConsoleWriter) {\n\treturn func(w *ConsoleWriter) {\n\t\tw.Out = TestWriter{T: t, Frame: 6}\n\t}\n}\n\n// FilteredLevelWriter writes only logs at Level or above to Writer.\n//\n// It should be used only in combination with MultiLevelWriter when you\n// want to write to multiple destinations at different levels. Otherwise\n// you should just set the level on the logger and filter events early.\n// When using MultiLevelWriter then you set the level on the logger to\n// the lowest of the levels you use for writers.\ntype FilteredLevelWriter struct {\n\tWriter LevelWriter\n\tLevel  Level\n}\n\n// Write writes to the underlying Writer.\nfunc (w *FilteredLevelWriter) Write(p []byte) (int, error) {\n\treturn w.Writer.Write(p)\n}\n\n// WriteLevel calls WriteLevel of the underlying Writer only if the level is equal\n// or above the Level.\nfunc (w *FilteredLevelWriter) WriteLevel(level Level, p []byte) (int, error) {\n\tif level >= w.Level {\n\t\treturn w.Writer.WriteLevel(level, p)\n\t}\n\treturn len(p), nil\n}\n\n// Call the underlying writer's Close method if it is an io.Closer. Otherwise\n// does nothing.\nfunc (w *FilteredLevelWriter) Close() error {\n\tif closer, ok := w.Writer.(io.Closer); ok {\n\t\treturn closer.Close()\n\t}\n\treturn nil\n}\n\nvar triggerWriterPool = &sync.Pool{\n\tNew: func() interface{} {\n\t\treturn bytes.NewBuffer(make([]byte, 0, 1024))\n\t},\n}\n\n// TriggerLevelWriter buffers log lines at the ConditionalLevel or below\n// until a trigger level (or higher) line is emitted. Log lines with level\n// higher than ConditionalLevel are always written out to the destination\n// writer. If trigger never happens, buffered log lines are never written out.\n//\n// It can be used to configure \"log level per request\".\ntype TriggerLevelWriter struct {\n\t// Destination writer. If LevelWriter is provided (usually), its WriteLevel is used\n\t// instead of Write.\n\tio.Writer\n\n\t// ConditionalLevel is the level (and below) at which lines are buffered until\n\t// a trigger level (or higher) line is emitted. Usually this is set to DebugLevel.\n\tConditionalLevel Level\n\n\t// TriggerLevel is the lowest level that triggers the sending of the conditional\n\t// level lines. Usually this is set to ErrorLevel.\n\tTriggerLevel Level\n\n\tbuf       *bytes.Buffer\n\ttriggered bool\n\tmu        sync.Mutex\n}\n\nfunc (w *TriggerLevelWriter) WriteLevel(l Level, p []byte) (n int, err error) {\n\tw.mu.Lock()\n\tdefer w.mu.Unlock()\n\n\t// At first trigger level or above log line, we flush the buffer and change the\n\t// trigger state to triggered.\n\tif !w.triggered && l >= w.TriggerLevel {\n\t\terr := w.trigger()\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t}\n\n\t// Unless triggered, we buffer everything at and below ConditionalLevel.\n\tif !w.triggered && l <= w.ConditionalLevel {\n\t\tif w.buf == nil {\n\t\t\tw.buf = triggerWriterPool.Get().(*bytes.Buffer)\n\t\t}\n\n\t\t// We prefix each log line with a byte with the level.\n\t\t// Hopefully we will never have a level value which equals a newline\n\t\t// (which could interfere with reconstruction of log lines in the trigger method).\n\t\tw.buf.WriteByte(byte(l))\n\t\tw.buf.Write(p)\n\t\treturn len(p), nil\n\t}\n\n\t// Anything above ConditionalLevel is always passed through.\n\t// Once triggered, everything is passed through.\n\tif lw, ok := w.Writer.(LevelWriter); ok {\n\t\treturn lw.WriteLevel(l, p)\n\t}\n\treturn w.Write(p)\n}\n\n// trigger expects lock to be held.\nfunc (w *TriggerLevelWriter) trigger() error {\n\tif w.triggered {\n\t\treturn nil\n\t}\n\tw.triggered = true\n\n\tif w.buf == nil {\n\t\treturn nil\n\t}\n\n\tp := w.buf.Bytes()\n\tfor len(p) > 0 {\n\t\t// We do not use bufio.Scanner here because we already have full buffer\n\t\t// in the memory and we do not want extra copying from the buffer to\n\t\t// scanner's token slice, nor we want to hit scanner's token size limit,\n\t\t// and we also want to preserve newlines.\n\t\ti := bytes.IndexByte(p, '\\n')\n\t\tline := p[0 : i+1]\n\t\tp = p[i+1:]\n\t\t// We prefixed each log line with a byte with the level.\n\t\tlevel := Level(line[0])\n\t\tline = line[1:]\n\t\tvar err error\n\t\tif lw, ok := w.Writer.(LevelWriter); ok {\n\t\t\t_, err = lw.WriteLevel(level, line)\n\t\t} else {\n\t\t\t_, err = w.Write(line)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Trigger forces flushing the buffer and change the trigger state to\n// triggered, if the writer has not already been triggered before.\nfunc (w *TriggerLevelWriter) Trigger() error {\n\tw.mu.Lock()\n\tdefer w.mu.Unlock()\n\n\treturn w.trigger()\n}\n\n// Close closes the writer and returns the buffer to the pool.\nfunc (w *TriggerLevelWriter) Close() error {\n\tw.mu.Lock()\n\tdefer w.mu.Unlock()\n\n\tif w.buf == nil {\n\t\treturn nil\n\t}\n\n\t// We return the buffer only if it has not grown above the limit.\n\t// This prevents accumulation of large buffers in the pool just\n\t// because occasionally a large buffer might be needed.\n\tif w.buf.Cap() <= TriggerLevelWriterBufferReuseLimit {\n\t\tw.buf.Reset()\n\t\ttriggerWriterPool.Put(w.buf)\n\t}\n\tw.buf = nil\n\n\treturn nil\n}\n"
  },
  {
    "path": "writer_test.go",
    "content": "//go:build !binary_log && !windows\n// +build !binary_log,!windows\n\npackage zerolog\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"reflect\"\n\t\"testing\"\n)\n\ntype closableBuffer struct {\n\t*bytes.Buffer\n\tclosed     bool\n\tcloseError error\n}\n\nfunc (cb *closableBuffer) Close() error {\n\tcb.closed = true\n\treturn cb.closeError\n}\n\ntype errorWriter struct {\n\twriteError error\n\tshortWrite bool\n}\n\nfunc (ew *errorWriter) Write(p []byte) (int, error) {\n\tif ew.writeError != nil {\n\t\treturn 0, ew.writeError\n\t}\n\tif ew.shortWrite {\n\t\treturn len(p) - 1, nil // Return short write\n\t}\n\treturn len(p), nil\n}\n\nfunc (ew *errorWriter) WriteLevel(level Level, p []byte) (int, error) {\n\treturn ew.Write(p)\n}\n\nfunc TestMultiSyslogWriter(t *testing.T) {\n\tsw := &syslogTestWriter{}\n\tlog := New(MultiLevelWriter(SyslogLevelWriter(sw)))\n\tlog.Debug().Msg(\"debug\")\n\tlog.Info().Msg(\"info\")\n\tlog.Warn().Msg(\"warn\")\n\tlog.Error().Msg(\"error\")\n\tlog.Log().Msg(\"nolevel\")\n\twant := []syslogEvent{\n\t\t{\"Debug\", `{\"level\":\"debug\",\"message\":\"debug\"}` + \"\\n\"},\n\t\t{\"Info\", `{\"level\":\"info\",\"message\":\"info\"}` + \"\\n\"},\n\t\t{\"Warning\", `{\"level\":\"warn\",\"message\":\"warn\"}` + \"\\n\"},\n\t\t{\"Err\", `{\"level\":\"error\",\"message\":\"error\"}` + \"\\n\"},\n\t\t{\"Info\", `{\"message\":\"nolevel\"}` + \"\\n\"},\n\t}\n\tif got := sw.events; !reflect.DeepEqual(got, want) {\n\t\tt.Errorf(\"Invalid syslog message routing: want %v, got %v\", want, got)\n\t}\n}\n\nvar writeCalls int\n\ntype mockedWriter struct {\n\twantErr bool\n}\n\nfunc (c mockedWriter) Write(p []byte) (int, error) {\n\twriteCalls++\n\n\tif c.wantErr {\n\t\treturn -1, errors.New(\"Expected error\")\n\t}\n\n\treturn len(p), nil\n}\n\n// Tests that a new writer is only used if it actually works.\nfunc TestResilientMultiWriter(t *testing.T) {\n\ttests := []struct {\n\t\tname    string\n\t\twriters []io.Writer\n\t}{\n\t\t{\n\t\t\tname: \"All valid writers\",\n\t\t\twriters: []io.Writer{\n\t\t\t\tmockedWriter{\n\t\t\t\t\twantErr: false,\n\t\t\t\t},\n\t\t\t\tmockedWriter{\n\t\t\t\t\twantErr: false,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"All invalid writers\",\n\t\t\twriters: []io.Writer{\n\t\t\t\tmockedWriter{\n\t\t\t\t\twantErr: true,\n\t\t\t\t},\n\t\t\t\tmockedWriter{\n\t\t\t\t\twantErr: true,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"First invalid writer\",\n\t\t\twriters: []io.Writer{\n\t\t\t\tmockedWriter{\n\t\t\t\t\twantErr: true,\n\t\t\t\t},\n\t\t\t\tmockedWriter{\n\t\t\t\t\twantErr: false,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"First valid writer\",\n\t\t\twriters: []io.Writer{\n\t\t\t\tmockedWriter{\n\t\t\t\t\twantErr: false,\n\t\t\t\t},\n\t\t\t\tmockedWriter{\n\t\t\t\t\twantErr: true,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\twriters := tt.writers\n\t\tmultiWriter := MultiLevelWriter(writers...)\n\n\t\tlogger := New(multiWriter).With().Timestamp().Logger().Level(InfoLevel)\n\t\tlogger.Info().Msg(\"Test msg\")\n\n\t\tif len(writers) != writeCalls {\n\t\t\tt.Errorf(\"Expected %d writers to have been called but only %d were.\", len(writers), writeCalls)\n\t\t}\n\t\twriteCalls = 0\n\t}\n}\n\ntype testingLog struct {\n\ttesting.TB\n\tbuf bytes.Buffer\n}\n\nfunc (t *testingLog) Log(args ...interface{}) {\n\tif _, err := t.buf.WriteString(fmt.Sprint(args...)); err != nil {\n\t\tt.Error(err)\n\t}\n}\n\nfunc (t *testingLog) Logf(format string, args ...interface{}) {\n\tif _, err := t.buf.WriteString(fmt.Sprintf(format, args...)); err != nil {\n\t\tt.Error(err)\n\t}\n}\n\nfunc TestTestWriter(t *testing.T) {\n\ttests := []struct {\n\t\tname  string\n\t\twrite []byte\n\t\twant  []byte\n\t}{{\n\t\tname:  \"newline\",\n\t\twrite: []byte(\"newline\\n\"),\n\t\twant:  []byte(\"newline\"),\n\t}, {\n\t\tname:  \"oneline\",\n\t\twrite: []byte(\"oneline\"),\n\t\twant:  []byte(\"oneline\"),\n\t}, {\n\t\tname:  \"twoline\",\n\t\twrite: []byte(\"twoline\\n\\n\"),\n\t\twant:  []byte(\"twoline\"),\n\t}}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\ttb := &testingLog{TB: t} // Capture TB log buffer.\n\t\t\tw := TestWriter{T: tb}\n\n\t\t\tn, err := w.Write(tt.write)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t\tif n != len(tt.write) {\n\t\t\t\tt.Errorf(\"Expected %d write length but got %d\", len(tt.write), n)\n\t\t\t}\n\t\t\tp := tb.buf.Bytes()\n\t\t\tif !bytes.Equal(tt.want, p) {\n\t\t\t\tt.Errorf(\"Expected %q, got %q.\", tt.want, p)\n\t\t\t}\n\n\t\t\tlog := New(NewConsoleWriter(ConsoleTestWriter(t)))\n\t\t\tlog.Info().Str(\"name\", tt.name).Msg(\"Success!\")\n\n\t\t\ttb.buf.Reset()\n\t\t})\n\t}\n\n}\n\nfunc TestFilteredLevelWriter(t *testing.T) {\n\tbuf := bytes.Buffer{}\n\twriter := FilteredLevelWriter{\n\t\tWriter: LevelWriterAdapter{&buf},\n\t\tLevel:  InfoLevel,\n\t}\n\t_, err := writer.WriteLevel(DebugLevel, []byte(\"no\"))\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\t_, err = writer.WriteLevel(InfoLevel, []byte(\"yes\"))\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tp := buf.Bytes()\n\tif want := \"yes\"; !bytes.Equal([]byte(want), p) {\n\t\tt.Errorf(\"Expected %q, got %q.\", want, p)\n\t}\n}\n\ntype testWrite struct {\n\tLevel\n\tLine []byte\n}\n\nfunc TestTriggerLevelWriter(t *testing.T) {\n\ttests := []struct {\n\t\twrite []testWrite\n\t\twant  []byte\n\t\tall   []byte\n\t}{{\n\t\t[]testWrite{\n\t\t\t{DebugLevel, []byte(\"no\\n\")},\n\t\t\t{InfoLevel, []byte(\"yes\\n\")},\n\t\t},\n\t\t[]byte(\"yes\\n\"),\n\t\t[]byte(\"yes\\nno\\n\"),\n\t}, {\n\t\t[]testWrite{\n\t\t\t{DebugLevel, []byte(\"yes1\\n\")},\n\t\t\t{InfoLevel, []byte(\"yes2\\n\")},\n\t\t\t{ErrorLevel, []byte(\"yes3\\n\")},\n\t\t\t{DebugLevel, []byte(\"yes4\\n\")},\n\t\t},\n\t\t[]byte(\"yes2\\nyes1\\nyes3\\nyes4\\n\"),\n\t\t[]byte(\"yes2\\nyes1\\nyes3\\nyes4\\n\"),\n\t}}\n\n\tfor k, tt := range tests {\n\t\tt.Run(fmt.Sprintf(\"case=%d\", k), func(t *testing.T) {\n\t\t\tbuf := bytes.Buffer{}\n\t\t\twriter := TriggerLevelWriter{Writer: LevelWriterAdapter{&buf}, ConditionalLevel: DebugLevel, TriggerLevel: ErrorLevel}\n\t\t\tt.Cleanup(func() { writer.Close() })\n\t\t\tfor _, w := range tt.write {\n\t\t\t\t_, err := writer.WriteLevel(w.Level, w.Line)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Error(err)\n\t\t\t\t}\n\t\t\t}\n\t\t\tp := buf.Bytes()\n\t\t\tif want := tt.want; !bytes.Equal([]byte(want), p) {\n\t\t\t\tt.Errorf(\"Expected %q, got %q.\", want, p)\n\t\t\t}\n\t\t\terr := writer.Trigger()\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t\tp = buf.Bytes()\n\t\t\tif want := tt.all; !bytes.Equal([]byte(want), p) {\n\t\t\t\tt.Errorf(\"Expected %q, got %q.\", want, p)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestLevelWriterAdapter_Close(t *testing.T) {\n\t// Test with closable writer\n\tbuf := &bytes.Buffer{}\n\tadapter := LevelWriterAdapter{Writer: buf}\n\n\t// bytes.Buffer doesn't implement io.Closer, so Close should return nil\n\terr := adapter.Close()\n\tif err != nil {\n\t\tt.Errorf(\"Close should not return error for non-closable writer: %v\", err)\n\t}\n\n\t// Test with closable writer\n\tclosableBuf := &closableBuffer{Buffer: &bytes.Buffer{}}\n\tadapter2 := LevelWriterAdapter{Writer: closableBuf}\n\n\terr = adapter2.Close()\n\tif err != nil {\n\t\tt.Errorf(\"Close should not return error: %v\", err)\n\t}\n\tif !closableBuf.closed {\n\t\tt.Error(\"Close should have been called on closable writer\")\n\t}\n}\n\nfunc TestSyncWriter(t *testing.T) {\n\tbuf := &bytes.Buffer{}\n\n\t// Test SyncWriter with regular io.Writer\n\tsyncWriter := SyncWriter(buf)\n\n\t// Test Write\n\tdata := []byte(\"test data\")\n\tn, err := syncWriter.Write(data)\n\tif err != nil {\n\t\tt.Errorf(\"Write failed: %v\", err)\n\t}\n\tif n != len(data) {\n\t\tt.Errorf(\"Write returned wrong length: got %d, want %d\", n, len(data))\n\t}\n\tif got := buf.String(); got != string(data) {\n\t\tt.Errorf(\"Write wrote wrong data: got %q, want %q\", got, string(data))\n\t}\n\n\t// Test SyncWriter with LevelWriter - use it with a logger\n\tlevelBuf := &bytes.Buffer{}\n\tlevelWriter := LevelWriterAdapter{levelBuf}\n\tsyncLevelWriter := SyncWriter(levelWriter)\n\n\tlogger := New(syncLevelWriter)\n\tlogger.Info().Msg(\"test message\")\n\n\texpected := `{\"level\":\"info\",\"message\":\"test message\"}` + \"\\n\"\n\tif got := levelBuf.String(); got != expected {\n\t\tt.Errorf(\"SyncWriter with LevelWriter failed: got %q, want %q\", got, expected)\n\t}\n\n\t// Test SyncWriter Close with closable writer\n\tclosableBuf := &closableBuffer{Buffer: &bytes.Buffer{}, closed: false}\n\tclosableSyncWriter := SyncWriter(closableBuf)\n\n\tif closeable, ok := closableSyncWriter.(io.Closer); !ok {\n\t\tt.Error(\"SyncWriter should implement Close method\")\n\t} else {\n\t\terr := closeable.Close()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Close failed: %v\", err)\n\t\t}\n\t}\n\tif !closableBuf.closed {\n\t\tt.Error(\"Close should have been called on closable writer\")\n\t}\n\n\t// Test SyncWriter Close with closable writer that returns error\n\terrorBuf := &closableBuffer{Buffer: &bytes.Buffer{}, closed: false, closeError: io.EOF}\n\terrorSyncWriter := SyncWriter(errorBuf)\n\n\tif closeable, ok := errorSyncWriter.(io.Closer); !ok {\n\t\tt.Error(\"SyncWriter should implement Close method\")\n\t} else {\n\t\terr := closeable.Close()\n\t\tif err != io.EOF {\n\t\t\tt.Errorf(\"Close should have returned EOF error, got: %v\", err)\n\t\t}\n\t}\n\tif !errorBuf.closed {\n\t\tt.Error(\"Close should have been called on closable writer\")\n\t}\n}\n\nfunc TestMultiLevelWriter_Write(t *testing.T) {\n\t// Test successful writes\n\tbuf1 := &bytes.Buffer{}\n\tbuf2 := &bytes.Buffer{}\n\n\tmultiWriter := MultiLevelWriter(buf1, buf2)\n\n\tdata := []byte(\"test data\")\n\tn, err := multiWriter.Write(data)\n\tif err != nil {\n\t\tt.Errorf(\"Write failed: %v\", err)\n\t}\n\tif n != len(data) {\n\t\tt.Errorf(\"Write returned wrong length: got %d, want %d\", n, len(data))\n\t}\n\n\tif got1 := buf1.String(); got1 != string(data) {\n\t\tt.Errorf(\"First writer got wrong data: got %q, want %q\", got1, string(data))\n\t}\n\tif got2 := buf2.String(); got2 != string(data) {\n\t\tt.Errorf(\"Second writer got wrong data: got %q, want %q\", got2, string(data))\n\t}\n\n\t// Test with error writer\n\terrorWriter1 := &errorWriter{writeError: io.EOF}\n\tbuf3 := &bytes.Buffer{}\n\n\terrorMultiWriter := MultiLevelWriter(errorWriter1, buf3)\n\n\t_, err = errorMultiWriter.Write(data)\n\tif err != io.EOF {\n\t\tt.Errorf(\"Write should have returned EOF error, got: %v\", err)\n\t}\n\n\t// Test with short write\n\tshortWriter := &errorWriter{shortWrite: true}\n\tbuf4 := &bytes.Buffer{}\n\n\tshortMultiWriter := MultiLevelWriter(shortWriter, buf4)\n\n\t_, err = shortMultiWriter.Write(data)\n\tif err != io.ErrShortWrite {\n\t\tt.Errorf(\"Write should have returned ErrShortWrite, got: %v\", err)\n\t}\n}\n\nfunc TestMultiLevelWriter_WriteLevel(t *testing.T) {\n\t// Test successful writes\n\tbuf1 := &bytes.Buffer{}\n\tbuf2 := &bytes.Buffer{}\n\n\tmultiWriter := MultiLevelWriter(buf1, buf2)\n\n\tdata := []byte(\"test level data\")\n\tn, err := multiWriter.WriteLevel(InfoLevel, data)\n\tif err != nil {\n\t\tt.Errorf(\"WriteLevel failed: %v\", err)\n\t}\n\tif n != len(data) {\n\t\tt.Errorf(\"WriteLevel returned wrong length: got %d, want %d\", n, len(data))\n\t}\n\n\tif got1 := buf1.String(); got1 != string(data) {\n\t\tt.Errorf(\"First writer got wrong data: got %q, want %q\", got1, string(data))\n\t}\n\tif got2 := buf2.String(); got2 != string(data) {\n\t\tt.Errorf(\"Second writer got wrong data: got %q, want %q\", got2, string(data))\n\t}\n\n\t// Test with error writer\n\terrorWriter1 := &errorWriter{writeError: io.EOF}\n\tbuf3 := &bytes.Buffer{}\n\n\terrorMultiWriter := MultiLevelWriter(errorWriter1, buf3)\n\n\t_, err = errorMultiWriter.WriteLevel(InfoLevel, data)\n\tif err != io.EOF {\n\t\tt.Errorf(\"WriteLevel should have returned EOF error, got: %v\", err)\n\t}\n}\n\nfunc TestMultiLevelWriter_Close(t *testing.T) {\n\tbuf1 := &closableBuffer{Buffer: &bytes.Buffer{}, closed: false}\n\tbuf2 := &bytes.Buffer{} // non-closable\n\n\tmultiWriter := MultiLevelWriter(buf1, buf2)\n\n\t// Cast to concrete type to access Close\n\tmw := multiWriter.(multiLevelWriter)\n\n\terr := mw.Close()\n\tif err != nil {\n\t\tt.Errorf(\"Close failed: %v\", err)\n\t}\n\n\tif !buf1.closed {\n\t\tt.Error(\"First closable writer should have been closed\")\n\t}\n\n\t// Test multiLevelWriter Close with error\n\terrorBuf1 := &closableBuffer{Buffer: &bytes.Buffer{}, closed: false, closeError: io.EOF}\n\terrorBuf2 := &bytes.Buffer{} // non-closable\n\n\terrorMultiWriter := MultiLevelWriter(errorBuf1, errorBuf2)\n\temw := errorMultiWriter.(multiLevelWriter)\n\n\terr = emw.Close()\n\tif err != io.EOF {\n\t\tt.Errorf(\"Close should have returned EOF error, got: %v\", err)\n\t}\n\tif !errorBuf1.closed {\n\t\tt.Error(\"First closable writer should have been closed\")\n\t}\n}\n\nfunc TestNewTestWriter(t *testing.T) {\n\twriter := NewTestWriter(t)\n\n\tif writer.T != t {\n\t\tt.Error(\"NewTestWriter should set the testing interface\")\n\t}\n\tif writer.Frame != 0 {\n\t\tt.Errorf(\"NewTestWriter should set Frame to 0, got %d\", writer.Frame)\n\t}\n}\n\nfunc TestFilteredLevelWriter_Write(t *testing.T) {\n\tbuf := &bytes.Buffer{}\n\tfilteredWriter := FilteredLevelWriter{\n\t\tWriter: LevelWriterAdapter{buf},\n\t\tLevel:  InfoLevel,\n\t}\n\n\tdata := []byte(\"test data\")\n\tn, err := filteredWriter.Write(data)\n\tif err != nil {\n\t\tt.Errorf(\"Write failed: %v\", err)\n\t}\n\tif n != len(data) {\n\t\tt.Errorf(\"Write returned wrong length: got %d, want %d\", n, len(data))\n\t}\n\n\tif got := buf.String(); got != string(data) {\n\t\tt.Errorf(\"Write should always write: got %q, want %q\", got, string(data))\n\t}\n}\n\nfunc TestFilteredLevelWriter_Close(t *testing.T) {\n\tbuf := &closableBuffer{Buffer: &bytes.Buffer{}, closed: false}\n\tfilteredWriter := FilteredLevelWriter{\n\t\tWriter: LevelWriterAdapter{buf},\n\t\tLevel:  InfoLevel,\n\t}\n\n\terr := filteredWriter.Close()\n\tif err != nil {\n\t\tt.Errorf(\"Close failed: %v\", err)\n\t}\n\n\tif !buf.closed {\n\t\tt.Error(\"Underlying closable writer should have been closed\")\n\t}\n\n\t// Test FilteredLevelWriter Close with error\n\terrorBuf := &closableBuffer{Buffer: &bytes.Buffer{}, closed: false, closeError: io.EOF}\n\terrorFilteredWriter := FilteredLevelWriter{\n\t\tWriter: LevelWriterAdapter{errorBuf},\n\t\tLevel:  InfoLevel,\n\t}\n\n\terr = errorFilteredWriter.Close()\n\tif err != io.EOF {\n\t\tt.Errorf(\"Close should have returned EOF error, got: %v\", err)\n\t}\n\tif !errorBuf.closed {\n\t\tt.Error(\"Underlying closable writer should have been closed\")\n\t}\n}\n"
  }
]