zerowrap

package module
v1.4.0 Latest Latest
Warning

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

Go to latest
Published: May 6, 2026 License: MIT Imports: 14 Imported by: 0

README

zerowrap

A reusable Go package that wraps zerolog with context-based logger access and maximum abstraction.

Installation

go get github.com/bnema/zerowrap

For OpenTelemetry integration (optional):

go get github.com/bnema/zerowrap/otel

Quick Start

package main

import (
    "context"
    "github.com/bnema/zerowrap"
)

func main() {
    // Create a logger and attach to context
    logger := zerowrap.New(zerowrap.Config{
        Level:  "debug",
        Format: "console",
    })
    ctx := zerowrap.WithCtx(context.Background(), logger)

    // Use throughout your application
    log := zerowrap.FromCtx(ctx)
    log.Info().Msg("hello world")

    // Pass context to other functions
    doSomething(ctx)
}

func doSomething(ctx context.Context) {
    log := zerowrap.FromCtx(ctx)
    log.Debug().Msg("doing something")
}

Features

  • Context-based logger storage and retrieval
  • Add fields to loggers (single, multiple, or from structs)
  • Configurable logger creation with sensible defaults
  • File-based logging with rotation support and app-managed log paths
  • OpenTelemetry log bridging (optional sub-package)
  • Common field name constants for consistency
  • Error helpers for logging and returning errors in one line

API Reference

Context Functions
Function Description
FromCtx(ctx) Extract logger from context (returns no-op if none)
Ctx(ctx) Get pointer to logger in context
WithCtx(ctx, log) Attach logger to context
Field Helpers
Function Description
FromCtxWithField(ctx, key, value) Get logger with one additional field
FromCtxWithFields(ctx, fields) Get logger with multiple fields
FromCtxWithStruct(ctx, struct) Get logger with fields from struct tags
CtxWithField(ctx, key, value) Get new context with enriched logger
CtxWithFields(ctx, fields) Get new context with enriched logger
CtxWithStruct(ctx, struct) Get new context with enriched logger
Logger Creation
Function Description
New(cfg) Create logger with configuration
NewFromEnv(prefix) Create logger from environment variables
NewWithFile(cfg, fileCfg) Create logger with file output
Default() Create default logger (info level, console format)
WithHook(log, hook) Add hook to logger
Error Helpers (Logger methods)
Method Description
log.WrapErr(err, msg) Log and wrap error with message
log.WrapErrWithFields(err, msg, fields) Log and wrap error with fields
log.WrapErrf(err, format, args...) Log and wrap error with formatted message

Usage Examples

Basic Logging with Context
logger := zerowrap.Default()
ctx := zerowrap.WithCtx(context.Background(), logger)

log := zerowrap.FromCtx(ctx)
log.Info().Str("action", "start").Msg("application started")
Adding Fields
// Single field
log := zerowrap.FromCtxWithField(ctx, "user_id", 123)
log.Info().Msg("user action")

// Multiple fields
log := zerowrap.FromCtxWithFields(ctx, map[string]any{
    "user_id":    123,
    "request_id": "abc-123",
    "ip":         "192.168.1.1",
})
log.Info().Msg("request received")

// Enrich context for downstream use
ctx = zerowrap.CtxWithField(ctx, zerowrap.FieldComponent, "auth")
zerowrap.FromCtx(ctx).Info().Msg("authenticating") // includes component=auth
Struct Tags

Extract fields from structs using the log tag (falls back to json tag, then field name):

type RequestInfo struct {
    UserID    int    `log:"user_id"`
    RequestID string `log:"request_id"`
    IP        string `json:"ip_address"`
    Internal  string `log:"-"` // skipped
}

info := RequestInfo{
    UserID:    123,
    RequestID: "abc-123",
    IP:        "192.168.1.1",
    Internal:  "secret",
}

