Skip to main content

How Trace URLs Work

Every trace in ABV has a unique, permanent URL that points directly to the trace in the dashboard. Trace URLs provide instant access to the complete interaction timeline, including:
  • Full prompt and response content
  • Token usage and cost breakdowns
  • Latency metrics (first token, total duration)
  • Tool calls and function executions
  • Error messages and stack traces
  • Session context and user metadata

Understand Trace URL Structure

Trace URLs follow a consistent format:
https://app.abv.dev/traces/<trace-id>
  • app.abv.dev: ABV Dashboard (US region)
  • eu.app.abv.dev: EU region alternative
  • <trace-id>: Unique identifier for the trace (32-character hex string)
Example:
https://app.abv.dev/traces/a1b2c3d4e5f6789012345678abcdef12
Key properties:
  • Permanent: URLs never expire or change
  • Unique: Each trace has exactly one URL
  • Private by default: Only team members with access to the project can view
  • Shareable: Can be made public for external sharing (clients, community)

Get Trace URLs Programmatically

Retrieve trace URLs directly in your code for logging, debugging, or automated workflows.Python (decorator pattern):
from abvdev import ABV, observe

abv = ABV(api_key="sk-abv-...", host="https://app.abv.dev")

@observe()
def process_data():
    # Get the URL of the current trace
    trace_url = abv.get_trace_url()
    print(f"View trace at: {trace_url}")

    # Or construct URL from trace ID
    trace_id = abv.get_current_trace_id()
    trace_url = abv.get_trace_url(trace_id=trace_id)
    return trace_url

url = process_data()
# Output: "View trace at: https://app.abv.dev/traces/a1b2c3d4..."
Python (context managers):
with abv.start_as_current_span(name="process-request") as span:
    # Get trace URL
    trace_url = abv.get_trace_url()
    print(f"View trace at: {trace_url}")

    # Add to logs
    logger.info(f"Processing request. Trace: {trace_url}")
JavaScript/TypeScript:
import './instrumentation';  // Must be first import
import { ABVClient } from '@abvdev/client';
import { startObservation } from '@abvdev/tracing';

const abv = new ABVClient();

async function main() {
  const rootSpan = startObservation('my-trace', {
    input: { query: 'What is the capital of France?' }
  });

  // Get trace URL
  const traceUrl = await abv.getTraceUrl(rootSpan.traceId);
  console.log('Trace URL:', traceUrl);

  rootSpan.update({
    output: { content: 'The capital of France is Paris.' }
  });

  rootSpan.end();
}

main();

Add Trace URLs to Logs and Monitoring

Include trace URLs in your application logs to create instant links from logs to ABV traces.Python (logging integration):
import logging
from abvdev import ABV, observe

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

abv = ABV(api_key="sk-abv-...")

@observe()
def process_user_request(user_id, query):
    trace_url = abv.get_trace_url()

    # Add trace URL to logs
    logger.info(
        f"Processing request for user={user_id}. "
        f"ABV Trace: {trace_url}"
    )

    # Your code here
    result = perform_llm_call(query)

    if result.error:
        logger.error(
            f"LLM call failed for user={user_id}. "
            f"ABV Trace: {trace_url}"
        )

    return result

process_user_request(user_id="user-123", query="Summarize this document")
Benefits:
  • Click trace URLs in logs to jump directly to ABV dashboard
  • Correlate application logs with LLM traces instantly
  • No manual searching for traces by timestamp or metadata

Share Traces with Teammates (Private)

By default, trace URLs are private—only team members with access to your ABV project can view them.Sharing workflow:
  1. Get the trace URL (via SDK or dashboard)
  2. Share the URL via Slack, email, GitHub issue, Jira ticket, etc.
  3. Teammates click the URL and see the full trace (if they have project access)
Example: Share in Slack
import requests

# Get trace URL
trace_url = abv.get_trace_url()

# Post to Slack
slack_webhook_url = "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
requests.post(slack_webhook_url, json={
    "text": f"🚨 LLM error in production! Debug here: {trace_url}"
})
Access control:
  • Trace URLs respect ABV’s role-based access controls (RBAC)
  • Users must be logged into ABV and have project permissions
  • URLs work across sessions and devices (permanent links)

Make Traces Public (Optional)

For external sharing (clients, community, public demos), make traces public. Public traces are accessible to anyone with the URL—no ABV login required.Make public via UI:
  1. Open the trace in the ABV Dashboard
  2. Toggle “Private” to “Public”
  3. Share the same trace URL publicly
