From 590b7254c6376d0e8a104608b79065e35f933d7c Mon Sep 17 00:00:00 2001 From: Richard Graham Date: Mon, 7 Jan 2013 13:49:28 +0200 Subject: [PATCH 01/21] Add `raise_send_errors` flag to the base `Client`. This indicates whether the client will handle or raise any exceptions encountered while sending to Sentry. * The default value (False) matches the current behaviour in which the client handles the error. * Setting this argument to True allows for the calling application to handle the exception and thus determine its own retry and backoff strategies. --- raven/base.py | 11 +++++++++-- raven/contrib/django/client.py | 2 ++ tests/client/tests.py | 29 +++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/raven/base.py b/raven/base.py index 055a15db..3aaae544 100644 --- a/raven/base.py +++ b/raven/base.py @@ -120,9 +120,11 @@ class Client(object): _registry = TransportRegistry(transports=default_transports) - def __init__(self, dsn=None, **options): + def __init__(self, dsn=None, raise_send_errors=False, **options): o = options + self.raise_send_errors = raise_send_errors + # configure loggers first cls = self.__class__ self.state = ClientState() @@ -427,7 +429,10 @@ class Client(object): return bool(self.servers) def send_remote(self, url, data, headers={}): - if not self.state.should_try(): + # If the client is configured to raise errors on sending, + # the implication is that the backoff and retry strategies + # will be handled by the calling application + if not self.raise_send_errors and not self.state.should_try(): message = self._get_log_message(data) self.error_logger.error(message) return @@ -435,6 +440,8 @@ class Client(object): try: self._send_remote(url=url, data=data, headers=headers) except Exception, e: + if self.raise_send_errors: + raise if isinstance(e, urllib2.HTTPError): body = e.read() self.error_logger.error('Unable to reach Sentry log server: %s (url: %%s, body: %%s)' % (e,), url, body, diff --git a/raven/contrib/django/client.py b/raven/contrib/django/client.py index b19e8f5e..70385ced 100644 --- a/raven/contrib/django/client.py +++ b/raven/contrib/django/client.py @@ -130,6 +130,8 @@ class DjangoClient(Client): try: return self.send_integrated(kwargs) except Exception, e: + if self.raise_send_errors: + raise self.error_logger.error('Unable to record event: %s', e, exc_info=True) def send_integrated(self, kwargs): diff --git a/tests/client/tests.py b/tests/client/tests.py index c58b507c..00a77266 100644 --- a/tests/client/tests.py +++ b/tests/client/tests.py @@ -133,6 +133,35 @@ class ClientTest(TestCase): }, ) + @mock.patch('raven.base.Client._send_remote') + @mock.patch('raven.base.ClientState.should_try') + def test_raise_exception_on_send_error(self, should_try, _send_remote): + should_try.return_value = True + client = Client( + servers=['http://example.com'], + public_key='public', + secret_key='secret', + project=1, + ) + + # Test for the default behaviour in which a send error is handled by the client + _send_remote.side_effect = Exception() + client.capture('Message', data={}, date=None, time_spent=10, + extra={}, stack=None, tags=None, message='Test message') + self.assertEquals(client.state.status, client.state.ERROR) + + # Test for the case in which a send error is raised to the calling frame. + client = Client( + servers=['http://example.com'], + public_key='public', + secret_key='secret', + project=1, + raise_send_errors=True, + ) + with self.assertRaises(Exception): + client.capture('Message', data={}, date=None, time_spent=10, + extra={}, stack=None, tags=None, message='Test message') + def test_encode_decode(self): data = {'foo': 'bar'} encoded = self.client.encode(data) From 7e2e765c50b0b5814931e8e7cdf7f7c3cf964f96 Mon Sep 17 00:00:00 2001 From: Richard Graham Date: Mon, 7 Jan 2013 15:15:51 +0200 Subject: [PATCH 02/21] If a SentryHandler's client is set with `raise_send_errors = True`, do not handle the errors in `SentryHandler.emit`. --- raven/handlers/logbook.py | 2 ++ raven/handlers/logging.py | 2 ++ tests/handlers/logging/tests.py | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/raven/handlers/logbook.py b/raven/handlers/logbook.py index 5972dba3..0ed96a49 100644 --- a/raven/handlers/logbook.py +++ b/raven/handlers/logbook.py @@ -48,6 +48,8 @@ class SentryHandler(logbook.Handler): return self._emit(record) except Exception: + if self.client.raise_send_errors: + raise print >> sys.stderr, "Top level Sentry exception caught - failed creating log record" print >> sys.stderr, to_string(record.msg) print >> sys.stderr, to_string(traceback.format_exc()) diff --git a/raven/handlers/logging.py b/raven/handlers/logging.py index c59dee6c..c4c48f80 100644 --- a/raven/handlers/logging.py +++ b/raven/handlers/logging.py @@ -55,6 +55,8 @@ class SentryHandler(logging.Handler, object): return self._emit(record) except Exception: + if self.client.raise_send_errors: + raise print >> sys.stderr, "Top level Sentry exception caught - failed creating log record" print >> sys.stderr, to_string(record.msg) print >> sys.stderr, to_string(traceback.format_exc()) diff --git a/tests/handlers/logging/tests.py b/tests/handlers/logging/tests.py index f82d9ae0..e7299d05 100644 --- a/tests/handlers/logging/tests.py +++ b/tests/handlers/logging/tests.py @@ -1,5 +1,6 @@ import logging import sys +import mock from unittest2 import TestCase from raven.base import Client from raven.handlers.logging import SentryHandler @@ -46,6 +47,37 @@ class LoggingIntegrationTest(TestCase): self.assertEquals(msg['message'], 'This is a test error') self.assertEquals(msg['params'], ()) + @mock.patch('raven.base.Client._send_remote') + @mock.patch('raven.base.ClientState.should_try') + def test_exception_on_emit(self, should_try, _send_remote): + should_try.return_value = True + # Test for the default behaviour in which an exception is handled by the client or handler + client = Client( + servers=['http://example.com'], + public_key='public', + secret_key='secret', + project=1, + ) + handler = SentryHandler(client) + _send_remote.side_effect = Exception() + record = self.make_record('This is a test error') + handler.emit(record) + self.assertEquals(handler.client.state.status, handler.client.state.ERROR) + + # Test for the case in which a send error is raised to the calling frame. + client = Client( + servers=['http://example.com'], + public_key='public', + secret_key='secret', + project=1, + raise_send_errors=True, + ) + handler = SentryHandler(client) + _send_remote.side_effect = Exception() + with self.assertRaises(Exception): + record = self.make_record('This is a test error') + handler.emit(record) + def test_logger_extra_data(self): record = self.make_record('This is a test error', extra={'data': { 'url': 'http://example.com', From 75c6e4a1e78d52d698704ebe3a39a35b51c8ade7 Mon Sep 17 00:00:00 2001 From: Chirila Alexandru Date: Fri, 14 Mar 2014 08:43:28 +0200 Subject: [PATCH 03/21] Use lstrip to get rid of leading slashes, because in some cases the paths have already been replaced. --- raven/utils/stacks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/raven/utils/stacks.py b/raven/utils/stacks.py index 4e3f3430..a5894bbb 100644 --- a/raven/utils/stacks.py +++ b/raven/utils/stacks.py @@ -233,7 +233,7 @@ def get_stack_info(frames, transformer=transform, capture_locals=True, # This changes /foo/site-packages/baz/bar.py into baz/bar.py try: base_filename = sys.modules[module_name.split('.', 1)[0]].__file__ - filename = abs_path.split(base_filename.rsplit('/', 2)[0], 1)[-1][1:] + filename = abs_path.split(base_filename.rsplit('/', 2)[0], 1)[-1].lstrip("/") except: filename = abs_path From 8fe0aa7bf684f7859e14c27b0e462021e11f1ac2 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 31 Mar 2014 14:43:43 +0100 Subject: [PATCH 04/21] Ensure that ThreadedHTTPTransport sends all pending messages at exit A race condition in the ThreadedHTTPTransport exit hook meant that it was possible for some queued messages to not be sent. Rearrange the shutdown hook a bit to ensure that this cannot happen. --- raven/transport/threaded.py | 82 +++++++++++++++++++++++++------ tests/transport/threaded/tests.py | 33 ++++++++++--- 2 files changed, 92 insertions(+), 23 deletions(-) diff --git a/raven/transport/threaded.py b/raven/transport/threaded.py index e2479512..78fe5575 100644 --- a/raven/transport/threaded.py +++ b/raven/transport/threaded.py @@ -35,16 +35,65 @@ class AsyncWorker(object): self.start() def main_thread_terminated(self): - size = self._queue.qsize() - if size: + self._lock.acquire() + try: + if not self._thread: + # thread not started or already stopped - nothing to do + return + + # wake the processing thread up + self._queue.put_nowait(self._terminator) + timeout = self.options['shutdown_timeout'] - print("Sentry is attempting to send %s pending error messages" % size) - print("Waiting up to %s seconds" % timeout) - if os.name == 'nt': - print("Press Ctrl-Break to quit") - else: - print("Press Ctrl-C to quit") - self.stop(timeout=timeout) + + # wait briefly, initially + initial_timeout = 0.1 + if timeout < initial_timeout: + initial_timeout = timeout + + if not self._timed_queue_join(initial_timeout): + # if that didn't work, wait a bit longer + # NB that size is an approximation, because other threads may + # add or remove items + size = self._queue.qsize() + + print("Sentry is attempting to send %i pending error messages" + % size) + print("Waiting up to %s seconds" % timeout) + if os.name == 'nt': + print("Press Ctrl-Break to quit") + else: + print("Press Ctrl-C to quit") + self._timed_queue_join(timeout - initial_timeout) + + self._thread = None + + finally: + self._lock.release() + + def _timed_queue_join(self, timeout): + """ + implementation of Queue.join which takes a 'timeout' argument + + returns true on success, false on timeout + """ + deadline = time.time() + timeout + queue = self._queue + + queue.all_tasks_done.acquire() + try: + while queue.unfinished_tasks: + delay = deadline - time.time() + if(delay <= 0): + # timed out + return False + + queue.all_tasks_done.wait(timeout=delay) + + return True + + finally: + queue.all_tasks_done.release() def start(self): """ @@ -79,13 +128,16 @@ class AsyncWorker(object): def _target(self): while 1: record = self._queue.get() - if record is self._terminator: - break - callback, args, kwargs = record try: - callback(*args, **kwargs) - except Exception: - logger.error('Failed processing job', exc_info=True) + if record is self._terminator: + break + callback, args, kwargs = record + try: + callback(*args, **kwargs) + except Exception: + logger.error('Failed processing job', exc_info=True) + finally: + self._queue.task_done() time.sleep(0) diff --git a/tests/transport/threaded/tests.py b/tests/transport/threaded/tests.py index df787ca8..48b772f9 100644 --- a/tests/transport/threaded/tests.py +++ b/tests/transport/threaded/tests.py @@ -4,25 +4,27 @@ from raven.utils.testutils import TestCase from raven.base import Client from raven.transport.threaded import ThreadedHTTPTransport +from raven.utils.urlparse import urlparse class DummyThreadedScheme(ThreadedHTTPTransport): - - scheme = ['threaded+mock'] - def __init__(self, *args, **kwargs): super(ThreadedHTTPTransport, self).__init__(*args, **kwargs) self.events = [] + self.send_delay = 0 - def send_sync(self, data, headers): - self.events.append((data, headers)) + def send_sync(self, data, headers, success_cb, failure_cb): + # delay sending the message, to allow us to test that the shutdown + # hook waits correctly + time.sleep(self.send_delay) + + self.events.append((data, headers, success_cb, failure_cb)) class ThreadedTransportTest(TestCase): def setUp(self): - self.client = Client( - dsn="threaded+http://some_username:some_password@localhost:8143/1", - ) + self.url = "threaded+http://some_username:some_password@localhost:8143/1" + self.client = Client(dsn=self.url) @mock.patch('raven.transport.http.HTTPTransport.send') def test_does_send(self, send): @@ -33,3 +35,18 @@ class ThreadedTransportTest(TestCase): # TODO: This test could be more precise by ensuring it's sending the same params that are sent # to the ThreadedHTTPTransport.send() method self.assertEqual(send.call_count, 1) + + def test_shutdown_waits_for_send(self): + url = urlparse(self.url) + transport = DummyThreadedScheme(url) + transport.send_delay = 0.5 + + data = self.client.build_msg('raven.events.Message', message='foo') + transport.async_send(data, None, None, None) + + time.sleep(0.1) + + # this should wait for the message to get sent + transport.get_worker().main_thread_terminated() + + self.assertEqual(len(transport.events), 1) From 8d81e690b3b1e0e2a5c2ccb9a1e63ea335fa568b Mon Sep 17 00:00:00 2001 From: Tomek Date: Tue, 29 Apr 2014 10:51:19 +0200 Subject: [PATCH 05/21] Fix BaseUDPTransport in python3. --- raven/transport/udp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/raven/transport/udp.py b/raven/transport/udp.py index ff7a590e..3edc323f 100644 --- a/raven/transport/udp.py +++ b/raven/transport/udp.py @@ -48,7 +48,7 @@ class BaseUDPTransport(Transport): host, port = self._parsed_url.netloc.rsplit(':') addr_info = self._get_addr_info(host, int(port)) - self._send_data(auth_header + '\n\n' + data, addr_info) + self._send_data(auth_header.encode('utf-8') + b'\n\n' + data, addr_info) class UDPTransport(BaseUDPTransport): From 0b518aa3ce8ece2890516c24acacf80e660f68cb Mon Sep 17 00:00:00 2001 From: Tomek Date: Tue, 6 May 2014 08:34:55 +0200 Subject: [PATCH 06/21] Add unicode_literals import to udp transport --- raven/transport/udp.py | 1 + 1 file changed, 1 insertion(+) diff --git a/raven/transport/udp.py b/raven/transport/udp.py index 3edc323f..5aa09450 100644 --- a/raven/transport/udp.py +++ b/raven/transport/udp.py @@ -6,6 +6,7 @@ raven.transport.udp :license: BSD, see LICENSE for more details. """ from __future__ import absolute_import +from __future__ import unicode_literals from raven.transport.base import Transport From f7ae1383cb7257ba288a221ff493e4d48bef4b7b Mon Sep 17 00:00:00 2001 From: Matt Robenolt Date: Sat, 17 May 2014 17:24:42 -0700 Subject: [PATCH 07/21] Test against more pythons --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index bc47e345..38c5e0a1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,8 @@ python: - "2.7" - "3.2" - "3.3" + - "3.4" + - "pypy" env: matrix: - DJANGO=Django==1.4.10 @@ -46,6 +48,8 @@ matrix: env: DJANGO=Django==1.4.10 - python: "3.3" env: DJANGO=Django==1.4.10 + - python: "3.4" + env: DJANGO=Django==1.4.10 - python: "2.6" env: DJANGO="-e git+git://github.com/django/django.git#egg=Django" - python: "2.6" From c22d247fc0f53357edcf15f3914a0a5185da5f9a Mon Sep 17 00:00:00 2001 From: David Cramer Date: Sun, 18 May 2014 01:09:33 -0700 Subject: [PATCH 08/21] 4.2.2 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 6d36b97e..4ceedc3c 100755 --- a/setup.py +++ b/setup.py @@ -92,7 +92,7 @@ class PyTest(TestCommand): setup( name='raven', - version='4.2.1', + version='4.2.2', author='David Cramer', author_email='dcramer@gmail.com', url='http://github.com/getsentry/raven-python', From b629d5ce42f8aeee86bf41bd1e1740bebbf93266 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Sun, 18 May 2014 01:19:54 -0700 Subject: [PATCH 09/21] Gracefully handle disabled clients --- raven/contrib/flask.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/raven/contrib/flask.py b/raven/contrib/flask.py index 39edcf6b..d00a2d1d 100644 --- a/raven/contrib/flask.py +++ b/raven/contrib/flask.py @@ -207,11 +207,17 @@ class Sentry(object): def captureException(self, *args, **kwargs): assert self.client, 'captureException called before application configured' result = self.client.captureException(*args, **kwargs) - self.last_event_id = self.client.get_ident(result) + if result: + self.last_event_id = self.client.get_ident(result) + else: + self.last_event_id = None return result def captureMessage(self, *args, **kwargs): assert self.client, 'captureMessage called before application configured' result = self.client.captureMessage(*args, **kwargs) - self.last_event_id = self.client.get_ident(result) + if result: + self.last_event_id = self.client.get_ident(result) + else: + self.last_event_id = None return result From 757e060901b99fef6bb2fa6e0d4b1b79ef5e015b Mon Sep 17 00:00:00 2001 From: David Cramer Date: Sun, 18 May 2014 01:20:02 -0700 Subject: [PATCH 10/21] 4.2.3 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 4ceedc3c..7a303df8 100755 --- a/setup.py +++ b/setup.py @@ -92,7 +92,7 @@ class PyTest(TestCommand): setup( name='raven', - version='4.2.2', + version='4.2.3', author='David Cramer', author_email='dcramer@gmail.com', url='http://github.com/getsentry/raven-python', From 39c65bf8b500ed62d8469f5bc7ffe08badcf194c Mon Sep 17 00:00:00 2001 From: David Cramer Date: Tue, 20 May 2014 12:02:18 -0700 Subject: [PATCH 11/21] Update interfaces to use protocol 5/aliases --- raven/base.py | 24 +++++----- raven/contrib/bottle/utils.py | 2 +- raven/contrib/django/client.py | 13 +++--- raven/contrib/django/utils.py | 2 +- raven/contrib/tornado/__init__.py | 4 +- raven/contrib/webpy/utils.py | 2 +- raven/contrib/zope/__init__.py | 7 ++- raven/events.py | 25 +++++++---- raven/handlers/logging.py | 2 +- raven/processors.py | 14 +++--- raven/scripts/runner.py | 2 +- tests/base/tests.py | 18 ++++---- tests/contrib/bottle/tests.py | 18 +++----- tests/contrib/django/tests.py | 60 ++++++++++++------------- tests/contrib/flask/tests.py | 28 ++++++------ tests/contrib/tornado/tests.py | 74 +++++++++++++------------------ tests/contrib/webpy/tests.py | 8 ++-- tests/contrib/zerorpc/tests.py | 8 ++-- tests/handlers/logbook/tests.py | 12 ++--- tests/handlers/logging/tests.py | 16 +++---- tests/middleware/tests.py | 8 ++-- tests/processors/tests.py | 43 +++++++++--------- 22 files changed, 191 insertions(+), 199 deletions(-) diff --git a/raven/base.py b/raven/base.py index 0dfd3e16..0c422667 100644 --- a/raven/base.py +++ b/raven/base.py @@ -296,7 +296,7 @@ class Client(object): if k not in data: data[k] = v - if stack and 'sentry.interfaces.Stacktrace' not in data: + if stack and 'stacktrace' not in data: if stack is True: frames = iter_stack_frames() @@ -309,12 +309,12 @@ class Client(object): capture_locals=self.capture_locals, ) data.update({ - 'sentry.interfaces.Stacktrace': stack_info, + 'stacktrace': stack_info, }) - if 'sentry.interfaces.Stacktrace' in data: + if 'stacktrace' in data: if self.include_paths: - for frame in data['sentry.interfaces.Stacktrace']['frames']: + for frame in data['stacktrace']['frames']: if frame.get('in_app') is not None: continue @@ -332,10 +332,12 @@ class Client(object): ) if not culprit: - if 'sentry.interfaces.Stacktrace' in data: - culprit = get_culprit(data['sentry.interfaces.Stacktrace']['frames']) - elif data.get('sentry.interfaces.Exception', {}).get('stacktrace'): - culprit = get_culprit(data['sentry.interfaces.Exception']['stacktrace']['frames']) + if 'stacktrace' in data: + culprit = get_culprit(data['stacktrace']['frames']) + elif 'exception' in data: + stacktrace = data['exception']['values'][0].get('stacktrace') + if stacktrace: + culprit = get_culprit(stacktrace['frames']) if not data.get('level'): data['level'] = kwargs.get('level') or logging.ERROR @@ -407,7 +409,7 @@ class Client(object): >>> client.user_context({'email': 'foo@example.com'}) """ return self.context.merge({ - 'sentry.interfaces.User': data, + 'user': data, }) def http_context(self, data, **kwargs): @@ -417,7 +419,7 @@ class Client(object): >>> client.http_context({'url': 'http://example.com'}) """ return self.context.merge({ - 'sentry.interfaces.Http': data, + 'request': data, }) def extra_context(self, data, **kwargs): @@ -448,7 +450,7 @@ class Client(object): To use structured data (interfaces) with capture: >>> capture('raven.events.Message', message='foo', data={ - >>> 'sentry.interfaces.Http': { + >>> 'request': { >>> 'url': '...', >>> 'data': {}, >>> 'query_string': '...', diff --git a/raven/contrib/bottle/utils.py b/raven/contrib/bottle/utils.py index 9c755778..d19903ba 100644 --- a/raven/contrib/bottle/utils.py +++ b/raven/contrib/bottle/utils.py @@ -26,7 +26,7 @@ def get_data_from_request(request): formdata = {} data = { - 'sentry.interfaces.Http': { + 'request': { 'url': '%s://%s%s' % (urlparts.scheme, urlparts.netloc, urlparts.path), 'query_string': urlparts.query, 'method': request.method, diff --git a/raven/contrib/django/client.py b/raven/contrib/django/client.py index 8646cbb0..22bf4f1a 100644 --- a/raven/contrib/django/client.py +++ b/raven/contrib/django/client.py @@ -58,7 +58,7 @@ class DjangoClient(Client): result = {} if hasattr(request, 'user') and isinstance(request.user, BaseUser): - result['sentry.interfaces.User'] = self.get_user_info(request.user) + result['user'] = self.get_user_info(request.user) try: uri = request.build_absolute_uri() @@ -90,7 +90,7 @@ class DjangoClient(Client): environ = request.META result.update({ - 'sentry.interfaces.Http': { + 'request': { 'method': request.method, 'url': uri, 'query_string': request.META.get('QUERY_STRING'), @@ -106,10 +106,11 @@ class DjangoClient(Client): def build_msg(self, *args, **kwargs): data = super(DjangoClient, self).build_msg(*args, **kwargs) - stacks = ( - data.get('sentry.interfaces.Stacktrace'), - data.get('sentry.interfaces.Exception', {}).get('stacktrace'), - ) + stacks = [ + data.get('stacktrace'), + ] + if 'exception' in data: + stacks.append(data['exception']['values'][0]['stacktrace']) for stacktrace in filter(bool, stacks): for frame in stacktrace['frames']: diff --git a/raven/contrib/django/utils.py b/raven/contrib/django/utils.py index 39d7c8a3..a6192066 100644 --- a/raven/contrib/django/utils.py +++ b/raven/contrib/django/utils.py @@ -41,7 +41,7 @@ def get_data_from_template(source): context_line = source_lines[lineno] return { - 'sentry.interfaces.Template': { + 'template': { 'filename': origin.loadname, 'abs_path': origin.name, 'pre_context': pre_context, diff --git a/raven/contrib/tornado/__init__.py b/raven/contrib/tornado/__init__.py index 0ca9355b..0ba27606 100644 --- a/raven/contrib/tornado/__init__.py +++ b/raven/contrib/tornado/__init__.py @@ -183,7 +183,7 @@ class SentryMixin(object): :param return: A dictionary. """ return { - 'sentry.interfaces.Http': { + 'request': { 'url': self.request.full_url(), 'method': self.request.method, 'data': self.request.body, @@ -202,7 +202,7 @@ class SentryMixin(object): Truth calue testing """ return { - 'sentry.interfaces.User': { + 'user': { 'is_authenticated': True if self.get_current_user() else False } } diff --git a/raven/contrib/webpy/utils.py b/raven/contrib/webpy/utils.py index 781cb4b2..bc4fb4b9 100644 --- a/raven/contrib/webpy/utils.py +++ b/raven/contrib/webpy/utils.py @@ -15,7 +15,7 @@ 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': { + 'request': { 'url': '%s://%s%s' % (web.ctx['protocol'], web.ctx['host'], web.ctx['path']), 'query_string': web.ctx.query, 'method': web.ctx.method, diff --git a/raven/contrib/zope/__init__.py b/raven/contrib/zope/__init__.py index 126a62df..ca079c4c 100644 --- a/raven/contrib/zope/__init__.py +++ b/raven/contrib/zope/__init__.py @@ -86,9 +86,8 @@ class ZopeSentryHandler(SentryHandler): http['headers']['User-Agent'] = \ http['headers']['HTTP_USER_AGENT'] if 'QUERY_STRING' in http['headers']: - http['query_string'] = http['headers' - ]['QUERY_STRING'] - setattr(record, 'sentry.interfaces.Http', http) + http['query_string'] = http['headers']['QUERY_STRING'] + setattr(record, 'request', http) user = request.get('AUTHENTICATED_USER', None) if user is not None: user_dict = dict(id=user.getId(), @@ -96,7 +95,7 @@ class ZopeSentryHandler(SentryHandler): email=user.getProperty('email') or '') else: user_dict = {'is_authenticated': False} - setattr(record, 'sentry.interfaces.User', user_dict) + setattr(record, 'user', user_dict) except (AttributeError, KeyError): logger.warning('Could not extract data from request', exc_info=True) return super(ZopeSentryHandler, self).emit(record) diff --git a/raven/events.py b/raven/events.py index afc03e85..f0395e80 100644 --- a/raven/events.py +++ b/raven/events.py @@ -41,9 +41,10 @@ class Exception(BaseEvent): - module '__builtin__' (i.e. __builtin__.TypeError) - frames: a list of serialized frames (see _get_traceback_frames) """ + name = 'exception' def to_string(self, data): - exc = data['sentry.interfaces.Exception'] + exc = data[self.name]['values'][0] if exc['value']: return '%s: %s' % (exc['type'], exc['value']) return exc['type'] @@ -71,11 +72,13 @@ class Exception(BaseEvent): return { 'level': kwargs.get('level', logging.ERROR), - 'sentry.interfaces.Exception': { - 'value': to_unicode(exc_value), - 'type': str(exc_type), - 'module': to_unicode(exc_module), - 'stacktrace': stack_info, + self.name: { + 'values': [{ + 'value': to_unicode(exc_value), + 'type': str(exc_type), + 'module': to_unicode(exc_module), + 'stacktrace': stack_info, + }], }, } finally: @@ -92,10 +95,12 @@ class Message(BaseEvent): - message: 'My message from %s about %s' - params: ('foo', 'bar') """ + name = 'sentry.interfaces.Message' + def capture(self, message, params=(), formatted=None, **kwargs): message = to_unicode(message) data = { - 'sentry.interfaces.Message': { + self.name: { 'message': message, 'params': self.transform(params), }, @@ -112,13 +117,15 @@ class Query(BaseEvent): - query: 'SELECT * FROM table' - engine: 'postgesql_psycopg2' """ + name = 'sentry.interfaces.Query' + def to_string(self, data): - sql = data['sentry.interfaces.Query'] + sql = data[self.name] return sql['query'] def capture(self, query, engine, **kwargs): return { - 'sentry.interfaces.Query': { + self.name: { 'query': to_unicode(query), 'engine': str(engine), } diff --git a/raven/handlers/logging.py b/raven/handlers/logging.py index 1652f5a4..ab338b6d 100644 --- a/raven/handlers/logging.py +++ b/raven/handlers/logging.py @@ -159,7 +159,7 @@ class SentryHandler(logging.Handler, object): handler_kwargs = {'exc_info': record.exc_info} # HACK: discover a culprit when we normally couldn't - elif not (data.get('sentry.interfaces.Stacktrace') or data.get('culprit')) and (record.name or record.funcName): + elif not (data.get('stacktrace') or data.get('culprit')) and (record.name or record.funcName): culprit = label_from_frame({'module': record.name, 'function': record.funcName}) if culprit: data['culprit'] = culprit diff --git a/raven/processors.py b/raven/processors.py index 963f1559..1f218ea9 100644 --- a/raven/processors.py +++ b/raven/processors.py @@ -25,15 +25,15 @@ class Processor(object): if resp: data = resp - if 'sentry.interfaces.Stacktrace' in data: - self.filter_stacktrace(data['sentry.interfaces.Stacktrace']) + if 'stacktrace' in data: + self.filter_stacktrace(data['stacktrace']) - if 'sentry.interfaces.Exception' in data: - if 'stacktrace' in data['sentry.interfaces.Exception']: - self.filter_stacktrace(data['sentry.interfaces.Exception']['stacktrace']) + if 'exception' in data: + if 'stacktrace' in data['exception']: + self.filter_stacktrace(data['exception']['stacktrace']) - if 'sentry.interfaces.Http' in data: - self.filter_http(data['sentry.interfaces.Http']) + if 'request' in data: + self.filter_http(data['request']) return data diff --git a/raven/scripts/runner.py b/raven/scripts/runner.py index f99f12d7..4f985442 100644 --- a/raven/scripts/runner.py +++ b/raven/scripts/runner.py @@ -59,7 +59,7 @@ def send_test_message(client, options): data = options.get('data', { 'culprit': 'raven.scripts.runner', 'logger': 'raven.test', - 'sentry.interfaces.Http': { + 'request': { 'method': 'GET', 'url': 'http://example.com', } diff --git a/tests/base/tests.py b/tests/base/tests.py index 94df4a01..96b849ea 100644 --- a/tests/base/tests.py +++ b/tests/base/tests.py @@ -291,12 +291,12 @@ class ClientTest(TestCase): self.assertEquals(len(self.client.events), 1) event = self.client.events.pop(0) self.assertEquals(event['message'], 'ValueError: foo') - self.assertTrue('sentry.interfaces.Exception' in event) - exc = event['sentry.interfaces.Exception'] + self.assertTrue('exception' in event) + exc = event['exception']['values'][0] self.assertEquals(exc['type'], 'ValueError') self.assertEquals(exc['value'], 'foo') self.assertEquals(exc['module'], ValueError.__module__) # this differs in some Python versions - assert 'sentry.interfaces.Stacktrace' not in event + assert 'stacktrace' not in event stacktrace = exc['stacktrace'] self.assertEquals(len(stacktrace['frames']), 1) frame = stacktrace['frames'][0] @@ -329,7 +329,7 @@ class ClientTest(TestCase): self.assertEquals(len(self.client.events), 1) event = self.client.events.pop(0) self.assertEquals(event['message'], 'DecoratorTestException') - exc = event['sentry.interfaces.Exception'] + exc = event['exception']['values'][0] self.assertEquals(exc['type'], 'DecoratorTestException') self.assertEquals(exc['module'], self.DecoratorTestException.__module__) stacktrace = exc['stacktrace'] @@ -357,7 +357,7 @@ class ClientTest(TestCase): self.assertEquals(len(self.client.events), 1) event = self.client.events.pop(0) self.assertEquals(event['message'], 'test') - self.assertFalse('sentry.interfaces.Stacktrace' in event) + assert 'stacktrace' not in event self.assertTrue('timestamp' in event) def test_context(self): @@ -386,9 +386,9 @@ class ClientTest(TestCase): self.assertEquals(len(self.client.events), 1) event = self.client.events.pop(0) self.assertEquals(event['message'], 'test') - self.assertTrue('sentry.interfaces.Stacktrace' in event) - self.assertEquals(len(frames), len(event['sentry.interfaces.Stacktrace']['frames'])) - for frame, frame_i in zip(frames, event['sentry.interfaces.Stacktrace']['frames']): + assert 'stacktrace' in event + self.assertEquals(len(frames), len(event['stacktrace']['frames'])) + for frame, frame_i in zip(frames, event['stacktrace']['frames']): self.assertEquals(frame[0].f_code.co_filename, frame_i['abs_path']) self.assertEquals(frame[0].f_code.co_name, frame_i['function']) @@ -398,7 +398,7 @@ class ClientTest(TestCase): self.assertEquals(len(self.client.events), 1) event = self.client.events.pop(0) self.assertEquals(event['message'], 'test') - self.assertTrue('sentry.interfaces.Stacktrace' in event) + self.assertTrue('stacktrace' in event) self.assertTrue('timestamp' in event) def test_site(self): diff --git a/tests/contrib/bottle/tests.py b/tests/contrib/bottle/tests.py index 92cdbc01..18439ec0 100644 --- a/tests/contrib/bottle/tests.py +++ b/tests/contrib/bottle/tests.py @@ -1,5 +1,3 @@ -import logging - from exam import fixture from webtest import TestApp @@ -66,14 +64,10 @@ class BottleTest(BaseTest): self.assertEquals(len(self.raven.events), 1) event = self.raven.events.pop(0) - self.assertTrue('sentry.interfaces.Exception' in event) + assert 'exception' in event - exc = event['sentry.interfaces.Exception'] + exc = event['exception']['values'][0] self.assertEquals(exc['type'], 'ValueError') - self.assertEquals(exc['value'], 'hello world') - self.assertEquals(event['level'], logging.ERROR) - self.assertEquals(event['message'], 'ValueError: hello world') - self.assertEquals(event['culprit'], 'tests.contrib.bottle.tests in an_error') def test_captureException_captures_http(self): response = self.client.get('/capture/?foo=bar') @@ -83,8 +77,8 @@ class BottleTest(BaseTest): event = self.raven.events.pop(0) assert event['message'] == 'ValueError: Boom' - assert 'sentry.interfaces.Http' in event - assert 'sentry.interfaces.Exception' in event + assert 'request' in event + assert 'exception' in event def test_captureMessage_captures_http(self): response = self.client.get('/message/?foo=bar') @@ -93,5 +87,5 @@ class BottleTest(BaseTest): event = self.raven.events.pop(0) - self.assertTrue('sentry.interfaces.Message' in event) - self.assertTrue('sentry.interfaces.Http' in event) + assert 'sentry.interfaces.Message' in event + assert 'request' in event diff --git a/tests/contrib/django/tests.py b/tests/contrib/django/tests.py index 8ead4c65..490972d9 100644 --- a/tests/contrib/django/tests.py +++ b/tests/contrib/django/tests.py @@ -147,8 +147,8 @@ class DjangoClientTest(TestCase): self.assertEquals(len(self.raven.events), 1) event = self.raven.events.pop(0) - self.assertTrue('sentry.interfaces.Exception' in event) - exc = event['sentry.interfaces.Exception'] + assert 'exception' in event + exc = event['exception']['values'][0] self.assertEquals(exc['type'], 'ValueError') self.assertEquals(exc['value'], "invalid literal for int() with base 10: 'hello'") self.assertEquals(event['level'], logging.ERROR) @@ -160,8 +160,8 @@ class DjangoClientTest(TestCase): self.assertEquals(len(self.raven.events), 1) event = self.raven.events.pop(0) - self.assertTrue('sentry.interfaces.Exception' in event) - exc = event['sentry.interfaces.Exception'] + assert 'exception' in event + exc = event['exception']['values'][0] self.assertEquals(exc['type'], 'Exception') self.assertEquals(exc['value'], 'view exception') self.assertEquals(event['level'], logging.ERROR) @@ -178,7 +178,7 @@ class DjangoClientTest(TestCase): assert len(self.raven.events) == 1 event = self.raven.events.pop(0) - assert 'sentry.interfaces.User' not in event + assert 'user' not in event assert self.client.login(username='admin', password='admin') @@ -186,8 +186,8 @@ class DjangoClientTest(TestCase): self.assertEquals(len(self.raven.events), 1) event = self.raven.events.pop(0) - assert 'sentry.interfaces.User' in event - user_info = event['sentry.interfaces.User'] + assert 'user' in event + user_info = event['user'] assert user_info == { 'is_authenticated': True, 'username': user.username, @@ -226,8 +226,8 @@ class DjangoClientTest(TestCase): self.assertEquals(len(self.raven.events), 1) event = self.raven.events.pop(0) - self.assertTrue('sentry.interfaces.Exception' in event) - exc = event['sentry.interfaces.Exception'] + assert 'exception' in event + exc = event['exception']['values'][0] self.assertEquals(exc['type'], 'ImportError') self.assertEquals(exc['value'], 'request') self.assertEquals(event['level'], logging.ERROR) @@ -243,8 +243,8 @@ class DjangoClientTest(TestCase): self.assertEquals(len(self.raven.events), 1) event = self.raven.events.pop(0) - self.assertTrue('sentry.interfaces.Exception' in event) - exc = event['sentry.interfaces.Exception'] + assert 'exception' in event + exc = event['exception']['values'][0] self.assertEquals(exc['type'], 'ImportError') self.assertEquals(exc['value'], 'response') self.assertEquals(event['level'], logging.ERROR) @@ -261,8 +261,8 @@ class DjangoClientTest(TestCase): assert len(self.raven.events) == 2 event = self.raven.events.pop(0) - self.assertTrue('sentry.interfaces.Exception' in event) - exc = event['sentry.interfaces.Exception'] + assert 'exception' in event + exc = event['exception']['values'][0] self.assertEquals(exc['type'], 'Exception') self.assertEquals(exc['value'], 'view exception') self.assertEquals(event['level'], logging.ERROR) @@ -271,8 +271,8 @@ class DjangoClientTest(TestCase): event = self.raven.events.pop(0) - self.assertTrue('sentry.interfaces.Exception' in event) - exc = event['sentry.interfaces.Exception'] + assert 'exception' in event + exc = event['exception']['values'][0] self.assertEquals(exc['type'], 'ValueError') self.assertEquals(exc['value'], 'handler500') self.assertEquals(event['level'], logging.ERROR) @@ -286,8 +286,8 @@ class DjangoClientTest(TestCase): self.assertEquals(len(self.raven.events), 1) event = self.raven.events.pop(0) - self.assertTrue('sentry.interfaces.Exception' in event) - exc = event['sentry.interfaces.Exception'] + assert 'exception' in event + exc = event['exception']['values'][0] self.assertEquals(exc['type'], 'ImportError') self.assertEquals(exc['value'], 'view') self.assertEquals(event['level'], logging.ERROR) @@ -368,8 +368,8 @@ class DjangoClientTest(TestCase): self.assertEquals(event['level'], logging.INFO) self.assertEquals(event['logger'], 'http404') - self.assertTrue('sentry.interfaces.Http' in event) - http = event['sentry.interfaces.Http'] + assert 'request' in event + http = event['request'] self.assertEquals(http['url'], 'http://testserver/non-existent-page') self.assertEquals(http['method'], 'GET') self.assertEquals(http['query_string'], '') @@ -434,8 +434,8 @@ class DjangoClientTest(TestCase): self.assertEquals(len(self.raven.events), 1) event = self.raven.events.pop(0) - self.assertTrue('sentry.interfaces.Http' in event) - http = event['sentry.interfaces.Http'] + assert 'request' in event + http = event['request'] self.assertEquals(http['method'], 'POST') self.assertEquals(http['data'], '') @@ -449,8 +449,8 @@ class DjangoClientTest(TestCase): self.assertEquals(len(self.raven.events), 1) event = self.raven.events.pop(0) - self.assertTrue('sentry.interfaces.Http' in event) - http = event['sentry.interfaces.Http'] + assert 'request' in event + http = event['request'] self.assertEquals(http['method'], 'POST') self.assertEquals(http['data'], {'foo': 'bar', 'ham': 'spam'}) @@ -463,8 +463,8 @@ class DjangoClientTest(TestCase): self.assertEquals(len(self.raven.events), 1) event = self.raven.events.pop(0) - self.assertTrue('sentry.interfaces.Http' in event) - http = event['sentry.interfaces.Http'] + assert 'request' in event + http = event['request'] self.assertEquals(http['method'], 'POST') self.assertEquals(http['data'], '') self.assertTrue('headers' in http) @@ -483,7 +483,7 @@ class DjangoClientTest(TestCase): self.assertEquals(len(self.raven.events), 1) event = self.raven.events.pop(0) - frames = event['sentry.interfaces.Exception']['stacktrace']['frames'] + frames = event['exception']['values'][0]['stacktrace']['frames'] for frame in frames: if frame['module'].startswith('django.'): assert frame.get('in_app') is False @@ -527,8 +527,8 @@ class DjangoClientTest(TestCase): request.META['HTTP_HOST'] = 'example.com' result = self.raven.get_data_from_request(request) build_absolute_uri.assert_called_once_with() - assert 'sentry.interfaces.Http' in result - assert result['sentry.interfaces.Http']['url'] == 'http://example.com/' + assert 'request' in result + assert result['request']['url'] == 'http://example.com/' class DjangoTemplateTagTest(TestCase): @@ -568,8 +568,8 @@ class DjangoLoggingTest(TestCase): self.assertEquals(len(self.raven.events), 1) event = self.raven.events.pop(0) - self.assertTrue('sentry.interfaces.Http' in event) - http = event['sentry.interfaces.Http'] + assert 'request' in event + http = event['request'] self.assertEquals(http['method'], 'POST') diff --git a/tests/contrib/flask/tests.py b/tests/contrib/flask/tests.py index e741e8e5..ff94c63f 100644 --- a/tests/contrib/flask/tests.py +++ b/tests/contrib/flask/tests.py @@ -106,8 +106,8 @@ class FlaskTest(BaseTest): event = self.raven.events.pop(0) - self.assertTrue('sentry.interfaces.Exception' in event) - exc = event['sentry.interfaces.Exception'] + assert 'exception' in event + exc = event['exception']['values'][0] self.assertEquals(exc['type'], 'ValueError') self.assertEquals(exc['value'], 'hello world') self.assertEquals(event['level'], logging.ERROR) @@ -121,8 +121,8 @@ class FlaskTest(BaseTest): event = self.raven.events.pop(0) - self.assertTrue('sentry.interfaces.Http' in event) - http = event['sentry.interfaces.Http'] + assert 'request' in event + http = event['request'] self.assertEquals(http['url'], 'http://localhost/an-error/') self.assertEquals(http['query_string'], 'foo=bar') self.assertEquals(http['method'], 'GET') @@ -148,8 +148,8 @@ class FlaskTest(BaseTest): event = self.raven.events.pop(0) - self.assertTrue('sentry.interfaces.Http' in event) - http = event['sentry.interfaces.Http'] + assert 'request' in event + http = event['request'] self.assertEquals(http['url'], 'http://localhost/an-error/') self.assertEquals(http['query_string'], 'biz=baz') self.assertEquals(http['method'], 'POST') @@ -177,8 +177,8 @@ class FlaskTest(BaseTest): self.assertEquals(event['event_id'], response.headers['X-Sentry-ID']) assert event['message'] == 'ValueError: Boom' - assert 'sentry.interfaces.Http' in event - assert 'sentry.interfaces.Exception' in event + assert 'request' in event + assert 'exception' in event def test_captureMessage_captures_http(self): response = self.client.get('/message/?foo=bar') @@ -188,8 +188,8 @@ class FlaskTest(BaseTest): event = self.raven.events.pop(0) self.assertEquals(event['event_id'], response.headers['X-Sentry-ID']) - self.assertTrue('sentry.interfaces.Message' in event) - self.assertTrue('sentry.interfaces.Http' in event) + assert 'sentry.interfaces.Message' in event + assert 'request' in event @patch('flask.wrappers.RequestBase._load_form_data') def test_get_data_handles_disconnected_client(self, lfd): @@ -199,8 +199,8 @@ class FlaskTest(BaseTest): event = self.raven.events.pop(0) - self.assertTrue('sentry.interfaces.Http' in event) - http = event['sentry.interfaces.Http'] + assert 'request' in event + http = event['request'] self.assertEqual({}, http.get('data')) def test_error_handler_with_ignored_exception(self): @@ -249,5 +249,5 @@ class FlaskLoginTest(BaseTest): self.client.get('/an-error-logged-in/') event = self.raven.events.pop(0) assert event['message'] == 'ValueError: hello world' - assert 'sentry.interfaces.Http' in event - assert 'sentry.interfaces.User' in event + assert 'request' in event + assert 'user' in event diff --git a/tests/contrib/tornado/tests.py b/tests/contrib/tornado/tests.py index bd147f1d..0f665362 100644 --- a/tests/contrib/tornado/tests.py +++ b/tests/contrib/tornado/tests.py @@ -1,12 +1,6 @@ # -*- coding: utf-8 -*- -""" - tests - - Test the tornado Async Client -""" from __future__ import unicode_literals -import unittest from mock import patch from tornado import web, gen, testing from raven.contrib.tornado import SentryMixin, AsyncSentryClient @@ -88,17 +82,17 @@ class TornadoAsyncClientTestCase(testing.AsyncHTTPTestCase): self.assertEqual(send.call_count, 1) args, kwargs = send.call_args - self.assertTrue(('sentry.interfaces.User' in kwargs)) - self.assertTrue(('sentry.interfaces.Http' in kwargs)) - self.assertTrue(('sentry.interfaces.Exception' in kwargs)) + assert 'user' in kwargs + assert 'request' in kwargs + assert 'exception' in kwargs - http_data = kwargs['sentry.interfaces.Http'] + http_data = kwargs['request'] self.assertEqual(http_data['cookies'], None) self.assertEqual(http_data['url'], response.effective_url) self.assertEqual(http_data['query_string'], 'qs=qs') self.assertEqual(http_data['method'], 'GET') - user_data = kwargs['sentry.interfaces.User'] + user_data = kwargs['user'] self.assertEqual(user_data['is_authenticated'], False) @patch('raven.contrib.tornado.AsyncSentryClient.send') @@ -108,18 +102,18 @@ class TornadoAsyncClientTestCase(testing.AsyncHTTPTestCase): self.assertEqual(send.call_count, 1) args, kwargs = send.call_args - self.assertTrue(('sentry.interfaces.User' in kwargs)) - self.assertTrue(('sentry.interfaces.Http' in kwargs)) - self.assertTrue(('sentry.interfaces.Exception' in kwargs)) - self.assertTrue(('extra' in kwargs)) + assert 'user' in kwargs + assert 'request' in kwargs + assert 'exception' in kwargs + assert 'extra' in kwargs - http_data = kwargs['sentry.interfaces.Http'] + http_data = kwargs['request'] self.assertEqual(http_data['cookies'], None) self.assertEqual(http_data['url'], response.effective_url) self.assertEqual(http_data['query_string'], 'qs=qs') self.assertEqual(http_data['method'], 'GET') - user_data = kwargs['sentry.interfaces.User'] + user_data = kwargs['user'] self.assertEqual(user_data['is_authenticated'], False) assert 'extra_data' in kwargs['extra'] @@ -136,18 +130,18 @@ class TornadoAsyncClientTestCase(testing.AsyncHTTPTestCase): self.assertEqual(send.call_count, 1) args, kwargs = send.call_args - self.assertTrue(('sentry.interfaces.User' in kwargs)) - self.assertTrue(('sentry.interfaces.Http' in kwargs)) - self.assertTrue(('sentry.interfaces.Exception' in kwargs)) - self.assertTrue(('extra' in kwargs)) + assert 'user' in kwargs + assert 'request' in kwargs + assert 'exception' in kwargs + assert 'extra' in kwargs - http_data = kwargs['sentry.interfaces.Http'] + http_data = kwargs['request'] self.assertEqual(http_data['cookies'], None) self.assertEqual(http_data['url'], response.effective_url) self.assertEqual(http_data['query_string'], 'qs=qs') self.assertEqual(http_data['method'], 'GET') - user_data = kwargs['sentry.interfaces.User'] + user_data = kwargs['user'] self.assertEqual(user_data['is_authenticated'], False) assert 'extra_data' in kwargs['extra'] @@ -166,17 +160,17 @@ class TornadoAsyncClientTestCase(testing.AsyncHTTPTestCase): self.assertEqual(send.call_count, 1) args, kwargs = send.call_args - self.assertTrue(('sentry.interfaces.User' in kwargs)) - self.assertTrue(('sentry.interfaces.Http' in kwargs)) - self.assertTrue(('sentry.interfaces.Message' in kwargs)) + assert 'user' in kwargs + assert 'request' in kwargs + assert 'sentry.interfaces.Message' in kwargs - http_data = kwargs['sentry.interfaces.Http'] + http_data = kwargs['request'] self.assertEqual(http_data['cookies'], None) self.assertEqual(http_data['url'], response.effective_url) self.assertEqual(http_data['query_string'], 'qs=qs') self.assertEqual(http_data['method'], 'GET') - user_data = kwargs['sentry.interfaces.User'] + user_data = kwargs['user'] self.assertEqual(user_data['is_authenticated'], True) @patch('raven.contrib.tornado.AsyncSentryClient.send') @@ -186,17 +180,17 @@ class TornadoAsyncClientTestCase(testing.AsyncHTTPTestCase): self.assertEqual(send.call_count, 1) args, kwargs = send.call_args - self.assertTrue(('sentry.interfaces.User' in kwargs)) - self.assertTrue(('sentry.interfaces.Http' in kwargs)) - self.assertTrue(('sentry.interfaces.Exception' in kwargs)) + assert 'user' in kwargs + assert 'request' in kwargs + assert 'exception' in kwargs - http_data = kwargs['sentry.interfaces.Http'] + http_data = kwargs['request'] self.assertEqual(http_data['cookies'], None) self.assertEqual(http_data['url'], response.effective_url) self.assertEqual(http_data['query_string'], 'qs=qs') self.assertEqual(http_data['method'], 'GET') - user_data = kwargs['sentry.interfaces.User'] + user_data = kwargs['user'] self.assertEqual(user_data['is_authenticated'], False) @patch('raven.contrib.tornado.AsyncSentryClient.send') @@ -206,19 +200,15 @@ class TornadoAsyncClientTestCase(testing.AsyncHTTPTestCase): self.assertEqual(send.call_count, 1) args, kwargs = send.call_args - self.assertTrue(('sentry.interfaces.User' in kwargs)) - self.assertTrue(('sentry.interfaces.Http' in kwargs)) - self.assertTrue(('sentry.interfaces.Exception' in kwargs)) + assert 'user' in kwargs + assert 'request' in kwargs + assert 'exception' in kwargs - http_data = kwargs['sentry.interfaces.Http'] + http_data = kwargs['request'] self.assertEqual(http_data['cookies'], None) self.assertEqual(http_data['url'], response.effective_url) self.assertEqual(http_data['query_string'], 'qs=qs') self.assertEqual(http_data['method'], 'GET') - user_data = kwargs['sentry.interfaces.User'] + user_data = kwargs['user'] self.assertEqual(user_data['is_authenticated'], False) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/contrib/webpy/tests.py b/tests/contrib/webpy/tests.py index f2603a4c..29c8ae30 100644 --- a/tests/contrib/webpy/tests.py +++ b/tests/contrib/webpy/tests.py @@ -52,8 +52,8 @@ class WebPyTest(TestCase): self.assertEquals(len(self.store.events), 1) event = self.store.events.pop() - self.assertTrue('sentry.interfaces.Exception' in event) - exc = event['sentry.interfaces.Exception'] + assert 'exception' in event + exc = event['exception']['values'][0] self.assertEquals(exc['type'], 'ValueError') self.assertEquals(exc['value'], 'That\'s what she said') self.assertEquals(event['message'], 'ValueError: That\'s what she said') @@ -66,8 +66,8 @@ class WebPyTest(TestCase): event = self.store.events.pop() - self.assertTrue('sentry.interfaces.Http' in event) - http = event['sentry.interfaces.Http'] + assert 'request' in event + http = event['request'] self.assertEquals(http['url'], 'http://localhost/test') self.assertEquals(http['query_string'], '?biz=baz') self.assertEquals(http['method'], 'POST') diff --git a/tests/contrib/zerorpc/tests.py b/tests/contrib/zerorpc/tests.py index cf79295d..20b592d9 100644 --- a/tests/contrib/zerorpc/tests.py +++ b/tests/contrib/zerorpc/tests.py @@ -47,9 +47,9 @@ class ZeroRPCTest(TestCase): except zerorpc.exceptions.RemoteError as ex: self.assertEqual(ex.name, 'IndexError') self.assertEqual(len(self._sentry.events), 1) - exc = self._sentry.events[0]['sentry.interfaces.Exception'] + exc = self._sentry.events[0]['exception'] self.assertEqual(exc['type'], 'IndexError') - frames = self._sentry.events[0]['sentry.interfaces.Exception']['stacktrace']['frames'] + frames = exc['stacktrace']['frames'] self.assertEqual(frames[0]['function'], 'choice') self.assertEqual(frames[0]['module'], 'random') else: @@ -68,9 +68,9 @@ class ZeroRPCTest(TestCase): for attempt in xrange(0, 10): gevent.sleep(0.1) if len(self._sentry.events): - exc = self._sentry.events[0]['sentry.interfaces.Exception'] + exc = self._sentry.events[0]['exception'] self.assertEqual(exc['type'], 'IndexError') - frames = self._sentry.events[0]['sentry.interfaces.Exception']['stacktrace']['frames'] + frames = exc['stacktrace']['frames'] self.assertEqual(frames[0]['function'], 'choice') self.assertEqual(frames[0]['module'], 'random') return diff --git a/tests/handlers/logbook/tests.py b/tests/handlers/logbook/tests.py index a58d3e1e..12dbd7c2 100644 --- a/tests/handlers/logbook/tests.py +++ b/tests/handlers/logbook/tests.py @@ -37,7 +37,7 @@ class LogbookHandlerTest(TestCase): self.assertEquals(event['logger'], __name__) self.assertEquals(event['level'], 'error') self.assertEquals(event['message'], 'This is a test error') - self.assertFalse('sentry.interfaces.Exception' in event) + self.assertFalse('exception' in event) self.assertTrue('sentry.interfaces.Message' in event) msg = event['sentry.interfaces.Message'] self.assertEquals(msg['message'], 'This is a test error') @@ -49,7 +49,7 @@ class LogbookHandlerTest(TestCase): self.assertEquals(event['logger'], __name__) self.assertEquals(event['level'], 'warning') self.assertEquals(event['message'], 'This is a test warning') - self.assertFalse('sentry.interfaces.Exception' in event) + self.assertFalse('exception' in event) self.assertTrue('sentry.interfaces.Message' in event) msg = event['sentry.interfaces.Message'] self.assertEquals(msg['message'], 'This is a test warning') @@ -65,7 +65,7 @@ class LogbookHandlerTest(TestCase): else: expected = "u'http://example.com'" self.assertEquals(event['extra']['url'], expected) - self.assertFalse('sentry.interfaces.Exception' in event) + self.assertFalse('exception' in event) self.assertTrue('sentry.interfaces.Message' in event) msg = event['sentry.interfaces.Message'] self.assertEquals(msg['message'], 'This is a test info with a url') @@ -80,8 +80,8 @@ class LogbookHandlerTest(TestCase): event = client.events.pop(0) self.assertEquals(event['message'], 'This is a test info with an exception') - self.assertTrue('sentry.interfaces.Exception' in event) - exc = event['sentry.interfaces.Exception'] + assert 'exception' in event + exc = event['exception']['values'][0] self.assertEquals(exc['type'], 'ValueError') self.assertEquals(exc['value'], 'This is a test ValueError') self.assertTrue('sentry.interfaces.Message' in event) @@ -94,7 +94,7 @@ class LogbookHandlerTest(TestCase): self.assertEquals(len(client.events), 1) event = client.events.pop(0) self.assertEquals(event['message'], 'This is a test of args') - self.assertFalse('sentry.interfaces.Exception' in event) + assert 'exception' not in event self.assertTrue('sentry.interfaces.Message' in event) msg = event['sentry.interfaces.Message'] self.assertEquals(msg['message'], 'This is a test of {0}') diff --git a/tests/handlers/logging/tests.py b/tests/handlers/logging/tests.py index 905d0698..536d7e0d 100644 --- a/tests/handlers/logging/tests.py +++ b/tests/handlers/logging/tests.py @@ -42,7 +42,7 @@ class LoggingIntegrationTest(TestCase): self.assertEqual(event['logger'], 'root') self.assertEqual(event['level'], logging.INFO) self.assertEqual(event['message'], 'This is a test error') - self.assertFalse('sentry.interfaces.Exception' in event) + assert 'exception' not in event self.assertTrue('sentry.interfaces.Message' in event) msg = event['sentry.interfaces.Message'] self.assertEqual(msg['message'], 'This is a test error') @@ -89,8 +89,8 @@ class LoggingIntegrationTest(TestCase): event = self.client.events.pop(0) self.assertEqual(event['message'], 'This is a test info with an exception') - self.assertTrue('sentry.interfaces.Exception' in event) - exc = event['sentry.interfaces.Exception'] + assert 'exception' in event + exc = event['exception']['values'][0] self.assertEqual(exc['type'], 'ValueError') self.assertEqual(exc['value'], 'This is a test ValueError') self.assertTrue('sentry.interfaces.Message' in event) @@ -116,12 +116,12 @@ class LoggingIntegrationTest(TestCase): self.assertEqual(len(self.client.events), 1) event = self.client.events.pop(0) - self.assertTrue('sentry.interfaces.Stacktrace' in event) - frames = event['sentry.interfaces.Stacktrace']['frames'] + self.assertTrue('stacktrace' in event) + frames = event['stacktrace']['frames'] self.assertNotEquals(len(frames), 1) frame = frames[0] self.assertEqual(frame['module'], 'raven.handlers.logging') - self.assertFalse('sentry.interfaces.Exception' in event) + assert 'exception' not in event self.assertTrue('sentry.interfaces.Message' in event) self.assertEqual(event['culprit'], 'root in make_record') self.assertEqual(event['message'], 'This is a test of stacks') @@ -141,12 +141,12 @@ class LoggingIntegrationTest(TestCase): self.assertEqual(len(self.client.events), 1) event = self.client.events.pop(0) - assert 'sentry.interfaces.Stacktrace' in event + assert 'stacktrace' in event assert 'culprit' in event assert event['culprit'] == 'root in make_record' self.assertTrue('message' in event, event) self.assertEqual(event['message'], 'This is a test of stacks') - self.assertFalse('sentry.interfaces.Exception' in event) + assert 'exception' not in event self.assertTrue('sentry.interfaces.Message' in event) msg = event['sentry.interfaces.Message'] self.assertEqual(msg['message'], 'This is a test of stacks') diff --git a/tests/middleware/tests.py b/tests/middleware/tests.py index 50f90f50..c9a67243 100644 --- a/tests/middleware/tests.py +++ b/tests/middleware/tests.py @@ -65,15 +65,15 @@ class MiddlewareTestCase(TestCase): self.assertEquals(len(self.client.events), 1) event = self.client.events.pop(0) - self.assertTrue('sentry.interfaces.Exception' in event) - exc = event['sentry.interfaces.Exception'] + assert 'exception' in event + exc = event['exception']['values'][0] self.assertEquals(exc['type'], 'ValueError') self.assertEquals(exc['value'], 'hello world') self.assertEquals(event['level'], logging.ERROR) self.assertEquals(event['message'], 'ValueError: hello world') - self.assertTrue('sentry.interfaces.Http' in event) - http = event['sentry.interfaces.Http'] + assert 'request' in event + http = event['request'] self.assertEquals(http['url'], 'http://localhost/an-error') self.assertEquals(http['query_string'], 'foo=bar') self.assertEquals(http['method'], 'GET') diff --git a/tests/processors/tests.py b/tests/processors/tests.py index 794f4b81..ae230969 100644 --- a/tests/processors/tests.py +++ b/tests/processors/tests.py @@ -37,7 +37,7 @@ class SantizePasswordsProcessorTest(TestCase): def test_stacktrace(self): data = { - 'sentry.interfaces.Stacktrace': { + 'stacktrace': { 'frames': [{'vars': VARS}], } } @@ -45,8 +45,8 @@ class SantizePasswordsProcessorTest(TestCase): proc = SanitizePasswordsProcessor(Mock()) result = proc.process(data) - self.assertTrue('sentry.interfaces.Stacktrace' in result) - stack = result['sentry.interfaces.Stacktrace'] + self.assertTrue('stacktrace' in result) + stack = result['stacktrace'] self.assertTrue('frames' in stack) self.assertEquals(len(stack['frames']), 1) frame = stack['frames'][0] @@ -55,7 +55,7 @@ class SantizePasswordsProcessorTest(TestCase): def test_http(self): data = { - 'sentry.interfaces.Http': { + 'request': { 'data': VARS, 'env': VARS, 'headers': VARS, @@ -66,26 +66,25 @@ class SantizePasswordsProcessorTest(TestCase): proc = SanitizePasswordsProcessor(Mock()) result = proc.process(data) - self.assertTrue('sentry.interfaces.Http' in result) - http = result['sentry.interfaces.Http'] + self.assertTrue('request' in result) + http = result['request'] for n in ('data', 'env', 'headers', 'cookies'): self.assertTrue(n in http) self._check_vars_sanitized(http[n], proc) def test_querystring_as_string(self): data = { - 'sentry.interfaces.Http': { - 'query_string': - 'foo=bar&password=hello&the_secret=hello' - '&a_password_here=hello&api_key=secret_key', + 'request': { + 'query_string': 'foo=bar&password=hello&the_secret=hello' + '&a_password_here=hello&api_key=secret_key', } } proc = SanitizePasswordsProcessor(Mock()) result = proc.process(data) - self.assertTrue('sentry.interfaces.Http' in result) - http = result['sentry.interfaces.Http'] + self.assertTrue('request' in result) + http = result['request'] self.assertEquals( http['query_string'], 'foo=bar&password=%(m)s&the_secret=%(m)s' @@ -93,7 +92,7 @@ class SantizePasswordsProcessorTest(TestCase): def test_querystring_as_string_with_partials(self): data = { - 'sentry.interfaces.Http': { + 'request': { 'query_string': 'foo=bar&password&baz=bar', } } @@ -101,8 +100,8 @@ class SantizePasswordsProcessorTest(TestCase): proc = SanitizePasswordsProcessor(Mock()) result = proc.process(data) - self.assertTrue('sentry.interfaces.Http' in result) - http = result['sentry.interfaces.Http'] + self.assertTrue('request' in result) + http = result['request'] self.assertEquals(http['query_string'], 'foo=bar&password&baz=bar' % dict(m=proc.MASK)) def test_sanitize_credit_card(self): @@ -120,7 +119,7 @@ class SantizePasswordsProcessorTest(TestCase): class RemovePostDataProcessorTest(TestCase): def test_does_remove_data(self): data = { - 'sentry.interfaces.Http': { + 'request': { 'data': 'foo', } } @@ -128,22 +127,22 @@ class RemovePostDataProcessorTest(TestCase): proc = RemovePostDataProcessor(Mock()) result = proc.process(data) - self.assertTrue('sentry.interfaces.Http' in result) - http = result['sentry.interfaces.Http'] + self.assertTrue('request' in result) + http = result['request'] self.assertFalse('data' in http) class RemoveStackLocalsProcessorTest(TestCase): def test_does_remove_data(self): data = { - 'sentry.interfaces.Stacktrace': { - 'frames': [{'vars': VARS,}], + 'stacktrace': { + 'frames': [{'vars': VARS}], } } proc = RemoveStackLocalsProcessor(Mock()) result = proc.process(data) - self.assertTrue('sentry.interfaces.Stacktrace' in result) - stack = result['sentry.interfaces.Stacktrace'] + assert 'stacktrace' in result + stack = result['stacktrace'] for frame in stack['frames']: self.assertFalse('vars' in frame) From f106dab5237b19f4ea0f14d5a850638135372081 Mon Sep 17 00:00:00 2001 From: Bobo Li Date: Thu, 22 May 2014 00:55:14 +0000 Subject: [PATCH 12/21] Added HTTPS and transport options support to Tornado transport --- raven/transport/tornado.py | 9 +++++--- tests/transport/tornado/__init__.py | 0 tests/transport/tornado/tests.py | 36 +++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 tests/transport/tornado/__init__.py create mode 100644 tests/transport/tornado/tests.py diff --git a/raven/transport/tornado.py b/raven/transport/tornado.py index 9ea3a5b3..16425d8a 100644 --- a/raven/transport/tornado.py +++ b/raven/transport/tornado.py @@ -19,19 +19,22 @@ except: class TornadoHTTPTransport(HTTPTransport): - scheme = ['tornado+http'] + scheme = ['tornado+http', 'tornado+https'] - def __init__(self, parsed_url): + def __init__(self, parsed_url, **kwargs): if not has_tornado: raise ImportError('TornadoHTTPTransport requires tornado.') - super(TornadoHTTPTransport, self).__init__(parsed_url) + super(TornadoHTTPTransport, self).__init__(parsed_url, **kwargs) # remove the tornado+ from the protocol, as it is not a real protocol self._url = self._url.split('+', 1)[-1] def send(self, data, headers): kwargs = dict(method='POST', headers=headers, body=data) + kwargs["validate_cert"] = self.verify_ssl + kwargs["connect_timeout"] = self.timeout + kwargs["ca_certs"] = self.ca_certs # only use async if ioloop is running, otherwise it will never send if ioloop.IOLoop.initialized(): diff --git a/tests/transport/tornado/__init__.py b/tests/transport/tornado/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/transport/tornado/tests.py b/tests/transport/tornado/tests.py new file mode 100644 index 00000000..8ab9b090 --- /dev/null +++ b/tests/transport/tornado/tests.py @@ -0,0 +1,36 @@ +from __future__ import absolute_import + +import mock + +from raven.base import Client +from raven.utils.testutils import TestCase + + +class TornadoTransportTests(TestCase): + @mock.patch("raven.transport.tornado.HTTPClient") + def test_send(self, fake_client): + url = "https://user:pass@host:1234/1" + timeout = 1 + verify_ssl = 1 + ca_certs = "/some/path/somefile" + + fake = fake_client.return_value + raven_client = Client( + dsn="tornado+{0}?timeout={1}&verify_ssl={2}&ca_certs={3}". + format(url, timeout, verify_ssl, ca_certs)) + + raven_client.captureMessage(message="test") + + # make sure an instance of HTTPClient was created, since we are not in + # an IOLoop + fake_client.assert_called_once_with() + fake_fetch = fake.fetch + + # make sure we called fetch() which does the sending + self.assertEqual(fake_fetch.call_count, 1) + # only verify the special kwargs that we should be passing through, + # no need to verify the urls and whatnot + args, kwargs = fake_fetch.call_args + self.assertEqual(kwargs["connect_timeout"], timeout) + self.assertEqual(kwargs["validate_cert"], bool(verify_ssl)) + self.assertEqual(kwargs["ca_certs"], ca_certs) From 4170323fe8c2c0ce4f3cce57f7c75e7bf85581be Mon Sep 17 00:00:00 2001 From: David Cramer Date: Thu, 22 May 2014 00:32:03 -0700 Subject: [PATCH 13/21] Minor stylistic changes --- raven/transport/threaded.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/raven/transport/threaded.py b/raven/transport/threaded.py index 78fe5575..240242d8 100644 --- a/raven/transport/threaded.py +++ b/raven/transport/threaded.py @@ -9,10 +9,11 @@ from __future__ import absolute_import import atexit import logging -import time import threading import os +from time import sleep, time + from raven.transport.base import AsyncTransport from raven.transport.http import HTTPTransport from raven.utils.compat import Queue @@ -60,10 +61,12 @@ class AsyncWorker(object): print("Sentry is attempting to send %i pending error messages" % size) print("Waiting up to %s seconds" % timeout) + if os.name == 'nt': print("Press Ctrl-Break to quit") else: print("Press Ctrl-C to quit") + self._timed_queue_join(timeout - initial_timeout) self._thread = None @@ -77,14 +80,14 @@ class AsyncWorker(object): returns true on success, false on timeout """ - deadline = time.time() + timeout + deadline = time() + timeout queue = self._queue queue.all_tasks_done.acquire() try: while queue.unfinished_tasks: - delay = deadline - time.time() - if(delay <= 0): + delay = deadline - time() + if delay <= 0: # timed out return False @@ -126,7 +129,7 @@ class AsyncWorker(object): self._queue.put_nowait((callback, args, kwargs)) def _target(self): - while 1: + while True: record = self._queue.get() try: if record is self._terminator: @@ -139,7 +142,7 @@ class AsyncWorker(object): finally: self._queue.task_done() - time.sleep(0) + sleep(0) class ThreadedHTTPTransport(AsyncTransport, HTTPTransport): From d0fa51b1238a31f7117d026703b3256acef66b6f Mon Sep 17 00:00:00 2001 From: David Cramer Date: Wed, 28 May 2014 21:50:51 -0700 Subject: [PATCH 14/21] 4.3.0 --- CHANGES | 6 ++++++ setup.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 091a16ad..c956493c 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,9 @@ +Version 5.0.0 +------------- + +* Sentry client protocol is now version 5. +* Various improvements to threaded transport. + Version 4.2.0 ------------- diff --git a/setup.py b/setup.py index 7a303df8..e42a08c1 100755 --- a/setup.py +++ b/setup.py @@ -92,7 +92,7 @@ class PyTest(TestCommand): setup( name='raven', - version='4.2.3', + version='5.0.0', author='David Cramer', author_email='dcramer@gmail.com', url='http://github.com/getsentry/raven-python', From 683acffedc322f3f6ede606490d0e03e9a3a0b74 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Wed, 28 May 2014 21:53:53 -0700 Subject: [PATCH 15/21] Bump protocol version to explicit 5 --- raven/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/raven/base.py b/raven/base.py index 2648dc1d..72676571 100644 --- a/raven/base.py +++ b/raven/base.py @@ -115,7 +115,7 @@ class Client(object): >>> print "Exception caught; reference is %s" % ident """ logger = logging.getLogger('raven') - protocol_version = '4' + protocol_version = '5' _registry = TransportRegistry(transports=default_transports) From 5b8728ab80fd2330a3c41f45a1b204bd8c1fe22c Mon Sep 17 00:00:00 2001 From: David Cramer Date: Wed, 28 May 2014 22:04:32 -0700 Subject: [PATCH 16/21] Update test to match protocol --- tests/base/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/base/tests.py b/tests/base/tests.py index c6035232..0dce9834 100644 --- a/tests/base/tests.py +++ b/tests/base/tests.py @@ -187,7 +187,7 @@ class ClientTest(TestCase): 'Content-Type': 'application/octet-stream', 'X-Sentry-Auth': ( 'Sentry sentry_timestamp=1328055286.51, ' - 'sentry_client=raven-python/%s, sentry_version=4, ' + 'sentry_client=raven-python/%s, sentry_version=5, ' 'sentry_key=public, ' 'sentry_secret=secret' % (raven.VERSION,)) }, From a63925406ee787ec1ea7f2b26de27eff662b9791 Mon Sep 17 00:00:00 2001 From: Dan Riti Date: Thu, 29 May 2014 21:37:16 +0000 Subject: [PATCH 17/21] Add trailing comma to args tuple in Pylons documentation. --- docs/config/pylons.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/config/pylons.rst b/docs/config/pylons.rst index 2e033d5d..86028998 100644 --- a/docs/config/pylons.rst +++ b/docs/config/pylons.rst @@ -56,7 +56,7 @@ Add the following lines to your project's `.ini` file to setup `SentryHandler`: [handler_sentry] class = raven.handlers.logging.SentryHandler - args = ('SENTRY_DSN') + args = ('SENTRY_DSN',) level = NOTSET formatter = generic From fc07a773d76f503e92db837aedcf2b1537be88ed Mon Sep 17 00:00:00 2001 From: Matt Robenolt Date: Mon, 2 Jun 2014 17:35:02 -0700 Subject: [PATCH 18/21] Use certifi if available --- raven/conf/defaults.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/raven/conf/defaults.py b/raven/conf/defaults.py index 7cca949a..11e3d39d 100644 --- a/raven/conf/defaults.py +++ b/raven/conf/defaults.py @@ -62,4 +62,9 @@ PROCESSORS = ( # Default Project ID PROJECT = 1 -CA_BUNDLE = os.path.join(ROOT, 'data', 'cacert.pem') +try: + # Try for certifi first since they likely keep their bundle more up to date + import certifi + CA_BUNDLE = certifi.where() +except ImportError: + CA_BUNDLE = os.path.join(ROOT, 'data', 'cacert.pem') From 4ca60ea6fa009a220eb503025a20380a4f0bec58 Mon Sep 17 00:00:00 2001 From: allenhu Date: Wed, 4 Jun 2014 17:02:12 +0800 Subject: [PATCH 19/21] fix send_sync function param error in raven.contrib.async --- raven/contrib/async.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/raven/contrib/async.py b/raven/contrib/async.py index a272dba4..4f94abb4 100644 --- a/raven/contrib/async.py +++ b/raven/contrib/async.py @@ -26,4 +26,4 @@ class AsyncClient(Client): super(AsyncClient, self).send(**kwargs) def send(self, **kwargs): - self.worker.queue(self.send_sync, kwargs) + self.worker.queue(self.send_sync, **kwargs) From 86f7c1065bcb173ca80790f0538a17bc2fcfb48a Mon Sep 17 00:00:00 2001 From: David Cramer Date: Fri, 6 Jun 2014 06:20:23 -0700 Subject: [PATCH 20/21] Correctly accept kwargs with only dsn argument (fixes GH-460) --- raven/handlers/logbook.py | 2 +- raven/handlers/logging.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/raven/handlers/logbook.py b/raven/handlers/logbook.py index 605ed35e..cae177c1 100644 --- a/raven/handlers/logbook.py +++ b/raven/handlers/logbook.py @@ -23,7 +23,7 @@ class SentryHandler(logbook.Handler): if len(args) == 1: arg = args[0] if isinstance(arg, six.string_types): - self.client = kwargs.pop('client_cls', Client)(dsn=arg) + self.client = kwargs.pop('client_cls', Client)(dsn=arg, **kwargs) elif isinstance(arg, Client): self.client = arg else: diff --git a/raven/handlers/logging.py b/raven/handlers/logging.py index 7b482837..7c51aaf9 100644 --- a/raven/handlers/logging.py +++ b/raven/handlers/logging.py @@ -32,7 +32,7 @@ class SentryHandler(logging.Handler, object): if len(args) == 1: arg = args[0] if isinstance(arg, six.string_types): - self.client = client(dsn=arg) + self.client = client(dsn=arg, **kwargs) elif isinstance(arg, Client): self.client = arg else: From beb980d4ccaf4117b409789bc28b59d92a0a429b Mon Sep 17 00:00:00 2001 From: allenhu Date: Mon, 23 Jun 2014 11:17:08 +0800 Subject: [PATCH 21/21] fix the puzzle try..catch in contrib.django.models's get_client --- raven/contrib/django/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/raven/contrib/django/models.py b/raven/contrib/django/models.py index f5779306..7f41c9e9 100644 --- a/raven/contrib/django/models.py +++ b/raven/contrib/django/models.py @@ -142,7 +142,7 @@ def get_client(client=None): class_name = str(class_name) try: - instance = getattr(__import__(module, {}, {}, class_name), class_name)(**options) + Client = getattr(__import__(module, {}, {}, class_name), class_name) except ImportError: logger.exception('Failed to import client: %s', client) if not _client[1]: @@ -150,6 +150,7 @@ def get_client(client=None): client = 'raven.contrib.django.DjangoClient' _client = (client, get_client(client)) else: + instance = Client(**options) if not tmp_client: _client = (client, instance) return instance