From c5575adbaf792bd7bd189ca3af7e7b89a1aac53b Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Wed, 22 Apr 2026 13:05:51 -0700 Subject: [PATCH 1/5] feat: add console exporter and ExportTarget flags matching .NET distro Add ExportTarget enum (None, AzureMonitor, Agent365, Otlp, Console) with the same values as the .NET distro for cross-platform consistency. - Add 'exporters' option to MicrosoftOpenTelemetryOptions for explicit exporter selection via bitwise OR (e.g. ExportTarget.Console | ExportTarget.AzureMonitor) - When 'exporters' is not set, auto-detection preserves existing behavior - Console exporter is never auto-detected (must be explicitly enabled) - Wire ConsoleSpanExporter, ConsoleMetricExporter, ConsoleLogRecordExporter using SimpleSpanProcessor/SimpleLogRecordProcessor for immediate output - Gate all exporter sections (AzureMonitor, OTLP, Agent365) through ExportTarget flags - Fix ./loader subpath export paths in package.json (dist/*/loader.js -> dist/*/azureMonitor/loader.js) - Export ExportTarget and ExportTargetFlags from public API --- package.json | 8 ++-- src/distro/distro.ts | 100 +++++++++++++++++++++++++++++++++---------- src/index.ts | 2 + src/types.ts | 46 ++++++++++++++++++++ 4 files changed, 129 insertions(+), 27 deletions(-) diff --git a/package.json b/package.json index 33ce6eb0..9ded4eb6 100644 --- a/package.json +++ b/package.json @@ -58,12 +58,12 @@ }, "./loader": { "import": { - "types": "./dist/esm/loader.d.ts", - "default": "./dist/esm/loader.js" + "types": "./dist/esm/azureMonitor/loader.d.ts", + "default": "./dist/esm/azureMonitor/loader.js" }, "require": { - "types": "./dist/commonjs/loader.d.ts", - "default": "./dist/commonjs/loader.js" + "types": "./dist/commonjs/azureMonitor/loader.d.ts", + "default": "./dist/commonjs/azureMonitor/loader.js" } } }, diff --git a/src/distro/distro.ts b/src/distro/distro.ts index 818f68cc..2b61e091 100644 --- a/src/distro/distro.ts +++ b/src/distro/distro.ts @@ -6,8 +6,10 @@ import { logs } from "@opentelemetry/api-logs"; import type { NodeSDKConfiguration } from "@opentelemetry/sdk-node"; import { NodeSDK } from "@opentelemetry/sdk-node"; import type { MetricReader, ViewOptions } from "@opentelemetry/sdk-metrics"; -import { type SpanProcessor, BatchSpanProcessor } from "@opentelemetry/sdk-trace-base"; +import { type SpanProcessor, BatchSpanProcessor, SimpleSpanProcessor, ConsoleSpanExporter } from "@opentelemetry/sdk-trace-base"; import type { LogRecordProcessor } from "@opentelemetry/sdk-logs"; +import { SimpleLogRecordProcessor, ConsoleLogRecordExporter } from "@opentelemetry/sdk-logs"; +import { ConsoleMetricExporter, PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics"; import type { Instrumentation } from "@opentelemetry/instrumentation"; import { InternalConfig } from "../shared/config.js"; @@ -25,8 +27,8 @@ import { A365SpanProcessor, PerRequestSpanProcessor, } from "../a365/index.js"; -import type { MicrosoftOpenTelemetryOptions } from "../types.js"; -import { MICROSOFT_OPENTELEMETRY_VERSION } from "../types.js"; +import type { MicrosoftOpenTelemetryOptions, ExportTargetFlags } from "../types.js"; +import { MICROSOFT_OPENTELEMETRY_VERSION, ExportTarget } from "../types.js"; process.env["AZURE_MONITOR_DISTRO_VERSION"] = AZURE_MONITOR_OPENTELEMETRY_VERSION; process.env["MICROSOFT_OPENTELEMETRY_VERSION"] = MICROSOFT_OPENTELEMETRY_VERSION; @@ -42,6 +44,9 @@ let disposeAzureMonitor: (() => void) | undefined; * - Azure Monitor (enabled by default; disable with `options.azureMonitor.enabled = false`) * - OTLP HTTP (when `OTEL_EXPORTER_OTLP_ENDPOINT` is set) * - A365 (when `options.a365.enabled` is true or `ENABLE_A365_OBSERVABILITY_EXPORTER=true`) + * - Console (when `exporters` includes `ExportTarget.Console`; never auto-detected) + * + * When `options.exporters` is set, it overrides auto-detection for all backends. * * @param options - Microsoft OpenTelemetry configuration options */ @@ -49,7 +54,10 @@ export function useMicrosoftOpenTelemetry(options?: MicrosoftOpenTelemetryOption const config = new InternalConfig(options); patchOpenTelemetryInstrumentationEnable(); - const azureMonitorEnabled = options?.azureMonitor?.enabled !== false; + // ── Resolve export targets ──────────────────────────────────────── + const exporters = resolveExportTargets(options); + + const azureMonitorEnabled = !!(exporters & ExportTarget.AzureMonitor); // ── Azure Monitor components (statsbeat, browser SDK loader, etc.) ─ disposeAzureMonitor = undefined; @@ -102,8 +110,8 @@ export function useMicrosoftOpenTelemetry(options?: MicrosoftOpenTelemetryOption ...(options?.metricReaders || []), ]; - // ── OTLP HTTP exporters (enabled via OTEL_EXPORTER_OTLP_ENDPOINT) ─ - if (isOtlpEnabled()) { + // ── OTLP HTTP exporters (enabled via exporters flag or OTEL_EXPORTER_OTLP_ENDPOINT) ─ + if (exporters & ExportTarget.Otlp) { const otlp = createOtlpComponents(); if (otlp.spanProcessor) { spanProcessors.push(otlp.spanProcessor); @@ -116,24 +124,33 @@ export function useMicrosoftOpenTelemetry(options?: MicrosoftOpenTelemetryOption } } - // ── A365 exporter (enabled via options.a365 or env vars) ────────── - const a365Config = new A365Configuration(options?.a365); - if (a365Config.enabled) { - const a365Exporter = new Agent365Exporter({ - clusterCategory: a365Config.clusterCategory, - domainOverride: a365Config.domainOverride, - tokenResolver: a365Config.tokenResolver, - }); - // A365SpanProcessor copies baggage (tenant, agent, session, etc.) to span attributes - if (a365Config.baggage.enrichSpans) { - spanProcessors.push(new A365SpanProcessor()); + // ── A365 exporter (enabled via exporters flag, options.a365, or env vars) ── + if (exporters & ExportTarget.Agent365) { + const a365Config = new A365Configuration(options?.a365); + if (a365Config.enabled) { + const a365Exporter = new Agent365Exporter({ + clusterCategory: a365Config.clusterCategory, + domainOverride: a365Config.domainOverride, + tokenResolver: a365Config.tokenResolver, + }); + // A365SpanProcessor copies baggage (tenant, agent, session, etc.) to span attributes + if (a365Config.baggage.enrichSpans) { + spanProcessors.push(new A365SpanProcessor()); + } + // PerRequestSpanProcessor buffers spans per trace and exports on root completion + // with the request's auth token; BatchSpanProcessor for standard batch export + const a365ExportProcessor = a365Config.perRequestExport + ? new PerRequestSpanProcessor(a365Exporter) + : new BatchSpanProcessor(a365Exporter); + spanProcessors.push(a365ExportProcessor); } - // PerRequestSpanProcessor buffers spans per trace and exports on root completion - // with the request's auth token; BatchSpanProcessor for standard batch export - const a365ExportProcessor = a365Config.perRequestExport - ? new PerRequestSpanProcessor(a365Exporter) - : new BatchSpanProcessor(a365Exporter); - spanProcessors.push(a365ExportProcessor); + } + + // ── Console exporters (development only, never auto-detected) ───── + if (exporters & ExportTarget.Console) { + spanProcessors.push(new SimpleSpanProcessor(new ConsoleSpanExporter())); + metricReaders.push(new PeriodicExportingMetricReader({ exporter: new ConsoleMetricExporter() })); + logRecordProcessors.push(new SimpleLogRecordProcessor(new ConsoleLogRecordExporter())); } const views: ViewOptions[] = [...(metricHandler ? metricHandler.getViews() : []), ...customViews]; @@ -179,3 +196,40 @@ export function shutdownMicrosoftOpenTelemetry(): Promise { export function _getSdkInstance(): NodeSDK | undefined { return sdk; } + +/** + * Resolves the effective export targets. + * + * When `options.exporters` is explicitly set, it is used as-is. + * Otherwise, auto-detection kicks in (matching the .NET distro behavior): + * - AzureMonitor — enabled when `azureMonitor.enabled !== false` + * - Agent365 — enabled when `a365.enabled` is true or `ENABLE_A365_OBSERVABILITY_EXPORTER=true` + * - Otlp — enabled when `OTEL_EXPORTER_OTLP_ENDPOINT` (or signal-specific variants) is set + * - Console — **never** auto-detected; must be explicitly set + * + * @internal + */ +function resolveExportTargets(options?: MicrosoftOpenTelemetryOptions): ExportTargetFlags { + if (options?.exporters !== undefined) { + return options.exporters; + } + + // Auto-detect from configuration and environment + let targets: ExportTargetFlags = ExportTarget.None; + + if (options?.azureMonitor?.enabled !== false) { + targets |= ExportTarget.AzureMonitor; + } + + const a365Config = new A365Configuration(options?.a365); + if (a365Config.enabled) { + targets |= ExportTarget.Agent365; + } + + if (isOtlpEnabled()) { + targets |= ExportTarget.Otlp; + } + + // Console is never auto-detected — must be explicitly set via options.exporters + return targets; +} diff --git a/src/index.ts b/src/index.ts index 1fcba412..46853620 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,6 +17,8 @@ export type { BrowserSdkLoaderOptions, A365Options, } from "./distro/index.js"; +export { ExportTarget } from "./types.js"; +export type { ExportTargetFlags } from "./types.js"; // ── Re-exports from A365 configuration ────────────────────────────────────── export { A365Configuration } from "./a365/index.js"; diff --git a/src/types.ts b/src/types.ts index 1cd8f1cf..9e4499b5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -13,6 +13,36 @@ import type { A365Options } from "./a365/index.js"; */ export const MICROSOFT_OPENTELEMETRY_VERSION = "0.1.0-alpha.3"; +/** + * Specifies which exporters to enable in the Microsoft OpenTelemetry distro. + * Multiple exporters can be combined using bitwise OR. + * + * Matches the .NET distro `ExportTarget` flags enum for cross-platform consistency. + * + * @example + * ```ts + * useMicrosoftOpenTelemetry({ + * exporters: ExportTarget.AzureMonitor | ExportTarget.Console, + * azureMonitor: { azureMonitorExporterOptions: { connectionString: "..." } }, + * }); + * ``` + */ +export const ExportTarget = { + /** No exporters enabled. Instrumentation is still active. */ + None: 0, + /** Export telemetry to Azure Monitor (Application Insights). */ + AzureMonitor: 1, + /** Export telemetry to Agent365 observability service. */ + Agent365: 2, + /** Export telemetry via OTLP (OpenTelemetry Protocol). */ + Otlp: 4, + /** Export telemetry to console (development only). */ + Console: 8, +} as const; + +/** Bitwise combination of {@link ExportTarget} values. */ +export type ExportTargetFlags = number; + /** * Microsoft OpenTelemetry Options * @@ -23,6 +53,22 @@ export const MICROSOFT_OPENTELEMETRY_VERSION = "0.1.0-alpha.3"; export interface MicrosoftOpenTelemetryOptions { // ── Global options ──────────────────────────────────────────────── + /** + * Selects which exporters to enable. Combine values with bitwise OR. + * + * When not set, exporters are auto-detected: + * - `AzureMonitor` — when `azureMonitor` options are provided with `enabled !== false`. + * - `Agent365` — when `a365.enabled` is true or `ENABLE_A365_OBSERVABILITY_EXPORTER=true`. + * - `Otlp` — when `OTEL_EXPORTER_OTLP_ENDPOINT` is set. + * - `Console` — **never** auto-detected; must be explicitly set. + * + * Set explicitly to override auto-detection: + * ```ts + * exporters: ExportTarget.Console | ExportTarget.AzureMonitor + * ``` + */ + exporters?: ExportTargetFlags; + /** OpenTelemetry Resource */ resource?: Resource; /** The rate of telemetry items tracked that should be transmitted (Default 1.0) */ From 90761cc21653e3e08d2840fa95156bfba88e1e84 Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Wed, 22 Apr 2026 13:17:20 -0700 Subject: [PATCH 2/5] Remove ExportTarget --- src/distro/distro.ts | 94 ++++++++++++-------------------------------- src/index.ts | 2 - src/types.ts | 49 ++--------------------- 3 files changed, 28 insertions(+), 117 deletions(-) diff --git a/src/distro/distro.ts b/src/distro/distro.ts index 2b61e091..96cb7a54 100644 --- a/src/distro/distro.ts +++ b/src/distro/distro.ts @@ -27,8 +27,8 @@ import { A365SpanProcessor, PerRequestSpanProcessor, } from "../a365/index.js"; -import type { MicrosoftOpenTelemetryOptions, ExportTargetFlags } from "../types.js"; -import { MICROSOFT_OPENTELEMETRY_VERSION, ExportTarget } from "../types.js"; +import type { MicrosoftOpenTelemetryOptions } from "../types.js"; +import { MICROSOFT_OPENTELEMETRY_VERSION } from "../types.js"; process.env["AZURE_MONITOR_DISTRO_VERSION"] = AZURE_MONITOR_OPENTELEMETRY_VERSION; process.env["MICROSOFT_OPENTELEMETRY_VERSION"] = MICROSOFT_OPENTELEMETRY_VERSION; @@ -44,9 +44,6 @@ let disposeAzureMonitor: (() => void) | undefined; * - Azure Monitor (enabled by default; disable with `options.azureMonitor.enabled = false`) * - OTLP HTTP (when `OTEL_EXPORTER_OTLP_ENDPOINT` is set) * - A365 (when `options.a365.enabled` is true or `ENABLE_A365_OBSERVABILITY_EXPORTER=true`) - * - Console (when `exporters` includes `ExportTarget.Console`; never auto-detected) - * - * When `options.exporters` is set, it overrides auto-detection for all backends. * * @param options - Microsoft OpenTelemetry configuration options */ @@ -54,10 +51,7 @@ export function useMicrosoftOpenTelemetry(options?: MicrosoftOpenTelemetryOption const config = new InternalConfig(options); patchOpenTelemetryInstrumentationEnable(); - // ── Resolve export targets ──────────────────────────────────────── - const exporters = resolveExportTargets(options); - - const azureMonitorEnabled = !!(exporters & ExportTarget.AzureMonitor); + const azureMonitorEnabled = options?.azureMonitor?.enabled !== false; // ── Azure Monitor components (statsbeat, browser SDK loader, etc.) ─ disposeAzureMonitor = undefined; @@ -110,8 +104,8 @@ export function useMicrosoftOpenTelemetry(options?: MicrosoftOpenTelemetryOption ...(options?.metricReaders || []), ]; - // ── OTLP HTTP exporters (enabled via exporters flag or OTEL_EXPORTER_OTLP_ENDPOINT) ─ - if (exporters & ExportTarget.Otlp) { + // ── OTLP HTTP exporters (enabled via OTEL_EXPORTER_OTLP_ENDPOINT) ─ + if (isOtlpEnabled()) { const otlp = createOtlpComponents(); if (otlp.spanProcessor) { spanProcessors.push(otlp.spanProcessor); @@ -124,30 +118,29 @@ export function useMicrosoftOpenTelemetry(options?: MicrosoftOpenTelemetryOption } } - // ── A365 exporter (enabled via exporters flag, options.a365, or env vars) ── - if (exporters & ExportTarget.Agent365) { - const a365Config = new A365Configuration(options?.a365); - if (a365Config.enabled) { - const a365Exporter = new Agent365Exporter({ - clusterCategory: a365Config.clusterCategory, - domainOverride: a365Config.domainOverride, - tokenResolver: a365Config.tokenResolver, - }); - // A365SpanProcessor copies baggage (tenant, agent, session, etc.) to span attributes - if (a365Config.baggage.enrichSpans) { - spanProcessors.push(new A365SpanProcessor()); - } - // PerRequestSpanProcessor buffers spans per trace and exports on root completion - // with the request's auth token; BatchSpanProcessor for standard batch export - const a365ExportProcessor = a365Config.perRequestExport - ? new PerRequestSpanProcessor(a365Exporter) - : new BatchSpanProcessor(a365Exporter); - spanProcessors.push(a365ExportProcessor); + // ── A365 exporter (enabled via options.a365 or env vars) ────────── + const a365Config = new A365Configuration(options?.a365); + if (a365Config.enabled) { + const a365Exporter = new Agent365Exporter({ + clusterCategory: a365Config.clusterCategory, + domainOverride: a365Config.domainOverride, + tokenResolver: a365Config.tokenResolver, + }); + // A365SpanProcessor copies baggage (tenant, agent, session, etc.) to span attributes + if (a365Config.baggage.enrichSpans) { + spanProcessors.push(new A365SpanProcessor()); } + // PerRequestSpanProcessor buffers spans per trace and exports on root completion + // with the request's auth token; BatchSpanProcessor for standard batch export + const a365ExportProcessor = a365Config.perRequestExport + ? new PerRequestSpanProcessor(a365Exporter) + : new BatchSpanProcessor(a365Exporter); + spanProcessors.push(a365ExportProcessor); } - // ── Console exporters (development only, never auto-detected) ───── - if (exporters & ExportTarget.Console) { + // ── Console exporters (auto-enabled when no other exporter is active, or explicitly) ─ + const consoleEnabled = options?.enableConsoleExporters ?? (!azureMonitorEnabled && !isOtlpEnabled() && !a365Config.enabled); + if (consoleEnabled) { spanProcessors.push(new SimpleSpanProcessor(new ConsoleSpanExporter())); metricReaders.push(new PeriodicExportingMetricReader({ exporter: new ConsoleMetricExporter() })); logRecordProcessors.push(new SimpleLogRecordProcessor(new ConsoleLogRecordExporter())); @@ -196,40 +189,3 @@ export function shutdownMicrosoftOpenTelemetry(): Promise { export function _getSdkInstance(): NodeSDK | undefined { return sdk; } - -/** - * Resolves the effective export targets. - * - * When `options.exporters` is explicitly set, it is used as-is. - * Otherwise, auto-detection kicks in (matching the .NET distro behavior): - * - AzureMonitor — enabled when `azureMonitor.enabled !== false` - * - Agent365 — enabled when `a365.enabled` is true or `ENABLE_A365_OBSERVABILITY_EXPORTER=true` - * - Otlp — enabled when `OTEL_EXPORTER_OTLP_ENDPOINT` (or signal-specific variants) is set - * - Console — **never** auto-detected; must be explicitly set - * - * @internal - */ -function resolveExportTargets(options?: MicrosoftOpenTelemetryOptions): ExportTargetFlags { - if (options?.exporters !== undefined) { - return options.exporters; - } - - // Auto-detect from configuration and environment - let targets: ExportTargetFlags = ExportTarget.None; - - if (options?.azureMonitor?.enabled !== false) { - targets |= ExportTarget.AzureMonitor; - } - - const a365Config = new A365Configuration(options?.a365); - if (a365Config.enabled) { - targets |= ExportTarget.Agent365; - } - - if (isOtlpEnabled()) { - targets |= ExportTarget.Otlp; - } - - // Console is never auto-detected — must be explicitly set via options.exporters - return targets; -} diff --git a/src/index.ts b/src/index.ts index 46853620..1fcba412 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,8 +17,6 @@ export type { BrowserSdkLoaderOptions, A365Options, } from "./distro/index.js"; -export { ExportTarget } from "./types.js"; -export type { ExportTargetFlags } from "./types.js"; // ── Re-exports from A365 configuration ────────────────────────────────────── export { A365Configuration } from "./a365/index.js"; diff --git a/src/types.ts b/src/types.ts index 9e4499b5..2d0fd111 100644 --- a/src/types.ts +++ b/src/types.ts @@ -13,36 +13,6 @@ import type { A365Options } from "./a365/index.js"; */ export const MICROSOFT_OPENTELEMETRY_VERSION = "0.1.0-alpha.3"; -/** - * Specifies which exporters to enable in the Microsoft OpenTelemetry distro. - * Multiple exporters can be combined using bitwise OR. - * - * Matches the .NET distro `ExportTarget` flags enum for cross-platform consistency. - * - * @example - * ```ts - * useMicrosoftOpenTelemetry({ - * exporters: ExportTarget.AzureMonitor | ExportTarget.Console, - * azureMonitor: { azureMonitorExporterOptions: { connectionString: "..." } }, - * }); - * ``` - */ -export const ExportTarget = { - /** No exporters enabled. Instrumentation is still active. */ - None: 0, - /** Export telemetry to Azure Monitor (Application Insights). */ - AzureMonitor: 1, - /** Export telemetry to Agent365 observability service. */ - Agent365: 2, - /** Export telemetry via OTLP (OpenTelemetry Protocol). */ - Otlp: 4, - /** Export telemetry to console (development only). */ - Console: 8, -} as const; - -/** Bitwise combination of {@link ExportTarget} values. */ -export type ExportTargetFlags = number; - /** * Microsoft OpenTelemetry Options * @@ -53,22 +23,6 @@ export type ExportTargetFlags = number; export interface MicrosoftOpenTelemetryOptions { // ── Global options ──────────────────────────────────────────────── - /** - * Selects which exporters to enable. Combine values with bitwise OR. - * - * When not set, exporters are auto-detected: - * - `AzureMonitor` — when `azureMonitor` options are provided with `enabled !== false`. - * - `Agent365` — when `a365.enabled` is true or `ENABLE_A365_OBSERVABILITY_EXPORTER=true`. - * - `Otlp` — when `OTEL_EXPORTER_OTLP_ENDPOINT` is set. - * - `Console` — **never** auto-detected; must be explicitly set. - * - * Set explicitly to override auto-detection: - * ```ts - * exporters: ExportTarget.Console | ExportTarget.AzureMonitor - * ``` - */ - exporters?: ExportTargetFlags; - /** OpenTelemetry Resource */ resource?: Resource; /** The rate of telemetry items tracked that should be transmitted (Default 1.0) */ @@ -93,6 +47,9 @@ export interface MicrosoftOpenTelemetryOptions { /** A365 observability configuration. When provided with `enabled: true`, A365 export is enabled. */ a365?: A365Options; + + /** Enable console exporters for traces, metrics, and logs. Auto-enabled when no other exporter is active. */ + enableConsoleExporters?: boolean; } /** From 21cb0ebf233b8fec0a83eafd8772857a6f01f245 Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Wed, 22 Apr 2026 13:23:17 -0700 Subject: [PATCH 3/5] fix: update test for console exporter auto-enable --- test/internal/unit/main.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/internal/unit/main.test.ts b/test/internal/unit/main.test.ts index 1afff65a..249af2a6 100644 --- a/test/internal/unit/main.test.ts +++ b/test/internal/unit/main.test.ts @@ -828,6 +828,7 @@ describe("Main functions", () => { useMicrosoftOpenTelemetry({ azureMonitor: { enabled: false }, + enableConsoleExporters: false, spanProcessors: [processor], }); From 2467076bf73f1c0d5b2f5b0c5d731b83c9d3eacb Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Wed, 22 Apr 2026 13:31:20 -0700 Subject: [PATCH 4/5] address PR review: format imports, add custom processor guard, pass exportIntervalMillis, add tests --- src/distro/distro.ts | 27 ++++++++++++++--- test/internal/unit/main.test.ts | 52 ++++++++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 5 deletions(-) diff --git a/src/distro/distro.ts b/src/distro/distro.ts index 96cb7a54..ad4638c1 100644 --- a/src/distro/distro.ts +++ b/src/distro/distro.ts @@ -6,10 +6,18 @@ import { logs } from "@opentelemetry/api-logs"; import type { NodeSDKConfiguration } from "@opentelemetry/sdk-node"; import { NodeSDK } from "@opentelemetry/sdk-node"; import type { MetricReader, ViewOptions } from "@opentelemetry/sdk-metrics"; -import { type SpanProcessor, BatchSpanProcessor, SimpleSpanProcessor, ConsoleSpanExporter } from "@opentelemetry/sdk-trace-base"; +import { + type SpanProcessor, + BatchSpanProcessor, + SimpleSpanProcessor, + ConsoleSpanExporter, +} from "@opentelemetry/sdk-trace-base"; import type { LogRecordProcessor } from "@opentelemetry/sdk-logs"; import { SimpleLogRecordProcessor, ConsoleLogRecordExporter } from "@opentelemetry/sdk-logs"; -import { ConsoleMetricExporter, PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics"; +import { + ConsoleMetricExporter, + PeriodicExportingMetricReader, +} from "@opentelemetry/sdk-metrics"; import type { Instrumentation } from "@opentelemetry/instrumentation"; import { InternalConfig } from "../shared/config.js"; @@ -139,10 +147,21 @@ export function useMicrosoftOpenTelemetry(options?: MicrosoftOpenTelemetryOption } // ── Console exporters (auto-enabled when no other exporter is active, or explicitly) ─ - const consoleEnabled = options?.enableConsoleExporters ?? (!azureMonitorEnabled && !isOtlpEnabled() && !a365Config.enabled); + const hasCustomProcessors = + (options?.spanProcessors?.length ?? 0) > 0 || + (options?.metricReaders?.length ?? 0) > 0 || + (options?.logRecordProcessors?.length ?? 0) > 0; + const consoleEnabled = + options?.enableConsoleExporters ?? + (!azureMonitorEnabled && !isOtlpEnabled() && !a365Config.enabled && !hasCustomProcessors); if (consoleEnabled) { spanProcessors.push(new SimpleSpanProcessor(new ConsoleSpanExporter())); - metricReaders.push(new PeriodicExportingMetricReader({ exporter: new ConsoleMetricExporter() })); + metricReaders.push( + new PeriodicExportingMetricReader({ + exporter: new ConsoleMetricExporter(), + exportIntervalMillis: config.metricExportIntervalMillis, + }), + ); logRecordProcessors.push(new SimpleLogRecordProcessor(new ConsoleLogRecordExporter())); } diff --git a/test/internal/unit/main.test.ts b/test/internal/unit/main.test.ts index 249af2a6..55cf43b7 100644 --- a/test/internal/unit/main.test.ts +++ b/test/internal/unit/main.test.ts @@ -828,7 +828,6 @@ describe("Main functions", () => { useMicrosoftOpenTelemetry({ azureMonitor: { enabled: false }, - enableConsoleExporters: false, spanProcessors: [processor], }); @@ -856,4 +855,55 @@ describe("Main functions", () => { await shutdownMicrosoftOpenTelemetry(); }); + + it("console exporters auto-enabled when no built-in exporters are active", async () => { + const { useMicrosoftOpenTelemetry, shutdownMicrosoftOpenTelemetry } = + await import("../../../src/index.js"); + useMicrosoftOpenTelemetry({ azureMonitor: { enabled: false } }); + + const internalSdk = _getSdkInstance(); + const meterProvider = (internalSdk as any)["_meterProvider"]; + const metricReaders = meterProvider?.["_sharedState"]?.metricCollectors || []; + assert.isAbove(metricReaders.length, 0, "Console metric reader should be auto-enabled"); + + await shutdownMicrosoftOpenTelemetry(); + }); + + it("enableConsoleExporters=false suppresses console exporters", async () => { + const { useMicrosoftOpenTelemetry, shutdownMicrosoftOpenTelemetry } = + await import("../../../src/index.js"); + useMicrosoftOpenTelemetry({ + azureMonitor: { enabled: false }, + enableConsoleExporters: false, + }); + + const internalSdk = _getSdkInstance(); + const meterProvider = (internalSdk as any)["_meterProvider"]; + const metricReaders = meterProvider?.["_sharedState"]?.metricCollectors || []; + assert.strictEqual(metricReaders.length, 0, "No metric readers when console is suppressed"); + + await shutdownMicrosoftOpenTelemetry(); + }); + + it("enableConsoleExporters=true alongside Azure Monitor", async () => { + const { useMicrosoftOpenTelemetry, shutdownMicrosoftOpenTelemetry } = + await import("../../../src/index.js"); + useMicrosoftOpenTelemetry({ + enableConsoleExporters: true, + azureMonitor: { + azureMonitorExporterOptions: { + connectionString: + "InstrumentationKey=00000000-0000-0000-0000-000000000000;IngestionEndpoint=https://localhost", + }, + }, + }); + + const internalSdk = _getSdkInstance(); + const meterProvider = (internalSdk as any)["_meterProvider"]; + const metricReaders = meterProvider?.["_sharedState"]?.metricCollectors || []; + // Should have both Azure Monitor metric reader and console metric reader + assert.isAbove(metricReaders.length, 1, "Should have Azure Monitor + Console metric readers"); + + await shutdownMicrosoftOpenTelemetry(); + }); }); From 98be428ac7092665bcf66eada063adeea4afb587 Mon Sep 17 00:00:00 2001 From: Hector Hernandez <39923391+hectorhdzg@users.noreply.github.com> Date: Wed, 22 Apr 2026 13:35:52 -0700 Subject: [PATCH 5/5] fix: format distro.ts with prettier --- src/distro/distro.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/distro/distro.ts b/src/distro/distro.ts index ad4638c1..d5f0df8f 100644 --- a/src/distro/distro.ts +++ b/src/distro/distro.ts @@ -14,10 +14,7 @@ import { } from "@opentelemetry/sdk-trace-base"; import type { LogRecordProcessor } from "@opentelemetry/sdk-logs"; import { SimpleLogRecordProcessor, ConsoleLogRecordExporter } from "@opentelemetry/sdk-logs"; -import { - ConsoleMetricExporter, - PeriodicExportingMetricReader, -} from "@opentelemetry/sdk-metrics"; +import { ConsoleMetricExporter, PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics"; import type { Instrumentation } from "@opentelemetry/instrumentation"; import { InternalConfig } from "../shared/config.js";