This document is for an in-development version of Galaxy. You can alternatively view this page in the latest release if it exists or view the top of the latest release's documentation.

Source code for galaxy.web.framework.middleware.sentry


:copyright: (c) 2010-2012 by the Sentry Team, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
import time

    from raven import Client
    from raven.utils.wsgi import get_current_url, get_headers, get_environ
except ImportError:
    Client = None

from galaxy.web_stack import register_postfork_function

RAVEN_IMPORT_MESSAGE = ('The Python raven package is required to use this '
                        'feature, please install it')

[docs]class Sentry: """ A WSGI middleware which will attempt to capture any uncaught exceptions and send them to Sentry. """
[docs] def __init__(self, application, dsn, sloreq=0): assert Client is not None, RAVEN_IMPORT_MESSAGE self.application = application self.client = None self.sloreq_threshold = sloreq def postfork_sentry_client(): self.client = Client(dsn) register_postfork_function(postfork_sentry_client)
def __call__(self, environ, start_response): try: start_time = time.time() iterable = self.application(environ, start_response) dt = (time.time() - start_time) if self.sloreq_threshold and dt > self.sloreq_threshold: self.handle_slow_request(environ, dt) except Exception: self.handle_exception(environ) raise try: yield from iterable except Exception: self.handle_exception(environ) raise finally: # wsgi spec requires iterable to call close if it exists # see http://blog.dscpl.com.au/2012/10/obligations-for-calling-close-on.html if iterable and hasattr(iterable, 'close') and callable(iterable.close): try: iterable.close() except Exception: self.handle_exception(environ)
[docs] def handle_slow_request(self, environ, dt): headers = dict(get_headers(environ)) if 'Authorization' in headers: headers['Authorization'] = 'redacted' if 'Cookie' in headers: headers['Cookie'] = 'redacted' cak = environ.get('controller_action_key', None) or environ.get('PATH_INFO', "NOPATH").strip('/').replace('/', '.') event_id = self.client.captureMessage( "SLOREQ: %s" % cak, data={ 'sentry.interfaces.Http': { 'method': environ.get('REQUEST_METHOD'), 'url': get_current_url(environ, strip_querystring=True), 'query_string': environ.get('QUERY_STRING'), 'headers': headers, 'env': dict(get_environ(environ)), } }, extra={ 'request_id': environ.get('request_id', 'Unknown'), 'request_duration_millis': dt * 1000 }, level="warning", tags={ 'type': 'sloreq', 'action_key': cak } ) # Galaxy: store event_id in environment so we can show it to the user environ['sentry_event_id'] = event_id return event_id
[docs] def handle_exception(self, environ): headers = dict(get_headers(environ)) # Authorization header for REMOTE_USER sites consists of a base64() of # their plaintext password. It is a security issue for this password to # be exposed to a third party system which may or may not be under # control of the same administrators as the local Authentication # system. E.g. university LDAP systems. if 'Authorization' in headers: # Redact so the administrator knows that a value is indeed present. headers['Authorization'] = 'redacted' # Passing cookies allows for impersonation of users (depending on # remote service) and can be considered a security risk as well. For # multiple services running alongside Galaxy on the same host, this # could allow a sentry user with access to logs to impersonate a user # on another service. In the case of services like Jupyter, this can be # a serious concern as that would allow for terminal access. Furthermore, # very little debugging information can be gained as a result of having # access to all of the users cookies (including Galaxy cookies) if 'Cookie' in headers: headers['Cookie'] = 'redacted' event_id = self.client.captureException( data={ 'sentry.interfaces.Http': { 'method': environ.get('REQUEST_METHOD'), 'url': get_current_url(environ, strip_querystring=True), 'query_string': environ.get('QUERY_STRING'), # TODO # 'data': environ.get('wsgi.input'), 'headers': headers, 'env': dict(get_environ(environ)), } }, # Galaxy: add request id from environment if available extra={ 'request_id': environ.get('request_id', 'Unknown') } ) # Galaxy: store event_id in environment so we can show it to the user environ['sentry_event_id'] = event_id return event_id