Skip to content

New Text Encoder with Flattened Dot-Separated Keys for Improved Readability and Performance #1509

@sagarsuperuser

Description

@sagarsuperuser

Is your feature request related to a problem? Please describe.
Zap’s current encoders (JSON, console) don’t provide an easy-to-read text output for nested objects:

  • JSONEncoder is structured but verbose and less operator-friendly for quick inspection in terminals.
  • ConsoleEncoder is compact but does not handle nested objects in a way that makes them easy to grep or read.
  • Alternatives like Kubernetes’ klog textlogger and Go’s slog.TextHandler provide text output, but are either slower or less flexible.

Describe the solution you'd like
Introduce a new zapcore.Encoder implementation (e.g., TextEncoder) with the following features:

  1. Dot-flattened keys for nested objects
    Example:

    logger.Info("user created", zap.Any("user", User{
      Name: "Alice",
      Addr: Addr{City: "Bangalore", Zip: "560001"},
    }))

    Current JSONEncoder output:

    {"user": {"Name": "Alice", "Addr": {"City": "Bangalore", "Zip": "560001"}}}

    Proposed TextEncoder output:

    "user.Name"="Alice" "user.Addr.City"="Bangalore" "user.Addr.Zip"="560001"
    

    This makes logs more human-readable and grep-friendly.

  2. Performance improvements
    Benchmarks show this encoder is faster than both k8s textlogger and slog.TextHandler, while competitive with zap’s json encoder.

  3. Seamless zap integration

    • Provided as a zapcore.Encoder implementation.
    • Configurable via zapcore.NewTextEncoder(cfg) or zap configuration.

Describe alternatives you've considered

  • Using JSONEncoder → too verbose and not grep-friendly.
  • Using ConsoleEncoder → compact but nested structures are still hard to scan.
  • Using Kubernetes’ klog textlogger / slog.TextHandler → slower and not integrated with zap.

Is this a breaking change?
No. This would be an additive change introducing a new encoder.
Existing users of JSONEncoder and ConsoleEncoder remain unaffected.


Additional context

Log a message and 10 fields

(https://github.com/sagarsuperuser/zap/tree/text-encoder#log-a-message-and-10-fields)

Package Time (ns/op) Time % vs Zap Objects Allocated
zap (json) 656 +0% 5 allocs/op
⚡ zap (text) 852 +30% 5 allocs/op
⚡ zap (sugared json) 854 +30% 10 allocs/op
⚡ zap (sugared text) 1071 +63% 10 allocs/op
zerolog 318 -52% 1 alloc/op
go-kit 1989 +203% 56 allocs/op
slog 2122 +224% 41 allocs/op
slog (LogAttrs) 2161 +229% 40 allocs/op
apex/log 10762 +1539% 64 allocs/op
log15 12049 +1736% 73 allocs/op
logrus 12721 +1840% 84 allocs/op
klog/v2 textlogger 2776 +323% 45 allocs/op

Example log with nested fields:

(https://github.com/sagarsuperuser/zap/tree/text-encoder?tab=readme-ov-file#example-logging-structured-http-requestresponse-using-text-format)

"level"="info" "ts"=1758088355.259905 "caller"="zap_test/main.go:57" "msg"="served" "http.request.method"="GET" "http.request.url"="/api/v1/users" "http.response.status"=200 "http.response.bytes"=1234

Next Steps

Would the maintainers be open to including this encoder in zap core as an additional zapcore.Encoder implementation?
I can submit a draft PR with tests, benchmarks, and documentation once there’s guidance.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions