|
| 1 | +title: How to Report Errors in Flask Web Apps with Sentry |
| 2 | +slug: report-errors-flask-web-apps-sentry |
| 3 | +meta: Learn how to use Sentry and the Flask integration to easily report errors in your Python-based web applications. |
| 4 | +category: post |
| 5 | +date: 2020-06-30 |
| 6 | +modified: 2020-07-02 |
| 7 | +newsletters: False |
| 8 | +headerimage: /img/headers/flask-sentry.jpg |
| 9 | +headeralt: Logos for the implementations used in this blog post. Copyright their respective owners. |
| 10 | + |
| 11 | + |
| 12 | +[Flask](/flask.html) web applications are highly customizable by developers |
| 13 | +thanks to the [framework](/web-frameworks.html)'s extension-based |
| 14 | +architecture, but that flexibility can sometimes lead to more errors |
| 15 | +when you run the application due to rough edges between the libraries. |
| 16 | + |
| 17 | +Reporting errors is crucial to running a well-functioning Flask web |
| 18 | +application, so this tutorial will guide you through adding a free, basic |
| 19 | +[Sentry](https://sentry.io) configuration to a fresh Flask project. |
| 20 | + |
| 21 | + |
| 22 | +## Tutorial Requirements |
| 23 | +Ensure you have Python 3 installed, because Python 2 reached its |
| 24 | +end-of-life at the beginning of 2020 and is no longer supported. |
| 25 | +Preferrably, you should have |
| 26 | +[Python 3.7 or greater installed](https://www.python.org/downloads/) |
| 27 | +in your [development environment](/development-environments.html). |
| 28 | +This tutorial will also use: |
| 29 | + |
| 30 | +* [Flask](/flask.html) web framework, |
| 31 | + [version 1.1.2](https://github.com/pallets/flask/releases/tag/1.1.2) |
| 32 | +* a hosted Sentry instance on [sentry.io](https://sentry.io), which we'll |
| 33 | + need an account to access |
| 34 | +* the [Sentry Python helper library](https://pypi.org/project/sentry-sdk/) to |
| 35 | + send exception data to our Sentry instance, with the |
| 36 | + [Flask integration](https://docs.sentry.io/platforms/python/flask/) |
| 37 | + |
| 38 | +All code in this blog post is available open source under the MIT license |
| 39 | +on GitHub under the |
| 40 | +[report-errors-flask-web-apps-sentry directory of the blog-code-examples](https://github.com/fullstackpython/blog-code-examples/tree/master/report-errors-flask-web-apps-sentry) |
| 41 | +repository. Use the source code as you desire for your own projects. |
| 42 | + |
| 43 | + |
| 44 | +## Development environment set up |
| 45 | +Change into the directory where you keep your Python |
| 46 | +[virtual environments](/virtual-environments-virtualenvs-venvs.html). |
| 47 | +Create a new virtualenv for this project using the following |
| 48 | +command. |
| 49 | + |
| 50 | +Install the Flask and Sentry-SDK code libraries into a new Python |
| 51 | +virtual environment using the following commands: |
| 52 | + |
| 53 | +```bash |
| 54 | +python -m venv sentryflask |
| 55 | +source sentryflask/bin/activate |
| 56 | + |
| 57 | +pip install flask>=1.1.2 sentry-sdk[flask]==0.15.1 |
| 58 | +``` |
| 59 | + |
| 60 | +Note that we installed the Flask integration as part of the Sentry |
| 61 | +SDK, which is why the dependency is `sentry-sdk[flask]` rather than |
| 62 | +just `sentry-sdk`. |
| 63 | + |
| 64 | +Now that we have all of our dependencies installed we can code up a |
| 65 | +little application to show how the error reporting works. |
| 66 | + |
| 67 | + |
| 68 | +## Creating the application |
| 69 | +We have everything we need to start building our application. Create |
| 70 | +a new directory for your project. I've called mine |
| 71 | +[report-errors-flask-web-apps-sentry](https://github.com/fullstackpython/blog-code-examples/tree/master/report-errors-flask-web-apps-sentry) |
| 72 | +in the examples repository but you can use a shorter name if you |
| 73 | +prefer. Open a new file named `app.py` and write the following code in it. |
| 74 | + |
| 75 | +```python |
| 76 | +# app.py |
| 77 | +from flask import Flask, escape, request |
| 78 | + |
| 79 | + |
| 80 | +app = Flask(__name__) |
| 81 | + |
| 82 | + |
| 83 | +@app.route('/divide/<int:numerator>/by/<int:denominator>/') |
| 84 | +def hello(numerator, denominator): |
| 85 | + answer = numerator / denominator |
| 86 | + return f'{numerator} can be divided by {denominator} {answer} times.' |
| 87 | + |
| 88 | +``` |
| 89 | + |
| 90 | +The above code is a short Flask application that allows input via the URL for |
| 91 | +two integer values: a numerator and a denominator. |
| 92 | + |
| 93 | +Save the file and run it using the `flask run` command: |
| 94 | + |
| 95 | +```bash |
| 96 | +env FLASK_APP=app.py flask run |
| 97 | +``` |
| 98 | + |
| 99 | +If you see the following output on the command line that means the development |
| 100 | +server is working properly: |
| 101 | + |
| 102 | +```bash |
| 103 | + * Serving Flask app "app.py" |
| 104 | + * Environment: production |
| 105 | + WARNING: This is a development server. Do not use it in a production deployment. |
| 106 | + Use a production WSGI server instead. |
| 107 | + * Debug mode: off |
| 108 | + * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) |
| 109 | +``` |
| 110 | + |
| 111 | +Test it by going to http://localhost:5000/divide/50/by/10/ and you will |
| 112 | +get the following output in your web browser: |
| 113 | + |
| 114 | +<img src="/img/200630-python-flask-sentry/division-success.png" width="100%" class="shot rnd outl" alt="Successful division of 50 by 10."> |
| 115 | + |
| 116 | +With our base application working, we can now add error reporting for |
| 117 | +the situations that do not work as expected. |
| 118 | + |
| 119 | + |
| 120 | +## Adding Sentry to our app |
| 121 | +It's time to add Sentry with the Flask integration into the mix, so that we |
| 122 | +can easily see when the route errors out due to bad input. |
| 123 | + |
| 124 | +Sentry can either be [self-hosted](https://github.com/getsentry/onpremise) or |
| 125 | +used as a cloud service through [Sentry.io](https://sentry.io). In this |
| 126 | +tutorial we will use the cloud hosted version because it's faster than |
| 127 | +setting up your own server as well as free for smaller projects. |
| 128 | + |
| 129 | +Go to [Sentry.io's homepage](https://sentry.io). |
| 130 | + |
| 131 | +<img src="/img/200525-sentry/sentry-homepage.jpg" width="100%" class="shot rnd outl" alt="Sentry.io homepage where you can sign up for a free account."> |
| 132 | + |
| 133 | +Sign into your account or sign up for a new free account. You will be at |
| 134 | +the main account dashboard after logging in or completing the Sentry sign |
| 135 | +up process. |
| 136 | + |
| 137 | +There are no errors logged on our account dashboard yet, which is as |
| 138 | +expected because we have not yet connected our account to our Python |
| 139 | +application. |
| 140 | + |
| 141 | +<img src="/img/200525-sentry/sentry-empty-dashboard.jpg" width="100%" class="shot rnd outl" alt="Blank Sentry account dashboard."> |
| 142 | + |
| 143 | +You'll want to create a new Sentry Project just for this application so |
| 144 | +click "Projects" in the left sidebar to go to the Projects page. |
| 145 | + |
| 146 | +<img src="/img/200525-sentry/create-project.jpg" width="100%" class="shot rnd outl" alt="Button to create a new Sentry project."> |
| 147 | + |
| 148 | +On the Projects page, click the "Create Project" button in the top right |
| 149 | +corner of the page. |
| 150 | + |
| 151 | +<img src="/img/200525-sentry/create-new-project-screen.jpg" width="100%" class="shot rnd outl" alt="Create a new Sentry project."> |
| 152 | + |
| 153 | +You can either choose "Flask" or select "Python". I usually just choose |
| 154 | +"Python" if I do not yet know what framework I'll be using to build my |
| 155 | +application. Next, give your new Project a name and then press the "Create |
| 156 | +Project" button. Our new project is ready to integrate with our Python code. |
| 157 | + |
| 158 | +We need the unique identifier for our account and project to authorize our |
| 159 | +Python code to send errors to this Sentry instance. The easiest way to get |
| 160 | +what we need is to go to the |
| 161 | +[Python+Flask documentation page](https://docs.sentry.io/platforms/python/flask/) |
| 162 | +and read how to configure the SDK. |
| 163 | + |
| 164 | +<img src="/img/200525-sentry/python-sentry-quickstart.jpg" width="100%" class="shot rnd outl" alt="The Sentry docs show you exactly what you need to export to connect to your account."> |
| 165 | + |
| 166 | +Copy the string parameter for the `init` method and set it |
| 167 | +[as an environment variable](https://www.twilio.com/blog/2017/01/how-to-set-environment-variables.html) |
| 168 | +rather than having it exposed in your project's code. |
| 169 | + |
| 170 | +```bash |
| 171 | +export SENTRY_DSN='https://yourkeygoeshere.ingest.sentry.io/project-number' |
| 172 | +``` |
| 173 | + |
| 174 | +**Make sure to replace "yourkeygoeshere" with your own unique identifier |
| 175 | +and "project-number" with the ID that matches the project you just |
| 176 | +created.** |
| 177 | + |
| 178 | +Check that the `SENTRY_DSN` is set properly in your shell using the `echo` |
| 179 | +command: |
| 180 | + |
| 181 | +```bash |
| 182 | +echo $SENTRY_DSN |
| 183 | +``` |
| 184 | + |
| 185 | + |
| 186 | +Update `app.py` with the following highlighted lines of code. |
| 187 | + |
| 188 | +```python |
| 189 | +# app.py |
| 190 | +~~import os |
| 191 | +~~import sentry_sdk |
| 192 | +from flask import Flask, escape, request |
| 193 | +~~from sentry_sdk.integrations.flask import FlaskIntegration |
| 194 | + |
| 195 | + |
| 196 | +~~sentry_sdk.init( |
| 197 | +~~ dsn=os.getenv('SENTRY_DSN'), integrations=[FlaskIntegration()] |
| 198 | +~~) |
| 199 | + |
| 200 | + |
| 201 | +app = Flask(__name__) |
| 202 | + |
| 203 | + |
| 204 | +@app.route('/divide/<int:numerator>/by/<int:denominator>/') |
| 205 | +def hello(numerator, denominator): |
| 206 | + answer = numerator / denominator |
| 207 | + return f'{numerator} can be divided by {denominator} {answer} times.' |
| 208 | +``` |
| 209 | + |
| 210 | +The above new lines of code initialize the Sentry client and allow it to |
| 211 | +properly send any errors that occur over to the right Sentry service. |
| 212 | + |
| 213 | + |
| 214 | +## Testing the Sentry Integration |
| 215 | +The Sentry dashboard shows that the service is still waiting for events. |
| 216 | + |
| 217 | +<img src="/img/200630-python-flask-sentry/waiting-for-events.jpg" width="100%" class="shot rnd outl" alt="Sentry dashboard, without any error data shown."> |
| 218 | + |
| 219 | +Let's make an error happen to see if we've properly connected the Flask integration |
| 220 | +with our application. |
| 221 | + |
| 222 | +Try to divide by zero, by going to http://localhost:5000/divide/50/by/0/ in |
| 223 | +your web browser. You should get an "Internal Server Error". |
| 224 | + |
| 225 | +<img src="/img/200630-python-flask-sentry/internal-server-error.png" width="100%" class="shot rnd outl" alt="Flask HTTP status code 500 for internal server error."> |
| 226 | + |
| 227 | +Back over in the Sentry dashboard, the error appears in the list. |
| 228 | + |
| 229 | +<img src="/img/200630-python-flask-sentry/zero-division-error.png" width="100%" class="shot rnd outl" alt="Sentry dashboard showing the exact ZeroDivisionError."> |
| 230 | + |
| 231 | +We can drill into the error by clicking on it and get a ton more information, |
| 232 | +not just about our application but also about the client that visited the |
| 233 | +site. This is handy if you have an issue in a specific browser or other |
| 234 | +type of client when building an [API](/application-programming-interfaces.html). |
| 235 | + |
| 236 | +<img src="/img/200630-python-flask-sentry/error-details.jpg" width="100%" class="shot rnd outl" alt="ZeroDivisionError error details in the Sentry user interface."> |
| 237 | + |
| 238 | +With that in place, you can now build out the rest of your Flask application |
| 239 | +knowing that all of the exceptions will be tracked in Sentry. |
| 240 | + |
| 241 | + |
| 242 | +## What's next? |
| 243 | +We just finished building a Flask app to show how quickly the hosted |
| 244 | +version of Sentry can be added to applications so you do not lose |
| 245 | +track of your error messages. |
| 246 | + |
| 247 | +Next, you can try one of these tutorials to add other useful features to your |
| 248 | +new application: |
| 249 | + |
| 250 | +* [Responding to SMS Text Messages with Python & Flask](/blog/respond-sms-text-messages-python-flask.html) |
| 251 | +* [Develop and Run Flask Apps within Docker Containers](/blog/develop-flask-web-apps-docker-containers-macos.html) |
| 252 | +* [Add Okta Authentication to an Existing Flask App](/blog/okta-user-auth-existing-flask-web-app.html) |
| 253 | + |
| 254 | +You can also determine what to code next in your Python project by reading |
| 255 | +the [Full Stack Python table of contents page](/table-of-contents.html). |
| 256 | + |
| 257 | +Questions? Contact me via Twitter |
| 258 | +[@fullstackpython](https://twitter.com/fullstackpython) |
| 259 | +or [@mattmakai](https://twitter.com/mattmakai). I am also on GitHub with |
| 260 | +the username [mattmakai](https://github.com/mattmakai). |
| 261 | + |
| 262 | +If you see an issue or error in this tutorial, please |
| 263 | +[fork the source repository on GitHub](https://github.com/mattmakai/fullstackpython/blob/master/content/posts/200630-report-errors-flask-web-apps-sentry.markdown) |
| 264 | +and submit a pull request with the fix. |
0 commit comments