Improve error handling
* Report errors to admin by e-mail * Return details of error in JSON for API calls * Show error details page for non-API calls
This commit is contained in:
parent
db7b13b706
commit
7a3cb474ac
46
matcher/error_mail.py
Normal file
46
matcher/error_mail.py
Normal file
|
@ -0,0 +1,46 @@
|
|||
import logging
|
||||
from logging.handlers import SMTPHandler
|
||||
from logging import Formatter
|
||||
from flask import request
|
||||
|
||||
PROJECT = 'osm-wikidata'
|
||||
|
||||
class MatcherSMTPHandler(SMTPHandler):
|
||||
def getSubject(self, record): # noqa: N802
|
||||
return (f'{PROJECT} error: {record.exc_info[0].__name__}'
|
||||
if (record.exc_info and record.exc_info[0])
|
||||
else f'{PROJECT} error: {record.pathname}:{record.lineno:d}')
|
||||
|
||||
|
||||
class RequestFormatter(Formatter):
|
||||
def format(self, record):
|
||||
record.request = request
|
||||
return super().format(record)
|
||||
|
||||
|
||||
def setup_error_mail(app):
|
||||
if not app.config.get('ERROR_MAIL'):
|
||||
return
|
||||
formatter = RequestFormatter('''
|
||||
Message type: {levelname}
|
||||
Location: {pathname:s}:{lineno:d}
|
||||
Module: {module:s}
|
||||
Function: {funcName:s}
|
||||
Time: {asctime:s}
|
||||
GET args: {request.args!r}
|
||||
URL: {request.url}
|
||||
|
||||
Message:
|
||||
|
||||
{message:s}
|
||||
''', style='{')
|
||||
|
||||
mail_handler = MatcherSMTPHandler(app.config['SMTP_HOST'],
|
||||
app.config['MAIL_FROM'],
|
||||
app.config['ADMINS'],
|
||||
app.name + ' error')
|
||||
mail_handler.setFormatter(formatter)
|
||||
|
||||
mail_handler.setLevel(logging.ERROR)
|
||||
app.logger.propagate = True
|
||||
app.logger.addHandler(mail_handler)
|
33
web_view.py
33
web_view.py
|
@ -5,12 +5,14 @@ from flask import (Flask, render_template, request, jsonify, redirect, url_for,
|
|||
from sqlalchemy import func
|
||||
from sqlalchemy.sql.expression import update
|
||||
from matcher import (nominatim, model, database, commons, wikidata, wikidata_api,
|
||||
osm_oauth, edit, mail, api)
|
||||
osm_oauth, edit, mail, api, error_mail)
|
||||
from werkzeug.debug.tbtools import get_current_traceback
|
||||
from matcher.data import property_map
|
||||
from time import time, sleep
|
||||
from requests_oauthlib import OAuth1Session
|
||||
from lxml import etree
|
||||
from sqlalchemy.orm.attributes import flag_modified
|
||||
import werkzeug.exceptions
|
||||
import inspect
|
||||
import flask_login
|
||||
import requests
|
||||
import json
|
||||
|
@ -24,6 +26,7 @@ re_point = re.compile(r'^POINT\((.+) (.+)\)$')
|
|||
app = Flask(__name__)
|
||||
app.debug = True
|
||||
app.config.from_object('config.default')
|
||||
error_mail.setup_error_mail(app)
|
||||
|
||||
login_manager = flask_login.LoginManager(app)
|
||||
login_manager.login_view = 'login_route'
|
||||
|
@ -47,6 +50,32 @@ def shutdown_session(exception=None):
|
|||
def global_user():
|
||||
g.user = flask_login.current_user._get_current_object()
|
||||
|
||||
def dict_repr_values(d):
|
||||
return {key: repr(value) for key, value in d.items()}
|
||||
|
||||
|
||||
@app.errorhandler(werkzeug.exceptions.InternalServerError)
|
||||
def exception_handler(e):
|
||||
tb = get_current_traceback()
|
||||
last_frame = next(frame for frame in reversed(tb.frames) if not frame.is_library)
|
||||
last_frame_args = inspect.getargs(last_frame.code)
|
||||
if request.path.startswith("/api/"):
|
||||
return cors_jsonify({
|
||||
"success": False,
|
||||
"error": tb.exception,
|
||||
"traceback": tb.plaintext,
|
||||
"locals": dict_repr_values(last_frame.locals),
|
||||
"last_function": {
|
||||
"name": tb.frames[-1].function_name,
|
||||
"args": repr(last_frame_args),
|
||||
},
|
||||
}), 500
|
||||
|
||||
return render_template('show_error.html',
|
||||
tb=tb,
|
||||
last_frame=last_frame,
|
||||
last_frame_args=last_frame_args), 500
|
||||
|
||||
def cors_jsonify(*args, **kwargs):
|
||||
response = jsonify(*args, **kwargs)
|
||||
response.headers["Access-Control-Allow-Origin"] = "*"
|
||||
|
|
Loading…
Reference in a new issue