zaptext

package module
v0.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 8, 2025 License: MIT Imports: 15 Imported by: 1

README

Zaptext

Zap text encoder for human-readable logging

Build status Go Reference GoReport

Zaptext provides a text encoder for Zap that outputs human-readable logs instead of JSON. Perfect for development, debugging, or when you need logs that are easy to read by humans.

Features

  • Human-readable text output instead of JSON
  • Proper key=value formatting for fields
  • Automatic quoting of strings containing spaces
  • Array and object formatting: array=[1,2,3]
  • Support for all Zap data types: strings, numbers, booleans, durations, timestamps, complex numbers
  • Configurable time and duration formatting
  • Thread-safe and performant
  • Reflection-based encoder for arbitrary Go data structures
    • Object pooling for memory efficiency
    • HTML character escaping for web-safe output
    • JSON-compatible complex number formatting
    • Configurable maximum recursion depth (default: 32)
    • Comprehensive error handling and infinite recursion protection

Quick Start

package main

import (
    "os"
    "github.com/kaiiak/zaptext"
    "go.uber.org/zap"
    "go.uber.org/zap/zapcore"
)

func main() {
    // Configure the encoder
    cfg := zap.NewProductionEncoderConfig()
    cfg.EncodeTime = zapcore.ISO8601TimeEncoder
    cfg.EncodeDuration = zapcore.StringDurationEncoder
    
    // Create text encoder
    encoder := zaptext.NewTextEncoder(cfg)
    
    // Create logger
    core := zapcore.NewCore(encoder, zapcore.AddSync(os.Stdout), zap.InfoLevel)
    logger := zap.New(core)
    
    logger.Info("User logged in",
        zap.String("user_id", "12345"),
        zap.Bool("admin", true),
        zap.Duration("session_duration", time.Minute*30),
    )
    // Output: time=2023-09-02T10:30:15Z INFO User logged in user_id=12345 admin=true session_duration=30m
}

ReflectEncoder Usage

The ReflectEncoder provides reflection-based encoding of arbitrary Go data structures into JSON-like format:

package main

import (
    "bytes"
    "fmt"
    "github.com/kaiiak/zaptext"
)

type User struct {
    ID       int      `json:"id"`
    Name     string   `json:"name"`
    Email    string   `json:"email"`
    Tags     []string `json:"tags"`
    IsActive bool     `json:"active"`
}

func main() {
    // Create a user object
    user := User{
        ID:       123,
        Name:     "John Doe",
        Email:    "[email protected]",
        Tags:     []string{"developer", "admin"},
        IsActive: true,
    }
    
    // Create ReflectEncoder
    var buf bytes.Buffer
    encoder := zaptext.NewReflectEncoder(&buf)
    defer encoder.Release() // Always release back to pool
    
    // Configure options
    encoder.SetEscapeHTML(true)      // Enable HTML escaping
    encoder.SetMaxDepth(50)          // Set maximum recursion depth
    
    // Encode the object
    err := encoder.Encode(user)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    
    fmt.Println(buf.String())
    // Output: {"id":123,"name":"John Doe","email":"[email protected]","tags":["developer","admin"],"active":true}
}
ReflectEncoder Features
  • Object Pooling: Uses sync.Pool for efficient memory allocation
  • HTML Escaping: Optional HTML character escaping for web-safe output
  • Complex Numbers: JSON-compatible format {"real": 1.0, "imag": 2.0}
  • Maximum Depth Protection: Configurable recursion depth limit (default: 32)
  • Comprehensive Type Support: All Go primitive types, arrays, slices, maps, structs
  • Special Handling: time.Time formatted as RFC3339, nil pointers skipped
  • Error Handling: Maintains error state and provides detailed error messages

Output Format

The text encoder produces logs in this format:

time=2023-09-02T10:30:15Z INFO User action user_id=12345 action="create profile" success=true duration=150ms tags=[user,profile,create] complex=1+2i

Where:

  • Field values are formatted as key=value
  • Strings with spaces are automatically quoted: action="create profile"
  • Arrays use brackets: tags=[user,profile,create]
  • Complex numbers: complex=1+2i
  • All standard Zap field types are supported
ReflectEncoder Output Examples

Complex Object:

{"id":123,"name":"John Doe","email":"[email protected]","tags":["developer","admin"],"active":true,"balance":1234.56,"metadata":{"last_login":"2023-09-02T10:30:15Z","login_count":42}}

Complex Numbers (JSON-compatible):

{"real":1.5,"imag":2.5}

HTML Escaping:

{"description":"User \u003cscript\u003ealert('xss')\u003c/script\u003e input","safe":"\u0026 \u003c \u003e"}

Comparison with JSON Encoder

JSON Output (default zap):

