🐍 Python Integration Guide

Learn how to instrument your Python applications with OpenTelemetry to send distributed traces to TraceKit.

90% Automatic Tracing!

With the right libraries, most of your application will be traced automatically with minimal setup. No need to manually instrument every function.

📋

Prerequisites

  • • Python 3.8 or higher
  • • An active TraceKit account
  • • A generated API key from the API Keys page

🔍 What Gets Traced Automatically?

With proper setup, these operations are traced automatically with zero manual instrumentation:

ComponentSetupAuto-Traced?
HTTP EndpointsAuto-instrument Flask/Django/FastAPI✓ Yes
Database QueriesSQLAlchemy, psycopg2, pymongo auto-instrumentation✓ Yes
HTTP Client Callsrequests, httpx, aiohttp instrumentation✓ Yes
Redis Operationsredis-py instrumentation✓ Yes
Celery TasksCelery instrumentation✓ Yes
Custom Business LogicManual spans (optional)Manual

📦 Installation

Install the required OpenTelemetry packages:

# Install TraceKit Python SDK
pip install tracekit-apm

# Framework-specific installation
pip install tracekit-apm[flask]    # For Flask
pip install tracekit-apm[django]   # For Django
pip install tracekit-apm[fastapi]  # For FastAPI
pip install tracekit-apm[all]      # All frameworks

⚙️ Basic Setup

Create a tracing initialization module in your application:

Create tracing.py

# Simple initialization with TraceKit SDK
import tracekit
import os

# Initialize TraceKit with code monitoring enabled
client = tracekit.init(
    api_key=os.getenv('TRACEKIT_API_KEY'),
    service_name=os.getenv('SERVICE_NAME', 'my-python-app'),
    endpoint=os.getenv('TRACEKIT_ENDPOINT', '{ appURL }'),
    enable_code_monitoring=True  # Enable live debugging
)

# That's it! TraceKit handles all OpenTelemetry setup automatically

🚀 Framework Integration

TraceKit works seamlessly with popular Python web frameworks through OpenTelemetry instrumentation.

Flask

# app.py
from flask import Flask, request, jsonify
import tracekit
from tracekit.middleware.flask import init_flask_app
import os

# Create Flask app
app = Flask(__name__)

# Initialize TraceKit with code monitoring
client = tracekit.init(
    api_key=os.getenv("TRACEKIT_API_KEY"),
    service_name="my-flask-app",
    endpoint=os.getenv("TRACEKIT_ENDPOINT", "{ appURL }"),
    enable_code_monitoring=True  # Enable live debugging
)

# Add TraceKit middleware (auto-traces all routes!)
init_flask_app(app, client)

@app.route("/api/users/<int:user_id>")
def get_user(user_id):
    # Capture snapshot for debugging (optional)
    if client.get_snapshot_client():
        client.capture_snapshot('get-user', {
            'user_id': user_id,
            'request_path': request.path,
            'request_method': request.method
        })

    return jsonify({"id": user_id, "name": "Alice"})

if __name__ == "__main__":
    app.run(port=5000)

Django

# settings.py
from tracing import init_tracer
import os

# Initialize tracing at Django startup
init_tracer(
    service_name="my-django-app",
    endpoint=os.getenv("TRACEKIT_ENDPOINT", "{ appURL }"),
    api_key=os.getenv("TRACEKIT_API_KEY")
)

# Add Django instrumentation to INSTALLED_APPS (not required but recommended)
# Then use:
# opentelemetry-instrument python manage.py runserver

# Or instrument manually in your WSGI/ASGI file:
from opentelemetry.instrumentation.django import DjangoInstrumentor
DjangoInstrumentor().instrument()

FastAPI

# main.py
from fastapi import FastAPI
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
import os
from tracing import init_tracer

# Initialize tracing
init_tracer(
    service_name="my-fastapi-app",
    endpoint=os.getenv("TRACEKIT_ENDPOINT", "{ appURL }"),
    api_key=os.getenv("TRACEKIT_API_KEY")
)

