Before you start
Create the project with the Python integration type.
| What you need | Why it matters |
|---|---|
| Project API token | Authenticates your app when sending events |
| Logister instance base URL | Used to build the ingest, check-in, and deployment endpoints |
| Python 3.11+ app | Lets you install and run logister-python |
| Optional GitHub App installation | Lets 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.
| Need | What appears in Logister |
|---|---|
| Catch web request failures | Issues from FastAPI, Django, or Flask with structured traceback frames, request metadata, route context, and runtime details. |
| Keep existing logs useful | Standard logging records in the activity feed, with logger.exception(...) and exc_info records reported as errors. |
| Understand request load | Optional FastAPI, Django, and Flask request spans that feed request load waterfall charts on the performance page. |
| Understand worker behavior | Celery task runtime, failures, retries, queue metadata, optional check-ins, and the worker hostname when capture_hostname=True. |
| Debug with Python context | Chained exceptions, runtime metadata, logger metadata, optional frame locals, and source/deployment context when configured. |
| Keep sensitive values out of events | Default 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 history | repository, 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 operations | Metrics, transactions, spans, logs, and check-ins from the shared client. |
Setup flow
Recommended installation path
- Create the project. Logister opens its setup path at the API key step; create the key there.
- Install
logister-pythonin the Python app. - Configure the client with environment variables or direct initialization, including environment, release, repository, commit SHA, and branch when available.
- 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. - Add the framework integration you need first: FastAPI, Django, Flask, or Celery.
- Attach
instrument_logging()if you want standard Pythonloggingrecords to flow into Logister too. - Use the shared client for metrics, logs, transactions, spans, and check-ins across the rest of the app.
- 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.
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.
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)"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].
| Rule | What matches |
|---|---|
| Sensitive keys | The 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 values | Any string is scrubbed for Bearer <x> and Basic <x> credentials, JWT-shaped strings, and lgst_-prefixed keys. |
| URLs | Captured 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.
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 option | Environment variable | Default | What it adds when enabled |
|---|---|---|---|
redact_keys | LOGISTER_REDACT_KEYS | empty | Extra exact-match keys to redact on top of the defaults. |
capture_client_ip | LOGISTER_CAPTURE_CLIENT_IP | false | client_ip and the X-Forwarded-For header on FastAPI, Django, and Flask request events. |
capture_hostname | LOGISTER_CAPTURE_HOSTNAME | false | The host name in runtime metadata, including the Celery worker hostname. |
| (environment only) | LOGISTER_CAPTURE_LOCALS | false | Frame 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.
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.
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",
)| Field | Set it from | Used for |
|---|---|---|
repository | LOGISTER_REPOSITORY or GITHUB_REPOSITORY | Finds the GitHub repository connected to the project. |
commit_sha | LOGISTER_COMMIT_SHA or GITHUB_SHA | Resolves stack frames and deployment context to the exact commit. |
branch | LOGISTER_BRANCH or GITHUB_REF_NAME | Falls back when an event has no commit or deployment record yet. |
release | Your app version, build ID, tag, or package version | Connects 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.
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.
root = logging.getLogger()
root.setLevel(logging.INFO)
instrument_logging(client, logger=root)FastAPI
Capture request timings and uncaught exceptions with middleware.
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.
MIDDLEWARE = [
# ...
"logister.django.LogisterMiddleware",
]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.
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.
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.
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.
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_ipandX-Forwarded-Foronly when you setcapture_client_ip=TrueorLOGISTER_CAPTURE_CLIENT_IP=true. - Hostname is opt-in. Runtime metadata, including the Celery worker hostname, omits the host name unless you set
capture_hostname=TrueorLOGISTER_CAPTURE_HOSTNAME=true. Theruntime_namefield (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 withredact_keysorLOGISTER_REDACT_KEYS. - Frame locals skip
self,cls, andrequestwhenLOGISTER_CAPTURE_LOCALS=true. - Automatic captures log instead of raising. To see integration delivery failures, enable
DEBUGon thelogisterlogger. Direct client calls still raiseLogisterError. - Flush at shutdown. FastAPI, Django's async path, and logging inside a running event loop deliver on a background thread. Call
client.flush()orclient.close()before the process exits. - Root logger handlers.
instrument_logging()ignores records fromlogister,httpx, andhttpcore, so it is safe to attach to the root logger atINFO. - Use HTTPS. A
LOGISTER_BASE_URLthat uses plainhttp://to anything other than a loopback host (localhost,*.localhost,127.0.0.0/8,::1) emits aUserWarning.
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.