Skip to content

Enable SDK Stats and route to the SDK Stats ingestion endpoint - #2741

Open
Jackson Weber (JacksonWeber) wants to merge 36 commits into
microsoft:mainfrom
JacksonWeber:jacksonweber/enable-sdk-stats
Open

Enable SDK Stats and route to the SDK Stats ingestion endpoint#2741
Jackson Weber (JacksonWeber) wants to merge 36 commits into
microsoft:mainfrom
JacksonWeber:jacksonweber/enable-sdk-stats

Conversation

@JacksonWeber

@JacksonWeber Jackson Weber (JacksonWeber) commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Re-enables the SDK Stats manager (createStatsMgr) and routes its events to the distro-owned SDK Stats ingestion endpoint used by opentelemetry-distro-dotnet, instead of piggy-backing on the customer's breeze endpoint.

Endpoints (from Azure/azure-sdk-for-net StatsbeatConstants.cs

  • Non-EU -> https://stats.monitor.azure.com/v2/track
  • EU -> https://eu.stats.monitor.azure.com/v2/track
  • Placeholder iKey 00000000-0000-0000-0000-000000000000, same Breeze envelope, no auth. EU vs non-EU chosen from the customer endpoint region.

Configurable Destination (new stats.monitor endpoint vs. legacy breeze)

The SDK Stats destination is now selectable at runtime via the CDN / dynamic config:

  • New config field config.stats (IStatsBeatConfig), overridable via the CDN / cfgSync dynamic config.
  • config.stats.mode (eStatsEndpointType): SdkStats (default) sends to stats.monitor.azure.com / eu.stats.monitor.azure.com; Breeze sends to the legacy breeze endpoint (the customer's own breeze host, using the Microsoft-owned SDK Stats iKey c4a29126… / 7dc56bab… by region).
  • The destination iKey and ingestion endpoint are resolved per-event in _track from the live config, so CDN changes take effect at runtime. A full endCfg key map (with explicit key and new url) can also be supplied via config.stats.endCfg to fully override routing.

Note: in Breeze mode the events flow through the normal channel to the customer's breeze endpoint (legacy behaviour); in SdkStats mode they bypass the customer buffer and are excluded from request counting.

Changes

  • StatsBeat.ts — SDK Stats endpoint constants, EU/non-EU region detection (getStatsEndpoint), per-event destination + placeholder-iKey stamping, enabled-by-default feature gate (opt-out via featureOptIn: "sdkStats"), createSdkStatsMgrConfig() helper, and configurable mode + key map support.
  • AppInsightsCore.ts / IAppInsightsCore.ts / index.ts — restore getStatsBeat/setStatsMgr, fields, unload cleanup, stubs, interface methods, and exports.
  • Sender.ts — restore request-counting hooks + statsBeatData; redirect SDK Stats items to the SDK Stats endpoint via a per-item URL override on _doSend/_getPayload, bypassing the customer buffer; SDK Stats sends are excluded from counting (no feedback loop); optimized property access for minification efficiency.
  • AISku.ts — create + init + set the manager after core init (kept out of core/AISKULight for bundle size).
  • IConfiguration.ts — added stats?: IStatsBeatConfig field for dynamic config.
  • StatsType.ts — added eStatsEndpointType enum (SdkStats / Breeze).
  • IStatsBeat.ts — added IStatsBeatConfig.mode and IStatsBeatKeyMap.url fields.
  • Tests — re-enabled all SDK Stats unit tests (StatsBeat.Tests.ts in both core and channel-js), fixed import paths, updated terminology from "statsbeat" to "SDK Stats" in test names and assertions.

Architecture note

createStatsMgr lives in core, which cannot depend on the channel's Breeze EnvelopeCreator. So SDK Stats events flow through _core.track() (reusing the channel's envelope creation) and the redirect to the SDK Stats endpoint happens in the Sender via a per-item endpoint override.

Verification

  • Verified with tsc --noEmit from source using the repo's exact compiler settings — core, channel, and AISKU compile with 0 errors.
  • Reviewed for minification efficiency — no spread operators, optional chaining, or nullish coalescing; optimized repeated property access.
  • Unit tests uncommented and updated with proper terminology.

Follow-ups (left for review)

  • Update bundle-size test thresholds (AISKULightSize, AppInsightsCoreSize) if needed.
  • Run npm run lint-fix + npm run fullCleanBuild before marking ready (requires environment outside VS Code due to Rush/ESLint plugin version conflicts).

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

Completes and enables the SDK Stats manager (createStatsMgr) and routes
the resulting events to the distro-owned SDK Stats ingestion endpoint
(stats.monitor.azure.com / eu.stats.monitor.azure.com) instead of the
customer's breeze endpoint, matching the Microsoft OpenTelemetry distro.

- StatsBeat.ts: add SDK Stats endpoint constants, EU/non-EU region
  detection, per-event destination + placeholder iKey stamping,
  enabled-by-default feature gate, and createSdkStatsMgrConfig().
- AppInsightsCore / IAppInsightsCore / index: restore getStatsBeat,
  setStatsMgr, fields, unload cleanup, stubs, and exports.
- Sender.ts: restore request-counting hooks and statsBeatData; redirect
  SDK Stats items to the SDK Stats endpoint via a per-item URL override,
  bypassing the customer buffer; exclude SDK Stats sends from counting.
- AISku.ts: create, init and set the SDK Stats manager after core init.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds support for sending SDK Stats to either the new SDK Stats endpoint
(stats.monitor.azure.com) or the legacy breeze endpoints, selectable at
runtime via config.stats (IStatsBeatConfig), which is overridable through
the CDN / dynamic config.

- StatsType.ts: add eStatsEndpointType (SdkStats / Breeze) enum.
- IStatsBeat.ts: add IStatsBeatConfig.mode and IStatsBeatKeyMap.url.
- IConfiguration.ts: add stats?: IStatsBeatConfig (dynamic-config surface).
- StatsBeat.ts: factor EU detection into _isEuEndpoint; add
  getStatsBreezeIKey + breeze SDK Stats iKey constants; resolve the
  destination iKey/endpoint per-event in _track based on the (dynamic)
  mode; createSdkStatsMgrConfig now reads config.stats so the endpoint
  and key map can be overridden via the CDN at runtime. Defaults to the
  SDK Stats endpoint.
- index.ts: export the new enum, helper and constants.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Cache telemetryItem.data reference to avoid repeated property lookups.
Reduces minified output size in hot path (runs for every telemetry item).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Uncommented all SDK Stats test files
- Updated test case names from 'StatsBeat' to 'SDK Stats'
- Updated test assertions to use 'SDK Stats' terminology
- Fixed import paths for relocated modules
- Tests now reference the feature as 'SDK Stats' in all user-facing messages

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@JacksonWeber
Jackson Weber (JacksonWeber) marked this pull request as ready for review June 17, 2026 22:43
@JacksonWeber
Jackson Weber (JacksonWeber) requested a review from a team as a code owner June 17, 2026 22:43
Copilot AI review requested due to automatic review settings June 17, 2026 22:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR re-enables the SDK Stats (StatsBeat) manager in the Application Insights JavaScript SDK and adds support for routing SDK Stats telemetry to a distro-owned ingestion endpoint (EU vs non-EU), rather than piggy-backing on the customer’s Breeze endpoint. It introduces a runtime-selectable destination mode via config/dynamic config, restores core APIs for stats manager access, and updates the Sender channel to redirect per-item sends when the SDK Stats endpoint override marker is present.

Changes:

  • Added SDK Stats endpoint + iKey resolution (EU vs non-EU), plus configurable routing mode (SDK Stats endpoint vs legacy Breeze) and per-event destination stamping.
  • Restored core APIs (getStatsBeat / setStatsMgr) and exports to support enabling SDK Stats from AISKU without increasing core bundle size.
  • Updated Sender to detect SDK Stats endpoint overrides, bypass the customer buffer for SDK Stats sends, and re-enabled associated unit tests.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
shared/AppInsightsCore/Tests/Unit/src/aiunittests.ts Re-enabled SDK Stats unit test suite execution.
shared/AppInsightsCore/Tests/Unit/src/ai/StatsBeat.Tests.ts Re-enabled and updated core SDK Stats unit tests.
shared/AppInsightsCore/src/interfaces/ai/IStatsBeat.ts Added SDK Stats routing config surface (mode, per-keymap url).
shared/AppInsightsCore/src/interfaces/ai/IConfiguration.ts Added stats?: IStatsBeatConfig for runtime/dynamic config control.
shared/AppInsightsCore/src/interfaces/ai/IAppInsightsCore.ts Restored getStatsBeat / setStatsMgr APIs on the core interface.
shared/AppInsightsCore/src/index.ts Re-exported SDK Stats enums, interfaces, and core helper APIs/constants.
shared/AppInsightsCore/src/enums/ai/StatsType.ts Added eStatsEndpointType / StatsEndpointType for routing mode selection.
shared/AppInsightsCore/src/core/StatsBeat.ts Implemented SDK Stats endpoint/iKey resolution, routing mode support, and default manager config helper.
shared/AppInsightsCore/src/core/AppInsightsCore.ts Restored core state + lifecycle cleanup for stats manager / stats beat instance.
channels/applicationinsights-channel-js/Tests/Unit/src/StatsBeat.tests.ts Re-enabled channel-level SDK Stats tests for Sender request counting / emission.
channels/applicationinsights-channel-js/Tests/Unit/src/aichannel.tests.ts Re-enabled channel SDK Stats tests in the channel test runner.
channels/applicationinsights-channel-js/src/Sender.ts Added per-item SDK Stats endpoint override handling + direct send path, and restored request counting hooks.
AISKU/src/AISku.ts Enabled SDK Stats from AISKU after core init via createSdkStatsMgrConfig().

Comment thread channels/applicationinsights-channel-js/Tests/Unit/src/StatsBeat.tests.ts Outdated
Comment thread channels/applicationinsights-channel-js/Tests/Unit/src/StatsBeat.tests.ts Outdated
Comment thread shared/AppInsightsCore/Tests/Unit/src/ai/InternalSdkStats.Tests.ts
Comment thread shared/AppInsightsCore/Tests/Unit/src/ai/StatsBeat.Tests.ts Outdated
- Initialize the core before init-ing the stats manager against the same core instance so the manager actually enables (channel + core tests)
- Match the stats endCfg to the Sender's endpoint so metrics are tracked
- Pass an IStatsBeatState to getStatsBeat() when wiring the count spy
- Fix the xhr test config built with '&&' that discarded the Sender config
- Guard the dynamic-config test against double core initialization

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
IConfiguration exposes 'stats' (IStatsBeatConfig) at the top level, not under a '_sdk' wrapper. Update the SDK Stats unit tests to set config.stats and read cfg.stats so they compile against IConfiguration.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…size budget

- Tick the real short interval (shrtInt*1000 ms) so the stats timer fires in the count/countException tests
- FeatureOptInMode.none and a null mode fall back to the SDK default (enabled), so assert SDK Stats stays enabled rather than being removed
- Bump core size budget to 135 KB raw / 55 KB deflate to cover the re-enabled getStatsBeat/setStatsMgr core APIs (was 133/54)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The 'SDK Stats initializes when stats is true' test initialized the stats manager but never called core.setStatsMgr(), so core.getStatsBeat() returned null. Register the manager with the core so the instance is created.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Enabling SDK Stats from AISKU pulls in the re-enabled core getStatsBeat/setStatsMgr APIs and stats manager wiring, growing the AISKU bundle. Bump the budget to 178 KB / 72 KB deflate (was 175/71).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread shared/AppInsightsCore/src/core/StatsBeat.ts Outdated
…-sdk-stats

# Conflicts:
#	AISKU/Tests/Unit/src/AISKUSize.Tests.ts
#	AISKU/src/AISku.ts
#	channels/applicationinsights-channel-js/src/Sender.ts
#	shared/AppInsightsCore/Tests/Unit/src/ai/AppInsightsCoreSize.Tests.ts
#	shared/AppInsightsCore/src/index.ts
The new SDK Stats code in AppInsightsCore pushes the core bundle past
its 135 KB budget and the AISKU bundle past its 178 KB / 72 KB budgets.
Bump core to 137 KB and AISKU to 181 KB / 73 KB to match measured sizes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…g support

Replace the detached config snapshot in createSdkStatsMgrConfig with the
setDf/ref pattern so SDK Stats defaults live in the single global config
(overridable via CDN/dynamic config and the SKUs) and runtime changes are
tracked. Updates IStatsMgrConfig.getCfg to receive IWatchDetails.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…g refactor

The setDf/ref refactor in createSdkStatsMgrConfig pushes the core deflate
size just past 55 KB (55.01 KB) and the AISKU deflate budget; bump core
deflate 55 -> 56 KB and AISKU deflate 73 -> 74 KB to match.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…g from the global config

The SDK Stats manager now references the single global config (config.stats) directly and picks up dynamic/CDN changes at runtime, gated behind the sdkStats feature flag (enabled by default, opt-out via featureOptIn). Removes the createSdkStatsMgrConfig factory and IStatsMgrConfig interface; init is now init(core, featureName?). Updates AISKU and StatsBeat tests accordingly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.

Comment thread shared/AppInsightsCore/src/core/StatsBeat.ts Outdated
Comment thread shared/AppInsightsCore/Tests/Unit/src/ai/StatsBeat.Tests.ts
Comment thread shared/AppInsightsCore/src/core/StatsBeat.ts Outdated
Comment thread shared/AppInsightsCore/src/core/StatsBeat.ts Outdated
Comment thread shared/AppInsightsCore/src/core/StatsBeat.ts Outdated
Comment thread shared/AppInsightsCore/src/core/StatsBeat.ts Outdated
Comment thread shared/AppInsightsCore/src/core/InternalSdkStats.ts Outdated
…tination

Remove the breeze-endpoint mode and key-map/endCfg routing so SDK Stats
collection is gated solely on the remote cfg enabled flag and sent to the
cfg-provided host. Parsing is fail-closed (enabled === true, url must be a
string).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…d config + ref()

Address PR review: drop the persistent _internalSdkStatsConfig field and the
details.ref() hack. Copy the override fetch fn and interval into local vars in
the config-change handler, making the override fetch fn a dynamic leaf so merged
runtime updates still re-run the handler. Seed the stats default via a plain
object (not cfgDfMerge) so setDf no longer marks stats as a reference.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Address PR review: the setDf cast was only needed because the generic CfgType
propagated into onConfigChange. Pin onConfigChange to IConfiguration (we only
touch IConfiguration-level config) so setDf infers IConfiguration and the cast
is no longer required.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The type field was never read for logic (getSdkStats isolates instances
by endpoint, not type) and customer stats are handled separately via
createSdkStatsNotifCbk, so the enum is no longer needed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…zipped)

Refactor InternalSdkStats and SdkStatsNotificationCbk to shrink the core
minified bundle: objForEachKey merge, shared count/inc helpers, hoisted
repeated string literals, arrIndexOf for EU lookup, reuse MetricDataType.

Core min.js: raw 139595 -> 139218 (-377 B), gzip 56693 -> 56566 (-127 B).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread shared/AppInsightsCore/src/core/SdkStatsNotificationCbk.ts Fixed
testCleanup nulled the initialized AppInsightsCore without unloading it,
leaking hooks/timers/config watchers into subsequent tests. Call
core.unload(false) when initialized, matching the core test convention.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Revert the _incCounts extraction so success/bucket counts assign directly
to their objCreate(null) counters. The extracted target parameter hid the
null-prototype provenance, causing CodeQL js/prototype-polluting-assignment
to flag target[t]. Direct assignment restores the safe-sink recognition.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 27 out of 27 changed files in this pull request and generated 3 comments.

Comment thread shared/AppInsightsCore/src/core/InternalSdkStats.ts Outdated
Comment thread channels/applicationinsights-channel-js/src/Sender.ts
Comment thread shared/AppInsightsCore/Tests/Unit/src/ai/InternalSdkStats.Tests.ts Outdated
* gate collection and resolve the ingestion host. EU endpoint is used for EU data-boundary regions.
*/
export const STATS_SDK_CFG_URL_NON_EU = "https://data.stats.monitor.azure.com/cfg/v1.json";
export const STATS_SDK_CFG_URL_EU = "https://eu-data.stats.monitor.azure.com/cfg/v1.json";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We should minimize the duplication here as strings can't be compressed.
We should have a
const scheme = "https://";
const baseUrl = "data.stats.monitor.azure.com/cfg/v1.json";
and then create the above from these :-)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We can avoid these strings by not sending in the event that we can't find the URLs from the config service.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Changed this to pull from cfgUrl and construct the EU endpoint based on that.

