velocity

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Apr 4, 2026 License: MIT Imports: 17 Imported by: 0

README

Velocity
CI Go Reference Go Report Card Release License Go Version

Fast, allocation optimised structured logging for Go with rich terminal output for heavy log presentation and logging. Battle tested and hardened through years of heavy log use. Extracted from TensorFoundry's FoundryOS where it powers all CLI logging.

We used this instead of pTerm for speed and efficiency, which was used previously in tools like Olla, but we hit the limits with FoundryOS.

Install

go get github.com/tensorfoundrylabs/velocity

Quick Start

log := velocity.New(os.Stdout)
log.Info("server started", velocity.String("addr", ":8080"), velocity.Int("workers", 4))

Or use a preset:

log := velocity.NewDevelopment()                                // coloured console, debug level
log := velocity.NewWithBuilder(velocity.PresetProduction())     // structured JSON, info level

Packages

Velocity is split into three packages:

import (
    "github.com/tensorfoundrylabs/velocity"              // core logging, writers, config, themes
    "github.com/tensorfoundrylabs/velocity/pretty"       // CLI display: boxes, panels, banners, tables, trees, progress
    velocityslog "github.com/tensorfoundrylabs/velocity/slog"  // log/slog bridge
)
Package Description
velocity Core logger, fields (String, Int, Error, Float64, Bool, Duration, Time, Stringer, Bytes), writers (console, JSON, multi, ring buffer), config, themes, templates, buffers, pools
velocity/pretty Pretty, Box, Panel, Banner, Table, Tree, Bullet, KeyValue, SystemInfo, TreeItem, ProgressBar, Spinner, MultiProgress
velocity/slog Handler, NewHandler, NewLogger (package name: velocityslog)

Features

  • Zero-alloc fields on hot paths. Typed constructors (String, Int, Float64, Bool, Duration, Error) use unsafe.Pointer + int64 storage, no interface{} boxing
  • Entry pooling via sync.Pool with atomic ref counting and CAS-based return. Tiered buffer pools (512B to 32KB)
  • Atomic level control. Single atomic load per log call; entries below threshold never allocate
  • Log sampling. CountSampler logs first N, then every Mth. Checked before pool acquisition
  • Caller capture. WithCaller(true) renders file:line in JSON, console, and ring buffer writers
  • 4 colour themes. Night Owl (RGB), Solarized, Dracula, Nord (256-colour). ANSI codes pre-cached at theme init
  • 4 templates. Default (badge), Simple (text), Minimal (message only), JSON (RFC3339Nano)
  • 5 presets. Development, Production, Container, Testing, HighPerformance
  • Pretty printing (in velocity/pretty). Section, Box, Panel, Banner, Bullet, KeyValue, SystemInfo. Unicode-safe alignment
  • Tree display (in velocity/pretty). Hierarchical data with box-drawing characters
  • Tables (in velocity/pretty). Auto-width columns with box-drawing borders
  • Progress (in velocity/pretty). ProgressBar, Spinner (5 animation styles), MultiProgress. Thread-safe with CAS-guarded stop
  • Child loggers. With() for scoped fields, WithTemplate() for output format. Both inherit writers, sampler, base fields
  • Context integration. NewContext(), FromContext(), ContextWithFields()
  • Dynamic writers. AddWriter()/RemoveWriter() at runtime, thread-safe. Workers close their own writer on shutdown
  • Ring buffer writer. Lock-free CAS ring buffer with batched flushing, bounded spins, min size enforcement
  • JSON writer. Hand-rolled serialisation, handles NaN/Infinity, base64 bytes, proper escaping. No encoding/json
  • Typed nil safety. Error and Stringer constructors catch typed nils via reflect to prevent panics
  • Nil-safe. Every public method handles nil receivers gracefully
  • slog bridge (in velocity/slog). NewHandler implements log/slog.Handler for incremental adoption. WithAttrs/WithGroup with dotted key prefixes. Pre-converted fields, cached group prefix
  • Testable. Overridable FatalHandler, NewForTesting() constructor

Performance

Comparative benchmarks

Velocity was built because we needed something faster and lighter than pterm for high-volume logging in FoundryOS. Here's how it stacks up against the popular Go logging libraries (AMD Ryzen 9 5950X, Go 1.24, writing to io.Discard):

Library Info (no fields) Info (3 fields) With + Info Disabled level
velocity 31 ns / 0 alloc 67 ns / 1 alloc 186 ns / 4 alloc 41 ns / 0 alloc
zerolog 89 ns / 0 alloc 204 ns / 0 alloc 422 ns / 2 alloc 10 ns / 0 alloc
zap 240 ns / 0 alloc 525 ns / 1 alloc 1319 ns / 6 alloc 9 ns / 0 alloc
slog 663 ns / 0 alloc 1666 ns / 4 alloc 1684 ns / 11 alloc 10 ns / 0 alloc
charmbracelet/log 4 ns / 0 alloc 6 ns / 0 alloc 2618 ns / 5 alloc 4 ns / 0 alloc
pterm 12926 ns / 65 alloc 25334 ns / 144 alloc 13125 ns / 65 alloc 19 ns / 0 alloc

Velocity is ~3x faster than zerolog and ~8x faster than zap on the hot logging path. charmbracelet/log's near-zero numbers are from short-circuiting format work when writing to non-TTY output; its With cost (2618 ns) shows the real overhead. pterm is a display library first, and its allocation profile reflects that.

Realistic workload benchmarks

These cover the scenarios that actually matter in production: accumulated context, mixed field types, large messages, and parallel contention.

Scenario velocity zerolog zap slog
Accumulated context (10 fields) 45 ns / 0 alloc 99 ns / 0 alloc 344 ns / 0 alloc 672 ns / 0 alloc
Mixed field types (8 types) 153 ns / 4 alloc 799 ns / 2 alloc 1307 ns / 1 alloc 2481 ns / 8 alloc
Error field 96 ns / 1 alloc 136 ns / 0 alloc 510 ns / 1 alloc 912 ns / 1 alloc
Large message (1 KB) 43 ns / 0 alloc 419 ns / 0 alloc 1509 ns / 0 alloc 2255 ns / 1 alloc
10 inline fields 117 ns / 3 alloc 383 ns / 0 alloc 1159 ns / 1 alloc 3170 ns / 10 alloc
Parallel (16 goroutines) 53 ns / 1 alloc 22 ns / 0 alloc 150 ns / 1 alloc 279 ns / 0 alloc

