Documentation Index
Fetch the complete documentation index at: https://docs.getnetra.ai/llms.txt
Use this file to discover all available pages before exploring further.
Manual tracing gives you complete control over span creation, attributes, and lifecycle. Use it when you need to trace custom operations, add detailed metadata, or track usage and costs.
Getting Started
To start manual tracing, you’ll need to:
- Import the required classes from Netra
- Create a new span using
start_span()
- Track your operations within the span
- Add relevant attributes and events
Creating Spans
Use start_span() to create a span that wraps a block of code. In Python, use it as a context manager. In TypeScript, explicitly call end() when done.
from netra import Netra
# Use as context manager (recommended)
with Netra.start_span("process-document") as span:
result = process_document(doc)
span.set_attribute("document.pages", result.page_count)
# Span automatically ends when exiting the context
Span Parameters
| Parameter | Type | Description |
|---|
name | string | Name of the span (required) |
attributes | dict/object | Initial attributes to set on the span |
module_name / moduleName | string | Module or component name for organization |
as_type / asType | SpanType | Type of span (SPAN, GENERATION, TOOL, etc.) |
from netra import Netra, SpanType
with Netra.start_span(
"generate-summary",
attributes={
"input.length": len(document),
"model": "gpt-4",
},
module_name="summarization",
as_type=SpanType.GENERATION,
) as span:
# Your code here
pass
Span Types
Use the as_type parameter to categorize spans. This helps Netra display them correctly and enables type-specific features.
| Type | Use For |
|---|
SpanType.GENERATION | LLM completions, image generation |
SpanType.EMBEDDING | Vector embedding operations |
SpanType.TOOL | Function calls, API requests, DB queries |
SpanType.AGENT | AI agent reasoning and decisions |
SpanType.SPAN | General operations (default) |
See Spans for detailed guidance on when to use each type.
Local Span Blocking
You can block specific spans locally within a particular span scope. This is useful when you want to filter out noisy child spans (like HTTP requests) within a specific operation.
from netra import Netra
# Block POST spans within this scope
with Netra.start_span("image-generation", attributes={"blocked_spans": ["POST", "GET"]}) as span:
# HTTP spans named "POST" or "GET" created within this scope will be filtered
generate_image(prompt)
This is different from global blocked_spans in Netra.init() which blocks spans across the entire application. Local blocking only affects spans created within the specific parent span’s scope.
SpanWrapper Methods
The start_span() function returns a SpanWrapper object with methods for adding context to your spans.
Setting Span Attributes
Add custom key-value pairs to provide context about the operation:
with Netra.start_span("search-products") as span:
span.set_attribute("query", user_query)
span.set_attribute("filters.category", category)
span.set_attribute("filters.price_range", [min_price, max_price])
span.set_attribute("results.count", len(results))
LLM-Specific Attributes
For LLM operations, use dedicated methods to set prompts, models, and system information:
from netra import Netra, SpanType
with Netra.start_span("generate-response", as_type=SpanType.GENERATION) as span:
span.set_prompt(user_message)
span.set_negative_prompt("blurry, low quality") # For image generation
span.set_model("gpt-4-turbo")
span.set_llm_system("openai")
response = generate_response(user_message)
span.set_attribute("completion", response.content)
Recording Events
Track significant moments within a span’s lifecycle:
with Netra.start_span("order-processing") as span:
span.add_event("validation-started")
validate_order(order)
span.add_event("validation-completed", {"valid": True})
span.add_event("payment-started")
payment = process_payment(order)
span.add_event("payment-completed", {
"transaction_id": payment.id,
"amount": payment.amount,
})
Tracking Usage Data
Use UsageModel to track token usage and costs for LLM operations:
from netra import Netra, SpanType, UsageModel
with Netra.start_span("llm-call", as_type=SpanType.GENERATION) as span:
response = openai.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": prompt}],
)
# Track usage
span.set_usage([
UsageModel(
model="gpt-4",
cost_in_usd=calculate_cost(response.usage),
usage_type="chat",
units_used=1,
)
])
UsageModel Fields
| Field | Type | Description |
|---|
model | string | Model name used |
cost_in_usd / costInUsd | float | Calculated cost in USD |
usage_type / usageType | string | Type of usage (e.g., “chat”, “image_generation”) |
units_used / unitsUsed | int | Number of units consumed |
Adding Action Tracking
Use ActionModel to track discrete actions, tool calls, or database operations within a span:
from netra import Netra, ActionModel
with Netra.start_span("agent-execution") as span:
# Record actions taken by the agent
span.set_action([
ActionModel(
action="DB",
action_type="INSERT",
affected_records=[
{"record_id": "user_123", "record_type": "user"},
{"record_id": "profile_456", "record_type": "profile"},
],
metadata={
"table": "users",
"operation_id": "tx_789",
"duration_ms": "45",
},
success=True,
),
ActionModel(
action="API",
action_type="CALL",
metadata={
"endpoint": "/api/v1/process",
"method": "POST",
"status_code": "200",
},
success=True,
),
])
ActionModel Fields
| Field | Type | Description |
|---|
action | string | Action category (e.g., “DB”, “API”, “CACHE”) |
action_type / actionType | string | Action subtype (e.g., “INSERT”, “SELECT”, “CALL”) |
affected_records / affectedRecords | array | List of affected records with record_id and record_type |
metadata | dict/object | Additional metadata as key-value pairs |
success | boolean | Whether the action succeeded |
Error Handling
Mark spans as errors when operations fail:
from netra import Netra
with Netra.start_span("risky-operation") as span:
try:
result = risky_operation()
span.set_success()
except Exception as e:
span.set_error(str(e))
raise
When using Python’s context manager, exceptions are automatically recorded and the span is marked as an error. You can still explicitly call set_error() for custom error messages.
Nested Spans
Create hierarchical traces by nesting spans. Child spans automatically inherit the parent context:
from netra import Netra
def process_order(order: dict):
with Netra.start_span("process-order") as parent_span:
parent_span.set_attribute("order.id", order["id"])
# Child span for validation
with Netra.start_span("validate-order"):
validate_order(order)
# Child span for payment
with Netra.start_span("process-payment") as payment_span:
payment = process_payment(order)
payment_span.set_attribute("payment.id", payment.id)
# Child span for fulfillment
with Netra.start_span("fulfill-order"):
fulfill_order(order)
Accessing the Current Span
Get the currently active span to add attributes from anywhere in your code:
from netra import Netra
def log_user_action(action: str):
current_span = Netra.get_current_span()
if current_span:
current_span.add_event("user-action", {"action": action})
# Usage within a traced operation
with Netra.start_span("user-session"):
# ... somewhere deep in the call stack ...
log_user_action("clicked-submit")
Example: RAG Pipeline
This example demonstrates nested spans with multiple span types - a common pattern for AI pipelines.
from netra import Netra, SpanType, UsageModel
def rag_pipeline(query: str):
with Netra.start_span("rag-pipeline") as pipeline_span:
pipeline_span.set_attribute("query", query)
# Step 1: Generate embedding
with Netra.start_span(
"generate-embedding", as_type=SpanType.EMBEDDING
) as embed_span:
embedding = embed_model.embed(query)
embed_span.set_usage([
UsageModel(
model="text-embedding-3-small",
usage_type="embedding",
units_used=1
)
])
# Step 2: Retrieve documents
with Netra.start_span(
"retrieve-documents", as_type=SpanType.TOOL
) as retrieve_span:
documents = vector_store.search(embedding, top_k=5)
retrieve_span.set_attribute("documents.count", len(documents))
# Step 3: Generate response
with Netra.start_span(
"generate-response", as_type=SpanType.GENERATION
) as generate_span:
generate_span.set_prompt(query)
generate_span.set_model("gpt-4")
generate_span.set_llm_system("openai")
response = openai.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": build_context(documents)},
{"role": "user", "content": query},
],
)
generate_span.set_usage([
UsageModel(
model="gpt-4",
cost_in_usd=calculate_cost(response.usage),
usage_type="chat",
units_used=1,
)
])
pipeline_span.set_success()
return response.choices[0].message.content
Best Practices
- Use context managers in Python - They ensure spans are properly closed even when exceptions occur.
- End spans in TypeScript - Always call
span.end() in both success and error paths, preferably in a finally block.
- Add meaningful attributes - Include information that will help you debug and analyze traces later.
- Track usage for LLM calls - Use
setUsage() to monitor token consumption and costs.
- Use appropriate span types - Set
as_type to categorize spans correctly (GENERATION for LLM calls, TOOL for function calls, etc.).
- Handle errors explicitly - Call
setError() with descriptive messages to make debugging easier.
- Use local span blocking - Filter noisy child spans when you only care about the parent operation.
- Add events for milestones - Use
addEvent() to mark important points in long-running operations.
Learn More