By default, ABV generates random trace IDs and span IDs for every trace. This works fine for basic observability, but limits advanced use cases:
Distributed tracing: Can’t correlate events across multiple services
External system integration: Can’t map ABV traces to your support tickets, orders, or sessions
Programmatic access: Can’t fetch specific traces via API using your own IDs
Default ID format:
Trace ID: Random 32-character hex string (W3C Trace Context compliant)
Span ID: Random 16-character hex string
For advanced workflows, bring your own trace IDs.
Generate Deterministic Trace IDs from External IDs
ABV provides utilities to generate W3C-compliant trace IDs from any seed string. This creates deterministic, reproducible IDs from your external identifiers.Why deterministic IDs?
Same input always produces the same trace ID
Correlate ABV traces with support tickets, order IDs, session tokens
Re-generate the same trace ID later for scoring or retrieval
Python:
from abvdev import ABVabv = ABV(api_key="sk-abv-...")# Generate trace ID from external ID (always the same output for same input)external_id = "support-ticket-12345"trace_id = abv.create_trace_id(seed=external_id)# trace_id: "a1b2c3d4e5f6789012345678abcdef12" (32 hex chars)
Now you can regenerate this trace ID anytime using the same external ID.
Use Custom Trace IDs in Your Code
Once you have a trace ID, pass it to ABV when creating traces or spans. This ensures all events use your custom ID instead of a random one.Python (decorator pattern):
from abvdev import observe, ABVabv = ABV(api_key="sk-abv-...")@observe()def process_user_request(user_id, request_data): return f"Processed request for {user_id}"# Generate trace ID from external IDexternal_trace_id = "order-abc-123"trace_id = abv.create_trace_id(seed=external_trace_id)# Pass as special keyword argumentprocess_user_request( user_id="user-456", request_data={"query": "refund status"}, abv_trace_id=trace_id # Custom trace ID)
Python (manual span creation):
with abv.start_as_current_span( name="process-order", trace_context={"trace_id": trace_id}) as span: # Your code here—this span uses the custom trace ID result = process_order(order_id="abc-123")
Note: When setting a custom trace ID in JS/TS, you must provide a parentSpanContext with an arbitrary spanId. This detaches the span from the active context.
Propagate Trace IDs Across Services
For distributed tracing, propagate the trace ID from the entry point through all downstream services via HTTP headers, message queues, or RPC metadata.HTTP headers (W3C Trace Context standard):
traceparent: 00-<trace-id>-<parent-span-id>-01
Python example (service A → service B):
# Service A: Extract trace ID and forwardimport requestsfrom abvdev import ABVabv = ABV(api_key="sk-abv-...")current_trace_id = abv.get_current_trace_id()# Forward to service B with traceparent headerresponse = requests.post( "https://service-b.example.com/process", headers={"traceparent": f"00-{current_trace_id}-{span_id}-01"}, json={"data": "payload"})
Service B: Extract and use trace ID:
# Service B: Extract trace ID from headerdef handle_request(request): traceparent = request.headers.get("traceparent") if traceparent: parts = traceparent.split("-") trace_id = parts[1] # Extract trace ID # Use the same trace ID in service B with abv.start_as_current_span( name="process-in-service-b", trace_context={"trace_id": trace_id} ): # Your code here pass
Now both services log events under the same trace ID. In the ABV Dashboard, you see a unified timeline across services.
Access Trace IDs Programmatically
Retrieve the current trace ID at runtime for logging, debugging, or passing to external systems.Python:
from abvdev import ABVabv = ABV(api_key="sk-abv-...")with abv.start_as_current_span(name="my-operation") as span: # Option 1: Access via span trace_id = span.trace_id # Option 2: Get from ABV client current_trace_id = abv.get_current_trace_id() current_span_id = abv.get_current_observation_id() print(f"Trace ID: {trace_id}") print(f"Span ID: {current_span_id}")
JavaScript/TypeScript:
import { startActiveObservation, getActiveTraceId } from '@abvdev/tracing';await startActiveObservation('my-operation', async (span) => { const traceId = getActiveTraceId(); console.log(`Current trace ID: ${traceId}`); // Use trace ID in logs or external systems await logToExternalSystem({ traceId, event: 'operation-start' });});
Store these IDs in your logs or databases to link back to ABV traces later.
Distributed Tracing: Correlate Events Across Microservices
Modern applications span multiple services: API gateways, authentication services, LLM backends, RAG retrievers, databases, caching layers. When a user request fails, you need to see the full journey—not just isolated logs from each service.The problem:
Without shared trace IDs, each service logs events independently. You have:
API gateway logs: “Request received at 10:45:12”
Auth service logs: “User validated at 10:45:13”
LLM backend logs: “LLM call failed at 10:45:15”
No connection between these events
The solution with custom trace IDs:
API gateway generates a trace ID when the request arrives
Passes trace ID to auth service via HTTP header (traceparent)
Auth service extracts trace ID and uses it for its logs
Auth service forwards trace ID to LLM backend
LLM backend logs events with the same trace ID
ABV groups all events into one trace
In the dashboard:
Search for the trace ID and see the complete timeline:
How trace IDs propagate across services:Implementation:
# API Gateway (entry point)trace_id = abv.create_trace_id(seed=f"request-{request_id}")# Pass to downstream services via HTTP headerheaders = {"traceparent": f"00-{trace_id}-{span_id}-01"}response = requests.post("https://auth-service/validate", headers=headers, ...)# Auth service extracts and uses trace IDincoming_trace_id = extract_trace_id_from_header(request.headers)with abv.start_as_current_span(name="validate-user", trace_context={"trace_id": incoming_trace_id}): # Validation logic pass
Benefits:
Root cause analysis: See exactly where requests fail in multi-service workflows
Performance optimization: Identify slow services in the critical path
Debugging: Trace request flow end-to-end with one query
Deeplinking: Jump from Your UI to ABV Traces
Your support dashboard, admin panel, or internal tools display user sessions, orders, or tickets. When investigating issues, you want to jump directly to the corresponding ABV trace without searching manually.The problem:
ABV assigns random trace IDs (e.g., f3a2b1c9-4567-8901-2345-6789abcdef01). Your support ticket has ID ticket-12345. No connection between them.The solution with deterministic trace IDs:
Generate ABV trace IDs from your external IDs:
# When logging the traceticket_id = "ticket-12345"trace_id = abv.create_trace_id(seed=ticket_id)# trace_id: "a1b2c3d4e5f6789012345678abcdef12" (deterministic)# Store ticket_id in trace metadata for reverse lookupwith abv.start_as_current_span( name="handle-support-ticket", trace_context={"trace_id": trace_id}) as span: span.set_attribute("ticket_id", ticket_id)
In your support dashboard:
# User clicks "View ABV Trace" button for ticket-12345ticket_id = "ticket-12345"trace_id = abv.create_trace_id(seed=ticket_id) # Regenerate same ID# Construct deeplink URLabv_url = f"https://app.abv.dev/traces/{trace_id}"# Redirect user to ABV trace
Benefits:
Instant navigation from your tools to ABV traces
No manual searching or copy-pasting trace IDs
Better support workflows: “View this user’s LLM traces” → one click
Evaluations: Score Traces by External IDs
You’re running A/B tests, experiments, or benchmarks. Each experiment has an ID (e.g., experiment-2025-01-v2). You need to fetch all traces from that experiment and score them programmatically.The problem:
ABV’s random trace IDs don’t map to your experiment IDs. You can add metadata (metadata.experiment_id = "experiment-2025-01-v2"), but fetching and scoring requires API queries.The solution with deterministic trace IDs:
Generate trace IDs that include your experiment ID:
# When running experimentexperiment_id = "experiment-2025-01-v2"user_request_id = "request-789"# Combine experiment ID + request ID for unique traceseed = f"{experiment_id}-{user_request_id}"trace_id = abv.create_trace_id(seed=seed)# Log trace with custom IDwith abv.start_as_current_span( name="experiment-run", trace_context={"trace_id": trace_id}) as span: span.set_attribute("experiment_id", experiment_id) # Run LLM call
Later, score all traces from experiment:
# Fetch all request IDs from experimentrequest_ids = ["request-789", "request-790", "request-791"]for request_id in request_ids: # Regenerate trace ID seed = f"{experiment_id}-{request_id}" trace_id = abv.create_trace_id(seed=seed) # Fetch trace via API trace = abv.get_trace(trace_id) # Score the trace abv.score(trace_id=trace_id, name="quality", value=0.85)
Benefits:
Programmatic access to specific traces using your IDs
Batch scoring for experiments without complex queries
Reproducible evaluations (same seed = same trace ID every time)
API Integration: Fetch Traces Using Your IDs
You want to fetch ABV traces programmatically via API, but you only know your external IDs (session tokens, order IDs, user IDs). ABV’s random trace IDs force you to query by metadata, which is slower and more complex.The solution with deterministic trace IDs:
Generate ABV trace IDs from your external IDs, then fetch directly by trace ID (fast, precise).Example: Fetch trace for a specific order
import requests# Your order IDorder_id = "order-abc-123"# Regenerate ABV trace ID from order IDtrace_id = abv.create_trace_id(seed=order_id)# Fetch trace via ABV API (direct lookup by trace ID)response = requests.get( f"https://app.abv.dev/api/public/traces/{trace_id}", headers={"Authorization": f"Bearer {api_key}"})trace = response.json()print(f"Order {order_id} cost: ${trace['calculatedTotalCost']}")
Benefits:
Direct trace retrieval by ID (no complex metadata queries)
ABV’s trace IDs follow the W3C Trace Context standard, making them compatible with OpenTelemetry and other observability tools. If you’re already using OpenTelemetry, ABV integrates seamlessly.W3C Trace Context format:
from opentelemetry import tracetracer = trace.get_tracer(__name__)with tracer.start_as_current_span("my-operation") as span: # Get W3C-compliant trace ID trace_id = format(span.get_span_context().trace_id, "032x") # Use in ABV (already compatible) print(f"Trace ID: {trace_id}")
Benefits:
Use the same trace IDs across ABV and other observability tools (Datadog, Honeycomb, Jaeger)
Vendor-neutral: Switch tools without changing instrumentation
Standards-based: Follow W3C best practices for distributed tracing
Use the @observe() decorator to automatically trace functions. Pass custom trace IDs via the special abv_trace_id keyword argument.Setup:
pip install abvdev
Basic usage:
from abvdev import observe, ABVimport uuid# Initialize ABV clientabv = ABV( api_key="sk-abv-...", host="https://app.abv.dev" # or "https://eu.app.abv.dev" for EU)@observe()def process_user_request(user_id, request_data): # Function logic return f"Processed request for {user_id}"# Generate custom trace ID from external IDexternal_id = "session-abc-123"trace_id = abv.create_trace_id(seed=external_id)# Call function with custom trace IDprocess_user_request( user_id="user-456", request_data={"query": "account balance"}, abv_trace_id=trace_id # Special keyword argument)
Key points:
abv_trace_id is a special keyword argument recognized by @observe()
Trace ID must be a 32-character lowercase hexadecimal string
Use abv.create_trace_id(seed="...") to generate W3C-compliant IDs from any string
Python SDK: Manual Span Creation
For more control, create spans manually with custom trace contexts.Create span with custom trace ID:
from abvdev import ABVabv = ABV(api_key="sk-abv-...", host="https://app.abv.dev")# Option 1: Use deterministic trace ID from external IDexternal_id = "order-xyz-789"trace_id = abv.create_trace_id(seed=external_id)# Option 2: Use a predefined trace ID (must be 32 hex chars)# trace_id = "abcdef1234567890abcdef1234567890"# Create span with custom trace IDwith abv.start_as_current_span( name="process-order", trace_context={ "trace_id": trace_id, "parent_span_id": "fedcba0987654321" # Optional, 16 hex chars }) as span: print(f"This span has trace_id: {span.trace_id}") # Your code here result = process_order(order_id="xyz-789")
Access current trace ID:
with abv.start_as_current_span(name="outer-operation") as span: # Option 1: Access via span trace_id = span.trace_id # Option 2: Get from ABV client current_trace_id = abv.get_current_trace_id() current_span_id = abv.get_current_observation_id() print(f"Trace ID: {current_trace_id}") print(f"Span ID: {current_span_id}")
JavaScript/TypeScript SDK: Custom Trace IDs
Use the @abvdev/tracing package to create spans with custom trace IDs.Setup:
import dotenv from 'dotenv';dotenv.config();import { NodeSDK } from '@opentelemetry/sdk-node';import { ABVSpanProcessor } from '@abvdev/otel';const sdk = new NodeSDK({ spanProcessors: [ new ABVSpanProcessor({ apiKey: process.env.ABV_API_KEY, baseUrl: process.env.ABV_BASE_URL, exportMode: 'immediate', flushAt: 1, flushInterval: 1 }) ]});sdk.start();
Create span with custom trace ID:
import './instrumentation'; // Must be first importimport { createTraceId, startObservation } from '@abvdev/tracing';async function main() { // Generate deterministic trace ID from external ID const externalId = 'support-ticket-252525'; const abvTraceId = await createTraceId(externalId); // Start span with custom trace ID const rootSpan = startObservation( 'process-ticket', { input: { ticketId: externalId }, output: { status: 'resolved' } }, { parentSpanContext: { traceId: abvTraceId, spanId: '0123456789abcdef', // Arbitrary 16-char hex traceFlags: 1 // Mark as sampled } } ); // Your code here await processTicket(externalId); rootSpan.end(); // Later, regenerate the same trace ID for scoring const scoringTraceId = await createTraceId(externalId); console.log(scoringTraceId === abvTraceId); // true}main();
Important:
parentSpanContext.spanId must be a valid 16-character hex string
The parent span doesn’t actually exist—it’s only used for trace ID inheritance
Setting parentSpanContext detaches the span from the active context
JavaScript/TypeScript SDK: Access Current Trace ID
Retrieve the active trace ID at runtime.Example:
import './instrumentation'; // Must be first importimport { startActiveObservation, getActiveTraceId, updateActiveTrace} from '@abvdev/tracing';async function main() { await startActiveObservation('run', async (span) => { // Update span metadata span.update({ input: { query: 'What is the capital of France?' } }); // Update trace metadata updateActiveTrace({ metadata: { userId: 'user-123' } }); // Get current trace ID const traceId = getActiveTraceId(); console.log(`Current trace ID: ${traceId}`); // Use in logs or external systems await logEvent({ traceId, event: 'query-started' }); });}main();
Use cases:
Log trace ID to external systems (Datadog, Splunk)
Include trace ID in API responses for debugging
Pass trace ID to downstream services via HTTP headers
Distributed Tracing: Propagate Trace IDs Across Services
For microservices architectures, propagate trace IDs using HTTP headers following the W3C Trace Context standard.Service A (Python): Send request with trace ID
import requestsfrom abvdev import ABVabv = ABV(api_key="sk-abv-...")# Get current trace IDwith abv.start_as_current_span(name="call-service-b") as span: trace_id = span.trace_id span_id = format(span.get_span_context().span_id, "016x") # Construct W3C traceparent header traceparent = f"00-{trace_id}-{span_id}-01" # Send request to service B response = requests.post( "https://service-b.example.com/process", headers={"traceparent": traceparent}, json={"data": "payload"} )
Service B (Python): Extract and use trace ID
from flask import Flask, requestfrom abvdev import ABVapp = Flask(__name__)abv = ABV(api_key="sk-abv-...")@app.route("/process", methods=["POST"])def handle_request(): # Extract traceparent header traceparent = request.headers.get("traceparent") if traceparent: # Parse W3C traceparent format: 00-<trace-id>-<span-id>-<flags> parts = traceparent.split("-") trace_id = parts[1] # Extract trace ID parent_span_id = parts[2] # Extract parent span ID # Use the same trace ID in service B with abv.start_as_current_span( name="process-in-service-b", trace_context={ "trace_id": trace_id, "parent_span_id": parent_span_id } ) as span: # Process request result = process_data(request.json) return {"status": "success", "result": result} # Fallback if no traceparent header return {"error": "Missing traceparent header"}, 400
Service A (TypeScript): Send request with trace ID
import express from 'express';import { startObservation } from '@abvdev/tracing';const app = express();app.post('/process', async (req, res) => { const traceparent = req.headers['traceparent']; if (traceparent) { // Parse W3C traceparent format const [version, traceId, parentSpanId, flags] = traceparent.split('-'); // Create span with inherited trace ID const span = startObservation( 'process-in-service-b', { input: req.body }, { parentSpanContext: { traceId, spanId: parentSpanId, traceFlags: parseInt(flags, 16) } } ); // Process request const result = await processData(req.body); span.end(); res.json({ status: 'success', result }); } else { res.status(400).json({ error: 'Missing traceparent header' }); }});app.listen(3000);
Result:
In the ABV Dashboard, search for the trace ID and see events from both services grouped together in one timeline.
OpenTelemetry: Using Trace IDs
If you’re using OpenTelemetry directly (without ABV SDKs), trace IDs are managed by the OpenTelemetry SDK. ABV automatically ingests OpenTelemetry spans with their trace IDs.Access trace ID in OpenTelemetry (Python):
from opentelemetry import tracefrom opentelemetry.trace import Status, StatusCodetracer = trace.get_tracer(__name__)with tracer.start_as_current_span("my-operation") as span: # Get trace ID (returns an integer) trace_id_int = span.get_span_context().trace_id # Format as 32-character hex string trace_id = format(trace_id_int, "032x") print(f"Trace ID: {trace_id}") # Set custom attributes span.set_attribute("custom.trace_id", trace_id) # Your code here result = perform_operation() # Set span status span.set_status(Status(StatusCode.OK))
Access trace ID in OpenTelemetry (JavaScript/TypeScript):
import { trace } from '@opentelemetry/api';const tracer = trace.getTracer('my-service');const span = tracer.startSpan('my-operation');// Get trace IDconst traceId = span.spanContext().traceId;console.log(`Trace ID: ${traceId}`);// Your code hereperformOperation();span.end();
ABV integration:
ABV’s span processors automatically extract trace IDs from OpenTelemetry spans
No code changes needed—just configure ABVSpanProcessor
Trace IDs in ABV Dashboard match OpenTelemetry trace IDs exactly