# Copyright (C) 2015-2020 OpenIO SAS, as part of OpenIO SDS
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 3.0 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library.
from gunicorn.app.base import BaseApplication
from gunicorn.glogging import Logger
from werkzeug.wrappers import Request, Response
from werkzeug.utils import escape
from werkzeug.exceptions import HTTPException, InternalServerError, \
ServiceUnavailable
from oio.common.exceptions import ServiceBusy
from oio.common.utils import CPU_COUNT
from oio.common.configuration import read_conf
from oio.common.logger import get_logger
[docs]class Application(BaseApplication):
access_log_fmt = '%(bind0)s %(h)s:%({remote}p)s %(m)s %(s)s %(D)s ' + \
'%(B)s %(l)s %({x-oio-req-id}i)s %(U)s?%(q)s'
def __init__(self, app, conf, logger_class=None):
self.conf = conf
self.application = app
self.logger_class = logger_class
super(Application, self).__init__()
[docs] def load_config(self):
bind = '%s:%s' % (self.conf.get('bind_addr', '127.0.0.1'),
self.conf.get('bind_port', '8000'))
self.cfg.set('bind', bind)
self.cfg.set('backlog', self.conf.get('backlog', 2048))
self.cfg.set('workers', self.conf.get('workers', CPU_COUNT))
self.cfg.set('worker_class', self.conf.get('worker_class', 'eventlet'))
self.cfg.set('worker_connections', self.conf.get(
'worker_connections', 1000))
self.cfg.set('syslog_prefix', self.conf.get('syslog_prefix', ''))
self.cfg.set('syslog_addr', self.conf.get('log_address', '/dev/log'))
self.cfg.set('accesslog', None)
self.cfg.set('keepalive', 30)
self.cfg.set('access_log_format', self.conf.get('access_log_format',
self.access_log_fmt))
if self.logger_class:
# reactivate after
# self.cfg.set('logger_class', self.logger_class)
pass
[docs] def load(self):
return self.application
[docs]class ServiceLogger(Logger):
def __init__(self, cfg):
super(ServiceLogger, self).__init__(cfg)
prefix = cfg.syslog_prefix if cfg.syslog_prefix else ''
address = cfg.syslog_addr if cfg.syslog_addr else '/dev/log'
error_conf = {
'syslog_prefix': prefix,
'log_facility': 'LOG_LOCAL0',
'log_address': address
}
access_conf = {
'syslog_prefix': prefix,
'log_facility': 'LOG_LOCAL1',
'log_address': address
}
self.error_log = get_logger(error_conf, 'log')
self.access_log = get_logger(access_conf, 'access')
[docs] def atoms(self, resp, req, environ, request_time):
atoms = super(ServiceLogger, self).atoms(resp, req, environ,
request_time)
# We may bind on several addresses and ports but I don't
# know how to identify for which one we are logging
index = 0
for bind in self.cfg.bind:
atoms['bind%d' % index] = bind
index += 1
return atoms
[docs] def access(self, resp, req, environ, request_time):
# do not log status requests
if environ.get('PATH_INFO', '/') != '/status':
super(ServiceLogger, self).access(resp, req, environ, request_time)
[docs]class WerkzeugApp(object):
def __init__(self, url_map=None, logger=None):
self.url_map = url_map
self.logger = logger or get_logger(None)
[docs] def dispatch_request(self, req):
adapter = self.url_map.bind_to_environ(req.environ)
try:
endpoint, params = adapter.match()
resp = getattr(self, 'on_' + endpoint)(req, **params)
except HTTPException as exc:
resp = exc
except ServiceBusy as exc:
if self.logger:
self.logger.error(str(exc))
resp = ServiceUnavailable(
"Could not satisfy the request: %s" % exc)
except Exception as exc:
if self.logger:
self.logger.exception('ERROR Unhandled exception in request')
resp = InternalServerError('Unmanaged error: %s' % exc)
if isinstance(resp, HTTPException) and not resp.response:
resp.response = Response(escape(resp.description), resp.code)
return resp
[docs] def wsgi_app(self, environ, start_response):
req = Request(environ)
resp = self.dispatch_request(req)
return resp(environ, start_response)
def __call__(self, environ, start_response):
return self.wsgi_app(environ, start_response)
[docs]def init_request_processor(conf_file, app_name, app_factory, *args, **kwargs):
conf = read_conf(conf_file, app_name)
if 'logger' in kwargs:
logger = kwargs.pop('logger')
else:
logger = get_logger(conf, app_name,
verbose=kwargs.pop('verbose', False))
app = app_factory(conf)
return (app, conf, logger, app_name)