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')
|
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
|
Caveats
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
|
|
@ -9,50 +9,60 @@ raven.contrib.zerorpc
|
||||||
import inspect
|
import inspect
|
||||||
|
|
||||||
from raven.base import Client
|
from raven.base import Client
|
||||||
from raven.utils.stacks import iter_traceback_frames
|
|
||||||
|
|
||||||
|
|
||||||
class SentryMiddleware(object):
|
class SentryMiddleware(object):
|
||||||
"""Sentry/Raven middleware for ZeroRPC.
|
"""Sentry/Raven middleware for ZeroRPC.
|
||||||
|
|
||||||
|
>>> import zerorpc
|
||||||
|
>>> from raven.contrib.zerorpc import SentryMiddleware
|
||||||
>>> sentry = SentryMiddleware(dsn='udp://..../')
|
>>> sentry = SentryMiddleware(dsn='udp://..../')
|
||||||
>>> zerorpc.Context.get_instance().register_middleware(sentry)
|
>>> zerorpc.Context.get_instance().register_middleware(sentry)
|
||||||
|
|
||||||
Exceptions detected server-side in ZeroRPC will be submitted to Sentry (and
|
Exceptions detected server-side in ZeroRPC will be submitted to Sentry (and
|
||||||
propagated to the client as well).
|
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):
|
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._sentry_client = client or Client(**kwargs)
|
||||||
self._hide_zerorpc_frames = hide_zerorpc_frames
|
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"""
|
"""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.ServerBase._async_task
|
||||||
# - core.Pattern*.process_call
|
# - core.Pattern*.process_call
|
||||||
# - context.Context.middleware_call_procedure
|
|
||||||
# - core.DecoratorBase.__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:
|
if self._hide_zerorpc_frames:
|
||||||
exc_traceback = exc_info[2]
|
traceback = exc_info[2]
|
||||||
for zerorpc_frame, tb_lineno in iter_traceback_frames(exc_traceback):
|
while traceback:
|
||||||
|
zerorpc_frame = traceback.tb_frame
|
||||||
zerorpc_frame.f_locals['__traceback_hide__'] = True
|
zerorpc_frame.f_locals['__traceback_hide__'] = True
|
||||||
frame_info = inspect.getframeinfo(zerorpc_frame)
|
frame_info = inspect.getframeinfo(zerorpc_frame)
|
||||||
# Is there a better way than this (or looking up the filenames or
|
# Is there a better way than this (or looking up the filenames
|
||||||
# hardcoding the number of frames to skip) to know when we are out
|
# or hardcoding the number of frames to skip) to know when we
|
||||||
# of zerorpc?
|
# are out of zerorpc?
|
||||||
if frame_info.function == '__call__':
|
if frame_info.function == '__call__' \
|
||||||
|
or frame_info.function == '_receiver':
|
||||||
break
|
break
|
||||||
|
traceback = traceback.tb_next
|
||||||
|
|
||||||
self._sentry_client.captureException(
|
self._sentry_client.captureException(
|
||||||
exc_info,
|
exc_info,
|
||||||
extra=task_context
|
extra=task_ctx
|
||||||
)
|
)
|
||||||
|
|
|
@ -36,6 +36,7 @@ class ZeroRPCTest(unittest2.TestCase):
|
||||||
client=self._sentry
|
client=self._sentry
|
||||||
))
|
))
|
||||||
|
|
||||||
|
def test_zerorpc_middleware_with_reqrep(self):
|
||||||
self._server = zerorpc.Server(random)
|
self._server = zerorpc.Server(random)
|
||||||
self._server.bind(self._server_endpoint)
|
self._server.bind(self._server_endpoint)
|
||||||
gevent.spawn(self._server.run)
|
gevent.spawn(self._server.run)
|
||||||
|
@ -43,7 +44,6 @@ class ZeroRPCTest(unittest2.TestCase):
|
||||||
self._client = zerorpc.Client()
|
self._client = zerorpc.Client()
|
||||||
self._client.connect(self._server_endpoint)
|
self._client.connect(self._server_endpoint)
|
||||||
|
|
||||||
def test_zerorpc_middleware(self):
|
|
||||||
try:
|
try:
|
||||||
self._client.choice([])
|
self._client.choice([])
|
||||||
except zerorpc.exceptions.RemoteError, ex:
|
except zerorpc.exceptions.RemoteError, ex:
|
||||||
|
@ -58,6 +58,28 @@ class ZeroRPCTest(unittest2.TestCase):
|
||||||
|
|
||||||
self.fail('An IndexError exception should have been raised an catched')
|
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):
|
def tearDown(self):
|
||||||
self._client.close()
|
self._client.close()
|
||||||
self._server.close()
|
self._server.close()
|
||||||
|
|
Loading…
Reference in New Issue