log := zerowrap.FromCtxWithStruct(ctx, info)
log.Info().Msg("request info")
// Output includes: user_id=123 request_id=abc-123 ip_address=192.168.1.1
Logger Configuration
log := zerowrap.New(zerowrap.Config{
    Level:      "debug",           // trace, debug, info, warn, error, fatal, panic
    Format:     "console",         // console or json
    TimeFormat: time.RFC3339,      // custom time format
    Output:     os.Stdout,         // custom output writer
    Caller:     true,              // include caller info (file:line)
})
Environment Variables
// Reads MYAPP_LOG_LEVEL and MYAPP_LOG_FORMAT
log := zerowrap.NewFromEnv("MYAPP")
File Logging
log, cleanup, err := zerowrap.NewWithFile(
    zerowrap.Config{
        Level:  "info",
        Format: "console",
    },
    zerowrap.FileConfig{
        Enabled:    true,
        Path:       "/var/log/myapp/app.log",
        MaxSize:    100,  // MB
        MaxBackups: 3,
        MaxAge:     28,   // days
        Compress:   true,
    },
)
if err != nil {
    panic(err)
}
defer cleanup()

ctx := zerowrap.WithCtx(context.Background(), log)
// Logs go to both console (formatted) and file (JSON)
Error Handling

Log and return errors in one line using Logger methods:

func connectDB(ctx context.Context) error {
    log := zerowrap.FromCtx(ctx)

    db, err := sql.Open("postgres", connStr)
    if err != nil {
        return log.WrapErr(err, "failed to open database")
    }

    if err := db.Ping(); err != nil {
        return log.WrapErr(err, "database ping failed")
    }

    return nil
}

// With fields
func queryUser(ctx context.Context, userID int) (*User, error) {
    log := zerowrap.FromCtx(ctx)

    user, err := db.GetUser(userID)
    if err != nil {
        return nil, log.WrapErrWithFields(err, "user query failed", map[string]any{
            "user_id": userID,
        })
    }
    return user, nil
}

// With formatted message
func connectToHost(ctx context.Context, host string) error {
    log := zerowrap.FromCtx(ctx)

    conn, err := net.Dial("tcp", host)
    if err != nil {
        return log.WrapErrf(err, "failed to connect to %s", host)
    }
    defer conn.Close()
    return nil
}
App-Managed File Logging

When you don't want to hard-code a full file path, set AppName and let zerowrap choose the standard OS-specific log directory:

fileCfg := zerowrap.FileConfig{
    Enabled:    true,
    AppName:    "MyApp",
    Mode:       zerowrap.FileModeSession,
    FileFormat: zerowrap.FileFormatConsole,
}

// Inspect the resolved path without creating a logger
path, err := zerowrap.ResolveLogPath(fileCfg)
if err != nil {
    panic(err)
}
fmt.Println("Log file:", path)

log, cleanup, err := zerowrap.NewWithFile(
    zerowrap.Config{Level: "debug", Format: "console"},
    fileCfg,
)
if err != nil {
    panic(err)
}
defer cleanup()

Default OS locations (when no BaseDir override is supplied):

OS Log root
macOS ~/Library/Logs/<AppName>/
Linux $XDG_STATE_HOME/<AppName>/logs/ or ~/.local/state/<AppName>/logs/
Windows %LOCALAPPDATA%\<AppName>\Logs\

Modes

Constant Behavior
FileModeSingle Writes to <root>/app.log (or <root>/<Name>.log). All runs share the same file.
FileModeSession Writes to <root>/sessions/<session-id>/<Name>.log (default app.log). Each process generates a unique session directory that is shared across all loggers in that process.

File formats

Constant Description
FileFormatJSON JSON lines (default). Best for ingestion and log aggregation.
FileFormatConsole Human-readable text without ANSI colors. Useful for local apps, CLI tools, and support logs.

When both Path and AppName are set, Path takes priority. Set BaseDir to relocate the app root while keeping the standard layout. For example, BaseDir: "/tmp" with AppName: "MyApp" resolves under /tmp/MyApp/logs/.

Compatibility note: FileConfig is intended to be used with keyed fields. This release adds fields to FileConfig, so callers using unkeyed composite literals should switch to keyed literals.

OpenTelemetry Integration
import (
    "github.com/bnema/zerowrap"
    "github.com/bnema/zerowrap/otel"
)

// Using global provider
log := zerowrap.New(cfg).Hook(otel.NewHook("my-service"))

// Using custom provider
provider := // your OTel logger provider
log := zerowrap.New(cfg).Hook(otel.NewHookWithProvider(provider, "my-service"))

ctx := zerowrap.WithCtx(context.Background(), log)
// Logs now flow to both zerolog output AND OpenTelemetry

Field Constants

Common field names for consistency across your application:

// Identity & Tracing
zerowrap.FieldComponent      // "component"
zerowrap.FieldRequestID      // "request_id"
zerowrap.FieldTraceID        // "trace_id"
zerowrap.FieldSpanID         // "span_id"
zerowrap.FieldCorrelationID  // "correlation_id"
zerowrap.FieldSessionID      // "session_id"
zerowrap.FieldUserID         // "user_id"

// HTTP/API
zerowrap.FieldMethod    // "method"
zerowrap.FieldPath      // "path"
zerowrap.FieldStatus    // "status"
zerowrap.FieldClientIP  // "client_ip"

// Service/Infra
zerowrap.FieldService  // "service"
zerowrap.FieldVersion  // "version"
zerowrap.FieldHost     // "host"
zerowrap.FieldEnv      // "env"

// Operations
zerowrap.FieldAction     // "action"
zerowrap.FieldOperation  // "operation"
zerowrap.FieldError      // "error"
zerowrap.FieldDuration   // "duration_ms"

// Data
zerowrap.FieldCount  // "count"
zerowrap.FieldSize   // "size_bytes"

// Clean Architecture - Layers
zerowrap.FieldLayer    // "layer" (domain, usecase, adapter)
zerowrap.FieldUseCase  // "usecase" (e.g., "CreateUser")

// Clean Architecture - Adapters
zerowrap.FieldAdapter      // "adapter" (http, grpc, postgres)
zerowrap.FieldAdapterType  // "adapter_type" (in/out)
zerowrap.FieldHandler      // "handler"
zerowrap.FieldRepository   // "repository"
zerowrap.FieldGateway      // "gateway"

// Database/Storage
zerowrap.FieldTable     // "table"
zerowrap.FieldQuery     // "query"
zerowrap.FieldDatabase  // "database"

// Messaging/Events
zerowrap.FieldEvent    // "event"
zerowrap.FieldTopic    // "topic"
zerowrap.FieldQueue    // "queue"
zerowrap.FieldPayload  // "payload"

// Entity/Resource
zerowrap.FieldEntity      // "entity"
zerowrap.FieldEntityID    // "entity_id"
zerowrap.FieldEntityType  // "entity_type"

Usage:

ctx = zerowrap.CtxWithField(ctx, zerowrap.FieldComponent, "database")
ctx = zerowrap.CtxWithField(ctx, zerowrap.FieldRequestID, requestID)

License

MIT License - see LICENSE file.

Documentation

Overview

Package zerowrap provides a reusable wrapper around zerolog for context-based logging.

It simplifies common logging patterns by providing:

  • Context-based logger storage and retrieval
  • A Logger type with error wrapping helpers
  • Field enrichment (single, multiple, or from structs)
  • Configurable logger creation with sensible defaults
  • File-based logging with rotation support
  • OpenTelemetry integration (optional sub-package)

Logger Type

The Logger type wraps zerolog.Logger and provides additional convenience methods. All zerolog.Logger methods are available via embedding:

type Logger struct {
    zerolog.Logger
}

Logger methods:

log.WrapErr(err, msg) error           // Log and wrap error
log.WrapErrWithFields(err, msg, fields) error  // Log with fields and wrap
log.WrapErrf(err, format, args...) error       // Log and wrap with formatted message
log.WithField(key, value) Logger      // Return logger with added field
log.WithFields(fields) Logger         // Return logger with added fields
log.WithStruct(s) Logger              // Return logger with fields from struct

