From 5b488e9168ffb5c51a4fc774aa752f4e88f43f52 Mon Sep 17 00:00:00 2001 From: Louis Opter Date: Sat, 29 Dec 2012 22:49:29 +0100 Subject: [PATCH 1/4] Port the ZeroRPC middleware to the new ZeroRPC middleware API --- docs/config/zerorpc.rst | 6 ++++++ raven/contrib/zerorpc/__init__.py | 5 ++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/config/zerorpc.rst b/docs/config/zerorpc.rst index 626994cb..4d6ae468 100644 --- a/docs/config/zerorpc.rst +++ b/docs/config/zerorpc.rst @@ -21,6 +21,12 @@ submits exceptions to Sentry. This behavior can be disabled by passing the sentry = SentryMiddleware(hide_zerorpc_frames=False, dsn='udp://public_key:secret_key@example.com:4242/1') +Compatibility +------------- + +- ZeroRPC-Python < 0.4.0 is compatible with Raven <= 3.0.0; +- ZeroRPC-Python >= 0.4.0 requires Raven > 3.0.0. + Caveats ------- diff --git a/raven/contrib/zerorpc/__init__.py b/raven/contrib/zerorpc/__init__.py index 7a5064c8..7623ab09 100644 --- a/raven/contrib/zerorpc/__init__.py +++ b/raven/contrib/zerorpc/__init__.py @@ -33,13 +33,12 @@ class SentryMiddleware(object): self._sentry_client = client or Client(**kwargs) self._hide_zerorpc_frames = hide_zerorpc_frames - def inspect_error(self, task_context, exc_info): + def server_inspect_exception(self, req_event, rep_event, task_ctx, exc_info): """Called when an exception has been raised in the code run by ZeroRPC""" # Hide the zerorpc internal frames for readability, frames to hide are: # - core.ServerBase._async_task # - core.Pattern*.process_call - # - context.Context.middleware_call_procedure # - core.DecoratorBase.__call__ if self._hide_zerorpc_frames: exc_traceback = exc_info[2] @@ -54,5 +53,5 @@ class SentryMiddleware(object): self._sentry_client.captureException( exc_info, - extra=task_context + extra=task_ctx ) From c4f9ec699af2963f57fccae34d26ec17c9bf7f14 Mon Sep 17 00:00:00 2001 From: Louis Opter Date: Sun, 30 Dec 2012 10:25:50 +0100 Subject: [PATCH 2/4] Don't hide the whole traceback on a PUSH/PULL or PUB/SUB ZeroRPC server This changeset fixes the hide_zerorpc_frames option for the ZeroRPC middleware. PUSH/PULL or PUB/SUB ZeroRPC servers were simply not handled. I'm also moving away from raven.utils.stacks.iter_traceback_frames because the special variable __traceback_hide__ seems to stick across exceptions and iter_traceback_frames doesn't return already hidden frames. --- raven/contrib/zerorpc/__init__.py | 23 ++++++++++++++--------- tests/contrib/zerorpc/tests.py | 24 +++++++++++++++++++++++- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/raven/contrib/zerorpc/__init__.py b/raven/contrib/zerorpc/__init__.py index 7623ab09..4ddd0cf2 100644 --- a/raven/contrib/zerorpc/__init__.py +++ b/raven/contrib/zerorpc/__init__.py @@ -9,8 +9,6 @@ raven.contrib.zerorpc import inspect from raven.base import Client -from raven.utils.stacks import iter_traceback_frames - class SentryMiddleware(object): """Sentry/Raven middleware for ZeroRPC. @@ -36,20 +34,27 @@ class SentryMiddleware(object): def server_inspect_exception(self, req_event, rep_event, task_ctx, exc_info): """Called when an exception has been raised in the code run by ZeroRPC""" - # Hide the zerorpc internal frames for readability, frames to hide are: + # Hide the zerorpc internal frames for readability, for a REQ/REP or + # REQ/STREAM server the frames to hide are: # - core.ServerBase._async_task # - core.Pattern*.process_call # - core.DecoratorBase.__call__ + # + # For a PUSH/PULL or PUB/SUB server the frame to hide is: + # - core.Puller._receiver if self._hide_zerorpc_frames: - exc_traceback = exc_info[2] - for zerorpc_frame, tb_lineno in iter_traceback_frames(exc_traceback): + traceback = exc_info[2] + while traceback: + zerorpc_frame = traceback.tb_frame zerorpc_frame.f_locals['__traceback_hide__'] = True frame_info = inspect.getframeinfo(zerorpc_frame) - # Is there a better way than this (or looking up the filenames or - # hardcoding the number of frames to skip) to know when we are out - # of zerorpc? - if frame_info.function == '__call__': + # Is there a better way than this (or looking up the filenames + # or hardcoding the number of frames to skip) to know when we + # are out of zerorpc? + if frame_info.function == '__call__' \ + or frame_info.function == '_receiver': break + traceback = traceback.tb_next self._sentry_client.captureException( exc_info, diff --git a/tests/contrib/zerorpc/tests.py b/tests/contrib/zerorpc/tests.py index ac2e6685..1f58f0ea 100644 --- a/tests/contrib/zerorpc/tests.py +++ b/tests/contrib/zerorpc/tests.py @@ -36,6 +36,7 @@ class ZeroRPCTest(unittest2.TestCase): client=self._sentry )) + def test_zerorpc_middleware_with_reqrep(self): self._server = zerorpc.Server(random) self._server.bind(self._server_endpoint) gevent.spawn(self._server.run) @@ -43,7 +44,6 @@ class ZeroRPCTest(unittest2.TestCase): self._client = zerorpc.Client() self._client.connect(self._server_endpoint) - def test_zerorpc_middleware(self): try: self._client.choice([]) except zerorpc.exceptions.RemoteError, ex: @@ -58,6 +58,28 @@ class ZeroRPCTest(unittest2.TestCase): self.fail('An IndexError exception should have been raised an catched') + def test_zerorpc_middleware_with_pushpull(self): + self._server = zerorpc.Puller(random) + self._server.bind(self._server_endpoint) + gevent.spawn(self._server.run) + + self._client = zerorpc.Pusher() + self._client.connect(self._server_endpoint) + + self._client.choice([]) + + for attempt in xrange(0, 10): + gevent.sleep(0.1) + if len(self._sentry.events): + exc = self._sentry.events[0]['sentry.interfaces.Exception'] + self.assertEqual(exc['type'], 'IndexError') + frames = self._sentry.events[0]['sentry.interfaces.Stacktrace']['frames'] + self.assertEqual(frames[0]['function'], 'choice') + self.assertEqual(frames[0]['module'], 'random') + return + + self.fail('An IndexError exception should have been sent to Sentry') + def tearDown(self): self._client.close() self._server.close() From 92b45e54b778aade3bdf8edb1ea5592eb272b149 Mon Sep 17 00:00:00 2001 From: Louis Opter Date: Mon, 7 Jan 2013 16:05:07 +0100 Subject: [PATCH 3/4] Improve the ZeroRPC middleware docstrings a bit --- raven/contrib/zerorpc/__init__.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/raven/contrib/zerorpc/__init__.py b/raven/contrib/zerorpc/__init__.py index 4ddd0cf2..02319e5e 100644 --- a/raven/contrib/zerorpc/__init__.py +++ b/raven/contrib/zerorpc/__init__.py @@ -13,21 +13,27 @@ from raven.base import Client class SentryMiddleware(object): """Sentry/Raven middleware for ZeroRPC. + >>> import zerorpc + >>> from raven.contrib.zerorpc import SentryMiddleware >>> sentry = SentryMiddleware(dsn='udp://..../') >>> zerorpc.Context.get_instance().register_middleware(sentry) Exceptions detected server-side in ZeroRPC will be submitted to Sentry (and propagated to the client as well). - hide_zerorpc_frames: modify the exception stacktrace to remove the internal - zerorpc frames (True by default to make the stacktrace - as readable as possible); - client: use an existing raven.Client object, otherwise one will be - instantiated from the keyword arguments. - """ def __init__(self, hide_zerorpc_frames=True, client=None, **kwargs): + """Create a middleware object that can be injected in a ZeroRPC server. + + - hide_zerorpc_frames: modify the exception stacktrace to remove the + internal zerorpc frames (True by default to make + the stacktrace as readable as possible); + - client: use an existing raven.Client object, otherwise one will be + instantiated from the keyword arguments. + + """ + self._sentry_client = client or Client(**kwargs) self._hide_zerorpc_frames = hide_zerorpc_frames From fbb403a2ee2d7a016a6c5b5260c03a9ec8143244 Mon Sep 17 00:00:00 2001 From: Louis Opter Date: Mon, 7 Jan 2013 16:07:34 +0100 Subject: [PATCH 4/4] Update compatible versions in the ZeroRPC middleware documentation --- docs/config/zerorpc.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/config/zerorpc.rst b/docs/config/zerorpc.rst index 4d6ae468..e64436f1 100644 --- a/docs/config/zerorpc.rst +++ b/docs/config/zerorpc.rst @@ -24,8 +24,8 @@ submits exceptions to Sentry. This behavior can be disabled by passing the Compatibility ------------- -- ZeroRPC-Python < 0.4.0 is compatible with Raven <= 3.0.0; -- ZeroRPC-Python >= 0.4.0 requires Raven > 3.0.0. +- ZeroRPC-Python < 0.4.0 is compatible with Raven <= 3.1.0; +- ZeroRPC-Python >= 0.4.0 requires Raven > 3.1.0. Caveats -------