Skip to main content

Arguslog Python SDK — error tracking for server-side Python apps

Project description

arguslog (Python SDK)

PyPI version Python versions License

Python SDK for Arguslog — a multi-tenant error tracking platform. Captures unhandled exceptions, manually-reported errors, and logging records from Python 3.9+ apps, then ships them to the Arguslog ingest endpoint where they're fingerprinted, stored, and surfaced on the dashboard.

Zero runtime dependencies. Uses stdlib urllib.request for transport and threading.Thread for the background sender. No requests, no httpx, no async runtime — drops cleanly into any Python service.

Install

pip install arguslog
# or
poetry add arguslog
# or
uv add arguslog

Quick start

import arguslog

arguslog.init(
    "arguslog://<key>@<host>/api/<projectId>",
    environment="production",
    release="1.2.3",
)

try:
    do_something_risky()
except Exception as exc:
    arguslog.capture_exception(exc)

# Always flush before short-lived processes exit — the background sender
# won't get a chance to drain otherwise.
arguslog.flush()

The SDK is a no-op until init runs, so importing the module from places that load before bootstrap is safe.

DSN format

arguslog://<publicKey>@<host>/api/<projectId>

The publicKey is project-scoped. Get it from your Arguslog project settings page; load it from os.environ like any other secret.

Public API

The module-level functions wrap a singleton ArguslogClient — same shape across the JS / Java / Python SDKs so events from a Python service look identical to ones from a Node service for the same project.

arguslog.init(dsn_or_options, transport=None, **options)   # → ArguslogClient
arguslog.get_client() -> Optional[ArguslogClient]

arguslog.capture_exception(exc, level="error", tags=None) -> Optional[str]   # event id
arguslog.capture_message(msg, level="info", tags=None)    -> Optional[str]

arguslog.set_user({"id": ..., "email": ..., "username": ...})  # email auto-scrubbed
arguslog.set_tag(key, value)
arguslog.set_context(name, dict)
arguslog.add_breadcrumb({"category": ..., "message": ..., "level": ..., "data": {...}})

arguslog.flush()        # block until queue drained or flush_timeout_seconds elapses
arguslog.close()        # flush + tear down background thread

init accepts either a DSN string (with optional kwargs) or an ArguslogOptions instance:

from arguslog import ArguslogOptions, init

init(
    ArguslogOptions(
        dsn="arguslog://<key>@<host>/api/<projectId>",
        environment="production",
        release="1.2.3",
        sample_rate=1.0,
        max_queue_size=256,
        flush_timeout_seconds=2.0,
        scrubbing_enabled=True,
        extra_scrub_patterns=[r"cust_[a-z0-9]+"],
        debug=False,
    )
)
Option Type Default Notes
dsn str req. See "DSN format" above.
environment str None E.g. production, staging.
release str None Free-form version stamped on every event.
sample_rate float 0.0–1.0 1.0 Fraction of events kept.
max_queue_size int 256 Background queue size; events dropped when full (logged at WARN).
flush_timeout_seconds float 2.0 Upper bound for flush().
scrubbing_enabled bool True Redact emails/IPs/credit-cards from messages and contexts.
extra_scrub_patterns list[str] [] Extra regex strings to scrub.
debug bool False Logs every send to stderr — never enable in production.

Integrations

Uncaught exceptions (sys.excepthook)

Captures anything that propagates out of your top-level frame (and that hasn't been caught by try/except). Preserves the previous hook so debuggers / IPython keep working.

import arguslog
from arguslog.integrations.excepthook import install_excepthook

arguslog.init("arguslog://<key>@<host>/api/<projectId>")
install_excepthook(arguslog.get_client())

install_excepthook returns an uninstall() callable for tests / hot-reload teardown. KeyboardInterrupt is intentionally not captured (it's user intent, not an error).

logging integration

Forwards logging.ERROR-or-higher records into Arguslog as either capture_exception (when exc_info is attached) or capture_message:

import logging
import arguslog
from arguslog.integrations.logging import install_logging_handler

arguslog.init("arguslog://<key>@<host>/api/<projectId>")
install_logging_handler(arguslog.get_client(), level=logging.ERROR)

logger = logging.getLogger("billing")

try:
    charge_card(order_id)
except Exception:
    logger.exception("payment failed")  # → capture_exception with full traceback

Records carry the logger name as a logger tag so dashboard filters can pivot on it.

The mapping is:

Python level Arguslog level
DEBUG debug
INFO info
WARNING warning
ERROR error
CRITICAL fatal

Custom numeric levels round down to the nearest standard level.

Framework recipes

Flask

from flask import Flask, g, request
import arguslog
from arguslog.integrations.excepthook import install_excepthook

arguslog.init(os.environ["ARGUSLOG_DSN"], release=os.environ.get("RELEASE"))
install_excepthook(arguslog.get_client())

app = Flask(__name__)

@app.before_request
def attach_user():
    arguslog.add_breadcrumb({
        "category": "http",
        "message": f"{request.method} {request.path}",
        "level": "info",
        "data": {"method": request.method, "path": request.path},
    })
    if user := getattr(g, "user", None):
        arguslog.set_user({"id": str(user.id), "email": user.email})

@app.errorhandler(Exception)
def on_error(exc):
    arguslog.capture_exception(exc, tags={"framework": "flask"})
    raise  # let Flask's default handler render the 500

For better isolation, push these into per-request thread-locals using flask.g and a teardown_request that calls arguslog.set_user(None).

Django

# myproject/middleware.py
import arguslog

class ArguslogMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        arguslog.add_breadcrumb({
            "category": "http",
            "message": f"{request.method} {request.path}",
            "level": "info",
        })
        if request.user.is_authenticated:
            arguslog.set_user({"id": str(request.user.pk), "email": request.user.email})
        return self.get_response(request)

    def process_exception(self, request, exception):
        arguslog.capture_exception(exception, tags={"framework": "django"})
        return None  # let Django render its own error page
# settings.py
MIDDLEWARE = [
    "myproject.middleware.ArguslogMiddleware",
    *MIDDLEWARE,
]

Add arguslog.init(os.environ["ARGUSLOG_DSN"]) to your wsgi.py / asgi.py so the SDK boots before the first request lands.

FastAPI

from contextlib import asynccontextmanager
from fastapi import FastAPI, Request
import arguslog
from arguslog.integrations.excepthook import install_excepthook

@asynccontextmanager
async def lifespan(_: FastAPI):
    arguslog.init(os.environ["ARGUSLOG_DSN"], release=os.environ.get("RELEASE"))
    install_excepthook(arguslog.get_client())
    yield
    arguslog.close()

app = FastAPI(lifespan=lifespan)

@app.middleware("http")
async def arguslog_breadcrumb(request: Request, call_next):
    arguslog.add_breadcrumb({
        "category": "http",
        "message": f"{request.method} {request.url.path}",
        "level": "info",
    })
    return await call_next(request)

@app.exception_handler(Exception)
async def capture(_: Request, exc: Exception):
    arguslog.capture_exception(exc, tags={"framework": "fastapi"})
    raise exc

CLI tools / short-lived scripts

For one-shot scripts, always call flush() before exit. Python tears down threads abruptly on interpreter shutdown, which strands the background sender mid-request:

import arguslog

arguslog.init(os.environ["ARGUSLOG_DSN"])
try:
    main()
finally:
    arguslog.flush()  # blocks up to flush_timeout_seconds

flush_timeout_seconds (default 2.0) bounds the wait so a flapping ingest endpoint doesn't hang your CLI.

AWS Lambda / serverless

Same pattern as CLIs — the runtime freezes the moment your handler returns:

import arguslog

arguslog.init(os.environ["ARGUSLOG_DSN"])

def handler(event, context):
    try:
        return business_logic(event)
    except Exception as exc:
        arguslog.capture_exception(exc)
        raise
    finally:
        arguslog.flush()  # critical — Lambda freezes on return

Threads, asyncio, multiprocessing

  • Threads. The singleton client is thread-safe; set_user/set_tag/breadcrumbs share a single global scope. If you need per-thread isolation, instantiate ArguslogClient directly and inject it.
  • asyncio. The transport runs on its own background thread, so awaiting flush() works from any event loop without blocking.
  • multiprocessing. Forked workers must call arguslog.init(...) themselves — the parent's background thread doesn't survive os.fork().

Sourcemap / release upload

Python apps don't have JavaScript-style sourcemaps, but you can still cut a release tag so events from this version are grouped together on the dashboard. Use @arguslog/cli from your release pipeline:

arguslog releases new "$RELEASE" --project 42

Then pass that exact RELEASE string as release="…" in init(). Exact match is how the dashboard groups events under a release.

Troubleshooting

Events not appearing on the dashboard. Set debug=True in init — the SDK logs every send (and every send failure) to stderr. A 401 means the public key is wrong; a connection error means the DSN host isn't reachable from your environment (proxy, NAT, VPC egress).

Events dropped after a burst. Check the WARN-level log line about arguslog: queue full, dropping event. Either bump max_queue_size (default 256) or lower sample_rate for high-throughput services.

flush() returns immediately on a fresh interpreter. Make sure init(...) was called first. flush on an uninitialised module is a no-op.

Stack traces are truncated to the framework frame. You captured a re-raised exception. capture_exception honors __traceback__, but if you re-raise without raise … from exc, Python may chain a new frame as the root. Capture closer to the original try/except.

PermissionError: [Errno 13] permission denied on the credentials file (CLI). That's the Arguslog CLI, not the Python SDK — see @arguslog/cli for that flow.

Example apps

  • examples/django-todo — a runnable Django 6 TODO app that wires every public SDK surface: init with the full options bag, install_excepthook + install_logging_handler, request middleware with a correlation id, set_user / set_tag / set_context, breadcrumbs, capture-message / capture-exception, scrubbing, sync flush, severity ladder. Each demo URL fires one event so you can click through the dashboard side-by-side with the source.

Source

The full implementation lives in the arguslog monorepo at python-sdk/. Issues and PRs welcome.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

arguslog-1.0.6.tar.gz (60.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

arguslog-1.0.6-py3-none-any.whl (14.8 kB view details)

Uploaded Python 3

File details

Details for the file arguslog-1.0.6.tar.gz.

File metadata

  • Download URL: arguslog-1.0.6.tar.gz
  • Upload date:
  • Size: 60.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for arguslog-1.0.6.tar.gz
Algorithm Hash digest
SHA256 8f72bdc47406c614b5270fddab0dd72e0627724c7244260e12c2de5ee7f0a28c
MD5 1d472db3200e969a166e3dd921dcc651
BLAKE2b-256 141b94619e1cc998b64f15adb96213414778c7d95d24e01da3fb0a171f35ef7c

See more details on using hashes here.

File details

Details for the file arguslog-1.0.6-py3-none-any.whl.

File metadata

  • Download URL: arguslog-1.0.6-py3-none-any.whl
  • Upload date:
  • Size: 14.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for arguslog-1.0.6-py3-none-any.whl
Algorithm Hash digest
SHA256 1ceada14efac8ccfa68424e2f5e2f5252b8968e36d3c47865a9c171e0f73c9a0
MD5 5a82e39d088658d5bbbac6256568bbee
BLAKE2b-256 da16c5e398f86e7ed3c1ce05d466cc3d67f6bc24acba212fcfbde838b559ac9f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page