Quick Start

// Create and attach logger to context
logger := zerowrap.New(zerowrap.Config{
    Level:  "debug",
    Format: "console",
})
ctx := zerowrap.WithCtx(context.Background(), logger)

// Use throughout your application
log := zerowrap.FromCtx(ctx)
log.Info().Msg("hello world")

Context Functions

Store and retrieve loggers from context:

FromCtx(ctx) Logger                   // Get logger from context (no-op if none)
Ctx(ctx) *zerolog.Logger              // Get pointer to underlying zerolog.Logger
WithCtx(ctx, log) context.Context     // Attach logger to context
WithCtxZerolog(ctx, log) context.Context  // Attach zerolog.Logger to context

Field Helpers

Get logger with additional fields:

FromCtxWithField(ctx, key, value) Logger      // One field
FromCtxWithFields(ctx, fields) Logger         // Multiple fields
FromCtxWithStruct(ctx, s) Logger              // Fields from struct tags

Get new context with enriched logger:

CtxWithField(ctx, key, value) context.Context
CtxWithFields(ctx, fields) context.Context
CtxWithStruct(ctx, s) context.Context

Struct Tags

Extract fields from structs using the `log` tag (falls back to `json`, then field name):

type Request struct {
    UserID    int    `log:"user_id"`
    RequestID string `log:"request_id"`
    IP        string `json:"ip_address"`
    Internal  string `log:"-"`  // skipped
}

log := zerowrap.FromCtxWithStruct(ctx, Request{UserID: 123, RequestID: "abc"})

Logger Creation

Create loggers with configuration:

New(cfg Config) Logger                        // Create with config
NewFromEnv(prefix string) Logger              // Create from env vars
NewWithFile(cfg, fileCfg) (Logger, func(), error)  // Create with file output
Default() Logger                              // Default logger (info, console)
WithHook(log, hook) Logger                    // Add hook to logger

Config

Configuration for logger creation:

type Config struct {
    Level      string     // trace, debug, info, warn, error, fatal, panic
    Format     string     // json or console
    TimeFormat string     // time format (default: time.RFC3339)
    Output     io.Writer  // output writer (default: os.Stderr)
    Caller     bool       // include caller info (file:line)
}

FileConfig

Configuration for file-based logging with rotation and app-managed paths. Use keyed fields when constructing FileConfig values; unkeyed composite literals are not supported as this exported configuration struct may grow:

type FileConfig struct {
    Enabled    bool       // toggle file logging
    Path       string     // full explicit file path override (takes priority when set)
    AppName    string     // enables app-managed log paths when Path is empty
    BaseDir    string     // overrides root directory before AppName when using app-managed paths
    Name       string     // log file name (defaults to "app" with .log extension if missing)
    Mode       FileMode   // app-managed file layout: "single" or "session" (default: "single")
    FileFormat FileFormat // file output format: "json" or "console" (default: "json")
    MaxSize    int        // max size in MB before rotation (default: 100)
    MaxBackups int        // max old files to retain (default: 3)
    MaxAge     int        // max days to retain (default: 28)
    Compress   bool       // compress rotated files
}

Error Helpers

Log and return wrapped errors in one line:

func doSomething(ctx context.Context) error {
    log := zerowrap.FromCtx(ctx)

    // Simple wrap
    if err != nil {
        return log.WrapErr(err, "failed to connect")
    }

    // With fields
    if err != nil {
        return log.WrapErrWithFields(err, "query failed", map[string]any{
            "table": tableName,
        })
    }

    // With formatted message
    if err != nil {
        return log.WrapErrf(err, "failed to connect to %s", host)
    }
}

Field Constants

Common field names for consistency:

// Identity & Tracing
FieldComponent, FieldRequestID, FieldTraceID, FieldSpanID
FieldCorrelationID, FieldSessionID, FieldUserID

// HTTP/API
FieldMethod, FieldPath, FieldStatus, FieldClientIP

