LlamaIndex Tutorial (TypeScript): adding observability for beginners
This tutorial shows you how to add observability to a LlamaIndex TypeScript app so you can see what your agents are doing, how long each step takes, and where failures happen. You need this once your app moves beyond local testing and you want trace data for debugging, latency tracking, and production support.
What You'll Need
- •Node.js 18+ installed
- •A TypeScript project with
npmorpnpm - •
llamaindexinstalled - •An OpenAI API key
- •A Phoenix instance for tracing:
- •Local:
docker run -p 6006:6006 arizephoenix/phoenix:latest - •Or a hosted Phoenix endpoint
- •Local:
- •Basic familiarity with
Settings,Document,VectorStoreIndex, and query engines in LlamaIndex
Step-by-Step
- •
Install the packages you need.
You only need the core LlamaIndex package plus the OpenTelemetry bridge that sends traces to Phoenix. Keep the dependency set small; observability should be easy to add to existing apps.
npm install llamaindex @arizeai/openinference-instrumentation-llamaindex @opentelemetry/api @opentelemetry/sdk-trace-node @opentelemetry/resources @opentelemetry/exporter-trace-otlp-http dotenv - •
Set up your environment variables.
This keeps secrets out of source control and makes it easy to point the same app at local Phoenix or a hosted collector.
# .env OPENAI_API_KEY=your_openai_api_key PHOENIX_COLLECTOR_ENDPOINT=http://localhost:6006/v1/traces OTEL_SERVICE_NAME=llamaindex-ts-observability-demo - •
Initialize tracing before you import or create your index.
In practice, tracing must start early so LlamaIndex operations are captured from the first embedding call through retrieval and response synthesis.
// tracing.ts import "dotenv/config"; import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node"; import { Resource } from "@opentelemetry/resources"; import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http"; import { SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base"; export function initTracing() { const provider = new NodeTracerProvider({ resource: new Resource({ "service.name": process.env.OTEL_SERVICE_NAME ?? "llamaindex-ts-app", }), }); const exporter = new OTLPTraceExporter({ url: process.env.PHOENIX_COLLECTOR_ENDPOINT, }); provider.addSpanProcessor(new SimpleSpanProcessor(exporter)); provider.register(); } - •
Instrument LlamaIndex and build a small query app.
This example creates an in-memory index, runs one query, and emits spans automatically through the instrumented runtime. The important part is that tracing is enabled before any LlamaIndex work starts.
// index.ts import "dotenv/config"; import { initTracing } from "./tracing"; import { LlamaIndexInstrumentation } from "@arizeai/openinference-instrumentation-llamaindex"; initTracing(); new LlamaIndexInstrumentation().instrument(); import { Settings, Document, VectorStoreIndex, OpenAI, } from "llamaindex"; Settings.llm = new OpenAI({ model: "gpt-4o-mini", apiKey: process.env.OPENAI_API_KEY, }); async function main() { const docs = [ new Document({ text: "Phoenix captures traces for LLM applications.", }), new Document({ text: "LlamaIndex can build retrieval pipelines over documents.", }), ]; const index = await VectorStoreIndex.fromDocuments(docs); const queryEngine = index.asQueryEngine(); const response = await queryEngine.query({ query: "What does Phoenix capture?", }); console.log(String(response)); } main().catch((err) => { console.error(err); process.exit(1); }); - •
Run the app and inspect the trace in Phoenix.
If everything is wired correctly, you should see spans for embedding, indexing, retrieval, and synthesis. That gives you a full request timeline instead of just a final answer string.
Testing It
Run your Phoenix container first if you're using local tracing, then execute the TypeScript app with your normal runner. For example, use npx tsx index.ts or compile with tsc and run the output with Node.
If tracing is working, open Phoenix at http://localhost:6006 and look for a new trace under your service name. You should see nested spans rather than a single flat request event.
A good sanity check is to change the query text and confirm that each run creates a separate trace. If nothing appears, verify that initTracing() runs before importing or constructing LlamaIndex objects, and confirm that PHOENIX_COLLECTOR_ENDPOINT matches your collector URL exactly.
Next Steps
- •Add custom spans around business logic like policy checks, tool calls, and document filtering.
- •Learn how to attach metadata such as tenant ID, conversation ID, and environment tags to traces.
- •Export traces to a managed observability backend once local Phoenix is stable in development.
Keep learning
- •The complete AI Agents Roadmap — my full 8-step breakdown
- •Free: The AI Agent Starter Kit — PDF checklist + starter code
- •Work with me — I build AI for banks and insurance companies
By Cyprian Aarons, AI Consultant at Topiax.
Want the complete 8-step roadmap?
Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.
Get the Starter Kit