zerolog

repository·master·Indexed 11 days ago

https://github.com/rs/zerolog

A high-performance, zero-allocation JSON logger for Go. It features a chaining API for structured logging with minimal memory overhead, supporting both JSON and CBOR encoding. Key capabilities include leveled logging, contextualization via context.Context, net/http integration through hlog, and a ConsoleWriter for human-readable output.

Tokens
16.6K
Snippets
65
Records
81
Agent score
94%

What's inside zerolog

  1. Overview of zerolog features

    master

    zerolog is a fast, simple, zero-allocation JSON logger for Go. It focuses on efficient structured logging using a unique chaining API that avoids reflection and allocations.

    Key features include:

    • Performance: Blazing fast with low to zero allocation.
    • Structured Logging: Native JSON and CBOR encoding formats.
    • Logging Control: Leveled logging, sampling, and hooks.
    • Contextualization: Support for contextual fields and context.Context integration.
    • Integrations: net/http integration and log/slog compatibility.
    • Developer Experience: Error logging with optional stacktraces and a zerolog.ConsoleWriter for pretty-printed console output during development.
  2. Use Zerolog Lint to find missing log finishers

    master

    Zerolog Lint is a tool designed to detect missing log event finishers in zerolog code. In zerolog, a log chain must end with a finisher like Msg() or Msgf() to actually write the event. If these are omitted, the log will not occur, and no compile-time error is raised. This linter inspects method call chains on zerolog.Event to ensure a finisher is present.

    Note: This tool is DEPRECATED. It is recommended to use zerologlint instead, which integrates directly with go vet and golangci-lint.

    go run cmd/lint/lint.go <package>
  3. Memory management: Do not hold references to pooled objects

    master

    Zerolog uses sync.Pool for high performance. To avoid memory corruption or unexpected behavior, follow these rules:

    1. *Event objects: The Event returned by level-specific functions (e.g., Info(), Debug()) is pooled. Do not hold a reference to it after calling Msg(), Msgf(), Send(), or MsgFunc(). This includes references inside Hook.Run() or MarshalZerologObject callbacks.
    2. Array objects: Objects returned from Context.CreateArray() or Event.CreateArray() are pooled. Do not hold references to them inside MarshalZerologArray callbacks or your own code after the buffering call completes.
    3. Dictionary objects: Objects returned from Context.CreateDict() or Event.CreateDict() must not be referenced after being buffered by Array.Dict(), Context.Dict(), or Event.Dict().
  4. Integrate zerolog with context.Context

    master

    You can pass loggers through your application using Go's context.Context.

    1. Attach a logger to a context: Use logger.WithContext(ctx).
    2. Retrieve a logger from a context: Use zerolog.Ctx(ctx). If no logger is found in the context, it returns zerolog.DefaultContextLogger (or a disabled logger if that is nil).

    You can also use Hooks to extract information (like trace IDs) from the context and add them to the log event automatically.

    func someFunc(ctx context.Context) {
        // Get Logger from the go Context
        logger := zerolog.Ctx(ctx)
        logger.Info().Msg("Hello")
    }
    
    func f() {
        logger := zerolog.New(os.Stdout)
        ctx := context.Background()
    
        // Attach the Logger to the context.Context
        ctx = logger.WithContext(ctx)
        someFunc(ctx)
    }
  5. Compare JSON vs CBOR for logging

    master

    Zerolog supports CBOR (Concise Binary Object Representation) encoding as an alternative to JSON. Switching to CBOR can provide significant performance improvements in two areas:

    1. CPU Usage: CBOR reduces the time required to write log messages. Notable savings include:
      • LogFieldType/Times-32: ~90.71% reduction
      • LogFieldType/Durs-32: ~72.44% reduction
      • LogFieldType/Floats-32: ~57.98% reduction
      • LogFields-32: ~62.89% reduction
    2. Message Size: CBOR produces smaller log files. For a standard log message containing an integer, timestamp, and string, CBOR can reduce the plain file size by approximately 40% compared to JSON (e.g., 550 KB vs 920 KB for 10,000 messages).

    Note that when using compression (like zlib), the difference in compressed file size may become negligible.

    | Log Format |  Plain File Size (in KB) | Compressed File Size (in KB) |
    | :--- | :---: | :---: |
    | JSON | 920 | 28 |
    | CBOR | 550 | 28 |
  6. Avoid field duplication in JSON output

    master

    Zerolog does not perform de-duplication of keys. If you use the same key multiple times in a single event, the resulting JSON will contain multiple identical keys. While many JSON parsers will take the last value, this behavior is not guaranteed across all consumers.

    logger := zerolog.New(os.Stderr).With().Timestamp().Logger()
    logger.Info().
           Timestamp().
           Msg("dup")
    // Output: {"level":"info","time":1494567715,"time":1494567715,"message":"dup"}
  7. Ensure concurrency safety when updating context

    master

    The UpdateContext method is not concurrency safe. To safely add context in concurrent environments (like HTTP handlers), use the With() method to create a child logger instead of modifying a shared logger instance.

    func handler(w http.ResponseWriter, r *http.Request) {
        // Create a child logger for concurrency safety
        logger := log.Logger.With().Logger()
    
        // Add context fields, for example User-Agent from HTTP headers
        logger.UpdateContext(func(c zerolog.Context) zerolog.Context {
            ...
        })
    }
  8. Configure and Use Log Levels

    master

    zerolog provides several log levels. You can control which logs are emitted by setting the global level using zerolog.SetGlobalLevel. Only logs with a level greater than or equal to the set level will be written. To disable all logging, use zerolog.Disabled.

    Available Levels (Highest to Lowest):

    • zerolog.PanicLevel (5)
    • zerolog.FatalLevel (4)
    • zerolog.ErrorLevel (3)
    • zerolog.WarnLevel (2)
    • zerolog.InfoLevel (1)
    • zerolog.DebugLevel (0)
    • zerolog.TraceLevel (-1)

    Important: When using the chaining API (e.g., log.Info().Msg(...)), you must call either .Msg() or .Msgf(). If you omit these, the log will not be emitted and no compile-time error will occur.

    package main
    
    import (
        "flag"
        "github.com/rs/zerolog"
        "github.com/rs/zerolog/log"
    )
    
    func main() {
        zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
        debug := flag.Bool("debug", false, "sets log level to debug")
    
        flag.Parse()
    
        // Default level for this example is info, unless debug flag is present
        zerolog.SetGlobalLevel(zerolog.InfoLevel)
        if *debug {
            zerolog.SetGlobalLevel(zerolog.DebugLevel)
        }
    
        log.Debug().Msg("This message appears only when log level set to Debug")
        log.Info().Msg("This message appears when log level set to Debug or Info")
    
        if e := log.Debug(); e.Enabled() {
            // Compute log output only if enabled to save resources
            value := "bar"
            e.Str("foo", value).Msg("some debug message")
        }
    }
  9. Integrate zerolog with net/http

    master

    The github.com/rs/zerolog/hlog package provides helpers for integrating zerolog into HTTP middleware.

    • hlog.NewHandler(logger): Creates a handler that provides the logger via the request context.
    • hlog.AccessHandler(...): Logs request details (method, status, size, duration).
    • hlog.RemoteAddrHandler, hlog.UserAgentHandler, etc.: Automatically adds request metadata to the log context.
    // Example using hlog in an HTTP middleware chain
    c = c.Append(hlog.NewHandler(log))
    c = c.Append(hlog.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) {
        hlog.FromRequest(r).Info().
            Str("method", r.Method).
            Int("status", status).
            Msg("")
    }))
    
    // Inside the handler:
    hlog.FromRequest(r).Info().Msg("Something happened")
  10. Use Zerolog PrettyLog to colorize JSON logs

    master

    Zerolog PrettyLog is a CLI utility designed to colorize and pretty-print structured JSON logs produced by zerolog.

    Because zerolog outputs to stderr by default rather than stdout, you must redirect the stderr stream to the prettylog tool to view formatted logs. The method for redirection depends on your operating system.

    ### Linux
    Redirect `stderr` to `prettylog` while leaving `stdout` unaffected:
    
    # Using a compiled version
    some_program_with_zerolog 2> >(prettylog)
    
    # Running directly with `go run`
    some_program_with_zerolog 2> >(go run cmd/prettylog/prettylog.go)
    
    ### Windows
    Redirect `stderr` to `stdout` and pipe it to `prettylog`:
    
    # Using a compiled version
    some_program_with_zerolog 2>&1 | prettylog
    
    # Running directly with `go run`
    some_program_with_zerolog 2>&1 | go run cmd/prettylog/prettylog.go
  11. Use Pretty Console Logging

    master

    For human-readable, colorized output (useful for local development), use zerolog.ConsoleWriter. You can customize the output by configuring fields like FormatLevel, FormatMessage, FormatFieldName, and FormatFieldValue.

    // Basic pretty logging
    log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
    log.Info().Str("foo", "bar").Msg("Hello world")
    
    // Advanced customization
    output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}
    output.FormatLevel = func(i interface{}) string {
        return strings.ToUpper(fmt.Sprintf("| %-6s|", i))
    }
    // ... other formatters
    log := zerolog.New(output).With().Timestamp().Logger()