def __init__(
     self,
     instrumentation_key: str,
     span_processor: AzureMetricsSpanProcessor,
 ):
     self._instrumentation_key = instrumentation_key
     self._span_processor = span_processor
     self._sender = LiveMetricsSender(self._instrumentation_key)
     self.subscribed = True
示例#2
0
 def __init__(self, instrumentation_key):
     super().__init__()
     self.instrumentation_key = instrumentation_key
     self.thread_event = threading.Event()
     self.interval = PING_INTERVAL
     self.is_user_subscribed = False
     self.last_send_succeeded = False
     self.last_request_success_time = 0
     self.sender = LiveMetricsSender(self.instrumentation_key)
     self.start()
 def test_post(self):
     """Test post."""
     sender = LiveMetricsSender(
         instrumentation_key=self._instrumentation_key)
     envelope = LiveMetricEnvelope()
     with mock.patch("requests.post") as request:
         sender.post(envelope)
         self.assertTrue(request.called)
         self.assertEqual(
             request.call_args[1].get("url"),
             "https://rt.services.visualstudio.com/QuickPulseService.svc/post?ikey={0}"
             .format(self._instrumentation_key),
         )
         self.assertIsNotNone(request.call_args[1].get("data"))
         headers = request.call_args[1].get("headers")
         self.assertEqual(headers.get("Expect"), "100-continue")
         self.assertEqual(headers.get("Content-Type"),
                          "application/json; charset=utf-8")
         self.assertTrue(
             headers.get("x-ms-qps-transmission-time").isdigit())
示例#4
0
class LiveMetricsPing(threading.Thread):
    """Ping to Live Metrics service

    Ping to determine if user is subscribed and live metrics need to be send.
    """

    daemon = True

    def __init__(self, instrumentation_key):
        super().__init__()
        self.instrumentation_key = instrumentation_key
        self.thread_event = threading.Event()
        self.interval = PING_INTERVAL
        self.is_user_subscribed = False
        self.last_send_succeeded = False
        self.last_request_success_time = 0
        self.sender = LiveMetricsSender(self.instrumentation_key)
        self.start()

    def run(self):
        self.ping()
        while not self.thread_event.wait(self.interval):
            self.ping()

    def ping(self):
        envelope = utils.create_metric_envelope(self.instrumentation_key)
        token = attach(set_value("suppress_instrumentation", True))
        response = self.sender.ping(envelope)
        detach(token)
        if response.ok:
            if not self.last_send_succeeded:
                self.interval = PING_INTERVAL
                self.last_send_succeeded = True
            self.last_request_success_time = time.time()
            if (response.headers.get(
                    utils.LIVE_METRICS_SUBSCRIBED_HEADER) == "true"):
                self.is_user_subscribed = True
        else:
            self.last_send_succeeded = False
            if time.time() >= self.last_request_success_time + 60:
                self.interval = FALLBACK_INTERVAL

    def shutdown(self):
        self.thread_event.set()
class LiveMetricsExporter(MetricsExporter):
    """Live Metrics Exporter

    Export data to Azure Live Metrics service and determine if user is subscribed.
    """

    def __init__(
        self,
        instrumentation_key: str,
        span_processor: AzureMetricsSpanProcessor,
    ):
        self._instrumentation_key = instrumentation_key
        self._span_processor = span_processor
        self._sender = LiveMetricsSender(self._instrumentation_key)
        self.subscribed = True

    def export(
        self, metric_records: typing.Sequence[MetricRecord]
    ) -> MetricsExportResult:
        envelope = self._metric_to_live_metrics_envelope(metric_records)
        try:
            response = self._sender.post(envelope)
            if response.ok:
                self.subscribed = (
                    response.headers.get(utils.LIVE_METRICS_SUBSCRIBED_HEADER)
                    == "true"
                )
                return MetricsExportResult.SUCCESS

        except Exception:  # pylint: disable=broad-except
            logger.warning("Exception occurred while exporting the data.")

        return MetricsExportResult.FAILURE

    def _metric_to_live_metrics_envelope(
        self, metric_records: typing.Sequence[MetricRecord]
    ) -> LiveMetricEnvelope:

        envelope = utils.create_metric_envelope(self._instrumentation_key)
        envelope.documents = self._get_live_metric_documents()
        envelope.metrics = []

        # Add metrics
        for metric_record in metric_records:
            value = 0
            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.count
            else:
                # sum or lv
                value = metric_record.aggregator.checkpoint
            if value is None:
                logger.warning("Value is none. Default to 0.")
                value = 0
            envelope.metrics.append(
                LiveMetric(name=metric.name, value=value, weight=1)
            )

        return envelope

    def _get_live_metric_documents(
        self,
    ) -> typing.Sequence[LiveMetricDocument]:
        live_metric_documents = []
        while self._span_processor.documents:
            envelope = self._span_processor.documents.popleft()
            base_type = envelope.data.base_type
            if base_type:
                document = LiveMetricDocument(
                    quickpulse_type=self._get_live_metric_type(base_type),
                    document_type=self._get_live_metric_document_type(
                        base_type
                    ),
                    properties=self._get_aggregated_properties(envelope),
                    version="1.0",
                )
                live_metric_documents.append(document)
            else:
                logger.warning(
                    "Document type invalid; not sending live metric document"
                )

        return live_metric_documents

    def _get_live_metric_type(self, base_type: str) -> str:
        if base_type == "EventData":
            return "EventTelemetryDocument"
        if base_type == "ExceptionData":
            return "ExceptionTelemetryDocument"
        if base_type == "MessageData":
            return "TraceTelemetryDocument"
        if base_type == "MetricData":
            return "MetricTelemetryDocument"
        if base_type == "RequestData":
            return "RequestTelemetryDocument"
        if base_type == "RemoteDependencyData":
            return "DependencyTelemetryDocument"
        if base_type == "AvailabilityData":
            return "AvailabilityTelemetryDocument"
        return ""

    def _get_live_metric_document_type(self, base_type: str) -> str:
        if base_type == "EventData":
            return "Event"
        if base_type == "ExceptionData":
            return "Exception"
        if base_type == "MessageData":
            return "Trace"
        if base_type == "MetricData":
            return "Metric"
        if base_type == "RequestData":
            return "Request"
        if base_type == "RemoteDependencyData":
            return "RemoteDependency"
        if base_type == "AvailabilityData":
            return "Availability"
        return ""

    def _get_aggregated_properties(self, envelope: Envelope) -> typing.Dict:
        aggregated_properties = {}
        measurements = (
            envelope.data.base_data.measurements
            if envelope.data.base_data and envelope.data.base_data.measurements
            else []
        )
        for key in measurements:
            aggregated_properties[key] = measurements[key]
        properties = (
            envelope.data.base_data.properties
            if envelope.data.base_data and envelope.data.base_data.properties
            else []
        )
        for key in properties:
            aggregated_properties[key] = properties[key]
        return aggregated_properties
 def test_constructor(self):
     """Test the constructor."""
     sender = LiveMetricsSender(
         instrumentation_key=self._instrumentation_key)
     self.assertEqual(sender._instrumentation_key,
                      self._instrumentation_key)