zerolog wins the parallel benchmark thanks to its lock-free event chaining design. Velocity wins everything else.

Run the benchmarks yourself

# Comparative benchmarks against other libraries
make bench-compare

# Quick single-pass comparison
make bench-compare-short

# Velocity internal benchmarks only
go test -bench=. -benchmem -count=3 ./...

The benchmark suite lives in benchmarks/ as a separate Go module. Add new libraries by appending to the libraries slice in benchmarks/bench_test.go.

Internal benchmarks

Operation ns/op allocs/op
Info, no fields 31 0
Info, 5 pre-built fields 47 0
Info, 10 fields 56 0
Level check (disabled) 2.7 0
Sampler check 9.0 0
Entry pool round-trip 21 0
Int field construction 1.8 0
ConsoleWriter, 5 fields 694 3
JSONWriter, 5 fields 949 1
JSONWriter, parallel 153 1
slog handler, 3 string attrs 490 6
Parallel Info, 3 fields 46 0

Run internal benchmarks: go test -bench=. -benchmem -count=3 ./...

Presets

Preset Output Level Use Case
PresetDevelopment Coloured console Debug Local dev
PresetProduction JSON Info Structured log aggregation
PresetContainer JSON to stdout Info Docker/K8s
PresetTesting Provided writer Debug Test harnesses
PresetHighPerformance JSON to stderr Info High-volume with sampling

Integration

log/slog bridge

Use velocity as the backend for Go's standard structured logging:

import velocityslog "github.com/tensorfoundrylabs/velocity/slog"

logger := velocity.NewDevelopment()
slog.SetDefault(velocityslog.NewLogger(logger))

slog.Info("request handled", "method", "GET", "status", 200, "duration", 42*time.Millisecond)

Groups produce dotted keys: slog.WithGroup("server").With("host", "localhost") renders as server.host.

Pretty printing

Use velocity/pretty for rich CLI output:

import "github.com/tensorfoundrylabs/velocity/pretty"

p := pretty.New(os.Stdout, velocity.ThemeNightOwl)
p.Box("Deploy Complete", "All services running")
p.Banner("v2.1.0 - Production release")

Log rotation with lumberjack

Velocity's Config.ConsoleOutput and Config.StructuredOutput accept any io.Writer:

rotator := &lumberjack.Logger{Filename: "/var/log/app.log", MaxSize: 500, Compress: true}
cfg := velocity.DefaultProductionConfig()
cfg.StructuredOutput = rotator
log := velocity.NewWithConfig(cfg)

Dependencies

One: golang.org/x/term for TTY detection. No other external dependencies. Zero third-party test dependencies.

Similar Libraries

  • pTerm - Our favourite visually pleasing library for terminal output we modeled the styles on, but Velocity offers speed and efficiency for high-volume logging.
  • logrus - Another popular structured logging library, but Velocity is designed for speed and efficiency in CLI applications.

Licence

MIT

Documentation

Overview

Package velocity is a high-performance structured logging library for Go CLI applications.

Velocity provides rich terminal output with themed colours, structured fields, tree displays, tables, progress indicators, and JSON output — all with zero-allocation field encoding on hot paths.

Quick start:

log := velocity.New(os.Stdout)
log.Info("server started", velocity.String("addr", ":8080"))

For more control, use the builder or functional options:

log := velocity.NewWithOptions(
    velocity.WithLevel(velocity.LevelDebug),
    velocity.WithTheme(velocity.ThemeDracula),
)

Velocity includes five presets for common scenarios:

  • PresetDevelopment: verbose, coloured console output
  • PresetProduction: structured JSON, info level and above
  • PresetContainer: JSON with container-friendly defaults
  • PresetTesting: minimal output for test harnesses
  • PresetHighPerformance: sampling and ring-buffer batching

Package velocity provides high-performance structured logging for Go applications.

Index

Constants

View Source
const (
	HintConsoleLog    = 256
	HintStructuredLog = 1024
	HintStackTrace    = 4096
)
View Source
const (
	LevelStrDebug = "DEBUG"
	LevelStrInfo  = "INFO"
	LevelStrWarn  = "WARN"
	LevelStrError = "ERROR"
	LevelStrFatal = "FATAL"
	LevelStrOff   = "OFF"
)
View Source
const (
	DefaultRingBufferSize = 1024
	DefaultBatchSize      = 64
	DefaultFlushInterval  = 10 * time.Millisecond
)
View Source
const Reset = "\033[0m"

Variables

