From 8ba9028d749c761bfe5a9e58f3c01fcf953327d8 Mon Sep 17 00:00:00 2001 From: jerevoss Date: Tue, 4 Jun 2024 14:55:47 -0700 Subject: [PATCH 1/2] Configure exporters and sampler directly in configurator --- .../azure-monitor-opentelemetry/CHANGELOG.md | 5 +- .../_autoinstrumentation/configurator.py | 27 ++++++++- .../_autoinstrumentation/distro.py | 18 ------ .../azure/monitor/opentelemetry/_constants.py | 10 +++- .../azure/monitor/opentelemetry/_version.py | 2 +- .../azure-monitor-opentelemetry/setup.py | 15 ++--- .../autoinstrumentation/test_configurator.py | 56 ++++++++++++++++++- .../tests/autoinstrumentation/test_distro.py | 20 +++---- 8 files changed, 109 insertions(+), 44 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry/CHANGELOG.md b/sdk/monitor/azure-monitor-opentelemetry/CHANGELOG.md index 547397865f13..b3e7b5a8e5ee 100644 --- a/sdk/monitor/azure-monitor-opentelemetry/CHANGELOG.md +++ b/sdk/monitor/azure-monitor-opentelemetry/CHANGELOG.md @@ -1,9 +1,12 @@ # Release History -## 1.5.1 (Unreleased) +## 1.6.0 (Unreleased) ### Features Added +- Rework autoinstrumentation: Configure exporters and samplers directly + ([#35890](https://github.com/Azure/azure-sdk-for-python/pull/35890)) + ### Breaking Changes ### Bugs Fixed diff --git a/sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_autoinstrumentation/configurator.py b/sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_autoinstrumentation/configurator.py index e229ff901902..479dd0d092bf 100644 --- a/sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_autoinstrumentation/configurator.py +++ b/sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_autoinstrumentation/configurator.py @@ -5,12 +5,25 @@ # -------------------------------------------------------------------------- +from os import environ from warnings import warn +from opentelemetry.environment_variables import ( + OTEL_LOGS_EXPORTER, + OTEL_METRICS_EXPORTER, + OTEL_TRACES_EXPORTER, +) from opentelemetry.sdk._configuration import _OTelSDKConfigurator +from azure.monitor.opentelemetry.exporter import ApplicationInsightsSampler # pylint: disable=import-error,no-name-in-module from azure.monitor.opentelemetry.exporter._utils import _is_attach_enabled # pylint: disable=import-error,no-name-in-module -from azure.monitor.opentelemetry._constants import _PREVIEW_ENTRY_POINT_WARNING +from azure.monitor.opentelemetry._constants import ( + _PREVIEW_ENTRY_POINT_WARNING, + LOG_EXPORTER_NAMES_ARG, + METRIC_EXPORTER_NAMES_ARG, + SAMPLER_ARG, + TRACE_EXPORTER_NAMES_ARG, +) from azure.monitor.opentelemetry._diagnostics.diagnostic_logging import ( AzureDiagnosticLogging, _ATTACH_FAILURE_CONFIGURATOR, @@ -26,6 +39,18 @@ def _configure(self, **kwargs): if not _is_attach_enabled(): warn(_PREVIEW_ENTRY_POINT_WARNING) try: + if environ.get(OTEL_TRACES_EXPORTER, "").lower().strip() != "none": + kwargs.setdefault(TRACE_EXPORTER_NAMES_ARG, ["azure-monitor-opentelemetry-exporter"]) + try: + sample_rate = float(environ.get("OTEL_TRACES_SAMPLER_ARG", 1.0)) + except ValueError: + sample_rate = 1.0 + kwargs.setdefault(SAMPLER_ARG, ApplicationInsightsSampler(sample_rate)) + if environ.get(OTEL_METRICS_EXPORTER, "").lower().strip() != "none": + kwargs.setdefault(METRIC_EXPORTER_NAMES_ARG, ["azure-monitor-opentelemetry-exporter"]) + if environ.get(OTEL_LOGS_EXPORTER, "").lower().strip() != "none": + kwargs.setdefault(LOG_EXPORTER_NAMES_ARG, ["azure-monitor-opentelemetry-exporter"]) + # As of OTel SDK 1.25.0, exporters passed as kwargs will be added to those specified in env vars. super()._configure(**kwargs) AzureStatusLogger.log_status(True) AzureDiagnosticLogging.info( diff --git a/sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_autoinstrumentation/distro.py b/sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_autoinstrumentation/distro.py index 23935eb87653..2bb4ea885d95 100644 --- a/sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_autoinstrumentation/distro.py +++ b/sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_autoinstrumentation/distro.py @@ -6,18 +6,12 @@ from os import environ from warnings import warn -from opentelemetry.environment_variables import ( - OTEL_LOGS_EXPORTER, - OTEL_METRICS_EXPORTER, - OTEL_TRACES_EXPORTER, -) from opentelemetry.instrumentation.distro import ( # type: ignore BaseDistro, ) from opentelemetry.sdk.environment_variables import ( _OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED, OTEL_EXPERIMENTAL_RESOURCE_DETECTORS, - OTEL_TRACES_SAMPLER, ) from azure.core.settings import settings @@ -62,18 +56,6 @@ def _configure(self, **kwargs) -> None: def _configure_auto_instrumentation() -> None: - environ.setdefault( - OTEL_METRICS_EXPORTER, "azure_monitor_opentelemetry_exporter" - ) - environ.setdefault( - OTEL_TRACES_EXPORTER, "azure_monitor_opentelemetry_exporter" - ) - environ.setdefault( - OTEL_LOGS_EXPORTER, "azure_monitor_opentelemetry_exporter" - ) - environ.setdefault( - OTEL_TRACES_SAMPLER, "azure_monitor_opentelemetry_sampler" - ) environ.setdefault( _OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED, "true" ) diff --git a/sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_constants.py b/sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_constants.py index 7cfc09ec5f7b..f7d6137aef9e 100644 --- a/sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_constants.py +++ b/sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_constants.py @@ -8,7 +8,7 @@ _AZURE_MONITOR_DISTRO_VERSION_ARG, ) -# --------------------Configuration------------------------------------------ +# --------------------Distro Configuration------------------------------------------ CONNECTION_STRING_ARG = "connection_string" ENABLE_LIVE_METRICS_ARG = "enable_live_metrics" @@ -24,6 +24,14 @@ SPAN_PROCESSORS_ARG = "span_processors" +# --------------------Autoinstrumentation Configuration------------------------------------------ + +LOG_EXPORTER_NAMES_ARG = "log_exporter_names" +METRIC_EXPORTER_NAMES_ARG = "metric_exporter_names" +SAMPLER_ARG = "sampler" +TRACE_EXPORTER_NAMES_ARG = "trace_exporter_names" + + # --------------------Diagnostic/status logging------------------------------ _LOG_PATH_LINUX = "/var/log/applicationinsights" diff --git a/sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_version.py b/sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_version.py index c976ef6f0545..16918308191f 100644 --- a/sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_version.py +++ b/sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_version.py @@ -4,4 +4,4 @@ # license information. # -------------------------------------------------------------------------- -VERSION = "1.5.1" +VERSION = "1.6.0" diff --git a/sdk/monitor/azure-monitor-opentelemetry/setup.py b/sdk/monitor/azure-monitor-opentelemetry/setup.py index cf8e66da403c..7a72dd0751a9 100644 --- a/sdk/monitor/azure-monitor-opentelemetry/setup.py +++ b/sdk/monitor/azure-monitor-opentelemetry/setup.py @@ -88,14 +88,15 @@ "azure-core<2.0.0,>=1.28.0", "azure-core-tracing-opentelemetry~=1.0.0b11", "azure-monitor-opentelemetry-exporter~=1.0.0b26", - "opentelemetry-instrumentation-django~=0.42b0", - "opentelemetry-instrumentation-fastapi~=0.42b0", - "opentelemetry-instrumentation-flask~=0.42b0", - "opentelemetry-instrumentation-psycopg2~=0.42b0", - "opentelemetry-instrumentation-requests~=0.42b0", - "opentelemetry-instrumentation-urllib~=0.42b0", - "opentelemetry-instrumentation-urllib3~=0.42b0", + "opentelemetry-instrumentation-django~=0.46b0", + "opentelemetry-instrumentation-fastapi~=0.46b0", + "opentelemetry-instrumentation-flask~=0.46b0", + "opentelemetry-instrumentation-psycopg2~=0.46b0", + "opentelemetry-instrumentation-requests~=0.46b0", + "opentelemetry-instrumentation-urllib~=0.46b0", + "opentelemetry-instrumentation-urllib3~=0.46b0", "opentelemetry-resource-detector-azure~=0.1.4", + "opentelemetry-sdk~=1.25", ], entry_points={ "opentelemetry_distro": [ diff --git a/sdk/monitor/azure-monitor-opentelemetry/tests/autoinstrumentation/test_configurator.py b/sdk/monitor/azure-monitor-opentelemetry/tests/autoinstrumentation/test_configurator.py index c1c2cf4c04df..2eb027fc62d0 100644 --- a/sdk/monitor/azure-monitor-opentelemetry/tests/autoinstrumentation/test_configurator.py +++ b/sdk/monitor/azure-monitor-opentelemetry/tests/autoinstrumentation/test_configurator.py @@ -12,29 +12,79 @@ ) +@patch.dict("os.environ", {}, clear=True) class TestConfigurator(TestCase): + @patch("azure.monitor.opentelemetry._autoinstrumentation.configurator.super") + @patch("azure.monitor.opentelemetry._autoinstrumentation.configurator.ApplicationInsightsSampler") @patch("azure.monitor.opentelemetry._autoinstrumentation.configurator._is_attach_enabled", return_value=True) @patch( "azure.monitor.opentelemetry._autoinstrumentation.configurator.AzureDiagnosticLogging" ) - def test_configure(self, mock_diagnostics, attach_mock): + def test_configure(self, mock_diagnostics, attach_mock, sampler_mock, super_mock): + sampler_mock.return_value = "TEST_SAMPLER" configurator = AzureMonitorConfigurator() with warnings.catch_warnings(): warnings.simplefilter("error") - configurator._configure() + configurator._configure(auto_instrumentation_version="TEST_VERSION") + sampler_mock.assert_called_once_with(1.0) + super_mock()._configure.assert_called_once_with( + auto_instrumentation_version="TEST_VERSION", + trace_exporter_names=["azure-monitor-opentelemetry-exporter"], + metric_exporter_names=["azure-monitor-opentelemetry-exporter"], + log_exporter_names=["azure-monitor-opentelemetry-exporter"], + sampler="TEST_SAMPLER", + ) mock_diagnostics.info.assert_called_once_with( "Azure Monitor Configurator configured successfully.", _ATTACH_SUCCESS_CONFIGURATOR ) + + @patch.dict("os.environ", {"OTEL_TRACES_SAMPLER_ARG": "0.5"}, clear=True) + @patch("azure.monitor.opentelemetry._autoinstrumentation.configurator.super") + @patch("azure.monitor.opentelemetry._autoinstrumentation.configurator.ApplicationInsightsSampler") + @patch("azure.monitor.opentelemetry._autoinstrumentation.configurator._is_attach_enabled", return_value=True) + @patch( + "azure.monitor.opentelemetry._autoinstrumentation.configurator.AzureDiagnosticLogging" + ) + def test_configure_sampler_arg(self, mock_diagnostics, attach_mock, sampler_mock, super_mock): + sampler_mock.return_value = "TEST_SAMPLER" + configurator = AzureMonitorConfigurator() + with warnings.catch_warnings(): + warnings.simplefilter("error") + configurator._configure(auto_instrumentation_version="TEST_VERSION") + sampler_mock.assert_called_once_with(0.5) + super_mock()._configure.assert_called_once_with( + auto_instrumentation_version="TEST_VERSION", + trace_exporter_names=["azure-monitor-opentelemetry-exporter"], + metric_exporter_names=["azure-monitor-opentelemetry-exporter"], + log_exporter_names=["azure-monitor-opentelemetry-exporter"], + sampler="TEST_SAMPLER", + ) + mock_diagnostics.info.assert_called_once_with( + "Azure Monitor Configurator configured successfully.", + _ATTACH_SUCCESS_CONFIGURATOR + ) + + + @patch("azure.monitor.opentelemetry._autoinstrumentation.configurator.super") + @patch("azure.monitor.opentelemetry._autoinstrumentation.configurator.ApplicationInsightsSampler") @patch("azure.monitor.opentelemetry._autoinstrumentation.configurator._is_attach_enabled", return_value=False) @patch( "azure.monitor.opentelemetry._autoinstrumentation.configurator.AzureDiagnosticLogging" ) - def test_configure_preview(self, mock_diagnostics, attach_mock): + def test_configure_preview(self, mock_diagnostics, attach_mock, sampler_mock, super_mock): + sampler_mock.return_value = "TEST_SAMPLER" configurator = AzureMonitorConfigurator() with self.assertWarns(Warning): configurator._configure() + sampler_mock.assert_called_once_with(1.0) + super_mock()._configure.assert_called_once_with( + trace_exporter_names=["azure-monitor-opentelemetry-exporter"], + metric_exporter_names=["azure-monitor-opentelemetry-exporter"], + log_exporter_names=["azure-monitor-opentelemetry-exporter"], + sampler="TEST_SAMPLER", + ) mock_diagnostics.info.assert_called_once_with( "Azure Monitor Configurator configured successfully.", _ATTACH_SUCCESS_CONFIGURATOR diff --git a/sdk/monitor/azure-monitor-opentelemetry/tests/autoinstrumentation/test_distro.py b/sdk/monitor/azure-monitor-opentelemetry/tests/autoinstrumentation/test_distro.py index c4a59d293109..d935d2052168 100644 --- a/sdk/monitor/azure-monitor-opentelemetry/tests/autoinstrumentation/test_distro.py +++ b/sdk/monitor/azure-monitor-opentelemetry/tests/autoinstrumentation/test_distro.py @@ -35,20 +35,16 @@ def test_configure(self, mock_diagnostics, azure_core_mock, attach_mock): self.assertEqual( environ, { - "OTEL_METRICS_EXPORTER": "azure_monitor_opentelemetry_exporter", - "OTEL_TRACES_EXPORTER": "azure_monitor_opentelemetry_exporter", - "OTEL_LOGS_EXPORTER": "azure_monitor_opentelemetry_exporter", - "OTEL_TRACES_SAMPLER": "azure_monitor_opentelemetry_sampler", "OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED": "true", "OTEL_EXPERIMENTAL_RESOURCE_DETECTORS": "azure_app_service", } ) @patch.dict("os.environ", { - "OTEL_METRICS_EXPORTER": "custom_metrics_exporter", - "OTEL_TRACES_EXPORTER": "custom_traces_exporter", - "OTEL_LOGS_EXPORTER": "custom_logs_exporter", - "OTEL_TRACES_SAMPLER": "custom_traces_sampler", + # "OTEL_METRICS_EXPORTER": "custom_metrics_exporter", + # "OTEL_TRACES_EXPORTER": "custom_traces_exporter", + # "OTEL_LOGS_EXPORTER": "custom_logs_exporter", + # "OTEL_TRACES_SAMPLER": "custom_traces_sampler", "OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED": "false", "OTEL_EXPERIMENTAL_RESOURCE_DETECTORS": "custom_resource_detector", }, clear=True) @@ -72,10 +68,10 @@ def test_configure_env_vars_set(self, mock_diagnostics, azure_core_mock, attach_ self.assertEqual( environ, { - "OTEL_METRICS_EXPORTER": "custom_metrics_exporter", - "OTEL_TRACES_EXPORTER": "custom_traces_exporter", - "OTEL_LOGS_EXPORTER": "custom_logs_exporter", - "OTEL_TRACES_SAMPLER": "custom_traces_sampler", + # "OTEL_METRICS_EXPORTER": "custom_metrics_exporter", + # "OTEL_TRACES_EXPORTER": "custom_traces_exporter", + # "OTEL_LOGS_EXPORTER": "custom_logs_exporter", + # "OTEL_TRACES_SAMPLER": "custom_traces_sampler", "OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED": "false", "OTEL_EXPERIMENTAL_RESOURCE_DETECTORS": "custom_resource_detector", } From 3a691cd0409a6948ff517feb9fd13e6f1ce82050 Mon Sep 17 00:00:00 2001 From: jerevoss Date: Tue, 4 Jun 2024 16:19:29 -0700 Subject: [PATCH 2/2] lint --- .../monitor/opentelemetry/_autoinstrumentation/configurator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_autoinstrumentation/configurator.py b/sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_autoinstrumentation/configurator.py index 479dd0d092bf..cbbd23f58c57 100644 --- a/sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_autoinstrumentation/configurator.py +++ b/sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_autoinstrumentation/configurator.py @@ -43,7 +43,7 @@ def _configure(self, **kwargs): kwargs.setdefault(TRACE_EXPORTER_NAMES_ARG, ["azure-monitor-opentelemetry-exporter"]) try: sample_rate = float(environ.get("OTEL_TRACES_SAMPLER_ARG", 1.0)) - except ValueError: + except ValueError: sample_rate = 1.0 kwargs.setdefault(SAMPLER_ARG, ApplicationInsightsSampler(sample_rate)) if environ.get(OTEL_METRICS_EXPORTER, "").lower().strip() != "none":