Make public via SDK (Python):
from abvdev import ABV, observe

abv = ABV(api_key="sk-abv-...")

@observe()
def process_data():
    # Make the current trace public
    abv.update_current_trace(public=True)

    # Get the public URL
    trace_url = abv.get_trace_url()
    print(f"Share this public trace: {trace_url}")

process_data()
Make public via SDK (JavaScript/TypeScript):
import { startObservation } from '@abvdev/tracing';

const rootSpan = startObservation('my-trace');

// Make trace public
rootSpan.updateTrace({ public: true });

rootSpan.end();
Security note: Only make traces public if they don’t contain sensitive data (PII, API keys, proprietary prompts). Use masking to redact sensitive content before sharing.

Why Use Trace URLs?

Text descriptions of bugs are vague and hard to reproduce. “The LLM sometimes gives wrong answers” isn’t actionable. Trace URLs provide concrete evidence.Without trace URLs:
  • Developer: “Can you describe the bug?”
  • Reporter: “The LLM said Paris is in Germany”
  • Developer: “What was the input? What model? What timestamp?”
  • Reporter: “Uh, I don’t remember… it was yesterday around 3pm”
  • Developer spends hours searching logs and trying to reproduce
With trace URLs:
  • Reporter files GitHub issue: “LLM gave incorrect geography answer: [trace URL]”
  • Developer clicks URL, sees:
    • Input: “What country is Paris in?”
    • Output: “Paris is in Germany”
    • Model: gpt-3.5-turbo
    • Timestamp: 2025-01-15 15:23:45
    • Prompt version: v2.3 (identifies the regression)
  • Developer reproduces immediately and fixes the prompt
Best practices:
  • Include trace URLs in GitHub issues, Jira tickets, Linear tasks
  • Add trace URLs to error logs and exception handlers
  • Make traces public if sharing with external contributors or clients
Example: Automated bug report with trace URL
import requests

@observe()
def process_query(query):
    try:
        result = llm.generate(query)
        return result
    except Exception as e:
        # Get trace URL
        trace_url = abv.get_trace_url()

        # File GitHub issue automatically
        create_github_issue(
            title=f"LLM error: {str(e)}",
            body=f"Error occurred during query processing.\n\nTrace: {trace_url}\n\nStack trace:\n```\n{traceback.format_exc()}\n```"
        )
        raise
Support teams waste hours gathering context when users report issues. “It didn’t work” requires back-and-forth to extract details.Traditional support workflow:
  1. User: “The chatbot gave me wrong refund info”
  2. Support: “Can you send a screenshot?”
  3. User: “Here’s the screenshot” (blurry image)
  4. Support: “What was your account ID?”
  5. User: “[email protected]
  6. Support: “What time did this happen?”
  7. User: “Around 2pm yesterday”
  8. Support forwards vague details to engineering
  9. Engineering searches logs for 30 minutes to find the interaction
With trace URLs:
  1. User: “The chatbot gave me wrong refund info”
  2. Support searches traces by user email in ABV
  3. Support finds the trace, clicks “Share URL”
  4. Support sends trace URL to engineering: “This trace shows the issue”
  5. Engineering clicks URL, sees full interaction in 5 seconds
  6. Engineering identifies prompt used outdated policy docs (RAG issue)
  7. Fix deployed within hours instead of days
Implementation:
# Store trace URLs in customer support tickets
@observe()
def handle_support_query(user_id, query):
    trace_url = abv.get_trace_url()

    # Add trace URL to support ticket
    support_ticket = create_ticket(
        user_id=user_id,
        description=query,
        metadata={"abv_trace_url": trace_url}
    )

    # Process query
    response = generate_response(query)

    return response
Benefits:
  • Time to resolution: Days → hours
  • Context loss: Eliminated
  • Customer satisfaction: Improved (faster fixes)
Debugging complex issues requires collaboration. Describing problems over Slack or email loses context. Trace URLs preserve everything.Without trace URLs (inefficient):
  • You: “Hey, the summarization feature is failing for long documents”
  • Teammate: “What error?”
  • You: “Timeout after 30 seconds”
  • Teammate: “What model?”
  • You: “GPT-4”
  • Teammate: “What was the input?”
  • You: “Uh, let me find the logs… it was a 10-page PDF”
  • Back-and-forth continues for 20 minutes
With trace URLs (instant):
  • You: “Hey, summarization failing for long docs: [trace URL]”
  • Teammate clicks URL, sees:
    • Input: 10-page PDF (3,000 tokens)
    • Model: GPT-4
    • Timeout after 30 seconds
    • Cost: $0.15 (expensive!)
  • Teammate: “Ah, we need to chunk the document or use Claude (128k context)”
  • Problem solved in 2 minutes
Best practices:
  • Share trace URLs in Slack, Microsoft Teams, or email
  • Add trace URLs to code review comments
  • Reference trace URLs in pull request descriptions
Example: Share trace in Slack automatically
@observe()
def expensive_operation(data):
    result = process_data(data)

    # Get trace URL
    trace_url = abv.get_trace_url()
    trace_cost = abv.get_current_trace_cost()

    # Alert team if expensive
    if trace_cost > 1.00:  # $1 threshold
        post_to_slack(
            channel="#llm-alerts",
            message=f"💸 Expensive trace detected: ${trace_cost:.2f}\nDebug: {trace_url}"
        )

    return result
Pull request descriptions and code review comments benefit from concrete examples. Instead of saying “This change improves latency,” link to before/after traces.Example: Pull request description with traces
## Summary
Optimized the document summarization prompt to reduce tokens and latency.

## Changes
- Reduced system prompt from 500 to 150 tokens
- Removed redundant few-shot examples
- Switched from `gpt-4` to `gpt-3.5-turbo` for simple summaries

## Evidence
**Before (v1.2.3):**
- Trace: https://app.abv.dev/traces/abc123...
- Tokens: 1,200 (input) + 300 (output)
- Latency: 4.2 seconds
- Cost: $0.08

**After (v1.3.0):**
- Trace: https://app.abv.dev/traces/def456...
- Tokens: 350 (input) + 280 (output)
- Latency: 1.8 seconds
- Cost: $0.01

**Result:** 57% latency reduction, 87% cost savings, no quality degradation.
Benefits:
  • Reviewers see concrete data instead of claims
  • Evidence-based code reviews
  • Easier to spot regressions or unintended side effects
When running experiments in Jupyter notebooks, display trace URLs inline for interactive debugging.Example: Notebook workflow
from IPython.display import display, HTML
from abvdev import ABV, observe

abv = ABV(api_key="sk-abv-...")

@observe()
def run_experiment(prompt_variant, input_text):
    response = llm.generate(prompt=prompt_variant, input=input_text)

    # Get trace URL
    trace_url = abv.get_trace_url()

    # Display clickable link in notebook
    display(HTML(f'<a href="{trace_url}" target="_blank">View Trace</a>'))

    return response

# Run experiment
for variant in ["v1", "v2", "v3"]:
    print(f"\n=== Testing {variant} ===")
    result = run_experiment(variant, "Summarize this article")
    print(f"Output: {result}")
Output in notebook:
=== Testing v1 ===
[View Trace]  ← Clickable link
Output: This is a summary...

=== Testing v2 ===
[View Trace]  ← Clickable link
Output: Another summary...
Benefits:
  • Click traces directly from notebook cells
  • Compare experiment results side-by-side in ABV Dashboard
  • Reproducible experiments (trace URLs never expire)
Application logs and monitoring dashboards (Datadog, Splunk, CloudWatch) show high-level metrics, but lack LLM-specific context. Add trace URLs to bridge the gap.Example: Structured logging with trace URLs
import structlog
from abvdev import ABV, observe

logger = structlog.get_logger()
abv = ABV(api_key="sk-abv-...")

@observe()
def process_request(request_id, user_id, query):
    trace_url = abv.get_trace_url()

    # Log with trace URL
    logger.info(
        "llm_request_started",
        request_id=request_id,
        user_id=user_id,
        abv_trace_url=trace_url
    )

    result = llm.generate(query)

    logger.info(
        "llm_request_completed",
        request_id=request_id,
        user_id=user_id,
        latency_ms=result.latency,
        tokens=result.tokens,
        abv_trace_url=trace_url
    )

    return result
Log output (JSON):
{
  "event": "llm_request_started",
  "request_id": "req-abc-123",
  "user_id": "user-456",
  "abv_trace_url": "https://app.abv.dev/traces/a1b2c3d4...",
  "timestamp": "2025-01-15T15:23:45Z"
}
In Datadog/Splunk:
  • Search logs by request ID
  • Click the abv_trace_url field to open the trace in ABV
  • See full LLM interaction (prompt, response, costs) alongside application logs
Benefits:
  • Single pane of glass: Logs + LLM traces in one workflow
  • Faster root cause analysis (correlate errors with LLM behavior)
  • No manual searching across systems

