From 19a075b5ea53be43a2a56707b36e1c66b273146c Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Wed, 14 Jan 2026 16:19:01 -0800 Subject: [PATCH 1/9] Add user id and auth id support --- .../exporter/_generated/models/_models.py | 5 +- .../exporter/_generated/models/_models_py3.py | 60 ++- .../exporter/export/logs/_exporter.py | 75 +++- .../exporter/export/trace/_exporter.py | 139 +++++-- .../tests/logs/test_logs.py | 192 +++++++-- .../tests/trace/test_trace.py | 373 ++++++++++++------ 6 files changed, 629 insertions(+), 215 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_generated/models/_models.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_generated/models/_models.py index ceaf61192c50..4cbcd29ccef2 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_generated/models/_models.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_generated/models/_models.py @@ -6,7 +6,6 @@ # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- -from azure.core.exceptions import HttpResponseError import msrest.serialization @@ -379,7 +378,7 @@ class PageViewData(MonitorDomain): _validation = { "version": {"required": True}, "id": {"required": True, "max_length": 512, "min_length": 0}, - "name": {"required": True, "max_length": 1024, "min_length": 0}, + "name": {"required": True, "max_length": 512, "min_length": 0}, "url": {"max_length": 2048, "min_length": 0}, "referred_uri": {"max_length": 2048, "min_length": 0}, } @@ -478,7 +477,7 @@ class PageViewPerfData(MonitorDomain): _validation = { "version": {"required": True}, "id": {"required": True, "max_length": 512, "min_length": 0}, - "name": {"required": True, "max_length": 1024, "min_length": 0}, + "name": {"required": True, "max_length": 512, "min_length": 0}, "url": {"max_length": 2048, "min_length": 0}, } diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_generated/models/_models_py3.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_generated/models/_models_py3.py index fefe33af86cb..32374f701e41 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_generated/models/_models_py3.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_generated/models/_models_py3.py @@ -9,7 +9,6 @@ import datetime from typing import Any, Dict, List, Optional, Union -from azure.core.exceptions import HttpResponseError import msrest.serialization from ._azure_monitor_client_enums import * @@ -36,7 +35,13 @@ class MonitorDomain(msrest.serialization.Model): "version": {"key": "ver", "type": "int"}, } - def __init__(self, *, version: int = 2, additional_properties: Optional[Dict[str, Any]] = None, **kwargs): + def __init__( + self, + *, + version: int = 2, + additional_properties: Optional[Dict[str, Any]] = None, + **kwargs + ): """ :keyword additional_properties: Unmatched properties from the message are deserialized to this collection. @@ -141,7 +146,9 @@ def __init__( :keyword measurements: Collection of custom measurements. :paramtype measurements: dict[str, float] """ - super(AvailabilityData, self).__init__(additional_properties=additional_properties, version=version, **kwargs) + super(AvailabilityData, self).__init__( + additional_properties=additional_properties, version=version, **kwargs + ) self.id = id self.name = name self.duration = duration @@ -214,7 +221,9 @@ def __init__( :keyword measurements: Collection of custom measurements. :paramtype measurements: dict[str, float] """ - super(MessageData, self).__init__(additional_properties=additional_properties, version=version, **kwargs) + super(MessageData, self).__init__( + additional_properties=additional_properties, version=version, **kwargs + ) self.message = message self.severity_level = severity_level self.properties = properties @@ -361,7 +370,9 @@ def __init__( :keyword properties: Collection of custom properties. :paramtype properties: dict[str, str] """ - super(MetricsData, self).__init__(additional_properties=additional_properties, version=version, **kwargs) + super(MetricsData, self).__init__( + additional_properties=additional_properties, version=version, **kwargs + ) self.metrics = metrics self.properties = properties @@ -381,7 +392,13 @@ class MonitorBase(msrest.serialization.Model): "base_data": {"key": "baseData", "type": "MonitorDomain"}, } - def __init__(self, *, base_type: Optional[str] = None, base_data: Optional["MonitorDomain"] = None, **kwargs): + def __init__( + self, + *, + base_type: Optional[str] = None, + base_data: Optional["MonitorDomain"] = None, + **kwargs + ): """ :keyword base_type: Name of item (B section) if any. If telemetry data is derived straight from this, this should be null. @@ -428,7 +445,7 @@ class PageViewData(MonitorDomain): _validation = { "version": {"required": True}, "id": {"required": True, "max_length": 512, "min_length": 0}, - "name": {"required": True, "max_length": 1024, "min_length": 0}, + "name": {"required": True, "max_length": 512, "min_length": 0}, "url": {"max_length": 2048, "min_length": 0}, "referred_uri": {"max_length": 2048, "min_length": 0}, } @@ -485,7 +502,9 @@ def __init__( :keyword measurements: Collection of custom measurements. :paramtype measurements: dict[str, float] """ - super(PageViewData, self).__init__(additional_properties=additional_properties, version=version, **kwargs) + super(PageViewData, self).__init__( + additional_properties=additional_properties, version=version, **kwargs + ) self.id = id self.name = name self.url = url @@ -540,7 +559,7 @@ class PageViewPerfData(MonitorDomain): _validation = { "version": {"required": True}, "id": {"required": True, "max_length": 512, "min_length": 0}, - "name": {"required": True, "max_length": 1024, "min_length": 0}, + "name": {"required": True, "max_length": 512, "min_length": 0}, "url": {"max_length": 2048, "min_length": 0}, } @@ -616,7 +635,9 @@ def __init__( :keyword measurements: Collection of custom measurements. :paramtype measurements: dict[str, float] """ - super(PageViewPerfData, self).__init__(additional_properties=additional_properties, version=version, **kwargs) + super(PageViewPerfData, self).__init__( + additional_properties=additional_properties, version=version, **kwargs + ) self.id = id self.name = name self.url = url @@ -869,7 +890,9 @@ def __init__( :keyword measurements: Collection of custom measurements. :paramtype measurements: dict[str, float] """ - super(RequestData, self).__init__(additional_properties=additional_properties, version=version, **kwargs) + super(RequestData, self).__init__( + additional_properties=additional_properties, version=version, **kwargs + ) self.id = id self.name = name self.duration = duration @@ -961,7 +984,12 @@ class TelemetryErrorDetails(msrest.serialization.Model): } def __init__( - self, *, index: Optional[int] = None, status_code: Optional[int] = None, message: Optional[str] = None, **kwargs + self, + *, + index: Optional[int] = None, + status_code: Optional[int] = None, + message: Optional[str] = None, + **kwargs ): """ :keyword index: The index in the original payload of the item. @@ -1033,7 +1061,9 @@ def __init__( :keyword measurements: Collection of custom measurements. :paramtype measurements: dict[str, float] """ - super(TelemetryEventData, self).__init__(additional_properties=additional_properties, version=version, **kwargs) + super(TelemetryEventData, self).__init__( + additional_properties=additional_properties, version=version, **kwargs + ) self.name = name self.properties = properties self.measurements = measurements @@ -1320,13 +1350,13 @@ class TrackResponse(msrest.serialization.Model): "errors": {"key": "errors", "type": "[TelemetryErrorDetails]"}, } - def __init__( # type: ignore + def __init__( self, *, items_received: Optional[int] = None, items_accepted: Optional[int] = None, errors: Optional[List["TelemetryErrorDetails"]] = None, - **kwargs # type: Any + **kwargs ): """ :keyword items_received: The number of items received. diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py index b9ec20119620..5d169d5db8f2 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py @@ -5,20 +5,22 @@ from typing import Optional, Sequence, Any from opentelemetry._logs.severity import SeverityNumber +from opentelemetry.sdk._logs import ReadableLogRecord +from opentelemetry.sdk._logs.export import LogRecordExporter, LogRecordExportResult from opentelemetry.semconv.attributes.exception_attributes import ( EXCEPTION_ESCAPED, EXCEPTION_MESSAGE, EXCEPTION_STACKTRACE, EXCEPTION_TYPE, ) -from opentelemetry.sdk._logs import ReadableLogRecord -from opentelemetry.sdk._logs.export import LogRecordExporter, LogRecordExportResult from azure.monitor.opentelemetry.exporter import _utils from azure.monitor.opentelemetry.exporter._constants import ( + _APPLICATION_INSIGHTS_EVENT_MARKER_ATTRIBUTE, + _DEFAULT_LOG_MESSAGE, _EXCEPTION_ENVELOPE_NAME, _MESSAGE_ENVELOPE_NAME, - _DEFAULT_LOG_MESSAGE, + _MICROSOFT_CUSTOM_EVENT_NAME, ) from azure.monitor.opentelemetry.exporter._generated.models import ( ContextTagKeys, @@ -34,10 +36,6 @@ ExportResult, ) from azure.monitor.opentelemetry.exporter.export.trace import _utils as trace_utils -from azure.monitor.opentelemetry.exporter._constants import ( - _APPLICATION_INSIGHTS_EVENT_MARKER_ATTRIBUTE, - _MICROSOFT_CUSTOM_EVENT_NAME, -) from azure.monitor.opentelemetry.exporter.statsbeat._state import ( get_statsbeat_shutdown, get_statsbeat_custom_events_feature_set, @@ -45,6 +43,30 @@ set_statsbeat_custom_events_feature_set, ) +try: + from opentelemetry.semconv.logs import ( + LogRecordAttributes as _SemconvLogRecordAttributes, + ) +except ImportError: + _SemconvLogRecordAttributes = None +try: + from opentelemetry.semconv._incubating.attributes import ( + enduser_attributes as _enduser_attributes, + ) +except ImportError: + _enduser_attributes = None + +_ENDUSER_ID_ATTRIBUTE = ( + getattr(_SemconvLogRecordAttributes, "ENDUSER_ID", None) + or getattr(_enduser_attributes, "ENDUSER_ID", None) + or "enduser.id" +) +_ENDUSER_PSEUDO_ID_ATTRIBUTE = ( + getattr(_SemconvLogRecordAttributes, "ENDUSER_PSEUDO_ID", None) + or getattr(_enduser_attributes, "ENDUSER_PSEUDO_ID", None) + or "enduser.pseudo.id" +) + _logger = logging.getLogger(__name__) _DEFAULT_SPAN_ID = 0 @@ -56,7 +78,9 @@ class AzureMonitorLogExporter(BaseExporter, LogRecordExporter): """Azure Monitor Log exporter for OpenTelemetry.""" - def export(self, batch: Sequence[ReadableLogRecord], **kwargs: Any) -> LogRecordExportResult: + def export( + self, batch: Sequence[ReadableLogRecord], **kwargs: Any + ) -> LogRecordExportResult: # pylint: disable=unused-argument """Export log data. @@ -71,7 +95,9 @@ def export(self, batch: Sequence[ReadableLogRecord], **kwargs: Any) -> LogRecord self._handle_transmit_from_storage(envelopes, result) return _get_log_export_result(result) except Exception: # pylint: disable=broad-except - _logger.exception("Exception occurred while exporting the data.") # pylint: disable=C4769 + _logger.exception( + "Exception occurred while exporting the data." + ) # pylint: disable=C4769 return _get_log_export_result(ExportResult.FAILED_NOT_RETRYABLE) def shutdown(self) -> None: @@ -89,7 +115,9 @@ def _log_to_envelope(self, readable_log_record: ReadableLogRecord) -> TelemetryI # pylint: disable=docstring-keyword-should-match-keyword-only @classmethod - def from_connection_string(cls, conn_str: str, **kwargs: Any) -> "AzureMonitorLogExporter": + def from_connection_string( + cls, conn_str: str, **kwargs: Any + ) -> "AzureMonitorLogExporter": """ Create an AzureMonitorLogExporter from a connection string. This is the recommended way of instantiation if a connection string is passed in @@ -111,7 +139,9 @@ def _log_data_is_event(readable_log_record: ReadableLogRecord) -> bool: log_record = readable_log_record.log_record is_event = None if log_record.attributes: - is_event = log_record.attributes.get(_MICROSOFT_CUSTOM_EVENT_NAME) or log_record.attributes.get( + is_event = log_record.attributes.get( + _MICROSOFT_CUSTOM_EVENT_NAME + ) or log_record.attributes.get( _APPLICATION_INSIGHTS_EVENT_MARKER_ATTRIBUTE ) # type: ignore return is_event is not None @@ -121,12 +151,25 @@ def _log_data_is_event(readable_log_record: ReadableLogRecord) -> bool: # pylint: disable=too-many-statements def _convert_log_to_envelope(readable_log_record: ReadableLogRecord) -> TelemetryItem: log_record = readable_log_record.log_record - time_stamp = log_record.timestamp if log_record.timestamp is not None else log_record.observed_timestamp + time_stamp = ( + log_record.timestamp + if log_record.timestamp is not None + else log_record.observed_timestamp + ) envelope = _utils._create_telemetry_item(time_stamp) envelope.tags.update(_utils._populate_part_a_fields(readable_log_record.resource)) # type: ignore envelope.tags[ContextTagKeys.AI_OPERATION_ID] = "{:032x}".format( # type: ignore log_record.trace_id or _DEFAULT_TRACE_ID ) + if _ENDUSER_ID_ATTRIBUTE in log_record.attributes: + envelope.tags[ContextTagKeys.AI_USER_AUTH_USER_ID] = log_record.attributes[ + _ENDUSER_ID_ATTRIBUTE + ] + if _ENDUSER_PSEUDO_ID_ATTRIBUTE in log_record.attributes: + envelope.tags[ContextTagKeys.AI_USER_ID] = log_record.attributes[ + _ENDUSER_PSEUDO_ID_ATTRIBUTE + ] + envelope.tags[ContextTagKeys.AI_OPERATION_PARENT_ID] = "{:016x}".format( # type: ignore log_record.span_id or _DEFAULT_SPAN_ID ) @@ -236,7 +279,7 @@ def _map_body_to_message(log_body: Any) -> str: try: return json.dumps(log_body)[:32768] - except: # pylint: disable=bare-except + except Exception: # pylint: disable=broad-except return str(log_body)[:32768] @@ -257,5 +300,9 @@ def _is_ignored_attribute(key: str) -> bool: def _set_statsbeat_custom_events_feature(): - if is_statsbeat_enabled() and not get_statsbeat_shutdown() and not get_statsbeat_custom_events_feature_set(): + if ( + is_statsbeat_enabled() + and not get_statsbeat_shutdown() + and not get_statsbeat_custom_events_feature_set() + ): set_statsbeat_custom_events_feature_set() 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 123dfbf0d7e3..2f2c82bb2e18 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 @@ -14,6 +14,13 @@ ) from opentelemetry.semconv.trace import DbSystemValues, SpanAttributes from opentelemetry.semconv._incubating.attributes import gen_ai_attributes + +try: + from opentelemetry.semconv._incubating.attributes import ( + enduser_attributes as _enduser_attributes, + ) +except ImportError: + _enduser_attributes = None from opentelemetry.sdk.resources import Resource from opentelemetry.sdk.trace import ReadableSpan from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult @@ -56,6 +63,13 @@ __all__ = ["AzureMonitorTraceExporter"] +_ENDUSER_ID_ATTRIBUTE = getattr(SpanAttributes, "ENDUSER_ID", None) or getattr( + _enduser_attributes, "ENDUSER_ID", "enduser.id" +) +_ENDUSER_PSEUDO_ID_ATTRIBUTE = getattr( + SpanAttributes, "ENDUSER_PSEUDO_ID", None +) or getattr(_enduser_attributes, "ENDUSER_PSEUDO_ID", "enduser.pseudo.id") + _STANDARD_OPENTELEMETRY_ATTRIBUTE_PREFIXES = [ "http.", "db.", @@ -108,7 +122,9 @@ def __init__(self, **kwargs: Any): super().__init__(**kwargs) def export( - self, spans: Sequence[ReadableSpan], **kwargs: Any # pylint: disable=unused-argument + self, + spans: Sequence[ReadableSpan], + **kwargs: Any # pylint: disable=unused-argument ) -> SpanExportResult: """Export span data. @@ -125,7 +141,9 @@ def export( resource = tracer_provider.resource # type: ignore envelopes.append(self._get_otel_resource_envelope(resource)) except AttributeError as e: - _logger.exception("Failed to derive Resource from Tracer Provider: %s", e) # pylint: disable=C4769 + _logger.exception( + "Failed to derive Resource from Tracer Provider: %s", e + ) # pylint: disable=C4769 for span in spans: envelopes.append(self._span_to_envelope(span)) envelopes.extend(self._span_events_to_envelopes(span)) @@ -134,7 +152,9 @@ def export( self._handle_transmit_from_storage(envelopes, result) return _get_trace_export_result(result) except Exception: # pylint: disable=broad-except - _logger.exception("Exception occurred while exporting the data.") # pylint: disable=C4769 + _logger.exception( + "Exception occurred while exporting the data." + ) # pylint: disable=C4769 return _get_trace_export_result(ExportResult.FAILED_NOT_RETRYABLE) def shutdown(self) -> None: @@ -152,7 +172,9 @@ def _get_otel_resource_envelope(self, resource: Resource) -> TelemetryItem: attributes = resource.attributes envelope = _utils._create_telemetry_item(time_ns()) envelope.name = _METRIC_ENVELOPE_NAME - envelope.tags.update(_utils._populate_part_a_fields(resource)) # pylint: disable=W0212 + envelope.tags.update( + _utils._populate_part_a_fields(resource) + ) # pylint: disable=W0212 envelope.instrumentation_key = self._instrumentation_key data_point = MetricDataPoint( name="_OTELRESOURCE_"[:1024], @@ -182,12 +204,16 @@ def _span_events_to_envelopes(self, span: ReadableSpan) -> Sequence[TelemetryIte return envelopes def _should_collect_otel_resource_metric(self): - disabled = environ.get(_APPLICATIONINSIGHTS_OPENTELEMETRY_RESOURCE_METRIC_DISABLED) + disabled = environ.get( + _APPLICATIONINSIGHTS_OPENTELEMETRY_RESOURCE_METRIC_DISABLED + ) return disabled is None or disabled.lower() != "true" # pylint: disable=docstring-keyword-should-match-keyword-only @classmethod - def from_connection_string(cls, conn_str: str, **kwargs: Any) -> "AzureMonitorTraceExporter": + def from_connection_string( + cls, conn_str: str, **kwargs: Any + ) -> "AzureMonitorTraceExporter": """ Create an AzureMonitorTraceExporter from a connection string. This is the recommended way of instantiation if a connection string is passed in @@ -221,13 +247,23 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: duration = span.end_time - span.start_time envelope = _utils._create_telemetry_item(start_time) envelope.tags.update(_utils._populate_part_a_fields(span.resource)) - envelope.tags[ContextTagKeys.AI_OPERATION_ID] = "{:032x}".format(span.context.trace_id) - if SpanAttributes.ENDUSER_ID in span.attributes: - envelope.tags[ContextTagKeys.AI_USER_ID] = span.attributes[SpanAttributes.ENDUSER_ID] + envelope.tags[ContextTagKeys.AI_OPERATION_ID] = "{:032x}".format( + span.context.trace_id + ) + if _ENDUSER_ID_ATTRIBUTE in span.attributes: + envelope.tags[ContextTagKeys.AI_USER_AUTH_USER_ID] = span.attributes[ + _ENDUSER_ID_ATTRIBUTE + ] + if _ENDUSER_PSEUDO_ID_ATTRIBUTE in span.attributes: + envelope.tags[ContextTagKeys.AI_USER_ID] = span.attributes[ + _ENDUSER_PSEUDO_ID_ATTRIBUTE + ] if _utils._is_any_synthetic_source(span.attributes): envelope.tags[ContextTagKeys.AI_OPERATION_SYNTHETIC_SOURCE] = "True" if span.parent and span.parent.span_id: - envelope.tags[ContextTagKeys.AI_OPERATION_PARENT_ID] = "{:016x}".format(span.parent.span_id) + envelope.tags[ContextTagKeys.AI_OPERATION_PARENT_ID] = "{:016x}".format( + span.parent.span_id + ) if span.kind in (SpanKind.CONSUMER, SpanKind.SERVER): envelope.name = _REQUEST_ENVELOPE_NAME data = RequestData( @@ -256,7 +292,10 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: difference = (start_time / 1000000) - enqueued_time total += difference data.measurements["timeSinceEnqueued"] = max(0, total / len(span.links)) - elif HTTP_REQUEST_METHOD in span.attributes or SpanAttributes.HTTP_METHOD in span.attributes: # HTTP + elif ( + HTTP_REQUEST_METHOD in span.attributes + or SpanAttributes.HTTP_METHOD in span.attributes + ): # HTTP path = "" user_agent = trace_utils._get_user_agent(span.attributes) if user_agent: @@ -268,7 +307,8 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: # Http specific logic for ai.operation.name if SpanAttributes.HTTP_ROUTE in span.attributes: envelope.tags[ContextTagKeys.AI_OPERATION_NAME] = "{} {}".format( - span.attributes.get(HTTP_REQUEST_METHOD) or span.attributes.get(SpanAttributes.HTTP_METHOD), + span.attributes.get(HTTP_REQUEST_METHOD) + or span.attributes.get(SpanAttributes.HTTP_METHOD), span.attributes[SpanAttributes.HTTP_ROUTE], ) elif url: @@ -278,14 +318,15 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: if not path: path = "/" envelope.tags[ContextTagKeys.AI_OPERATION_NAME] = "{} {}".format( - span.attributes.get(HTTP_REQUEST_METHOD) or span.attributes.get(SpanAttributes.HTTP_METHOD), + span.attributes.get(HTTP_REQUEST_METHOD) + or span.attributes.get(SpanAttributes.HTTP_METHOD), path, ) except Exception: # pylint: disable=broad-except pass - status_code = span.attributes.get(HTTP_RESPONSE_STATUS_CODE) or span.attributes.get( - SpanAttributes.HTTP_STATUS_CODE - ) + status_code = span.attributes.get( + HTTP_RESPONSE_STATUS_CODE + ) or span.attributes.get(SpanAttributes.HTTP_STATUS_CODE) if status_code: try: status_code = int(status_code) # type: ignore @@ -295,12 +336,17 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: status_code = 0 data.response_code = str(status_code) # Success criteria for server spans depends on span.success and the actual status code - data.success = span.status.is_ok and status_code and status_code not in range(400, 500) + data.success = ( + span.status.is_ok and status_code and status_code not in range(400, 500) + ) elif SpanAttributes.MESSAGING_SYSTEM in span.attributes: # Messaging if span.attributes.get(SpanAttributes.MESSAGING_DESTINATION): - if span.attributes.get(CLIENT_ADDRESS) or span.attributes.get(SpanAttributes.NET_PEER_NAME): + if span.attributes.get(CLIENT_ADDRESS) or span.attributes.get( + SpanAttributes.NET_PEER_NAME + ): data.source = "{}/{}".format( - span.attributes.get(CLIENT_ADDRESS) or span.attributes.get(SpanAttributes.NET_PEER_NAME), + span.attributes.get(CLIENT_ADDRESS) + or span.attributes.get(SpanAttributes.NET_PEER_NAME), span.attributes.get(SpanAttributes.MESSAGING_DESTINATION), ) elif span.attributes.get(SpanAttributes.NET_PEER_IP): @@ -309,7 +355,9 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: span.attributes.get(SpanAttributes.MESSAGING_DESTINATION), ) else: - data.source = span.attributes.get(SpanAttributes.MESSAGING_DESTINATION, "") + data.source = span.attributes.get( + SpanAttributes.MESSAGING_DESTINATION, "" + ) # Apply truncation # See https://github.com/MohanGsk/ApplicationInsights-Home/tree/master/EndpointSpecs/Schemas/Bond if envelope.tags.get(ContextTagKeys.AI_OPERATION_NAME): @@ -345,7 +393,10 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: # https://github.com/Azure/azure-sdk-for-python/issues/9256 data.type = span.attributes[_AZURE_SDK_NAMESPACE_NAME] data.target = trace_utils._get_azure_sdk_target_source(span.attributes) - elif HTTP_REQUEST_METHOD in span.attributes or SpanAttributes.HTTP_METHOD in span.attributes: # HTTP + elif ( + HTTP_REQUEST_METHOD in span.attributes + or SpanAttributes.HTTP_METHOD in span.attributes + ): # HTTP data.type = "HTTP" user_agent = trace_utils._get_user_agent(span.attributes) if user_agent: @@ -355,7 +406,8 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: # Http specific logic for ai.operation.name if SpanAttributes.HTTP_ROUTE in span.attributes: envelope.tags[ContextTagKeys.AI_OPERATION_NAME] = "{} {}".format( - span.attributes.get(HTTP_REQUEST_METHOD) or span.attributes.get(SpanAttributes.HTTP_METHOD), + span.attributes.get(HTTP_REQUEST_METHOD) + or span.attributes.get(SpanAttributes.HTTP_METHOD), span.attributes[SpanAttributes.HTTP_ROUTE], ) # data @@ -368,16 +420,18 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: # http specific logic for name if path: data.name = "{} {}".format( - span.attributes.get(HTTP_REQUEST_METHOD) or span.attributes.get(SpanAttributes.HTTP_METHOD), + span.attributes.get(HTTP_REQUEST_METHOD) + or span.attributes.get(SpanAttributes.HTTP_METHOD), path, ) envelope.tags[ContextTagKeys.AI_OPERATION_NAME] = "{} {}".format( - span.attributes.get(HTTP_REQUEST_METHOD) or span.attributes.get(SpanAttributes.HTTP_METHOD), + span.attributes.get(HTTP_REQUEST_METHOD) + or span.attributes.get(SpanAttributes.HTTP_METHOD), path, ) - status_code = span.attributes.get(HTTP_RESPONSE_STATUS_CODE) or span.attributes.get( - SpanAttributes.HTTP_STATUS_CODE - ) + status_code = span.attributes.get( + HTTP_RESPONSE_STATUS_CODE + ) or span.attributes.get(SpanAttributes.HTTP_STATUS_CODE) if status_code: try: status_code = int(status_code) # type: ignore @@ -439,7 +493,9 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: 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: - data.type = "Queue Message | {}".format(span.attributes[_AZURE_SDK_NAMESPACE_NAME]) + data.type = "Queue Message | {}".format( + span.attributes[_AZURE_SDK_NAMESPACE_NAME] + ) target = trace_utils._get_azure_sdk_target_source(span.attributes) else: data.type = "Queue Message" @@ -453,7 +509,9 @@ 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_PREFIX.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 @@ -507,9 +565,13 @@ def _convert_span_events_to_envelopes(span: ReadableSpan) -> Sequence[TelemetryI for event in span.events: envelope = _utils._create_telemetry_item(event.timestamp) envelope.tags.update(_utils._populate_part_a_fields(span.resource)) - envelope.tags[ContextTagKeys.AI_OPERATION_ID] = "{:032x}".format(span.context.trace_id) + envelope.tags[ContextTagKeys.AI_OPERATION_ID] = "{:032x}".format( + span.context.trace_id + ) if span.context and span.context.span_id: - envelope.tags[ContextTagKeys.AI_OPERATION_PARENT_ID] = "{:016x}".format(span.context.span_id) + envelope.tags[ContextTagKeys.AI_OPERATION_PARENT_ID] = "{:016x}".format( + span.context.span_id + ) # sampleRate if span.attributes and _SAMPLE_RATE_KEY in span.attributes: @@ -562,7 +624,11 @@ def _check_instrumentation_span(span: ReadableSpan) -> None: # `azure-` or `azure.` is a prefix if span.instrumentation_scope.name.startswith("azure"): # spec-case for Azure AI SDKs - identified by `az.namespace` attribute - if span.attributes and span.attributes.get(_AZURE_SDK_NAMESPACE_NAME) == "Microsoft.CognitiveServices": + if ( + span.attributes + and span.attributes.get(_AZURE_SDK_NAMESPACE_NAME) + == "Microsoft.CognitiveServices" + ): _utils.add_instrumentation(_AZURE_AI_SDK_NAME) else: _utils.add_instrumentation(_AZURE_SDK_OPENTELEMETRY_NAME) @@ -571,7 +637,9 @@ def _check_instrumentation_span(span: ReadableSpan) -> None: # `opentelemetry.instrumentation.` as a prefix if span.instrumentation_scope.name.startswith("opentelemetry.instrumentation."): # The string after the prefix is the name of the instrumentation - name = span.instrumentation_scope.name.split("opentelemetry.instrumentation.", 1)[1] + name = span.instrumentation_scope.name.split( + "opentelemetry.instrumentation.", 1 + )[1] # Update the bit map to indicate instrumentation is being used _utils.add_instrumentation(name) @@ -580,7 +648,10 @@ def _is_standard_attribute(key: str) -> bool: for prefix in _STANDARD_OPENTELEMETRY_ATTRIBUTE_PREFIXES: if key.startswith(prefix): return True - return key in _STANDARD_AZURE_MONITOR_ATTRIBUTES or key in _STANDARD_OPENTELEMETRY_HTTP_ATTRIBUTES + return ( + key in _STANDARD_AZURE_MONITOR_ATTRIBUTES + or key in _STANDARD_OPENTELEMETRY_HTTP_ATTRIBUTES + ) def _get_trace_export_result(result: ExportResult) -> SpanExportResult: diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/logs/test_logs.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/logs/test_logs.py index dc89cf40aeca..b282b5141aaa 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/logs/test_logs.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/logs/test_logs.py @@ -62,7 +62,9 @@ class TestAzureLogExporter(unittest.TestCase): def setUpClass(cls): os.environ.pop("APPLICATIONINSIGHTS_STATSBEAT_DISABLED_ALL", None) os.environ.pop("APPINSIGHTS_INSTRUMENTATIONKEY", None) - os.environ["APPINSIGHTS_INSTRUMENTATIONKEY"] = "1234abcd-5678-4efa-8abc-1234567890ab" + os.environ["APPINSIGHTS_INSTRUMENTATIONKEY"] = ( + "1234abcd-5678-4efa-8abc-1234567890ab" + ) os.environ["APPLICATIONINSIGHTS_STATSBEAT_DISABLED_ALL"] = "true" cls._exporter = cls._exporter_class() span_context = SpanContext( @@ -80,7 +82,26 @@ def setUpClass(cls): severity_text="WARNING", severity_number=SeverityNumber.WARN, body="Test message", - attributes={"test": "attribute", "ai.operation.name": "TestOperationName"}, + attributes={ + "test": "attribute", + "ai.operation.name": "TestOperationName", + }, + ), + resource=Resource.create(attributes={"asd": "test_resource"}), + instrumentation_scope=InstrumentationScope("test_name"), + ) + cls._log_data_user_fields = _logs.ReadWriteLogRecord( + LogRecord( + timestamp=1646865018558419456, + context=ctx, + severity_text="WARNING", + severity_number=SeverityNumber.WARN, + body="Test message", + attributes={ + "test": "attribute", + "enduser.id": "test-auth", + "enduser.pseudo.id": "test-user", + }, ), resource=Resource.create(attributes={"asd": "test_resource"}), instrumentation_scope=InstrumentationScope("test_name"), @@ -92,7 +113,10 @@ def setUpClass(cls): severity_text="WARNING", severity_number=SeverityNumber.WARN, body="", - attributes={"test": "attribute", "ai.operation.name": "TestOperationName"}, + attributes={ + "test": "attribute", + "ai.operation.name": "TestOperationName", + }, ), resource=Resource.create(attributes={"asd": "test_resource"}), instrumentation_scope=InstrumentationScope("test_name"), @@ -116,7 +140,10 @@ def setUpClass(cls): severity_text="WARNING", severity_number=SeverityNumber.WARN, body={"foo": {"bar": "baz", "qux": 42}}, - attributes={"test": "attribute", "ai.operation.name": "TestOperationName"}, + attributes={ + "test": "attribute", + "ai.operation.name": "TestOperationName", + }, ), resource=Resource.create(attributes={"asd": "test_resource"}), instrumentation_scope=InstrumentationScope("test_name"), @@ -246,7 +273,12 @@ def setUpClass(cls): severity_text="EXCEPTION", severity_number=SeverityNumber.FATAL, body="test exception", - attributes={"test": "attribute", EXCEPTION_TYPE: "", EXCEPTION_MESSAGE: "", EXCEPTION_STACKTRACE: ""}, + attributes={ + "test": "attribute", + EXCEPTION_TYPE: "", + EXCEPTION_MESSAGE: "", + EXCEPTION_STACKTRACE: "", + }, ), resource=Resource.create(attributes={"asd": "test_resource"}), instrumentation_scope=InstrumentationScope("test_name"), @@ -258,7 +290,12 @@ def setUpClass(cls): severity_text="EXCEPTION", severity_number=SeverityNumber.FATAL, body="", - attributes={"test": "attribute", EXCEPTION_TYPE: "", EXCEPTION_MESSAGE: "", EXCEPTION_STACKTRACE: ""}, + attributes={ + "test": "attribute", + EXCEPTION_TYPE: "", + EXCEPTION_MESSAGE: "", + EXCEPTION_STACKTRACE: "", + }, ), resource=Resource.create(attributes={"asd": "test_resource"}), instrumentation_scope=InstrumentationScope("test_name"), @@ -351,31 +388,53 @@ def test_log_to_envelope_partA(self): self._log_data.resource = resource envelope = exporter._log_to_envelope(self._log_data) - self.assertEqual(envelope.instrumentation_key, "1234abcd-5678-4efa-8abc-1234567890ab") + self.assertEqual( + envelope.instrumentation_key, "1234abcd-5678-4efa-8abc-1234567890ab" + ) self.assertIsNotNone(envelope.tags) self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_DEVICE_ID), azure_monitor_context[ContextTagKeys.AI_DEVICE_ID] + envelope.tags.get(ContextTagKeys.AI_DEVICE_ID), + azure_monitor_context[ContextTagKeys.AI_DEVICE_ID], ) self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_DEVICE_LOCALE), azure_monitor_context[ContextTagKeys.AI_DEVICE_LOCALE] + envelope.tags.get(ContextTagKeys.AI_DEVICE_LOCALE), + azure_monitor_context[ContextTagKeys.AI_DEVICE_LOCALE], ) self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_DEVICE_TYPE), azure_monitor_context[ContextTagKeys.AI_DEVICE_TYPE] + envelope.tags.get(ContextTagKeys.AI_DEVICE_TYPE), + azure_monitor_context[ContextTagKeys.AI_DEVICE_TYPE], ) self.assertEqual( envelope.tags.get(ContextTagKeys.AI_INTERNAL_SDK_VERSION), azure_monitor_context[ContextTagKeys.AI_INTERNAL_SDK_VERSION], ) - self.assertEqual(envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE), "testServiceNamespace.testServiceName") - self.assertEqual(envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE_INSTANCE), "testServiceInstanceId") - self.assertEqual(envelope.tags.get(ContextTagKeys.AI_INTERNAL_NODE_NAME), "testServiceInstanceId") + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE), + "testServiceNamespace.testServiceName", + ) + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE_INSTANCE), + "testServiceInstanceId", + ) + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_INTERNAL_NODE_NAME), + "testServiceInstanceId", + ) trace_id = self._log_data.log_record.trace_id - self.assertEqual(envelope.tags.get(ContextTagKeys.AI_OPERATION_ID), "{:032x}".format(trace_id)) + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_OPERATION_ID), + "{:032x}".format(trace_id), + ) span_id = self._log_data.log_record.span_id - self.assertEqual(envelope.tags.get(ContextTagKeys.AI_OPERATION_PARENT_ID), "{:016x}".format(span_id)) + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_OPERATION_PARENT_ID), + "{:016x}".format(span_id), + ) self._log_data.resource = old_resource - self.assertEqual(envelope.tags.get(ContextTagKeys.AI_OPERATION_NAME), "TestOperationName") + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_OPERATION_NAME), "TestOperationName" + ) def test_log_to_envelope_partA_default(self): exporter = self._exporter @@ -383,8 +442,12 @@ def test_log_to_envelope_partA_default(self): resource = Resource({"service.name": "testServiceName"}) self._log_data.resource = resource envelope = exporter._log_to_envelope(self._log_data) - self.assertEqual(envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE), "testServiceName") - self.assertEqual(envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE_INSTANCE), platform.node()) + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE), "testServiceName" + ) + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE_INSTANCE), platform.node() + ) self.assertEqual( envelope.tags.get(ContextTagKeys.AI_INTERNAL_NODE_NAME), envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE_INSTANCE), @@ -402,6 +465,15 @@ def test_log_to_envelope_log(self): self.assertEqual(envelope.data.base_data.severity_level, 2) self.assertEqual(envelope.data.base_data.properties["test"], "attribute") + def test_log_to_envelope_user_fields(self): + exporter = self._exporter + envelope = exporter._log_to_envelope(self._log_data_user_fields) + + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_USER_AUTH_USER_ID), "test-auth" + ) + self.assertEqual(envelope.tags.get(ContextTagKeys.AI_USER_ID), "test-user") + def test_log_to_envelope_log_none(self): exporter = self._exporter envelope = exporter._log_to_envelope(self._log_data_none) @@ -415,7 +487,9 @@ def test_log_to_envelope_log_empty(self): self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.Message") self.assertEqual(envelope.data.base_type, "MessageData") self.assertEqual(envelope.data.base_data.message, _DEFAULT_LOG_MESSAGE) - self.assertEqual(envelope.tags.get(ContextTagKeys.AI_OPERATION_NAME), "TestOperationName") + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_OPERATION_NAME), "TestOperationName" + ) def test_log_to_envelope_log_empty_with_whitespaces(self): exporter = self._exporter @@ -429,16 +503,24 @@ def test_log_to_envelope_log_complex_body(self): envelope = exporter._log_to_envelope(self._log_data_complex_body) self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.Message") self.assertEqual(envelope.data.base_type, "MessageData") - self.assertEqual(envelope.data.base_data.message, json.dumps(self._log_data_complex_body.log_record.body)) - self.assertEqual(envelope.tags.get(ContextTagKeys.AI_OPERATION_NAME), "TestOperationName") + self.assertEqual( + envelope.data.base_data.message, + json.dumps(self._log_data_complex_body.log_record.body), + ) + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_OPERATION_NAME), "TestOperationName" + ) def test_log_to_envelope_log_complex_body_not_serializeable(self): exporter = self._exporter - envelope = exporter._log_to_envelope(self._log_data_complex_body_not_serializeable) + envelope = exporter._log_to_envelope( + self._log_data_complex_body_not_serializeable + ) self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.Message") self.assertEqual(envelope.data.base_type, "MessageData") self.assertEqual( - envelope.data.base_data.message, str(self._log_data_complex_body_not_serializeable.log_record.body) + envelope.data.base_data.message, + str(self._log_data_complex_body_not_serializeable.log_record.body), ) def test_log_to_envelope_exception_with_string_message(self): @@ -451,7 +533,9 @@ def test_log_to_envelope_exception_with_string_message(self): self.assertEqual(envelope.data.base_data.severity_level, 4) self.assertEqual(envelope.data.base_data.properties["test"], "attribute") self.assertEqual(len(envelope.data.base_data.exceptions), 1) - self.assertEqual(envelope.data.base_data.exceptions[0].type_name, "ZeroDivisionError") + self.assertEqual( + envelope.data.base_data.exceptions[0].type_name, "ZeroDivisionError" + ) self.assertEqual(envelope.data.base_data.exceptions[0].message, "Test message") self.assertTrue(envelope.data.base_data.exceptions[0].has_full_stack) self.assertEqual( @@ -469,8 +553,12 @@ def test_log_to_envelope_exception_with_exc_message(self): self.assertEqual(envelope.data.base_data.severity_level, 4) self.assertEqual(envelope.data.base_data.properties["test"], "attribute") self.assertEqual(len(envelope.data.base_data.exceptions), 1) - self.assertEqual(envelope.data.base_data.exceptions[0].type_name, "ZeroDivisionError") - self.assertEqual(envelope.data.base_data.exceptions[0].message, "test exception message") + self.assertEqual( + envelope.data.base_data.exceptions[0].type_name, "ZeroDivisionError" + ) + self.assertEqual( + envelope.data.base_data.exceptions[0].message, "test exception message" + ) self.assertTrue(envelope.data.base_data.exceptions[0].has_full_stack) self.assertEqual( envelope.data.base_data.exceptions[0].stack, @@ -503,7 +591,9 @@ def test_log_to_envelope_exception_with_blank_exception(self): self.assertEqual(envelope.data.base_data.properties["test"], "attribute") self.assertEqual(len(envelope.data.base_data.exceptions), 1) self.assertEqual(envelope.data.base_data.exceptions[0].type_name, "Exception") - self.assertEqual(envelope.data.base_data.exceptions[0].message, "test exception") + self.assertEqual( + envelope.data.base_data.exceptions[0].message, "test exception" + ) self.assertTrue(envelope.data.base_data.exceptions[0].has_full_stack) self.assertEqual(envelope.data.base_data.exceptions[0].stack, "") @@ -515,7 +605,9 @@ def test_log_to_envelope_event(self): self.assertEqual(envelope.time, ns_to_iso_str(record.timestamp)) self.assertEqual(envelope.data.base_type, "EventData") self.assertEqual(envelope.data.base_data.name, record.body) - self.assertEqual(envelope.data.base_data.properties["event_key"], "event_attribute") + self.assertEqual( + envelope.data.base_data.properties["event_key"], "event_attribute" + ) def test_log_to_envelope_event_complex_body(self): exporter = self._exporter @@ -525,17 +617,23 @@ def test_log_to_envelope_event_complex_body(self): self.assertEqual(envelope.time, ns_to_iso_str(record.timestamp)) self.assertEqual(envelope.data.base_type, "EventData") self.assertEqual(envelope.data.base_data.name, json.dumps(record.body)) - self.assertEqual(envelope.data.base_data.properties["event_key"], "event_attribute") + self.assertEqual( + envelope.data.base_data.properties["event_key"], "event_attribute" + ) def test_log_to_envelope_event_complex_body_not_serializeable(self): exporter = self._exporter - envelope = exporter._log_to_envelope(self._log_data_event_complex_body_not_serializeable) + envelope = exporter._log_to_envelope( + self._log_data_event_complex_body_not_serializeable + ) record = self._log_data_event_complex_body_not_serializeable.log_record self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.Event") self.assertEqual(envelope.time, ns_to_iso_str(record.timestamp)) self.assertEqual(envelope.data.base_type, "EventData") self.assertEqual(envelope.data.base_data.name, str(record.body)) - self.assertEqual(envelope.data.base_data.properties["event_key"], "event_attribute") + self.assertEqual( + envelope.data.base_data.properties["event_key"], "event_attribute" + ) def test_log_to_envelope_custom_event(self): exporter = self._exporter @@ -546,7 +644,9 @@ def test_log_to_envelope_custom_event(self): self.assertEqual(envelope.time, ns_to_iso_str(record.timestamp)) self.assertEqual(envelope.data.base_type, "EventData") self.assertEqual(envelope.data.base_data.name, "event_name") - self.assertEqual(envelope.data.base_data.properties["event_key"], "event_attribute") + self.assertEqual( + envelope.data.base_data.properties["event_key"], "event_attribute" + ) def test_log_to_envelope_timestamp(self): exporter = self._exporter @@ -592,9 +692,17 @@ def test_log_to_envelope_synthetic_source(self): ) envelope = exporter._log_to_envelope(log_data) - self.assertEqual(envelope.tags.get(ContextTagKeys.AI_OPERATION_SYNTHETIC_SOURCE), "True") - self.assertEqual(envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE), "testServiceNamespace.testServiceName") - self.assertEqual(envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE_INSTANCE), "testServiceInstanceId") + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_OPERATION_SYNTHETIC_SOURCE), "True" + ) + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE), + "testServiceNamespace.testServiceName", + ) + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE_INSTANCE), + "testServiceInstanceId", + ) def test_log_to_envelope_synthetic_load_always_on(self): exporter = self._exporter @@ -630,9 +738,17 @@ def test_log_to_envelope_synthetic_load_always_on(self): ) envelope = exporter._log_to_envelope(log_data) - self.assertEqual(envelope.tags.get(ContextTagKeys.AI_OPERATION_SYNTHETIC_SOURCE), "True") - self.assertEqual(envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE), "testServiceNamespace.testServiceName") - self.assertEqual(envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE_INSTANCE), "testServiceInstanceId") + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_OPERATION_SYNTHETIC_SOURCE), "True" + ) + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE), + "testServiceNamespace.testServiceName", + ) + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE_INSTANCE), + "testServiceInstanceId", + ) class TestAzureLogExporterWithDisabledStorage(TestAzureLogExporter): 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 6f1d41e0565c..af1ef20a9353 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 @@ -9,13 +9,10 @@ from unittest import mock # pylint: disable=import-error -from opentelemetry.trace import get_tracer_provider, set_tracer_provider +from opentelemetry.trace import set_tracer_provider from opentelemetry.sdk import trace, resources -from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import SpanExportResult -from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter -from opentelemetry.sdk.trace.export import SimpleSpanProcessor from opentelemetry.sdk.util.instrumentation import InstrumentationScope from opentelemetry.semconv.attributes.exception_attributes import ( EXCEPTION_ESCAPED, @@ -64,10 +61,16 @@ class TestAzureTraceExporter(unittest.TestCase): def setUpClass(cls): os.environ.pop("APPLICATIONINSIGHTS_STATSBEAT_DISABLED_ALL", None) os.environ.pop("APPINSIGHTS_INSTRUMENTATIONKEY", None) - os.environ.pop("APPLICATIONINSIGHTS_OPENTELEMETRY_RESOURCE_METRIC_DISABLED", None) - os.environ["APPINSIGHTS_INSTRUMENTATIONKEY"] = "1234abcd-5678-4efa-8abc-1234567890ab" + os.environ.pop( + "APPLICATIONINSIGHTS_OPENTELEMETRY_RESOURCE_METRIC_DISABLED", None + ) + os.environ["APPINSIGHTS_INSTRUMENTATIONKEY"] = ( + "1234abcd-5678-4efa-8abc-1234567890ab" + ) os.environ["APPLICATIONINSIGHTS_STATSBEAT_DISABLED_ALL"] = "true" - os.environ["APPLICATIONINSIGHTS_OPENTELEMETRY_RESOURCE_METRIC_DISABLED"] = "true" + os.environ["APPLICATIONINSIGHTS_OPENTELEMETRY_RESOURCE_METRIC_DISABLED"] = ( + "true" + ) cls._exporter = AzureMonitorTraceExporter( tracer_provider=trace.TracerProvider(), ) @@ -78,7 +81,6 @@ def tearDownClass(cls): def test_constructor(self): """Test the constructor.""" - tp = trace.TracerProvider() exporter = AzureMonitorTraceExporter( connection_string="InstrumentationKey=4321abcd-5678-4efa-8abc-1234567890ab", ) @@ -288,7 +290,8 @@ def test_span_to_envelope_partA(self): context=context, resource=resource, attributes={ - "enduser.id": "testId", + "enduser.id": "testAuthId", + "enduser.pseudo.id": "testUserId", "user_agent.synthetic.type": "bot", }, parent=context, @@ -297,29 +300,54 @@ def test_span_to_envelope_partA(self): test_span.end() envelope = exporter._span_to_envelope(test_span) - self.assertEqual(envelope.instrumentation_key, "1234abcd-5678-4efa-8abc-1234567890ab") + self.assertEqual( + envelope.instrumentation_key, "1234abcd-5678-4efa-8abc-1234567890ab" + ) self.assertIsNotNone(envelope.tags) self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_DEVICE_ID), azure_monitor_context[ContextTagKeys.AI_DEVICE_ID] + envelope.tags.get(ContextTagKeys.AI_DEVICE_ID), + azure_monitor_context[ContextTagKeys.AI_DEVICE_ID], ) self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_DEVICE_LOCALE), azure_monitor_context[ContextTagKeys.AI_DEVICE_LOCALE] + envelope.tags.get(ContextTagKeys.AI_DEVICE_LOCALE), + azure_monitor_context[ContextTagKeys.AI_DEVICE_LOCALE], ) self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_DEVICE_TYPE), azure_monitor_context[ContextTagKeys.AI_DEVICE_TYPE] + envelope.tags.get(ContextTagKeys.AI_DEVICE_TYPE), + azure_monitor_context[ContextTagKeys.AI_DEVICE_TYPE], ) self.assertEqual( envelope.tags.get(ContextTagKeys.AI_INTERNAL_SDK_VERSION), azure_monitor_context[ContextTagKeys.AI_INTERNAL_SDK_VERSION], ) - self.assertEqual(envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE), "testServiceNamespace.testServiceName") - self.assertEqual(envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE_INSTANCE), "testServiceInstanceId") - self.assertEqual(envelope.tags.get(ContextTagKeys.AI_INTERNAL_NODE_NAME), "testServiceInstanceId") - self.assertEqual(envelope.tags.get(ContextTagKeys.AI_OPERATION_ID), "{:032x}".format(context.trace_id)) - self.assertEqual(envelope.tags.get(ContextTagKeys.AI_USER_ID), "testId") - self.assertEqual(envelope.tags.get(ContextTagKeys.AI_OPERATION_SYNTHETIC_SOURCE), "True") - self.assertEqual(envelope.tags.get(ContextTagKeys.AI_OPERATION_PARENT_ID), "{:016x}".format(context.span_id)) + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE), + "testServiceNamespace.testServiceName", + ) + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE_INSTANCE), + "testServiceInstanceId", + ) + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_INTERNAL_NODE_NAME), + "testServiceInstanceId", + ) + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_OPERATION_ID), + "{:032x}".format(context.trace_id), + ) + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_USER_AUTH_USER_ID), "testAuthId" + ) + self.assertEqual(envelope.tags.get(ContextTagKeys.AI_USER_ID), "testUserId") + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_OPERATION_SYNTHETIC_SOURCE), "True" + ) + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_OPERATION_PARENT_ID), + "{:016x}".format(context.span_id), + ) def test_span_to_envelope_partA_default(self): exporter = self._exporter @@ -337,8 +365,12 @@ def test_span_to_envelope_partA_default(self): test_span.start() test_span.end() envelope = exporter._span_to_envelope(test_span) - self.assertEqual(envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE), "testServiceName") - self.assertEqual(envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE_INSTANCE), platform.node()) + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE), "testServiceName" + ) + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE_INSTANCE), platform.node() + ) self.assertEqual( envelope.tags.get(ContextTagKeys.AI_INTERNAL_NODE_NAME), envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE_INSTANCE), @@ -371,16 +403,22 @@ def test_span_to_envelope_client_http(self): span._status = Status(status_code=StatusCode.OK) envelope = exporter._span_to_envelope(span) - self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.RemoteDependency") + self.assertEqual( + envelope.name, "Microsoft.ApplicationInsights.RemoteDependency" + ) self.assertEqual(envelope.time, "2019-12-04T21:18:36.027613Z") self.assertEqual(envelope.data.base_data.name, "GET /wiki/Rabbit") self.assertEqual(envelope.data.base_data.id, "a6f5d48acb4d31d9") self.assertEqual(envelope.data.base_data.duration, "0.00:00:01.001") self.assertTrue(envelope.data.base_data.success) - self.assertEqual(envelope.data.base_data.data, "https://www.wikipedia.org/wiki/Rabbit") + self.assertEqual( + envelope.data.base_data.data, "https://www.wikipedia.org/wiki/Rabbit" + ) self.assertEqual(envelope.data.base_type, "RemoteDependencyData") - self.assertEqual(envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "GET /wiki/Rabbit") + self.assertEqual( + envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "GET /wiki/Rabbit" + ) self.assertEqual(envelope.data.base_data.type, "HTTP") self.assertEqual(envelope.data.base_data.target, "service") self.assertEqual( @@ -452,7 +490,10 @@ def test_span_to_envelope_client_http(self): envelope = exporter._span_to_envelope(span) self.assertEqual(envelope.data.base_data.target, "www.example.com") - span._attributes = {"http.request.method": "GET", "gen_ai.system": "az.ai.inference"} + 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, "GET /") @@ -476,7 +517,10 @@ def test_span_to_envelope_client_http(self): "http.target": "/path/12314/?q=ddds#123", } envelope = exporter._span_to_envelope(span) - self.assertEqual(envelope.data.base_data.data, "https://www.wikipedia.org/path/12314/?q=ddds#123") + self.assertEqual( + envelope.data.base_data.data, + "https://www.wikipedia.org/path/12314/?q=ddds#123", + ) span._attributes = { "http.method": "GET", @@ -486,7 +530,10 @@ def test_span_to_envelope_client_http(self): "http.target": "/path/12314/?q=ddds#123", } envelope = exporter._span_to_envelope(span) - self.assertEqual(envelope.data.base_data.data, "https://example.com:8080/path/12314/?q=ddds#123") + self.assertEqual( + envelope.data.base_data.data, + "https://example.com:8080/path/12314/?q=ddds#123", + ) span._attributes = { "http.method": "GET", @@ -496,7 +543,10 @@ def test_span_to_envelope_client_http(self): "http.target": "/path/12314/?q=ddds#123", } envelope = exporter._span_to_envelope(span) - self.assertEqual(envelope.data.base_data.data, "https://192.168.0.1:8080/path/12314/?q=ddds#123") + self.assertEqual( + envelope.data.base_data.data, + "https://192.168.0.1:8080/path/12314/?q=ddds#123", + ) # Stable semconv span._attributes = { @@ -504,7 +554,10 @@ def test_span_to_envelope_client_http(self): "url.full": "https://www.wikipedia.org/path/12314/?q=ddds#124", } envelope = exporter._span_to_envelope(span) - self.assertEqual(envelope.data.base_data.data, "https://www.wikipedia.org/path/12314/?q=ddds#124") + self.assertEqual( + envelope.data.base_data.data, + "https://www.wikipedia.org/path/12314/?q=ddds#124", + ) # result_code span._attributes = { @@ -574,7 +627,9 @@ def test_span_to_envelope_client_db(self): span._status = Status(status_code=StatusCode.OK) envelope = exporter._span_to_envelope(span) - self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.RemoteDependency") + self.assertEqual( + envelope.name, "Microsoft.ApplicationInsights.RemoteDependency" + ) self.assertEqual(envelope.time, "2019-12-04T21:18:36.027613Z") self.assertEqual(envelope.data.base_data.name, "test") self.assertEqual(envelope.data.base_data.id, "a6f5d48acb4d31d9") @@ -693,7 +748,9 @@ def test_span_to_envelope_client_rpc(self): span._status = Status(status_code=StatusCode.OK) envelope = exporter._span_to_envelope(span) - self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.RemoteDependency") + self.assertEqual( + envelope.name, "Microsoft.ApplicationInsights.RemoteDependency" + ) self.assertEqual(envelope.time, "2019-12-04T21:18:36.027613Z") self.assertEqual(envelope.data.base_data.name, "test") self.assertEqual(envelope.data.base_data.id, "a6f5d48acb4d31d9") @@ -739,7 +796,9 @@ def test_span_to_envelope_client_messaging(self): span._status = Status(status_code=StatusCode.OK) envelope = exporter._span_to_envelope(span) - self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.RemoteDependency") + self.assertEqual( + envelope.name, "Microsoft.ApplicationInsights.RemoteDependency" + ) self.assertEqual(envelope.time, "2019-12-04T21:18:36.027613Z") self.assertEqual(envelope.data.base_data.name, "test") self.assertEqual(envelope.data.base_data.id, "a6f5d48acb4d31d9") @@ -783,7 +842,9 @@ def test_span_to_envelope_client_gen_ai(self): span._status = Status(status_code=StatusCode.UNSET) envelope = exporter._span_to_envelope(span) - self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.RemoteDependency") + self.assertEqual( + envelope.name, "Microsoft.ApplicationInsights.RemoteDependency" + ) self.assertEqual(envelope.time, "2019-12-04T21:18:36.027613Z") self.assertEqual(envelope.data.base_data.name, "test") self.assertEqual(envelope.data.base_data.id, "a6f5d48acb4d31d9") @@ -846,7 +907,9 @@ def test_span_to_envelope_client_multiple_types_with_gen_ai(self): envelope = exporter._span_to_envelope(span) self.assertEqual(envelope.data.base_data.type, "GenAI | az.ai.inference") - self.assertEqual(envelope.data.base_data.target, "test_address/test_destination") + self.assertEqual( + envelope.data.base_data.target, "test_address/test_destination" + ) def test_span_to_envelope_client_azure(self): exporter = self._exporter @@ -873,7 +936,9 @@ def test_span_to_envelope_client_azure(self): span._status = Status(status_code=StatusCode.OK) envelope = exporter._span_to_envelope(span) - self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.RemoteDependency") + self.assertEqual( + envelope.name, "Microsoft.ApplicationInsights.RemoteDependency" + ) self.assertEqual(envelope.time, "2019-12-04T21:18:36.027613Z") self.assertEqual(envelope.data.base_data.name, "test") self.assertEqual(envelope.data.base_data.id, "a6f5d48acb4d31d9") @@ -884,7 +949,9 @@ def test_span_to_envelope_client_azure(self): self.assertEqual(envelope.data.base_type, "RemoteDependencyData") self.assertEqual(envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "test") self.assertEqual(envelope.data.base_data.type, "Microsoft.EventHub") - self.assertEqual(envelope.data.base_data.target, "test_address/test_destination") + self.assertEqual( + envelope.data.base_data.target, "test_address/test_destination" + ) self.assertEqual(len(envelope.data.base_data.properties), 2) # target @@ -918,7 +985,9 @@ def test_span_to_envelope_producer_messaging(self): span._status = Status(status_code=StatusCode.OK) envelope = exporter._span_to_envelope(span) - self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.RemoteDependency") + self.assertEqual( + envelope.name, "Microsoft.ApplicationInsights.RemoteDependency" + ) self.assertEqual(envelope.time, "2019-12-04T21:18:36.027613Z") self.assertEqual(envelope.data.base_data.name, "test") self.assertEqual(envelope.data.base_data.id, "a6f5d48acb4d31d9") @@ -947,7 +1016,9 @@ def test_span_to_envelope_producer_messaging(self): "message_bus.destination": "/myeventhub", } envelope = exporter._span_to_envelope(span) - self.assertEqual(envelope.data.base_data.type, "Queue Message | Microsoft.EventHub") + self.assertEqual( + envelope.data.base_data.type, "Queue Message | Microsoft.EventHub" + ) self.assertEqual(envelope.data.base_data.target, "Test_peer//myeventhub") self.assertEqual(len(envelope.data.base_data.properties), 2) @@ -974,7 +1045,9 @@ def test_span_to_envelope_internal(self): span._status = Status(status_code=StatusCode.OK) envelope = exporter._span_to_envelope(span) - self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.RemoteDependency") + self.assertEqual( + envelope.name, "Microsoft.ApplicationInsights.RemoteDependency" + ) self.assertEqual(envelope.time, "2019-12-04T21:18:36.027613Z") self.assertEqual(envelope.data.base_data.name, "test") self.assertEqual(envelope.data.base_data.id, "a6f5d48acb4d31d9") @@ -1050,7 +1123,9 @@ def test_span_envelope_request_azure(self): self.assertEqual(envelope.data.base_data.response_code, "0") self.assertTrue(envelope.data.base_data.success) self.assertEqual(envelope.data.base_data.source, "Test_peer//myeventhub") - self.assertEqual(envelope.data.base_data.measurements["timeSinceEnqueued"], 2000000000000) + self.assertEqual( + envelope.data.base_data.measurements["timeSinceEnqueued"], 2000000000000 + ) self.assertEqual(len(envelope.data.base_data.properties), 3) # enqueued time @@ -1131,9 +1206,13 @@ def test_span_envelope_server_http(self): self.assertEqual(envelope.data.base_data.duration, "0.00:00:01.001") self.assertEqual(envelope.data.base_data.response_code, "200") self.assertTrue(envelope.data.base_data.success) - self.assertEqual(envelope.data.base_data.url, "https://www.wikipedia.org/wiki/Rabbit") + self.assertEqual( + envelope.data.base_data.url, "https://www.wikipedia.org/wiki/Rabbit" + ) - self.assertEqual(envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "GET /wiki/Rabbit") + self.assertEqual( + envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "GET /wiki/Rabbit" + ) self.assertEqual(envelope.tags["ai.user.userAgent"], "agent") self.assertEqual(envelope.tags[ContextTagKeys.AI_LOCATION_IP], "client_ip") self.assertEqual(len(envelope.data.base_data.properties), 0) @@ -1188,7 +1267,10 @@ def test_span_envelope_server_http(self): self.assertEqual(envelope.tags[ContextTagKeys.AI_LOCATION_IP], "peer_ip") ## Stable http semconv - span._attributes = {"http.request.method": "GET", "client.address": "client_address"} + span._attributes = { + "http.request.method": "GET", + "client.address": "client_address", + } envelope = exporter._span_to_envelope(span) self.assertEqual(envelope.tags[ContextTagKeys.AI_LOCATION_IP], "client_address") @@ -1208,7 +1290,9 @@ def test_span_envelope_server_http(self): "url.full": "https://www.example.org:80/path?query", } envelope = exporter._span_to_envelope(span) - self.assertEqual(envelope.data.base_data.url, "https://www.example.org:80/path?query") + self.assertEqual( + envelope.data.base_data.url, "https://www.example.org:80/path?query" + ) span._attributes = { "http.request.method": "GET", @@ -1219,7 +1303,9 @@ def test_span_envelope_server_http(self): "server.port": "80", } envelope = exporter._span_to_envelope(span) - self.assertEqual(envelope.data.base_data.url, "https://www.example.org:80/path?query") + self.assertEqual( + envelope.data.base_data.url, "https://www.example.org:80/path?query" + ) span._attributes = { "http.method": "GET", @@ -1247,7 +1333,9 @@ def test_span_envelope_server_http(self): "http.route": "/wiki/Rabbit/test", } envelope = exporter._span_to_envelope(span) - self.assertEqual(envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "GET /wiki/Rabbit/test") + self.assertEqual( + envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "GET /wiki/Rabbit/test" + ) self.assertEqual(envelope.data.base_data.name, "GET /wiki/Rabbit/test") ## Stable http semconv @@ -1256,7 +1344,9 @@ def test_span_envelope_server_http(self): "http.route": "/wiki/Rabbit/test", } envelope = exporter._span_to_envelope(span) - self.assertEqual(envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "GET /wiki/Rabbit/test") + self.assertEqual( + envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "GET /wiki/Rabbit/test" + ) self.assertEqual(envelope.data.base_data.name, "GET /wiki/Rabbit/test") span._attributes = { @@ -1264,7 +1354,9 @@ def test_span_envelope_server_http(self): "http.url": "https://www.wikipedia.org/wiki/Rabbit/test", } envelope = exporter._span_to_envelope(span) - self.assertEqual(envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "GET /wiki/Rabbit/test") + self.assertEqual( + envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "GET /wiki/Rabbit/test" + ) self.assertEqual(envelope.data.base_data.name, "GET /wiki/Rabbit/test") span._attributes = { @@ -1272,7 +1364,9 @@ def test_span_envelope_server_http(self): "url.full": "https://www.wikipedia.org/wiki/Rabbit/test", } envelope = exporter._span_to_envelope(span) - self.assertEqual(envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "GET /wiki/Rabbit/test") + self.assertEqual( + envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "GET /wiki/Rabbit/test" + ) self.assertEqual(envelope.data.base_data.name, "GET /wiki/Rabbit/test") # Default is span name @@ -1484,14 +1578,19 @@ def test_span_to_envelope_properties_std_metrics(self): "http.status_code": 200, }, kind=SpanKind.CLIENT, - instrumentation_scope=InstrumentationScope("opentelemetry.instrumentation.requests"), + instrumentation_scope=InstrumentationScope( + "opentelemetry.instrumentation.requests" + ), ) span._status = Status(status_code=StatusCode.OK) span.start(start_time=start_time) span.end(end_time=end_time) envelope = exporter._span_to_envelope(span) self.assertEqual(len(envelope.data.base_data.properties), 1) - self.assertEqual(envelope.data.base_data.properties["_MS.ProcessedByMetricExtractors"], "True") + self.assertEqual( + envelope.data.base_data.properties["_MS.ProcessedByMetricExtractors"], + "True", + ) def test_span_events_to_envelopes_exception(self): exporter = self._exporter @@ -1526,32 +1625,48 @@ def test_span_events_to_envelopes_exception(self): self.assertEqual(len(envelopes), 1) envelope = envelopes[0] self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.Exception") - self.assertEqual(envelope.instrumentation_key, "1234abcd-5678-4efa-8abc-1234567890ab") + self.assertEqual( + envelope.instrumentation_key, "1234abcd-5678-4efa-8abc-1234567890ab" + ) self.assertIsNotNone(envelope.tags) self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_DEVICE_ID), azure_monitor_context[ContextTagKeys.AI_DEVICE_ID] + envelope.tags.get(ContextTagKeys.AI_DEVICE_ID), + azure_monitor_context[ContextTagKeys.AI_DEVICE_ID], ) self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_DEVICE_LOCALE), azure_monitor_context[ContextTagKeys.AI_DEVICE_LOCALE] + envelope.tags.get(ContextTagKeys.AI_DEVICE_LOCALE), + azure_monitor_context[ContextTagKeys.AI_DEVICE_LOCALE], ) self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_DEVICE_TYPE), azure_monitor_context[ContextTagKeys.AI_DEVICE_TYPE] + envelope.tags.get(ContextTagKeys.AI_DEVICE_TYPE), + azure_monitor_context[ContextTagKeys.AI_DEVICE_TYPE], ) self.assertEqual( envelope.tags.get(ContextTagKeys.AI_INTERNAL_SDK_VERSION), azure_monitor_context[ContextTagKeys.AI_INTERNAL_SDK_VERSION], ) - self.assertEqual(envelope.tags.get(ContextTagKeys.AI_OPERATION_ID), "{:032x}".format(span.context.trace_id)) self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_OPERATION_PARENT_ID), "{:016x}".format(span.context.span_id) + envelope.tags.get(ContextTagKeys.AI_OPERATION_ID), + "{:032x}".format(span.context.trace_id), + ) + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_OPERATION_PARENT_ID), + "{:016x}".format(span.context.span_id), ) self.assertEqual(envelope.time, "2019-12-04T21:18:36.027613Z") self.assertEqual(len(envelope.data.base_data.properties), 0) self.assertEqual(len(envelope.data.base_data.exceptions), 1) - self.assertEqual(envelope.data.base_data.exceptions[0].type_name, "ZeroDivisionError") - self.assertEqual(envelope.data.base_data.exceptions[0].message, "zero division error") + self.assertEqual( + envelope.data.base_data.exceptions[0].type_name, "ZeroDivisionError" + ) + self.assertEqual( + envelope.data.base_data.exceptions[0].message, "zero division error" + ) self.assertEqual(envelope.data.base_data.exceptions[0].has_full_stack, True) - self.assertEqual(envelope.data.base_data.exceptions[0].stack, "Traceback: ZeroDivisionError, division by zero") + self.assertEqual( + envelope.data.base_data.exceptions[0].stack, + "Traceback: ZeroDivisionError, division by zero", + ) self.assertEqual(envelope.data.base_type, "ExceptionData") def test_span_events_to_envelopes_message(self): @@ -1584,24 +1699,33 @@ def test_span_events_to_envelopes_message(self): self.assertEqual(len(envelopes), 1) envelope = envelopes[0] self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.Message") - self.assertEqual(envelope.instrumentation_key, "1234abcd-5678-4efa-8abc-1234567890ab") + self.assertEqual( + envelope.instrumentation_key, "1234abcd-5678-4efa-8abc-1234567890ab" + ) self.assertIsNotNone(envelope.tags) self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_DEVICE_ID), azure_monitor_context[ContextTagKeys.AI_DEVICE_ID] + envelope.tags.get(ContextTagKeys.AI_DEVICE_ID), + azure_monitor_context[ContextTagKeys.AI_DEVICE_ID], ) self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_DEVICE_LOCALE), azure_monitor_context[ContextTagKeys.AI_DEVICE_LOCALE] + envelope.tags.get(ContextTagKeys.AI_DEVICE_LOCALE), + azure_monitor_context[ContextTagKeys.AI_DEVICE_LOCALE], ) self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_DEVICE_TYPE), azure_monitor_context[ContextTagKeys.AI_DEVICE_TYPE] + envelope.tags.get(ContextTagKeys.AI_DEVICE_TYPE), + azure_monitor_context[ContextTagKeys.AI_DEVICE_TYPE], ) self.assertEqual( envelope.tags.get(ContextTagKeys.AI_INTERNAL_SDK_VERSION), azure_monitor_context[ContextTagKeys.AI_INTERNAL_SDK_VERSION], ) - self.assertEqual(envelope.tags.get(ContextTagKeys.AI_OPERATION_ID), "{:032x}".format(span.context.trace_id)) self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_OPERATION_PARENT_ID), "{:016x}".format(span.context.span_id) + envelope.tags.get(ContextTagKeys.AI_OPERATION_ID), + "{:032x}".format(span.context.trace_id), + ) + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_OPERATION_PARENT_ID), + "{:016x}".format(span.context.span_id), ) self.assertEqual(envelope.time, "2019-12-04T21:18:36.027613Z") self.assertEqual(len(envelope.data.base_data.properties), 1) @@ -1644,24 +1768,33 @@ def test_span_events_to_envelopes_sample_rate(self): envelope = envelopes[0] self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.Message") self.assertEqual(envelope.sample_rate, 50) - self.assertEqual(envelope.instrumentation_key, "1234abcd-5678-4efa-8abc-1234567890ab") + self.assertEqual( + envelope.instrumentation_key, "1234abcd-5678-4efa-8abc-1234567890ab" + ) self.assertIsNotNone(envelope.tags) self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_DEVICE_ID), azure_monitor_context[ContextTagKeys.AI_DEVICE_ID] + envelope.tags.get(ContextTagKeys.AI_DEVICE_ID), + azure_monitor_context[ContextTagKeys.AI_DEVICE_ID], ) self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_DEVICE_LOCALE), azure_monitor_context[ContextTagKeys.AI_DEVICE_LOCALE] + envelope.tags.get(ContextTagKeys.AI_DEVICE_LOCALE), + azure_monitor_context[ContextTagKeys.AI_DEVICE_LOCALE], ) self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_DEVICE_TYPE), azure_monitor_context[ContextTagKeys.AI_DEVICE_TYPE] + envelope.tags.get(ContextTagKeys.AI_DEVICE_TYPE), + azure_monitor_context[ContextTagKeys.AI_DEVICE_TYPE], ) self.assertEqual( envelope.tags.get(ContextTagKeys.AI_INTERNAL_SDK_VERSION), azure_monitor_context[ContextTagKeys.AI_INTERNAL_SDK_VERSION], ) - self.assertEqual(envelope.tags.get(ContextTagKeys.AI_OPERATION_ID), "{:032x}".format(span.context.trace_id)) self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_OPERATION_PARENT_ID), "{:016x}".format(span.context.span_id) + envelope.tags.get(ContextTagKeys.AI_OPERATION_ID), + "{:032x}".format(span.context.trace_id), + ) + self.assertEqual( + envelope.tags.get(ContextTagKeys.AI_OPERATION_PARENT_ID), + "{:016x}".format(span.context.span_id), ) self.assertEqual(envelope.time, "2019-12-04T21:18:36.027613Z") self.assertEqual(len(envelope.data.base_data.properties), 1) @@ -1669,7 +1802,9 @@ def test_span_events_to_envelopes_sample_rate(self): self.assertEqual(envelope.data.base_data.message, "test event") self.assertEqual(envelope.data.base_type, "MessageData") - @mock.patch("azure.monitor.opentelemetry.exporter.export.trace._exporter.get_tracer_provider") + @mock.patch( + "azure.monitor.opentelemetry.exporter.export.trace._exporter.get_tracer_provider" + ) def test_export_otel_resource_metric(self, mock_get_tracer_provider): del os.environ["APPLICATIONINSIGHTS_OPENTELEMETRY_RESOURCE_METRIC_DISABLED"] mock_tracer_provider = mock.Mock() @@ -1761,7 +1896,9 @@ def test_check_instrumentation_span(self): span = mock.Mock() span.attributes = {} span.instrumentation_scope.name = "opentelemetry.instrumentation.test" - with mock.patch("azure.monitor.opentelemetry.exporter._utils.add_instrumentation") as add: + with mock.patch( + "azure.monitor.opentelemetry.exporter._utils.add_instrumentation" + ) as add: _check_instrumentation_span(span) add.assert_called_once_with("test") @@ -1769,7 +1906,9 @@ def test_check_instrumentation_span_not_instrumentation(self): span = mock.Mock() span.attributes = {} span.instrumentation_scope.name = "__main__" - with mock.patch("azure.monitor.opentelemetry.exporter._utils.add_instrumentation") as add: + with mock.patch( + "azure.monitor.opentelemetry.exporter._utils.add_instrumentation" + ) as add: _check_instrumentation_span(span) add.assert_not_called() @@ -1778,48 +1917,74 @@ def test_check_instrumentation_span_azure_sdk(self): span.attributes = {} span.instrumentation_scope.name = "azure.foo.bar.__init__" - with mock.patch("azure.monitor.opentelemetry.exporter._utils.add_instrumentation") as add: + with mock.patch( + "azure.monitor.opentelemetry.exporter._utils.add_instrumentation" + ) as add: _check_instrumentation_span(span) add.assert_called_once_with(_AZURE_SDK_OPENTELEMETRY_NAME) @mock.patch("opentelemetry.trace.get_tracer_provider") - def test_check_instrumentation_span_azure_sdk_otel_span(self, mock_get_tracer_provider): + def test_check_instrumentation_span_azure_sdk_otel_span( + self, mock_get_tracer_provider + ): mock_get_tracer_provider.return_value = self.get_tracer_provider() with OpenTelemetrySpan() as azure_sdk_span: - with mock.patch("azure.monitor.opentelemetry.exporter._utils.add_instrumentation") as add: + with mock.patch( + "azure.monitor.opentelemetry.exporter._utils.add_instrumentation" + ) as add: _check_instrumentation_span(azure_sdk_span.span_instance) add.assert_called_once_with(_AZURE_SDK_OPENTELEMETRY_NAME) - with mock.patch("azure.monitor.opentelemetry.exporter._utils.add_instrumentation") as add: - azure_sdk_span.add_attribute(_AZURE_SDK_NAMESPACE_NAME, "Microsoft.ServiceBus") + with mock.patch( + "azure.monitor.opentelemetry.exporter._utils.add_instrumentation" + ) as add: + azure_sdk_span.add_attribute( + _AZURE_SDK_NAMESPACE_NAME, "Microsoft.ServiceBus" + ) _check_instrumentation_span(azure_sdk_span.span_instance) add.assert_called_once_with(_AZURE_SDK_OPENTELEMETRY_NAME) - with mock.patch("azure.monitor.opentelemetry.exporter._utils.add_instrumentation") as add: - azure_sdk_span.add_attribute(_AZURE_SDK_NAMESPACE_NAME, "Microsoft.CognitiveServices") + with mock.patch( + "azure.monitor.opentelemetry.exporter._utils.add_instrumentation" + ) as add: + azure_sdk_span.add_attribute( + _AZURE_SDK_NAMESPACE_NAME, "Microsoft.CognitiveServices" + ) _check_instrumentation_span(azure_sdk_span.span_instance) add.assert_called_once_with(_AZURE_AI_SDK_NAME) @mock.patch("opentelemetry.trace.get_tracer_provider") - def test_check_instrumentation_span_azure_sdk_span_impl(self, mock_get_tracer_provider): + def test_check_instrumentation_span_azure_sdk_span_impl( + self, mock_get_tracer_provider + ): mock_get_tracer_provider.return_value = self.get_tracer_provider() settings.tracing_implementation = "opentelemetry" try: azure_sdk_span = settings.tracing_implementation()(name="test") - with mock.patch("azure.monitor.opentelemetry.exporter._utils.add_instrumentation") as add: + with mock.patch( + "azure.monitor.opentelemetry.exporter._utils.add_instrumentation" + ) as add: _check_instrumentation_span(azure_sdk_span.span_instance) add.assert_called_once_with(_AZURE_SDK_OPENTELEMETRY_NAME) - with mock.patch("azure.monitor.opentelemetry.exporter._utils.add_instrumentation") as add: - azure_sdk_span.add_attribute(_AZURE_SDK_NAMESPACE_NAME, "Microsoft.ServiceBus") + with mock.patch( + "azure.monitor.opentelemetry.exporter._utils.add_instrumentation" + ) as add: + azure_sdk_span.add_attribute( + _AZURE_SDK_NAMESPACE_NAME, "Microsoft.ServiceBus" + ) _check_instrumentation_span(azure_sdk_span.span_instance) add.assert_called_once_with(_AZURE_SDK_OPENTELEMETRY_NAME) - with mock.patch("azure.monitor.opentelemetry.exporter._utils.add_instrumentation") as add: - azure_sdk_span.add_attribute(_AZURE_SDK_NAMESPACE_NAME, "Microsoft.CognitiveServices") + with mock.patch( + "azure.monitor.opentelemetry.exporter._utils.add_instrumentation" + ) as add: + azure_sdk_span.add_attribute( + _AZURE_SDK_NAMESPACE_NAME, "Microsoft.CognitiveServices" + ) _check_instrumentation_span(azure_sdk_span.span_instance) add.assert_called_once_with(_AZURE_AI_SDK_NAME) @@ -1827,7 +1992,9 @@ def test_check_instrumentation_span_azure_sdk_span_impl(self, mock_get_tracer_pr settings.tracing_implementation = None @mock.patch("opentelemetry.trace.get_tracer_provider") - def test_check_instrumentation_span_azure_sdk_get_tracer(self, mock_get_tracer_provider): + def test_check_instrumentation_span_azure_sdk_get_tracer( + self, mock_get_tracer_provider + ): mock_get_tracer_provider.return_value = self.get_tracer_provider() if not get_azure_sdk_tracer: @@ -1836,28 +2003,12 @@ def test_check_instrumentation_span_azure_sdk_get_tracer(self, mock_get_tracer_p azure_sdk_tracer = get_azure_sdk_tracer(library_name="azure-foo-bar") azure_sdk_span = azure_sdk_tracer.start_span(name="test") - with mock.patch("azure.monitor.opentelemetry.exporter._utils.add_instrumentation") as add: - _check_instrumentation_span(azure_sdk_span) - add.assert_called_once_with(_AZURE_SDK_OPENTELEMETRY_NAME) - - with mock.patch("azure.monitor.opentelemetry.exporter._utils.add_instrumentation") as add: - azure_sdk_span.set_attribute(_AZURE_SDK_NAMESPACE_NAME, "Microsoft.ServiceBus") + with mock.patch( + "azure.monitor.opentelemetry.exporter._utils.add_instrumentation" + ) as add: _check_instrumentation_span(azure_sdk_span) add.assert_called_once_with(_AZURE_SDK_OPENTELEMETRY_NAME) - with mock.patch("azure.monitor.opentelemetry.exporter._utils.add_instrumentation") as add: - azure_sdk_span.set_attribute(_AZURE_SDK_NAMESPACE_NAME, "Microsoft.CognitiveServices") - _check_instrumentation_span(azure_sdk_span) - add.assert_called_once_with(_AZURE_AI_SDK_NAME) - - not_azure_sdk_span = get_azure_sdk_tracer(library_name="not-azure-foo-bar").start_span(name="test") - with mock.patch("azure.monitor.opentelemetry.exporter._utils.add_instrumentation") as add: - _check_instrumentation_span(not_azure_sdk_span) - add.assert_not_called() - def get_tracer_provider(self): - tracer_provider = TracerProvider() - span_exporter = InMemorySpanExporter() - processor = SimpleSpanProcessor(span_exporter) - tracer_provider.add_span_processor(processor) - return tracer_provider + return trace.TracerProvider() + From 4e7badd539e3802811a84aec411831bb5a2fc65d Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Wed, 14 Jan 2026 16:21:43 -0800 Subject: [PATCH 2/9] Lint --- .../exporter/export/logs/_exporter.py | 25 +++++++++---------- .../exporter/export/trace/_exporter.py | 2 +- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py index 5d169d5db8f2..c67e86a87f31 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py @@ -13,6 +13,18 @@ EXCEPTION_STACKTRACE, EXCEPTION_TYPE, ) +try: + from opentelemetry.semconv.logs import ( + LogRecordAttributes as _SemconvLogRecordAttributes, + ) +except ImportError: + _SemconvLogRecordAttributes = None +try: + from opentelemetry.semconv._incubating.attributes import ( + enduser_attributes as _enduser_attributes, + ) +except ImportError: + _enduser_attributes = None from azure.monitor.opentelemetry.exporter import _utils from azure.monitor.opentelemetry.exporter._constants import ( @@ -43,19 +55,6 @@ set_statsbeat_custom_events_feature_set, ) -try: - from opentelemetry.semconv.logs import ( - LogRecordAttributes as _SemconvLogRecordAttributes, - ) -except ImportError: - _SemconvLogRecordAttributes = None -try: - from opentelemetry.semconv._incubating.attributes import ( - enduser_attributes as _enduser_attributes, - ) -except ImportError: - _enduser_attributes = None - _ENDUSER_ID_ATTRIBUTE = ( getattr(_SemconvLogRecordAttributes, "ENDUSER_ID", None) or getattr(_enduser_attributes, "ENDUSER_ID", None) 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 2f2c82bb2e18..b0993e28d281 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 @@ -124,7 +124,7 @@ def __init__(self, **kwargs: Any): def export( self, spans: Sequence[ReadableSpan], - **kwargs: Any # pylint: disable=unused-argument + **_kwargs: Any ) -> SpanExportResult: """Export span data. From 192341055634e88ffd7f42fb5ff4a322b4eb0b62 Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Wed, 14 Jan 2026 16:28:46 -0800 Subject: [PATCH 3/9] Update --- .../exporter/_generated/models/_models.py | 5 +- .../exporter/_generated/models/_models_py3.py | 60 +++++-------------- 2 files changed, 18 insertions(+), 47 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_generated/models/_models.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_generated/models/_models.py index 4cbcd29ccef2..ceaf61192c50 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_generated/models/_models.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_generated/models/_models.py @@ -6,6 +6,7 @@ # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- +from azure.core.exceptions import HttpResponseError import msrest.serialization @@ -378,7 +379,7 @@ class PageViewData(MonitorDomain): _validation = { "version": {"required": True}, "id": {"required": True, "max_length": 512, "min_length": 0}, - "name": {"required": True, "max_length": 512, "min_length": 0}, + "name": {"required": True, "max_length": 1024, "min_length": 0}, "url": {"max_length": 2048, "min_length": 0}, "referred_uri": {"max_length": 2048, "min_length": 0}, } @@ -477,7 +478,7 @@ class PageViewPerfData(MonitorDomain): _validation = { "version": {"required": True}, "id": {"required": True, "max_length": 512, "min_length": 0}, - "name": {"required": True, "max_length": 512, "min_length": 0}, + "name": {"required": True, "max_length": 1024, "min_length": 0}, "url": {"max_length": 2048, "min_length": 0}, } diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_generated/models/_models_py3.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_generated/models/_models_py3.py index 32374f701e41..fefe33af86cb 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_generated/models/_models_py3.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_generated/models/_models_py3.py @@ -9,6 +9,7 @@ import datetime from typing import Any, Dict, List, Optional, Union +from azure.core.exceptions import HttpResponseError import msrest.serialization from ._azure_monitor_client_enums import * @@ -35,13 +36,7 @@ class MonitorDomain(msrest.serialization.Model): "version": {"key": "ver", "type": "int"}, } - def __init__( - self, - *, - version: int = 2, - additional_properties: Optional[Dict[str, Any]] = None, - **kwargs - ): + def __init__(self, *, version: int = 2, additional_properties: Optional[Dict[str, Any]] = None, **kwargs): """ :keyword additional_properties: Unmatched properties from the message are deserialized to this collection. @@ -146,9 +141,7 @@ def __init__( :keyword measurements: Collection of custom measurements. :paramtype measurements: dict[str, float] """ - super(AvailabilityData, self).__init__( - additional_properties=additional_properties, version=version, **kwargs - ) + super(AvailabilityData, self).__init__(additional_properties=additional_properties, version=version, **kwargs) self.id = id self.name = name self.duration = duration @@ -221,9 +214,7 @@ def __init__( :keyword measurements: Collection of custom measurements. :paramtype measurements: dict[str, float] """ - super(MessageData, self).__init__( - additional_properties=additional_properties, version=version, **kwargs - ) + super(MessageData, self).__init__(additional_properties=additional_properties, version=version, **kwargs) self.message = message self.severity_level = severity_level self.properties = properties @@ -370,9 +361,7 @@ def __init__( :keyword properties: Collection of custom properties. :paramtype properties: dict[str, str] """ - super(MetricsData, self).__init__( - additional_properties=additional_properties, version=version, **kwargs - ) + super(MetricsData, self).__init__(additional_properties=additional_properties, version=version, **kwargs) self.metrics = metrics self.properties = properties @@ -392,13 +381,7 @@ class MonitorBase(msrest.serialization.Model): "base_data": {"key": "baseData", "type": "MonitorDomain"}, } - def __init__( - self, - *, - base_type: Optional[str] = None, - base_data: Optional["MonitorDomain"] = None, - **kwargs - ): + def __init__(self, *, base_type: Optional[str] = None, base_data: Optional["MonitorDomain"] = None, **kwargs): """ :keyword base_type: Name of item (B section) if any. If telemetry data is derived straight from this, this should be null. @@ -445,7 +428,7 @@ class PageViewData(MonitorDomain): _validation = { "version": {"required": True}, "id": {"required": True, "max_length": 512, "min_length": 0}, - "name": {"required": True, "max_length": 512, "min_length": 0}, + "name": {"required": True, "max_length": 1024, "min_length": 0}, "url": {"max_length": 2048, "min_length": 0}, "referred_uri": {"max_length": 2048, "min_length": 0}, } @@ -502,9 +485,7 @@ def __init__( :keyword measurements: Collection of custom measurements. :paramtype measurements: dict[str, float] """ - super(PageViewData, self).__init__( - additional_properties=additional_properties, version=version, **kwargs - ) + super(PageViewData, self).__init__(additional_properties=additional_properties, version=version, **kwargs) self.id = id self.name = name self.url = url @@ -559,7 +540,7 @@ class PageViewPerfData(MonitorDomain): _validation = { "version": {"required": True}, "id": {"required": True, "max_length": 512, "min_length": 0}, - "name": {"required": True, "max_length": 512, "min_length": 0}, + "name": {"required": True, "max_length": 1024, "min_length": 0}, "url": {"max_length": 2048, "min_length": 0}, } @@ -635,9 +616,7 @@ def __init__( :keyword measurements: Collection of custom measurements. :paramtype measurements: dict[str, float] """ - super(PageViewPerfData, self).__init__( - additional_properties=additional_properties, version=version, **kwargs - ) + super(PageViewPerfData, self).__init__(additional_properties=additional_properties, version=version, **kwargs) self.id = id self.name = name self.url = url @@ -890,9 +869,7 @@ def __init__( :keyword measurements: Collection of custom measurements. :paramtype measurements: dict[str, float] """ - super(RequestData, self).__init__( - additional_properties=additional_properties, version=version, **kwargs - ) + super(RequestData, self).__init__(additional_properties=additional_properties, version=version, **kwargs) self.id = id self.name = name self.duration = duration @@ -984,12 +961,7 @@ class TelemetryErrorDetails(msrest.serialization.Model): } def __init__( - self, - *, - index: Optional[int] = None, - status_code: Optional[int] = None, - message: Optional[str] = None, - **kwargs + self, *, index: Optional[int] = None, status_code: Optional[int] = None, message: Optional[str] = None, **kwargs ): """ :keyword index: The index in the original payload of the item. @@ -1061,9 +1033,7 @@ def __init__( :keyword measurements: Collection of custom measurements. :paramtype measurements: dict[str, float] """ - super(TelemetryEventData, self).__init__( - additional_properties=additional_properties, version=version, **kwargs - ) + super(TelemetryEventData, self).__init__(additional_properties=additional_properties, version=version, **kwargs) self.name = name self.properties = properties self.measurements = measurements @@ -1350,13 +1320,13 @@ class TrackResponse(msrest.serialization.Model): "errors": {"key": "errors", "type": "[TelemetryErrorDetails]"}, } - def __init__( + def __init__( # type: ignore self, *, items_received: Optional[int] = None, items_accepted: Optional[int] = None, errors: Optional[List["TelemetryErrorDetails"]] = None, - **kwargs + **kwargs # type: Any ): """ :keyword items_received: The number of items received. From 50b2a8347ae43a7f7c9703fc7cd00f95324f4bff Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Thu, 15 Jan 2026 16:37:25 -0800 Subject: [PATCH 4/9] Update --- .../exporter/export/logs/_exporter.py | 3 ++ .../exporter/export/trace/_exporter.py | 28 ++++++++++++------- .../tests/logs/test_logs.py | 2 ++ .../tests/trace/test_trace.py | 27 +++++++++++++++++- 4 files changed, 49 insertions(+), 11 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py index c67e86a87f31..09b5838b9c5d 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py @@ -13,6 +13,7 @@ EXCEPTION_STACKTRACE, EXCEPTION_TYPE, ) + try: from opentelemetry.semconv.logs import ( LogRecordAttributes as _SemconvLogRecordAttributes, @@ -294,6 +295,8 @@ def _is_ignored_attribute(key: str) -> bool: EXCEPTION_ESCAPED, _APPLICATION_INSIGHTS_EVENT_MARKER_ATTRIBUTE, _MICROSOFT_CUSTOM_EVENT_NAME, + _ENDUSER_ID_ATTRIBUTE, + _ENDUSER_PSEUDO_ID_ATTRIBUTE, ) ) 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 b0993e28d281..226d7505da2e 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 @@ -63,12 +63,24 @@ __all__ = ["AzureMonitorTraceExporter"] -_ENDUSER_ID_ATTRIBUTE = getattr(SpanAttributes, "ENDUSER_ID", None) or getattr( - _enduser_attributes, "ENDUSER_ID", "enduser.id" +_ENDUSER_ID_ATTRIBUTE = ( + getattr(SpanAttributes, "ENDUSER_ID", None) + or ( + getattr(_enduser_attributes, "ENDUSER_ID", None) + if _enduser_attributes is not None + else None + ) + or "enduser.id" +) +_ENDUSER_PSEUDO_ID_ATTRIBUTE = ( + getattr(SpanAttributes, "ENDUSER_PSEUDO_ID", None) + or ( + getattr(_enduser_attributes, "ENDUSER_PSEUDO_ID", None) + if _enduser_attributes is not None + else None + ) + or "enduser.pseudo.id" ) -_ENDUSER_PSEUDO_ID_ATTRIBUTE = getattr( - SpanAttributes, "ENDUSER_PSEUDO_ID", None -) or getattr(_enduser_attributes, "ENDUSER_PSEUDO_ID", "enduser.pseudo.id") _STANDARD_OPENTELEMETRY_ATTRIBUTE_PREFIXES = [ "http.", @@ -121,11 +133,7 @@ def __init__(self, **kwargs: Any): self._tracer_provider = kwargs.pop("tracer_provider", None) super().__init__(**kwargs) - def export( - self, - spans: Sequence[ReadableSpan], - **_kwargs: Any - ) -> SpanExportResult: + def export(self, spans: Sequence[ReadableSpan], **_kwargs: Any) -> SpanExportResult: """Export span data. :param spans: Open Telemetry Spans to export. diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/logs/test_logs.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/logs/test_logs.py index b282b5141aaa..a53e612f7b04 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/logs/test_logs.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/logs/test_logs.py @@ -473,6 +473,8 @@ def test_log_to_envelope_user_fields(self): envelope.tags.get(ContextTagKeys.AI_USER_AUTH_USER_ID), "test-auth" ) self.assertEqual(envelope.tags.get(ContextTagKeys.AI_USER_ID), "test-user") + self.assertNotIn("enduser.id", envelope.data.base_data.properties) + self.assertNotIn("enduser.pseudo.id", envelope.data.base_data.properties) def test_log_to_envelope_log_none(self): exporter = self._exporter 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 af1ef20a9353..c0c35fc3e738 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 @@ -2009,6 +2009,31 @@ def test_check_instrumentation_span_azure_sdk_get_tracer( _check_instrumentation_span(azure_sdk_span) add.assert_called_once_with(_AZURE_SDK_OPENTELEMETRY_NAME) + with mock.patch( + "azure.monitor.opentelemetry.exporter._utils.add_instrumentation" + ) as add: + azure_sdk_span.set_attribute( + _AZURE_SDK_NAMESPACE_NAME, "Microsoft.ServiceBus" + ) + _check_instrumentation_span(azure_sdk_span) + add.assert_called_once_with(_AZURE_SDK_OPENTELEMETRY_NAME) + + with mock.patch( + "azure.monitor.opentelemetry.exporter._utils.add_instrumentation" + ) as add: + azure_sdk_span.set_attribute( + _AZURE_SDK_NAMESPACE_NAME, "Microsoft.CognitiveServices" + ) + _check_instrumentation_span(azure_sdk_span) + add.assert_called_once_with(_AZURE_AI_SDK_NAME) + + other_tracer = self.get_tracer_provider().get_tracer("not-azure-foo-bar") + other_span = other_tracer.start_span(name="test") + with mock.patch( + "azure.monitor.opentelemetry.exporter._utils.add_instrumentation" + ) as add: + _check_instrumentation_span(other_span) + add.assert_not_called() + def get_tracer_provider(self): return trace.TracerProvider() - From 6c53a15cb4d3d8ab1ab57e96e3a7472de23a624b Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Thu, 15 Jan 2026 16:42:22 -0800 Subject: [PATCH 5/9] Add check for log_record.attributes to be available --- .../opentelemetry/exporter/export/logs/_exporter.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py index 09b5838b9c5d..ca20ba425eb3 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py @@ -161,11 +161,17 @@ def _convert_log_to_envelope(readable_log_record: ReadableLogRecord) -> Telemetr envelope.tags[ContextTagKeys.AI_OPERATION_ID] = "{:032x}".format( # type: ignore log_record.trace_id or _DEFAULT_TRACE_ID ) - if _ENDUSER_ID_ATTRIBUTE in log_record.attributes: + if ( + log_record.attributes + and _ENDUSER_ID_ATTRIBUTE in log_record.attributes + ): envelope.tags[ContextTagKeys.AI_USER_AUTH_USER_ID] = log_record.attributes[ _ENDUSER_ID_ATTRIBUTE ] - if _ENDUSER_PSEUDO_ID_ATTRIBUTE in log_record.attributes: + if ( + log_record.attributes + and _ENDUSER_PSEUDO_ID_ATTRIBUTE in log_record.attributes: + ) envelope.tags[ContextTagKeys.AI_USER_ID] = log_record.attributes[ _ENDUSER_PSEUDO_ID_ATTRIBUTE ] From ac1834f513c6ca48e4d2b3a5a75fc7434e9928e4 Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Thu, 15 Jan 2026 16:59:11 -0800 Subject: [PATCH 6/9] Add changelog --- sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md b/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md index 77d608f56827..cc9ce03c5119 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md @@ -3,13 +3,14 @@ ## 1.0.0b47 (Unreleased) ### Features Added +- Add support for user id and authId +([#44662](https://github.com/Azure/azure-sdk-for-python/pull/44662)) ### Breaking Changes ### Bugs Fixed ### Other Changes - - Declare support for Python 3.13 and 3.14 ([#44550](https://github.com/Azure/azure-sdk-for-python/pull/44550)) From c98aaed4fe97339a013daee4822f971f741c13f4 Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Fri, 16 Jan 2026 11:01:08 -0800 Subject: [PATCH 7/9] Update --- .../opentelemetry/exporter/export/logs/_exporter.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py index ca20ba425eb3..8ec849e8e1c0 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py @@ -161,17 +161,11 @@ def _convert_log_to_envelope(readable_log_record: ReadableLogRecord) -> Telemetr envelope.tags[ContextTagKeys.AI_OPERATION_ID] = "{:032x}".format( # type: ignore log_record.trace_id or _DEFAULT_TRACE_ID ) - if ( - log_record.attributes - and _ENDUSER_ID_ATTRIBUTE in log_record.attributes - ): + if log_record.attributes and _ENDUSER_ID_ATTRIBUTE in log_record.attributes: envelope.tags[ContextTagKeys.AI_USER_AUTH_USER_ID] = log_record.attributes[ _ENDUSER_ID_ATTRIBUTE ] - if ( - log_record.attributes - and _ENDUSER_PSEUDO_ID_ATTRIBUTE in log_record.attributes: - ) + if log_record.attributes and _ENDUSER_PSEUDO_ID_ATTRIBUTE in log_record.attributes: envelope.tags[ContextTagKeys.AI_USER_ID] = log_record.attributes[ _ENDUSER_PSEUDO_ID_ATTRIBUTE ] From 45f916dca2bab7c4d6ab7d6ae8e54f436e91545c Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Fri, 16 Jan 2026 13:18:42 -0800 Subject: [PATCH 8/9] Lint --- .../exporter/export/logs/_exporter.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py index 8ec849e8e1c0..ddab28b942bc 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py @@ -157,20 +157,22 @@ def _convert_log_to_envelope(readable_log_record: ReadableLogRecord) -> Telemetr else log_record.observed_timestamp ) envelope = _utils._create_telemetry_item(time_stamp) - envelope.tags.update(_utils._populate_part_a_fields(readable_log_record.resource)) # type: ignore - envelope.tags[ContextTagKeys.AI_OPERATION_ID] = "{:032x}".format( # type: ignore + tags = envelope.tags or {} + envelope.tags = tags + tags.update(_utils._populate_part_a_fields(readable_log_record.resource)) # type: ignore + tags[ContextTagKeys.AI_OPERATION_ID] = "{:032x}".format( # type: ignore log_record.trace_id or _DEFAULT_TRACE_ID ) if log_record.attributes and _ENDUSER_ID_ATTRIBUTE in log_record.attributes: - envelope.tags[ContextTagKeys.AI_USER_AUTH_USER_ID] = log_record.attributes[ + tags[ContextTagKeys.AI_USER_AUTH_USER_ID] = log_record.attributes[ _ENDUSER_ID_ATTRIBUTE ] if log_record.attributes and _ENDUSER_PSEUDO_ID_ATTRIBUTE in log_record.attributes: - envelope.tags[ContextTagKeys.AI_USER_ID] = log_record.attributes[ + tags[ContextTagKeys.AI_USER_ID] = log_record.attributes[ _ENDUSER_PSEUDO_ID_ATTRIBUTE ] - envelope.tags[ContextTagKeys.AI_OPERATION_PARENT_ID] = "{:016x}".format( # type: ignore + tags[ContextTagKeys.AI_OPERATION_PARENT_ID] = "{:016x}".format( # type: ignore log_record.span_id or _DEFAULT_SPAN_ID ) if ( @@ -178,15 +180,15 @@ def _convert_log_to_envelope(readable_log_record: ReadableLogRecord) -> Telemetr and ContextTagKeys.AI_OPERATION_NAME in log_record.attributes and log_record.attributes[ContextTagKeys.AI_OPERATION_NAME] is not None ): - envelope.tags[ContextTagKeys.AI_OPERATION_NAME] = log_record.attributes.get( # type: ignore + tags[ContextTagKeys.AI_OPERATION_NAME] = log_record.attributes.get( # type: ignore ContextTagKeys.AI_OPERATION_NAME ) if _utils._is_any_synthetic_source(log_record.attributes): - envelope.tags[ContextTagKeys.AI_OPERATION_SYNTHETIC_SOURCE] = "True" # type: ignore + tags[ContextTagKeys.AI_OPERATION_SYNTHETIC_SOURCE] = "True" # type: ignore # Special use case: Customers want to be able to set location ip on log records location_ip = trace_utils._get_location_ip(log_record.attributes) if location_ip: - envelope.tags[ContextTagKeys.AI_LOCATION_IP] = location_ip # type: ignore + tags[ContextTagKeys.AI_LOCATION_IP] = location_ip # type: ignore properties = _utils._filter_custom_properties( log_record.attributes, lambda key, val: not _is_ignored_attribute(key) # type: ignore ) From e3086f8634b9191e6602a2e9f7f42e6b0633059a Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Fri, 16 Jan 2026 15:03:57 -0800 Subject: [PATCH 9/9] Black format --- .../exporter/export/logs/_exporter.py | 40 +-- .../exporter/export/trace/_exporter.py | 131 +++------- .../tests/logs/test_logs.py | 80 ++---- .../tests/trace/test_trace.py | 236 +++++------------- 4 files changed, 123 insertions(+), 364 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py index ddab28b942bc..664b26189aae 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py @@ -78,9 +78,7 @@ class AzureMonitorLogExporter(BaseExporter, LogRecordExporter): """Azure Monitor Log exporter for OpenTelemetry.""" - def export( - self, batch: Sequence[ReadableLogRecord], **kwargs: Any - ) -> LogRecordExportResult: + def export(self, batch: Sequence[ReadableLogRecord], **kwargs: Any) -> LogRecordExportResult: # pylint: disable=unused-argument """Export log data. @@ -95,9 +93,7 @@ def export( self._handle_transmit_from_storage(envelopes, result) return _get_log_export_result(result) except Exception: # pylint: disable=broad-except - _logger.exception( - "Exception occurred while exporting the data." - ) # pylint: disable=C4769 + _logger.exception("Exception occurred while exporting the data.") # pylint: disable=C4769 return _get_log_export_result(ExportResult.FAILED_NOT_RETRYABLE) def shutdown(self) -> None: @@ -115,9 +111,7 @@ def _log_to_envelope(self, readable_log_record: ReadableLogRecord) -> TelemetryI # pylint: disable=docstring-keyword-should-match-keyword-only @classmethod - def from_connection_string( - cls, conn_str: str, **kwargs: Any - ) -> "AzureMonitorLogExporter": + def from_connection_string(cls, conn_str: str, **kwargs: Any) -> "AzureMonitorLogExporter": """ Create an AzureMonitorLogExporter from a connection string. This is the recommended way of instantiation if a connection string is passed in @@ -139,9 +133,7 @@ def _log_data_is_event(readable_log_record: ReadableLogRecord) -> bool: log_record = readable_log_record.log_record is_event = None if log_record.attributes: - is_event = log_record.attributes.get( - _MICROSOFT_CUSTOM_EVENT_NAME - ) or log_record.attributes.get( + is_event = log_record.attributes.get(_MICROSOFT_CUSTOM_EVENT_NAME) or log_record.attributes.get( _APPLICATION_INSIGHTS_EVENT_MARKER_ATTRIBUTE ) # type: ignore return is_event is not None @@ -151,26 +143,16 @@ def _log_data_is_event(readable_log_record: ReadableLogRecord) -> bool: # pylint: disable=too-many-statements def _convert_log_to_envelope(readable_log_record: ReadableLogRecord) -> TelemetryItem: log_record = readable_log_record.log_record - time_stamp = ( - log_record.timestamp - if log_record.timestamp is not None - else log_record.observed_timestamp - ) + time_stamp = log_record.timestamp if log_record.timestamp is not None else log_record.observed_timestamp envelope = _utils._create_telemetry_item(time_stamp) tags = envelope.tags or {} envelope.tags = tags tags.update(_utils._populate_part_a_fields(readable_log_record.resource)) # type: ignore - tags[ContextTagKeys.AI_OPERATION_ID] = "{:032x}".format( # type: ignore - log_record.trace_id or _DEFAULT_TRACE_ID - ) + tags[ContextTagKeys.AI_OPERATION_ID] = "{:032x}".format(log_record.trace_id or _DEFAULT_TRACE_ID) # type: ignore if log_record.attributes and _ENDUSER_ID_ATTRIBUTE in log_record.attributes: - tags[ContextTagKeys.AI_USER_AUTH_USER_ID] = log_record.attributes[ - _ENDUSER_ID_ATTRIBUTE - ] + tags[ContextTagKeys.AI_USER_AUTH_USER_ID] = log_record.attributes[_ENDUSER_ID_ATTRIBUTE] if log_record.attributes and _ENDUSER_PSEUDO_ID_ATTRIBUTE in log_record.attributes: - tags[ContextTagKeys.AI_USER_ID] = log_record.attributes[ - _ENDUSER_PSEUDO_ID_ATTRIBUTE - ] + tags[ContextTagKeys.AI_USER_ID] = log_record.attributes[_ENDUSER_PSEUDO_ID_ATTRIBUTE] tags[ContextTagKeys.AI_OPERATION_PARENT_ID] = "{:016x}".format( # type: ignore log_record.span_id or _DEFAULT_SPAN_ID @@ -304,9 +286,5 @@ def _is_ignored_attribute(key: str) -> bool: def _set_statsbeat_custom_events_feature(): - if ( - is_statsbeat_enabled() - and not get_statsbeat_shutdown() - and not get_statsbeat_custom_events_feature_set() - ): + if is_statsbeat_enabled() and not get_statsbeat_shutdown() and not get_statsbeat_custom_events_feature_set(): set_statsbeat_custom_events_feature_set() 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 226d7505da2e..ecd2c452c34d 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 @@ -65,20 +65,12 @@ _ENDUSER_ID_ATTRIBUTE = ( getattr(SpanAttributes, "ENDUSER_ID", None) - or ( - getattr(_enduser_attributes, "ENDUSER_ID", None) - if _enduser_attributes is not None - else None - ) + or (getattr(_enduser_attributes, "ENDUSER_ID", None) if _enduser_attributes is not None else None) or "enduser.id" ) _ENDUSER_PSEUDO_ID_ATTRIBUTE = ( getattr(SpanAttributes, "ENDUSER_PSEUDO_ID", None) - or ( - getattr(_enduser_attributes, "ENDUSER_PSEUDO_ID", None) - if _enduser_attributes is not None - else None - ) + or (getattr(_enduser_attributes, "ENDUSER_PSEUDO_ID", None) if _enduser_attributes is not None else None) or "enduser.pseudo.id" ) @@ -149,9 +141,7 @@ def export(self, spans: Sequence[ReadableSpan], **_kwargs: Any) -> SpanExportRes resource = tracer_provider.resource # type: ignore envelopes.append(self._get_otel_resource_envelope(resource)) except AttributeError as e: - _logger.exception( - "Failed to derive Resource from Tracer Provider: %s", e - ) # pylint: disable=C4769 + _logger.exception("Failed to derive Resource from Tracer Provider: %s", e) # pylint: disable=C4769 for span in spans: envelopes.append(self._span_to_envelope(span)) envelopes.extend(self._span_events_to_envelopes(span)) @@ -160,9 +150,7 @@ def export(self, spans: Sequence[ReadableSpan], **_kwargs: Any) -> SpanExportRes self._handle_transmit_from_storage(envelopes, result) return _get_trace_export_result(result) except Exception: # pylint: disable=broad-except - _logger.exception( - "Exception occurred while exporting the data." - ) # pylint: disable=C4769 + _logger.exception("Exception occurred while exporting the data.") # pylint: disable=C4769 return _get_trace_export_result(ExportResult.FAILED_NOT_RETRYABLE) def shutdown(self) -> None: @@ -180,9 +168,7 @@ def _get_otel_resource_envelope(self, resource: Resource) -> TelemetryItem: attributes = resource.attributes envelope = _utils._create_telemetry_item(time_ns()) envelope.name = _METRIC_ENVELOPE_NAME - envelope.tags.update( - _utils._populate_part_a_fields(resource) - ) # pylint: disable=W0212 + envelope.tags.update(_utils._populate_part_a_fields(resource)) # pylint: disable=W0212 envelope.instrumentation_key = self._instrumentation_key data_point = MetricDataPoint( name="_OTELRESOURCE_"[:1024], @@ -212,16 +198,12 @@ def _span_events_to_envelopes(self, span: ReadableSpan) -> Sequence[TelemetryIte return envelopes def _should_collect_otel_resource_metric(self): - disabled = environ.get( - _APPLICATIONINSIGHTS_OPENTELEMETRY_RESOURCE_METRIC_DISABLED - ) + disabled = environ.get(_APPLICATIONINSIGHTS_OPENTELEMETRY_RESOURCE_METRIC_DISABLED) return disabled is None or disabled.lower() != "true" # pylint: disable=docstring-keyword-should-match-keyword-only @classmethod - def from_connection_string( - cls, conn_str: str, **kwargs: Any - ) -> "AzureMonitorTraceExporter": + def from_connection_string(cls, conn_str: str, **kwargs: Any) -> "AzureMonitorTraceExporter": """ Create an AzureMonitorTraceExporter from a connection string. This is the recommended way of instantiation if a connection string is passed in @@ -255,23 +237,15 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: duration = span.end_time - span.start_time envelope = _utils._create_telemetry_item(start_time) envelope.tags.update(_utils._populate_part_a_fields(span.resource)) - envelope.tags[ContextTagKeys.AI_OPERATION_ID] = "{:032x}".format( - span.context.trace_id - ) + envelope.tags[ContextTagKeys.AI_OPERATION_ID] = "{:032x}".format(span.context.trace_id) if _ENDUSER_ID_ATTRIBUTE in span.attributes: - envelope.tags[ContextTagKeys.AI_USER_AUTH_USER_ID] = span.attributes[ - _ENDUSER_ID_ATTRIBUTE - ] + envelope.tags[ContextTagKeys.AI_USER_AUTH_USER_ID] = span.attributes[_ENDUSER_ID_ATTRIBUTE] if _ENDUSER_PSEUDO_ID_ATTRIBUTE in span.attributes: - envelope.tags[ContextTagKeys.AI_USER_ID] = span.attributes[ - _ENDUSER_PSEUDO_ID_ATTRIBUTE - ] + envelope.tags[ContextTagKeys.AI_USER_ID] = span.attributes[_ENDUSER_PSEUDO_ID_ATTRIBUTE] if _utils._is_any_synthetic_source(span.attributes): envelope.tags[ContextTagKeys.AI_OPERATION_SYNTHETIC_SOURCE] = "True" if span.parent and span.parent.span_id: - envelope.tags[ContextTagKeys.AI_OPERATION_PARENT_ID] = "{:016x}".format( - span.parent.span_id - ) + envelope.tags[ContextTagKeys.AI_OPERATION_PARENT_ID] = "{:016x}".format(span.parent.span_id) if span.kind in (SpanKind.CONSUMER, SpanKind.SERVER): envelope.name = _REQUEST_ENVELOPE_NAME data = RequestData( @@ -300,10 +274,7 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: difference = (start_time / 1000000) - enqueued_time total += difference data.measurements["timeSinceEnqueued"] = max(0, total / len(span.links)) - elif ( - HTTP_REQUEST_METHOD in span.attributes - or SpanAttributes.HTTP_METHOD in span.attributes - ): # HTTP + elif HTTP_REQUEST_METHOD in span.attributes or SpanAttributes.HTTP_METHOD in span.attributes: # HTTP path = "" user_agent = trace_utils._get_user_agent(span.attributes) if user_agent: @@ -315,8 +286,7 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: # Http specific logic for ai.operation.name if SpanAttributes.HTTP_ROUTE in span.attributes: envelope.tags[ContextTagKeys.AI_OPERATION_NAME] = "{} {}".format( - span.attributes.get(HTTP_REQUEST_METHOD) - or span.attributes.get(SpanAttributes.HTTP_METHOD), + span.attributes.get(HTTP_REQUEST_METHOD) or span.attributes.get(SpanAttributes.HTTP_METHOD), span.attributes[SpanAttributes.HTTP_ROUTE], ) elif url: @@ -326,15 +296,14 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: if not path: path = "/" envelope.tags[ContextTagKeys.AI_OPERATION_NAME] = "{} {}".format( - span.attributes.get(HTTP_REQUEST_METHOD) - or span.attributes.get(SpanAttributes.HTTP_METHOD), + span.attributes.get(HTTP_REQUEST_METHOD) or span.attributes.get(SpanAttributes.HTTP_METHOD), path, ) except Exception: # pylint: disable=broad-except pass - status_code = span.attributes.get( - HTTP_RESPONSE_STATUS_CODE - ) or span.attributes.get(SpanAttributes.HTTP_STATUS_CODE) + status_code = span.attributes.get(HTTP_RESPONSE_STATUS_CODE) or span.attributes.get( + SpanAttributes.HTTP_STATUS_CODE + ) if status_code: try: status_code = int(status_code) # type: ignore @@ -344,17 +313,12 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: status_code = 0 data.response_code = str(status_code) # Success criteria for server spans depends on span.success and the actual status code - data.success = ( - span.status.is_ok and status_code and status_code not in range(400, 500) - ) + data.success = span.status.is_ok and status_code and status_code not in range(400, 500) elif SpanAttributes.MESSAGING_SYSTEM in span.attributes: # Messaging if span.attributes.get(SpanAttributes.MESSAGING_DESTINATION): - if span.attributes.get(CLIENT_ADDRESS) or span.attributes.get( - SpanAttributes.NET_PEER_NAME - ): + if span.attributes.get(CLIENT_ADDRESS) or span.attributes.get(SpanAttributes.NET_PEER_NAME): data.source = "{}/{}".format( - span.attributes.get(CLIENT_ADDRESS) - or span.attributes.get(SpanAttributes.NET_PEER_NAME), + span.attributes.get(CLIENT_ADDRESS) or span.attributes.get(SpanAttributes.NET_PEER_NAME), span.attributes.get(SpanAttributes.MESSAGING_DESTINATION), ) elif span.attributes.get(SpanAttributes.NET_PEER_IP): @@ -363,9 +327,7 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: span.attributes.get(SpanAttributes.MESSAGING_DESTINATION), ) else: - data.source = span.attributes.get( - SpanAttributes.MESSAGING_DESTINATION, "" - ) + data.source = span.attributes.get(SpanAttributes.MESSAGING_DESTINATION, "") # Apply truncation # See https://github.com/MohanGsk/ApplicationInsights-Home/tree/master/EndpointSpecs/Schemas/Bond if envelope.tags.get(ContextTagKeys.AI_OPERATION_NAME): @@ -401,10 +363,7 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: # https://github.com/Azure/azure-sdk-for-python/issues/9256 data.type = span.attributes[_AZURE_SDK_NAMESPACE_NAME] data.target = trace_utils._get_azure_sdk_target_source(span.attributes) - elif ( - HTTP_REQUEST_METHOD in span.attributes - or SpanAttributes.HTTP_METHOD in span.attributes - ): # HTTP + elif HTTP_REQUEST_METHOD in span.attributes or SpanAttributes.HTTP_METHOD in span.attributes: # HTTP data.type = "HTTP" user_agent = trace_utils._get_user_agent(span.attributes) if user_agent: @@ -414,8 +373,7 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: # Http specific logic for ai.operation.name if SpanAttributes.HTTP_ROUTE in span.attributes: envelope.tags[ContextTagKeys.AI_OPERATION_NAME] = "{} {}".format( - span.attributes.get(HTTP_REQUEST_METHOD) - or span.attributes.get(SpanAttributes.HTTP_METHOD), + span.attributes.get(HTTP_REQUEST_METHOD) or span.attributes.get(SpanAttributes.HTTP_METHOD), span.attributes[SpanAttributes.HTTP_ROUTE], ) # data @@ -428,18 +386,16 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: # http specific logic for name if path: data.name = "{} {}".format( - span.attributes.get(HTTP_REQUEST_METHOD) - or span.attributes.get(SpanAttributes.HTTP_METHOD), + span.attributes.get(HTTP_REQUEST_METHOD) or span.attributes.get(SpanAttributes.HTTP_METHOD), path, ) envelope.tags[ContextTagKeys.AI_OPERATION_NAME] = "{} {}".format( - span.attributes.get(HTTP_REQUEST_METHOD) - or span.attributes.get(SpanAttributes.HTTP_METHOD), + span.attributes.get(HTTP_REQUEST_METHOD) or span.attributes.get(SpanAttributes.HTTP_METHOD), path, ) - status_code = span.attributes.get( - HTTP_RESPONSE_STATUS_CODE - ) or span.attributes.get(SpanAttributes.HTTP_STATUS_CODE) + status_code = span.attributes.get(HTTP_RESPONSE_STATUS_CODE) or span.attributes.get( + SpanAttributes.HTTP_STATUS_CODE + ) if status_code: try: status_code = int(status_code) # type: ignore @@ -501,9 +457,7 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem: 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: - data.type = "Queue Message | {}".format( - span.attributes[_AZURE_SDK_NAMESPACE_NAME] - ) + data.type = "Queue Message | {}".format(span.attributes[_AZURE_SDK_NAMESPACE_NAME]) target = trace_utils._get_azure_sdk_target_source(span.attributes) else: data.type = "Queue Message" @@ -517,9 +471,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_PREFIX.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 @@ -573,13 +525,9 @@ def _convert_span_events_to_envelopes(span: ReadableSpan) -> Sequence[TelemetryI for event in span.events: envelope = _utils._create_telemetry_item(event.timestamp) envelope.tags.update(_utils._populate_part_a_fields(span.resource)) - envelope.tags[ContextTagKeys.AI_OPERATION_ID] = "{:032x}".format( - span.context.trace_id - ) + envelope.tags[ContextTagKeys.AI_OPERATION_ID] = "{:032x}".format(span.context.trace_id) if span.context and span.context.span_id: - envelope.tags[ContextTagKeys.AI_OPERATION_PARENT_ID] = "{:016x}".format( - span.context.span_id - ) + envelope.tags[ContextTagKeys.AI_OPERATION_PARENT_ID] = "{:016x}".format(span.context.span_id) # sampleRate if span.attributes and _SAMPLE_RATE_KEY in span.attributes: @@ -632,11 +580,7 @@ def _check_instrumentation_span(span: ReadableSpan) -> None: # `azure-` or `azure.` is a prefix if span.instrumentation_scope.name.startswith("azure"): # spec-case for Azure AI SDKs - identified by `az.namespace` attribute - if ( - span.attributes - and span.attributes.get(_AZURE_SDK_NAMESPACE_NAME) - == "Microsoft.CognitiveServices" - ): + if span.attributes and span.attributes.get(_AZURE_SDK_NAMESPACE_NAME) == "Microsoft.CognitiveServices": _utils.add_instrumentation(_AZURE_AI_SDK_NAME) else: _utils.add_instrumentation(_AZURE_SDK_OPENTELEMETRY_NAME) @@ -645,9 +589,7 @@ def _check_instrumentation_span(span: ReadableSpan) -> None: # `opentelemetry.instrumentation.` as a prefix if span.instrumentation_scope.name.startswith("opentelemetry.instrumentation."): # The string after the prefix is the name of the instrumentation - name = span.instrumentation_scope.name.split( - "opentelemetry.instrumentation.", 1 - )[1] + name = span.instrumentation_scope.name.split("opentelemetry.instrumentation.", 1)[1] # Update the bit map to indicate instrumentation is being used _utils.add_instrumentation(name) @@ -656,10 +598,7 @@ def _is_standard_attribute(key: str) -> bool: for prefix in _STANDARD_OPENTELEMETRY_ATTRIBUTE_PREFIXES: if key.startswith(prefix): return True - return ( - key in _STANDARD_AZURE_MONITOR_ATTRIBUTES - or key in _STANDARD_OPENTELEMETRY_HTTP_ATTRIBUTES - ) + return key in _STANDARD_AZURE_MONITOR_ATTRIBUTES or key in _STANDARD_OPENTELEMETRY_HTTP_ATTRIBUTES def _get_trace_export_result(result: ExportResult) -> SpanExportResult: diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/logs/test_logs.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/logs/test_logs.py index a53e612f7b04..50ded1df9cf4 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/logs/test_logs.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/logs/test_logs.py @@ -62,9 +62,7 @@ class TestAzureLogExporter(unittest.TestCase): def setUpClass(cls): os.environ.pop("APPLICATIONINSIGHTS_STATSBEAT_DISABLED_ALL", None) os.environ.pop("APPINSIGHTS_INSTRUMENTATIONKEY", None) - os.environ["APPINSIGHTS_INSTRUMENTATIONKEY"] = ( - "1234abcd-5678-4efa-8abc-1234567890ab" - ) + os.environ["APPINSIGHTS_INSTRUMENTATIONKEY"] = "1234abcd-5678-4efa-8abc-1234567890ab" os.environ["APPLICATIONINSIGHTS_STATSBEAT_DISABLED_ALL"] = "true" cls._exporter = cls._exporter_class() span_context = SpanContext( @@ -388,9 +386,7 @@ def test_log_to_envelope_partA(self): self._log_data.resource = resource envelope = exporter._log_to_envelope(self._log_data) - self.assertEqual( - envelope.instrumentation_key, "1234abcd-5678-4efa-8abc-1234567890ab" - ) + self.assertEqual(envelope.instrumentation_key, "1234abcd-5678-4efa-8abc-1234567890ab") self.assertIsNotNone(envelope.tags) self.assertEqual( envelope.tags.get(ContextTagKeys.AI_DEVICE_ID), @@ -432,9 +428,7 @@ def test_log_to_envelope_partA(self): "{:016x}".format(span_id), ) self._log_data.resource = old_resource - self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_OPERATION_NAME), "TestOperationName" - ) + self.assertEqual(envelope.tags.get(ContextTagKeys.AI_OPERATION_NAME), "TestOperationName") def test_log_to_envelope_partA_default(self): exporter = self._exporter @@ -442,12 +436,8 @@ def test_log_to_envelope_partA_default(self): resource = Resource({"service.name": "testServiceName"}) self._log_data.resource = resource envelope = exporter._log_to_envelope(self._log_data) - self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE), "testServiceName" - ) - self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE_INSTANCE), platform.node() - ) + self.assertEqual(envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE), "testServiceName") + self.assertEqual(envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE_INSTANCE), platform.node()) self.assertEqual( envelope.tags.get(ContextTagKeys.AI_INTERNAL_NODE_NAME), envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE_INSTANCE), @@ -469,9 +459,7 @@ def test_log_to_envelope_user_fields(self): exporter = self._exporter envelope = exporter._log_to_envelope(self._log_data_user_fields) - self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_USER_AUTH_USER_ID), "test-auth" - ) + self.assertEqual(envelope.tags.get(ContextTagKeys.AI_USER_AUTH_USER_ID), "test-auth") self.assertEqual(envelope.tags.get(ContextTagKeys.AI_USER_ID), "test-user") self.assertNotIn("enduser.id", envelope.data.base_data.properties) self.assertNotIn("enduser.pseudo.id", envelope.data.base_data.properties) @@ -489,9 +477,7 @@ def test_log_to_envelope_log_empty(self): self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.Message") self.assertEqual(envelope.data.base_type, "MessageData") self.assertEqual(envelope.data.base_data.message, _DEFAULT_LOG_MESSAGE) - self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_OPERATION_NAME), "TestOperationName" - ) + self.assertEqual(envelope.tags.get(ContextTagKeys.AI_OPERATION_NAME), "TestOperationName") def test_log_to_envelope_log_empty_with_whitespaces(self): exporter = self._exporter @@ -509,15 +495,11 @@ def test_log_to_envelope_log_complex_body(self): envelope.data.base_data.message, json.dumps(self._log_data_complex_body.log_record.body), ) - self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_OPERATION_NAME), "TestOperationName" - ) + self.assertEqual(envelope.tags.get(ContextTagKeys.AI_OPERATION_NAME), "TestOperationName") def test_log_to_envelope_log_complex_body_not_serializeable(self): exporter = self._exporter - envelope = exporter._log_to_envelope( - self._log_data_complex_body_not_serializeable - ) + envelope = exporter._log_to_envelope(self._log_data_complex_body_not_serializeable) self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.Message") self.assertEqual(envelope.data.base_type, "MessageData") self.assertEqual( @@ -535,9 +517,7 @@ def test_log_to_envelope_exception_with_string_message(self): self.assertEqual(envelope.data.base_data.severity_level, 4) self.assertEqual(envelope.data.base_data.properties["test"], "attribute") self.assertEqual(len(envelope.data.base_data.exceptions), 1) - self.assertEqual( - envelope.data.base_data.exceptions[0].type_name, "ZeroDivisionError" - ) + self.assertEqual(envelope.data.base_data.exceptions[0].type_name, "ZeroDivisionError") self.assertEqual(envelope.data.base_data.exceptions[0].message, "Test message") self.assertTrue(envelope.data.base_data.exceptions[0].has_full_stack) self.assertEqual( @@ -555,12 +535,8 @@ def test_log_to_envelope_exception_with_exc_message(self): self.assertEqual(envelope.data.base_data.severity_level, 4) self.assertEqual(envelope.data.base_data.properties["test"], "attribute") self.assertEqual(len(envelope.data.base_data.exceptions), 1) - self.assertEqual( - envelope.data.base_data.exceptions[0].type_name, "ZeroDivisionError" - ) - self.assertEqual( - envelope.data.base_data.exceptions[0].message, "test exception message" - ) + self.assertEqual(envelope.data.base_data.exceptions[0].type_name, "ZeroDivisionError") + self.assertEqual(envelope.data.base_data.exceptions[0].message, "test exception message") self.assertTrue(envelope.data.base_data.exceptions[0].has_full_stack) self.assertEqual( envelope.data.base_data.exceptions[0].stack, @@ -593,9 +569,7 @@ def test_log_to_envelope_exception_with_blank_exception(self): self.assertEqual(envelope.data.base_data.properties["test"], "attribute") self.assertEqual(len(envelope.data.base_data.exceptions), 1) self.assertEqual(envelope.data.base_data.exceptions[0].type_name, "Exception") - self.assertEqual( - envelope.data.base_data.exceptions[0].message, "test exception" - ) + self.assertEqual(envelope.data.base_data.exceptions[0].message, "test exception") self.assertTrue(envelope.data.base_data.exceptions[0].has_full_stack) self.assertEqual(envelope.data.base_data.exceptions[0].stack, "") @@ -607,9 +581,7 @@ def test_log_to_envelope_event(self): self.assertEqual(envelope.time, ns_to_iso_str(record.timestamp)) self.assertEqual(envelope.data.base_type, "EventData") self.assertEqual(envelope.data.base_data.name, record.body) - self.assertEqual( - envelope.data.base_data.properties["event_key"], "event_attribute" - ) + self.assertEqual(envelope.data.base_data.properties["event_key"], "event_attribute") def test_log_to_envelope_event_complex_body(self): exporter = self._exporter @@ -619,23 +591,17 @@ def test_log_to_envelope_event_complex_body(self): self.assertEqual(envelope.time, ns_to_iso_str(record.timestamp)) self.assertEqual(envelope.data.base_type, "EventData") self.assertEqual(envelope.data.base_data.name, json.dumps(record.body)) - self.assertEqual( - envelope.data.base_data.properties["event_key"], "event_attribute" - ) + self.assertEqual(envelope.data.base_data.properties["event_key"], "event_attribute") def test_log_to_envelope_event_complex_body_not_serializeable(self): exporter = self._exporter - envelope = exporter._log_to_envelope( - self._log_data_event_complex_body_not_serializeable - ) + envelope = exporter._log_to_envelope(self._log_data_event_complex_body_not_serializeable) record = self._log_data_event_complex_body_not_serializeable.log_record self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.Event") self.assertEqual(envelope.time, ns_to_iso_str(record.timestamp)) self.assertEqual(envelope.data.base_type, "EventData") self.assertEqual(envelope.data.base_data.name, str(record.body)) - self.assertEqual( - envelope.data.base_data.properties["event_key"], "event_attribute" - ) + self.assertEqual(envelope.data.base_data.properties["event_key"], "event_attribute") def test_log_to_envelope_custom_event(self): exporter = self._exporter @@ -646,9 +612,7 @@ def test_log_to_envelope_custom_event(self): self.assertEqual(envelope.time, ns_to_iso_str(record.timestamp)) self.assertEqual(envelope.data.base_type, "EventData") self.assertEqual(envelope.data.base_data.name, "event_name") - self.assertEqual( - envelope.data.base_data.properties["event_key"], "event_attribute" - ) + self.assertEqual(envelope.data.base_data.properties["event_key"], "event_attribute") def test_log_to_envelope_timestamp(self): exporter = self._exporter @@ -694,9 +658,7 @@ def test_log_to_envelope_synthetic_source(self): ) envelope = exporter._log_to_envelope(log_data) - self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_OPERATION_SYNTHETIC_SOURCE), "True" - ) + self.assertEqual(envelope.tags.get(ContextTagKeys.AI_OPERATION_SYNTHETIC_SOURCE), "True") self.assertEqual( envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE), "testServiceNamespace.testServiceName", @@ -740,9 +702,7 @@ def test_log_to_envelope_synthetic_load_always_on(self): ) envelope = exporter._log_to_envelope(log_data) - self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_OPERATION_SYNTHETIC_SOURCE), "True" - ) + self.assertEqual(envelope.tags.get(ContextTagKeys.AI_OPERATION_SYNTHETIC_SOURCE), "True") self.assertEqual( envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE), "testServiceNamespace.testServiceName", 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 c0c35fc3e738..aa0d8002b13c 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 @@ -61,16 +61,10 @@ class TestAzureTraceExporter(unittest.TestCase): def setUpClass(cls): os.environ.pop("APPLICATIONINSIGHTS_STATSBEAT_DISABLED_ALL", None) os.environ.pop("APPINSIGHTS_INSTRUMENTATIONKEY", None) - os.environ.pop( - "APPLICATIONINSIGHTS_OPENTELEMETRY_RESOURCE_METRIC_DISABLED", None - ) - os.environ["APPINSIGHTS_INSTRUMENTATIONKEY"] = ( - "1234abcd-5678-4efa-8abc-1234567890ab" - ) + os.environ.pop("APPLICATIONINSIGHTS_OPENTELEMETRY_RESOURCE_METRIC_DISABLED", None) + os.environ["APPINSIGHTS_INSTRUMENTATIONKEY"] = "1234abcd-5678-4efa-8abc-1234567890ab" os.environ["APPLICATIONINSIGHTS_STATSBEAT_DISABLED_ALL"] = "true" - os.environ["APPLICATIONINSIGHTS_OPENTELEMETRY_RESOURCE_METRIC_DISABLED"] = ( - "true" - ) + os.environ["APPLICATIONINSIGHTS_OPENTELEMETRY_RESOURCE_METRIC_DISABLED"] = "true" cls._exporter = AzureMonitorTraceExporter( tracer_provider=trace.TracerProvider(), ) @@ -300,9 +294,7 @@ def test_span_to_envelope_partA(self): test_span.end() envelope = exporter._span_to_envelope(test_span) - self.assertEqual( - envelope.instrumentation_key, "1234abcd-5678-4efa-8abc-1234567890ab" - ) + self.assertEqual(envelope.instrumentation_key, "1234abcd-5678-4efa-8abc-1234567890ab") self.assertIsNotNone(envelope.tags) self.assertEqual( envelope.tags.get(ContextTagKeys.AI_DEVICE_ID), @@ -337,13 +329,9 @@ def test_span_to_envelope_partA(self): envelope.tags.get(ContextTagKeys.AI_OPERATION_ID), "{:032x}".format(context.trace_id), ) - self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_USER_AUTH_USER_ID), "testAuthId" - ) + self.assertEqual(envelope.tags.get(ContextTagKeys.AI_USER_AUTH_USER_ID), "testAuthId") self.assertEqual(envelope.tags.get(ContextTagKeys.AI_USER_ID), "testUserId") - self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_OPERATION_SYNTHETIC_SOURCE), "True" - ) + self.assertEqual(envelope.tags.get(ContextTagKeys.AI_OPERATION_SYNTHETIC_SOURCE), "True") self.assertEqual( envelope.tags.get(ContextTagKeys.AI_OPERATION_PARENT_ID), "{:016x}".format(context.span_id), @@ -365,12 +353,8 @@ def test_span_to_envelope_partA_default(self): test_span.start() test_span.end() envelope = exporter._span_to_envelope(test_span) - self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE), "testServiceName" - ) - self.assertEqual( - envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE_INSTANCE), platform.node() - ) + self.assertEqual(envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE), "testServiceName") + self.assertEqual(envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE_INSTANCE), platform.node()) self.assertEqual( envelope.tags.get(ContextTagKeys.AI_INTERNAL_NODE_NAME), envelope.tags.get(ContextTagKeys.AI_CLOUD_ROLE_INSTANCE), @@ -403,22 +387,16 @@ def test_span_to_envelope_client_http(self): span._status = Status(status_code=StatusCode.OK) envelope = exporter._span_to_envelope(span) - self.assertEqual( - envelope.name, "Microsoft.ApplicationInsights.RemoteDependency" - ) + self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.RemoteDependency") self.assertEqual(envelope.time, "2019-12-04T21:18:36.027613Z") self.assertEqual(envelope.data.base_data.name, "GET /wiki/Rabbit") self.assertEqual(envelope.data.base_data.id, "a6f5d48acb4d31d9") self.assertEqual(envelope.data.base_data.duration, "0.00:00:01.001") self.assertTrue(envelope.data.base_data.success) - self.assertEqual( - envelope.data.base_data.data, "https://www.wikipedia.org/wiki/Rabbit" - ) + self.assertEqual(envelope.data.base_data.data, "https://www.wikipedia.org/wiki/Rabbit") self.assertEqual(envelope.data.base_type, "RemoteDependencyData") - self.assertEqual( - envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "GET /wiki/Rabbit" - ) + self.assertEqual(envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "GET /wiki/Rabbit") self.assertEqual(envelope.data.base_data.type, "HTTP") self.assertEqual(envelope.data.base_data.target, "service") self.assertEqual( @@ -627,9 +605,7 @@ def test_span_to_envelope_client_db(self): span._status = Status(status_code=StatusCode.OK) envelope = exporter._span_to_envelope(span) - self.assertEqual( - envelope.name, "Microsoft.ApplicationInsights.RemoteDependency" - ) + self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.RemoteDependency") self.assertEqual(envelope.time, "2019-12-04T21:18:36.027613Z") self.assertEqual(envelope.data.base_data.name, "test") self.assertEqual(envelope.data.base_data.id, "a6f5d48acb4d31d9") @@ -748,9 +724,7 @@ def test_span_to_envelope_client_rpc(self): span._status = Status(status_code=StatusCode.OK) envelope = exporter._span_to_envelope(span) - self.assertEqual( - envelope.name, "Microsoft.ApplicationInsights.RemoteDependency" - ) + self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.RemoteDependency") self.assertEqual(envelope.time, "2019-12-04T21:18:36.027613Z") self.assertEqual(envelope.data.base_data.name, "test") self.assertEqual(envelope.data.base_data.id, "a6f5d48acb4d31d9") @@ -796,9 +770,7 @@ def test_span_to_envelope_client_messaging(self): span._status = Status(status_code=StatusCode.OK) envelope = exporter._span_to_envelope(span) - self.assertEqual( - envelope.name, "Microsoft.ApplicationInsights.RemoteDependency" - ) + self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.RemoteDependency") self.assertEqual(envelope.time, "2019-12-04T21:18:36.027613Z") self.assertEqual(envelope.data.base_data.name, "test") self.assertEqual(envelope.data.base_data.id, "a6f5d48acb4d31d9") @@ -842,9 +814,7 @@ def test_span_to_envelope_client_gen_ai(self): span._status = Status(status_code=StatusCode.UNSET) envelope = exporter._span_to_envelope(span) - self.assertEqual( - envelope.name, "Microsoft.ApplicationInsights.RemoteDependency" - ) + self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.RemoteDependency") self.assertEqual(envelope.time, "2019-12-04T21:18:36.027613Z") self.assertEqual(envelope.data.base_data.name, "test") self.assertEqual(envelope.data.base_data.id, "a6f5d48acb4d31d9") @@ -907,9 +877,7 @@ def test_span_to_envelope_client_multiple_types_with_gen_ai(self): envelope = exporter._span_to_envelope(span) self.assertEqual(envelope.data.base_data.type, "GenAI | az.ai.inference") - self.assertEqual( - envelope.data.base_data.target, "test_address/test_destination" - ) + self.assertEqual(envelope.data.base_data.target, "test_address/test_destination") def test_span_to_envelope_client_azure(self): exporter = self._exporter @@ -936,9 +904,7 @@ def test_span_to_envelope_client_azure(self): span._status = Status(status_code=StatusCode.OK) envelope = exporter._span_to_envelope(span) - self.assertEqual( - envelope.name, "Microsoft.ApplicationInsights.RemoteDependency" - ) + self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.RemoteDependency") self.assertEqual(envelope.time, "2019-12-04T21:18:36.027613Z") self.assertEqual(envelope.data.base_data.name, "test") self.assertEqual(envelope.data.base_data.id, "a6f5d48acb4d31d9") @@ -949,9 +915,7 @@ def test_span_to_envelope_client_azure(self): self.assertEqual(envelope.data.base_type, "RemoteDependencyData") self.assertEqual(envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "test") self.assertEqual(envelope.data.base_data.type, "Microsoft.EventHub") - self.assertEqual( - envelope.data.base_data.target, "test_address/test_destination" - ) + self.assertEqual(envelope.data.base_data.target, "test_address/test_destination") self.assertEqual(len(envelope.data.base_data.properties), 2) # target @@ -985,9 +949,7 @@ def test_span_to_envelope_producer_messaging(self): span._status = Status(status_code=StatusCode.OK) envelope = exporter._span_to_envelope(span) - self.assertEqual( - envelope.name, "Microsoft.ApplicationInsights.RemoteDependency" - ) + self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.RemoteDependency") self.assertEqual(envelope.time, "2019-12-04T21:18:36.027613Z") self.assertEqual(envelope.data.base_data.name, "test") self.assertEqual(envelope.data.base_data.id, "a6f5d48acb4d31d9") @@ -1016,9 +978,7 @@ def test_span_to_envelope_producer_messaging(self): "message_bus.destination": "/myeventhub", } envelope = exporter._span_to_envelope(span) - self.assertEqual( - envelope.data.base_data.type, "Queue Message | Microsoft.EventHub" - ) + self.assertEqual(envelope.data.base_data.type, "Queue Message | Microsoft.EventHub") self.assertEqual(envelope.data.base_data.target, "Test_peer//myeventhub") self.assertEqual(len(envelope.data.base_data.properties), 2) @@ -1045,9 +1005,7 @@ def test_span_to_envelope_internal(self): span._status = Status(status_code=StatusCode.OK) envelope = exporter._span_to_envelope(span) - self.assertEqual( - envelope.name, "Microsoft.ApplicationInsights.RemoteDependency" - ) + self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.RemoteDependency") self.assertEqual(envelope.time, "2019-12-04T21:18:36.027613Z") self.assertEqual(envelope.data.base_data.name, "test") self.assertEqual(envelope.data.base_data.id, "a6f5d48acb4d31d9") @@ -1123,9 +1081,7 @@ def test_span_envelope_request_azure(self): self.assertEqual(envelope.data.base_data.response_code, "0") self.assertTrue(envelope.data.base_data.success) self.assertEqual(envelope.data.base_data.source, "Test_peer//myeventhub") - self.assertEqual( - envelope.data.base_data.measurements["timeSinceEnqueued"], 2000000000000 - ) + self.assertEqual(envelope.data.base_data.measurements["timeSinceEnqueued"], 2000000000000) self.assertEqual(len(envelope.data.base_data.properties), 3) # enqueued time @@ -1206,13 +1162,9 @@ def test_span_envelope_server_http(self): self.assertEqual(envelope.data.base_data.duration, "0.00:00:01.001") self.assertEqual(envelope.data.base_data.response_code, "200") self.assertTrue(envelope.data.base_data.success) - self.assertEqual( - envelope.data.base_data.url, "https://www.wikipedia.org/wiki/Rabbit" - ) + self.assertEqual(envelope.data.base_data.url, "https://www.wikipedia.org/wiki/Rabbit") - self.assertEqual( - envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "GET /wiki/Rabbit" - ) + self.assertEqual(envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "GET /wiki/Rabbit") self.assertEqual(envelope.tags["ai.user.userAgent"], "agent") self.assertEqual(envelope.tags[ContextTagKeys.AI_LOCATION_IP], "client_ip") self.assertEqual(len(envelope.data.base_data.properties), 0) @@ -1290,9 +1242,7 @@ def test_span_envelope_server_http(self): "url.full": "https://www.example.org:80/path?query", } envelope = exporter._span_to_envelope(span) - self.assertEqual( - envelope.data.base_data.url, "https://www.example.org:80/path?query" - ) + self.assertEqual(envelope.data.base_data.url, "https://www.example.org:80/path?query") span._attributes = { "http.request.method": "GET", @@ -1303,9 +1253,7 @@ def test_span_envelope_server_http(self): "server.port": "80", } envelope = exporter._span_to_envelope(span) - self.assertEqual( - envelope.data.base_data.url, "https://www.example.org:80/path?query" - ) + self.assertEqual(envelope.data.base_data.url, "https://www.example.org:80/path?query") span._attributes = { "http.method": "GET", @@ -1333,9 +1281,7 @@ def test_span_envelope_server_http(self): "http.route": "/wiki/Rabbit/test", } envelope = exporter._span_to_envelope(span) - self.assertEqual( - envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "GET /wiki/Rabbit/test" - ) + self.assertEqual(envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "GET /wiki/Rabbit/test") self.assertEqual(envelope.data.base_data.name, "GET /wiki/Rabbit/test") ## Stable http semconv @@ -1344,9 +1290,7 @@ def test_span_envelope_server_http(self): "http.route": "/wiki/Rabbit/test", } envelope = exporter._span_to_envelope(span) - self.assertEqual( - envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "GET /wiki/Rabbit/test" - ) + self.assertEqual(envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "GET /wiki/Rabbit/test") self.assertEqual(envelope.data.base_data.name, "GET /wiki/Rabbit/test") span._attributes = { @@ -1354,9 +1298,7 @@ def test_span_envelope_server_http(self): "http.url": "https://www.wikipedia.org/wiki/Rabbit/test", } envelope = exporter._span_to_envelope(span) - self.assertEqual( - envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "GET /wiki/Rabbit/test" - ) + self.assertEqual(envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "GET /wiki/Rabbit/test") self.assertEqual(envelope.data.base_data.name, "GET /wiki/Rabbit/test") span._attributes = { @@ -1364,9 +1306,7 @@ def test_span_envelope_server_http(self): "url.full": "https://www.wikipedia.org/wiki/Rabbit/test", } envelope = exporter._span_to_envelope(span) - self.assertEqual( - envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "GET /wiki/Rabbit/test" - ) + self.assertEqual(envelope.tags[ContextTagKeys.AI_OPERATION_NAME], "GET /wiki/Rabbit/test") self.assertEqual(envelope.data.base_data.name, "GET /wiki/Rabbit/test") # Default is span name @@ -1578,9 +1518,7 @@ def test_span_to_envelope_properties_std_metrics(self): "http.status_code": 200, }, kind=SpanKind.CLIENT, - instrumentation_scope=InstrumentationScope( - "opentelemetry.instrumentation.requests" - ), + instrumentation_scope=InstrumentationScope("opentelemetry.instrumentation.requests"), ) span._status = Status(status_code=StatusCode.OK) span.start(start_time=start_time) @@ -1625,9 +1563,7 @@ def test_span_events_to_envelopes_exception(self): self.assertEqual(len(envelopes), 1) envelope = envelopes[0] self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.Exception") - self.assertEqual( - envelope.instrumentation_key, "1234abcd-5678-4efa-8abc-1234567890ab" - ) + self.assertEqual(envelope.instrumentation_key, "1234abcd-5678-4efa-8abc-1234567890ab") self.assertIsNotNone(envelope.tags) self.assertEqual( envelope.tags.get(ContextTagKeys.AI_DEVICE_ID), @@ -1656,12 +1592,8 @@ def test_span_events_to_envelopes_exception(self): self.assertEqual(envelope.time, "2019-12-04T21:18:36.027613Z") self.assertEqual(len(envelope.data.base_data.properties), 0) self.assertEqual(len(envelope.data.base_data.exceptions), 1) - self.assertEqual( - envelope.data.base_data.exceptions[0].type_name, "ZeroDivisionError" - ) - self.assertEqual( - envelope.data.base_data.exceptions[0].message, "zero division error" - ) + self.assertEqual(envelope.data.base_data.exceptions[0].type_name, "ZeroDivisionError") + self.assertEqual(envelope.data.base_data.exceptions[0].message, "zero division error") self.assertEqual(envelope.data.base_data.exceptions[0].has_full_stack, True) self.assertEqual( envelope.data.base_data.exceptions[0].stack, @@ -1699,9 +1631,7 @@ def test_span_events_to_envelopes_message(self): self.assertEqual(len(envelopes), 1) envelope = envelopes[0] self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.Message") - self.assertEqual( - envelope.instrumentation_key, "1234abcd-5678-4efa-8abc-1234567890ab" - ) + self.assertEqual(envelope.instrumentation_key, "1234abcd-5678-4efa-8abc-1234567890ab") self.assertIsNotNone(envelope.tags) self.assertEqual( envelope.tags.get(ContextTagKeys.AI_DEVICE_ID), @@ -1768,9 +1698,7 @@ def test_span_events_to_envelopes_sample_rate(self): envelope = envelopes[0] self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.Message") self.assertEqual(envelope.sample_rate, 50) - self.assertEqual( - envelope.instrumentation_key, "1234abcd-5678-4efa-8abc-1234567890ab" - ) + self.assertEqual(envelope.instrumentation_key, "1234abcd-5678-4efa-8abc-1234567890ab") self.assertIsNotNone(envelope.tags) self.assertEqual( envelope.tags.get(ContextTagKeys.AI_DEVICE_ID), @@ -1802,9 +1730,7 @@ def test_span_events_to_envelopes_sample_rate(self): self.assertEqual(envelope.data.base_data.message, "test event") self.assertEqual(envelope.data.base_type, "MessageData") - @mock.patch( - "azure.monitor.opentelemetry.exporter.export.trace._exporter.get_tracer_provider" - ) + @mock.patch("azure.monitor.opentelemetry.exporter.export.trace._exporter.get_tracer_provider") def test_export_otel_resource_metric(self, mock_get_tracer_provider): del os.environ["APPLICATIONINSIGHTS_OPENTELEMETRY_RESOURCE_METRIC_DISABLED"] mock_tracer_provider = mock.Mock() @@ -1896,9 +1822,7 @@ def test_check_instrumentation_span(self): span = mock.Mock() span.attributes = {} span.instrumentation_scope.name = "opentelemetry.instrumentation.test" - with mock.patch( - "azure.monitor.opentelemetry.exporter._utils.add_instrumentation" - ) as add: + with mock.patch("azure.monitor.opentelemetry.exporter._utils.add_instrumentation") as add: _check_instrumentation_span(span) add.assert_called_once_with("test") @@ -1906,9 +1830,7 @@ def test_check_instrumentation_span_not_instrumentation(self): span = mock.Mock() span.attributes = {} span.instrumentation_scope.name = "__main__" - with mock.patch( - "azure.monitor.opentelemetry.exporter._utils.add_instrumentation" - ) as add: + with mock.patch("azure.monitor.opentelemetry.exporter._utils.add_instrumentation") as add: _check_instrumentation_span(span) add.assert_not_called() @@ -1917,74 +1839,48 @@ def test_check_instrumentation_span_azure_sdk(self): span.attributes = {} span.instrumentation_scope.name = "azure.foo.bar.__init__" - with mock.patch( - "azure.monitor.opentelemetry.exporter._utils.add_instrumentation" - ) as add: + with mock.patch("azure.monitor.opentelemetry.exporter._utils.add_instrumentation") as add: _check_instrumentation_span(span) add.assert_called_once_with(_AZURE_SDK_OPENTELEMETRY_NAME) @mock.patch("opentelemetry.trace.get_tracer_provider") - def test_check_instrumentation_span_azure_sdk_otel_span( - self, mock_get_tracer_provider - ): + def test_check_instrumentation_span_azure_sdk_otel_span(self, mock_get_tracer_provider): mock_get_tracer_provider.return_value = self.get_tracer_provider() with OpenTelemetrySpan() as azure_sdk_span: - with mock.patch( - "azure.monitor.opentelemetry.exporter._utils.add_instrumentation" - ) as add: + with mock.patch("azure.monitor.opentelemetry.exporter._utils.add_instrumentation") as add: _check_instrumentation_span(azure_sdk_span.span_instance) add.assert_called_once_with(_AZURE_SDK_OPENTELEMETRY_NAME) - with mock.patch( - "azure.monitor.opentelemetry.exporter._utils.add_instrumentation" - ) as add: - azure_sdk_span.add_attribute( - _AZURE_SDK_NAMESPACE_NAME, "Microsoft.ServiceBus" - ) + with mock.patch("azure.monitor.opentelemetry.exporter._utils.add_instrumentation") as add: + azure_sdk_span.add_attribute(_AZURE_SDK_NAMESPACE_NAME, "Microsoft.ServiceBus") _check_instrumentation_span(azure_sdk_span.span_instance) add.assert_called_once_with(_AZURE_SDK_OPENTELEMETRY_NAME) - with mock.patch( - "azure.monitor.opentelemetry.exporter._utils.add_instrumentation" - ) as add: - azure_sdk_span.add_attribute( - _AZURE_SDK_NAMESPACE_NAME, "Microsoft.CognitiveServices" - ) + with mock.patch("azure.monitor.opentelemetry.exporter._utils.add_instrumentation") as add: + azure_sdk_span.add_attribute(_AZURE_SDK_NAMESPACE_NAME, "Microsoft.CognitiveServices") _check_instrumentation_span(azure_sdk_span.span_instance) add.assert_called_once_with(_AZURE_AI_SDK_NAME) @mock.patch("opentelemetry.trace.get_tracer_provider") - def test_check_instrumentation_span_azure_sdk_span_impl( - self, mock_get_tracer_provider - ): + def test_check_instrumentation_span_azure_sdk_span_impl(self, mock_get_tracer_provider): mock_get_tracer_provider.return_value = self.get_tracer_provider() settings.tracing_implementation = "opentelemetry" try: azure_sdk_span = settings.tracing_implementation()(name="test") - with mock.patch( - "azure.monitor.opentelemetry.exporter._utils.add_instrumentation" - ) as add: + with mock.patch("azure.monitor.opentelemetry.exporter._utils.add_instrumentation") as add: _check_instrumentation_span(azure_sdk_span.span_instance) add.assert_called_once_with(_AZURE_SDK_OPENTELEMETRY_NAME) - with mock.patch( - "azure.monitor.opentelemetry.exporter._utils.add_instrumentation" - ) as add: - azure_sdk_span.add_attribute( - _AZURE_SDK_NAMESPACE_NAME, "Microsoft.ServiceBus" - ) + with mock.patch("azure.monitor.opentelemetry.exporter._utils.add_instrumentation") as add: + azure_sdk_span.add_attribute(_AZURE_SDK_NAMESPACE_NAME, "Microsoft.ServiceBus") _check_instrumentation_span(azure_sdk_span.span_instance) add.assert_called_once_with(_AZURE_SDK_OPENTELEMETRY_NAME) - with mock.patch( - "azure.monitor.opentelemetry.exporter._utils.add_instrumentation" - ) as add: - azure_sdk_span.add_attribute( - _AZURE_SDK_NAMESPACE_NAME, "Microsoft.CognitiveServices" - ) + with mock.patch("azure.monitor.opentelemetry.exporter._utils.add_instrumentation") as add: + azure_sdk_span.add_attribute(_AZURE_SDK_NAMESPACE_NAME, "Microsoft.CognitiveServices") _check_instrumentation_span(azure_sdk_span.span_instance) add.assert_called_once_with(_AZURE_AI_SDK_NAME) @@ -1992,9 +1888,7 @@ def test_check_instrumentation_span_azure_sdk_span_impl( settings.tracing_implementation = None @mock.patch("opentelemetry.trace.get_tracer_provider") - def test_check_instrumentation_span_azure_sdk_get_tracer( - self, mock_get_tracer_provider - ): + def test_check_instrumentation_span_azure_sdk_get_tracer(self, mock_get_tracer_provider): mock_get_tracer_provider.return_value = self.get_tracer_provider() if not get_azure_sdk_tracer: @@ -2003,35 +1897,23 @@ def test_check_instrumentation_span_azure_sdk_get_tracer( azure_sdk_tracer = get_azure_sdk_tracer(library_name="azure-foo-bar") azure_sdk_span = azure_sdk_tracer.start_span(name="test") - with mock.patch( - "azure.monitor.opentelemetry.exporter._utils.add_instrumentation" - ) as add: + with mock.patch("azure.monitor.opentelemetry.exporter._utils.add_instrumentation") as add: _check_instrumentation_span(azure_sdk_span) add.assert_called_once_with(_AZURE_SDK_OPENTELEMETRY_NAME) - with mock.patch( - "azure.monitor.opentelemetry.exporter._utils.add_instrumentation" - ) as add: - azure_sdk_span.set_attribute( - _AZURE_SDK_NAMESPACE_NAME, "Microsoft.ServiceBus" - ) + with mock.patch("azure.monitor.opentelemetry.exporter._utils.add_instrumentation") as add: + azure_sdk_span.set_attribute(_AZURE_SDK_NAMESPACE_NAME, "Microsoft.ServiceBus") _check_instrumentation_span(azure_sdk_span) add.assert_called_once_with(_AZURE_SDK_OPENTELEMETRY_NAME) - with mock.patch( - "azure.monitor.opentelemetry.exporter._utils.add_instrumentation" - ) as add: - azure_sdk_span.set_attribute( - _AZURE_SDK_NAMESPACE_NAME, "Microsoft.CognitiveServices" - ) + with mock.patch("azure.monitor.opentelemetry.exporter._utils.add_instrumentation") as add: + azure_sdk_span.set_attribute(_AZURE_SDK_NAMESPACE_NAME, "Microsoft.CognitiveServices") _check_instrumentation_span(azure_sdk_span) add.assert_called_once_with(_AZURE_AI_SDK_NAME) other_tracer = self.get_tracer_provider().get_tracer("not-azure-foo-bar") other_span = other_tracer.start_span(name="test") - with mock.patch( - "azure.monitor.opentelemetry.exporter._utils.add_instrumentation" - ) as add: + with mock.patch("azure.monitor.opentelemetry.exporter._utils.add_instrumentation") as add: _check_instrumentation_span(other_span) add.assert_not_called()