# journald `PRIORITY` → OpenTelemetry severity, as the stanza operator list # every journald receiver in this tree attaches. The table it produces and # what a consumer does with it: docs/scheduler/observability.md, "Log severity". # # ONE file, imported by both tiers (`agent-modules/otel.nix` reads the # container's own journal, `host-modules/swarm-otel.nix` the host's), because # the mapping is the part that must not drift. The two receivers are otherwise # unrelated — one a fixed stanza, one parameterised inside a container config — # so a copy in each is two tables to keep in step, and getting one wrong is # silent: every line still arrives, it just arrives labelled as the wrong thing. # # ⚠️ **The direction is inverted.** syslog PRIORITY counts DOWN in urgency # (0 = emerg, 7 = debug) where OpenTelemetry's severity number counts UP # (1 = trace, 21 = fatal). Passed through as a severity number, debug renders # as critical and emerg as unspecified, and nothing reports an error. [ { type = "severity_parser"; # The journald input unmarshals each `journalctl -o json` line into the # entry BODY as a map, so every journal field is addressed under `body.`. # `attributes.PRIORITY` is the spelling that looks equally plausible and # finds nothing — the parser then reports a missing field per entry rather # than failing to start. parse_from = "body.PRIORITY"; # 🔑 NOT optional, and the single easiest thing to leave off. By default the # severity parser sets the severity NUMBER from the mapping and leaves the # severity TEXT as the original value it matched on — so `severity_text` # would arrive in VictoriaLogs as the literal string `"6"`. That is a # populated field, it passes every check that asks whether severity is set, # and it is not a level any consumer renders. With this on, the text is the # recommended short name for the mapped number: `FATAL`, `ERROR`, `WARN`, # `INFO`, `DEBUG`. The TEXT is the whole interface here — VictoriaLogs has # no ingest parameter naming a level field, it auto-detects one by trying # `severity_text`, `SeverityText`, `severity`, `Severity` in that order, so # the value has to arrive already spelled as a word it knows. overwrite_text = true; # A record whose PRIORITY the parser cannot read must still REACH the # store. Explicit rather than inherited from the operator default, because # the alternative (`drop`) would make the "lines with no severity" panel on # the logs dashboard read zero by deleting its own evidence — the exact # empty-result-as-success failure this mapping exists to end. on_error = "send"; # Strings, because that is what arrives: `journalctl -o json` renders every # journal field as a JSON string, so `PRIORITY` is `"6"` and not `6`. The # parser stringifies its mapping values too, so an integer spelling here # would also match — the strings are for the reader, to keep the config # shaped like the data. mapping = { fatal = [ "0" "1" "2" ]; error = "3"; warn = "4"; info = [ "5" "6" ]; debug = "7"; }; } ]