@vvlad1973/simple-logger 1.2.0

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.
@@ -0,0 +1,111 @@
1
+ /* JSDoc prettify.js theme */
2
+
3
+ /* plain text */
4
+ .pln {
5
+ color: #000000;
6
+ font-weight: normal;
7
+ font-style: normal;
8
+ }
9
+
10
+ /* string content */
11
+ .str {
12
+ color: #006400;
13
+ font-weight: normal;
14
+ font-style: normal;
15
+ }
16
+
17
+ /* a keyword */
18
+ .kwd {
19
+ color: #000000;
20
+ font-weight: bold;
21
+ font-style: normal;
22
+ }
23
+
24
+ /* a comment */
25
+ .com {
26
+ font-weight: normal;
27
+ font-style: italic;
28
+ }
29
+
30
+ /* a type name */
31
+ .typ {
32
+ color: #000000;
33
+ font-weight: normal;
34
+ font-style: normal;
35
+ }
36
+
37
+ /* a literal value */
38
+ .lit {
39
+ color: #006400;
40
+ font-weight: normal;
41
+ font-style: normal;
42
+ }
43
+
44
+ /* punctuation */
45
+ .pun {
46
+ color: #000000;
47
+ font-weight: bold;
48
+ font-style: normal;
49
+ }
50
+
51
+ /* lisp open bracket */
52
+ .opn {
53
+ color: #000000;
54
+ font-weight: bold;
55
+ font-style: normal;
56
+ }
57
+
58
+ /* lisp close bracket */
59
+ .clo {
60
+ color: #000000;
61
+ font-weight: bold;
62
+ font-style: normal;
63
+ }
64
+
65
+ /* a markup tag name */
66
+ .tag {
67
+ color: #006400;
68
+ font-weight: normal;
69
+ font-style: normal;
70
+ }
71
+
72
+ /* a markup attribute name */
73
+ .atn {
74
+ color: #006400;
75
+ font-weight: normal;
76
+ font-style: normal;
77
+ }
78
+
79
+ /* a markup attribute value */
80
+ .atv {
81
+ color: #006400;
82
+ font-weight: normal;
83
+ font-style: normal;
84
+ }
85
+
86
+ /* a declaration */
87
+ .dec {
88
+ color: #000000;
89
+ font-weight: bold;
90
+ font-style: normal;
91
+ }
92
+
93
+ /* a variable name */
94
+ .var {
95
+ color: #000000;
96
+ font-weight: normal;
97
+ font-style: normal;
98
+ }
99
+
100
+ /* a function name */
101
+ .fun {
102
+ color: #000000;
103
+ font-weight: bold;
104
+ font-style: normal;
105
+ }
106
+
107
+ /* Specify class=linenums on a pre to get line numbering */
108
+ ol.linenums {
109
+ margin-top: 0;
110
+ margin-bottom: 0;
111
+ }
@@ -0,0 +1,132 @@
1
+ /* Tomorrow Theme */
2
+ /* Original theme - https://github.com/chriskempson/tomorrow-theme */
3
+ /* Pretty printing styles. Used with prettify.js. */
4
+ /* SPAN elements with the classes below are added by prettyprint. */
5
+ /* plain text */
6
+ .pln {
7
+ color: #4d4d4c; }
8
+
9
+ @media screen {
10
+ /* string content */
11
+ .str {
12
+ color: #718c00; }
13
+
14
+ /* a keyword */
15
+ .kwd {
16
+ color: #8959a8; }
17
+
18
+ /* a comment */
19
+ .com {
20
+ color: #8e908c; }
21
+
22
+ /* a type name */
23
+ .typ {
24
+ color: #4271ae; }
25
+
26
+ /* a literal value */
27
+ .lit {
28
+ color: #f5871f; }
29
+
30
+ /* punctuation */
31
+ .pun {
32
+ color: #4d4d4c; }
33
+
34
+ /* lisp open bracket */
35
+ .opn {
36
+ color: #4d4d4c; }
37
+
38
+ /* lisp close bracket */
39
+ .clo {
40
+ color: #4d4d4c; }
41
+
42
+ /* a markup tag name */
43
+ .tag {
44
+ color: #c82829; }
45
+
46
+ /* a markup attribute name */
47
+ .atn {
48
+ color: #f5871f; }
49
+
50
+ /* a markup attribute value */
51
+ .atv {
52
+ color: #3e999f; }
53
+
54
+ /* a declaration */
55
+ .dec {
56
+ color: #f5871f; }
57
+
58
+ /* a variable name */
59
+ .var {
60
+ color: #c82829; }
61
+
62
+ /* a function name */
63
+ .fun {
64
+ color: #4271ae; } }
65
+ /* Use higher contrast and text-weight for printable form. */
66
+ @media print, projection {
67
+ .str {
68
+ color: #060; }
69
+
70
+ .kwd {
71
+ color: #006;
72
+ font-weight: bold; }
73
+
74
+ .com {
75
+ color: #600;
76
+ font-style: italic; }
77
+
78
+ .typ {
79
+ color: #404;
80
+ font-weight: bold; }
81
+
82
+ .lit {
83
+ color: #044; }
84
+
85
+ .pun, .opn, .clo {
86
+ color: #440; }
87
+
88
+ .tag {
89
+ color: #006;
90
+ font-weight: bold; }
91
+
92
+ .atn {
93
+ color: #404; }
94
+
95
+ .atv {
96
+ color: #060; } }
97
+ /* Style */
98
+ /*
99
+ pre.prettyprint {
100
+ background: white;
101
+ font-family: Consolas, Monaco, 'Andale Mono', monospace;
102
+ font-size: 12px;
103
+ line-height: 1.5;
104
+ border: 1px solid #ccc;
105
+ padding: 10px; }
106
+ */
107
+
108
+ /* Specify class=linenums on a pre to get line numbering */
109
+ ol.linenums {
110
+ margin-top: 0;
111
+ margin-bottom: 0; }
112
+
113
+ /* IE indents via margin-left */
114
+ li.L0,
115
+ li.L1,
116
+ li.L2,
117
+ li.L3,
118
+ li.L4,
119
+ li.L5,
120
+ li.L6,
121
+ li.L7,
122
+ li.L8,
123
+ li.L9 {
124
+ /* */ }
125
+
126
+ /* Alternate shading for lines */
127
+ li.L1,
128
+ li.L3,
129
+ li.L5,
130
+ li.L7,
131
+ li.L9 {
132
+ /* */ }
@@ -0,0 +1,44 @@
1
+ /* reset css */
2
+ html, body, div, span, applet, object, iframe,
3
+ h1, h2, h3, h4, h5, h6, p, blockquote, pre,
4
+ a, abbr, acronym, address, big, cite, code,
5
+ del, dfn, em, img, ins, kbd, q, s, samp,
6
+ small, strike, strong, sub, sup, tt, var,
7
+ b, u, i, center,
8
+ dl, dt, dd, ol, ul, li,
9
+ fieldset, form, label, legend,
10
+ table, caption, tbody, tfoot, thead, tr, th, td,
11
+ article, aside, canvas, details, embed,
12
+ figure, figcaption, footer, header, hgroup,
13
+ menu, nav, output, ruby, section, summary,
14
+ time, mark, audio, video {
15
+ margin: 0;
16
+ padding: 0;
17
+ border: 0;
18
+ font-size: 100%;
19
+ font: inherit;
20
+ vertical-align: baseline;
21
+ }
22
+ /* HTML5 display-role reset for older browsers */
23
+ article, aside, details, figcaption, figure,
24
+ footer, header, hgroup, menu, nav, section {
25
+ display: block;
26
+ }
27
+ body {
28
+ line-height: 1;
29
+ }
30
+ ol, ul {
31
+ list-style: none;
32
+ }
33
+ blockquote, q {
34
+ quotes: none;
35
+ }
36
+ blockquote:before, blockquote:after,
37
+ q:before, q:after {
38
+ content: '';
39
+ content: none;
40
+ }
41
+ table {
42
+ border-collapse: collapse;
43
+ border-spacing: 0;
44
+ }
package/jsdoc.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "source": {
3
+ "include": ["./src/"],
4
+ "includePattern": "\\.(ts)$",
5
+ "excludePattern": "(^|\\/|\\\\)node_modules"
6
+ },
7
+ "plugins": ["node_modules/better-docs/typescript"],
8
+ "opts": {
9
+ "destination": "./docs",
10
+ "recurse": true,
11
+ "template": "node_modules/better-docs"
12
+ }
13
+ }
14
+
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@vvlad1973/simple-logger",
3
+ "version": "1.2.0",
4
+ "main": "dist/index.js",
5
+ "types": "dist/index.d.ts",
6
+ "type": "module",
7
+ "scripts": {
8
+ "test": "npm run build && node --loader ts-node/esm --test",
9
+ "build": "tsc",
10
+ "doc": "jsdoc -c jsdoc.json"
11
+ },
12
+ "author": "Vladislav Vnukovskiy <vvlad1973@gmail.com>",
13
+ "license": "MIT with Commercial Use",
14
+ "description": "A simple logging utility for JavaScript applications, providing customizable log levels and flexible external logger integration.",
15
+ "devDependencies": {
16
+ "@types/node": "^20.16.1",
17
+ "better-docs": "^2.7.3",
18
+ "jsdoc": "^4.0.3",
19
+ "ts-node": "^10.9.2",
20
+ "typescript": "^5.5.4"
21
+ },
22
+ "dependencies": {
23
+ "vvlad1973-utils": "^2.6.0"
24
+ }
25
+ }
@@ -0,0 +1,188 @@
1
+ import { describe, it, beforeEach } from 'node:test';
2
+ import assert from 'assert';
3
+ import SimpleLogger from '../simple-logger.js';
4
+
5
+ type MockFunction = {
6
+ (...args: any[]): void;
7
+ mock: { calls: any[][] };
8
+ };
9
+
10
+ type NoopFunction = {
11
+ (...args: any[]): void;
12
+ mock: { calls: any[][] };
13
+ };
14
+
15
+ function createMockLoggerMethod(): MockFunction {
16
+ const calls: any[][] = [];
17
+ const mockFn = function (...args: any[]) {
18
+ calls.push(args);
19
+ } as MockFunction;
20
+ mockFn.mock = { calls };
21
+ return mockFn;
22
+ }
23
+
24
+ function createNoopMethod(): MockFunction {
25
+ const calls: any[][] = [];
26
+ const mockFn = function (...args: any[]) {} as MockFunction;
27
+ mockFn.mock = { calls };
28
+ return mockFn;
29
+ }
30
+ type ExternalLoggerMock = {
31
+ trace: MockFunction | NoopFunction;
32
+ debug: MockFunction | NoopFunction;
33
+ info: MockFunction | NoopFunction;
34
+ warn: MockFunction | NoopFunction;
35
+ error: MockFunction | NoopFunction;
36
+ fatal: MockFunction | NoopFunction;
37
+ silent: MockFunction | NoopFunction;
38
+ };
39
+
40
+ function createExternalLoggerMock(): ExternalLoggerMock {
41
+ return {
42
+ trace: createMockLoggerMethod(),
43
+ debug: createMockLoggerMethod(),
44
+ info: createMockLoggerMethod(),
45
+ warn: createMockLoggerMethod(),
46
+ error: createMockLoggerMethod(),
47
+ fatal: createMockLoggerMethod(),
48
+ silent:createMockLoggerMethod(),
49
+ };
50
+ }
51
+
52
+ describe('SimpleLogger', () => {
53
+ let logger: SimpleLogger;
54
+ let externalLoggerMock: ExternalLoggerMock;
55
+
56
+ beforeEach(() => {
57
+ externalLoggerMock = createExternalLoggerMock();
58
+ logger = new SimpleLogger('info', externalLoggerMock);
59
+ });
60
+
61
+ it('should call external logger methods appropriately', () => {
62
+ logger.trace('trace message');
63
+ logger.debug('debug message');
64
+ logger.info('info message');
65
+ logger.warn('warn message');
66
+ logger.error('error message');
67
+ logger.fatal('fatal message');
68
+
69
+ assert.strictEqual(externalLoggerMock.trace.mock.calls.length, 0);
70
+ assert.strictEqual(externalLoggerMock.debug.mock.calls.length, 0);
71
+ assert.strictEqual(externalLoggerMock.info.mock.calls.length, 1);
72
+ assert.strictEqual(
73
+ externalLoggerMock.info.mock.calls[0][0],
74
+ 'info message'
75
+ );
76
+ assert.strictEqual(externalLoggerMock.warn.mock.calls.length, 1);
77
+ assert.strictEqual(
78
+ externalLoggerMock.warn.mock.calls[0][0],
79
+ 'warn message'
80
+ );
81
+ assert.strictEqual(externalLoggerMock.error.mock.calls.length, 1);
82
+ assert.strictEqual(
83
+ externalLoggerMock.error.mock.calls[0][0],
84
+ 'error message'
85
+ );
86
+ assert.strictEqual(externalLoggerMock.fatal.mock.calls.length, 1);
87
+ assert.strictEqual(
88
+ externalLoggerMock.fatal.mock.calls[0][0],
89
+ 'fatal message'
90
+ );
91
+ });
92
+
93
+ it('should log function start and end', () => {
94
+ logger.setLevel('TRACE');
95
+
96
+ function testFunction() {
97
+ logger.logFunctionStart();
98
+ logger.logFunctionEnd();
99
+ }
100
+
101
+ testFunction();
102
+
103
+ assert.strictEqual(externalLoggerMock.trace.mock.calls.length, 2);
104
+ assert.strictEqual(
105
+ externalLoggerMock.trace.mock.calls[0][0],
106
+ 'Function start: testFunction'
107
+ );
108
+ assert.strictEqual(
109
+ externalLoggerMock.trace.mock.calls[1][0],
110
+ 'Function end: testFunction'
111
+ );
112
+ });
113
+ });
114
+
115
+ describe('SimpleLogger2', () => {
116
+ let logger: SimpleLogger;
117
+ let externalLoggerMock: ExternalLoggerMock;
118
+
119
+ beforeEach(() => {
120
+ externalLoggerMock = createExternalLoggerMock();
121
+ logger = new SimpleLogger('info', externalLoggerMock);
122
+ });
123
+
124
+ it('should use console as default external logger', () => {
125
+ const defaultLogger = new SimpleLogger();
126
+ assert.ok(defaultLogger);
127
+ });
128
+
129
+ it('should set the correct log level', () => {
130
+ logger.setLevel('DEBUG');
131
+ assert.notStrictEqual(logger.trace, () => {});
132
+ assert.strictEqual(typeof logger.debug, 'function');
133
+ assert.strictEqual(typeof logger.info, 'function');
134
+ });
135
+
136
+ it('should call external logger methods appropriately', () => {
137
+ logger.trace('trace message');
138
+ logger.debug('debug message');
139
+ logger.info('info message');
140
+ logger.warn('warn message');
141
+ logger.error('error message');
142
+ logger.fatal('fatal message');
143
+
144
+ assert.strictEqual(externalLoggerMock.trace.mock.calls.length, 0); // because default level is INFO
145
+ assert.strictEqual(externalLoggerMock.debug.mock.calls.length, 0); // because default level is INFO
146
+ assert.strictEqual(externalLoggerMock.info.mock.calls.length, 1);
147
+ assert.strictEqual(
148
+ externalLoggerMock.info.mock.calls[0][0],
149
+ 'info message'
150
+ );
151
+ assert.strictEqual(externalLoggerMock.warn.mock.calls.length, 1);
152
+ assert.strictEqual(
153
+ externalLoggerMock.warn.mock.calls[0][0],
154
+ 'warn message'
155
+ );
156
+ assert.strictEqual(externalLoggerMock.error.mock.calls.length, 1);
157
+ assert.strictEqual(
158
+ externalLoggerMock.error.mock.calls[0][0],
159
+ 'error message'
160
+ );
161
+ assert.strictEqual(externalLoggerMock.fatal.mock.calls.length, 1);
162
+ assert.strictEqual(
163
+ externalLoggerMock.fatal.mock.calls[0][0],
164
+ 'fatal message'
165
+ );
166
+ });
167
+
168
+ it('should log function start and end', () => {
169
+ logger.setLevel('TRACE');
170
+
171
+ function testFunction() {
172
+ logger.logFunctionStart();
173
+ logger.logFunctionEnd();
174
+ }
175
+
176
+ testFunction();
177
+
178
+ assert.strictEqual(externalLoggerMock.trace.mock.calls.length, 2);
179
+ assert.strictEqual(
180
+ externalLoggerMock.trace.mock.calls[0][0],
181
+ 'Function start: testFunction'
182
+ );
183
+ assert.strictEqual(
184
+ externalLoggerMock.trace.mock.calls[1][0],
185
+ 'Function end: testFunction'
186
+ );
187
+ });
188
+ });
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { default } from './simple-logger.js'
2
+ export * from './simple-logger.js'
@@ -0,0 +1,188 @@
1
+ /**
2
+ * A module for simple logging with various logging levels.
3
+ * @module SimpleLogger
4
+ */
5
+
6
+ import { getCallerName } from 'vvlad1973-utils';
7
+
8
+ export type ExternalLogger = {
9
+ trace?: LogFn;
10
+ debug?: LogFn;
11
+ info?: LogFn;
12
+ warn?: LogFn;
13
+ error?: LogFn;
14
+ fatal?: LogFn;
15
+ silent?: LogFn;
16
+ level?: string;
17
+ };
18
+
19
+ interface LogFn {
20
+ <T extends object>(obj: T, msg?: string, ...args: any[]): void;
21
+ (obj: unknown, msg?: string, ...args: any[]): void;
22
+ (msg: string, ...args: any[]): void;
23
+ }
24
+
25
+ const loggerLevels = [
26
+ 'trace',
27
+ 'debug',
28
+ 'info',
29
+ 'warn',
30
+ 'error',
31
+ 'fatal',
32
+ 'silent',
33
+ ] as const;
34
+ export type LoggerLevel = (typeof loggerLevels)[number];
35
+ /**
36
+ * A simple logger class with various logging levels.
37
+ * @class
38
+ * @param {string} [level='info'] - Initial logging level.
39
+ * @param {ExternalLogger | undefined | null} [externalLogger=null] - Optional external logger to use.
40
+ */
41
+ export class SimpleLogger {
42
+ #levels = loggerLevels;
43
+ #currentLevel: number = 2;
44
+ #logger: ExternalLogger | Console = console;
45
+
46
+ isExternal: boolean = false;
47
+
48
+ constructor(level?: LoggerLevel, externalLogger?: ExternalLogger) {
49
+ if (externalLogger && this.isValidLogger(externalLogger)) {
50
+ this.setExternalLogger(externalLogger, level);
51
+ } else {
52
+ this.setLevel(level ?? 'info');
53
+ }
54
+ }
55
+
56
+ trace: LogFn = (...args: any[]) => {};
57
+ debug: LogFn = (...args: any[]) => {};
58
+ info: LogFn = (...args: any[]) => {};
59
+ warn: LogFn = (...args: any[]) => {};
60
+ error: LogFn = (...args: any[]) => {};
61
+ fatal: LogFn = (...args: any[]) => {};
62
+ silent: LogFn = (...args: any[]) => {};
63
+
64
+ /**
65
+ * Updates the logging methods based on the current logging level.
66
+ * @private
67
+ */
68
+ private updateLoggingMethods() {
69
+ try {
70
+ const noop = (...args: any[]) => {};
71
+ this.trace =
72
+ this.#currentLevel <= 0 ? this.getExternalLoggerMethod('trace') : noop;
73
+ this.debug =
74
+ this.#currentLevel <= 1 ? this.getExternalLoggerMethod('debug') : noop;
75
+ this.info =
76
+ this.#currentLevel <= 2 ? this.getExternalLoggerMethod('info') : noop;
77
+ this.warn =
78
+ this.#currentLevel <= 3 ? this.getExternalLoggerMethod('warn') : noop;
79
+ this.error =
80
+ this.#currentLevel <= 4 ? this.getExternalLoggerMethod('error') : noop;
81
+ this.fatal =
82
+ this.#currentLevel <= 5
83
+ ? this.getExternalLoggerMethod('fatal', 'error')
84
+ : noop;
85
+ this.silent = noop;
86
+ } catch (error) {
87
+ console.error(error);
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Checks if the provided logger is valid.
93
+ * @private
94
+ * @param {any} logger - The logger to check.
95
+ * @returns {boolean} - True if the logger is valid, otherwise false.
96
+ */
97
+ private isValidLogger(logger: any): boolean {
98
+ return loggerLevels.every((method) => {
99
+ return typeof logger[method] === 'function';
100
+ });
101
+ }
102
+
103
+ /**
104
+ * Gets the external logger method for a specific level.
105
+ * @param {string} method - The logger method to get.
106
+ * @param {string} [fallbackMethod] - The fallback logger method to get.
107
+ * @returns {LoggerMethod} - The logger method or a no-op function.
108
+ * @private
109
+ */
110
+ private getExternalLoggerMethod(
111
+ method: keyof ExternalLogger,
112
+ fallbackMethod?: keyof ExternalLogger
113
+ ): LogFn {
114
+ try {
115
+ const loggerMethod = (this.#logger as ExternalLogger)[method];
116
+
117
+ if (typeof loggerMethod === 'function') {
118
+ return loggerMethod.bind(this.#logger);
119
+ } else if (typeof fallbackMethod !== 'undefined') {
120
+ const fallbackLoggerMethod = (this.#logger as ExternalLogger)[
121
+ fallbackMethod
122
+ ];
123
+
124
+ if (typeof fallbackLoggerMethod === 'function') {
125
+ return fallbackLoggerMethod.bind(this.#logger);
126
+ }
127
+ }
128
+ return () => {};
129
+ } catch (error) {
130
+ console.error(error);
131
+ return () => {};
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Sets the logging level.
137
+ * @param {string} level - The logging level to set.
138
+ */
139
+ public setLevel(level: string) {
140
+ const index = this.#levels.indexOf(level.toLowerCase() as LoggerLevel);
141
+ if (index !== -1) {
142
+ this.#currentLevel = index;
143
+
144
+ if (typeof (this.#logger as ExternalLogger).level === 'string') {
145
+ (this.#logger as ExternalLogger).level = level;
146
+ }
147
+ this.updateLoggingMethods();
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Sets an external logger to be used.
153
+ * @param {ExternalLogger | undefined | null} logger - The external logger to set.
154
+ */
155
+ public setExternalLogger(
156
+ logger: ExternalLogger | undefined | null,
157
+ level?: LoggerLevel
158
+ ) {
159
+ if (logger && this.isValidLogger(logger)) {
160
+ this.#logger = logger;
161
+ this.isExternal = true;
162
+ } else {
163
+ this.#logger = console;
164
+ this.isExternal = false;
165
+ }
166
+ this.setLevel(level ?? logger?.level ?? this.#levels[this.#currentLevel]);
167
+ }
168
+
169
+ /**
170
+ * Logs the start of a function.
171
+ * @param {string} [functionName] - Optional function name to log.
172
+ */
173
+ public logFunctionStart(functionName?: string) {
174
+ const callerName = functionName || getCallerName();
175
+ this.trace(`Function start: ${callerName}`);
176
+ }
177
+
178
+ /**
179
+ * Logs the end of a function.
180
+ * @param {string} [functionName] - Optional function name to log.
181
+ */
182
+ public logFunctionEnd(functionName?: string) {
183
+ const callerName = functionName || getCallerName();
184
+ this.trace(`Function end: ${callerName}`);
185
+ }
186
+ }
187
+
188
+ export default SimpleLogger;