Skip to content

Commit 49a86ba

Browse files
fix: flatten the map for span attributes
Signed-off-by: Shivanshu Raj Shrivastava <[email protected]>
1 parent f193435 commit 49a86ba

File tree

2 files changed

+82
-3
lines changed
  • instrumentation/opentelemetry-instrumentation-celery

2 files changed

+82
-3
lines changed

instrumentation/opentelemetry-instrumentation-celery/src/opentelemetry/instrumentation/celery/utils.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,17 @@
5555
)
5656

5757

58+
def flatten_dict(d, parent_key="", sep="."):
59+
items = []
60+
for k, v in d.items():
61+
new_key = f"{parent_key}{sep}{k}" if parent_key else k
62+
if isinstance(v, dict):
63+
items.extend(flatten_dict(v, new_key, sep=sep).items())
64+
else:
65+
items.append((new_key, v))
66+
return dict(items)
67+
68+
5869
# pylint:disable=too-many-branches
5970
def set_attributes_from_context(span, context):
6071
"""Helper to extract meta values from a Celery Context"""
@@ -95,6 +106,14 @@ def set_attributes_from_context(span, context):
95106
SpanAttributes.MESSAGING_DESTINATION, routing_key
96107
)
97108

109+
# Flatten the dictionary
110+
if value:
111+
flattened = flatten_dict(value)
112+
for k, v in flattened.items():
113+
if v is not None and v != "":
114+
span.set_attribute(f"celery.delivery_info.{k}", v)
115+
continue
116+
98117
elif key == "id":
99118
attribute_name = SpanAttributes.MESSAGING_MESSAGE_ID
100119

@@ -115,6 +134,15 @@ def set_attributes_from_context(span, context):
115134
value = "topic"
116135
break
117136

137+
# If the value is a dictionary, flatten it
138+
if isinstance(value, dict):
139+
flattened = flatten_dict(value)
140+
for nested_key, nested_value in flattened.items():
141+
if nested_value is not None and nested_value != "":
142+
nested_attr_name = f"celery.{key}.{nested_key}"
143+
span.set_attribute(nested_attr_name, nested_value)
144+
continue
145+
118146
# set attribute name if not set specially for a key
119147
if attribute_name is None:
120148
attribute_name = f"celery.{key}"

instrumentation/opentelemetry-instrumentation-celery/tests/test_utils.py

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,7 @@ def test_set_attributes_from_context(self):
5858
span.attributes.get(SpanAttributes.MESSAGING_DESTINATION), "celery"
5959
)
6060

61-
self.assertEqual(
62-
span.attributes["celery.delivery_info"], str({"eager": True})
63-
)
61+
self.assertEqual(span.attributes["celery.delivery_info.eager"], True)
6462
self.assertEqual(span.attributes.get("celery.eta"), "soon")
6563
self.assertEqual(span.attributes.get("celery.expires"), "later")
6664
self.assertEqual(span.attributes.get("celery.hostname"), "localhost")
@@ -72,6 +70,59 @@ def test_set_attributes_from_context(self):
7270
)
7371
self.assertNotIn("custom_meta", span.attributes)
7472

73+
def test_set_nested_attributes_from_context(self):
74+
context = {
75+
"correlation_id": "44b7f305",
76+
"delivery_info": {
77+
"eager": True,
78+
"routing_key": "api_gateway",
79+
"priority": 0,
80+
"redelivered": False,
81+
},
82+
"eta": {"time": "soon", "date": "today"},
83+
"expires": "later",
84+
"hostname": "localhost",
85+
"id": "44b7f305",
86+
"reply_to": "44b7f305",
87+
"retries": 4,
88+
"timelimit": ("now", "later"),
89+
"custom_meta": "custom_value",
90+
"routing_key": "celery",
91+
}
92+
93+
span = trace._Span("name", mock.Mock(spec=trace_api.SpanContext))
94+
utils.set_attributes_from_context(span, context)
95+
96+
self.assertEqual(
97+
span.attributes.get(SpanAttributes.MESSAGING_MESSAGE_ID),
98+
"44b7f305",
99+
)
100+
self.assertEqual(
101+
span.attributes.get(SpanAttributes.MESSAGING_CONVERSATION_ID),
102+
"44b7f305",
103+
)
104+
self.assertEqual(
105+
span.attributes.get(SpanAttributes.MESSAGING_DESTINATION), "celery"
106+
)
107+
108+
self.assertEqual(span.attributes["celery.delivery_info.eager"], True)
109+
self.assertEqual(
110+
span.attributes["celery.delivery_info.routing_key"], "api_gateway"
111+
)
112+
self.assertEqual(span.attributes["celery.delivery_info.priority"], 0)
113+
self.assertEqual(
114+
span.attributes["celery.delivery_info.redelivered"], False
115+
)
116+
self.assertEqual(span.attributes.get("celery.eta.time"), "soon")
117+
self.assertEqual(span.attributes.get("celery.eta.date"), "today")
118+
119+
self.assertEqual(span.attributes.get("celery.reply_to"), "44b7f305")
120+
self.assertEqual(span.attributes.get("celery.retries"), 4)
121+
self.assertEqual(
122+
span.attributes.get("celery.timelimit"), ("now", "later")
123+
)
124+
self.assertNotIn("custom_meta", span.attributes)
125+
75126
def test_set_attributes_not_recording(self):
76127
# it should extract only relevant keys
77128
context = {

0 commit comments

Comments
 (0)