Arguslog Python SDK — error tracking for server-side Python apps
Project description
arguslog (Python SDK)
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, instantiateArguslogClientdirectly 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 surviveos.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.
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file arguslog-1.0.1.tar.gz.
File metadata
- Download URL: arguslog-1.0.1.tar.gz
- Upload date:
- Size: 46.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a004f58f215bba44c2985ae58914b2f27f99a8cc8cdd71780000f496f5605dd2
|
|
| MD5 |
dc900b1366bf6eb23fa1204ddc59266f
|
|
| BLAKE2b-256 |
a86a673de7766bb41613d2592d0b18270465e43aadf0ac49076209333c44e673
|
Provenance
The following attestation bundles were made for arguslog-1.0.1.tar.gz:
Publisher:
release-sdk-python.yml on petarnenov/arguslog
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
arguslog-1.0.1.tar.gz -
Subject digest:
a004f58f215bba44c2985ae58914b2f27f99a8cc8cdd71780000f496f5605dd2 - Sigstore transparency entry: 1476067926
- Sigstore integration time:
-
Permalink:
petarnenov/arguslog@da2cafac55b0074c90fc377dd6475cbfbc98265c -
Branch / Tag:
refs/tags/python-sdk-v1.0.1 - Owner: https://github.com/petarnenov
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-sdk-python.yml@da2cafac55b0074c90fc377dd6475cbfbc98265c -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file arguslog-1.0.1-py3-none-any.whl.
File metadata
- Download URL: arguslog-1.0.1-py3-none-any.whl
- Upload date:
- Size: 14.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
af78a93c09589c7e5aa7497fa9db87efd4d4dbba1106bf86f0330d2a8c1f89e7
|
|
| MD5 |
e35c9081f78340aa27faee51edc64051
|
|
| BLAKE2b-256 |
707227cf6a73bcd614052f0ed09ced0159ff5b48d5dd8aa42d26d773b601ba7c
|
Provenance
The following attestation bundles were made for arguslog-1.0.1-py3-none-any.whl:
Publisher:
release-sdk-python.yml on petarnenov/arguslog
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
arguslog-1.0.1-py3-none-any.whl -
Subject digest:
af78a93c09589c7e5aa7497fa9db87efd4d4dbba1106bf86f0330d2a8c1f89e7 - Sigstore transparency entry: 1476068096
- Sigstore integration time:
-
Permalink:
petarnenov/arguslog@da2cafac55b0074c90fc377dd6475cbfbc98265c -
Branch / Tag:
refs/tags/python-sdk-v1.0.1 - Owner: https://github.com/petarnenov
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-sdk-python.yml@da2cafac55b0074c90fc377dd6475cbfbc98265c -
Trigger Event:
workflow_dispatch
-
Statement type: