Skip to content

Commit

Permalink
feat(bm_monitor.py): add logging and signal handling for improved mon…
Browse files Browse the repository at this point in the history
…itoring and graceful shutdown

Introduce logging to replace print statements, providing better control over log levels and output formatting. This change enhances the ability to monitor application behavior and debug issues. Add signal handling to ensure the application shuts down gracefully on receiving SIGINT or SIGTERM, which helps in resource management and prevents abrupt disconnections. These improvements aim to enhance the maintainability and reliability of the application.
  • Loading branch information
mauvehed committed Dec 22, 2024
2 parents ba3640b + 8b2f0bf commit 643cc94
Showing 1 changed file with 64 additions and 12 deletions.
76 changes: 64 additions & 12 deletions bm_monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@
import socketio
import http.client, urllib
from zoneinfo import ZoneInfo
import logging
import signal
import sys

# Configure logging
logging.basicConfig(
level=logging.DEBUG if cfg.verbose else logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
handlers=[
logging.StreamHandler() # Ensure logs are sent to stdout
]
)

# library only needed if Discord is configured in config.py
if cfg.discord:
Expand All @@ -37,9 +49,40 @@
last_TG_activity = {}
last_OM_activity = {}

logging.debug("Configuration loaded:")
logging.debug(f"pushover_token: {cfg.pushover_token}")
logging.debug(f"pushover_user: {cfg.pushover_user}")
logging.debug(f"discord_wh_url: {cfg.discord_wh_url}")
logging.debug(f"telegram_api_id: {cfg.telegram_api_id}")
logging.debug(f"telegram_api_hash: {cfg.telegram_api_hash}")
logging.debug(f"talkgroups: {cfg.talkgroups}")
logging.debug(f"callsigns: {cfg.callsigns}")
logging.debug(f"noisy_calls: {cfg.noisy_calls}")

#############################
##### Define Functions

# Handle graceful shutdown on Ctrl+C or SIGTERM
def signal_handler(sig, frame):
"""Handles system signal interrupts for graceful application shutdown.
This function is designed to intercept system signals and perform a clean disconnection from the socket connection before terminating the application. It ensures that resources are properly released and the application exits smoothly.
Args:
sig (int): The signal number received.
frame (frame): The current stack frame.
Returns:
None
"""
logging.info("Shutting down gracefully...")
sio.disconnect()
sys.exit(0)

signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)

# Send push notification via Pushover. Disabled if not configured in config.py
def push_pushover(msg):
"""Sends a notification to a Discord channel or thread via webhook.
Expand Down Expand Up @@ -70,10 +113,14 @@ def push_discord(wh_url, msg, thread_id=None):
:param msg: Message content
:param thread_id: Optional thread ID for posting to a specific thread
"""
if thread_id:
wh_url = f"{wh_url}?thread_id={thread_id}"
webhook = DiscordWebhook(url=wh_url, content=msg)
response = webhook.execute()
try:
if thread_id:
wh_url = f"{wh_url}?thread_id={thread_id}"
webhook = DiscordWebhook(url=wh_url, content=msg)
response = webhook.execute()
logging.info("Discord notification sent.")
except Exception as e:
logging.error(f"Failed to send Discord notification: {e}")

# Send pager notification via DAPNET. Disabled if not configured in config.py
def push_dapnet(msg):
Expand Down Expand Up @@ -124,15 +171,15 @@ def construct_message(c):
else:
out += str(tg) + ' at '
out += time + ' (' + str(duration) + ' seconds) US/Central'
# finally return the text message
logging.debug(f"Constructed message: {out}")
return out

#############################
##### Define SocketIO Callback Functions

@sio.event
def connect():
print('connection established')
logging.info('Connection established with Brandmeister network.')

@sio.on("mqtt")
def on_mqtt(data):
Expand All @@ -158,15 +205,21 @@ def on_mqtt(data):
now = int(time.time())

if cfg.verbose and callsign in cfg.noisy_calls:
print("ignored noisy ham " + callsign)
logging.info(f"Ignored noisy ham {callsign}")

elif event == 'Session-Stop' and callsign != '':
if cfg.verbose:
if str(tg) in map(str, cfg.talkgroups):
logging.debug(f"Processing event: Event={event}, Callsign={callsign}, Talkgroup={tg}")
if callsign in cfg.callsigns:
if callsign not in last_OM_activity:
logging.debug(f"First activity recorded for {callsign}")
last_OM_activity[callsign] = 9999999
inactivity = now - last_OM_activity[callsign]
if callsign not in last_OM_activity or inactivity >= cfg.min_silence:
logging.debug(f"Inactivity for {callsign}: {inactivity} seconds")
if inactivity >= cfg.min_silence:
if tg in cfg.talkgroups and stop_time > 0:
logging.debug(f"Activity matches monitored talkgroups: {tg}")
last_TG_activity[tg] = now
last_OM_activity[callsign] = now
notify = True
Expand All @@ -179,7 +232,7 @@ def on_mqtt(data):
if tg not in last_TG_activity or inactivity >= cfg.min_silence:
notify = True
elif cfg.verbose:
print("ignored activity in TG " + str(tg) + " from " + callsign + ": last action " + str(inactivity) + " seconds ago.")
logging.info(f"Ignored activity in TG {tg} from {callsign}: last action {inactivity} seconds ago.")
last_TG_activity[tg] = now

if notify:
Expand All @@ -193,12 +246,11 @@ def on_mqtt(data):
thread_id = cfg.thread_map.get(str(tg)) # Fetch thread ID for the talkgroup
push_discord(cfg.discord_wh_url, construct_message(call), thread_id=thread_id)
if cfg.verbose:
#print("TalkerAlias: " + talkeralias)
print("Discord message " + construct_message(call) + " sent to thread " + str(thread_id) + " for TG " + str(tg))
logging.info(f"Discord message {construct_message(call)} sent to thread {thread_id} for TG {tg}")

@sio.event
def disconnect():
print('disconnected from server')
logging.warning('Disconnected from Brandmeister network.')

#############################
##### Main Program
Expand Down

0 comments on commit 643cc94

Please sign in to comment.