Tracing With The opentelemetry
Library
Using OpenTelemetry for AI Application Tracing
Problem: You want to implement standardized, vendor-neutral tracing for your AI applications that follows industry conventions and can be easily understood by other developers and tools.
Solution: Use OpenTelemetry with the semantic conventions for generative AI systems to create consistent, standardized traces that work with LangFuse and other observability platforms.
Why Use OpenTelemetry for AI Tracing?
Standards-Based Approach
- ✅ Industry standard - OpenTelemetry is the Cloud Native Computing Foundation (CNCF, a subsiduary of the Linux Foundation) standard for observability
- ✅ Semantic conventions - Standardized attribute names for AI operations
- ✅ Vendor neutral - Works with multiple observability backends
- ✅ Future-proof - Evolves with the industry
Rich AI Context
- ✅ Model tracking - Standardized attributes for model name, version, parameters
- ✅ Token usage - Consistent input/output token measurement
- ✅ Tool calls - Structured tracing for AI tool/function usage
- ✅ Conversation flow - Proper prompt/completion tracking
Complete Example: OpenAI Chat Completion
Here’s a complete example that traces an OpenAI chat completion using OpenTelemetry semantic conventions:
import os
import time
import uuid
from base64 import b64encode
from getpass import getuser
from dotenv import dotenv_values
from openai import OpenAI
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk import resources
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
from opentelemetry.trace import Status, StatusCode
# Load environment variables
= dotenv_values()
secrets
# Setup OpenTelemetry to export to LangFuse
def setup_telemetry():
"""Configure OpenTelemetry to send traces to LangFuse."""
# Create authentication string
= b64encode(
auth_string f"{secrets['PUBLIC_KEY']}:{secrets['SECRET_KEY']}".encode()
).decode()
# Configure OTLP via environment variables (like the working application)
"OTEL_EXPORTER_OTLP_ENDPOINT"] = (
os.environ['LANGFUSE_HOST'] + "/api/public/otel"
secrets[
)"OTEL_EXPORTER_OTLP_HEADERS"] = (
os.environ[f"Authorization=Basic {auth_string}"
)
# Create exporter using environment variables
= OTLPSpanExporter()
exporter
# Create tracer provider with service information
= resources.Resource.create({
resource "service.name": "ai-tracing-demo",
"service.version": "1.0.0"
})
# Create provider and add span processor
= TracerProvider(resource=resource)
provider
provider.add_span_processor(SimpleSpanProcessor(exporter))
# Set the tracer provider
trace.set_tracer_provider(provider)
return trace.get_tracer(__name__)
def trace_openai_completion():
"""Example function that traces an OpenAI completion with semantic conventions."""
# Setup telemetry
= setup_telemetry()
tracer
# Generate session and conversation context
= str(uuid.uuid4())
session_id = getuser()
username
# Initialize OpenAI client
= OpenAI(api_key=secrets["OPENAI_API_KEY"])
client
# Create main span for the AI operation
with tracer.start_as_current_span("openai_chat_completion") as span:
# Set standardized GenAI attributes
span.set_attributes({# LangFuse session tracking
"langfuse.session.id": session_id,
"user.id": username,
# OpenTelemetry GenAI semantic conventions
"gen_ai.system": "openai",
"gen_ai.operation.name": "chat.completions",
"gen_ai.request.model": "gpt-3.5-turbo",
"gen_ai.request.temperature": 0.7,
"gen_ai.request.max_tokens": 150,
})
# Prepare the conversation
= [
messages "role": "system", "content": "You are a helpful AI assistant."},
{"role": "user", "content": "Explain machine learning in simple terms."}
{
]
# Add prompt attributes using semantic conventions
for i, message in enumerate(messages):
span.set_attributes({f"gen_ai.prompt.{i}.role": message["role"],
f"gen_ai.prompt.{i}.content": message["content"]
})
try:
# Time the API call
= time.time()
start_time
# Make the OpenAI API call
= client.chat.completions.create(
response ="gpt-3.5-turbo",
model=messages,
messages=0.7,
temperature=150
max_tokens
)
= time.time()
end_time
# Set response attributes using semantic conventions
span.set_attributes({"gen_ai.response.id": response.id,
"gen_ai.response.model": response.model,
"gen_ai.response.finish_reason": response.choices[0].finish_reason,
"gen_ai.usage.input_tokens": response.usage.prompt_tokens,
"gen_ai.usage.output_tokens": response.usage.completion_tokens,
"gen_ai.usage.total_tokens": response.usage.total_tokens,
# Performance metrics
"gen_ai.response.duration_ms": int((end_time - start_time) * 1000),
# Response content
"gen_ai.completion.0.role": response.choices[0].message.role,
"gen_ai.completion.0.content": response.choices[0].message.content
})
# Add events for key milestones
"request_sent", {
span.add_event("timestamp": start_time,
"model": "gpt-3.5-turbo"
})
"response_received", {
span.add_event("timestamp": end_time,
"tokens_used": response.usage.total_tokens
})
# Set successful status
span.set_status(Status(StatusCode.OK))
return {
"response": response.choices[0].message.content,
"usage": response.usage.model_dump(),
"session_id": session_id
}
except Exception as e:
# Set error status and attributes
str(e)))
span.set_status(Status(StatusCode.ERROR,
span.set_attributes({"error.type": type(e).__name__,
"error.message": str(e)
})"error_occurred", {
span.add_event("error.type": type(e).__name__,
"error.message": str(e)
})raise
if __name__ == "__main__":
= trace_openai_completion()
result print(f"AI Response: {result['response']}")
print(f"Token Usage: {result['usage']}")
print(f"Session ID: {result['session_id']}")
Key Benefits of This Approach
Standardized Attributes
Using OpenTelemetry’s GenAI semantic conventions ensures: - Consistent naming - gen_ai.request.model
instead of custom names - Interoperability - Works with any OpenTelemetry-compatible backend - Community support - Follows industry standards
Rich Context
- Model information - Track model versions, parameters, and capabilities
- Token usage - Monitor costs and performance across different models
- Tool tracking - Understand how AI agents use tools and functions
- Error handling - Standardized error attributes and status codes
Vendor Flexibility
- Multi-backend - Switch between LangFuse, Jaeger, or other providers
- Future-proof - OpenTelemetry evolves with the observability ecosystem
- No vendor lock-in - Standard attributes work everywhere
When to Use This Approach
Use OpenTelemetry when: - ✅ You want industry-standard observability - ✅ You need multi-vendor compatibility - ✅ You’re building production AI applications - ✅ You want rich semantic context for AI operations - ✅ You need to comply with observability standards
Use SDK-specific approaches when: - ❌ You only need basic tracing for development - ❌ You want minimal setup overhead - ❌ You’re prototyping or learning
What’s Next?
Now that you understand OpenTelemetry tracing with LangFuse:
- Python SDK - Use the convenient Python SDK
- Raw Requests - Troubleshoot with HTTP requests
- Azure Deployment - Deploy your own LangFuse instance
🌟 You’re ready to implement standardized, vendor-neutral tracing!