Skip to content

Instantly share code, notes, and snippets.

@wichert
Created September 25, 2018 13:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wichert/1fb5ee81d1ff0ba9e96624a55d41350f to your computer and use it in GitHub Desktop.
Save wichert/1fb5ee81d1ff0ba9e96624a55d41350f to your computer and use it in GitHub Desktop.
import logging
import os
from pyramid.httpexceptions import WSGIHTTPException
from pyramid.tweens import EXCVIEW
from raven import Client
from raven import fetch_package_version
from raven.utils.wsgi import get_environ
try:
from raven.contrib.celery import register_signal, register_logger_signal
import celery # NOQA
HAVE_CELERY = True
except ImportError:
HAVE_CELERY = False
log = logging.getLogger(__name__)
class NewSentryContext:
"""Event used to setup a Sentry context for the current request.
This is typically used to add data to the context, for example:
.. code-block:: python
@subscriber(PrepareSentryContext)
def add_client_os(event):
event.context.merge({
'os': get_client_os(event.request),
})
Keep in mind that this event is fired very early during request processing,
before Pyramid has done context lookup, authentication or authorisation.
"""
def __init__(self, request, context):
self.request = request
self.context = context
class MockClient(object):
def captureException(self, *a, **kw):
pass
def captureMessage(self, *a, **kw):
pass
def create_client(config):
registry = config.registry
settings = config.get_settings()
params = {
'release': fetch_package_version(registry.package_name),
'environment': os.environ.get('SENTRY_ENVIRONMENT', 'development'),
}
params.update({k[7:]: v
for (k, v) in settings.items()
if k.startswith('sentry.')})
params['tags'] = {
'application': registry.package_name,
}
if os.environ.get('SENTRY_DSN'):
params.pop('dsn', None) # Environment wins from config file
elif not params.get('dsn'):
return None
return Client(**params)
def sentry_tween_factory(handler, registry):
def sentry_tween(request):
client = request.raven
client.context.activate()
data = None
try:
if request.content_type == 'application/data':
data = request.json_body
elif request.content_type == 'application/x-www-form-urlencoded':
data = dict(request.POST)
else:
data = request.body
except ValueError:
pass
client.http_context({
'method': request.method,
'url': request.path_url,
'query_string': request.query_string,
'headers': dict(request.headers),
'env': dict(get_environ(request.environ)),
'data': data,
})
request.registry.notify(NewSentryContext(request, client.context))
try:
response = handler(request)
except (WSGIHTTPException, StopIteration, GeneratorExit):
raise
except SystemExit as e:
if e.code != 0:
client.captureException()
raise
except Exception:
client.captureException()
raise
finally:
client.context.clear()
client.transaction.clear()
return response
return sentry_tween
def includeme(config):
client = create_client(config)
if client is None:
log.warn('No sentry DSN found in environment or config.')
client = MockClient()
else:
config.add_tween('curvetips.logging.sentry.sentry_tween_factory', under=EXCVIEW)
if HAVE_CELERY:
register_logger_signal(client)
register_signal(client, ignore_expected=True)
config.registry['raven'] = client
config.add_request_method(lambda r: client, 'raven', reify=True)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment