def test_transmission_206_500(self): test_envelope = TelemetryItem(name="testEnvelope", time=datetime.now()) custom_envelopes_to_export = [ TelemetryItem(name="Test", time=datetime.now()), TelemetryItem(name="Test", time=datetime.now()), test_envelope ] with mock.patch("requests.Session.request") as post: post.return_value = MockResponse( 206, json.dumps({ "itemsReceived": 5, "itemsAccepted": 3, "errors": [ { "index": 0, "statusCode": 400, "message": "" }, { "index": 2, "statusCode": 500, "message": "Internal Server Error", }, ], }), ) result = self._base._transmit(custom_envelopes_to_export) self.assertEqual(result, ExportResult.FAILED_RETRYABLE) self._base.storage.get() self.assertEqual(self._base.storage.get().get()[0]["name"], "testEnvelope")
def setUpClass(cls): os.environ[ "APPINSIGHTS_INSTRUMENTATIONKEY"] = "1234abcd-5678-4efa-8abc-1234567890ab" cls._base = BaseExporter() cls._envelopes_to_export = [ TelemetryItem(name="Test", time=datetime.now()) ]
def _transmit_from_storage(self) -> None: for blob in self.storage.gets(): # give a few more seconds for blob lease operation # to reduce the chance of race (for perf consideration) if blob.lease(self._timeout + 5): envelopes = [TelemetryItem(**x) for x in blob.get()] result = self._transmit(list(envelopes)) if result == ExportResult.FAILED_RETRYABLE: blob.lease(1) else: blob.delete()
def _convert_log_to_envelope(log_data: LogData) -> TelemetryItem: log_record = log_data.log_record envelope = TelemetryItem( name="", instrumentation_key="", tags=dict(_utils.azure_monitor_context), time=ns_to_iso_str(log_record.timestamp), ) envelope.tags.update(_utils._populate_part_a_fields(log_record.resource)) envelope.tags["ai.operation.id"] = "{:032x}".format( log_record.trace_id or _DEFAULT_TRACE_ID ) envelope.tags["ai.operation.parentId"] = "{:016x}".format( log_record.span_id or _DEFAULT_SPAN_ID ) properties = { k: v for k, v in log_record.attributes.items() if k not in _EXCEPTION_ATTRS } exc_type = log_record.attributes.get(SpanAttributes.EXCEPTION_TYPE) exc_message = log_record.attributes.get(SpanAttributes.EXCEPTION_MESSAGE) # pylint: disable=line-too-long stack_trace = log_record.attributes.get(SpanAttributes.EXCEPTION_STACKTRACE) severity_level = _get_severity_level(log_record.severity_number) # Exception telemetry if exc_type is not None or exc_message is not None: envelope.name = "Microsoft.ApplicationInsights.Exception" has_full_stack = stack_trace is not None exc_details = TelemetryExceptionDetails( type_name=exc_type, message=exc_message, has_full_stack=has_full_stack, stack=stack_trace, ) data = TelemetryExceptionData( severity_level=severity_level, properties=properties, exceptions=[exc_details], ) # pylint: disable=line-too-long envelope.data = MonitorBase(base_data=data, base_type="TelemetryExceptionData") else: # Message telemetry envelope.name = "Microsoft.ApplicationInsights.Message" # pylint: disable=line-too-long # Severity number: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/data-model.md#field-severitynumber data = MessageData( message=log_record.body, severity_level=severity_level, properties=properties, ) envelope.data = MonitorBase(base_data=data, base_type="MessageData") return envelope
def test_transmission_206_bogus(self): envelopes_to_export = map(lambda x: x.as_dict(), tuple( [TelemetryItem(name="testEnvelope", time=datetime.now())])) self._base.storage.put(envelopes_to_export) with mock.patch("requests.Session.request") as post: post.return_value = MockResponse( 206, json.dumps( { "itemsReceived": 5, "itemsAccepted": 3, "errors": [{"foo": 0, "bar": 1}], } ), ) result = self._base._transmit(self._envelopes_to_export) self.assertEqual(result, ExportResult.FAILED_NOT_RETRYABLE)
def _convert_span_to_envelope(span: Span) -> TelemetryItem: envelope = TelemetryItem( name="", instrumentation_key="", tags=dict(_utils.azure_monitor_context), time=ns_to_iso_str(span.start_time), ) if span.resource and span.resource.attributes: service_name = span.resource.attributes.get(ResourceAttributes.SERVICE_NAME) service_namespace = span.resource.attributes.get(ResourceAttributes.SERVICE_NAMESPACE) service_instance_id = span.resource.attributes.get(ResourceAttributes.SERVICE_INSTANCE_ID) if service_name: if service_namespace: envelope.tags["ai.cloud.role"] = service_namespace + \ "." + service_name else: envelope.tags["ai.cloud.role"] = service_name if service_instance_id: envelope.tags["ai.cloud.roleInstance"] = service_instance_id else: envelope.tags["ai.cloud.roleInstance"] = platform.node() # hostname default envelope.tags["ai.internal.nodeName"] = envelope.tags["ai.cloud.roleInstance"] envelope.tags["ai.operation.id"] = "{:032x}".format(span.context.trace_id) if SpanAttributes.ENDUSER_ID in span.attributes: envelope.tags["ai.user.id"] = span.attributes[SpanAttributes.ENDUSER_ID] if span.parent and span.parent.span_id: envelope.tags["ai.operation.parentId"] = "{:016x}".format( span.parent.span_id ) # pylint: disable=too-many-nested-blocks if span.kind in (SpanKind.CONSUMER, SpanKind.SERVER): envelope.name = "Microsoft.ApplicationInsights.Request" data = RequestData( name=span.name, id="{:016x}".format(span.context.span_id), duration=_utils.ns_to_duration(span.end_time - span.start_time), response_code="0", success=span.status.is_ok, properties={}, ) envelope.data = MonitorBase(base_data=data, base_type="RequestData") if SpanAttributes.HTTP_METHOD in span.attributes: # HTTP url = "" path = "" if SpanAttributes.HTTP_USER_AGENT in span.attributes: # TODO: Not exposed in Swagger, need to update def envelope.tags["ai.user.userAgent"] = span.attributes[SpanAttributes.HTTP_USER_AGENT] if SpanAttributes.HTTP_CLIENT_IP in span.attributes: envelope.tags["ai.location.ip"] = span.attributes[SpanAttributes.HTTP_CLIENT_IP] elif SpanAttributes.NET_PEER_IP in span.attributes: envelope.tags["ai.location.ip"] = span.attributes[SpanAttributes.NET_PEER_IP] # url if SpanAttributes.HTTP_URL in span.attributes: url = span.attributes[SpanAttributes.HTTP_URL] elif SpanAttributes.HTTP_SCHEME in span.attributes and SpanAttributes.HTTP_TARGET in span.attributes: scheme = span.attributes[SpanAttributes.HTTP_SCHEME] http_target = span.attributes[SpanAttributes.HTTP_TARGET] if SpanAttributes.HTTP_HOST in span.attributes: url = "{}://{}{}".format( scheme, span.attributes[SpanAttributes.HTTP_HOST], http_target, ) elif SpanAttributes.NET_HOST_PORT in span.attributes: host_port = span.attributes[SpanAttributes.NET_HOST_PORT] if SpanAttributes.HTTP_SERVER_NAME in span.attributes: server_name = span.attributes[SpanAttributes.HTTP_SERVER_NAME] url = "{}://{}:{}{}".format( scheme, server_name, host_port, http_target, ) elif SpanAttributes.NET_HOST_NAME in span.attributes: host_name = span.attributes[SpanAttributes.NET_HOST_NAME] url = "{}://{}:{}{}".format( scheme, host_name, host_port, http_target, ) data.url = url # Http specific logic for ai.operation.name if SpanAttributes.HTTP_ROUTE in span.attributes: envelope.tags["ai.operation.name"] = "{} {}".format( span.attributes[SpanAttributes.HTTP_METHOD], span.attributes[SpanAttributes.HTTP_ROUTE], ) elif url: try: parse_url = urlparse(url) path = parse_url.path if not path: path = "/" envelope.tags["ai.operation.name"] = "{} {}".format( span.attributes[SpanAttributes.HTTP_METHOD], path, ) except Exception: # pylint: disable=broad-except pass else: envelope.tags["ai.operation.name"] = span.name if SpanAttributes.HTTP_STATUS_CODE in span.attributes: status_code = span.attributes[SpanAttributes.HTTP_STATUS_CODE] data.response_code = str(status_code) elif SpanAttributes.MESSAGING_SYSTEM in span.attributes: # Messaging envelope.tags["ai.operation.name"] = span.name if SpanAttributes.NET_PEER_IP in span.attributes: envelope.tags["ai.location.ip"] = span.attributes[SpanAttributes.NET_PEER_IP] if SpanAttributes.MESSAGING_DESTINATION in span.attributes: if SpanAttributes.NET_PEER_NAME in span.attributes: data.properties["source"] = "{}/{}".format( span.attributes[SpanAttributes.NET_PEER_NAME], span.attributes[SpanAttributes.MESSAGING_DESTINATION], ) elif SpanAttributes.NET_PEER_IP in span.attributes: data.properties["source"] = "{}/{}".format( span.attributes[SpanAttributes.NET_PEER_IP], span.attributes[SpanAttributes.MESSAGING_DESTINATION], ) else: data.properties["source"] = span.attributes[SpanAttributes.MESSAGING_DESTINATION] else: # Other envelope.tags["ai.operation.name"] = span.name if SpanAttributes.NET_PEER_IP in span.attributes: envelope.tags["ai.location.ip"] = span.attributes[SpanAttributes.NET_PEER_IP] # Apply truncation if data.url: data.url = data.url[:2048] # Breeze max length if data.response_code: data.response_code = data.response_code[:1024] # Breeze max length if envelope.tags["ai.operation.name"]: data.name = envelope.tags["ai.operation.name"][:1024] # Breeze max length else: # INTERNAL, CLIENT, PRODUCER envelope.name = "Microsoft.ApplicationInsights.RemoteDependency" # TODO: ai.operation.name for non-server spans data = RemoteDependencyData( name=span.name, id="{:016x}".format(span.context.span_id), result_code="0", duration=_utils.ns_to_duration(span.end_time - span.start_time), success=span.status.is_ok, properties={}, ) envelope.data = MonitorBase( base_data=data, base_type="RemoteDependencyData" ) target = None if SpanAttributes.PEER_SERVICE in span.attributes: target = span.attributes[SpanAttributes.PEER_SERVICE] else: if SpanAttributes.NET_PEER_NAME in span.attributes: target = span.attributes[SpanAttributes.NET_PEER_NAME] elif SpanAttributes.NET_PEER_IP in span.attributes: target = span.attributes[SpanAttributes.NET_PEER_IP] if SpanAttributes.NET_PEER_PORT in span.attributes: port = span.attributes[SpanAttributes.NET_PEER_PORT] # TODO: check default port for rpc # This logic assumes default ports never conflict across dependency types if port != _get_default_port_http(span.attributes.get(SpanAttributes.HTTP_SCHEME)) and \ port != _get_default_port_db(span.attributes.get(SpanAttributes.DB_SYSTEM)): target = "{}:{}".format(target, port) if span.kind is SpanKind.CLIENT: if SpanAttributes.HTTP_METHOD in span.attributes: # HTTP data.type = "HTTP" if SpanAttributes.HTTP_USER_AGENT in span.attributes: # TODO: Not exposed in Swagger, need to update def envelope.tags["ai.user.userAgent"] = span.attributes[SpanAttributes.HTTP_USER_AGENT] scheme = span.attributes.get(SpanAttributes.HTTP_SCHEME) # url url = "" if SpanAttributes.HTTP_URL in span.attributes: url = span.attributes[SpanAttributes.HTTP_URL] elif scheme and SpanAttributes.HTTP_TARGET in span.attributes: http_target = span.attributes[SpanAttributes.HTTP_TARGET] if SpanAttributes.HTTP_HOST in span.attributes: url = "{}://{}{}".format( scheme, span.attributes[SpanAttributes.HTTP_HOST], http_target, ) elif SpanAttributes.NET_PEER_PORT in span.attributes: peer_port = span.attributes[SpanAttributes.NET_PEER_PORT] if SpanAttributes.NET_PEER_NAME in span.attributes: peer_name = span.attributes[SpanAttributes.NET_PEER_NAME] url = "{}://{}:{}{}".format( scheme, peer_name, peer_port, http_target, ) elif SpanAttributes.NET_PEER_IP in span.attributes: peer_ip = span.attributes[SpanAttributes.NET_PEER_IP] url = "{}://{}:{}{}".format( scheme, peer_ip, peer_port, http_target, ) target_from_url = "" path = "" if url: try: parse_url = urlparse(url) path = parse_url.path if not path: path = "/" if parse_url.port == _get_default_port_http(scheme): target_from_url = parse_url.hostname else: target_from_url = parse_url.netloc except Exception: # pylint: disable=broad-except pass # http specific logic for name if path: data.name = "{} {}".format( span.attributes[SpanAttributes.HTTP_METHOD], path, ) # http specific logic for target if SpanAttributes.PEER_SERVICE not in span.attributes: if SpanAttributes.HTTP_HOST in span.attributes: host = span.attributes[SpanAttributes.HTTP_HOST] try: # urlparse insists on absolute URLs starting with "//" # This logic assumes host does not include a "//" host_name = urlparse("//" + host) if host_name.port == _get_default_port_http(scheme): target = host_name.hostname else: target = host except Exception: # pylint: disable=broad-except logger.warning("Error while parsing hostname.") elif target_from_url: target = target_from_url # data is url data.data = url if SpanAttributes.HTTP_STATUS_CODE in span.attributes: status_code = span.attributes[SpanAttributes.HTTP_STATUS_CODE] data.result_code = str(status_code) elif SpanAttributes.DB_SYSTEM in span.attributes: # Database db_system = span.attributes[SpanAttributes.DB_SYSTEM] if not _is_sql_db(db_system): data.type = db_system else: data.type = "SQL" # data is the full statement or operation if SpanAttributes.DB_STATEMENT in span.attributes: data.data = span.attributes[SpanAttributes.DB_STATEMENT] elif SpanAttributes.DB_OPERATION in span.attributes: data.data = span.attributes[SpanAttributes.DB_OPERATION] # db specific logic for target if SpanAttributes.DB_NAME in span.attributes: db_name = span.attributes[SpanAttributes.DB_NAME] if target is None: target = db_name else: target = "{}|{}".format(target, db_name) if target is None: target = db_system elif SpanAttributes.RPC_SYSTEM in span.attributes: # Rpc data.type = SpanAttributes.RPC_SYSTEM # TODO: data.data for rpc if target is None: target = span.attributes[SpanAttributes.RPC_SYSTEM] else: # TODO: Azure specific types data.type = "N/A" elif span.kind is SpanKind.PRODUCER: # Messaging data.type = "Queue Message" # TODO: data.data for messaging # TODO: Special logic for data.target for messaging? else: # SpanKind.INTERNAL if span.parent: data.type = "InProc" data.success = True # Apply truncation if data.result_code: data.result_code = data.result_code[:1024] if data.data: data.data = data.data[:8192] if target: data.target = target[:1024] if data.name: data.name = data.name[:1024] for key, val in span.attributes.items(): # Remove Opentelemetry related span attributes from custom dimensions if key.startswith("http.") or \ key.startswith("db.") or \ key.startswith("rpc.") or \ key.startswith("net.") or \ key.startswith("messaging."): continue # Apply truncation rules # Max key length is 150, value is 8192 if not key or len(key) > 150 or val is None: continue data.properties[key] = val[:8192] if span.links: # Max length for value is 8192 # Since links are a fixed length (80) in json, max number of links would be 102 links = [] for link in span.links: if len(links) > 102: break operation_id = "{:032x}".format(link.context.trace_id) span_id = "{:016x}".format(link.context.span_id) links.append({"operation_Id": operation_id, "id": span_id}) data.properties["_MS.links"] = json.dumps(links) return envelope
def _convert_span_to_envelope(span: Span) -> TelemetryItem: envelope = TelemetryItem( name="", instrumentation_key="", tags=dict(_utils.azure_monitor_context), time=ns_to_iso_str(span.start_time), ) if span.resource and span.resource.attributes: service_name = span.resource.attributes.get("service.name") service_namespace = span.resource.attributes.get("service.namespace") service_instance_id = span.resource.attributes.get( "service.instance.id") if service_name: if service_namespace: envelope.tags["ai.cloud.role"] = service_namespace + \ "." + service_name else: envelope.tags["ai.cloud.role"] = service_name if service_instance_id: envelope.tags["ai.cloud.roleInstance"] = service_instance_id else: envelope.tags["ai.cloud.roleInstance"] = platform.node( ) # hostname default envelope.tags["ai.internal.nodeName"] = envelope.tags[ "ai.cloud.roleInstance"] envelope.tags["ai.operation.id"] = "{:032x}".format(span.context.trace_id) if "enduser.id" in span.attributes: envelope.tags["ai.user.id"] = span.attributes["enduser.id"] if span.parent: envelope.tags["ai.operation.parentId"] = "{:016x}".format( span.parent.span_id) # pylint: disable=too-many-nested-blocks if span.kind in (SpanKind.CONSUMER, SpanKind.SERVER): envelope.name = "Microsoft.ApplicationInsights.Request" data = RequestData( name=span.name[:1024], # Breeze max length id="{:016x}".format(span.context.span_id), duration=_utils.ns_to_duration(span.end_time - span.start_time), response_code="0", success=span.status.is_ok, properties={}, ) envelope.data = MonitorBase(base_data=data, base_type="RequestData") if "http.method" in span.attributes: # HTTP envelope.tags["ai.operation.name"] = "{} {}".format( span.attributes["http.method"], span.name, ) data.properties["request.name"] = data.name url = "" if "http.user_agent" in span.attributes: # TODO: Not exposed in Swagger, need to update def envelope.tags["ai.user.userAgent"] = span.attributes[ "http.user_agent"] if "http.client_ip" in span.attributes: envelope.tags["ai.location.ip"] = span.attributes[ "http.client_ip"] elif "net.peer.ip" in span.attributes: envelope.tags["ai.location.ip"] = span.attributes[ "net.peer.ip"] # url if "http.url" in span.attributes: url = span.attributes["http.url"] elif "http.scheme" in span.attributes and "http.target" in span.attributes: scheme = span.attributes["http.scheme"] http_target = span.attributes["http.target"] if "http.host" in span.attributes: url = "{}://{}{}".format( scheme, span.attributes["http.host"], http_target, ) elif "net.host.port" in span.attributes: host_port = span.attributes["net.host.port"] if "http.server_name" in span.attributes: server_name = span.attributes["http.server_name"] url = "{}://{}:{}{}".format( scheme, server_name, host_port, http_target, ) elif "net.host.name" in span.attributes: host_name = span.attributes["net.host.name"] url = "{}://{}:{}{}".format( scheme, host_name, host_port, http_target, ) if url: url = url[:2048] # Breeze max length data.url = url data.properties["request.url"] = url if "http.status_code" in span.attributes: status_code = span.attributes["http.status_code"] data.response_code = str(status_code) elif "messaging.system" in span.attributes: # Messaging envelope.tags["ai.operation.name"] = span.name if "net.peer.ip" in span.attributes: envelope.tags["ai.location.ip"] = span.attributes[ "net.peer.ip"] if "messaging.destination" in span.attributes: if "net.peer.name" in span.attributes: data.properties["source"] = "{}/{}".format( span.attributes["net.peer.name"], span.attributes["messaging.destination"], ) elif "net.peer.ip" in span.attributes: data.properties["source"] = "{}/{}".format( span.attributes["net.peer.ip"], span.attributes["messaging.destination"], ) else: data.properties["source"] = span.attributes[ "messaging.destination"] else: # Other envelope.tags["ai.operation.name"] = span.name if "net.peer.ip" in span.attributes: envelope.tags["ai.location.ip"] = span.attributes[ "net.peer.ip"] data.response_code = data.response_code[:1024] # Breeze max length else: # INTERNAL, CLIENT, PRODUCER envelope.name = "Microsoft.ApplicationInsights.RemoteDependency" # TODO: ai.operation.name for non-server spans data = RemoteDependencyData( name=span.name[:1024], # Breeze max length id="{:016x}".format(span.context.span_id), result_code=str(span.status.status_code.value), duration=_utils.ns_to_duration(span.end_time - span.start_time), success=span.status.is_ok, properties={}, ) envelope.data = MonitorBase(base_data=data, base_type="RemoteDependencyData") target = None if "peer.service" in span.attributes: target = span.attributes["peer.service"] else: if "net.peer.name" in span.attributes: target = span.attributes["net.peer.name"] elif "net.peer.ip" in span.attributes: target = span.attributes["net.peer.ip"] if "net.peer.port" in span.attributes: port = span.attributes["net.peer.port"] # TODO: check default port for rpc # This logic assumes default ports never conflict across dependency types if port != _get_default_port_http(span.attributes.get("http.scheme")) and \ port != _get_default_port_db(span.attributes.get("db.system")): target = "{}:{}".format(target, port) if span.kind is SpanKind.CLIENT: if "http.method" in span.attributes: # HTTP data.type = "HTTP" if "http.user_agent" in span.attributes: # TODO: Not exposed in Swagger, need to update def envelope.tags["ai.user.userAgent"] = span.attributes[ "http.user_agent"] scheme = span.attributes.get("http.scheme") url = "" # Target if "http.url" in span.attributes: url = span.attributes["http.url"] # http specific logic for target if "peer.service" not in span.attributes: try: parse_url = urlparse(url) if parse_url.port == _get_default_port_http( scheme): target = parse_url.hostname else: target = parse_url.netloc except Exception: # pylint: disable=broad-except logger.warning("Error while parsing url.") # http specific logic for target if "peer.service" not in span.attributes and "http.host" in span.attributes: host = span.attributes["http.host"] try: # urlparse insists on absolute URLs starting with "//" # This logic assumes host does not include a "//" host_name = urlparse("//" + host) if host_name.port == _get_default_port_http(scheme): target = host_name.hostname else: target = host except Exception: # pylint: disable=broad-except logger.warning("Error while parsing hostname.") # url if not url: if scheme and "http.target" in span.attributes: http_target = span.attributes["http.target"] if "http.host" in span.attributes: url = "{}://{}{}".format( scheme, span.attributes["http.host"], http_target, ) elif "net.peer.port" in span.attributes: peer_port = span.attributes["net.peer.port"] if "net.peer.name" in span.attributes: peer_name = span.attributes["net.peer.name"] url = "{}://{}:{}{}".format( scheme, peer_name, peer_port, http_target, ) elif "net.peer.ip" in span.attributes: peer_ip = span.attributes["net.peer.ip"] url = "{}://{}:{}{}".format( scheme, peer_ip, peer_port, http_target, ) # data is url data.data = url if "http.status_code" in span.attributes: status_code = span.attributes["http.status_code"] data.result_code = str(status_code) elif "db.system" in span.attributes: # Database db_system = span.attributes["db.system"] if _is_relational_db(db_system): data.type = "SQL" else: data.type = db_system # data is the full statement if "db.statement" in span.attributes: data.data = span.attributes["db.statement"] # db specific logic for target if "db.name" in span.attributes: db_name = span.attributes["db.name"] if target is None: target = db_name else: target = "{}/{}".format(target, db_name) if target is None: target = db_system elif "rpc.system" in span.attributes: # Rpc data.type = "rpc.system" # TODO: data.data for rpc # rpc specific logic for target if "peer.service" not in span.attributes: target = span.attributes["rpc.system"] else: # TODO: Azure specific types data.type = "N/A" elif span.kind is SpanKind.PRODUCER: # Messaging data.type = "Queue Message" # TODO: data.data for messaging # TODO: Special logic for data.target for messaging? else: # SpanKind.INTERNAL data.type = "InProc" data.success = True # Apply truncation if data.result_code: data.result_code = data.result_code[:1024] if data.data: data.data = data.data[:8192] if target: data.target = target[:1024] for key, val in span.attributes.items(): # Remove Opentelemetry related span attributes from custom dimensions if key.startswith("http.") or \ key.startswith("db.") or \ key.startswith("rpc.") or \ key.startswith("net.") or \ key.startswith("messaging."): continue # Apply truncation rules # Max key length is 150, value is 8192 if not key or len(key) > 150 or val is None: continue data.properties[key] = val[:8192] if span.links: # Max length for value is 8192 # Since links are a fixed length (80) in json, max number of links would be 102 links = [] for link in span.links: if len(links) > 102: break operation_id = "{:032x}".format(link.context.trace_id) span_id = "{:016x}".format(link.context.span_id) links.append({"operation_Id": operation_id, "id": span_id}) data.properties["_MS.links"] = json.dumps(links) return envelope
def _convert_span_to_envelope(span: Span) -> TelemetryItem: envelope = TelemetryItem( name="", instrumentation_key="", tags=dict(_utils.azure_monitor_context), time=ns_to_iso_str(span.start_time), ) if span.resource and span.resource.attributes: # TODO: Get Resource attributes from OpenTelemetry SDK when available service_name = span.resource.attributes.get("service.name") service_namespace = span.resource.attributes.get("service.namespace") service_instance_id = span.resource.attributes.get( "service.instance.id") if service_name: if service_namespace: envelope.tags["ai.cloud.role"] = service_namespace + \ "." + service_name else: envelope.tags["ai.cloud.role"] = service_name if service_instance_id: envelope.tags["ai.cloud.roleInstance"] = service_instance_id envelope.tags["ai.operation.id"] = "{:032x}".format(span.context.trace_id) parent = span.parent if parent: envelope.tags["ai.operation.parentId"] = "{:016x}".format( parent.span_id) if span.kind in (SpanKind.CONSUMER, SpanKind.SERVER): envelope.name = "Microsoft.ApplicationInsights.Request" data = RequestData( name=span.name, id="{:016x}".format(span.context.span_id), duration=_utils.ns_to_duration(span.end_time - span.start_time), response_code=str(span.status.status_code.value), success=span.status.is_ok, properties={}, ) envelope.data = MonitorBase(base_data=data, base_type="RequestData") if "http.method" in span.attributes: # HTTP if "http.route" in span.attributes: envelope.tags["ai.operation.name"] = span.attributes[ "http.route"] elif "http.path" in span.attributes: envelope.tags["ai.operation.name"] = span.attributes[ "http.path"] else: envelope.tags["ai.operation.name"] = span.name if "http.url" in span.attributes: data.url = span.attributes["http.url"] data.properties["request.url"] = span.attributes["http.url"] if "http.status_code" in span.attributes: status_code = span.attributes["http.status_code"] data.response_code = str(status_code) elif "messaging.system" in span.attributes: # Messaging envelope.tags["ai.operation.name"] = span.name if "messaging.destination" in span.attributes: if "net.peer.name" in span.attributes: data.properties["source"] = "{}/{}".format( span.attributes["net.peer.name"], span.attributes["messaging.destination"], ) elif "net.peer.ip" in span.attributes: data.properties["source"] = "{}/{}".format( span.attributes["net.peer.ip"], span.attributes["messaging.destination"], ) else: data.properties["source"] = span.attributes[ "messaging.destination"] else: envelope.name = "Microsoft.ApplicationInsights.RemoteDependency" data = RemoteDependencyData( name=span.name, id="{:016x}".format(span.context.span_id), result_code=str(span.status.status_code.value), duration=_utils.ns_to_duration(span.end_time - span.start_time), success=span.status.is_ok, properties={}, ) envelope.data = MonitorBase(base_data=data, base_type="RemoteDependencyData") if span.kind in (SpanKind.CLIENT, SpanKind.PRODUCER): if "http.method" in span.attributes: # HTTP data.type = "HTTP" if "net.peer.port" in span.attributes: name = "" if "net.peer.name" in span.attributes: name = span.attributes["net.peer.name"] elif "net.peer.ip" in span.attributes: name = str(span.attributes["net.peer.ip"]) data.target = "{}:{}".format( name, str(span.attributes["net.peer.port"]), ) elif "http.url" in span.attributes: url = span.attributes["http.url"] # data is the url data.data = url parse_url = urlparse(url) # target matches authority (host:port) data.target = parse_url.netloc if "http.status_code" in span.attributes: status_code = span.attributes["http.status_code"] data.result_code = str(status_code) elif "db.system" in span.attributes: # Database data.type = span.attributes["db.system"] # data is the full statement if "db.statement" in span.attributes: data.data = span.attributes["db.statement"] if "db.name" in span.attributes: data.target = span.attributes["db.name"] else: data.target = span.attributes["db.system"] elif "rpc.system" in span.attributes: # Rpc data.type = "rpc.system" if "rpc.service" in span.attributes: data.target = span.attributes["rpc.service"] else: data.target = span.attributes["rpc.system"] elif "messaging.system" in span.attributes: # Messaging data.type = "Queue Message | {}" \ .format(span.attributes["messaging.system"]) if "net.peer.ip" in span.attributes and \ "messaging.destination" in span.attributes: data.target = "{}/{}".format( span.attributes["net.peer.ip"], span.attributes["messaging.destination"]) else: data.target = span.attributes["messaging.system"] else: # TODO: Azure specific types data.type = "N/A" else: # SpanKind.INTERNAL data.type = "InProc" data.success = True for key in span.attributes: # Remove Opentelemetry related span attributes from custom dimensions if key.startswith("http.") or \ key.startswith("db.") or \ key.startswith("rpc.") or \ key.startswith("net.") or \ key.startswith("messaging."): continue data.properties[key] = span.attributes[key] if span.links: links = [] for link in span.links: operation_id = "{:032x}".format(link.context.trace_id) span_id = "{:016x}".format(link.context.span_id) links.append({"operation_Id": operation_id, "id": span_id}) data.properties["_MS.links"] = json.dumps(links) # TODO: tracestate, tags return envelope