# Create FastAPI app
app = FastAPI()

# Auto-instrument FastAPI
FastAPIInstrumentor.instrument_app(app)

@app.get("/api/users")
async def get_users():
    return {"users": ["alice", "bob", "charlie"]}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

📸 Code Monitoring (Live Debugging)

TraceKit includes production-safe code monitoring for live debugging without redeployment. Capture variable state and stack traces at any point in your code.

🔑

Key Features

  • Synchronous API - No await needed (works in both sync and async code)
  • Auto-Registration - Breakpoints automatically created on first call
  • Background Sync - SDK polls for active breakpoints every 30 seconds
  • Rate Limited - Max 1 capture per second per breakpoint
  • Production Safe - No performance impact when inactive

Adding Snapshots

@app.route("/api/checkout")
def checkout():
    cart = request.get_json()
    user_id = cart['user_id']

    # Capture snapshot at this point (synchronous - no await)
    if client.get_snapshot_client():
        client.capture_snapshot('checkout-validation', {
            'user_id': user_id,
            'cart_items': len(cart.get('items', [])),
            'total_amount': cart.get('total', 0),
        })

    # Process payment...
    result = process_payment(cart)

    # Another checkpoint
    if client.get_snapshot_client():
        client.capture_snapshot('payment-complete', {
            'user_id': user_id,
            'payment_id': result['payment_id'],
            'success': result['success'],
        })

    return jsonify({'status': 'success', 'result': result})
📊

What Gets Captured

  • • Variable values at capture point
  • • Full call stack with file/line numbers
  • • Request context (HTTP method, URL, headers)
  • • Execution timestamp

🔗 Nested Spans (Parent-Child Relationships)

Create nested spans to track operations within requests. Parent spans are created automatically by the Flask middleware, and child spans link automatically.

from opentelemetry import trace

@app.route("/api/users/<int:user_id>")
def get_user(user_id):
    # Get the tracer
    tracer = trace.get_tracer(__name__)

    # Parent span is auto-created by Flask middleware
    # Create child span using context manager
    with tracer.start_as_current_span('db.query.user') as span:
        span.set_attributes({
            'db.system': 'postgresql',
            'db.operation': 'SELECT',
            'db.table': 'users',
            'db.statement': 'SELECT * FROM users WHERE id = ?',
            'user.id': user_id
        })

        user = fetch_user_from_db(user_id)

        span.set_attributes({
            'user.found': user is not None,
            'user.role': user.get('role') if user else None
        })

    return jsonify(user)
🌳

Trace Hierarchy

GET /api/users/1 (parent - auto-created) └─ db.query.user (child - manually created)

Automatic Instrumentation Libraries

These libraries automatically create child spans for common operations. Set them up once, and every call is traced automatically.

Database Queries

Automatically trace all database operations:

SQLAlchemy

from sqlalchemy import create_engine
from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor

# Create engine
engine = create_engine("postgresql://user:pass@localhost/mydb")

# Instrument SQLAlchemy (one line!)
SQLAlchemyInstrumentor().instrument(engine=engine)

# Now all queries are automatically traced
from sqlalchemy.orm import Session
with Session(engine) as session:
    users = session.query(User).all()  # This query is traced!

HTTP Client Calls

Automatically trace all outgoing HTTP requests:

from opentelemetry.instrumentation.requests import RequestsInstrumentor
import requests

# Instrument requests library (one line!)
RequestsInstrumentor().instrument()

# Now all HTTP requests are automatically traced
response = requests.get("https://api.example.com/users")  # Traced!

Redis Operations

Trace Redis commands automatically:

from redis import Redis
from opentelemetry.instrumentation.redis import RedisInstrumentor

# Instrument Redis
RedisInstrumentor().instrument()

# Create Redis client
redis_client = Redis(host='localhost', port=6379)

