Logging Module
The logging module provides a high-performance, structured logging system based on zerolog with support for context-aware logging, error detail capture, and modular logger management.
Features
- High-Performance Logging: Based on zerolog for minimal overhead
- Structured Logging: JSON-formatted logs with automatic field extraction
- Context-Aware Logging: Request-scoped logging with automatic field injection
- Error Detail Capture: Automatic error enrichment with stack traces and metadata
- Modular Loggers: Namespaced loggers for different components
- Multiple Output Formats: Console and JSON output formats
Design Notes
- The package intentionally uses
zerolog as its concrete backend and exposes Event as an alias of zerolog.Event for a fluent, low-overhead API.
Logger.WithFields(...) returns a derived logger and does not mutate the original logger.
WithLogger(ctx, ...) is the convenience entry point for attaching a logger to a context.
WithFields(ctx, ...) is the convenience entry point for attaching request-scoped fields to a context.
CreateFieldsCtx(...) snapshots the initial field map so later mutations to the caller's map do not leak into logging context.
WithLogger(...), WithFields(...), GetFieldsFromCtx(...), and WithDisabled(...) are nil-safe helpers for request-scoped logging flows.
- Global helpers such as
log.Info(ctx) and log.Err(err, ctx) honor the logger stored in the context.
FromCtx(ctx) is the ergonomic way to fetch the effective logger from a context when you need to keep chaining logger methods.
UpdateFieldsCtx(...) returns a new context and does not mutate field maps already stored in parent contexts.
- When logger fields and context fields use the same key, context fields win. This lets request-scoped metadata override module defaults safely.
Recommended Patterns
- Typical request flow: use
WithLogger + WithFields, then call log.Info(ctx) / log.Err(err, ctx).
- Need direct logger chaining from context: use
log.FromCtx(ctx).
- Low-level explicit construction: keep
CreateCtx / CreateFieldsCtx for places where you want stricter, non-helper semantics.
Installation
go get github.com/pubgo/funk/v2/log
Quick Start
Basic Logging
import "github.com/pubgo/funk/v2/log"
// Use the global logger
log.Info().Msg("Application started")
// Log with fields
log.Info().Str("component", "server").Int("port", 8080).Msg("Server listening")
// Error logging with automatic error detail capture
err := someOperation()
if err != nil {
log.Err(err).Msg("Operation failed")
}
Modular Logging
import "github.com/pubgo/funk/v2/log"
// Create a module-specific logger
logger := log.GetLogger("database")
// Use the module logger
logger.Info().Msg("Connecting to database")
logger.Err(dbError).Msg("Database connection failed")
Context-Aware Logging
import (
"context"
"github.com/pubgo/funk/v2/log"
)
// Create a context with fields
ctx := context.Background()
ctx = log.WithLogger(ctx, log.GetLogger("request"))
ctx = log.WithFields(ctx, map[string]any{
"request_id": "abc123",
"user_id": 42,
})
// Log with context (fields are automatically included)
log.Info(ctx).Msg("Processing request")
// Global helpers also use the context-bound logger automatically.
// If you need logger chaining, fetch the effective logger from context.
log.FromCtx(ctx).WithName("db").Info(ctx).Msg("Query finished")
Context Helper Cheat Sheet
WithLogger: bind a request-scoped logger to context
WithFields: append request-scoped fields
log.Info(ctx) / log.Err(err, ctx): the common path for emitting logs
FromCtx: fetch the effective logger when you need more chaining
CreateCtx / CreateFieldsCtx: lower-level explicit constructors
Core Concepts
Logger Interface
The module provides a Logger interface for consistent logging operations:
type Logger interface {
Debug(ctx ...context.Context) *zerolog.Event
Info(ctx ...context.Context) *zerolog.Event
Warn(ctx ...context.Context) *zerolog.Event
Error(ctx ...context.Context) *zerolog.Event
Err(err error, ctx ...context.Context) *zerolog.Event
// ... other methods
}
Global Logger
A global logger is available for simple use cases:
// Direct logging functions
log.Debug().Msg("Debug message")
log.Info().Msg("Info message")
log.Warn().Msg("Warning message")
log.Error().Msg("Error message")
log.Fatal().Msg("Fatal message")
log.Panic().Msg("Panic message")
Module Loggers
Module-specific loggers provide namespacing and component isolation:
// Create a logger for a specific module
dbLogger := log.GetLogger("database")
cacheLogger := log.GetLogger("cache")
// Use module-specific loggers
dbLogger.Info().Msg("Database operation")
cacheLogger.Info().Msg("Cache operation")
Context Integration
Context-aware logging automatically includes relevant fields:
// Add fields to context
ctx = log.WithFields(ctx, map[string]any{
"trace_id": generateTraceID(),
"span_id": generateSpanID(),
})
// Log with context (fields are automatically included)
log.Info(ctx).Msg("Processing with trace context")
Advanced Usage
Custom Logger Configuration
import (
"os"
"github.com/rs/zerolog"
"github.com/pubgo/funk/v2/log"
)
// Create a custom zerolog logger
customLogger := zerolog.New(os.Stdout).With().Timestamp().Logger()
// Set as global logger
log.SetLogger(&customLogger)
Logger Levels
import "github.com/rs/zerolog"
// Set global log level
zerolog.SetGlobalLevel(zerolog.InfoLevel)
// Create logger with specific level
logger := log.GetLogger("module").WithLevel(zerolog.DebugLevel)
Error Detail Capture
The module automatically enriches error logs with detailed information:
import "github.com/pubgo/funk/v2/errors"
// Create an error with metadata
err := errors.New("database connection failed", errors.Tags{
"host": "localhost",
"port": 5432,
})
// Log error (automatically includes error details)
log.Err(err).Msg("Connection attempt failed")
Slog / Std Adapter Usage
import (
"log/slog"
"github.com/pubgo/funk/v2/log"
)
ctx := log.WithLogger(nil, log.GetLogger("request"))
// slog adapter
slogger := slog.New(log.NewSlog(log.FromCtx(ctx)))
slogger.Info("handled request")
// std-style adapter
std := log.NewStd(log.FromCtx(ctx))
std.Println("request", "finished")
Notes:
NewSlog(nil) and NewStd(nil) safely fall back to the global logger.
StdLogger.Println(...) follows standard-library-style spacing between arguments.
Event Building
// Build complex log events
log.Info().
Str("component", "auth").
Int("user_id", 12345).
Dict("metadata", log.NewEvent().
Str("ip", "192.168.1.1").
Str("user_agent", "Mozilla/5.0")).
Msg("User login")
API Reference
Core Functions
| Function |
Description |
FromCtx(ctx context.Context) |
Get the effective logger from context |
GetFromCtx(ctx context.Context, loggers ...Logger) |
Low-level logger lookup with optional fallback |
GetLogger(names ...string) |
Get a module-specific logger |
SetLogger(log *zerolog.Logger) |
Set the global logger |
CreateCtx(ctx context.Context, ll Logger) |
Create a context with an explicit logger |
WithLogger(ctx context.Context, ll Logger) |
Attach a logger to context |
WithFields(ctx context.Context, fields Fields) |
Add fields to context |
CreateFieldsCtx(ctx context.Context, evt Fields) |
Create a context with initial fields |
UpdateFieldsCtx(ctx context.Context, fields Fields) |
Add or override fields in a derived context |
GetFieldsFromCtx(ctx context.Context) |
Extract fields from context |
Logging Functions
| Function |
Description |
Debug(ctx ...context.Context) |
Start a debug level log event |
Info(ctx ...context.Context) |
Start an info level log event |
Warn(ctx ...context.Context) |
Start a warning level log event |
Error(ctx ...context.Context) |
Start an error level log event |
Err(err error, ctx ...context.Context) |
Start an error log event with error detail |
Fatal(ctx ...context.Context) |
Start a fatal log event |
Panic(ctx ...context.Context) |
Start a panic log event |
Utility Functions
| Function |
Description |
NewEvent() |
Create a new dictionary event |
RecordErr(logs ...Logger) |
Create an error recording function |
Output(w io.Writer) |
Create logger with custom output |
OutputWriter(w func([]byte) (int, error)) |
Create logger with custom writer function |
NewSlog(log Logger) |
Create a slog.Handler backed by log.Logger |
NewStd(log Logger) |
Create a std-style logger adapter |
Best Practices
- Use Module Loggers: Create module-specific loggers for better organization
- Include Context: Use context-aware logging for request tracing
- Structure Logs: Use structured logging with relevant fields
- Appropriate Levels: Use appropriate log levels for different scenarios
- Avoid Sensitive Data: Never log passwords, tokens, or other sensitive information
- Error Detail: Always log errors with sufficient context for debugging
- Treat Loggers as Immutable: Chain
WithName / WithFields to create derived loggers instead of expecting in-place mutation
- Prefer Context For Request Data: Put request-specific values in
context.Context rather than module logger defaults
- The package keeps
zerolog as the execution engine to preserve its allocation profile and fluent builder API.
- Recent regression tests ensure context-field merging does not leak state into reusable loggers.
- Benchmarks are included in
z_bench_test.go for context merging and context-aware info logging so future changes can be measured quickly.
Integration Patterns
With Error Handling
import (
"github.com/pubgo/funk/v2/log"
"github.com/pubgo/funk/v2/errors"
"github.com/pubgo/funk/v2/result"
)
func processRequest() result.Error {
err := someOperation()
if err != nil {
// Log error with full detail
log.Err(err).Msg("Request processing failed")
return result.ErrOf(err)
}
return result.Error{}
}
With Configuration
import (
"github.com/pubgo/funk/v2/log"
"github.com/pubgo/funk/v2/config"
)
type LogConfig struct {
Level string `yaml:"level"`
Format string `yaml:"format"`
}
cfg := config.Load[LogConfig]()
logger := log.GetLogger("app")
logger.Info().
Str("log_level", cfg.T.Level).
Str("log_format", cfg.T.Format).
Msg("Logging configured")
With Context and Tracing
import (
"context"
"github.com/pubgo/funk/v2/log"
"github.com/google/uuid"
)
func handleRequest(ctx context.Context) {
// Add request ID to context
requestID := uuid.New().String()
ctx = log.WithLogger(ctx, log.GetLogger("request"))
ctx = log.WithFields(ctx, map[string]any{
"request_id": requestID,
})
// Log with request context
log.Info(ctx).Msg("Handling request")
// Continue chaining on the effective context logger when needed.
log.FromCtx(ctx).WithName("downstream").Info(ctx).Msg("Calling processData")
// Pass context to downstream functions
processData(ctx)
}