| Filename | Latest commit message | Latest commit date |
|---|---|---|
A record written to stdout carries no priority, so journald files the whole stream at one level and the swarm log store shows `info` whatever level `tracing` gave it. Under a systemd unit the process's stdout already *is* the journal, so the fix is to speak the journal protocol directly and let each record carry its own severity. New `hive-log` crate holds the one sink chooser, called by `hive-c0re`, `hive-agent` and `swarm-controller`. It builds the same `EnvFilter` those binaries always built, then installs exactly one layer — never both, since a journald layer stacked on the `fmt` layer under a unit stores every record twice. The choice is an fstat compare, not a presence test: a child inherits `$JOURNAL_STREAM` even when its own stdout was redirected elsewhere, so the variable existing proves nothing. The crate parses `dev:inode` out of it and compares both numbers against an fstat of stdout, the descriptor the `fmt` layer writes to by default. No match, unset, or unparseable takes the `fmt` branch. A journald layer that fails to construct despite a match falls back to `fmt` and warns through it — a process must never fail to start because of its logger. |
||
| .. | ||
| src | ||
| Cargo.toml | ||
| README.md | ||
hive-log
One shared log-sink chooser for the hyperhive daemons: hive_log::init()
builds the EnvFilter every binary built before, then installs exactly
one sink.
Why one sink and not both
Under a systemd unit the process's stdout already is the journal.
Adding a journald layer on top of the fmt layer therefore stores every
record twice — once as the native journal entry, once as the text line
fmt wrote to a descriptor that leads to the same journal.
Writing to stdout also throws the severity away. A stdout line carries no
priority field, so journald files the whole stream at one level and the
swarm log store shows info whatever level tracing gave the record.
Logging natively carries the level across. That is the reason to prefer
journald where a journal exists, and the reason never to run both layers.
How the choice happens
systemd sets $JOURNAL_STREAM to the device and inode numbers of the
journal-connected descriptor, in decimal, separated by a colon.
Checking that the variable exists is not enough. A child process
inherits $JOURNAL_STREAM from its parent even when the parent redirected
that child's stdout somewhere else, so presence alone reports a journal
that the process does not actually write to. hive-log instead stats
stdout — the descriptor the fmt layer writes to by default, and the one
a journald layer would double up on — and compares both numbers against
the parsed pair.
A match installs the native journald layer alone. No match, an unset
variable, or a value that does not parse installs the fmt layer alone.
When the journald layer fails to build
tracing_journald::layer() validates the journal socket up front and
returns an io::Result, so a missing journal surfaces as an error rather
than a silent no-op. On that error hive-log installs the fmt layer and
emits one warning through it. A process must never fail to start because
of its logger, and a logger that drops every record in silence is worse
than a degraded one that says so.
Scope
The sink chooser and its tests. Nothing else belongs here: the crate exists so three binaries share one copy of this decision, not as a place for general utilities.