Merge pull request #237 from lopter/master

Update the ZeroRPC middleware for zerorpc-python >= 0.4.0
This commit is contained in:
David Cramer 2013-01-21 16:04:27 -08:00
commit 3caf9083b5
3 changed files with 57 additions and 19 deletions

View File

@ -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.1.0;
- ZeroRPC-Python >= 0.4.0 requires Raven > 3.1.0.
Caveats
-------

View File

@ -9,50 +9,60 @@ 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.
>>> 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
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:
# 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
# - context.Context.middleware_call_procedure
# - 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,
extra=task_context
extra=task_ctx
)

View File

@ -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()