Skip to content

Commit ef1081c

Browse files
rads-1996Copilot
andauthored
Add Operation Name Propagation for Dependencies and Logs (#43588)
* Add operation name to dependencies * Add operation name to logs * Add CHANGELOG * Updated CHANGELOG * Remove extra space Co-authored-by: Copilot <[email protected]> * Fix data.name override Co-authored-by: Copilot <[email protected]> * added type safety * Retrigger CI/CD pipeline --------- Co-authored-by: Copilot <[email protected]>
1 parent 685381f commit ef1081c

File tree

5 files changed

+39
-5
lines changed

5 files changed

+39
-5
lines changed

sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
## 1.0.0b45 (Unreleased)
44

55
### Features Added
6+
- Added Operation Name Propagation for Dependencies and Logs
7+
([#43588](https://github.com/Azure/azure-sdk-for-python/pull/43588))
68
- Added local storage support for multiple users on the same Linux system
79
([#43483](https://github.com/Azure/azure-sdk-for-python/pull/43483))
810

sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ def _log_data_is_event(log_data: LogData) -> bool:
116116

117117

118118
# pylint: disable=protected-access
119+
# pylint: disable=too-many-statements
119120
def _convert_log_to_envelope(log_data: LogData) -> TelemetryItem:
120121
log_record = log_data.log_record
121122
time_stamp = log_record.timestamp if log_record.timestamp is not None else log_record.observed_timestamp
@@ -127,6 +128,14 @@ def _convert_log_to_envelope(log_data: LogData) -> TelemetryItem:
127128
envelope.tags[ContextTagKeys.AI_OPERATION_PARENT_ID] = "{:016x}".format( # type: ignore
128129
log_record.span_id or _DEFAULT_SPAN_ID
129130
)
131+
if (
132+
log_record.attributes
133+
and ContextTagKeys.AI_OPERATION_NAME in log_record.attributes
134+
and log_record.attributes[ContextTagKeys.AI_OPERATION_NAME] is not None
135+
):
136+
envelope.tags[ContextTagKeys.AI_OPERATION_NAME] = log_record.attributes.get( # type: ignore
137+
ContextTagKeys.AI_OPERATION_NAME
138+
)
130139
if _utils._is_any_synthetic_source(log_record.attributes):
131140
envelope.tags[ContextTagKeys.AI_OPERATION_SYNTHETIC_SOURCE] = "True" # type: ignore
132141
# Special use case: Customers want to be able to set location ip on log records

sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_exporter.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,6 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem:
321321
data.url = data.url[:2048]
322322
else: # INTERNAL, CLIENT, PRODUCER
323323
envelope.name = _REMOTE_DEPENDENCY_ENVELOPE_NAME
324-
# TODO: ai.operation.name for non-server spans
325324
time = 0
326325
if span.end_time and span.start_time:
327326
time = span.end_time - span.start_time
@@ -334,6 +333,7 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem:
334333
properties={},
335334
)
336335
envelope.data = MonitorBase(base_data=data, base_type="RemoteDependencyData")
336+
envelope.tags[ContextTagKeys.AI_OPERATION_NAME] = span.name
337337
target = trace_utils._get_target_for_dependency_from_peer(span.attributes)
338338
if span.kind is SpanKind.CLIENT:
339339
gen_ai_attributes_val = ""
@@ -351,6 +351,12 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem:
351351
# TODO: Not exposed in Swagger, need to update def
352352
envelope.tags["ai.user.userAgent"] = user_agent
353353
url = trace_utils._get_url_for_http_dependency(span.attributes)
354+
# Http specific logic for ai.operation.name
355+
if SpanAttributes.HTTP_ROUTE in span.attributes:
356+
envelope.tags[ContextTagKeys.AI_OPERATION_NAME] = "{} {}".format(
357+
span.attributes.get(HTTP_REQUEST_METHOD) or span.attributes.get(SpanAttributes.HTTP_METHOD),
358+
span.attributes[SpanAttributes.HTTP_ROUTE],
359+
)
354360
# data
355361
if url:
356362
data.data = url
@@ -365,6 +371,10 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem:
365371
span.attributes.get(SpanAttributes.HTTP_METHOD),
366372
path,
367373
)
374+
envelope.tags[ContextTagKeys.AI_OPERATION_NAME] = "{} {}".format(
375+
span.attributes.get(HTTP_REQUEST_METHOD) or span.attributes.get(SpanAttributes.HTTP_METHOD),
376+
path,
377+
)
368378
status_code = span.attributes.get(HTTP_RESPONSE_STATUS_CODE) or \
369379
span.attributes.get(SpanAttributes.HTTP_STATUS_CODE)
370380
if status_code:
@@ -447,7 +457,9 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem:
447457
data.type += " | {}".format(span.attributes[_AZURE_SDK_NAMESPACE_NAME])
448458
# Apply truncation
449459
# See https://github.com/MohanGsk/ApplicationInsights-Home/tree/master/EndpointSpecs/Schemas/Bond
450-
if data.name:
460+
if envelope.tags.get(ContextTagKeys.AI_OPERATION_NAME):
461+
data.name = envelope.tags[ContextTagKeys.AI_OPERATION_NAME][:1024]
462+
elif data.name:
451463
data.name = str(data.name)[:1024]
452464
if data.result_code:
453465
data.result_code = str(data.result_code)[:1024]

sdk/monitor/azure-monitor-opentelemetry-exporter/tests/logs/test_logs.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def setUpClass(cls):
7373
severity_number=SeverityNumber.WARN,
7474
body="Test message",
7575
resource=Resource.create(attributes={"asd": "test_resource"}),
76-
attributes={"test": "attribute"},
76+
attributes={"test": "attribute", "ai.operation.name": "TestOperationName"},
7777
),
7878
InstrumentationScope("test_name"),
7979
)
@@ -87,7 +87,7 @@ def setUpClass(cls):
8787
severity_number=SeverityNumber.WARN,
8888
body="",
8989
resource=Resource.create(attributes={"asd": "test_resource"}),
90-
attributes={"test": "attribute"},
90+
attributes={"test": "attribute", "ai.operation.name": "TestOperationName"},
9191
),
9292
InstrumentationScope("test_name"),
9393
)
@@ -115,7 +115,7 @@ def setUpClass(cls):
115115
severity_number=SeverityNumber.WARN,
116116
body={"foo": {"bar": "baz", "qux": 42}},
117117
resource=Resource.create(attributes={"asd": "test_resource"}),
118-
attributes={"test": "attribute"},
118+
attributes={"test": "attribute", "ai.operation.name": "TestOperationName"},
119119
),
120120
InstrumentationScope("test_name"),
121121
)
@@ -394,6 +394,7 @@ def test_log_to_envelope_partA(self):
394394
span_id = self._log_data.log_record.span_id
395395
self.assertEqual(envelope.tags.get(ContextTagKeys.AI_OPERATION_PARENT_ID), "{:016x}".format(span_id))
396396
self._log_data.log_record.resource = old_resource
397+
self.assertEqual(envelope.tags.get(ContextTagKeys.AI_OPERATION_NAME), "TestOperationName")
397398