View Source
var ErrWriterClosed = errors.New("writer is closed")
View Source
var TemplateDefault = &Template{
	showTime:         true,
	timeFormat:       time.RFC3339,
	showLevel:        true,
	levelStyle:       LevelStyleBadge,
	showMessage:      true,
	showFields:       true,
	fieldSep:         " ",
	fieldPairSep:     ": ",
	fieldDisplayMode: FieldDisplayInline,
	useColours:       true,
}
View Source
var TemplateJSON = &Template{
	showTime:    true,
	timeFormat:  time.RFC3339Nano,
	showLevel:   true,
	levelStyle:  LevelStyleText,
	showMessage: true,
	showFields:  true,
	useColours:  false,
}
View Source
var TemplateMinimal = &Template{
	showTime:         false,
	showLevel:        false,
	showMessage:      true,
	showFields:       false,
	fieldDisplayMode: FieldDisplayInline,
	useColours:       false,
}
View Source
var TemplateSimple = &Template{
	showTime:         false,
	showLevel:        true,
	levelStyle:       LevelStyleText,
	showMessage:      true,
	showFields:       true,
	fieldSep:         " ",
	fieldPairSep:     ": ",
	fieldDisplayMode: FieldDisplayInline,
	useColours:       true,
}
View Source
var ThemeDracula = cachedTheme(Theme{
	Name:             "Dracula",
	DebugColour:      Colour256(141),
	InfoColour:       Colour256(81),
	WarnColour:       Colour256(228),
	ErrorColour:      Colour256(212),
	FatalColour:      Colour256(196),
	TimestampColour:  Colour256(59),
	MessageColour:    Colour256(231),
	FieldKeyColour:   Colour256(59),
	FieldValColour:   Colour256(188),
	ErrorValColour:   Colour256(212),
	StatusOKColour:   Colour256(84),
	StatusFailColour: Colour256(212),
	StatusWarnColour: Colour256(228),
	StatusInfoColour: Colour256(81),
	TableHeader:      Colour256(87),
})
View Source
var ThemeNightOwl = cachedTheme(Theme{
	Name:             "Night Owl",
	DebugColour:      RGB(0xC7, 0x92, 0xEA),
	InfoColour:       RGB(0x82, 0xAA, 0xFF),
	WarnColour:       RGB(0xFF, 0xCB, 0x6B),
	ErrorColour:      RGB(0xFF, 0x55, 0x72),
	FatalColour:      RGB(0xFF, 0x00, 0x00),
	TimestampColour:  RGB(0x7E, 0x8E, 0xA6),
	MessageColour:    RGB(0xE0, 0xE0, 0xE0),
	FieldKeyColour:   RGB(0x7E, 0x8E, 0xA6),
	FieldValColour:   RGB(0xD3, 0xD3, 0xD3),
	ErrorValColour:   RGB(0xFF, 0x55, 0x72),
	StatusOKColour:   RGB(0x80, 0xD4, 0xAA),
	StatusFailColour: RGB(0xFF, 0x55, 0x72),
	StatusWarnColour: RGB(0xFF, 0xCB, 0x6B),
	StatusInfoColour: RGB(0x82, 0xAA, 0xFF),
	TableHeader:      RGB(0x7F, 0xD3, 0xFF),
})
View Source
var ThemeNord = cachedTheme(Theme{
	Name:             "Nord",
	DebugColour:      Colour256(139),
	InfoColour:       Colour256(109),
	WarnColour:       Colour256(180),
	ErrorColour:      Colour256(191),
	FatalColour:      Colour256(167),
	TimestampColour:  Colour256(59),
	MessageColour:    Colour256(216),
	FieldKeyColour:   Colour256(59),
	FieldValColour:   Colour256(188),
	ErrorValColour:   Colour256(191),
	StatusOKColour:   Colour256(108),
	StatusFailColour: Colour256(167),
	StatusWarnColour: Colour256(180),
	StatusInfoColour: Colour256(109),
	TableHeader:      Colour256(110),
})
View Source
var ThemeSolarized = cachedTheme(Theme{
	Name:             "Solarized",
	DebugColour:      Colour256(61),
	InfoColour:       Colour256(33),
	WarnColour:       Colour256(136),
	ErrorColour:      Colour256(160),
	FatalColour:      Colour256(124),
	TimestampColour:  Colour256(8),
	MessageColour:    Colour256(7),
	FieldKeyColour:   Colour256(8),
	FieldValColour:   Colour256(7),
	ErrorValColour:   Colour256(160),
	StatusOKColour:   Colour256(64),
	StatusFailColour: Colour256(160),
	StatusWarnColour: Colour256(136),
	StatusInfoColour: Colour256(33),
	TableHeader:      Colour256(37),
})

Functions

func AppendString

func AppendString(b []byte, s string) []byte

func ByteSlice

func ByteSlice(s string) []byte

ByteSlice provides zero-copy string to byte conversion. SAFETY: The returned slice must not be modified.

func ContextWithFields

func ContextWithFields(ctx context.Context, fields ...Field) context.Context

ContextWithFields returns a new context with additional fields that will be prepended to any logger retrieved via FromContext. Fields accumulate across middleware layers.

func FieldValueToString

func FieldValueToString(f Field) string

FieldValueToString converts a Field value to string representation. Handles all Field types without allocations where possible.

This is shared logic used by both StreamingWriter and external adapters to avoid duplication (DRY). The function doesn't depend on any struct state, making it ideal as a package-level utility.

func GetBuffer

func GetBuffer(hint int) *bytes.Buffer

func GetTemplateBuffer

func GetTemplateBuffer() *bytes.Buffer

func IsTerminalWriter

func IsTerminalWriter(w io.Writer) bool

IsTerminalWriter reports whether w is a terminal, using term.IsTerminal when possible. Used to auto-detect colour support.

func NewContext

func NewContext(ctx context.Context, l *Logger) context.Context

NewContext returns a new context carrying the logger.

func PutBuffer

func PutBuffer(buf *bytes.Buffer)

func PutFieldSlice

func PutFieldSlice(fields []Field)

PutFieldSlice returns a field slice to the pool for reuse. The slice is reset to avoid data leakage.

func PutTemplateBuffer

func PutTemplateBuffer(buf *bytes.Buffer)

func UnsafeString

func UnsafeString(b []byte) string

UnsafeString provides zero-copy byte to string conversion. SAFETY: The byte slice must not be modified after conversion.

Types

type AtomicLevel

type AtomicLevel struct {
	// contains filtered or unexported fields
}

AtomicLevel provides thread-safe level management with zero-cost reads.

func NewAtomicLevel

func NewAtomicLevel(l Level) *AtomicLevel

func (*AtomicLevel) CompareAndSwap

func (al *AtomicLevel) CompareAndSwap(old, newLevel Level) bool

func (*AtomicLevel) Enabled

func (al *AtomicLevel) Enabled(l Level) bool

Enabled checks if the given level is enabled. This is the critical path - called on every log attempt.

func (*AtomicLevel) Level

func (al *AtomicLevel) Level() Level

func (*AtomicLevel) SetLevel

