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 __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())
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)