output

package
v2.0.0 Latest Latest
Warning

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

Go to latest
Published: Jul 14, 2026 License: MIT Imports: 22 Imported by: 0

Documentation

Overview

Package output implements Sypl outputs: an output processes, and writes a message to its writer - anything implementing io.Writer.

Built-ins: Console (stdout), StdErr (error levels to stderr), File, and FileBased (files), SafeBuffer (an in-memory concurrent-safe buffer), ElasticSearch (one request per document), and the reliability set:

  • Async: wraps ANY output into a bounded, buffered, asynchronous one - a single worker drains the buffer preserving FIFO order, with Block, DropNewest, or DropOldest full-buffer policies.
  • ElasticSearchBulk (and ...WithDynamicIndex): batches documents into _bulk requests via esutil's BulkIndexer - the high-throughput sibling of ElasticSearch.
  • RotatingFile: a file output with native size-based rotation, backup timestamping, and count/age pruning.
  • Recorder: captures structured snapshots of everything written - a test-assertion helper for Sypl consumers.

Outputs that buffer implement `Flush() error`; outputs owning resources implement `Close() error` (io.Closer). Close is idempotent, and writes after Close return a typed, per-output sentinel error - never a panic. Flush after Close is a no-op.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrAsyncClosed is returned when writing to a closed async output.
	ErrAsyncClosed = errors.New("async output is closed")

	// ErrAsyncDropped is the sentinel wrapped into drop notifications
	// delivered to the error handler. Check it with `errors.Is`.
	ErrAsyncDropped = errors.New("async output dropped a message")
)
View Source
var ErrRotatingFileClosed = errors.New("rotating file output is closed")

ErrRotatingFileClosed is returned when writing to a closed rotating file output.

View Source
var ErrRotatingFileUnavailable = errors.New("rotating file output has no open live file")

ErrRotatingFileUnavailable is returned - wrapped with the underlying cause - while the rotating writer has no open live file: a mid-rotation failure closed it, and reopening the original path failed too. Every write (and Flush) retries the reopen first, so the output self-heals once the filesystem recovers. Check it with `errors.Is`.

NOTE: Deliberately NOT `os.ErrClosed`-classed: the output layer treats `os.ErrClosed` as a benign closed-writer condition, and swallows it - this error must surface (e.g. through `Sypl.SetErrorHandler`).

Functions

func Recorder

func Recorder(maxLevel level.Level, processors ...processor.IProcessor) (*RecorderOutput, IOutput)

Recorder is a built-in `output` - named `Recorder` - capturing a structured snapshot (level, original, and processed content, fields, tags, output, and processors names, timestamp) of every message it actually writes: level gating, flags, and processors are applied through the standard pipeline first. Designed for test assertions by Sypl consumers.

It returns both the concrete recorder - for `Messages`, `Len`, and `Reset` - and the `IOutput` to register on the logger; they're the same instance. Thread-safe.

Types

type AsyncOption

type AsyncOption func(*asyncOutput)

AsyncOption configures the async output wrapper.

func AsyncWithBufferSize

func AsyncWithBufferSize(size int) AsyncOption

AsyncWithBufferSize sets the buffer capacity. Non-positive values fall back to the default (1024).

func AsyncWithErrorHandler

func AsyncWithErrorHandler(handler func(error)) AsyncOption

AsyncWithErrorHandler sets the handler receiving the wrapped output's write errors, drop notifications (wrapping `ErrAsyncDropped`), interval-flush errors, and panics from the wrapped output - converted to errors, so a misbehaving sink never kills the worker, nor the host process. The handler may be called concurrently.

func AsyncWithFlushInterval

func AsyncWithFlushInterval(interval time.Duration) AsyncOption

AsyncWithFlushInterval periodically flushes the WRAPPED output - useful for time-buffered inner outputs (e.g. the ElasticSearch bulk output). Zero (the default) disables it. Flush errors are delivered to the error handler.

func AsyncWithPolicy

func AsyncWithPolicy(policy AsyncPolicy) AsyncOption

AsyncWithPolicy sets the full-buffer policy. Default: AsyncPolicyBlock.

type AsyncPolicy

type AsyncPolicy int

AsyncPolicy determines what happens when a message is written to an async output whose buffer is full.

const (
	// AsyncPolicyBlock blocks the writer until buffer space is available.
	// It's the default policy - no message is ever dropped.
	AsyncPolicyBlock AsyncPolicy = iota

	// AsyncPolicyDropNewest drops the incoming message.
	AsyncPolicyDropNewest

	// AsyncPolicyDropOldest drops the oldest buffered message, making room
	// for the incoming one.
	AsyncPolicyDropOldest
)

Available policies.

func (AsyncPolicy) String

func (p AsyncPolicy) String() string

String interface implementation.

type IOutput