func (al *AtomicLevel) SetLevel(l Level)

type BufferPool

type BufferPool struct {
	// contains filtered or unexported fields
}

BufferPool manages byte buffers of various sizes for efficient logging output. Using a tiered pool reduces garbage collection pressure during high-throughput logging.

func NewBufferPool

func NewBufferPool() *BufferPool

func (*BufferPool) Get

func (bp *BufferPool) Get(hint int) *bytes.Buffer

Get retrieves a buffer from the pool, selecting the size based on the hint.

func (*BufferPool) Put

func (bp *BufferPool) Put(buf *bytes.Buffer)

Put returns a buffer to the appropriate pool based on its capacity. Buffers are not resized to preserve their grown capacity for reuse.

type Builder

type Builder struct {
	// contains filtered or unexported fields
}

func ApplyOptions

func ApplyOptions(b *Builder, opts ...Option) *Builder

func NewConfig

func NewConfig() *Builder

func PresetContainer

func PresetContainer() *Builder

func PresetDevelopment

func PresetDevelopment() *Builder

func PresetHighPerformance

func PresetHighPerformance() *Builder

func PresetProduction

func PresetProduction() *Builder

func PresetTesting

func PresetTesting(w io.Writer) *Builder

func (*Builder) Build

func (b *Builder) Build() (*Config, error)

func (*Builder) Clone

func (b *Builder) Clone() *Builder

func (*Builder) DisableColour

func (b *Builder) DisableColour() *Builder

func (*Builder) MustBuild

func (b *Builder) MustBuild() *Config

func (*Builder) MustWithDisplayTimezone

func (b *Builder) MustWithDisplayTimezone(tz string) *Builder

func (*Builder) WithBufferSize

func (b *Builder) WithBufferSize(size int) *Builder

func (*Builder) WithColour

func (b *Builder) WithColour(enabled bool) *Builder

func (*Builder) WithDisplayTimezone

func (b *Builder) WithDisplayTimezone(tz string) (*Builder, error)

WithDisplayTimezone sets the timezone for displaying timestamps in console output. Logs are always stored in UTC internally, but this controls how they're displayed.

func (*Builder) WithFatalHandler

func (b *Builder) WithFatalHandler(fn FatalHandler) *Builder

func (*Builder) WithFieldDisplayMode

func (b *Builder) WithFieldDisplayMode(mode FieldDisplayMode) *Builder

func (*Builder) WithFieldPoolSize

func (b *Builder) WithFieldPoolSize(size int) *Builder

func (*Builder) WithFormat

func (b *Builder) WithFormat(format Format) *Builder

func (*Builder) WithLevel

func (b *Builder) WithLevel(level Level) *Builder

func (*Builder) WithOutput

func (b *Builder) WithOutput(w io.Writer) *Builder

func (*Builder) WithSampling

func (b *Builder) WithSampling(initial, thereafter uint32) *Builder

func (*Builder) WithStructuredLevel

func (b *Builder) WithStructuredLevel(level Level) *Builder

func (*Builder) WithStructuredOutput

func (b *Builder) WithStructuredOutput(w io.Writer) *Builder

func (*Builder) WithTheme

func (b *Builder) WithTheme(theme *Theme) *Builder

func (*Builder) WithTimeFormat

func (b *Builder) WithTimeFormat(format string) *Builder

type BytesBuffer

type BytesBuffer struct {
	// contains filtered or unexported fields
}

func NewBytesBuffer

func NewBytesBuffer(buf *bytes.Buffer) *BytesBuffer

func (*BytesBuffer) AppendTime

func (b *BytesBuffer) AppendTime(t time.Time, layout string)

AppendTime writes a formatted timestamp directly into the buffer, avoiding the intermediate string allocation from time.Format.

func (*BytesBuffer) Bytes

func (b *BytesBuffer) Bytes() []byte

func (*BytesBuffer) String

func (b *BytesBuffer) String() string

func (*BytesBuffer) Write

func (b *BytesBuffer) Write(p []byte) (int, error)

func (*BytesBuffer) WriteByte

func (b *BytesBuffer) WriteByte(c byte) error

func (*BytesBuffer) WriteInt

func (b *BytesBuffer) WriteInt(i int64)

func (*BytesBuffer) WriteString

func (b *BytesBuffer) WriteString(s string)

type Colour

type Colour struct {
	// contains filtered or unexported fields
}

func Colour256

func Colour256(c int) Colour

func RGB

func RGB(r, g, b uint8) Colour

func (Colour) ANSI

func (c Colour) ANSI(foreground bool) string

ANSI generates the escape sequence for foreground or background colours.

type Config

type Config struct {
	ConsoleOutput io.Writer

	// FatalHandler overrides the default os.Exit(1) called after Fatal().
	// Useful in tests. If nil, defaults to os.Exit(1).
	FatalHandler FatalHandler

	StructuredOutput io.Writer
	ConsoleTheme     *Theme
	Sampler          Sampler

	// Logs are always stored in UTC, but can be displayed in a different timezone
	DisplayTimezone *time.Location

	TimeFormat string

	StructuredFormat Format

	BufferSize    int
	FieldPoolSize int

	FieldDisplayMode FieldDisplayMode
	ConsoleLevel     Level

	StructuredLevel Level

	DisableColour bool

	// AddCaller enables capturing file:line and function name for each log entry
	AddCaller bool
	// CallerSkip is the number of stack frames to skip when capturing caller information
	// Default is 0, increase for wrapper functions
	CallerSkip int
}

func DefaultConfig

func DefaultConfig() *Config

func DefaultContainerConfig

func DefaultContainerConfig() *Config

DefaultContainerConfig creates a config for containerised environments: JSON to stdout, info level.

func DefaultDevelopmentConfig

func DefaultDevelopmentConfig() *Config

DefaultDevelopmentConfig creates a config for development: coloured console, debug level, no structured output.

func DefaultHighPerformanceConfig

func DefaultHighPerformanceConfig() *Config

DefaultHighPerformanceConfig creates a config for high throughput: minimal output, sampling enabled.