* The placeholder instrumentation key used when reporting SDK statistics to the distro-owned
* SDK Stats ingestion endpoint. The endpoint does not require authentication, the placeholder
* key only satisfies the connection-string / envelope iKey requirement and is ignored
* server-side. This matches the convention used by the Microsoft OpenTelemetry distros.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

"ignored server-side" is not always true.

If the config returns breeze as the URL then no stats will be collected, so we should not send any telemetry (regardless of the backend) without a instrumentation key -- this can be in the config and we can have dynamic config provide / replace the value.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Should put the ikey in the config as well. We could be sending to breeze, so this is still required.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I removed the hardcoded old STATS_SDK_IKEY and source it now from the dynamic config using config.stats.iKey. Should not work for sending this to breeze as well in case that ever happens in the future.

import { getResponseText, isFeatureEnabled, openXhr } from "../utils/HelperFuncs";

const STATS_COLLECTION_SHORT_INTERVAL: number = 900000; // 15 minutes
const STATS_MIN_INTERVAL_SECONDS = 60; // 1 minute

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Do we really want to send the events every minute (assuming the SDK is actively sending data)?
I believe that .Net only sends every 30/60 minutes...
Which means we would need to cache this in session / local storage or something.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Let's discuss this further, my understanding was that we can't expect the session to live that long so lowering the timing threshold would give us a better chance of collecting telemetry. Let's discuss further in the meeting Monday.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We should utilize session storage. We should start this at daily, but pull from the config so we can change this on the fly later.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added the session storage for the internal SDK Stats events storage. Set the default collection interval to 1 hr but it should accept a stats.shrtInt value from the dynamic config to allow us to determine how often internal SDK stats are sent.

