From ad7178ebcbeb75729349c97d287949c8e4c0e295 Mon Sep 17 00:00:00 2001 From: Andreas Wrede Date: Sun, 29 Mar 2026 20:29:33 -0400 Subject: [PATCH] Move threshhold to server, move eventlog to notify --- .hb.yaml | 2 +- .hb.yaml.swp | Bin 12288 -> 0 bytes hbd/server/http.py | 2 +- hbd/server/main.py | 53 +++++++--------------------- hbd/server/notify.py | 38 ++++++++++++++++++++ hbd/{client => server}/threshold.py | 5 ++- hbd/server/ws.py | 9 ++--- 7 files changed, 61 insertions(+), 48 deletions(-) delete mode 100644 .hb.yaml.swp rename hbd/{client => server}/threshold.py (99%) diff --git a/.hb.yaml b/.hb.yaml index 6196c35..f4cb140 100644 --- a/.hb.yaml +++ b/.hb.yaml @@ -2,7 +2,7 @@ hb_port: 50003 hbd_host: '' #logfile: "/home/andreas/public_html/messages/andreas" -logfile: "/home/andreas/logs/heartbeat/andreas" +logfile: "/home/andreas/logs/heartbeat/heartbeat.log" #logfile: "/Users/andreas/public_html/messages/andreas" logfmt: "msg" grace: 40 diff --git a/.hb.yaml.swp b/.hb.yaml.swp deleted file mode 100644 index 4cc71eb88f5251c96153ca17629aa2e108834d09..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2&5smC7{&`dhzLUTpoUA)a{wYt&+NzS>_ie}7eqcbe64^=CNv4{152Cc=hC!iyEUIhzCzbxOyh>;#Hzg^>p{n8nYyLk)%5LWvZ**dg`sGt7=22 zUgPbz&e7$1li=A$$ajC<4e#&YK~{b~LRjKU&Xj4{_8EtX=5pJRyz!$#&>e)@4LFm! z$C-BPL9aexar9^b8(|%=4m_R%neHEL?^&OpYc`BP%{f3{d}00Zv}`ZyfOWt+U>&dy zSO=^F)&c8)b>N9~K&Ly%$H@GtBKwu%cgH_|i&y(&9k32q2do3u0qcNuz&c%bG}0P_j?>={D7*@@)w|NrH`|Nq!a$Zy~m@HrR&7aRaDfnDI?^MpJA_rYCo z2Yd=X0Tb{JSOqQc3V0fPv4;>1&Vv2m!EQqCfnUMb;5L|mYv47oAM63U!NcbW`3k%b z-UF-PFxUt7f}eH~atoXRJHe0767mDM1HJ^e!7U)bb#NXW1BbxNU?=zsKKu^ufp3BF z<$GWs>wtB@I$#~J4p;~NFAmV^@non&k3}82+^9E5RZquECSfw@(DqDq%B9B>-6`VS zX(7dpM5u^FnRh=_!)`2+PzzZG&Wy5H$yFoF7gaOrF^`3uY$LOoY$ekyWV|%%&PbWp zDjgk2t^yJHNR@1eOeQSq#_UG7$Gl;dst#>7Ha#dxHM*9?Jua!}(;|*)L5G~DTQpR7 zlN*7B_To~ju~nx=&#@cSM9@RcM&sOZ>hnI!B7L}$=!X%y$6P5k;Hq9^HFKj|-ks($I?#0ouQr#h+bf}ZcaD-Al<*`#VpooTk7DZmb!lZ0kIdSoF_xu}| zPtjx4sk@ph?V2tgm%5uW9iY>{R=SyzZX~d9U9LSh;#%>6J^D$aq$3kq{N@v=DDEP2ZSOV;sJkidSW zA2`#Yg&a5^5|U;rP;vxqQxeF+$?Ht1TjZRr2B+rd*EcR)SiPp#Zl3NnF9#RHE4^hb zgg4L3#A`9+iAmF2T047nPM+VmdXh=bR|jX)t7~GlzYv~``wmG|mikQdZZ9<9Gv5{! zZrSP(Uy3vkO6O^3$uJS)#5qEpu@J+MHy5X6sWA~V#RL;5f&-?!AQDRDA#7rHabCna z8;K~ImO0Y(P0?iR8BP^b$7QH6M!7YV^pLd*2RKWds&;C7hrZfeXtvsO zbFJpma^C1nuYEh^*+}Ul1QwQ?i_N+Dg~irVyO5atr`}_%dMr*IK&578QBAmf-}H-? zRrzK!hN9D$X)h?uCHW=xk@*XP8?pn*JRD*R4T2M>A7i^Yu^M1Yo7en&bKG?-Z&jUC zY1WHEuN&w%a?7)?Y;y_~@E{3#Gv~!TOd>UN M=wd?S#+*a`1~>VGzW@LL diff --git a/hbd/server/http.py b/hbd/server/http.py index 60dac76..fb96828 100644 --- a/hbd/server/http.py +++ b/hbd/server/http.py @@ -299,7 +299,7 @@ async def start( active_alerts = threshold_checker.get_active_alerts(host.alert_states) else: # Fallback if no threshold checker - from hbd.client.threshold import AlertLevel + from hbd.server.threshold import AlertLevel active_alerts = [ state for state in host.alert_states.values() if state.level != AlertLevel.OK diff --git a/hbd/server/main.py b/hbd/server/main.py index 9c54ce7..e7424ed 100644 --- a/hbd/server/main.py +++ b/hbd/server/main.py @@ -13,40 +13,16 @@ from . import udp from . import hbdclass from . import ws as ws_mod +from . import notify as notify_mod logger = logging.getLogger(__name__) msg_to_websockets = ws_mod.broadcast +eventlog = notify_mod.log -logf = None lastfm = ["", "", ""] # shared runtime collections and helpers -msgs = [] - - -def initlog(logfile): - try: - return open(logfile, "a+") - except Exception as e: - import sys - - print("cannot open loffile %s, using STDERR: %s" % (logfile, e)) - return sys.stderr - - -def log(host, m, service=None): - ts = time.time() - s = f"{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(ts))} {host or ''} {m}" - msgs.append(s) - logger.info(s) - if logf: - try: - logf.write(s + "\n") - logf.flush() - except Exception as e: - logger.warning("failed to write to logfile: %s", e) - msg_to_websockets("message", s) - +msgs = notify_mod.msgs def cleanup_function(config): """This function will be executed upon program exit.""" @@ -84,7 +60,7 @@ async def _run_async(config): from . import notify as notify_mod from . import monitor as monitor_mod from . import journal as journal_mod - from ..client import threshold as threshold_mod + from . import threshold as threshold_mod notify_mod.setup(config) @@ -125,7 +101,7 @@ async def _run_async(config): ctx = dict( config=config, hbdclass=hbdclass, - log=log, + log=eventlog, pushmsg=pushmsg, msg_to_websockets=msg_to_websockets, msg_journal=msg_journal, @@ -149,7 +125,7 @@ async def _run_async(config): config=config, hbdclass=hbdclass, msgs_getter=lambda: msgs, - log=log, + log=eventlog, pushmsg=pushmsg, msg_to_websockets=msg_to_websockets, threshold_checker=threshold_checker, @@ -172,7 +148,7 @@ async def _run_async(config): dns_task = None try: dns_task = dns_mod.start_dns_worker( - hbdclass, config, log=log, pushmsg=pushmsg, loop=loop + hbdclass, config, log=eventlog, pushmsg=pushmsg, loop=loop ) logger.info("dns update worker started") except Exception as e: @@ -211,7 +187,7 @@ async def _run_async(config): for h in sorted(hbdclass.Host.hosts) ], get_msgs=lambda: msgs, - verbose=config.get("verbose", False), + config=config, ) ) logger.info("WebSocket task started") @@ -224,7 +200,7 @@ async def _run_async(config): monitor_mod.start( config=config, hbdclass=hbdclass, - log=log, + log=eventlog, pushmsg=pushmsg, msg_to_websockets=msg_to_websockets, ) @@ -347,7 +323,6 @@ def run(config): Manually manages the event loop to ensure clean shutdown. """ - global logf import os logging.basicConfig( @@ -355,8 +330,8 @@ def run(config): ) load_pickled_hosts(config, hbdclass) - logf = initlog(logfile=config.get("logfile", "messages.log")) - log(None, f"hbd version {__version__} starting up") + notify_mod.initlog(logfile=config.get("logfile", "messages.log")) + eventlog(None, f"hbd version {__version__} starting up") # Create and set the event loop manually loop = asyncio.new_event_loop() @@ -371,11 +346,7 @@ def run(config): finally: cleanup_function(config) logger.info("hbd shutdown complete") - if logf and logf != sys.stderr: - try: - logf.close() - except Exception: - pass + notify_mod.closelog() # Explicitly close the loop try: # Cancel all remaining tasks diff --git a/hbd/server/notify.py b/hbd/server/notify.py index 5ea95ec..d215c06 100644 --- a/hbd/server/notify.py +++ b/hbd/server/notify.py @@ -7,13 +7,50 @@ import urllib.parse import subprocess import smtplib import time +import sys +from . import ws as ws_mod DEFAULT_PUSHPROVIDERS = ["all", "pushover", "mattermost", "signal"] +msg_to_websockets = ws_mod.broadcast # module-level configuration set via setup() _config = {} logger = logging.getLogger(__name__) +msgs = [] +logf = None + +def initlog(logfile): + global logf + try: + logf = open(logfile, "a+") + return logf + except Exception as e: + import sys + + print("cannot open logfile %s, using STDERR: %s" % (logfile, e)) + return sys.stderr + +def closelog(): + global logf + if logf and logf != sys.stderr: + try: + logf.close() + except Exception: + pass + +def log(host, m, service=None): + ts = time.time() + s = f"{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(ts))} {host or ''} {m}" + msgs.append(s) + logger.info(s) + if logf: + try: + logf.write(s + "\n") + logf.flush() + except Exception as e: + logger.warning("failed to write to logfile: %s", e) + msg_to_websockets("message", s) def setup(cfg: dict): """Initialize notifier defaults from a configuration dict.""" @@ -160,6 +197,7 @@ def pushmsg(cfg: dict, msg: str, debug: int = 0): Returns a dict of results per provider. """ + results = {} p = cfg.get("pushsrv", "pushover") if p in ("all", "pushover"): diff --git a/hbd/client/threshold.py b/hbd/server/threshold.py similarity index 99% rename from hbd/client/threshold.py rename to hbd/server/threshold.py index 7d1e345..f0d4e3f 100644 --- a/hbd/client/threshold.py +++ b/hbd/server/threshold.py @@ -13,9 +13,10 @@ import logging import time from enum import Enum from typing import Dict, Any, Optional, Tuple, Callable +from . import notify as notify_mod logger = logging.getLogger(__name__) - +eventlog = notify_mod.log class AlertLevel(Enum): """Alert severity levels.""" @@ -505,6 +506,8 @@ class ThresholdChecker: )) except Exception as e: logger.debug(f"Failed to log threshold event to journal: {e}") + # Log to eventlog as well + eventlog(host_name, message, service="threshold") def _check_renotify( self, diff --git a/hbd/server/ws.py b/hbd/server/ws.py index 6ca373e..3ebac21 100644 --- a/hbd/server/ws.py +++ b/hbd/server/ws.py @@ -12,7 +12,7 @@ from typing import Callable, Iterable, Optional import websockets logger = logging.getLogger(__name__) - +logger.setLevel(logging.INFO) _connections = set() _loop: Optional[asyncio.AbstractEventLoop] = None _get_hosts: Optional[Callable[[], Iterable]] = None @@ -78,7 +78,7 @@ async def start( ssl_context=None, get_hosts: Optional[Callable] = None, get_msgs: Optional[Callable] = None, - verbose: bool = False, + config: dict = {}, ): """Start WebSocket servers and block until cancelled. @@ -90,12 +90,13 @@ async def start( _loop = asyncio.get_running_loop() _get_hosts = get_hosts _get_msgs = get_msgs - _verbose = verbose + _verbose = config.get("verbose", False), + _debug = config.get("debug", False), servers = [] # plain WebSocket websockets_logger = logging.getLogger("websockets.server") - websockets_logger.setLevel(logging.DEBUG if verbose else logging.INFO) + websockets_logger.setLevel(logging.DEBUG if _debug > 2 else logging.INFO) # regular WebSocket ws_server = websockets.serve(_handler, host, ws_port) # , subprotocols=["hbd"]) servers.append(ws_server)