func DefaultProductionConfig

func DefaultProductionConfig() *Config

DefaultProductionConfig creates a config for production: JSON output, info level, no console.

func DefaultTestingConfig

func DefaultTestingConfig(w io.Writer) *Config

DefaultTestingConfig creates a config for tests: writes to w, debug level, colours off.

type ConsoleWriter

type ConsoleWriter struct {
	// contains filtered or unexported fields
}

func NewConsoleWriter

func NewConsoleWriter(out io.Writer, theme *Theme) *ConsoleWriter

func NewConsoleWriterWithOptions

func NewConsoleWriterWithOptions(out io.Writer, theme *Theme, displayTimezone *time.Location, fieldDisplayMode FieldDisplayMode) *ConsoleWriter

func NewConsoleWriterWithTimezone

func NewConsoleWriterWithTimezone(out io.Writer, theme *Theme, displayTimezone *time.Location) *ConsoleWriter

func (*ConsoleWriter) Close

func (w *ConsoleWriter) Close() error

func (*ConsoleWriter) IsTTY

func (w *ConsoleWriter) IsTTY() bool

func (*ConsoleWriter) SetTemplate

func (w *ConsoleWriter) SetTemplate(t *Template)

func (*ConsoleWriter) SetTheme

func (w *ConsoleWriter) SetTheme(theme *Theme)

func (*ConsoleWriter) Write

func (w *ConsoleWriter) Write(e *Entry) error

type ConsoleWriterRB

type ConsoleWriterRB struct {
	// contains filtered or unexported fields
}

ConsoleWriterRB is a high-performance console writer using a lock-free ring buffer. This eliminates mutex contention and provides batched writing for better throughput.

func NewConsoleWriterRB

func NewConsoleWriterRB(out io.Writer, theme *Theme, displayTimezone *time.Location, fieldMode FieldDisplayMode) *ConsoleWriterRB

func (*ConsoleWriterRB) Close

func (w *ConsoleWriterRB) Close() error

func (*ConsoleWriterRB) Metrics

func (w *ConsoleWriterRB) Metrics() (writes, errors, dropped uint64)

func (*ConsoleWriterRB) SetTheme

func (w *ConsoleWriterRB) SetTheme(theme *Theme)

func (*ConsoleWriterRB) Write

func (w *ConsoleWriterRB) Write(e *Entry) error

Write writes a formatted log entry using the ring buffer. This method is lock-free and optimised for high throughput.

type CountSampler

type CountSampler struct {
	// Initial is the number of entries to log initially per level.
	// After Initial entries, only every Thereafter-th entry is logged.
	Initial uint64

	// Thereafter logs every Nth entry after Initial is exhausted.
	// If 0, no entries are logged after Initial.
	Thereafter uint64
	// contains filtered or unexported fields
}

CountSampler logs the first N entries, then every Mth entry thereafter. This is useful for high-volume logging where you want to capture initial entries but reduce volume over time.

func NewCountSampler

func NewCountSampler(initial, thereafter uint64) *CountSampler

NewCountSampler creates a sampler that logs the first `initial` entries, then every `thereafter`-th entry. Common values: initial=100, thereafter=100.

func (*CountSampler) Sample

func (s *CountSampler) Sample(level Level, _ string) bool

Sample implements Sampler.

type Entry

type Entry struct {
	Time time.Time

	Message string

	Caller   string
	Function string

	Fields []Field

	Line int

	Level Level
	// contains filtered or unexported fields
}

Entry represents a single log entry designed for reuse via sync.Pool.

func GetEntry

func GetEntry() *Entry

func (*Entry) Buffer

func (e *Entry) Buffer() *bytes.Buffer

Buffer returns the entry's buffer for direct writing to avoid allocations.

func (*Entry) Bytes

func (e *Entry) Bytes() []byte

Bytes returns the formatted entry as bytes. The returned slice is only valid until Release() is called.

func (*Entry) Release

func (e *Entry) Release()

Release decrements the reference count and returns to pool when zero. Uses atomic swap to ensure only one goroutine performs cleanup and pool return.

func (*Entry) Reset

func (e *Entry) Reset()

Reset prepares the Entry for reuse, clearing all fields but keeping allocated memory.

func (*Entry) Retain

func (e *Entry) Retain()

Retain increments the reference count. Call this before passing the entry to async handlers.

func (*Entry) SetLevel

func (e *Entry) SetLevel(level Level) *Entry

func (*Entry) SetMessage

func (e *Entry) SetMessage(msg string) *Entry

func (*Entry) SetTime

func (e *Entry) SetTime(t time.Time) *Entry

func (*Entry) String

func (e *Entry) String() string

String returns the formatted entry as a string. Allocates - avoid in hot paths.

func (*Entry) WithError

func (e *Entry) WithError(err error) *Entry

func (*Entry) WithField

func (e *Entry) WithField(key string, value any) *Entry

func (*Entry) WithFields

func (e *Entry) WithFields(fields ...Field) *Entry

func (*Entry) Write

func (e *Entry) Write()

type FatalHandler

type FatalHandler func()

FatalHandler is called after a Fatal log entry is written. Default behaviour is os.Exit(1). Override in tests to prevent process exit.

type Field

type Field struct {
	Key string

	Type FieldType
	// contains filtered or unexported fields
}

Field represents a structured log field optimised for minimal allocations.

Safety: Uses unsafe.Pointer to avoid interface{} allocations in hot paths. This is safe because:

  1. Escape analysis ensures referenced values remain on heap
  2. Field constructors take address of parameters, forcing heap allocation
  3. Values are never modified after Field creation (immutable pattern)
  4. Type discrimination via FieldType ensures correct pointer casting

func Any

func Any(key string, val any) Field

Any creates a field with arbitrary value. Avoid in hot paths as it causes allocations.

func Bool

func Bool(key string, val bool) Field

func Bytes

func Bytes(key string, val []byte) Field

func Duration

func Duration(key string, val time.Duration) Field

func Error

func Error(key string, val error) Field

func F

func F(key string, value any) Field

F creates a field with automatic type detection. For performance-critical code, use typed constructors instead.

func Float64

func Float64(key string, val float64) Field

func GetFieldSlice

func GetFieldSlice() []Field

func GetFieldSliceWithCapacity

func GetFieldSliceWithCapacity(capacity int) []Field

func Int

func Int(key string, val int) Field

func Int64

func Int64(key string, val int64) Field

func Milliseconds

func Milliseconds(key string, val time.Duration) Field

Milliseconds creates a float field with duration in milliseconds. Useful for showing precise timing for fast operations (e.g., "0.5ms" instead of "0s").

func String

func String(key, val string) Field

func Stringer

func Stringer(key string, val fmt.Stringer) Field

func Time

func Time(key string, val time.Time) Field

func (Field) Value

func (f Field) Value() any

Value returns the field's value based on its type. This method allocates and should be avoided in hot paths.

type FieldDisplayMode

type FieldDisplayMode int
const (
	FieldDisplayInline FieldDisplayMode = iota
	FieldDisplayTree
)

func (FieldDisplayMode) String

func (m FieldDisplayMode) String() string

type FieldType

type FieldType byte

FieldType identifies the type of a field for zero-allocation serialisation.

const (
	FieldTypeUnknown FieldType = iota
	FieldTypeString
	FieldTypeInt
	FieldTypeInt64
	FieldTypeFloat64
	FieldTypeBool
	FieldTypeTime
	FieldTypeDuration
	FieldTypeError
	FieldTypeStringer
	FieldTypeBytes
	FieldTypeAny // Avoid in hot paths
)

type Fields

type Fields struct {
	// contains filtered or unexported fields
}

func NewFields

func NewFields(capacity int) *Fields

func (*Fields) Add

func (fs *Fields) Add(key string, value any) *Fields

func (*Fields) AddField

func (fs *Fields) AddField(f Field) *Fields

func (*Fields) Reset

func (fs *Fields) Reset()

func (*Fields) Slice

func (fs *Fields) Slice() []Field

type FilteredWriter

type FilteredWriter struct {
	// contains filtered or unexported fields
}

func NewFilteredWriter

func NewFilteredWriter(w Writer, fn func(*Entry) bool) *FilteredWriter

func (*FilteredWriter) Close

func (fw *FilteredWriter) Close() error

func (*FilteredWriter) Write

func (fw *FilteredWriter) Write(e *Entry) error

type Format

type Format int
const (
	FormatJSON Format = iota
)

func (Format) String

func (f Format) String() string

type Formatter

type Formatter struct {
	// contains filtered or unexported fields
}

func NewFormatter

func NewFormatter(hint int) *Formatter

func (*Formatter) AppendByte

func (f *Formatter) AppendByte(b byte) *Formatter

func (*Formatter) Bytes

func (f *Formatter) Bytes() []byte

func (*Formatter) Release

func (f *Formatter) Release()

func (*Formatter) String

func (f *Formatter) String() string

func (*Formatter) WriteInt

func (f *Formatter) WriteInt(i int64) *Formatter

func (*Formatter) WriteString

func (f *Formatter) WriteString(s string) *Formatter

type JSONWriter

type JSONWriter struct {
	// contains filtered or unexported fields
}

func NewJSONWriter

func NewJSONWriter(out io.Writer) *JSONWriter

func (*JSONWriter) Close

func (w *JSONWriter) Close() error

func (*JSONWriter) Write

func (w *JSONWriter) Write(e *Entry) error

type Level

type Level int32
const (
	LevelDebug Level = iota
	LevelInfo
	LevelWarn
	LevelError
	LevelFatal
	LevelOff
)

func MustParseLevel

func MustParseLevel(level string) Level

MustParseLevel converts a string level to Level constant. Panics on invalid level (useful for config initialization). Valid levels (case-insensitive): debug, info, warn, error, fatal, off

func (Level) ConciseLabel

func (l Level) ConciseLabel() string

ConciseLabel returns a 4-character concise representation of the log level for console output. These labels are designed to be visually balanced and easy to scan.

func (Level) Icon

func (l Level) Icon() string

func (Level) Short

func (l Level) Short() byte

Short returns a single character representation for compact output.

func (Level) String

func (l Level) String() string

type LevelStyle

type LevelStyle int
const (
	LevelStyleText LevelStyle = iota
	LevelStyleIcon
	LevelStyleBadge
)

type Logger

type Logger struct {
	// contains filtered or unexported fields
}

func FromContext

func FromContext(ctx context.Context) *Logger

FromContext retrieves the logger from ctx. If ctx carries additional fields via ContextWithFields, they are prepended via With() before returning. Returns NopLogger() if no logger is stored — never returns nil.

func New

func New(w io.Writer) *Logger

func NewDevelopment

func NewDevelopment() *Logger

NewDevelopment creates a logger optimised for development with colourful console output.

func NewForTesting

func NewForTesting(w io.Writer) *Logger

func NewWithBuilder

func NewWithBuilder(builder *Builder) *Logger

func NewWithConfig

func NewWithConfig(cfg *Config) *Logger

func NewWithOptions

func NewWithOptions(opts ...Option) *Logger

func NopLogger

func NopLogger() *Logger

func (*Logger) AddWriter

func (l *Logger) AddWriter(name string, w Writer)

AddWriter adds a named writer to receive log entries. Thread-safe for concurrent calls. Writers process entries asynchronously via MultiWriter.

func (*Logger) Banner

func (l *Logger) Banner(lines ...string)

Banner prints multiple lines of text without formatting. Newlines are automatically added after each line.

func (*Logger) Close

func (l *Logger) Close() error

Close closes any additional writers that were added to the logger. Thread-safe and nil-safe - returns nil if logger is nil or has no additional writers.

func (*Logger) Debug

func (l *Logger) Debug(msg string, fields ...Field)

func (*Logger) DebugDetailed

func (l *Logger) DebugDetailed(msg string, fields ...Field)