type IOutput interface {
	meta.IMeta

	// String interface.
	String() string

	// GetBuiltinLogger returns the Golang's builtin logger.
	GetBuiltinLogger() *builtin.Builtin

	// GetFormatter returns the formatter.
	GetFormatter() formatter.IFormatter

	// SetFormatter sets the formatter.
	SetFormatter(fmtr formatter.IFormatter) IOutput

	// GetMaxLevel returns the max level.
	GetMaxLevel() level.Level

	// SetMaxLevel sets the max level.
	SetMaxLevel(l level.Level) IOutput

	// AddProcessors adds one or more processors.
	AddProcessors(processors ...processor.IProcessor) IOutput

	// SetProcessors sets one or more processors.
	SetProcessors(processors ...processor.IProcessor) IOutput

	// GetProcessors returns registered processors.
	GetProcessors() []processor.IProcessor

	// GetProcessorsNames returns the names of the registered processors.
	GetProcessorsNames() []string

	// GetWriter returns the writer.
	GetWriter() io.Writer

	// SetWriter sets the writer.
	SetWriter(w io.Writer) IOutput

	// Write write the message to the defined output.
	Write(m message.IMessage) error
}

IOutput specifies what an output does.

func Async

func Async(o IOutput, opts ...AsyncOption) IOutput

Async wraps `o` into a bounded, buffered, asynchronous output: writes enqueue into a buffer (default capacity: 1024), and a single worker goroutine drains it to `o` - preserving FIFO order. All other `IOutput` methods are proxied to `o`, so Sypl-level dispatch (name matching, level checks, status checks) behaves identically.

Capabilities: - `Flush() error`: guarantees everything enqueued BEFORE the call was written to `o` (or dropped by policy), then flushes `o` - if `o` implements `Flush() error`. SNAPSHOT semantics: messages enqueued after the call may remain buffered, so Flush returns even under sustained concurrent writes. - `Close() error`: flushes, stops the worker, and closes `o` - if `o` implements `io.Closer`. Idempotent. Writes after Close return `ErrAsyncClosed`.

Hung sinks: direct Flush, and Close calls wait - UNBOUNDED - on the wrapped output's in-flight write, so a sink that never returns blocks them forever. The Fatal path's pre-exit flush is time-bounded (the process exits regardless), but direct Flush/Close are NOT: prefer bounded sinks (e.g. writers carrying I/O deadlines/timeouts), and `AsyncWithFlushInterval` for time-buffered inner outputs, so draining never depends on a single unbounded call.

See the `Async*` options for buffer size, full-buffer policy, error handling, and periodic flushing.

func Console

func Console(maxLevel level.Level, processors ...processor.IProcessor) IOutput

Console is a built-in `output` - named `Console`, that writes to `stdout`.

func File

func File(name string, path string, maxLevel level.Level, processors ...processor.IProcessor) IOutput

File is a built-in `output` - named `File`, that writes to the specified file.

NOTE: If the common used "-" is used, it will behave as a Console writing to stdout. NOTE: If no path is provided, it'll create one in the OS's temp directory. NOTE: If the dir and/or file does not exist, it will be created.

func FileBased

func FileBased(
	name string,
	maxLevel level.Level,
	writer io.Writer,
	processors ...processor.IProcessor,
) IOutput

FileBased is a built-in `output`, that writes to the specified file.

func New

func New(name string,
	maxLevel level.Level,
	w io.Writer,
	processors ...processor.IProcessor,
) IOutput

New is the Output factory.

func RotatingFile

func RotatingFile(
	name, path string,
	maxLevel level.Level,
	cfg RotationConfig,
	processors ...processor.IProcessor,
) (IOutput, error)

RotatingFile is a built-in `output` that writes to the specified file, rotating it by size: when a write would push the live file beyond `cfg.MaxSizeBytes`, the file is closed, renamed to `<path>.<UTC timestamp>`, and reopened fresh - then backups beyond `cfg.MaxBackups`, or older than `cfg.MaxAgeDays`, are pruned (inline, no goroutines).

Capabilities: `Flush() error` (file sync), and idempotent `Close() error`. Writes after Close return `ErrRotatingFileClosed`.

Notes: - Unlike `File`, it returns an error - it never calls log.Fatalf. - Missing parent directories are created. - Any file named `<path>.*` is treated as a backup by pruning. - SELF-HEALING: a mid-rotation failure (e.g. the rename was denied) reopens the original path, so writes keep landing there - the failure is surfaced through the write's error, and rotation retries on the next size-threshold write. When even the reopen fails, writes return `ErrRotatingFileUnavailable` - retrying the reopen each time - until the filesystem recovers.

func SafeBuffer

func SafeBuffer(maxLevel level.Level, processors ...processor.IProcessor) (*safebuffer.Buffer, IOutput)

SafeBuffer is a built-in `output` - named `Buffer`, that writes to the buffer.

func StdErr

func StdErr(processors ...processor.IProcessor) IOutput

StdErr is a built-in `output` - named `StdErr`, that only writes to `stderr` message @ Error level.

type Proxy

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

