@quatrain/log 1.0.2 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # @quatrain/log
2
+
3
+ A structured logging package for Quatrain applications. It uses an adapter pattern to allow for different logging backends, such as the console, file, or a cloud logging service.
4
+
5
+ ## Features
6
+
7
+ - **Multiple Log Levels**: Supports `DEBUG`, `INFO`, `WARN`, `ERROR`, and `CRITICAL`.
8
+ - **Adapter Pattern**: Pluggable adapters for various logging outputs.
9
+ - **Domain-specific Loggers**: Create separate logger instances for different parts of your application.
10
+ - **Structured JSON Output**: Logs can be formatted as JSON for easy parsing by log management systems.
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install @quatrain/log
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ```typescript
21
+ import { Log, LogLevel } from '@quatrain/log'
22
+ import { ConsoleLoggerAdapter } from '@quatrain/log' // Example adapter
23
+
24
+ const logger = Log.addLogger('api', new ConsoleLoggerAdapter(), true)
25
+ logger.info('Server has started.', { port: 3000 })
26
+ ```
@@ -1,4 +1,11 @@
1
- import { LogLevel } from './Log';
1
+ export declare enum LogLevel {
2
+ TRACE = 0,
3
+ DEBUG = 1,
4
+ INFO = 2,
5
+ WARN = 3,
6
+ ERROR = 4,
7
+ SILENT = 5
8
+ }
2
9
  export interface LoggerType {
3
10
  log(message: string): void;
4
11
  debug(message: string): void;
@@ -15,15 +22,11 @@ export declare abstract class AbstractLoggerAdapter implements LoggerType {
15
22
  constructor(prefix?: string, level?: LogLevel);
16
23
  logLevel(level: LogLevel): void;
17
24
  formatLogMessage: (messages: any[], loglevel?: LogLevel) => string;
18
- /**
19
- * Log message using defined logger
20
- * @param message string | object
21
- * @param level string
22
- */
23
- log(...messages: any): void;
24
- debug(message: any): void;
25
- warn(message: any): void;
26
- info(message: any): void;
27
- error(message: any): void;
28
- trace(message: any): void;
25
+ log(...messages: any[]): void;
26
+ debug(...messages: any[]): void;
27
+ warn(...messages: any[]): void;
28
+ info(...messages: any[]): void;
29
+ error(...messages: any[]): void;
30
+ trace(...messages: any[]): void;
29
31
  }
32
+ //# sourceMappingURL=AbstractLoggerAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AbstractLoggerAdapter.d.ts","sourceRoot":"","sources":["../src/AbstractLoggerAdapter.ts"],"names":[],"mappings":"AAAA,oBAAY,QAAQ;IACjB,KAAK,IAAI;IACT,KAAK,IAAI;IACT,IAAI,IAAI;IACR,IAAI,IAAI;IACR,KAAK,IAAI;IACT,MAAM,IAAI;CACZ;AAED,MAAM,WAAW,UAAU;IACxB,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAED,eAAO,MAAM,aAAa,UAOzB,CAAA;AAED,8BAAsB,qBAAsB,YAAW,UAAU;IAC9D,SAAS,CAAC,GAAG,EAAE,MAAM,CAAK;IAC1B,SAAS,CAAC,SAAS,EAAE,QAAQ,CAAgB;IAC7C,SAAS,CAAC,OAAO,EAAE,GAAG,CAAY;gBAEtB,MAAM,SAAK,EAAE,KAAK,GAAE,QAAwB;IAIxD,QAAQ,CAAC,KAAK,EAAE,QAAQ;IAIxB,gBAAgB,aACH,GAAG,EAAE,aACL,QAAQ,KAClB,MAAM,CAgBR;IAOD,GAAG,CAAC,GAAG,QAAQ,EAAE,GAAG,EAAE,GAAG,IAAI;IAI7B,KAAK,CAAC,GAAG,QAAQ,EAAE,GAAG,EAAE,GAAG,IAAI;IAI/B,IAAI,CAAC,GAAG,QAAQ,EAAE,GAAG,EAAE,GAAG,IAAI;IAI9B,IAAI,CAAC,GAAG,QAAQ,EAAE,GAAG,EAAE,GAAG,IAAI;IAI9B,KAAK,CAAC,GAAG,QAAQ,EAAE,GAAG,EAAE,GAAG,IAAI;IAI/B,KAAK,CAAC,GAAG,QAAQ,EAAE,GAAG,EAAE,GAAG,IAAI;CAGjC"}
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AbstractLoggerAdapter = exports.loglevelNames = exports.LogLevel = void 0;
4
+ var LogLevel;
5
+ (function (LogLevel) {
6
+ LogLevel[LogLevel["TRACE"] = 0] = "TRACE";
7
+ LogLevel[LogLevel["DEBUG"] = 1] = "DEBUG";
8
+ LogLevel[LogLevel["INFO"] = 2] = "INFO";
9
+ LogLevel[LogLevel["WARN"] = 3] = "WARN";
10
+ LogLevel[LogLevel["ERROR"] = 4] = "ERROR";
11
+ LogLevel[LogLevel["SILENT"] = 5] = "SILENT";
12
+ })(LogLevel || (exports.LogLevel = LogLevel = {}));
13
+ exports.loglevelNames = [
14
+ 'TRACE',
15
+ 'DEBUG',
16
+ 'INFO',
17
+ 'WARN',
18
+ 'ERROR',
19
+ 'SILENT',
20
+ ];
21
+ class AbstractLoggerAdapter {
22
+ _me = '';
23
+ _logLevel = LogLevel.WARN;
24
+ _logger = undefined;
25
+ constructor(prefix = '', level = LogLevel.WARN) {
26
+ this._me = prefix;
27
+ }
28
+ logLevel(level) {
29
+ this._logLevel = level;
30
+ }
31
+ formatLogMessage = (messages, loglevel = LogLevel.INFO) => {
32
+ const flatMessages = messages.flat();
33
+ const prefix = `${new Date().toISOString()} - [${this._me}]`;
34
+ const strs = flatMessages.map((message) => {
35
+ if (message instanceof Error) {
36
+ return message.stack || message.message;
37
+ }
38
+ if (typeof message === 'object' && message !== null) {
39
+ return JSON.stringify(message);
40
+ }
41
+ return String(message);
42
+ });
43
+ return `${prefix} ${strs.join(' ')}`;
44
+ };
45
+ log(...messages) {
46
+ throw new Error(`This method needs to be implemtend in child class`);
47
+ }
48
+ debug(...messages) {
49
+ throw new Error(`This method needs to be implemtend in child class`);
50
+ }
51
+ warn(...messages) {
52
+ throw new Error(`This method needs to be implemtend in child class`);
53
+ }
54
+ info(...messages) {
55
+ throw new Error(`This method needs to be implemtend in child class`);
56
+ }
57
+ error(...messages) {
58
+ throw new Error(`This method needs to be implemtend in child class`);
59
+ }
60
+ trace(...messages) {
61
+ throw new Error(`This method needs to be implemtend in child class`);
62
+ }
63
+ }
64
+ exports.AbstractLoggerAdapter = AbstractLoggerAdapter;
65
+ //# sourceMappingURL=AbstractLoggerAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AbstractLoggerAdapter.js","sourceRoot":"","sources":["../src/AbstractLoggerAdapter.ts"],"names":[],"mappings":";;;AAAA,IAAY,QAOX;AAPD,WAAY,QAAQ;IACjB,yCAAS,CAAA;IACT,yCAAS,CAAA;IACT,uCAAQ,CAAA;IACR,uCAAQ,CAAA;IACR,yCAAS,CAAA;IACT,2CAAU,CAAA;AACb,CAAC,EAPW,QAAQ,wBAAR,QAAQ,QAOnB;AAWY,QAAA,aAAa,GAAG;IAC1B,OAAO;IACP,OAAO;IACP,MAAM;IACN,MAAM;IACN,OAAO;IACP,QAAQ;CACV,CAAA;AAED,MAAsB,qBAAqB;IAC9B,GAAG,GAAW,EAAE,CAAA;IAChB,SAAS,GAAa,QAAQ,CAAC,IAAI,CAAA;IACnC,OAAO,GAAQ,SAAS,CAAA;IAElC,YAAY,MAAM,GAAG,EAAE,EAAE,QAAkB,QAAQ,CAAC,IAAI;QACrD,IAAI,CAAC,GAAG,GAAG,MAAM,CAAA;IACpB,CAAC;IAED,QAAQ,CAAC,KAAe;QACrB,IAAI,CAAC,SAAS,GAAG,KAAK,CAAA;IACzB,CAAC;IAED,gBAAgB,GAAG,CAChB,QAAe,EACf,WAAqB,QAAQ,CAAC,IAAI,EAC3B,EAAE;QAET,MAAM,YAAY,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAA;QAEpC,MAAM,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,OAAO,IAAI,CAAC,GAAG,GAAG,CAAA;QAC5D,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,OAAY,EAAE,EAAE;YAC5C,IAAI,OAAO,YAAY,KAAK,EAAE,CAAC;gBAC5B,OAAO,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,OAAO,CAAA;YAC1C,CAAC;YACD,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;gBACnD,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;YACjC,CAAC;YACD,OAAO,MAAM,CAAC,OAAO,CAAC,CAAA;QACzB,CAAC,CAAC,CAAA;QAEF,OAAO,GAAG,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAA;IACvC,CAAC,CAAA;IAOD,GAAG,CAAC,GAAG,QAAe;QACnB,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;IACvE,CAAC;IAED,KAAK,CAAC,GAAG,QAAe;QACrB,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;IACvE,CAAC;IAED,IAAI,CAAC,GAAG,QAAe;QACpB,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;IACvE,CAAC;IAED,IAAI,CAAC,GAAG,QAAe;QACpB,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;IACvE,CAAC;IAED,KAAK,CAAC,GAAG,QAAe;QACrB,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;IACvE,CAAC;IAED,KAAK,CAAC,GAAG,QAAe;QACrB,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;IACvE,CAAC;CACH;AA9DD,sDA8DC"}
@@ -1,12 +1,6 @@
1
- import { AbstractLoggerAdapter } from './AbstractLoggerAdapter';
2
- import { LogLevel } from './Log';
1
+ import { AbstractLoggerAdapter, LogLevel } from './AbstractLoggerAdapter';
3
2
  export declare class DefaultLoggerAdapter extends AbstractLoggerAdapter {
4
3
  constructor(prefix?: string, level?: LogLevel);
5
- /**
6
- * Log message using defined logger
7
- * @param messages array
8
- * @param level LogLevel
9
- */
10
4
  log(messages: any[], level?: LogLevel): void;
11
5
  logLevel(level: LogLevel): void;
12
6
  debug(...messages: any): void;
@@ -15,3 +9,4 @@ export declare class DefaultLoggerAdapter extends AbstractLoggerAdapter {
15
9
  error(...messages: any): void;
16
10
  trace(...messages: any): void;
17
11
  }
12
+ //# sourceMappingURL=DefaultLoggerAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DefaultLoggerAdapter.d.ts","sourceRoot":"","sources":["../src/DefaultLoggerAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAIzE,qBAAa,oBAAqB,SAAQ,qBAAqB;gBAChD,MAAM,SAAK,EAAE,KAAK,GAAE,QAAyB;IAWzD,GAAG,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,KAAK,GAAE,QAAyB,GAAG,IAAI;IAyB5D,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IAK/B,KAAK,CAAC,GAAG,QAAQ,EAAE,GAAG,GAAG,IAAI;IAI7B,IAAI,CAAC,GAAG,QAAQ,EAAE,GAAG,GAAG,IAAI;IAI5B,IAAI,CAAC,GAAG,QAAQ,EAAE,GAAG,GAAG,IAAI;IAI5B,KAAK,CAAC,GAAG,QAAQ,EAAE,GAAG,GAAG,IAAI;IAI7B,KAAK,CAAC,GAAG,QAAQ,EAAE,GAAG,GAAG,IAAI;CAG/B"}
@@ -6,35 +6,30 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.DefaultLoggerAdapter = void 0;
7
7
  const AbstractLoggerAdapter_1 = require("./AbstractLoggerAdapter");
8
8
  const loglevel_1 = __importDefault(require("loglevel"));
9
- const Log_1 = require("./Log");
9
+ const chalk_1 = __importDefault(require("chalk"));
10
10
  class DefaultLoggerAdapter extends AbstractLoggerAdapter_1.AbstractLoggerAdapter {
11
- constructor(prefix = '', level = Log_1.LogLevel.DEBUG) {
11
+ constructor(prefix = '', level = AbstractLoggerAdapter_1.LogLevel.DEBUG) {
12
12
  super(prefix, level);
13
13
  this._logger = loglevel_1.default;
14
14
  this._logger.setLevel(level);
15
15
  }
16
- /**
17
- * Log message using defined logger
18
- * @param messages array
19
- * @param level LogLevel
20
- */
21
16
  log(messages, level = this._logLevel) {
22
17
  const message = this.formatLogMessage(messages);
23
18
  switch (level) {
24
- case Log_1.LogLevel.TRACE:
25
- this._logger.trace(message);
19
+ case AbstractLoggerAdapter_1.LogLevel.TRACE:
20
+ this._logger.trace(chalk_1.default.grey(message));
26
21
  break;
27
- case Log_1.LogLevel.DEBUG:
28
- this._logger.debug(message);
22
+ case AbstractLoggerAdapter_1.LogLevel.DEBUG:
23
+ this._logger.debug(chalk_1.default.yellow(message));
29
24
  break;
30
- case Log_1.LogLevel.INFO:
31
- this._logger.info(message);
25
+ case AbstractLoggerAdapter_1.LogLevel.INFO:
26
+ this._logger.info(chalk_1.default.green(message));
32
27
  break;
33
- case Log_1.LogLevel.WARN:
34
- this._logger.warn(message);
28
+ case AbstractLoggerAdapter_1.LogLevel.WARN:
29
+ this._logger.warn(chalk_1.default.red(message));
35
30
  break;
36
- case Log_1.LogLevel.ERROR:
37
- this._logger.error(message);
31
+ case AbstractLoggerAdapter_1.LogLevel.ERROR:
32
+ this._logger.error(chalk_1.default.bgRedBright(message));
38
33
  break;
39
34
  default:
40
35
  this._logger.log(message);
@@ -46,19 +41,20 @@ class DefaultLoggerAdapter extends AbstractLoggerAdapter_1.AbstractLoggerAdapter
46
41
  this._logger.setLevel(level);
47
42
  }
48
43
  debug(...messages) {
49
- this.log(messages[0], Log_1.LogLevel.DEBUG);
44
+ this.log(messages, AbstractLoggerAdapter_1.LogLevel.DEBUG);
50
45
  }
51
46
  warn(...messages) {
52
- this.log(messages[0], Log_1.LogLevel.WARN);
47
+ this.log(messages, AbstractLoggerAdapter_1.LogLevel.WARN);
53
48
  }
54
49
  info(...messages) {
55
- this.log(messages[0], Log_1.LogLevel.INFO);
50
+ this.log(messages, AbstractLoggerAdapter_1.LogLevel.INFO);
56
51
  }
57
52
  error(...messages) {
58
- this.log(messages[0], Log_1.LogLevel.ERROR);
53
+ this.log(messages, AbstractLoggerAdapter_1.LogLevel.ERROR);
59
54
  }
60
55
  trace(...messages) {
61
- this.log(messages[0], Log_1.LogLevel.TRACE);
56
+ this.log(messages, AbstractLoggerAdapter_1.LogLevel.TRACE);
62
57
  }
63
58
  }
64
59
  exports.DefaultLoggerAdapter = DefaultLoggerAdapter;
60
+ //# sourceMappingURL=DefaultLoggerAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DefaultLoggerAdapter.js","sourceRoot":"","sources":["../src/DefaultLoggerAdapter.ts"],"names":[],"mappings":";;;;;;AAAA,mEAAyE;AACzE,wDAA6B;AAC7B,kDAAyB;AAEzB,MAAa,oBAAqB,SAAQ,6CAAqB;IAC5D,YAAY,MAAM,GAAG,EAAE,EAAE,QAAkB,gCAAQ,CAAC,KAAK;QACtD,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;QACpB,IAAI,CAAC,OAAO,GAAG,kBAAM,CAAA;QACrB,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC/B,CAAC;IAOD,GAAG,CAAC,QAAe,EAAE,QAAkB,IAAI,CAAC,SAAS;QAClD,MAAM,OAAO,GAAG,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAA;QAE/C,QAAQ,KAAK,EAAE,CAAC;YACb,KAAK,gCAAQ,CAAC,KAAK;gBAChB,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,eAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAA;gBACvC,MAAK;YACR,KAAK,gCAAQ,CAAC,KAAK;gBAChB,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,eAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAA;gBACzC,MAAK;YACR,KAAK,gCAAQ,CAAC,IAAI;gBACf,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,eAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAA;gBACvC,MAAK;YACR,KAAK,gCAAQ,CAAC,IAAI;gBACf,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,eAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAA;gBACrC,MAAK;YACR,KAAK,gCAAQ,CAAC,KAAK;gBAChB,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,eAAK,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAA;gBAC9C,MAAK;YACR;gBACG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;gBACzB,MAAK;QACX,CAAC;IACJ,CAAC;IAED,QAAQ,CAAC,KAAe;QACrB,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;QACrB,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC/B,CAAC;IAED,KAAK,CAAC,GAAG,QAAa;QACnB,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,gCAAQ,CAAC,KAAK,CAAC,CAAA;IACrC,CAAC;IAED,IAAI,CAAC,GAAG,QAAa;QAClB,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,gCAAQ,CAAC,IAAI,CAAC,CAAA;IACpC,CAAC;IAED,IAAI,CAAC,GAAG,QAAa;QAClB,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,gCAAQ,CAAC,IAAI,CAAC,CAAA;IACpC,CAAC;IAED,KAAK,CAAC,GAAG,QAAa;QACnB,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,gCAAQ,CAAC,KAAK,CAAC,CAAA;IACrC,CAAC;IAED,KAAK,CAAC,GAAG,QAAa;QACnB,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,gCAAQ,CAAC,KAAK,CAAC,CAAA;IACrC,CAAC;CACH;AA7DD,oDA6DC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const DefaultLoggerAdapter_1 = require("./DefaultLoggerAdapter");
7
+ const Log_1 = require("./Log");
8
+ const loglevel_1 = __importDefault(require("loglevel"));
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ jest.mock('loglevel', () => ({
11
+ setLevel: jest.fn(),
12
+ trace: jest.fn(),
13
+ debug: jest.fn(),
14
+ info: jest.fn(),
15
+ warn: jest.fn(),
16
+ error: jest.fn(),
17
+ log: jest.fn(),
18
+ }));
19
+ jest.mock('chalk', () => ({
20
+ grey: jest.fn((s) => s),
21
+ yellow: jest.fn((s) => s),
22
+ green: jest.fn((s) => s),
23
+ red: jest.fn((s) => s),
24
+ bgRedBright: jest.fn((s) => s),
25
+ }));
26
+ describe('DefaultLoggerAdapter', () => {
27
+ let adapter;
28
+ beforeEach(() => {
29
+ jest.clearAllMocks();
30
+ // Mock timestamp for deterministic tests
31
+ jest.spyOn(Log_1.Log, 'timestamp').mockReturnValue('2023-01-01T00:00:00.000Z');
32
+ adapter = new DefaultLoggerAdapter_1.DefaultLoggerAdapter('TEST', Log_1.LogLevel.DEBUG);
33
+ });
34
+ it('should initialize and set the log level', () => {
35
+ expect(loglevel_1.default.setLevel).toHaveBeenCalledWith(Log_1.LogLevel.DEBUG);
36
+ });
37
+ it('should log debug messages with yellow color', () => {
38
+ adapter.debug('debug message');
39
+ expect(loglevel_1.default.debug).toHaveBeenCalledWith('2023-01-01T00:00:00.000Z - [TEST] debug message');
40
+ expect(chalk_1.default.yellow).toHaveBeenCalled();
41
+ });
42
+ it('should log info messages with green color', () => {
43
+ adapter.info('info message');
44
+ expect(loglevel_1.default.info).toHaveBeenCalledWith('2023-01-01T00:00:00.000Z - [TEST] info message');
45
+ expect(chalk_1.default.green).toHaveBeenCalled();
46
+ });
47
+ it('should log multiple arguments correctly', () => {
48
+ adapter.info('msg1', 'msg2', { key: 'val' });
49
+ expect(loglevel_1.default.info).toHaveBeenCalledWith(expect.stringContaining('msg1 msg2 {"key":"val"}'));
50
+ });
51
+ it('should log warn messages with red color', () => {
52
+ adapter.warn('warn message');
53
+ expect(loglevel_1.default.warn).toHaveBeenCalledWith('2023-01-01T00:00:00.000Z - [TEST] warn message');
54
+ expect(chalk_1.default.red).toHaveBeenCalled();
55
+ });
56
+ it('should log error messages with bgRedBright color', () => {
57
+ adapter.error('error message');
58
+ expect(loglevel_1.default.error).toHaveBeenCalledWith('2023-01-01T00:00:00.000Z - [TEST] error message');
59
+ expect(chalk_1.default.bgRedBright).toHaveBeenCalled();
60
+ });
61
+ it('should log trace messages with grey color', () => {
62
+ adapter.trace('trace message');
63
+ expect(loglevel_1.default.trace).toHaveBeenCalledWith('2023-01-01T00:00:00.000Z - [TEST] trace message');
64
+ expect(chalk_1.default.grey).toHaveBeenCalled();
65
+ });
66
+ });
package/dist/Log.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ import { AbstractLoggerAdapter, LogLevel } from './AbstractLoggerAdapter';
2
+ export type LoggerRegistry<T extends AbstractLoggerAdapter> = {
3
+ [x: string]: T;
4
+ };
5
+ export declare class Log {
6
+ static defaultLogger: string;
7
+ protected static _loggers: LoggerRegistry<any>;
8
+ static timestamp(): string;
9
+ static addLogger(alias: string, logger?: AbstractLoggerAdapter, setDefault?: boolean): any;
10
+ static getLogger<T extends AbstractLoggerAdapter>(alias?: string): T;
11
+ static log(...messages: any[]): void;
12
+ static debug(...messages: any[]): void;
13
+ static warn(...messages: any[]): void;
14
+ static info(...messages: any[]): void;
15
+ static error(...messages: any[]): void;
16
+ static trace(...messages: any[]): void;
17
+ }
18
+ export { LogLevel };
19
+ //# sourceMappingURL=Log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Log.d.ts","sourceRoot":"","sources":["../src/Log.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAGzE,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,qBAAqB,IAAI;IAC3D,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAA;CAChB,CAAA;AACD,qBAAa,GAAG;IACb,MAAM,CAAC,aAAa,SAAa;IAEjC,SAAS,CAAC,MAAM,CAAC,QAAQ,EAAE,cAAc,CAAC,GAAG,CAAC,CAAK;IAGnD,MAAM,CAAC,SAAS;IAIhB,MAAM,CAAC,SAAS,CACb,KAAK,EAAE,MAAM,EACb,MAAM,GAAE,qBAAkD,EAC1D,UAAU,GAAE,OAAe;IAU9B,MAAM,CAAC,SAAS,CAAC,CAAC,SAAS,qBAAqB,EAC7C,KAAK,GAAE,MAA2B,GAClC,CAAC;IAgBJ,MAAM,CAAC,GAAG,CAAC,GAAG,QAAQ,EAAE,GAAG,EAAE,GAAG,IAAI;IAIpC,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ,EAAE,GAAG,EAAE,GAAG,IAAI;IAItC,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,EAAE,GAAG,EAAE,GAAG,IAAI;IAIrC,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,EAAE,GAAG,EAAE,GAAG,IAAI;IAIrC,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ,EAAE,GAAG,EAAE,GAAG,IAAI;IAItC,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ,EAAE,GAAG,EAAE,GAAG,IAAI;CAGxC;AAED,OAAO,EAAE,QAAQ,EAAE,CAAA"}
package/dist/Log.js ADDED
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LogLevel = exports.Log = void 0;
4
+ const AbstractLoggerAdapter_1 = require("./AbstractLoggerAdapter");
5
+ Object.defineProperty(exports, "LogLevel", { enumerable: true, get: function () { return AbstractLoggerAdapter_1.LogLevel; } });
6
+ const DefaultLoggerAdapter_1 = require("./DefaultLoggerAdapter");
7
+ class Log {
8
+ static defaultLogger = '@default';
9
+ static _loggers = {};
10
+ static timestamp() {
11
+ return new Date().toISOString();
12
+ }
13
+ static addLogger(alias, logger = new DefaultLoggerAdapter_1.DefaultLoggerAdapter(), setDefault = false) {
14
+ this._loggers[alias] = logger;
15
+ if (setDefault) {
16
+ this.defaultLogger = alias;
17
+ }
18
+ return this._loggers[alias];
19
+ }
20
+ static getLogger(alias = this.defaultLogger) {
21
+ if (alias === '@default' && !this._loggers['@default']) {
22
+ this._loggers['@default'] = new DefaultLoggerAdapter_1.DefaultLoggerAdapter();
23
+ }
24
+ if (this._loggers[alias]) {
25
+ return this._loggers[alias];
26
+ }
27
+ else {
28
+ throw new Error(`Unknown logger alias: '${alias}'`);
29
+ }
30
+ }
31
+ static log(...messages) {
32
+ return Log.getLogger().log(...messages);
33
+ }
34
+ static debug(...messages) {
35
+ return Log.getLogger().debug(...messages);
36
+ }
37
+ static warn(...messages) {
38
+ return Log.getLogger().warn(...messages);
39
+ }
40
+ static info(...messages) {
41
+ return Log.getLogger().info(...messages);
42
+ }
43
+ static error(...messages) {
44
+ return Log.getLogger().error(...messages);
45
+ }
46
+ static trace(...messages) {
47
+ return Log.getLogger().trace(...messages);
48
+ }
49
+ }
50
+ exports.Log = Log;
51
+ //# sourceMappingURL=Log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Log.js","sourceRoot":"","sources":["../src/Log.ts"],"names":[],"mappings":";;;AAAA,mEAAyE;AAwEhE,yFAxEuB,gCAAQ,OAwEvB;AAvEjB,iEAA6D;AAK7D,MAAa,GAAG;IACb,MAAM,CAAC,aAAa,GAAG,UAAU,CAAA;IAEvB,MAAM,CAAC,QAAQ,GAAwB,EAAE,CAAA;IAGnD,MAAM,CAAC,SAAS;QACb,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;IAClC,CAAC;IAED,MAAM,CAAC,SAAS,CACb,KAAa,EACb,SAAgC,IAAI,2CAAoB,EAAE,EAC1D,aAAsB,KAAK;QAE3B,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,MAAM,CAAA;QAC7B,IAAI,UAAU,EAAE,CAAC;YACd,IAAI,CAAC,aAAa,GAAG,KAAK,CAAA;QAC7B,CAAC;QAED,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC9B,CAAC;IAED,MAAM,CAAC,SAAS,CACb,QAAgB,IAAI,CAAC,aAAa;QAElC,IAAI,KAAK,KAAK,UAAU,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YACtD,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,IAAI,2CAAoB,EAAE,CAAA;QACzD,CAAC;QAED,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACxB,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;QAC9B,CAAC;aAAM,CAAC;YACL,MAAM,IAAI,KAAK,CAAC,0BAA0B,KAAK,GAAG,CAAC,CAAA;QACtD,CAAC;IACJ,CAAC;IAMD,MAAM,CAAC,GAAG,CAAC,GAAG,QAAe;QAC1B,OAAO,GAAG,CAAC,SAAS,EAAE,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAA;IAC1C,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,GAAG,QAAe;QAC5B,OAAO,GAAG,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC,CAAA;IAC5C,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,GAAG,QAAe;QAC3B,OAAO,GAAG,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAA;IAC3C,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,GAAG,QAAe;QAC3B,OAAO,GAAG,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAA;IAC3C,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,GAAG,QAAe;QAC5B,OAAO,GAAG,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC,CAAA;IAC5C,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,GAAG,QAAe;QAC5B,OAAO,GAAG,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC,CAAA;IAC5C,CAAC;;AA/DJ,kBAgEC"}
@@ -2,3 +2,4 @@ import { Log, LogLevel } from './Log';
2
2
  import { AbstractLoggerAdapter } from './AbstractLoggerAdapter';
3
3
  import { DefaultLoggerAdapter } from './DefaultLoggerAdapter';
4
4
  export { Log, LogLevel, AbstractLoggerAdapter, DefaultLoggerAdapter };
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AACrC,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAA;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAE7D,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,CAAA"}
@@ -8,3 +8,4 @@ const AbstractLoggerAdapter_1 = require("./AbstractLoggerAdapter");
8
8
  Object.defineProperty(exports, "AbstractLoggerAdapter", { enumerable: true, get: function () { return AbstractLoggerAdapter_1.AbstractLoggerAdapter; } });
9
9
  const DefaultLoggerAdapter_1 = require("./DefaultLoggerAdapter");
10
10
  Object.defineProperty(exports, "DefaultLoggerAdapter", { enumerable: true, get: function () { return DefaultLoggerAdapter_1.DefaultLoggerAdapter; } });
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,+BAAqC;AAI5B,oFAJA,SAAG,OAIA;AAAE,yFAJA,cAAQ,OAIA;AAHtB,mEAA+D;AAGvC,sGAHf,6CAAqB,OAGe;AAF7C,iEAA6D;AAEd,qGAFtC,2CAAoB,OAEsC"}
package/package.json CHANGED
@@ -1,38 +1,39 @@
1
1
  {
2
- "name": "@quatrain/log",
3
- "version": "1.0.2",
4
- "description": "Logger for business apps",
5
- "main": "lib/index.js",
6
- "types": "lib/index.d.ts",
7
- "files": [
8
- "lib/",
9
- "README.md"
10
- ],
11
- "author": "Quatrain Développement SAS <developers@quatrain.com>",
12
- "license": "MIT",
13
- "devDependencies": {
14
- "@tsconfig/recommended": "^1.0.1",
15
- "@types/jest": "^27.0.3",
16
- "@types/node": "^22.10.1",
17
- "jest": "^27.4.7",
18
- "jest-node-exports-resolver": "^1.1.6",
19
- "trace-unhandled": "^2.0.1",
20
- "ts-jest": "^27.1.2",
21
- "ts-node": "^10.4.0",
22
- "typescript": "^5.1.5"
23
- },
24
- "dependencies": {
25
- "loglevel": "^1.9.2"
26
- },
27
- "scripts": {
28
- "test-ci": "jest --runInBand",
29
- "build": "tsc",
30
- "wbuild": "tsc --watch",
31
- "bump-to": "yarn version",
32
- "hash": "node ../../bin/hashFolder.js",
33
- "hash:persist": "yarn hash > .hash_latest.txt",
34
- "hash:compare": "yarn hash > .hash_newest.txt && cmp -s .hash_latest.txt .hash_newest.txt",
35
- "publish": "yarn hash:compare || yarn publish:process",
36
- "publish:process": "yarn version patch && yarn build && yarn npm publish --access public && yarn hash:persist"
37
- }
38
- }
2
+ "name": "@quatrain/log",
3
+ "version": "1.1.1",
4
+ "description": "Logger for business apps",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist/",
9
+ "src/",
10
+ "README.md"
11
+ ],
12
+ "author": "Quatrain Développement SAS <developers@quatrain.com>",
13
+ "license": "AGPL-3.0-only",
14
+ "devDependencies": {
15
+ "@tsconfig/recommended": "^1.0.1",
16
+ "@types/jest": "^30.0.0",
17
+ "@types/node": "^22.10.1",
18
+ "jest": "^30.2.0",
19
+ "trace-unhandled": "^2.0.1",
20
+ "ts-jest": "^29.4.1",
21
+ "ts-node": "^10.9.1",
22
+ "typescript": "^5.2.2"
23
+ },
24
+ "dependencies": {
25
+ "chalk": "^4.1.2",
26
+ "loglevel": "^1.9.2"
27
+ },
28
+ "scripts": {
29
+ "test-ci": "jest --runInBand",
30
+ "build": "tsc",
31
+ "wbuild": "tsc --watch",
32
+ "bump-to": "yarn version",
33
+ "hash": "node ../../bin/hashFolder.js",
34
+ "hash:persist": "yarn hash > .hash_latest.txt",
35
+ "hash:compare": "yarn hash > .hash_newest.txt && cmp -s .hash_latest.txt .hash_newest.txt",
36
+ "release": "yarn hash:compare || yarn release:process",
37
+ "release:process": "yarn version patch && yarn build && npm publish --access public && yarn hash:persist"
38
+ }
39
+ }
@@ -0,0 +1,90 @@
1
+ export enum LogLevel {
2
+ TRACE = 0,
3
+ DEBUG = 1,
4
+ INFO = 2,
5
+ WARN = 3,
6
+ ERROR = 4,
7
+ SILENT = 5,
8
+ }
9
+
10
+ export interface LoggerType {
11
+ log(message: string): void
12
+ debug(message: string): void
13
+ warn(message: string): void
14
+ info(message: string): void
15
+ error(message: string): void
16
+ trace(message: string): void
17
+ }
18
+
19
+ export const loglevelNames = [
20
+ 'TRACE',
21
+ 'DEBUG',
22
+ 'INFO',
23
+ 'WARN',
24
+ 'ERROR',
25
+ 'SILENT',
26
+ ]
27
+
28
+ export abstract class AbstractLoggerAdapter implements LoggerType {
29
+ protected _me: string = ''
30
+ protected _logLevel: LogLevel = LogLevel.WARN
31
+ protected _logger: any = undefined
32
+
33
+ constructor(prefix = '', level: LogLevel = LogLevel.WARN) {
34
+ this._me = prefix
35
+ }
36
+
37
+ logLevel(level: LogLevel) {
38
+ this._logLevel = level
39
+ }
40
+
41
+ formatLogMessage = (
42
+ messages: any[],
43
+ loglevel: LogLevel = LogLevel.INFO
44
+ ): string => {
45
+ // Flatten nested arrays (from rest parameter spreading)
46
+ const flatMessages = messages.flat()
47
+
48
+ const prefix = `${new Date().toISOString()} - [${this._me}]`
49
+ const strs = flatMessages.map((message: any) => {
50
+ if (message instanceof Error) {
51
+ return message.stack || message.message
52
+ }
53
+ if (typeof message === 'object' && message !== null) {
54
+ return JSON.stringify(message)
55
+ }
56
+ return String(message)
57
+ })
58
+
59
+ return `${prefix} ${strs.join(' ')}`
60
+ }
61
+
62
+ /**
63
+ * Log message using defined logger
64
+ * @param message string | object
65
+ * @param level string
66
+ */
67
+ log(...messages: any[]): void {
68
+ throw new Error(`This method needs to be implemtend in child class`)
69
+ }
70
+
71
+ debug(...messages: any[]): void {
72
+ throw new Error(`This method needs to be implemtend in child class`)
73
+ }
74
+
75
+ warn(...messages: any[]): void {
76
+ throw new Error(`This method needs to be implemtend in child class`)
77
+ }
78
+
79
+ info(...messages: any[]): void {
80
+ throw new Error(`This method needs to be implemtend in child class`)
81
+ }
82
+
83
+ error(...messages: any[]): void {
84
+ throw new Error(`This method needs to be implemtend in child class`)
85
+ }
86
+
87
+ trace(...messages: any[]): void {
88
+ throw new Error(`This method needs to be implemtend in child class`)
89
+ }
90
+ }
@@ -0,0 +1,103 @@
1
+ import { DefaultLoggerAdapter } from './DefaultLoggerAdapter'
2
+ import { LogLevel } from './AbstractLoggerAdapter'
3
+ import logger from 'loglevel'
4
+ import chalk from 'chalk'
5
+
6
+ jest.mock('loglevel', () => ({
7
+ setLevel: jest.fn(),
8
+ trace: jest.fn(),
9
+ debug: jest.fn(),
10
+ info: jest.fn(),
11
+ warn: jest.fn(),
12
+ error: jest.fn(),
13
+ log: jest.fn(),
14
+ }))
15
+
16
+ jest.mock('chalk', () => ({
17
+ grey: jest.fn((s) => s),
18
+ yellow: jest.fn((s) => s),
19
+ green: jest.fn((s) => s),
20
+ red: jest.fn((s) => s),
21
+ bgRedBright: jest.fn((s) => s),
22
+ }))
23
+
24
+ describe('DefaultLoggerAdapter', () => {
25
+ let adapter: DefaultLoggerAdapter
26
+
27
+ beforeEach(() => {
28
+ jest.clearAllMocks()
29
+ // Use Jest to mock the global Date
30
+ jest.useFakeTimers().setSystemTime(new Date('2023-01-01T00:00:00.000Z'))
31
+ adapter = new DefaultLoggerAdapter('TEST', LogLevel.DEBUG)
32
+ })
33
+
34
+ afterEach(() => {
35
+ jest.useRealTimers()
36
+ })
37
+
38
+ it('should initialize and set the log level', () => {
39
+ expect(logger.setLevel).toHaveBeenCalledWith(LogLevel.DEBUG)
40
+ })
41
+
42
+ it('should log debug messages with yellow color', () => {
43
+ adapter.debug('debug message')
44
+ expect(logger.debug).toHaveBeenCalledWith(
45
+ '2023-01-01T00:00:00.000Z - [TEST] debug message'
46
+ )
47
+ expect(chalk.yellow).toHaveBeenCalled()
48
+ })
49
+
50
+ it('should log info messages with green color', () => {
51
+ adapter.info('info message')
52
+ expect(logger.info).toHaveBeenCalledWith(
53
+ '2023-01-01T00:00:00.000Z - [TEST] info message'
54
+ )
55
+ expect(chalk.green).toHaveBeenCalled()
56
+ })
57
+
58
+ it('should log multiple arguments correctly', () => {
59
+ adapter.info('msg1', 'msg2', { key: 'val' })
60
+ expect(logger.info).toHaveBeenCalledWith(
61
+ expect.stringContaining('msg1 msg2 {"key":"val"}')
62
+ )
63
+ })
64
+
65
+ it('should format Error objects correctly', () => {
66
+ const error = new Error('test error')
67
+ adapter.error(error)
68
+ expect(logger.error).toHaveBeenCalledWith(
69
+ expect.stringContaining('test error')
70
+ )
71
+ })
72
+
73
+ it('should not mutate the input messages array', () => {
74
+ const input = ['original message']
75
+ adapter.info(input)
76
+ expect(input).toHaveLength(1)
77
+ expect(input[0]).toBe('original message')
78
+ })
79
+
80
+ it('should log warn messages with red color', () => {
81
+ adapter.warn('warn message')
82
+ expect(logger.warn).toHaveBeenCalledWith(
83
+ '2023-01-01T00:00:00.000Z - [TEST] warn message'
84
+ )
85
+ expect(chalk.red).toHaveBeenCalled()
86
+ })
87
+
88
+ it('should log error messages with bgRedBright color', () => {
89
+ adapter.error('error message')
90
+ expect(logger.error).toHaveBeenCalledWith(
91
+ '2023-01-01T00:00:00.000Z - [TEST] error message'
92
+ )
93
+ expect(chalk.bgRedBright).toHaveBeenCalled()
94
+ })
95
+
96
+ it('should log trace messages with grey color', () => {
97
+ adapter.trace('trace message')
98
+ expect(logger.trace).toHaveBeenCalledWith(
99
+ '2023-01-01T00:00:00.000Z - [TEST] trace message'
100
+ )
101
+ expect(chalk.grey).toHaveBeenCalled()
102
+ })
103
+ })
@@ -0,0 +1,66 @@
1
+ import { AbstractLoggerAdapter, LogLevel } from './AbstractLoggerAdapter'
2
+ import logger from 'loglevel'
3
+ import chalk from 'chalk'
4
+
5
+ export class DefaultLoggerAdapter extends AbstractLoggerAdapter {
6
+ constructor(prefix = '', level: LogLevel = LogLevel.DEBUG) {
7
+ super(prefix, level)
8
+ this._logger = logger
9
+ this._logger.setLevel(level)
10
+ }
11
+
12
+ /**
13
+ * Log message using defined logger
14
+ * @param messages array
15
+ * @param level LogLevel
16
+ */
17
+ log(messages: any[], level: LogLevel = this._logLevel): void {
18
+ const message = this.formatLogMessage(messages)
19
+
20
+ switch (level) {
21
+ case LogLevel.TRACE:
22
+ this._logger.trace(chalk.grey(message))
23
+ break
24
+ case LogLevel.DEBUG:
25
+ this._logger.debug(chalk.yellow(message))
26
+ break
27
+ case LogLevel.INFO:
28
+ this._logger.info(chalk.green(message))
29
+ break
30
+ case LogLevel.WARN:
31
+ this._logger.warn(chalk.red(message))
32
+ break
33
+ case LogLevel.ERROR:
34
+ this._logger.error(chalk.bgRedBright(message))
35
+ break
36
+ default:
37
+ this._logger.log(message)
38
+ break
39
+ }
40
+ }
41
+
42
+ logLevel(level: LogLevel): void {
43
+ super.logLevel(level)
44
+ this._logger.setLevel(level)
45
+ }
46
+
47
+ debug(...messages: any): void {
48
+ this.log(messages, LogLevel.DEBUG)
49
+ }
50
+
51
+ warn(...messages: any): void {
52
+ this.log(messages, LogLevel.WARN)
53
+ }
54
+
55
+ info(...messages: any): void {
56
+ this.log(messages, LogLevel.INFO)
57
+ }
58
+
59
+ error(...messages: any): void {
60
+ this.log(messages, LogLevel.ERROR)
61
+ }
62
+
63
+ trace(...messages: any): void {
64
+ this.log(messages, LogLevel.TRACE)
65
+ }
66
+ }
@@ -0,0 +1,45 @@
1
+ import { Log } from './Log'
2
+ import { DefaultLoggerAdapter } from './DefaultLoggerAdapter'
3
+
4
+ jest.mock('./DefaultLoggerAdapter')
5
+
6
+ describe('Log Class (Static Registry)', () => {
7
+ beforeEach(() => {
8
+ jest.clearAllMocks()
9
+ // Reset the internal registry for clean tests
10
+ // @ts-ignore - accessing protected member for testing
11
+ Log._loggers = {}
12
+ })
13
+
14
+ it('should lazily initialize the @default logger on first access', () => {
15
+ // Ensure registry is empty
16
+ // @ts-ignore
17
+ expect(Log._loggers['@default']).toBeUndefined()
18
+
19
+ const logger = Log.getLogger('@default')
20
+
21
+ expect(logger).toBeInstanceOf(DefaultLoggerAdapter)
22
+ expect(DefaultLoggerAdapter).toHaveBeenCalled()
23
+ })
24
+
25
+ it('should correctly proxy multiple arguments to the underlying adapter', () => {
26
+ const logger = Log.getLogger('@default')
27
+ const infoSpy = jest.spyOn(logger, 'info').mockImplementation(() => {})
28
+
29
+ Log.info('arg1', 'arg2', { data: 123 })
30
+
31
+ expect(infoSpy).toHaveBeenCalledWith('arg1', 'arg2', { data: 123 })
32
+ })
33
+
34
+ it('should allow adding a custom logger and setting it as default', () => {
35
+ const customLogger = new DefaultLoggerAdapter('CUSTOM')
36
+ Log.addLogger('custom', customLogger, true)
37
+
38
+ expect(Log.defaultLogger).toBe('custom')
39
+ expect(Log.getLogger()).toBe(customLogger)
40
+ })
41
+
42
+ it('should throw an error when requesting an unknown logger alias', () => {
43
+ expect(() => Log.getLogger('ghost-logger')).toThrow(/Unknown logger alias/)
44
+ })
45
+ })
package/src/Log.ts ADDED
@@ -0,0 +1,73 @@
1
+ import { AbstractLoggerAdapter, LogLevel } from './AbstractLoggerAdapter'
2
+ import { DefaultLoggerAdapter } from './DefaultLoggerAdapter'
3
+
4
+ export type LoggerRegistry<T extends AbstractLoggerAdapter> = {
5
+ [x: string]: T
6
+ }
7
+ export class Log {
8
+ static defaultLogger = '@default'
9
+
10
+ protected static _loggers: LoggerRegistry<any> = {}
11
+
12
+ // How timestamp are formatted
13
+ static timestamp() {
14
+ return new Date().toISOString()
15
+ }
16
+
17
+ static addLogger(
18
+ alias: string,
19
+ logger: AbstractLoggerAdapter = new DefaultLoggerAdapter(),
20
+ setDefault: boolean = false
21
+ ) {
22
+ this._loggers[alias] = logger
23
+ if (setDefault) {
24
+ this.defaultLogger = alias
25
+ }
26
+
27
+ return this._loggers[alias]
28
+ }
29
+
30
+ static getLogger<T extends AbstractLoggerAdapter>(
31
+ alias: string = this.defaultLogger
32
+ ): T {
33
+ if (alias === '@default' && !this._loggers['@default']) {
34
+ this._loggers['@default'] = new DefaultLoggerAdapter()
35
+ }
36
+
37
+ if (this._loggers[alias]) {
38
+ return this._loggers[alias]
39
+ } else {
40
+ throw new Error(`Unknown logger alias: '${alias}'`)
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Log message using defined logger
46
+ * @param message string | object
47
+ */
48
+ static log(...messages: any[]): void {
49
+ return Log.getLogger().log(...messages)
50
+ }
51
+
52
+ static debug(...messages: any[]): void {
53
+ return Log.getLogger().debug(...messages)
54
+ }
55
+
56
+ static warn(...messages: any[]): void {
57
+ return Log.getLogger().warn(...messages)
58
+ }
59
+
60
+ static info(...messages: any[]): void {
61
+ return Log.getLogger().info(...messages)
62
+ }
63
+
64
+ static error(...messages: any[]): void {
65
+ return Log.getLogger().error(...messages)
66
+ }
67
+
68
+ static trace(...messages: any[]): void {
69
+ return Log.getLogger().trace(...messages)
70
+ }
71
+ }
72
+
73
+ export { LogLevel }
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { Log, LogLevel } from './Log'
2
+ import { AbstractLoggerAdapter } from './AbstractLoggerAdapter'
3
+ import { DefaultLoggerAdapter } from './DefaultLoggerAdapter'
4
+
5
+ export { Log, LogLevel, AbstractLoggerAdapter, DefaultLoggerAdapter }
@@ -1,57 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AbstractLoggerAdapter = exports.loglevelNames = void 0;
4
- const Log_1 = require("./Log");
5
- exports.loglevelNames = [
6
- 'TRACE',
7
- 'DEBUG',
8
- 'INFO',
9
- 'WARN',
10
- 'ERROR',
11
- 'SILENT',
12
- ];
13
- class AbstractLoggerAdapter {
14
- constructor(prefix = '', level = Log_1.LogLevel.WARN) {
15
- this._me = '';
16
- this._logLevel = Log_1.LogLevel.WARN;
17
- this._logger = undefined;
18
- this.formatLogMessage = (messages, loglevel = 3) => {
19
- if (!Array.isArray(messages)) {
20
- messages = [messages];
21
- }
22
- messages.unshift(`${Log_1.Log.timestamp()} - [${this._me}]`);
23
- const strs = messages.map((message) => {
24
- return typeof message !== 'object' ? message : JSON.stringify(message);
25
- });
26
- return strs.join(' ');
27
- };
28
- this._me = prefix;
29
- }
30
- logLevel(level) {
31
- this._logLevel = level;
32
- }
33
- /**
34
- * Log message using defined logger
35
- * @param message string | object
36
- * @param level string
37
- */
38
- log(...messages) {
39
- throw new Error(`This method needs to be implemtend in child class`);
40
- }
41
- debug(message) {
42
- throw new Error(`This method needs to be implemtend in child class`);
43
- }
44
- warn(message) {
45
- throw new Error(`This method needs to be implemtend in child class`);
46
- }
47
- info(message) {
48
- throw new Error(`This method needs to be implemtend in child class`);
49
- }
50
- error(message) {
51
- throw new Error(`This method needs to be implemtend in child class`);
52
- }
53
- trace(message) {
54
- throw new Error(`This method needs to be implemtend in child class`);
55
- }
56
- }
57
- exports.AbstractLoggerAdapter = AbstractLoggerAdapter;
package/lib/Log.d.ts DELETED
@@ -1,29 +0,0 @@
1
- import { AbstractLoggerAdapter } from './AbstractLoggerAdapter';
2
- export declare enum LogLevel {
3
- TRACE = 0,
4
- DEBUG = 1,
5
- INFO = 2,
6
- WARN = 3,
7
- ERROR = 4,
8
- SILENT = 5
9
- }
10
- export type LoggerRegistry<T extends AbstractLoggerAdapter> = {
11
- [x: string]: T;
12
- };
13
- export declare class Log {
14
- static defaultLogger: string;
15
- protected static _loggers: LoggerRegistry<any>;
16
- static timestamp(): string;
17
- static addLogger(alias: string, logger?: AbstractLoggerAdapter, setDefault?: boolean): any;
18
- static getLogger<T extends AbstractLoggerAdapter>(alias?: string): T;
19
- /**
20
- * Log message using defined logger
21
- * @param message string | object
22
- */
23
- static log(message: any): void;
24
- static debug(message: any): void;
25
- static warn(message: any): void;
26
- static info(message: any): void;
27
- static error(message: any): void;
28
- static trace(message: any): void;
29
- }
package/lib/Log.js DELETED
@@ -1,61 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Log = exports.LogLevel = void 0;
4
- const DefaultLoggerAdapter_1 = require("./DefaultLoggerAdapter");
5
- var LogLevel;
6
- (function (LogLevel) {
7
- LogLevel[LogLevel["TRACE"] = 0] = "TRACE";
8
- LogLevel[LogLevel["DEBUG"] = 1] = "DEBUG";
9
- LogLevel[LogLevel["INFO"] = 2] = "INFO";
10
- LogLevel[LogLevel["WARN"] = 3] = "WARN";
11
- LogLevel[LogLevel["ERROR"] = 4] = "ERROR";
12
- LogLevel[LogLevel["SILENT"] = 5] = "SILENT";
13
- })(LogLevel || (exports.LogLevel = LogLevel = {}));
14
- class Log {
15
- // How timestamp are formatted
16
- static timestamp() {
17
- return new Date().toISOString();
18
- }
19
- static addLogger(alias, logger = new DefaultLoggerAdapter_1.DefaultLoggerAdapter(), setDefault = false) {
20
- this._loggers[alias] = logger;
21
- if (setDefault) {
22
- this.defaultLogger = alias;
23
- }
24
- return this._loggers[alias];
25
- }
26
- static getLogger(alias = this.defaultLogger) {
27
- if (this._loggers[alias]) {
28
- return this._loggers[alias];
29
- }
30
- else {
31
- throw new Error(`Unknown logger alias: '${alias}'`);
32
- }
33
- }
34
- /**
35
- * Log message using defined logger
36
- * @param message string | object
37
- */
38
- static log(message) {
39
- return Log.getLogger().log(message);
40
- }
41
- static debug(message) {
42
- return Log.getLogger().debug(message);
43
- }
44
- static warn(message) {
45
- return Log.getLogger().warn(message);
46
- }
47
- static info(message) {
48
- return Log.getLogger().info(message);
49
- }
50
- static error(message) {
51
- return Log.getLogger().error(message);
52
- }
53
- static trace(message) {
54
- return Log.getLogger().trace(message);
55
- }
56
- }
57
- exports.Log = Log;
58
- Log.defaultLogger = '@default';
59
- Log._loggers = {
60
- '@default': new DefaultLoggerAdapter_1.DefaultLoggerAdapter(),
61
- };