# All operations are now traced!
redis_client.set("key", "value")  # Traced!
value = redis_client.get("key")   # Traced!

🚀 Local UI (Development Mode)

Debug your Python application locally without creating an account. TraceKit Local UI runs on your machine at http://localhost:9999 and automatically receives traces when you run your app in development mode.

💡

Automatic Detection

The Python SDK automatically detects when Local UI is running on port 9999 and sends traces to both Local UI and cloud (if you have an API key configured).

Quick Start

1. Install the Local UI:

npm install -g @tracekit/local-ui

2. Start the Local UI:

tracekit-local

3. Run your app in development mode:

ENV=development python app.py

4. Open your browser:

http://localhost:9999

Features

🔍

Auto-Detection

SDK checks for Local UI at localhost:9999 on startup

Real-Time Updates

See traces instantly with WebSocket live updates

🔒

Development Only

Only activates when ENV=development

🌐

Works Offline

No internet connection required - everything runs locally

Benefits

  • See your first trace in under 60 seconds
  • Debug locally without switching to the cloud dashboard
  • Stay in your flow - everything runs on your machine
  • Works completely offline
  • Perfect for development and demos
💡

Troubleshooting

If traces aren't appearing in Local UI, check:

  • Local UI is running (curl http://localhost:9999/api/health)
  • ENV=development is set
  • SDK version is v0.3.2 or higher
  • Check console for "🔍 Local UI detected" message

🌐 Service Discovery

TraceKit automatically instruments outgoing HTTP calls to create service dependency graphs. When your service makes an HTTP request to another service, TraceKit creates CLIENT spans and injects trace context headers.

Supported HTTP Clients

  • requests - Python requests library
  • httpx - Modern HTTP client
  • aiohttp - Async HTTP client
  • urllib3 - Low-level HTTP client

Custom Service Name Mappings

For local development or when service names can't be inferred from hostnames, configure service name mappings:

import tracekit
import os

client = tracekit.init(
    api_key=os.getenv('TRACEKIT_API_KEY'),
    service_name='my-service',
    # Map localhost URLs to actual service names
    service_name_mappings={
        'localhost:8082': 'payment-service',
        'localhost:8083': 'user-service',
        'localhost:8084': 'inventory-service',
    }
)

# Now requests to localhost:8082 will show as "payment-service"
import requests
response = requests.get('http://localhost:8082/charge')
# -> Creates CLIENT span with peer.service = "payment-service"

This maps localhost:8082 to "payment-service" in your service graph.

Service Name Detection

TraceKit intelligently extracts service names from URLs:

URLExtracted Service Name
http://payment-service:3000payment-service
http://payment.internalpayment
http://payment.svc.cluster.localpayment
https://api.example.comapi.example.com
💡

Viewing Service Dependencies

Visit your TraceKit dashboard to see the Service Map - a visual graph showing which services call which, with health metrics and latency data.

🔧 Manual Instrumentation (Optional)

For custom business logic that isn't covered by auto-instrumentation libraries, you can manually create spans. This is optional and only needed for specific operations you want to measure.

from opentelemetry import trace

@app.route("/api/users/<int:user_id>")
def get_user(user_id):
    # Get the tracer
    tracer = trace.get_tracer(__name__)

    # Parent span is auto-created by Flask middleware
    # Create child span using context manager
    with tracer.start_as_current_span('db.query.user') as span:
        span.set_attributes({
            'db.system': 'postgresql',
            'db.operation': 'SELECT',
            'db.table': 'users',
            'db.statement': 'SELECT * FROM users WHERE id = ?',
            'user.id': user_id
        })

        user = fetch_user_from_db(user_id)

        span.set_attributes({
            'user.found': user is not None,
            'user.role': user.get('role') if user else None
        })

    return jsonify(user)

# Creates a nested trace:
# GET /api/users/1 (parent span - auto-created)
#   └─ db.query.user (child span - manually created)

🔐 Environment Variables

Best practice: Store sensitive configuration in environment variables:

# .env
TRACEKIT_API_KEY=ctxio_your_generated_api_key_here
TRACEKIT_ENDPOINT={ appURL }
SERVICE_NAME=my-python-app

🏭 Production Configuration

⚠️

Production Checklist

  • • Use HTTPS/TLS for the OTLP endpoint
  • • Store API keys in a secrets manager (AWS Secrets Manager, HashiCorp Vault)
  • • Set appropriate service names and versions
  • • Configure resource attributes (deployment.environment, host.name, etc.)
  • • Adjust sampling rates if needed for high-traffic services

🔧 Troubleshooting

Traces Not Appearing?

  1. Verify your API key is correct and not revoked
  2. Check the endpoint URL matches your TraceKit instance
  3. Ensure all required packages are installed
  4. Check application logs for OpenTelemetry errors
  5. Verify TraceKit is running and accessible

Complete Example

Here's a complete working example with Flask:

# Complete Flask example with TraceKit SDK
from flask import Flask, request, jsonify
from opentelemetry import trace
import tracekit
from tracekit.middleware.flask import init_flask_app
import os
import time

# Create Flask app
app = Flask(__name__)

# Initialize TraceKit with code monitoring
client = tracekit.init(
    api_key=os.getenv("TRACEKIT_API_KEY"),
    service_name=os.getenv("SERVICE_NAME", "flask-api"),
    endpoint=os.getenv("TRACEKIT_ENDPOINT", "{ appURL }"),
    enable_code_monitoring=True
)

# Add TraceKit middleware
init_flask_app(app, client)

@app.route("/api/users")
def get_users():
    return jsonify({"users": ["alice", "bob", "charlie"]})

@app.route("/api/users/<int:user_id>")
def get_user(user_id):
    # Capture snapshot for debugging
    if client.get_snapshot_client():
        client.capture_snapshot('get-user', {
            'user_id': user_id,
            'request_path': request.path,
            'request_method': request.method
        })

    # Create nested span for database query
    tracer = trace.get_tracer(__name__)
    with tracer.start_as_current_span('db.query.user') as span:
        span.set_attributes({
            'db.system': 'postgresql',
            'db.operation': 'SELECT',
            'db.table': 'users',
            'user.id': user_id
        })
        time.sleep(0.01)  # Simulate DB query

    return jsonify({"id": user_id, "name": "Alice"})

if __name__ == "__main__":
    print("Flask app starting with TraceKit tracing and code monitoring enabled")
    app.run(host="0.0.0.0", port=5000)
🎉

You're all set!

Your Python application is now sending traces to TraceKit. Visit the Dashboard to see your traces.

Custom Metrics

Track custom metrics like request counts, queue sizes, and response times using the TraceKit metrics API.

Counter

Track monotonically increasing values (requests, events):

import tracekit

client = tracekit.init(
    api_key="your-api-key",
    service_name="my-service"
)

# Create a counter with optional tags
counter = client.counter("http.requests.total", tags={"service": "api"})

# Increment by 1
counter.inc()

# Add a specific value
counter.add(5)

Gauge

Track values that can go up or down (queue size, connections):

# Create a gauge
gauge = client.gauge("http.connections.active")

# Set to specific value
gauge.set(42)

# Increment/decrement
gauge.inc()
gauge.dec()

Histogram

Track value distributions (latencies, sizes):

# Create a histogram with tags
histogram = client.histogram("http.request.duration", tags={"unit": "ms"})

# Record values
histogram.record(45.2)
histogram.record(123.5)

🚀 Next Steps

  • Add auto-instrumentation libraries for components you use (Redis, Celery, MongoDB, etc.)
  • Explore your traces on the Traces page to identify performance bottlenecks
  • Optionally add custom spans for specific business logic you want to measure
  • Configure sampling for high-traffic services to reduce overhead
  • Set up alert rules to get notified when issues occur