zapang

package module
v1.3.0 Latest Latest
Warning

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

Go to latest
Published: Mar 19, 2026 License: MIT Imports: 17 Imported by: 0

README

zapang

Structured logging for Go services, built on zap.

Human-readable console output with colored error traces + clean JSON export for log aggregation — out of the box.

Install

go get github.com/s4bb4t/zapang

Quick start

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

log := zapang.New(ctx, "my-service", zapang.Config{
    Level:       "info",
    Environment: "local",
}, nil)

log.Info("started", zap.String("addr", ":8080"))

Console output

19 Mar 16:33:07  INFO  ./main.go:25  started  addr=:8080  service=my-service

Fields rendered as key=value pairs, not JSON. Timestamps in 02 Jan 15:04:05 format.

Errors from go-faster/errors (or any fmt.Formatter implementation) are rendered as a colored multi-line block:

19 Mar 16:33:07  ERROR  ./main.go:30  failed  error=handle request: parse: invalid input  service=my-service
handle request:                              ← bold red
    main.handleRequest
        ./handler.go:45                      ← dim
  - parse:                                   ← bold red
    main.parse
        ./parser.go:12                       ← dim
  - invalid input:                           ← bold red
    main.validate
        ./validator.go:8                     ← dim

JSON export

For log aggregation (ClickHouse, Loki, ELK, etc.) — parallel JSON output without errorVerbose:

// File
log := zapang.New(ctx, "svc", zapang.Config{
    Level:       "info",
    Environment: "prod",
    ExportPath:  "/var/log/app/svc.jsonl",
}, nil)

// Any io.Writer (works in any environment)
log = zapang.New(ctx, "svc", zapang.Config{
    Level:        "info",
    ExportWriter: myWriter, // Kafka, ClickHouse HTTP, pipe, etc.
}, nil)

Output:

{"level":"error","timestamp":"2026-03-19T16:33:11.110086+03:00","caller":"./main.go:30","message":"failed","service":"svc","error":"handle request: parse: invalid input"}

Timestamps in RFC3339Nano. No errorVerbose — only the short error string.

Configuration

zapang.Config{
    Level:             "info",          // debug, info, warn, error, dpanic, panic, fatal
    Environment:       "local",         // local, dev, prod
    ExportPath:        "",              // file path, "stdout", "stderr" (dev/prod only)
    ExportWriter:      nil,             // io.Writer for JSON export (any env)
    DisableCaller:     false,           // hide caller file:line
    DisableStacktrace: false,           // disable stacktraces
    StacktraceLevel:   "error",         // min level for stacktraces
    Sampling: &zapang.SamplingConfig{
        Initial:    100,                // entries per second before sampling
        Thereafter: 100,                // keep every Nth entry after Initial
    },
}

DefaultLoggerConfig() returns sensible defaults (info level, local env, sampling 100/100).

Context propagation

// Attach logger to context
ctx = zapang.WithContext(ctx, log)

// Retrieve from context (falls back to global)
log = zapang.FromContext(ctx)

Dynamic log level

// At init
log, level := zapang.NewWithLevel(ctx, "svc", cfg, nil)

// At runtime
level.SetLevel(zapcore.DebugLevel)

// Or via global
zapang.SetGlobalLevel("debug")

HTTP middleware

mux := http.NewServeMux()
handler := zapang.HTTPMiddleware(log)(
    zapang.RecoveryMiddleware(log)(mux),
)

Logs method, path, status, latency, client IP, response size. Level by status: 5xx → Error, 4xx → Warn, rest → Info. Recovery middleware catches panics.

OpenTelemetry

// Enrich logger with trace/span IDs from context
log = zapang.WithOtelContext(ctx, log)

// Or get logger from context + OTel in one call
log = zapang.FromOtelContext(ctx)

// Attach to a specific span
log = zapang.LoggerWithSpan(log, span)

// Log a trace event
zapang.TraceEvent(log, span, "cache miss", zapang.CacheKey("user:42"))

Field helpers

Pre-built zap.Field functions for structured logging:

Domain Fields
HTTP RequestID, Method, Path, StatusCode, Latency, LatencyMs, ClientIP, UserAgent, RequestSize, ResponseSize
Tracing TraceID, SpanID, ParentSpanID
User UserID, TenantID, SessionID
Error Error, ErrorType, ErrorCode
Database DBOperation, DBTable, DBDuration, RowsAffected
Cache CacheHit, CacheKey
Queue QueueName, MessageID
gRPC GRPCMethod, GRPCService, GRPCCode
Meta Component, Operation, Version, Environment

Documentation

Overview

Package logger provides a production-ready structured logging solution built on Uber's Zap. It supports environment-aware configuration, context propagation with trace IDs, log sampling, and graceful shutdown.

Index

Constants

View Source
const (
	EnvLocal = "local"
	EnvProd  = "prod"
	EnvDev   = "dev"
)

Variables

This section is empty.

Functions

func CacheHit

func CacheHit(hit bool) zap.Field

Cache fields for cache operation logging.

func CacheKey

func CacheKey(key string) zap.Field

func ClientIP

func ClientIP(ip string) zap.Field

func Component

func Component(name string) zap.Field

Component identifies the component generating the log.

func DBDuration

func DBDuration(d time.Duration) zap.Field

func DBOperation

func DBOperation(op string) zap.Field

Database fields for database operation logging.

func DBTable

func DBTable(table string) zap.Field

func Environment

func Environment(env string) zap.Field

Environment for deployment environment.

func Error

func Error(err error) zap.Field

Error fields for error logging.

func ErrorCode

func ErrorCode(code string) zap.Field

func ErrorType

func ErrorType(err error) zap.Field

func FromContext

func FromContext(ctx context.Context) *zap.Logger

FromContext retrieves the logger from context, or returns the global logger.

func FromOtelContext

func FromOtelContext(ctx context.Context) *zap.Logger

FromOtelContext retrieves the logger from context and enriches it with OpenTelemetry trace information if available.

func GRPCCode

func GRPCCode(code string) zap.Field

func GRPCMethod

func GRPCMethod(method string) zap.Field

gRPC fields for gRPC request logging.

func GRPCService

func GRPCService(service string) zap.Field

func Global

func Global() *zap.Logger

Global returns the global logger instance.

func GlobalLevel

func GlobalLevel() zap.AtomicLevel

GlobalLevel returns the global logger's AtomicLevel for dynamic level control.

func HTTPMiddleware

func HTTPMiddleware(log *zap.Logger) func(http.Handler) http.Handler

HTTPMiddleware returns a middleware that logs HTTP requests. It captures method, path, status, latency, and request metadata.

func Latency

func Latency(d time.Duration) zap.Field

func LatencyMs

func LatencyMs(d time.Duration) zap.Field

func LoggerWithSpan

func LoggerWithSpan(log *zap.Logger, span trace.Span) *zap.Logger

LoggerWithSpan creates a new logger with span information attached. This is useful when you want to correlate logs with a specific span.

func MessageID

func MessageID(id string) zap.Field

func Method

func Method(method string) zap.Field

func New

func New(ctx context.Context, serviceName string, cfg Config, w io.Writer) *zap.Logger

New creates a new *zap.Logger based on the provided configuration. The serviceName is added as a permanent field to all log entries. If w is provided, logs will also be written to it (useful for testing).

Output behavior:

  • All environments: Human-readable console output to stdout
  • Dev/Prod with ExportPath: Additional JSON output for log aggregation

func NewWithLevel

func NewWithLevel(ctx context.Context, serviceName string, cfg Config, w io.Writer) (*zap.Logger, zap.AtomicLevel)

NewWithLevel creates a new *zap.Logger and returns its AtomicLevel for dynamic level control. Use this when you need to change the log level at runtime.

func Operation

func Operation(name string) zap.Field

Operation identifies the operation being performed.

func ParentSpanID

func ParentSpanID(id string) zap.Field

func Path

func Path(path string) zap.Field

func QueueName

func QueueName(name string) zap.Field

Queue fields for message queue logging.

func RecoveryMiddleware

func RecoveryMiddleware(log *zap.Logger) func(http.Handler) http.Handler

RecoveryMiddleware returns a middleware that recovers from panics and logs them.

func RequestID

func RequestID(id string) zap.Field

Request fields for HTTP request logging.

func RequestSize

func RequestSize(size int64) zap.Field