// Service/Infra
FieldService, FieldVersion, FieldHost, FieldEnv

// Operations
FieldAction, FieldOperation, FieldError, FieldDuration

// Data
FieldCount, FieldSize

// Clean Architecture - Layers
FieldLayer, FieldUseCase

// Clean Architecture - Adapters
FieldAdapter, FieldAdapterType, FieldHandler, FieldRepository, FieldGateway

// Database/Storage
FieldTable, FieldQuery, FieldDatabase

// Messaging/Events
FieldEvent, FieldTopic, FieldQueue, FieldPayload

// Entity/Resource
FieldEntity, FieldEntityID, FieldEntityType

Usage:

ctx = zerowrap.CtxWithField(ctx, zerowrap.FieldComponent, "database")
ctx = zerowrap.CtxWithFields(ctx, map[string]any{
    zerowrap.FieldRequestID: requestID,
    zerowrap.FieldUserID:    userID,
})

Environment Variables

Create logger from environment variables:

// Reads MYAPP_LOG_LEVEL and MYAPP_LOG_FORMAT
log := zerowrap.NewFromEnv("MYAPP")

File Logging

Create logger with file output and rotation:

log, cleanup, err := zerowrap.NewWithFile(
    zerowrap.Config{Level: "info", Format: "console"},
    zerowrap.FileConfig{
        Enabled:    true,
        Path:       "/var/log/app.log",
        MaxSize:    100,  // MB
        MaxBackups: 3,
        MaxAge:     28,   // days
        Compress:   true,
    },
)
if err != nil {
    panic(err)
}
defer cleanup()

App-Managed File Paths

When Path is empty and AppName is set, the library automatically resolves log paths using OS-specific conventions:

Linux:   $XDG_STATE_HOME/<app>/logs/<name>.log (falls back to ~/.local/state/<app>/logs/<name>.log)
macOS:   ~/Library/Logs/<app>/<name>.log
Windows: %LOCALAPPDATA%\<app>\Logs\<name>.log

The Path field always takes priority as an explicit full-path override. Directories are created automatically for app-managed paths.

FileModeSingle writes <root>/<name>.log.
FileModeSession writes <root>/sessions/<session-id>/<name>.log and
shares the session directory across loggers in the same process.
FileFormatConsole writes human-readable file output without ANSI colors.

cfg := zerowrap.Config{Level: "info", Format: "console"}
fileCfg := zerowrap.FileConfig{
    Enabled:    true,
    AppName:    "MyApp",
    Mode:       zerowrap.FileModeSession,
    FileFormat: zerowrap.FileFormatConsole,
}

path, err := zerowrap.ResolveLogPath(fileCfg)
if err != nil {
    panic(err)
}
_ = path

log, cleanup, err := zerowrap.NewWithFile(cfg, fileCfg)
if err != nil {
    panic(err)
}
defer cleanup()
_ = log

OpenTelemetry Integration

For OpenTelemetry log bridging, use the optional otel sub-package:

import "github.com/bnema/zerowrap/otel"

// Using global provider
log := zerowrap.New(cfg).Hook(otel.NewHook("my-service"))

// Using custom provider
log := zerowrap.New(cfg).Hook(otel.NewHookWithProvider(provider, "my-service"))

Field Propagation Pattern

The key pattern is to enrich the context with fields EARLY (at request entry points), so all downstream code automatically includes those fields in logs:

// Middleware: add request-scoped fields once
func RequestMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        ctx := r.Context()

        // Define fields early - they propagate to ALL downstream logs
        ctx = zerowrap.CtxWithFields(ctx, map[string]any{
            zerowrap.FieldRequestID: uuid.New().String(),
            zerowrap.FieldMethod:    r.Method,
            zerowrap.FieldPath:      r.URL.Path,
            zerowrap.FieldClientIP:  r.RemoteAddr,
        })

        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

