diff --git a/sdk/monitor/microsoft-opentelemetry-exporter-azuremonitor/CHANGELOG.md b/sdk/monitor/microsoft-opentelemetry-exporter-azuremonitor/CHANGELOG.md index 5c6697015048..ffa8cc0861de 100644 --- a/sdk/monitor/microsoft-opentelemetry-exporter-azuremonitor/CHANGELOG.md +++ b/sdk/monitor/microsoft-opentelemetry-exporter-azuremonitor/CHANGELOG.md @@ -6,6 +6,9 @@ - Rename Azure Trace exporter class, only allow connection string configuration ([#15349](https://github.com/Azure/azure-sdk-for-python/pull/15349)) +- OpenTelemetry Exporter use Resources API to retrieve cloud role props + ([#15816](https://github.com/Azure/azure-sdk-for-python/pull/15816)) + - Change span to envelope conversion to adhere to common schema and other languages ([#15344](https://github.com/Azure/azure-sdk-for-python/pull/15344)) diff --git a/sdk/monitor/microsoft-opentelemetry-exporter-azuremonitor/microsoft/opentelemetry/exporter/azuremonitor/_utils.py b/sdk/monitor/microsoft-opentelemetry-exporter-azuremonitor/microsoft/opentelemetry/exporter/azuremonitor/_utils.py index c34feef2ae0f..75fd643a96de 100644 --- a/sdk/monitor/microsoft-opentelemetry-exporter-azuremonitor/microsoft/opentelemetry/exporter/azuremonitor/_utils.py +++ b/sdk/monitor/microsoft-opentelemetry-exporter-azuremonitor/microsoft/opentelemetry/exporter/azuremonitor/_utils.py @@ -1,9 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. import locale -import os import platform -import sys import threading import time @@ -18,8 +16,6 @@ ).version azure_monitor_context = { - "ai.cloud.role": os.path.basename(sys.argv[0]) or "Python Application", - "ai.cloud.roleInstance": platform.node(), "ai.device.id": platform.node(), "ai.device.locale": locale.getdefaultlocale()[0], "ai.device.osVersion": platform.version(), diff --git a/sdk/monitor/microsoft-opentelemetry-exporter-azuremonitor/microsoft/opentelemetry/exporter/azuremonitor/export/trace/_exporter.py b/sdk/monitor/microsoft-opentelemetry-exporter-azuremonitor/microsoft/opentelemetry/exporter/azuremonitor/export/trace/_exporter.py index a878fdf9abb3..c55e267778b7 100644 --- a/sdk/monitor/microsoft-opentelemetry-exporter-azuremonitor/microsoft/opentelemetry/exporter/azuremonitor/export/trace/_exporter.py +++ b/sdk/monitor/microsoft-opentelemetry-exporter-azuremonitor/microsoft/opentelemetry/exporter/azuremonitor/export/trace/_exporter.py @@ -77,6 +77,20 @@ def convert_span_to_envelope(span: Span) -> TelemetryItem: tags=dict(_utils.azure_monitor_context), time=ns_to_iso_str(span.start_time), ) + if span.resource and span.resource.attributes: + # TODO: Get Resource attributes from OpenTelemetry SDK when available + service_name = span.resource.attributes.get("service.name") + service_namespace = span.resource.attributes.get("service.namespace") + service_instance_id = span.resource.attributes.get("service.instance.id") + if service_name: + if service_namespace: + envelope.tags["ai.cloud.role"] = service_namespace + \ + "." + service_name + else: + envelope.tags["ai.cloud.role"] = service_name + if service_instance_id: + envelope.tags["ai.cloud.roleInstance"] = service_instance_id + envelope.tags["ai.operation.id"] = "{:032x}".format(span.context.trace_id) parent = span.parent if parent: diff --git a/sdk/monitor/microsoft-opentelemetry-exporter-azuremonitor/tests/trace/test_trace.py b/sdk/monitor/microsoft-opentelemetry-exporter-azuremonitor/tests/trace/test_trace.py index c9f77771e608..0df10c477942 100644 --- a/sdk/monitor/microsoft-opentelemetry-exporter-azuremonitor/tests/trace/test_trace.py +++ b/sdk/monitor/microsoft-opentelemetry-exporter-azuremonitor/tests/trace/test_trace.py @@ -9,7 +9,7 @@ from unittest import mock # pylint: disable=import-error -from opentelemetry.sdk import trace +from opentelemetry.sdk import trace, resources from opentelemetry.sdk.trace.export import SpanExportResult from opentelemetry.trace import Link, SpanContext, SpanKind from opentelemetry.trace.status import Status, StatusCode @@ -143,9 +143,51 @@ def test_export_not_retryable(self): def test_span_to_envelope_none(self): exporter = self._exporter - self.assertIsNone(exporter._span_to_envelope(None)) + test_span = trace._Span( + name="test", + context=SpanContext( + trace_id=36873507687745823477771305566750195431, + span_id=12030755672171557338, + is_remote=False, + ), + ) + test_span.start() + test_span.end() + + def test_span_to_envelope_tags(self): + exporter = self._exporter + test_span = trace._Span( + name="test", + context=SpanContext( + trace_id=36873507687745823477771305566750195431, + span_id=12030755672171557338, + is_remote=False, + ), + ) + test_span.start() + test_span.end() + envelope = exporter._span_to_envelope(test_span) + + self.assertIsNotNone(envelope.tags) + self.assertIsNone(envelope.tags.get("ai.cloud.role")) + self.assertIsNone(envelope.tags.get("ai.cloud.roleInstance")) + self.assertIsNotNone(envelope.tags.get("ai.device.id")) + self.assertIsNotNone(envelope.tags.get("ai.device.locale")) + self.assertIsNotNone(envelope.tags.get("ai.device.osVersion")) + self.assertIsNotNone(envelope.tags.get("ai.device.type")) + self.assertIsNotNone(envelope.tags.get("ai.internal.sdkVersion")) + + test_span.resource = resources.Resource( + {"service.name": "testServiceName", + "service.namespace": "testServiceNamespace", + "service.instance.id": "testServiceInstanceId"}) + envelope = exporter._span_to_envelope(test_span) + self.assertEqual(envelope.tags.get("ai.cloud.role"), "testServiceNamespace.testServiceName") + self.assertEqual(envelope.tags.get( + "ai.cloud.roleInstance"), "testServiceInstanceId") # pylint: disable=too-many-statements + def test_span_to_envelope(self): exporter = AzureMonitorTraceExporter( connection_string="InstrumentationKey=12345678-1234-5678-abcd-12345678abcd",