diff --git a/src/azureMonitor/index.ts b/src/azureMonitor/index.ts index 4202257..d894bd8 100644 --- a/src/azureMonitor/index.ts +++ b/src/azureMonitor/index.ts @@ -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"; @@ -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 || diff --git a/src/azureMonitor/metrics/quickpulse/liveMetrics.ts b/src/azureMonitor/metrics/quickpulse/liveMetrics.ts index 2566210..ee5bf77 100644 --- a/src/azureMonitor/metrics/quickpulse/liveMetrics.ts +++ b/src/azureMonitor/metrics/quickpulse/liveMetrics.ts @@ -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"; @@ -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(); @@ -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; diff --git a/src/distro/distro.ts b/src/distro/distro.ts index 5d23386..28b6c98 100644 --- a/src/distro/distro.ts +++ b/src/distro/distro.ts @@ -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"; @@ -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"; @@ -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); } @@ -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, @@ -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, @@ -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 @@ -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 diff --git a/src/sdkstats/manager.ts b/src/sdkstats/manager.ts index c57216b..49da1b6 100644 --- a/src/sdkstats/manager.ts +++ b/src/sdkstats/manager.ts @@ -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` → @@ -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`). * @@ -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. * @@ -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 @@ -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); @@ -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; @@ -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, diff --git a/src/sdkstats/metrics.ts b/src/sdkstats/metrics.ts index 83aacc0..55a8622 100644 --- a/src/sdkstats/metrics.ts +++ b/src/sdkstats/metrics.ts @@ -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; @@ -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; @@ -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. */ @@ -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; @@ -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. @@ -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. diff --git a/src/sdkstats/networkStats.ts b/src/sdkstats/networkStats.ts index e86d6a2..6a524bf 100644 --- a/src/sdkstats/networkStats.ts +++ b/src/sdkstats/networkStats.ts @@ -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 @@ -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 */ @@ -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 @@ -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` @@ -129,7 +129,7 @@ export function drain(metric: NetworkMetricName): Map { } /** - * @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) { diff --git a/src/sdkstats/otlpWrapper.ts b/src/sdkstats/otlpWrapper.ts index 872c4fe..eb8ee12 100644 --- a/src/sdkstats/otlpWrapper.ts +++ b/src/sdkstats/otlpWrapper.ts @@ -2,11 +2,11 @@ // Licensed under the MIT License. /** - * Network statsbeat wrappers for OTLP exporters. + * Network SDKStats wrappers for OTLP exporters. * * The upstream OTLP HTTP exporters do not surface HTTP status codes — only * the {@link ExportResult} enum and any raised exception. The decorators - * here capture that signal so the network statsbeat pipeline can record + * here capture that signal so the network SDKStats pipeline can record * success counts per endpoint. * * Mirrors `src/microsoft/opentelemetry/_sdkstats/_otlp_wrapper.py` from the @@ -75,7 +75,7 @@ function wrapExport( } /** - * Span exporter decorator that records network statsbeat counts. + * Span exporter decorator that records network SDKStats counts. */ export class NetworkStatsSpanExporter implements SpanExporter { private readonly host: string; @@ -98,7 +98,7 @@ export class NetworkStatsSpanExporter implements SpanExporter { } /** - * Metric exporter decorator that records network statsbeat counts. + * Metric exporter decorator that records network SDKStats counts. * * `selectAggregationTemporality` / `selectAggregation` are forwarded only * when the inner exporter defines them — preserving its preferences while @@ -134,7 +134,7 @@ export class NetworkStatsMetricExporter implements PushMetricExporter { } /** - * Log exporter decorator that records network statsbeat counts. + * Log exporter decorator that records network SDKStats counts. */ export class NetworkStatsLogExporter implements LogRecordExporter { private readonly host: string; diff --git a/src/sdkstats/state.ts b/src/sdkstats/state.ts index 3970d74..36c6f4d 100644 --- a/src/sdkstats/state.ts +++ b/src/sdkstats/state.ts @@ -6,7 +6,7 @@ * * Feature and instrumentation flags are stored as bitmasks so they can be * combined and reported efficiently. The bitmask values are intentionally - * compatible with the Azure Monitor Exporter statsbeat encoding so that + * compatible with the Azure Monitor Exporter SDKStats encoding so that * Azure Monitor consumers see no behavioural change when the distro * bridges its bits into the exporter's existing pipeline. * @@ -14,14 +14,14 @@ * Python distro (microsoft/opentelemetry-distro-python#89). */ -import { StatsbeatFeature, StatsbeatInstrumentation } from "../types.js"; +import { SdkStatsFeature, SdkStatsInstrumentation } from "../types.js"; /** * Distro-specific feature flags, in addition to the - * {@link StatsbeatFeature} flags shared with the Azure Monitor exporter. + * {@link SdkStatsFeature} flags shared with the Azure Monitor exporter. * * These bit values intentionally start above the values used by - * {@link StatsbeatFeature} so that distro bits and exporter bits can be + * {@link SdkStatsFeature} so that distro bits and exporter bits can be * OR-combined into a single 64-bit mask without collision. */ export enum SdkStatsDistroFeature { @@ -76,7 +76,7 @@ let _featureBits = 0; let _instrumentationBits = 0; let _shutdown = false; -export function setSdkStatsFeature(flag: StatsbeatFeature | SdkStatsDistroFeature): void { +export function setSdkStatsFeature(flag: SdkStatsFeature | SdkStatsDistroFeature): void { _featureBits |= flag; } @@ -84,7 +84,7 @@ export function getSdkStatsFeatureFlags(): number { return _featureBits; } -export function setSdkStatsInstrumentation(flag: StatsbeatInstrumentation): void { +export function setSdkStatsInstrumentation(flag: SdkStatsInstrumentation): void { _instrumentationBits |= flag; } diff --git a/src/types.ts b/src/types.ts index 15e6e62..1959207 100644 --- a/src/types.ts +++ b/src/types.ts @@ -135,10 +135,10 @@ export interface OpenAIAgentsInstrumentationConfig extends InstrumentationConfig export interface LangChainInstrumentationConfig extends InstrumentationConfig {} /** - * Statsbeat Features Configuration interface + * SDK Stats Features Configuration interface * @internal */ -export interface StatsbeatFeatures { +export interface SdkStatsFeatures { diskRetry?: boolean; aadHandling?: boolean; browserSdkLoader?: boolean; @@ -153,10 +153,10 @@ export interface StatsbeatFeatures { } /** - * Statsbeat Features Mapping + * SDK Stats Features Mapping * @internal */ -export const StatsbeatFeaturesMap = new Map([ +export const SdkStatsFeaturesMap = new Map([ ["diskRetry", 1], ["aadHandling", 2], ["browserSdkLoader", 4], @@ -171,10 +171,10 @@ export const StatsbeatFeaturesMap = new Map([ ]); /** - * Statsbeat Instrumentations Configuration interface + * SDK Stats Instrumentations Configuration interface * @internal */ -export interface StatsbeatInstrumentations { +export interface SdkStatsInstrumentations { /** Azure Monitor Supported Instrumentations */ azureSdk?: boolean; mongoDb?: boolean; @@ -215,10 +215,10 @@ export interface StatsbeatInstrumentations { } /** - * Statsbeat Instrumentation and Feature Option interface + * SDK Stats Instrumentation and Feature Option interface * @internal */ -export interface StatsbeatOption { +export interface SdkStatsOption { option: string; value: boolean; } @@ -273,7 +273,7 @@ export const AzureMonitorSampleRate = "microsoft.sample_rate"; */ export const APPLICATIONINSIGHTS_SDKSTATS_DISABLED = "APPLICATIONINSIGHTS_SDKSTATS_DISABLED"; -export enum StatsbeatFeature { +export enum SdkStatsFeature { NONE = 0, DISK_RETRY = 1, AAD_HANDLING = 2, @@ -288,7 +288,7 @@ export enum StatsbeatFeature { OTLP = 1024, } -export enum StatsbeatInstrumentation { +export enum SdkStatsInstrumentation { /** Azure Monitor Supported Instrumentations */ NONE = 0, AZURE_CORE_TRACING = 1, @@ -331,41 +331,41 @@ export enum StatsbeatInstrumentation { } /** - * Statsbeat Instrumentation Mapping + * SDK Stats Instrumentation Mapping * @internal */ -export const StatsbeatInstrumentationMap = new Map([ - ["@opentelemetry/instrumentation-amqplib", StatsbeatInstrumentation.AMQPLIB], - ["@opentelemetry/instrumentation-cucumber", StatsbeatInstrumentation.CUCUMBER], - ["@opentelemetry/instrumentation-dataloader", StatsbeatInstrumentation.DATALOADER], - ["@opentelemetry/instrumentation-fs", StatsbeatInstrumentation.FS], - ["@opentelemetry/instrumentation-lru-memoizer", StatsbeatInstrumentation.LRU_MEMOIZER], - ["@opentelemetry/instrumentation-mongoose", StatsbeatInstrumentation.MONGOOSE], - ["@opentelemetry/instrumentation-runtime-node", StatsbeatInstrumentation.RUNTIME_NODE], - ["@opentelemetry/instrumentation-socket.io", StatsbeatInstrumentation.SOCKET_IO], - ["@opentelemetry/instrumentation-tedious", StatsbeatInstrumentation.TEDIOUS], - ["@opentelemetry/instrumentation-undici", StatsbeatInstrumentation.UNDICI], - ["@opentelemetry/instrumentation-cassandra-driver", StatsbeatInstrumentation.CASSANDRA], - ["@opentelemetry/instrumentation-connect", StatsbeatInstrumentation.CONNECT], - ["@opentelemetry/instrumentation-dns", StatsbeatInstrumentation.DNS], - ["@opentelemetry/instrumentation-express", StatsbeatInstrumentation.EXPRESS], - ["@opentelemetry/instrumentation-fastify", StatsbeatInstrumentation.FASTIFY], - ["@opentelemetry/instrumentation-generic-pool", StatsbeatInstrumentation.GENERIC_POOL], - ["@opentelemetry/instrumentation-graphql", StatsbeatInstrumentation.GRAPHQL], - ["@opentelemetry/instrumentation-hapi", StatsbeatInstrumentation.HAPI], - ["@opentelemetry/instrumentation-ioredis", StatsbeatInstrumentation.IOREDIS], - ["@opentelemetry/instrumentation-knex", StatsbeatInstrumentation.KNEX], - ["@opentelemetry/instrumentation-koa", StatsbeatInstrumentation.KOA], - ["@opentelemetry/instrumentation-memcached", StatsbeatInstrumentation.MEMCACHED], - ["@opentelemetry/instrumentation-mysql2", StatsbeatInstrumentation.MYSQL2], - ["@opentelemetry/instrumentation-nestjs-core", StatsbeatInstrumentation.NESTJS_CORE], - ["@opentelemetry/instrumentation-net", StatsbeatInstrumentation.NET], - ["@opentelemetry/instrumentation-pino", StatsbeatInstrumentation.PINO], - ["@opentelemetry/instrumentation-restify", StatsbeatInstrumentation.RESTIFY], - ["@opentelemetry/instrumentation-router", StatsbeatInstrumentation.ROUTER], +export const SdkStatsInstrumentationMap = new Map([ + ["@opentelemetry/instrumentation-amqplib", SdkStatsInstrumentation.AMQPLIB], + ["@opentelemetry/instrumentation-cucumber", SdkStatsInstrumentation.CUCUMBER], + ["@opentelemetry/instrumentation-dataloader", SdkStatsInstrumentation.DATALOADER], + ["@opentelemetry/instrumentation-fs", SdkStatsInstrumentation.FS], + ["@opentelemetry/instrumentation-lru-memoizer", SdkStatsInstrumentation.LRU_MEMOIZER], + ["@opentelemetry/instrumentation-mongoose", SdkStatsInstrumentation.MONGOOSE], + ["@opentelemetry/instrumentation-runtime-node", SdkStatsInstrumentation.RUNTIME_NODE], + ["@opentelemetry/instrumentation-socket.io", SdkStatsInstrumentation.SOCKET_IO], + ["@opentelemetry/instrumentation-tedious", SdkStatsInstrumentation.TEDIOUS], + ["@opentelemetry/instrumentation-undici", SdkStatsInstrumentation.UNDICI], + ["@opentelemetry/instrumentation-cassandra-driver", SdkStatsInstrumentation.CASSANDRA], + ["@opentelemetry/instrumentation-connect", SdkStatsInstrumentation.CONNECT], + ["@opentelemetry/instrumentation-dns", SdkStatsInstrumentation.DNS], + ["@opentelemetry/instrumentation-express", SdkStatsInstrumentation.EXPRESS], + ["@opentelemetry/instrumentation-fastify", SdkStatsInstrumentation.FASTIFY], + ["@opentelemetry/instrumentation-generic-pool", SdkStatsInstrumentation.GENERIC_POOL], + ["@opentelemetry/instrumentation-graphql", SdkStatsInstrumentation.GRAPHQL], + ["@opentelemetry/instrumentation-hapi", SdkStatsInstrumentation.HAPI], + ["@opentelemetry/instrumentation-ioredis", SdkStatsInstrumentation.IOREDIS], + ["@opentelemetry/instrumentation-knex", SdkStatsInstrumentation.KNEX], + ["@opentelemetry/instrumentation-koa", SdkStatsInstrumentation.KOA], + ["@opentelemetry/instrumentation-memcached", SdkStatsInstrumentation.MEMCACHED], + ["@opentelemetry/instrumentation-mysql2", SdkStatsInstrumentation.MYSQL2], + ["@opentelemetry/instrumentation-nestjs-core", SdkStatsInstrumentation.NESTJS_CORE], + ["@opentelemetry/instrumentation-net", SdkStatsInstrumentation.NET], + ["@opentelemetry/instrumentation-pino", SdkStatsInstrumentation.PINO], + ["@opentelemetry/instrumentation-restify", SdkStatsInstrumentation.RESTIFY], + ["@opentelemetry/instrumentation-router", SdkStatsInstrumentation.ROUTER], ]); -export interface StatsbeatEnvironmentConfig { - instrumentation: StatsbeatInstrumentation; - feature: StatsbeatFeature; +export interface SdkStatsEnvironmentConfig { + instrumentation: SdkStatsInstrumentation; + feature: SdkStatsFeature; } diff --git a/src/utils/index.ts b/src/utils/index.ts index a02300e..5e62f69 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -17,5 +17,5 @@ export { unlinkAsync, } from "./fileSystem.js"; export { isFunctionApp, parseResourceDetectorsFromEnvVar } from "./common.js"; -export { getInstance } from "./statsbeat.js"; +export { getInstance } from "./sdkStats.js"; export { patchOpenTelemetryInstrumentationEnable } from "./opentelemetryInstrumentationPatcher.js"; diff --git a/src/utils/opentelemetryInstrumentationPatcher.ts b/src/utils/opentelemetryInstrumentationPatcher.ts index 4485e5d..908ee2f 100644 --- a/src/utils/opentelemetryInstrumentationPatcher.ts +++ b/src/utils/opentelemetryInstrumentationPatcher.ts @@ -2,16 +2,16 @@ // Licensed under the MIT License. import type { Instrumentation } from "@opentelemetry/instrumentation"; -import type { StatsbeatEnvironmentConfig } from "../types.js"; -import { AZURE_MONITOR_STATSBEAT_FEATURES, StatsbeatInstrumentationMap } from "../types.js"; +import type { SdkStatsEnvironmentConfig } from "../types.js"; +import { AZURE_MONITOR_STATSBEAT_FEATURES, SdkStatsInstrumentationMap } from "../types.js"; import { Logger } from "../shared/logging/index.js"; /** - * Patch OpenTelemetry Instrumentation enablement to update the statsbeat environment variable with the enabled instrumentations + * Patch OpenTelemetry Instrumentation enablement to update the SDK Stats environment variable with the enabled instrumentations * @internal */ export function patchOpenTelemetryInstrumentationEnable(): void { - const emptyStatsbeatConfig: string = JSON.stringify({ instrumentation: 0, feature: 0 }); + const emptySdkStatsConfig: string = JSON.stringify({ instrumentation: 0, feature: 0 }); try { require.resolve("@opentelemetry/instrumentation"); // eslint-disable-next-line @typescript-eslint/no-require-imports @@ -19,25 +19,25 @@ export function patchOpenTelemetryInstrumentationEnable(): void { const originalModuleDefinition = autoLoaderUtils.enableInstrumentations; - // Parses the enabled instrumentations and then ammends the statsbeat instrumentation environment variable + // Parses the enabled instrumentations and then ammends the SDK Stats instrumentation environment variable autoLoaderUtils.enableInstrumentations = function (instrumentations: Instrumentation[]) { try { if (instrumentations.length > 0) { - const statsbeatOptions: StatsbeatEnvironmentConfig = JSON.parse( - process.env[AZURE_MONITOR_STATSBEAT_FEATURES] || emptyStatsbeatConfig, + const sdkStatsOptions: SdkStatsEnvironmentConfig = JSON.parse( + process.env[AZURE_MONITOR_STATSBEAT_FEATURES] || emptySdkStatsConfig, ); - let updatedStatsbeat = {}; + let updatedSdkStats = {}; for (let i = 0; i < instrumentations.length; i++) { - updatedStatsbeat = { - instrumentation: (statsbeatOptions.instrumentation |= - StatsbeatInstrumentationMap.get(instrumentations[i].instrumentationName) || 0), - feature: statsbeatOptions.feature, + updatedSdkStats = { + instrumentation: (sdkStatsOptions.instrumentation |= + SdkStatsInstrumentationMap.get(instrumentations[i].instrumentationName) || 0), + feature: sdkStatsOptions.feature, }; } - process.env[AZURE_MONITOR_STATSBEAT_FEATURES] = JSON.stringify(updatedStatsbeat); + process.env[AZURE_MONITOR_STATSBEAT_FEATURES] = JSON.stringify(updatedSdkStats); } } catch (_e) { - Logger.getInstance().warn("Failed to parse the statsbeat environment variable"); + Logger.getInstance().warn("Failed to parse the SDK Stats environment variable"); } // eslint-disable-next-line prefer-rest-params return originalModuleDefinition.apply(this, arguments); diff --git a/src/utils/sdkStats.ts b/src/utils/sdkStats.ts new file mode 100644 index 0000000..0598d95 --- /dev/null +++ b/src/utils/sdkStats.ts @@ -0,0 +1,158 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import type { + SdkStatsEnvironmentConfig, + SdkStatsFeatures, + SdkStatsInstrumentations, + SdkStatsOption, +} from "../types.js"; +import { + AZURE_MONITOR_STATSBEAT_FEATURES, + SdkStatsFeature, + SdkStatsFeaturesMap, + SdkStatsInstrumentation, +} from "../types.js"; + +let instance: SdkStatsConfiguration; + +class SdkStatsConfiguration { + // Initial SDK Stats options + private initializedByShim = false; + private currentSdkStatsInstrumentations: SdkStatsInstrumentations = {}; + private currentSdkStatsFeatures: SdkStatsFeatures = {}; + + constructor() { + // Check for shim initialization upon construction + try { + if ( + JSON.parse(process.env[AZURE_MONITOR_STATSBEAT_FEATURES] || "{}").feature & + SdkStatsFeature.SHIM + ) { + this.initializedByShim = true; + } + } catch (_error) { + // Fail silently — SDK Stats is best-effort + } + } + + public setSdkStatsFeatures = ( + sdkStatsInstrumentations: SdkStatsInstrumentations, + sdkStatsFeatures: SdkStatsFeatures, + ) => { + let sdkStatsEnv: SdkStatsEnvironmentConfig; + try { + sdkStatsEnv = JSON.parse(process.env[AZURE_MONITOR_STATSBEAT_FEATURES] || "{}"); + } catch (_error) { + // Fail silently — SDK Stats is best-effort + return; + } + this.currentSdkStatsInstrumentations = { + ...this.currentSdkStatsInstrumentations, + ...sdkStatsInstrumentations, + }; + this.currentSdkStatsFeatures = { ...this.currentSdkStatsFeatures, ...sdkStatsFeatures }; + + // Set the SDK Stats options for community instrumentations based on the environment variable + sdkStatsInstrumentations = { + ...this.currentSdkStatsInstrumentations, + amqplib: sdkStatsEnv!.instrumentation & SdkStatsInstrumentation.AMQPLIB ? true : false, + cucumber: sdkStatsEnv!.instrumentation & SdkStatsInstrumentation.CUCUMBER ? true : false, + dataloader: sdkStatsEnv!.instrumentation & SdkStatsInstrumentation.DATALOADER ? true : false, + fs: sdkStatsEnv!.instrumentation & SdkStatsInstrumentation.FS ? true : false, + lruMemoizer: + sdkStatsEnv!.instrumentation & SdkStatsInstrumentation.LRU_MEMOIZER ? true : false, + mongoose: sdkStatsEnv!.instrumentation & SdkStatsInstrumentation.MONGOOSE ? true : false, + runtimeNode: + sdkStatsEnv!.instrumentation & SdkStatsInstrumentation.RUNTIME_NODE ? true : false, + socketIo: sdkStatsEnv!.instrumentation & SdkStatsInstrumentation.SOCKET_IO ? true : false, + tedious: sdkStatsEnv!.instrumentation & SdkStatsInstrumentation.TEDIOUS ? true : false, + undici: sdkStatsEnv!.instrumentation & SdkStatsInstrumentation.UNDICI ? true : false, + cassandra: sdkStatsEnv!.instrumentation & SdkStatsInstrumentation.CASSANDRA ? true : false, + connect: sdkStatsEnv!.instrumentation & SdkStatsInstrumentation.CONNECT ? true : false, + dns: sdkStatsEnv!.instrumentation & SdkStatsInstrumentation.DNS ? true : false, + express: sdkStatsEnv!.instrumentation & SdkStatsInstrumentation.EXPRESS ? true : false, + fastify: sdkStatsEnv!.instrumentation & SdkStatsInstrumentation.FASTIFY ? true : false, + genericPool: + sdkStatsEnv!.instrumentation & SdkStatsInstrumentation.GENERIC_POOL ? true : false, + graphql: sdkStatsEnv!.instrumentation & SdkStatsInstrumentation.GRAPHQL ? true : false, + hapi: sdkStatsEnv!.instrumentation & SdkStatsInstrumentation.HAPI ? true : false, + ioredis: sdkStatsEnv!.instrumentation & SdkStatsInstrumentation.IOREDIS ? true : false, + knex: sdkStatsEnv!.instrumentation & SdkStatsInstrumentation.KNEX ? true : false, + koa: sdkStatsEnv!.instrumentation & SdkStatsInstrumentation.KOA ? true : false, + memcached: sdkStatsEnv!.instrumentation & SdkStatsInstrumentation.MEMCACHED ? true : false, + mysql2: sdkStatsEnv!.instrumentation & SdkStatsInstrumentation.MYSQL2 ? true : false, + nestjsCore: sdkStatsEnv!.instrumentation & SdkStatsInstrumentation.NESTJS_CORE ? true : false, + net: sdkStatsEnv!.instrumentation & SdkStatsInstrumentation.NET ? true : false, + pino: sdkStatsEnv!.instrumentation & SdkStatsInstrumentation.PINO ? true : false, + restify: sdkStatsEnv!.instrumentation & SdkStatsInstrumentation.RESTIFY ? true : false, + router: sdkStatsEnv!.instrumentation & SdkStatsInstrumentation.ROUTER ? true : false, + }; + + let instrumentationBitMap = SdkStatsInstrumentation.NONE; + + const instrumentationArray: Array = Object.entries( + sdkStatsInstrumentations, + ).map((entry) => { + return { option: entry[0], value: entry[1] }; + }); + + // Map the instrumentation options to a bit map + for (let i = 0; i < instrumentationArray.length; i++) { + if (instrumentationArray[i].value) { + instrumentationBitMap |= 2 ** i; + } + } + + // Create feature bit map + let featureBitMap = SdkStatsFeature.NONE; + + if (this.initializedByShim) { + this.currentSdkStatsFeatures.shim = true; + } else { + this.currentSdkStatsFeatures.distro = true; + } + + if (sdkStatsFeatures.liveMetrics) { + this.currentSdkStatsFeatures.liveMetrics = true; + } + + const featureArray: Array = Object.entries(this.currentSdkStatsFeatures).map( + (entry) => { + return { option: entry[0], value: entry[1] }; + }, + ); + + // Map the feature options to a bit map + for (let i = 0; i < featureArray.length; i++) { + if (featureArray[i].value) { + featureBitMap |= SdkStatsFeaturesMap.get(featureArray[i].option)!; + } + } + + // Merge old SDK Stats options with new SDK Stats options overriding any common properties + try { + const currentFeaturesBitMap = Number(process.env[AZURE_MONITOR_STATSBEAT_FEATURES]); + if (!isNaN(currentFeaturesBitMap)) { + featureBitMap |= currentFeaturesBitMap; + } + process.env[AZURE_MONITOR_STATSBEAT_FEATURES] = JSON.stringify({ + instrumentation: instrumentationBitMap, + feature: featureBitMap, + }); + } catch (_error) { + // Fail silently — SDK Stats is best-effort + } + }; +} + +/** + * Singleton SDK Stats instance. + * @internal + */ +export function getInstance(): SdkStatsConfiguration { + if (!instance) { + instance = new SdkStatsConfiguration(); + } + return instance; +} diff --git a/src/utils/statsbeat.ts b/src/utils/statsbeat.ts deleted file mode 100644 index c9978be..0000000 --- a/src/utils/statsbeat.ts +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -import type { - StatsbeatEnvironmentConfig, - StatsbeatFeatures, - StatsbeatInstrumentations, - StatsbeatOption, -} from "../types.js"; -import { - AZURE_MONITOR_STATSBEAT_FEATURES, - StatsbeatFeature, - StatsbeatFeaturesMap, - StatsbeatInstrumentation, -} from "../types.js"; - -let instance: StatsbeatConfiguration; - -class StatsbeatConfiguration { - // Initial Statsbeat options - private initializedByShim = false; - private currentStatsbeatInstrumentations: StatsbeatInstrumentations = {}; - private currentStatsbeatFeatures: StatsbeatFeatures = {}; - - constructor() { - // Check for shim initialization upon construction - try { - if ( - JSON.parse(process.env[AZURE_MONITOR_STATSBEAT_FEATURES] || "{}").feature & - StatsbeatFeature.SHIM - ) { - this.initializedByShim = true; - } - } catch (_error) { - // Fail silently — statsbeat is best-effort - } - } - - public setStatsbeatFeatures = ( - statsbeatInstrumentations: StatsbeatInstrumentations, - statsbeatFeatures: StatsbeatFeatures, - ) => { - let statsbeatEnv: StatsbeatEnvironmentConfig; - try { - statsbeatEnv = JSON.parse(process.env[AZURE_MONITOR_STATSBEAT_FEATURES] || "{}"); - } catch (_error) { - // Fail silently — statsbeat is best-effort - return; - } - this.currentStatsbeatInstrumentations = { - ...this.currentStatsbeatInstrumentations, - ...statsbeatInstrumentations, - }; - this.currentStatsbeatFeatures = { ...this.currentStatsbeatFeatures, ...statsbeatFeatures }; - - // Set the statsbeat options for community instrumentations based on the environment variable - statsbeatInstrumentations = { - ...this.currentStatsbeatInstrumentations, - amqplib: statsbeatEnv!.instrumentation & StatsbeatInstrumentation.AMQPLIB ? true : false, - cucumber: statsbeatEnv!.instrumentation & StatsbeatInstrumentation.CUCUMBER ? true : false, - dataloader: - statsbeatEnv!.instrumentation & StatsbeatInstrumentation.DATALOADER ? true : false, - fs: statsbeatEnv!.instrumentation & StatsbeatInstrumentation.FS ? true : false, - lruMemoizer: - statsbeatEnv!.instrumentation & StatsbeatInstrumentation.LRU_MEMOIZER ? true : false, - mongoose: statsbeatEnv!.instrumentation & StatsbeatInstrumentation.MONGOOSE ? true : false, - runtimeNode: - statsbeatEnv!.instrumentation & StatsbeatInstrumentation.RUNTIME_NODE ? true : false, - socketIo: statsbeatEnv!.instrumentation & StatsbeatInstrumentation.SOCKET_IO ? true : false, - tedious: statsbeatEnv!.instrumentation & StatsbeatInstrumentation.TEDIOUS ? true : false, - undici: statsbeatEnv!.instrumentation & StatsbeatInstrumentation.UNDICI ? true : false, - cassandra: statsbeatEnv!.instrumentation & StatsbeatInstrumentation.CASSANDRA ? true : false, - connect: statsbeatEnv!.instrumentation & StatsbeatInstrumentation.CONNECT ? true : false, - dns: statsbeatEnv!.instrumentation & StatsbeatInstrumentation.DNS ? true : false, - express: statsbeatEnv!.instrumentation & StatsbeatInstrumentation.EXPRESS ? true : false, - fastify: statsbeatEnv!.instrumentation & StatsbeatInstrumentation.FASTIFY ? true : false, - genericPool: - statsbeatEnv!.instrumentation & StatsbeatInstrumentation.GENERIC_POOL ? true : false, - graphql: statsbeatEnv!.instrumentation & StatsbeatInstrumentation.GRAPHQL ? true : false, - hapi: statsbeatEnv!.instrumentation & StatsbeatInstrumentation.HAPI ? true : false, - ioredis: statsbeatEnv!.instrumentation & StatsbeatInstrumentation.IOREDIS ? true : false, - knex: statsbeatEnv!.instrumentation & StatsbeatInstrumentation.KNEX ? true : false, - koa: statsbeatEnv!.instrumentation & StatsbeatInstrumentation.KOA ? true : false, - memcached: statsbeatEnv!.instrumentation & StatsbeatInstrumentation.MEMCACHED ? true : false, - mysql2: statsbeatEnv!.instrumentation & StatsbeatInstrumentation.MYSQL2 ? true : false, - nestjsCore: - statsbeatEnv!.instrumentation & StatsbeatInstrumentation.NESTJS_CORE ? true : false, - net: statsbeatEnv!.instrumentation & StatsbeatInstrumentation.NET ? true : false, - pino: statsbeatEnv!.instrumentation & StatsbeatInstrumentation.PINO ? true : false, - restify: statsbeatEnv!.instrumentation & StatsbeatInstrumentation.RESTIFY ? true : false, - router: statsbeatEnv!.instrumentation & StatsbeatInstrumentation.ROUTER ? true : false, - }; - - let instrumentationBitMap = StatsbeatInstrumentation.NONE; - - const instrumentationArray: Array = Object.entries( - statsbeatInstrumentations, - ).map((entry) => { - return { option: entry[0], value: entry[1] }; - }); - - // Map the instrumentation options to a bit map - for (let i = 0; i < instrumentationArray.length; i++) { - if (instrumentationArray[i].value) { - instrumentationBitMap |= 2 ** i; - } - } - - // Create feature bit map - let featureBitMap = StatsbeatFeature.NONE; - - if (this.initializedByShim) { - this.currentStatsbeatFeatures.shim = true; - } else { - this.currentStatsbeatFeatures.distro = true; - } - - if (statsbeatFeatures.liveMetrics) { - this.currentStatsbeatFeatures.liveMetrics = true; - } - - const featureArray: Array = Object.entries(this.currentStatsbeatFeatures).map( - (entry) => { - return { option: entry[0], value: entry[1] }; - }, - ); - - // Map the feature options to a bit map - for (let i = 0; i < featureArray.length; i++) { - if (featureArray[i].value) { - featureBitMap |= StatsbeatFeaturesMap.get(featureArray[i].option)!; - } - } - - // Merge old statsbeat options with new statsbeat options overriding any common properties - try { - const currentFeaturesBitMap = Number(process.env[AZURE_MONITOR_STATSBEAT_FEATURES]); - if (!isNaN(currentFeaturesBitMap)) { - featureBitMap |= currentFeaturesBitMap; - } - process.env[AZURE_MONITOR_STATSBEAT_FEATURES] = JSON.stringify({ - instrumentation: instrumentationBitMap, - feature: featureBitMap, - }); - } catch (_error) { - // Fail silently — statsbeat is best-effort - } - }; -} - -/** - * Singleton Statsbeat instance. - * @internal - */ -export function getInstance(): StatsbeatConfiguration { - if (!instance) { - instance = new StatsbeatConfiguration(); - } - return instance; -} diff --git a/test/internal/unit/a365/agent365NetworkStats.test.ts b/test/internal/unit/a365/agent365NetworkStats.test.ts index 429e73c..3ed6e75 100644 --- a/test/internal/unit/a365/agent365NetworkStats.test.ts +++ b/test/internal/unit/a365/agent365NetworkStats.test.ts @@ -51,7 +51,7 @@ function exportSpan(exporter: Agent365Exporter): Promise { return new Promise((resolve) => exporter.export([makeSpan()], (r) => resolve(r.code))); } -describe("Agent365Exporter network statsbeat", () => { +describe("Agent365Exporter network SDKStats", () => { let fetchSpy: ReturnType; beforeEach(() => { diff --git a/test/internal/unit/azureMonitor/getAzureMonitorStatsbeatFeatures.test.ts b/test/internal/unit/azureMonitor/getAzureMonitorSdkStatsFeatures.test.ts similarity index 77% rename from test/internal/unit/azureMonitor/getAzureMonitorStatsbeatFeatures.test.ts rename to test/internal/unit/azureMonitor/getAzureMonitorSdkStatsFeatures.test.ts index 01ff433..e24ca83 100644 --- a/test/internal/unit/azureMonitor/getAzureMonitorStatsbeatFeatures.test.ts +++ b/test/internal/unit/azureMonitor/getAzureMonitorSdkStatsFeatures.test.ts @@ -3,16 +3,16 @@ import { describe, it, expect } from "vitest"; import { InternalConfig } from "../../../../src/shared/config.js"; -import { getAzureMonitorStatsbeatFeatures } from "../../../../src/azureMonitor/index.js"; +import { getAzureMonitorSdkStatsFeatures } from "../../../../src/azureMonitor/index.js"; import { SEMRESATTRS_K8S_CLUSTER_NAME } from "@opentelemetry/semantic-conventions"; import { resourceFromAttributes } from "@opentelemetry/resources"; -describe("getAzureMonitorStatsbeatFeatures", () => { +describe("getAzureMonitorSdkStatsFeatures", () => { it("should return browserSdkLoader true when enabled in config", () => { const config = new InternalConfig(); config.browserSdkLoaderOptions.enabled = true; - const features = getAzureMonitorStatsbeatFeatures(config); + const features = getAzureMonitorSdkStatsFeatures(config); expect(features.browserSdkLoader).toBe(true); }); @@ -20,7 +20,7 @@ describe("getAzureMonitorStatsbeatFeatures", () => { const config = new InternalConfig(); config.browserSdkLoaderOptions.enabled = false; - const features = getAzureMonitorStatsbeatFeatures(config); + const features = getAzureMonitorSdkStatsFeatures(config); expect(features.browserSdkLoader).toBe(false); }); @@ -30,21 +30,21 @@ describe("getAzureMonitorStatsbeatFeatures", () => { getToken: () => Promise.resolve({ token: "test", expiresOnTimestamp: Date.now() + 10000 }), }; - const features = getAzureMonitorStatsbeatFeatures(config); + const features = getAzureMonitorSdkStatsFeatures(config); expect(features.aadHandling).toBe(true); }); it("should return aadHandling false when no credential is provided", () => { const config = new InternalConfig(); - const features = getAzureMonitorStatsbeatFeatures(config); + const features = getAzureMonitorSdkStatsFeatures(config); expect(features.aadHandling).toBe(false); }); it("should return diskRetry true when disableOfflineStorage is falsy", () => { const config = new InternalConfig(); - const features = getAzureMonitorStatsbeatFeatures(config); + const features = getAzureMonitorSdkStatsFeatures(config); expect(features.diskRetry).toBe(true); }); @@ -52,7 +52,7 @@ describe("getAzureMonitorStatsbeatFeatures", () => { const config = new InternalConfig(); config.azureMonitorExporterOptions.disableOfflineStorage = true; - const features = getAzureMonitorStatsbeatFeatures(config); + const features = getAzureMonitorSdkStatsFeatures(config); expect(features.diskRetry).toBe(false); }); @@ -62,14 +62,14 @@ describe("getAzureMonitorStatsbeatFeatures", () => { [SEMRESATTRS_K8S_CLUSTER_NAME]: "my-cluster", }); - const features = getAzureMonitorStatsbeatFeatures(config); + const features = getAzureMonitorSdkStatsFeatures(config); expect(features.aksResourceDetectorPopulation).toBe(true); }); it("should not detect AKS resource when no k8s attributes are present", () => { const config = new InternalConfig(); - const features = getAzureMonitorStatsbeatFeatures(config); + const features = getAzureMonitorSdkStatsFeatures(config); expect(features.aksResourceDetectorPopulation).toBe(false); }); }); diff --git a/test/internal/unit/main.test.ts b/test/internal/unit/main.test.ts index 05ecf73..21b6fa0 100644 --- a/test/internal/unit/main.test.ts +++ b/test/internal/unit/main.test.ts @@ -14,18 +14,18 @@ import { import type { MeterProvider, ViewOptions } from "@opentelemetry/sdk-metrics"; import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics"; import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http"; -import type { StatsbeatEnvironmentConfig } from "../../../src/types.js"; +import type { SdkStatsEnvironmentConfig } from "../../../src/types.js"; import { AZURE_MONITOR_STATSBEAT_FEATURES, APPLICATIONINSIGHTS_SDKSTATS_DISABLED, - StatsbeatFeature, - StatsbeatInstrumentation, - StatsbeatInstrumentationMap, + SdkStatsFeature, + SdkStatsInstrumentation, + SdkStatsInstrumentationMap, } from "../../../src/types.js"; import { getOsPrefix } from "../../../src/azureMonitor/utils/common.js"; import type { ReadableSpan, Span, SpanProcessor } from "@opentelemetry/sdk-trace-base"; import type { LogRecordProcessor, SdkLogRecord } from "@opentelemetry/sdk-logs"; -import { getInstance } from "../../../src/utils/statsbeat.js"; +import { getInstance } from "../../../src/utils/sdkStats.js"; import type { Instrumentation, InstrumentationConfig } from "@opentelemetry/instrumentation"; import { describe, it, beforeEach, afterEach, expect, assert, vi, afterAll } from "vitest"; import { OpenAIAgentsTraceInstrumentor } from "../../../src/genai/instrumentations/openai/openAIAgentsTraceInstrumentor.js"; @@ -317,7 +317,7 @@ describe("Main functions", () => { expect(meterConfig?.views).toContain(customView); }); - it("should set statsbeat features", () => { + it("should set SDK Stats features", () => { const config: MicrosoftOpenTelemetryOptions = { instrumentationOptions: { azureSdk: { @@ -348,28 +348,28 @@ describe("Main functions", () => { const output = JSON.parse(String(process.env["AZURE_MONITOR_STATSBEAT_FEATURES"])); const features = Number(output["feature"]); const instrumentations = Number(output["instrumentation"]); - assert.notOk(features & StatsbeatFeature.AAD_HANDLING, "AAD_HANDLING is set"); - assert.notOk(features & StatsbeatFeature.DISK_RETRY, "DISK_RETRY is set"); - assert.notOk(features & StatsbeatFeature.BROWSER_SDK_LOADER, "BROWSER_SDK_LOADER is set"); - assert.ok(features & StatsbeatFeature.DISTRO, "DISTRO is not set"); + assert.notOk(features & SdkStatsFeature.AAD_HANDLING, "AAD_HANDLING is set"); + assert.notOk(features & SdkStatsFeature.DISK_RETRY, "DISK_RETRY is set"); + assert.notOk(features & SdkStatsFeature.BROWSER_SDK_LOADER, "BROWSER_SDK_LOADER is set"); + assert.ok(features & SdkStatsFeature.DISTRO, "DISTRO is not set"); assert.strictEqual(features, 8); assert.ok( - instrumentations & StatsbeatInstrumentation.AZURE_CORE_TRACING, + instrumentations & SdkStatsInstrumentation.AZURE_CORE_TRACING, "AZURE_CORE_TRACING not set", ); - assert.notOk(features & StatsbeatFeature.SHIM, "SHIM is set"); + assert.notOk(features & SdkStatsFeature.SHIM, "SHIM is set"); assert.notOk( - features & StatsbeatFeature.AKS_RESOURCE_DETECTOR_POPULATION, + features & SdkStatsFeature.AKS_RESOURCE_DETECTOR_POPULATION, "AKS_RESOURCE_DETECTOR_POPULATION should not be set", ); - assert.ok(instrumentations & StatsbeatInstrumentation.MONGODB, "MONGODB not set"); - assert.ok(instrumentations & StatsbeatInstrumentation.MYSQL, "MYSQL not set"); - assert.ok(instrumentations & StatsbeatInstrumentation.POSTGRES, "POSTGRES not set"); - assert.ok(instrumentations & StatsbeatInstrumentation.REDIS, "REDIS not set"); + assert.ok(instrumentations & SdkStatsInstrumentation.MONGODB, "MONGODB not set"); + assert.ok(instrumentations & SdkStatsInstrumentation.MYSQL, "MYSQL not set"); + assert.ok(instrumentations & SdkStatsInstrumentation.POSTGRES, "POSTGRES not set"); + assert.ok(instrumentations & SdkStatsInstrumentation.REDIS, "REDIS not set"); assert.strictEqual(instrumentations, 31); }); - it("should set shim feature in statsbeat if env var is populated", () => { + it("should set shim feature in SDK Stats if env var is populated", () => { getInstance()["initializedByShim"] = true; const config: MicrosoftOpenTelemetryOptions = { azureMonitor: { @@ -381,7 +381,7 @@ describe("Main functions", () => { useMicrosoftOpenTelemetry(config); const output = JSON.parse(String(process.env["AZURE_MONITOR_STATSBEAT_FEATURES"])); const features = Number(output["feature"]); - assert.ok(features & StatsbeatFeature.SHIM, `SHIM is not set ${features}`); + assert.ok(features & SdkStatsFeature.SHIM, `SHIM is not set ${features}`); }); it("should set AKS_RESOURCE_DETECTOR_POPULATION feature when AKS resource attributes are populated", () => { @@ -400,7 +400,7 @@ describe("Main functions", () => { const output = JSON.parse(String(process.env["AZURE_MONITOR_STATSBEAT_FEATURES"])); const features = Number(output["feature"]); assert.ok( - features & StatsbeatFeature.AKS_RESOURCE_DETECTOR_POPULATION, + features & SdkStatsFeature.AKS_RESOURCE_DETECTOR_POPULATION, `AKS_RESOURCE_DETECTOR_POPULATION is not set ${features}`, ); }); @@ -417,17 +417,17 @@ describe("Main functions", () => { const output = JSON.parse(String(process.env["AZURE_MONITOR_STATSBEAT_FEATURES"])); const features = Number(output["feature"]); assert.notOk( - features & StatsbeatFeature.AKS_RESOURCE_DETECTOR_POPULATION, + features & SdkStatsFeature.AKS_RESOURCE_DETECTOR_POPULATION, "AKS_RESOURCE_DETECTOR_POPULATION should not be set", ); }); - it("should use statsbeat features if already available", () => { + it("should use SDK Stats features if already available", () => { const env = <{ [id: string]: string }>{}; let current = 0; - current |= StatsbeatFeature.AAD_HANDLING; - current |= StatsbeatFeature.DISK_RETRY; - current |= StatsbeatFeature.LIVE_METRICS; + current |= SdkStatsFeature.AAD_HANDLING; + current |= SdkStatsFeature.DISK_RETRY; + current |= SdkStatsFeature.LIVE_METRICS; env.AZURE_MONITOR_STATSBEAT_FEATURES = current.toString(); process.env = env; const config: MicrosoftOpenTelemetryOptions = { @@ -440,11 +440,11 @@ describe("Main functions", () => { useMicrosoftOpenTelemetry(config); const output = JSON.parse(String(process.env["AZURE_MONITOR_STATSBEAT_FEATURES"])); const numberOutput = Number(output["feature"]); - assert.ok(numberOutput & StatsbeatFeature.AAD_HANDLING, "AAD_HANDLING not set"); - assert.ok(numberOutput & StatsbeatFeature.DISK_RETRY, "DISK_RETRY not set"); - assert.ok(numberOutput & StatsbeatFeature.DISTRO, "DISTRO not set"); - assert.notOk(numberOutput & StatsbeatFeature.BROWSER_SDK_LOADER, "BROWSER_SDK_LOADER is set"); - assert.ok(numberOutput & StatsbeatFeature.LIVE_METRICS, "LIVE_METRICS is not set"); + assert.ok(numberOutput & SdkStatsFeature.AAD_HANDLING, "AAD_HANDLING not set"); + assert.ok(numberOutput & SdkStatsFeature.DISK_RETRY, "DISK_RETRY not set"); + assert.ok(numberOutput & SdkStatsFeature.DISTRO, "DISTRO not set"); + assert.notOk(numberOutput & SdkStatsFeature.BROWSER_SDK_LOADER, "BROWSER_SDK_LOADER is set"); + assert.ok(numberOutput & SdkStatsFeature.LIVE_METRICS, "LIVE_METRICS is not set"); }); it("should capture the app service SDK prefix correctly", () => { @@ -588,7 +588,7 @@ describe("Main functions", () => { }); }); - it("should update statsbeat env var based on reading instrumentations array", () => { + it("should update SDK Stats env var based on reading instrumentations array", () => { const config: MicrosoftOpenTelemetryOptions = { instrumentationOptions: { azureSdk: { enabled: false }, @@ -608,28 +608,28 @@ describe("Main functions", () => { }, }; useMicrosoftOpenTelemetry(config); - const emptyStatsbeatConfig: string = JSON.stringify({ instrumentation: 0, feature: 0 }); + const emptySdkStatsConfig: string = JSON.stringify({ instrumentation: 0, feature: 0 }); - const statsbeatOptions: StatsbeatEnvironmentConfig = JSON.parse( - process.env[AZURE_MONITOR_STATSBEAT_FEATURES] || emptyStatsbeatConfig, + const sdkStatsOptions: SdkStatsEnvironmentConfig = JSON.parse( + process.env[AZURE_MONITOR_STATSBEAT_FEATURES] || emptySdkStatsConfig, ); const instrumentations = [testInstrumentation]; - let updatedStatsbeat = { instrumentation: 0, feature: 0 }; + let updatedSdkStats = { instrumentation: 0, feature: 0 }; - // Dynamic statsbeat update logic + // Dynamic SDK Stats update logic for (let i = 0; i < instrumentations.length; i++) { - updatedStatsbeat = { - instrumentation: (statsbeatOptions.instrumentation |= - StatsbeatInstrumentationMap.get(instrumentations[i].instrumentationName) || 0), - feature: statsbeatOptions.feature, + updatedSdkStats = { + instrumentation: (sdkStatsOptions.instrumentation |= + SdkStatsInstrumentationMap.get(instrumentations[i].instrumentationName) || 0), + feature: sdkStatsOptions.feature, }; } - assert.strictEqual(updatedStatsbeat.instrumentation, StatsbeatInstrumentation.FS); + assert.strictEqual(updatedSdkStats.instrumentation, SdkStatsInstrumentation.FS); }); it("should detect MULTI_IKEY feature when AZURE_MONITOR_STATSBEAT_FEATURES has MULTI_IKEY enabled", () => { const env = <{ [id: string]: string }>{}; - env[AZURE_MONITOR_STATSBEAT_FEATURES] = String(StatsbeatFeature.MULTI_IKEY); + env[AZURE_MONITOR_STATSBEAT_FEATURES] = String(SdkStatsFeature.MULTI_IKEY); process.env = env; const config: MicrosoftOpenTelemetryOptions = { azureMonitor: { @@ -643,13 +643,13 @@ describe("Main functions", () => { feature?: number; }; const features = Number(output["feature"] || 0); - assert.ok(features & StatsbeatFeature.MULTI_IKEY, "MULTI_IKEY not detected"); + assert.ok(features & SdkStatsFeature.MULTI_IKEY, "MULTI_IKEY not detected"); void shutdownMicrosoftOpenTelemetry(); }); it("should not detect MULTI_IKEY feature when AZURE_MONITOR_STATSBEAT_FEATURES has MULTI_IKEY disabled", () => { const env = <{ [id: string]: string }>{}; - env[AZURE_MONITOR_STATSBEAT_FEATURES] = String(StatsbeatFeature.DISTRO); + env[AZURE_MONITOR_STATSBEAT_FEATURES] = String(SdkStatsFeature.DISTRO); process.env = env; const config: MicrosoftOpenTelemetryOptions = { azureMonitor: { @@ -664,7 +664,7 @@ describe("Main functions", () => { }; const features = Number(output["feature"] || 0); assert.ok( - !(features & StatsbeatFeature.MULTI_IKEY), + !(features & SdkStatsFeature.MULTI_IKEY), "MULTI_IKEY detected when it should not be", ); void shutdownMicrosoftOpenTelemetry(); @@ -687,10 +687,10 @@ describe("Main functions", () => { }; const features = Number(output["feature"] || 0); assert.ok( - features & StatsbeatFeature.CUSTOMER_SDKSTATS, + features & SdkStatsFeature.CUSTOMER_SDKSTATS, "CUSTOMER_SDKSTATS feature should be detected when customer explicitly disables SDK stats", ); - assert.ok(features & StatsbeatFeature.DISTRO, "DISTRO feature should also be set"); + assert.ok(features & SdkStatsFeature.DISTRO, "DISTRO feature should also be set"); void shutdownMicrosoftOpenTelemetry(); }); @@ -711,10 +711,10 @@ describe("Main functions", () => { }; const features = Number(output["feature"] || 0); assert.ok( - !(features & StatsbeatFeature.CUSTOMER_SDKSTATS), + !(features & SdkStatsFeature.CUSTOMER_SDKSTATS), "CUSTOMER_SDKSTATS feature should not be detected when env var is not 'true'", ); - assert.ok(features & StatsbeatFeature.DISTRO, "DISTRO feature should still be set"); + assert.ok(features & SdkStatsFeature.DISTRO, "DISTRO feature should still be set"); void shutdownMicrosoftOpenTelemetry(); }); @@ -735,10 +735,10 @@ describe("Main functions", () => { }; const features = Number(output["feature"] || 0); assert.ok( - !(features & StatsbeatFeature.CUSTOMER_SDKSTATS), + !(features & SdkStatsFeature.CUSTOMER_SDKSTATS), "CUSTOMER_SDKSTATS feature should not be detected when env var is undefined", ); - assert.ok(features & StatsbeatFeature.DISTRO, "DISTRO feature should still be set"); + assert.ok(features & SdkStatsFeature.DISTRO, "DISTRO feature should still be set"); void shutdownMicrosoftOpenTelemetry(); }); @@ -911,20 +911,20 @@ describe("Main functions", () => { } } - // Azure Monitor statsbeat env var should reflect OTLP-only scenario - const statsbeatRaw = process.env["AZURE_MONITOR_STATSBEAT_FEATURES"]; - if (statsbeatRaw) { - const statsbeat = JSON.parse(statsbeatRaw); + // Azure Monitor SDK Stats env var should reflect OTLP-only scenario + const sdkStatsRaw = process.env["AZURE_MONITOR_STATSBEAT_FEATURES"]; + if (sdkStatsRaw) { + const sdkStats = JSON.parse(sdkStatsRaw); // DISTRO feature SHOULD be set — the distro is being used regardless of backend - expect(statsbeat.feature & StatsbeatFeature.DISTRO).toBeTruthy(); + expect(sdkStats.feature & SdkStatsFeature.DISTRO).toBeTruthy(); // OTLP feature should be set - expect(statsbeat.feature & StatsbeatFeature.OTLP).toBeTruthy(); + expect(sdkStats.feature & SdkStatsFeature.OTLP).toBeTruthy(); } void shutdownMicrosoftOpenTelemetry(); }); - it("should set A365 feature bit in statsbeat when A365 is enabled", () => { + it("should set A365 feature bit in SDK Stats when A365 is enabled", () => { const env = <{ [id: string]: string }>{}; process.env = env; @@ -939,7 +939,7 @@ describe("Main functions", () => { const output = JSON.parse(String(process.env[AZURE_MONITOR_STATSBEAT_FEATURES])); const features = Number(output.feature); - assert.ok(features & StatsbeatFeature.A365, "A365 feature bit should be set"); + assert.ok(features & SdkStatsFeature.A365, "A365 feature bit should be set"); void shutdownMicrosoftOpenTelemetry(); }); @@ -957,13 +957,13 @@ describe("Main functions", () => { if (raw) { const output = JSON.parse(raw); const features = Number(output.feature); - assert.notOk(features & StatsbeatFeature.A365, "A365 feature bit should not be set"); + assert.notOk(features & SdkStatsFeature.A365, "A365 feature bit should not be set"); } void shutdownMicrosoftOpenTelemetry(); }); - it("should set OTLP feature bit in statsbeat when OTLP endpoint is configured", () => { + it("should set OTLP feature bit in SDK Stats when OTLP endpoint is configured", () => { const env = <{ [id: string]: string }>{}; env.OTEL_EXPORTER_OTLP_ENDPOINT = "http://localhost:4318"; process.env = env; @@ -975,7 +975,7 @@ describe("Main functions", () => { const output = JSON.parse(String(process.env[AZURE_MONITOR_STATSBEAT_FEATURES])); const features = Number(output.feature); - assert.ok(features & StatsbeatFeature.OTLP, "OTLP feature bit should be set"); + assert.ok(features & SdkStatsFeature.OTLP, "OTLP feature bit should be set"); void shutdownMicrosoftOpenTelemetry(); }); @@ -994,7 +994,7 @@ describe("Main functions", () => { const output = JSON.parse(String(process.env[AZURE_MONITOR_STATSBEAT_FEATURES])); const features = Number(output.feature); - assert.notOk(features & StatsbeatFeature.OTLP, "OTLP feature bit should not be set"); + assert.notOk(features & SdkStatsFeature.OTLP, "OTLP feature bit should not be set"); void shutdownMicrosoftOpenTelemetry(); }); @@ -1015,8 +1015,8 @@ describe("Main functions", () => { const output = JSON.parse(String(process.env[AZURE_MONITOR_STATSBEAT_FEATURES])); const features = Number(output.feature); - assert.ok(features & StatsbeatFeature.A365, "A365 feature bit should be set"); - assert.ok(features & StatsbeatFeature.OTLP, "OTLP feature bit should be set"); + assert.ok(features & SdkStatsFeature.A365, "A365 feature bit should be set"); + assert.ok(features & SdkStatsFeature.OTLP, "OTLP feature bit should be set"); void shutdownMicrosoftOpenTelemetry(); }); diff --git a/test/internal/unit/statsbeat.test.ts b/test/internal/unit/sdkStats.test.ts similarity index 61% rename from test/internal/unit/statsbeat.test.ts rename to test/internal/unit/sdkStats.test.ts index 4c9d440..e2e06fb 100644 --- a/test/internal/unit/statsbeat.test.ts +++ b/test/internal/unit/sdkStats.test.ts @@ -2,10 +2,10 @@ // Licensed under the MIT License. import { describe, it, beforeEach, afterEach, expect } from "vitest"; -import { getInstance } from "../../../src/utils/statsbeat.js"; -import { AZURE_MONITOR_STATSBEAT_FEATURES, StatsbeatFeature } from "../../../src/types.js"; +import { getInstance } from "../../../src/utils/sdkStats.js"; +import { AZURE_MONITOR_STATSBEAT_FEATURES, SdkStatsFeature } from "../../../src/types.js"; -describe("StatsbeatConfiguration — a365 and otlp feature flags", () => { +describe("SdkStatsConfiguration — a365 and otlp feature flags", () => { let originalEnv: NodeJS.ProcessEnv; beforeEach(() => { @@ -19,64 +19,64 @@ describe("StatsbeatConfiguration — a365 and otlp feature flags", () => { it("should set A365 feature bit (512) when a365 is enabled", () => { const sb = getInstance(); - sb.setStatsbeatFeatures({}, { a365: true }); + sb.setSdkStatsFeatures({}, { a365: true }); const output = JSON.parse(String(process.env[AZURE_MONITOR_STATSBEAT_FEATURES])); const features = Number(output.feature); - expect(features & StatsbeatFeature.A365).toBeTruthy(); + expect(features & SdkStatsFeature.A365).toBeTruthy(); }); it("should not set A365 feature bit when a365 is not enabled", () => { const sb = getInstance(); - sb.setStatsbeatFeatures({}, { a365: false }); + sb.setSdkStatsFeatures({}, { a365: false }); const output = JSON.parse(String(process.env[AZURE_MONITOR_STATSBEAT_FEATURES])); const features = Number(output.feature); - expect(features & StatsbeatFeature.A365).toBeFalsy(); + expect(features & SdkStatsFeature.A365).toBeFalsy(); }); it("should set OTLP feature bit (1024) when otlp is enabled", () => { const sb = getInstance(); - sb.setStatsbeatFeatures({}, { otlp: true }); + sb.setSdkStatsFeatures({}, { otlp: true }); const output = JSON.parse(String(process.env[AZURE_MONITOR_STATSBEAT_FEATURES])); const features = Number(output.feature); - expect(features & StatsbeatFeature.OTLP).toBeTruthy(); + expect(features & SdkStatsFeature.OTLP).toBeTruthy(); }); it("should not set OTLP feature bit when otlp is not enabled", () => { const sb = getInstance(); - sb.setStatsbeatFeatures({}, { otlp: false }); + sb.setSdkStatsFeatures({}, { otlp: false }); const output = JSON.parse(String(process.env[AZURE_MONITOR_STATSBEAT_FEATURES])); const features = Number(output.feature); - expect(features & StatsbeatFeature.OTLP).toBeFalsy(); + expect(features & SdkStatsFeature.OTLP).toBeFalsy(); }); it("should set both A365 and OTLP feature bits simultaneously", () => { const sb = getInstance(); - sb.setStatsbeatFeatures({}, { a365: true, otlp: true }); + sb.setSdkStatsFeatures({}, { a365: true, otlp: true }); const output = JSON.parse(String(process.env[AZURE_MONITOR_STATSBEAT_FEATURES])); const features = Number(output.feature); - expect(features & StatsbeatFeature.A365).toBeTruthy(); - expect(features & StatsbeatFeature.OTLP).toBeTruthy(); + expect(features & SdkStatsFeature.A365).toBeTruthy(); + expect(features & SdkStatsFeature.OTLP).toBeTruthy(); }); it("should preserve existing feature bits when adding a365/otlp", () => { // Seed the env var with an existing feature bitmap process.env[AZURE_MONITOR_STATSBEAT_FEATURES] = String( - StatsbeatFeature.AAD_HANDLING | StatsbeatFeature.DISK_RETRY, + SdkStatsFeature.AAD_HANDLING | SdkStatsFeature.DISK_RETRY, ); const sb = getInstance(); - sb.setStatsbeatFeatures({}, { a365: true, otlp: true }); + sb.setSdkStatsFeatures({}, { a365: true, otlp: true }); const output = JSON.parse(String(process.env[AZURE_MONITOR_STATSBEAT_FEATURES])); const features = Number(output.feature); - expect(features & StatsbeatFeature.AAD_HANDLING).toBeTruthy(); - expect(features & StatsbeatFeature.DISK_RETRY).toBeTruthy(); - expect(features & StatsbeatFeature.A365).toBeTruthy(); - expect(features & StatsbeatFeature.OTLP).toBeTruthy(); + expect(features & SdkStatsFeature.AAD_HANDLING).toBeTruthy(); + expect(features & SdkStatsFeature.DISK_RETRY).toBeTruthy(); + expect(features & SdkStatsFeature.A365).toBeTruthy(); + expect(features & SdkStatsFeature.OTLP).toBeTruthy(); }); }); diff --git a/test/internal/unit/sdkstats/metrics.test.ts b/test/internal/unit/sdkstats/metrics.test.ts index 04b0278..fd6c55c 100644 --- a/test/internal/unit/sdkstats/metrics.test.ts +++ b/test/internal/unit/sdkstats/metrics.test.ts @@ -21,8 +21,8 @@ import { } from "../../../../src/sdkstats/state.js"; import { MICROSOFT_OPENTELEMETRY_VERSION, - StatsbeatFeature, - StatsbeatInstrumentation, + SdkStatsFeature, + SdkStatsInstrumentation, } from "../../../../src/types.js"; import { SdkStatsDistroFeature } from "../../../../src/sdkstats/state.js"; @@ -64,7 +64,7 @@ describe("sdkstats/metrics", () => { }); it("emits a Feature observation with the OR'd feature bitmask and common dims", async () => { - setSdkStatsFeature(StatsbeatFeature.DISTRO); + setSdkStatsFeature(SdkStatsFeature.DISTRO); setSdkStatsFeature(SdkStatsDistroFeature.A365_EXPORT); setSdkStatsFeature(SdkStatsDistroFeature.OTLP_EXPORT); @@ -97,7 +97,7 @@ describe("sdkstats/metrics", () => { expect(typeof point.attributes.os).toBe("string"); const expectedBits = - StatsbeatFeature.DISTRO | + SdkStatsFeature.DISTRO | SdkStatsDistroFeature.A365_EXPORT | SdkStatsDistroFeature.OTLP_EXPORT; // Bitmask is sent as a string per spec (customDimensions are string-typed). @@ -107,8 +107,8 @@ describe("sdkstats/metrics", () => { }); it("emits a Feature.instrumentations observation tagged with type=1", async () => { - setSdkStatsInstrumentation(StatsbeatInstrumentation.MONGODB); - setSdkStatsInstrumentation(StatsbeatInstrumentation.REDIS); + setSdkStatsInstrumentation(SdkStatsInstrumentation.MONGODB); + setSdkStatsInstrumentation(SdkStatsInstrumentation.REDIS); const { PeriodicExportingMetricReader } = await import("@opentelemetry/sdk-metrics"); const exporter = new InMemoryMetricExporter(AggregationTemporality.CUMULATIVE); @@ -130,14 +130,14 @@ describe("sdkstats/metrics", () => { const point = instrMetrics[0].dataPoints[0]; expect(point.attributes.type).toBe(FEATURE_TYPE_INSTRUMENTATION); expect(point.attributes.feature).toBe( - String(StatsbeatInstrumentation.MONGODB | StatsbeatInstrumentation.REDIS), + String(SdkStatsInstrumentation.MONGODB | SdkStatsInstrumentation.REDIS), ); await meterProvider.shutdown(); }); it("uses the supplied distro version when provided", async () => { - setSdkStatsFeature(StatsbeatFeature.DISTRO); + setSdkStatsFeature(SdkStatsFeature.DISTRO); const { PeriodicExportingMetricReader } = await import("@opentelemetry/sdk-metrics"); const exporter = new InMemoryMetricExporter(AggregationTemporality.CUMULATIVE); const reader = new PeriodicExportingMetricReader({ @@ -161,8 +161,8 @@ describe("sdkstats/metrics", () => { describe("networkOnly mode", () => { it("skips Feature/Feature.instrumentations gauges but still registers network gauges", async () => { // Set bits that would normally trigger feature/instrumentation observations. - setSdkStatsFeature(StatsbeatFeature.DISTRO); - setSdkStatsInstrumentation(StatsbeatInstrumentation.MONGODB); + setSdkStatsFeature(SdkStatsFeature.DISTRO); + setSdkStatsInstrumentation(SdkStatsInstrumentation.MONGODB); // Drop a network counter so a request_success_count observation will fire. _resetNetworkStatsForTest(); recordSuccess("a365", "contoso.example.com"); diff --git a/test/internal/unit/sdkstats/state.test.ts b/test/internal/unit/sdkstats/state.test.ts index 574894a..b8eb86f 100644 --- a/test/internal/unit/sdkstats/state.test.ts +++ b/test/internal/unit/sdkstats/state.test.ts @@ -16,7 +16,7 @@ import { setSdkStatsInstrumentation, setSdkStatsShutdown, } from "../../../../src/sdkstats/state.js"; -import { StatsbeatFeature, StatsbeatInstrumentation } from "../../../../src/types.js"; +import { SdkStatsFeature, SdkStatsInstrumentation } from "../../../../src/types.js"; describe("sdkstats/state", () => { beforeEach(() => { @@ -53,21 +53,19 @@ describe("sdkstats/state", () => { expect(getSdkStatsFeatureFlags()).toBe(0); }); - it("ORs in shared StatsbeatFeature values", () => { - setSdkStatsFeature(StatsbeatFeature.DISTRO); - expect(getSdkStatsFeatureFlags()).toBe(StatsbeatFeature.DISTRO); - setSdkStatsFeature(StatsbeatFeature.LIVE_METRICS); - expect(getSdkStatsFeatureFlags()).toBe( - StatsbeatFeature.DISTRO | StatsbeatFeature.LIVE_METRICS, - ); + it("ORs in shared SdkStatsFeature values", () => { + setSdkStatsFeature(SdkStatsFeature.DISTRO); + expect(getSdkStatsFeatureFlags()).toBe(SdkStatsFeature.DISTRO); + setSdkStatsFeature(SdkStatsFeature.LIVE_METRICS); + expect(getSdkStatsFeatureFlags()).toBe(SdkStatsFeature.DISTRO | SdkStatsFeature.LIVE_METRICS); }); - it("ORs in distro-specific values without colliding with StatsbeatFeature", () => { - setSdkStatsFeature(StatsbeatFeature.DISTRO); + it("ORs in distro-specific values without colliding with SdkStatsFeature", () => { + setSdkStatsFeature(SdkStatsFeature.DISTRO); setSdkStatsFeature(SdkStatsDistroFeature.A365_EXPORT); setSdkStatsFeature(SdkStatsDistroFeature.OTLP_EXPORT); const expected = - StatsbeatFeature.DISTRO | + SdkStatsFeature.DISTRO | SdkStatsDistroFeature.A365_EXPORT | SdkStatsDistroFeature.OTLP_EXPORT; expect(getSdkStatsFeatureFlags()).toBe(expected); @@ -83,10 +81,10 @@ describe("sdkstats/state", () => { describe("instrumentation bitmask", () => { it("starts at 0 and ORs in values", () => { expect(getSdkStatsInstrumentationFlags()).toBe(0); - setSdkStatsInstrumentation(StatsbeatInstrumentation.MONGODB); - setSdkStatsInstrumentation(StatsbeatInstrumentation.REDIS); + setSdkStatsInstrumentation(SdkStatsInstrumentation.MONGODB); + setSdkStatsInstrumentation(SdkStatsInstrumentation.REDIS); expect(getSdkStatsInstrumentationFlags()).toBe( - StatsbeatInstrumentation.MONGODB | StatsbeatInstrumentation.REDIS, + SdkStatsInstrumentation.MONGODB | SdkStatsInstrumentation.REDIS, ); }); }); @@ -109,7 +107,7 @@ describe("sdkstats/state", () => { describe("_resetSdkStatsStateForTest", () => { it("clears all bitmasks and the shutdown flag", () => { setSdkStatsFeature(SdkStatsDistroFeature.A365_EXPORT); - setSdkStatsInstrumentation(StatsbeatInstrumentation.MONGODB); + setSdkStatsInstrumentation(SdkStatsInstrumentation.MONGODB); setSdkStatsShutdown(true); _resetSdkStatsStateForTest(); expect(getSdkStatsFeatureFlags()).toBe(0);