From 5158ca945d110bd7b5ba5f7abc57378998be431b Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Tue, 2 Jun 2026 14:59:34 -0700 Subject: [PATCH 1/4] Track live metrics disabling in feature sdkstats --- .../CHANGELOG.md | 2 + .../exporter/_quickpulse/_live_metrics.py | 10 ++- .../tests/quickpulse/test_live_metrics.py | 80 +++++++++++++++---- 3 files changed, 73 insertions(+), 19 deletions(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md b/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md index fbcf65d95e09..9d926b53c761 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md @@ -16,6 +16,8 @@ ### Bugs Fixed ### Other Changes +- Track live metrics disabling in feature SDKstats + ([#47141](https://github.com/Azure/azure-sdk-for-python/pull/47141)) ## 1.0.0b52 (2026-05-12) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_quickpulse/_live_metrics.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_quickpulse/_live_metrics.py index b9d7b76e4e64..dcc5cb30bd59 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_quickpulse/_live_metrics.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_quickpulse/_live_metrics.py @@ -45,10 +45,7 @@ def enable_live_metrics(**kwargs: Any) -> None: # pylint: disable=C4758 if config_manager: config_manager.register_callback(get_quickpulse_configuration_callback) - # We can detect feature usage for statsbeat since we are in an opt-in model currently - # Once we move to live metrics on-by-default, we will have to check for both explicit usage - # and whether or not user is actually using live metrics (being on live metrics blade in UX) - set_statsbeat_live_metrics_feature_set() + # Live metrics disable tracking is handled via local config flow. def get_quickpulse_configuration_callback(settings: Dict[str, str]) -> None: @@ -74,8 +71,13 @@ def get_quickpulse_configuration_callback(settings: Dict[str, str]) -> None: resource=manager._resource, # pylint:disable=protected-access ) elif live_metrics_enabled is False and manager.is_initialized(): + # Track explicit live metrics disable for statsbeat feature reporting. + set_statsbeat_live_metrics_feature_set() # Disable live metrics if it's currently enabled manager.shutdown() + elif live_metrics_enabled is False: + # Track explicit live metrics disable even when quickpulse is already off. + set_statsbeat_live_metrics_feature_set() def shutdown_live_metrics() -> bool: diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/quickpulse/test_live_metrics.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/quickpulse/test_live_metrics.py index 3559538dd005..e38be1acea13 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/quickpulse/test_live_metrics.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/tests/quickpulse/test_live_metrics.py @@ -8,7 +8,10 @@ from opentelemetry.sdk.resources import Resource -from azure.monitor.opentelemetry.exporter._quickpulse._live_metrics import enable_live_metrics +from azure.monitor.opentelemetry.exporter._quickpulse._live_metrics import ( + enable_live_metrics, + get_quickpulse_configuration_callback, +) class TestLiveMetrics(unittest.TestCase): @@ -35,8 +38,8 @@ def test_enable_live_metrics_basic(self, manager_mock, statsbeat_mock): credential="test-credential", ) - # Verify statsbeat feature was set - statsbeat_mock.assert_called_once() + # Enable path should not explicitly track statsbeat feature. + statsbeat_mock.assert_not_called() @mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics.set_statsbeat_live_metrics_feature_set") @mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics.get_quickpulse_manager") @@ -54,8 +57,8 @@ def test_enable_live_metrics_initialization_fails(self, manager_mock, statsbeat_ # Verify manager was initialized with connection string mock_manager_instance.initialize.assert_called_once_with(connection_string="InstrumentationKey=test-key") - # Verify statsbeat feature was still set (regardless of initialization success) - statsbeat_mock.assert_called_once() + # Enable path should not explicitly track statsbeat feature. + statsbeat_mock.assert_not_called() @mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics.set_statsbeat_live_metrics_feature_set") @mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics.get_quickpulse_manager") @@ -73,8 +76,8 @@ def test_enable_live_metrics_with_minimal_args(self, manager_mock, statsbeat_moc # Verify initialization was attempted with no kwargs mock_manager_instance.initialize.assert_called_once_with() - # Verify statsbeat feature was set - statsbeat_mock.assert_called_once() + # Enable path should not explicitly track statsbeat feature. + statsbeat_mock.assert_not_called() @mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics.set_statsbeat_live_metrics_feature_set") @mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics.get_quickpulse_manager") @@ -142,8 +145,8 @@ def test_enable_live_metrics_empty_string_connection(self, manager_mock, statsbe # Verify initialization was called with empty string mock_manager_instance.initialize.assert_called_once_with(connection_string="") - # Verify statsbeat feature was set - statsbeat_mock.assert_called_once() + # Enable path should not explicitly track statsbeat feature. + statsbeat_mock.assert_not_called() @mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics.set_statsbeat_live_metrics_feature_set") @mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics.get_quickpulse_manager") @@ -178,8 +181,8 @@ def test_enable_live_metrics_complex_resource(self, manager_mock, statsbeat_mock connection_string="InstrumentationKey=test-key", resource=mock_resource ) - # Verify statsbeat feature was set - statsbeat_mock.assert_called_once() + # Enable path should not explicitly track statsbeat feature. + statsbeat_mock.assert_not_called() @mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics.set_statsbeat_live_metrics_feature_set") @mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics.get_quickpulse_manager") @@ -205,8 +208,8 @@ def test_enable_live_metrics_multiple_calls(self, manager_mock, statsbeat_mock): ] mock_manager_instance.initialize.assert_has_calls(expected_calls) - # Verify statsbeat feature was set twice - self.assertEqual(statsbeat_mock.call_count, 2) + # Enable path should not explicitly track statsbeat feature. + statsbeat_mock.assert_not_called() @mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics.set_statsbeat_live_metrics_feature_set") @mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics.get_quickpulse_manager") @@ -233,8 +236,55 @@ def test_enable_live_metrics_kwargs_preservation(self, manager_mock, statsbeat_m # Verify all kwargs were passed through to initialize mock_manager_instance.initialize.assert_called_once_with(**custom_kwargs) - # Verify statsbeat feature was set - statsbeat_mock.assert_called_once() + # Enable path should not explicitly track statsbeat feature. + statsbeat_mock.assert_not_called() + + @mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics.set_statsbeat_live_metrics_feature_set") + @mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics.evaluate_feature") + @mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics.get_quickpulse_manager") + def test_get_quickpulse_configuration_callback_disable_tracks_feature( + self, manager_mock, evaluate_mock, statsbeat_mock + ): + mock_manager_instance = mock.Mock() + mock_manager_instance.is_initialized.return_value = True + manager_mock.return_value = mock_manager_instance + evaluate_mock.return_value = False + + get_quickpulse_configuration_callback({}) + + statsbeat_mock.assert_called_once_with() + mock_manager_instance.shutdown.assert_called_once() + + @mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics.set_statsbeat_live_metrics_feature_set") + @mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics.evaluate_feature") + @mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics.get_quickpulse_manager") + def test_get_quickpulse_configuration_callback_enable_does_not_track_feature( + self, manager_mock, evaluate_mock, statsbeat_mock + ): + mock_manager_instance = mock.Mock() + mock_manager_instance.is_initialized.return_value = True + manager_mock.return_value = mock_manager_instance + evaluate_mock.return_value = True + + get_quickpulse_configuration_callback({}) + + statsbeat_mock.assert_not_called() + + @mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics.set_statsbeat_live_metrics_feature_set") + @mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics.evaluate_feature") + @mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics.get_quickpulse_manager") + def test_get_quickpulse_configuration_callback_disable_tracks_feature_when_manager_off( + self, manager_mock, evaluate_mock, statsbeat_mock + ): + mock_manager_instance = mock.Mock() + mock_manager_instance.is_initialized.return_value = False + manager_mock.return_value = mock_manager_instance + evaluate_mock.return_value = False + + get_quickpulse_configuration_callback({}) + + statsbeat_mock.assert_called_once_with() + mock_manager_instance.shutdown.assert_not_called() # cSpell:enable From bc96e1b929dae32d132f02ec9b73b3d355740386 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Tue, 2 Jun 2026 15:03:37 -0700 Subject: [PATCH 2/4] Update PR number --- sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md b/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md index 9d926b53c761..d791aab6e7e9 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md @@ -17,7 +17,7 @@ ### Other Changes - Track live metrics disabling in feature SDKstats - ([#47141](https://github.com/Azure/azure-sdk-for-python/pull/47141)) + ([#47297](https://github.com/Azure/azure-sdk-for-python/pull/47297)) ## 1.0.0b52 (2026-05-12) From 37886d7f5aab0b6f9cd720f14127d3dd5640a454 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Wed, 3 Jun 2026 09:35:08 -0700 Subject: [PATCH 3/4] Address feedback --- .../monitor/opentelemetry/exporter/_quickpulse/_live_metrics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_quickpulse/_live_metrics.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_quickpulse/_live_metrics.py index dcc5cb30bd59..1ab63b7734e3 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_quickpulse/_live_metrics.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_quickpulse/_live_metrics.py @@ -71,7 +71,7 @@ def get_quickpulse_configuration_callback(settings: Dict[str, str]) -> None: resource=manager._resource, # pylint:disable=protected-access ) elif live_metrics_enabled is False and manager.is_initialized(): - # Track explicit live metrics disable for statsbeat feature reporting. + # Track explicit live metrics disable for statsbeat feature reporting. (Tracking the disable live metrics feature starting 06/03/2026) set_statsbeat_live_metrics_feature_set() # Disable live metrics if it's currently enabled manager.shutdown() From a2979f21b5ece1deacacfcb94db4ed0495ad4454 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Wed, 3 Jun 2026 09:59:28 -0700 Subject: [PATCH 4/4] Fix lint --- .../opentelemetry/exporter/_quickpulse/_live_metrics.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_quickpulse/_live_metrics.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_quickpulse/_live_metrics.py index 1ab63b7734e3..267eeaa4b8e8 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_quickpulse/_live_metrics.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_quickpulse/_live_metrics.py @@ -71,7 +71,8 @@ def get_quickpulse_configuration_callback(settings: Dict[str, str]) -> None: resource=manager._resource, # pylint:disable=protected-access ) elif live_metrics_enabled is False and manager.is_initialized(): - # Track explicit live metrics disable for statsbeat feature reporting. (Tracking the disable live metrics feature starting 06/03/2026) + # Track explicit live metrics disable for statsbeat feature reporting. + # (Tracking the disable live metrics feature starting 06/03/2026) set_statsbeat_live_metrics_feature_set() # Disable live metrics if it's currently enabled manager.shutdown()