func ResponseSize

func ResponseSize(size int) zap.Field

func RowsAffected

func RowsAffected(n int64) zap.Field

func SessionID

func SessionID(id string) zap.Field

func SetGlobalLevel

func SetGlobalLevel(level string)

SetGlobalLevel dynamically changes the global logger's level.

func SpanID

func SpanID(id string) zap.Field

func StatusCode

func StatusCode(code int) zap.Field

func TenantID

func TenantID(id string) zap.Field

func TraceEvent

func TraceEvent(log *zap.Logger, span trace.Span, msg string, fields ...zap.Field)

TraceEvent logs a trace event as a zap log entry. This helps correlate application logs with distributed traces.

func TraceID

func TraceID(id string) zap.Field

Tracing fields for distributed tracing correlation.

func UserAgent

func UserAgent(ua string) zap.Field

func UserID

func UserID(id string) zap.Field

User fields for user context.

func Version

func Version(v string) zap.Field

Version for service versioning.

func WithContext

func WithContext(ctx context.Context, l *zap.Logger) context.Context

WithContext returns a new context with the logger attached.

func WithError

func WithError(l *zap.Logger, err error) *zap.Logger

WithError returns a new logger with the error attached.

func WithOtelContext

func WithOtelContext(ctx context.Context, log *zap.Logger) *zap.Logger

WithOtelContext extracts trace and span IDs from an OpenTelemetry context and returns a logger with those fields attached.

func WithTraceID

func WithTraceID(l *zap.Logger, traceID, spanID string) *zap.Logger

WithTraceID returns a new logger with trace and span IDs attached.

Types

type Config

type Config struct {
	// Level is the minimum enabled logging level.
	// Valid values: debug, info, warn, error, dpanic, panic, fatal
	Level string `yaml:"level" json:"level" mapstructure:"level"`

	// Environment controls logger behavior.
	// "local" - only human-readable console output
	// "dev", "prod" - human-readable console + optional JSON export
	Environment string `yaml:"environment" json:"environment" mapstructure:"environment"`

	// ExportPath is an optional path for JSON log export (only for dev/prod).
	// Can be a file path or "stdout"/"stderr".
	// If empty, JSON export is disabled.
	ExportPath string `yaml:"export_path" json:"export_path" mapstructure:"export_path"`

	// ExportWriter is an optional writer for JSON log export.
	// When set, JSON-encoded logs are written here in addition to console output.
	// Use this to pipe logs directly into ClickHouse, Loki, Kafka, etc.
	// Takes precedence over ExportPath. Works in any environment.
	ExportWriter io.Writer `yaml:"-" json:"-" mapstructure:"-"`

	// Sampling configures log sampling for high-throughput applications.
	Sampling *SamplingConfig `yaml:"sampling,omitempty" json:"sampling" mapstructure:"sampling"`

	// DisableCaller stops annotating logs with the calling function's file name and line number.
	DisableCaller bool `yaml:"disable_caller" json:"disable_caller" mapstructure:"disable_caller"`

	// DisableStacktrace disables automatic stacktrace capturing.
	DisableStacktrace bool `yaml:"disable_stacktrace" json:"disable_stacktrace" mapstructure:"disable_stacktrace"`

	// StacktraceLevel is the minimum level at which stacktraces are captured.
	// Valid values: debug, info, warn, error, dpanic, panic, fatal
	StacktraceLevel string `yaml:"stacktrace_level" json:"stacktrace_level" mapstructure:"stacktrace_level"`
}

Config holds configuration for the application logger.

func DefaultLoggerConfig

func DefaultLoggerConfig() Config

DefaultLoggerConfig returns a sensible default configuration.

type OtelCore

type OtelCore struct {
	zap.Logger
}

OtelCore is a zapcore.Core wrapper that automatically adds trace context. Use this when you want all logs to automatically include trace IDs.

type SamplingConfig

type SamplingConfig struct {
	// Initial is the number of entries with the same level and message to log per second.
	Initial int `yaml:"initial"`

	// Thereafter is the number of entries to drop for each duplicate after Initial.
	Thereafter int `yaml:"thereafter"`
}

SamplingConfig sets a sampling policy for repeated log entries.

Jump to

Keyboard shortcuts

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