Merge pull request #237 from lopter/master
Update the ZeroRPC middleware for zerorpc-python >= 0.4.0
This commit is contained in:
commit
3caf9083b5
|
@ -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
|
||||
-------
|
||||
|
||||
|
|
|
@ -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
|
||||
"""
|
||||
|
||||
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.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, hide_zerorpc_frames=True, client=None, **kwargs):
|
||||
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
|
||||
)
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue