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 ¶
- func CorrelationID(ctx context.Context) string
- func CorrelationIDFromContext(ctx context.Context) (string, bool)
- func EnsureCorrelationID(ctx context.Context) (context.Context, string)
- func IsJournald() bool
- func New(cfg Config) *slog.Logger
- func NewCorrelationID() string
- func NewJournalHandler(level slog.Level) slog.Handler
- func ParseLevel(s string) slog.Level
- func WithCorrelationID(ctx context.Context, id string) context.Context
- type BufferSink
- type Config
- type Format
- type RotateWriter
- type SamplingConfig
- type SamplingHandler
- func (s *SamplingHandler) Enabled(ctx context.Context, level slog.Level) bool
- func (s *SamplingHandler) Handle(ctx context.Context, r slog.Record) error
- func (s *SamplingHandler) Stop()
- func (s *SamplingHandler) WithAttrs(attrs []slog.Attr) slog.Handler
- func (s *SamplingHandler) WithGroup(name string) slog.Handler
- type Sink
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CorrelationID ¶
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 ¶
CorrelationIDFromContext returns the correlation ID stored in ctx and whether one was present.
func EnsureCorrelationID ¶
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 ¶
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 ¶
NewJournalHandler returns a journal-native slog.Handler at the given minimum level, writing to os.Stderr.
func ParseLevel ¶
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.
Types ¶
type BufferSink ¶ added in v0.2.0
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 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.
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) Handle ¶
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.
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" )