{"level":"info","ts":1693648215,"msg":"User action","user_id":"12345","action":"create profile","success":true,"duration":150000000,"tags":["user","profile","create"]}

Text Output (zaptext):

time=2023-09-02T10:30:15Z INFO User action user_id=12345 action="create profile" success=true duration=150ms tags=[user,profile,create]

Thanks

  • zap: The excellent structured logging library this encoder extends

Documentation

Overview

Package zaptext provides text-based encoding for structured logging. The ReflectEncoder provides reflection-based encoding of arbitrary Go data structures into a JSON-like format, with object pooling for performance optimization.

Index

Constants

View Source
const (
	// DefaultMaxDepth is the default maximum recursion depth to prevent infinite loops
	DefaultMaxDepth = 32
)

Variables

This section is empty.

Functions

func CustomTimeEncoderFactory

func CustomTimeEncoderFactory(layout string) zapcore.TimeEncoder

CustomTimeEncoderFactory return a zapcore.TimeEncoder format time with custom layout

func NewTextEncoder

func NewTextEncoder(cfg zapcore.EncoderConfig) zapcore.Encoder

Types

type ReflectEncoder

type ReflectEncoder struct {
	// contains filtered or unexported fields
}

ReflectEncoder provides reflection-based encoding of Go data structures into JSON-like format. It supports object pooling for performance optimization and includes protection against infinite recursion. The encoder handles all Go primitive types, compound types (arrays, slices, maps, structs), and special cases like time.Time formatting.

Example usage:

encoder := zaptext.NewReflectEncoder(os.Stdout)
defer encoder.Release() // Return to pool for reuse
err := encoder.Encode(myStruct)

func NewReflectEncoder

func NewReflectEncoder(w io.Writer) *ReflectEncoder

NewReflectEncoder creates a new ReflectEncoder from the object pool. The encoder is configured to write to the provided io.Writer. Always call Release() when done to return the encoder to the pool.

func (*ReflectEncoder) Encode

func (enc *ReflectEncoder) Encode(obj any) error

Encode encodes the given object using reflection into JSON-like format. It handles all Go primitive types, compound types, and includes special handling for time.Time. The method includes protection against infinite recursion with a maximum depth limit. If an error occurs, the encoder maintains error state for subsequent calls.

func (*ReflectEncoder) Release added in v0.0.2

func (enc *ReflectEncoder) Release()

Release returns the encoder back to the pool for reuse. This should always be called when done with an encoder to optimize memory usage.

func (*ReflectEncoder) SetEscapeHTML added in v0.0.2

func (enc *ReflectEncoder) SetEscapeHTML(escape bool)

SetEscapeHTML configures whether to escape HTML characters in strings. When enabled (default), characters like <, >, and & are escaped to their HTML entities. This is useful when the output might be embedded in HTML documents.

func (*ReflectEncoder) SetMaxDepth added in v0.0.2

func (enc *ReflectEncoder) SetMaxDepth(depth int)

SetMaxDepth configures the maximum recursion depth to prevent infinite loops. The default value is 32. Setting a lower value may prevent encoding of deeply nested structures, while higher values may risk stack overflow with circular references.

type TextEncoder

type TextEncoder struct {
	*zapcore.EncoderConfig
	// contains filtered or unexported fields
}

func (*TextEncoder) AddArray

func (enc *TextEncoder) AddArray(key string, marshaler zapcore.ArrayMarshaler) (err error)

Logging-specific marshalers.

func (*TextEncoder) AddBinary

func (enc *TextEncoder) AddBinary(key string, value []byte)

Built-in types. for arbitrary bytes

func (*TextEncoder) AddBool

func (enc *TextEncoder) AddBool(key string, value bool)

func (*TextEncoder) AddByteString

func (enc *TextEncoder) AddByteString(key string, value []byte)

func (*TextEncoder) AddComplex128

func (enc *TextEncoder) AddComplex128(key string, value complex128)

func (*TextEncoder) AddComplex64

func (enc *TextEncoder) AddComplex64(key string, value complex64)

func (*TextEncoder) AddDuration

func (enc *TextEncoder) AddDuration(key string, value time.Duration)

func (*TextEncoder) AddFloat32

func (enc *TextEncoder) AddFloat32(key string, value float32)

func (*TextEncoder) AddFloat64

func (enc *TextEncoder) AddFloat64(key string, value float64)

func (*TextEncoder) AddInt

func (enc *TextEncoder) AddInt(key string, value int)

func (*TextEncoder) AddInt16

func (enc *TextEncoder) AddInt16(key string, value int16)

func (*TextEncoder) AddInt32

func (enc *TextEncoder) AddInt32(key string, value int32)

func (*TextEncoder) AddInt64

func (enc *TextEncoder) AddInt64(key string, value int64)

func (*TextEncoder) AddInt8

