diff --git a/CHANGELOG.md b/CHANGELOG.md index 30dcea6d..4a4f9471 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,8 +24,10 @@ ([#228](https://github.com/microsoft/ApplicationInsights-Python/pull/228)) - Removing diagnostic logging from its module's logger. ([#225](https://github.com/microsoft/ApplicationInsights-Python/pull/225)) -- Configure logging instrumenation root +- Add ability to specify logger for logging configuration ([#227](https://github.com/microsoft/ApplicationInsights-Python/pull/227)) +- Add metric configuration to distro api + ([#232](https://github.com/microsoft/ApplicationInsights-Python/pull/232)) ## [1.0.0b8](https://github.com/microsoft/ApplicationInsights-Python/releases/tag/v1.0.0b8) - 2022-09-26 diff --git a/azure-monitor-opentelemetry-distro/README.md b/azure-monitor-opentelemetry-distro/README.md index ecd12c8e..7abc6231 100644 --- a/azure-monitor-opentelemetry-distro/README.md +++ b/azure-monitor-opentelemetry-distro/README.md @@ -20,6 +20,15 @@ The [Azure Monitor OpenTelemetry exporters][azure_monitor_opentelemetry_exporter Currently, all instrumentations available in OpenTelemetry are in a beta state, meaning they are not stable and may have breaking changes in the future. Efforts are being made in pushing these to a more stable state. +### Prerequisites: + +To use this package, you must have: + +* Azure subscription - [Create a free account][azure_sub] +* Azure Monitor - [How to use application insights][application_insights_namespace] +* Opentelemetry SDK - [Opentelemetry SDK for Python][ot_sdk_python] +* Python 3.7 or later - [Install Python][python] + ### Install the package Install the Azure Monitor Opentelemetry Distro with [pip][pip]: @@ -33,26 +42,25 @@ pip install azure-monitor-opentelemetry-distro --pre You can use `configure_azure_monitor` to set up instrumentation for your app to Azure Monitor. `configure_azure_monitor` supports the following optional arguments: * connection_string - The [connection string][connection_string_doc] for your Application Insights resource. The connection string will be automatically populated from the `APPLICATIONINSIGHTS_CONNECTION_STRING` environment variable if not explicitly passed in. -* instrumentations = Specifies the libraries with [instrumentations][ot_instrumentations] that you would like to use. Accepts a comma separated list. e.g. `["requests", "flask"]` -* service_name = Specifies the [service][service_semantic_convention_doc] name. -* service_namespace = Specifies the [service][service_semantic_convention_doc] namespace. -* service_instance_id = Specifies the [service][service_semantic_convention_doc] instance id. -* disable_logging = If set to `True`, disables collection and export of logging telemetry. Defaults to False. -* logging_level = Specifies the [logging level][logging_level] of the Opentelemetry Logging Handler. Defaults to logging.NOTSET. +* instrumentations - Specifies the libraries with [instrumentations][ot_instrumentations] that you would like to use. Accepts a comma separated list. e.g. `["requests", "flask"]` +* service_name - Specifies the [service][service_semantic_convention_doc] name. +* service_namespace - Specifies the [service][service_semantic_convention_doc] namespace. +* service_instance_id - Specifies the [service][service_semantic_convention_doc] instance id. +* disable_logging - If set to `True`, disables collection and export of logging telemetry. +* disable_metrics - If set to `True`, disables collection and export of metric telemetry. +* disable_tracing - If set to `True`, disables collection and export of distributed tracing telemetry. +* logging_level - Specifies the [logging level][logging_level] of the Opentelemetry Logging Handler. Ex: logging.WARNING. * logger_name = Specifies the [logger name][logger_name_hierarchy_doc] under which all logging will be instrumented. Defaults to "" which corresponds to the root logger. -* logging_export_interval_millis = Specifies the export interval of the logging exporter in milliseconds. Defaults to 30,000. -* disable_tracing = If set to `True`, disables collection and export of distributed tracing telemetry. -* sampling_ratio = Specifies the ratio of distributed tracing telemetry to be [sampled][application_insights_sampling]. Accepted values are in the range [0,1]. Defaults to 1.0, meaning no telemetry is sampled out. -* tracing_export_interval_millis = Specifies the export interval of the distributed tracing exporter in milliseconds. Defaults to 30,000. +* logging_export_interval_millis - Specifies the logging export interval in milliseconds. Defaults to 30,000. +* views - Specifies the list of [views][opentelemetry_specification_view] to configure for the metric pipeline. See [here][ot_sdk_python_view_examples] for example usage. +* sampling_ratio - Specifies the ratio of distributed tracing telemetry to be [sampled][application_insights_sampling]. Accepted values are in the range [0,1]. Defaults to 1.0, meaning no telemetry is sampled out. +* tracing_export_interval_millis - Specifies the distributed tracing export interval in milliseconds. Defaults to 30,000. See additional [configuration related to exporting here][exporter_configuration_docs]. -### Prerequisites: -To use this package, you must have: -* Azure subscription - [Create a free account][azure_sub] -* Azure Monitor - [How to use application insights][application_insights_namespace] -* Opentelemetry SDK - [Opentelemetry SDK for Python][ot_sdk_python] -* Python 3.7 or later - [Install Python][python] +### Example code + +Samples are available [here][samples] to demonstrate how to utilize the above configuration options. ### Additional documentation @@ -72,10 +80,13 @@ To use this package, you must have: [ot_instrumentations]: https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation [ot_python_docs]: https://opentelemetry.io/docs/instrumentation/python/ [ot_sdk_python]: https://github.com/open-telemetry/opentelemetry-python +[ot_sdk_python_view_examples]: https://github.com/open-telemetry/opentelemetry-python/tree/main/docs/examples/metrics/views [opentelemetry_instrumentation_requests]: https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-requests [opentelemetry_instrumentation_django]: https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-django [opentelemetry_instrumentation_flask]: https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-flask [opentelemetry_instrumentation_psycopg2]: https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-psycopg2 +[opentelemetry_specification_view]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#view [python]: https://www.python.org/downloads/ [pip]: https://pypi.org/project/pip/ +[samples]: https://github.com/microsoft/ApplicationInsights-Python/tree/main/azure-monitor-opentelemetry-distro/samples [service_semantic_convention_doc]: https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/resource/semantic_conventions#service \ No newline at end of file diff --git a/azure-monitor-opentelemetry-distro/azure/monitor/opentelemetry/distro/__init__.py b/azure-monitor-opentelemetry-distro/azure/monitor/opentelemetry/distro/__init__.py index 45f2a8c6..2bc29d82 100644 --- a/azure-monitor-opentelemetry-distro/azure/monitor/opentelemetry/distro/__init__.py +++ b/azure-monitor-opentelemetry-distro/azure/monitor/opentelemetry/distro/__init__.py @@ -11,8 +11,10 @@ from azure.monitor.opentelemetry.exporter import ( ApplicationInsightsSampler, AzureMonitorLogExporter, + AzureMonitorMetricExporter, AzureMonitorTraceExporter, ) +from opentelemetry.metrics import set_meter_provider from opentelemetry.sdk._logs import ( LoggerProvider, LoggingHandler, @@ -20,6 +22,8 @@ set_logger_provider, ) from opentelemetry.sdk._logs.export import BatchLogRecordProcessor +from opentelemetry.sdk.metrics import MeterProvider +from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader from opentelemetry.sdk.resources import Resource from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor @@ -48,9 +52,10 @@ def configure_azure_monitor(**kwargs): disable_tracing = configurations.get("disable_tracing", False) disable_logging = configurations.get("disable_logging", False) + disable_metrics = configurations.get("disable_metrics", False) resource = None - if not disable_logging or not disable_tracing: + if not disable_logging or not disable_tracing or not disable_metrics: resource = _get_resource(configurations) # Setup tracing pipeline @@ -61,6 +66,10 @@ def configure_azure_monitor(**kwargs): if not disable_logging: _setup_logging(resource, configurations) + # Setup metrics pipeline + if not disable_metrics: + _setup_metrics(resource, configurations) + # Setup instrumentations # Instrumentations need to be setup last so to use the global providers # instanstiated in the other setup steps @@ -118,6 +127,18 @@ def _setup_logging(resource: Resource, configurations: Dict[str, Any]): getLogger(logger_name).addHandler(handler) +def _setup_metrics(resource: Resource, configurations: Dict[str, Any]): + views = configurations.get("views", ()) + metric_exporter = AzureMonitorMetricExporter(**configurations) + reader = PeriodicExportingMetricReader(metric_exporter) + meter_provider = MeterProvider( + metric_readers=[reader], + resource=resource, + views=views, + ) + set_meter_provider(meter_provider) + + def _setup_instrumentations(configurations: Dict[str, Any]): instrumentations = configurations.get("instrumentations", []) for lib_name in instrumentations: diff --git a/azure-monitor-opentelemetry-distro/samples/logging/simple.py b/azure-monitor-opentelemetry-distro/samples/logging/simple.py index 75912efa..07ca5a7a 100644 --- a/azure-monitor-opentelemetry-distro/samples/logging/simple.py +++ b/azure-monitor-opentelemetry-distro/samples/logging/simple.py @@ -13,6 +13,7 @@ connection_string="", service_name="foo_service", logging_level=WARN, + disable_metrics=True, disable_tracing=True, ) diff --git a/azure-monitor-opentelemetry-distro/samples/metrics/attributes.py b/azure-monitor-opentelemetry-distro/samples/metrics/attributes.py new file mode 100644 index 00000000..729d849d --- /dev/null +++ b/azure-monitor-opentelemetry-distro/samples/metrics/attributes.py @@ -0,0 +1,38 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +from azure.monitor.opentelemetry.distro import configure_azure_monitor +from opentelemetry import metrics + +# Configure Azure monitor collection telemetry pipeline +configure_azure_monitor( + connection_string="", + service_name="metric_attribute_service", + disable_logging=True, + disable_tracing=True, +) + +attribute_set1 = {"key1": "val1"} +attribute_set2 = {"key2": "val2"} +large_attribute_set = {} +for i in range(20): + key = "key{}".format(i) + val = "val{}".format(i) + large_attribute_set[key] = val + +meter = metrics.get_meter_provider().get_meter("sample") + +# Counter +counter = meter.create_counter("attr1_counter") +counter.add(1, attribute_set1) + +# Counter2 +counter2 = meter.create_counter("attr2_counter") +counter2.add(10, attribute_set1) +counter2.add(30, attribute_set2) + +# Counter3 +counter3 = meter.create_counter("large_attr_counter") +counter3.add(100, attribute_set1) +counter3.add(200, large_attribute_set) + +input() diff --git a/azure-monitor-opentelemetry-distro/samples/metrics/instruments.py b/azure-monitor-opentelemetry-distro/samples/metrics/instruments.py new file mode 100644 index 00000000..d9b6f1b8 --- /dev/null +++ b/azure-monitor-opentelemetry-distro/samples/metrics/instruments.py @@ -0,0 +1,61 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +from typing import Iterable + +from azure.monitor.opentelemetry.distro import configure_azure_monitor +from opentelemetry import metrics +from opentelemetry.metrics import CallbackOptions, Observation + +# Configure Azure monitor collection telemetry pipeline +configure_azure_monitor( + connection_string="", + service_name="metric_instrument_service", + disable_logging=True, + disable_tracing=True, +) + +# Create a namespaced meter +meter = metrics.get_meter_provider().get_meter("sample") + +# Callback functions for observable instruments +def observable_counter_func(options: CallbackOptions) -> Iterable[Observation]: + yield Observation(1, {}) + + +def observable_up_down_counter_func( + options: CallbackOptions, +) -> Iterable[Observation]: + yield Observation(-10, {}) + + +def observable_gauge_func(options: CallbackOptions) -> Iterable[Observation]: + yield Observation(9, {}) + + +# Counter +counter = meter.create_counter("counter") +counter.add(1) + +# Async Counter +observable_counter = meter.create_observable_counter( + "observable_counter", [observable_counter_func] +) + +# UpDownCounter +updown_counter = meter.create_up_down_counter("updown_counter") +updown_counter.add(1) +updown_counter.add(-5) + +# Async UpDownCounter +observable_updown_counter = meter.create_observable_up_down_counter( + "observable_updown_counter", [observable_up_down_counter_func] +) + +# Histogram +histogram = meter.create_histogram("histogram") +histogram.record(99.9) + +# Async Gauge +gauge = meter.create_observable_gauge("gauge", [observable_gauge_func]) + +input() diff --git a/azure-monitor-opentelemetry-distro/samples/metrics/views.py b/azure-monitor-opentelemetry-distro/samples/metrics/views.py new file mode 100644 index 00000000..10158f2f --- /dev/null +++ b/azure-monitor-opentelemetry-distro/samples/metrics/views.py @@ -0,0 +1,31 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +from azure.monitor.opentelemetry.distro import configure_azure_monitor +from opentelemetry import metrics +from opentelemetry.sdk.metrics import Counter +from opentelemetry.sdk.metrics.view import View + +# Create a view matching the counter instrument `my.counter` +# and configure the new name `my.counter.total` for the result metrics stream +change_metric_name_view = View( + connection_string="", + instrument_type=Counter, + instrument_name="my.counter", + name="my.counter.total", +) + +# Configure Azure monitor collection telemetry pipeline +configure_azure_monitor( + connection_string="", + service_name="metric_views_service", + disable_logging=True, + disable_tracing=True, + metrics_export_interval_millis=30000, + views=[change_metric_name_view], +) + +meter = metrics.get_meter_provider().get_meter("view-name-change") +my_counter = meter.create_counter("my.counter") +my_counter.add(100) + +input() diff --git a/azure-monitor-opentelemetry-distro/samples/tracing/client.py b/azure-monitor-opentelemetry-distro/samples/tracing/client.py index 5c6730ef..fd56a788 100644 --- a/azure-monitor-opentelemetry-distro/samples/tracing/client.py +++ b/azure-monitor-opentelemetry-distro/samples/tracing/client.py @@ -16,6 +16,7 @@ connection_string="", service_name="client_service_name", disable_logging=True, + disable_metrics=True, instrumentations=["requests"], tracing_export_interval_millis=15000, ) diff --git a/azure-monitor-opentelemetry-distro/samples/tracing/db_psycopg2.py b/azure-monitor-opentelemetry-distro/samples/tracing/db_psycopg2.py index 79a3c5b7..ff9f3e8d 100644 --- a/azure-monitor-opentelemetry-distro/samples/tracing/db_psycopg2.py +++ b/azure-monitor-opentelemetry-distro/samples/tracing/db_psycopg2.py @@ -11,6 +11,7 @@ connection_string="", service_name="psycopg2_service_name", disable_logging=True, + disable_metrics=True, instrumentations=["psycopg2"], tracing_export_interval_millis=15000, ) diff --git a/azure-monitor-opentelemetry-distro/samples/tracing/django/sample/example/views.py b/azure-monitor-opentelemetry-distro/samples/tracing/django/sample/example/views.py index 05da83b4..618e3f23 100644 --- a/azure-monitor-opentelemetry-distro/samples/tracing/django/sample/example/views.py +++ b/azure-monitor-opentelemetry-distro/samples/tracing/django/sample/example/views.py @@ -13,6 +13,7 @@ service_name="django_service_name", instrumentations=["django"], disable_logging=True, + disable_metrics=True, tracing_export_interval_millis=15000, ) diff --git a/azure-monitor-opentelemetry-distro/samples/tracing/server_flask.py b/azure-monitor-opentelemetry-distro/samples/tracing/server_flask.py index 5ff0c5da..6fef59b9 100644 --- a/azure-monitor-opentelemetry-distro/samples/tracing/server_flask.py +++ b/azure-monitor-opentelemetry-distro/samples/tracing/server_flask.py @@ -11,6 +11,7 @@ connection_string="", service_name="flask_service_name", disable_logging=True, + disable_metrics=True, instrumentations=["flask"], tracing_export_interval_millis=15000, ) diff --git a/azure-monitor-opentelemetry-distro/samples/tracing/simple.py b/azure-monitor-opentelemetry-distro/samples/tracing/simple.py index fbca7f1f..2756bcc0 100644 --- a/azure-monitor-opentelemetry-distro/samples/tracing/simple.py +++ b/azure-monitor-opentelemetry-distro/samples/tracing/simple.py @@ -12,6 +12,7 @@ service_name="foo_service", tracing_export_interval_millis=15000, disable_logging=True, + disable_metrics=True, ) tracer = trace.get_tracer(__name__) diff --git a/azure-monitor-opentelemetry-distro/tests/configuration/test_configure.py b/azure-monitor-opentelemetry-distro/tests/configuration/test_configure.py index 0a36328a..da7cceec 100644 --- a/azure-monitor-opentelemetry-distro/tests/configuration/test_configure.py +++ b/azure-monitor-opentelemetry-distro/tests/configuration/test_configure.py @@ -20,6 +20,7 @@ _get_resource, _setup_instrumentations, _setup_logging, + _setup_metrics, _setup_tracing, configure_azure_monitor, ) @@ -30,6 +31,9 @@ class TestConfigure(unittest.TestCase): @patch( "azure.monitor.opentelemetry.distro._setup_instrumentations", ) + @patch( + "azure.monitor.opentelemetry.distro._setup_metrics", + ) @patch( "azure.monitor.opentelemetry.distro._setup_logging", ) @@ -44,12 +48,14 @@ def test_configure_azure_monitor( resource_mock, tracing_mock, logging_mock, + metrics_mock, instrumentation_mock, ): kwargs = { "connection_string": "test_cs", - "disable_logging": False, "disable_tracing": False, + "disable_logging": False, + "disable_metrics": False, "logging_export_interval_millis": 10000, "logging_level": "test_logging_level", "logger_name": "test_logger_name", @@ -58,6 +64,7 @@ def test_configure_azure_monitor( "service_instance_id": "test_id", "sampling_ratio": 0.5, "tracing_export_interval_millis": 15000, + "views": "test_views", } resource_init_mock = Mock() resource_mock.return_value = resource_init_mock @@ -65,11 +72,15 @@ def test_configure_azure_monitor( resource_mock.assert_called_once_with(kwargs) tracing_mock.assert_called_once_with(resource_init_mock, kwargs) logging_mock.assert_called_once_with(resource_init_mock, kwargs) + metrics_mock.assert_called_once_with(resource_init_mock, kwargs) instrumentation_mock.assert_called_once_with(kwargs) @patch( "azure.monitor.opentelemetry.distro._setup_instrumentations", ) + @patch( + "azure.monitor.opentelemetry.distro._setup_metrics", + ) @patch( "azure.monitor.opentelemetry.distro._setup_logging", ) @@ -84,12 +95,14 @@ def test_configure_azure_monitor_disable_tracing( resource_mock, tracing_mock, logging_mock, + metrics_mock, instrumentation_mock, ): kwargs = { "connection_string": "test_cs", - "disable_logging": False, "disable_tracing": True, + "disable_logging": False, + "disable_metrics": False, "logging_export_interval_millis": 10000, "logging_level": "test_logging_level", "logger_name": "test_logger_name", @@ -98,6 +111,7 @@ def test_configure_azure_monitor_disable_tracing( "service_instance_id": "test_id", "sampling_ratio": 0.5, "tracing_export_interval_millis": 15000, + "views": "test_views", } resource_init_mock = Mock() resource_mock.return_value = resource_init_mock @@ -105,11 +119,15 @@ def test_configure_azure_monitor_disable_tracing( resource_mock.assert_called_once_with(kwargs) tracing_mock.assert_not_called() logging_mock.assert_called_once_with(resource_init_mock, kwargs) + metrics_mock.assert_called_once_with(resource_init_mock, kwargs) instrumentation_mock.assert_called_once_with(kwargs) @patch( "azure.monitor.opentelemetry.distro._setup_instrumentations", ) + @patch( + "azure.monitor.opentelemetry.distro._setup_metrics", + ) @patch( "azure.monitor.opentelemetry.distro._setup_logging", ) @@ -124,12 +142,14 @@ def test_configure_azure_monitor_disable_logging( resource_mock, tracing_mock, logging_mock, + metrics_mock, instrumentation_mock, ): kwargs = { "connection_string": "test_cs", - "disable_logging": True, "disable_tracing": False, + "disable_logging": True, + "disable_metrics": False, "logging_export_interval_millis": 10000, "logging_level": "test_logging_level", "logger_name": "test_logger_name", @@ -138,6 +158,7 @@ def test_configure_azure_monitor_disable_logging( "service_instance_id": "test_id", "sampling_ratio": 0.5, "tracing_export_interval_millis": 15000, + "views": "test_views", } resource_init_mock = Mock() resource_mock.return_value = resource_init_mock @@ -145,6 +166,53 @@ def test_configure_azure_monitor_disable_logging( resource_mock.assert_called_once_with(kwargs) tracing_mock.assert_called_once_with(resource_init_mock, kwargs) logging_mock.assert_not_called() + metrics_mock.assert_called_once_with(resource_init_mock, kwargs) + instrumentation_mock.assert_called_once_with(kwargs) + + @patch( + "azure.monitor.opentelemetry.distro._setup_instrumentations", + ) + @patch( + "azure.monitor.opentelemetry.distro._setup_metrics", + ) + @patch( + "azure.monitor.opentelemetry.distro._setup_logging", + ) + @patch( + "azure.monitor.opentelemetry.distro._setup_tracing", + ) + @patch( + "azure.monitor.opentelemetry.distro._get_resource", + ) + def test_configure_azure_monitor_disable_metrics( + self, + resource_mock, + tracing_mock, + logging_mock, + metrics_mock, + instrumentation_mock, + ): + kwargs = { + "connection_string": "test_cs", + "disable_tracing": False, + "disable_logging": False, + "disable_metrics": True, + "logging_export_interval_millis": 10000, + "logging_level": "test_logging_level", + "service_name": "test_service_name", + "service_namespace": "test_namespace", + "service_instance_id": "test_id", + "sampling_ratio": 0.5, + "tracing_export_interval_millis": 15000, + "views": "test_views", + } + resource_init_mock = Mock() + resource_mock.return_value = resource_init_mock + configure_azure_monitor(**kwargs) + resource_mock.assert_called_once_with(kwargs) + tracing_mock.assert_called_once_with(resource_init_mock, kwargs) + logging_mock.assert_called_once_with(resource_init_mock, kwargs) + metrics_mock.assert_not_called() instrumentation_mock.assert_called_once_with(kwargs) @patch( @@ -297,6 +365,49 @@ def test_setup_logging( logging_handler_init_mock ) + @patch( + "azure.monitor.opentelemetry.distro.PeriodicExportingMetricReader", + ) + @patch( + "azure.monitor.opentelemetry.distro.AzureMonitorMetricExporter", + ) + @patch( + "azure.monitor.opentelemetry.distro.set_meter_provider", + ) + @patch( + "azure.monitor.opentelemetry.distro.MeterProvider", + autospec=True, + ) + def test_setup_metrics( + self, + mp_mock, + set_meter_provider_mock, + metric_exporter_mock, + reader_mock, + ): + resource_mock = Mock() + mp_init_mock = Mock() + mp_mock.return_value = mp_init_mock + metric_exp_init_mock = Mock() + metric_exporter_mock.return_value = metric_exp_init_mock + reader_init_mock = Mock() + reader_mock.return_value = reader_init_mock + + configurations = { + "connection_string": "test_cs", + "disable_metrics": False, + "views": "test_views", + } + _setup_metrics(resource_mock, configurations) + mp_mock.assert_called_once_with( + metric_readers=[reader_init_mock], + resource=resource_mock, + views="test_views", + ) + set_meter_provider_mock.assert_called_once_with(mp_init_mock) + metric_exporter_mock.assert_called_once() + reader_mock.assert_called_once_with(metric_exp_init_mock) + @patch("azure.monitor.opentelemetry.distro.getattr") def test_setup_instrumentations( self,