From 4350dacea7e47cdbd9850fa89874f80a1985d576 Mon Sep 17 00:00:00 2001
From: Martin Mares <mj@ucw.cz>
Date: Sun, 22 Sep 2024 12:32:34 +0200
Subject: [PATCH] E-mail administrator when a background action crashes

Closes #109.
---
 owl/__init__.py | 23 +++++++++++++++--------
 1 file changed, 15 insertions(+), 8 deletions(-)

diff --git a/owl/__init__.py b/owl/__init__.py
index 0695db4..b604a09 100644
--- a/owl/__init__.py
+++ b/owl/__init__.py
@@ -8,7 +8,9 @@ import logging
 import os
 from sqlalchemy import select
 from sqlalchemy.orm import joinedload
+import sys
 import tempfile
+from typing import Callable
 import werkzeug.exceptions
 
 import owl.assets as assets
@@ -291,19 +293,26 @@ try:
     # asynchronous evaluation. Each mule is notified by signals, but it wakes
     # up and scans its queue occasionally in case a signal was lost.
 
+    def background_action(handler: Callable[[], None]) -> None:
+        try:
+            with app.app_context():
+                handler()
+        except Exception as e:
+            exc_info = sys.exc_info()
+            app.logger.error('Error when processing background action: %s', e, exc_info=exc_info)
+            owl.error_mail.log_exception(app, 'background', {}, exc_info)
+
     # Mule 1
 
     @timer(300, target='mule1')
     def mule1_timer(signum):
         app.logger.debug('Mule 1: Received timer tick')
-        with app.app_context():
-            owl.notify.send_notify()
+        background_action(owl.notify.send_notify)
 
     @signal(42, target='mule1')
     def mule1_signal(signum):
         app.logger.debug('Mule 1: Received signal')
-        with app.app_context():
-            owl.notify.send_notify()
+        background_action(owl.notify.send_notify)
 
     def wake_up_notify_mule():
         app.logger.debug('Mule 1: Sending wakeup signal')
@@ -314,14 +323,12 @@ try:
     @timer(300, target='mule2')
     def mule2_timer(signum):
         app.logger.debug('Mule 2: Received timer tick')
-        with app.app_context():
-            owl.auto_eval.process_queue()
+        background_action(owl.auto_eval.process_queue)
 
     @signal(43, target='mule2')
     def mule2_signal(signum):
         app.logger.debug('Mule 2: Received signal')
-        with app.app_context():
-            owl.auto_eval.process_queue()
+        background_action(owl.auto_eval.process_queue)
 
     def wake_up_eval_mule():
         app.logger.debug('Mule 2: Sending wakeup signal')
-- 
GitLab