398399
def test_log_to_envelope_partA_default(self):
399400
exporter = self._exporter
@@ -433,6 +434,7 @@ def test_log_to_envelope_log_empty(self):
433434
self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.Message")
434435
self.assertEqual(envelope.data.base_type, "MessageData")
435436
self.assertEqual(envelope.data.base_data.message, _DEFAULT_LOG_MESSAGE)
437+
self.assertEqual(envelope.tags.get(ContextTagKeys.AI_OPERATION_NAME), "TestOperationName")
436438

437439
def test_log_to_envelope_log_empty_with_whitespaces(self):
438440
exporter = self._exporter
@@ -447,6 +449,7 @@ def test_log_to_envelope_log_complex_body(self):
447449
self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.Message")
448450
self.assertEqual(envelope.data.base_type, "MessageData")
449451
self.assertEqual(envelope.data.base_data.message, json.dumps(self._log_data_complex_body.log_record.body))
452+
self.assertEqual(envelope.tags.get(ContextTagKeys.AI_OPERATION_NAME), "TestOperationName")
450453

451454
def test_log_to_envelope_log_complex_body_not_serializeable(self):
452455
exporter = self._exporter

sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ def test_span_to_envelope_client_http(self):
380380
self.assertEqual(envelope.data.base_data.data, "https://www.wikipedia.org/wiki/Rabbit")
381381