DebugDetailed logs a debug message with fields always displayed in tree format

func (*Logger) Error

func (l *Logger) Error(msg string, fields ...Field)

func (*Logger) ErrorDetailed

func (l *Logger) ErrorDetailed(msg string, fields ...Field)

ErrorDetailed logs an error message with fields always displayed in tree format

func (*Logger) Fatal

func (l *Logger) Fatal(msg string, fields ...Field)

func (*Logger) Info

func (l *Logger) Info(msg string, fields ...Field)

func (*Logger) InfoDetailed

func (l *Logger) InfoDetailed(msg string, fields ...Field)

InfoDetailed logs an info message with fields always displayed in tree format

func (*Logger) Level

func (l *Logger) Level() Level

func (*Logger) LogEntry

func (l *Logger) LogEntry(e *Entry)

LogEntry dispatches a pre-populated entry to all configured writers. Used by slog bridge and other external adapters.

func (*Logger) Raw

func (l *Logger) Raw(message string)

Raw prints a message without any formatting, timestamp, or level. The caller is responsible for including newlines if desired.

func (*Logger) RemoveWriter

func (l *Logger) RemoveWriter(name string)

RemoveWriter removes a named writer. Thread-safe for concurrent calls.

func (*Logger) SetLevel

func (l *Logger) SetLevel(level Level)

func (*Logger) SetTemplate

func (l *Logger) SetTemplate(t *Template)

func (*Logger) Status

func (l *Logger) Status() *StatusFormatter

Status returns the StatusFormatter for coloured status indicators. Safe to call even if logger is nil - returns a non-coloured formatter.

func (*Logger) Warn

func (l *Logger) Warn(msg string, fields ...Field)

func (*Logger) WarnDetailed

func (l *Logger) WarnDetailed(msg string, fields ...Field)

WarnDetailed logs a warning message with fields always displayed in tree format

func (*Logger) With

func (l *Logger) With(fields ...Field) *Logger

With returns a child logger that prepends the given fields to every log entry. The child shares writers, config, and sampler with the parent. Level is snapshotted at the time of the call; dynamic parent level changes do not propagate to the child after creation.

func (*Logger) WithTemplate

func (l *Logger) WithTemplate(t *Template) *Logger

WithTemplate creates a child logger with a different output template. The consoleWriter is intentionally recreated so the new template takes effect.

type MultiWriter

type MultiWriter struct {
	// contains filtered or unexported fields
}

func NewMultiWriter

func NewMultiWriter() *MultiWriter

func (*MultiWriter) AddWriter

func (mw *MultiWriter) AddWriter(name string, w Writer)

func (*MultiWriter) Close

func (mw *MultiWriter) Close() error

func (*MultiWriter) RemoveWriter

func (mw *MultiWriter) RemoveWriter(name string)

func (*MultiWriter) Stats

func (mw *MultiWriter) Stats() map[string]int

func (*MultiWriter) Write

func (mw *MultiWriter) Write(e *Entry) error

type NoOpWriter

type NoOpWriter struct{}

func (*NoOpWriter) Close

func (*NoOpWriter) Close() error

func (*NoOpWriter) Write

func (*NoOpWriter) Write(_ *Entry) error

type Option

type Option func(*Builder)

Option is a functional option pattern for configuring a Logger. Options are applied in order, so later options override earlier ones.

func WithBufferSize

func WithBufferSize(size int) Option

func WithCaller

func WithCaller(enabled bool) Option

WithCaller enables or disables caller information capture (file:line and function name).

func WithCallerSkip

func WithCallerSkip(skip int) Option

WithCallerSkip sets the number of stack frames to skip when capturing caller information. Use this when wrapping the logger to skip your wrapper's frames.

func WithColourDisabled

func WithColourDisabled() Option

func WithColourEnabled

func WithColourEnabled(enabled bool) Option

func WithConsoleOutput

func WithConsoleOutput(w io.Writer) Option

func WithDisplayTimezone

func WithDisplayTimezone(tz string) Option

WithDisplayTimezone sets the timezone for displaying timestamps. The timezone parameter should be a valid IANA timezone name (e.g., "America/New_York", "Australia/Sydney"). Logs are always stored in UTC internally, but this controls how they're displayed in console output. Panics if the timezone name is invalid (use during logger initialisation).

func WithFieldPoolSize

func WithFieldPoolSize(size int) Option

func WithFormat

func WithFormat(format Format) Option

func WithLevel

func WithLevel(level Level) Option

func WithSampler

func WithSampler(s Sampler) Option

WithSampler sets a sampler for the logger. Pass nil to disable sampling (default).

func WithSampling

func WithSampling(initial, thereafter uint32) Option

WithSampling enables log sampling using a CountSampler. initial is the number of initial messages to log before sampling begins. thereafter is the sampling interval (1 in thereafter messages).

func WithStructuredLevel

func WithStructuredLevel(level Level) Option

func WithStructuredOutput

func WithStructuredOutput(w io.Writer) Option

func WithTheme

func WithTheme(theme *Theme) Option

func WithTimeFormat

func WithTimeFormat(format string) Option

type RingBuffer

type RingBuffer struct {
	// contains filtered or unexported fields
}

RingBuffer implements a lock-free ring buffer for batched writing. Uses atomic operations to minimise contention and provide high throughput.

func NewRingBuffer

func NewRingBuffer(writer io.Writer, size int) *RingBuffer

NewRingBuffer creates a new ring buffer with the specified size. Size must be a power of 2 for optimal performance.

func (*RingBuffer) Close

func (rb *RingBuffer) Close() error

func (*RingBuffer) DroppedCount

func (rb *RingBuffer) DroppedCount() uint64

func (*RingBuffer) Write

func (rb *RingBuffer) Write(data []byte) bool

Write adds a log entry to the ring buffer. Returns false if the buffer is full and the message was dropped.

type RingBufferEntry

type RingBufferEntry struct {
	// contains filtered or unexported fields
}

type Sampler

type Sampler interface {
	// Sample returns true if the entry should be logged.
	// Called on every log attempt when sampling is enabled.
	Sample(level Level, msg string) bool
}

