axonpush
Python SDKIntegrations

Loguru

A Loguru sink that forwards records to axonpush as OpenTelemetry-shaped app.log events.

pip install "axonpush[loguru]"

Tested against loguru>=0.7,<1.0.

Add the sink

from loguru import logger
from axonpush import AxonPush
from axonpush.integrations.loguru import create_axonpush_loguru_sink

client = AxonPush()

sink = create_axonpush_loguru_sink(
    client=client,
    channel_id=channel_id,
    service_name="my-api",
    environment="production",
)

logger.add(sink, serialize=True)

serialize=True is required. It tells Loguru to hand the sink a JSON string of the record, which is what the sink parses. Without it the sink receives a formatted message and cannot recover the structured fields.

Then log normally. Keyword arguments and bound context both become attributes:

logger.info("user signed in", user_id=42, method="oauth")
logger.warning("rate limit approaching", endpoint="/api/search", remaining=3)

try:
    search()
except RuntimeError:
    logger.exception("search backend failed", endpoint="/api/search")

request_logger = logger.bind(request_id="req-9f21", user_id=42)
request_logger.info("handling request")

Constructor

create_axonpush_loguru_sink(
    *,
    client,                  # AxonPush or AsyncAxonPush - required
    channel_id,              # str UUID - required
    source="app",            # "app" -> app.log, "agent" -> agent.log
    service_name=None,
    service_version=None,
    environment=None,
    agent_id=None,
    mode=None,               # "background" (default) | "sync"
    queue_size=1000,
    shutdown_timeout=2.0,
)

Every argument is keyword-only. source and mode are validated at construction, anything other than "agent"/"app" and "background"/"sync" raises ValueError.

In "background" mode the call site stays O(microseconds): the sink pushes onto a bounded queue drained by one daemon thread. Pass a sync AxonPush for this, an AsyncAxonPush gets no worker and publishes inline.

What each record becomes

FieldValue
identifierThe record’s name, the module that logged it
event_typeapp.log, or agent.log when source="agent"
payload.bodyThe Loguru message
payload.severityNumber / payload.severityTextOTel severity mapped from the Loguru level name
payload.timeUnixNanoThe record timestamp, in nanoseconds
payload.attributescode.filepath, code.filename, code.function, code.lineno, code.namespace, logger.name, process.pid, thread.name, plus everything in record["extra"]
payload.resourceservice.name, service.version, deployment.environment when configured

A record with an exception also carries exception.type and exception.message.

The trace id comes from the active trace context, so log lines emitted inside a traced request join that trace.

Flushing and removing

sink.flush(timeout=1.0)   # block until the queue drains
sink.close()              # drain and stop the worker

Loguru identifies sinks by the id logger.add() returned, so removing one is a two-step:

sink_id = logger.add(sink, serialize=True)
...
sink.flush(timeout=1.0)
logger.remove(sink_id)
sink.close()

In Lambda and other freeze-between-invocations runtimes, flush once per invocation. flush_after_invocation is re-exported from this module and works the same way as it does for the stdlib handler:

from axonpush.integrations.loguru import (
    create_axonpush_loguru_sink,
    flush_after_invocation,
)

@flush_after_invocation(sink)
def lambda_handler(event, context):
    ...