Writing Queries
Learn how to query your observability data in Berserk
Berserk uses Kusto Query Language (KQL) to query logs, traces, and metrics. If you've used Azure Data Explorer, Azure Monitor, or Microsoft Sentinel, you already know KQL.
A KQL query starts with a dataset name and flows through a pipeline of operators separated by |. Each operator takes the output of the previous step and transforms it.
default
| where severity_text == "ERROR"
| project $time, body, resource.attributes["service.name"]
| tail 20This query reads from the default dataset, filters to error-level records, selects three columns, and returns the last 20.
If you get an error that default does not exist, try .show tables or bzrk dataset list to see which tables are available.
Datasets
Every query starts with a dataset name. This is the dataset you created when setting up ingestion:
default
| take 10Filtering with where
The where operator filters rows based on a condition:
default
| where severity_text == "ERROR"Combine conditions with and and or:
default
| where severity_text == "ERROR" and resource.attributes["service.name"] == "payment"Use contains for substring matching, or has for whole-word matching (faster):
default
| where body has "timeout"Filtering by time
Use ago() to filter relative to now:
default
| where $time > ago(1h)
| where severity_text == "ERROR"Or use between for an absolute range:
default
| where $time between (datetime(2026-03-01) .. datetime(2026-03-10))Selecting columns with project
Use project to select and rename columns:
default
| where severity_text == "ERROR"
| project timestamp = $time, body, resource.attributes["service.name"]Use extend to add computed columns while keeping all existing ones:
default
| extend duration_ms = toreal(duration) / 1000Counting and aggregating with summarize
Count rows:
default
| where severity_text == "ERROR"
| countGroup and aggregate with summarize:
default
| where $time > ago(1h)
| summarize error_count = count() by tostring(resource.attributes["service.name"])Create time-series buckets with bin():
default
| where $time > ago(24h)
| summarize count() by bin($time, 1h), severity_textSorting and limiting
Sort with sort by (or its alias order by):
default
| where severity_text == "ERROR"
| sort by $time descGet the top N rows by a value:
default
| top 5 by duration descReturn an arbitrary sample with take:
default
| take 100Return the most recent 100 with tail:
default
| tail 100This is the same as saying | top 100 by $time. The difference between tail and take is that take terminates the query early -- as soon as it has found N values to present to the user, while tail has to use indexes to find the most recent data.
Multiple result tables
Use semicolons to run independent queries in a single request, each producing its own result table:
default | where severity_text == "ERROR" | count;
default | where severity_text == "WARN" | countUse fork to split a single pipeline into multiple branches:
default
| fork
Errors=(where severity_text == "ERROR" | count)
Recent=(take 10)See fork for details on named branches, supported operators, and shared-scan optimization.
Working with semi-structured data
Observability data often has fields that aren't part of a fixed schema. Berserk stores the full original record in a special $raw column and can automatically resolve unknown field names from it. See $raw and Field Resolution for details.
default
| where attributes.http.status_code >= 500
| project $time, method = attributes.http.method, url = attributes.http.urlQuick reference
| Operator | What it does | Example |
|---|---|---|
where | Filter rows | where severity_text == "ERROR" |
project | Select/rename columns | project $time, body |
extend | Add computed columns | extend ms = duration / 1000 |
summarize | Aggregate | summarize count() by tostring(resource.attributes["service.name"]) |
sort by | Order rows | sort by $time desc |
top | First N by a value | top 10 by duration desc |
take | Arbitrary N rows | take 100 |
tail | Most recent N rows | tail 100 |
distinct | Unique values | distinct resource.attributes["service.name"] |
count | Count rows | count |
join | Combine tables | join (other) on id |
fork | Multiple result tables | fork (count) (take 5) |
; | Independent queries | T | count; T | take 5 |
Reference
- Scalar Functions — String, datetime, dynamic, math, type conversion, and more
- Tabular Operators — where, project, summarize, join, sort, and more
- Aggregate Functions — count, dcount, avg, sum, percentile, and more