title: JSON formatted logging with Flask and gunicorn
body: <p>I am trying to do something very similar to what's explained here: <a href="https://sebest.github.io/post/protips-using-gunicorn-inside-a-docker-image/" rel="noreferrer">https://sebest.github.io/post/protips-using-gunicorn-inside-a-docker-image/</a></p>
  
  <p>I want to get my Flask app + gunicorn both outputting JSON formatted logging. I've got this working for the Flask app, but I can't seem to get it working with gunicorn.</p>
  
  <p>Here's my current output:</p>
  
  <pre><code> * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
   * Restarting with stat
   * Debugger is active!
   * Debugger pin code: 317-187-130
  192.168.99.1 - - [12/Jan/2016 20:09:29] "GET /nothing HTTP/1.1" 404 -
  {"asctime": "2016-01-12 20:20:25,541", "levelname": "WARNING", "module": "app", "funcName": "err", "lineno": 17, "message": "will assert false..."}
  192.168.99.1 - - [12/Jan/2016 20:20:25] "GET /err HTTP/1.1" 500 -
  </code></pre>
  
  <p>The line that begins <code>{"asctime":</code> is the output of the code <code>app.logger.warning('will assert false...')</code> which is being correctly logged as JSON. Yay. The lines that begin with <code>192.168.99.1</code> are output from my gunicorn WSGI server and, frustratingly, they are not JSON formatted.</p>
  
  <p>Here's the command I am using to start gunicorn:</p>
  
  <pre><code>gunicorn --log-config gunicorn_logging.conf -c gunicorn_config.py api.app:app
  </code></pre>
  
  <p>where the <code>gunicorn_logging.conf</code> file contains:</p>
  
  <pre><code>[loggers]
  keys=root, gunicorn.error
  
  [handlers]
  keys=console
  
  [formatters]
  keys=json
  
  [logger_root]
  level=INFO
  handlers=console
  
  [logger_gunicorn.error]
  level=ERROR
  handlers=console
  propagate=0
  qualname=gunicorn.error
  
  [handler_console]
  class=StreamHandler
  formatter=json
  args=(sys.stdout, )
  
  [formatter_json]
  class=jsonlogging.JSONFormatter
  </code></pre>
  
  <p>and the file <code>gunicorn_config.py</code> contains:</p>
  
  <pre class="lang-python prettyprint-override"><code>import os
  import multiprocessing
  
  addr = os.environ.get('HTTP_ADDR', '0.0.0.0')
  port = os.environ.get('HTTP_PORT', '5000')
  loglevel = os.environ.get('LOG_LEVEL', 'info')
  
  bind = '{0}:{1}'.format(addr, port)
  workers = multiprocessing.cpu_count() * 5 + 1
  worker_class = 'gevent'
  timeout = 0
  </code></pre>
  
  <p>Here's the output of <code>pip freeze</code>:</p>
  
  <pre><code>aniso8601==1.1.0
  coverage==4.0.3
  flake8==2.5.1
  Flask==0.10.1
  Flask-MySQLdb==0.2.0
  Flask-RESTful==0.3.5
  Flask-Script==2.0.5
  gevent==1.1rc3
  greenlet==0.4.9
  gunicorn==19.4.5
  itsdangerous==0.24
  Jinja2==2.8
  json-logging-py==0.2
  MarkupSafe==0.23
  marshmallow==2.4.2
  mccabe==0.3.1
  mysqlclient==1.3.7
  nose==1.3.7
  pep8==1.5.7
  pyflakes==1.0.0
  python-dateutil==2.4.2
  python-json-logger==0.1.4
  pytz==2015.7
  six==1.10.0
  SQLAlchemy==1.0.11
  Werkzeug==0.11.3
  </code></pre>