logister-python
Python SDK for sending errors, logs, metrics, transactions, spans, and scheduled-job check-ins to Logister.
Install it from PyPI as logister-python.
Use it in APIs, workers, schedulers, command-line jobs, and internal services. It includes:
- a shared
LogisterClient - native Python
loggingintegration FastAPIrequest instrumentationDjangorequest middlewareCelerytask instrumentationFlaskrequest instrumentation
Supports Python 3.11 and newer.
- Main Logister app: https://github.com/taimoorq/logister
- Product docs: https://logister.org/docs/
- Insights guide: https://logister.org/docs/product/#insights
- Python integration docs: https://logister.org/docs/integrations/python/
- PyPI package: https://pypi.org/project/logister-python/
Quick start
Create a project in Logister and generate a project API key under Project settings → API keys.
python -m pip install logister-python
export LOGISTER_API_KEY="<project-api-key>"
export LOGISTER_BASE_URL="https://logister.example.com"
export LOGISTER_ENVIRONMENT="development"
Send a test error:
from logister import LogisterClient
with LogisterClient.from_env(default_context={"service": "checkout-api"}) as client:
try:
raise RuntimeError("README test error")
except RuntimeError as error:
client.capture_exception(
error,
fingerprint="readme-test-error",
context={"component": "checkout"},
)
Open the project inbox and confirm that README test error appears. A 401 response usually means the key or base URL is wrong; the Python integration guide covers framework setup and troubleshooting.
Table Of Contents
- Quick start
- What This Package Is For
- Package Links
- Install From PyPI
- Environment Variables
- Core Client
- Python Logging
- Error Capture
- FastAPI
- Celery
- Django
- Flask
- Check-ins
- Using project Insights
- GitHub source context and deployments
- Event Mapping
- Development
- Publishing
- Release Flow
What This Package Is For
Use logister-python when you want a Python service to send operational telemetry into Logister through the published PyPI package instead of wiring raw HTTP calls by hand.
- API and web apps: FastAPI, Django, Flask
- Worker and scheduler processes: Celery, cron-style jobs, CLI tasks
- Standard-library logging pipelines:
loggingto Logister events - Shared custom instrumentation: errors, logs, metrics, transactions, spans, and check-ins
Package Links
- PyPI package: https://pypi.org/project/logister-python/
- GitHub releases: https://github.com/taimoorq/logister-python/releases
- Source repository: https://github.com/taimoorq/logister-python
- Integration docs: https://logister.org/docs/integrations/python/
Install From PyPI
Core client:
pip install logister-python
With uv:
uv add logister-python
FastAPI support:
pip install 'logister-python[fastapi]'
Celery support:
pip install 'logister-python[celery]'
Django support:
pip install 'logister-python[django]'
Flask support:
pip install 'logister-python[flask]'
Package index: https://pypi.org/project/logister-python/
Environment Variables
LogisterClient.from_env() reads:
LOGISTER_API_KEYLOGISTER_BASE_URL(defaults tohttps://logister.org)LOGISTER_TIMEOUT(defaults to5.0)LOGISTER_ENVIRONMENTLOGISTER_RELEASELOGISTER_REPOSITORY(falls back toGITHUB_REPOSITORY)LOGISTER_COMMIT_SHA(falls back toGITHUB_SHA)LOGISTER_BRANCH(falls back toGITHUB_REF_NAME)LOGISTER_CAPTURE_LOCALS(true/false, defaults tofalse)
Core Client
Use the shared client when you are wiring a script, worker, CLI task, or framework hook and want one place to send custom events.
from logister import LogisterClient
client = LogisterClient.from_env(default_context={"service": "api"})
client.capture_message("Application booted", level="info")
client.capture_metric(
"cache.hit_rate",
0.98,
unit="ratio",
level="info",
fingerprint="metric:cache.hit_rate",
context={"cache": "primary"},
)
client.capture_transaction("POST /checkout", 182.4, request_id="req_123")
client.capture_span(
"render checkout",
82.1,
kind="render",
status="ok",
trace_id="trace_123",
parent_span_id="span_root",
context={"route": "POST /checkout"},
)
Python Logging
If your app already uses the standard library logging module, this is usually the easiest way to start sending application logs into Logister without rewriting call sites.
import logging
from logister import LogisterClient, instrument_logging
client = LogisterClient.from_env(default_context={"service": "api"})
logger = logging.getLogger("checkout")
instrument_logging(client, logger=logger)
logger.warning("Inventory cache miss", extra={"request_id": "req_123", "sku": "sku_42"})
What this records:
- standard Python log records as
logevents logger.exception(...)and other records withexc_infoaserrorevents- logger metadata like logger name, module, file, function, line number, process, and thread
- extra record fields passed through
extra={...}so request IDs, trace IDs, and app-specific details show up in Logister
You can also manage the underlying HTTP client explicitly:
from logister import LogisterClient
with LogisterClient.from_env() as client:
client.capture_message("Worker online")
Error Capture
Python error reports are most useful when you include the service or component name and let the SDK send the traceback structure for you.
from logister import LogisterClient
client = LogisterClient.from_env(default_context={"service": "checkout"})
try:
run_checkout()
except Exception as exc:
client.capture_exception(
exc,
fingerprint="checkout-failed",
context={
"component": "checkout",
"order_id": 1234,
},
)
Captured Python exceptions include structured traceback frames, backtrace text, exception module and qualified class name, chained exceptions from raise ... from ..., and runtime metadata like Python version, platform, hostname, and process ID.
Set LOGISTER_CAPTURE_LOCALS=true if you want frame locals included in error events for the Logister UI.
Frame locals can contain passwords, tokens, request bodies, and personal data. Leave this option off unless you have reviewed what your application keeps in local variables and your retention policy permits collecting it.
FastAPI
This is the cleanest path for modern Python API services.
from fastapi import FastAPI
from logister import LogisterClient, instrument_fastapi
app = FastAPI()
logister = LogisterClient.from_env(default_context={"service": "api"})
instrument_fastapi(app, logister, capture_spans=True)
What this records:
- request duration as a
transaction - optional root
serverspans for request load waterfall charts whencapture_spans=True - uncaught request exceptions as an
error - request metadata like method, path, route, full URL, selected headers, client IP, path params, query string,
x-request-id, andx-trace-id
You can customize transaction naming:
instrument_fastapi(
app,
logister,
transaction_namer=lambda request: f"{request.method} {request.url.path}",
)
Celery
This is the worker-side path when your Python app does meaningful work outside the request cycle.
from celery import Celery
from logister import LogisterClient, instrument_celery
celery_app = Celery("billing")
logister = LogisterClient.from_env(default_context={"service": "worker"})
instrument_celery(
celery_app,
logister,
monitor_slug_factory=lambda task: getattr(task, "name", None),
)
What this records:
- task runtime as a
transaction - task failures as an
error - task retries as a warning
log - optional task-level
check_inevents when you providemonitor_slug_factory - task metadata like queue, module, retry count, ETA, and worker hostname when Celery exposes it
Django
Use middleware when you want a Django app to report request timing and uncaught view exceptions with very little setup.
Use the built-in middleware directly when env-based configuration is enough:
MIDDLEWARE = [
# ...
"logister.django.LogisterMiddleware",
]
LogisterMiddleware reads the same LOGISTER_* environment variables as LogisterClient.from_env().
If you want to build the client yourself, bind it with build_django_middleware():
from logister import LogisterClient, build_django_middleware
logister = LogisterClient.from_env(default_context={"service": "django-web"})
ConfiguredLogisterMiddleware = build_django_middleware(logister)
ConfiguredLogisterMiddlewareWithSpans = build_django_middleware(logister, capture_spans=True)
What Django middleware records:
- request duration as a
transaction - optional root
serverspans for request load waterfall charts whencapture_spans=True - uncaught view exceptions via
process_exception()as anerror - request metadata like method, path, route, full URL, selected headers, status code, client IP, query string,
X-Request-ID, andX-Trace-ID
Flask
Use the Flask hooks when you want lightweight request instrumentation without changing your route code.
from flask import Flask
from logister import LogisterClient, instrument_flask
app = Flask(__name__)
logister = LogisterClient.from_env(default_context={"service": "flask-web"})
instrument_flask(app, logister, capture_spans=True)
What Flask instrumentation records:
- request duration as a
transaction - optional root
serverspans for request load waterfall charts whencapture_spans=True - uncaught request exceptions as an
error - request metadata like method, path, full URL, endpoint, blueprint, selected headers, status code, client IP, query string,
X-Request-ID, andX-Trace-ID
Check-ins
Check-ins are a good fit for scheduled jobs, cron-style imports, and the “did this worker actually run?” questions Python teams usually end up debugging.
from logister import LogisterClient
client = LogisterClient.from_env(default_context={"service": "scheduler"})
client.check_in(
"nightly-import",
"ok",
release="worker@2026.05.21",
expected_interval_seconds=3600,
duration_ms=842.7,
trace_id="trace-123",
request_id="req-123",
)
Using project Insights
The Logister project Insights tab combines Inbox, Activity, and Performance data into live dashboard views. Python services get the most useful Insights view when they send consistent LOGISTER_ENVIRONMENT, LOGISTER_RELEASE, and stable top-level context attributes.
Use default_context for attributes that should be present on most events, and pass per-event context for route, queue, worker, or feature dimensions:
from logister import LogisterClient
client = LogisterClient.from_env(
default_context={
"service": "billing-api",
"region": "us-east-1",
}
)
client.capture_metric(
"queue.depth",
42,
unit="jobs",
context={
"service": "billing-worker",
"queue": "billing",
"tenant_tier": "enterprise",
},
)
client.capture_transaction(
"POST /checkout",
182.4,
context={
"route": "POST /checkout",
"feature_flag": "new_checkout",
"tenant_tier": "enterprise",
},
request_id="req_123",
)
client.capture_span(
"render checkout",
82.1,
kind="render",
status="ok",
trace_id="trace_123",
parent_span_id="span_root",
context={
"route": "POST /checkout",
"tenant_tier": "enterprise",
},
)
client.capture_message(
"payment provider retry",
level="warn",
context={
"service": "billing-worker",
"provider": "stripe",
"queue": "billing",
},
)
client.check_in(
"nightly-reconcile",
"ok",
expected_interval_seconds=3600,
duration_ms=842.7,
context={
"service": "billing-worker",
"queue": "reconcile",
},
)
Practical Insights recipes:
- Release validation: set
LOGISTER_RELEASE, then filter Insights to the new release and compare error count, transaction P95, and custom metrics. - Worker monitoring: report metrics such as
queue.depth,queue.latency,task.retry_count, orcelery.active_taskswith stablequeueandservicecontext keys. - Performance triage: enable
capture_spans=Truefor FastAPI, Django, or Flask instrumentation to feed request load waterfall charts, then add route-level logs and metrics with matchingroutevalues. - Instrumentation audit: open Insights after deploy and confirm errors, logs, metrics, transactions, spans, and check-ins all appear in the recent stream.
Keep custom attributes stable and low-cardinality. Good top-level context keys include service, region, queue, route, tenant_tier, provider, and feature_flag. Avoid raw IDs, emails, request bodies, SQL text, and per-user values as Insights dimensions.
GitHub source context and deployments
When a Logister project is connected to a GitHub repository, LogisterClient.from_env() can attach source context automatically:
export LOGISTER_ENVIRONMENT=production
export LOGISTER_RELEASE=checkout@2026.06.18
export LOGISTER_REPOSITORY=acme/checkout
export LOGISTER_COMMIT_SHA="$(git rev-parse HEAD)"
export LOGISTER_BRANCH="$(git branch --show-current)"
client = LogisterClient.from_env(default_context={"service": "checkout-api"})
client.capture_exception(error)
CI/CD can also record the release-to-commit mapping directly:
client.record_deployment(
release="checkout@2026.06.18",
environment="production",
repository="acme/checkout",
commit_sha="4f8c2d1a9b7e6c5d4a3b2c1d0e9f8a7b6c5d4e3f",
branch="main",
workflow_run_url="https://github.com/acme/checkout/actions/runs/123",
)
Event Mapping
| Application signal | Logister event |
|---|---|
| Web request or task duration | transaction |
| Uncaught or manually captured exception | error |
| Application log or warning | log |
| Counter, gauge, or measurement | metric |
| Request or custom operation segment | span |
| Scheduled-job heartbeat | check_in |
Development
python -m pip install --upgrade pip 'setuptools>=83'
python -m pip install -e '.[dev,fastapi,celery,django,flask]' pip-audit
python -m pip_audit
python -m pytest
python -m build
Publishing
pyproject.toml is the package version source of truth. Update it and CHANGELOG.md together. After CI passes on main, the release-from-main workflow creates a matching vX.Y.Z tag and dispatches publish.yml.
- Merge the version bump to
main, or push a matching tag such asvX.Y.Z - GitHub Actions tests and builds the distributions once
- PyPI Trusted Publishing uploads those distributions with OIDC
- the workflow creates the GitHub Release only after the upload succeeds
Release Flow
CHANGELOG.mdtracks package releases- Git tags trigger the ordered PyPI publish and GitHub Release flow
- This package keeps its own versioning separate from the main Logister app
- PyPI versions are immutable; corrections require a new patch version
Verify both release surfaces:
curl -fsSL https://pypi.org/pypi/logister-python/json | jq -r .info.version
gh release view vX.Y.Z
Dependency verification
The package retains bounded runtime/framework ranges. CI and release builds use
requirements/ci.txt for reproducible tooling and extra dependencies across
Python 3.11–3.14; markers keep Django 5.2 on Python 3.11 and Django 6.1 on newer
runtimes. Weekly CI resolves the declared ranges afresh, then audits and tests
all extras together so new upstream releases are noticed even before lock updates.
Regenerate the constraints with uv 0.12.13 and review the resulting diff:
uv pip compile pyproject.toml requirements/tooling.in --all-extras --universal --python-version 3.11 --no-annotate --output-file requirements/ci.txt
python -m pip install -c requirements/ci.txt -e '.[dev,fastapi,celery,django,flask]' pip-audit hatchling
python -m pip_audit
python -m pytest
python -m build --no-isolation
Constraints apply to maintainer checks and builds; they do not replace the supported dependency ranges installed by SDK consumers.
Coordinated release preparation
For a coordinated ecosystem release, keep the version-changing PR unmerged until the final agreed Rails PR has been published and its deployment verified. Recheck the upstream contract/workflow pin against that final backend commit before merge. Successful source CI, a tag, or a release-impact dispatch alone is not backend readiness. After independent review, merging the new version runs CI, creates an immutable tag, and explicitly dispatches publication. A tag without a package remains incomplete.
To recover an existing reviewed tag, dispatch the publisher workflow from main
with -f tag=vX.Y.Z (Python uses publish.yml; other SDKs use release.yml). The
workflow checks out that exact tag, proves it belongs to main, and verifies public
package identity before creating the GitHub Release. Never move a consumed tag.
Weekly CI audits/tests current dependencies and cannot trigger automatic publication. Dependabot groups compatible minor/patch updates; major toolchain migrations keep separate PRs. Pin Actions to full commits and retain supported runtime floors.
Reliable event delivery
from logister import LogisterClient, RetryPolicy
client = LogisterClient(api_key="your-project-ingest-key", retry_policy=RetryPolicy())
event = client.prepare_event(event_type="log", level="info", message="Job started")
client.send_prepared_event(event)
results = client.send_events([event])
for result in results:
if result.error is not None:
print(result.event_id, type(result.error).__name__)
Each capture serializes its UUID, timestamp and context before the first request.
Retain a PreparedEvent to replay it without creating another logical event.
Ingestion retries network errors, 408/425/429 and transient 5xx up to three attempts;
Retry-After is capped at five seconds. maximum_attempts=1 disables retries.
The 15-second default retry budget is shared across a batch call, including splits
and fallback. HTTP phase timeouts also apply; synchronous HTTP calls can overrun
that budget until their current phase timeout expires. Interrupts propagate.
Batch calls accept at most 1,000 prepared events or dictionaries matching
prepare_event, send at most 100 per HTTP request, and return one ordered result
per event. Failed/unsent events remain visible after a timeout or rejection.
HTTP acceptance means durable ingestion, not completed projection or symbolication.
Deployments and dedicated check-ins retain their single-request behavior.
Release files for logister-python 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| logister_python-0.4.0.tar.gz | 37.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| logister_python-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 61.8 kB
Release files / logister_python-0.4.0.tar.gz
| Download URL | logister_python-0.4.0.tar.gz |
|---|---|
| Size | 37.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
894d2bd688d38663fffeb4839b5068a7ad9d6b1f3eccc8a8bc345d224173e476
|
|
BLAKE2b-256 checksum How to use checksums |
f6deef184563c7871bd91be766c59eab4cf3a490a3dbddf4d5091ec3c7a4a664
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.
Transparency logRelease files / logister_python-0.4.0-py3-none-any.whl
| Download URL | logister_python-0.4.0-py3-none-any.whl |
|---|---|
| Size | 24.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5ca56f86d91dbdc6d5c4e1b0103f2637edc815a0b180402e8806d1284ee16efe
|
|
BLAKE2b-256 checksum How to use checksums |
a97a6ac18d3cd55827135b83b9223b3892afff08094dc421ba27adb5334ea196
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.
Transparency log