Skip to main content

Backend Setup: Python

This guide walks you through instrumenting a Python application with the SF Veritas SDK for Sailfish Enterprise.

Auto-Installation

If you connected GitHub and received Auto-Installation PRs, the API key, service identifier, and graphql endpoint are already configured for you. Merge the PR and you're done.

Getting Your API Key

  1. Open the Sailfish dashboard
  2. Log in with your enterprise email
  3. Navigate to Settings > Configuration
  4. Copy your company's API key

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:

from sf_veritas import setup_interceptors

setup_interceptors(
api_key="<see-api-key-from-your-account-settings-page>",
service_identifier='acme-corp/web-api/src/main.py', # Format: <org>/<repo>/<path-to-this-file>
service_version='1.0.0',
)

# Your application code continues below...

Configuration Options

OptionTypeRequiredDescription
api_keystrYesYour Sailfish Enterprise API key
service_identifierstrNoUnique identifier in <org>/<repo>/<path> format. Falls back to SERVICE_VERSION or GIT_SHA env vars if not set
service_versionstrNoVersion of your service
graphql_endpointstrNoSailfish collector endpoint. Defaults to the Sailfish cloud endpoint. Override for self-hosted or local setups
service_display_namestrNoHuman-readable name shown in the Sailfish dashboard
git_shastrNoGit commit SHA. Auto-detected from CI/CD env vars (see below)
git_orgstrNoGit organization name. Auto-detected from GITHUB_REPOSITORY
git_repostrNoGit repository name. Auto-detected from GITHUB_REPOSITORY
git_providerstrNoGit provider (e.g. github, gitlab)
domains_to_not_propagate_headers_tolist[str]NoExternal domains where Sailfish headers should not be injected
routes_to_skip_network_hopslist[str]NoURL patterns to exclude from network hop capture

Framework Examples

Django

Critical: Placement matters

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
from sf_veritas import setup_interceptors
setup_interceptors(
api_key="<see-api-key-from-your-account-settings-page>",
service_identifier='acme-corp/django-api/manage.py', # Format: <org>/<repo>/<path-to-this-file>
service_version='1.0.0',
)

from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)

if __name__ == '__main__':
main()
Add it to all three entry points

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 pointUsed by
manage.pypython manage.py runserver (local dev)
wsgi.pygunicorn myproject.wsgi:application
asgi.pyuvicorn 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
from sf_veritas import setup_interceptors
setup_interceptors(
api_key="<see-api-key-from-your-account-settings-page>",
service_identifier='acme-corp/django-api/wsgi.py', # Format: <org>/<repo>/<path-to-this-file>
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
from sf_veritas import setup_interceptors
setup_interceptors(
api_key="<see-api-key-from-your-account-settings-page>",
service_identifier='acme-corp/django-api/asgi.py', # Format: <org>/<repo>/<path-to-this-file>
service_version='1.0.0',
)

application = get_asgi_application()

FastAPI

# main.py
from sf_veritas import setup_interceptors

setup_interceptors(
api_key="<see-api-key-from-your-account-settings-page>",
service_identifier='acme-corp/fastapi-api/src/main.py', # Format: <org>/<repo>/<path-to-this-file>
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 Sailfish
return {'users': []}

Flask

# app.py
from sf_veritas import setup_interceptors

setup_interceptors(
api_key="<see-api-key-from-your-account-settings-page>",
service_identifier='acme-corp/flask-api/src/app.py', # Format: <org>/<repo>/<path-to-this-file>
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 Sailfish
return {'users': []}

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)

LibraryProducerConsumer
Celerytask.apply_async() / .delay()task_prerun / task_postrun signals
confluent-kafkaProducer.produce()Consumer.poll()
aiokafkaAIOKafkaProducer.send() / send_and_wait()AIOKafkaConsumer __anext__ / getmany
kafka-pythonKafkaProducer.send()consumer iteration

Each works automatically — just import the library and the SDK's patches wire up on setup_interceptors().

Example

from sf_veritas import setup_interceptors
setup_interceptors(
api_key="<see-api-key-from-your-account-settings-page>",
service_identifier='acme-corp/order-service/src/main.py',
service_version='1.0.0',
)

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)

In the Sailfish dashboard you will 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

GIT_SHA

The GIT_SHA environment variable allows Sailfish to correlate telemetry with specific commits.

The SDK auto-detects GIT_SHA from common CI/CD platforms:

CI PlatformEnvironment Variable
GenericGIT_SHA
GitHub ActionsGITHUB_SHA
GitLab CICI_COMMIT_SHA
Bitbucket PipelinesBITBUCKET_COMMIT
VercelVERCEL_GIT_COMMIT_SHA
CircleCICIRCLE_SHA1
HerokuHEROKU_SLUG_COMMIT
RenderRENDER_GIT_COMMIT
RailwayRAILWAY_GIT_COMMIT_SHA
AWS CodeBuildCODEBUILD_RESOLVED_SOURCE_VERSION

For custom CI systems, set it manually:

# 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 }}

Verifying the Setup

  1. Deploy your application with the Sailfish SDK configured
  2. Trigger some activity in your application
  3. Open the Sailfish dashboard
  4. You should see telemetry appearing in your project

Troubleshooting

No logs appearing

  1. Check the API key: Ensure api_key is set to your Enterprise API key
  2. Check the service identifier: Ensure service_identifier uses the <org>/<repo>/<path> format
  3. Check console output: Look for SF Veritas initialization messages in your terminal

Connection errors

  1. Ensure your deployment can reach https://api-service.sailfish.ai (or your custom graphql_endpoint if configured)
  2. Check that outbound HTTPS (port 443) is not blocked by a firewall or network policy

Import errors

  1. Ensure sf-veritas is installed: pip show sf-veritas
  2. Check you're using Python 3.8+
  3. Verify you're in the correct virtual environment

Multi-Service Setup

When running multiple Python services, give each a unique service_identifier following the <org>/<repo>/<path> format:

# user-service/main.py
from sf_veritas import setup_interceptors
setup_interceptors(
api_key="<see-api-key-from-your-account-settings-page>",
service_identifier='acme-corp/user-service/src/main.py', # Format: <org>/<repo>/<path-to-this-file>
service_version='1.0.0',
)

# order-service/main.py
from sf_veritas import setup_interceptors
setup_interceptors(
api_key="<ApiKey />",
service_identifier='acme-corp/order-service/src/main.py', # Format: <org>/<repo>/<path-to-this-file>
service_version='1.0.0',
)

Use the service filter in the Sailfish dashboard to switch between services.

Next Steps


Local Development

Looking to set up SF Veritas for local development with the Desktop App? See the Desktop App Python guide.