Commit 2a9ab21e by Gabe Mulley

support event processors

parent 35691e74
......@@ -184,3 +184,75 @@ class TestTrack(TestCase): # pylint: disable=missing-docstring
self.tracker.emit(sentinel.name)
self.assert_backend_called_with(sentinel.name)
def test_single_processor(self):
self.tracker.processors.append(self.change_name)
self.tracker.emit(sentinel.name)
self.assert_backend_called_with(sentinel.changed_name)
def change_name(self, event):
"""Modify the event type of the event"""
event['name'] = sentinel.changed_name
return event
def test_non_callable_processor(self):
self.tracker.processors.append(object())
self.tracker.emit(sentinel.name)
self.assert_backend_called_with(sentinel.name)
def test_callable_class_processor(self):
class SampleProcessor(object):
"""An event processing class"""
def __call__(self, event):
"""Modify the event type"""
event['name'] = sentinel.class_name
self.tracker.processors.append(SampleProcessor())
self.tracker.emit(sentinel.name)
self.assert_backend_called_with(sentinel.class_name)
def test_processor_chain(self):
def ensure_modified_event(event):
"""Assert the first processor added a field to the event"""
self.assertIn(sentinel.key, event)
self.assertEquals(event[sentinel.key], sentinel.value)
return event
self.tracker.processors.extend([self.change_name, ensure_modified_event])
self.tracker.emit(sentinel.name)
self.assert_backend_called_with(sentinel.changed_name)
def test_processor_failure(self):
def always_fail(event): # pylint: disable=unused-argument
"""Always raises an error"""
raise ValueError
self.tracker.processors.extend([always_fail, self.change_name])
self.tracker.emit(sentinel.name)
self.assert_backend_called_with(sentinel.changed_name)
def test_processor_returns_none(self):
def return_none(event): # pylint: disable=unused-argument
"""Don't return the event"""
pass
self.tracker.processors.append(return_none)
self.tracker.emit(sentinel.name)
self.assert_backend_called_with(sentinel.name)
def test_processor_modifies_the_same_event_object(self):
def forget_return(event):
"""Modify the event without returning it"""
event['name'] = sentinel.forgotten_return
def ensure_name_changed(event):
"""Assert the event type has been modified even though the event wasn't returned"""
self.assertEquals(event['name'], sentinel.forgotten_return)
self.tracker.processors.extend([forget_return, ensure_name_changed])
self.tracker.emit(sentinel.name)
self.assert_backend_called_with(sentinel.forgotten_return)
......@@ -36,9 +36,10 @@ class Tracker(object):
Track application events. Holds references to a set of backends that will
be used to persist any events that are emitted.
"""
def __init__(self, backends=None, context_locator=None):
def __init__(self, backends=None, context_locator=None, processors=None):
self.backends = backends or {}
self.context_locator = context_locator or DefaultContextLocator()
self.processors = processors or []
@property
def located_context(self):
......@@ -61,16 +62,44 @@ class Tracker(object):
Note that all values provided must be serializable.
"""
full_event = {
event = {
'name': name or UNKNOWN_EVENT_TYPE,
'timestamp': datetime.now(UTC),
'data': data or {},
'context': self.resolve_context()
}
event = self.process_event(event)
self.send_to_backends(event)
def process_event(self, event):
"""
Executes all event processors on the event in order.
`event` is a nested dictionary that represents the event.
Returns the modified event.
"""
for processor in self.processors:
try:
modified_event = processor(event)
if modified_event is not None:
event = modified_event
except Exception: # pylint: disable=broad-except
LOG.exception(
'Failed to execute processor: {0}'.format(processor)
)
return event
def send_to_backends(self, event):
"""Sends the event to all registered backends."""
for name, backend in self.backends.iteritems():
try:
backend.send(full_event)
backend.send(event)
except Exception: # pylint: disable=broad-except
LOG.exception(
'Unable to send event to backend: {0}'.format(name)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment