Metadata and attributes are key-value pairs that allow you to add custom contextual information to your traces and spans. This enrichment is invaluable for debugging, analysis, filtering, and gaining deeper insights into your LLM application’s behavior.

LangWatch distinguishes between two main types of custom data:

  • Trace Metadata: Information that applies to the entire lifecycle of a request or a complete operation.
  • Span Attributes: Information specific to a particular unit of work or step within a trace.

This tutorial will guide you through capturing both types using the Python SDK.

Trace Metadata

Trace metadata provides context for the entire trace. It’s ideal for information that remains constant throughout the execution of a traced operation, such as:

  • User identifiers (user_id)
  • Session or conversation identifiers (session_id, thread_id)
  • Application version (app_version)
  • Environment (env: "production")
  • A/B testing flags or variant names

You can set trace metadata when a trace is initiated or update it at any point while the trace is active.

Setting Trace Metadata at Initialization

The easiest way to add metadata to a trace is by passing a metadata dictionary to the langwatch.trace() decorator or context manager.

import langwatch
import os

# Initialize LangWatch (ensure this is done once in your application)
langwatch.setup(api_key=os.getenv("LANGWATCH_API_KEY"))

@langwatch.trace(name="UserQueryHandler", metadata={"user_id": "user_123", "session_id": "session_abc"})
def handle_user_query(query: str):
    # Your application logic here
    # For example, process the query and interact with an LLM
    processed_query = f"Query processed: {query}"
    
    # You can also update trace metadata from within the trace
    current_trace = langwatch.get_current_trace()
    if current_trace:
        current_trace.update(metadata={"query_language": "en"})
        
    return processed_query

handle_user_query("Hello, LangWatch!")

In this example, user_id and session_id are attached to the “UserQueryHandler” trace from the start. Later, query_language is added.

Refer to the langwatch.trace() API reference for more details on its parameters.

Updating Trace Metadata Dynamically

If you need to add or modify trace metadata after the trace has started (e.g., based on some intermediate result), you can use the update() method on the LangWatchTrace object.

You can get the current trace object using langwatch.get_current_trace() or from the langwatch.trace() context manager.

import langwatch
import os
import uuid

langwatch.setup(api_key=os.getenv("LANGWATCH_API_KEY"))

def process_customer_request(customer_id: str, request_details: dict):
    trace_metadata = {
        "customer_id": customer_id,
        "initial_request_type": request_details.get("type")
    }
    with langwatch.trace(name="CustomerRequestFlow", metadata=trace_metadata) as current_trace:
        # Simulate some processing
        print(f"Processing request for customer {customer_id}")
        
        # Update metadata based on some condition or result
        is_priority_customer = customer_id.startswith("vip_")
        current_trace.update(metadata={"priority_customer": is_priority_customer})
        
        # ... further processing ...
        
        if request_details.get("type") == "complaint":
            current_trace.update(metadata={"escalation_needed": True})
            
        print(f"Trace ID: {current_trace.id}") # Example of accessing trace properties

process_customer_request(f"vip_{uuid.uuid4()}", {"type": "complaint", "content": "Service issue"})
process_customer_request(f"user_{uuid.uuid4()}", {"type": "inquiry", "content": "Product question"})

Span Attributes

Span attributes (often referred to simply as “attributes”) provide context for a specific operation or unit of work within a trace. They are useful for details that are relevant only to that particular step. Examples include:

  • For an LLM call span: model_name, prompt_template_version, temperature
  • For a tool call span: tool_name, api_endpoint, specific input parameters
  • For a RAG span: retrieved_document_ids, chunk_count
  • Custom business logic flags or intermediate results specific to that span.

Setting Span Attributes

You can set attributes on a span when it’s created using the attributes parameter (less common for dynamic values) or, more typically, by calling the update() method on the LangWatchSpan object.

The update() method is flexible and allows you to pass attributes as keyword arguments.

import langwatch
import os

langwatch.setup(api_key=os.getenv("LANGWATCH_API_KEY"))

@langwatch.trace(name="ArticleGenerator")
def generate_article(topic: str):
    with langwatch.span(name="FetchResearchData", type="tool") as research_span:
        # Simulate fetching data
        research_data = f"Data about {topic}"
        research_span.update(
            source="internal_db", 
            query_complexity="medium",
            items_retrieved=10
        )
        # research_span.set_attributes({"source": "internal_db"}) # Also works

    with langwatch.span(name="GenerateText", type="llm") as llm_span:
        llm_span.update(model="gpt-4o-mini", prompt_length=len(topic))
        # Simulate LLM call
        article_text = f"Here's an article about {topic} based on {research_data}."
        llm_span.update(output_length=len(article_text), tokens_used=150)
    
    return article_text

generate_article("AI in Healthcare")

In the first example, source, query_complexity, and items_retrieved are added to the “FetchResearchData” span. Similarly, model, prompt_length, output_length, and tokens_used contextualize the “GenerateText” LLM span.

The LangWatchSpan object also has a set_attributes() method which takes a dictionary, similar to OpenTelemetry’s underlying span API.

For more details on span parameters and methods, see the langwatch.span() API reference and the LangWatchSpan object methods.

Key Differences: Trace Metadata vs. Span Attributes

FeatureTrace MetadataSpan Attributes
ScopeEntire trace (e.g., a whole user request)Specific span (e.g., one LLM call, one tool use)
GranularityCoarse-grained, applies to the overall operationFine-grained, applies to a specific part of the operation
PurposeGeneral context for the entire operationSpecific details about a particular step or action
Examplesuser_id, session_id, app_versionmodel_name, tool_parameters, retrieved_chunk_id
SDK Accesslangwatch.trace(metadata={...})
trace.update(metadata={...})
langwatch.span(attributes={...})
span.update(key=value, ...)
span.set_attributes({...})

When to use which:

  • Use Trace Metadata for information that you’d want to associate with every single span within that trace, or that defines the overarching context of the request (e.g., who initiated it, what version of the service is running).
  • Use Span Attributes for details specific to the execution of that particular span. This helps in understanding the parameters, behavior, and outcome of individual components within your trace.

Viewing in LangWatch

All captured trace metadata and span attributes will be visible in the LangWatch UI.

  • Trace Metadata is typically displayed in the trace details view, providing an overview of the entire operation.
  • Span Attributes are shown when you inspect individual spans within a trace.

This rich contextual data allows you to:

  • Filter and search for traces and spans based on specific metadata or attribute values.
  • Analyze performance by correlating metrics with different metadata/attributes (e.g., comparing latencies for different user_ids or model_names).
  • Debug issues by quickly understanding the context and parameters of a failed or slow operation.

Conclusion

Effectively using trace metadata and span attributes is crucial for maximizing the observability of your LLM applications. By enriching your traces with relevant contextual information, you empower yourself to better understand, debug, and optimize your systems with LangWatch.

Remember to instrument your code thoughtfully, adding data that provides meaningful insights without being overly verbose.