export const STATS_SDK_FEATURE = "sdkStats";

// EU data-boundary regions, mirrors the EU region set used by the Azure Monitor OpenTelemetry exporter
const STATS_EU_REGIONS = [

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We can probably reduce this eg.
"france" vs "francecentral", "francesouth" etc.

or we extract the duplicate portions "south", "central", "east", "west" and the countries and then create the merge strings within the array.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Replaced 12 full region names with 8 shared prefixes. Also moved to using a regex parse. Copilot suggested this and looks like it saves an addition 71 bytes.

* Returns the SDK Stats config URL (`cfg/v1.json`) for the endpoint (EU vs non-EU).
*/
export function getStatsCfgUrl(endpoint: string): string {
return _isEuEndpoint(endpoint) ? STATS_SDK_CFG_URL_EU : STATS_SDK_CFG_URL_NON_EU;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We should cache the "endpoint" resolution to avoid running the _isEuEndpoint for every time _track is called.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Just create local map for this (cache result)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added a cache mapping customer endpoints to resolved SDK Stats config URLs, and updated _isEuEndpoint() to now run only once per endpoint instead of for each metric passed to _track(). Cache will clear automatically upon stats.cfgUrl change.

"language": STATS_LANGUAGE,
"version": internalSdkStatsStats.sdkVer || "unknown",
"endpoint": "breeze",
"host": _networkCounter.host

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Some additional properties that are web specific that would be good to have reported as part of the SDK Stats is whether the SDK was loaded via the snippet and what version ( ai.context.internal.snippetVer``) and if it was loaded via CDN or npm (ai.context.internal.sdkSrc<- the CDN name it was loaded from) Set via the_findSdkSourceFile()` in AISku.ts (not currently setfor 1DS) -- we just have not have anywhere to report this before

@JacksonWeber Jackson Weber (JacksonWeber) Jul 25, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That's a great idea. Happy to brainstorm some more values that we could pass here. I can propose a spec PR for SDK Stats from there. How would you feel about that coming as a part of a follow up PR since any change should be additive?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We could use feature/instrumentation stats for the collection of this information to reduce size over the wire.

Follow up discussion with Hector/Leighton/myself to determine the best approach.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've added the snippet version to the SDK version in place of something like a distro or exporter per the naming convention established in the spec. As for collecting NPM load vs. CDN load, I'd like to address this in a follow up PR for feature SDK stats if possible since this PR is already quite large.

internalSdkStatsEvent.data = internalSdkStatsEvent.data || {};
internalSdkStatsEvent.data[STATS_SDK_ENDPOINT_KEY] = url;

_core.track(internalSdkStatsEvent);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I don't think this is going to work??

As it looks like you are passing in the core from the AISku instance (which is the users configured instance), which will be batched and sent to the users endpoint (channel control).

It also means that when the end-users have their own telemetry initializers attached to the core to add / remove properties they may add / remove PII to the internal SDK events, also if they have not written their initializer correctly (they expect fields / properties that don't exist for a metric (the sdk stats event) it could cause an unhandled JavaScript exception. And I've seen some sites throw a complete page load failure error (causing their site to be declared "offline") on any unhandled exception.

While we need the root _core instance to monitor it for any changed config (loaded via the dynamic config or users changes their endpoint url), we should create instance and initialize our own instance of the SDK configured with the cfg.json endpoint.

which means that the init function should have an additional callback to support the creating of the SDK Instance (as not everyone uses core, properties and sender), so that for the main operation (SKU) we can control what is included (so AISku Lite there is no properties, for 1DS there is no Sender or AI Properties, so the SKU's in 1DS have to initialize themselves) etc.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Start with 3p (but constructed to work with both), in particular the JSLL shim might be a good place to start.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The customer’s core is now only used to observe dynamic configuration and endpoint changes. SDK Stats no longer passes through the customer’s telemetry initializers, plugins, batching, headers, or ingestion endpoint.

Each SKU now provides a callback to create the appropriate isolated SDK Stats pipeline. I'll do some more testing with this new approach, but from what I've done so far it looks to be working.

- Type _defaultStatsCfgFetch / _parseStatsCfg as nullable (IInternalSdkStatsCfgResult | null) to match the actual null-on-failure contract
- Do not forward customer configured headers when the send url is overridden for the SDK Stats endpoint
- Use a numeric startTime in the InternalSdkStats count() test so the duration arithmetic is valid

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Removes the hardcoded STATS_SDK_CFG_URL_NON_EU / STATS_SDK_CFG_URL_EU
constants and instead reads the SDK Stats config url from the dynamic
config (config.stats.cfgUrl). When no url has been configured no SDK
Stats are collected or sent.

- cfgUrl is registered as a dynamic property so the value delivered by
  the CDN configuration after initialization re-triggers the handler
- the EU data-boundary url is derived by inserting the 'eu-' host prefix
  into the configured url, so only a single url needs to be configured
- getStatsCfgUrl() now takes the configured url and returns null when
  none is available
- _cfgCache uses objCreate(null) as it is now keyed by a config supplied
  value

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Store in-progress counters in session storage, default exports to a one-hour interval, and reschedule active windows when dynamic configuration changes. Preserve counters while remote configuration is unresolved and require the SDK Stats config URL and instrumentation key before sending.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use a compact prefix regex for EU data-boundary region detection while preserving coverage for all previously supported regions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Cache the resolved config URL per customer endpoint and invalidate the cache when the dynamic cfgUrl changes, avoiding repeated EU-region resolution for each metric.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Store only the snippet version in dynamic stats config and append it as the snp component when Sender creates SDK Stats. Keep redirected SDK Stats transport completions isolated so they cannot count themselves or replace the customer endpoint.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Create SDK Stats through a SKU-provided core factory so customer initializers, plugins, headers, buffering, and endpoints cannot affect internal telemetry. AISKU supplies a minimal AppInsightsCore and Sender pipeline, while the manager observes the customer core only for dynamic configuration and recreates or unloads the isolated core as its endpoint and iKey change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0c471708-eb77-4878-8738-55114be7bab1
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0c471708-eb77-4878-8738-55114be7bab1
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0c471708-eb77-4878-8738-55114be7bab1
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.

4 participants