Skip to content

Logging

Introduction

To help you learn more about what's happening within your application, Arvel provides logging through the Log facade. Logging is structured and OpenTelemetry-backed: every message carries a name plus arbitrary key/value context, and flows into the OTel pipeline alongside your traces and metrics.

The observability subsystem is auto-registeredLog works out of the box, no provider to add.

Writing Log Messages

The first argument is a short, machine-friendly message name; everything else is keyword context:

from arvel.facades import Log

Log.info("user.created", user_id=42)
Log.warning("rate.limited", ip="203.0.113.5")

Log Levels

Method When to use
Log.debug(message, **context) Diagnostic detail for development
Log.info(message, **context) Normal, noteworthy events
Log.warning(message, **context) Something unexpected but recoverable
Log.error(message, *, exc=None, **context) A failure; pass the exception via exc=
Log.critical(message, **context) A failure demanding immediate attention

error takes a keyword-only exc so the exception and stack are captured:

try:
    await charge(card)
except PaymentError as e:
    Log.error("charge.failed", exc=e, amount=99)

Structured Context

Pass any keyword arguments and they become structured fields on the log record — no string interpolation. This keeps logs queryable:

Log.info("order.placed", order_id=order.id, total=order.total, currency="USD")

Contextual Information

Sometimes you want every log line in a unit of work to share the same fields. with_context returns a child logger with those fields bound:

log = Log.with_context(request_id="abc123")
log.info("request.received")
log.warning("rate.limited")   # both carry request_id=abc123

Channels

channel(name) returns a logger that uses name as its OpenTelemetry instrumentation scope — a clean way to attribute logs to a module:

logger = Log.channel("payments")
logger.error("charge.failed", amount=99)

This is the idiomatic module-level pattern across the framework:

from arvel.facades import Log

logger = Log.channel(__name__)

Configuration

Logging is configured through ObservabilityConfig (the OTEL_* environment variables), shared with tracing and metrics. The ObservabilityServiceProvider is auto-registered and mounts the ObservabilityMiddleware, so request context is attached to logs automatically.