Play nicely with journalctl's colourisation. #37

Closed
opened 2026-07-07 17:17:33 +01:00 by danny · 2 comments
danny commented 2026-07-07 17:17:33 +01:00 (Migrated from gitlab.keyop.co.uk)

Currently journalctl --user -u nfq_forwarder --follow will show logs, but we lose both the colourisation of the go logger and any journalctl colourisation.

I believe journalctl can show colours based on messages (but I could be wrong about that). If it can, ensure our messages can properly trigger these colourisations as we would expect, e.g. 'errors' in red.

Currently `journalctl --user -u nfq_forwarder --follow` will show logs, but we lose both the colourisation of the go logger and any journalctl colourisation. I believe journalctl can show colours based on messages (but I could be wrong about that). If it can, ensure our messages can properly trigger these colourisations as we would expect, e.g. 'errors' in red.
danny commented 2026-07-07 17:19:39 +01:00 (Migrated from gitlab.keyop.co.uk)

changed the description

changed the description
Collaborator

Investigation + fix

Confirmed: journalctl colourises journal entries by their real PRIORITY field (severity), not
by parsing message text. Today, every line nfq_forwarder writes to stderr under systemd gets the
same blanket priority, since a plain byte stream carries no per-line signal - so journalctl
can't tell an Info line from an Error one and colourises nothing.

systemd's own mechanism for exactly this is SyslogLevelPrefix=yes (already systemd's default
for a journal-captured service, now made explicit in docs/nfq_forwarder.service for
self-documentation): journald parses a <N> prefix (0-7, the traditional syslog/kernel
printk() priority scale) at the start of each line and uses it as that entry's real priority,
stripping the prefix from the stored message.

internal/log/syslog_prefix.go adds this: syslogPriority maps our five levels onto that scale
(Debug→7, Info→6, Warn→4, Error→3, FatalLevel→2 "crit"), and a small
prefixWriter/syslogPrefixHandler pair (following the exact same wrapping pattern
bufferHandler already uses) prepends the right <N> to each line - only when stderr isn't a
terminal (an interactive session already gets tint's own ANSI colours; the raw prefix would just
be noise there).

Verified end to end against a real journald, not just unit tests: piped a small throwaway
program's output through systemd-cat --level-prefix=true (mirroring the unit's
SyslogLevelPrefix=yes) and confirmed via journalctl --output=json that each entry's
PRIORITY field exactly matches (7/6/4/3 for debug/info/warn/error) with the prefix correctly
stripped from the message, and via the pager's raw ANSI escapes that journalctl renders DEBUG
dim grey, INFO plain, WARN bold yellow, and ERROR bold red - exactly the behaviour requested.

make lint/make test clean.

## Investigation + fix Confirmed: journalctl colourises journal entries by their real `PRIORITY` field (severity), not by parsing message text. Today, every line nfq_forwarder writes to stderr under systemd gets the *same* blanket priority, since a plain byte stream carries no per-line signal - so journalctl can't tell an `Info` line from an `Error` one and colourises nothing. systemd's own mechanism for exactly this is `SyslogLevelPrefix=yes` (already systemd's default for a journal-captured service, now made explicit in `docs/nfq_forwarder.service` for self-documentation): journald parses a `<N>` prefix (0-7, the traditional syslog/kernel `printk()` priority scale) at the start of each line and uses it as that entry's real priority, stripping the prefix from the stored message. `internal/log/syslog_prefix.go` adds this: `syslogPriority` maps our five levels onto that scale (`Debug`→7, `Info`→6, `Warn`→4, `Error`→3, `FatalLevel`→2 "crit"), and a small `prefixWriter`/`syslogPrefixHandler` pair (following the exact same wrapping pattern `bufferHandler` already uses) prepends the right `<N>` to each line - only when stderr isn't a terminal (an interactive session already gets tint's own ANSI colours; the raw prefix would just be noise there). **Verified end to end against a real journald**, not just unit tests: piped a small throwaway program's output through `systemd-cat --level-prefix=true` (mirroring the unit's `SyslogLevelPrefix=yes`) and confirmed via `journalctl --output=json` that each entry's `PRIORITY` field exactly matches (7/6/4/3 for debug/info/warn/error) with the prefix correctly stripped from the message, and via the pager's raw ANSI escapes that journalctl renders DEBUG dim grey, INFO plain, WARN bold yellow, and **ERROR bold red** - exactly the behaviour requested. `make lint`/`make test` clean.
danny closed this issue 2026-07-09 21:46:51 +01:00
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
keyop-go/nfq_forwarder#37
No description provided.