iZZiSwift

iZZiSwift

Learning by Sharing Swift Programing and more …

  • Swift Programming
    • Swift Language
    • i0S Swift Issue
  • Devices
  • MacOS
  • Python Programming
  • Kotlin Programming
  • Memo

Why is Flask application not creating any logs when hosted by Gunicorn?

January 15, 2021 Bell Jacquise
Python Programming

Question or problem about Python programming:

I’m trying to add logging to a web application which uses Flask.

When hosted using the built-in server (i.e. python3 server.py), logging works. When hosted using Gunicorn, the log file is not created.

The simplest code which reproduces the problem is this one:

#!/usr/bin/env python

import logging
from flask import Flask
flaskApp = Flask(__name__)


@flaskApp.route('/')
def index():
    flaskApp.logger.info('Log message')
    print('Direct output')
    return 'Hello World\n'


if __name__ == "__main__":
    logHandler = logging.FileHandler('/var/log/demo/app.log')
    logHandler.setLevel(logging.INFO)
    flaskApp.logger.addHandler(logHandler)
    flaskApp.logger.setLevel(logging.INFO)
    flaskApp.run()

The application is called using:

gunicorn server:flaskApp -b :80 -w 4
    --access-gfile /var/log/demo/access.log
    --error-logfile /var/log/demo/error.log

When doing a request to the home page of the site, the following happens:

Note that:

What’s happening?

How to solve the problem:

Solution 1:

When you use python3 server.py you are running the server3.py script.

When you use gunicorn server:flaskApp ... you are running the gunicorn startup script which then imports the module server and looks for the variable flaskApp in that module.

Since server.py is being imported the __name__ var will contain "server", not "__main__" and therefore you log handler setup code is not being run.

You could simply move the log handler setup code outside of the if __name__ == "__main__": stanza. But ensure that you keep flaskApp.run() in there since you do not want that to be run when gunicorn imports server.

More about what does if __name__ == “__main__”: do?

Solution 2:

This approach works for me: Import the Python logging module and add gunicorn’s error handlers to it. Then your logger will log into the gunicorn error log file:

import logging

app = Flask(__name__)

gunicorn_error_logger = logging.getLogger('gunicorn.error')
app.logger.handlers.extend(gunicorn_error_logger.handlers)
app.logger.setLevel(logging.DEBUG)
app.logger.debug('this will show in the log')

My Gunicorn startup script is configured to output log entries to a file like so:

gunicorn main:app \
    --workers 4 \
    --bind 0.0.0.0:9000 \
    --log-file /app/logs/gunicorn.log \
    --log-level DEBUG \
    --reload

Solution 3:

There are a couple of reasons behind this: Gunicorn has its own loggers, and it’s controlling log level through that mechanism. A fix for this would be to add app.logger.setLevel(logging.DEBUG).
But what’s the problem with this approach? Well, first off, that’s hard-coded into the application itself. Yes, we could refactor that out into an environment variable, but then we have two different log levels: one for the Flask application, but a totally separate one for Gunicorn, which is set through the –log-level parameter (values like “debug”, “info”, “warning”, “error”, and “critical”).

A great solution to solve this problem is the following snippet:

import logging
from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/')
def default_route():
    """Default route"""
    app.logger.debug('this is a DEBUG message')
    app.logger.info('this is an INFO message')
    app.logger.warning('this is a WARNING message')
    app.logger.error('this is an ERROR message')
    app.logger.critical('this is a CRITICAL message')
    return jsonify('hello world')

if __name__ == '__main__':
    app.run(host=0.0.0.0, port=8000, debug=True)

else:
    gunicorn_logger = logging.getLogger('gunicorn.error')
    app.logger.handlers = gunicorn_logger.handlers
    app.logger.setLevel(gunicorn_logger.level)

Refrence: Code and Explanation is taken from here

Hope this helps!

Tagged activepython boost-python bpython cpython epd-python error-logging Flask flask-extensions flask-login flask-migrate flask-restful flask-sqlalchemy google-api-python-client gunicorn ipython ipython-magic ipython-notebook ipython-parallel kotlin-logging logging

Post navigation

Swift: converting between Arrays of ‘Protocol’ and Arrays of implementing Class
Swift – Impossible to use “registerNib” on my tableView to add a custom cell

Related Posts

Python Programming

Assigning a variable NaN in python without numpy

November 21, 2020 Jeffrey Schneider
Python Programming

Most pythonic way to delete a file which may not exist

September 29, 2020 Simon Abital

Getting the array as GET query parameters in Python

January 10, 2021 Jeffrey Schneider

Same Catagory Posts

  • 5 Steps to Learning Python the Right Way
  • Create a incrementing filename in Python
  • Check whether a file exists without exceptions
  • “yield” keyword in python
  • meaning of if __name__ == “__main__”
  • Metaclasses in Python
  • Ternary conditional operator in Python
  • Check whether a file exists without exceptions
  • Merge two dictionaries in a single expression in Python
  • Call an external command in Python
iZZiSwift | Developed by iZZi Team from 2017