Skip to content

Commit 77a56de

Browse files
Implement custom logger interface based on console methods
1 parent 149c8d1 commit 77a56de

File tree

8 files changed

+58
-36
lines changed

8 files changed

+58
-36
lines changed

src/logger/__tests__/index.spec.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,10 @@ test('SPLIT LOGGER / isLogLevelString utility function', () => {
1616
expect(isLogLevelString(LOG_LEVELS.DEBUG)).toBe(true); // Calling isLogLevelString should return true with a LOG_LEVELS value
1717
expect(isLogLevelString('ERROR')).toBe(true); // Calling isLogLevelString should return true with a string equal to some LOG_LEVELS value
1818
expect(isLogLevelString('INVALID LOG LEVEL')).toBe(false); // Calling isLogLevelString should return false with a string not equal to any LOG_LEVELS value
19-
2019
});
2120

2221
test('SPLIT LOGGER / LogLevels exposed mappings', () => {
2322
expect(LogLevels).toEqual(LOG_LEVELS); // Exposed log levels should contain the levels we want.
24-
2523
});
2624

2725
test('SPLIT LOGGER / Logger class shape', () => {
@@ -94,27 +92,22 @@ function testLogLevels(levelToTest: SplitIO.LogLevel) {
9492

9593
// Restore spied object.
9694
consoleLogSpy.mockRestore();
97-
9895
}
9996

10097
test('SPLIT LOGGER / Logger class public methods behavior - instance.debug', () => {
10198
testLogLevels(LogLevels.DEBUG);
102-
10399
});
104100

105101
test('SPLIT LOGGER / Logger class public methods behavior - instance.info', () => {
106102
testLogLevels(LogLevels.INFO);
107-
108103
});
109104

110105
test('SPLIT LOGGER / Logger class public methods behavior - instance.warn', () => {
111106
testLogLevels(LogLevels.WARN);
112-
113107
});
114108

115109
test('SPLIT LOGGER / Logger class public methods behavior - instance.error', () => {
116110
testLogLevels(LogLevels.ERROR);
117-
118111
});
119112

120113
test('SPLIT LOGGER / _sprintf', () => {

src/logger/index.ts

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,23 +41,31 @@ const defaultOptions = {
4141
prefix: 'splitio',
4242
logLevel: LogLevels.NONE,
4343
showLevel: true,
44-
logger(formattedMsg: string) { console.log(formattedMsg); }
44+
};
45+
46+
const defaultConsoleLogger: SplitIO.Logger = {
47+
debug(message: string) { console.log(message); },
48+
info(message: string) { console.log(message); },
49+
warn(message: string) { console.log(message); },
50+
error(message: string) { console.log(message); }
4551
};
4652

4753
export class Logger implements ILogger {
4854

4955
private options: Required<ILoggerOptions>;
5056
private codes: Map<number, string>;
5157
private logLevel: number;
58+
private logger: SplitIO.Logger;
5259

5360
constructor(options?: ILoggerOptions, codes?: Map<number, string>) {
5461
this.options = objectAssign({}, defaultOptions, options);
5562
this.codes = codes || new Map();
5663
this.logLevel = LogLevelIndexes[this.options.logLevel];
64+
this.logger = defaultConsoleLogger;
5765
}
5866

59-
setLogger(logger: (formattedMsg: string, level: SplitIO.LogLevel, msg: string) => void) {
60-
this.options.logger = logger;
67+
setLogger(logger: SplitIO.Logger) {
68+
this.logger = logger;
6169
}
6270

6371
setLogLevel(logLevel: SplitIO.LogLevel) {
@@ -66,32 +74,30 @@ export class Logger implements ILogger {
6674
}
6775

6876
debug(msg: string | number, args?: any[]) {
69-
if (this._shouldLog(LogLevelIndexes.DEBUG)) this._log(LogLevels.DEBUG, msg, args);
77+
if (this._shouldLog(LogLevelIndexes.DEBUG)) this.logger.debug(this._log(LogLevels.DEBUG, msg, args));
7078
}
7179

7280
info(msg: string | number, args?: any[]) {
73-
if (this._shouldLog(LogLevelIndexes.INFO)) this._log(LogLevels.INFO, msg, args);
81+
if (this._shouldLog(LogLevelIndexes.INFO)) this.logger.info(this._log(LogLevels.INFO, msg, args));
7482
}
7583

7684
warn(msg: string | number, args?: any[]) {
77-
if (this._shouldLog(LogLevelIndexes.WARN)) this._log(LogLevels.WARN, msg, args);
85+
if (this._shouldLog(LogLevelIndexes.WARN)) this.logger.warn(this._log(LogLevels.WARN, msg, args));
7886
}
7987

8088
error(msg: string | number, args?: any[]) {
81-
if (this._shouldLog(LogLevelIndexes.ERROR)) this._log(LogLevels.ERROR, msg, args);
89+
if (this._shouldLog(LogLevelIndexes.ERROR)) this.logger.error(this._log(LogLevels.ERROR, msg, args));
8290
}
8391

84-
private _log(level: SplitIO.LogLevel, msg: string | number, args?: any[]) {
92+
private _log(level: SplitIO.LogLevel, msg: string | number, args?: any[]): string {
8593
if (typeof msg === 'number') {
8694
const format = this.codes.get(msg);
8795
msg = format ? _sprintf(format, args) : `Message code ${msg}${args ? ', with args: ' + args.toString() : ''}`;
8896
} else {
8997
if (args) msg = _sprintf(msg, args);
9098
}
9199

92-
const formattedText = this._generateLogMessage(level, msg);
93-
94-
this.options.logger(formattedText, level, msg);
100+
return this._generateLogMessage(level, msg);
95101
}
96102

97103
private _generateLogMessage(level: SplitIO.LogLevel, text: string) {

src/logger/types.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@ export interface ILoggerOptions {
44
prefix?: string;
55
logLevel?: SplitIO.LogLevel;
66
showLevel?: boolean; // @TODO remove this param eventually since it is not being set `false` anymore
7-
logger?: (formattedMsg: string, level: SplitIO.LogLevel, msg: string) => void;
87
}
98

109
export interface ILogger extends SplitIO.ILogger {
11-
setLogger(logger: (formattedMsg: string, level: SplitIO.LogLevel, msg: string) => void): void;
10+
setLogger(logger: SplitIO.Logger): void;
1211

1312
debug(msg: any): void;
1413
debug(msg: string | number, args?: any[]): void;

src/utils/settingsValidation/logger/__tests__/index.spec.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@ import { loggerMock, getLoggerLogLevel } from '../../../../logger/__tests__/sdkL
33
import { validateLogger as pluggableValidateLogger } from '../pluggableLogger';
44
import { validateLogger as builtinValidateLogger } from '../builtinLogger';
55

6+
const customLogger = {
7+
debug: jest.fn(),
8+
info: jest.fn(),
9+
warn: jest.fn(),
10+
error: jest.fn()
11+
};
12+
613
const testTargets = [
714
[pluggableValidateLogger],
815
[builtinValidateLogger]
@@ -11,7 +18,10 @@ const testTargets = [
1118
describe('logger validators', () => {
1219

1320
const consoleLogSpy = jest.spyOn(global.console, 'log');
14-
afterEach(() => { consoleLogSpy.mockClear(); });
21+
afterEach(() => {
22+
consoleLogSpy.mockClear();
23+
customLogger.error.mockClear();
24+
});
1525

1626
test.each(testTargets)('returns a NONE logger if `debug` property is not defined or false', (validateLogger) => { // @ts-ignore
1727
expect(getLoggerLogLevel(validateLogger({}))).toBe('NONE');
@@ -24,15 +34,16 @@ describe('logger validators', () => {
2434
test.each(testTargets)('returns a NONE logger if `debug` property is invalid and logs the error', (validateLogger) => {
2535
expect(getLoggerLogLevel(validateLogger({ debug: null }))).toBe('NONE');
2636
expect(getLoggerLogLevel(validateLogger({ debug: 10 }))).toBe('NONE');
27-
expect(getLoggerLogLevel(validateLogger({ debug: {} }))).toBe('NONE');
37+
expect(getLoggerLogLevel(validateLogger({ debug: {}, logger: customLogger }))).toBe('NONE');
2838

2939
if (validateLogger === builtinValidateLogger) {
3040
// for builtinValidateLogger, a logger cannot be passed as `debug` property
3141
expect(getLoggerLogLevel(validateLogger({ debug: loggerMock }))).toBe('NONE');
32-
expect(consoleLogSpy).toBeCalledTimes(4);
33-
} else {
3442
expect(consoleLogSpy).toBeCalledTimes(3);
43+
} else {
44+
expect(consoleLogSpy).toBeCalledTimes(2);
3545
}
46+
expect(customLogger.error).toBeCalledTimes(1);
3647
});
3748

3849
test.each(testTargets)('returns a logger with the provided log level if `debug` property is true or a string log level', (validateLogger) => {

src/utils/settingsValidation/logger/builtinLogger.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { ILogger } from '../../../logger/types';
33
import { isLocalStorageAvailable } from '../../env/isLocalStorageAvailable';
44
import { isNode } from '../../env/isNode';
55
import { codesDebug } from '../../../logger/messages/debug';
6-
import { getLogLevel } from './commons';
6+
import { getLogLevel, isLogger } from './commons';
77
import SplitIO from '../../../../types/splitio';
88

99
const allCodes = new Map(codesDebug);
@@ -46,10 +46,10 @@ export function validateLogger(settings: { debug: unknown, logger?: unknown }):
4646
const logLevel: SplitIO.LogLevel | undefined = debug !== undefined ? getLogLevel(debug) : initialLogLevel;
4747

4848
const log = new Logger({ logLevel: logLevel || initialLogLevel }, allCodes);
49-
if (typeof logger === 'function') log.setLogger(logger as (formattedMsg: string, level: SplitIO.LogLevel, msg: string) => void);
49+
if (isLogger(logger)) log.setLogger(logger);
5050

5151
// @ts-ignore // if logLevel is undefined at this point, it means that settings `debug` value is invalid
52-
if (!logLevel) log._log(LogLevels.ERROR, 'Invalid Log Level - No changes to the logs will be applied.');
52+
if (!logLevel) log.logger.error(log._log(LogLevels.ERROR, 'Invalid Log Level - No changes to the logs will be applied.'));
5353

5454
return log;
5555
}

src/utils/settingsValidation/logger/commons.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,7 @@ export function getLogLevel(debugValue: unknown): SplitIO.LogLevel | undefined {
1818
debugValue :
1919
undefined;
2020
}
21+
22+
export function isLogger(log: any): log is SplitIO.Logger {
23+
return log !== null && typeof log === 'object' && typeof log.debug === 'function' && typeof log.info === 'function' && typeof log.warn === 'function' && typeof log.error === 'function';
24+
}

src/utils/settingsValidation/logger/pluggableLogger.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { Logger, LogLevels } from '../../../logger';
22
import { ILogger } from '../../../logger/types';
33
import SplitIO from '../../../../types/splitio';
4-
import { getLogLevel } from './commons';
4+
import { getLogLevel, isLogger } from './commons';
55

6-
function isLogger(log: any): log is ILogger {
7-
return log !== null && typeof log === 'object' && typeof log.debug === 'function' && typeof log.info === 'function' && typeof log.warn === 'function' && typeof log.error === 'function' && typeof log.setLogLevel === 'function';
6+
function isILogger(log: any): log is ILogger {
7+
return isLogger(log) && typeof (log as any).setLogLevel === 'function';
88
}
99

1010
// By default it starts disabled.
@@ -22,18 +22,18 @@ export function validateLogger(settings: { debug: unknown, logger?: unknown }):
2222
let logLevel: SplitIO.LogLevel | undefined = initialLogLevel;
2323

2424
if (debug !== undefined) {
25-
if (isLogger(debug)) {
26-
if (typeof logger === 'function') debug.setLogger(logger as (formattedMsg: string, level: SplitIO.LogLevel, msg: string) => void);
25+
if (isILogger(debug)) {
26+
if (isLogger(logger)) debug.setLogger(logger);
2727
return debug;
2828
}
2929
logLevel = getLogLevel(settings.debug);
3030
}
3131

3232
const log = new Logger({ logLevel: logLevel || initialLogLevel });
33-
if (typeof logger === 'function') log.setLogger(logger as (formattedMsg: string, level: SplitIO.LogLevel, msg: string) => void);
33+
if (isLogger(logger)) log.setLogger(logger);
3434

3535
// @ts-ignore // `debug` value is invalid if logLevel is undefined at this point
36-
if (!logLevel) log._log(LogLevels.ERROR, 'Invalid `debug` value at config. Logs will be disabled.');
36+
if (!logLevel) log.logger.error(log._log(LogLevels.ERROR, 'Invalid `debug` value at config. Logs will be disabled.'));
3737

3838
return log;
3939
}

types/splitio.d.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ interface ISharedSettings {
9191
* Do not change these settings unless you're working an advanced use case, like connecting to the Split proxy.
9292
*/
9393
urls?: SplitIO.UrlSettings;
94-
logger?: (formattedMsg: string, level: SplitIO.LogLevel, msg: string) => void;
94+
logger?: SplitIO.Logger;
9595
}
9696
/**
9797
* Common settings properties for SDKs with synchronous API (standalone and localhost modes).
@@ -564,7 +564,7 @@ declare namespace SplitIO {
564564
telemetry: string;
565565
};
566566
readonly integrations?: IntegrationFactory[];
567-
readonly logger?: (formattedMsg: string, level: SplitIO.LogLevel, msg: string) => void;
567+
readonly logger?: Logger;
568568
readonly debug: boolean | LogLevel | ILogger;
569569
readonly version: string;
570570
/**
@@ -595,6 +595,15 @@ declare namespace SplitIO {
595595
* Log levels.
596596
*/
597597
type LogLevel = 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'NONE';
598+
/**
599+
* Custom logger interface.
600+
*/
601+
interface Logger {
602+
debug(message: string): any;
603+
info(message: string): any;
604+
warn(message: string): any;
605+
error(message: string): any;
606+
}
598607
/**
599608
* Logger API
600609
*/

0 commit comments

Comments
 (0)