Documentation / Python

Integrate a Python app with Logister.

The best path for Python projects is the published PyPI package logister-python. It is designed for the places Python teams usually need observability first: API requests, worker jobs, scheduled tasks, and standard-library logging, with first-party integrations for FastAPI, Django, Flask, and Celery.

Before you start

Create the project with the Python integration type.

What you needWhy it matters
Project API tokenAuthenticates your app when sending events
Logister instance base URLUsed to build the ingest, check-in, and deployment endpoints
Python 3.11+ appLets you install and run logister-python
Optional GitHub App installationLets Logister use repository, commit_sha, branch, and deployment records for source excerpts and deploy links

What you get

Use the Python integration across APIs, workers, scheduled tasks, and deploys.

NeedWhat appears in Logister
Catch web request failuresIssues from FastAPI, Django, or Flask with structured traceback frames, request metadata, route context, and runtime details.
Keep existing logs usefulStandard logging records in the activity feed, with logger.exception(...) and exc_info records reported as errors.
Understand request loadOptional FastAPI, Django, and Flask request spans that feed request load waterfall charts on the performance page.
Understand worker behaviorCelery task runtime, failures, retries, queue metadata, optional check-ins, and the worker hostname when capture_hostname=True.
Debug with Python contextChained exceptions, runtime metadata, logger metadata, optional frame locals, and source/deployment context when configured.
Keep sensitive values out of eventsDefault redaction of sensitive keys, credential-like values, and sensitive URL query parameters before events leave the process. Client IP and hostname are sent only when you opt in.
Resolve source and deploy historyrepository, commit_sha, branch, release, and deployment records that let Logister show source excerpts, GitHub links, and "started after deploy" context when a GitHub repository is connected to the project.
Monitor custom operationsMetrics, transactions, spans, logs, and check-ins from the shared client.

Setup flow

Recommended installation path

  1. Create the project. Logister opens its setup path at the API key step; create the key there.
  2. Install logister-python in the Python app.
  3. Configure the client with environment variables or direct initialization, including environment, release, repository, commit SHA, and branch when available.
  4. Review the privacy defaults: add app-specific keys to LOGISTER_REDACT_KEYS, and opt in to client IP or hostname capture only if you need them.
  5. Add the framework integration you need first: FastAPI, Django, Flask, or Celery.
  6. Attach instrument_logging() if you want standard Python logging records to flow into Logister too.
  7. Use the shared client for metrics, logs, transactions, spans, and check-ins across the rest of the app.
  8. From CI/CD, call record_deployment() after a successful deploy so releases map to exact commits.

Tip

Python teams usually get the most value by starting with uncaught exceptions plus request or task timing, then layering in logging, request spans, source context, deployment records, custom metrics, and check-ins once the main API and worker paths are visible.

Install

Install logister-python from PyPI.

Use the published PyPI package when you want a Python API, worker, scheduler, or internal service to send errors, logs, metrics, transactions, spans, check-ins, and deployment records into Logister.

shell
pip install logister-python
uv add logister-python
pip install 'logister-python[fastapi]'
pip install 'logister-python[django]'
pip install 'logister-python[celery]'
pip install 'logister-python[flask]'

Frame locals

Set LOGISTER_CAPTURE_LOCALS=true if you want frame locals attached to captured Python exceptions in the Logister UI. Frame locals can contain passwords, tokens, request bodies, and personal data, so leave this off unless you have reviewed what your application keeps in local variables and your retention policy permits collecting it. When it is on, self, cls, and request are skipped, and other locals go through the default redaction: a local named password is replaced, and bearer tokens or JWTs in a repr are scrubbed.

shell
export LOGISTER_API_KEY="your-project-api-token"
export LOGISTER_BASE_URL="https://your-logister-host.example"
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)"
python
from logister import LogisterClient

client = LogisterClient.from_env(default_context={"service": "api"})

client.capture_message("Application booted")
client.capture_metric(
    "cache.hit_rate",
    0.98,
    unit="ratio",
    level="info",
    fingerprint="metric:cache.hit_rate",
    context={"cache": "primary"},
)

