79

I've been working on a new dev platform using nginx/gunicorn and Flask for my application.

Ops-wise, everything works fine - the issue I'm having is with debugging the Flask layer. When there's an error in my code, I just get a straight 500 error returned to the browser and nothing shows up on the console or in my logs.

I've tried many different configs/options.. I guess I must be missing something obvious.

My gunicorn.conf:

import os

bind = '127.0.0.1:8002'
workers = 3
backlog = 2048
worker_class = "sync"
debug = True
proc_name = 'gunicorn.proc'
pidfile = '/tmp/gunicorn.pid'
logfile = '/var/log/gunicorn/debug.log'
loglevel = 'debug'

An example of some Flask code that borks- testserver.py:

from flask import Flask
from flask import render_template_string
from werkzeug.contrib.fixers import ProxyFix

app = Flask(__name__)

@app.route('/')
def index():
    n = 1/0
    return "DIV/0 worked!"

And finally, the command to run the flask app in gunicorn:

gunicorn -c gunicorn.conf.py testserver:app

Thanks y'all

Paco
  • 4,520
  • 3
  • 29
  • 53
mafrosis
  • 2,720
  • 1
  • 25
  • 34

7 Answers7

90

The accepted solution doesn't work for me.

Gunicorn is a pre-forking environment and apparently the Flask debugger doesn't work in a forking environment.

Attention

Even though the interactive debugger does not work in forking environments (which makes it nearly impossible to use on production servers) [...]

Even if you set app.debug = True, you will still only get an empty page with the message Internal Server Error if you run with gunicorn testserver:app. The best you can do with gunicorn is to run it with gunicorn --debug testserver:app. That gives you the trace in addition to the Internal Server Error message. However, this is just the same text trace that you see in the terminal and not the Flask debugger.

Adding the if __name__ ... section to the testserver.py and running python testserver.py to start the server in development gets you the Flask debugger. In other words, don't use gunicorn in development if you want the Flask debugger.

app = Flask(__name__)
app.config['DEBUG'] = True

if __name__ == '__main__':
    app.run()

## Tip for Heroku users: Personally I still like to use `foreman start`, instead of `python testserver.py` since [it sets up all the env variables for me](https://devcenter.heroku.com/articles/config-vars#using-foreman). To get this to work:

Contents of Procfile

web: bin/web

Contents of bin/web, file is relative to project root

#!/bin/sh

if [ "$FLASK_ENV" == "development" ]; then
        python app.py
else
        gunicorn app:app -w 3
fi

In development, create a .env file relative to project root with the following contents (docs here)

FLASK_ENV=development
DEBUG=True

Also, don't forget to change the app.config['DEBUG']... line in testserver.py to something that won't run Flask in debug mode in production.

app.config['DEBUG'] = os.environ.get('DEBUG', False)
petezurich
  • 9,280
  • 9
  • 43
  • 57
Nick Zalutskiy
  • 14,952
  • 7
  • 53
  • 50
  • 1
    Just to note, the original question was actually about getting the stack trace shown *at all*. I don't use the interactive debugger. Nice answer though. – mafrosis Jan 03 '13 at 23:05
  • the .env file in those docs is for heroku envs. If you set the heroku env FLASK_ENV=development then when it ran the procfile live it would execute the first if and run python app.py. with debug still set to True. Or i'm i missing something. – Drew Verlee May 14 '13 at 07:34
  • its worth noting that you have to make the bin/web executable with chmod +x bin/web. – Drew Verlee May 15 '13 at 17:38
  • 4
    @nick-zalutskiy the [`--debug` flag has been removed since version `19.3`](http://docs.gunicorn.org/en/19.3/news.html#id8), could you update your answer? – Édouard Lopez Aug 04 '16 at 22:25
  • This is such a violation of the dev-prod parity principle it makes me cringe. But so I take there's no alternative to using Werkzeug for local dev if we want to be able to use Flask's debug mode? – Fabien Snauwaert Nov 24 '22 at 09:51
52

The Flask config is entirely separate from gunicorn's. Following the Flask documentation on config files, a good solution would be change my source to this:

app = Flask(__name__)
app.config.from_pyfile('config.py')

And in config.py:

DEBUG = True
mafrosis
  • 2,720
  • 1
  • 25
  • 34
  • And of course this makes sense, if we treat gunicorn and Flask as discrete entities. Unlike running Django in gunicorn. – mafrosis Jan 22 '12 at 06:37
  • 10
    That's the right answer. By default Flask runs in production mode where it does not report errors. For handling errors in production mode you want to have a look at this: http://flask.pocoo.org/docs/errorhandling/ – Armin Ronacher Jan 22 '12 at 11:03
  • which version ? gunicorn: error: unrecognized arguments: -d gunicorn: error: unrecognized arguments: --debug $ gunicorn -v gunicorn (version 19.9.0) – U.V. Nov 23 '18 at 18:22
24

For Heroku users, there is a simpler solution than creating a bin/web script like suggested by Nick.

Instead of foreman start, just use foreman run python app.py if you want to debug your application in development.

aristidesfl
  • 1,310
  • 10
  • 15
1

I had similiar problem when running flask under gunicorn I didn't see stacktraces in browser (had to look at logs every time). Setting DEBUG, FLASK_DEBUG, or anything mentioned on this page didn't work. Finally I did this:

app = Flask(__name__)
app.config.from_object(settings_map[environment])
if environment == 'development':
    from werkzeug.debug import DebuggedApplication
    app_runtime = DebuggedApplication(app, evalex=False)
else:
    app_runtime = app

Note evalex is disabled because interactive debbugging won't work with forking (gunicorn).

jazgot
  • 1,943
  • 14
  • 25
1

I used this:

gunicorn "swagger_server.__main__:app" -w 4 -b 0.0.0.0:8080
Steven McConnon
  • 2,650
  • 2
  • 16
  • 21
1

You cannot really run it with gunicorn and for example use the flask reload option upon code changes.

I've used following snippets in my api launchpoint:

app = Flask(__name__)

try:
    if os.environ["yourapp_environment"] == "local":
        run_as_local = True
        # some other local configs e.g. paths
        app.logger.info('Running server in local development mode!')
except KeyError as err:
    if "yourapp_environment" in err.args:
        run_as_local = False
        # some other production configs e.g. paths
        app.logger.info('No "yourapp_environment env" given so app running server in production mode!')
    else:
        raise

...
...
...

if __name__ == '__main__':
    if run_as_local:
        app.run(host='127.0.0.1', port='8058', debug=True)
    else:
        app.run(host='0.0.0.0')

For above solution you need to give export yourapp_environment = "local" in the console.

now I can run my local as python api.py and prod gunicorn --bind 0.0.0.0:8058 api:app

The else statement app.run() is not actually needed, but I keep it for reminding me about host, port etc.

eemilk
  • 1,375
  • 13
  • 17
-3

Try setting the debug flag on the run command like so

gunicorn -c gunicorn.conf.py --debug testserver:app

and keep the DEBUG = True in your Flask application. There must be a reason why your debug option is not being applied from the config file but for now the above note should get you going.

Édouard Lopez
  • 40,270
  • 28
  • 126
  • 178
Fuchida
  • 428
  • 6
  • 16