// Handler: add user-specific fields after auth
func UserHandler(ctx context.Context, userID string) error {
    // Add user field - now ALL downstream logs include user_id
    ctx = zerowrap.CtxWithField(ctx, zerowrap.FieldUserID, userID)

    // This log has: request_id, method, path, client_ip, user_id
    zerowrap.FromCtx(ctx).Info().Msg("user authenticated")

    return processUser(ctx)  // ctx carries all fields downstream
}

// Service layer: just use the context, fields are already there
func processUser(ctx context.Context) error {
    log := zerowrap.FromCtx(ctx)

    // This log automatically has ALL parent fields
    log.Debug().Msg("processing user")

    if err := db.Query(ctx); err != nil {
        // Error log includes all fields: request_id, method, path, client_ip, user_id
        return log.WrapErr(err, "database query failed")
    }
    return nil
}

Complete Example

func main() {
    // Create logger with service-level fields
    logger := zerowrap.New(zerowrap.Config{
        Level:  "debug",
        Format: "console",
    })
    ctx := zerowrap.WithCtx(context.Background(), logger)

    // Service-level fields: present in ALL logs
    ctx = zerowrap.CtxWithFields(ctx, map[string]any{
        zerowrap.FieldService: "my-api",
        zerowrap.FieldVersion: "1.0.0",
        zerowrap.FieldEnv:     "production",
    })

    // Start server with enriched context
    server.Start(ctx)
}

func HandleRequest(ctx context.Context, req *Request) error {
    // Request-level fields: added at entry point
    ctx = zerowrap.CtxWithFields(ctx, map[string]any{
        zerowrap.FieldRequestID: req.ID,
        zerowrap.FieldUserID:    req.UserID,
        zerowrap.FieldPath:      req.Path,
    })

    log := zerowrap.FromCtx(ctx)
    log.Info().Msg("request started")

    // All downstream calls use same enriched context
    if err := validateRequest(ctx, req); err != nil {
        return log.WrapErr(err, "validation failed")
    }

    if err := processRequest(ctx, req); err != nil {
        return log.WrapErr(err, "processing failed")
    }

    log.Info().Msg("request completed")
    return nil
}

func validateRequest(ctx context.Context, req *Request) error {
    log := zerowrap.FromCtx(ctx)
    // Log automatically includes: service, version, env, request_id, user_id, path
    log.Debug().Msg("validating request")
    return nil
}

func processRequest(ctx context.Context, req *Request) error {
    log := zerowrap.FromCtx(ctx)
    // Same fields propagated here too
    log.Debug().Msg("processing request")

    if err := callExternalAPI(ctx); err != nil {
        // Error includes all context fields for easy debugging
        return log.WrapErr(err, "external API call failed")
    }
    return nil
}

Index

Constants

View Source
const (
	// Identity & Tracing
	FieldComponent     = "component"
	FieldRequestID     = "request_id"
	FieldTraceID       = "trace_id"
	FieldSpanID        = "span_id"
	FieldCorrelationID = "correlation_id"
	FieldSessionID     = "session_id"
	FieldUserID        = "user_id"

	// HTTP/API
	FieldMethod   = "method"
	FieldPath     = "path"
	FieldStatus   = "status"
	FieldClientIP = "client_ip"

	// Service/Infra
	FieldService = "service"
	FieldVersion = "version"
	FieldHost    = "host"
	FieldEnv     = "env"

	// Operations
	FieldAction    = "action"
	FieldOperation = "operation"
	FieldError     = "error"
	FieldDuration  = "duration_ms"

	// Data
	FieldCount = "count"
	FieldSize  = "size_bytes"

	// Clean Architecture - Layers
	FieldLayer   = "layer"   // domain, usecase, adapter
	FieldUseCase = "usecase" // e.g., "CreateUser", "GetOrder"

	// Clean Architecture - Adapters
	FieldAdapter     = "adapter"      // e.g., "http", "grpc", "postgres"
	FieldAdapterType = "adapter_type" // "in" (driving) or "out" (driven)
	FieldHandler     = "handler"      // HTTP/gRPC handler name
	FieldRepository  = "repository"   // repository name
	FieldGateway     = "gateway"      // external service gateway

	// Database/Storage
	FieldTable    = "table"
	FieldQuery    = "query"
	FieldDatabase = "database"

	// Messaging/Events
	FieldEvent   = "event"
	FieldTopic   = "topic"
	FieldQueue   = "queue"
	FieldPayload = "payload"

	// Entity/Resource
	FieldEntity     = "entity"      // e.g., "user", "order"
	FieldEntityID   = "entity_id"   // ID of the entity being operated on
	FieldEntityType = "entity_type" // type of entity
)