LogisterClient.from_env() reads LOGISTER_REPOSITORY, LOGISTER_COMMIT_SHA, and LOGISTER_BRANCH. In GitHub Actions it falls back to GITHUB_REPOSITORY, GITHUB_SHA, and GITHUB_REF_NAME when the Logister-specific variables are not set.

Package links: PyPI and GitHub. Self-hosted backend: Logister app on GitHub.

Privacy and redaction

Check what leaves the process before you add more context.

Every event's context is redacted before it is serialized, using the same default rules as the other Logister SDKs. Matching values are replaced with [REDACTED].

RuleWhat matches
Sensitive keysThe key is lowercased with every non-alphanumeric character removed, then compared. It matches when it equals one of password, passwd, pwd, secret, token, apikey, authorization, proxyauthorization, cookie, setcookie, sessiontoken, sessionkey, credential, credentials, privatekey, clientsecret, accesstoken, refreshtoken, idtoken, authtoken, xapikey, xauthtoken, xaccesstoken, xcsrftoken, xxsrftoken, signature, ssn, creditcard, cardnumber, or cvv, or when it contains token, secret, password, passwd, apikey, authorization, credential, or cookie. So api_key, X-API-Key, accessToken, and stripe_secret_key all match.
Credential-like valuesAny string is scrubbed for Bearer <x> and Basic <x> credentials, JWT-shaped strings, and lgst_-prefixed keys.
URLsCaptured URLs, query strings, and Referer headers keep scheme, host, and path. Userinfo is dropped, and the value of any sensitive query parameter is replaced.

Redaction covers the request URL, query string, and headers, path_params and view_args, default_context and per-call context, log record extra fields, span context, exception messages, frame locals, and the event message. The SDK's own envelope fields (uuid, event_type, and the trace, span, request, and session IDs) are added after redaction and are never changed.

Add your own exact-match keys, normalized the same way, with LOGISTER_REDACT_KEYS (comma-separated) or LogisterClient(redact_keys=[...]). The opt-in capture settings below work the same way: an environment variable read by LogisterClient.from_env(), or a constructor argument.

shell
export LOGISTER_REDACT_KEYS="order_ref,member_number"
export LOGISTER_CAPTURE_CLIENT_IP="false"
export LOGISTER_CAPTURE_HOSTNAME="false"
export LOGISTER_CAPTURE_LOCALS="false"
Client optionEnvironment variableDefaultWhat it adds when enabled
redact_keysLOGISTER_REDACT_KEYSemptyExtra exact-match keys to redact on top of the defaults.
capture_client_ipLOGISTER_CAPTURE_CLIENT_IPfalseclient_ip and the X-Forwarded-For header on FastAPI, Django, and Flask request events.
capture_hostnameLOGISTER_CAPTURE_HOSTNAMEfalseThe host name in runtime metadata, including the Celery worker hostname.
(environment only)LOGISTER_CAPTURE_LOCALSfalseFrame locals on error events. self, cls, and request are skipped; other locals are redacted.

The Python executable path is not sent. If you want the same rules in your own code, the helpers are public: from logister.redaction import redact, redact_url, scrub_string.

Verify it

With LOGISTER_REDACT_KEYS=order_ref set, send one test event with context={"api_key": "test-value", "order_ref": "A-1"}. In the event detail, both values should show [REDACTED], and request events should have no client_ip or hostname unless you enabled them.

Failure isolation

Automatic captures never raise into your application.

The FastAPI, Django, and Flask integrations, the Celery signal handlers, and the logging handler catch their own failures. A rejected API key (401 or other 4xx), a 5xx, a network error, a serialization bug, or a failing transaction_namer or monitor_slug_factory is logged at DEBUG on the logister logger and the event is dropped. When a request handler or task raises, your original exception propagates unchanged.

Direct calls such as client.capture_message(...) and client.capture_exception(...) still raise LogisterError, so scripts and CI jobs can see delivery failures.

