merge: testing → main (reconcile 2-week divergence)

This commit is contained in:
2026-04-28 18:36:00 -04:00
parent 499836c9e4
commit 862e4dbb31
1235 changed files with 160255 additions and 7996 deletions

View File

@@ -48,23 +48,67 @@ class Rfc5424Formatter(logging.Formatter):
msg = record.getMessage()
if record.exc_info:
msg += "\n" + self.formatException(record.exc_info)
app = getattr(record, "decnet_component", self._app)
return (
f"<{prival}>1 {ts} {self._hostname} {self._app}"
f"<{prival}>1 {ts} {self._hostname} {app}"
f" {os.getpid()} {record.name} - {msg}"
)
def _configure_logging(dev: bool) -> None:
"""Install the RFC 5424 handler on the root logger (idempotent)."""
"""Install RFC 5424 handlers on the root logger (idempotent).
Always adds a StreamHandler (stderr). Also adds a RotatingFileHandler
writing to DECNET_SYSTEM_LOGS (default: decnet.system.log in $PWD) so
all microservice daemons — which redirect stderr to /dev/null — still
produce readable logs. File handler is skipped under pytest.
"""
from decnet.logging.inode_aware_handler import InodeAwareRotatingFileHandler
root = logging.getLogger()
# Avoid adding duplicate handlers on re-import (e.g. during testing)
# Guard: if our StreamHandler is already installed, all handlers are set.
if any(isinstance(h, logging.StreamHandler) and isinstance(h.formatter, Rfc5424Formatter)
for h in root.handlers):
return
handler = logging.StreamHandler()
handler.setFormatter(Rfc5424Formatter())
fmt = Rfc5424Formatter()
root.setLevel(logging.DEBUG if dev else logging.INFO)
root.addHandler(handler)
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(fmt)
root.addHandler(stream_handler)
# Skip the file handler during pytest runs to avoid polluting the test cwd.
_in_pytest = any(k.startswith("PYTEST") for k in os.environ)
if not _in_pytest:
_log_path = os.environ.get("DECNET_SYSTEM_LOGS", "decnet.system.log")
# Never let file-handler attach failure kill the process. The
# stream handler above is already installed, so losing the file
# handler just means 'tail syslog / journalctl instead' — the
# daemon itself must keep running. This path trips most
# commonly under systemd with ProtectSystem=full + ProtectHome=
# read-only when an operator hasn't passed a writable
# DECNET_SYSTEM_LOGS yet.
try:
file_handler = InodeAwareRotatingFileHandler(
_log_path,
mode="a",
maxBytes=10 * 1024 * 1024, # 10 MB
backupCount=5,
encoding="utf-8",
)
file_handler.setFormatter(fmt)
root.addHandler(file_handler)
# Drop root ownership when invoked via sudo so non-root follow-up
# commands (e.g. `decnet api` after `sudo decnet deploy`) can append.
from decnet.privdrop import chown_to_invoking_user
chown_to_invoking_user(_log_path)
except OSError as exc:
logging.getLogger(__name__).warning(
"could not open %s (%s); continuing with stderr-only logging. "
"Set DECNET_SYSTEM_LOGS to a writable path to silence this.",
_log_path, exc,
)
_dev = os.environ.get("DECNET_DEVELOPER", "").lower() == "true"