Skip to main content
Velo provides a clean, developer-friendly API for structured logging. This guide covers the essential logging methods and how to use them effectively.

Creating a logger

The simplest way to get started is with the New function, which creates a logger writing to the provided io.Writer:
logger := velo.New(os.Stderr)
defer logger.Close() // Always flush the buffer before exit
For quick debugging or simple applications, you can use the global default logger:
velo.Info("application started")
velo.Debug("debug information", "version", "1.0.0")
Always call Close() on your logger before your application exits to ensure all buffered logs are written. Failing to close the logger may result in lost log entries.

Log levels

Velo supports standard log levels from most to least verbose:
1

Debug

Use for detailed diagnostic information during development:
logger.Debug("processing request", "endpoint", "/api/users")
2

Info

Use for general informational messages:
logger.Info("server started", "port", 8080)
3

Warn

Use for potentially harmful situations:
logger.Warn("high memory usage", "usage", "85%")
4

Error

Use for error events that might still allow the application to continue:
logger.Error("failed to connect to database", "error", err)
5

Panic

Logs a message then calls panic():
logger.Panic("critical failure", "component", "auth")
6

Fatal

Logs a message then calls os.Exit(1):
logger.Fatal("cannot recover", "reason", "config missing")

Logging with fields

Velo accepts loosely-typed key-value pairs for structured logging:
logger.Info("failed to fetch URL",
  "url", url,
  "attempt", 3,
  "backoff", time.Second,
)
Fields are always provided as alternating key-value pairs. The keys should be strings, and values can be any type.
For maximum performance and zero allocations, use the strongly-typed fields API covered in the typed fields guide.

Formatted logging

For situations where you need string formatting, use the *f methods:
logger.Infof("processing %d items from %s", count, source)
logger.Errorf("connection failed after %d attempts", retries)
Formatted logging methods use fmt.Sprintf internally, which incurs allocation overhead. Avoid using these in performance-critical paths.

Dynamic log levels

You can change the minimum log level at runtime:
logger.SetLevel(velo.DebugLevel) // Show all debug logs
logger.SetLevel(velo.WarnLevel)  // Only show warnings and above
The logger discards any messages below the configured level, which helps reduce overhead in production.

Default logger operations

Velo provides package-level functions that operate on the global default logger:
velo.Info("using default logger")
velo.SetLevel(velo.ErrorLevel)
velo.Debug("this won't appear") // Below ErrorLevel
You can replace the default logger:
customLogger := velo.NewWithOptions(os.Stdout, velo.Options{
  ReportTimestamp: true,
  Formatter: velo.JSONFormatter,
})
velo.SetDefault(customLogger)

Flushing logs

For asynchronous loggers, use Sync() to flush buffered entries immediately:
logger.Error("critical error occurred", "error", err)
logger.Sync() // Ensure the error is written immediately
This is particularly useful before application shutdown or after logging critical errors.

Next steps

Typed fields

Use strongly-typed fields for zero-allocation logging

Configuration

Configure formatters, timestamps, and advanced options

Build docs developers (and LLMs) love