Values that JSON cannot represent, such as UUIDs, datetimes, Decimal, and custom objects (for example Flask <uuid:...> view args and FastAPI path params), are serialized with str(), and circular structures are cut.

The FastAPI middleware, Django's async middleware path, and log records emitted inside a running event loop prepare each event on the caller, then deliver it on one background thread with a 1,000-event queue. A slow Logister instance does not block the event loop. When the queue is full, new events are dropped. Call client.flush() or client.close() at shutdown; close() waits for queued events up to the client timeout. The HTTP client and background thread are recreated after fork(), so pre-forking servers do not share connections with the parent.

python
import logging

# Show SDK delivery failures while you set up or debug an integration.
logging.basicConfig(level=logging.INFO)
logging.getLogger("logister").setLevel(logging.DEBUG)

# At shutdown, wait for events queued by background delivery.
client.flush()
client.close()

Verify it

If framework events do not appear, enable DEBUG on the logister logger and repeat the request. A logged 401 usually means the API key or LOGISTER_BASE_URL is wrong. To confirm the key and URL outside the framework, run a direct client.capture_exception(...) call from a script: it raises LogisterError instead of logging.

Source and deployments

Record the deployed commit when CI/CD knows it.

If the project is connected to the GitHub App, source context lets Logister resolve traceback frames to repository files. Deployment records tell Logister which commit was deployed for a release and environment.

python
from datetime import datetime, timezone

client.record_deployment(
    release="checkout@2026.06.18",
    environment="production",
    repository="acme/checkout",
    commit_sha="4f8c2d1a9b7e6c5d4a3b2c1d0e9f8a7b6c5d4e3f",
    branch="main",
    deployed_at=datetime.now(timezone.utc),
    release_tag="v2026.06.18",
    workflow_run_url="https://github.com/acme/checkout/actions/runs/123",
)
FieldSet it fromUsed for
repositoryLOGISTER_REPOSITORY or GITHUB_REPOSITORYFinds the GitHub repository connected to the project.
commit_shaLOGISTER_COMMIT_SHA or GITHUB_SHAResolves stack frames and deployment context to the exact commit.
branchLOGISTER_BRANCH or GITHUB_REF_NAMEFalls back when an event has no commit or deployment record yet.
releaseYour app version, build ID, tag, or package versionConnects event filters, release health, and deployments.

Verify it

After a deploy, send one test exception with the same release. Open the event detail and check for source lookup status, deployment context, and GitHub links. If source is missing, confirm the GitHub App is installed, linked to the project, and the repository is connected; then check that the event has repository plus either commit_sha or a matching deployment record.

Python logging

Forward standard logging records without changing every call site.

python
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"})

This is usually the easiest adoption path for existing Python services. The logging integration sends standard log records as log events and records with exc_info, including logger.exception(...), as error events. It also forwards logger metadata and extra record fields like request IDs and trace IDs; extra fields go through the default redaction.

The example attaches the handler to one named logger, so only records from checkout and its child loggers are sent. You can also attach it to the root logger at INFO: records from the logister, httpx, and httpcore loggers are ignored, and a record emitted while the handler is already sending is dropped, so each log line produces exactly one event.

python
root = logging.getLogger()
root.setLevel(logging.INFO)

instrument_logging(client, logger=root)

FastAPI

Capture request timings and uncaught exceptions with middleware.

python
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)

The FastAPI integration records each request as a transaction, optionally records a root server span when capture_spans=True, and captures uncaught request exceptions as error events, including route, full URL, selected headers, path params, status code, and common correlation headers. The client IP and X-Forwarded-For are added only with capture_client_ip=True.

Events are prepared on the request task and delivered on a background thread, so the middleware never blocks the event loop on Logister. Call client.flush() or client.close() in your shutdown hook so queued events are sent. See Failure isolation.

Django

Use middleware for request timing and uncaught view errors.

python
MIDDLEWARE = [
    # ...
    "logister.django.LogisterMiddleware",
]
python
from logister import LogisterClient, build_django_middleware

