A platform-wide error rate hides per-customer disasters. In a real thirty-minute window on our reference app, one merchant failed 45% of checkouts while the overall failure rate read 7.8% — a three-and-a-half point move nobody would page on. Cohorts answers the question that number cannot: who is this broken for?

What you need

One thing: spans carrying an attribute worth grouping by, and a session id to group them into.
  • Backend only works. Any attribute on your server spans — tenant.id, plan, deployment.region, feature.flag — is a dimension.
  • Frontend included works better, because failures that never reach your server (a bundle that 404s, a fetch that dies at the CDN, a redirect that never returns) have no server-side record at all. See Browser instrumentation.
Nothing is registered in advance. Add an attribute today and it is a dimension on the next query.

Reading the view

Pick a dimension. Every value becomes a row, ranked worst first, with the population rate marked on each bar so you can see the gap at a glance. Lift is the number to read. Above 1 means the cohort carries more damage than its size explains. A cohort at 8.4% of traffic and 48.6% of failures has a lift of 5.8 — that single figure is the finding.

Drill down

Click a row to get the sessions in that cohort, worst first. Click a session to get every span it touched, across every service, ordered by time — the browser’s page load, the fetch from the form, your API’s server span, the database call, and the downstream service that actually failed.
Session drill-down scopes by trace id, not by the session.id attribute. Baggage rides on fetch and XHR, so page navigations and static assets never carry it — scoping by trace closes that gap, and works even when your frontend propagates nothing at all.

How ranking works

Two choices that make the list survive contact with real data. Sessions, not spans. “What fraction of this customer’s spans errored” overweights whoever retried the most. The question teams actually ask is what fraction of their checkouts — or logins, or uploads — failed. A Wilson lower bound, not the raw rate. A cohort with one failure in three sessions has a 33% failure rate and would top a naive list forever. Its 95% lower bound is about 5%, so it sinks on its own — no arbitrary “minimum 50 sessions” threshold anyone has to defend. A session counts as failed if any span in it has an error status or an HTTP status ≥ 400. Both, because a 402 is often recorded with span status Unset — status alone would miss every decline.

Cardinality

Dimensions are discovered by sampling attribute keys, then filtered:
  • A key with one value is not a dimension.
  • A key with roughly as many distinct values as sessions is an identifier, not a dimension.
Both are hidden automatically. Tens of thousands of distinct values per dimension is fine. Put unbounded identifiers — order ids, email addresses, full URLs — on spans if you want them for debugging, but do not expect to group by them.
Rank on attributes your server stamps. Anything the browser writes is attacker-controlled: a user with devtools can reassign their session to another tenant. Client attributes are the right place for build version, experiment arm, or viewport — not for anything that decides attribution, billing, or an SLA.