From 8b4c7c1d08b7fe1e5e056593bfa34072580f7cc7 Mon Sep 17 00:00:00 2001 From: Karlie Li Date: Fri, 20 Jun 2025 14:32:44 -0700 Subject: [PATCH 01/17] add azure monitor exporter trace ai field --- .../exporter/export/trace/_exporter.py | 4 ++- .../exporter/export/trace/_utils.py | 6 +++++ .../tests/trace/test_trace.py | 26 ++++++++++++++++++- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_exporter.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_exporter.py index b0643499c978..0011baad8c2b 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_exporter.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_exporter.py @@ -407,7 +407,7 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: span.attributes, ) elif gen_ai_attributes.GEN_AI_SYSTEM in span.attributes: # GenAI - data.type = span.attributes[gen_ai_attributes.GEN_AI_SYSTEM] + data.type = "GenAI | {}".format(span.attributes[gen_ai_attributes.GEN_AI_SYSTEM]) else: data.type = "N/A" elif span.kind is SpanKind.PRODUCER: # Messaging @@ -428,6 +428,8 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: data.type = "InProc" if _AZURE_SDK_NAMESPACE_NAME in span.attributes: data.type += " | {}".format(span.attributes[_AZURE_SDK_NAMESPACE_NAME]) + elif gen_ai_attributes.GEN_AI_SYSTEM in span.attributes: # GenAI + data.type = "GenAI | {}".format(span.attributes[gen_ai_attributes.GEN_AI_SYSTEM]) # Apply truncation # See https://github.com/MohanGsk/ApplicationInsights-Home/tree/master/EndpointSpecs/Schemas/Bond if data.name: diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_utils.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_utils.py index fa8fc17fe9b5..ed39aa7bb8af 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_utils.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_utils.py @@ -11,6 +11,7 @@ user_agent_attributes, ) from opentelemetry.semconv.trace import DbSystemValues, SpanAttributes +from opentelemetry.semconv._incubating.attributes import gen_ai_attributes from opentelemetry.util.types import Attributes @@ -202,6 +203,11 @@ def _get_target_and_path_for_http_dependency( target = parsed_url.hostname elif parsed_url.netloc: target = parsed_url.netloc + elif gen_ai_attributes.GEN_AI_SYSTEM in attributes: + # If no fields are available to set target using standard rules, set Dependency Target to gen_ai.system if present + gen_ai_system = attributes.get(gen_ai_attributes.GEN_AI_SYSTEM) + if gen_ai_system: + target = gen_ai_system if not target: # Get target from peer.* attributes that are NOT peer.service target = _get_target_for_dependency_from_peer(attributes) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py index 4b44b1f1926f..e71b58d8840e 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py @@ -762,8 +762,32 @@ def test_span_to_envelope_client_gen_ai(self): self.assertEqual(envelope.data.base_data.result_code, "0") self.assertEqual(envelope.data.base_type, "RemoteDependencyData") - self.assertEqual(envelope.data.base_data.type, "az.ai.inference") + self.assertEqual(envelope.data.base_data.type, "GenAI | az.ai.inference") self.assertEqual(len(envelope.data.base_data.properties), 1) + + def test_span_to_envelope_client_internal_genai_type(self): + exporter = self._exporter + start_time = 1575494316027613500 + end_time = start_time + 1001000000 + + span = trace._Span( + name="genai-test", + context=SpanContext( + trace_id=36873507687745823477771305566750195431, + span_id=12030755672171557337, + is_remote=False, + ), + attributes={ + "gen_ai.system": "az.ai.inference", + }, + kind=SpanKind.INTERNAL, + ) + span.start(start_time=start_time) + span.end(end_time=end_time) + span._status = Status(status_code=StatusCode.UNSET) + envelope = exporter._span_to_envelope(span) + + self.assertEqual(envelope.data.base_data.type, "GenAI | az.ai.inference") def test_span_to_envelope_client_azure(self): exporter = self._exporter From a51b96ea40fddc16ab482b8aa24fda7160cb2e7a Mon Sep 17 00:00:00 2001 From: Karlie Li Date: Fri, 20 Jun 2025 17:47:43 -0700 Subject: [PATCH 02/17] update --- .../tests/trace/test_trace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py index e71b58d8840e..eb32af690734 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py @@ -771,7 +771,7 @@ def test_span_to_envelope_client_internal_genai_type(self): end_time = start_time + 1001000000 span = trace._Span( - name="genai-test", + name="test", context=SpanContext( trace_id=36873507687745823477771305566750195431, span_id=12030755672171557337, From 1b6099c47c55cbfe53932597f96eee7ff0476ed7 Mon Sep 17 00:00:00 2001 From: Karlie Li Date: Thu, 26 Jun 2025 15:28:30 -0700 Subject: [PATCH 03/17] update --- .../exporter/export/trace/_exporter.py | 16 ++++---- .../exporter/export/trace/_utils.py | 23 ++++++++--- .../tests/trace/test_trace.py | 38 ++++++++++++++++++- 3 files changed, 63 insertions(+), 14 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_exporter.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_exporter.py index 0011baad8c2b..18b52ea4fbf2 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_exporter.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_exporter.py @@ -96,6 +96,8 @@ _SAMPLE_RATE_KEY, ] +_GEN_AI_ATTRIBUTE_PREFIXE = "GenAI | {}" + class AzureMonitorTraceExporter(BaseExporter, SpanExporter): """Azure Monitor Trace exporter for OpenTelemetry.""" @@ -333,7 +335,9 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: envelope.data = MonitorBase(base_data=data, base_type="RemoteDependencyData") target = trace_utils._get_target_for_dependency_from_peer(span.attributes) if span.kind is SpanKind.CLIENT: - if _AZURE_SDK_NAMESPACE_NAME in span.attributes: # Azure specific resources + if gen_ai_attributes.GEN_AI_SYSTEM in span.attributes: # GenAI + data.type = _GEN_AI_ATTRIBUTE_PREFIXE.format(span.attributes[gen_ai_attributes.GEN_AI_SYSTEM]) + elif _AZURE_SDK_NAMESPACE_NAME in span.attributes: # Azure specific resources # Currently only eventhub and servicebus are supported # https://github.com/Azure/azure-sdk-for-python/issues/9256 data.type = span.attributes[_AZURE_SDK_NAMESPACE_NAME] @@ -348,7 +352,7 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: # data if url: data.data = url - target, path = trace_utils._get_target_and_path_for_http_dependency( + target, path = trace_utils._get_target_and_path_for_http_dependency_with_gen_ai( span.attributes, url, ) @@ -406,8 +410,6 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: target, # type: ignore span.attributes, ) - elif gen_ai_attributes.GEN_AI_SYSTEM in span.attributes: # GenAI - data.type = "GenAI | {}".format(span.attributes[gen_ai_attributes.GEN_AI_SYSTEM]) else: data.type = "N/A" elif span.kind is SpanKind.PRODUCER: # Messaging @@ -426,10 +428,10 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: ) else: # SpanKind.INTERNAL data.type = "InProc" - if _AZURE_SDK_NAMESPACE_NAME in span.attributes: + if gen_ai_attributes.GEN_AI_SYSTEM in span.attributes: # GenAI + data.type = _GEN_AI_ATTRIBUTE_PREFIXE.format(span.attributes[gen_ai_attributes.GEN_AI_SYSTEM]) + elif _AZURE_SDK_NAMESPACE_NAME in span.attributes: data.type += " | {}".format(span.attributes[_AZURE_SDK_NAMESPACE_NAME]) - elif gen_ai_attributes.GEN_AI_SYSTEM in span.attributes: # GenAI - data.type = "GenAI | {}".format(span.attributes[gen_ai_attributes.GEN_AI_SYSTEM]) # Apply truncation # See https://github.com/MohanGsk/ApplicationInsights-Home/tree/master/EndpointSpecs/Schemas/Bond if data.name: diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_utils.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_utils.py index ed39aa7bb8af..34f1f2913be2 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_utils.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_utils.py @@ -203,16 +203,29 @@ def _get_target_and_path_for_http_dependency( target = parsed_url.hostname elif parsed_url.netloc: target = parsed_url.netloc - elif gen_ai_attributes.GEN_AI_SYSTEM in attributes: - # If no fields are available to set target using standard rules, set Dependency Target to gen_ai.system if present - gen_ai_system = attributes.get(gen_ai_attributes.GEN_AI_SYSTEM) - if gen_ai_system: - target = gen_ai_system if not target: # Get target from peer.* attributes that are NOT peer.service target = _get_target_for_dependency_from_peer(attributes) return (target, path) +@no_type_check +def _get_target_and_path_for_http_dependency_with_gen_ai( + attributes: Attributes, + url: Optional[str] = "", # Usually populated by _get_url_for_http_dependency() +) -> Tuple[Optional[str], str]: + target = "" + path = "/" + # set target using standard rules + target, path = _get_target_and_path_for_http_dependency(attributes, url) + + # If no fields are available to set target using standard rules, set Dependency Target to gen_ai.system if present + if not target: + if attributes and gen_ai_attributes.GEN_AI_SYSTEM in attributes: + gen_ai_system = attributes.get(gen_ai_attributes.GEN_AI_SYSTEM) + if gen_ai_system: + target = gen_ai_system + return (target, path) + @no_type_check def _get_target_for_db_dependency( diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py index eb32af690734..9b53b16824f1 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py @@ -440,6 +440,15 @@ def test_span_to_envelope_client_http(self): envelope = exporter._span_to_envelope(span) self.assertEqual(envelope.data.base_data.target, "www.example.com") + # Set target with gen_ai if no fields are available to set target using standard rules + span._attributes = { + "http.request.method": "GET", + "gen_ai.system": "az.ai.inference" + } + envelope = exporter._span_to_envelope(span) + self.assertEqual(envelope.data.base_data.target, "az.ai.inference") + self.assertEqual(envelope.data.base_data.name, "test") + # url # spell-checker:ignore ddds span._attributes = { @@ -765,7 +774,7 @@ def test_span_to_envelope_client_gen_ai(self): self.assertEqual(envelope.data.base_data.type, "GenAI | az.ai.inference") self.assertEqual(len(envelope.data.base_data.properties), 1) - def test_span_to_envelope_client_internal_genai_type(self): + def test_span_to_envelope_client_internal_gen_ai_type(self): exporter = self._exporter start_time = 1575494316027613500 end_time = start_time + 1001000000 @@ -788,6 +797,31 @@ def test_span_to_envelope_client_internal_genai_type(self): envelope = exporter._span_to_envelope(span) self.assertEqual(envelope.data.base_data.type, "GenAI | az.ai.inference") + + def test_span_to_envelope_client_mutiple_types_with_gen_ai(self): + exporter = self._exporter + start_time = 1575494316027613500 + end_time = start_time + 1001000000 + + span = trace._Span( + name="test", + context=SpanContext( + trace_id=36873507687745823477771305566750195431, + span_id=12030755672171557337, + is_remote=False, + ), + attributes={ + "gen_ai.system": "az.ai.inference", + "az.namespace": "Microsoft.EventHub", + }, + kind=SpanKind.INTERNAL, + ) + span.start(start_time=start_time) + span.end(end_time=end_time) + span._status = Status(status_code=StatusCode.UNSET) + envelope = exporter._span_to_envelope(span) + + self.assertEqual(envelope.data.base_data.type, "GenAI | az.ai.inference") def test_span_to_envelope_client_azure(self): exporter = self._exporter @@ -834,7 +868,7 @@ def test_span_to_envelope_client_azure(self): envelope = exporter._span_to_envelope(span) self.assertEqual(envelope.data.base_data.target, "messaging") - def test_span_to_envelope_producer_messaging(self): + def test_span_to_envelope_producer_messaging(self) exporter = self._exporter start_time = 1575494316027613500 end_time = start_time + 1001000000 From ab0d801f7b03bac3489200fce256eea39a2a5789 Mon Sep 17 00:00:00 2001 From: Karlie Li Date: Thu, 26 Jun 2025 15:32:28 -0700 Subject: [PATCH 04/17] update --- .../tests/trace/test_trace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py index 9b53b16824f1..31f2eb569fde 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py @@ -868,7 +868,7 @@ def test_span_to_envelope_client_azure(self): envelope = exporter._span_to_envelope(span) self.assertEqual(envelope.data.base_data.target, "messaging") - def test_span_to_envelope_producer_messaging(self) + def test_span_to_envelope_producer_messaging(self): exporter = self._exporter start_time = 1575494316027613500 end_time = start_time + 1001000000 From 545b4b9361fa555bbf4a3d4001de1667ce584d3c Mon Sep 17 00:00:00 2001 From: Karlie Li Date: Fri, 27 Jun 2025 00:42:26 -0700 Subject: [PATCH 05/17] update --- .../opentelemetry/exporter/export/trace/_utils.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_utils.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_utils.py index 34f1f2913be2..ab3c688342e4 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_utils.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_utils.py @@ -219,11 +219,10 @@ def _get_target_and_path_for_http_dependency_with_gen_ai( target, path = _get_target_and_path_for_http_dependency(attributes, url) # If no fields are available to set target using standard rules, set Dependency Target to gen_ai.system if present - if not target: - if attributes and gen_ai_attributes.GEN_AI_SYSTEM in attributes: - gen_ai_system = attributes.get(gen_ai_attributes.GEN_AI_SYSTEM) - if gen_ai_system: - target = gen_ai_system + if not target and attributes: + gen_ai_system = span.attributes[gen_ai_attributes.GEN_AI_SYSTEM] + if gen_ai_system: + target = gen_ai_system return (target, path) From 34543fa3b9722fde3cf98f923bfe55f188bf9d21 Mon Sep 17 00:00:00 2001 From: Karlie Li Date: Fri, 27 Jun 2025 01:08:31 -0700 Subject: [PATCH 06/17] update --- .../opentelemetry/exporter/export/trace/_exporter.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_exporter.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_exporter.py index 18b52ea4fbf2..2ceef67091e0 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_exporter.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_exporter.py @@ -335,9 +335,7 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: envelope.data = MonitorBase(base_data=data, base_type="RemoteDependencyData") target = trace_utils._get_target_for_dependency_from_peer(span.attributes) if span.kind is SpanKind.CLIENT: - if gen_ai_attributes.GEN_AI_SYSTEM in span.attributes: # GenAI - data.type = _GEN_AI_ATTRIBUTE_PREFIXE.format(span.attributes[gen_ai_attributes.GEN_AI_SYSTEM]) - elif _AZURE_SDK_NAMESPACE_NAME in span.attributes: # Azure specific resources + if _AZURE_SDK_NAMESPACE_NAME in span.attributes: # Azure specific resources # Currently only eventhub and servicebus are supported # https://github.com/Azure/azure-sdk-for-python/issues/9256 data.type = span.attributes[_AZURE_SDK_NAMESPACE_NAME] @@ -412,6 +410,10 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: ) else: data.type = "N/A" + # gen_ai take precedence over other mappings (ex. HTTP) even if their attributes are also present on the span. + # overrides any previously set type. + if gen_ai_attributes.GEN_AI_SYSTEM in span.attributes: # GenAI + data.type = _GEN_AI_ATTRIBUTE_PREFIXE.format(span.attributes[gen_ai_attributes.GEN_AI_SYSTEM]) elif span.kind is SpanKind.PRODUCER: # Messaging # Currently only eventhub and servicebus are supported that produce PRODUCER spans if _AZURE_SDK_NAMESPACE_NAME in span.attributes: From 0a5a8b18e878a2261462ab976253b73d78eb8aef Mon Sep 17 00:00:00 2001 From: Karlie Li Date: Fri, 27 Jun 2025 10:50:47 -0700 Subject: [PATCH 07/17] udpate --- .../azure/monitor/opentelemetry/exporter/export/trace/_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_utils.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_utils.py index ab3c688342e4..780b9196c9c8 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_utils.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_utils.py @@ -220,7 +220,7 @@ def _get_target_and_path_for_http_dependency_with_gen_ai( # If no fields are available to set target using standard rules, set Dependency Target to gen_ai.system if present if not target and attributes: - gen_ai_system = span.attributes[gen_ai_attributes.GEN_AI_SYSTEM] + gen_ai_system = attributes[gen_ai_attributes.GEN_AI_SYSTEM] if gen_ai_system: target = gen_ai_system return (target, path) From 14eacb343d0ad9fa6cf9b82adb4bf2a3880549d2 Mon Sep 17 00:00:00 2001 From: Karlie Li Date: Fri, 27 Jun 2025 11:05:12 -0700 Subject: [PATCH 08/17] update --- .../tests/trace/test_trace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py index 31f2eb569fde..72f6701772c3 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py @@ -447,7 +447,7 @@ def test_span_to_envelope_client_http(self): } envelope = exporter._span_to_envelope(span) self.assertEqual(envelope.data.base_data.target, "az.ai.inference") - self.assertEqual(envelope.data.base_data.name, "test") + self.assertEqual(envelope.data.base_data.name, " GET /") # url # spell-checker:ignore ddds From b1cbf8e17cd23952e53c4817876b89e73b3d974c Mon Sep 17 00:00:00 2001 From: Karlie Li Date: Fri, 27 Jun 2025 11:05:59 -0700 Subject: [PATCH 09/17] update --- .../tests/trace/test_trace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py index 72f6701772c3..fef024a5d045 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py @@ -447,7 +447,7 @@ def test_span_to_envelope_client_http(self): } envelope = exporter._span_to_envelope(span) self.assertEqual(envelope.data.base_data.target, "az.ai.inference") - self.assertEqual(envelope.data.base_data.name, " GET /") + self.assertEqual(envelope.data.base_data.name, "GET /") # url # spell-checker:ignore ddds From 7b1df84bac960398286d8b182084f40cc2b333bc Mon Sep 17 00:00:00 2001 From: Karlie Li Date: Fri, 27 Jun 2025 11:55:48 -0700 Subject: [PATCH 10/17] update --- .../azure/monitor/opentelemetry/exporter/export/trace/_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_utils.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_utils.py index 780b9196c9c8..30482da00bc3 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_utils.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_utils.py @@ -219,7 +219,7 @@ def _get_target_and_path_for_http_dependency_with_gen_ai( target, path = _get_target_and_path_for_http_dependency(attributes, url) # If no fields are available to set target using standard rules, set Dependency Target to gen_ai.system if present - if not target and attributes: + if not target and attributes and gen_ai_attributes.GEN_AI_SYSTEM in attributes: gen_ai_system = attributes[gen_ai_attributes.GEN_AI_SYSTEM] if gen_ai_system: target = gen_ai_system From afd842e09f94924bd5e27261e86bb344017235cf Mon Sep 17 00:00:00 2001 From: Karlie Li Date: Fri, 27 Jun 2025 13:01:29 -0700 Subject: [PATCH 11/17] update --- .../azure-monitor-opentelemetry-exporter/CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md b/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md index 29d0ae9d791b..a63c7157d077 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md @@ -10,6 +10,13 @@ ### Other Changes +## 1.0.0b40 () + +### Other Changes + +- Support AI Foundry by Handling GEN_AI_SYSTEM Attributes with [Spec](https://github.com/aep-health-and-standards/Telemetry-Collection-Spec/blob/main/ApplicationInsights/genai_semconv_mapping.md) + ([#41705](https://github.com/Azure/azure-sdk-for-python/pull/41705)) + ## 1.0.0b37 (2025-05-29) ### Features Added From 62eda6ee9fe8a50552315ac9289a758739d7191b Mon Sep 17 00:00:00 2001 From: Karlie Li Date: Fri, 27 Jun 2025 13:04:41 -0700 Subject: [PATCH 12/17] update --- .../azure-monitor-opentelemetry-exporter/CHANGELOG.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md b/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md index a63c7157d077..29d0ae9d791b 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md @@ -10,13 +10,6 @@ ### Other Changes -## 1.0.0b40 () - -### Other Changes - -- Support AI Foundry by Handling GEN_AI_SYSTEM Attributes with [Spec](https://github.com/aep-health-and-standards/Telemetry-Collection-Spec/blob/main/ApplicationInsights/genai_semconv_mapping.md) - ([#41705](https://github.com/Azure/azure-sdk-for-python/pull/41705)) - ## 1.0.0b37 (2025-05-29) ### Features Added From a470c5bcb4d7b11412e74b3ac5b1a0a63d37f9cb Mon Sep 17 00:00:00 2001 From: Karlie Li Date: Fri, 27 Jun 2025 13:12:18 -0700 Subject: [PATCH 13/17] update --- sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md b/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md index 5ea624ceb339..905a8101f2bc 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md @@ -10,6 +10,8 @@ ### Other Changes +- Support AI Foundry by Handling GEN_AI_SYSTEM Attributes with [Spec](https://github.com/aep-health-and-standards/Telemetry-Collection-Spec/blob/main/ApplicationInsights/genai_semconv_mapping.md) ([#41705](https://github.com/Azure/azure-sdk-for-python/pull/41705)) + ## 1.0.0b39 (2025-06-25) ### Bugs Fixed From 172e44009be3a78e3cad9e2bb0c2d8dbd4d0e75f Mon Sep 17 00:00:00 2001 From: Karlie Li Date: Fri, 27 Jun 2025 13:20:00 -0700 Subject: [PATCH 14/17] update --- .../opentelemetry/exporter/export/trace/_exporter.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_exporter.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_exporter.py index 2ceef67091e0..45943b64941b 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_exporter.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_exporter.py @@ -96,7 +96,7 @@ _SAMPLE_RATE_KEY, ] -_GEN_AI_ATTRIBUTE_PREFIXE = "GenAI | {}" +_GEN_AI_ATTRIBUTE_PREFIX = "GenAI | {}" class AzureMonitorTraceExporter(BaseExporter, SpanExporter): @@ -413,7 +413,7 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: # gen_ai take precedence over other mappings (ex. HTTP) even if their attributes are also present on the span. # overrides any previously set type. if gen_ai_attributes.GEN_AI_SYSTEM in span.attributes: # GenAI - data.type = _GEN_AI_ATTRIBUTE_PREFIXE.format(span.attributes[gen_ai_attributes.GEN_AI_SYSTEM]) + data.type = _GEN_AI_ATTRIBUTE_PREFIX.format(span.attributes[gen_ai_attributes.GEN_AI_SYSTEM]) elif span.kind is SpanKind.PRODUCER: # Messaging # Currently only eventhub and servicebus are supported that produce PRODUCER spans if _AZURE_SDK_NAMESPACE_NAME in span.attributes: @@ -431,7 +431,7 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: else: # SpanKind.INTERNAL data.type = "InProc" if gen_ai_attributes.GEN_AI_SYSTEM in span.attributes: # GenAI - data.type = _GEN_AI_ATTRIBUTE_PREFIXE.format(span.attributes[gen_ai_attributes.GEN_AI_SYSTEM]) + data.type = _GEN_AI_ATTRIBUTE_PREFIX.format(span.attributes[gen_ai_attributes.GEN_AI_SYSTEM]) elif _AZURE_SDK_NAMESPACE_NAME in span.attributes: data.type += " | {}".format(span.attributes[_AZURE_SDK_NAMESPACE_NAME]) # Apply truncation From f09e2655685f918884e3d9eb2a05bab40215b11f Mon Sep 17 00:00:00 2001 From: Karlie Li Date: Fri, 27 Jun 2025 17:20:26 -0700 Subject: [PATCH 15/17] update --- .../exporter/export/trace/_exporter.py | 17 ++++++++++++----- .../exporter/export/trace/_utils.py | 17 ----------------- .../tests/trace/test_trace.py | 9 ++++++++- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_exporter.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_exporter.py index 45943b64941b..206d0b2dd064 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_exporter.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_exporter.py @@ -335,6 +335,13 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: envelope.data = MonitorBase(base_data=data, base_type="RemoteDependencyData") target = trace_utils._get_target_for_dependency_from_peer(span.attributes) if span.kind is SpanKind.CLIENT: + gen_ai_attributes_val = "" + # gen_ai take precedence over other mappings (ex. HTTP) even if their attributes are also present on the span. + # following mappings will override the type + if gen_ai_attributes.GEN_AI_SYSTEM in span.attributes: # GenAI + gen_ai_attributes_val = span.attributes[gen_ai_attributes.GEN_AI_SYSTEM] + if gen_ai_attributes_val: + data.type = _GEN_AI_ATTRIBUTE_PREFIX.format(gen_ai_attributes_val) if _AZURE_SDK_NAMESPACE_NAME in span.attributes: # Azure specific resources # Currently only eventhub and servicebus are supported # https://github.com/Azure/azure-sdk-for-python/issues/9256 @@ -350,7 +357,7 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: # data if url: data.data = url - target, path = trace_utils._get_target_and_path_for_http_dependency_with_gen_ai( + target, path = trace_utils._get_target_and_path_for_http_dependency( span.attributes, url, ) @@ -410,10 +417,10 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: ) else: data.type = "N/A" - # gen_ai take precedence over other mappings (ex. HTTP) even if their attributes are also present on the span. - # overrides any previously set type. - if gen_ai_attributes.GEN_AI_SYSTEM in span.attributes: # GenAI - data.type = _GEN_AI_ATTRIBUTE_PREFIX.format(span.attributes[gen_ai_attributes.GEN_AI_SYSTEM]) + + # If no fields are available to set target using standard rules, set Dependency Target to gen_ai.system if present + if not target and gen_ai_attributes_val: + target = gen_ai_attributes_val elif span.kind is SpanKind.PRODUCER: # Messaging # Currently only eventhub and servicebus are supported that produce PRODUCER spans if _AZURE_SDK_NAMESPACE_NAME in span.attributes: diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_utils.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_utils.py index 30482da00bc3..1bc6ee2de85c 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_utils.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_utils.py @@ -208,23 +208,6 @@ def _get_target_and_path_for_http_dependency( target = _get_target_for_dependency_from_peer(attributes) return (target, path) -@no_type_check -def _get_target_and_path_for_http_dependency_with_gen_ai( - attributes: Attributes, - url: Optional[str] = "", # Usually populated by _get_url_for_http_dependency() -) -> Tuple[Optional[str], str]: - target = "" - path = "/" - # set target using standard rules - target, path = _get_target_and_path_for_http_dependency(attributes, url) - - # If no fields are available to set target using standard rules, set Dependency Target to gen_ai.system if present - if not target and attributes and gen_ai_attributes.GEN_AI_SYSTEM in attributes: - gen_ai_system = attributes[gen_ai_attributes.GEN_AI_SYSTEM] - if gen_ai_system: - target = gen_ai_system - return (target, path) - @no_type_check def _get_target_for_db_dependency( diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py index 72059814659b..5a36ff69ec63 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py @@ -438,7 +438,6 @@ def test_span_to_envelope_client_http(self): envelope = exporter._span_to_envelope(span) self.assertEqual(envelope.data.base_data.target, "www.example.com") - # Set target with gen_ai if no fields are available to set target using standard rules span._attributes = { "http.request.method": "GET", "gen_ai.system": "az.ai.inference" @@ -447,6 +446,14 @@ def test_span_to_envelope_client_http(self): self.assertEqual(envelope.data.base_data.target, "az.ai.inference") self.assertEqual(envelope.data.base_data.name, "GET /") + span._attributes = { + "http.request.method": "GET", + "server.address": "www.example.com", + "gen_ai.system": "az.ai.inference" + } + envelope = exporter._span_to_envelope(span) + self.assertEqual(envelope.data.base_data.target, "www.example.com") + # url # spell-checker:ignore ddds span._attributes = { From 18b042bdb58fff02df87f90b58deab5e677b66ca Mon Sep 17 00:00:00 2001 From: Karlie Li Date: Fri, 27 Jun 2025 17:21:17 -0700 Subject: [PATCH 16/17] update --- .../azure/monitor/opentelemetry/exporter/export/trace/_utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_utils.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_utils.py index 1bc6ee2de85c..fa8fc17fe9b5 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_utils.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_utils.py @@ -11,7 +11,6 @@ user_agent_attributes, ) from opentelemetry.semconv.trace import DbSystemValues, SpanAttributes -from opentelemetry.semconv._incubating.attributes import gen_ai_attributes from opentelemetry.util.types import Attributes From 956153997181b462bd34e35c6cfcb6767a7dcb4d Mon Sep 17 00:00:00 2001 From: Karlie Li Date: Fri, 27 Jun 2025 17:43:47 -0700 Subject: [PATCH 17/17] update --- .../tests/trace/test_trace.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py index 5a36ff69ec63..d90eacc3a0ef 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/trace/test_trace.py @@ -449,6 +449,8 @@ def test_span_to_envelope_client_http(self): span._attributes = { "http.request.method": "GET", "server.address": "www.example.com", + "server.port": 80, + "url.scheme": "http", "gen_ai.system": "az.ai.inference" } envelope = exporter._span_to_envelope(span) @@ -776,7 +778,7 @@ def test_span_to_envelope_client_gen_ai(self): self.assertEqual(envelope.data.base_data.result_code, "0") self.assertEqual(envelope.data.base_type, "RemoteDependencyData") - self.assertEqual(envelope.data.base_data.type, "GenAI | az.ai.inference") + self.assertEqual(envelope.data.base_data.type, "N/A") self.assertEqual(len(envelope.data.base_data.properties), 1) def test_span_to_envelope_client_internal_gen_ai_type(self):