Skip to content

Comments

Enable Customer SDK Stats#2707

Open
JacksonWeber wants to merge 13 commits intomicrosoft:betafrom
JacksonWeber:jacksonweber/enable-customer-sdk-stats
Open

Enable Customer SDK Stats#2707
JacksonWeber wants to merge 13 commits intomicrosoft:betafrom
JacksonWeber:jacksonweber/enable-customer-sdk-stats

Conversation

@JacksonWeber
Copy link

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:

  • Added support for SDK statistics reporting, including new configuration options (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]
  • Introduced ISdkStatsNotifCbk and related imports to facilitate SDK stats notification callbacks. (AISKU/src/AISku.ts AISKU/src/AISku.tsL14-R21)

Telemetry Event Notification Enhancements:

  • Extended the notification system to include a new eventsRetry event, 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]
  • Updated the Sender plugin 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:

  • Added a bT (baseType) property to IInternalStorageItem for 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.

@JacksonWeber JacksonWeber marked this pull request as ready for review February 24, 2026 18:07
@JacksonWeber JacksonWeber requested a review from a team as a code owner February 24, 2026 18:07
Copilot AI review requested due to automatic review settings February 24, 2026 18:07
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 eventsRetry notification 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

Comment on lines +617 to +628
_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;
}
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
if (mgr) {
let items = _extractTelemetryItems(payload);
if (items) {
mgr.eventsDiscarded(items, 1 /* NonRetryableStatus */);
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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);

Copilot uses AI. Check for mistakes.
let items: ITelemetryItem[] = [];
arrForEach(payload, (p) => {
if (p) {
items.push({ name: "", baseType: p.bT || "EventData" } as ITelemetryItem);
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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);

Copilot uses AI. Check for mistakes.
Comment on lines +28 to +38
public registerTests() {
this._testCreation();
this._testEventsSent();
this._testEventsDiscarded();
this._testEventsRetry();
this._testFlush();
this._testUnload();
this._testBaseTypeMapping();
this._testSdkStatsMetricFiltering();
this._testNotificationManagerIntegration();
}
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +625 to +626
_sdkStatsListener.unload();
_core.removeNotificationListener(_sdkStatsListener);
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
_sdkStatsListener.unload();
_core.removeNotificationListener(_sdkStatsListener);
try {
_sdkStatsListener.unload();
_core.removeNotificationListener(_sdkStatsListener);
} catch (e) {
// Swallow any errors to ensure core.unload() is still called
}

Copilot uses AI. Check for mistakes.
@JacksonWeber JacksonWeber changed the title Jacksonweber/enable customer sdk stats Enable Customer SDK Stats Feb 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant