Skip to content

Commit 6d11ecc

Browse files
authored
Refactor customer sdk stats (#42969)
1 parent 46d8caa commit 6d11ecc

File tree

24 files changed

+2194
-2100
lines changed

24 files changed

+2194
-2100
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
([#42655](https://github.com/Azure/azure-sdk-for-python/pull/42655))
2020
- Customer Facing SDKStats: Added telemetry_success field to dropped items as per [Spec] - https://github.com/aep-health-and-standards/Telemetry-Collection-Spec/pull/606
2121
([#42846](https://github.com/Azure/azure-sdk-for-python/pull/42846))
22+
- Customer Facing SDKStats: Refactor to use `Manager` and `Singleton` pattern
23+
([#42969](https://github.com/Azure/azure-sdk-for-python/pull/42969))
2224

2325
### Breaking Changes
2426

sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_constants.py

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@
154154
_UNKNOWN = "UNKNOWN"
155155

156156
# Customer Facing SDKStats
157+
157158
_APPLICATIONINSIGHTS_SDKSTATS_ENABLED_PREVIEW = "APPLICATIONINSIGHTS_SDKSTATS_ENABLED_PREVIEW"
158159
_APPLICATIONINSIGHTS_SDKSTATS_EXPORT_INTERVAL = "APPLICATIONINSIGHTS_SDKSTATS_EXPORT_INTERVAL"
159160
_CUSTOMER_SDKSTATS_LANGUAGE = "python"
@@ -179,27 +180,18 @@ class CustomerSdkStatsMetricName(str, Enum, metaclass=CaseInsensitiveEnumMeta):
179180
ITEM_DROP_COUNT = "preview.item.dropped.count"
180181
ITEM_RETRY_COUNT = "preview.item.retry.count"
181182

182-
class CustomerSdkStatsProperties:
183-
language: str
184-
version: str
185-
compute_type: str
186-
def __init__(self, language: str, version: str, compute_type: str):
187-
self.language = language
188-
self.version = version
189-
self.compute_type = compute_type
190-
191183
## Map from Azure Monitor envelope base_types to TelemetryType
192184
_TYPE_MAP = {
193-
"EventData": _CUSTOM_EVENT,
194-
"MetricData": _CUSTOM_METRIC,
195-
"RemoteDependencyData": _DEPENDENCY,
196-
"ExceptionData": _EXCEPTION,
197-
"PageViewData": _PAGE_VIEW,
198-
"MessageData": _TRACE,
199-
"RequestData": _REQUEST,
200-
"PerformanceCounterData": _PERFORMANCE_COUNTER,
201-
"AvailabilityData": _AVAILABILITY,
202-
}
185+
"EventData": _CUSTOM_EVENT,
186+
"MetricData": _CUSTOM_METRIC,
187+
"RemoteDependencyData": _DEPENDENCY,
188+
"ExceptionData": _EXCEPTION,
189+
"PageViewData": _PAGE_VIEW,
190+
"MessageData": _TRACE,
191+
"RequestData": _REQUEST,
192+
"PerformanceCounterData": _PERFORMANCE_COUNTER,
193+
"AvailabilityData": _AVAILABILITY,
194+
}
203195

204196
# Exception categories
205197
class _exception_categories(Enum):

sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_generated/models/_models_py3.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1326,7 +1326,7 @@ def __init__( # type: ignore
13261326
items_received: Optional[int] = None,
13271327
items_accepted: Optional[int] = None,
13281328
errors: Optional[List["TelemetryErrorDetails"]] = None,
1329-
**kwargs
1329+
**kwargs # type: Any
13301330
):
13311331
"""
13321332
:keyword items_received: The number of items received.

sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_storage.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
from azure.monitor.opentelemetry.exporter._utils import PeriodicTask
1515

16-
from azure.monitor.opentelemetry.exporter.statsbeat._state import (
16+
from azure.monitor.opentelemetry.exporter.statsbeat.customer._state import (
1717
get_local_storage_setup_state_exception,
1818
get_local_storage_setup_state_readonly,
1919
set_local_storage_setup_state_exception,
@@ -46,7 +46,7 @@ class StorageExportResult(Enum):
4646
# pylint: disable=broad-except
4747
class LocalFileBlob:
4848
def __init__(self, fullpath: str) -> None:
49-
self.fullpath = fullpath
49+
self.fullpath: str = fullpath
5050

5151
def delete(self) -> None:
5252
try:
@@ -82,7 +82,7 @@ def put(self, data: List[Any], lease_period: int = 0) -> Union[StorageExportResu
8282

8383
def lease(self, period: int) -> Optional['LocalFileBlob']:
8484
timestamp = _now() + _seconds(period)
85-
fullpath = self.fullpath
85+
fullpath: str = self.fullpath
8686
if fullpath.endswith(".lock"):
8787
fullpath = fullpath[: fullpath.rindex("@")]
8888
fullpath += "@{}.lock".format(_fmt(timestamp))
@@ -189,7 +189,7 @@ def gets(self) -> Generator[LocalFileBlob, None, None]:
189189
else:
190190
pass
191191

192-
def get(self) -> Optional['LocalFileBlob']:
192+
def get(self) -> Optional[LocalFileBlob]:
193193
if not self._enabled:
194194
return None
195195
cursor = self.gets()

sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/_base.py

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -52,18 +52,21 @@
5252
from azure.monitor.opentelemetry.exporter.statsbeat._state import (
5353
get_statsbeat_initial_success,
5454
get_statsbeat_shutdown,
55-
get_customer_sdkstats_shutdown,
5655
increment_and_check_statsbeat_failure_count,
5756
is_statsbeat_enabled,
5857
set_statsbeat_initial_success,
59-
is_customer_sdkstats_enabled,
6058
)
6159
from azure.monitor.opentelemetry.exporter.statsbeat._utils import (
6260
_update_requests_map,
63-
_track_dropped_items_from_storage,
64-
_track_dropped_items,
65-
_track_retry_items,
66-
_track_successful_items,
61+
)
62+
from azure.monitor.opentelemetry.exporter.statsbeat.customer._utils import (
63+
track_dropped_items_from_storage,
64+
track_dropped_items,
65+
track_retry_items,
66+
track_successful_items,
67+
)
68+
from azure.monitor.opentelemetry.exporter.statsbeat.customer._state import (
69+
get_customer_stats_manager,
6770
)
6871

6972

@@ -111,6 +114,7 @@ def __init__(self, **kwargs: Any) -> None:
111114
self._credential = _get_authentication_credential(**kwargs)
112115
self._consecutive_redirects = 0 # To prevent circular redirects
113116
self._disable_offline_storage = kwargs.get("disable_offline_storage", False)
117+
self._connection_string = parsed_connection_string._connection_string
114118
self._endpoint = parsed_connection_string.endpoint
115119
self._region = parsed_connection_string.region
116120
self._instrumentation_key = parsed_connection_string.instrumentation_key
@@ -159,10 +163,10 @@ def __init__(self, **kwargs: Any) -> None:
159163
config.http_logging_policy or HttpLoggingPolicy(**kwargs),
160164
]
161165

162-
self.client = AzureMonitorClient(
166+
self.client: AzureMonitorClient = AzureMonitorClient(
163167
host=self._endpoint, connection_timeout=self._timeout, policies=policies, **kwargs
164168
)
165-
self.storage = None
169+
self.storage: Optional[LocalFileStorage] = None
166170
if not self._disable_offline_storage:
167171
self.storage = LocalFileStorage( # pyright: ignore
168172
path=self._storage_directory, # type: ignore
@@ -187,7 +191,7 @@ def __init__(self, **kwargs: Any) -> None:
187191
# customer sdkstats initialization
188192
if self._should_collect_customer_sdkstats():
189193

190-
from azure.monitor.opentelemetry.exporter.statsbeat._customer_sdkstats import collect_customer_sdkstats
194+
from azure.monitor.opentelemetry.exporter.statsbeat.customer import collect_customer_sdkstats
191195
# Collect customer sdkstats metrics
192196
collect_customer_sdkstats(self)
193197

@@ -212,15 +216,15 @@ def _handle_transmit_from_storage(self, envelopes: List[TelemetryItem], result:
212216
envelopes_to_store = [x.as_dict() for x in envelopes]
213217
result_from_storage_put = self.storage.put(envelopes_to_store)
214218
if self._should_collect_customer_sdkstats():
215-
_track_dropped_items_from_storage(result_from_storage_put, envelopes)
219+
track_dropped_items_from_storage(result_from_storage_put, envelopes)
216220
elif result == ExportResult.SUCCESS:
217221
# Try to send any cached events
218222
self._transmit_from_storage()
219223

220224
else:
221225
# Track items that would have been retried but are dropped since client has local storage disabled
222226
if self._should_collect_customer_sdkstats():
223-
_track_dropped_items(envelopes, DropCode.CLIENT_STORAGE_DISABLED)
227+
track_dropped_items(envelopes, DropCode.CLIENT_STORAGE_DISABLED)
224228

225229
# pylint: disable=too-many-branches
226230
# pylint: disable=too-many-nested-blocks
@@ -259,7 +263,7 @@ def _transmit(self, envelopes: List[TelemetryItem]) -> ExportResult:
259263

260264
# Track successful items in customer sdkstats
261265
if self._should_collect_customer_sdkstats():
262-
_track_successful_items(envelopes)
266+
track_successful_items(envelopes)
263267
else: # 206
264268
reach_ingestion = True
265269
resend_envelopes = []
@@ -268,13 +272,13 @@ def _transmit(self, envelopes: List[TelemetryItem]) -> ExportResult:
268272
resend_envelopes.append(envelopes[error.index]) # type: ignore
269273
# Track retried items in customer sdkstats
270274
if self._should_collect_customer_sdkstats():
271-
_track_retry_items(resend_envelopes, error)
275+
track_retry_items(resend_envelopes, error)
272276
else:
273277
if not self._is_stats_exporter():
274278
# Track dropped items in customer sdkstats, non-retryable scenario
275279
if self._should_collect_customer_sdkstats():
276280
if error is not None and hasattr(error, "index") and error.index is not None and isinstance(error.status_code, int):
277-
_track_dropped_items([envelopes[error.index]], error.status_code)
281+
track_dropped_items([envelopes[error.index]], error.status_code)
278282
logger.error(
279283
"Data drop %s: %s %s.",
280284
error.status_code,
@@ -285,12 +289,12 @@ def _transmit(self, envelopes: List[TelemetryItem]) -> ExportResult:
285289
envelopes_to_store = [x.as_dict() for x in resend_envelopes]
286290
result_from_storage = self.storage.put(envelopes_to_store, 0)
287291
if self._should_collect_customer_sdkstats():
288-
_track_dropped_items_from_storage(result_from_storage, resend_envelopes)
292+
track_dropped_items_from_storage(result_from_storage, resend_envelopes)
289293
self._consecutive_redirects = 0
290294
elif resend_envelopes:
291295
# Track items that would have been retried but are dropped since client has local storage disabled
292296
if self._should_collect_customer_sdkstats():
293-
_track_dropped_items(resend_envelopes, DropCode.CLIENT_STORAGE_DISABLED)
297+
track_dropped_items(resend_envelopes, DropCode.CLIENT_STORAGE_DISABLED)
294298
# Mark as not retryable because we already write to storage here
295299
result = ExportResult.FAILED_NOT_RETRYABLE
296300
except HttpResponseError as response_error:
@@ -304,7 +308,7 @@ def _transmit(self, envelopes: List[TelemetryItem]) -> ExportResult:
304308
# Log error for 401: Unauthorized, 403: Forbidden to assist with customer troubleshooting
305309
if not self._is_stats_exporter():
306310
if self._should_collect_customer_sdkstats():
307-
_track_retry_items(envelopes, response_error)
311+
track_retry_items(envelopes, response_error)
308312
if response_error.status_code == 401:
309313
logger.error(
310314
"Retryable server side error: %s. " \
@@ -313,8 +317,6 @@ def _transmit(self, envelopes: List[TelemetryItem]) -> ExportResult:
313317
response_error.message,
314318
)
315319
elif response_error.status_code == 403:
316-
if self._should_collect_customer_sdkstats():
317-
_track_retry_items(envelopes, response_error)
318320
logger.error(
319321
"Retryable server side error: %s. " \
320322
"Your application may be configured with a token credential " \
@@ -330,7 +332,7 @@ def _transmit(self, envelopes: List[TelemetryItem]) -> ExportResult:
330332

331333
if not self._is_stats_exporter():
332334
if self._should_collect_customer_sdkstats() and isinstance(response_error.status_code, int):
333-
_track_dropped_items(envelopes, response_error.status_code)
335+
track_dropped_items(envelopes, response_error.status_code)
334336
elif _is_redirect_code(response_error.status_code):
335337
self._consecutive_redirects = self._consecutive_redirects + 1
336338
# pylint: disable=W0212
@@ -349,7 +351,7 @@ def _transmit(self, envelopes: List[TelemetryItem]) -> ExportResult:
349351
else:
350352
if not self._is_stats_exporter():
351353
if self._should_collect_customer_sdkstats():
352-
_track_dropped_items(envelopes, DropCode.CLIENT_EXCEPTION, _exception_categories.CLIENT_EXCEPTION.value)
354+
track_dropped_items(envelopes, DropCode.CLIENT_EXCEPTION, _exception_categories.CLIENT_EXCEPTION.value)
353355
logger.error(
354356
"Error parsing redirect information.",
355357
)
@@ -358,7 +360,7 @@ def _transmit(self, envelopes: List[TelemetryItem]) -> ExportResult:
358360
if not self._is_stats_exporter():
359361
# Track dropped items in customer sdkstats, non-retryable scenario
360362
if self._should_collect_customer_sdkstats():
361-
_track_dropped_items(
363+
track_dropped_items(
362364
envelopes,
363365
DropCode.CLIENT_EXCEPTION,
364366
_exception_categories.CLIENT_EXCEPTION.value
@@ -384,14 +386,14 @@ def _transmit(self, envelopes: List[TelemetryItem]) -> ExportResult:
384386
)
385387
# Track dropped items in customer sdkstats, non-retryable scenario
386388
if self._should_collect_customer_sdkstats() and isinstance(response_error.status_code, int):
387-
_track_dropped_items(envelopes, response_error.status_code)
389+
track_dropped_items(envelopes, response_error.status_code)
388390
if _is_invalid_code(response_error.status_code):
389391
# Shutdown statsbeat on invalid code from customer endpoint
390392
# Import here to avoid circular dependencies
391393
from azure.monitor.opentelemetry.exporter.statsbeat._statsbeat import (
392394
shutdown_statsbeat_metrics,
393395
)
394-
from azure.monitor.opentelemetry.exporter.statsbeat._customer_sdkstats import (
396+
from azure.monitor.opentelemetry.exporter.statsbeat.customer import (
395397
shutdown_customer_sdkstats_metrics,
396398
)
397399

@@ -407,7 +409,7 @@ def _transmit(self, envelopes: List[TelemetryItem]) -> ExportResult:
407409

408410
# Track retry items in customer sdkstats for client-side exceptions
409411
if self._should_collect_customer_sdkstats():
410-
_track_retry_items(envelopes, request_error)
412+
track_retry_items(envelopes, request_error)
411413

412414
if self._should_collect_stats():
413415
exc_type = request_error.exc_type
@@ -420,7 +422,7 @@ def _transmit(self, envelopes: List[TelemetryItem]) -> ExportResult:
420422

421423
# Track dropped items in customer sdkstats for general exceptions
422424
if self._should_collect_customer_sdkstats():
423-
_track_dropped_items(envelopes, DropCode.CLIENT_EXCEPTION, _exception_categories.CLIENT_EXCEPTION.value)
425+
track_dropped_items(envelopes, DropCode.CLIENT_EXCEPTION, _exception_categories.CLIENT_EXCEPTION.value)
424426

425427
if self._should_collect_stats():
426428
_update_requests_map(_REQ_EXCEPTION_NAME[1], value=ex.__class__.__name__)
@@ -467,10 +469,10 @@ def _should_collect_stats(self):
467469

468470
# check to see whether its the case of customer sdkstats collection
469471
def _should_collect_customer_sdkstats(self):
470-
# Don't collect customer sdkstats for instrumentation collection, sdkstats exporter or customer sdkstats exporter
472+
manager = get_customer_stats_manager()
471473
return (
472-
is_customer_sdkstats_enabled()
473-
and not get_customer_sdkstats_shutdown()
474+
manager.is_enabled
475+
and not manager.is_shutdown
474476
and not self._is_stats_exporter()
475477
and not self._is_customer_sdkstats_exporter()
476478
and not self._instrumentation_collection

0 commit comments

Comments
 (0)