def __str__(self): return '{}(name="{}", context={}, kind={}, parent={}, start_time={}, end_time={})'.format( type(self).__name__, self.name, self.context, self.kind, repr(self.parent), util.ns_to_iso_str(self.start_time) if self.start_time else "None", util.ns_to_iso_str(self.end_time) if self.end_time else "None", )
def test_value_recorder_to_envelope(self): aggregator = MinMaxSumCountAggregator() aggregator.update(123) aggregator.take_checkpoint() record = MetricRecord(self._test_value_recorder, self._test_labels, aggregator) exporter = self._exporter envelope = exporter._metric_to_envelope(record) self.assertIsInstance(envelope, Envelope) self.assertEqual(envelope.ver, 1) self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.Metric") self.assertEqual(envelope.time, ns_to_iso_str(aggregator.last_update_timestamp)) self.assertEqual(envelope.sample_rate, None) self.assertEqual(envelope.seq, None) self.assertEqual(envelope.ikey, "1234abcd-5678-4efa-8abc-1234567890ab") self.assertEqual(envelope.flags, None) self.assertIsInstance(envelope.data, Data) self.assertIsInstance(envelope.data.base_data, MetricData) self.assertEqual(envelope.data.base_data.ver, 2) self.assertEqual(len(envelope.data.base_data.metrics), 1) self.assertIsInstance(envelope.data.base_data.metrics[0], DataPoint) self.assertEqual(envelope.data.base_data.metrics[0].ns, "testdesc") self.assertEqual(envelope.data.base_data.metrics[0].name, "testname") self.assertEqual(envelope.data.base_data.metrics[0].value, 1) self.assertEqual(envelope.data.base_data.properties["environment"], "staging") self.assertIsNotNone(envelope.tags["ai.cloud.role"]) self.assertIsNotNone(envelope.tags["ai.cloud.roleInstance"]) self.assertIsNotNone(envelope.tags["ai.device.id"]) self.assertIsNotNone(envelope.tags["ai.device.locale"]) self.assertIsNotNone(envelope.tags["ai.device.osVersion"]) self.assertIsNotNone(envelope.tags["ai.device.type"]) self.assertIsNotNone(envelope.tags["ai.internal.sdkVersion"])
def to_json(self) -> str: return json.dumps( { "body": self.body, "name": self.name, "severity_number": repr(self.severity_number), "severity_text": self.severity_text, "attributes": self.attributes, "timestamp": ns_to_iso_str(self.timestamp), "trace_id": f"0x{format_trace_id(self.trace_id)}" if self.trace_id is not None else "", "span_id": f"0x{format_span_id(self.span_id)}" if self.span_id is not None else "", "trace_flags": self.trace_flags, "resource": repr(self.resource.attributes) if self.resource else "", }, indent=4, )
def metric_to_envelope( self, metric_record: MetricRecord ) -> protocol.Envelope: if not metric_record: return None envelope = protocol.Envelope( ikey=self.options.instrumentation_key, tags=dict(utils.azure_monitor_context), time=ns_to_iso_str( metric_record.metric.get_handle( metric_record.label_set ).last_update_timestamp ), ) envelope.name = "Microsoft.ApplicationInsights.Metric" data_point = protocol.DataPoint( ns=metric_record.metric.name, name=metric_record.metric.description, value=metric_record.aggregator.checkpoint, kind=protocol.DataPointType.MEASUREMENT, ) properties = {} for label_tuple in metric_record.label_set.labels: properties[label_tuple[0]] = label_tuple[1] data = protocol.MetricData(metrics=[data_point], properties=properties) envelope.data = protocol.Data(base_data=data, base_type="MetricData") return envelope
def _format_events(events): f_events = [] for event in events: f_event = OrderedDict() f_event["name"] = event.name f_event["timestamp"] = util.ns_to_iso_str(event.timestamp) f_event["attributes"] = Span._format_attributes(event.attributes) f_events.append(f_event) return f_events
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 to_json(self, indent=4): parent_id = None if self.parent is not None: if isinstance(self.parent, Span): ctx = self.parent.context parent_id = f"0x{trace_api.format_span_id(ctx.span_id)}" elif isinstance(self.parent, SpanContext): parent_id = ( f"0x{trace_api.format_span_id(self.parent.span_id)}" ) start_time = None if self._start_time: start_time = util.ns_to_iso_str(self._start_time) end_time = None if self._end_time: end_time = util.ns_to_iso_str(self._end_time) if self._status is not None: status = OrderedDict() status["status_code"] = str(self._status.status_code.name) if self._status.description: status["description"] = self._status.description f_span = OrderedDict() f_span["name"] = self._name f_span["context"] = self._format_context(self._context) f_span["kind"] = str(self.kind) f_span["parent_id"] = parent_id f_span["start_time"] = start_time f_span["end_time"] = end_time if self._status is not None: f_span["status"] = status f_span["attributes"] = self._format_attributes(self._attributes) f_span["events"] = self._format_events(self._events) f_span["links"] = self._format_links(self._links) f_span["resource"] = self._format_attributes(self._resource.attributes) return json.dumps(f_span, indent=indent)
def _metric_to_envelope(self, metric_record: MetricRecord) -> protocol.Envelope: if not metric_record: return None envelope = protocol.Envelope( ikey=self.options.instrumentation_key, tags=dict(utils.azure_monitor_context), time=ns_to_iso_str(metric_record.aggregator.last_update_timestamp), ) envelope.name = "Microsoft.ApplicationInsights.Metric" value = 0 _min = None _max = None count = None metric = metric_record.instrument if isinstance(metric, ValueObserver): # mmscl value = metric_record.aggregator.checkpoint.last elif isinstance(metric, ValueRecorder): # mmsc value = metric_record.aggregator.checkpoint.sum _min = metric_record.aggregator.checkpoint.min _max = metric_record.aggregator.checkpoint.max count = metric_record.aggregator.checkpoint.count else: # sum or lv value = metric_record.aggregator.checkpoint if value is None: logger.warning("Value is none. Default to 0.") value = 0 data_point = protocol.DataPoint( ns=metric.description, name=metric.name, value=value, min=_min, max=_max, count=count, kind=protocol.DataPointType.MEASUREMENT.value, ) properties = {} for label_tuple in metric_record.labels: properties[label_tuple[0]] = label_tuple[1] data = protocol.MetricData(metrics=[data_point], properties=properties) envelope.data = protocol.Data(base_data=data, base_type="MetricData") return envelope
def test_attributes_to_json(self): context = trace_api.SpanContext( trace_id=0x000000000000000000000000DEADBEEF, span_id=0x00000000DEADBEF0, is_remote=False, trace_flags=trace_api.TraceFlags(trace_api.TraceFlags.SAMPLED), ) span = trace._Span("span-name", context, resource=Resource({})) span.set_attribute("key", "value") span.add_event("event", {"key2": "value2"}, 123) date_str = ns_to_iso_str(123) self.assertEqual( span.to_json(indent=None), '{"name": "span-name", "context": {"trace_id": "0x000000000000000000000000deadbeef", "span_id": "0x00000000deadbef0", "trace_state": "[]"}, "kind": "SpanKind.INTERNAL", "parent_id": null, "start_time": null, "end_time": null, "status": {"status_code": "UNSET"}, "attributes": {"key": "value"}, "events": [{"name": "event", "timestamp": "' + date_str + '", "attributes": {"key2": "value2"}}], "links": [], "resource": {}}', )
def _metric_to_envelope(self, metric_record: MetricRecord) -> protocol.Envelope: if not metric_record: return None # TODO: Opentelemetry has timestamp for Observer, awaiting release # TODO: Timestamp info is also moved into aggregators _time = time_ns() if isinstance(metric_record.metric, Metric): _time = metric_record.metric.bind(dict( metric_record.labels)).last_update_timestamp envelope = protocol.Envelope( ikey=self.options.instrumentation_key, tags=dict(utils.azure_monitor_context), time=ns_to_iso_str(_time), ) envelope.name = "Microsoft.ApplicationInsights.Metric" value = 0 metric = metric_record.metric if isinstance(metric, Counter): value = metric_record.aggregator.checkpoint elif isinstance(metric, Observer): value = metric_record.aggregator.checkpoint.last if not value: value = 0 else: # TODO: What do measure aggregations look like in AI? logger.warning("Measure metric recorded.") data_point = protocol.DataPoint( ns=metric_record.metric.description, name=metric_record.metric.name, value=value, kind=protocol.DataPointType.MEASUREMENT.value, ) properties = {} for label_tuple in metric_record.labels: properties[label_tuple[0]] = label_tuple[1] data = protocol.MetricData(metrics=[data_point], properties=properties) envelope.data = protocol.Data(base_data=data, base_type="MetricData") return envelope
def _span_to_envelope(self, span: Span) -> protocol.Envelope: if not span: return None envelope = protocol.Envelope( ikey=self.options.instrumentation_key, tags=dict(utils.azure_monitor_context), time=ns_to_iso_str(span.start_time), ) envelope.tags["ai.operation.id"] = "{:032x}".format( span.context.trace_id) parent = span.parent if isinstance(parent, Span): parent = parent.context 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 = protocol.Request( id="{:016x}".format(span.context.span_id), duration=utils.ns_to_duration(span.end_time - span.start_time), response_code=str(span.status.canonical_code.value), success=span.status.canonical_code == StatusCanonicalCode. OK, # Modify based off attributes or Status properties={}, ) envelope.data = protocol.Data(base_data=data, base_type="RequestData") if "http.method" in span.attributes: data.name = span.attributes["http.method"] if "http.route" in span.attributes: data.name = data.name + " " + span.attributes["http.route"] envelope.tags["ai.operation.name"] = data.name data.properties["request.name"] = data.name elif "http.path" in span.attributes: data.properties["request.name"] = ( data.name + " " + span.attributes["http.path"]) 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) data.success = 200 <= status_code < 400 else: envelope.name = "Microsoft.ApplicationInsights.RemoteDependency" data = protocol.RemoteDependency( name=span.name, id="{:016x}".format(span.context.span_id), result_code=str(span.status.canonical_code.value), duration=utils.ns_to_duration(span.end_time - span.start_time), success=span.status.canonical_code == StatusCanonicalCode. OK, # Modify based off attributes or Status properties={}, ) envelope.data = protocol.Data(base_data=data, base_type="RemoteDependencyData") if span.kind in (SpanKind.CLIENT, SpanKind.PRODUCER): if ("component" in span.attributes and span.attributes["component"] == "http"): data.type = "HTTP" if "http.url" in span.attributes: url = span.attributes["http.url"] # data is the url data.data = url parse_url = urlparse(url) # TODO: error handling, probably put scheme as well # target matches authority (host:port) data.target = parse_url.netloc if "http.method" in span.attributes: # name is METHOD/path data.name = (span.attributes["http.method"] + "/" + parse_url.path) if "http.status_code" in span.attributes: status_code = span.attributes["http.status_code"] data.result_code = str(status_code) data.success = 200 <= status_code < 400 else: # SpanKind.INTERNAL data.type = "InProc" data.success = True for key in span.attributes: # This removes redundant data from ApplicationInsights if key.startswith("http."): 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
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), ) 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
def span_to_envelope(self, span): # noqa pylint: disable=too-many-branches envelope = protocol.Envelope( iKey=self.options.instrumentation_key, tags=dict(util.azure_monitor_context), time=ns_to_iso_str(span.start_time), ) envelope.tags["ai.operation.id"] = "{:032x}".format( span.context.trace_id) parent = span.parent if isinstance(parent, Span): parent = parent.context if parent: envelope.tags[ "ai.operation.parentId"] = "|{:032x}.{:016x}.".format( parent.trace_id, parent.span_id) if span.kind in (SpanKind.CONSUMER, SpanKind.SERVER): envelope.name = "Microsoft.ApplicationInsights.Request" data = protocol.Request( id="|{:032x}.{:016x}.".format(span.context.trace_id, span.context.span_id), duration=self.ns_to_duration(span.end_time - span.start_time), responseCode="0", success=False, properties={}, ) envelope.data = protocol.Data(baseData=data, baseType="RequestData") if "http.method" in span.attributes: data.name = span.attributes["http.method"] if "http.route" in span.attributes: data.name = data.name + " " + span.attributes["http.route"] envelope.tags["ai.operation.name"] = data.name if "http.url" in span.attributes: data.url = span.attributes["http.url"] if "http.status_code" in span.attributes: status_code = span.attributes["http.status_code"] data.responseCode = str(status_code) data.success = 200 <= status_code < 400 else: envelope.name = "Microsoft.ApplicationInsights.RemoteDependency" data = protocol.RemoteDependency( name=span.name, id="|{:032x}.{:016x}.".format(span.context.trace_id, span.context.span_id), resultCode="0", # TODO duration=self.ns_to_duration(span.end_time - span.start_time), success=True, # TODO properties={}, ) envelope.data = protocol.Data(baseData=data, baseType="RemoteDependencyData") if span.kind in (SpanKind.CLIENT, SpanKind.PRODUCER): data.type = "HTTP" # TODO if "http.url" in span.attributes: url = span.attributes["http.url"] # TODO: error handling, probably put scheme as well data.name = urlparse(url).netloc if "http.status_code" in span.attributes: data.resultCode = str(span.attributes["http.status_code"]) else: # SpanKind.INTERNAL data.type = "InProc" for key in span.attributes: data.properties[key] = span.attributes[key] if span.links: links = [] for link in span.links: links.append({ "operation_Id": "{:032x}".format(link.context.trace_id), "id": "|{:032x}.{:016x}.".format(link.context.trace_id, link.context.span_id), }) data.properties["_MS.links"] = json.dumps(links) print(data.properties["_MS.links"]) # TODO: tracestate, tags return envelope