The sooner you know about an error that your Python web application throws in production, the faster you can solve it. This post will show you how to send custom exception reporting emails with Twilio SendGrid for Python-built Flask web applications.
You will need the following software to work through this tutorial:
- Python 3.6 or newer. If your operating system does not provide a Python interpreter, you can go to python.org to download an installer.
- A Twilio SendGrid account. This article will walk through setting that up if you don’t already have an account. A free SendGrid account allows you to send up to 100 emails per day forever.
Using Twilio SendGrid
We start by setting up SendGrid for programmatic access. If you already have an account, skip to the “Getting your API key” section.
Create a Twilio SendGrid account by:
- Heading over to Twilio SendGrid’s sign-up page.
- Create a username, password, and email address.
- Proceed with the remainder of the form and then click “Create Account.”
There will be another prompt for a bit more information and then you’ll need to finish the verification process. This multi-step verification process helps us keep out malicious spammers while serving legitimate developers like yourself.
You will receive an email at the address you used to sign up to verify your account. Navigate to the SendGrid dashboard when you have finished verification.
Obtaining an API key
We need an API key from Twilio SendGrid so that we can authenticate and make API requests.
Navigate to the dashboard and click “Settings.” Then click on “API Keys.”
Click the “Create API Key” button and name the API Key “Flask Error Reports.” Select “Restricted Access.”
Next, click on the “Mail Send” dropdown. Enable the “Mail Send” option by using the slider.
Scroll to the bottom and click the “Create & View” button. Copy the key and then paste it somewhere secure because we’ll be using it in our application.
Create a Python development environment
Now we need to set up our Python environment before we write the code. We’ll make a new directory for our project and create a virtual environment under it. We then install the Python packages that we need within the virtual environment.
If you are using a Unix or Mac OS system, open a terminal and enter the following commands to do the tasks described above:
For those of you following the tutorial on Windows, enter the following commands in a command prompt window:
The last command uses
pip, the Python package installer, to install the two packages that we are going to use in this project, which are:
- Flask is a microframework for web applications.
- Flask can optionally use python-dotenv for managing environment variables, so we are also installing this package.
Note: The SendGrid library is the official Twilio SendGrid Python API client.
Setting up the Flask application
We can now start writing the code for our basic Flask application. Navigate into the flask-error-alerts directory in the previous step and start your editor of choice. We are going to create two files:
The first file is called our “dot-env” file. We will place our SendGrid API key from the previous step here, as well as other configuration values for our Flask application. The second file is where our Flask application will be located. Let’s make some changes to these files and test our Flask application.
Edit the .env file to contain:
Edit app.py to contain:
Now we can check the application runs as expected. Open a terminal to make sure our virtualenv is active and then we’ll run the Flask app with the built-in development server:
Next, let’s navigate our browser to localhost:5000/ and check that we see the “Helloworld!” text.
You’re good to go if you see a screen like the one in the screenshot above.
Configuring alerts with SendGrid
Creating an unhandled exception
Now we need to configure the Flask application to send email alerts when there are unhandled exceptions. The first thing we are going to do is modify our
app.py to throw an unhandled exception.
Now that we have created a situation where an unhandled exception occurs, let’s restart the application and observe the current behavior. Use “Ctrl+C” to stop the Flask application, and let’s start it again:
Now, when we navigate to localhost:5000/, we are getting a 500 internal server–uh oh! But this is what we expected–the same will happen on an application that crashes due to a bug.
Hooking into Flask unhandled exceptions
We need to add some code that will allow us to hook into Flask’s built-in exception handler so the application will send off an email in response to an unhandled exception. Once we’ve done that, we will be able to use SendGrid to send an email about the exception. Fire up that text editor and let’s add this code to app.py:
Flask does have built-in error handlers (read more about that here). We use the
@app.errorhandler decorator to register a function with the Flask application. It’s quite similar to the
@app.route decorator except that it allows us to register a function that gets executed when particular types of errors occur in our application. In this case, we are paying attention to the
InternalServerError exception. This is the exception that Flask and it’s underlying utility library Werkzeug raise whenever an unhandled exception occurs.
Note: We are using Python’s built-in traceback module to retrieve the traceback information. The variable we created called
error_tb is a string that contains the traceback. This traceback is exactly the same as what we see in the terminal when the unhandled exception occurs in the Flask application. We will pass this information into our SendGrid email in the next section.
We also added a call to the
app.finalize_request method. This maintains the default behaviour of our Flask application:when an unhandled exception occurs, we will still return the internal server error response to the browser.
Sending the alert email with SendGrid
We’re now at the point where we can set up the code to send the alert email. So let’s do that by updating app.py to contain the following:
We have added a function called
create_message which sets up a SendMail
Mail object. This object pulls in the
FROM_EMAIL and the
TO_EMAIL from the .env file. With that said we need to add the following lines into our .env file:
Because we are using the .env file to load values, we also had to add a call to the dotenv module’s
load_dotenv function. This ensures that when our code runs, the required environment variables will be available in our application code.
Near the top we have created an instance of the SendGrid API client:
This client automatically looks for an environment variable named
SENDGRID_API_KEY and uses it to authenticate against the SendGrid servers. As with the other variables in our .env file, the
load_dotenv call ensures that the variable is imported into the environment of the process.
The last noteworthy addition in this section is the SendGrid API call:
All that’s happening in that code block is that we attempt to send the email using SendGrid but if an exception occurs, we just print out what the error was. In a production application, you might want to retry this operation because the most likely source of error here is that the SendGrid service is temporarily unavailable.
Testing the alerts
Once you’ve made the changes to your code, let’s test it out! So the expectation here is that we visit our Flask application at localhost:5000/ we are going to:
- Receive a 500 internal server error in the browser.
- Receive an email alert that contains the traceback.
To test that we need to restart the Flask application so that our changes take effect. So if you have the Flask development server running, stop that with “Ctrl+C” and then restart it with:
Now let’s navigate to localhost:5000/ and receive the 500 internal server error. A minute or two later you should receive an email that looks like the following:
There you have it! We have built out a pretty handy alerting system with a relatively small amount of code. Our Flask application will now send email alerts whenever unhandled exceptions occur.