Explore documentation
Schema migration Feb ’26
We’re unifying how we store user-defined metrics extracted from logs and spans alongside Prometheus metrics.
Going forward, there’s just one way to query your metrics.
Schema overview
Our new row-based schema stores all data in a unified format:
- Metrics stored as named values with labels in a
tagsmap - Original event counts available per row type as
sum(logs_count),sum(spans_count),sum(exceptions_count),sum(replays_count),sum(web_events_count),sum(metrics_count), andsum(extracted_metrics_count) - Aggregated metric functions for efficient querying
| Old schema | New schema |
|---|---|
response_status |
label('response_status') |
buckets |
bucket_bounds + bucket_counts |
p50, p90, p95, p99 |
histogramQuantile(0.5), histogramQuantile(0.9), ... |
_row_type = 'logs' (or 'log') |
sum(logs_count) for log-row counts; analogous columns for other row types |
Accessing tags and labels
All non-aggregated columns that were previously accessed directly must now use the label() function. Labels are always strings, so toString(label(...)) is redundant.
Standard columns
The following columns are part of the schema itself and don't need the label() wrapper.
Counting events by category
Each ingested event creates multiple rows — one for the original event and several for any metrics derived from it. Use the per-row-type count columns to scope a count to a single category without writing an explicit predicate:
| Category | Count column |
|---|---|
| Original log event | logs_count |
| Original span event | spans_count |
| Exception | exceptions_count |
| Replay | replays_count |
| Web event | web_events_count |
| Metric (sent directly) | metrics_count |
| Metric (extracted from logs/spans) | extracted_metrics_count |
<X>_count is events_count on rows of that kind and 0 everywhere else, so sum(<X>_count) filters and counts in a single expression.
Count log events
(countMerge(events_count) still works for a generic across-row-type count — sum(logs_count) simply scopes it.)
Don't add a row-type predicate when querying named metrics
Type handling for labels
Since labels are always strings, you must handle type conversions explicitly when comparing to numbers.
Numeric comparison
Histograms and buckets
The new schema uses bucket_bounds and bucket_counts, with the counts storing deltas. sumForEachMerge() is used to sum these deltas.
Quantiles and percentiles
Use the histogramQuantile(p) helper function for single metrics, or quantilePrometheusHistogramArrayMergeIf(p)(bucket_quantiles, condition) for multiple metrics.
Event counting
A single event can be represented as multiple metric rows, so countMerge(events_count) on a source that emits extracted metrics will overcount. Use sum(logs_count) (or the appropriate sum(<X>_count) for the row type you want) to get just the original events.
countMerge(events_count) remains valid if you genuinely want the total row count (events + extracted metrics combined).
This change will go in effect for existing organizations during February ’26.
Let us know at hello@betterstack.com if you have any feedback. We hope this change will make the product more straightforward and intuitive going forward.