logger

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Jun 11, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Overview

Package logger provides VORTEX's structured logging built on the Go standard library's log/slog (Non-Negotiable Rule #10: standard library first).

It supports two output formats — machine-readable JSON for production and a human-friendly text format for local development — and a correlation ID that flows through a request or agent task via context.Context so every log line emitted while handling one unit of work can be tied together.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CorrelationID

func CorrelationID(ctx context.Context) string

CorrelationID returns the correlation ID stored in ctx, or "" if none is set. It is the string-only form used by the log handlers.

func CorrelationIDFromContext

func CorrelationIDFromContext(ctx context.Context) (string, bool)

CorrelationIDFromContext returns the correlation ID stored in ctx and whether one was present.

func EnsureCorrelationID

func EnsureCorrelationID(ctx context.Context) (context.Context, string)

EnsureCorrelationID returns ctx and the existing correlation ID if one is present; otherwise it generates a new ID, stores it in a derived context, and returns both.

func IsJournald

func IsJournald() bool

IsJournald reports whether the process is running under systemd with its stdout/stderr connected to the journal. systemd sets JOURNAL_STREAM (and, since v232, INVOCATION_ID) for services it launches; the presence of the private control socket is a further signal that systemd is PID 1.

func New

func New(cfg Config) *slog.Logger

New builds a *slog.Logger from cfg. It installs a handler that automatically promotes a correlation ID stored in the record's context to a top-level attribute, so callers never have to thread it through manually.

When Output is nil and Sink is SinkAuto on a systemd host, a journal-native handler is used (priority-prefixed lines on stderr for journalctl). Otherwise the configured Format (text/JSON) is written to the resolved sink.

func NewCorrelationID

func NewCorrelationID() string

NewCorrelationID returns a fresh 32-character lowercase hex correlation ID (16 random bytes). If the system RNG fails — effectively never — it falls back to a timestamp-derived hex value so an ID is always produced.

func NewJournalHandler

func NewJournalHandler(level slog.Level) slog.Handler

NewJournalHandler returns a journal-native slog.Handler at the given minimum level, writing to os.Stderr.

func ParseLevel

func ParseLevel(s string) slog.Level

ParseLevel converts a config string ("debug", "info", "warn", "error") into a slog.Level. Unknown values fall back to Info. The mapping mirrors the observability.log_level field in vortex.cue.

func WithCorrelationID

func WithCorrelationID(ctx context.Context, id string) context.Context

WithCorrelationID returns a copy of ctx carrying the given correlation ID. Records logged with that context (via Logger.InfoContext etc.) include the ID automatically.

Types

type BufferSink added in v0.2.0

type BufferSink interface {
	Record(time, level, msg string, fields map[string]string)
}

BufferSink receives structured log records for the in-memory log viewer. It is satisfied by *api.LogBuffer via an adapter (logger stays decoupled from the api package).

type Config

type Config struct {
	// Level is the minimum level that will be emitted. Defaults to Info.
	Level slog.Level
	// Format selects JSON or text output. Defaults to FormatJSON.
	Format Format
	// Output is where records are written. When nil, the Sink selects the
	// destination. An explicit Output overrides Sink (used by tests).
	Output io.Writer
	// AddSource includes source file:line in each record when true.
	AddSource bool
	// Sink selects the output destination when Output is nil. Defaults to
	// SinkAuto, which uses the systemd journal under a service or stdout
	// otherwise.
	Sink Sink
	// Path is the log file path when Sink is SinkFile.
	Path string
	// Sampling enables windowed sampling of Debug/Info records (Warn/Error
	// always pass). Enable in production when a route processes >10k req/s.
	Sampling bool
	// Buffer, when set, also receives every emitted record (in addition to the
	// normal sink) — used to feed the TUI/API log viewer. Optional.
	Buffer BufferSink
}

Config configures a Logger.

type Format

type Format int

Format selects the encoding used for log records.

const (
	// FormatJSON emits one JSON object per line. Use in production.
	FormatJSON Format = iota
	// FormatText emits human-readable key=value lines. Use for local dev.
	FormatText
)

type RotateWriter

type RotateWriter struct {
	Path       string
	MaxSizeMB  int  // rotate when the file exceeds this size; default 100
	MaxAgeDays int  // delete backups older than this; default 7
	MaxBackups int  // keep at most this many backups; default 5
	Compress   bool // gzip rotated files; default true
	// contains filtered or unexported fields
}

RotateWriter is an io.Writer that writes to a log file and rotates it when it grows past MaxSizeMB or, via a background ticker, when rotated backups age out. It plugs directly into slog as a sink. It is safe for concurrent Write calls.

Rotation renames the active file to "<path>.<timestamp>" (and gzips it when Compress is set), then opens a fresh file at the original path. Backups beyond MaxBackups, or older than MaxAgeDays, are removed.

func NewRotateWriter

func NewRotateWriter(cfg RotateWriter) (*RotateWriter, error)

NewRotateWriter opens (creating if needed) the log file at cfg.Path, applies defaults for any zero fields, and starts a background goroutine that prunes aged-out backups hourly.

func (*RotateWriter) Close

func (r *RotateWriter) Close() error

Close flushes and closes the file and stops the background goroutine. It is idempotent.

func (*RotateWriter) Rotate

func (r *RotateWriter) Rotate() error

Rotate forces a rotation regardless of current size.

func (*RotateWriter) Write

func (r *RotateWriter) Write(p []byte) (int, error)

Write appends p to the current file, rotating immediately if the write pushes the file past MaxSizeMB.

type SamplingConfig

type SamplingConfig struct {
	// Tick is the sampling window; counters reset at this interval. Default 1s.
	Tick time.Duration
	// First is how many records per (level,message) to log unconditionally at
	// the start of each window. Default 10.
	First int
	// Thereafter is the 1-in-N rate applied after First within a window.
	// Default 100.
	Thereafter int
}

SamplingConfig tunes the SamplingHandler.

type SamplingHandler

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

SamplingHandler wraps another slog.Handler and rate-limits high-volume Debug and Info records. Within each Tick window, the first First records for a given (level, message) pair pass through; after that only every Thereafter-th record does. Warn and Error records are never sampled — they always pass.

This bounds log volume on hot paths (e.g. a route doing >10k req/s) without losing the signal of the first occurrences or any warning/error.

func NewSamplingHandler

func NewSamplingHandler(h slog.Handler, cfg SamplingConfig) *SamplingHandler

NewSamplingHandler wraps h with sampling per cfg, applying defaults for zero fields. It starts a background goroutine that clears counters every Tick.

func (*SamplingHandler) Enabled

func (s *SamplingHandler) Enabled(ctx context.Context, level slog.Level) bool

func (*SamplingHandler) Handle

func (s *SamplingHandler) Handle(ctx context.Context, r slog.Record) error

Handle applies sampling to Debug/Info and passes Warn/Error through.

func (*SamplingHandler) Stop

func (s *SamplingHandler) Stop()

Stop ends the background reset goroutine. Safe to call multiple times.

func (*SamplingHandler) WithAttrs

func (s *SamplingHandler) WithAttrs(attrs []slog.Attr) slog.Handler

func (*SamplingHandler) WithGroup

func (s *SamplingHandler) WithGroup(name string) slog.Handler

type Sink

type Sink string

Sink selects where log records are written.

const (
	// SinkAuto writes to the systemd journal when running as a service,
	// otherwise to stdout.
	SinkAuto Sink = "auto"
	// SinkStdout writes to standard output.
	SinkStdout Sink = "stdout"
	// SinkStderr writes to standard error.
	SinkStderr Sink = "stderr"
	// SinkFile writes to the file named by Config.Path.
	SinkFile Sink = "file"
)

Jump to

Keyboard shortcuts

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