Zap Logging Library for Go

repository·master·Indexed 12 days ago

https://github.com/uber-go/zap

A fast, structured, leveled logging library for Go designed to minimize allocations and CPU overhead. It provides two interfaces: a high-performance, strongly-typed `Logger` for critical paths and a more flexible, loosely typed `SugaredLogger` for ease of use. Zap includes opinionated presets like `NewProduction()` and `NewDevelopment()`, as well as support for custom encoders and sinks via the `zapcore` package.

Tokens
15.4K
Snippets
62
Records
77
Agent score
97%

What's inside Zap

  1. What is DPanic and when to use it

    master

    DPanic (Development Panic) is a log level designed for errors that are theoretically possible but should never actually occur in a healthy system.

    • In Development: It logs at PanicLevel (triggering a panic).
    • In Production: It logs at ErrorLevel (without crashing).

    Use DPanic instead of a standard panic() to catch logic errors during development without risking application crashes in production environments.

    // Instead of this:
    if err != nil {
      panic(fmt.Sprintf("shouldn't ever get here: %v", err))
    }
    
    // Use DPanic:
    logger.DPanic("shouldn't ever get here", zap.Error(err))
  2. How Logger and SugaredLogger differ

    master

    Zap provides two distinct logging interfaces to allow developers to choose between ease of use and performance:

    • Logger: Optimized for high-performance, low-allocation paths. It requires strongly typed fields (e.g., zap.String("key", "value")). This avoids the overhead of reflection and interface{} allocations.
    • SugaredLogger: A wrapper around Logger that provides a more familiar, loosely typed API. It supports printf-style formatting and key-value pairs (e.g., "key", value). While slightly slower and more allocation-heavy than the base Logger, it is still significantly faster than most other structured logging libraries.
  3. How log sampling works in zap

    master

    Zap uses sampling to prevent application performance degradation during error floods. When many similar log entries (identified by the same message) are logged rapidly, zap begins dropping duplicates to preserve CPU and I/O throughput.

    Note that the production configuration returned by NewProductionConfig() has sampling enabled by default, which may cause repeated logs within a second to be dropped.

  4. Install zap via go get

    master

    To install zap, use the following command. Note that while the source code is hosted on GitHub, the official import path is go.uber.org/zap. You should avoid referencing github.com/uber-go/zap in your code to ensure compatibility and allow for future repository moves.

    go get -u go.uber.org/zap
  5. What is a SugaredLogger and when to use it

    master

    A SugaredLogger is a wrapper around the base Logger that provides a slower but less verbose API. While the standard Logger requires strongly-typed Field objects for structured logging, the SugaredLogger allows for loosely-typed, printf-style, or key-value pair logging.

    Use SugaredLogger when development speed and code brevity are more important than maximum performance. You can convert any Logger to a SugaredLogger using its .Sugar() method. Because converting between them is inexpensive, it is a common pattern to use SugaredLogger at application boundaries and convert back to a Logger (via .Desugar()) in performance-critical code paths.

    // Example of converting between Logger and SugaredLogger
    logger := zap.NewProduction()
    sugared := logger.Sugar()
    
    // Use sugared API
    sugared.Infow("message", "key", "value")
    
    // Convert back to base Logger for performance
    baseLogger := sugared.Desugar()
  6. Implement a custom Sink for Zap

    master

    A Sink is an interface used to define custom output destinations for Zap logs. To implement a custom sink, you must satisfy the Sink interface, which embeds zapcore.WriteSyncer and io.Closer.

    Once you have implemented your sink, you can register it globally using RegisterSink. This allows Zap to instantiate your sink using a specific URI scheme (e.g., myscheme://path/to/resource).

    Requirements for the factory function:

    • It must accept a *url.URL as an argument.
    • It must return a Sink and an error.
    • The scheme must be ASCII and follow RFC 3986 (must start with a letter and contain only letters, digits, '.', '+', or '-').
    // 1. Define your sink implementation
    type MySink struct {
        // ... implementation
    }
    func (s *MySink) Write(p []byte) (n int, err error) { /* ... */ }
    func (s *MySink) Sync() error { /* ... */ }
    func (s *MySink) Close() error { /* ... */ }
    
    // 2. Register the factory
    err := zap.RegisterSink("myscheme", func(u *url.URL) (zap.Sink, error) {
        return &MySink{ /* ... */ }, nil
    })
    if err != nil {
        // handle error
    }
  7. Extend Zap with custom encoders or sinks

    master

    The zap package is a wrapper around the go.uber.org/zap/zapcore interfaces. To extend Zap for custom requirements, you should implement the following interfaces from the zapcore package:

    • zapcore.Encoder: To support new encoding formats (e.g., BSON).
    • zapcore.WriteSyncer: To support new log sinks (e.g., Kafka).
    • zapcore.Core: For more complex custom logic or exception aggregation services (e.g., Sentry).
  8. How Array() works with zapcore.ArrayMarshaler

    master

    The zap.Array(key, val) function is the underlying constructor for all typed array helpers. It creates a Field with the zapcore.ArrayMarshalerType.

    It accepts any type that implements the zapcore.ArrayMarshaler interface. This allows you to create custom, highly efficient array-like logging fields by implementing the MarshalLogArray(arr zapcore.ArrayEncoder) error method on your own types.

    // Example of a custom ArrayMarshaler
    type MyCustomArray []int
    
    func (m MyCustomArray) MarshalLogArray(arr zapcore.ArrayEncoder) error {
    	for _, v := range m {
    		arr.AppendInt(int64(v))
    	}
    	return nil
    }
    
    // Usage
    logger.Info("custom array", zap.Array("my_array", MyCustomArray{1, 2, 3}))
  9. Choosing between Logger and SugaredLogger

    master

    Zap provides two types of loggers depending on your performance and ergonomics requirements:

    1. SugaredLogger: Use this when performance is important but not critical. It is 4-10x faster than other structured logging packages and supports both structured (key-value pairs) and printf-style logging. It is loosely typed.
    2. Logger: Use this in high-performance hot paths where every microsecond and allocation matters. It is even faster than SugaredLogger and allocates far less, but it only supports strongly-typed, structured logging using zap.Fields.

    Converting between the two is simple and inexpensive: use .Sugar() to get a SugaredLogger from a Logger, and .Desugar() to get a Logger from a SugaredLogger.

    // Using SugaredLogger
    sugar := zap.NewExample().Sugar()
    defer sugar.Sync()
    sugar.Infow("failed to fetch URL",
      "url", "http://example.com",
      "attempt", 3,
      "backoff", time.Second,
    )
    sugar.Infof("failed to fetch URL: %s", "http://example.com")
    
    // Using Logger
    logger := zap.NewExample()
    defer logger.Sync()
    logger.Info("failed to fetch URL",
      zap.String("url", "http://example.com"),
      zap.Int("attempt", 3),
      zap.Duration("backoff", time.Second),
    )
    
    // Converting between them
    logger := zap.NewExample()
    sugar := logger.Sugar()
    plain := sugar.Desugar()
  10. Customize Logger behavior with Options

    master
    Zap uses the Option interface to configure Logger instances. Options are typically passed to constructor functions like zap.New(...). Most options are created via helper functions that return an Option implementation.