Start sending data in under 5 minutes
Pick your integration below. Every step is copyable — install the SDK, add the middleware, and watch requests, errors, metrics and traces land in your dashboard.
Install the SDK
Python 3.10+ required. Middleware ships for FastAPI, Django and Flask; the FastAPI middleware is a Starlette BaseHTTPMiddleware, so it works on any Starlette app.
pip install ledger-sdkCreate a project and API key
Sign in to the dashboard, create a project, then generate an API key under Settings. The key is shown only once — copy it now.
Store the key in your environment
Never hardcode the key in source control.
export LEDGER_API_KEY="ledger_proj_1_your_api_key"
export LEDGER_BASE_URL="https://ledger-server.jtuta.cloud"Add the middleware — FastAPI
Every request, response and exception is captured automatically.
import os
from contextlib import asynccontextmanager
from fastapi import FastAPI
from ledger import LedgerClient
from ledger.integrations.fastapi import LedgerMiddleware
ledger = LedgerClient(
api_key=os.getenv("LEDGER_API_KEY"),
base_url=os.getenv("LEDGER_BASE_URL"),
service_name="my-service",
)
@asynccontextmanager
async def lifespan(app: FastAPI):
yield
await ledger.shutdown()
app = FastAPI(lifespan=lifespan)
app.add_middleware(LedgerMiddleware, ledger_client=ledger)Add the middleware — Django
Add the client to settings.py and register the middleware.
# settings.py
import os
from ledger import LedgerClient
LEDGER_CLIENT = LedgerClient(
api_key=os.getenv("LEDGER_API_KEY"),
base_url=os.getenv("LEDGER_BASE_URL"),
)
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.middleware.common.CommonMiddleware",
"ledger.integrations.django.LedgerMiddleware",
]Add the middleware — Flask
Attach the client to the app config and wrap it.
import os
from flask import Flask
from ledger import LedgerClient
from ledger.integrations.flask import LedgerMiddleware
app = Flask(__name__)
ledger = LedgerClient(
api_key=os.getenv("LEDGER_API_KEY"),
base_url=os.getenv("LEDGER_BASE_URL"),
)
app.config["LEDGER_CLIENT"] = ledger
LedgerMiddleware(app)Send manual logs
Attach any attributes you want to filter on later.
ledger.log_info("User logged in", attributes={"user_id": 123})
ledger.log_warning("Slow query", attributes={"duration_ms": 450})
ledger.log_error("Payment failed", attributes={"error_code": "CARD_DECLINED"})
try:
result = process_payment()
except Exception as e:
ledger.log_exception(e, message="Payment processing failed")Forward standard library logging
Optional. Routes every logging.getLogger(...) call — yours and third-party libraries — to Ledger.
ledger.instrument_logging()
import logging
logging.getLogger(__name__).warning("this reaches Ledger too")Hook up your existing logging library
Optional. If you already use loguru or structlog, tee it into Ledger instead of rewriting call sites — records take the same path as ledger.log_info, so trace correlation and truncation still apply. SQLAlchemy queries can be traced as child spans too.
import structlog
from ledger.integrations.loguru import add_loguru_sink
from ledger.integrations.sqlalchemy import instrument
from ledger.integrations.structlog import ledger_structlog_processor
# loguru: returns a handler id you can pass to logger.remove() later
add_loguru_sink(ledger, level="INFO")
# structlog: place before your rendering processor
structlog.configure(
processors=[
ledger_structlog_processor(ledger),
structlog.processors.JSONRenderer(),
]
)
# SQLAlchemy: one span per query
instrument(engine)Skip noisy paths
Health checks and probes do not need to be logged. By default only requests matching a registered route are logged, so 404 scanner noise is dropped already.
app.add_middleware(
LedgerMiddleware,
ledger_client=ledger,
exclude_paths=["/health", "/metrics"],
)Capture the real visitor IP behind a proxy
Optional. Behind a reverse proxy or load balancer, only the direct peer address is trusted by default — that's your proxy, not the visitor — so endpoint logs show the proxy's address and country stays empty until this is set. Two things both have to be true: your proxy must forward X-Forwarded-For (most don't by default), and trusted_proxies must be set to the address range your app actually receives connections from. If you get the range wrong, the SDK logs a one-time warning naming the address it actually observed, so you can copy it straight in.
app.add_middleware(
LedgerMiddleware,
ledger_client=ledger,
trusted_proxies=["10.0.0.0/8"], # replace with your proxy's actual address range
)Open the dashboard
Start your app, hit an endpoint, then open Explore. Logs appear within seconds.