-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Refactor statsbeat to use StatsbeatManager
#42716
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
d117a83
b5e7146
06e1962
68c5cd0
6baf1db
459f249
1f9bdce
9eb9e87
f3eb046
0d7023e
f176562
11c43d1
185fa1e
5ed5eb4
2c6376c
e650161
8ffa33c
7d25262
c81cf00
d90156e
0012049
f67d122
eb154e0
326bf46
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -380,10 +380,13 @@ def _get_scope(aad_audience=None): | |
|
||
class Singleton(type): | ||
_instance = None | ||
_lock = threading.Lock() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We will be leveraging the |
||
|
||
def __call__(cls, *args, **kwargs): | ||
def __call__(cls, *args: Any, **kwargs: Any): | ||
if not cls._instance: | ||
cls._instance = super(Singleton, cls).__call__(*args, **kwargs) | ||
with cls._lock: | ||
lzchen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if not cls._instance: | ||
cls._instance = super(Singleton, cls).__call__(*args, **kwargs) | ||
return cls._instance | ||
|
||
def _get_telemetry_type(item: TelemetryItem): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -51,6 +51,7 @@ | |
from azure.monitor.opentelemetry.exporter.statsbeat._state import ( | ||
get_statsbeat_initial_success, | ||
get_statsbeat_shutdown, | ||
get_customer_sdkstats_shutdown, | ||
increment_and_check_statsbeat_failure_count, | ||
is_statsbeat_enabled, | ||
set_statsbeat_initial_success, | ||
|
@@ -99,9 +100,11 @@ def __init__(self, **kwargs: Any) -> None: | |
# self._configuration_manager = _ConfigurationManager() | ||
|
||
self._api_version = kwargs.get("api_version") or _SERVICE_API_LATEST | ||
# We do not need to use entra Id if this is a sdkStats exporter | ||
if self._is_stats_exporter(): | ||
self._credential = None | ||
else: | ||
# We use the credential on a regular exporter or customer sdkStats exporter | ||
self._credential = _get_authentication_credential(**kwargs) | ||
self._consecutive_redirects = 0 # To prevent circular redirects | ||
self._disable_offline_storage = kwargs.get("disable_offline_storage", False) | ||
|
@@ -157,8 +160,8 @@ def __init__(self, **kwargs: Any) -> None: | |
) | ||
self.storage = None | ||
if not self._disable_offline_storage: | ||
self.storage = LocalFileStorage( | ||
path=self._storage_directory, | ||
self.storage = LocalFileStorage( # pyright: ignore | ||
path=self._storage_directory, # type: ignore | ||
max_size=self._storage_max_size, | ||
maintenance_period=self._storage_maintenance_period, | ||
retention_period=self._storage_retention_period, | ||
|
@@ -170,10 +173,12 @@ def __init__(self, **kwargs: Any) -> None: | |
|
||
# statsbeat initialization | ||
if self._should_collect_stats(): | ||
# Import here to avoid circular dependencies | ||
from azure.monitor.opentelemetry.exporter.statsbeat._statsbeat import collect_statsbeat_metrics | ||
|
||
collect_statsbeat_metrics(self) | ||
try: | ||
# Import here to avoid circular dependencies | ||
from azure.monitor.opentelemetry.exporter.statsbeat._statsbeat import collect_statsbeat_metrics | ||
collect_statsbeat_metrics(self) | ||
except Exception as e: # pylint: disable=broad-except | ||
logger.warning("Failed to initialize statsbeat metrics: %s", e) | ||
|
||
# Initialize customer sdkstats if enabled | ||
self._customer_sdkstats_metrics = None | ||
|
@@ -453,21 +458,20 @@ def _should_collect_stats(self): | |
is_statsbeat_enabled() | ||
and not get_statsbeat_shutdown() | ||
and not self._is_stats_exporter() | ||
and not self._is_customer_sdkstats_exporter() | ||
and not self._instrumentation_collection | ||
) | ||
|
||
|
||
# check to see whether its the case of customer sdkstats collection | ||
def _should_collect_customer_sdkstats(self): | ||
# Import here to avoid circular dependencies | ||
from azure.monitor.opentelemetry.exporter.statsbeat._state import get_customer_sdkstats_shutdown | ||
|
||
env_value = os.environ.get("APPLICATIONINSIGHTS_SDKSTATS_ENABLED_PREVIEW", "") | ||
is_customer_sdkstats_enabled = env_value.lower() == "true" | ||
# Don't collect customer sdkstats for instrumentation collection or customer sdkstats exporter | ||
# Don't collect customer sdkstats for instrumentation collection, sdkstats exporter or customer sdkstats exporter | ||
return ( | ||
is_customer_sdkstats_enabled | ||
and not get_customer_sdkstats_shutdown() | ||
and not self._is_stats_exporter() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is an important change, we do not want to collect cx stats on regular stats exporter. |
||
and not self._is_customer_sdkstats_exporter() | ||
and not self._instrumentation_collection | ||
) | ||
|
@@ -477,7 +481,7 @@ def _is_statsbeat_initializing_state(self): | |
return self._is_stats_exporter() and not get_statsbeat_shutdown() and not get_statsbeat_initial_success() | ||
|
||
def _is_stats_exporter(self): | ||
return self.__class__.__name__ == "_StatsBeatExporter" | ||
return getattr(self, "_is_sdkstats", False) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I removed |
||
|
||
def _is_customer_sdkstats_exporter(self): | ||
return getattr(self, '_is_customer_sdkstats', False) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,6 +40,7 @@ | |
_APPLICATIONINSIGHTS_METRIC_NAMESPACE_OPT_IN, | ||
_AUTOCOLLECTED_INSTRUMENT_NAMES, | ||
_METRIC_ENVELOPE_NAME, | ||
_STATSBEAT_METRIC_NAME_MAPPINGS, | ||
) | ||
from azure.monitor.opentelemetry.exporter import _utils | ||
from azure.monitor.opentelemetry.exporter._generated.models import ( | ||
|
@@ -75,13 +76,15 @@ class AzureMonitorMetricExporter(BaseExporter, MetricExporter): | |
"""Azure Monitor Metric exporter for OpenTelemetry.""" | ||
|
||
def __init__(self, **kwargs: Any) -> None: | ||
self._is_sdkstats = kwargs.get("is_sdkstats", False) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We move these attribute settings BEFORE the parent |
||
self._is_customer_sdkstats = kwargs.get("is_customer_sdkstats", False) | ||
self._metrics_to_log_analytics = self._determine_metrics_to_log_analytics() | ||
BaseExporter.__init__(self, **kwargs) | ||
MetricExporter.__init__( | ||
self, | ||
preferred_temporality=APPLICATION_INSIGHTS_METRIC_TEMPORALITIES, # type: ignore | ||
preferred_aggregation=kwargs.get("preferred_aggregation"), # type: ignore | ||
) | ||
self._metrics_to_log_analytics = self._determine_metrics_to_log_analytics() | ||
|
||
# pylint: disable=R1702 | ||
def export( | ||
|
@@ -157,7 +160,13 @@ def _point_to_envelope( | |
# When Metrics to Log Analytics is disabled, only send Standard metrics and _OTELRESOURCE_ | ||
if not self._metrics_to_log_analytics and name not in _AUTOCOLLECTED_INSTRUMENT_NAMES: | ||
return None | ||
envelope = _convert_point_to_envelope(point, name, resource, scope) | ||
|
||
# Apply statsbeat metric name mapping if this is a statsbeat exporter | ||
final_metric_name = name | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is added since we got rid of |
||
if self._is_sdkstats and name in _STATSBEAT_METRIC_NAME_MAPPINGS: | ||
final_metric_name = _STATSBEAT_METRIC_NAME_MAPPINGS[name] | ||
|
||
envelope = _convert_point_to_envelope(point, final_metric_name, resource, scope) | ||
if name in _AUTOCOLLECTED_INSTRUMENT_NAMES: | ||
envelope = _handle_std_metric_envelope(envelope, name, point.attributes) # type: ignore | ||
if envelope is not None: | ||
|
@@ -182,8 +191,11 @@ def _determine_metrics_to_log_analytics(self) -> bool: | |
:return: False if metrics should not be sent to Log Analytics, True otherwise. | ||
:rtype: bool | ||
""" | ||
# If sdkStats exporter, always send to LA | ||
if self._is_sdkstats: | ||
return True | ||
# Disabling metrics to Log Analytics via env var is currently only specified for AKS Attach scenarios. | ||
if not _utils._is_on_aks() or not _utils._is_attach_enabled() or self._is_stats_exporter(): | ||
if not _utils._is_on_aks() or not _utils._is_attach_enabled(): | ||
return True | ||
env_var = os.environ.get(_APPLICATIONINSIGHTS_METRICS_TO_LOGANALYTICS_ENABLED) | ||
if not env_var: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# Copyright (c) Microsoft Corporation. All rights reserved. | ||
# Licensed under the MIT License. | ||
|
||
""" | ||
Statsbeat metrics collection module. | ||
|
||
This module provides a singleton-based, thread-safe manager for collecting | ||
and reporting statsbeat metrics. | ||
""" | ||
|
||
from azure.monitor.opentelemetry.exporter.statsbeat._statsbeat import ( | ||
collect_statsbeat_metrics, | ||
shutdown_statsbeat_metrics, | ||
) | ||
from azure.monitor.opentelemetry.exporter.statsbeat._manager import ( | ||
StatsbeatConfig, | ||
StatsbeatManager, | ||
) | ||
|
||
__all__ = [ | ||
'StatsbeatConfig', | ||
'StatsbeatManager', | ||
'collect_statsbeat_metrics', | ||
'shutdown_statsbeat_metrics', | ||
] |
Uh oh!
There was an error while loading. Please reload this page.