From 09cc182addd4954d1c617b8eb1ec7d0616ebb793 Mon Sep 17 00:00:00 2001 From: MarioJGMsoft Date: Mon, 27 Apr 2026 15:58:58 +0000 Subject: [PATCH 01/21] feat: deprecate LogLevel.default and .error and remove all calls to those values. --- .../api-report/core-interfaces.beta.api.md | 2 ++ .../core-interfaces.legacy.alpha.api.md | 2 ++ .../core-interfaces.legacy.beta.api.md | 2 ++ .../core-interfaces.legacy.public.api.md | 2 ++ .../api-report/core-interfaces.public.api.md | 2 ++ packages/common/core-interfaces/src/logger.ts | 8 ++++---- .../telemetry-utils.legacy.beta.api.md | 4 ++-- packages/utils/telemetry-utils/src/logger.ts | 19 ++++++++++--------- .../utils/telemetry-utils/src/mockLogger.ts | 4 ++-- .../telemetry-utils/src/telemetryTypes.ts | 12 ++++++------ .../src/test/childLogger.spec.ts | 12 ++++++------ .../src/test/multiSinkLogger.spec.ts | 14 +++++++------- 12 files changed, 47 insertions(+), 36 deletions(-) diff --git a/packages/common/core-interfaces/api-report/core-interfaces.beta.api.md b/packages/common/core-interfaces/api-report/core-interfaces.beta.api.md index 39a9b3379798..0562ed5986c8 100644 --- a/packages/common/core-interfaces/api-report/core-interfaces.beta.api.md +++ b/packages/common/core-interfaces/api-report/core-interfaces.beta.api.md @@ -333,7 +333,9 @@ export type LogLevel = (typeof LogLevel)[keyof typeof LogLevel]; // @public export interface LogLevelConst { + // @deprecated readonly default: 20; + // @deprecated readonly error: 30; readonly essential: 30; readonly info: 20; diff --git a/packages/common/core-interfaces/api-report/core-interfaces.legacy.alpha.api.md b/packages/common/core-interfaces/api-report/core-interfaces.legacy.alpha.api.md index ef43fde81f7b..9378cbc5d36a 100644 --- a/packages/common/core-interfaces/api-report/core-interfaces.legacy.alpha.api.md +++ b/packages/common/core-interfaces/api-report/core-interfaces.legacy.alpha.api.md @@ -478,7 +478,9 @@ export type LogLevel = (typeof LogLevel)[keyof typeof LogLevel]; // @public export interface LogLevelConst { + // @deprecated readonly default: 20; + // @deprecated readonly error: 30; readonly essential: 30; readonly info: 20; diff --git a/packages/common/core-interfaces/api-report/core-interfaces.legacy.beta.api.md b/packages/common/core-interfaces/api-report/core-interfaces.legacy.beta.api.md index cae6efefbae2..3975eeb54bfd 100644 --- a/packages/common/core-interfaces/api-report/core-interfaces.legacy.beta.api.md +++ b/packages/common/core-interfaces/api-report/core-interfaces.legacy.beta.api.md @@ -465,7 +465,9 @@ export type LogLevel = (typeof LogLevel)[keyof typeof LogLevel]; // @public export interface LogLevelConst { + // @deprecated readonly default: 20; + // @deprecated readonly error: 30; readonly essential: 30; readonly info: 20; diff --git a/packages/common/core-interfaces/api-report/core-interfaces.legacy.public.api.md b/packages/common/core-interfaces/api-report/core-interfaces.legacy.public.api.md index 31e263d9e2c0..2f53560ad17c 100644 --- a/packages/common/core-interfaces/api-report/core-interfaces.legacy.public.api.md +++ b/packages/common/core-interfaces/api-report/core-interfaces.legacy.public.api.md @@ -333,7 +333,9 @@ export type LogLevel = (typeof LogLevel)[keyof typeof LogLevel]; // @public export interface LogLevelConst { + // @deprecated readonly default: 20; + // @deprecated readonly error: 30; readonly essential: 30; readonly info: 20; diff --git a/packages/common/core-interfaces/api-report/core-interfaces.public.api.md b/packages/common/core-interfaces/api-report/core-interfaces.public.api.md index 31e263d9e2c0..2f53560ad17c 100644 --- a/packages/common/core-interfaces/api-report/core-interfaces.public.api.md +++ b/packages/common/core-interfaces/api-report/core-interfaces.public.api.md @@ -333,7 +333,9 @@ export type LogLevel = (typeof LogLevel)[keyof typeof LogLevel]; // @public export interface LogLevelConst { + // @deprecated readonly default: 20; + // @deprecated readonly error: 30; readonly essential: 30; readonly info: 20; diff --git a/packages/common/core-interfaces/src/logger.ts b/packages/common/core-interfaces/src/logger.ts index 563d17182769..33a44a287890 100644 --- a/packages/common/core-interfaces/src/logger.ts +++ b/packages/common/core-interfaces/src/logger.ts @@ -81,13 +81,13 @@ export interface LogLevelConst { /** * Default LogLevel - * @remarks Prefer {@link LogLevelConst.info | LogLevel.info} when selecting a level explicitly since this will be deprecated and removed in a future release. + * @deprecated Prefer {@link LogLevelConst.info | LogLevel.info} when selecting a level explicitly to preserve prior treatment. */ readonly default: 20; /** * To log errors. - * @remarks Prefer {@link LogLevelConst.essential | LogLevel.essential} when selecting a level since this will be deprecated and removed in a future release. + * @deprecated Prefer {@link LogLevelConst.essential | LogLevel.essential} when selecting a level. */ readonly error: 30; } @@ -120,13 +120,13 @@ export interface ITelemetryBaseLogger { /** * Log a telemetry event, if it meets the appropriate log-level threshold (see {@link ITelemetryBaseLogger.minLogLevel}). * @param event - The event to log. - * @param logLevel - The log level of the event. Default: {@link LogLevelConst.default | LogLevel.default}. + * @param logLevel - The log level of the event. Default: {@link LogLevelConst.info | LogLevel.info}. */ send(event: ITelemetryBaseEvent, logLevel?: LogLevel): void; /** * Minimum log level to be logged. - * @defaultValue {@link LogLevelConst.default | LogLevel.default}. + * @defaultValue {@link LogLevelConst.info | LogLevel.info}. */ minLogLevel?: LogLevel; } diff --git a/packages/utils/telemetry-utils/api-report/telemetry-utils.legacy.beta.api.md b/packages/utils/telemetry-utils/api-report/telemetry-utils.legacy.beta.api.md index 0d9251f8cc7e..1af6c1b4480a 100644 --- a/packages/utils/telemetry-utils/api-report/telemetry-utils.legacy.beta.api.md +++ b/packages/utils/telemetry-utils/api-report/telemetry-utils.legacy.beta.api.md @@ -34,9 +34,9 @@ export interface ITelemetryLoggerExt extends ITelemetryBaseLogger { // @deprecated sendErrorEvent(event: ITelemetryErrorEventExt, error?: unknown): void; // @deprecated - sendPerformanceEvent(event: ITelemetryPerformanceEventExt, error?: unknown, logLevel?: typeof LogLevel.verbose | typeof LogLevel.default): void; + sendPerformanceEvent(event: ITelemetryPerformanceEventExt, error?: unknown, logLevel?: typeof LogLevel.verbose | typeof LogLevel.info): void; // @deprecated - sendTelemetryEvent(event: ITelemetryGenericEventExt, error?: unknown, logLevel?: typeof LogLevel.verbose | typeof LogLevel.default): void; + sendTelemetryEvent(event: ITelemetryGenericEventExt, error?: unknown, logLevel?: typeof LogLevel.verbose | typeof LogLevel.info): void; } // @beta @legacy (undocumented) diff --git a/packages/utils/telemetry-utils/src/logger.ts b/packages/utils/telemetry-utils/src/logger.ts index ec49c2d36993..7ea05780904b 100644 --- a/packages/utils/telemetry-utils/src/logger.ts +++ b/packages/utils/telemetry-utils/src/logger.ts @@ -230,12 +230,12 @@ export abstract class TelemetryLogger implements TelemetryLoggerExt { public sendTelemetryEvent( event: ITelemetryGenericEventExt, error?: unknown, - logLevel: typeof LogLevel.verbose | typeof LogLevel.default = LogLevel.default, + logLevel: typeof LogLevel.verbose | typeof LogLevel.info = LogLevel.info, ): void { this.sendTelemetryEventCore( { ...event, category: event.category ?? "generic" }, error, - event.category === "error" ? LogLevel.error : logLevel, + event.category === "error" ? LogLevel.essential : logLevel, ); } @@ -280,7 +280,7 @@ export abstract class TelemetryLogger implements TelemetryLoggerExt { category: "error", }, error, - LogLevel.error, + LogLevel.essential, ); } @@ -295,7 +295,7 @@ export abstract class TelemetryLogger implements TelemetryLoggerExt { public sendPerformanceEvent( event: ITelemetryPerformanceEventExt, error?: unknown, - logLevel: typeof LogLevel.verbose | typeof LogLevel.default = LogLevel.default, + logLevel: typeof LogLevel.verbose | typeof LogLevel.info = LogLevel.info, ): void { const perfEvent = { ...event, @@ -305,7 +305,7 @@ export abstract class TelemetryLogger implements TelemetryLoggerExt { this.sendTelemetryEventCore( perfEvent, error, - perfEvent.category === "error" ? LogLevel.error : logLevel, + perfEvent.category === "error" ? LogLevel.essential : logLevel, ); } @@ -518,8 +518,9 @@ export class ChildLogger extends TelemetryLogger { } private shouldFilterOutEvent(event: ITelemetryBaseEvent, logLevel?: LogLevel): boolean { - const eventLogLevel = logLevel ?? LogLevel.default; - const configLogLevel = this.baseLogger.minLogLevel ?? LogLevel.default; + // Determine the log level of the event. If none is provided, default to essential + const eventLogLevel = logLevel ?? LogLevel.essential; + const configLogLevel = this.baseLogger.minLogLevel ?? LogLevel.info; // Filter out in case event log level is below what is wanted in config. return eventLogLevel < configLogLevel; } @@ -618,7 +619,7 @@ export class MultiSinkLogger extends TelemetryLogger { super(namespace, realProperties); this.loggers = loggers; - this._minLogLevelOfAllLoggers = LogLevel.default; + this._minLogLevelOfAllLoggers = LogLevel.info; this.calculateMinLogLevel(); } @@ -630,7 +631,7 @@ export class MultiSinkLogger extends TelemetryLogger { if (this.loggers.length > 0) { const logLevels: LogLevel[] = []; for (const logger of this.loggers) { - logLevels.push(logger.minLogLevel ?? LogLevel.default); + logLevels.push(logger.minLogLevel ?? LogLevel.info); } this._minLogLevelOfAllLoggers = Math.min(...logLevels) as LogLevel; } diff --git a/packages/utils/telemetry-utils/src/mockLogger.ts b/packages/utils/telemetry-utils/src/mockLogger.ts index cf52590cbdeb..a413c9e1b2d0 100644 --- a/packages/utils/telemetry-utils/src/mockLogger.ts +++ b/packages/utils/telemetry-utils/src/mockLogger.ts @@ -41,7 +41,7 @@ export class MockLogger implements ITelemetryBaseLogger { public readonly minLogLevel: LogLevel; public constructor(minLogLevel?: LogLevel) { - this.minLogLevel = minLogLevel ?? LogLevel.default; + this.minLogLevel = minLogLevel ?? LogLevel.info; } /** @@ -59,7 +59,7 @@ export class MockLogger implements ITelemetryBaseLogger { * {@inheritDoc @fluidframework/core-interfaces#ITelemetryBaseLogger.send} */ public send(event: ITelemetryBaseEvent, logLevel?: LogLevel): void { - if ((logLevel ?? LogLevel.default) >= this.minLogLevel) { + if ((logLevel ?? LogLevel.info) >= this.minLogLevel) { this._events.push(event); } } diff --git a/packages/utils/telemetry-utils/src/telemetryTypes.ts b/packages/utils/telemetry-utils/src/telemetryTypes.ts index 09270dfcf25f..ba79d132373a 100644 --- a/packages/utils/telemetry-utils/src/telemetryTypes.ts +++ b/packages/utils/telemetry-utils/src/telemetryTypes.ts @@ -143,7 +143,7 @@ export interface ITelemetryLoggerExt extends ITelemetryBaseLogger { sendTelemetryEvent( event: ITelemetryGenericEventExt, error?: unknown, - logLevel?: typeof LogLevel.verbose | typeof LogLevel.default, + logLevel?: typeof LogLevel.verbose | typeof LogLevel.info, ): void; /** @@ -166,7 +166,7 @@ export interface ITelemetryLoggerExt extends ITelemetryBaseLogger { sendPerformanceEvent( event: ITelemetryPerformanceEventExt, error?: unknown, - logLevel?: typeof LogLevel.verbose | typeof LogLevel.default, + logLevel?: typeof LogLevel.verbose | typeof LogLevel.info, ): void; } @@ -183,12 +183,12 @@ export interface TelemetryLoggerExt extends ITelemetryBaseLogger { * Send an information telemetry event. * @param event - Event to send. * @param error - Optional error object to log. - * @param logLevel - Optional level of the log. Default: {@link @fluidframework/core-interfaces#LogLevel.default}. + * @param logLevel - Optional level of the log. Default: {@link @fluidframework/core-interfaces#LogLevelConst.info | LogLevel.info}. */ sendTelemetryEvent( event: ITelemetryGenericEventExt, error?: unknown, - logLevel?: typeof LogLevel.verbose | typeof LogLevel.default, + logLevel?: typeof LogLevel.verbose | typeof LogLevel.info, ): void; /** @@ -202,11 +202,11 @@ export interface TelemetryLoggerExt extends ITelemetryBaseLogger { * Send a performance telemetry event. * @param event - Event to send * @param error - Optional error object to log. - * @param logLevel - Optional level of the log. Default: {@link @fluidframework/core-interfaces#LogLevelConst.default | LogLevel.default}. + * @param logLevel - Optional level of the log. Default: {@link @fluidframework/core-interfaces#LogLevelConst.info | LogLevel.info}. */ sendPerformanceEvent( event: ITelemetryPerformanceEventExt, error?: unknown, - logLevel?: typeof LogLevel.verbose | typeof LogLevel.default, + logLevel?: typeof LogLevel.verbose | typeof LogLevel.info, ): void; } diff --git a/packages/utils/telemetry-utils/src/test/childLogger.spec.ts b/packages/utils/telemetry-utils/src/test/childLogger.spec.ts index be7e75d132b6..0bb1961bad34 100644 --- a/packages/utils/telemetry-utils/src/test/childLogger.spec.ts +++ b/packages/utils/telemetry-utils/src/test/childLogger.spec.ts @@ -194,15 +194,15 @@ describe("ChildLogger", () => { sent = true; }, - minLogLevel: LogLevel.error, + minLogLevel: LogLevel.essential, }; const childLogger1 = createChildLogger({ logger }); - childLogger1.send({ category: "error", eventName: "testEvent" }, LogLevel.error); + childLogger1.send({ category: "error", eventName: "testEvent" }, LogLevel.essential); assert(sent, "event should be sent"); sent = false; - childLogger1.send({ category: "generic", eventName: "testEvent" }, LogLevel.default); + childLogger1.send({ category: "generic", eventName: "testEvent" }, LogLevel.info); assert(!sent, "event should not be sent"); }); @@ -257,10 +257,10 @@ describe("ChildLogger", () => { } sent = true; }, - minLogLevel: LogLevel.default, + minLogLevel: LogLevel.info, }; const multiSinkLogger = createMultiSinkLogger({ - loggers: [logger1, new MockLogger(LogLevel.error)], + loggers: [logger1, new MockLogger(LogLevel.essential)], }); const childLogger1 = createChildLogger({ logger: multiSinkLogger, @@ -269,7 +269,7 @@ describe("ChildLogger", () => { childLogger1.send({ category: "generic", eventName: "testEvent" }, LogLevel.verbose); assert(!sent, "verbose event should not be sent"); - childLogger1.send({ category: "generic", eventName: "testEvent" }, LogLevel.default); + childLogger1.send({ category: "generic", eventName: "testEvent" }, LogLevel.info); assert(sent, "default event should be sent"); }); }); diff --git a/packages/utils/telemetry-utils/src/test/multiSinkLogger.spec.ts b/packages/utils/telemetry-utils/src/test/multiSinkLogger.spec.ts index 74efa4ec3c14..1a899f599d31 100644 --- a/packages/utils/telemetry-utils/src/test/multiSinkLogger.spec.ts +++ b/packages/utils/telemetry-utils/src/test/multiSinkLogger.spec.ts @@ -66,14 +66,14 @@ describe("MultiSinkLogger", () => { }); it("MultiSink logger set the logLevel to min logLevel of all loggers", () => { - const logger1 = new MockLogger(LogLevel.error); - const logger2 = new MockLogger(LogLevel.default); + const logger1 = new MockLogger(LogLevel.essential); + const logger2 = new MockLogger(LogLevel.info); const multiSink = createMultiSinkLogger({ loggers: [createChildLogger({ logger: logger1 }), logger2], }); assert.strictEqual( multiSink.minLogLevel, - LogLevel.default, + LogLevel.info, "Min loglevel should be set correctly", ); @@ -94,7 +94,7 @@ describe("MultiSinkLogger", () => { }); assert.strictEqual( multiSink.minLogLevel, - LogLevel.default, + LogLevel.info, "Min loglevel should be set correctly to default", ); }); @@ -107,14 +107,14 @@ describe("MultiSinkLogger", () => { (multiSink as MultiSinkLogger).addLogger(new MockLogger()); assert.strictEqual( multiSink.minLogLevel, - LogLevel.default, + LogLevel.info, "Min loglevel should be set correctly to default", ); - (multiSink as MultiSinkLogger).addLogger(new MockLogger(LogLevel.default)); + (multiSink as MultiSinkLogger).addLogger(new MockLogger(LogLevel.info)); assert.strictEqual( multiSink.minLogLevel, - LogLevel.default, + LogLevel.info, "Min loglevel should be set correctly to default", ); From 352567421e27859f858e1e3880bab3010e4f7283 Mon Sep 17 00:00:00 2001 From: MarioJGMsoft Date: Mon, 27 Apr 2026 16:10:34 +0000 Subject: [PATCH 02/21] feat: send LogLevel as undefined and default to essential on send logic --- packages/utils/telemetry-utils/src/logger.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/utils/telemetry-utils/src/logger.ts b/packages/utils/telemetry-utils/src/logger.ts index 7ea05780904b..5991014865fd 100644 --- a/packages/utils/telemetry-utils/src/logger.ts +++ b/packages/utils/telemetry-utils/src/logger.ts @@ -230,7 +230,7 @@ export abstract class TelemetryLogger implements TelemetryLoggerExt { public sendTelemetryEvent( event: ITelemetryGenericEventExt, error?: unknown, - logLevel: typeof LogLevel.verbose | typeof LogLevel.info = LogLevel.info, + logLevel?: typeof LogLevel.verbose | typeof LogLevel.info, ): void { this.sendTelemetryEventCore( { ...event, category: event.category ?? "generic" }, @@ -295,7 +295,7 @@ export abstract class TelemetryLogger implements TelemetryLoggerExt { public sendPerformanceEvent( event: ITelemetryPerformanceEventExt, error?: unknown, - logLevel: typeof LogLevel.verbose | typeof LogLevel.info = LogLevel.info, + logLevel?: typeof LogLevel.verbose | typeof LogLevel.info, ): void { const perfEvent = { ...event, @@ -534,7 +534,8 @@ export class ChildLogger extends TelemetryLogger { if (this.shouldFilterOutEvent(event, logLevel)) { return; } - this.baseLogger.send(this.prepareEvent(event), logLevel); + // If no log level was provided, default to essential + this.baseLogger.send(this.prepareEvent(event), logLevel ?? LogLevel.essential); } } From 67614c8c4944280a003be5735de78ebb69b06ee3 Mon Sep 17 00:00:00 2001 From: MarioJGMsoft Date: Wed, 29 Apr 2026 22:36:19 +0000 Subject: [PATCH 03/21] refactor: properly ordered imports and removed LogLevel.default --- .../container-loader/src/test/container.spec.ts | 3 ++- packages/utils/telemetry-utils/src/logger.ts | 11 +++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/loader/container-loader/src/test/container.spec.ts b/packages/loader/container-loader/src/test/container.spec.ts index cdeed1b6c545..f3a154e05391 100644 --- a/packages/loader/container-loader/src/test/container.spec.ts +++ b/packages/loader/container-loader/src/test/container.spec.ts @@ -9,7 +9,8 @@ import { TypedEventEmitter, type IProvideLayerCompatDetails, } from "@fluid-internal/client-utils"; -import { AttachState, type IAudience } from "@fluidframework/container-definitions"; +import type { IAudience } from "@fluidframework/container-definitions"; +import { AttachState } from "@fluidframework/container-definitions"; import type { ICriticalContainerError, IContainer, diff --git a/packages/utils/telemetry-utils/src/logger.ts b/packages/utils/telemetry-utils/src/logger.ts index 1608bb1cdcc5..0d579154a22a 100644 --- a/packages/utils/telemetry-utils/src/logger.ts +++ b/packages/utils/telemetry-utils/src/logger.ts @@ -695,6 +695,7 @@ export class PerformanceEvent { * @param emitLogs - should this instance emit logs. If set to false, logs will not be emitted to the logger, * but measurements will still be performed and any specified markers will be generated. * @param logLevel - optional {@link LogLevel} for events emitted by this performance event. + * If unspecified, {@link @fluidframework/core-interfaces#LogLevelConst.default | LogLevel.default} will be used. * @returns An instance of {@link PerformanceEvent} */ public static start( @@ -702,7 +703,7 @@ export class PerformanceEvent { event: ITelemetryGenericEventExt, markers?: IPerformanceEventMarkers, emitLogs: boolean = true, - logLevel?: typeof LogLevel.verbose | typeof LogLevel.default, + logLevel?: typeof LogLevel.verbose | typeof LogLevel.info, ): PerformanceEvent { return new PerformanceEvent( extractTelemetryLoggerExt(logger), @@ -722,6 +723,7 @@ export class PerformanceEvent { * @param sampleThreshold - events with the same name and category will be sent to the logger * only when we hit this many executions of the task. If unspecified, all events will be sent. * @param logLevel - optional {@link LogLevel} for events emitted by this performance event. + * If unspecified, {@link @fluidframework/core-interfaces#LogLevelConst.info | LogLevel.info} will be used. * @returns The results of the executed task * * @remarks Note that if the "same" event (category + eventName) would be emitted by different @@ -735,7 +737,7 @@ export class PerformanceEvent { callback: (event: PerformanceEvent) => T, markers?: IPerformanceEventMarkers, sampleThreshold: number = 1, - logLevel?: typeof LogLevel.verbose | typeof LogLevel.default, + logLevel?: typeof LogLevel.verbose | typeof LogLevel.info, ): T { const perfEvent = PerformanceEvent.start( logger, @@ -764,6 +766,7 @@ export class PerformanceEvent { * @param sampleThreshold - events with the same name and category will be sent to the logger * only when we hit this many executions of the task. If unspecified, all events will be sent. * @param logLevel - optional {@link LogLevel} for events emitted by this performance event. + * If unspecified, {@link @fluidframework/core-interfaces#LogLevelConst.default | LogLevel.default} will be used. * @returns The results of the executed task * * @remarks Note that if the "same" event (category + eventName) would be emitted by different @@ -777,7 +780,7 @@ export class PerformanceEvent { callback: (event: PerformanceEvent) => Promise, markers?: IPerformanceEventMarkers, sampleThreshold: number = 1, - logLevel?: typeof LogLevel.verbose | typeof LogLevel.default, + logLevel?: typeof LogLevel.verbose | typeof LogLevel.info, ): Promise { const perfEvent = PerformanceEvent.start( logger, @@ -809,7 +812,7 @@ export class PerformanceEvent { event: ITelemetryGenericEventExt, private readonly markers: IPerformanceEventMarkers = { end: true, cancel: "generic" }, private readonly emitLogs: boolean = true, - private readonly logLevel?: typeof LogLevel.verbose | typeof LogLevel.default, + private readonly logLevel?: typeof LogLevel.verbose | typeof LogLevel.info, ) { this.event = { ...event }; if (this.markers.start) { From a568d12a27045c26d52161761c36fdca389044aa Mon Sep 17 00:00:00 2001 From: MarioJGMsoft Date: Wed, 29 Apr 2026 22:39:15 +0000 Subject: [PATCH 04/21] docs: fixed comments --- packages/utils/telemetry-utils/src/logger.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/utils/telemetry-utils/src/logger.ts b/packages/utils/telemetry-utils/src/logger.ts index 0d579154a22a..ff151144c0e4 100644 --- a/packages/utils/telemetry-utils/src/logger.ts +++ b/packages/utils/telemetry-utils/src/logger.ts @@ -695,7 +695,7 @@ export class PerformanceEvent { * @param emitLogs - should this instance emit logs. If set to false, logs will not be emitted to the logger, * but measurements will still be performed and any specified markers will be generated. * @param logLevel - optional {@link LogLevel} for events emitted by this performance event. - * If unspecified, {@link @fluidframework/core-interfaces#LogLevelConst.default | LogLevel.default} will be used. + * If unspecified, {@link @fluidframework/core-interfaces#LogLevelConst.info | LogLevel.info} will be used. * @returns An instance of {@link PerformanceEvent} */ public static start( @@ -766,7 +766,7 @@ export class PerformanceEvent { * @param sampleThreshold - events with the same name and category will be sent to the logger * only when we hit this many executions of the task. If unspecified, all events will be sent. * @param logLevel - optional {@link LogLevel} for events emitted by this performance event. - * If unspecified, {@link @fluidframework/core-interfaces#LogLevelConst.default | LogLevel.default} will be used. + * If unspecified, {@link @fluidframework/core-interfaces#LogLevelConst.info | LogLevel.info} will be used. * @returns The results of the executed task * * @remarks Note that if the "same" event (category + eventName) would be emitted by different From 9a816820ae973cca94d69db3fbe60f93e62ad465 Mon Sep 17 00:00:00 2001 From: MarioJGMsoft Date: Wed, 29 Apr 2026 22:47:13 +0000 Subject: [PATCH 05/21] docs: updated docs --- packages/common/core-interfaces/src/logger.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/common/core-interfaces/src/logger.ts b/packages/common/core-interfaces/src/logger.ts index 33a44a287890..a42bb69911b0 100644 --- a/packages/common/core-interfaces/src/logger.ts +++ b/packages/common/core-interfaces/src/logger.ts @@ -82,12 +82,14 @@ export interface LogLevelConst { /** * Default LogLevel * @deprecated Prefer {@link LogLevelConst.info | LogLevel.info} when selecting a level explicitly to preserve prior treatment. + * @see {@link https://github.com/microsoft/FluidFramework/issues/26969 | Issue #26969} for removal tracking. */ readonly default: 20; /** * To log errors. * @deprecated Prefer {@link LogLevelConst.essential | LogLevel.essential} when selecting a level. + * @see {@link https://github.com/microsoft/FluidFramework/issues/26969 | Issue #26969} for removal tracking. */ readonly error: 30; } From da948a1b26cf7307ebc532b37beebee735154474 Mon Sep 17 00:00:00 2001 From: MarioJGMsoft Date: Wed, 29 Apr 2026 23:24:50 +0000 Subject: [PATCH 06/21] docs: added changesets --- .changeset/salty-colts-appear.md | 15 +++++++++++++++ .changeset/soft-doors-trade.md | 20 ++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 .changeset/salty-colts-appear.md create mode 100644 .changeset/soft-doors-trade.md diff --git a/.changeset/salty-colts-appear.md b/.changeset/salty-colts-appear.md new file mode 100644 index 000000000000..29047d214ebe --- /dev/null +++ b/.changeset/salty-colts-appear.md @@ -0,0 +1,15 @@ +--- +"@fluidframework/core-interfaces": minor +"__section": deprecation +--- + +Deprecated LogLevel.default and LogLevel.error + +`LogLevel.default` and `LogLevel.error` in `@fluidframework/core-interfaces` are deprecated in favor of the semantically clearer `LogLevel.info` and `LogLevel.essential` respectively. + +#### Migration + +- `LogLevel.default` (= `20`) → use `LogLevel.info` +- `LogLevel.error` (= `30`) → use `LogLevel.essential` + +Removal is tracked in [issue #26969](https://github.com/microsoft/FluidFramework/issues/26969) and is planned for the v3.0 major release. diff --git a/.changeset/soft-doors-trade.md b/.changeset/soft-doors-trade.md new file mode 100644 index 000000000000..7c3370f6590f --- /dev/null +++ b/.changeset/soft-doors-trade.md @@ -0,0 +1,20 @@ +--- +"@fluidframework/telemetry-utils": minor +"__section": other +--- + +ChildLogger now tags events with no explicit `logLevel` as `essential` + +`@fluidframework/telemetry-utils` previously forwarded events with no explicit `logLevel` to the base logger as `undefined` (effectively treated as `LogLevel.info` = `20`). It now forwards them as `LogLevel.essential` (= `30`). Specifically: + +- `ChildLogger.shouldFilterOutEvent` now falls back to `LogLevel.essential` when an event has no `logLevel` (previously fell back to `LogLevel.default` = `LogLevel.info` = `20`). +- `ChildLogger.send` now forwards `LogLevel.essential` to the base logger when no `logLevel` is supplied, instead of forwarding `undefined`. +- The implicit `= LogLevel.default` parameter defaults on `TelemetryLogger.sendTelemetryEvent` and `sendPerformanceEvent` were removed; callers that omit `logLevel` now propagate `undefined` through the chain, where `ChildLogger` applies the new `essential` fallback above. + +#### Impact for downstream consumers + +Internal Fluid events that previously arrived at the base logger with `logLevel = undefined` will now arrive tagged as `30` (`essential`). Hosts that filter telemetry by numeric level should not start dropping `logLevel = 20` events as "non-essential" unless **all** running Fluid versions include this change — older versions still send those same critical events at level `20`, and dropping them would lose telemetry. + +#### Events already explicitly tagged as `LogLevel.info` + +Note that this change only affects events emitted with **no** explicit `logLevel`. A set of events that are already explicitly tagged with `LogLevel.info` (= `20`) was introduced in [PR #27126](https://github.com/microsoft/FluidFramework/pull/27126) and is unaffected by this change — those events continue to be emitted at level `20` by design and are the intended target for hosts that want to drop non-essential telemetry once all running Fluid versions include both PR #27126 and this change. From c0d93962b0092c3b93387bfe25de47998bba8065 Mon Sep 17 00:00:00 2001 From: MarioJGMsoft Date: Wed, 29 Apr 2026 23:38:37 +0000 Subject: [PATCH 07/21] docs: added removal timeline --- packages/common/core-interfaces/src/logger.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/common/core-interfaces/src/logger.ts b/packages/common/core-interfaces/src/logger.ts index a42bb69911b0..cde2687216b9 100644 --- a/packages/common/core-interfaces/src/logger.ts +++ b/packages/common/core-interfaces/src/logger.ts @@ -81,14 +81,14 @@ export interface LogLevelConst { /** * Default LogLevel - * @deprecated Prefer {@link LogLevelConst.info | LogLevel.info} when selecting a level explicitly to preserve prior treatment. + * @deprecated 2.101.0. Removed in 3.0.0. Prefer {@link LogLevelConst.info | LogLevel.info} when selecting a level explicitly to preserve prior treatment. * @see {@link https://github.com/microsoft/FluidFramework/issues/26969 | Issue #26969} for removal tracking. */ readonly default: 20; /** * To log errors. - * @deprecated Prefer {@link LogLevelConst.essential | LogLevel.essential} when selecting a level. + * @deprecated 2.101.0. Removed in 3.0.0. Prefer {@link LogLevelConst.essential | LogLevel.essential} when selecting a level. * @see {@link https://github.com/microsoft/FluidFramework/issues/26969 | Issue #26969} for removal tracking. */ readonly error: 30; From f3a92b6010bedaecbc030ca618c74301f524dc80 Mon Sep 17 00:00:00 2001 From: MarioJGMsoft Date: Wed, 29 Apr 2026 23:39:42 +0000 Subject: [PATCH 08/21] docs: updated deprecation message --- packages/common/core-interfaces/src/logger.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/common/core-interfaces/src/logger.ts b/packages/common/core-interfaces/src/logger.ts index cde2687216b9..ab32c3b9a7de 100644 --- a/packages/common/core-interfaces/src/logger.ts +++ b/packages/common/core-interfaces/src/logger.ts @@ -81,14 +81,14 @@ export interface LogLevelConst { /** * Default LogLevel - * @deprecated 2.101.0. Removed in 3.0.0. Prefer {@link LogLevelConst.info | LogLevel.info} when selecting a level explicitly to preserve prior treatment. + * @deprecated Prefer {@link LogLevelConst.info | LogLevel.info} when selecting a level explicitly to preserve prior treatment. Planned to be removed in 3.0.0. * @see {@link https://github.com/microsoft/FluidFramework/issues/26969 | Issue #26969} for removal tracking. */ readonly default: 20; /** * To log errors. - * @deprecated 2.101.0. Removed in 3.0.0. Prefer {@link LogLevelConst.essential | LogLevel.essential} when selecting a level. + * @deprecated Prefer {@link LogLevelConst.essential | LogLevel.essential} when selecting a level. Planned to be removed in 3.0.0. * @see {@link https://github.com/microsoft/FluidFramework/issues/26969 | Issue #26969} for removal tracking. */ readonly error: 30; From 0ba1037734cce7a3a5b7f274e7f962b3a259a372 Mon Sep 17 00:00:00 2001 From: MarioJGMsoft Date: Mon, 4 May 2026 22:04:24 +0000 Subject: [PATCH 09/21] docs: addressed docs comments --- .changeset/soft-doors-trade.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.changeset/soft-doors-trade.md b/.changeset/soft-doors-trade.md index 7c3370f6590f..97d6ed157f96 100644 --- a/.changeset/soft-doors-trade.md +++ b/.changeset/soft-doors-trade.md @@ -5,7 +5,7 @@ ChildLogger now tags events with no explicit `logLevel` as `essential` -`@fluidframework/telemetry-utils` previously forwarded events with no explicit `logLevel` to the base logger as `undefined` (effectively treated as `LogLevel.info` = `20`). It now forwards them as `LogLevel.essential` (= `30`). Specifically: +`@fluidframework/telemetry-utils` previously forwarded events with no explicit `logLevel` to the base logger as `undefined` (treated as `LogLevel.info` = `20`). It now forwards them as `LogLevel.essential` (= `30`). Specifically: - `ChildLogger.shouldFilterOutEvent` now falls back to `LogLevel.essential` when an event has no `logLevel` (previously fell back to `LogLevel.default` = `LogLevel.info` = `20`). - `ChildLogger.send` now forwards `LogLevel.essential` to the base logger when no `logLevel` is supplied, instead of forwarding `undefined`. @@ -13,8 +13,8 @@ ChildLogger now tags events with no explicit `logLevel` as `essential` #### Impact for downstream consumers -Internal Fluid events that previously arrived at the base logger with `logLevel = undefined` will now arrive tagged as `30` (`essential`). Hosts that filter telemetry by numeric level should not start dropping `logLevel = 20` events as "non-essential" unless **all** running Fluid versions include this change — older versions still send those same critical events at level `20`, and dropping them would lose telemetry. +Internal Fluid events that previously arrived at the base logger with `logLevel = undefined` will now arrive tagged as `30` (`essential`). Hosts that filter telemetry by numeric level should not start dropping `logLevel = 20` events as "non-essential" unless **all** running Fluid versions include this change. Older versions still send those same critical events at level `20`, and dropping them would lose telemetry. #### Events already explicitly tagged as `LogLevel.info` -Note that this change only affects events emitted with **no** explicit `logLevel`. A set of events that are already explicitly tagged with `LogLevel.info` (= `20`) was introduced in [PR #27126](https://github.com/microsoft/FluidFramework/pull/27126) and is unaffected by this change — those events continue to be emitted at level `20` by design and are the intended target for hosts that want to drop non-essential telemetry once all running Fluid versions include both PR #27126 and this change. +Note that this change only affects events emitted with **no** explicit `logLevel`. A set of events that are already explicitly tagged with `LogLevel.info` (= `20`) was introduced in [PR #27126](https://github.com/microsoft/FluidFramework/pull/27126) and is unaffected by this change. Those events continue to be emitted at level `20` by design and are the intended target for hosts that want to drop non-essential telemetry once all running Fluid versions include both PR #27126 and this change. From 5f1205d803f9a6460ae1fec39cd7cb001ded4844 Mon Sep 17 00:00:00 2001 From: MarioJGMsoft Date: Tue, 5 May 2026 20:07:40 +0000 Subject: [PATCH 10/21] docs: updated chengeset title --- .changeset/soft-doors-trade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/soft-doors-trade.md b/.changeset/soft-doors-trade.md index 97d6ed157f96..02d2148c7fcc 100644 --- a/.changeset/soft-doors-trade.md +++ b/.changeset/soft-doors-trade.md @@ -3,7 +3,7 @@ "__section": other --- -ChildLogger now tags events with no explicit `logLevel` as `essential` +ChildLogger now tags events with no explicit logLevel as essential `@fluidframework/telemetry-utils` previously forwarded events with no explicit `logLevel` to the base logger as `undefined` (treated as `LogLevel.info` = `20`). It now forwards them as `LogLevel.essential` (= `30`). Specifically: From ba3935869644259374aa92efbce07208121b6776 Mon Sep 17 00:00:00 2001 From: MarioJGMsoft Date: Tue, 5 May 2026 20:17:21 +0000 Subject: [PATCH 11/21] feat: applied changes to docs and code based on comments --- packages/common/core-interfaces/src/logger.ts | 2 +- packages/utils/telemetry-utils/src/logger.ts | 7 ++++--- packages/utils/telemetry-utils/src/telemetryTypes.ts | 4 ++-- .../utils/telemetry-utils/src/test/childLogger.spec.ts | 4 ++++ 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/common/core-interfaces/src/logger.ts b/packages/common/core-interfaces/src/logger.ts index ab32c3b9a7de..aafe1e4a4f2e 100644 --- a/packages/common/core-interfaces/src/logger.ts +++ b/packages/common/core-interfaces/src/logger.ts @@ -122,7 +122,7 @@ export interface ITelemetryBaseLogger { /** * Log a telemetry event, if it meets the appropriate log-level threshold (see {@link ITelemetryBaseLogger.minLogLevel}). * @param event - The event to log. - * @param logLevel - The log level of the event. Default: {@link LogLevelConst.info | LogLevel.info}. + * @param logLevel - The log level of the event. If undefined, the event will be sent with {@link LogLevelConst.essential | LogLevel.essential}. */ send(event: ITelemetryBaseEvent, logLevel?: LogLevel): void; diff --git a/packages/utils/telemetry-utils/src/logger.ts b/packages/utils/telemetry-utils/src/logger.ts index ff151144c0e4..b54b106fd061 100644 --- a/packages/utils/telemetry-utils/src/logger.ts +++ b/packages/utils/telemetry-utils/src/logger.ts @@ -695,7 +695,7 @@ export class PerformanceEvent { * @param emitLogs - should this instance emit logs. If set to false, logs will not be emitted to the logger, * but measurements will still be performed and any specified markers will be generated. * @param logLevel - optional {@link LogLevel} for events emitted by this performance event. - * If unspecified, {@link @fluidframework/core-interfaces#LogLevelConst.info | LogLevel.info} will be used. + * If unspecified, {@link @fluidframework/core-interfaces#LogLevelConst.essential | LogLevel.essential} will be used. * @returns An instance of {@link PerformanceEvent} */ public static start( @@ -723,7 +723,7 @@ export class PerformanceEvent { * @param sampleThreshold - events with the same name and category will be sent to the logger * only when we hit this many executions of the task. If unspecified, all events will be sent. * @param logLevel - optional {@link LogLevel} for events emitted by this performance event. - * If unspecified, {@link @fluidframework/core-interfaces#LogLevelConst.info | LogLevel.info} will be used. + * If unspecified, {@link @fluidframework/core-interfaces#LogLevelConst.essential | LogLevel.essential} will be used. * @returns The results of the executed task * * @remarks Note that if the "same" event (category + eventName) would be emitted by different @@ -766,7 +766,8 @@ export class PerformanceEvent { * @param sampleThreshold - events with the same name and category will be sent to the logger * only when we hit this many executions of the task. If unspecified, all events will be sent. * @param logLevel - optional {@link LogLevel} for events emitted by this performance event. - * If unspecified, {@link @fluidframework/core-interfaces#LogLevelConst.info | LogLevel.info} will be used. + * If unspecified, the `logLevel` property is left unset and the effective default is determined by + * the logger implementation. * @returns The results of the executed task * * @remarks Note that if the "same" event (category + eventName) would be emitted by different diff --git a/packages/utils/telemetry-utils/src/telemetryTypes.ts b/packages/utils/telemetry-utils/src/telemetryTypes.ts index ba79d132373a..b9b0c6ca3ac3 100644 --- a/packages/utils/telemetry-utils/src/telemetryTypes.ts +++ b/packages/utils/telemetry-utils/src/telemetryTypes.ts @@ -183,7 +183,7 @@ export interface TelemetryLoggerExt extends ITelemetryBaseLogger { * Send an information telemetry event. * @param event - Event to send. * @param error - Optional error object to log. - * @param logLevel - Optional level of the log. Default: {@link @fluidframework/core-interfaces#LogLevelConst.info | LogLevel.info}. + * @param logLevel - Optional level of the log. If undefined, the event will be sent with {@link @fluidframework/core-interfaces#LogLevelConst.essential | LogLevel.essential}. */ sendTelemetryEvent( event: ITelemetryGenericEventExt, @@ -202,7 +202,7 @@ export interface TelemetryLoggerExt extends ITelemetryBaseLogger { * Send a performance telemetry event. * @param event - Event to send * @param error - Optional error object to log. - * @param logLevel - Optional level of the log. Default: {@link @fluidframework/core-interfaces#LogLevelConst.info | LogLevel.info}. + * @param logLevel - Optional level of the log. If undefined, the event will be sent with {@link @fluidframework/core-interfaces#LogLevelConst.essential | LogLevel.essential}. */ sendPerformanceEvent( event: ITelemetryPerformanceEventExt, diff --git a/packages/utils/telemetry-utils/src/test/childLogger.spec.ts b/packages/utils/telemetry-utils/src/test/childLogger.spec.ts index 0bb1961bad34..27a7d1413962 100644 --- a/packages/utils/telemetry-utils/src/test/childLogger.spec.ts +++ b/packages/utils/telemetry-utils/src/test/childLogger.spec.ts @@ -204,6 +204,10 @@ describe("ChildLogger", () => { sent = false; childLogger1.send({ category: "generic", eventName: "testEvent" }, LogLevel.info); assert(!sent, "event should not be sent"); + + sent = false; + childLogger1.send({ category: "generic", eventName: "testEvent" }); + assert(sent, "default event should be sent"); }); it("should receive verbose events with min loglevel set as verbose", () => { From bad57744d48d199f43b4070b33ae58e6853856ba Mon Sep 17 00:00:00 2001 From: MarioJGMsoft Date: Wed, 6 May 2026 17:20:26 +0000 Subject: [PATCH 12/21] feat: made logLevel required in various levels and added tests --- .changeset/soft-doors-trade.md | 2 +- packages/utils/telemetry-utils/src/logger.ts | 35 +++-- .../src/test/childLogger.spec.ts | 8 +- .../src/test/multiSinkLogger.spec.ts | 140 +++++++++++++++++- .../telemetry-utils/src/test/utils.spec.ts | 78 +++++++++- packages/utils/telemetry-utils/src/utils.ts | 26 ++-- 6 files changed, 255 insertions(+), 34 deletions(-) diff --git a/.changeset/soft-doors-trade.md b/.changeset/soft-doors-trade.md index 02d2148c7fcc..9ce0479f8a40 100644 --- a/.changeset/soft-doors-trade.md +++ b/.changeset/soft-doors-trade.md @@ -3,7 +3,7 @@ "__section": other --- -ChildLogger now tags events with no explicit logLevel as essential +All internal implementations of ITelemetryLoggerExt now tag events with no explicit logLevel as essential `@fluidframework/telemetry-utils` previously forwarded events with no explicit `logLevel` to the base logger as `undefined` (treated as `LogLevel.info` = `20`). It now forwards them as `LogLevel.essential` (= `30`). Specifically: diff --git a/packages/utils/telemetry-utils/src/logger.ts b/packages/utils/telemetry-utils/src/logger.ts index b54b106fd061..5f77f39c0895 100644 --- a/packages/utils/telemetry-utils/src/logger.ts +++ b/packages/utils/telemetry-utils/src/logger.ts @@ -217,7 +217,7 @@ export abstract class TelemetryLogger implements TelemetryLoggerExt { * * @param event - the event to send */ - public abstract send(event: ITelemetryBaseEvent, logLevel?: LogLevel): void; + public abstract send(event: ITelemetryBaseEvent, logLevel: LogLevel): void; /** * Send a telemetry event with the logger @@ -230,7 +230,10 @@ export abstract class TelemetryLogger implements TelemetryLoggerExt { public sendTelemetryEvent( event: ITelemetryGenericEventExt, error?: unknown, - logLevel?: typeof LogLevel.verbose | typeof LogLevel.info, + logLevel: + | typeof LogLevel.verbose + | typeof LogLevel.info + | typeof LogLevel.essential = LogLevel.essential, ): void { this.sendTelemetryEventCore( { ...event, category: event.category ?? "generic" }, @@ -244,12 +247,12 @@ export abstract class TelemetryLogger implements TelemetryLoggerExt { * * @param event - the event to send * @param error - optional error object to log - * @param logLevel - optional level of the log. + * @param logLevel - level of the log. */ - protected sendTelemetryEventCore( + private sendTelemetryEventCore( event: ITelemetryGenericEventExt & { category: TelemetryEventCategory }, - error?: unknown, - logLevel?: LogLevel, + error: unknown, + logLevel: LogLevel, ): void { const newEvent = convertToBaseEvent(event); if (error !== undefined) { @@ -295,7 +298,10 @@ export abstract class TelemetryLogger implements TelemetryLoggerExt { public sendPerformanceEvent( event: ITelemetryPerformanceEventExt, error?: unknown, - logLevel?: typeof LogLevel.verbose | typeof LogLevel.info, + logLevel: + | typeof LogLevel.verbose + | typeof LogLevel.info + | typeof LogLevel.essential = LogLevel.essential, ): void { const perfEvent = { ...event, @@ -517,9 +523,9 @@ export class ChildLogger extends TelemetryLogger { return this.baseLogger.minLogLevel; } - private shouldFilterOutEvent(event: ITelemetryBaseEvent, logLevel?: LogLevel): boolean { - // Determine the log level of the event. If none is provided, default to essential - const eventLogLevel = logLevel ?? LogLevel.essential; + private shouldFilterOutEvent(event: ITelemetryBaseEvent, logLevel: LogLevel): boolean { + // Determine the log level of the event. The log level is now required, so it will always be provided. + const eventLogLevel = logLevel; const configLogLevel = this.baseLogger.minLogLevel ?? LogLevel.info; // Filter out in case event log level is below what is wanted in config. return eventLogLevel < configLogLevel; @@ -530,12 +536,11 @@ export class ChildLogger extends TelemetryLogger { * * @param event - the event to send */ - public send(event: ITelemetryBaseEvent, logLevel?: LogLevel): void { + public send(event: ITelemetryBaseEvent, logLevel: LogLevel): void { if (this.shouldFilterOutEvent(event, logLevel)) { return; } - // If no log level was provided, default to essential - this.baseLogger.send(this.prepareEvent(event), logLevel ?? LogLevel.essential); + this.baseLogger.send(this.prepareEvent(event), logLevel); } } @@ -655,10 +660,10 @@ export class MultiSinkLogger extends TelemetryLogger { * * @param event - the event to send to all the registered logger */ - public send(event: ITelemetryBaseEvent): void { + public send(event: ITelemetryBaseEvent, logLevel: LogLevel): void { const newEvent = this.prepareEvent(event); for (const logger of this.loggers) { - logger.send(newEvent); + logger.send(newEvent, logLevel); } } } diff --git a/packages/utils/telemetry-utils/src/test/childLogger.spec.ts b/packages/utils/telemetry-utils/src/test/childLogger.spec.ts index 27a7d1413962..19a5e3fb932f 100644 --- a/packages/utils/telemetry-utils/src/test/childLogger.spec.ts +++ b/packages/utils/telemetry-utils/src/test/childLogger.spec.ts @@ -32,7 +32,7 @@ describe("ChildLogger", () => { }, }); - childLogger1.send({ category: "generic", eventName: "test1" }); + childLogger1.send({ category: "generic", eventName: "test1" }, LogLevel.essential); assert(sent, "event should be sent"); sent = false; @@ -65,7 +65,7 @@ describe("ChildLogger", () => { }, }); - childLogger2.send({ category: "generic", eventName: "testEvent" }); + childLogger2.send({ category: "generic", eventName: "testEvent" }, LogLevel.essential); assert(sent, "event should be sent"); }); @@ -94,7 +94,7 @@ describe("ChildLogger", () => { }, }); - childLogger2.send({ category: "generic", eventName: "testEvent" }); + childLogger2.send({ category: "generic", eventName: "testEvent" }, LogLevel.essential); assert(sent, "event should be sent"); }); @@ -123,7 +123,7 @@ describe("ChildLogger", () => { }, }); - childLogger2.send({ category: "generic", eventName: "testEvent" }); + childLogger2.send({ category: "generic", eventName: "testEvent" }, LogLevel.essential); assert(sent, "event should be sent"); }); diff --git a/packages/utils/telemetry-utils/src/test/multiSinkLogger.spec.ts b/packages/utils/telemetry-utils/src/test/multiSinkLogger.spec.ts index 1a899f599d31..11ed26bd2c8e 100644 --- a/packages/utils/telemetry-utils/src/test/multiSinkLogger.spec.ts +++ b/packages/utils/telemetry-utils/src/test/multiSinkLogger.spec.ts @@ -5,11 +5,34 @@ import { strict as assert } from "node:assert"; -import { LogLevel } from "@fluidframework/core-interfaces"; +import { + type ITelemetryBaseEvent, + type ITelemetryBaseLogger, + LogLevel, +} from "@fluidframework/core-interfaces"; import { type MultiSinkLogger, createChildLogger, createMultiSinkLogger } from "../logger.js"; import { MockLogger } from "../mockLogger.js"; +interface RecordedEntry { + event: ITelemetryBaseEvent; + logLevel: LogLevel | undefined; +} + +function createRecordingSink(minLogLevel: LogLevel = LogLevel.verbose): { + sink: ITelemetryBaseLogger; + recorded: RecordedEntry[]; +} { + const recorded: RecordedEntry[] = []; + const sink: ITelemetryBaseLogger = { + send: (event, logLevel): void => { + recorded.push({ event, logLevel }); + }, + minLogLevel, + }; + return { sink, recorded }; +} + describe("MultiSinkLogger", () => { it("Pushes logs to all sinks", () => { const logger1 = new MockLogger(); @@ -125,4 +148,119 @@ describe("MultiSinkLogger", () => { "Min loglevel should be set correctly to verbose", ); }); + + describe("logLevel propagation", () => { + it("Forwards LogLevel.essential to every sink when sendTelemetryEvent omits logLevel", () => { + const { sink: sinkA, recorded: recordedA } = createRecordingSink(); + const { sink: sinkB, recorded: recordedB } = createRecordingSink(); + const multiSink = createMultiSinkLogger({ loggers: [sinkA, sinkB] }); + + multiSink.sendTelemetryEvent({ eventName: "noLogLevel" }); + + assert.strictEqual(recordedA.length, 1); + assert.strictEqual(recordedB.length, 1); + assert.strictEqual(recordedA[0]?.logLevel, LogLevel.essential); + assert.strictEqual(recordedB[0]?.logLevel, LogLevel.essential); + }); + + it("Forwards LogLevel.essential to every sink when sendPerformanceEvent omits logLevel", () => { + const { sink: sinkA, recorded: recordedA } = createRecordingSink(); + const { sink: sinkB, recorded: recordedB } = createRecordingSink(); + const multiSink = createMultiSinkLogger({ loggers: [sinkA, sinkB] }); + + multiSink.sendPerformanceEvent({ eventName: "perfNoLogLevel" }); + + assert.strictEqual(recordedA[0]?.logLevel, LogLevel.essential); + assert.strictEqual(recordedB[0]?.logLevel, LogLevel.essential); + }); + + it("Forwards LogLevel.essential to every sink for sendErrorEvent", () => { + const { sink: sinkA, recorded: recordedA } = createRecordingSink(); + const { sink: sinkB, recorded: recordedB } = createRecordingSink(); + const multiSink = createMultiSinkLogger({ loggers: [sinkA, sinkB] }); + + multiSink.sendErrorEvent({ eventName: "errorEvent" }); + + assert.strictEqual(recordedA[0]?.logLevel, LogLevel.essential); + assert.strictEqual(recordedB[0]?.logLevel, LogLevel.essential); + }); + + for (const explicitLevel of [LogLevel.verbose, LogLevel.info] as const) { + it(`Forwards explicit logLevel (${explicitLevel}) unchanged through MultiSink to every sink via sendTelemetryEvent`, () => { + const { sink: sinkA, recorded: recordedA } = createRecordingSink(LogLevel.verbose); + const { sink: sinkB, recorded: recordedB } = createRecordingSink(LogLevel.verbose); + const multiSink = createMultiSinkLogger({ loggers: [sinkA, sinkB] }); + + multiSink.sendTelemetryEvent({ eventName: "explicit" }, undefined, explicitLevel); + + assert.strictEqual(recordedA[0]?.logLevel, explicitLevel); + assert.strictEqual(recordedB[0]?.logLevel, explicitLevel); + }); + + it(`Forwards explicit logLevel (${explicitLevel}) unchanged through MultiSink via sendPerformanceEvent`, () => { + const { sink: sinkA, recorded: recordedA } = createRecordingSink(LogLevel.verbose); + const { sink: sinkB, recorded: recordedB } = createRecordingSink(LogLevel.verbose); + const multiSink = createMultiSinkLogger({ loggers: [sinkA, sinkB] }); + + multiSink.sendPerformanceEvent( + { eventName: "explicitPerf" }, + undefined, + explicitLevel, + ); + + assert.strictEqual(recordedA[0]?.logLevel, explicitLevel); + assert.strictEqual(recordedB[0]?.logLevel, explicitLevel); + }); + } + + it("Forwards LogLevel.essential through Child -> MultiSink -> sinks (no explicit logLevel)", () => { + const { sink: sinkA, recorded: recordedA } = createRecordingSink(); + const { sink: sinkB, recorded: recordedB } = createRecordingSink(); + const multiSink = createMultiSinkLogger({ loggers: [sinkA, sinkB] }); + const child = createChildLogger({ logger: multiSink }); + + child.sendTelemetryEvent({ eventName: "chainDefault" }); + + assert.strictEqual(recordedA[0]?.logLevel, LogLevel.essential); + assert.strictEqual(recordedB[0]?.logLevel, LogLevel.essential); + }); + + it("Forwards explicit LogLevel.info through Child -> MultiSink -> sinks", () => { + const { sink: sinkA, recorded: recordedA } = createRecordingSink(LogLevel.verbose); + const { sink: sinkB, recorded: recordedB } = createRecordingSink(LogLevel.verbose); + const multiSink = createMultiSinkLogger({ loggers: [sinkA, sinkB] }); + const child = createChildLogger({ logger: multiSink }); + + child.sendTelemetryEvent({ eventName: "chainExplicit" }, undefined, LogLevel.info); + + assert.strictEqual(recordedA[0]?.logLevel, LogLevel.info); + assert.strictEqual(recordedB[0]?.logLevel, LogLevel.info); + }); + + it("Forwards explicit logLevel through MultiSink -> [Child(sinkA), sinkB]", () => { + const { sink: sinkA, recorded: recordedA } = createRecordingSink(LogLevel.verbose); + const { sink: sinkB, recorded: recordedB } = createRecordingSink(LogLevel.verbose); + const multiSink = createMultiSinkLogger({ + loggers: [createChildLogger({ logger: sinkA }), sinkB], + }); + + multiSink.sendTelemetryEvent({ eventName: "mixedChain" }, undefined, LogLevel.verbose); + + assert.strictEqual(recordedA[0]?.logLevel, LogLevel.verbose); + assert.strictEqual(recordedB[0]?.logLevel, LogLevel.verbose); + }); + + it("Regression: MultiSinkLogger.send forwards explicit logLevel to every sink (was previously dropped)", () => { + const { sink: sinkA, recorded: recordedA } = createRecordingSink(LogLevel.verbose); + const { sink: sinkB, recorded: recordedB } = createRecordingSink(LogLevel.verbose); + const multiSink = createMultiSinkLogger({ + loggers: [sinkA, sinkB], + }) as MultiSinkLogger; + + multiSink.send({ category: "generic", eventName: "directSend" }, LogLevel.verbose); + + assert.strictEqual(recordedA[0]?.logLevel, LogLevel.verbose); + assert.strictEqual(recordedB[0]?.logLevel, LogLevel.verbose); + }); + }); }); diff --git a/packages/utils/telemetry-utils/src/test/utils.spec.ts b/packages/utils/telemetry-utils/src/test/utils.spec.ts index a6498e693174..26833904973f 100644 --- a/packages/utils/telemetry-utils/src/test/utils.spec.ts +++ b/packages/utils/telemetry-utils/src/test/utils.spec.ts @@ -5,10 +5,11 @@ import { strict as assert } from "node:assert"; -import type { - ConfigTypes, - IConfigProviderBase, - ITelemetryBaseEvent, +import { + type ConfigTypes, + type IConfigProviderBase, + type ITelemetryBaseEvent, + LogLevel, } from "@fluidframework/core-interfaces"; import type { InternalCoreInterfacesUtilityTypes } from "@fluidframework/core-interfaces/internal"; @@ -360,6 +361,75 @@ describe("Sampling", () => { ); } }); + + describe("logLevel forwarding", () => { + function createCapturingLogger(): { + logger: TelemetryLoggerExt; + captures: { method: string; logLevel: LogLevel | undefined }[]; + } { + const captures: { method: string; logLevel: LogLevel | undefined }[] = []; + const logger: TelemetryLoggerExt = { + send: (_event, logLevel): void => { + captures.push({ method: "send", logLevel }); + }, + sendTelemetryEvent: (_event, _error, logLevel): void => { + captures.push({ method: "sendTelemetryEvent", logLevel }); + }, + sendErrorEvent: (_event, _error): void => { + captures.push({ method: "sendErrorEvent", logLevel: undefined }); + }, + sendPerformanceEvent: (_event, _error, logLevel): void => { + captures.push({ method: "sendPerformanceEvent", logLevel }); + }, + }; + const wrapped = mixinMonitoringContext(logger, { + getRawConfig: (): ConfigTypes => undefined, + }).logger; + return { logger: wrapped, captures }; + } + + it("Forwards explicit logLevel through sendTelemetryEvent to the wrapped logger", () => { + const { logger, captures } = createCapturingLogger(); + const sampled = createSampledLogger(logger); + + sampled.sendTelemetryEvent({ eventName: "x" }, undefined, LogLevel.verbose); + + assert.deepStrictEqual(captures, [ + { method: "sendTelemetryEvent", logLevel: LogLevel.verbose }, + ]); + }); + + it("Forwards explicit logLevel through sendPerformanceEvent to the wrapped logger", () => { + const { logger, captures } = createCapturingLogger(); + const sampled = createSampledLogger(logger); + + sampled.sendPerformanceEvent({ eventName: "x" }, undefined, LogLevel.info); + + assert.deepStrictEqual(captures, [ + { method: "sendPerformanceEvent", logLevel: LogLevel.info }, + ]); + }); + + it("Forwards explicit logLevel through send to the wrapped logger", () => { + const { logger, captures } = createCapturingLogger(); + const sampled = createSampledLogger(logger); + + sampled.send({ category: "generic", eventName: "x" }, LogLevel.verbose); + + assert.deepStrictEqual(captures, [{ method: "send", logLevel: LogLevel.verbose }]); + }); + + it("Forwards undefined logLevel when caller omits it", () => { + const { logger, captures } = createCapturingLogger(); + const sampled = createSampledLogger(logger); + + sampled.sendTelemetryEvent({ eventName: "x" }); + + assert.deepStrictEqual(captures, [ + { method: "sendTelemetryEvent", logLevel: undefined }, + ]); + }); + }); }); describe("tagCodeArtifacts", () => { diff --git a/packages/utils/telemetry-utils/src/utils.ts b/packages/utils/telemetry-utils/src/utils.ts index 7f9c8a188846..3dfb49540d04 100644 --- a/packages/utils/telemetry-utils/src/utils.ts +++ b/packages/utils/telemetry-utils/src/utils.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -import type { ITelemetryBaseEvent } from "@fluidframework/core-interfaces"; +import type { ITelemetryBaseEvent, LogLevel } from "@fluidframework/core-interfaces"; import { loggerToMonitoringContext } from "./config.js"; import type { @@ -66,7 +66,7 @@ export function createSampledLogger( monitoringContext.config.getBoolean("Fluid.Telemetry.DisableSampling") ?? false; const sampledLogger = { - send: (event: ITelemetryBaseEvent): void => { + send: (event: ITelemetryBaseEvent, logLevel?: LogLevel): void => { // The sampler uses the following logic for sending events: // 1. If isSamplingDisabled is true, then this means events should be unsampled. Therefore we send the event without any checks. // 2. If isSamplingDisabled is false, then event should be sampled using the event sampler, if the sampler is not defined just send all events, other use the eventSampler.sample() method. @@ -75,31 +75,39 @@ export function createSampledLogger( if (isSamplingDisabled && (skipLoggingWhenSamplingIsDisabled ?? false)) { return; } - logger.send(event); + logger.send(event, logLevel); } }, - sendTelemetryEvent: (event: ITelemetryGenericEventExt): void => { + sendTelemetryEvent: ( + event: ITelemetryGenericEventExt, + error?: unknown, + logLevel?: typeof LogLevel.verbose | typeof LogLevel.info, + ): void => { if (isSamplingDisabled || eventSampler === undefined || eventSampler.sample()) { if (isSamplingDisabled && (skipLoggingWhenSamplingIsDisabled ?? false)) { return; } - logger.sendTelemetryEvent(event); + logger.sendTelemetryEvent(event, error, logLevel); } }, - sendErrorEvent: (event: ITelemetryGenericEventExt): void => { + sendErrorEvent: (event: ITelemetryGenericEventExt, error?: unknown): void => { if (isSamplingDisabled || eventSampler === undefined || eventSampler.sample()) { if (isSamplingDisabled && (skipLoggingWhenSamplingIsDisabled ?? false)) { return; } - logger.sendErrorEvent(event); + logger.sendErrorEvent(event, error); } }, - sendPerformanceEvent: (event: ITelemetryGenericEventExt): void => { + sendPerformanceEvent: ( + event: ITelemetryGenericEventExt, + error?: unknown, + logLevel?: typeof LogLevel.verbose | typeof LogLevel.info, + ): void => { if (isSamplingDisabled || eventSampler === undefined || eventSampler.sample()) { if (isSamplingDisabled && (skipLoggingWhenSamplingIsDisabled ?? false)) { return; } - logger.sendPerformanceEvent(event); + logger.sendPerformanceEvent(event, error, logLevel); } }, isSamplingDisabled, From 71c1f07a0aca8d8b8faf13f40379f932db60c2ec Mon Sep 17 00:00:00 2001 From: MarioJGMsoft Date: Wed, 6 May 2026 18:28:17 +0000 Subject: [PATCH 13/21] fix: removed type import from normal import --- .../telemetry-utils/src/test/multiSinkLogger.spec.ts | 8 ++++---- packages/utils/telemetry-utils/src/test/utils.spec.ts | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/utils/telemetry-utils/src/test/multiSinkLogger.spec.ts b/packages/utils/telemetry-utils/src/test/multiSinkLogger.spec.ts index 11ed26bd2c8e..2b3bb915bc53 100644 --- a/packages/utils/telemetry-utils/src/test/multiSinkLogger.spec.ts +++ b/packages/utils/telemetry-utils/src/test/multiSinkLogger.spec.ts @@ -5,11 +5,11 @@ import { strict as assert } from "node:assert"; -import { - type ITelemetryBaseEvent, - type ITelemetryBaseLogger, - LogLevel, +import type { + ITelemetryBaseEvent, + ITelemetryBaseLogger, } from "@fluidframework/core-interfaces"; +import { LogLevel } from "@fluidframework/core-interfaces"; import { type MultiSinkLogger, createChildLogger, createMultiSinkLogger } from "../logger.js"; import { MockLogger } from "../mockLogger.js"; diff --git a/packages/utils/telemetry-utils/src/test/utils.spec.ts b/packages/utils/telemetry-utils/src/test/utils.spec.ts index 26833904973f..1c718d4c77ef 100644 --- a/packages/utils/telemetry-utils/src/test/utils.spec.ts +++ b/packages/utils/telemetry-utils/src/test/utils.spec.ts @@ -5,12 +5,12 @@ import { strict as assert } from "node:assert"; -import { - type ConfigTypes, - type IConfigProviderBase, - type ITelemetryBaseEvent, - LogLevel, +import type { + ConfigTypes, + IConfigProviderBase, + ITelemetryBaseEvent, } from "@fluidframework/core-interfaces"; +import { LogLevel } from "@fluidframework/core-interfaces"; import type { InternalCoreInterfacesUtilityTypes } from "@fluidframework/core-interfaces/internal"; import type { ITelemetryLoggerExt as ITelemetryLoggerExtInternal } from "@fluidframework/telemetry-utils/internal"; From 2c1318114b5f57126c66a17082053986982f1dbe Mon Sep 17 00:00:00 2001 From: MarioJGMsoft Date: Wed, 6 May 2026 21:53:03 +0000 Subject: [PATCH 14/21] fix: applied changes based on review --- .bash_profile | 0 .bashrc | 0 .claude/agents | 0 .claude/commands | 0 .gitconfig | 0 .idea | 0 .mcp.json | 0 .profile | 0 .ripgreprc | 0 .zprofile | 0 .zshrc | 0 packages/common/core-interfaces/src/logger.ts | 2 +- packages/utils/telemetry-utils/src/logger.ts | 29 +++++------- .../utils/telemetry-utils/src/mockLogger.ts | 2 +- .../src/test/childLogger.spec.ts | 47 ++++++++++++++++--- .../src/test/multiSinkLogger.spec.ts | 38 +++------------ .../telemetry-utils/src/test/utils.spec.ts | 6 +-- 17 files changed, 62 insertions(+), 62 deletions(-) create mode 100644 .bash_profile create mode 100644 .bashrc create mode 100644 .claude/agents create mode 100644 .claude/commands create mode 100644 .gitconfig create mode 100644 .idea create mode 100644 .mcp.json create mode 100644 .profile create mode 100644 .ripgreprc create mode 100644 .zprofile create mode 100644 .zshrc diff --git a/.bash_profile b/.bash_profile new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/.bashrc b/.bashrc new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/.claude/agents b/.claude/agents new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/.claude/commands b/.claude/commands new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/.gitconfig b/.gitconfig new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/.idea b/.idea new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/.profile b/.profile new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/.ripgreprc b/.ripgreprc new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/.zprofile b/.zprofile new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/.zshrc b/.zshrc new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/common/core-interfaces/src/logger.ts b/packages/common/core-interfaces/src/logger.ts index aafe1e4a4f2e..8c34de839a4a 100644 --- a/packages/common/core-interfaces/src/logger.ts +++ b/packages/common/core-interfaces/src/logger.ts @@ -122,7 +122,7 @@ export interface ITelemetryBaseLogger { /** * Log a telemetry event, if it meets the appropriate log-level threshold (see {@link ITelemetryBaseLogger.minLogLevel}). * @param event - The event to log. - * @param logLevel - The log level of the event. If undefined, the event will be sent with {@link LogLevelConst.essential | LogLevel.essential}. + * @param logLevel - The log level of the event. If undefined, the logLevel should be treated as {@link LogLevelConst.essential | LogLevel.essential}. */ send(event: ITelemetryBaseEvent, logLevel?: LogLevel): void; diff --git a/packages/utils/telemetry-utils/src/logger.ts b/packages/utils/telemetry-utils/src/logger.ts index 5f77f39c0895..7b04b738dada 100644 --- a/packages/utils/telemetry-utils/src/logger.ts +++ b/packages/utils/telemetry-utils/src/logger.ts @@ -217,7 +217,7 @@ export abstract class TelemetryLogger implements TelemetryLoggerExt { * * @param event - the event to send */ - public abstract send(event: ITelemetryBaseEvent, logLevel: LogLevel): void; + public abstract send(event: ITelemetryBaseEvent, logLevel?: LogLevel): void; /** * Send a telemetry event with the logger @@ -230,15 +230,12 @@ export abstract class TelemetryLogger implements TelemetryLoggerExt { public sendTelemetryEvent( event: ITelemetryGenericEventExt, error?: unknown, - logLevel: - | typeof LogLevel.verbose - | typeof LogLevel.info - | typeof LogLevel.essential = LogLevel.essential, + logLevel?: typeof LogLevel.verbose | typeof LogLevel.info, ): void { this.sendTelemetryEventCore( { ...event, category: event.category ?? "generic" }, error, - event.category === "error" ? LogLevel.essential : logLevel, + event.category === "error" ? LogLevel.essential : (logLevel ?? LogLevel.essential), ); } @@ -298,10 +295,7 @@ export abstract class TelemetryLogger implements TelemetryLoggerExt { public sendPerformanceEvent( event: ITelemetryPerformanceEventExt, error?: unknown, - logLevel: - | typeof LogLevel.verbose - | typeof LogLevel.info - | typeof LogLevel.essential = LogLevel.essential, + logLevel?: typeof LogLevel.verbose | typeof LogLevel.info, ): void { const perfEvent = { ...event, @@ -311,7 +305,7 @@ export abstract class TelemetryLogger implements TelemetryLoggerExt { this.sendTelemetryEventCore( perfEvent, error, - perfEvent.category === "error" ? LogLevel.essential : logLevel, + perfEvent.category === "error" ? LogLevel.essential : (logLevel ?? LogLevel.essential), ); } @@ -523,9 +517,8 @@ export class ChildLogger extends TelemetryLogger { return this.baseLogger.minLogLevel; } - private shouldFilterOutEvent(event: ITelemetryBaseEvent, logLevel: LogLevel): boolean { - // Determine the log level of the event. The log level is now required, so it will always be provided. - const eventLogLevel = logLevel; + private shouldFilterOutEvent(event: ITelemetryBaseEvent, logLevel?: LogLevel): boolean { + const eventLogLevel = logLevel ?? LogLevel.essential; const configLogLevel = this.baseLogger.minLogLevel ?? LogLevel.info; // Filter out in case event log level is below what is wanted in config. return eventLogLevel < configLogLevel; @@ -536,11 +529,11 @@ export class ChildLogger extends TelemetryLogger { * * @param event - the event to send */ - public send(event: ITelemetryBaseEvent, logLevel: LogLevel): void { + public send(event: ITelemetryBaseEvent, logLevel?: LogLevel): void { if (this.shouldFilterOutEvent(event, logLevel)) { return; } - this.baseLogger.send(this.prepareEvent(event), logLevel); + this.baseLogger.send(this.prepareEvent(event), logLevel ?? LogLevel.essential); } } @@ -660,10 +653,10 @@ export class MultiSinkLogger extends TelemetryLogger { * * @param event - the event to send to all the registered logger */ - public send(event: ITelemetryBaseEvent, logLevel: LogLevel): void { + public send(event: ITelemetryBaseEvent, logLevel?: LogLevel): void { const newEvent = this.prepareEvent(event); for (const logger of this.loggers) { - logger.send(newEvent, logLevel); + logger.send(newEvent, logLevel ?? LogLevel.essential); } } } diff --git a/packages/utils/telemetry-utils/src/mockLogger.ts b/packages/utils/telemetry-utils/src/mockLogger.ts index a413c9e1b2d0..2df2dea3df27 100644 --- a/packages/utils/telemetry-utils/src/mockLogger.ts +++ b/packages/utils/telemetry-utils/src/mockLogger.ts @@ -59,7 +59,7 @@ export class MockLogger implements ITelemetryBaseLogger { * {@inheritDoc @fluidframework/core-interfaces#ITelemetryBaseLogger.send} */ public send(event: ITelemetryBaseEvent, logLevel?: LogLevel): void { - if ((logLevel ?? LogLevel.info) >= this.minLogLevel) { + if ((logLevel ?? LogLevel.essential) >= this.minLogLevel) { this._events.push(event); } } diff --git a/packages/utils/telemetry-utils/src/test/childLogger.spec.ts b/packages/utils/telemetry-utils/src/test/childLogger.spec.ts index 19a5e3fb932f..6269e5130486 100644 --- a/packages/utils/telemetry-utils/src/test/childLogger.spec.ts +++ b/packages/utils/telemetry-utils/src/test/childLogger.spec.ts @@ -199,15 +199,15 @@ describe("ChildLogger", () => { const childLogger1 = createChildLogger({ logger }); childLogger1.send({ category: "error", eventName: "testEvent" }, LogLevel.essential); - assert(sent, "event should be sent"); + assert(sent, "essential event should be sent"); sent = false; childLogger1.send({ category: "generic", eventName: "testEvent" }, LogLevel.info); - assert(!sent, "event should not be sent"); + assert(!sent, "info event should not be sent"); sent = false; childLogger1.send({ category: "generic", eventName: "testEvent" }); - assert(sent, "default event should be sent"); + assert(sent, "event with undefined logLevel should be sent"); }); it("should receive verbose events with min loglevel set as verbose", () => { @@ -225,11 +225,11 @@ describe("ChildLogger", () => { const childLogger1 = createChildLogger({ logger }); childLogger1.send({ category: "generic", eventName: "testEvent" }, LogLevel.verbose); - assert(sent, "event should be sent"); + assert(sent, "verbose event should be sent"); sent = false; childLogger1.send({ category: "error", eventName: "testEvent" }); - assert(sent, "default event should be sent"); + assert(sent, "event with undefined logLevel should be sent"); }); it("should not receive verbose events with no min loglevel", () => { @@ -245,7 +245,7 @@ describe("ChildLogger", () => { const childLogger1 = createChildLogger({ logger }); childLogger1.send({ category: "error", eventName: "testEvent" }); - assert(sent, "default event should be sent"); + assert(sent, "event with undefined logLevel should be sent"); sent = false; childLogger1.send({ category: "generic", eventName: "testEvent" }, LogLevel.verbose); @@ -274,6 +274,39 @@ describe("ChildLogger", () => { assert(!sent, "verbose event should not be sent"); childLogger1.send({ category: "generic", eventName: "testEvent" }, LogLevel.info); - assert(sent, "default event should be sent"); + assert(sent, "info event should be sent"); + }); + + describe("logLevel forwarding", () => { + function createRecordingSink(): { + sink: ITelemetryBaseLogger; + recorded: { event: ITelemetryBaseEvent; logLevel: LogLevel | undefined }[]; + } { + const recorded: { event: ITelemetryBaseEvent; logLevel: LogLevel | undefined }[] = []; + const sink: ITelemetryBaseLogger = { + send: (event, logLevel): void => { + recorded.push({ event, logLevel }); + }, + }; + return { sink, recorded }; + } + + it("Forwards LogLevel.essential to the sink when `sendTelemetryEvent` omits logLevel", () => { + const { sink, recorded } = createRecordingSink(); + const child = createChildLogger({ logger: sink }); + + child.sendTelemetryEvent({ eventName: "chainDefault" }); + + assert.strictEqual(recorded[0]?.logLevel, LogLevel.essential); + }); + + it("Forwards explicit LogLevel.info to the sink via `sendTelemetryEvent`", () => { + const { sink, recorded } = createRecordingSink(); + const child = createChildLogger({ logger: sink }); + + child.sendTelemetryEvent({ eventName: "chainExplicit" }, undefined, LogLevel.info); + + assert.strictEqual(recorded[0]?.logLevel, LogLevel.info); + }); }); }); diff --git a/packages/utils/telemetry-utils/src/test/multiSinkLogger.spec.ts b/packages/utils/telemetry-utils/src/test/multiSinkLogger.spec.ts index 2b3bb915bc53..12a951a33343 100644 --- a/packages/utils/telemetry-utils/src/test/multiSinkLogger.spec.ts +++ b/packages/utils/telemetry-utils/src/test/multiSinkLogger.spec.ts @@ -150,20 +150,18 @@ describe("MultiSinkLogger", () => { }); describe("logLevel propagation", () => { - it("Forwards LogLevel.essential to every sink when sendTelemetryEvent omits logLevel", () => { + it("Forwards LogLevel.essential to every sink when `sendTelemetryEvent` omits logLevel", () => { const { sink: sinkA, recorded: recordedA } = createRecordingSink(); const { sink: sinkB, recorded: recordedB } = createRecordingSink(); const multiSink = createMultiSinkLogger({ loggers: [sinkA, sinkB] }); multiSink.sendTelemetryEvent({ eventName: "noLogLevel" }); - assert.strictEqual(recordedA.length, 1); - assert.strictEqual(recordedB.length, 1); assert.strictEqual(recordedA[0]?.logLevel, LogLevel.essential); assert.strictEqual(recordedB[0]?.logLevel, LogLevel.essential); }); - it("Forwards LogLevel.essential to every sink when sendPerformanceEvent omits logLevel", () => { + it("Forwards LogLevel.essential to every sink when `sendPerformanceEvent` omits logLevel", () => { const { sink: sinkA, recorded: recordedA } = createRecordingSink(); const { sink: sinkB, recorded: recordedB } = createRecordingSink(); const multiSink = createMultiSinkLogger({ loggers: [sinkA, sinkB] }); @@ -174,7 +172,7 @@ describe("MultiSinkLogger", () => { assert.strictEqual(recordedB[0]?.logLevel, LogLevel.essential); }); - it("Forwards LogLevel.essential to every sink for sendErrorEvent", () => { + it("Forwards LogLevel.essential to every sink for `sendErrorEvent`", () => { const { sink: sinkA, recorded: recordedA } = createRecordingSink(); const { sink: sinkB, recorded: recordedB } = createRecordingSink(); const multiSink = createMultiSinkLogger({ loggers: [sinkA, sinkB] }); @@ -186,7 +184,7 @@ describe("MultiSinkLogger", () => { }); for (const explicitLevel of [LogLevel.verbose, LogLevel.info] as const) { - it(`Forwards explicit logLevel (${explicitLevel}) unchanged through MultiSink to every sink via sendTelemetryEvent`, () => { + it(`Forwards explicit logLevel (${explicitLevel}) unchanged through MultiSink to every sink via \`sendTelemetryEvent\``, () => { const { sink: sinkA, recorded: recordedA } = createRecordingSink(LogLevel.verbose); const { sink: sinkB, recorded: recordedB } = createRecordingSink(LogLevel.verbose); const multiSink = createMultiSinkLogger({ loggers: [sinkA, sinkB] }); @@ -197,7 +195,7 @@ describe("MultiSinkLogger", () => { assert.strictEqual(recordedB[0]?.logLevel, explicitLevel); }); - it(`Forwards explicit logLevel (${explicitLevel}) unchanged through MultiSink via sendPerformanceEvent`, () => { + it(`Forwards explicit logLevel (${explicitLevel}) unchanged through MultiSink via \`sendPerformanceEvent\``, () => { const { sink: sinkA, recorded: recordedA } = createRecordingSink(LogLevel.verbose); const { sink: sinkB, recorded: recordedB } = createRecordingSink(LogLevel.verbose); const multiSink = createMultiSinkLogger({ loggers: [sinkA, sinkB] }); @@ -213,30 +211,6 @@ describe("MultiSinkLogger", () => { }); } - it("Forwards LogLevel.essential through Child -> MultiSink -> sinks (no explicit logLevel)", () => { - const { sink: sinkA, recorded: recordedA } = createRecordingSink(); - const { sink: sinkB, recorded: recordedB } = createRecordingSink(); - const multiSink = createMultiSinkLogger({ loggers: [sinkA, sinkB] }); - const child = createChildLogger({ logger: multiSink }); - - child.sendTelemetryEvent({ eventName: "chainDefault" }); - - assert.strictEqual(recordedA[0]?.logLevel, LogLevel.essential); - assert.strictEqual(recordedB[0]?.logLevel, LogLevel.essential); - }); - - it("Forwards explicit LogLevel.info through Child -> MultiSink -> sinks", () => { - const { sink: sinkA, recorded: recordedA } = createRecordingSink(LogLevel.verbose); - const { sink: sinkB, recorded: recordedB } = createRecordingSink(LogLevel.verbose); - const multiSink = createMultiSinkLogger({ loggers: [sinkA, sinkB] }); - const child = createChildLogger({ logger: multiSink }); - - child.sendTelemetryEvent({ eventName: "chainExplicit" }, undefined, LogLevel.info); - - assert.strictEqual(recordedA[0]?.logLevel, LogLevel.info); - assert.strictEqual(recordedB[0]?.logLevel, LogLevel.info); - }); - it("Forwards explicit logLevel through MultiSink -> [Child(sinkA), sinkB]", () => { const { sink: sinkA, recorded: recordedA } = createRecordingSink(LogLevel.verbose); const { sink: sinkB, recorded: recordedB } = createRecordingSink(LogLevel.verbose); @@ -250,7 +224,7 @@ describe("MultiSinkLogger", () => { assert.strictEqual(recordedB[0]?.logLevel, LogLevel.verbose); }); - it("Regression: MultiSinkLogger.send forwards explicit logLevel to every sink (was previously dropped)", () => { + it("Regression: `MultiSinkLogger.send` forwards explicit logLevel to every sink (was previously dropped)", () => { const { sink: sinkA, recorded: recordedA } = createRecordingSink(LogLevel.verbose); const { sink: sinkB, recorded: recordedB } = createRecordingSink(LogLevel.verbose); const multiSink = createMultiSinkLogger({ diff --git a/packages/utils/telemetry-utils/src/test/utils.spec.ts b/packages/utils/telemetry-utils/src/test/utils.spec.ts index 1c718d4c77ef..af1d3e3f43f9 100644 --- a/packages/utils/telemetry-utils/src/test/utils.spec.ts +++ b/packages/utils/telemetry-utils/src/test/utils.spec.ts @@ -388,7 +388,7 @@ describe("Sampling", () => { return { logger: wrapped, captures }; } - it("Forwards explicit logLevel through sendTelemetryEvent to the wrapped logger", () => { + it("Forwards explicit logLevel through `sendTelemetryEvent` to the wrapped logger", () => { const { logger, captures } = createCapturingLogger(); const sampled = createSampledLogger(logger); @@ -399,7 +399,7 @@ describe("Sampling", () => { ]); }); - it("Forwards explicit logLevel through sendPerformanceEvent to the wrapped logger", () => { + it("Forwards explicit logLevel through `sendPerformanceEvent` to the wrapped logger", () => { const { logger, captures } = createCapturingLogger(); const sampled = createSampledLogger(logger); @@ -410,7 +410,7 @@ describe("Sampling", () => { ]); }); - it("Forwards explicit logLevel through send to the wrapped logger", () => { + it("Forwards explicit logLevel through `send` to the wrapped logger", () => { const { logger, captures } = createCapturingLogger(); const sampled = createSampledLogger(logger); From 3b3f5fe4ae1fa94889d97fe1ad0e0d23e292103e Mon Sep 17 00:00:00 2001 From: MarioJGMsoft Date: Wed, 6 May 2026 22:00:58 +0000 Subject: [PATCH 15/21] docs: updated docs --- packages/utils/telemetry-utils/src/telemetryTypes.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/utils/telemetry-utils/src/telemetryTypes.ts b/packages/utils/telemetry-utils/src/telemetryTypes.ts index b9b0c6ca3ac3..886ddafc0199 100644 --- a/packages/utils/telemetry-utils/src/telemetryTypes.ts +++ b/packages/utils/telemetry-utils/src/telemetryTypes.ts @@ -136,7 +136,7 @@ export interface ITelemetryLoggerExt extends ITelemetryBaseLogger { * Send an information telemetry event. * @param event - Event to send. * @param error - Optional error object to log. - * @param logLevel - Optional level of the log. Default: {@link @fluidframework/core-interfaces#LogLevel.essential}. + * @param logLevel - Optional level of the log. If undefined, the event should be treated as {@link @fluidframework/core-interfaces#LogLevel.essential}. * @deprecated This method is being removed without a replacement. * @see {@link https://github.com/microsoft/FluidFramework/issues/26910 | Issue #26910} for details. */ @@ -159,7 +159,7 @@ export interface ITelemetryLoggerExt extends ITelemetryBaseLogger { * Send a performance telemetry event. * @param event - Event to send * @param error - Optional error object to log. - * @param logLevel - Optional level of the log. Default: {@link @fluidframework/core-interfaces#LogLevel.essential}. + * @param logLevel - Optional level of the log. If undefined, the event should be treated as {@link @fluidframework/core-interfaces#LogLevel.essential}. * @deprecated This method is being removed without a replacement. * @see {@link https://github.com/microsoft/FluidFramework/issues/26910 | Issue #26910} for details. */ @@ -183,7 +183,7 @@ export interface TelemetryLoggerExt extends ITelemetryBaseLogger { * Send an information telemetry event. * @param event - Event to send. * @param error - Optional error object to log. - * @param logLevel - Optional level of the log. If undefined, the event will be sent with {@link @fluidframework/core-interfaces#LogLevelConst.essential | LogLevel.essential}. + * @param logLevel - Optional level of the log. If undefined, the event should be treated as {@link @fluidframework/core-interfaces#LogLevelConst.essential | LogLevel.essential}. */ sendTelemetryEvent( event: ITelemetryGenericEventExt, @@ -202,7 +202,7 @@ export interface TelemetryLoggerExt extends ITelemetryBaseLogger { * Send a performance telemetry event. * @param event - Event to send * @param error - Optional error object to log. - * @param logLevel - Optional level of the log. If undefined, the event will be sent with {@link @fluidframework/core-interfaces#LogLevelConst.essential | LogLevel.essential}. + * @param logLevel - Optional level of the log. If undefined, the event should be treated as {@link @fluidframework/core-interfaces#LogLevelConst.essential | LogLevel.essential}. */ sendPerformanceEvent( event: ITelemetryPerformanceEventExt, From fbd560a7690f8bb9a24ff8ae92d676ddef9ac3e2 Mon Sep 17 00:00:00 2001 From: MarioJGMsoft Date: Wed, 6 May 2026 22:02:42 +0000 Subject: [PATCH 16/21] docs: removed changeset --- .changeset/soft-doors-trade.md | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 .changeset/soft-doors-trade.md diff --git a/.changeset/soft-doors-trade.md b/.changeset/soft-doors-trade.md deleted file mode 100644 index 9ce0479f8a40..000000000000 --- a/.changeset/soft-doors-trade.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -"@fluidframework/telemetry-utils": minor -"__section": other ---- - -All internal implementations of ITelemetryLoggerExt now tag events with no explicit logLevel as essential - -`@fluidframework/telemetry-utils` previously forwarded events with no explicit `logLevel` to the base logger as `undefined` (treated as `LogLevel.info` = `20`). It now forwards them as `LogLevel.essential` (= `30`). Specifically: - -- `ChildLogger.shouldFilterOutEvent` now falls back to `LogLevel.essential` when an event has no `logLevel` (previously fell back to `LogLevel.default` = `LogLevel.info` = `20`). -- `ChildLogger.send` now forwards `LogLevel.essential` to the base logger when no `logLevel` is supplied, instead of forwarding `undefined`. -- The implicit `= LogLevel.default` parameter defaults on `TelemetryLogger.sendTelemetryEvent` and `sendPerformanceEvent` were removed; callers that omit `logLevel` now propagate `undefined` through the chain, where `ChildLogger` applies the new `essential` fallback above. - -#### Impact for downstream consumers - -Internal Fluid events that previously arrived at the base logger with `logLevel = undefined` will now arrive tagged as `30` (`essential`). Hosts that filter telemetry by numeric level should not start dropping `logLevel = 20` events as "non-essential" unless **all** running Fluid versions include this change. Older versions still send those same critical events at level `20`, and dropping them would lose telemetry. - -#### Events already explicitly tagged as `LogLevel.info` - -Note that this change only affects events emitted with **no** explicit `logLevel`. A set of events that are already explicitly tagged with `LogLevel.info` (= `20`) was introduced in [PR #27126](https://github.com/microsoft/FluidFramework/pull/27126) and is unaffected by this change. Those events continue to be emitted at level `20` by design and are the intended target for hosts that want to drop non-essential telemetry once all running Fluid versions include both PR #27126 and this change. From 5d5fcb165cba310b76329837423451144a507c78 Mon Sep 17 00:00:00 2001 From: MarioJGMsoft Date: Wed, 6 May 2026 22:05:23 +0000 Subject: [PATCH 17/21] docs: updated performance docs --- packages/utils/telemetry-utils/src/logger.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/utils/telemetry-utils/src/logger.ts b/packages/utils/telemetry-utils/src/logger.ts index 7b04b738dada..7028911c5c25 100644 --- a/packages/utils/telemetry-utils/src/logger.ts +++ b/packages/utils/telemetry-utils/src/logger.ts @@ -764,8 +764,7 @@ export class PerformanceEvent { * @param sampleThreshold - events with the same name and category will be sent to the logger * only when we hit this many executions of the task. If unspecified, all events will be sent. * @param logLevel - optional {@link LogLevel} for events emitted by this performance event. - * If unspecified, the `logLevel` property is left unset and the effective default is determined by - * the logger implementation. + * If unspecified, {@link @fluidframework/core-interfaces#LogLevelConst.essential | LogLevel.essential} will be used. * @returns The results of the executed task * * @remarks Note that if the "same" event (category + eventName) would be emitted by different From bee89e10e851faf2be7ade3419ff3ea0519b41a3 Mon Sep 17 00:00:00 2001 From: MarioJGMsoft Date: Wed, 6 May 2026 22:12:17 +0000 Subject: [PATCH 18/21] chore: untrack dev-container bind-mounted files accidentally added Removes 11 zero-byte files added by mistake in 2c1318114b. These are bind-mounted from the dev container host (shell rc files, IDE config, MCP config) and don't belong in the repo. --- .bash_profile | 0 .bashrc | 0 .claude/agents | 0 .claude/commands | 0 .gitconfig | 0 .idea | 0 .mcp.json | 0 .profile | 0 .ripgreprc | 0 .zprofile | 0 .zshrc | 0 11 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .bash_profile delete mode 100644 .bashrc delete mode 100644 .claude/agents delete mode 100644 .claude/commands delete mode 100644 .gitconfig delete mode 100644 .idea delete mode 100644 .mcp.json delete mode 100644 .profile delete mode 100644 .ripgreprc delete mode 100644 .zprofile delete mode 100644 .zshrc diff --git a/.bash_profile b/.bash_profile deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/.bashrc b/.bashrc deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/.claude/agents b/.claude/agents deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/.claude/commands b/.claude/commands deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/.gitconfig b/.gitconfig deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/.idea b/.idea deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/.mcp.json b/.mcp.json deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/.profile b/.profile deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/.ripgreprc b/.ripgreprc deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/.zprofile b/.zprofile deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/.zshrc b/.zshrc deleted file mode 100644 index e69de29bb2d1..000000000000 From 1131b6208569d53ce8ed377602bdc9ce1bc65b10 Mon Sep 17 00:00:00 2001 From: MarioJGMsoft Date: Wed, 6 May 2026 22:14:42 +0000 Subject: [PATCH 19/21] docs: updated docs message --- packages/utils/telemetry-utils/src/telemetryTypes.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/utils/telemetry-utils/src/telemetryTypes.ts b/packages/utils/telemetry-utils/src/telemetryTypes.ts index 886ddafc0199..500c69176c54 100644 --- a/packages/utils/telemetry-utils/src/telemetryTypes.ts +++ b/packages/utils/telemetry-utils/src/telemetryTypes.ts @@ -136,7 +136,7 @@ export interface ITelemetryLoggerExt extends ITelemetryBaseLogger { * Send an information telemetry event. * @param event - Event to send. * @param error - Optional error object to log. - * @param logLevel - Optional level of the log. If undefined, the event should be treated as {@link @fluidframework/core-interfaces#LogLevel.essential}. + * @param logLevel - Optional level of the log. If undefined, the logLevel should be treated as {@link @fluidframework/core-interfaces#LogLevel.essential}. * @deprecated This method is being removed without a replacement. * @see {@link https://github.com/microsoft/FluidFramework/issues/26910 | Issue #26910} for details. */ @@ -159,7 +159,7 @@ export interface ITelemetryLoggerExt extends ITelemetryBaseLogger { * Send a performance telemetry event. * @param event - Event to send * @param error - Optional error object to log. - * @param logLevel - Optional level of the log. If undefined, the event should be treated as {@link @fluidframework/core-interfaces#LogLevel.essential}. + * @param logLevel - Optional level of the log. If undefined, the logLevel should be treated as {@link @fluidframework/core-interfaces#LogLevel.essential}. * @deprecated This method is being removed without a replacement. * @see {@link https://github.com/microsoft/FluidFramework/issues/26910 | Issue #26910} for details. */ @@ -183,7 +183,7 @@ export interface TelemetryLoggerExt extends ITelemetryBaseLogger { * Send an information telemetry event. * @param event - Event to send. * @param error - Optional error object to log. - * @param logLevel - Optional level of the log. If undefined, the event should be treated as {@link @fluidframework/core-interfaces#LogLevelConst.essential | LogLevel.essential}. + * @param logLevel - Optional level of the log. If undefined, the logLevel should be treated as {@link @fluidframework/core-interfaces#LogLevelConst.essential | LogLevel.essential}. */ sendTelemetryEvent( event: ITelemetryGenericEventExt, @@ -202,7 +202,7 @@ export interface TelemetryLoggerExt extends ITelemetryBaseLogger { * Send a performance telemetry event. * @param event - Event to send * @param error - Optional error object to log. - * @param logLevel - Optional level of the log. If undefined, the event should be treated as {@link @fluidframework/core-interfaces#LogLevelConst.essential | LogLevel.essential}. + * @param logLevel - Optional level of the log. If undefined, the logLevel should be treated as {@link @fluidframework/core-interfaces#LogLevelConst.essential | LogLevel.essential}. */ sendPerformanceEvent( event: ITelemetryPerformanceEventExt, From 1205a20df35f74221c0695c8563c0218339076b4 Mon Sep 17 00:00:00 2001 From: MarioJGMsoft Date: Thu, 7 May 2026 22:07:45 +0000 Subject: [PATCH 20/21] feat: applied changes based on comments --- .changeset/salty-colts-appear.md | 10 ++-- packages/utils/telemetry-utils/src/logger.ts | 12 ++-- .../telemetry-utils/src/telemetryTypes.ts | 8 ++- .../src/test/childLogger.spec.ts | 41 ++----------- .../src/test/telemetryLogger.spec.ts | 57 ++++++++++++++++++- 5 files changed, 78 insertions(+), 50 deletions(-) diff --git a/.changeset/salty-colts-appear.md b/.changeset/salty-colts-appear.md index 29047d214ebe..9abf1432cb57 100644 --- a/.changeset/salty-colts-appear.md +++ b/.changeset/salty-colts-appear.md @@ -5,11 +5,13 @@ Deprecated LogLevel.default and LogLevel.error -`LogLevel.default` and `LogLevel.error` in `@fluidframework/core-interfaces` are deprecated in favor of the semantically clearer `LogLevel.info` and `LogLevel.essential` respectively. +`LogLevel.default` and `LogLevel.error` in `@fluidframework/core-interfaces` are deprecated in favor of the semantically clearer `LogLevel.info` and `LogLevel.essential`. #### Migration -- `LogLevel.default` (= `20`) → use `LogLevel.info` -- `LogLevel.error` (= `30`) → use `LogLevel.essential` +The recommended replacement depends on how the value is used: -Removal is tracked in [issue #26969](https://github.com/microsoft/FluidFramework/issues/26969) and is planned for the v3.0 major release. +- For an **event's `logLevel`** (e.g. the `logLevel` argument to `sendTelemetryEvent` / `sendPerformanceEvent` / `ITelemetryBaseLogger.send`), the recommendation is `LogLevel.essential`. +- For a logger's **`minLogLevel`** (the threshold that filters events), `LogLevel.info` is the recommendation. + +See [issue #26969](https://github.com/microsoft/FluidFramework/issues/26969) for full guidance and removal tracking (planned for v3.0). diff --git a/packages/utils/telemetry-utils/src/logger.ts b/packages/utils/telemetry-utils/src/logger.ts index 7028911c5c25..5abf73a0239f 100644 --- a/packages/utils/telemetry-utils/src/logger.ts +++ b/packages/utils/telemetry-utils/src/logger.ts @@ -224,8 +224,8 @@ export abstract class TelemetryLogger implements TelemetryLoggerExt { * * @param event - the event to send * @param error - optional error object to log - * @param logLevel - optional level of the log. It category of event is set as error, - * then the logLevel will be upgraded to be an error. + * @param logLevel - optional level of the log. If the event's category is `error`, + * the logLevel will be upgraded to {@link @fluidframework/core-interfaces#LogLevelConst.essential | LogLevel.essential}. */ public sendTelemetryEvent( event: ITelemetryGenericEventExt, @@ -289,8 +289,8 @@ export abstract class TelemetryLogger implements TelemetryLoggerExt { * * @param event - Event to send * @param error - optional error object to log - * @param logLevel - optional level of the log. It category of event is set as error, - * then the logLevel will be upgraded to be an error. + * @param logLevel - optional level of the log. If the event's category is `error`, + * the logLevel will be upgraded to {@link @fluidframework/core-interfaces#LogLevelConst.essential | LogLevel.essential}. */ public sendPerformanceEvent( event: ITelemetryPerformanceEventExt, @@ -805,12 +805,12 @@ export class PerformanceEvent { private readonly startTime = performanceNow(); private startMark?: string; - protected constructor( + private constructor( private readonly logger: TelemetryLoggerExt, event: ITelemetryGenericEventExt, private readonly markers: IPerformanceEventMarkers = { end: true, cancel: "generic" }, private readonly emitLogs: boolean = true, - private readonly logLevel?: typeof LogLevel.verbose | typeof LogLevel.info, + private readonly logLevel: typeof LogLevel.verbose | typeof LogLevel.info | undefined, ) { this.event = { ...event }; if (this.markers.start) { diff --git a/packages/utils/telemetry-utils/src/telemetryTypes.ts b/packages/utils/telemetry-utils/src/telemetryTypes.ts index 500c69176c54..b444a4a64e44 100644 --- a/packages/utils/telemetry-utils/src/telemetryTypes.ts +++ b/packages/utils/telemetry-utils/src/telemetryTypes.ts @@ -137,6 +137,7 @@ export interface ITelemetryLoggerExt extends ITelemetryBaseLogger { * @param event - Event to send. * @param error - Optional error object to log. * @param logLevel - Optional level of the log. If undefined, the logLevel should be treated as {@link @fluidframework/core-interfaces#LogLevel.essential}. + * If the event's category is `error`, the logLevel will be upgraded to {@link @fluidframework/core-interfaces#LogLevel.essential}. * @deprecated This method is being removed without a replacement. * @see {@link https://github.com/microsoft/FluidFramework/issues/26910 | Issue #26910} for details. */ @@ -160,6 +161,7 @@ export interface ITelemetryLoggerExt extends ITelemetryBaseLogger { * @param event - Event to send * @param error - Optional error object to log. * @param logLevel - Optional level of the log. If undefined, the logLevel should be treated as {@link @fluidframework/core-interfaces#LogLevel.essential}. + * If the event's category is `error`, the logLevel will be upgraded to {@link @fluidframework/core-interfaces#LogLevel.essential}. * @deprecated This method is being removed without a replacement. * @see {@link https://github.com/microsoft/FluidFramework/issues/26910 | Issue #26910} for details. */ @@ -183,7 +185,8 @@ export interface TelemetryLoggerExt extends ITelemetryBaseLogger { * Send an information telemetry event. * @param event - Event to send. * @param error - Optional error object to log. - * @param logLevel - Optional level of the log. If undefined, the logLevel should be treated as {@link @fluidframework/core-interfaces#LogLevelConst.essential | LogLevel.essential}. + * @param logLevel - Optional level of the log. If undefined, the logLevel will be treated as {@link @fluidframework/core-interfaces#LogLevelConst.essential | LogLevel.essential}. + * If the event's category is `error`, the logLevel will be upgraded to {@link @fluidframework/core-interfaces#LogLevelConst.essential | LogLevel.essential}. */ sendTelemetryEvent( event: ITelemetryGenericEventExt, @@ -202,7 +205,8 @@ export interface TelemetryLoggerExt extends ITelemetryBaseLogger { * Send a performance telemetry event. * @param event - Event to send * @param error - Optional error object to log. - * @param logLevel - Optional level of the log. If undefined, the logLevel should be treated as {@link @fluidframework/core-interfaces#LogLevelConst.essential | LogLevel.essential}. + * @param logLevel - Optional level of the log. If undefined, the logLevel will be treated as {@link @fluidframework/core-interfaces#LogLevelConst.essential | LogLevel.essential}. + * If the event's category is `error`, the logLevel will be upgraded to {@link @fluidframework/core-interfaces#LogLevelConst.essential | LogLevel.essential}. */ sendPerformanceEvent( event: ITelemetryPerformanceEventExt, diff --git a/packages/utils/telemetry-utils/src/test/childLogger.spec.ts b/packages/utils/telemetry-utils/src/test/childLogger.spec.ts index 6269e5130486..9a713bc16e64 100644 --- a/packages/utils/telemetry-utils/src/test/childLogger.spec.ts +++ b/packages/utils/telemetry-utils/src/test/childLogger.spec.ts @@ -32,7 +32,7 @@ describe("ChildLogger", () => { }, }); - childLogger1.send({ category: "generic", eventName: "test1" }, LogLevel.essential); + childLogger1.send({ category: "generic", eventName: "test1" }); assert(sent, "event should be sent"); sent = false; @@ -65,7 +65,7 @@ describe("ChildLogger", () => { }, }); - childLogger2.send({ category: "generic", eventName: "testEvent" }, LogLevel.essential); + childLogger2.send({ category: "generic", eventName: "testEvent" }); assert(sent, "event should be sent"); }); @@ -94,7 +94,7 @@ describe("ChildLogger", () => { }, }); - childLogger2.send({ category: "generic", eventName: "testEvent" }, LogLevel.essential); + childLogger2.send({ category: "generic", eventName: "testEvent" }); assert(sent, "event should be sent"); }); @@ -123,7 +123,7 @@ describe("ChildLogger", () => { }, }); - childLogger2.send({ category: "generic", eventName: "testEvent" }, LogLevel.essential); + childLogger2.send({ category: "generic", eventName: "testEvent" }); assert(sent, "event should be sent"); }); @@ -276,37 +276,4 @@ describe("ChildLogger", () => { childLogger1.send({ category: "generic", eventName: "testEvent" }, LogLevel.info); assert(sent, "info event should be sent"); }); - - describe("logLevel forwarding", () => { - function createRecordingSink(): { - sink: ITelemetryBaseLogger; - recorded: { event: ITelemetryBaseEvent; logLevel: LogLevel | undefined }[]; - } { - const recorded: { event: ITelemetryBaseEvent; logLevel: LogLevel | undefined }[] = []; - const sink: ITelemetryBaseLogger = { - send: (event, logLevel): void => { - recorded.push({ event, logLevel }); - }, - }; - return { sink, recorded }; - } - - it("Forwards LogLevel.essential to the sink when `sendTelemetryEvent` omits logLevel", () => { - const { sink, recorded } = createRecordingSink(); - const child = createChildLogger({ logger: sink }); - - child.sendTelemetryEvent({ eventName: "chainDefault" }); - - assert.strictEqual(recorded[0]?.logLevel, LogLevel.essential); - }); - - it("Forwards explicit LogLevel.info to the sink via `sendTelemetryEvent`", () => { - const { sink, recorded } = createRecordingSink(); - const child = createChildLogger({ logger: sink }); - - child.sendTelemetryEvent({ eventName: "chainExplicit" }, undefined, LogLevel.info); - - assert.strictEqual(recorded[0]?.logLevel, LogLevel.info); - }); - }); }); diff --git a/packages/utils/telemetry-utils/src/test/telemetryLogger.spec.ts b/packages/utils/telemetry-utils/src/test/telemetryLogger.spec.ts index 01041d727b26..584ff6a9c687 100644 --- a/packages/utils/telemetry-utils/src/test/telemetryLogger.spec.ts +++ b/packages/utils/telemetry-utils/src/test/telemetryLogger.spec.ts @@ -6,6 +6,7 @@ import { strict as assert } from "node:assert"; import type { ITelemetryBaseEvent, Tagged } from "@fluidframework/core-interfaces"; +import { LogLevel } from "@fluidframework/core-interfaces"; import { type ITelemetryLoggerPropertyBag, @@ -17,8 +18,10 @@ import type { TelemetryEventPropertyTypeExt } from "../telemetryTypes.js"; class TestTelemetryLogger extends TelemetryLogger { public events: ITelemetryBaseEvent[] = []; - public send(event: ITelemetryBaseEvent): void { + public logLevels: (LogLevel | undefined)[] = []; + public send(event: ITelemetryBaseEvent, logLevel?: LogLevel): void { this.events.push(this.prepareEvent(event)); + this.logLevels.push(logLevel); } } @@ -197,6 +200,58 @@ describe("TelemetryLogger", () => { } }); }); + + describe("logLevel forwarding", () => { + it("`sendTelemetryEvent` without logLevel forwards LogLevel.essential", () => { + const logger = new TestTelemetryLogger(); + logger.sendTelemetryEvent({ eventName: "noLevel" }); + assert.deepStrictEqual(logger.logLevels, [LogLevel.essential]); + }); + + it("`sendTelemetryEvent` with explicit LogLevel.info forwards LogLevel.info", () => { + const logger = new TestTelemetryLogger(); + logger.sendTelemetryEvent({ eventName: "infoLevel" }, undefined, LogLevel.info); + assert.deepStrictEqual(logger.logLevels, [LogLevel.info]); + }); + + it("`sendTelemetryEvent` with category 'error' forwards LogLevel.essential even when info is requested", () => { + const logger = new TestTelemetryLogger(); + logger.sendTelemetryEvent( + { eventName: "errorEvent", category: "error" }, + undefined, + LogLevel.info, + ); + assert.deepStrictEqual(logger.logLevels, [LogLevel.essential]); + }); + + it("`sendPerformanceEvent` without logLevel forwards LogLevel.essential", () => { + const logger = new TestTelemetryLogger(); + logger.sendPerformanceEvent({ eventName: "perfNoLevel" }); + assert.deepStrictEqual(logger.logLevels, [LogLevel.essential]); + }); + + it("`sendPerformanceEvent` with explicit LogLevel.info forwards LogLevel.info", () => { + const logger = new TestTelemetryLogger(); + logger.sendPerformanceEvent({ eventName: "perfInfo" }, undefined, LogLevel.info); + assert.deepStrictEqual(logger.logLevels, [LogLevel.info]); + }); + + it("`sendPerformanceEvent` with category 'error' forwards LogLevel.essential even when info is requested", () => { + const logger = new TestTelemetryLogger(); + logger.sendPerformanceEvent( + { eventName: "perfError", category: "error" }, + undefined, + LogLevel.info, + ); + assert.deepStrictEqual(logger.logLevels, [LogLevel.essential]); + }); + + it("`sendErrorEvent` forwards LogLevel.essential", () => { + const logger = new TestTelemetryLogger(); + logger.sendErrorEvent({ eventName: "errEvent" }); + assert.deepStrictEqual(logger.logLevels, [LogLevel.essential]); + }); + }); }); describe("convertToBasePropertyType", () => { From 03adcc416d33ff085ba8e042faa42cdc6d4804e2 Mon Sep 17 00:00:00 2001 From: MarioJGMsoft Date: Thu, 7 May 2026 23:12:56 +0000 Subject: [PATCH 21/21] feat: updated guidance and returned childLogger--Sink tests --- .changeset/salty-colts-appear.md | 8 ++-- .../src/test/childLogger.spec.ts | 43 +++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/.changeset/salty-colts-appear.md b/.changeset/salty-colts-appear.md index 9abf1432cb57..92ac8510cb52 100644 --- a/.changeset/salty-colts-appear.md +++ b/.changeset/salty-colts-appear.md @@ -9,9 +9,11 @@ Deprecated LogLevel.default and LogLevel.error #### Migration -The recommended replacement depends on how the value is used: +The recommended replacement for `LogLevel.default` depends on how the value is used: -- For an **event's `logLevel`** (e.g. the `logLevel` argument to `sendTelemetryEvent` / `sendPerformanceEvent` / `ITelemetryBaseLogger.send`), the recommendation is `LogLevel.essential`. -- For a logger's **`minLogLevel`** (the threshold that filters events), `LogLevel.info` is the recommendation. +- For an **event's default `logLevel`** (e.g. the `logLevel` argument to `sendTelemetryEvent` / `sendPerformanceEvent` / `ITelemetryBaseLogger.send`), the recommendation is `LogLevel.essential`. +- For a logger's **default `minLogLevel`** (the threshold that filters events), `LogLevel.info` is the recommendation. + +The replacement for `LogLevel.error` should always be essential. See [issue #26969](https://github.com/microsoft/FluidFramework/issues/26969) for full guidance and removal tracking (planned for v3.0). diff --git a/packages/utils/telemetry-utils/src/test/childLogger.spec.ts b/packages/utils/telemetry-utils/src/test/childLogger.spec.ts index 9a713bc16e64..fda2a29d254b 100644 --- a/packages/utils/telemetry-utils/src/test/childLogger.spec.ts +++ b/packages/utils/telemetry-utils/src/test/childLogger.spec.ts @@ -277,3 +277,46 @@ describe("ChildLogger", () => { assert(sent, "info event should be sent"); }); }); +describe("logLevel forwarding", () => { + function createRecordingSink(minLogLevel?: LogLevel): { + sink: ITelemetryBaseLogger; + recorded: { event: ITelemetryBaseEvent; logLevel: LogLevel | undefined }[]; + } { + const recorded: { event: ITelemetryBaseEvent; logLevel: LogLevel | undefined }[] = []; + const sink: ITelemetryBaseLogger = { + send: (event, logLevel): void => { + recorded.push({ event, logLevel }); + }, + minLogLevel, + }; + return { sink, recorded }; + } + + it("Forwards LogLevel.essential to the sink when `sendTelemetryEvent` omits logLevel", () => { + const { sink, recorded } = createRecordingSink(); + const child = createChildLogger({ logger: sink }); + + child.sendTelemetryEvent({ eventName: "chainDefault" }); + + assert.strictEqual(recorded[0]?.logLevel, LogLevel.essential); + }); + + it("Forwards explicit LogLevel.info to the sink via `sendTelemetryEvent`", () => { + const { sink, recorded } = createRecordingSink(); + const child = createChildLogger({ logger: sink }); + + child.sendTelemetryEvent({ eventName: "chainExplicit" }, undefined, LogLevel.info); + + assert.strictEqual(recorded[0]?.logLevel, LogLevel.info); + }); + + it("Forwards LogLevel.verbose to the sink via `sendTelemetryEvent`", () => { + // You have to add a minLogLevel of verbose to the sink to receive verbose events, otherwise they will be dropped. + const { sink, recorded } = createRecordingSink(LogLevel.verbose); + const child = createChildLogger({ logger: sink }); + + child.sendTelemetryEvent({ eventName: "chainDefault" }, undefined, LogLevel.verbose); + + assert.strictEqual(recorded[0]?.logLevel, LogLevel.verbose); + }); +});