def test_ungrouped_batcher_process_exists(self): meter = metrics.MeterProvider().get_meter(__name__) batcher = UngroupedBatcher(True) aggregator = CounterAggregator() aggregator2 = CounterAggregator() metric = metrics.Counter( "available memory", "available memory", "bytes", int, meter, ("environment", ), ) label_set = metrics.LabelSet() _batch_map = {} _batch_map[(metric, label_set)] = aggregator aggregator2.update(1.0) batcher._batch_map = _batch_map record = metrics.Record(metric, label_set, aggregator2) batcher.process(record) self.assertEqual(len(batcher._batch_map), 1) self.assertIsNotNone(batcher._batch_map.get((metric, label_set))) self.assertEqual( batcher._batch_map.get((metric, label_set)).current, 0) self.assertEqual( batcher._batch_map.get((metric, label_set)).checkpoint, 1.0)
def test_merge(self): counter = CounterAggregator() counter2 = CounterAggregator() counter.checkpoint = 1.0 counter2.checkpoint = 3.0 counter.merge(counter2) self.assertEqual(counter.checkpoint, 4.0)
def test_merge(self): counter = CounterAggregator() counter2 = CounterAggregator() counter.checkpoint = 1.0 counter2.checkpoint = 3.0 counter2.last_update_timestamp = 123 counter.merge(counter2) self.assertEqual(counter.checkpoint, 4.0) self.assertEqual(counter.last_update_timestamp, 123)
def aggregator_for(self, metric_type: Type[MetricT]) -> Aggregator: """Returns an aggregator based on metric type. Aggregators keep track of and updates values when metrics get updated. """ # pylint:disable=R0201 if issubclass(metric_type, Counter): return CounterAggregator() if issubclass(metric_type, Measure): return MinMaxSumCountAggregator() # TODO: Add other aggregators return CounterAggregator()
def aggregator_for(self, instrument_type: Type[InstrumentT]) -> Aggregator: """Returns an aggregator based on metric instrument type. Aggregators keep track of and updates values when metrics get updated. """ # pylint:disable=R0201 if issubclass(instrument_type, (Counter, UpDownCounter)): return CounterAggregator() if issubclass(instrument_type, (SumObserver, UpDownSumObserver)): return LastValueAggregator() if issubclass(instrument_type, ValueRecorder): return MinMaxSumCountAggregator() if issubclass(instrument_type, ValueObserver): return ValueObserverAggregator() # TODO: Add other aggregators return CounterAggregator()
def test_update(self, time_mock): time_mock.return_value = 123 counter = CounterAggregator() counter.update(1.0) counter.update(2.0) self.assertEqual(counter.current, 3.0) self.assertEqual(counter.last_update_timestamp, 123)
def test_counter_to_prometheus(self): meter = get_meter_provider().get_meter(__name__) metric = meter.create_metric( "test@name", "testdesc", "unit", int, metrics.Counter, ["environment@", "os"], ) kvp = {"environment@": "staging", "os": "Windows"} label_set = meter.get_label_set(kvp) aggregator = CounterAggregator() aggregator.update(123) aggregator.take_checkpoint() record = MetricRecord(aggregator, label_set, metric) collector = CustomCollector("testprefix") collector.add_metrics_data([record]) for prometheus_metric in collector.collect(): self.assertEqual(type(prometheus_metric), CounterMetricFamily) self.assertEqual(prometheus_metric.name, "testprefix_test_name") self.assertEqual(prometheus_metric.documentation, "testdesc") self.assertTrue(len(prometheus_metric.samples) == 1) self.assertEqual(prometheus_metric.samples[0].value, 123) self.assertTrue(len(prometheus_metric.samples[0].labels) == 2) self.assertEqual( prometheus_metric.samples[0].labels["environment_"], "staging") self.assertEqual(prometheus_metric.samples[0].labels["os"], "Windows")
def test_export(self): with self._registry_register_patch: record = MetricRecord(CounterAggregator(), self._test_label_set, self._test_metric) exporter = PrometheusMetricsExporter() result = exporter.export([record]) # pylint: disable=protected-access self.assertEqual(len(exporter._collector._metrics_to_export), 1) self.assertIs(result, MetricsExportResult.SUCCESS)
def test_export(self, ): record = MetricRecord(CounterAggregator(), self._test_labels, self._test_metric) exporter = self._exporter with mock.patch( "azure_monitor.export.metrics.AzureMonitorMetricsExporter._transmit" ) as transmit: # noqa: E501 transmit.return_value = ExportResult.SUCCESS result = exporter.export([record]) self.assertEqual(result, MetricsExportResult.SUCCESS)
def test_concurrent_update(self): counter = CounterAggregator() with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor: fut1 = executor.submit(self.call_update, counter) fut2 = executor.submit(self.call_update, counter) updapte_total = fut1.result() + fut2.result() counter.take_checkpoint() self.assertEqual(updapte_total, counter.checkpoint)
def test_export_exception(self, logger_mock): record = MetricRecord(CounterAggregator(), self._test_labels, self._test_metric) exporter = self._exporter with mock.patch( "azure_monitor.export.metrics.AzureMonitorMetricsExporter._transmit", throw(Exception), ): # noqa: E501 result = exporter.export([record]) self.assertEqual(result, MetricsExportResult.FAILED_NOT_RETRYABLE) self.assertEqual(logger_mock.exception.called, True)
def test_export_failed_retryable(self): record = MetricRecord(CounterAggregator(), self._test_labels, self._test_metric) exporter = self._exporter with mock.patch( "azure_monitor.export.metrics.AzureMonitorMetricsExporter._transmit" ) as transmit: # noqa: E501 transmit.return_value = ExportResult.FAILED_RETRYABLE storage_mock = mock.Mock() exporter.storage.put = storage_mock result = exporter.export([record]) self.assertEqual(result, MetricsExportResult.FAILED_RETRYABLE) self.assertEqual(storage_mock.call_count, 1)
def test_concurrent_update_and_checkpoint(self): counter = CounterAggregator() checkpoint_total = 0 with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor: fut = executor.submit(self.call_update, counter) while not fut.done(): counter.take_checkpoint() checkpoint_total += counter.checkpoint counter.take_checkpoint() checkpoint_total += counter.checkpoint self.assertEqual(fut.result(), checkpoint_total)
def test_finished_collection_stateless(self): meter = metrics.MeterProvider().get_meter(__name__) batcher = UngroupedBatcher(False) aggregator = CounterAggregator() metric = metrics.Counter( "available memory", "available memory", "bytes", int, meter, ("environment", ), ) aggregator.update(1.0) label_set = metrics.LabelSet() _batch_map = {} _batch_map[(metric, label_set)] = aggregator batcher._batch_map = _batch_map batcher.finished_collection() self.assertEqual(len(batcher._batch_map), 0)
def test_metric_to_envelope(self): aggregator = CounterAggregator() aggregator.update(123) aggregator.take_checkpoint() record = MetricRecord( aggregator, self._test_label_set, self._test_metric ) exporter = AzureMonitorMetricsExporter() 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( record.metric.get_handle( record.label_set ).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.assertEqual(envelope.data.base_data.metrics[0].ns, "testname") self.assertEqual(envelope.data.base_data.metrics[0].name, "testdesc") self.assertEqual(envelope.data.base_data.metrics[0].value, 123) 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 test_checkpoint_set(self): meter = metrics.MeterProvider().get_meter(__name__) batcher = UngroupedBatcher(True) aggregator = CounterAggregator() metric = metrics.Counter( "available memory", "available memory", "bytes", int, meter, ("environment", ), ) aggregator.update(1.0) label_set = metrics.LabelSet() _batch_map = {} _batch_map[(metric, label_set)] = aggregator batcher._batch_map = _batch_map records = batcher.checkpoint_set() self.assertEqual(len(records), 1) self.assertEqual(records[0].metric, metric) self.assertEqual(records[0].label_set, label_set) self.assertEqual(records[0].aggregator, aggregator)
def test_export(self): meter = metrics.MeterProvider().get_meter(__name__) exporter = ConsoleMetricsExporter() metric = metrics.Counter( "available memory", "available memory", "bytes", int, meter, ("environment",), ) labels = {"environment": "staging"} aggregator = CounterAggregator() record = MetricRecord(aggregator, labels, metric) result = '{}(data="{}", labels="{}", value={})'.format( ConsoleMetricsExporter.__name__, metric, labels, aggregator.checkpoint, ) with mock.patch("sys.stdout") as mock_stdout: exporter.export([record]) mock_stdout.write.assert_any_call(result)
def test_update(self): counter = CounterAggregator() counter.update(1.0) counter.update(2.0) self.assertEqual(counter.current, 3.0)
def test_export(self): client = mock.Mock() exporter = CloudMonitoringMetricsExporter( project_id=self.project_id, client=client ) exporter.project_name = self.project_name exporter.export( [ MetricRecord( MockMetric(), (("label1", "value1"),), UnsupportedAggregator(), ) ] ) client.create_time_series.assert_not_called() client.create_metric_descriptor.return_value = MetricDescriptor( **{ "name": None, "type": "custom.googleapis.com/OpenTelemetry/name", "display_name": "name", "description": "description", "labels": [ LabelDescriptor(key="label1", value_type="STRING"), LabelDescriptor(key="label2", value_type="INT64"), ], "metric_kind": "GAUGE", "value_type": "DOUBLE", } ) counter_one = CounterAggregator() counter_one.checkpoint = 1 counter_one.last_update_timestamp = (WRITE_INTERVAL + 1) * 1e9 exporter.export( [ MetricRecord( MockMetric(), (("label1", "value1"), ("label2", 1),), counter_one, ), MetricRecord( MockMetric(), (("label1", "value2"), ("label2", 2),), counter_one, ), ] ) series1 = TimeSeries() series1.metric.type = "custom.googleapis.com/OpenTelemetry/name" series1.metric.labels["label1"] = "value1" series1.metric.labels["label2"] = "1" point = series1.points.add() point.value.int64_value = 1 point.interval.end_time.seconds = WRITE_INTERVAL + 1 point.interval.end_time.nanos = 0 series2 = TimeSeries() series2.metric.type = "custom.googleapis.com/OpenTelemetry/name" series2.metric.labels["label1"] = "value2" series2.metric.labels["label2"] = "2" point = series2.points.add() point.value.int64_value = 1 point.interval.end_time.seconds = WRITE_INTERVAL + 1 point.interval.end_time.nanos = 0 client.create_time_series.assert_has_calls( [mock.call(self.project_name, [series1, series2])] ) # Attempting to export too soon after another export with the exact # same labels leads to it being dropped counter_two = CounterAggregator() counter_two.checkpoint = 1 counter_two.last_update_timestamp = (WRITE_INTERVAL + 2) * 1e9 exporter.export( [ MetricRecord( MockMetric(), (("label1", "value1"), ("label2", 1),), counter_two, ), MetricRecord( MockMetric(), (("label1", "value2"), ("label2", 2),), counter_two, ), ] ) self.assertEqual(client.create_time_series.call_count, 1) # But exporting with different labels is fine counter_two.checkpoint = 2 exporter.export( [ MetricRecord( MockMetric(), (("label1", "changed_label"), ("label2", 2),), counter_two, ), ] ) series3 = TimeSeries() series3.metric.type = "custom.googleapis.com/OpenTelemetry/name" series3.metric.labels["label1"] = "changed_label" series3.metric.labels["label2"] = "2" point = series3.points.add() point.value.int64_value = 2 point.interval.end_time.seconds = WRITE_INTERVAL + 2 point.interval.end_time.nanos = 0 client.create_time_series.assert_has_calls( [ mock.call(self.project_name, [series1, series2]), mock.call(self.project_name, [series3]), ] )
def test_get_metric_descriptor(self): client = mock.Mock() exporter = CloudMonitoringMetricsExporter( project_id=self.project_id, client=client ) exporter.project_name = self.project_name self.assertIsNone( exporter._get_metric_descriptor( MetricRecord(MockMetric(), (), UnsupportedAggregator()) ) ) record = MetricRecord( MockMetric(), (("label1", "value1"),), CounterAggregator(), ) metric_descriptor = exporter._get_metric_descriptor(record) client.create_metric_descriptor.assert_called_with( self.project_name, MetricDescriptor( **{ "name": None, "type": "custom.googleapis.com/OpenTelemetry/name", "display_name": "name", "description": "description", "labels": [ LabelDescriptor(key="label1", value_type="STRING") ], "metric_kind": "GAUGE", "value_type": "INT64", } ), ) # Getting a cached metric descriptor shouldn't use another call cached_metric_descriptor = exporter._get_metric_descriptor(record) self.assertEqual(client.create_metric_descriptor.call_count, 1) self.assertEqual(metric_descriptor, cached_metric_descriptor) # Drop labels with values that aren't string, int or bool exporter._get_metric_descriptor( MetricRecord( MockMetric(name="name2", value_type=float), ( ("label1", "value1"), ("label2", dict()), ("label3", 3), ("label4", False), ), CounterAggregator(), ) ) client.create_metric_descriptor.assert_called_with( self.project_name, MetricDescriptor( **{ "name": None, "type": "custom.googleapis.com/OpenTelemetry/name2", "display_name": "name2", "description": "description", "labels": [ LabelDescriptor(key="label1", value_type="STRING"), LabelDescriptor(key="label3", value_type="INT64"), LabelDescriptor(key="label4", value_type="BOOL"), ], "metric_kind": "GAUGE", "value_type": "DOUBLE", } ), )
def test_checkpoint(self): counter = CounterAggregator() counter.update(2.0) counter.take_checkpoint() self.assertEqual(counter.current, 0) self.assertEqual(counter.checkpoint, 2.0)