Flask Error/Exception Monitoring App¶
Quick start¶
Installation¶
To install Flask Error Monitor, open an interactive shell and run:
pip install flask-error-monitor
User Guide¶
Note
- It will mask all the variables which contain password and secret in their name.
- Recorded exceptions will be visible to http://example.com/dev/error
Using Flask Error Monitor as simple as plugging any flask extension. An instance of instance of AppErrorMonitor needs to be created and have to be configured with the correct data. Monitoring feature can be configured either using object based configuration or app-based configuration, the only important thing here is we should have all the required key configs in the app.config otherwise it will fail.
Recording exception/error¶
An error/exception can be recorded using decorator as well function call. - To record the error using decorator, decorate a function with track_exception - Where as to record error using function call use record_exception function.
All the data will be stored in the configured data store and these data will be available at /dev/error/ or at configure URL path.
Example setup¶
For object based configuration add settings.py
...
APP_ERROR_SEND_NOTIFICATION = True
APP_ERROR_RECIPIENT_EMAIL = ('example@example.com',)
APP_ERROR_SUBJECT_PREFIX = "Server Error"
APP_ERROR_EMAIL_SENDER = 'user@example.com'
app.py
from flask import Flask
from flask_mail import Mail
import settings
from flask_error_monitor import AppErrorMonitor
from flask_sqlalchemy import SQLAlchemy
...
app = Flask(__name__)
app.config.from_object(settings)
db = SQLAlchemy(app)
class MyMailer(Mail, NotificationMixin):
def notify(self, message, exception):
self.send(message)
mail = MyMailer(app=app)
error_monitor = AppErrorMonitor(app=app, db=db, notifier=mail)
....
....
# Record exception when 404 error code is raised
@app.errorhandler(403)
def error_403(e):
error_monitor.record_exception()
# any custom logic
# Record error using decorator
@app.errorhandler(500)
@error_monitor.track_exception
def error_500(e):
# some custom logic
....
Here, app, db and notifier parameters are optional. Alternatively, you could use the init_app() method.
If you start this application and navigate to http://localhost:5000/dev/error, you should see an empty page.
Configure at the end¶
Use error_monitor.init_app method to configure
error_monitor = AppErrorMonitor()
...
error_monitor.init_app(app=app, db=db, notifier=mail)
Config details¶
- Enable or disable notification sending feature
APP_ERROR_SEND_NOTIFICATION = False
- Email recipient list
APP_ERROR_RECIPIENT_EMAIL = None
- Email subject prefix to be used by email sender
APP_ERROR_SUBJECT_PREFIX = ""
- Mask value with following string
APP_ERROR_MASK_WITH = "**************"
- Masking rule
App can mask all the variables whose lower case name contains one of the configured string .. code:
APP_ERROR_MASKED_KEY_HAS = ("password", "secret")
- Above configuration will mask the variable names like
password secret PassWord THis_Is_SEcret
Note
Any variable names whose lower case string contains either password or secret
- Browse link in your service app
List of exceptions can be seen at /dev/error, but you can have other prefix as well due to some securities or other reasons.
APP_ERROR_URL_PREFIX = "/dev/error"
- Email address used to construct Message object
APP_ERROR_EMAIL_SENDER = "prod-issue@example.com"
Ticketing¶
Ticketing interface can be used to create tickets in the systems like Jira, Bugzilla etc, ticketing can be enabled using ticketing interface.
Using TicketingMixin¶
implement raise_ticket method of TicketingMixin interface
from flask_error_monitor import TicketingMixin
class Ticketing(TicketingMixin):
def raise_ticket(self, exception):
# Put your logic here
# create app as
app = Flask(__name__)
db = SQLAlchemy(app)
error_monitor = AppErrorMonitor(app=app, db=db, ticketing=Ticketing() )
db.create_all()
...
Custom Context¶
Having more and more context about failure always help in debugging, by default this app captures HTTP headers, URL parameters, any post data. More data can be included like data-center name, server details and any other, by default these details are not captured. Nonetheless these details can be captured using ContextBuilderMixin.
Using ContextBuilderMixin¶
implement get_context method of ContextBuilderMixin, default context builders capture request body, headers and URL parameters
class DefaultContextBuilder(ContextBuilderMixin):
"""
Default request builder, this records, form data, header and URL parameters and mask them if necessary
"""
def get_context(self, request, masking=None):
form = dict(request.form)
headers = dict(request.headers)
if masking:
for key in form:
masked, value = masking(key)
if masked:
form[key] = value
for key in headers:
masked, value = masking(key)
if masked:
headers[key] = value
request_data = str({
'headers': headers,
'args': dict(request.args),
'form': form
})
return request_data
Implementing custom context builder, in fact we can extend the default one to add more details as well
from flask_error_monitor.defaults import DefaultContextBuilder
class ContextBuilder(DefaultContextBuilder):
def get_context(self, request, masking=None):
context = super().get_context(request, masking=masking)
# add logic to update context
return context
from flask_error_monitor import ContextBuilderMixin
class ContextBuilder(TicketingMixin):
def get_context(self, request, masking=None):
# add logic here
This custom context builder can be supplied as parameter of AppErrorMonitor constructor.
...
app = Flask(__name__)
db = SQLAlchemy(app)
error_monitor = AppErrorMonitor(app=app, db=db, ticketing=Ticketing() )
db.create_all()
return app, db, error_monitor
...