Skip to main content
Applies to:
  • Plan - Pro and Enterprise for custom SQL charts
  • Deployment - Any
  • Use case - How to build accurate multi-project cost dashboards and SQL charts that filter by root-span metadata (user_id) while aggregating estimated_cost() across traces/projects

Summary

Use Cross-trace filtering for cost charts filtered by root-span metadata so contributing child spans remain included.

What is happening

Span filtering matches individual spans. A filter on a field that exists on a different span will exclude those measured spans. Cross-trace filtering includes all spans from traces where any span matches the filter.

Fix or suggestion

For unattributed cost across projects, use a per-trace aggregation pattern. Group by root_span_id, surface metadata with any_value(), sum estimated_cost() for the selected spans, then filter for missing user_id and aggregate in an outer query. Example per-trace rollup across projects:

Option 1: Use spans shape and WHERE is_root for root-only inspection

Use this when you only need root-span rows. WHERE is_root = true is also valid. Example: count root spans and roots with user_id for one project.

Option 2: Use traces or summary shape with ANY_SPAN(...)

Use this when you need the traces or summary shape but must restrict traces by a root-span condition. Example: match traces whose root span has a metadata.user_id value of 123.

How to confirm it worked

  • Use the root-count query to inspect where user_id is present. Roots without user_id will still have zero root_spans_with_user_id.
  • Compare summed estimated_cost() before and after switching to Cross-trace filtering. Totals can change because additional spans contribute. Each span’s estimated cost calculation remains unchanged.

Notes

  • Select projects by passing their IDs to project_logs() because project_id is not guaranteed as a row field.
  • For a per-project breakdown, query each project separately. Passing multiple project IDs combines their data.