Langfuse Observability Patterns
Quick Guide: Use the Langfuse TypeScript SDK (built on OpenTelemetry) to add observability to LLM applications. Install
@langfuse/tracing,@langfuse/otel, and@opentelemetry/sdk-nodefor core tracing. UsestartActiveObservation()for automatic context propagation orobserve()to wrap functions. Use@langfuse/openaiwithobserveOpenAI()for zero-config OpenAI tracing. UseLangfuseClientfrom@langfuse/clientfor prompt management, scores, and datasets. Always callforceFlush()orsdk.shutdown()in short-lived processes.
<critical_requirements>
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST import and register instrumentation.ts at the top of your entry point BEFORE any other imports -- OpenTelemetry must instrument modules before they are loaded)
(You MUST call forceFlush() or sdk.shutdown() in short-lived processes (serverless, scripts, CLI tools) -- events are batched and will be lost without explicit flushing)
(You MUST use @langfuse/openai with observeOpenAI() for OpenAI SDK tracing -- do NOT manually create generation observations for OpenAI calls when the wrapper handles it automatically)
(You MUST set LANGFUSE_SECRET_KEY, LANGFUSE_PUBLIC_KEY, and LANGFUSE_BASE_URL via environment variables -- never hardcode credentials)
(You MUST use startActiveObservation() or observe() for nested tracing -- manual startObservation() requires explicit .end() calls and does NOT propagate context automatically)
</critical_requirements>
Auto-detection: Langfuse, langfuse, @langfuse/tracing, @langfuse/otel, @langfuse/client, @langfuse/openai, LangfuseSpanProcessor, LangfuseClient, startActiveObservation, startObservation, observeOpenAI, langfuse.score, langfuse.prompt, langfuse.dataset, LANGFUSE_SECRET_KEY, LANGFUSE_PUBLIC_KEY, forceFlush
When to use:
- Adding observability and tracing to LLM application code (any provider)
- Wrapping OpenAI SDK calls for automatic token/cost tracking
- Managing prompt templates with versioning, labels, and variable compilation
- Evaluating LLM output quality with scores (numeric, categorical, boolean)
- Running experiments against datasets for regression testing
- Tracking sessions, users, and metadata across multi-turn conversations
- Monitoring LLM costs and token usage in production
Key patterns covered:
- OpenTelemetry setup with
LangfuseSpanProcessor - Tracing with
startActiveObservation,observe, and manualstartObservation - Observation types (span, generation, agent, tool, retriever, evaluator, embedding, chain, guardrail)
- OpenAI SDK auto-instrumentation with
observeOpenAI() - Prompt management (get, compile, text vs chat prompts, versioning)
- Scores and evaluations (numeric, categorical, boolean)
- Datasets and experiments for testing
- Flush, shutdown, and lifecycle management
When NOT to use:
- You only need basic
console.logdebugging -- Langfuse is for structured production observability - You want provider-specific tracing built into an AI SDK -- check if your framework has native observability
- You need APM/infrastructure monitoring (CPU, memory, HTTP latency) -- use a general-purpose observability tool
Examples Index
- Core: Setup & Configuration -- OpenTelemetry setup, instrumentation file, client init, flush/shutdown
- Tracing -- startActiveObservation, observe, manual tracing, nesting, observation types, metadata
- OpenAI Integration -- observeOpenAI wrapper, streaming, token tracking, custom attributes
- Prompt Management -- getPrompt, compile, text vs chat, versioning, caching
- Scores & Datasets -- Numeric/categorical/boolean scores, datasets, experiments
- Quick API Reference -- Package index, environment variables, observation types, score methods
<philosophy>
Philosophy
Langfuse provides open-source LLM observability built on OpenTelemetry. The SDK (v4+, August 2025) is a ground-up rewrite using OTel as the tracing backbone, meaning traces integrate naturally with the broader observability ecosystem.
Core principles:
- OpenTelemetry-native -- Built on OTel spans and context propagation. Langfuse observations are wrappers around OTel spans with LLM-specific attributes (model, tokens, cost). This means any OTel-compatible instrumentation library works alongside Langfuse.
- Zero-latency tracing -- All trace events are queued locally and flushed in background batches. Your application's response time is not affected by observability.
- Modular packages --
@langfuse/tracingfor instrumentation,@langfuse/clientfor prompts/scores/datasets,@langfuse/openaifor OpenAI auto-instrumentation. Install only what you need. - Context-first --
startActiveObservation()automatically propagates parent-child relationships. Nested observations inherit context without manual ID threading. - Observation types -- LLM-specific types (
generation,agent,tool,retriever,evaluator,embedding) provide semantic meaning to traces, enabling richer dashboard views and filtering.
<patterns>
Core Patterns
Pattern 1: OpenTelemetry Setup
Create an instrumentation.ts file and import it at the top of your entry point.
// instrumentation.ts
import { NodeSDK } from "@opentelemetry/sdk-node";
import { LangfuseSpanProcessor } from "@langfuse/otel";
const sdk = new NodeSDK({
spanProcessors: [new LangfuseSpanProcessor()],
});
sdk.start();
export { sdk };
// index.ts -- import instrumentation FIRST
import "./instrumentation";
// All other imports AFTER instrumentation
import { startActiveObservation } from "@langfuse/tracing";
Why good: OTel must instrument modules before they are loaded; importing instrumentation first ensures all subsequent imports are traced automatically
// BAD: importing instrumentation after other modules
import { startActiveObservation } from "@langfuse/tracing";
import "./instrumentation"; // TOO LATE -- tracing won't capture earlier imports
Why bad: Auto-instrumentation of LLM SDKs requires OTel to be initialized before those modules are imported
See: examples/core.md for environment variables, sampling, masking, and production configuration
Pattern 2: Tracing with startActiveObservation
The primary instrumentation pattern. Creates an observation, makes it the active context, and automatically ends it when the callback completes.
import { startActiveObservation } from "@langfuse/tracing";
async function handleRequest(query: string): Promise<string> {
return await startActiveObservation("handle-request", async (span) => {
span.update({ input: { query } });
// Nested observation -- automatically becomes a child
const result = await startActiveObservation(
"process-query",
async (child) => {
child.update({ input: { query } });
const answer = await callLLM(query);
child.update({ output: { answer } });
return answer;
},
);
span.update({ output: { result } });
return result;
});
}
Why good: Automatic context propagation, automatic end on callback completion, nesting creates parent-child hierarchy without manual ID management
// BAD: using startObservation without ending it
import { startObservation } from "@langfuse/tracing";
const span = startObservation("my-span");
await doWork();
// span.end() never called -- observation stays open forever
Why bad: Manual startObservation requires explicit .end() calls; forgetting creates open-