Built-in handlers

Updated Nov 11, 2025

FemtoStreamHandler

  • FemtoStreamHandler.stderr() (default) and .stdout() log to the respective standard streams. Calls return immediately; the handler thread flushes the underlying io::Write.
  • handler.flush() waits (up to one second by default) for the worker to flush buffered writes and returns True on success. handler.close() shuts down the worker thread and should be called before process exit.
  • To tune capacity, flush timeout, or formatters use StreamHandlerBuilder. It provides .with_capacity(n), .with_flush_timeout_ms(ms), and .with_formatter(callable_or_id) fluent methods before calling .build().

FemtoFileHandler

  • Constructor signature: FemtoFileHandler(path, capacity=1024, flush_interval=1, policy="drop"). The flush interval counts records, not seconds.
  • policy controls queue overflow handling:
  • "drop" (default) discards records and raises RuntimeError.
  • "block" blocks the caller until the worker makes room.
  • "timeout:N" blocks for N milliseconds before giving up.
  • Use handler.flush() and handler.close() to ensure on-disk consistency. Always close the handler when the application shuts down.
  • FileHandlerBuilder mirrors these options and also exposes .with_overflow_policy(OverflowPolicy.drop()/block()/timeout(ms)) and .with_formatter(…). Formatter identifiers other than "default" are not wired up yet; pass a callable (taking a mapping and returning a string) to attach custom formatting logic.

FemtoRotatingFileHandler

  • Wraps FemtoFileHandler with size-based rotation. Instantiate via FemtoRotatingFileHandler(path, options=HandlerOptions(…)).
  • HandlerOptions fields:
  • capacity, flush_interval, and policy mirror FemtoFileHandler.
  • rotation=(max_bytes, backup_count) enables rollover when both values are greater than zero. Set (0, 0) to disable rotation entirely.
  • Rotation renames the active log file to .1, shifts older backups up to backup_count, and truncates the live file. If opening a fresh file fails the implementation falls back to appending to the existing file and logs the reason.
  • RotatingFileHandlerBuilder provides the same fluent API as the file builder plus .with_max_bytes() and .with_backup_count().

FemtoSocketHandler

  • Socket handlers must be built via SocketHandlerBuilder. Typical usage:
from femtologging import SocketHandlerBuilder

socket_handler = (
    SocketHandlerBuilder()
    .with_tcp("127.0.0.1", 9020)
    .with_capacity(2048)
    .with_connect_timeout_ms(5000)
    .with_write_timeout_ms(1000)
    .with_tls("logs.example.com", insecure=False)
    .with_backoff(base_ms=100, cap_ms=5000, reset_after_ms=30000, deadline_ms=120000)
    .build()
)
  • Default transport is TCP to localhost:9020. Call .with_unix_path() to use Unix sockets on POSIX systems. TLS only works with TCP transports; attempting to combine TLS and Unix sockets raises HandlerConfigError.
  • Records are serialized to MessagePack maps: {logger, level, message, timestamp_ns, filename, line_number, module_path, thread_id, thread_name, key_values} and framed with a 4-byte big-endian length prefix.
  • The worker reconnects automatically using exponential backoff. Payloads that exceed the configured max_frame_size are dropped.
  • handler.flush() forces the worker to flush the active socket. Always call handler.close() to terminate the worker thread cleanly.

Custom Python handlers

class Collector:
    def __init__(self) -> None:
        self.records: list[tuple[str, str, str]] = []

    def handle(self, logger: str, level: str, message: str) -> None:
        # Runs inside the FemtoLogger worker thread
        self.records.append((logger, level, message))

collector = Collector()
logger.add_handler(collector)

The handler’s handle method must be callable and thread-safe. Exceptions are printed to stderr and counted as handler errors, so prefer defensive code and avoid raising from handle.