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.
- 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
- The package intentionally uses
zerologas its concrete backend and exposesEventas an alias ofzerolog.Eventfor 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(...), andWithDisabled(...)are nil-safe helpers for request-scoped logging flows.- Global helpers such as
log.Info(ctx)andlog.Err(err, ctx)honor the logger stored in the context. log.Err(err, ctx)automatically addserror_id,error_chain,error_tags, anderror_detailfor funk errors.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.
- Typical request flow: use
WithLogger+WithFields, then calllog.Info(ctx)/log.Err(err, ctx). - Need direct logger chaining from context: use
log.FromCtx(ctx). - Low-level explicit construction: keep
CreateCtx/CreateFieldsCtxfor places where you want stricter, non-helper semantics.
go get github.com/pubgo/funk/v2/logimport "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")
}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")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")WithLogger: bind a request-scoped logger to contextWithFields: append request-scoped fieldslog.Info(ctx)/log.Err(err, ctx): the common path for emitting logsFromCtx: fetch the effective logger when you need more chainingCreateCtx/CreateFieldsCtx: lower-level explicit constructors
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
}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-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-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")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)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)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")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)andNewStd(nil)safely fall back to the global logger.StdLogger.Println(...)follows standard-library-style spacing between arguments.
// 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")| 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 |
| 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 |
| 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 |
- 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/WithFieldsto create derived loggers instead of expecting in-place mutation - Prefer Context For Request Data: Put request-specific values in
context.Contextrather than module logger defaults
- The package keeps
zerologas 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.gofor context merging and context-aware info logging so future changes can be measured quickly.
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{}
}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")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)
}