siglog

package module
v0.0.0-...-c134bcf Latest Latest
Warning

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

Go to latest
Published: Oct 22, 2026 License: MIT Imports: 20 Imported by: 0

README

siglog

A compact, structured logging library for high-throughput Go services.

Built around a sharded ring buffer and a length-prefixed binary record format — with readable console output when you need it.

Go Status Format

Pre-release notice: siglog is still evolving. Its current ring-buffer overflow policy can discard previously buffered records when all tried shards are full. Do not use this version for loss-intolerant audit or financial records. See Delivery and limitations.

Why siglog?

  • Sharded ring buffer to spread writes across independently protected byte rings.
  • Length-prefixed binary records so readers can move to the next record without decoding the current payload.
  • Typed fields for strings, integers, floats, booleans, durations, timestamps, raw bytes, and UUIDs.
  • Multiple transports: console, file, TCP, UDP, and HTTP.
  • Automatic interval- and size-triggered flushing behind the Logger API.
  • Bounded transport retries with errors returned after the configured attempts are exhausted.
  • Optional statistics and rate limiting for monitoring without forcing aggregate counters onto every logging call.

Installation

After publishing, replace PhoneixAI with the GitHub account or organization that owns this repository:

go get github.com/PhoneixAI/siglog

The final module path in go.mod must match this import path before release.

Quick start

Create a logger with balanced defaults. By default, siglog writes readable, color-aware lines to the console while keeping its internal record format binary.

package main

import (
    "errors"
    "log"
    "time"

    "github.com/PhoneixAI/siglog"
)

func main() {
    logger, err := siglog.New(
        siglog.WithFields(siglog.String("service", "checkout")),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer logger.Close()
    logger.EnableStats(true).TrackErrorTypes(16)

    logger.Info("service started", siglog.Int("port", 8080))
    logger.Warn("slow request", siglog.Duration("elapsed", 280*time.Millisecond))
    logger.Error(
        "request failed",
        siglog.String("error_type", "db_timeout"),
        siglog.NamedErr("err", errors.New("database deadline exceeded")),
    )
    logger.Infof("worker %d is ready", 3)

    if err := logger.Flush(); err != nil {
        log.Printf("siglog flush failed: %v", err)
    }

    log.Print(logger.Stats().Format(true))
}

New starts background flushing automatically. Always call Close when the logger is no longer needed; it stops the flusher, flushes remaining records, and closes transports. Info and Error only enqueue encoded records, so transport failures are reported by Flush, LastError, statistics, and Close, not by the logging call itself.

Configure the buffer: three levels

1. Defaults
logger, err := siglog.New()

The balanced default profile uses four shards with 64 KiB physical capacity per shard, a 64 KiB flush threshold, and a 250 ms periodic flush interval. These are starting values, not throughput guarantees.

2. Choose a profile
logger, err := siglog.New(
    siglog.WithBufferProfile(siglog.ProfileHighThroughput),
)

Available profiles:

Profile Shards Physical capacity per shard Flush threshold Interval
ProfileBalanced (default) 8 64 KiB 64 KiB 250 ms
ProfileLowMemory 4 16 KiB 8 KiB 100 ms
ProfileHighThroughput 32 256 KiB 256 KiB 250 ms

Profiles are convenient presets. Benchmark them with your own message sizes, goroutine count, and transport before selecting one for production.

3. Set both shard parameters manually
logger, err := siglog.New(
    siglog.WithBuffer(siglog.BufferConfig{
        ShardCount:    8,
        ShardCapacity: 64 * siglog.KB, // physical capacity, including one reserved byte
    }),
    siglog.WithFlushSize(32 * siglog.KB),
    siglog.WithFlushInterval(100 * time.Millisecond),
)

WithBuffer and WithBufferProfile are mutually exclusive. The constructor rejects invalid configurations, including a flush threshold larger than the total writable capacity. A physical shard capacity of N stores at most N-1 bytes, and each encoded record must fit inside one shard.

Select the destination

Without WithTransport, output goes to the readable console transport. Supply a different transport when you want binary records written to a file or network endpoint:

file, err := os.OpenFile("app.siglog", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o640)
if err != nil {
    return err
}

logger, err := siglog.New(
    siglog.WithTransport(siglog.NewFileTransport(file, siglog.SyncNever)),
    siglog.WithBufferProfile(siglog.ProfileBalanced),
)
if err != nil {
    _ = file.Close()
    return err
}
defer logger.Close()

When a non-console transport is selected, it receives the binary record stream. The console transport is the one that decodes records into human-readable output.

Basic API

Logging levels
logger.Debug("cache lookup", siglog.String("key", "user:42"))
logger.Info("request completed", siglog.Int("status", 200))
logger.Warn("retrying request", siglog.Int("attempt", 2))
logger.Error("database query failed", siglog.String("error_type", "db_timeout"))
logger.Fatalf("configuration is invalid: %s", reason) // logs; does not exit the process

Debug, Info, Warn, Error, and Fatal accept a message plus typed fields. The *f methods format a message with fmt.Sprintf semantics. Fatal logs at the fatal level but intentionally does not terminate the process.

Context fields

Use With for fields that should accompany every record from a derived logger:

requestLogger := logger.With(
    siglog.String("request_id", requestID),
    siglog.String("component", "payments"),
)
requestLogger.Info("payment authorized", siglog.String("currency", "EUR"))

A derived logger shares the parent pipeline, statistics, and rate-limit configuration.

Structured fields

Available constructors include:

Constructor Use
String(key, value) Text values
Int(key, value) Signed integers
Uint(key, value) / Uint64(key, value) Unsigned integers
Float(key, value) Floating-point values
Bool(key, value) Booleans
Time(key, value) time.Time values
Duration(key, value) time.Duration values
NamedErr(key, err) Error message as a field
RawBytes(key, value) Arbitrary bytes
UUID(key, value) A 16-byte UUID value

For error statistics, use a stable classification such as siglog.String("error_type", "db_timeout") rather than unique error messages or request IDs.

Buffering and shutdown

Most applications do not need to construct a Pipeline; New creates and starts it internally. Use Logger.Flush() to synchronously attempt to send the current batch and Logger.Close() to stop background work, flush remaining records, and close transports.

WithFlushSize requests a flush when the approximate buffered byte count reaches the configured threshold. WithFlushInterval controls periodic flushing; zero disables timer-based flushing, but size-triggered and explicit flushes still work.

NewWithPipeline remains available as a low-level integration hook for tests or advanced setups that manage a Pipeline themselves. Its caller must start that pipeline. Prefer New with functional options for normal application code.

Transports

NewTransport(...) creates a MultiTransport fan-out group. It sends a batch to each configured destination and returns combined errors.

Console
console := siglog.NewConsoleTransport() // human-readable, ANSI color auto-detected
console.WithColor(false)                // explicitly disable colors

The console transport decodes binary records to readable lines. Colors are automatically enabled only for terminal output and respect NO_COLOR. WithWriter can direct output to another io.Writer.

File
file, err := os.OpenFile("app.siglog", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o640)
if err != nil {
    return err
}
fileTransport := siglog.NewFileTransport(file, siglog.SyncNever)

Use SyncAlways when each batch should call File.Sync; this has a performance cost. The transport owns and closes the supplied file.

TCP, UDP, and HTTP
tcp, err := siglog.NewTCPTransport("127.0.0.1:9000")
if err != nil {
    return err
}
udp, err := siglog.NewUDPTransport("127.0.0.1:9001")
if err != nil {
    return err
}
httpTransport := siglog.NewHTTPTransport("https://logs.example.com/ingest", nil)
httpTransport.Headers.Set("Authorization", "Bearer <token>")

TCP uses a persistent connection. UDP sends a batch as one datagram, so keep the batch small enough for your network. HTTP sends batches with POST and reports non-2xx statuses as errors. Configure timeouts and headers according to your environment.

Retries and error handling

  • RetryPolicy.MaxAttempts includes the initial attempt. The default policy allows three attempts with a short bounded backoff.
  • HTTP 429 and 5xx responses are retryable. Other HTTP 4xx statuses are treated as permanent errors.
  • Custom transports can implement Retryable() bool on their error type to classify failures. Unclassified custom transport errors are retried up to the configured limit.
  • File and TCP write failures are not replayed automatically if a partial write may already have occurred; replaying the full batch could duplicate a prefix and corrupt the byte stream.
  • After the configured attempts are exhausted, Logger.Flush() returns an error. The failed batch is not retained or retried in the background; it is counted as unconfirmed and may be lost.
  • Periodic flush errors can be inspected with logger.LastError(), logger.Stats(), and the error returned by logger.Close().

Logging methods such as Info and Error do not return transport errors because records are buffered. Use explicit logger.Flush() when the caller needs to handle a flush error immediately.

Rate limiting and statistics

logger.LimitRate(1_000, time.Second) // at most 1,000 log calls per fixed one-second window
logger.EnableStats(true)             // aggregate statistics are disabled by default
logger.TrackErrorTypes(32)           // optional bounded map of error classifications

stats := logger.Stats()
log.Print(stats)              // plain text
log.Print(stats.Format(true)) // ANSI-colored text

Rate-limit settings and statistics are shared by loggers created with With. The rate limiter uses fixed windows, which may permit a burst near a window boundary. The error-type map is bounded; additional distinct classifications are grouped under other.

Statistics include attempted calls by level, dropped/rate-limited calls, encoding and buffer errors, flush errors, unconfirmed records, and optionally counts by error type. These are diagnostics, not a durable audit ledger.

Binary record format

The current record format is length-prefixed and uses big-endian integers:

Part Size Purpose
Total record length 4 bytes Skip to the next record without decoding this one
Timestamp 8 bytes Unix timestamp in nanoseconds
Level 1 byte DEBUG, INFO, WARN, ERROR, or FATAL
Payload header 12 bytes Caller-file length, caller line, message length, field count
Payload Variable Caller path, message, and typed field entries

Every field entry contains a type byte, key length, value length, key bytes, and value bytes. Numeric values occupy fixed-width binary slots; strings and raw bytes are stored directly. This is intended for fast sequential traversal and compact batching, not for direct display in a terminal.

The binary format is not yet versioned as a stable public wire protocol. Treat it as an implementation detail until a format version and compatibility policy are published.

Build and test

gofmt -w *.go
go test ./...
go test -race ./...
go vet ./...
go test -cover ./...

Run the benchmarks with:

go test -run '^$' -bench . -benchmem ./...

Always benchmark on the target hardware and workload. Throughput, allocation counts, and contention depend on shard count, record size, flush interval, transport latency, and concurrency.

CLI

siglog is a Go library, not a standalone command-line application. It does not currently provide commands such as siglog stats or siglog decode. ConsoleTransport provides readable terminal output inside an application, and LoggerStats.Format(true) formats colored statistics. A separate binary-log inspection CLI can be added after the binary format is versioned.

Delivery and current limitations

  • The ring buffer uses fixed-capacity shards. Its current overflow path can discard previously buffered bytes when the attempted shards are full; this must be resolved before relying on lossless behavior.
  • Record ordering across shards is not globally deterministic.
  • Delivery is best-effort. Retries do not provide exactly-once delivery, and multi-transport fan-out can have partial success.
  • Failed batches are not retained after the bounded retry policy is exhausted. Monitor LastError, flush errors, and unconfirmed-record statistics.
  • Do not use this pre-release as the sole storage path for audit, compliance, or financial events that must never be lost.

License

Choose a license and add the corresponding LICENSE file before publishing this repository.

Documentation

Index

Constants

View Source
const (
	KB = 1 << (10 * iota) // 1 << 10 (1 Килобайт)KB
	MB = 1 << (10 * iota) // 1 << 20 (1 Мегабайт)MB
	GB = 1 << (10 * iota) // 1 << 30 (1 Гигабайт)GB
)

Variables

View Source
var (
	// ErrNoPipeline is returned when Flush is called without a configured pipeline.
	ErrNoPipeline = errors.New("siglog: logger has no pipeline")
	// ErrNoTransport is returned when the configured pipeline has no transport.
	ErrNoTransport = errors.New("siglog: pipeline has no transport")
	// ErrPipelineClosed is returned when trying to add a record after Close begins.
	ErrPipelineClosed = errors.New("siglog: pipeline is closing or closed")
	// ErrRecordTooLarge is returned when a record cannot fit in one shard.
	ErrRecordTooLarge = errors.New("siglog: record exceeds shard writable capacity")
	// ErrBufferFull is retained for explicit buffer-full reporting.
	ErrBufferFull = errors.New("siglog: buffer is full")
	// ErrTransportClosed is returned when writing to a closed transport.
	ErrTransportClosed = errors.New("siglog: transport is closed")
	// ErrMalformedRecord is returned when a binary record cannot be decoded.
	ErrMalformedRecord = errors.New("siglog: malformed binary record")
)

Functions

func IsProtocolError

func IsProtocolError(err error) bool

IsProtocolError reports whether err or one of its wrapped errors is a ProtocolError. This helper is optional; errors.As can be used directly.

func Trace

func Trace(message string, err error) error

Trace adds a message to err. It returns nil when err is nil, so wrapping a successful operation cannot accidentally create an error.

Types

type Buffer

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

Buffer distributes records across a fixed ring of shards. The ring of shards is intentionally retained; each Shard owns its byte ring and its lock.

func NewBuffer

func NewBuffer(shardCount int, shardCapacity int) *Buffer

NewBuffer creates a sharded buffer. Each shard reserves one byte, so its writable capacity is shardCapacity-1. Invalid small inputs are normalized.

func (*Buffer) Len

func (b *Buffer) Len() int

Len returns the approximate number of buffered bytes. With size tracking enabled it is an O(1) atomic read; otherwise it scans the shards.

func (*Buffer) SetTrackLen

func (b *Buffer) SetTrackLen(enabled bool)

SetTrackLen enables the aggregate byte counter used by size-triggered flushing. Configure it before concurrent writes. When disabled, Len scans shards on demand and writes avoid a shared atomic update on every record.

type BufferConfig

type BufferConfig struct {
	ShardCount    int
	ShardCapacity int
}

BufferConfig controls the sharded ring buffer. ShardCapacity is the physical byte capacity of each shard, including the one reserved byte that distinguishes a full ring from an empty ring. A shard can therefore store ShardCapacity-1 bytes.

type BufferProfile

type BufferProfile uint8

BufferProfile selects a tested starting point for the ring-buffer configuration. Profiles are defaults to benchmark against your workload, not throughput guarantees.

const (
	// ProfileBalanced favors a moderate memory footprint and general workloads.
	ProfileBalanced BufferProfile = iota + 1
	// ProfileLowMemory reduces the memory reserved for ring buffers.
	ProfileLowMemory
	// ProfileHighThroughput reserves more ring-buffer memory and uses more shards.
	ProfileHighThroughput
)

type CallInfo

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

CallInfo describes the source location associated with a log record. Its fields are private so the value can be extended without breaking callers.

func ReceiveInfo

func ReceiveInfo() *CallInfo

ReceiveInfo returns the file and line of the caller of ReceiveInfo.

func (*CallInfo) File

func (c *CallInfo) File() string

File returns the source file path, or an empty string when unavailable.

func (*CallInfo) Line

func (c *CallInfo) Line() int

Line returns the source line, or zero when unavailable.

func (*CallInfo) String

func (c *CallInfo) String() string

String returns the source location in file:line form.

type Clock

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

Clock caches a formatted wall-clock timestamp. It refreshes every millisecond and should be closed when no longer used. The cached bytes are immutable and never exposed directly to callers.

func NewClock

func NewClock(layout string) *Clock

NewClock starts a timestamp cache using layout, following time.Time.Format. Close stops its background goroutine. An empty layout is passed to Format as-is.

func (*Clock) AppendTime

func (c *Clock) AppendTime(dst []byte) []byte

AppendTime appends the latest cached timestamp to dst. It avoids allocating a separate slice when dst already has enough capacity.

func (*Clock) Close

func (c *Clock) Close()

Close stops the refresh goroutine. It is safe to call more than once.

func (*Clock) TimeBytes

func (c *Clock) TimeBytes() []byte

TimeBytes returns a copy of the latest cached timestamp. The returned bytes may be modified safely by the caller without affecting other readers.

type ConsoleTransport

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

ConsoleTransport decodes binary records and writes human-readable lines. ANSI colors are enabled automatically only when the target writer is a terminal.

func NewConsoleTransport

func NewConsoleTransport() *ConsoleTransport

NewConsoleTransport creates a color-aware, human-readable stdout transport. Use WithWriter to redirect output and WithColor to explicitly control colors.

func (*ConsoleTransport) Close

func (t *ConsoleTransport) Close() error

Close is a no-op because ConsoleTransport does not own stdout or custom writers.

func (*ConsoleTransport) WithColor

func (t *ConsoleTransport) WithColor(enabled bool) *ConsoleTransport

WithColor explicitly enables or disables ANSI colors.

func (*ConsoleTransport) WithWriter

func (t *ConsoleTransport) WithWriter(writer io.Writer) *ConsoleTransport

WithWriter directs console output to a custom writer, useful for tests and embedding. Color detection is not repeated; call WithColor to choose it.

func (*ConsoleTransport) Write

func (t *ConsoleTransport) Write(data []byte) error

type Field

type Field struct {
	Key    string
	StrVal string
	NumVal int64
	Kind   Kind
}

Field represents a key-value pair without using interfaces. NumVal is always 64 bits so timestamps, durations, float bit patterns and uint64 values are encoded consistently on 32-bit and 64-bit Go targets. Use the constructors below rather than constructing numeric fields manually.

func Bool

func Bool(key string, val bool) Field

func Duration

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

func Float

func Float(key string, val float64) Field

func Int

func Int(key string, val int) Field

func NamedErr

func NamedErr(key string, err error) Field

func RawBytes

func RawBytes(key string, b []byte) Field

func String

func String(key, val string) Field

func Time

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

func UUID

func UUID(key string, id [16]byte) Field

func Uint

func Uint(key string, num uint) Field

func Uint64

func Uint64(key string, num uint64) Field

type FileTransport

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

func NewFileTransport

func NewFileTransport(file *os.File, mode SyncMode) *FileTransport

NewFileTransport writes binary batches to file. SyncAlways calls Sync after each batch.

func (*FileTransport) Close

func (t *FileTransport) Close() error

Close closes the underlying file once. The transport takes ownership of file.

func (*FileTransport) Write

func (t *FileTransport) Write(data []byte) error

Write writes the complete batch and optionally syncs it to stable storage.

type HTTPStatusError

type HTTPStatusError struct {
	StatusCode int
	Status     string
	Body       string
}

HTTPStatusError represents a non-2xx response. 429 and 5xx responses are retryable; other status codes are treated as permanent client-side failures.

func (*HTTPStatusError) Error

func (e *HTTPStatusError) Error() string

func (*HTTPStatusError) Retryable

func (e *HTTPStatusError) Retryable() bool

type HTTPTransport

type HTTPTransport struct {
	Client  *http.Client
	Headers http.Header
	// contains filtered or unexported fields
}

func NewHTTPTransport

func NewHTTPTransport(host string, client *http.Client) *HTTPTransport

NewHTTPTransport creates an HTTP POST transport. When client is nil, a client with a five-second timeout is used. A supplied client retains its own timeout settings, so callers should configure one to keep shutdown bounded.

func (*HTTPTransport) Close

func (t *HTTPTransport) Close() error

Close is a no-op; net/http manages HTTP connections through its Client.

func (*HTTPTransport) Write

func (t *HTTPTransport) Write(data []byte) error

Write posts the complete binary batch and rejects non-2xx HTTP status codes.

type Kind

type Kind byte
const (
	KindInt Kind = iota
	KindString
	KindFloat
	KindBool
	KindUint
	KindDuration
	KindRawBytes
	KindUUID
)

type Logger

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

Logger writes binary records through an internal Pipeline. Applications use Logger's methods rather than manipulating the pipeline or its ring-buffer shards. Context fields are immutable after construction; use With to derive a logger.

func New

func New(opts ...Option) (*Logger, error)

New creates a logger using the balanced default profile or the supplied options. With no WithTransport option, output is written as readable, color-aware console lines. New starts background flushing; always call Close when the logger is done.

func NewWithPipeline

func NewWithPipeline(pipeline *Pipeline, fields ...Field) *Logger

NewWithPipeline attaches a logger to an existing Pipeline. This is a low-level integration hook for tests or advanced setups; the caller is responsible for starting the pipeline. Most applications should use New with functional options.

func (*Logger) Close

func (l *Logger) Close() error

Close stops background flushing, performs a final flush, and closes transports. It is safe to call more than once. Derived loggers from With share this lifecycle.

func (*Logger) Debug

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

Debug logs a debug message.

func (*Logger) Debugf

func (l *Logger) Debugf(format string, args ...any)

Debugf logs a formatted debug message.

func (*Logger) EnableStats

func (l *Logger) EnableStats(enabled bool) *Logger

EnableStats controls aggregate counters. Statistics are disabled by default to minimize hot-path overhead. Enabling them starts counting immediately; disabling them preserves already collected values without further updates.

func (*Logger) Error

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

Error logs an error message.

func (*Logger) Errorf

func (l *Logger) Errorf(format string, args ...any)

Errorf logs a formatted error message.

func (*Logger) Fatal

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

Fatal logs a fatal-level record. It deliberately does not terminate the process.

func (*Logger) Fatalf

func (l *Logger) Fatalf(format string, args ...any)

Fatalf logs a formatted fatal-level message without terminating the process.

func (*Logger) Flush

func (l *Logger) Flush() error

Flush sends the currently buffered batch to configured transports and returns the final transport error after the configured bounded retry attempts.

func (*Logger) Info

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

Info logs an informational message.

func (*Logger) Infof

func (l *Logger) Infof(format string, args ...any)

Infof logs a formatted informational message.

func (*Logger) LastError

func (l *Logger) LastError() error

LastError returns the most recent asynchronous flush failure, if any.

func (*Logger) LimitRate

func (l *Logger) LimitRate(max int, interval time.Duration) *Logger

LimitRate allows at most max messages per fixed interval. A non-positive max or interval disables limiting. The configuration is shared with loggers derived through With. Configure the limit before concurrent log calls. Fixed windows are efficient but can allow a burst at a window boundary.

func (*Logger) LogDebug

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

func (*Logger) LogError

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

func (*Logger) LogFatal

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

func (*Logger) LogInfo

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

LogInfo, LogWarn, LogError, LogDebug and LogFatal are explicit-name aliases.

func (*Logger) LogWarn

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

func (*Logger) Stats

func (l *Logger) Stats() LoggerStats

Stats returns a point-in-time snapshot of activity counters. Per-error-type counts are empty unless TrackErrorTypes was explicitly enabled.

func (*Logger) TrackErrorTypes

func (l *Logger) TrackErrorTypes(maxKinds int) *Logger

TrackErrorTypes enables bounded per-error-type counters and aggregate stats. maxKinds limits the number of concrete error-type keys; additional keys are counted under the extra "other" bucket. Passing maxKinds <= 0 disables detailed tracking. Aggregate counters remain enabled until EnableStats(false).

func (*Logger) Warn

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

Warn logs a warning message.

func (*Logger) Warnf

func (l *Logger) Warnf(format string, args ...any)

Warnf logs a formatted warning message.

func (*Logger) With

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

With returns a derived logger, preserving context fields and sharing the pipeline, statistics and rate-limit configuration with the parent.

type LoggerStats

type LoggerStats struct {
	Total                   uint64            `json:"total"`
	Debug                   uint64            `json:"debug"`
	Info                    uint64            `json:"info"`
	Warn                    uint64            `json:"warn"`
	Error                   uint64            `json:"error"`
	Fatal                   uint64            `json:"fatal"`
	Dropped                 uint64            `json:"dropped"`
	RateLimited             uint64            `json:"rate_limited"`
	EncodingErrors          uint64            `json:"encoding_errors"`
	FlushErrors             uint64            `json:"flush_errors"`
	FlushRecordsUnconfirmed uint64            `json:"flush_records_unconfirmed"`
	BufferErrors            uint64            `json:"buffer_errors"`
	ErrorsByType            map[string]uint64 `json:"errors_by_type,omitempty"`
}

LoggerStats is a snapshot of logging activity collected while EnableStats(true) is active. Statistics are disabled by default to minimize high-load overhead. Total and per-level counters count attempted calls, including rate-limited or otherwise dropped calls. RateLimited is a subset of Dropped. FlushErrors reflects batch-write failures reported by the attached Pipeline. ErrorsByType is derived from the "error_type" or "error" string field on Error/Fatal calls; a stable value such as "db_timeout" is preferable.

func (LoggerStats) Format

func (s LoggerStats) Format(color bool) string

Format returns a compact statistics report. Set color=true to request ANSI colors; callers should disable it for files, CI logs, and non-terminal output.

func (LoggerStats) String

func (s LoggerStats) String() string

String renders a stable, human-readable, non-colored representation of the snapshot.

type MultiTransport

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

MultiTransport fans each binary batch out to every configured destination. Delivery is best-effort with bounded retries; if the whole batch is retried later after one destination succeeded and another failed, successful sinks may receive the same batch more than once.

func NewTransport

func NewTransport(transports ...Transport) *MultiTransport

NewTransport creates a group that writes each batch to every configured transport. A failing transport is retried independently, so another transport's successful write is not repeated within the same Write call.

func (*MultiTransport) Close

func (t *MultiTransport) Close() error

Close closes each configured transport once and joins any errors.

func (*MultiTransport) SetRetryPolicy

func (t *MultiTransport) SetRetryPolicy(policy RetryPolicy) error

SetRetryPolicy configures retries for subsequent writes. Configure this before starting concurrent logging. MaxAttempts=1 disables retries.

func (*MultiTransport) Write

func (t *MultiTransport) Write(data []byte) error

Write sends a batch to every transport, retrying failures according to RetryPolicy.

type MultiTransportError

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

MultiTransportError reports failures from one or more destinations. It wraps all per-transport errors for errors.Is/errors.As and is retryable only when every failed destination classified its final error as retryable.

func (*MultiTransportError) Error

func (e *MultiTransportError) Error() string

func (*MultiTransportError) Retryable

func (e *MultiTransportError) Retryable() bool

Retryable is false if any failed destination reported a permanent error.

func (*MultiTransportError) Unwrap

func (e *MultiTransportError) Unwrap() error

type Option

type Option func(*loggerConfig) error

Option configures a Logger created by New. Options are validated before any background goroutine is started.

func WithBuffer

func WithBuffer(config BufferConfig) Option

WithBuffer uses an explicit shard count and physical capacity per shard. Flush settings remain independently configurable. If WithFlushSize is not supplied, the constructor chooses a conservative threshold based on capacity.

func WithBufferProfile

func WithBufferProfile(profile BufferProfile) Option

WithBufferProfile selects a preset for shard count, per-shard capacity, flush interval, and flush threshold. Later WithFlushInterval/WithFlushSize options override the profile's corresponding values. It cannot be combined with WithBuffer; choose either a profile or explicit shard settings.

func WithFields

func WithFields(fields ...Field) Option

WithFields adds persistent context fields that are included in every record written by the new logger. Call Logger.With to derive a contextual logger later.

func WithFlushInterval

func WithFlushInterval(interval time.Duration) Option

WithFlushInterval sets the period for background flushing. A value of zero disables timer-triggered flushing; size-triggered flushes and Close still work.

func WithFlushSize

func WithFlushSize(size int) Option

WithFlushSize requests a flush when buffered bytes reach size. Zero disables size-triggered flushing. A negative size is invalid.

func WithRetryPolicy

func WithRetryPolicy(policy RetryPolicy) Option

WithRetryPolicy configures the bounded retry behavior for transport writes. MaxAttempts includes the initial write; a value of one disables retries.

func WithTransport

func WithTransport(transports ...Transport) Option

WithTransport adds one or more destinations. Batches are sent to all configured transports through MultiTransport. If no transport option is supplied, New uses a human-readable console transport.

type Pipeline

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

func NewPipeline

func NewPipeline(count, capacity int, timeout time.Duration, transport *MultiTransport) *Pipeline

NewPipeline creates a pipeline. The shard count and capacities are normalized by NewBuffer. A timeout <= 0 disables timer-based flushes; Flush or Close still drains buffered records.

func (*Pipeline) Close

func (p *Pipeline) Close() error

Close is idempotent. It prevents new pushes, waits for Start if running, flushes the records still in the ring buffer, closes transports, and returns any final flush/close error. If an earlier batch failed, Close also reports the most recent flush failure so asynchronous errors are not silently lost.

func (*Pipeline) Flush

func (p *Pipeline) Flush(flushing []byte) error

Flush writes an already assembled batch. It returns an error after the MultiTransport retry policy is exhausted; the supplied batch is not retained. Use FlushBuffer or Logger.Flush to drain the pipeline's ring buffer.

func (*Pipeline) FlushBuffer

func (p *Pipeline) FlushBuffer() error

FlushBuffer drains records from Buffer and writes them to the configured transport. On failure, it returns the final transport error and discards that unconfirmed batch rather than retaining memory or retrying it automatically. It serializes periodic, size-triggered, and explicit flush operations.

func (*Pipeline) FlushErrors

func (p *Pipeline) FlushErrors() uint64

FlushErrors returns the cumulative number of batch-write failures after the configured MultiTransport retry policy was exhausted.

func (*Pipeline) LastError

func (p *Pipeline) LastError() error

LastError returns the most recent flush failure. Successful later flushes do not clear it, because a failed batch has already been reported as unconfirmed.

func (*Pipeline) Push

func (p *Pipeline) Push(data []byte) error

Push adds one complete encoded record to the ring buffer.

func (*Pipeline) SetFlushSize

func (p *Pipeline) SetFlushSize(size int) *Pipeline

SetFlushSize requests a flush once the approximate buffered byte count reaches size. A non-positive size disables size-triggered flushes. Call before Start for deterministic configuration.

func (*Pipeline) Start

func (p *Pipeline) Start()

Start runs the periodic/size-triggered flusher until Close is called. Calling Start more than once has no effect. Each failed batch is attempted according to MultiTransport's bounded RetryPolicy and then reported through LastError, statistics, and Close; Pipeline does not retry that batch in the background.

type ProtocolError

type ProtocolError struct {
	Protocol string
	Address  string
	Message  string
}

ProtocolError describes a protocol-level failure returned by a transport.

func (*ProtocolError) Error

func (e *ProtocolError) Error() string

type RetryClassifier

type RetryClassifier interface {
	Retryable() bool
}

RetryClassifier lets a custom transport error control whether MultiTransport retries it. Errors without this interface are retried using the configured bounded policy; errors classified as permanent are returned immediately.

type RetryPolicy

type RetryPolicy struct {
	MaxAttempts    int
	InitialBackoff time.Duration
	MaxBackoff     time.Duration
}

RetryPolicy controls transport write retries. MaxAttempts includes the first attempt. A value of one disables retries.

type Shard

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

Shard is a fixed-size, lock-protected ring buffer. One byte is deliberately left unused, so a physical capacity of N can store at most N-1 bytes.

func NewShard

func NewShard(capacity int) *Shard

NewShard creates a ring buffer. Capacity is normalized to at least two bytes because one slot is reserved to distinguish full from empty.

type SyncMode

type SyncMode uint8

SyncMode controls whether FileTransport calls fsync after each write.

const (
	SyncNever SyncMode = iota
	SyncAlways
)

type TCPTransport

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

TCPTransport writes batches over a persistent TCP connection. Writes have a finite default deadline so Pipeline.Close cannot wait forever on a stalled peer.

func NewTCPTransport

func NewTCPTransport(address string) (*TCPTransport, error)

NewTCPTransport connects to address, limiting connection establishment to five seconds.

func (*TCPTransport) Close

func (t *TCPTransport) Close() error

Close closes the TCP connection once and returns the same close error thereafter.

func (*TCPTransport) WithWriteTimeout

func (t *TCPTransport) WithWriteTimeout(timeout time.Duration) *TCPTransport

WithWriteTimeout sets the deadline for each TCP write. A non-positive value disables the deadline; configure it before concurrent use.

func (*TCPTransport) Write

func (t *TCPTransport) Write(data []byte) error

Write sends all bytes over TCP, handling short writes until complete or error.

type TracedError

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

TracedError adds context to an existing error while preserving its identity for errors.Is and errors.As.

func (*TracedError) Error

func (e *TracedError) Error() string

func (*TracedError) Unwrap

func (e *TracedError) Unwrap() error

Unwrap exposes the wrapped error to the standard errors package.

type Transport

type Transport interface {
	Write(data []byte) error
	Close() error
}

Transport is a destination for an encoded batch of binary log records.

type UDPTransport

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

UDPTransport sends each batch as one datagram. The entire batch must fit in a single UDP datagram; callers should use small FlushSize/record sizes.

func NewUDPTransport

func NewUDPTransport(address string) (*UDPTransport, error)

NewUDPTransport connects a UDP socket to address with a bounded dial timeout.

func (*UDPTransport) Close

func (t *UDPTransport) Close() error

Close closes the UDP connection once and returns the same close error thereafter.

func (*UDPTransport) WithWriteTimeout

func (t *UDPTransport) WithWriteTimeout(timeout time.Duration) *UDPTransport

WithWriteTimeout sets the deadline for each UDP datagram write. A non-positive value disables the deadline; configure it before concurrent use.

func (*UDPTransport) Write

func (t *UDPTransport) Write(data []byte) error

Write sends exactly one UDP datagram; it never splits a short write into multiple packets.

Jump to

Keyboard shortcuts

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