From 7566a17e6d689a60172204a1ca10115128136fd2 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Wed, 9 Jul 2025 12:02:15 -0300 Subject: [PATCH] feat: add custom logger support via logger callback option --- src/logger/__tests__/sdkLogger.mock.ts | 1 + src/logger/index.ts | 7 ++++++- src/logger/types.ts | 9 ++++++--- .../settingsValidation/logger/builtinLogger.ts | 5 +++-- src/utils/settingsValidation/logger/commons.ts | 18 +++++++----------- .../logger/pluggableLogger.ts | 10 +++++++--- types/splitio.d.ts | 2 ++ 7 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/logger/__tests__/sdkLogger.mock.ts b/src/logger/__tests__/sdkLogger.mock.ts index b7c4aa25..e358a760 100644 --- a/src/logger/__tests__/sdkLogger.mock.ts +++ b/src/logger/__tests__/sdkLogger.mock.ts @@ -6,6 +6,7 @@ export const loggerMock = { debug: jest.fn(), info: jest.fn(), setLogLevel: jest.fn(), + setLogger: jest.fn(), mockClear() { this.warn.mockClear(); diff --git a/src/logger/index.ts b/src/logger/index.ts index 662e1f86..0663bd12 100644 --- a/src/logger/index.ts +++ b/src/logger/index.ts @@ -41,6 +41,7 @@ const defaultOptions = { prefix: 'splitio', logLevel: LogLevels.NONE, showLevel: true, + logger(formattedMsg: string) { console.log(formattedMsg); } }; export class Logger implements ILogger { @@ -55,6 +56,10 @@ export class Logger implements ILogger { this.logLevel = LogLevelIndexes[this.options.logLevel]; } + setLogger(logger: (formattedMsg: string, level: SplitIO.LogLevel, msg: string) => void) { + this.options.logger = logger; + } + setLogLevel(logLevel: SplitIO.LogLevel) { this.options.logLevel = logLevel; this.logLevel = LogLevelIndexes[logLevel]; @@ -86,7 +91,7 @@ export class Logger implements ILogger { const formattedText = this._generateLogMessage(level, msg); - console.log(formattedText); + this.options.logger(formattedText, level, msg); } private _generateLogMessage(level: SplitIO.LogLevel, text: string) { diff --git a/src/logger/types.ts b/src/logger/types.ts index 2f05b3ba..2987971d 100644 --- a/src/logger/types.ts +++ b/src/logger/types.ts @@ -1,12 +1,15 @@ import SplitIO from '../../types/splitio'; export interface ILoggerOptions { - prefix?: string, - logLevel?: SplitIO.LogLevel, - showLevel?: boolean, // @TODO remove this param eventually since it is not being set `false` anymore + prefix?: string; + logLevel?: SplitIO.LogLevel; + showLevel?: boolean; // @TODO remove this param eventually since it is not being set `false` anymore + logger?: (formattedMsg: string, level: SplitIO.LogLevel, msg: string) => void; } export interface ILogger extends SplitIO.ILogger { + setLogger(logger: (formattedMsg: string, level: SplitIO.LogLevel, msg: string) => void): void; + debug(msg: any): void; debug(msg: string | number, args?: any[]): void; diff --git a/src/utils/settingsValidation/logger/builtinLogger.ts b/src/utils/settingsValidation/logger/builtinLogger.ts index 4f099c7c..6448d9f0 100644 --- a/src/utils/settingsValidation/logger/builtinLogger.ts +++ b/src/utils/settingsValidation/logger/builtinLogger.ts @@ -40,12 +40,13 @@ if (/^(enabled?|on)/i.test(initialState)) { * @param settings - user config object, with an optional `debug` property of type boolean or string log level. * @returns a logger instance with the log level at `settings.debug`. If `settings.debug` is invalid or not provided, `initialLogLevel` is used. */ -export function validateLogger(settings: { debug: unknown }): ILogger { - const { debug } = settings; +export function validateLogger(settings: { debug: unknown, logger?: unknown }): ILogger { + const { debug, logger } = settings; const logLevel: SplitIO.LogLevel | undefined = debug !== undefined ? getLogLevel(debug) : initialLogLevel; const log = new Logger({ logLevel: logLevel || initialLogLevel }, allCodes); + if (typeof logger === 'function') log.setLogger(logger as (formattedMsg: string, level: SplitIO.LogLevel, msg: string) => void); // @ts-ignore // if logLevel is undefined at this point, it means that settings `debug` value is invalid if (!logLevel) log._log(LogLevels.ERROR, 'Invalid Log Level - No changes to the logs will be applied.'); diff --git a/src/utils/settingsValidation/logger/commons.ts b/src/utils/settingsValidation/logger/commons.ts index 8c11cbbb..f34503df 100644 --- a/src/utils/settingsValidation/logger/commons.ts +++ b/src/utils/settingsValidation/logger/commons.ts @@ -10,15 +10,11 @@ import SplitIO from '../../../../types/splitio'; * @returns LogLevel of the given debugValue or undefined if the provided value is invalid */ export function getLogLevel(debugValue: unknown): SplitIO.LogLevel | undefined { - if (typeof debugValue === 'boolean') { - if (debugValue) { - return LogLevels.DEBUG; - } else { - return LogLevels.NONE; - } - } else if (typeof debugValue === 'string' && isLogLevelString(debugValue)) { - return debugValue; - } else { - return undefined; - } + return typeof debugValue === 'boolean' ? + debugValue ? + LogLevels.DEBUG : + LogLevels.NONE : + typeof debugValue === 'string' && isLogLevelString(debugValue) ? + debugValue : + undefined; } diff --git a/src/utils/settingsValidation/logger/pluggableLogger.ts b/src/utils/settingsValidation/logger/pluggableLogger.ts index 063134c9..05172d5f 100644 --- a/src/utils/settingsValidation/logger/pluggableLogger.ts +++ b/src/utils/settingsValidation/logger/pluggableLogger.ts @@ -17,16 +17,20 @@ let initialLogLevel = LogLevels.NONE; * @returns a logger instance, that might be: the provided logger at `settings.debug`, or one with the given `debug` log level, * or one with NONE log level if `debug` is not defined or invalid. */ -export function validateLogger(settings: { debug: unknown }): ILogger { - const { debug } = settings; +export function validateLogger(settings: { debug: unknown, logger?: unknown }): ILogger { + const { debug, logger } = settings; let logLevel: SplitIO.LogLevel | undefined = initialLogLevel; if (debug !== undefined) { - if (isLogger(debug)) return debug; + if (isLogger(debug)) { + if (typeof logger === 'function') debug.setLogger(logger as (formattedMsg: string, level: SplitIO.LogLevel, msg: string) => void); + return debug; + } logLevel = getLogLevel(settings.debug); } const log = new Logger({ logLevel: logLevel || initialLogLevel }); + if (typeof logger === 'function') log.setLogger(logger as (formattedMsg: string, level: SplitIO.LogLevel, msg: string) => void); // @ts-ignore // `debug` value is invalid if logLevel is undefined at this point if (!logLevel) log._log(LogLevels.ERROR, 'Invalid `debug` value at config. Logs will be disabled.'); diff --git a/types/splitio.d.ts b/types/splitio.d.ts index 377d3234..956a063b 100644 --- a/types/splitio.d.ts +++ b/types/splitio.d.ts @@ -87,6 +87,7 @@ interface ISharedSettings { * Do not change these settings unless you're working an advanced use case, like connecting to the Split proxy. */ urls?: SplitIO.UrlSettings; + logger?: (formattedMsg: string, level: SplitIO.LogLevel, msg: string) => void; } /** * Common settings properties for SDKs with synchronous API (standalone and localhost modes). @@ -559,6 +560,7 @@ declare namespace SplitIO { telemetry: string; }; readonly integrations?: IntegrationFactory[]; + readonly logger?: (formattedMsg: string, level: SplitIO.LogLevel, msg: string) => void; readonly debug: boolean | LogLevel | ILogger; readonly version: string; /**