Implementation Patterns

Use the @observe() decorator and retrieve trace URLs with abv.get_trace_url().Basic usage:
from abvdev import ABV, observe

abv = ABV(api_key="sk-abv-...", host="https://app.abv.dev")

@observe()
def process_data():
    # Get the URL of the current trace
    trace_url = abv.get_trace_url()
    print(f"View trace at: {trace_url}")

    # Or get URL from trace ID
    trace_id = abv.get_current_trace_id()
    trace_url_from_id = abv.get_trace_url(trace_id=trace_id)

    return trace_url

url = process_data()
# Output: "View trace at: https://app.abv.dev/traces/a1b2c3d4..."
Add to logs:
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

@observe()
def process_query(query):
    trace_url = abv.get_trace_url()
    logger.info(f"Processing query. Trace: {trace_url}")

    result = llm.generate(query)
    return result
Use abv.start_as_current_span() and retrieve trace URLs within the context.Example:
from abvdev import ABV

abv = ABV(api_key="sk-abv-...", host="https://app.abv.dev")

with abv.start_as_current_span(name="process-request") as span:
    # Get trace URL
    trace_url = abv.get_trace_url()
    print(f"View trace at: {trace_url}")

    # Add to logs or monitoring
    logger.info(f"Trace URL: {trace_url}")

    # Your code here
    result = perform_operation()
Use the @abvdev/client package to retrieve trace URLs from trace IDs.Setup:
npm install @abvdev/tracing @abvdev/client
Example:
import './instrumentation';  // Must be first import
import { ABVClient } from '@abvdev/client';
import { startObservation } from '@abvdev/tracing';

const abv = new ABVClient();

async function main() {
  const rootSpan = startObservation('my-trace', {
    input: { query: 'What is the capital of France?' }
  });

  // Get trace URL
  const traceUrl = await abv.getTraceUrl(rootSpan.traceId);
  console.log('Trace URL:', traceUrl);

  // Add to logs
  console.log(`Processing query. Trace: ${traceUrl}`);

  rootSpan.update({
    output: { content: 'The capital of France is Paris.' }
  });

  rootSpan.end();
}

main();
Make traces publicly accessible (no login required) for external sharing.Python (decorator pattern):
from abvdev import ABV, observe

abv = ABV(api_key="sk-abv-...")

@observe()
def process_data():
    # Make the current trace public
    abv.update_current_trace(public=True)

    # Get the public URL
    trace_url = abv.get_trace_url()
    print(f"Share this public trace: {trace_url}")

process_data()
Python (context managers):
with abv.start_as_current_span(name="process-request") as span:
    # Make this trace public
    span.update_trace(public=True)

    # Get the URL to share
    trace_url = abv.get_trace_url()
    print(f"Share this trace publicly: {trace_url}")
JavaScript/TypeScript:
import { startObservation } from '@abvdev/tracing';

const rootSpan = startObservation('my-trace');

// Make trace public
rootSpan.updateTrace({ public: true });

// Get trace URL
const traceUrl = await abv.getTraceUrl(rootSpan.traceId);
console.log(`Share publicly: ${traceUrl}`);

rootSpan.end();
Security warning: Only make traces public if they don’t contain:
  • PII (emails, phone numbers, addresses)
  • API keys, passwords, or tokens
  • Proprietary prompts or system instructions
Use masking to redact sensitive content before sharing publicly.
If you have the trace ID, you can construct the trace URL manually without calling the SDK.URL format:
https://app.abv.dev/traces/<trace-id>
Python:
def construct_trace_url(trace_id: str, region: str = "us") -> str:
    base_url = "https://app.abv.dev" if region == "us" else "https://eu.app.abv.dev"
    return f"{base_url}/traces/{trace_id}"

# Usage
trace_id = abv.get_current_trace_id()
trace_url = construct_trace_url(trace_id)
print(trace_url)
# Output: https://app.abv.dev/traces/a1b2c3d4e5f6789012345678abcdef12
JavaScript/TypeScript:
function constructTraceUrl(traceId: string, region: 'us' | 'eu' = 'us'): string {
  const baseUrl = region === 'us' ? 'https://app.abv.dev' : 'https://eu.app.abv.dev';
  return `${baseUrl}/traces/${traceId}`;
}

// Usage
const traceId = getActiveTraceId();
const traceUrl = constructTraceUrl(traceId);
console.log(traceUrl);
// Output: https://app.abv.dev/traces/a1b2c3d4e5f6789012345678abcdef12

Next Steps