Skip to main content

Sinks

A sink is a destination for LogRecords. The logger fans every emitted record out to every attached sink that passes its min_severity filter. Sinks are independent — a failure in one does not affect the others.

The Sink protocol

Every sink implements four methods:

Sink {
id: string # diagnostic identifier (e.g., "console:auto", "file:/var/log/app.jsonl")
emit(record): void # non-blocking; queues the record for delivery
flush(timeout): void # block until buffered records are delivered or the timeout fires
close(): void # flush + release resources
supports_severity(severity_number): bool # filter hint; rejected severities are skipped early
}

The emit method does not block the caller. Blocking I/O (network exporters, file flush) goes through an internal buffer plus a worker; if the buffer overflows, the sink drops the oldest record by default and increments a drop counter. The buffer / worker is sink-specific — the Phase 1 ConsoleSink and FileSink synchronise stdout / file writes under a lock, while the planned OTLPSink (Phase 2) uses a background worker.

Phase 1 sinks

Three sinks ship in the first release of every binding:

SinkWhere records goDefault modeConfiguration
ConsoleSinksys.stderr (default) or any text streamauto (TTY → pretty, non-TTY → JSON)mode, stream, min_severity
FileSinkA file on disk, JSON-linesJSON-lines with size-based rotationpath, max_bytes, keep, min_severity
InMemorySinkAn in-process ring bufferdropped on overflowcapacity, min_severity

ConsoleSink

ConsoleSink writes records to sys.stderr (or a chosen text stream) as either coloured pretty text or compact JSON-lines. The default mode="auto" switches based on TTY detection — an interactive terminal gets the pretty mode; piped or redirected output gets JSON.

from dagstack.logger import ConsoleSink

# Auto mode: pretty on a TTY, JSON otherwise.
sink = ConsoleSink(mode="auto")

# Force JSON for container logs.
sink = ConsoleSink(mode="json", min_severity=9)

# Force pretty for a debug terminal.
sink = ConsoleSink(mode="pretty")

FileSink

FileSink writes Canonical JSON-lines to a file, rotating by file size. The Python binding uses stdlib RotatingFileHandler under the hood — a battle-tested rotation implementation — and applies the dagstack JSON-lines format on top.

from dagstack.logger import FileSink

sink = FileSink(
"/var/log/order-service.jsonl",
max_bytes=100_000_000, # rotate at 100 MB
keep=10, # keep 10 archived files
min_severity=9, # INFO and above
)

max_bytes=0 disables rotation (the file grows until the operator truncates or moves it). keep=0 deletes archived files immediately on rotation — only the live file remains.

InMemorySink

InMemorySink accumulates records in a bounded ring buffer; the oldest records are dropped when the buffer is full. Designed for tests and for short-lived diagnostic captures.

from dagstack.logger import InMemorySink

sink = InMemorySink(capacity=100)
# ... emit some records ...

records = sink.records() # snapshot copy
assert any(r.body == "expected message" for r in records)

sink.clear() # reset for the next test

InMemorySink does not implement wire serialisation — it stores LogRecord objects directly so test assertions can inspect typed body, attributes, and severity_number without parsing JSON.

Multi-sink routing

A logger can be configured with multiple sinks. Each sink applies its own min_severity filter independently of the others. A common production setup writes warnings and above to the console, all INFO+ to a file for forensics, and errors only to a remote sink.

from dagstack.logger import ConsoleSink, FileSink, configure

configure(
root_level="DEBUG",
sinks=[
ConsoleSink(mode="pretty", min_severity=13), # WARN+ on the console
FileSink("/var/log/app.jsonl", max_bytes=100_000_000, keep=10, min_severity=9),
],
)

Roadmap

The Phase 1 set ships in every binding. Phase 2 adds the OTel exporter and a few common production sinks; Phase 3 adds cloud and high-throughput integrations.

SinkPhaseNotes
OTLPSink2OTLP/gRPC or OTLP/HTTP — the primary production exporter.
SyslogSink2BSD syslog and RFC 5424 transports.
SentrySink2ERROR and above only; body and attrs become a Sentry event.
LokiSink2For deployments without an OTel collector.
FluentBitForwardSink2Fluent Bit Forward protocol — common sidecar pattern.
CloudWatchLogsSink3AWS CloudWatch Logs with batch put.
GCPCloudLoggingSink3Google Cloud Logging with OAuth / service account.
KafkaSink3High-throughput pipelines.
ElasticsearchSink3Elasticsearch bulk API.

See also