grafana: tell the logs datasource that severity_text is the level

Grafana's log-level buttons filter on a field called `level`; no row in
the store has one. The store's name for it is `severity_text`, chosen by
VictoriaLogs' OTLP ingester rather than by us — v1.52.0's
`app/vlinsert/opentelemetry/pb.go` writes it unconditionally and the
ingest parameters have no `_level_field` to rename it with. So the
mapping is made on the reader: the VictoriaLogs datasource, which was
provisioned with no `jsonData` at all, now carries a `logLevelRules`
entry per severity the journald parser can emit.

`logLevelRules` is the datasource plugin's only level-related jsonData
key — there is no field-name setting and no OpenTelemetry preset to
switch on. It is read off `instanceSettings.jsonData` in the plugin's
`datasource.ts` and typed in its `configuration/LogLevelRules/types.ts`,
both recovered from the sourcemap shipped in the pinned artifact
(`grafanaPlugins.victoriametrics-logs-datasource` 0.26.3). Each enabled
rule appends an `OR severity_text:="INFO"`-shaped term to the query a
level button emits, next to the `level:…` term that matches nothing.

A wrong rule here fails silently: Grafana provisions unknown jsonData
without complaint and the buttons go on returning zero rows. The three
ways to get one wrong — a non-literal `enabled`, a non-canonical `level`
spelling, a value that is not the stored text — are recorded at the
binding, and a module-eval arm pins them along with the real failure
mode, a severity added to `nix/journald-severity.nix` and not here.

`Unspecified` is left unmapped on purpose: it is the store's own
rendering of an absent severity and the thing the logs dashboard's "no
severity" panel counts.

Refs #4560
This commit is contained in:
atlas 2026-09-20 16:13:43 +02:00 committed by mara
commit 8e9ca3ee6c
3 changed files with 119 additions and 0 deletions

View file

@ -243,6 +243,43 @@ let
lib.any (e: lib.hasInfix "PRIORITY:*" e) counts
&& lib.any (e: lib.hasInfix "PRIORITY:\"\"" e) counts;
}
{
# The reader's half of the same mapping. The collector writes a severity
# TEXT; nothing downstream reads it as a level unless the datasource is
# told which field holds it, and that telling is a list of rules rather
# than a field name — so it goes stale one severity at a time. Asserted
# against ../journald-severity.nix rather than a literal list here,
# because the way this regresses is a severity added to the parser and
# not to Grafana: every line still arrives, the new one is just
# unfilterable, and no query errors to say so.
#
# `enabled` and the canonical `level` spelling are pinned alongside
# because both fail silently in the direction of "provisions fine,
# returns nothing" — see the comment on `logLevelRules` in
# ../host-modules/swarm-grafana.nix.
name = "the logs datasource maps every severity the collector can emit to a log level";
ok =
let
sources =
grafanaOldPath.containers.swarm-grafana.config.services.grafana.provision.datasources.settings.datasources;
logs = lib.head (lib.filter (d: d.uid == "swarm-victorialogs") sources);
rules = logs.jsonData.logLevelRules or [ ];
# `overwrite_text` makes the stored text the OpenTelemetry short name
# for each mapped severity, which is the parser's own key uppercased.
emitted = map lib.toUpper (lib.attrNames (lib.head (import ../journald-severity.nix)).mapping);
canonical = [
"critical"
"error"
"warning"
"info"
"debug"
"trace"
];
in
rules != [ ]
&& lib.all (r: r.field == "severity_text" && r.enabled == true && lib.elem r.level canonical) rules
&& lib.all (text: lib.any (r: r.value == text) rules) emitted;
}
];
in
runGroup "grafana" cases