logister = LogisterClient.from_env(default_context={"service": "django-web"})
ConfiguredLogisterMiddleware = build_django_middleware(logister, capture_spans=True)

The Django middleware captures request duration as a transaction, can emit root request spans with capture_spans=True, and uses process_exception() to report uncaught exceptions as error events. The client IP and X-Forwarded-For are added only with capture_client_ip=True. On Django's async path, events are delivered on a background thread like FastAPI.

Flask

Instrument request timing and uncaught request exceptions with app hooks.

python
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)

The Flask integration records each request as a transaction, can emit root request spans with capture_spans=True, and captures uncaught request exceptions as error events, including full URL, endpoint, blueprint, selected headers, status code, and common correlation headers. The client IP and X-Forwarded-For are added only with capture_client_ip=True.

Celery

Instrument task runtime, failures, retries, and optional check-ins.

python
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),
)

The Celery integration captures task runtime as transaction events, failures as error events, retries as warning log events, and optional monitor heartbeats as check_in events, plus task metadata like queue, module, retries, and ETA when Celery exposes them. The worker hostname is added only with capture_hostname=True. Signal handlers never raise into the worker; see Failure isolation.

Custom events

Use the same client for direct metrics, logs, spans, and check-ins.

python
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",
    request_id="req-123",
    context={"route": "POST /checkout"},
)

client.capture_exception(
    exc,
    fingerprint="checkout-failed",
    context={"component": "checkout", "order_id": 1234},
)

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",
)

Captured Python exceptions include structured traceback frames, chained exceptions from raise ... from ..., and runtime metadata like Python version, platform, and process ID (hostname only with capture_hostname=True). Direct client calls such as client.capture_exception(...) and client.capture_message(...) raise LogisterError when delivery fails, unlike the automatic integrations. Metric captures accept value, unit, level, fingerprint, environment, release, trace ID, request ID, session ID, and user ID fields. Span captures accept span ID, parent span ID, kind, status, start/end timestamps, trace ID, request ID, and custom context for request load charts.

Verification
Events page checklist
transactions visible
request spans visible
uncaught framework errors visible
Celery task events visible
Python logging events visible
custom metrics, spans, and check-ins present
source context visible on events when configured
deployment context visible after record_deployment runs
sensitive context keys and query parameters show [REDACTED]

Upgrading to 0.6.0

Review these defaults after you upgrade.

  • Client IP is opt-in. Changed default: FastAPI, Django, and Flask events include client_ip and X-Forwarded-For only when you set capture_client_ip=True or LOGISTER_CAPTURE_CLIENT_IP=true.
  • Hostname is opt-in. Runtime metadata, including the Celery worker hostname, omits the host name unless you set capture_hostname=True or LOGISTER_CAPTURE_HOSTNAME=true. The runtime_name field (sys.executable) was removed.
  • Redaction is on by default. Sensitive keys, credential-like values, and sensitive URL query parameters now show [REDACTED]. Add app-specific keys with redact_keys or LOGISTER_REDACT_KEYS.
  • Frame locals skip self, cls, and request when LOGISTER_CAPTURE_LOCALS=true.
  • Automatic captures log instead of raising. To see integration delivery failures, enable DEBUG on the logister logger. Direct client calls still raise LogisterError.
  • Flush at shutdown. FastAPI, Django's async path, and logging inside a running event loop deliver on a background thread. Call client.flush() or client.close() before the process exits.
  • Root logger handlers. instrument_logging() ignores records from logister, httpx, and httpcore, so it is safe to attach to the root logger at INFO.
  • Use HTTPS. A LOGISTER_BASE_URL that uses plain http:// to anything other than a loopback host (localhost, *.localhost, 127.0.0.0/8, ::1) emits a UserWarning.

Release notes: logister-python releases on GitHub.

Follow requests between projects

Logister 3.7 adds explicit project connections and request-identifier evidence. See request correlation setup for the required SDK versions, origin allowlists, permissions, and verification.