Skip to content

blackbull.logger

blackbull.logger

Queued logging and import-time logging gates.

Configure DEBUG before importing guarded code: raising logger levels later does not enable log/debug_gate wrappers. Sink I/O belongs off the event loop.

BatchWriteHandler

Bases: Handler

Flush formatted records when a batch fills or its interval expires.

One flusher thread handles all batches. close() drains trailing records and joins it. Do not inherit this live thread across fork.

ColoredFormatter

Bases: Formatter

A logging.Formatter that colours %(levelname)s by severity.

Takes the same fmt / datefmt / style arguments as its base and behaves identically except that the level name is wrapped in an ANSI colour sequence. Only the copy of the record being formatted is coloured, so other handlers on the same record still see the plain name.

The escape sequences are written unconditionally — this is a terminal format, and pointing it at a file or a pipe puts them in the output. Use JsonFormatter or a plain logging.Formatter for anything that is not a terminal.

JsonFormatter

Bases: Formatter

Emit one JSON object per log line (logging approach 3 — structured JSON).

Every record carries timestamp / level / logger / message. Access-log records (blackbull.access) additionally attach the structured fields from AccessLogRecord.as_extra via extra= — those are lifted to first-class JSON keys (client_ip, method, path, http_version, status, response_bytes, duration_ms, and close_code for WebSocket disconnects). exc_info is rendered as a formatted traceback string when present.

Runs on the QueueListener thread (it is the sink handler's formatter), so the access record's format() string build still happens off the event loop, exactly as with the plain-text default.

debug_gate(logger)

Capture DEBUG enablement at import time.

Configure the level before importing guarded code; later level changes do not update this bool. Guard argument evaluation as well as the logging call.

enqueue_access_log(msg, extra=None)

Enqueue an access record; return False when synchronous fallback is needed.

The caller owns the INFO gate. This bypasses handler/filter chains, so use it only when the access logger has no user handlers or filters.

log(fn)

Decorator: log call arguments at DEBUG level.

Automatically uses the logger of the module where @log is applied, determined by inspecting the caller's frame at decoration time.

When the module logger is not enabled for DEBUG at decoration time (i.e. at import), the decorator is a zero-cost no-op: the original function is returned unwrapped, so there is no extra function-call overhead in production. The trade-off is that raising the log level to DEBUG after modules have been imported will not activate logging for already-decorated functions.

setup_async_logging(handlers=None, *, log_format=None, syslog_addr=None, batch_size=None, batch_timeout_ms=None, log_file=None)

Install a QueueHandler on the blackbull logger hierarchy.

After this call every logger.debug/info/warning in the event loop enqueues a LogRecord and returns immediately. A daemon thread (QueueListener) drains the queue and forwards records to handlers.

Idempotent — a second call before teardown_async_logging is a no-op.

Parameters

handlers: Handlers the background listener should write to. Defaults to the non-NullHandler handlers already on the blackbull logger, or the sink built by _build_sink_handlers when none exist. log_format, syslog_addr, batch_size, batch_timeout_ms, log_file: Sink configuration forwarded to _build_sink_handlers (only used when handlers is None and no handlers are pre-attached). The server startup passes these from Settings (get_settings()); each defaults to None → the matching BB_* env var.

teardown_async_logging()

Stop the QueueListener and restore a NullHandler on blackbull.