Add hooks to send email when uncaught exception occurs

This commit is contained in:
Lance Edgar 2023-09-29 11:46:34 -05:00
parent d83204495b
commit fc58594699
2 changed files with 74 additions and 4 deletions

View file

@ -25,7 +25,13 @@ WuttaPOS app
""" """
import json import json
import logging
import os import os
import socket
import sys
import threading
from collections import OrderedDict
from traceback import format_exception
from rattail import app as base from rattail import app as base
from rattail.config import make_config from rattail.config import make_config
@ -37,6 +43,9 @@ import wuttapos
from wuttapos.util import get_pos_batch_handler from wuttapos.util import get_pos_batch_handler
log = logging.getLogger(__name__)
class WuttaAppHandler(base.AppHandler): class WuttaAppHandler(base.AppHandler):
""" """
Custom app handler for WuttaPOS Custom app handler for WuttaPOS
@ -55,6 +64,61 @@ def main(page: ft.Page):
config = make_config() config = make_config()
app = config.get_app() app = config.get_app()
model = app.model model = app.model
handler = get_pos_batch_handler(config)
# nb. as of python 3.10 the original hook is accessible, we needn't save the ref
# cf. https://docs.python.org/3/library/threading.html#threading.__excepthook__
orig_thread_hook = threading.excepthook
hostname = socket.gethostname()
email_context = OrderedDict([
('hostname', hostname),
('ipaddress', socket.gethostbyname(hostname)),
('terminal', handler.get_terminal_id() or '??'),
])
def send_error_email(traceback):
extra_context = OrderedDict(email_context)
try:
uuid = page.session.get('user_uuid')
if uuid:
session = app.make_session()
user = session.get(model.User, uuid)
extra_context['username'] = user.username
batch = handler.get_current_batch(user, create=False)
if batch:
extra_context['batchid'] = batch.id_str
session.close()
else:
extra_context['username'] = 'n/a'
app.send_email('uncaught_exception', {
'extra_context': extra_context,
'traceback': traceback,
})
except:
log.exception("failed to send error email")
def sys_exc_hook(exc_type, exc_value, exc_traceback):
traceback = ''.join(format_exception(exc_type, exc_value, exc_traceback)).strip()
send_error_email(traceback)
sys.__excepthook__(exc_type, exc_value, exc_traceback)
def thread_exc_hook(args):
traceback = ''.join(format_exception(args.exc_type, args.exc_value, args.exc_traceback)).strip()
send_error_email(traceback)
orig_thread_hook(args)
# custom exception hook for main process
sys.excepthook = sys_exc_hook
# custom exception hook for threads (requires python 3.8)
# cf. https://docs.python.org/3/library/threading.html#threading.excepthook
v = sys.version_info
if v.major >= 3 and v.minor >= 8:
threading.excepthook = thread_exc_hook
page.title = f"WuttaPOS v{wuttapos.__version__}" page.title = f"WuttaPOS v{wuttapos.__version__}"
page.window_full_screen = True page.window_full_screen = True
@ -137,7 +201,6 @@ def main(page: ft.Page):
if user: if user:
page.session.set('user_uuid', uuid) page.session.set('user_uuid', uuid)
page.session.set('user_display', str(user)) page.session.set('user_display', str(user))
handler = get_pos_batch_handler(config)
batch = handler.get_current_batch(user, create=False) batch = handler.get_current_batch(user, create=False)
if batch: if batch:
page.session.set('txn_display', batch.id_str) page.session.set('txn_display', batch.id_str)

View file

@ -657,7 +657,7 @@ class POSView(WuttaView):
meta_button_width = meta_button_height * 2 meta_button_width = meta_button_height * 2
meta_font_size = tenkey_font_size meta_font_size = tenkey_font_size
def meta_button(text, on_click=None, bgcolor='blue'): def meta_button(text, on_click=None, bgcolor='blue', data=None):
return ft.Container(content=ft.Text(text, size=meta_font_size, return ft.Container(content=ft.Text(text, size=meta_font_size,
weight=ft.FontWeight.BOLD), weight=ft.FontWeight.BOLD),
height=meta_button_height, height=meta_button_height,
@ -666,7 +666,8 @@ class POSView(WuttaView):
alignment=ft.alignment.center, alignment=ft.alignment.center,
border=ft.border.all(1, 'black'), border=ft.border.all(1, 'black'),
border_radius=ft.border_radius.all(5), border_radius=ft.border_radius.all(5),
bgcolor=bgcolor) bgcolor=bgcolor,
data=data)
self.meta_menu = ft.Container( self.meta_menu = ft.Container(
content=ft.Column( content=ft.Column(
@ -695,7 +696,8 @@ class POSView(WuttaView):
ft.Row( ft.Row(
[ [
meta_button("TODO", bgcolor='blue', on_click=self.not_supported), meta_button("TODO", bgcolor='blue', on_click=self.not_supported),
meta_button("TODO", bgcolor='blue', on_click=self.not_supported), meta_button("TODO", bgcolor='blue', on_click=self.not_supported,
data={'error': True}),
], ],
spacing=0, spacing=0,
), ),
@ -867,6 +869,11 @@ class POSView(WuttaView):
self.page.update() self.page.update()
def not_supported(self, e=None, feature=None): def not_supported(self, e=None, feature=None):
# test error handler
if e.control.data and e.control.data.get('error'):
raise ValueError("NOT YET SUPPORTED")
text = "NOT YET SUPPORTED" text = "NOT YET SUPPORTED"
if not feature and e: if not feature and e:
feature = e.control.content.value feature = e.control.content.value