Common field name constants for consistent logging across applications.

Variables

This section is empty.

Functions

func Ctx

func Ctx(ctx context.Context) *zerolog.Logger

Ctx returns a pointer to the underlying zerolog.Logger in context. This is for compatibility with zerolog's Ctx pattern. If no logger is found, returns a pointer to a disabled logger.

func CtxWithField

func CtxWithField(ctx context.Context, key string, value any) context.Context

CtxWithField returns a new context with an enriched logger containing the field.

func CtxWithFields

func CtxWithFields(ctx context.Context, fields map[string]any) context.Context

CtxWithFields returns a new context with an enriched logger containing the fields.

func CtxWithStruct

func CtxWithStruct(ctx context.Context, s any) context.Context

CtxWithStruct returns a new context with an enriched logger containing fields from struct.

func ResolveLogPath added in v1.4.0

func ResolveLogPath(fileCfg FileConfig) (string, error)

ResolveLogPath resolves the log file path from the given FileConfig.

If Path is set, it is returned directly and no other fields are validated. If Path is empty and AppName is empty, an empty path is returned with no error. Otherwise, an app-managed path is built from AppName, defaulting Name to "app", Mode to FileModeSingle, and FileFormat to FileFormatJSON.

func WithCtx

func WithCtx(ctx context.Context, log Logger) context.Context

WithCtx attaches the logger to the context and returns the new context.

func WithCtxZerolog added in v1.1.0

func WithCtxZerolog(ctx context.Context, log zerolog.Logger) context.Context

WithCtxZerolog attaches a zerolog.Logger to the context. Use this when working directly with zerolog loggers.

Types

type Config

type Config struct {
	// Level is the minimum log level (trace, debug, info, warn, error, fatal, panic).
	// Defaults to "info" if empty or invalid.
	Level string

	// Format is the output format: "json" or "console".
	// Defaults to "console" if empty or invalid.
	Format string

	// TimeFormat is the time format string.
	// Defaults to time.RFC3339 if empty.
	TimeFormat string

	// Output is the writer for log output.
	// Defaults to os.Stderr if nil.
	Output io.Writer

	// Caller adds caller information (file:line) to log entries.
	Caller bool
}

Config holds logger configuration options.

type FileConfig

type FileConfig struct {
	// Enabled toggles file logging on/off.
	Enabled bool

	// Path is the full log file path. If set, it takes priority over app-managed path fields.
	Path string

	// AppName enables app-managed log paths when Path is empty.
	AppName string

	// BaseDir overrides the app-managed root directory before AppName.
	BaseDir string

	// Name is the log file name. Defaults to "app". The .log extension is added if missing.
	Name string

	// Mode controls app-managed file layout. Defaults to FileModeSingle.
	Mode FileMode

	// FileFormat controls the file output format. Defaults to FileFormatJSON.
	FileFormat FileFormat

	// MaxSize is the maximum size in megabytes before rotation.
	// Defaults to 100 MB if 0.
	MaxSize int

	// MaxBackups is the maximum number of old log files to retain.
	// Defaults to 3 if 0.
	MaxBackups int

	// MaxAge is the maximum number of days to retain old log files.
	// Defaults to 28 if 0.
	MaxAge int

	// Compress determines if rotated files should be compressed.
	Compress bool
}

FileConfig holds configuration for file-based logging. Use keyed fields when constructing FileConfig values; unkeyed composite literals are not supported as this exported configuration struct may grow.

type FileFormat added in v1.4.0

