Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/azureMonitor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@

/**
* Azure Monitor–specific initialization that runs alongside the distro.
* Statsbeat, Browser SDK Loader, and Live Metrics SDK prefix are
* SDK Stats, Browser SDK Loader, and Live Metrics SDK prefix are
* Azure Monitor concerns — not part of the generic OTel distro lifecycle.
*/

import type { InternalConfig } from "../shared/config.js";
import type { StatsbeatFeatures } from "../types.js";
import type { SdkStatsFeatures } from "../types.js";
import { BrowserSdkLoader } from "./browserSdkLoader/browserSdkLoader.js";
import { setSdkPrefix } from "./metrics/quickpulse/utils.js";
import { Logger } from "../shared/logging/index.js";
Expand Down Expand Up @@ -53,12 +53,12 @@ export function validateAzureMonitorConfig(config: InternalConfig): boolean {
}

/**
* Compute Azure Monitor–specific statsbeat features from the config.
* Compute Azure Monitor–specific SDK Stats features from the config.
* Does not write to the env var — the caller consolidates all features.
*
* @internal
*/
export function getAzureMonitorStatsbeatFeatures(config: InternalConfig): StatsbeatFeatures {
export function getAzureMonitorSdkStatsFeatures(config: InternalConfig): SdkStatsFeatures {
const resourceAttributes = config.resource.attributes;
const aksResourceDetected =
SEMRESATTRS_K8S_CLUSTER_NAME in resourceAttributes ||
Expand Down
12 changes: 6 additions & 6 deletions src/azureMonitor/metrics/quickpulse/liveMetrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ import type {
} from "./types.js";
import { QuickPulseOpenTelemetryMetricNames } from "./types.js";
import { hrTimeToMilliseconds, suppressTracing } from "@opentelemetry/core";
import { getInstance } from "../../../utils/statsbeat.js";
import { getInstance } from "../../../utils/sdkStats.js";
import type { CollectionConfigurationError } from "../../generated/index.js";
import { Filter } from "./filtering/filter.js";
import { Validator } from "./filtering/validator.js";
Expand Down Expand Up @@ -127,7 +127,7 @@ export class LiveMetrics {
private lastExceptionRate: { count: number; time: number } = { count: 0, time: 0 };
private lastCpuUsage: NodeJS.CpuUsage;
private lastHrTime: bigint;
private statsbeatOptionsUpdated = false;
private sdkStatsOptionsUpdated = false;
private etag: string = "";
private errorTracker: CollectionConfigurationErrorTracker =
new CollectionConfigurationErrorTracker();
Expand Down Expand Up @@ -282,10 +282,10 @@ export class LiveMetrics {
if (this.meterProvider) {
return;
}
// Turn on live metrics active collection for statsbeat
if (!this.statsbeatOptionsUpdated) {
getInstance().setStatsbeatFeatures({}, { liveMetrics: true });
this.statsbeatOptionsUpdated = true;
// Turn on live metrics active collection for SDK Stats
if (!this.sdkStatsOptionsUpdated) {
getInstance().setSdkStatsFeatures({}, { liveMetrics: true });
this.sdkStatsOptionsUpdated = true;
}
this.totalDependencyCount = 0;
this.totalExceptionCount = 0;
Expand Down
41 changes: 20 additions & 21 deletions src/distro/distro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ import { ConnectionStringParser } from "../azureMonitor/utils/connectionStringPa
import { AZURE_MONITOR_OPENTELEMETRY_VERSION } from "../types.js";
import { patchOpenTelemetryInstrumentationEnable } from "../utils/opentelemetryInstrumentationPatcher.js";
import { parseResourceDetectorsFromEnvVar } from "../utils/common.js";
import { getInstance as getStatsbeatInstance } from "../utils/statsbeat.js";
import { getInstance as getSdkStatsInstance } from "../utils/sdkStats.js";
import {
setupAzureMonitorComponents,
hasAzureMonitorConnectionString,
validateAzureMonitorConfig,
getAzureMonitorStatsbeatFeatures,
getAzureMonitorSdkStatsFeatures,
} from "../azureMonitor/index.js";
import { isOtlpEnabled, createOtlpComponents } from "../otlp/index.js";
import { A365Configuration, Agent365Exporter, A365SpanProcessor } from "../a365/index.js";
Expand All @@ -40,13 +40,13 @@ import type {
InstrumentationOptions,
OpenAIAgentsInstrumentationConfig,
LangChainInstrumentationConfig,
StatsbeatFeatures,
StatsbeatInstrumentations,
SdkStatsFeatures,
SdkStatsInstrumentations,
} from "../types.js";
import {
MICROSOFT_OPENTELEMETRY_VERSION,
APPLICATIONINSIGHTS_SDKSTATS_DISABLED,
StatsbeatFeature,
SdkStatsFeature,
} from "../types.js";
import { createInstrumentations, createSampler, createViews } from "./instrumentations.js";
import { Logger } from "../shared/logging/index.js";
Expand Down Expand Up @@ -163,15 +163,14 @@ export function useMicrosoftOpenTelemetry(options?: MicrosoftOpenTelemetryOption
(!!options?.azureMonitor || hasAzureMonitorConnectionString(config));
const azureMonitorEnabled = azureMonitorRequested && validateAzureMonitorConfig(config);

// ── SDKStats: record distro feature bits for ALL paths ──────────────────
// ── SDKStats: record distro feature bits for ALL paths ──────────────────
// These bits are emitted via SDKStats regardless of which exporter is
// active. When Azure Monitor is enabled the exporter package's own
// Statsbeat picks them up via `AZURE_MONITOR_STATSBEAT_FEATURES`; when
// it is not, the standalone `SdkStatsManager` initialised below carries
// them to the well-known Statsbeat ingestion endpoint.
// SDKStats pipeline picks them up via `AZURE_MONITOR_STATSBEAT_FEATURES`;
// when it is not, the standalone `SdkStatsManager` initialised below
// carries them to the well-known SDKStats ingestion endpoint.
const otlpActive = isOtlpEnabled();
setSdkStatsFeature(StatsbeatFeature.DISTRO);
setSdkStatsFeature(SdkStatsFeature.DISTRO);
if (a365Config.enabled) {
setSdkStatsFeature(SdkStatsDistroFeature.A365_EXPORT);
}
Expand All @@ -182,13 +181,13 @@ export function useMicrosoftOpenTelemetry(options?: MicrosoftOpenTelemetryOption
// Reset dispose callback to avoid stale references from a previous initialization
disposeAzureMonitor = undefined;

// ── Azure Monitor components (statsbeat, browser SDK loader, etc.) ─
// ── Azure Monitor components (SDK Stats, browser SDK loader, etc.) ─
if (azureMonitorEnabled) {
disposeAzureMonitor = setupAzureMonitorComponents(config);
}

// ── Statsbeat (feature & instrumentation tracking for all paths) ──
const statsbeatInstrumentations: StatsbeatInstrumentations = {
// ── SDK Stats (feature & instrumentation tracking for all paths) ──
const sdkStatsInstrumentations: SdkStatsInstrumentations = {
azureSdk: config.instrumentationOptions?.azureSdk?.enabled,
mongoDb: config.instrumentationOptions?.mongoDb?.enabled,
mySql: config.instrumentationOptions?.mySql?.enabled,
Expand All @@ -197,9 +196,9 @@ export function useMicrosoftOpenTelemetry(options?: MicrosoftOpenTelemetryOption
bunyan: config.instrumentationOptions?.bunyan?.enabled,
winston: config.instrumentationOptions?.winston?.enabled,
};
const statsbeatFeatures: StatsbeatFeatures = {
const sdkStatsFeatures: SdkStatsFeatures = {
...(azureMonitorEnabled
? getAzureMonitorStatsbeatFeatures(config)
? getAzureMonitorSdkStatsFeatures(config)
: {
browserSdkLoader: false,
aadHandling: false,
Expand All @@ -210,7 +209,7 @@ export function useMicrosoftOpenTelemetry(options?: MicrosoftOpenTelemetryOption
otlp: otlpActive,
customerSdkStats: process.env[APPLICATIONINSIGHTS_SDKSTATS_DISABLED]?.toLowerCase() === "true",
};
getStatsbeatInstance().setStatsbeatFeatures(statsbeatInstrumentations, statsbeatFeatures);
getSdkStatsInstance().setSdkStatsFeatures(sdkStatsInstrumentations, sdkStatsFeatures);

// ── Register global providers ─────────────────────────────────────
// Remove global providers in OpenTelemetry, these would be overridden if present
Expand Down Expand Up @@ -378,19 +377,19 @@ export function useMicrosoftOpenTelemetry(options?: MicrosoftOpenTelemetryOption
sdk.start();

// ── SDKStats: standalone pipeline ─────────────────────────────────
// The standalone pipeline ALWAYS runs so per-export network statsbeat
// The standalone pipeline ALWAYS runs so per-export network SDKStats
// (`Request_Success_Count` gauge) for A365 / OTLP transmits is captured.
//
// - When Azure Monitor is enabled (`networkOnly: true`): only the
// network gauges are registered. The Feature / Feature.instrumentations
// long-interval statsbeat is owned by the AzMon exporter, with our
// distro bits bridged in via `setStatsbeatFeatures` →
// `AZURE_MONITOR_STATSBEAT_FEATURES`. Network statsbeat is safe to
// long-interval metrics are owned by the AzMon exporter, with our
// distro bits bridged in via `setSdkStatsFeatures` →
// `AZURE_MONITOR_STATSBEAT_FEATURES`. The network pipeline is safe to
// coexist because the (endpoint, host) attributes partition the
// time series (AzMon ingestion hosts vs A365 / OTLP hosts).
// - When Azure Monitor is disabled: the standalone pipeline owns the
// full set (feature + instrumentation + network) and ships them to
// the well-known statsbeat endpoint.
// the well-known SDKStats endpoint.
//
// `cikey` is reported as a customDimension on every SDKStats
// observation per the spec, but ONLY when the customer is exporting
Expand Down
30 changes: 15 additions & 15 deletions src/sdkstats/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@

/**
* SDKStats manager — sends SDK self-telemetry to the Application Insights
* statsbeat ingestion endpoint, independent of the customer's telemetry
* SDKStats ingestion endpoint, independent of the customer's telemetry
* pipeline.
*
* When the full Azure Monitor pipeline is enabled, the exporter package's
* own statsbeat machinery handles SDKStats emission and the distro just
* own SDKStats machinery handles SDKStats emission and the distro just
* publishes its bits via the `AZURE_MONITOR_STATSBEAT_FEATURES` env var
* for the exporter to read. For A365-only, OTLP-only, or Console-only
* customers this manager spins up a standalone `MeterProvider` →
Expand Down Expand Up @@ -45,8 +45,8 @@ const DEFAULT_LONG_EXPORT_INTERVAL_MS = 24 * 60 * 60 * 1000;
const SDKSTATS_LONG_EXPORT_INTERVAL_ENV = "APPLICATIONINSIGHTS_STATS_LONG_EXPORT_INTERVAL";

/**
* Default short export interval (15 minutes) for network statsbeat
* counters. Matches the Application Insights statsbeat short-interval
* Default short export interval (15 minutes) for network SDKStats
* counters. Matches the Application Insights SDKStats short-interval
* cadence used by the Python distro (`_get_stats_short_export_interval()`
* in `azure.monitor.opentelemetry.exporter.statsbeat._utils`).
*
Expand All @@ -65,7 +65,7 @@ const SDKSTATS_SHORT_EXPORT_INTERVAL_ENV = "APPLICATIONINSIGHTS_STATS_SHORT_EXPO
/**
* Override env var: redirect SDKStats envelopes to a custom App
* Insights connection string. When unset, SDKStats flow to the
* Microsoft-owned statsbeat resource (`NON_EU_CONNECTION_STRING` in
* Microsoft-owned SDKStats resource (`NON_EU_CONNECTION_STRING` in
* the AzMon exporter package). Primarily useful for testing.
* Matches the Python distro env var name.
*
Expand Down Expand Up @@ -111,7 +111,7 @@ export class SdkStatsManager {
}

/**
* Set up SDKStats export via the Azure Monitor statsbeat endpoint.
* Set up SDKStats export via the Azure Monitor SDKStats endpoint.
*
* Returns `true` if the standalone pipeline was initialized (or was
* already initialized), `false` if SDKStats are disabled via env var
Expand All @@ -129,9 +129,9 @@ export class SdkStatsManager {
// The exporter package's `exports` map blocks subpath imports, so
// we resolve the package's own package.json to find its install
// location on disk and require the internal modules by absolute
// path. The statsbeat exporter is the correct vehicle for SDKStats
// — it tags envelopes with the statsbeat ikey/endpoint and avoids
// recursive statsbeat-of-statsbeat reporting via its
// path. The AzureMonitorStatsbeatExporter is the correct vehicle for
// SDKStats — it tags envelopes with the SDKStats ikey/endpoint and
// avoids recursive SDKStats-of-SDKStats reporting via its
// `isStatsbeatExporter` flag.
const baseUrl = getModuleParentURL() ?? pathToFileURL(process.cwd() + "/").href;
const requireFromHere = createRequire(baseUrl);
Expand All @@ -140,20 +140,20 @@ export class SdkStatsManager {
);
const exporterPackageDir = dirname(exporterPackageJsonPath);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const statsbeatExporterModule: any = requireFromHere(
const sdkStatsExporterModule: any = requireFromHere(
join(exporterPackageDir, "dist", "esm", "export", "statsbeat", "statsbeatExporter.js"),
);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const statsbeatTypesModule: any = requireFromHere(
const sdkStatsTypesModule: any = requireFromHere(
join(exporterPackageDir, "dist", "esm", "export", "statsbeat", "types.js"),
);
const AzureMonitorStatsbeatExporter = statsbeatExporterModule.AzureMonitorStatsbeatExporter;
const NON_EU_CONNECTION_STRING = statsbeatTypesModule.NON_EU_CONNECTION_STRING;
const AzureMonitorStatsbeatExporter = sdkStatsExporterModule.AzureMonitorStatsbeatExporter;
const NON_EU_CONNECTION_STRING = sdkStatsTypesModule.NON_EU_CONNECTION_STRING;

// Allow overriding the SDKStats ingestion target via env var,
// matching the Python distro's APPLICATIONINSIGHTS_STATS_CONNECTION_STRING
// hook. Primarily useful for testing — production should leave
// this unset so SDKStats flows to the Microsoft-owned statsbeat
// this unset so SDKStats flows to the Microsoft-owned SDKStats
// resource (NON_EU_CONNECTION_STRING).
const connectionString =
process.env[SDKSTATS_CONNECTION_STRING_ENV] ?? NON_EU_CONNECTION_STRING;
Expand All @@ -177,7 +177,7 @@ export class SdkStatsManager {
});
}

// Short-interval pipeline (15 min) — network statsbeat gauges.
// Short-interval pipeline (15 min) — network SDKStats gauges.
const shortExporter = new AzureMonitorStatsbeatExporter({
connectionString,
disableOfflineStorage: true,
Expand Down
20 changes: 10 additions & 10 deletions src/sdkstats/metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ export const FEATURE_TYPE_INSTRUMENTATION = 1;

const FEATURE_METRIC_NAME = "Feature";
const INSTRUMENTATION_METRIC_NAME = "Feature.instrumentations";
const STATSBEAT_LANGUAGE = "node";
const SDKSTATS_LANGUAGE = "node";

/**
* Per-metric configuration for the network statsbeat gauges.
* Per-metric configuration for the network SDKStats gauges.
*/
interface NetworkGaugeSpec {
metric: NetworkMetricName;
Expand Down Expand Up @@ -66,13 +66,13 @@ export interface SdkStatsMetricsOptions {
/**
* When `true`, skip the Feature / Feature.instrumentations gauges. Used
* on the Azure-Monitor-enabled path because the AzMon exporter's own
* long-interval statsbeat already emits those gauges (with our distro
* long-interval SDKStats already emits those gauges (with our distro
* bits bridged in via `AZURE_MONITOR_STATSBEAT_FEATURES`); registering
* them here would double-count.
*
* The network statsbeat gauge (`Request_Success_Count`) is always
* The network SDKStats gauge (`Request_Success_Count`) is always
* registered regardless of this flag — coexistence with AzMon's own
* network statsbeat is safe because the (endpoint, host) attributes
* network SDKStats is safe because the (endpoint, host) attributes
* partition the time series.
*/
networkOnly?: boolean;
Expand All @@ -84,7 +84,7 @@ export interface SdkStatsMetricsOptions {
*/
longMeterProvider?: MeterProvider;
/**
* MeterProvider for short-interval gauges (network statsbeat like
* MeterProvider for short-interval gauges (network SDKStats like
* `Request_Success_Count`). Gauges are registered on a meter from
* this provider so they export at the short (15 min) cadence.
*/
Expand All @@ -94,7 +94,7 @@ export interface SdkStatsMetricsOptions {
/**
* Registers observable gauges that emit feature/instrumentation data
* derived from the global SDKStats state, plus per-export network
* statsbeat counters drained from {@link ./networkStats.js}.
* SDKStats counters drained from {@link ./networkStats.js}.
*/
export class SdkStatsMetrics {
private readonly commonAttributes: Record<string, string>;
Expand Down Expand Up @@ -141,13 +141,13 @@ export class SdkStatsMetrics {
attach: "Manual",
runtimeVersion: process.version,
os: os.type(),
language: STATSBEAT_LANGUAGE,
language: SDKSTATS_LANGUAGE,
version: distroVersion || MICROSOFT_OPENTELEMETRY_VERSION,
cikey: cikey || "N/A",
};

// Feature / instrumentation bitmask gauges are skipped when running
// alongside the Azure Monitor exporter's own statsbeat — that pipeline
// alongside the Azure Monitor exporter's own SDKStats — that pipeline
// already emits them (with our distro bits bridged in via
// `_bridge_sdkstats_to_azure_monitor`) and would collide with these.
// These gauges are registered on the long-interval MeterProvider.
Expand All @@ -165,7 +165,7 @@ export class SdkStatsMetrics {
instrumentationGauge.addCallback(this.observeInstrumentations);
}

// Network statsbeat gauges — always registered on the short-interval
// Network SDKStats gauges — always registered on the short-interval
// MeterProvider. Each callback drains the counts accumulated by
// exporters between observations and emits one Observation per
// (endpoint, host) tuple.
Expand Down
14 changes: 7 additions & 7 deletions src/sdkstats/networkStats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Licensed under the MIT License.

/**
* Network statsbeat accumulator for SDK self-telemetry.
* Network SDKStats accumulator for SDK self-telemetry.
*
* Per-export success counts for telemetry exporters. Exporters call
* {@link recordSuccess} after each successful transmit; the
Expand All @@ -13,17 +13,17 @@
* distro (microsoft/opentelemetry-distro-python#144).
*/

// Metric names must match the AzMon statsbeat backend's recognized
// Metric names must match the AzMon SDKStats backend's recognized
// schema (see `StatsbeatCounter` enum in
// `@azure/monitor-opentelemetry-exporter/dist/esm/export/statsbeat/types.js`).
// Sending envelopes under any other name returns HTTP 200 but the
// backend doesn't index them, so they're invisible in the statsbeat
// backend doesn't index them, so they're invisible in the SDKStats
// dashboards. The constants below intentionally match the wire-format
// names — do NOT rename them to lowercase.
export const REQUEST_SUCCESS_NAME = "Request_Success_Count";

/**
* Names of registered network statsbeat metrics, in registration order.
* Names of registered network SDKStats metrics, in registration order.
*
* @internal
*/
Expand All @@ -32,7 +32,7 @@ export const NETWORK_METRIC_NAMES = [REQUEST_SUCCESS_NAME] as const;
export type NetworkMetricName = (typeof NETWORK_METRIC_NAMES)[number];

/**
* Composite key for an aggregated network statsbeat counter.
* Composite key for an aggregated network SDKStats counter.
*
* Per the Application Insights SDKStats spec the per-key dimensions are
* `endpoint` (category, e.g. "otlp", "a365") and `host` (stamp-specific
Expand Down Expand Up @@ -75,7 +75,7 @@ export function recordSuccess(endpoint: string, host: string): void {
/**
* Compute the stamp-specific short host for the SDKStats `host` dimension.
*
* Mirrors `getShortHost` in the AzMon exporter's `NetworkStatsbeatMetrics`
* Mirrors `getShortHost` in the AzMon exporter's `NetworkStatsbeatMetrics` class
* but additionally strips any trailing port (`:4318`) so localhost-style
* URLs report a clean `localhost` instead of `localhost:4318`. Examples:
* `https://westus2-1.in.applicationinsights.azure.com` → `westus2`
Expand Down Expand Up @@ -129,7 +129,7 @@ export function drain(metric: NetworkMetricName): Map<NetworkKey, number> {
}

/**
* @internal Test-only: clear all network statsbeat counters.
* @internal Test-only: clear all network SDKStats counters.
*/
export function _resetAllForTest(): void {
for (const name of NETWORK_METRIC_NAMES) {
Expand Down
Loading
Loading