All tags attached to a trace are visible in the ABV UI. You’ll see them as clickable labels on each trace, making it easy to identify categories at a glance.
3
Filter traces by tags
Click any tag in the UI to filter your trace list. The filter shows only traces that include that specific tag, reducing noise and focusing your analysis.
4
Combine tags for precise filtering
Use multiple tag filters simultaneously to narrow down exactly what you need. For example, filter by both production and error to see only production errors.
5
Use tags in analytics and exports
Tags are included in all exports and available for grouping in custom dashboards. Use them to segment performance metrics, cost analysis, or error rates by any dimension you choose.
Filter by tag to compare metrics side-by-side and make data-driven rollout decisions.Combine with metadata for richer analysis: use tags for simple categories (prompt:v1) and metadata for detailed attributes.
Version Tracking Across Deployments
Tag traces with your application version to isolate errors by deployment.
Filter by version:2.3.0 to see only new deployment errors and compare error rates across versions.Set version tags automatically via environment variables for complete deployment visibility.
Environment Separation
Separate dev, staging, and production traffic for clearer debugging.
Compare metrics to discover that RAG costs 3x more than few-shot or that chain-of-thought has higher latency but better accuracy.Optimize technique selection based on cost and performance data.
Error Categorization
Categorize errors for effective triage: rate limits, validation failures, or unexpected errors.
Filter by error type to identify quota issues, bad input, or unexpected failures.Set up alerts on specific error tags to get notified only for critical issues.
User Cohorts & A/B Testing
Tag traces with user cohorts to measure adoption and performance across customer segments.
Filter by tier:premium for paying customers or compare latency across regions. Segment cost analysis by customer tier.Avoid PII in tags—use cohort identifiers (tier:premium) not personal info (user:john@example.com).
The simplest approach for functions already decorated with @observe():
from abvdev import observe, ABV# Initialize ABV clientabv = ABV( api_key="sk-abv-...", host="https://app.abv.dev" # or "https://eu.app.abv.dev" for EU)@observe()def process_document(doc_id, use_rag=True): # Add tags to categorize this trace tags = ["document-processing"] if use_rag: tags.append("rag") abv.update_current_trace(tags=tags) # ... your processing logic ... return result# Call the function - tags are automatically attachedprocess_document("doc-123", use_rag=True)
When to use: For functions already using the @observe() decorator. Minimal code changes required.
When creating spans directly using context managers:
from abvdev import ABVabv = ABV( api_key="sk-abv-...", host="https://app.abv.dev")# Add tags when creating the root spanwith abv.start_as_current_span(name="data-pipeline") as root_span: # Add tags to the trace root_span.update_trace(tags=["etl", "production", "v2.0"]) # You can add more tags later from any span in the same trace with root_span.start_as_current_generation( name="llm-summary", model="gpt-4o" ) as gen: # Processing... gen.update_trace(tags=["summarization"]) # Adds to existing tags
When to use: For more control over span creation and nesting. Useful for complex workflows with multiple steps.
Update tags from anywhere within your traced code:
from abvdev import ABVabv = ABV( api_key="sk-abv-...", host="https://app.abv.dev")with abv.start_as_current_span(name="api-endpoint"): request_data = get_request() # Dynamically add tags based on request tags = ["api"] if request_data.get("beta_features"): tags.append("beta") if request_data.get("enterprise"): tags.append("enterprise") abv.update_current_trace(tags=tags) # ... processing ...
When to use: When tags depend on runtime conditions or need to be added from helper functions that don’t have direct span references.
ABV_API_KEY="sk-abv-..."ABV_BASE_URL="https://app.abv.dev" # or "https://eu.app.abv.dev" for EU
Create instrumentation.ts:
instrumentation.ts
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();
Using the context manager pattern:
import "./instrumentation"; // Must be first importimport { startActiveObservation, updateActiveTrace,} from "@abvdev/tracing";async function processQuery(query: string, useCache: boolean) { await startActiveObservation("query-processing", async (span) => { span.update({ input: { query, useCache }, }); // Add tags based on configuration const tags = ["query-processing"]; if (useCache) { tags.push("cached"); } updateActiveTrace({ tags }); // ... your processing logic ... });}processQuery("What is RAG?", true);
Wrapping existing functions:
import "./instrumentation"; // Must be first importimport { observe, updateActiveTrace } from "@abvdev/tracing";async function fetchUserData(userId: string) { // Determine user tier const user = await getUser(userId); updateActiveTrace({ tags: ["user-data", `tier:${user.tier}`, "api"], }); // ... fetch data logic ... return userData;}// Wrap the function to trace itconst tracedFetchUserData = observe(fetchUserData, { name: "fetch-user-data",});// Use the wrapped versionconst data = await tracedFetchUserData("user-123");
Creating spans manually:
import "./instrumentation"; // Must be first importimport { startObservation } from "@abvdev/tracing";const span = startObservation("document-analysis", { input: { documentId: "doc-123" },});// Add tags to the tracespan.updateTrace({ tags: ["document", "analysis", "production"],});// ... processing ...span.update({ output: analysisResult }).end();
Create a tag dictionary: Document your conventions in your team wiki or codebase:
# tags.py - Team tag conventionsENVIRONMENTS = ["dev", "staging", "production"]TECHNIQUES = ["rag", "few-shot", "chain-of-thought"]ERROR_TYPES = ["rate-limit", "validation", "timeout"]
Why it matters: Prevents tag proliferation (prod vs production vs prd), ensures team-wide consistency, and makes onboarding easier.
Never Put PII in Tags
Tags are designed for categorization, not sensitive data. They appear in UI filters, exports, and analytics dashboards.❌ Don’t use:
# Bad - contains PIItags = [ "user:john@example.com", "customer:acme-corp", "account:123-45-6789"]
✅ Instead use:
# Good - anonymous identifierstags = [ "tier:premium", "region:us-east", "cohort:enterprise-2024"]# Use metadata for identifiers with proper access controlsmetadata = { "user_id": "usr_abc123", # Internal ID, not email "tenant_id": "tenant_xyz", "account_hash": hash(account_number)}
Why it matters: Tags are visible to all project members and appear in exported data. PII in tags creates compliance risks and potential data leaks. Use metadata with appropriate access controls for sensitive identifiers.
Combine Tags with Metadata Strategically
Use both features together for maximum flexibility:Tags: Simple categories for filtering
Why it matters: Tags give you fast filtering. Metadata gives you deep analysis. Together they provide both speed and depth.
Tag Dynamically Based on Runtime Conditions
Add tags based on execution paths, not just static configuration:
@observe()def process_request(user_input: str): tags = ["api-request"] # Add tags based on validation if not is_valid_input(user_input): tags.append("validation-error") # Add tags based on execution path if needs_rag(user_input): tags.append("rag") tags.append("retrieval-heavy") else: tags.append("direct-completion") # Add tags based on performance start = time.time() result = execute(user_input) duration = time.time() - start if duration > 5.0: tags.append("slow-response") abv.update_current_trace(tags=tags) return result
Why it matters: Dynamic tagging captures what actually happened during execution, making it easier to identify patterns, debug issues, and optimize performance.
Limit the Number of Tags Per Trace
Keep tag lists focused and meaningful:Recommended: 3-7 tags per traceGood - focused and actionable:
Strategy: Ask “Will I actually filter by this?” If not, put it in metadata instead.Why it matters: Too many tags make the UI cluttered and filtering less effective. Focus on tags you’ll actually use for filtering, grouping, or alerting.