func (enc *TextEncoder) AddInt8(key string, value int8)

func (*TextEncoder) AddObject

func (enc *TextEncoder) AddObject(key string, marshaler zapcore.ObjectMarshaler) (err error)

func (*TextEncoder) AddReflected

func (enc *TextEncoder) AddReflected(key string, value any) (err error)

AddReflected uses reflection to serialize arbitrary objects, so it can be slow and allocation-heavy.

func (*TextEncoder) AddString

func (enc *TextEncoder) AddString(key, value string)

func (*TextEncoder) AddTime

func (enc *TextEncoder) AddTime(key string, value time.Time)

func (*TextEncoder) AddUint

func (enc *TextEncoder) AddUint(key string, value uint)

func (*TextEncoder) AddUint16

func (enc *TextEncoder) AddUint16(key string, value uint16)

func (*TextEncoder) AddUint32

func (enc *TextEncoder) AddUint32(key string, value uint32)

func (*TextEncoder) AddUint64

func (enc *TextEncoder) AddUint64(key string, value uint64)

func (*TextEncoder) AddUint8

func (enc *TextEncoder) AddUint8(key string, value uint8)

func (*TextEncoder) AddUintptr

func (enc *TextEncoder) AddUintptr(key string, value uintptr)

func (*TextEncoder) AppendArray

func (enc *TextEncoder) AppendArray(arr zapcore.ArrayMarshaler) (err error)

Logging-specific marshalers.{}

func (*TextEncoder) AppendBool

func (enc *TextEncoder) AppendBool(value bool)

func (*TextEncoder) AppendByteString

func (enc *TextEncoder) AppendByteString(value []byte)

for UTF-8 encoded bytes

func (*TextEncoder) AppendComplex128

func (enc *TextEncoder) AppendComplex128(value complex128)

func (*TextEncoder) AppendComplex64

func (enc *TextEncoder) AppendComplex64(value complex64)

func (*TextEncoder) AppendDuration

func (enc *TextEncoder) AppendDuration(value time.Duration)

Time-related types.

func (*TextEncoder) AppendFloat32

func (enc *TextEncoder) AppendFloat32(value float32)

func (*TextEncoder) AppendFloat64

func (enc *TextEncoder) AppendFloat64(value float64)

func (*TextEncoder) AppendInt

func (enc *TextEncoder) AppendInt(value int)

func (*TextEncoder) AppendInt16

func (enc *TextEncoder) AppendInt16(value int16)

func (*TextEncoder) AppendInt32

func (enc *TextEncoder) AppendInt32(value int32)

func (*TextEncoder) AppendInt64

func (enc *TextEncoder) AppendInt64(value int64)

func (*TextEncoder) AppendInt8

func (enc *TextEncoder) AppendInt8(value int8)

func (*TextEncoder) AppendObject

func (enc *TextEncoder) AppendObject(obj zapcore.ObjectMarshaler) (err error)

func (*TextEncoder) AppendReflected

func (enc *TextEncoder) AppendReflected(value any) (err error)

AppendReflected uses reflection to serialize arbitrary objects, so it's slow and allocation-heavy.

func (*TextEncoder) AppendString

func (enc *TextEncoder) AppendString(value string)

func (*TextEncoder) AppendTime

func (enc *TextEncoder) AppendTime(value time.Time)

func (*TextEncoder) AppendUint

func (enc *TextEncoder) AppendUint(value uint)

func (*TextEncoder) AppendUint16

func (enc *TextEncoder) AppendUint16(value uint16)

func (*TextEncoder) AppendUint32

func (enc *TextEncoder) AppendUint32(value uint32)

func (*TextEncoder) AppendUint64

func (enc *TextEncoder) AppendUint64(value uint64)

func (*TextEncoder) AppendUint8

func (enc *TextEncoder) AppendUint8(value uint8)

func (*TextEncoder) AppendUintptr

func (enc *TextEncoder) AppendUintptr(value uintptr)

func (*TextEncoder) Clone

func (enc *TextEncoder) Clone() zapcore.Encoder

Clone copies the encoder, ensuring that adding fields to the copy doesn't affect the original.

func (*TextEncoder) EncodeEntry

func (enc *TextEncoder) EncodeEntry(ent zapcore.Entry, fields []zapcore.Field) (buf *buffer.Buffer, err error)

EncodeEntry encodes an entry and fields, along with any accumulated context, into a byte buffer and returns it. Any fields that are empty, including fields on the `Entry` type, should be omitted.

func (*TextEncoder) OpenNamespace

func (enc *TextEncoder) OpenNamespace(key string)

OpenNamespace opens an isolated namespace where all subsequent fields will be added. Applications can use namespaces to prevent key collisions when injecting loggers into sub-components or third-party libraries.

Directories

Path Synopsis
example module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL