Skip to main content

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 logging integration
  • FastAPI request instrumentation
  • Django request middleware
  • Celery task instrumentation
  • Flask request instrumentation

Supports Python 3.11 and newer.

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

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: logging to Logister events
  • Shared custom instrumentation: errors, logs, metrics, transactions, spans, and check-ins

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_KEY
  • LOGISTER_BASE_URL (defaults to https://logister.org)
  • LOGISTER_TIMEOUT (defaults to 5.0)
  • LOGISTER_ENVIRONMENT
  • LOGISTER_RELEASE
  • LOGISTER_REPOSITORY (falls back to GITHUB_REPOSITORY)
  • LOGISTER_COMMIT_SHA (falls back to GITHUB_SHA)
  • LOGISTER_BRANCH (falls back to GITHUB_REF_NAME)
  • LOGISTER_CAPTURE_LOCALS (true / false, defaults to false)

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 log events
  • logger.exception(...) and other records with exc_info as error events
  • 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 server spans for request load waterfall charts when capture_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, and x-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_in events when you provide monitor_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 server spans for request load waterfall charts when capture_spans=True
  • uncaught view exceptions via process_exception() as an error
  • request metadata like method, path, route, full URL, selected headers, status code, client IP, query string, X-Request-ID, and X-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 server spans for request load waterfall charts when capture_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, and X-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, or celery.active_tasks with stable queue and service context keys.
  • Performance triage: enable capture_spans=True for FastAPI, Django, or Flask instrumentation to feed request load waterfall charts, then add route-level logs and metrics with matching route values.
  • 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 as vX.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.md tracks 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.

Request correlation (0.5.0+)

Django (sync and async), Flask, and FastAPI integrations establish a task-local request scope. Automatic and manual captures share the local server identity; strict W3C version 00 incoming headers preserve their remote parent and flags.

import httpx
from logister import outbound_trace_context

trace = outbound_trace_context()
response = httpx.get(
    "https://api.example.test/orders",
    headers=trace.headers_for("https://api.example.test/orders", allowed_origins=["https://api.example.test"]),
    follow_redirects=False,
)
if response.status_code >= 500:
    client.capture_exception(RuntimeError("Order request failed"), context=trace.fields())

current_trace_context() returns the immutable current handle. The outbound helper prepares headers; it does not send a request or automatically record an HTTP span. Recheck the destination on every redirect and exclude SDK export and token-issuer traffic. Queue propagation remains explicit.

A linked-project lookup also requires Logister 3.7+, the instance flag LOGISTER_CROSS_PROJECT_CORRELATIONS=true, and explicit project/environment connections under Settings → Integrations → Connected projects. Enable related requests on both projects. A connection never grants project access.

Use the returned request handle when reporting a handled HTTP failure later. Do not attach the most recent request to an unrelated crash or OS diagnostic. Configure each app's own release and environment; mobile and backend releases are independent. The backend shows exact identifier evidence and retention gaps. See the request correlation guide.

Release files for logister-python 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for logister-python 0.5.0
File Size Uploaded
logister_python-0.5.0.tar.gz 41.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for logister-python 0.5.0
File Interpreter ABI Platform
logister_python-0.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 69.1 kB

Release files / logister_python-0.5.0.tar.gz

Download URL logister_python-0.5.0.tar.gz
Size 41.8 kB
Tags Source
SHA-256 checksum
How to use checksums
78ab594baccba953d9906623732b441c9ef6960e60f58ec1edda8b70527e6ee2
BLAKE2b-256 checksum
How to use checksums
54884c539d14812b6a29a39d9dfa0b3bc929b1f92288b92d0ef684be24db4c44
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 24, 2026.

Transparency log

Release files / logister_python-0.5.0-py3-none-any.whl

Download URL logister_python-0.5.0-py3-none-any.whl
Size 27.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5934a401ef8176df8a70c816950b44ff3e8ee019797e3ed13ac3f2d63a7ede18
BLAKE2b-256 checksum
How to use checksums
b78b56a2b8a748f8ecf9c40f6d17388e72c71ee27a79070f7b404e41f8a1c10a
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 24, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.5.0 This release

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page