Conversation
…ub.com/JacksonWeber/ApplicationInsights-JS into jacksonweber/enable-customer-sdk-stats
There was a problem hiding this comment.
Pull request overview
This pull request introduces comprehensive SDK statistics tracking and enhances the notification system to support telemetry retry events. The main objective is to enable periodic reporting of SDK usage metrics (success, dropped, and retry counts) and provide visibility into the complete telemetry lifecycle including retry scenarios.
Changes:
- Adds
eventsRetrynotification callback to INotificationManager and INotificationListener interfaces for tracking telemetry retry events with HTTP status codes - Implements
SdkStatsNotificationCbk- a notification listener that accumulates telemetry counts by type and periodically reports them as metrics - Integrates SDK stats listener into AISku with automatic initialization (enabled by default) and proper cleanup during unload
- Updates Sender to trigger notifications for sent, discarded, and retried events, storing baseType in IInternalStorageItem for telemetry type mapping
- Adds comprehensive unit tests for the SDK stats notification callback covering all major scenarios
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| shared/AppInsightsCore/src/interfaces/ai/INotificationManager.ts | Adds eventsRetry method signature to notification manager interface |
| shared/AppInsightsCore/src/interfaces/ai/INotificationListener.ts | Adds optional eventsRetry callback to listener interface |
| shared/AppInsightsCore/src/index.ts | Exports new SDK stats notification callback factory and interfaces |
| shared/AppInsightsCore/src/core/SdkStatsNotificationCbk.ts | New file implementing the SDK stats accumulator and periodic metric flushing logic |
| shared/AppInsightsCore/src/core/NotificationManager.ts | Implements eventsRetry notification dispatch to all registered listeners |
| shared/AppInsightsCore/src/constants/InternalConstants.ts | Adds STR_EVENTS_RETRY constant for the new notification type |
| shared/AppInsightsCore/Tests/Unit/src/aiunittests.ts | Registers new SDK stats notification callback test suite |
| shared/AppInsightsCore/Tests/Unit/src/ai/SdkStatsNotificationCbk.Tests.ts | New comprehensive test suite covering all SDK stats functionality |
| channels/applicationinsights-channel-js/src/Sender.ts | Adds notification triggers for sent/discarded/retry events and extracts telemetry items for notifications |
| channels/applicationinsights-channel-js/src/Interfaces.ts | Adds bT (baseType) property to IInternalStorageItem for telemetry type mapping |
| AISKU/src/AISku.ts | Integrates SDK stats listener with initialization, configuration, and unload handling |
| _self.onunloadFlush(isAsync); | ||
|
|
||
| _removePageEventHandlers(); | ||
|
|
||
| // Unload SDK Stats listener BEFORE core.unload() tears down the pipeline. | ||
| // unload() calls core.track() to enqueue the accumulated metrics, | ||
| // then calls fnFlush to flush the channel so they actually get sent. | ||
| if (_sdkStatsListener && _core) { | ||
| _sdkStatsListener.unload(); | ||
| _core.removeNotificationListener(_sdkStatsListener); | ||
| _sdkStatsListener = null; | ||
| } |
There was a problem hiding this comment.
Potential double-flush issue in the unload sequence. At line 617, _self.onunloadFlush(isAsync) is called. Then at line 625, _sdkStatsListener.unload() is called, which internally calls the fnFlush callback configured at line 409, resulting in a second call to _self.onunloadFlush(false). This could cause telemetry to be sent twice or cause unexpected behavior if onunloadFlush is not idempotent. Consider either: (1) removing the fnFlush callback and relying on the explicit onunloadFlush call at line 617, or (2) adding a guard to prevent double-flushing.
| if (mgr) { | ||
| let items = _extractTelemetryItems(payload); | ||
| if (items) { | ||
| mgr.eventsDiscarded(items, 1 /* NonRetryableStatus */); |
There was a problem hiding this comment.
The eventsDiscarded notification call is missing the sendType parameter. According to the INotificationManager interface, eventsDiscarded accepts an optional third parameter (sendType) that should contain the HTTP status code when available. For consistency with the SDK stats drop.code mapping (which expects the actual HTTP status for NonRetryableStatus), consider passing the status code as the third parameter when it's available from the error context.
| mgr.eventsDiscarded(items, 1 /* NonRetryableStatus */); | |
| let statusCode: number | undefined = UNDEFINED_VALUE; | |
| if (event && (event as any).status && isNumber((event as any).status)) { | |
| statusCode = (event as any).status; | |
| } | |
| mgr.eventsDiscarded(items, 1 /* NonRetryableStatus */, statusCode); |
| let items: ITelemetryItem[] = []; | ||
| arrForEach(payload, (p) => { | ||
| if (p) { | ||
| items.push({ name: "", baseType: p.bT || "EventData" } as ITelemetryItem); |
There was a problem hiding this comment.
The extracted telemetry items use empty strings for the name property, which may cause issues for SDK stats filtering. The _isSdkStatsMetric function in SdkStatsNotificationCbk.ts checks item.name against specific metric names (Item_Success_Count, Item_Dropped_Count, Item_Retry_Count). With empty names, these items won't match the filter but also won't have meaningful identification. Consider either: (1) preserving the original item name if available from the payload, or (2) documenting that empty names are intentional for notification purposes.
| items.push({ name: "", baseType: p.bT || "EventData" } as ITelemetryItem); | |
| // Preserve a meaningful name for notification consumers: | |
| // - Prefer any stored event/envelope name (e.g. eN) when available | |
| // - Fall back to the baseType so name is never an empty string | |
| let baseType = p.bT || "EventData"; | |
| let name = (p as any).eN || baseType; | |
| items.push({ name: name, baseType: baseType } as ITelemetryItem); |
| public registerTests() { | ||
| this._testCreation(); | ||
| this._testEventsSent(); | ||
| this._testEventsDiscarded(); | ||
| this._testEventsRetry(); | ||
| this._testFlush(); | ||
| this._testUnload(); | ||
| this._testBaseTypeMapping(); | ||
| this._testSdkStatsMetricFiltering(); | ||
| this._testNotificationManagerIntegration(); | ||
| } |
There was a problem hiding this comment.
Missing test coverage for automatic timer-based flush. The SdkStatsNotificationCbk uses scheduleTimeout to automatically flush metrics after the configured interval, but there's no test validating this behavior. Consider adding a test that uses useFakeTimers: true and advances the clock to verify that metrics are automatically flushed after the configured interval without requiring manual flush() calls. This is important to ensure the periodic reporting feature works correctly in production.
| _sdkStatsListener.unload(); | ||
| _core.removeNotificationListener(_sdkStatsListener); |
There was a problem hiding this comment.
Consider adding error handling around the SDK stats listener unload sequence. If _sdkStatsListener.unload() throws an exception during the track() or fnFlush() calls, it could prevent core.unload() from executing at line 630, leaving the SDK in a partially unloaded state. Wrap the listener unload in a try-catch block to ensure core cleanup always proceeds even if SDK stats cleanup fails.
| _sdkStatsListener.unload(); | |
| _core.removeNotificationListener(_sdkStatsListener); | |
| try { | |
| _sdkStatsListener.unload(); | |
| _core.removeNotificationListener(_sdkStatsListener); | |
| } catch (e) { | |
| // Swallow any errors to ensure core.unload() is still called | |
| } |
…ub.com/JacksonWeber/ApplicationInsights-JS into jacksonweber/enable-customer-sdk-stats
This pull request introduces SDK statistics tracking and enhances event notification capabilities in the Application Insights SDK. The main focus is on enabling periodic SDK stats reporting, improving notification for telemetry event lifecycle (including retries), and supporting these features with new interfaces and configuration options.
SDK Statistics Tracking:
SDK_STATS,SDK_STATS_VERSION,SDK_STATS_FLUSH_INTERVAL) and default enablement in the config. The stats listener is registered during initialization and properly cleaned up on unload. (AISKU/src/AISku.ts[1] [2] [3] [4] [5] [6]ISdkStatsNotifCbkand related imports to facilitate SDK stats notification callbacks. (AISKU/src/AISku.tsAISKU/src/AISku.tsL14-R21)Telemetry Event Notification Enhancements:
eventsRetryevent, allowing listeners to be notified when telemetry items are retried, along with the relevant HTTP status code. (shared/AppInsightsCore/src/constants/InternalConstants.ts[1]shared/AppInsightsCore/src/core/NotificationManager.ts[2] [3] [4]Senderplugin to trigger notification callbacks for events sent, discarded, and retried, including logic to extract minimal telemetry items for notification purposes. (channels/applicationinsights-channel-js/src/Sender.ts[1] [2] [3] [4] [5]Internal Data Structure Updates:
bT(baseType) property toIInternalStorageItemfor mapping telemetry types in SDK stats and notification extraction. (channels/applicationinsights-channel-js/src/Interfaces.ts[1]channels/applicationinsights-channel-js/src/Sender.ts[2]These changes collectively improve observability, reliability, and extensibility of the telemetry pipeline, especially for SDK usage metrics and event retry handling.