ntlogger 2.3.0 → 2.4.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.
package/lib/logger.js CHANGED
@@ -1,220 +1,222 @@
1
- /**
2
- * NightTimeLogger: A Custom Ready-To-Go Logging Wrapper Built on Winston
3
- *
4
- * This utility is a customized wrapper around the Winston logging library.
5
- * It offers different log levels and formats and can be easily integrated into
6
- * any Node.js project.
7
- *
8
- * Author: Kevin R. (Kvrnn#6940, Syntax#5569)
9
- * Date: 03/31/2024
10
- * Current Version: 2.1.2
11
- * License: GPL-3.0
12
- */
13
- const winston = require('winston');
14
- const crypto = require('crypto');
15
-
16
- const { initPlugins, compilePluginComponents} = require('../plugins/index.js');
17
-
18
- // Define a map to hold the logger instances by their location
19
- const loggerInstances = new Map();
20
-
21
- const customSettings = {
22
- levels: {
23
- internal: 6,
24
- trace: 5,
25
- debug: 4,
26
- info: 3,
27
- warn: 2,
28
- error: 1,
29
- fatal: 0,
30
- },
31
- colors: {
32
- internal: '\x1b[93m', // Bright yellow
33
- trace: '\x1b[90m', // Light gray
34
- debug: '\x1b[37m', // White
35
- info: '\x1b[32m', // Green
36
- warn: '\x1b[33m', // Yellow
37
- error: '\x1b[31m', // Red
38
- fatal: '\x1b[35m', // Magenta
39
- },
40
- };
41
-
42
-
43
- const randomBrightColor = () => {
44
- // Configuration variables
45
- const minLuminanceThreshold = 0.03928;
46
- const linearConversionDivider = 12.92;
47
- const linearConversionBase = 1.055;
48
- const linearConversionExponent = 2.4;
49
- const luminanceCoefficients = { r: 0.2126, g: 0.7152, b: 0.0722 };
50
- // const minContrastRatio = 4.5; // WCAG AA standard for normal text
51
- const minContrastRatio = 2.5; // I like 2.5 will change if any issues arise
52
-
53
- // Helper function to convert sRGB to linear RGB
54
- const convertToLinear = (colorComponent) => {
55
- const scaledComponent = colorComponent / 255;
56
- return scaledComponent <= minLuminanceThreshold ? scaledComponent / linearConversionDivider
57
- : Math.pow((scaledComponent + 0.055) / linearConversionBase, linearConversionExponent);
58
- };
59
-
60
- while (true) {
61
- // Generate random RGB values
62
- const [red, green, blue] = [0, 1, 2].map(() => Math.floor(Math.random() * 256));
63
-
64
- // Convert to linear RGB values
65
- const [rLinear, gLinear, bLinear] = [red, green, blue].map(convertToLinear);
66
-
67
- // Calculate luminance
68
- const luminance = luminanceCoefficients.r * rLinear + luminanceCoefficients.g * gLinear + luminanceCoefficients.b * bLinear;
69
-
70
- // Calculate contrast ratio against black (which has a luminance of 0)
71
- const contrastRatio = (luminance + 0.05) / 0.05;
72
-
73
- // Check if the color meets the contrast ratio criterion
74
- if (contrastRatio >= minContrastRatio) {
75
- return `#${[red, green, blue].map(x => x.toString(16).padStart(2, '0')).join('')}`;
76
- }
77
- }
78
- };
79
-
80
- const generateSessionId = () => {
81
- return crypto.createHash('sha256').update(Date.now().toString()).digest('hex');
82
- };
83
-
84
- const sessionId = generateSessionId(); // Generate session ID once
85
- const color = randomBrightColor();
86
-
87
- // Custom console formatter with random color for session ID and set color for log level and message
88
- const consoleFormatter = (config) => winston.format.combine(
89
- winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
90
- winston.format.printf(({ timestamp, level, message, ...meta }) => {
91
- const shortSessionId = sessionId.substring(sessionId.length - 6); // Get the last 6 characters of the session ID
92
- const paddedLevel = level.padEnd(8); // Pad the level to ensure consistent spacing
93
-
94
- // Retrieve the corresponding ANSI color code for the level from custom settings
95
- const levelColor = customSettings.colors[level] || ''; // Default to no color if the level is unknown
96
- const resetCode = '\x1b[0m';
97
-
98
- // Apply the color to the padded level and the session ID
99
- // Note: The session ID color remains based on the 'color' variable
100
- return `${timestamp} [${levelColor}${paddedLevel}${resetCode}] [\x1b[38;2;${parseInt(color.substring(1, 3), 16)};${parseInt(color.substring(3, 5), 16)};${parseInt(color.substring(5, 7), 16)}mID: ${shortSessionId}${resetCode}] [${meta.location || 'Unknown'}]: ${levelColor}${message}${resetCode}`;
101
- }),
102
- );
103
-
104
- // Formatter for file logging which shows the full session ID
105
- const fileFormatter = (config) => winston.format.combine(
106
- winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
107
- winston.format.printf(({ timestamp, level, message, ...meta }) => {
108
- const paddedLevel = level.padEnd(8); // Pad the level to ensure consistent spacing
109
-
110
- return `${timestamp} [${paddedLevel}] [ID: ${sessionId}] [${meta.location || 'Unknown'}]: ${message} ${
111
- Object.keys(meta).length ? JSON.stringify(meta, null, 2) : ''
112
- }`;
113
- }),
114
- );
115
-
116
- const createLoggerInstance = (location = "Unknown", config = {}, transports) => {
117
- // Configuration options
118
- const {
119
- level = 'info',
120
- console = true,
121
- debug = false,
122
- file = true,
123
- filename = `${location}.log`,
124
- path = './logs',
125
- maxSize = 1048576,
126
- maxFiles = 5,
127
- timestamp = true
128
- } = config;
129
-
130
- let logTransport = [].filter(Boolean);
131
-
132
- if (console) {
133
- const consoleFormat = consoleFormatter(config);
134
-
135
- logTransport.push(new winston.transports.Console({ format: consoleFormat }),);
136
- }
137
-
138
- if (file) {
139
- const fileFormat = fileFormatter(config);
140
-
141
- // If you want really verbose logging, such as, log files per new logging instance with each level, follow this example
142
- // logTransport.push(
143
- // new winston.transports.File({ filename: `${path}/${filename}`, level: 'fatal', format:fileFormat ,maxsize: maxSize, maxFiles: maxFiles }),
144
- // new winston.transports.File({ filename: `${path}/${filename}`, level: 'error', format:fileFormat ,maxsize: maxSize, maxFiles: maxFiles }),
145
- // new winston.transports.File({ filename: `${path}/${filename}`, format:fileFormat, maxsize: maxSize, maxFiles: maxFiles }),
146
- // );
147
-
148
- // If you want to logger instance of same level to the same file, follow this example (default)
149
- logTransport.push(
150
- new winston.transports.File({ filename: `${path}/fatal.log`, level: 'fatal', format: fileFormat, maxsize: maxSize, maxFiles: maxFiles }),
151
- new winston.transports.File({ filename: `${path}/error.log`, level: 'error', format: fileFormat, maxsize: maxSize, maxFiles: maxFiles }),
152
- new winston.transports.File({ filename: `${path}/combined.log`, format: fileFormat, maxsize: maxSize, maxFiles: maxFiles }),
153
- );
154
- }
155
- if (transports) {logTransport = logTransport.concat
156
- (transports);}
157
-
158
- // Create a new logger instance
159
- const logger = winston.createLogger({
160
- level: level,
161
- levels: customSettings.levels,
162
- transports: logTransport,
163
- defaultMeta: {
164
- location,
165
- timeCreated: new Date().toLocaleTimeString('en-US', {hour12: true, hour: '2-digit', minute: '2-digit', second: '2-digit', fractionalSecondDigits: 3})
166
- }
167
- });
168
-
169
- // Cache the newly created logger instance
170
- loggerInstances.set(location, logger);
171
-
172
- if (debug === true) {
173
- // Extract RGB components from the color string
174
- const rgb = {
175
- r: parseInt(color.slice(1, 3), 16),
176
- g: parseInt(color.slice(3, 5), 16),
177
- b: parseInt(color.slice(5, 7), 16)
178
- };
179
-
180
- // Construct ANSI escape code for the RGB color
181
- const colorCode = `\x1b[38;2;${rgb.r};${rgb.g};${rgb.b}m`;
182
- const resetCode = `\x1b[0m`;
183
-
184
- logger.internal(`Logger instance created from ${location} with session ID: ${colorCode}${sessionId.slice(-6)}${resetCode}`);
185
- logger.internal(`Logger instance created from ${location} with color: ${colorCode}${color}${resetCode}`);
186
- logger.internal(`Logger instance created from ${location} with log level: ${colorCode}${logger.level}${resetCode}`);
187
- }
188
-
189
- return logger;
190
- }
191
-
192
- /***
193
- * Logger function
194
- *
195
- * @param {string} location - The location of the logger instance
196
- * @param {object} config - Configuration options for the logger instance
197
- * @returns {object} - The logger instance
198
- */
199
- const logger = (location = "Unknown", config = {}) => {
200
- // Check if a logger for the given location already exists
201
- if (loggerInstances.has(location)) {
202
- // If it does, grab the existing logger instance from the map
203
- const logger = loggerInstances.get(location);
204
-
205
- logger.internal(`Logger instance retrieved for location ${location}`); // Log the retrieval of the logger instance
206
- return logger;
207
- }
208
-
209
- let transports = null;
210
- // Initialize plugins if they are provided in the configuration
211
- if (config.plugins) {
212
- transports = initPlugins(config.plugins)
213
- }
214
-
215
- // If not, create a new logger instance for the location
216
- return createLoggerInstance(location, config, transports);
217
- };
218
-
219
- // Export a function to create or return the existing logger instance
1
+ /**
2
+ * NightTimeLogger: A Custom Ready-To-Go Logging Wrapper Built on Winston
3
+ *
4
+ * This utility is a customized wrapper around the Winston logging library.
5
+ * It offers different log levels and formats and can be easily integrated into
6
+ * any Node.js project.
7
+ *
8
+ * Author: Kevin R. (Kvrnn#6940, Syntax#5569)
9
+ * Date: 03/31/2024
10
+ * Current Version: 2.1.2
11
+ * License: GPL-3.0
12
+ */
13
+ const winston = require('winston');
14
+ const crypto = require('crypto');
15
+
16
+ const { initPlugins } = require('../plugins/index.js');
17
+
18
+ // Define a map to hold the logger instances by their location
19
+ const loggerInstances = new Map();
20
+
21
+ const customSettings = {
22
+ levels: {
23
+ internal: 6,
24
+ trace: 5,
25
+ debug: 4,
26
+ info: 3,
27
+ warn: 2,
28
+ error: 1,
29
+ fatal: 0,
30
+ },
31
+ colors: {
32
+ internal: '\x1b[93m', // Bright yellow
33
+ trace: '\x1b[90m', // Light gray
34
+ debug: '\x1b[37m', // White
35
+ info: '\x1b[32m', // Green
36
+ warn: '\x1b[33m', // Yellow
37
+ error: '\x1b[31m', // Red
38
+ fatal: '\x1b[35m', // Magenta
39
+ },
40
+ };
41
+
42
+
43
+ const randomBrightColor = () => {
44
+ // Configuration variables
45
+ const minLuminanceThreshold = 0.03928;
46
+ const linearConversionDivider = 12.92;
47
+ const linearConversionBase = 1.055;
48
+ const linearConversionExponent = 2.4;
49
+ const luminanceCoefficients = { r: 0.2126, g: 0.7152, b: 0.0722 };
50
+ // const minContrastRatio = 4.5; // WCAG AA standard for normal text
51
+ const minContrastRatio = 2.5; // I like 2.5 will change if any issues arise
52
+
53
+ // Helper function to convert sRGB to linear RGB
54
+ const convertToLinear = (colorComponent) => {
55
+ const scaledComponent = colorComponent / 255;
56
+ return scaledComponent <= minLuminanceThreshold ? scaledComponent / linearConversionDivider
57
+ : Math.pow((scaledComponent + 0.055) / linearConversionBase, linearConversionExponent);
58
+ };
59
+
60
+ while (true) {
61
+ // Generate random RGB values
62
+ const [red, green, blue] = [0, 1, 2].map(() => Math.floor(Math.random() * 256));
63
+
64
+ // Convert to linear RGB values
65
+ const [rLinear, gLinear, bLinear] = [red, green, blue].map(convertToLinear);
66
+
67
+ // Calculate luminance
68
+ const luminance = luminanceCoefficients.r * rLinear + luminanceCoefficients.g * gLinear + luminanceCoefficients.b * bLinear;
69
+
70
+ // Calculate contrast ratio against black (which has a luminance of 0)
71
+ const contrastRatio = (luminance + 0.05) / 0.05;
72
+
73
+ // Check if the color meets the contrast ratio criterion
74
+ if (contrastRatio >= minContrastRatio) {
75
+ return `#${[red, green, blue].map(x => x.toString(16).padStart(2, '0')).join('')}`;
76
+ }
77
+ }
78
+ };
79
+
80
+ const generateSessionId = () => {
81
+ return crypto.createHash('sha256').update(Date.now().toString()).digest('hex');
82
+ };
83
+
84
+ const sessionId = generateSessionId(); // Generate session ID once
85
+ const color = randomBrightColor();
86
+
87
+ // Custom console formatter with random color for session ID and set color for log level and message
88
+ const consoleFormatter = (config) => winston.format.combine(
89
+ winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
90
+ winston.format.printf(({ timestamp, level, message, ...meta }) => {
91
+ const shortSessionId = sessionId.substring(sessionId.length - 6); // Get the last 6 characters of the session ID
92
+ const paddedLevel = level.padEnd(8); // Pad the level to ensure consistent spacing
93
+
94
+ // Retrieve the corresponding ANSI color code for the level from custom settings
95
+ const levelColor = customSettings.colors[level] || ''; // Default to no color if the level is unknown
96
+ const resetCode = '\x1b[0m';
97
+
98
+ // Apply the color to the padded level and the session ID
99
+ // Note: The session ID color remains based on the 'color' variable
100
+ return `${timestamp} [${levelColor}${paddedLevel}${resetCode}] [\x1b[38;2;${parseInt(color.substring(1, 3), 16)};${parseInt(color.substring(3, 5), 16)};${parseInt(color.substring(5, 7), 16)}mID: ${shortSessionId}${resetCode}] [${meta.location || 'Unknown'}]: ${levelColor}${message}${resetCode}`;
101
+ }),
102
+ );
103
+
104
+ // Formatter for file logging which shows the full session ID
105
+ const fileFormatter = (config) => winston.format.combine(
106
+ winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
107
+ winston.format.printf(({ timestamp, level, message, ...meta }) => {
108
+ const paddedLevel = level.padEnd(8); // Pad the level to ensure consistent spacing
109
+
110
+ return `${timestamp} [${paddedLevel}] [ID: ${sessionId}] [${meta.location || 'Unknown'}]: ${message} ${
111
+ Object.keys(meta).length ? JSON.stringify(meta, null, 2) : ''
112
+ }`;
113
+ }),
114
+ );
115
+
116
+ const createLoggerInstance = (location = "Unknown", config = {}, transports) => {
117
+ // Configuration options
118
+ const {
119
+ level = 'info',
120
+ console = true,
121
+ debug = false,
122
+ file = true,
123
+ filename = `${location}.log`,
124
+ path = './logs',
125
+ maxSize = 1048576,
126
+ maxFiles = 5,
127
+ timestamp = true,
128
+ skipCache = false,
129
+ } = config;
130
+
131
+ let logTransport = [].filter(Boolean);
132
+
133
+ if (console) {
134
+ const consoleFormat = consoleFormatter(config);
135
+
136
+ logTransport.push(new winston.transports.Console({ format: consoleFormat }),);
137
+ }
138
+
139
+ if (file) {
140
+ const fileFormat = fileFormatter(config);
141
+
142
+ // If you want really verbose logging, such as, log files per new logging instance with each level, follow this example
143
+ // logTransport.push(
144
+ // new winston.transports.File({ filename: `${path}/${filename}`, level: 'fatal', format:fileFormat ,maxsize: maxSize, maxFiles: maxFiles }),
145
+ // new winston.transports.File({ filename: `${path}/${filename}`, level: 'error', format:fileFormat ,maxsize: maxSize, maxFiles: maxFiles }),
146
+ // new winston.transports.File({ filename: `${path}/${filename}`, format:fileFormat, maxsize: maxSize, maxFiles: maxFiles }),
147
+ // );
148
+
149
+ // If you want to logger instance of same level to the same file, follow this example (default)
150
+ logTransport.push(
151
+ new winston.transports.File({ filename: `${path}/fatal.log`, level: 'fatal', format: fileFormat, maxsize: maxSize, maxFiles: maxFiles }),
152
+ new winston.transports.File({ filename: `${path}/error.log`, level: 'error', format: fileFormat, maxsize: maxSize, maxFiles: maxFiles }),
153
+ new winston.transports.File({ filename: `${path}/combined.log`, format: fileFormat, maxsize: maxSize, maxFiles: maxFiles }),
154
+ );
155
+ }
156
+ if (transports) {logTransport = logTransport.concat
157
+ (transports);}
158
+
159
+ // Create a new logger instance
160
+ const logger = winston.createLogger({
161
+ level: level,
162
+ levels: customSettings.levels,
163
+ transports: logTransport,
164
+ defaultMeta: {
165
+ location,
166
+ ID: sessionId,
167
+ timeCreated: new Date().toLocaleTimeString('en-US', {hour12: true, hour: '2-digit', minute: '2-digit', second: '2-digit', fractionalSecondDigits: 3})
168
+ }
169
+ });
170
+
171
+ // Cache the newly created logger instance
172
+ loggerInstances.set(location, logger);
173
+
174
+ if (debug === true) {
175
+ // Extract RGB components from the color string
176
+ const rgb = {
177
+ r: parseInt(color.slice(1, 3), 16),
178
+ g: parseInt(color.slice(3, 5), 16),
179
+ b: parseInt(color.slice(5, 7), 16)
180
+ };
181
+
182
+ // Construct ANSI escape code for the RGB color
183
+ const colorCode = `\x1b[38;2;${rgb.r};${rgb.g};${rgb.b}m`;
184
+ const resetCode = `\x1b[0m`;
185
+
186
+ logger.internal(`Logger instance created from ${location} with session ID: ${colorCode}${sessionId.slice(-6)}${resetCode}`);
187
+ logger.internal(`Logger instance created from ${location} with color: ${colorCode}${color}${resetCode}`);
188
+ logger.internal(`Logger instance created from ${location} with log level: ${colorCode}${logger.level}${resetCode}`);
189
+ }
190
+
191
+ return logger;
192
+ }
193
+
194
+ /***
195
+ * Logger function
196
+ *
197
+ * @param {string} location - The location of the logger instance
198
+ * @param {object} config - Configuration options for the logger instance
199
+ * @returns {object} - The logger instance
200
+ */
201
+ const logger = (location = "Unknown", config = {}) => {
202
+ // Check if a logger for the given location already exists
203
+ if (loggerInstances.has(location) && !config.skipCache) {
204
+ // If it does, grab the existing logger instance from the map
205
+ const logger = loggerInstances.get(location);
206
+
207
+ logger.internal(`Logger instance retrieved for location ${location}`); // Log the retrieval of the logger instance
208
+ return logger;
209
+ }
210
+
211
+ let transports = null;
212
+ // Initialize plugins if they are provided in the configuration
213
+ if (config.plugins) {
214
+ transports = initPlugins(config.plugins)
215
+ }
216
+
217
+ // If not, create a new logger instance for the location
218
+ return createLoggerInstance(location, config, transports);
219
+ };
220
+
221
+ // Export a function to create or return the existing logger instance
220
222
  module.exports = logger;
