HTTP Instrumentation
Trace LLM calls per HTTP request in Flask, FastAPI, Django, and Starlette.
HTTP Instrumentation
HTTP instrumentation automatically creates a trace for each incoming HTTP request and nests all LLM calls made within that request under it. Session IDs are extracted from standard HTTP headers automatically.
HTTP instrumentation is currently available for Python only. TypeScript and Java HTTP tracing via Spring Boot and Servlet containers is handled automatically by the auto-tracing layer — see Auto-Instrumentation.
How It Works
When Observe.init() runs, the SDK:
- Patches the web framework's request lifecycle with OpenTelemetry hooks.
- Creates an HTTP span for each incoming request.
- Extracts session and request IDs from headers (
X-Session-ID,X-Request-ID, etc.). - Nests any LLM calls made during the request under the HTTP span.
Flask
pip install browserstack-ai-sdk opentelemetry-instrumentation-flaskOption 1 — Global instrumentation via Observe.init() (recommended):
import os
from browserstack_ai_sdk import Observe
from flask import Flask, request, jsonify
import openai
Observe.init(
public_key=os.environ["AISDK_PUBLIC_KEY"],
secret_key=os.environ["AISDK_SECRET_KEY"],
)
app = Flask(__name__)
openai_client = openai.OpenAI(api_key=os.environ["OPENAI_API_KEY"])
@app.route("/chat", methods=["POST"])
def chat():
user_message = request.json.get("message", "")
response = openai_client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": user_message}],
)
return jsonify({"reply": response.choices[0].message.content})
if __name__ == "__main__":
app.run(debug=True)Option 2 — Instrument a specific Flask app instance:
from browserstack_ai_sdk.http_instrumentation import instrument_flask_app
instrument_flask_app(app)What is captured per request: HTTP method, path, status code, session ID, all nested LLM calls.
FastAPI
pip install browserstack-ai-sdk opentelemetry-instrumentation-fastapiCall Observe.init() before creating the FastAPI() instance:
import os
from browserstack_ai_sdk import Observe
from fastapi import FastAPI
from pydantic import BaseModel
import openai
Observe.init(
public_key=os.environ["AISDK_PUBLIC_KEY"],
secret_key=os.environ["AISDK_SECRET_KEY"],
)
app = FastAPI()
openai_client = openai.AsyncOpenAI(api_key=os.environ["OPENAI_API_KEY"])
class ChatRequest(BaseModel):
message: str
@app.post("/chat")
async def chat(body: ChatRequest):
response = await openai_client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": body.message}],
)
return {"reply": response.choices[0].message.content}To instrument an existing app instance manually:
from browserstack_ai_sdk.http_instrumentation import instrument_fastapi_app
instrument_fastapi_app(app)What is captured per request: HTTP method, path, status code, all nested LLM calls. send/receive ASGI sub-spans are excluded to reduce noise.
Django
pip install browserstack-ai-sdk opentelemetry-instrumentation-djangoInitialize the SDK in your wsgi.py or asgi.py before the app starts:
# myproject/wsgi.py (or asgi.py)
import os
from browserstack_ai_sdk import Observe
Observe.init(
public_key=os.environ["AISDK_PUBLIC_KEY"],
secret_key=os.environ["AISDK_SECRET_KEY"],
)
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
application = get_wsgi_application()To instrument Django programmatically:
from browserstack_ai_sdk.http_instrumentation import instrument_django_app
instrument_django_app()Then use LLM clients normally inside your views:
# myapp/views.py
import openai
from django.http import JsonResponse
openai_client = openai.OpenAI()
def chat_view(request):
message = request.GET.get("message", "")
response = openai_client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": message}],
)
return JsonResponse({"reply": response.choices[0].message.content})Starlette
pip install browserstack-ai-sdk opentelemetry-instrumentation-starletteimport os
from browserstack_ai_sdk import Observe
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
import openai
Observe.init(
public_key=os.environ["AISDK_PUBLIC_KEY"],
secret_key=os.environ["AISDK_SECRET_KEY"],
)
openai_client = openai.AsyncOpenAI(api_key=os.environ["OPENAI_API_KEY"])
async def chat(request):
body = await request.json()
response = await openai_client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": body.get("message", "")}],
)
return JSONResponse({"reply": response.choices[0].message.content})
app = Starlette(routes=[Route("/chat", chat, methods=["POST"])])To instrument an existing Starlette app instance manually:
from browserstack_ai_sdk.http_instrumentation import instrument_starlette_app
instrument_starlette_app(app)Session ID Extraction
The SDK automatically reads session IDs from these request headers (in order of priority):
| Header | Description |
|---|---|
X-Session-ID | Primary session identifier |
Session-ID | Alternate session header |
X-Request-ID | Used as session ID if no session header is found |
X-Correlation-ID | Used as session ID fallback |
If no session ID header is present, a UUID is generated automatically for the request.