Skip to content
This repository was archived by the owner on Oct 23, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions raven/contrib/webpy/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"""
raven.contrib.webpy
~~~~~~~~~~~~~~~~~~~

:copyright: (c) 2013 by the Sentry Team, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""
from __future__ import absolute_import

import sys

import web

from raven.conf import setup_logging
from raven.handlers.logging import SentryHandler
from raven.contrib.webpy.utils import get_data_from_request


class SentryApplication(web.application):
"""
Web.py application for Sentry.

>>> sentry = Sentry(client, mapping=urls, fvars=globals())

Automatically configure logging::

>>> sentry = Sentry(client, logging=True, mapping=urls, fvars=globals())

Capture an exception::

>>> try:
>>> 1 / 0
>>> except ZeroDivisionError:
>>> sentry.captureException()

Capture a message::

>>> sentry.captureMessage('hello, world!')
"""
def __init__(self, client, logging=False, **kwargs):
self.client = client
self.logging = logging
if self.logging:
setup_logging(SentryHandler(self.client))
web.application.__init__(self, **kwargs)

def handle_exception(self, *args, **kwargs):
self.client.captureException(
exc_info=kwargs.get('exc_info'),
data=get_data_from_request(),
extra={
'app': self,
},
)

def handle(self):
try:
return web.application.handle(self)
except:
self.handle_exception(exc_info=sys.exc_info())
raise

def captureException(self, *args, **kwargs):
assert self.client, 'captureException called before application configured'
data = kwargs.get('data')
if data is None:
kwargs['data'] = get_data_from_request()

return self.client.captureException(*args, **kwargs)

def captureMessage(self, *args, **kwargs):
assert self.client, 'captureMessage called before application configured'
data = kwargs.get('data')
if data is None:
kwargs['data'] = get_data_from_request()

return self.client.captureMessage(*args, **kwargs)
26 changes: 26 additions & 0 deletions raven/contrib/webpy/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""
raven.contrib.webpy.utils
~~~~~~~~~~~~~~~~~~~~~~~~~~

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

import web

from raven.utils.wsgi import get_headers, get_environ


def get_data_from_request():
"""Returns request data extracted from web.ctx."""
return {
'sentry.interfaces.Http': {
'url': '%s://%s%s' % (web.ctx['protocol'], web.ctx['host'], web.ctx['path']),
'query_string': web.ctx.query,
'method': web.ctx.method,
'data': web.data(),
'headers': dict(get_headers(web.ctx.environ)),
'env': dict(get_environ(web.ctx.environ)),
}
}
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@
'webob',
'webtest',
'anyjson',
'paste',
'web.py'
] + flask_requires + flask_tests_requires + unittest2_requires


Expand Down
Empty file added tests/contrib/webpy/__init__.py
Empty file.
85 changes: 85 additions & 0 deletions tests/contrib/webpy/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
from exam import fixture
from paste.fixture import TestApp

from raven.base import Client
from raven.contrib.webpy import SentryApplication
from raven.utils.testutils import TestCase


class TempStoreClient(Client):
def __init__(self, servers=None, **kwargs):
self.events = []
super(TempStoreClient, self).__init__(servers=servers, **kwargs)

def is_enabled(self):
return True

def send(self, **kwargs):
self.events.append(kwargs)


class TestEndpoint(object):
def GET(self):
raise ValueError('That\'s what she said')

def POST(self):
raise TypeError('Potato')


urls = (
'/test', TestEndpoint
)


def create_app(client):
return SentryApplication(client=client, mapping=urls)


class WebPyTest(TestCase):
@fixture
def app(self):
self.store = TempStoreClient()
return create_app(self.store)

@fixture
def client(self):
return TestApp(self.app.wsgifunc())

def test_get(self):
resp = self.client.get('/test', expect_errors=True)

self.assertEquals(resp.status, 500)
self.assertEquals(len(self.store.events), 1)

event = self.store.events.pop()
self.assertTrue('sentry.interfaces.Exception' in event)
exc = event['sentry.interfaces.Exception']
self.assertEquals(exc['type'], 'ValueError')
self.assertEquals(exc['value'], 'That\'s what she said')
self.assertEquals(event['message'], 'ValueError: That\'s what she said')
self.assertEquals(event['culprit'], 'tests.contrib.webpy.tests in GET')

def test_post(self):
response = self.client.post('/test?biz=baz', params={'foo': 'bar'}, expect_errors=True)
self.assertEquals(response.status, 500)
self.assertEquals(len(self.store.events), 1)

event = self.store.events.pop()

self.assertTrue('sentry.interfaces.Http' in event)
http = event['sentry.interfaces.Http']
self.assertEquals(http['url'], 'http://localhost/test')
self.assertEquals(http['query_string'], '?biz=baz')
self.assertEquals(http['method'], 'POST')
self.assertEquals(http['data'], 'foo=bar')
self.assertTrue('headers' in http)
headers = http['headers']
self.assertTrue('Content-Length' in headers, headers.keys())
self.assertEquals(headers['Content-Length'], '7')
self.assertTrue('Content-Type' in headers, headers.keys())
self.assertEquals(headers['Content-Type'], 'application/x-www-form-urlencoded')
self.assertTrue('Host' in headers, headers.keys())
self.assertEquals(headers['Host'], 'localhost')
env = http['env']
self.assertTrue('SERVER_NAME' in env, env.keys())
self.assertEquals(env['SERVER_NAME'], 'localhost')