Proxy forwards every `IOutput` method to the wrapped (inner) output, so wrappers - such as the async output - behave, from Sypl's dispatch perspective (name matching, level checks, status checks), exactly like the output they wrap.

Chainable setters return `self` - the outer wrapper - instead of the inner output, so the wrapping survives chained configuration calls, e.g. `Async(o).SetFormatter(f)`.

Exported so capability-carrying wrappers living outside this package - such as the `es` submodule's bulk output - can embed it.

func NewProxy

func NewProxy(inner, self IOutput) *Proxy

NewProxy is the Proxy factory. `self` is the outer wrapper embedding this proxy.

func (*Proxy) AddProcessors

func (p *Proxy) AddProcessors(processors ...processor.IProcessor) IOutput

AddProcessors adds one or more processors to the inner output.

func (*Proxy) GetBuiltinLogger

func (p *Proxy) GetBuiltinLogger() *builtin.Builtin

GetBuiltinLogger returns the inner output's Golang's builtin logger.

func (*Proxy) GetFormatter

func (p *Proxy) GetFormatter() formatter.IFormatter

GetFormatter returns the inner output's formatter.

func (*Proxy) GetMaxLevel

func (p *Proxy) GetMaxLevel() level.Level

GetMaxLevel returns the inner output's max level.

func (*Proxy) GetName

func (p *Proxy) GetName() string

GetName returns the inner output name.

func (*Proxy) GetProcessors

func (p *Proxy) GetProcessors() []processor.IProcessor

GetProcessors returns the inner output's registered processors.

func (*Proxy) GetProcessorsNames

func (p *Proxy) GetProcessorsNames() []string

GetProcessorsNames returns the names of the inner output's registered processors.

func (*Proxy) GetStatus

func (p *Proxy) GetStatus() status.Status

GetStatus returns the inner output status.

func (*Proxy) GetWriter

func (p *Proxy) GetWriter() io.Writer

GetWriter returns the inner output's writer.

func (*Proxy) SetFormatter

func (p *Proxy) SetFormatter(fmtr formatter.IFormatter) IOutput

SetFormatter sets the inner output's formatter.

func (*Proxy) SetMaxLevel

func (p *Proxy) SetMaxLevel(l level.Level) IOutput

SetMaxLevel sets the inner output's max level.

func (*Proxy) SetProcessors

func (p *Proxy) SetProcessors(processors ...processor.IProcessor) IOutput

SetProcessors sets one or more processors on the inner output.

func (*Proxy) SetStatus

func (p *Proxy) SetStatus(s status.Status)

SetStatus sets the inner output status.

func (*Proxy) SetWriter

func (p *Proxy) SetWriter(w io.Writer) IOutput

SetWriter sets the inner output's writer.

func (*Proxy) String

func (p *Proxy) String() string

String interface implementation.

func (*Proxy) Write

func (p *Proxy) Write(m message.IMessage) error

Write writes the message to the inner output.

type Record

type Record struct {
	// Fields is a copy of the message's structured fields.
	Fields fields.Fields

	// Level is the message's level.
	Level level.Level

	// OriginalContent is the message's content before processing.
	OriginalContent string

	// OutputName is the output the message was routed to.
	OutputName string

	// ProcessedContent is the exact text written - processors, and
	// formatter applied.
	ProcessedContent string

	// ProcessorsNames is a copy of the processors names applied.
	ProcessorsNames []string

	// Tags is a copy of the message's tags - lexicographically sorted.
	Tags []string

	// Timestamp is the message's creation time.
	Timestamp time.Time
}

Record is a structured snapshot of a message the recorder output actually wrote - level gating, flags, and processors all applied.

type RecorderOutput

type RecorderOutput struct {
	*Proxy
	// contains filtered or unexported fields
}

RecorderOutput is an `IOutput` capturing a structured snapshot of every message it writes - a test-assertion helper for Sypl consumers.

func (*RecorderOutput) Len

func (r *RecorderOutput) Len() int

Len returns how many records were captured.

func (*RecorderOutput) Messages

func (r *RecorderOutput) Messages() []Record

Messages returns a defensive copy of the captured records.

func (*RecorderOutput) Reset

func (r *RecorderOutput) Reset()

Reset discards the captured records.

func (*RecorderOutput) Write

func (r *RecorderOutput) Write(m message.IMessage) error

Write runs the message through the standard output pipeline - snapshotting it if, and only if, it's actually written.

type RotationConfig

type RotationConfig struct {
	// MaxSizeBytes is the size threshold: a write that would push the live
	// file BEYOND it triggers a rotation first. A write landing exactly at
	// the limit does not rotate. Must be positive.
	MaxSizeBytes int64

	// MaxBackups caps how many rotated backups are kept - the oldest
	// beyond the cap are pruned on rotation. Zero keeps all.
	MaxBackups int

	// MaxAgeDays prunes - on rotation - backups whose modification time is
	// older than this many days. Zero keeps all.
	MaxAgeDays int
}

RotationConfig configures the rotating file output.

Jump to

Keyboard shortcuts

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