From e75de6a4a9c52762288c6b871f9376b6cfdc0acf Mon Sep 17 00:00:00 2001 From: Ben Donnelly Date: Mon, 19 Feb 2024 09:57:53 +0000 Subject: [PATCH 1/4] fix(attributes): add tracepoint attribute - add attributes for snapshot and context closes #35 --- src/deep/api/plugin/__init__.py | 3 ++- src/deep/api/plugin/otel.py | 16 +++++++++++++--- src/deep/api/plugin/python.py | 3 ++- src/deep/api/plugin/span/__init__.py | 4 +++- src/deep/api/tracepoint/eventsnapshot.py | 5 +++++ src/deep/processor/context/snapshot_action.py | 9 +++++---- src/deep/processor/context/span_action.py | 2 +- 7 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/deep/api/plugin/__init__.py b/src/deep/api/plugin/__init__.py index b7569d3..af3d908 100644 --- a/src/deep/api/plugin/__init__.py +++ b/src/deep/api/plugin/__init__.py @@ -148,10 +148,11 @@ class SnapshotDecorator(Plugin, abc.ABC): """Implement this to decorate collected snapshots with attributes.""" @abc.abstractmethod - def decorate(self, context: ActionContext) -> Optional[BoundedAttributes]: + def decorate(self, snapshot_id: str, context: ActionContext) -> Optional[BoundedAttributes]: """ Decorate a snapshot with additional data. + :param snapshot_id: the id of the collected snapshot :param context: the action context for this action :return: the additional attributes to attach diff --git a/src/deep/api/plugin/otel.py b/src/deep/api/plugin/otel.py index d0064ca..f4cff7b 100644 --- a/src/deep/api/plugin/otel.py +++ b/src/deep/api/plugin/otel.py @@ -91,14 +91,22 @@ class OTelPlugin(ResourceProvider, SnapshotDecorator, SpanProcessor): Provide span and trace information to the snapshot. """ - def create_span(self, name: str) -> Optional['Span']: + def create_span(self, name: str, context_id: str, tracepoint_id: str) -> Optional['Span']: """ Create and return a new span. :param name: the name of the span to create + :param context_id: the id of the context + :param tracepoint_id: the id of thr tracepoint :return: the created span """ - span = trace.get_tracer("deep").start_as_current_span(name, end_on_exit=False, attributes={'dynamic': 'deep'}) + span = trace.get_tracer("deep").start_as_current_span(name, + end_on_exit=False, + attributes={'dynamic': 'deep', + 'context': context_id, + "tracepoint": tracepoint_id + }, + ) if span: # noinspection PyUnresolvedReferences # this is a generator contextlib._GeneratorContextManager @@ -132,16 +140,18 @@ def resource(self) -> Optional[Resource]: return Resource.create(attributes=attributes) return None - def decorate(self, context: ActionContext) -> Optional[BoundedAttributes]: + def decorate(self, snapshot_id: str, context: ActionContext) -> Optional[BoundedAttributes]: """ Decorate a snapshot with additional data. + :param snapshot_id: the id of the collected snapshot :param context: the action context for this action :return: the additional attributes to attach """ span = self.current_span() if span is not None: + span.add_attribute("snapshot", snapshot_id) return BoundedAttributes(attributes={ "span_name": span.name, "trace_id": span.trace_id, diff --git a/src/deep/api/plugin/python.py b/src/deep/api/plugin/python.py index 22d7e38..f38343b 100644 --- a/src/deep/api/plugin/python.py +++ b/src/deep/api/plugin/python.py @@ -35,10 +35,11 @@ class PythonPlugin(ResourceProvider, SnapshotDecorator, TracepointLogger): This plugin provides the python version to the resource, and the thread name to the attributes. """ - def decorate(self, context: ActionContext) -> Optional[BoundedAttributes]: + def decorate(self, snapshot_id: str, context: ActionContext) -> Optional[BoundedAttributes]: """ Decorate a snapshot with additional data. + :param snapshot_id: the id of the collected snapshot :param context: the action context for this action :return: the additional attributes to attach diff --git a/src/deep/api/plugin/span/__init__.py b/src/deep/api/plugin/span/__init__.py index 4d68be8..e64b473 100644 --- a/src/deep/api/plugin/span/__init__.py +++ b/src/deep/api/plugin/span/__init__.py @@ -29,11 +29,13 @@ class SpanProcessor(Plugin, abc.ABC): """Span processor connects Deep to a span provider.""" @abc.abstractmethod - def create_span(self, name: str) -> Optional['Span']: + def create_span(self, name: str, context_id: str, tracepoint_id: str) -> Optional['Span']: """ Create and return a new span. :param name: the name of the span to create + :param context_id: the id of the context + :param tracepoint_id: the id of thr tracepoint :return: the created span """ pass diff --git a/src/deep/api/tracepoint/eventsnapshot.py b/src/deep/api/tracepoint/eventsnapshot.py index 6ead750..8849dcb 100644 --- a/src/deep/api/tracepoint/eventsnapshot.py +++ b/src/deep/api/tracepoint/eventsnapshot.py @@ -83,6 +83,11 @@ def id(self): """The id of this snapshot.""" return self._id + @property + def id_str(self): + """The id of this snapshot.""" + return format(self._id, "032x") + @property def tracepoint(self): """The tracepoint that triggered this snapshot.""" diff --git a/src/deep/processor/context/snapshot_action.py b/src/deep/processor/context/snapshot_action.py index 8d95216..13f2875 100644 --- a/src/deep/processor/context/snapshot_action.py +++ b/src/deep/processor/context/snapshot_action.py @@ -176,10 +176,12 @@ def process(self, ctx: 'TriggerContext') -> Optional[ActionCallback]: return DeferredSnapshotActionCallback(self.action_context, snapshot) def _decorate_snapshot(self, ctx): - attributes = BoundedAttributes(attributes={'ctx_id': ctx.id}, immutable=False) + attributes = BoundedAttributes( + attributes={'context': ctx.id, 'tracepoint': self.action_context.location_action.tracepoint.id}, + immutable=False) for decorator in ctx.config.snapshot_decorators: try: - decorate = decorator.decorate(self.action_context) + decorate = decorator.decorate(self.snapshot.id_str, self.action_context) if decorate is not None: attributes.merge_in(decorate) except Exception: @@ -230,8 +232,7 @@ def __init__(self, action_context: ActionContext, snapshot: EventSnapshot): :param action_context: the action context that created this result :param snapshot: the snapshot result """ - self.action_context = action_context - self.snapshot = snapshot + super().__init__(action_context, snapshot) def process(self, ctx: 'TriggerContext') -> Optional[ActionCallback]: """ diff --git a/src/deep/processor/context/span_action.py b/src/deep/processor/context/span_action.py index 7eb0846..341a411 100644 --- a/src/deep/processor/context/span_action.py +++ b/src/deep/processor/context/span_action.py @@ -86,7 +86,7 @@ def _process_action(self): spans = [] for span_processor in self.trigger_context.config.span_processors: - span = span_processor.create_span(name) + span = span_processor.create_span(name, self.trigger_context.id, self.location_action.tracepoint.id) if span: spans.append(span) From a2ed390765c07b3e4e81ab59cf746308c876f47b Mon Sep 17 00:00:00 2001 From: Ben Donnelly Date: Mon, 19 Feb 2024 10:45:58 +0000 Subject: [PATCH 2/4] fix(attributes): fix tests update CHANGELOG.md closes #35 --- CHANGELOG.md | 1 + tests/unit_tests/api/plugin/test_otel.py | 2 +- tests/unit_tests/api/plugin/test_python.py | 2 +- tests/unit_tests/processor/test_trigger_handler.py | 4 ++-- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7297b2f..925b8ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ - **[FEATURE]**: feat(capture): add support for capture tracepoints [#34](https://github.com/intergral/deep/pull/34) [@Umaaz](https://github.com/Umaaz) - **[BUGFIX]**: fix(duration): snapshot duration not set [#37](https://github.com/intergral/deep/pull/37) [@Umaaz](https://github.com/Umaaz) +- **[BUGFIX]**: fix(attributes): snapshot attributes not set [#38](https://github.com/intergral/deep/pull/38) [@Umaaz](https://github.com/Umaaz) # 1.1.0 (06/02/2024) diff --git a/tests/unit_tests/api/plugin/test_otel.py b/tests/unit_tests/api/plugin/test_otel.py index ef2e6f5..f507e07 100644 --- a/tests/unit_tests/api/plugin/test_otel.py +++ b/tests/unit_tests/api/plugin/test_otel.py @@ -39,7 +39,7 @@ def test_load_plugin(self): def test_collect_attributes(self): with trace.get_tracer_provider().get_tracer("test").start_as_current_span("test-span"): plugin = OTelPlugin() - attributes = plugin.decorate(None) + attributes = plugin.decorate("", None) self.assertIsNotNone(attributes) self.assertEqual("test-span", attributes.get("span_name")) self.assertIsNotNone(attributes.get("span_id")) diff --git a/tests/unit_tests/api/plugin/test_python.py b/tests/unit_tests/api/plugin/test_python.py index 6864539..376eb3b 100644 --- a/tests/unit_tests/api/plugin/test_python.py +++ b/tests/unit_tests/api/plugin/test_python.py @@ -27,6 +27,6 @@ def test_load_plugin(self): def test_collect_attributes(self): plugin = PythonPlugin() - attributes = plugin.decorate(None) + attributes = plugin.decorate("", None) self.assertIsNotNone(attributes) self.assertEqual("MainThread", attributes.get("thread_name")) diff --git a/tests/unit_tests/processor/test_trigger_handler.py b/tests/unit_tests/processor/test_trigger_handler.py index 51be700..f4722c4 100644 --- a/tests/unit_tests/processor/test_trigger_handler.py +++ b/tests/unit_tests/processor/test_trigger_handler.py @@ -256,7 +256,7 @@ def test_span_action(self): config = MockConfigService({}) mock_plugin = mockito.mock(spec=SpanProcessor) mock_span = mockito.mock() - mockito.when(mock_plugin).create_span('some_test_function').thenReturn(mock_span) + mockito.when(mock_plugin).create_span('some_test_function', mockito.ANY, mockito.ANY).thenReturn(mock_span) config.plugins = [mock_plugin] push = MockPushService(None, None) handler = TriggerHandler(config, push) @@ -285,7 +285,7 @@ def test_span_action(self): pushed = push.pushed self.assertEqual(0, len(pushed)) - mockito.verify(mock_plugin, mockito.times(1)).create_span("some_test_function") + mockito.verify(mock_plugin, mockito.times(1)).create_span("some_test_function", mockito.ANY, mockito.ANY) mockito.verify(mock_span, mockito.times(1)).close() From 3d6df268bf6f364c20d82e995db2b739579bd237 Mon Sep 17 00:00:00 2001 From: Ben Donnelly Date: Mon, 19 Feb 2024 11:00:24 +0000 Subject: [PATCH 3/4] fix(attributes): add additional test for span attributes update CHANGELOG.md closes #35 --- tests/unit_tests/api/plugin/test_otel.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/unit_tests/api/plugin/test_otel.py b/tests/unit_tests/api/plugin/test_otel.py index f507e07..7c92746 100644 --- a/tests/unit_tests/api/plugin/test_otel.py +++ b/tests/unit_tests/api/plugin/test_otel.py @@ -39,8 +39,16 @@ def test_load_plugin(self): def test_collect_attributes(self): with trace.get_tracer_provider().get_tracer("test").start_as_current_span("test-span"): plugin = OTelPlugin() - attributes = plugin.decorate("", None) + attributes = plugin.decorate("snap_id", None) self.assertIsNotNone(attributes) self.assertEqual("test-span", attributes.get("span_name")) self.assertIsNotNone(attributes.get("span_id")) self.assertIsNotNone(attributes.get("trace_id")) + self.assertEqual(plugin.current_span().proxy.attributes, {"snapshot":"snap_id"}) + + def test_create_span(self): + plugin = OTelPlugin() + span = plugin.create_span("test", "ctx_id", "tp_id") + self.assertIsNotNone(span) + attributes = span.proxy.attributes + self.assertEqual({"dynamic": "deep", "context": "ctx_id", "tracepoint": "tp_id"}, attributes) From 3d039275dd57a42195458b6c6aa340591d562970 Mon Sep 17 00:00:00 2001 From: Ben Donnelly Date: Mon, 19 Feb 2024 11:00:42 +0000 Subject: [PATCH 4/4] fix(attributes): add additional test for span attributes update CHANGELOG.md closes #35 --- tests/unit_tests/api/plugin/test_otel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit_tests/api/plugin/test_otel.py b/tests/unit_tests/api/plugin/test_otel.py index 7c92746..773a473 100644 --- a/tests/unit_tests/api/plugin/test_otel.py +++ b/tests/unit_tests/api/plugin/test_otel.py @@ -44,7 +44,7 @@ def test_collect_attributes(self): self.assertEqual("test-span", attributes.get("span_name")) self.assertIsNotNone(attributes.get("span_id")) self.assertIsNotNone(attributes.get("trace_id")) - self.assertEqual(plugin.current_span().proxy.attributes, {"snapshot":"snap_id"}) + self.assertEqual(plugin.current_span().proxy.attributes, {"snapshot": "snap_id"}) def test_create_span(self): plugin = OTelPlugin()