LangChain / LangGraph → OpenObserve
Automatically trace every chain execution, LLM call, tool invocation, and retrieval step in your LangChain or LangGraph application.
Prerequisites
- Python 3.8+
- An OpenObserve account (cloud or self-hosted)
- Your OpenObserve organisation ID and Base64-encoded auth token
Installation
For LangGraph applications, also install LangGraph:
Configuration
Create a .env file in your project root:
# OpenObserve instance URL
# Default for self-hosted: http://localhost:5080
OPENOBSERVE_URL=https://api.openobserve.ai/
# Your OpenObserve organisation slug or ID
OPENOBSERVE_ORG=your_org_id
# Basic auth token — Base64-encoded "email:password"
OPENOBSERVE_AUTH_TOKEN="Basic <your_base64_token>"
# LLM provider key (whichever backend LangChain is calling)
OPENAI_API_KEY=your-openai-key
Instrumentation
Call LangchainInstrumentor().instrument() before importing any LangChain modules to ensure all chains, agents, and tools are captured automatically.
LangChain
from opentelemetry.instrumentation.langchain import LangchainInstrumentor
from openobserve import openobserve_init
LangchainInstrumentor().instrument()
openobserve_init()
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
llm = ChatOpenAI(model="gpt-4o")
prompt = ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant."),
("user", "{question}"),
])
chain = prompt | llm | StrOutputParser()
response = chain.invoke({"question": "What is OpenTelemetry?"})
print(response)
LangGraph
LangGraph builds on LangChain, so the same instrumentor covers graph executions, node transitions, and all nested LLM calls.
from opentelemetry.instrumentation.langchain import LangchainInstrumentor
from openobserve import openobserve_init
LangchainInstrumentor().instrument()
openobserve_init()
from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
from typing import TypedDict
llm = ChatOpenAI(model="gpt-4o")
class State(TypedDict):
messages: list
def call_model(state: State):
response = llm.invoke(state["messages"])
return {"messages": state["messages"] + [response]}
graph = StateGraph(State)
graph.add_node("model", call_model)
graph.set_entry_point("model")
graph.add_edge("model", END)
app = graph.compile()
result = app.invoke({"messages": [{"role": "user", "content": "Hello!"}]})
print(result["messages"][-1].content)
What Gets Captured
| Attribute | Description |
|---|---|
langchain_request_type |
Span type: llm, chain, tool, retriever |
gen_ai_request_model |
Model name (e.g. gpt-4o) |
gen_ai_usage_input_tokens |
Input tokens consumed |
gen_ai_usage_output_tokens |
Output tokens generated |
llm_usage_tokens_total |
Total tokens |
llm_usage_cost_input |
Estimated input cost in USD |
llm_usage_cost_output |
Estimated output cost in USD |
llm_usage_cost_total |
Estimated total cost in USD |
langchain_retriever_query |
Query sent to a retriever |
langchain_tool_name |
Name of the tool invoked |
duration |
End-to-end span latency |
error |
Exception details on failure |
Each chain invocation produces a root span with child spans for every nested LLM call, tool use, and retrieval step.
Viewing Traces
- Log in to OpenObserve and navigate to Traces in the left sidebar
- Click any root span to expand the full chain execution tree
- Inspect token counts, latency, and retriever results at each step
Next Steps
With LangChain instrumented, every chain execution is automatically recorded in OpenObserve — including all nested LLM calls, tool invocations, and retrieval steps. From here you can visualise the full execution tree, track token usage per chain run, and identify latency bottlenecks across your pipeline.