Connexion: Unable to suppress annoying server requests logging with gevent backend

Created on 1 Oct 2018  Â·  3Comments  Â·  Source: zalando/connexion

Description

Unable to customize annoying logging with gevent backend

Expected behaviour

Switching between logging levels should be clear and maybe handled by connexion itself (backend independent), like:

logging.getLogger('gevent').setLevel(logging.ERROR)
# or
app = connexion.FlaskApp(__name__)
app.app.logger.setLevel(logging.ERROR)
(both of these are not working)

Actual behaviour

I get a huge amount of irrelevant requests information, no matter how I try to customize this default behaviour.
image

Steps to reproduce

Just create some example app and change backend to gevent, like:

# Constants are not relevant here
app.run(server='gevent',
        host=API_SERVER.HOST,
        port=API_SERVER.PORT,
        debug=False)

Full context for my code:

import logging
import connexion
from tensorhive.config import API, API_SERVER
from tensorhive.database import db_session
from flask_cors import CORS
from tensorhive.authorization import init_jwt

log = logging.getLogger(__name__)
'''
Constants are loaded from other module
API.TITLE = 'TensorHive API'
API.VERSION = 0.2
API.URL_PREFIX = 'api/0.2'
API.SPEC_FILE = 'api_specification.yml'
API.IMPL_LOCATION = 'tensorhive.api.controllers'
'''

class APIServer():
    def run_forever(self):
        app = connexion.FlaskApp(__name__)
        app.app.logger.setLevel(logging.ERROR)
        init_jwt(app.app)

        @app.app.teardown_appcontext
        def shutdown_session(exception=None):
            db_session.remove()

        app.add_api(API.SPEC_FILE,
                    arguments={
                        'title': API.TITLE, 
                        'version': API.VERSION,
                        'url_prefix': API.URL_PREFIX
                    },
                    resolver=connexion.RestyResolver(API.IMPL_LOCATION),
                    strict_validation=True)
        CORS(app.app)
        log.info('[âš™] Starting API server with {} backend'.format(API_SERVER.BACKEND))
        log.info('[✔] API documentation (Swagger UI) available at: http://{host}:{port}/{url_prefix}/ui/'.format(
            host=API_SERVER.HOST, 
            port=API_SERVER.PORT,
            url_prefix=API.URL_PREFIX))
        app.run(server=API_SERVER.BACKEND,
                host=API_SERVER.HOST,
                port=API_SERVER.PORT,
                debug=API_SERVER.DEBUG)


def start_api_server():
    APIServer().run_forever()

if __name__ == '__main__':
    start_api_server()

Additional info, my analysis:

I think the problematic line is:
https://github.com/zalando/connexion/blob/1fbb1ff941dee089dcf881ef46bcb079ac52502a/connexion/apps/flask_app.py#L112

Explanation (http://www.gevent.org/api/gevent.pywsgi.html):

log – If given, an object with a write method to which request (access) logs will be written. If not given, defaults to sys.stderr. You may pass None to disable request logging.

So it looks like you need to pass additional parameter to the WSGI constructor -> overriding default logging method

If you have some simple workaround for this, I'll be very grateful!

Output of the commands:

  • python --version
    Python 3.6.6 :: Anaconda, Inc.
  • pip show connexion | grep "^Version\:"
    Version: 1.4.2

Most helpful comment

Yikes, It looks like gevent pywsgi was unable to use standard python logging facilities because of locks in the library.
Luckily connexion passes **options to the underlying wsgi server.
We just need to pass it something with a callable write attribute:

class devnull:
    write = lambda _: None

app.run(server=API_SERVER.BACKEND,
        host=API_SERVER.HOST,
        port=API_SERVER.PORT,
        debug=API_SERVER.DEBUG,
        log=devnull)

All 3 comments

Yikes, It looks like gevent pywsgi was unable to use standard python logging facilities because of locks in the library.
Luckily connexion passes **options to the underlying wsgi server.
We just need to pass it something with a callable write attribute:

class devnull:
    write = lambda _: None

app.run(server=API_SERVER.BACKEND,
        host=API_SERVER.HOST,
        port=API_SERVER.PORT,
        debug=API_SERVER.DEBUG,
        log=devnull)

✓ Thank you so much, it worked perfectly! Issue can be closed.

An easier alternative is to set log=None

Was this page helpful?
0 / 5 - 0 ratings