382382
self.assertEqual(envelope.data.base_type, "RemoteDependencyData")
383+
self.assertEqual(envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "GET /wiki/Rabbit")
383384
self.assertEqual(envelope.data.base_data.type, "HTTP")
384385
self.assertEqual(envelope.data.base_data.target, "service")
385386
self.assertEqual(
@@ -584,6 +585,7 @@ def test_span_to_envelope_client_db(self):
584585
self.assertTrue(envelope.data.base_data.success)
585586

586587
self.assertEqual(envelope.data.base_type, "RemoteDependencyData")
588+
self.assertEqual(envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "test")
587589
self.assertEqual(envelope.data.base_data.type, "db2 system")
588590
self.assertEqual(envelope.data.base_data.target, "service")
589591
self.assertEqual(envelope.data.base_data.data, "SELECT * from test")
@@ -703,6 +705,7 @@ def test_span_to_envelope_client_rpc(self):
703705
self.assertEqual(envelope.data.base_data.result_code, "0")
704706

705707
self.assertEqual(envelope.data.base_type, "RemoteDependencyData")
708+
self.assertEqual(envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "test")
706709
self.assertEqual(envelope.data.base_data.type, "rpc.system")
707710
self.assertEqual(envelope.data.base_data.target, "service")
708711
self.assertEqual(len(envelope.data.base_data.properties), 0)
@@ -748,6 +751,7 @@ def test_span_to_envelope_client_messaging(self):
748751
self.assertEqual(envelope.data.base_data.result_code, "0")
749752

750753
self.assertEqual(envelope.data.base_type, "RemoteDependencyData")
754+
self.assertEqual(envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "test")
751755
self.assertEqual(envelope.data.base_data.type, "messaging")
752756
self.assertEqual(envelope.data.base_data.target, "celery")
753757
self.assertEqual(len(envelope.data.base_data.properties), 0)
@@ -791,6 +795,7 @@ def test_span_to_envelope_client_gen_ai(self):
791795
self.assertEqual(envelope.data.base_data.result_code, "0")
792796

793797
self.assertEqual(envelope.data.base_type, "RemoteDependencyData")
798+
self.assertEqual(envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "test")
794799
self.assertEqual(envelope.data.base_data.type, "GenAI | az.ai.inference")
795800
self.assertEqual(envelope.data.base_data.target, "az.ai.inference")
796801
self.assertEqual(len(envelope.data.base_data.properties), 1)
@@ -880,6 +885,7 @@ def test_span_to_envelope_client_azure(self):
880885
self.assertEqual(envelope.data.base_data.result_code, "0")
881886

882887
self.assertEqual(envelope.data.base_type, "RemoteDependencyData")
888+
self.assertEqual(envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "test")
883889
self.assertEqual(envelope.data.base_data.type, "Microsoft.EventHub")
884890
self.assertEqual(envelope.data.base_data.target, "test_address/test_destination")
885891
self.assertEqual(len(envelope.data.base_data.properties), 2)
@@ -924,6 +930,7 @@ def test_span_to_envelope_producer_messaging(self):
924930
self.assertEqual(envelope.data.base_data.result_code, "0")
925931

926932
self.assertEqual(envelope.data.base_type, "RemoteDependencyData")
933+
self.assertEqual(envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "test")
927934
self.assertEqual(envelope.data.base_data.type, "Queue Message | messaging")
928935
self.assertEqual(envelope.data.base_data.target, "celery")
929936
self.assertEqual(len(envelope.data.base_data.properties), 0)
@@ -978,6 +985,7 @@ def test_span_to_envelope_internal(self):
978985
self.assertTrue(envelope.data.base_data.success)
979986

980987
self.assertEqual(envelope.data.base_type, "RemoteDependencyData")
988+
self.assertEqual(envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "test")
981989
self.assertEqual(envelope.data.base_data.type, "InProc")
982990
self.assertEqual(envelope.data.base_data.result_code, "0")
983991
self.assertEqual(len(envelope.data.base_data.properties), 0)

0 commit comments

Comments
 (0)