Skip to content

Commit 21274c1

Browse files
thirasclaude
andcommitted
fix(ansible): close review findings on the daemon log-level stage
- Exempt decdn-node from the journald-priority drop in journal_rules and let daemon_level apply the same regex to its JSON level: every daemon line is journald-info, so a guardrail containing `info` dropped its warnings and errors before the JSON was ever read. - Default the priority guardrail to 'debug|trace'. TRACE normalises to `trace`, not a journald keyword, so it bypassed the old 'debug' default. - Accept only the five tracing levels (trimmed, case-folded); anything else keeps the journald level instead of becoming a label value. - `{% endraw +%}`: trim_blocks glued the stage's closing brace onto the template line in the rendered config. - Fix comments: upstream's formats are pretty/json (no "text"), the three places the unit name is hard-coded, and the empty-template path. validate.sh: assert the stage is wired in both directions, run the rendered journal_rules + daemon_level through the real Alloy for the defaults and a new info-dropping render variant, cover TRACE/lowercase/no-level/non-string cases, fail explicitly on readiness/entry-count timeouts and on an Alloy crash, and dump the Alloy log before cleanup deletes it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 2aa7399 commit 21274c1

5 files changed

Lines changed: 216 additions & 77 deletions

File tree

‎ansible/roles/grafana_alloy/README.md‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -226,14 +226,15 @@ deliberately lean and every lever is a variable:
226226
- Host metrics scrape at `60s`, not the node's `30s`.
227227
- The `filesystem` / `netdev` / `disk` exclude regexes drop virtual filesystems
228228
and container/virtual interfaces.
229-
- journald drops `debug` priority and a noisy-unit list
229+
- journald drops `debug` (and the daemon's `trace`) and a noisy-unit list
230230
(`grafana_alloy_logs_drop_priority_regex` / `_drop_unit_regex`, `""` disables
231231
either), and `grafana_alloy_logs_max_age` bounds the catch-up burst after an
232232
outage — without it a restart can replay days of journal in one go.
233233
- decdn-node's `level` label comes from its JSON `level` field, not the journald
234-
priority (journald reports every stdout line as `info`). It uses journald's
235-
keywords, so query `level="warning"`, not `warn`. Lines that are not JSON keep
236-
the journald level.
234+
priority (journald reports every stdout line as `info`), and the priority drop
235+
regex is applied to that JSON level for this unit. Values follow journald's
236+
keywords, so query `level="warning"`, not `warn`; TRACE becomes `trace`. Lines
237+
that are not JSON keep the journald level and are never priority-dropped.
237238

238239
## Hardening: the two relaxations machine monitoring requires
239240

‎ansible/roles/grafana_alloy/defaults/main.yml‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -225,10 +225,13 @@ grafana_alloy_logs_journal_path: ""
225225
# Cost guardrails, applied while the __journal* fields still exist. Both are
226226
# anchored regexes matched against the whole value; "" disables that rule.
227227
# Priority keywords are emerg/alert/crit/error/warning/notice/info/debug
228-
# (journald's numeric priority 3 surfaces as "error", NOT "err"). The priority
229-
# regex also drops decdn-node lines by their JSON `level`, normalised to these
230-
# same keywords (WARN -> warning, TRACE -> trace).
231-
grafana_alloy_logs_drop_priority_regex: 'debug'
228+
# (journald's numeric priority 3 surfaces as "error", NOT "err").
229+
# decdn-node is judged by its JSON `level` instead of its journald priority
230+
# (always info), anchored the same way (^(?:...)$) and normalised to these
231+
# keywords: WARN -> warning, DEBUG/INFO/ERROR unchanged, TRACE -> trace. `trace`
232+
# is not a journald keyword, which is why the default lists it: without it the
233+
# daemon's most verbose level would bypass the guardrail.
234+
grafana_alloy_logs_drop_priority_regex: 'debug|trace'
232235
grafana_alloy_logs_drop_unit_regex:
233236
'(session-\d+\.scope|user@\d+\.service|systemd-timesyncd\.service|cron\.service|systemd-logind\.service)'
234237

‎ansible/roles/grafana_alloy/templates/config.alloy.j2‎

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,27 @@ loki.relabel "journal_rules" {
268268

269269
// Cost guardrail: the regex is anchored to the WHOLE value by Prometheus
270270
// relabel semantics, so "debug" drops debug and nothing else.
271+
//
272+
// decdn-node is exempt: journald reports every line it writes as info, so
273+
// judging it by priority would be judging it by a level it never set (a
274+
// regex containing "info" would drop its errors). loki.process
275+
// "daemon_level" below applies the same regex to its real, JSON level. The
276+
// exemption blanks a scratch copy of the keyword for that one unit; like
277+
// every __-prefixed label it never leaves this ruleset.
271278
rule {
272279
source_labels = ["__journal_priority_keyword"]
280+
target_label = "__priority_guardrail"
281+
}
282+
283+
rule {
284+
source_labels = ["__journal__systemd_unit"]
285+
regex = "decdn-node\\.service"
286+
target_label = "__priority_guardrail"
287+
replacement = ""
288+
}
289+
290+
rule {
291+
source_labels = ["__priority_guardrail"]
273292
regex = {{ grafana_alloy_logs_drop_priority_regex | tojson }}
274293
action = "drop"
275294
}
@@ -305,12 +324,19 @@ loki.source.journal "host" {
305324
field of the tracing-subscriber JSON body, e.g. {"level":"WARN",...}.
306325
307326
This runs after loki.source.journal, where the __journal* fields are already
308-
gone, so it keys off the `unit` label journal_rules produced (the same unit
309-
name the service_name rule below pins — a rename must be mirrored in both).
310-
The value is normalised to journald's own keywords (WARN -> warning), so one
311-
`level` vocabulary spans every unit. A line that is not JSON (log_format =
312-
"text", or a panic written raw to stderr) extracts nothing; the template then
313-
renders empty, which removes the key, and the journald level stands. #}
327+
gone, so it keys off the `unit` label journal_rules produced. The unit name
328+
is the one decdn_node installs; it is also hard-coded in the journal_rules
329+
priority exemption above and the service_name rule below — a rename must be
330+
mirrored in all three (validate.sh pins them).
331+
332+
Only the five tracing levels are accepted, trimmed and case-folded, and WARN
333+
becomes journald's `warning`; DEBUG/INFO/ERROR already match journald's
334+
keywords. TRACE stays `trace`, which journald has no keyword for, so the
335+
priority guardrail default lists it explicitly. Anything else — a line that
336+
is not JSON (log_format = "pretty", or a panic written raw to stderr), JSON
337+
with no level, or an unexpected value — leaves decdn_level unset (the
338+
template stage deletes a key it renders empty), so stage.labels and
339+
stage.drop skip the line and its journald level stands. #}
314340
loki.process "daemon_level" {
315341
forward_to = [loki.relabel.journal_identity.receiver]
316342

@@ -323,16 +349,16 @@ loki.process "daemon_level" {
323349

324350
stage.template {
325351
source = "decdn_level"
326-
template = {% raw %}`{{ with .Value }}{{ if eq (ToUpper .) "WARN" }}warning{{ else }}{{ ToLower . }}{{ end }}{{ end }}`{% endraw %}
352+
template = {% raw %}`{{ $l := ToLower (TrimSpace (print .Value)) }}{{ if eq $l "warn" "warning" }}warning{{ else if eq $l "trace" "debug" "info" "error" }}{{ $l }}{{ end }}`{% endraw +%}
327353
}
328354

329355
stage.labels {
330356
values = { level = "decdn_level" }
331357
}
332358
{% if grafana_alloy_logs_drop_priority_regex | length > 0 %}
333359

334-
// The priority guardrail again, for the daemon's DEBUG lines: journald
335-
// reported them as info, so the journal_rules drop never saw them. Unlike
360+
// The priority guardrail for the daemon, which journal_rules exempts: the
361+
// same regex, applied to the level the daemon actually logged. Unlike
336362
// relabel regexes, stage.drop is NOT anchored, hence the explicit ^...$.
337363
stage.drop {
338364
source = "decdn_level"
@@ -372,7 +398,9 @@ loki.relabel "journal_identity" {
372398
so this catches only lines the daemon process writes: systemd's own
373399
start/stop/crash messages about it come from PID 1 as unit="init.scope". The
374400
name is the unit decdn_node installs, /etc/systemd/system/decdn-node.service
375-
— a rename there must be mirrored here (validate.sh pins it). #}
401+
— a rename there must be mirrored here AND in the journal_rules priority
402+
exemption and the loki.process "daemon_level" selector above (validate.sh
403+
pins all three), plus grafana_alloy_host_systemd_unit_include. #}
376404

377405
rule {
378406
source_labels = ["unit"]

‎ansible/tests/alloy-config/render.yml‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,24 @@
257257
# Hands validate.sh the release pin without making it parse YAML itself: the
258258
# values come from the role's own defaults, so the download it verifies is
259259
# byte-identical to the one the role installs.
260+
# A priority guardrail that includes `info`: journald reports every
261+
# decdn-node line as info, so this proves the daemon is judged by its JSON
262+
# level instead (its warnings and errors must survive). validate.sh runs
263+
# this file through the real pipeline.
264+
- name: Render the info-dropping priority-guardrail configuration
265+
ansible.builtin.template:
266+
src: "{{ playbook_dir }}/../../roles/grafana_alloy/templates/{{ item.src }}"
267+
dest: "{{ _ga_render_dir }}/priorityinfo.{{ item.ext }}"
268+
mode: "0644"
269+
loop: *ga_render_pair
270+
loop_control:
271+
label: "priorityinfo.{{ item.ext }}"
272+
vars:
273+
_ga_instance_id: decdn-node-1
274+
_ga_journal_groups: [systemd-journal, adm]
275+
grafana_alloy_bin_effective: /usr/bin/alloy
276+
grafana_alloy_logs_drop_priority_regex: 'info|debug|trace'
277+
260278
- name: Export the pinned release for the validation script
261279
ansible.builtin.copy:
262280
dest: "{{ _ga_render_dir }}/pin.env"

0 commit comments

Comments
 (0)