예제 #1
0
 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",
     )
예제 #2
0
    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"])
예제 #3
0
 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
예제 #5
0
 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
예제 #6
0
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
예제 #7
0
    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": {}}',
     )
예제 #10
0
    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
예제 #13
0
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
예제 #14
0
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
예제 #15
0
 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