package/package.json CHANGED
@@ -1,38 +1,43 @@
1
- {
2
- "name": "ntlogger",
3
- "version": "2.3.0",
4
- "repository": {
5
- "type": "git",
6
- "url": "git+https://github.com/NightSquawk/NightTimeLogger.git"
7
- },
8
- "author": "Kevin R. (Kvrnn#6940, Syntax#5569)",
9
- "description": "A Custom Ready-To-Go Logging Wrapper Built on Winston",
10
- "keywords": [
11
- "logger",
12
- "winston wrapper",
13
- "logger wrapper",
14
- "wrapper",
15
- "winston logger",
16
- "custom logger",
17
- "logging",
18
- "log management",
19
- "logging utility",
20
- "sentry compatible",
21
- "mysql compatible"
22
- ],
23
- "files": [
24
- "index.js",
25
- "lib/*.js"
26
- ],
27
- "license": "GPL-3.0",
28
- "dependencies": {
29
- "@sentry/node": "^8.17.0",
30
- "mysql2": "^3.10.2",
31
- "winston": "^3.13.1",
32
- "winston-transport": "^4.7.1"
33
- },
34
- "devDependencies": {
35
- "@sentry/profiling-node": "^8.17.0",
36
- "dotenv": "^16.4.5"
37
- }
38
- }
1
+ {
2
+ "name": "ntlogger",
3
+ "version": "2.4.0",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "git+https://github.com/NightSquawk/NightTimeLogger.git"
7
+ },
8
+ "author": "Kevin R. (Kvrnn#6940, Syntax#5569)",
9
+ "description": "A Custom Ready-To-Go Logging Wrapper Built on Winston",
10
+ "keywords": [
11
+ "logger",
12
+ "winston wrapper",
13
+ "logger wrapper",
14
+ "wrapper",
15
+ "winston logger",
16
+ "custom logger",
17
+ "logging",
18
+ "log management",
19
+ "logging utility",
20
+ "sentry compatible",
21
+ "mysql compatible"
22
+ ],
23
+ "files": [
24
+ "index.js",
25
+ "lib/*.js",
26
+ "plugins/*.js"
27
+ ],
28
+ "license": "GPL-3.0",
29
+ "dependencies": {
30
+ "@sentry/node": "^8.17.0",
31
+ "mysql2": "^3.10.2",
32
+ "winston": "^3.13.1",
33
+ "winston-transport": "^4.7.1"
34
+ },
35
+ "devDependencies": {
36
+ "@sentry/profiling-node": "^8.17.0",
37
+ "dotenv": "^16.4.5",
38
+ "jest": "^29.0.0"
39
+ },
40
+ "scripts": {
41
+ "test": "jest"
42
+ }
43
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * @file /plugins/index.js
3
+ * @description Initializes any plugins required by the application.
4
+ */
5
+
6
+ // Available plugins
7
+ const plugins = {
8
+ Sentry: require('./sentry'),
9
+ MySQL : require('./mysql'),
10
+ Jest: require('./jest'),
11
+
12
+ // TODO: Implement the following plugins
13
+ // mongodb: require('./mongodb'),
14
+ // redis: require('./redis'),
15
+ };
16
+
17
+ function checkPluginAvailability(pluginName) {
18
+ if (!plugins[pluginName]) {
19
+ throw new Error(`Plugin ${pluginName} is not available\nAvailable plugins: ${Object.keys(plugins).join(', ')}`);
20
+ }
21
+ }
22
+
23
+ function getPluginTransport(pluginName) {
24
+ checkPluginAvailability(pluginName);
25
+ return plugins[pluginName].transport;
26
+ }
27
+
28
+ /**
29
+ * Initializes plugins named and configured in the configuration object.
30
+ * @param {Object} config - The configuration object for the plugins
31
+ * @param {string} config.sentry - The configuration object for Sentry
32
+ * @returns {Array} - An array of plugin components
33
+ */
34
+ function initPlugins(config = {}) {
35
+ let pluginTransports = [];
36
+ try {
37
+ for (let plugin of config) {
38
+ if (!plugin.name) {
39
+ throw new Error('Plugin name is required');
40
+ } else {
41
+ checkPluginAvailability(plugin.name);
42
+ let customTransportClass = getPluginTransport(plugin.name);
43
+ pluginTransports.push(new customTransportClass(plugin.config));
44
+ }
45
+ }
46
+ return pluginTransports;
47
+ } catch (err) {
48
+ console.error('Error initializing plugins:', err);
49
+ }
50
+ }
51
+
52
+ module.exports = {
53
+ initPlugins,
54
+ };
@@ -0,0 +1,60 @@
1
+ /**
2
+ * @file /plugins/jest.js
3
+ * @description Stores log messages in memory for Jest testing.
4
+ */
5
+
6
+ const Transport = require('winston-transport');
7
+
8
+ /**
9
+ * Custom Winston transport for storing logs in memory for Jest assertions.
10
+ */
11
+ class JestTransport extends Transport {
12
+ constructor(opts = {}) {
13
+ super(opts);
14
+
15
+ this.name = 'JestTransport';
16
+ this.disc = 'Stores log messages in memory for Jest testing.';
17
+ this.logMessages = [];
18
+ }
19
+
20
+ /**
21
+ * Logs the message and stores it in memory.
22
+ * @param {Object} info - The log information object containing the message, level, and other metadata.
23
+ * @param {Function} callback - Callback function to indicate logging completion.
24
+ */
25
+ log(info, callback) {
26
+ setImmediate(() => {
27
+ this.emit('logged', info);
28
+ });
29
+
30
+ const { level, message, ...meta } = info;
31
+
32
+ // Store the log message in memory
33
+ this.logMessages.push({ level, message, meta });
34
+
35
+ callback();
36
+ }
37
+
38
+ /**
39
+ * Retrieve stored log messages filtered by a specific level, if provided.
40
+ * @param {string} [level] - The log level to filter messages by (e.g., 'info', 'error').
41
+ * @returns {Array} Array of filtered or all log messages.
42
+ */
43
+ getMessages(level) {
44
+ if (level) {
45
+ return this.logMessages.filter(log => log.level === level);
46
+ }
47
+ return this.logMessages;
48
+ }
49
+
50
+ /**
51
+ * Clear all stored log messages.
52
+ */
53
+ clearMessages() {
54
+ this.logMessages = [];
55
+ }
56
+ }
57
+
58
+ module.exports = {
59
+ transport: JestTransport,
60
+ };