feat: eventlog writes to dedicated events journal; init/backfill/close wiring

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-10 15:03:48 -04:00
co-authored by Claude Fable 5
parent 16c2922bea
commit a3f303e6ba
4 changed files with 82 additions and 3 deletions
+15 -2
View File
@@ -160,11 +160,18 @@ async def _run_async(config, config_path=None):
from . import threshold as threshold_mod
notify_mod.setup(config, loop=loop)
# Initialize message journal
msg_journal = journal_mod.get_journal(config)
await msg_journal.initialize()
# Initialize events journal (human-readable event log for the /log page)
events_journal = journal_mod.get_events_journal(config)
await events_journal.initialize()
if data.msgs:
# One-time seed on upgrade: only writes when the journal file is empty
await events_journal.backfill(data.msgs)
# Initialize threshold checker
threshold_checker = threshold_mod.ThresholdChecker(
config=config,
@@ -379,6 +386,12 @@ async def _run_async(config, config_path=None):
except Exception as e:
logger.warning("Error closing message journal: %s", e)
# Close events journal
try:
await events_journal.close()
except Exception as e:
logger.warning("Error closing events journal: %s", e)
# Signal DNS worker to exit and await it
try:
if "dns_task" in locals() and dns_task:
+19 -1
View File
@@ -33,6 +33,7 @@ msg_to_websockets = ws_mod.broadcast
# Module-level state set via setup()
_config: dict = {}
_loop: Optional[asyncio.AbstractEventLoop] = None
# Tracks which channels fired a WARNING/CRITICAL per host.
# {host_name: set of channel_names} — used to route RECOVER to the same channels.
@@ -70,8 +71,10 @@ class Notification:
def setup(cfg: dict, loop: Optional[asyncio.AbstractEventLoop] = None):
"""Initialize notifier from configuration dict."""
global _config
global _config, _loop
_config = dict(cfg)
if loop is not None:
_loop = loop
def reload_config(cfg: dict):
@@ -131,6 +134,21 @@ def eventlog(host, lvl, m, service=None):
except Exception as e:
logger.warning("failed to write to logfile: %s", e)
msg_to_websockets("message", msg)
_journal_event(msg)
def _journal_event(msg: dict):
"""Schedule an async write of *msg* to the events journal (no-op without a loop)."""
if _loop is None:
return
from . import journal as journal_mod
ej = journal_mod.get_events_journal()
if not ej.enabled:
return
try:
asyncio.run_coroutine_threadsafe(ej.log_event(msg), _loop)
except Exception as e:
logger.warning("failed to schedule events journal write: %s", e)
# ---------------------------------------------------------------------------