From 2094837a2c788e93c13add2edee07eadc1061276 Mon Sep 17 00:00:00 2001 From: Marc Brooks Date: Sun, 11 Jan 2026 09:57:13 -0600 Subject: [PATCH] Ensure that Event and Array objects returned to pool are cleared (#747) * Added documentation for DurationFieldFormat Added documentation for the new option added in PR#733 Added code markers some references to logger func/types. Also, did a format-document pass to get rid of trailing blanks, missing/extra blank lines, and other markdown warnings. * Added documentation for Concurrency Safety Added documentation to the Concurrency Safety section about not referencing Event objects after they are written. Closes #731 * Ensure that Event and Array objects returned to pool are cleared Help ensure no corruptible state remains in objects being returned to the pool. Always clear the contextual properties to ensure the recycled (or erroneously referenced) Event or Array are not accidentally used/shared. Sets the truncates the buffer (setting length, not capacity, to zero). Make it really obvious when you hold references as mentioned in #733 Clarified the documentation about holding references to CreateDict() events. --- README.md | 283 ++++++++++++++++++++++++++++-------------------------- array.go | 9 +- event.go | 13 ++- 3 files changed, 165 insertions(+), 140 deletions(-) diff --git a/README.md b/README.md index 1237baf..6659e2d 100644 --- a/README.md +++ b/README.md @@ -18,17 +18,17 @@ Find out [who uses zerolog](https://github.com/rs/zerolog/wiki/Who-uses-zerolog) ## Features -* [Blazing fast](#benchmarks) -* [Low to zero allocation](#benchmarks) -* [Leveled logging](#leveled-logging) -* [Sampling](#log-sampling) -* [Hooks](#hooks) -* [Contextual fields](#contextual-logging) -* [`context.Context` integration](#contextcontext-integration) -* [Integration with `net/http`](#integration-with-nethttp) -* [JSON and CBOR encoding formats](#binary-encoding) -* [Pretty logging for development](#pretty-logging) -* [Error Logging (with optional Stacktrace)](#error-logging) +- [Blazing fast](#benchmarks) +- [Low to zero allocation](#benchmarks) +- [Leveled logging](#leveled-logging) +- [Sampling](#log-sampling) +- [Hooks](#hooks) +- [Contextual fields](#contextual-logging) +- [`context.Context` integration](#contextcontext-integration) +- [Integration with `net/http`](#integration-with-nethttp) +- [JSON and CBOR encoding formats](#binary-encoding) +- [Pretty logging for development](#pretty-logging) +- [Error Logging (with optional Stacktrace)](#error-logging) ## Installation @@ -59,8 +59,9 @@ func main() { // Output: {"time":1516134303,"level":"debug","message":"hello world"} ``` + > Note: By default log writes to `os.Stderr` -> Note: The default log level for `log.Print` is *trace* +> Note: The default log level for `log.Print` is _trace_ ### Contextual Logging @@ -81,7 +82,7 @@ func main() { Str("Scale", "833 cents"). Float64("Interval", 833.09). Msg("Fibonacci is everywhere") - + log.Debug(). Str("Name", "Tom"). Send() @@ -118,15 +119,15 @@ func main() { **zerolog** allows for logging at the following levels (from highest to lowest): -* panic (`zerolog.PanicLevel`, 5) -* fatal (`zerolog.FatalLevel`, 4) -* error (`zerolog.ErrorLevel`, 3) -* warn (`zerolog.WarnLevel`, 2) -* info (`zerolog.InfoLevel`, 1) -* debug (`zerolog.DebugLevel`, 0) -* trace (`zerolog.TraceLevel`, -1) +- panic (`zerolog.PanicLevel`, 5) +- fatal (`zerolog.FatalLevel`, 4) +- error (`zerolog.ErrorLevel`, 3) +- warn (`zerolog.WarnLevel`, 2) +- info (`zerolog.InfoLevel`, 1) +- debug (`zerolog.DebugLevel`, 0) +- trace (`zerolog.TraceLevel`, -1) -You 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. +You 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. #### Setting Global Log Level @@ -212,17 +213,17 @@ You can log errors using the `Err` method package main import ( - "errors" + "errors" - "github.com/rs/zerolog" - "github.com/rs/zerolog/log" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" ) func main() { - zerolog.TimeFieldFormat = zerolog.TimeFormatUnix + zerolog.TimeFieldFormat = zerolog.TimeFormatUnix - err := errors.New("seems we have an error here") - log.Error().Err(err).Msg("") + err := errors.New("seems we have an error here") + log.Error().Err(err).Msg("") } // Output: {"level":"error","error":"seems we have an error here","time":1609085256} @@ -232,45 +233,45 @@ func main() { #### Error Logging with Stacktrace -Using `github.com/pkg/errors`, you can add a formatted stacktrace to your errors. +Using `github.com/pkg/errors`, you can add a formatted stacktrace to your errors. ```go package main import ( - "github.com/pkg/errors" - "github.com/rs/zerolog/pkgerrors" + "github.com/pkg/errors" + "github.com/rs/zerolog/pkgerrors" - "github.com/rs/zerolog" - "github.com/rs/zerolog/log" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" ) func main() { - zerolog.TimeFieldFormat = zerolog.TimeFormatUnix - zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack + zerolog.TimeFieldFormat = zerolog.TimeFormatUnix + zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack - err := outer() - log.Error().Stack().Err(err).Msg("") + err := outer() + log.Error().Stack().Err(err).Msg("") } func inner() error { - return errors.New("seems we have an error here") + return errors.New("seems we have an error here") } func middle() error { - err := inner() - if err != nil { - return err - } - return nil + err := inner() + if err != nil { + return err + } + return nil } func outer() error { - err := middle() - if err != nil { - return err - } - return nil + err := middle() + if err != nil { + return err + } + return nil } // 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} @@ -308,7 +309,6 @@ func main() { > NOTE: Using `Msgf` generates one allocation even when the logger is disabled. - ### Create logger instance to manage different outputs ```go @@ -393,7 +393,7 @@ log.Info().Str("foo", "bar"). Str("one", "test_one"). Str("three", "test_three"). Msg("Hello World") - + // Output: INFO TEST_ONE test_two (test_three) Hello World foo:bar ``` @@ -457,8 +457,8 @@ If your writer might be slow or not thread-safe and you need your log producers ```go wr := diode.NewWriter(os.Stdout, 1000, 10*time.Millisecond, func(missed int) { - fmt.Printf("Logger Dropped %d messages", missed) - }) + fmt.Printf("Logger Dropped %d messages", missed) + }) log := zerolog.New(wr) log.Print("test") ``` @@ -537,7 +537,7 @@ stdlog.Print("hello world") ### context.Context integration Go contexts are commonly passed throughout Go code, and this can help you pass -your Logger into places it might otherwise be hard to inject. The `Logger` +your Logger into places it might otherwise be hard to inject. The `Logger` instance may be attached to Go context (`context.Context`) using `Logger.WithContext(ctx)` and extracted from it using `zerolog.Ctx(ctx)`. For example: @@ -562,7 +562,7 @@ func someFunc(ctx context.Context) { ``` A second form of `context.Context` integration allows you to pass the current -context.Context into the logged event, and retrieve it from hooks. This can be +`context.Context` into the logged event, and retrieve it from hooks. This can be useful to log trace and span IDs or other information stored in the go context, and facilitates the unification of logging and tracing in some systems: @@ -640,17 +640,17 @@ if err := http.ListenAndServe(":8080", nil); err != nil { ``` ## Multiple Log Output -`zerolog.MultiLevelWriter` may be used to send the log message to multiple outputs. -In this example, we send the log message to both `os.Stdout` and the in-built ConsoleWriter. + +`zerolog.MultiLevelWriter` may be used to send the log message to multiple outputs. + +In this example, we send the log message to both `os.Stdout` and the in-built `ConsoleWriter`. + ```go func main() { - consoleWriter := zerolog.ConsoleWriter{Out: os.Stdout} - - multi := zerolog.MultiLevelWriter(consoleWriter, os.Stdout) - - logger := zerolog.New(multi).With().Timestamp().Logger() - - logger.Info().Msg("Hello World!") + consoleWriter := zerolog.ConsoleWriter{Out: os.Stdout} + multi := zerolog.MultiLevelWriter(consoleWriter, os.Stdout) + logger := zerolog.New(multi).With().Timestamp().Logger() + logger.Info().Msg("Hello World!") } // Output (Line 1: Console; Line 2: Stdout) @@ -662,47 +662,46 @@ func main() { Some settings can be changed and will be applied to all loggers: -* `log.Logger`: You can set this value to customize the global logger (the one used by package level methods). -* `zerolog.SetGlobalLevel`: Can raise the minimum level of all loggers. Call this with `zerolog.Disabled` to disable logging altogether (quiet mode). -* `zerolog.DisableSampling`: If argument is `true`, all sampled loggers will stop sampling and issue 100% of their log events. -* `zerolog.TimestampFieldName`: Can be set to customize `Timestamp` field name. -* `zerolog.LevelFieldName`: Can be set to customize level field name. -* `zerolog.MessageFieldName`: Can be set to customize message field name. -* `zerolog.ErrorFieldName`: Can be set to customize `Err` field name. -* `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. -* `zerolog.DurationFieldUnit`: Can be set to customize the unit for time.Duration type fields added by `Dur` (default: `time.Millisecond`). -* `zerolog.DurationFieldInteger`: If set to `true`, `Dur` fields are formatted as integers instead of floats (default: `false`). -* `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. -* `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) -for more details. +- `log.Logger`: You can set this value to customize the global logger (the one used by package level methods). +- `zerolog.SetGlobalLevel`: Can raise the minimum level of all loggers. Call this with `zerolog.Disabled` to disable logging altogether (quiet mode). +- `zerolog.DisableSampling`: If argument is `true`, all sampled loggers will stop sampling and issue 100% of their log events. +- `zerolog.TimestampFieldName`: Can be set to customize `Timestamp` field name. +- `zerolog.LevelFieldName`: Can be set to customize level field name. +- `zerolog.MessageFieldName`: Can be set to customize message field name. +- `zerolog.ErrorFieldName`: Can be set to customize `Err` field name. +- `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. +- `zerolog.DurationFieldUnit`: Can be set to customize the unit for time.Duration type fields added by `Dur` (default: `time.Millisecond`). +- `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). +- `zerolog.DurationFieldInteger`: If set to `true`, `Dur` fields are formatted as integers instead of floats (default: `false`). Deprecated: Use `zerolog.DurationFieldFormat = DurationFormatInt` instead. +- `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. +- `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) + for more details. ## Field Types ### Standard Types -* `Str` -* `Bool` -* `Int`, `Int8`, `Int16`, `Int32`, `Int64` -* `Uint`, `Uint8`, `Uint16`, `Uint32`, `Uint64` -* `Float32`, `Float64` +- `Str` +- `Bool` +- `Int`, `Int8`, `Int16`, `Int32`, `Int64` +- `Uint`, `Uint8`, `Uint16`, `Uint32`, `Uint64` +- `Float32`, `Float64` ### Advanced Fields -* `Err`: Takes an `error` and renders it as a string using the `zerolog.ErrorFieldName` field name. -* `Func`: Run a `func` only if the level is enabled. -* `Timestamp`: Inserts a timestamp field with `zerolog.TimestampFieldName` field name, formatted using `zerolog.TimeFieldFormat`. -* `Time`: Adds a field with time formatted with `zerolog.TimeFieldFormat`. -* `Dur`: Adds a field with `time.Duration`. -* `Dict`: Adds a sub-key/value as a field of the event. -* `RawJSON`: Adds a field with an already encoded JSON (`[]byte`) -* `Hex`: Adds a field with value formatted as a hexadecimal string (`[]byte`) -* `Interface`: Uses reflection to marshal the type. -* `IPAddr`: Adds a field with `net.IP`. -* `IPPrefix`: Adds a field with `net.IPNet`. -* `MACAddr`: Adds a field withg `net.HardwareAddr` - +- `Err`: Takes an `error` and renders it as a string using the `zerolog.ErrorFieldName` field name. +- `Func`: Run a `func` only if the level is enabled. +- `Timestamp`: Inserts a timestamp field with `zerolog.TimestampFieldName` field name, formatted using `zerolog.TimeFieldFormat`. +- `Time`: Adds a field with time formatted with `zerolog.TimeFieldFormat`. +- `Dur`: Adds a field with `time.Duration`. +- `Dict`: Adds a sub-key/value as a field of the event. +- `RawJSON`: Adds a field with an already encoded JSON (`[]byte`) +- `Hex`: Adds a field with value formatted as a hexadecimal string (`[]byte`) +- `Interface`: Uses reflection to marshal the type. +- `IPAddr`: Adds a field with `net.IP`. +- `IPPrefix`: Adds a field with `net.IPNet`. +- `MACAddr`: Adds a field with `net.HardwareAddr` + Most fields are also available in the slice format (`Strs` for `[]string`, `Errs` for `[]error` etc.) ## Binary Encoding @@ -713,21 +712,21 @@ In addition to the default JSON encoding, `zerolog` can produce binary logs usin go build -tags binary_log . ``` -To Decode binary encoded log files you can use any CBOR decoder. One has been tested to work +To decode binary encoded log files you can use any CBOR decoder. One has been tested to work with zerolog library is [CSD](https://github.com/toravir/csd/). ## Related Projects -* [grpc-zerolog](https://github.com/cheapRoc/grpc-zerolog): Implementation of `grpclog.LoggerV2` interface using `zerolog` -* [overlog](https://github.com/Trendyol/overlog): Implementation of `Mapped Diagnostic Context` interface using `zerolog` -* [zerologr](https://github.com/go-logr/zerologr): Implementation of `logr.LogSink` interface using `zerolog` -* [logze](https://github.com/maxbolgarin/logze): Implementation of `log/slog` interface using `zerolog` +- [grpc-zerolog](https://github.com/cheapRoc/grpc-zerolog): Implementation of `grpclog.LoggerV2` interface using `zerolog` +- [overlog](https://github.com/Trendyol/overlog): Implementation of `Mapped Diagnostic Context` interface using `zerolog` +- [zerologr](https://github.com/go-logr/zerologr): Implementation of `logr.LogSink` interface using `zerolog` +- [logze](https://github.com/maxbolgarin/logze): Implementation of `log/slog` interface using `zerolog` ## Benchmarks See [logbench](http://bench.zerolog.io/) for more comprehensive and up-to-date benchmarks. -All operations are allocation free (those numbers *include* JSON encoding): +All operations are allocation free (those numbers _include_ JSON encoding): ```text BenchmarkLogEmpty-8 100000000 19.1 ns/op 0 B/op 0 allocs/op @@ -739,50 +738,50 @@ BenchmarkLogFields-8 10000000 184 ns/op 0 B/op 0 allocs/op There are a few Go logging benchmarks and comparisons that include zerolog. -* [imkira/go-loggers-bench](https://github.com/imkira/go-loggers-bench) -* [uber-common/zap](https://github.com/uber-go/zap#performance) +- [imkira/go-loggers-bench](https://github.com/imkira/go-loggers-bench) +- [uber-common/zap](https://github.com/uber-go/zap#performance) Using Uber's zap comparison benchmark: Log a message and 10 fields: -| Library | Time | Bytes Allocated | Objects Allocated | -| :--- | :---: | :---: | :---: | -| zerolog | 767 ns/op | 552 B/op | 6 allocs/op | -| :zap: zap | 848 ns/op | 704 B/op | 2 allocs/op | -| :zap: zap (sugared) | 1363 ns/op | 1610 B/op | 20 allocs/op | -| go-kit | 3614 ns/op | 2895 B/op | 66 allocs/op | -| lion | 5392 ns/op | 5807 B/op | 63 allocs/op | -| logrus | 5661 ns/op | 6092 B/op | 78 allocs/op | -| apex/log | 15332 ns/op | 3832 B/op | 65 allocs/op | -| log15 | 20657 ns/op | 5632 B/op | 93 allocs/op | +| Library | Time | Bytes Allocated | Objects Allocated | +| :------------------ | :---------: | :-------------: | :---------------: | +| zerolog | 767 ns/op | 552 B/op | 6 allocs/op | +| :zap: zap | 848 ns/op | 704 B/op | 2 allocs/op | +| :zap: zap (sugared) | 1363 ns/op | 1610 B/op | 20 allocs/op | +| go-kit | 3614 ns/op | 2895 B/op | 66 allocs/op | +| lion | 5392 ns/op | 5807 B/op | 63 allocs/op | +| logrus | 5661 ns/op | 6092 B/op | 78 allocs/op | +| apex/log | 15332 ns/op | 3832 B/op | 65 allocs/op | +| log15 | 20657 ns/op | 5632 B/op | 93 allocs/op | Log a message with a logger that already has 10 fields of context: -| Library | Time | Bytes Allocated | Objects Allocated | -| :--- | :---: | :---: | :---: | -| zerolog | 52 ns/op | 0 B/op | 0 allocs/op | -| :zap: zap | 283 ns/op | 0 B/op | 0 allocs/op | -| :zap: zap (sugared) | 337 ns/op | 80 B/op | 2 allocs/op | -| lion | 2702 ns/op | 4074 B/op | 38 allocs/op | -| go-kit | 3378 ns/op | 3046 B/op | 52 allocs/op | -| logrus | 4309 ns/op | 4564 B/op | 63 allocs/op | -| apex/log | 13456 ns/op | 2898 B/op | 51 allocs/op | -| log15 | 14179 ns/op | 2642 B/op | 44 allocs/op | +| Library | Time | Bytes Allocated | Objects Allocated | +| :------------------ | :---------: | :-------------: | :---------------: | +| zerolog | 52 ns/op | 0 B/op | 0 allocs/op | +| :zap: zap | 283 ns/op | 0 B/op | 0 allocs/op | +| :zap: zap (sugared) | 337 ns/op | 80 B/op | 2 allocs/op | +| lion | 2702 ns/op | 4074 B/op | 38 allocs/op | +| go-kit | 3378 ns/op | 3046 B/op | 52 allocs/op | +| logrus | 4309 ns/op | 4564 B/op | 63 allocs/op | +| apex/log | 13456 ns/op | 2898 B/op | 51 allocs/op | +| log15 | 14179 ns/op | 2642 B/op | 44 allocs/op | Log a static string, without any context or `printf`-style templating: -| Library | Time | Bytes Allocated | Objects Allocated | -| :--- | :---: | :---: | :---: | -| zerolog | 50 ns/op | 0 B/op | 0 allocs/op | -| :zap: zap | 236 ns/op | 0 B/op | 0 allocs/op | -| standard library | 453 ns/op | 80 B/op | 2 allocs/op | -| :zap: zap (sugared) | 337 ns/op | 80 B/op | 2 allocs/op | -| go-kit | 508 ns/op | 656 B/op | 13 allocs/op | -| lion | 771 ns/op | 1224 B/op | 10 allocs/op | -| logrus | 1244 ns/op | 1505 B/op | 27 allocs/op | -| apex/log | 2751 ns/op | 584 B/op | 11 allocs/op | -| log15 | 5181 ns/op | 1592 B/op | 26 allocs/op | +| Library | Time | Bytes Allocated | Objects Allocated | +| :------------------ | :--------: | :-------------: | :---------------: | +| zerolog | 50 ns/op | 0 B/op | 0 allocs/op | +| :zap: zap | 236 ns/op | 0 B/op | 0 allocs/op | +| standard library | 453 ns/op | 80 B/op | 2 allocs/op | +| :zap: zap (sugared) | 337 ns/op | 80 B/op | 2 allocs/op | +| go-kit | 508 ns/op | 656 B/op | 13 allocs/op | +| lion | 771 ns/op | 1224 B/op | 10 allocs/op | +| logrus | 1244 ns/op | 1505 B/op | 27 allocs/op | +| apex/log | 2751 ns/op | 584 B/op | 11 allocs/op | +| log15 | 5181 ns/op | 1592 B/op | 26 allocs/op | ## Caveats @@ -802,7 +801,7 @@ In this case, many consumers will take the last value, but this is not guarantee ### Concurrency safety -Be careful when calling UpdateContext. It is not concurrency safe. Use the With method to create a child logger: +Be careful when calling `UpdateContext`. It is not concurrency safe. Use the `With()` method to create a child logger: ```go func handler(w http.ResponseWriter, r *http.Request) { @@ -815,3 +814,17 @@ func handler(w http.ResponseWriter, r *http.Request) { }) } ``` + +The `Event` object returned from the `Logger` level-specific message functions (e.g. `Log()`, `Trace()`, `Debug()`, etc.) +is allocated in `sync.Pool` memory that will be returned to the pool as soon as the `Msg()`, `Msgf()`, `Send()`, +or `MsgFunc()` writes the message and **must not** be accessed afterwards. + +**Do not** hold a reference to the `*Event` while in callback functions or your own code. This is especially important in +`Hook.Run()` and `HookFunc` functions or `MarshalZerologObject(e *Event)` callback (e.g. `LogObjectMarshaler` implementations). + +Any `Array` objects returned from `Context.CreateArray()` or `Event.CreateArray()` are from a `sync.Pool` so **do not** hold +references to them from within any `MarshalZerologArray(a *Array)` callback (e.g. `LogArrayMarshaler` implementations) or your +own code as they will be cleared and returned to the pool after being buffered by a call to `Context.Array()` or `Event.Array()`. + +Any _dictionary_ `Event` returned from `Context.CreateDict()` or `Event.CreateDict()` **must not** be referenced after being +buffered by a call to `Context.Dict()` or `Event.Dict()` as they will be cleared and returned to the pool. diff --git a/array.go b/array.go index ecbbb02..2220c06 100644 --- a/array.go +++ b/array.go @@ -25,6 +25,12 @@ type Array struct { } func putArray(a *Array) { + // prevent any subsequent use of the Array contextual state and truncate the buffer + a.stack = false + a.ctx = nil + a.ch = nil + a.buf = a.buf[:0] + // Proper usage of a sync.Pool requires each entry to have approximately // the same memory cost. To obtain this property when the stored type // contains a variably-sized buffer, we add a hard limit on the maximum buffer @@ -35,9 +41,6 @@ func putArray(a *Array) { if cap(a.buf) > maxSize { return } - a.stack = false - a.ctx = nil - a.ch = nil arrayPool.Put(a) } diff --git a/event.go b/event.go index 0cbd1b3..9e6a46c 100644 --- a/event.go +++ b/event.go @@ -32,6 +32,15 @@ type Event struct { } func putEvent(e *Event) { + // prevent any subsequent use of the Event contextual state and truncate the buffer + e.w = nil + e.done = nil + e.stack = false + e.ch = nil + e.skipFrame = 0 + e.ctx = nil + e.buf = e.buf[:0] + // Proper usage of a sync.Pool requires each entry to have approximately // the same memory cost. To obtain this property when the stored type // contains a variably-sized buffer, we add a hard limit on the maximum buffer @@ -463,8 +472,8 @@ func (e *Event) Ctx(ctx context.Context) *Event { } // GetCtx retrieves the Go context.Context which is optionally stored in the -// Event. This allows Hooks and functions passed to Func() to retrieve values -// which are stored in the context.Context. This can be useful in tracing, +// Event. This allows Hooks and functions passed to Func() to retrieve values +// which are stored in the context.Context. This can be useful in tracing, // where span information is commonly propagated in the context.Context. func (e *Event) GetCtx() context.Context { if e == nil || e.ctx == nil {