Sampler determines whether a log entry should be emitted. Implementations must be safe for concurrent use.

type StatusFormatter

type StatusFormatter struct {
	// contains filtered or unexported fields
}

StatusFormatter provides colour-aware status indicator formatting for operation results. Pre-caches ANSI codes at initialization for zero-allocation formatting.

func NewStatusFormatter

func NewStatusFormatter(theme *Theme, isTTY bool) *StatusFormatter

NewStatusFormatter creates a formatter that respects terminal capabilities and theme. Pass nil theme to disable colours.

func (*StatusFormatter) Fail

func (sf *StatusFormatter) Fail(text string) string

Fail formats a FAIL status with red colour when enabled.

func (*StatusFormatter) Info

func (sf *StatusFormatter) Info(text string) string

Info formats an INFO status with blue colour when enabled.

func (*StatusFormatter) Okay

func (sf *StatusFormatter) Okay(text string) string

Okay formats an OK status with green colour when enabled.

func (*StatusFormatter) Warn

func (sf *StatusFormatter) Warn(text string) string

Warn formats a WARN status with yellow colour when enabled.

type Template

type Template struct {
	// contains filtered or unexported fields
}

func (*Template) CalculatePrefixWidth

func (t *Template) CalculatePrefixWidth(entry *Entry) int

CalculatePrefixWidth determines indentation needed for tree alignment. Exported version for use by the Logger.

type TemplateBuilder

type TemplateBuilder struct {
	// contains filtered or unexported fields
}

func NewTemplateBuilder

func NewTemplateBuilder() *TemplateBuilder

func (*TemplateBuilder) Build

func (b *TemplateBuilder) Build() *Template

func (*TemplateBuilder) WithColours

func (b *TemplateBuilder) WithColours(enabled bool) *TemplateBuilder

func (*TemplateBuilder) WithFieldDisplayMode

func (b *TemplateBuilder) WithFieldDisplayMode(mode FieldDisplayMode) *TemplateBuilder

func (*TemplateBuilder) WithFields

func (b *TemplateBuilder) WithFields(enabled bool) *TemplateBuilder

func (*TemplateBuilder) WithLevel

func (b *TemplateBuilder) WithLevel(enabled bool) *TemplateBuilder

func (*TemplateBuilder) WithLevelStyle

func (b *TemplateBuilder) WithLevelStyle(style LevelStyle) *TemplateBuilder

func (*TemplateBuilder) WithMessage

func (b *TemplateBuilder) WithMessage(enabled bool) *TemplateBuilder

func (*TemplateBuilder) WithTime

func (b *TemplateBuilder) WithTime(enabled bool) *TemplateBuilder

func (*TemplateBuilder) WithTimeFormat

func (b *TemplateBuilder) WithTimeFormat(format string) *TemplateBuilder

type TemplateType

type TemplateType int
const (
	TemplateTypeDefault TemplateType = iota
	TemplateTypeSimple
	TemplateTypeMinimal
	TemplateTypeJSON
)

type Theme

type Theme struct {
	Name string

	DebugColour Colour
	InfoColour  Colour
	WarnColour  Colour
	ErrorColour Colour
	FatalColour Colour

	TimestampColour Colour
	MessageColour   Colour
	FieldKeyColour  Colour
	FieldValColour  Colour
	ErrorValColour  Colour

	// Status indicator colours for operation results
	StatusOKColour   Colour
	StatusFailColour Colour
	StatusWarnColour Colour
	StatusInfoColour Colour

	// Table header colour
	TableHeader Colour
	// contains filtered or unexported fields
}

func (*Theme) Cache

func (t *Theme) Cache()

Cache pre-computes ANSI sequences for all colours used in the hot-path template renderer. Call this after constructing a custom Theme to avoid per-log allocations.

func (*Theme) GetColourForLevel

func (t *Theme) GetColourForLevel(level Level) Colour

type Writer

type Writer interface {
	// Write writes an entry to the output.
	// The entry must not be modified after this call.
	Write(e *Entry) error

	Close() error
}

Writer defines the interface for log output writers. Implementations must be thread-safe and handle formatting independently.

type WriterFunc

type WriterFunc func(*Entry) error

WriterFunc adapts a function to the Writer interface.

func (WriterFunc) Close

func (WriterFunc) Close() error

func (WriterFunc) Write

func (f WriterFunc) Write(e *Entry) error

Directories

Path Synopsis
examples
basic command
Package main demonstrates the basic usage of the velocity logging library.
Package main demonstrates the basic usage of the velocity logging library.
custom-theme command
Custom theme example.
Custom theme example.
json-logging command
Package main demonstrates dual-output logging: coloured console for humans and newline-delimited JSON for log aggregators.
Package main demonstrates dual-output logging: coloured console for humans and newline-delimited JSON for log aggregators.
multi-writer command
Multi-writer example.
Multi-writer example.
pretty-output command
Package main demonstrates velocity's pretty-printing utilities for building rich CLI tool output.
Package main demonstrates velocity's pretty-printing utilities for building rich CLI tool output.
progress command
Progress bars and spinners example.
Progress bars and spinners example.
sampling command
Sampling example.
Sampling example.
slog-bridge command
slog bridge example.
slog bridge example.
tables command
Package main demonstrates velocity's table rendering for structured terminal output.
Package main demonstrates velocity's table rendering for structured terminal output.
terminal-velocity command
Terminal Velocity - GPU cluster deploy simulator.
Terminal Velocity - GPU cluster deploy simulator.
themes command
Package main cycles through velocity's four built-in themes so you can see how each one styles the different log levels.
Package main cycles through velocity's four built-in themes so you can see how each one styles the different log levels.
Package pretty provides styled terminal output utilities for CLI applications.
Package pretty provides styled terminal output utilities for CLI applications.
Package velocityslog bridges log/slog to a velocity Logger.
Package velocityslog bridges log/slog to a velocity Logger.

Jump to

Keyboard shortcuts

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