Backend Setup: Python
This guide walks you through instrumenting a Python application with the SF Veritas SDK
Installation
Install the SF Veritas package using your preferred package manager:
pip
pip install sf-veritas
Poetry
poetry add sf-veritas
uv
uv add sf-veritas
Pipenv
pipenv install sf-veritas
pdm
pdm add sf-veritas
pyproject.toml (manual)
If you manage dependencies directly in pyproject.toml, add sf-veritas to your dependencies:
[project]
dependencies = [
"sf-veritas",
# ... your other dependencies
]
Then install with your tool of choice:
pip install . # pip
poetry install # Poetry
uv sync # uv
pdm install # pdm
Basic Setup
Add the following to your application's entry point. The SDK activates when it detects the DEBUG=true environment variable, so it only runs during local development:
You must set the DEBUG=true environment variable when running your app locally. This is how SF Veritas knows to activate. Add it as a prefix to your run command:
DEBUG=true python main.py
DEBUG=true uvicorn main:app --reload
DEBUG=true flask run
DEBUG=true python manage.py runserver
import os
# Initialize SF Veritas ONLY in development mode
if os.environ.get('DEBUG') == 'true':
from sf_veritas import setup_interceptors
setup_interceptors(
api_key='sf-veritas-local',
graphql_endpoint='http://localhost:6776/graphql/',
service_identifier='my-python-service',
service_version='1.0.0',
)
# Your application code continues below...
Using a .env File
Instead of prefixing every command, create a .env file in your project root:
# .env
DEBUG=true
Then load it in your application (most frameworks support this automatically, or use python-dotenv):
pip install python-dotenv
from dotenv import load_dotenv
load_dotenv() # Loads .env file
import os
if os.environ.get('DEBUG') == 'true':
from sf_veritas import setup_interceptors
setup_interceptors(
api_key='sf-veritas-local',
graphql_endpoint='http://localhost:6776/graphql/',
service_identifier='my-python-service',
)
Add .env to your .gitignore to avoid committing development settings. Create a .env.example file for reference:
# .env.example
DEBUG=true
Configuration Options
| Option | Type | Required | Description |
|---|---|---|---|
api_key | str | Yes | Use "sf-veritas-local" for local development |
graphql_endpoint | str | Yes | URL of the local collector (default port 6776) |
service_identifier | str | Yes | Unique name for your service |
service_version | str | No | Version of your service |
Framework Examples
Django
setup_interceptors() must be called before Django initializes (before execute_from_command_line, get_wsgi_application(), or get_asgi_application()). This is because SF Veritas needs to monkey-patch the framework, logging, and HTTP clients before they are loaded.
Do NOT place setup_interceptors() at the bottom of settings.py — by that point Django's module loading is already underway and the interceptors cannot fully patch the framework. The correct location is manage.py (for runserver) or wsgi.py/asgi.py (for production deployments), before the Django application is created.
Recommended: manage.py (used by python manage.py runserver):
#!/usr/bin/env python
import os
import sys
def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
# Initialize SF Veritas BEFORE Django setup
if os.environ.get('DEBUG') == 'true':
from sf_veritas import setup_interceptors
setup_interceptors(
api_key='sf-veritas-local',
graphql_endpoint='http://localhost:6776/graphql/',
service_identifier='django-api',
service_version='1.0.0',
)
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()
Most Django projects use different entry points in different environments — manage.py for local dev, wsgi.py for Gunicorn, asgi.py for Uvicorn/Daphne. Add setup_interceptors() to all three files so telemetry works regardless of how Django is started. The call is idempotent, so duplicate initialization is safe.
| Entry point | Used by |
|---|---|
manage.py | python manage.py runserver (local dev) |
wsgi.py | gunicorn myproject.wsgi:application |
asgi.py | uvicorn myproject.asgi:application, daphne myproject.asgi:application |
For WSGI deployments (Gunicorn), add it to wsgi.py before get_wsgi_application():
# wsgi.py
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
# Initialize SF Veritas BEFORE the WSGI app is created
if os.environ.get('DEBUG') == 'true':
from sf_veritas import setup_interceptors
setup_interceptors(
api_key='sf-veritas-local',
graphql_endpoint='http://localhost:6776/graphql/',
service_identifier='django-api',
service_version='1.0.0',
)
application = get_wsgi_application()
For ASGI deployments (Uvicorn, Daphne), add it to asgi.py before get_asgi_application():
# asgi.py
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
# Initialize SF Veritas BEFORE the ASGI app is created
if os.environ.get('DEBUG') == 'true':
from sf_veritas import setup_interceptors
setup_interceptors(
api_key='sf-veritas-local',
graphql_endpoint='http://localhost:6776/graphql/',
service_identifier='django-api',
service_version='1.0.0',
)
application = get_asgi_application()
FastAPI
# main.py
import os
# Initialize SF Veritas only in development
if os.environ.get('DEBUG') == 'true':
from sf_veritas import setup_interceptors
setup_interceptors(
api_key='sf-veritas-local',
graphql_endpoint='http://localhost:6776/graphql/',
service_identifier='fastapi-api',
service_version='1.0.0',
)
from fastapi import FastAPI
app = FastAPI()
@app.get('/api/users')
async def get_users():
print('Fetching users') # This will appear in SF Veritas Console
return {'users': []}
# Run with: DEBUG=true uvicorn main:app --reload
Flask
# app.py
import os
# Initialize SF Veritas only in development
if os.environ.get('DEBUG') == 'true':
from sf_veritas import setup_interceptors
setup_interceptors(
api_key='sf-veritas-local',
graphql_endpoint='http://localhost:6776/graphql/',
service_identifier='flask-api',
service_version='1.0.0',
)
from flask import Flask
app = Flask(__name__)
@app.route('/api/users')
def get_users():
print('Fetching users') # This will appear in SF Veritas Console
return {'users': []}
# Run with: DEBUG=true flask run
Async Queue Tracing
Sailfish automatically stitches producer → broker → consumer into a single trace when your code uses an instrumented async-queue library. Background jobs show up in the same trace as the HTTP request that enqueued them.
HTTP POST /enqueue ──► producer.apply_async() ──► broker ──► worker.task
(inbound hop) (ENQUEUE hop) (CONSUME hop)
│────────── shared session + pageVisit ──────────│
Supported libraries (zero-config)
| Library | Producer | Consumer |
|---|---|---|
| Celery | task.apply_async() / .delay() | task_prerun / task_postrun signals |
| confluent-kafka | Producer.produce() | Consumer.poll() |
| aiokafka | AIOKafkaProducer.send() / send_and_wait() | AIOKafkaConsumer __anext__ / getmany |
| kafka-python | KafkaProducer.send() | consumer iteration |
Each works automatically — just import the library and the SDK's patches
wire up on setup_interceptors().
Example
import os
if os.getenv('SF_DEV') == 'true':
from sf_veritas import setup_interceptors
setup_interceptors(
api_key='sf-veritas-local',
service_identifier='order-service',
graphql_endpoint='http://localhost:6776/graphql/',
)
from celery import Celery
app = Celery('orders', broker='redis://localhost:6379/0')
@app.task
def send_receipt(order_id: int):
...
# In your HTTP handler:
send_receipt.delay(order.id)
# Run with: SF_DEV=true celery -A myapp worker
Open the Desktop App Console — you'll see one linked trace: the HTTP
request → an ENQUEUE hop for send_receipt → a CONSUME hop (from the
worker process) → any downstream HTTP calls the task makes.
Canvas, retry, and scheduled tasks
- Groups / chains / chords (
group,chain,chord) — every subtask is stitched into the same trace with a distinct requestId per hop. self.retry()/autoretry_for— each retry attempt gets its own requestId while preserving session + pageVisit.countdown=N/eta=...— the trace ID is captured at enqueue and flows through the broker unchanged; the worker's CONSUME hop lines up even if execution is delayed.
Runtime controls
Set SF_DISABLE_QUEUE_PATCHES=true to skip every queue-library patch
without disabling the rest of the SDK.
On startup, Sailfish logs one info line listing which libraries were patched, skipped (not installed), and failed — useful for confirming the integration.
Caveats
- Kafka transactional producers (if you use
producer.transaction()) emit their hop synchronously at.send(), not at.commit(). An aborted transaction still leaves its hop in the timeline.
Function Tracing
Use decorators to control which functions appear in the Flamechart:
from sf_veritas import capture_function_spans, skip_function_tracing
@capture_function_spans()
def important_business_logic():
"""This function will be traced in the Flamechart."""
process_data()
return result
@skip_function_tracing
def internal_helper():
"""This function won't be traced."""
pass
Decorator Options
@capture_function_spans(
include_arguments=True, # Capture function arguments
include_return_value=True, # Capture return values
sample_rate=1.0, # Sampling rate (0.0 to 1.0)
)
def traced_function():
pass
Verifying the Setup
- Start your application with
DEBUG=true:- Django:
DEBUG=true python manage.py runserver - FastAPI:
DEBUG=true uvicorn main:app --reload - Flask:
DEBUG=true flask run
- Django:
- In VS Code, open the Command Palette (
Ctrl+Shift+P/Cmd+Shift+P) - Run
SF Veritas: Show Console Logs - Trigger some activity in your application
- You should see logs appearing in the Console panel
Troubleshooting
No logs appearing
- Check the local server: Run
SF Veritas: Start Local Serverif not running - Verify the endpoint: Ensure
graphql_endpointmatches your server port - Check console output: Look for SF Veritas initialization messages in your terminal
Connection refused errors
- Verify the SF Veritas Desktop App is installed and running
- Check that the local server is running (look for server status in the Desktop App)
- Ensure port 6776 is not blocked by a firewall
Import errors
- Ensure
sf-veritasis installed:pip show sf-veritas - Check you're using Python 3.8+
- Verify you're in the correct virtual environment
Multi-Service Setup
When running multiple Python services locally, give each a unique service_identifier:
# user-service/main.py
if os.environ.get('DEBUG') == 'true':
from sf_veritas import setup_interceptors
setup_interceptors(
api_key='sf-veritas-local',
graphql_endpoint='http://localhost:6776/graphql/',
service_identifier='user-service',
service_version='1.0.0',
)
# order-service/main.py
if os.environ.get('DEBUG') == 'true':
from sf_veritas import setup_interceptors
setup_interceptors(
api_key='sf-veritas-local',
graphql_endpoint='http://localhost:6776/graphql/',
service_identifier='order-service',
service_version='1.0.0',
)
Use the service filter in the Console to switch between services.
How It Works
The environment variable controls when SF Veritas is active:
| Framework | Dev Check | Dev Command | SF Veritas |
|---|---|---|---|
| Django | DEBUG=true or settings.DEBUG | DEBUG=true python manage.py runserver | ✅ Active |
| FastAPI | DEBUG=true | DEBUG=true uvicorn main:app --reload | ✅ Active |
| Flask | DEBUG=true | DEBUG=true flask run | ✅ Active |
| Any | — | Production deploy (no DEBUG=true) | ❌ Not loaded |
All frameworks use the same DEBUG=true environment variable for consistency:
- Django: Also supports
settings.DEBUGinsettings.py - FastAPI: Use
DEBUG=trueprefix or.envfile - Flask: Use
DEBUG=trueprefix or.envfile
Enterprise Setup
This section applies to users with a Sailfish Enterprise account. If you're using the Desktop App for local development only, the basic setup above is all you need.
Enterprise users need two configurations — one for local development (Desktop App) and one for staging/production (Sailfish cloud). The SDK should detect which environment it's running in and configure itself accordingly.
Dual-Mode Configuration
import os
def setup_sailfish():
"""Configure SF Veritas for local dev or enterprise staging/production."""
if os.environ.get('DEBUG') == 'true':
# Local development — send to Desktop App
from sf_veritas import setup_interceptors
setup_interceptors(
api_key='sf-veritas-local',
graphql_endpoint='http://localhost:6776/graphql/',
service_identifier='my-python-service',
service_version='1.0.0',
)
elif os.environ.get('SAILFISH_API_KEY'):
# Staging/Production — send to Sailfish cloud
# Do NOT set graphql_endpoint — the SDK defaults to the cloud endpoint
from sf_veritas import setup_interceptors
setup_interceptors(
api_key=os.environ['SAILFISH_API_KEY'],
service_identifier='my-python-service',
service_version='1.0.0',
)
setup_sailfish()
Django Example
As noted above, place this in manage.py (or wsgi.py/asgi.py), not in settings.py:
# manage.py — before execute_from_command_line()
import os
_debug = os.environ.get('DEBUG') == 'true'
_sailfish_api_key = os.environ.get('SAILFISH_API_KEY')
if _debug:
# Local development
from sf_veritas import setup_interceptors
setup_interceptors(
api_key='sf-veritas-local',
graphql_endpoint='http://localhost:6776/graphql/',
service_identifier='django-api',
)
elif _sailfish_api_key:
# Staging/Production (Enterprise)
from sf_veritas import setup_interceptors
setup_interceptors(
api_key=_sailfish_api_key,
service_identifier='django-api',
)
Environment Variables for Enterprise
Set these in your staging/production deployment:
# Required — your company's Enterprise API key
SAILFISH_API_KEY=your-enterprise-api-key
# Required — the git commit SHA for this build
GIT_SHA=$(git rev-parse HEAD)
# Optional — override the endpoint (only for local dev, do NOT set in production)
# SAILFISH_GRAPHQL_ENDPOINT=http://localhost:6776/graphql/
GIT_SHA
The GIT_SHA environment variable allows Sailfish to correlate telemetry with specific commits. Set it in your build pipeline:
# Docker
docker build --build-arg GIT_SHA=$(git rev-parse HEAD) .
# In Dockerfile
ARG GIT_SHA
ENV GIT_SHA=$GIT_SHA
# CI/CD (GitHub Actions)
env:
GIT_SHA: ${{ github.sha }}
URL Override
You can override the GraphQL endpoint in two ways:
- Environment variable:
SAILFISH_GRAPHQL_ENDPOINT - Constructor argument:
graphql_endpointinsetup_interceptors()
| Environment | Endpoint | How |
|---|---|---|
| Local dev | http://localhost:6776/graphql/ | Set graphql_endpoint or SAILFISH_GRAPHQL_ENDPOINT |
| Staging/Production | Default (cloud) | Do not set either — the SDK uses https://api-service.sailfishqa.com/graphql/ automatically |