type FileFormat string

FileFormat controls the format used for the file writer.

const (
	// FileFormatJSON writes JSON lines to the log file.
	FileFormatJSON FileFormat = "json"

	// FileFormatConsole writes human-readable console output to the log file.
	FileFormatConsole FileFormat = "console"
)

type FileMode added in v1.4.0

type FileMode string

FileMode controls how app-managed log file paths are laid out.

const (
	// FileModeSingle writes to one stable file under the app log directory.
	FileModeSingle FileMode = "single"

	// FileModeSession writes under a process-level session directory.
	FileModeSession FileMode = "session"
)

type Logger added in v1.1.0

type Logger struct {
	zerolog.Logger
}

Logger wraps zerolog.Logger with additional convenience methods. All zerolog.Logger methods are available via embedding.

func Default

func Default() Logger

Default returns a sensible default logger writing to stderr with console format.

func FromCtx

func FromCtx(ctx context.Context) Logger

FromCtx extracts the logger from context. If no logger is found, returns a disabled (no-op) logger.

func FromCtxWithField

func FromCtxWithField(ctx context.Context, key string, value any) Logger

FromCtxWithField returns a logger with one additional field.

func FromCtxWithFields

func FromCtxWithFields(ctx context.Context, fields map[string]any) Logger

FromCtxWithFields returns a logger with multiple additional fields.

func FromCtxWithStruct

func FromCtxWithStruct(ctx context.Context, s any) Logger

FromCtxWithStruct returns a logger with fields extracted from struct tags. Uses `log` struct tag for field names, falls back to `json` tag, then field name. Fields tagged with "-" are skipped.

func New

func New(cfg Config) Logger

New creates a new Logger with the given configuration.

func NewFromEnv

func NewFromEnv(prefix string) Logger

NewFromEnv creates a logger configured from environment variables. Uses {prefix}_LOG_LEVEL and {prefix}_LOG_FORMAT. Example: with prefix "MYAPP", reads MYAPP_LOG_LEVEL and MYAPP_LOG_FORMAT.

func NewWithFile

func NewWithFile(cfg Config, fileCfg FileConfig) (Logger, func(), error)

NewWithFile creates a logger that writes to both stderr and a file. Returns the logger, a cleanup function that must be called to close the file, and any error encountered.

func WithHook

func WithHook(log Logger, hook zerolog.Hook) Logger

WithHook returns a new logger with the hook attached.

func (Logger) WithField added in v1.1.0

func (l Logger) WithField(key string, value any) Logger

WithField returns a new Logger with the field added.

func (Logger) WithFields added in v1.1.0

func (l Logger) WithFields(fields map[string]any) Logger

WithFields returns a new Logger with the fields added.

func (Logger) WithStruct added in v1.1.0

func (l Logger) WithStruct(s any) Logger

WithStruct returns a new Logger with fields extracted from struct tags.

func (Logger) WrapErr added in v1.1.0

func (l Logger) WrapErr(err error, msg string) error

WrapErr logs the error and returns a wrapped error with the message. Uses fmt.Errorf with %w for unwrapping support.

log := zerowrap.FromCtx(ctx)
if err != nil {
    return log.WrapErr(err, "failed to connect")
}

func (Logger) WrapErrWithFields added in v1.1.0

func (l Logger) WrapErrWithFields(err error, msg string, fields map[string]any) error

WrapErrWithFields logs with fields and returns a wrapped error.

if err != nil {
    return log.WrapErrWithFields(err, "query failed", map[string]any{"id": id})
}

func (Logger) WrapErrf added in v1.1.0

func (l Logger) WrapErrf(err error, format string, args ...any) error

WrapErrf logs the error and returns a wrapped error with a formatted message.

if err != nil {
    return log.WrapErrf(err, "failed to connect to %s", host)
}

Directories

Path Synopsis
Package otel provides OpenTelemetry log bridging for zerowrap.
Package otel provides OpenTelemetry log bridging for zerowrap.

Jump to

Keyboard shortcuts

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