Grafana
Connect Grafana to Berserk using the Berserk datasource plugin
The Berserk datasource plugin lets you query Berserk directly from Grafana dashboards using KQL.
Download the latest release for your platform from the GitHub releases page. Archives are available for Linux (x64, ARM64, ARM) and macOS (x64, ARM64).
Installation
Extract the plugin
Unzip the downloaded archive into your Grafana plugins directory:
unzip berserk-datasource-*.zip -d /var/lib/grafana/plugins/Allow unsigned plugin
Since the plugin is not signed, add it to the allow list in your Grafana configuration (grafana.ini or environment variable):
[plugins]
allow_loading_unsigned_plugins = berserk-datasourceOr with an environment variable:
GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS=berserk-datasourceRestart Grafana
Restart Grafana to load the plugin.
Add the datasource
In Grafana, go to Connections > Data sources > Add data source and search for Berserk. Set the URL to your Berserk cluster, which listens on port 9500 by default.
Set the service token
Berserk authenticates every request, so the datasource needs a service token. Create a service principal and mint a token with the CLI:
bzrk service-account create grafana --display-name "Grafana"
bzrk service-account token create grafana grafana-tokenYou can also create a service principal and token from the Service Accounts page in the Berserk admin UI.
Paste the resulting bzrk_sp_… token into the datasource's Service token field, then click Save & test (the health check verifies both reachability and the token). When provisioning Grafana from config, set it under secureJsonData.serviceToken rather than jsonData.
Authentication
Berserk authenticates every request at the gateway — the only authenticated edge, reachable on port 9500. The datasource authenticates as a service principal: it sends the service token as a bearer credential on each call, and the gateway resolves that token to a trusted identity before forwarding the request to the query engine. Every query from one datasource runs as that single service principal — there are no per-user credentials.
Set the token in the datasource's Service token field, as shown in Set the service token above. Grafana stores it encrypted under secureJsonData.serviceToken; it is never written to plain jsonData and cannot be read back through the API. Point the datasource URL (and, when provisioning, clusterUrl) at the gateway, not directly at the query service.
Provisioning from config
Supply the token from an environment variable backed by a secret rather than committing it:
datasources:
- name: Berserk
type: berserk-datasource
access: proxy
jsonData:
clusterUrl: http://gateway.bzrk.svc.cluster.local:9500
secureJsonData:
serviceToken: $BERSERK_SERVICE_TOKENRotating a token
Mint a fresh token, paste it into the datasource (or update the provisioned secret), then remove the old one from the Service Accounts page in the Berserk admin UI. Disabling the service principal there immediately blocks every datasource using its tokens.
A service principal can hold broad query access. Scope it to the databases the dashboards need, and use a separate service principal per Grafana instance so a leaked token can be revoked without disrupting the others.
Querying
The plugin supports KQL queries. Write your query in the editor and select a table from the dropdown. The plugin supports:
- Table format for log and trace data
- Time series for metrics and
summarize ... by bin(Timestamp, ...)queries - Alerting via Grafana alert rules
- Streaming for live tail queries
- Template variables for dynamic dashboards
Time Filter Macros
Panel and alert queries are automatically scoped to the dashboard's selected time range — the datasource forwards the range to Berserk, which applies it server-side, so a time filter is rarely needed.
A few cases still call for one: template-variable and annotation queries are not auto-scoped, and filtering on a custom datetime column needs the macro explicitly. The macros below cover those.
The macros below are still available for explicit control (for example, filtering on a custom datetime column). They are replaced with the dashboard's time range before the query is sent to Berserk:
| Macro | Expands to | Description |
|---|---|---|
$__timeFilter | timestamp >= datetime(...) and timestamp <= datetime(...) | Filter on the default timestamp column |
$__timeFilter(col) | col >= datetime(...) and col <= datetime(...) | Filter on a custom datetime column |
$__timeFrom | datetime(2025-01-01T00:00:00Z) | Start of the selected time range |
$__timeTo | datetime(2025-01-01T06:00:00Z) | End of the selected time range |
$__timeInterval | 5000ms | Recommended bin size based on the time range and panel width |
Use bin_auto(timestamp) to automatically choose a bin size based on the time range, or bin_auto(timestamp, 10m) to set a minimum bin size.
Example Queries
Count events over time by category:
default
| summarize count() by category, bin_auto(timestamp)
| sort by timestampAverage and p95 latency over time:
default
| where resource.service.name == "my-service"
| summarize
avg_duration=avg(toint(duration_ms)),
p95_duration=percentile(toint(duration_ms), 95)
by bin_auto(timestamp)
| sort by timestampTop pages by count:
default
| where name == "page_view"
| summarize count=count() by url.path
| sort by count desc
| take 20Error log table:
default
| where severity >= 3
| project timestamp, resource.service.name, body, error.message
| sort by timestamp desc
| take 100Parse structured log messages:
default
| where body contains "throughput"
| parse tostring(body) with "Local throughput: " Throughput:double " MB" *
| summarize min(Throughput), max(Throughput) by bin_auto(timestamp)