Skip to content

Commit 719e178

Browse files
committed
Address feedback
1 parent 7532d9a commit 719e178

7 files changed

Lines changed: 58 additions & 279 deletions

File tree

sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
- Add `StatsbeatManager.add_metric_callback` to let SDKs/distros add their own metric
77
observations to built-in statsbeat metrics
88
([#47363](https://github.com/Azure/azure-sdk-for-python/pull/47363))
9+
910
### Breaking Changes
1011
- Customer Facing SDKStats: Renamed metric dimension attributes from snake_case/dotted to camelCase
1112
(`compute_type` -> `computeType`, `telemetry_type` -> `telemetryType`, `telemetry_success` -> `telemetrySuccess`,

sdk/monitor/azure-monitor-opentelemetry-exporter/api.md

Lines changed: 0 additions & 183 deletions
This file was deleted.

sdk/monitor/azure-monitor-opentelemetry-exporter/api.metadata.yml

Lines changed: 0 additions & 3 deletions
This file was deleted.

sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/statsbeat/_manager.py

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# Licensed under the MIT License.
33
import logging
44
import threading
5-
from typing import Callable, Iterable, Optional, Any, Dict
5+
from typing import Callable, Iterable, List, Optional, Any, Dict
66

77
from opentelemetry.metrics import CallbackOptions, Observation
88
from opentelemetry.sdk.metrics import MeterProvider
@@ -20,8 +20,6 @@
2020
_get_stats_long_export_interval,
2121
_get_stats_short_export_interval,
2222
_get_connection_string_for_region_from_config,
23-
_ADDITIONAL_CALLBACKS,
24-
_ADDITIONAL_CALLBACKS_LOCK,
2523
)
2624
from azure.monitor.opentelemetry.exporter._utils import Singleton
2725

@@ -164,6 +162,11 @@ def __init__(self) -> None:
164162
# Set during first initialization, preserved in shutdown for potential re-initialization
165163
self._config: Optional[StatsbeatConfig] = None # type: ignore
166164

165+
# Extra observation callbacks contributed by SDKs/distros. Keyed by built-in
166+
# statsbeat metric name. Registered directly on the singleton instance, e.g.
167+
# ``StatsbeatManager()._additional_callbacks.setdefault(name, []).append(cb)``.
168+
self._additional_callbacks: Dict[str, List[Callable[[CallbackOptions], Iterable[Observation]]]] = {}
169+
167170
@staticmethod
168171
def _validate_config(config: Optional[StatsbeatConfig]) -> bool:
169172
"""Validate that a configuration has all required fields.
@@ -381,24 +384,4 @@ def is_initialized(self) -> bool:
381384
with self._lock:
382385
return self._initialized
383386

384-
def add_metric_callback(
385-
self,
386-
metric_name: str,
387-
callback: Callable[[CallbackOptions], Iterable[Observation]],
388-
) -> bool:
389-
"""Register an extra observation callback that an SDK/Distro with its own network sdkstats metric can use to
390-
contribute rows to a built-in statsbeat metric.
391-
392-
:param metric_name: Name of the built-in statsbeat metric to extend.
393-
:type metric_name: str
394-
:param callback: OpenTelemetry observable-gauge callback ``(CallbackOptions) -> Iterable[Observation]``.
395-
:type callback: Callable[[CallbackOptions], Iterable[Observation]]
396-
:returns: ``True`` if newly registered, ``False`` if already registered.
397-
:rtype: bool
398-
"""
399-
with _ADDITIONAL_CALLBACKS_LOCK:
400-
callbacks = _ADDITIONAL_CALLBACKS.setdefault(metric_name, [])
401-
if callback in callbacks:
402-
return False
403-
callbacks.append(callback)
404-
return True
387+

sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/statsbeat/_statsbeat_metrics.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
get_statsbeat_browser_sdk_loader_feature_set,
4242
)
4343
from azure.monitor.opentelemetry.exporter.statsbeat._utils import (
44-
_iter_additional_observations,
44+
_get_additional_observations,
4545
)
4646
from azure.monitor.opentelemetry.exporter import _utils
4747

@@ -382,7 +382,7 @@ def _get_success_count(self, options: CallbackOptions) -> Iterable[Observation]:
382382
if count != 0:
383383
observations.append(Observation(int(count), dict(attributes)))
384384
_REQUESTS_MAP[_REQ_SUCCESS_NAME[1]] = 0
385-
observations.extend(_iter_additional_observations(_REQ_SUCCESS_NAME[0], options))
385+
observations.extend(_get_additional_observations(_REQ_SUCCESS_NAME[0], options))
386386
return observations
387387

388388
# pylint: disable=unused-argument
@@ -397,7 +397,7 @@ def _get_failure_count(self, options: CallbackOptions) -> Iterable[Observation]:
397397
attributes["statusCode"] = code
398398
observations.append(Observation(int(count), dict(attributes)))
399399
_REQUESTS_MAP[_REQ_FAILURE_NAME[1]][code] = 0 # type: ignore
400-
observations.extend(_iter_additional_observations(_REQ_FAILURE_NAME[0], options))
400+
observations.extend(_get_additional_observations(_REQ_FAILURE_NAME[0], options))
401401
return observations
402402

403403
# pylint: disable=unused-argument
@@ -414,7 +414,7 @@ def _get_average_duration(self, options: CallbackOptions) -> Iterable[Observatio
414414
observations.append(Observation(result * 1000, dict(attributes)))
415415
_REQUESTS_MAP[_REQ_DURATION_NAME[1]] = 0
416416
_REQUESTS_MAP["count"] = 0
417-
observations.extend(_iter_additional_observations(_REQ_DURATION_NAME[0], options))
417+
observations.extend(_get_additional_observations(_REQ_DURATION_NAME[0], options))
418418
return observations
419419

420420
# pylint: disable=unused-argument
@@ -429,7 +429,7 @@ def _get_retry_count(self, options: CallbackOptions) -> Iterable[Observation]:
429429
attributes["statusCode"] = code
430430
observations.append(Observation(int(count), dict(attributes)))
431431
_REQUESTS_MAP[_REQ_RETRY_NAME[1]][code] = 0 # type: ignore
432-
observations.extend(_iter_additional_observations(_REQ_RETRY_NAME[0], options))
432+
observations.extend(_get_additional_observations(_REQ_RETRY_NAME[0], options))
433433
return observations
434434

435435
# pylint: disable=unused-argument
@@ -444,7 +444,7 @@ def _get_throttle_count(self, options: CallbackOptions) -> Iterable[Observation]
444444
attributes["statusCode"] = code
445445
observations.append(Observation(int(count), dict(attributes)))
446446
_REQUESTS_MAP[_REQ_THROTTLE_NAME[1]][code] = 0 # type: ignore
447-
observations.extend(_iter_additional_observations(_REQ_THROTTLE_NAME[0], options))
447+
observations.extend(_get_additional_observations(_REQ_THROTTLE_NAME[0], options))
448448
return observations
449449

450450
# pylint: disable=unused-argument
@@ -459,7 +459,7 @@ def _get_exception_count(self, options: CallbackOptions) -> Iterable[Observation
459459
attributes["exceptionType"] = code
460460
observations.append(Observation(int(count), dict(attributes)))
461461
_REQUESTS_MAP[_REQ_EXCEPTION_NAME[1]][code] = 0 # type: ignore
462-
observations.extend(_iter_additional_observations(_REQ_EXCEPTION_NAME[0], options))
462+
observations.extend(_get_additional_observations(_REQ_EXCEPTION_NAME[0], options))
463463
return observations
464464

465465

sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/statsbeat/_utils.py

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
import os
44
import logging
55
import json
6-
import threading
7-
from collections.abc import Iterable, Callable, Iterator # pylint: disable=import-error
6+
from collections.abc import Iterable # pylint: disable=import-error
87
from typing import Optional, Dict, List
98
from opentelemetry.metrics import CallbackOptions, Observation
109

@@ -28,9 +27,6 @@
2827
_REQUESTS_MAP_LOCK,
2928
)
3029

31-
_ADDITIONAL_CALLBACKS: Dict[str, List[Callable[[CallbackOptions], Iterable[Observation]]]] = {}
32-
_ADDITIONAL_CALLBACKS_LOCK = threading.Lock()
33-
3430

3531
def _get_stats_connection_string(endpoint: str) -> str:
3632
cs_env = os.environ.get(_APPLICATIONINSIGHTS_STATS_CONNECTION_STRING_ENV_NAME)
@@ -172,34 +168,36 @@ def _get_connection_string_for_region_from_config(target_region: str, settings:
172168
return None
173169

174170

175-
def _iter_additional_observations(metric_name: str, options: CallbackOptions) -> Iterator[Observation]:
176-
"""Yield observations contributed via :func:`add_metric_callback`.
171+
def _get_additional_observations(metric_name: str, options: CallbackOptions) -> List[Observation]:
172+
"""Return observations contributed by extra callbacks registered on :class:`StatsbeatManager`.
177173
178174
Invoked by the built-in ``_StatsbeatMetrics`` callbacks at collection time.
179-
Snapshots the registered callbacks under the registry lock to avoid
180-
mutation during iteration, then releases the lock before invoking user
181-
callbacks (so they cannot deadlock against the registry). Exceptions raised
182-
by individual callbacks are caught, logged, and skipped.
175+
Reads ``StatsbeatManager()._additional_callbacks`` (a live mutable dict on the
176+
singleton instance), which SDKs/distros populate directly. Exceptions raised by
177+
individual callbacks are caught, logged, and skipped.
183178
184179
:param metric_name: Name of the built-in statsbeat metric being collected.
185180
:type metric_name: str
186181
:param options: OpenTelemetry callback options forwarded to each registered callback.
187182
:type options: ~opentelemetry.metrics.CallbackOptions
188-
:returns: Iterator over observations contributed by registered callbacks.
189-
:rtype: Iterator[~opentelemetry.metrics.Observation]
183+
:returns: List of observations contributed by registered callbacks.
184+
:rtype: list[~opentelemetry.metrics.Observation]
190185
"""
186+
# Lazy import to avoid a circular import between _manager and _utils.
187+
from azure.monitor.opentelemetry.exporter.statsbeat._manager import StatsbeatManager # pylint: disable=import-outside-toplevel
191188

192-
with _ADDITIONAL_CALLBACKS_LOCK:
193-
callbacks = tuple(_ADDITIONAL_CALLBACKS.get(metric_name, ()))
189+
callbacks = StatsbeatManager()._additional_callbacks.get(metric_name, ()) # pylint: disable=protected-access
194190

191+
observations: List[Observation] = []
195192
iter_logger = logging.getLogger(__name__)
196193
for cb in callbacks:
197194
try:
198-
yield from cb(options)
195+
observations.extend(cb(options))
199196
except Exception: # pylint: disable=broad-except
200197
iter_logger.debug(
201198
"Extra statsbeat callback %r for %r raised; skipping.",
202199
cb,
203200
metric_name,
204201
exc_info=True,
205202
)
203+
return observations

0 commit comments

Comments
 (0)