log: send records natively to journald, keep stdout off-unit
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.
This commit is contained in:
parent
893747a0e4
commit
8cc7f90c98
12 changed files with 316 additions and 36 deletions
16
Cargo.toml
16
Cargo.toml
|
|
@ -14,6 +14,7 @@ members = [
|
|||
"hive-jobq",
|
||||
"hive-jobq-metrics",
|
||||
"hive-jobq-wire",
|
||||
"hive-log",
|
||||
"hive-matrix-mcp",
|
||||
"hive-metric",
|
||||
"hive-priv",
|
||||
|
|
@ -86,6 +87,7 @@ hive-agent-sock = { path = "hive-agent-sock" }
|
|||
hive-jobq = { path = "hive-jobq" }
|
||||
hive-jobq-metrics = { path = "hive-jobq-metrics" }
|
||||
hive-jobq-wire = { path = "hive-jobq-wire" }
|
||||
hive-log = { path = "hive-log" }
|
||||
hive-core-agent-sock = { path = "hive-core-agent-sock" }
|
||||
hive-claude = "0.1.1"
|
||||
hive-host-sock = { path = "hive-host-sock" }
|
||||
|
|
@ -147,6 +149,20 @@ tokio = { version = "1", features = [
|
|||
tokio-stream = { version = "0.1", features = ["sync"] }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
# The journald sink `hive-log` installs when the process runs under a
|
||||
# systemd unit. Tokio-maintained; its whole dependency set is `libc` +
|
||||
# `tracing-core` + `tracing-subscriber`, since it speaks the native journal
|
||||
# protocol over a `UnixDatagram` and links no C journal library.
|
||||
tracing-journald = "0.3.2"
|
||||
# `fs::fstat` — the safe wrapper `hive-log` compares a descriptor's device
|
||||
# and inode numbers against `$JOURNAL_STREAM` with. Already in the lock as
|
||||
# a transitive dependency; direct here so that comparison needs no
|
||||
# hand-written `unsafe libc::fstat` call. Only the `fs` API module is asked
|
||||
# for: rustix gates each one behind its own feature.
|
||||
rustix = { version = "1.1.4", default-features = false, features = [
|
||||
"std",
|
||||
"fs",
|
||||
] }
|
||||
reqwest = { version = "0.13", default-features = false, features = [
|
||||
# RFC 7662 introspection posts an urlencoded body; without this,
|
||||
# `.form()` does not exist and the alternative is percent-encoding a
|
||||
|
|
|
|||
Loading…
Reference in a new issue