Working with the observations table
In Langfuse v4, every operation your application performs (LLM calls, tool executions, retrievals, agent steps) is one row in a single observations table. A trace is just the set of rows sharing a trace_id; the data model docs explain the model.
That means more rows than the old traces table, which is expected. Work with filters and saved views, not by scrolling: filter by observation name, type, or model, then save the view. The filter sidebar shows available values with counts. Filters depend on trace-level attributes (user_id, session_id, tags) living on every observation; the SDKs propagate them via propagate_attributes() / propagateAttributes() (see adding attributes).
Common views
Save 2-3 of these as saved views for one-click access:
| I want to see… | How to filter |
|---|---|
| Key operations | Filter by observation name or type matching your most important operations |
| All LLM generations | Filter type = generation |
| Expensive LLM calls | Filter type = generation, sort by total_cost descending |
| Errors for a specific user | Filter by user_id and level = ERROR |
| Operations scoped to a session | Filter by session_id, sort by latency descending |
| All operations for one trace | Filter by trace_id |
| Application entry points | Filter Is Root Observation = true |
| Model latency comparison | Filter type = generation, group by model in a dashboard or sort by model column |
| Cost, latency, or errors by dimension | Self-serve dashboards |
Start with root observations
For projects that ingest with Python SDK ≥ 4.7.0 or JS/TS SDK ≥ 5.4.0, Langfuse applies Is Root Observation = true when you open the observations table without a saved view, shared filter link, or other table state. You see your application entry points first and drill into their child operations from there.
The filter includes two kinds of roots:
- Outer roots are top-level observations with no parent.
- App roots are the highest exported application observations. When an OpenTelemetry or infrastructure span is the outer parent and the SDK filters it from export, the exported child still carries its parent span ID, so a "no parent" filter alone would miss it. App-root-aware SDKs mark that entry point instead.
Remove the filter in the sidebar to see every operation; Langfuse remembers that choice. Saved views and links with table state keep their own filters, and if the filter matches no roots while other observations exist, Langfuse removes it automatically.
Data appearing with up to a 10-minute delay? You are ingesting via older SDKs or via OpenTelemetry without the x-langfuse-ingestion-version: 4 header. Upgrade to Python SDK ≥ 4.7.0 / JS/TS SDK ≥ 5.4.0, or set the header on your exporter; see Versions & Compatibility.
Last edited