Setup guide

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.

bash
pip install ledger-sdk
1

Create 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.

2

Store the key in your environment

Never hardcode the key in source control.

bash
export LEDGER_API_KEY="ledger_proj_1_your_api_key"
export LEDGER_BASE_URL="https://ledger-server.jtuta.cloud"
3

Add the middleware — FastAPI

Every request, response and exception is captured automatically.

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

Add the middleware — Django

Add the client to settings.py and register the middleware.

python
# 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",
]
5

Add the middleware — Flask

Attach the client to the app config and wrap it.

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

Send manual logs

Attach any attributes you want to filter on later.

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

Forward standard library logging

Optional. Routes every logging.getLogger(...) call — yours and third-party libraries — to Ledger.

python
ledger.instrument_logging()

import logging

logging.getLogger(__name__).warning("this reaches Ledger too")
8

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.

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

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.

python
app.add_middleware(
    LedgerMiddleware,
    ledger_client=ledger,
    exclude_paths=["/health", "/metrics"],
)
10

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.

python
app.add_middleware(
    LedgerMiddleware,
    ledger_client=ledger,
    trusted_proxies=["10.0.0.0/8"],  # replace with your proxy's actual address range
)
11

Open the dashboard

Start your app, hit an endpoint, then open Explore. Logs appear within seconds.

12