@mimik/mim-logger 1.0.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/README.md +168 -0
- package/configuration/config.js +78 -0
- package/eslint.config.js +79 -0
- package/index.js +279 -0
- package/lib/common.js +122 -0
- package/lib/consoleTransport.js +66 -0
- package/lib/formatLib.js +198 -0
- package/package.json +31 -0
- package/test/logger.spec.js +80 -0
package/README.md
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
<a name="MimLogger"></a>
|
|
2
|
+
|
|
3
|
+
## MimLogger
|
|
4
|
+
Logger instance
|
|
5
|
+
|
|
6
|
+
**Kind**: global class
|
|
7
|
+
|
|
8
|
+
* [MimLogger](#MimLogger)
|
|
9
|
+
* [.init(contextOrConfig)](#MimLogger+init) ⇒ [<code>MimLogger</code>](#MimLogger)
|
|
10
|
+
* [.getConfig()](#MimLogger+getConfig) ⇒ <code>Object</code>
|
|
11
|
+
* [._log(level, message, ...args)](#MimLogger+_log)
|
|
12
|
+
* [.log(level, message, ...args)](#MimLogger+log)
|
|
13
|
+
* [.error(message, ...args)](#MimLogger+error)
|
|
14
|
+
* [.warn(message, ...args)](#MimLogger+warn)
|
|
15
|
+
* [.info(message, ...args)](#MimLogger+info)
|
|
16
|
+
* [.verbose(message, ...args)](#MimLogger+verbose)
|
|
17
|
+
* [.debug(message, ...args)](#MimLogger+debug)
|
|
18
|
+
* [.silly(message, ...args)](#MimLogger+silly)
|
|
19
|
+
* [.flush()](#MimLogger+flush) ⇒ <code>Promise.<void></code>
|
|
20
|
+
* [.flushAndExit(exitCode)](#MimLogger+flushAndExit) ⇒ <code>Promise.<void></code>
|
|
21
|
+
* [.serializeError(err)](#MimLogger+serializeError) ⇒ <code>Object</code>
|
|
22
|
+
|
|
23
|
+
<a name="MimLogger+init"></a>
|
|
24
|
+
|
|
25
|
+
### mimLogger.init(contextOrConfig) ⇒ [<code>MimLogger</code>](#MimLogger)
|
|
26
|
+
Initializes the logger with context or configuration
|
|
27
|
+
|
|
28
|
+
**Kind**: instance method of [<code>MimLogger</code>](#MimLogger)
|
|
29
|
+
**Returns**: [<code>MimLogger</code>](#MimLogger) - Logger instance for chaining
|
|
30
|
+
|
|
31
|
+
| Param | Type | Description |
|
|
32
|
+
| --- | --- | --- |
|
|
33
|
+
| contextOrConfig | <code>Object</code> | mimik context object or config with env |
|
|
34
|
+
|
|
35
|
+
<a name="MimLogger+getConfig"></a>
|
|
36
|
+
|
|
37
|
+
### mimLogger.getConfig() ⇒ <code>Object</code>
|
|
38
|
+
Gets current configuration, refreshing from context if available
|
|
39
|
+
|
|
40
|
+
**Kind**: instance method of [<code>MimLogger</code>](#MimLogger)
|
|
41
|
+
**Returns**: <code>Object</code> - Current configuration
|
|
42
|
+
<a name="MimLogger+_log"></a>
|
|
43
|
+
|
|
44
|
+
### mimLogger.\_log(level, message, ...args)
|
|
45
|
+
Internal log method
|
|
46
|
+
|
|
47
|
+
**Kind**: instance method of [<code>MimLogger</code>](#MimLogger)
|
|
48
|
+
|
|
49
|
+
| Param | Type | Description |
|
|
50
|
+
| --- | --- | --- |
|
|
51
|
+
| level | <code>string</code> | Log level |
|
|
52
|
+
| message | <code>string</code> \| <code>Object</code> | Log message or object with message property |
|
|
53
|
+
| ...args | <code>\*</code> | Additional arguments (metadata, correlationId) |
|
|
54
|
+
|
|
55
|
+
<a name="MimLogger+log"></a>
|
|
56
|
+
|
|
57
|
+
### mimLogger.log(level, message, ...args)
|
|
58
|
+
Generic log method with explicit level
|
|
59
|
+
|
|
60
|
+
**Kind**: instance method of [<code>MimLogger</code>](#MimLogger)
|
|
61
|
+
|
|
62
|
+
| Param | Type | Description |
|
|
63
|
+
| --- | --- | --- |
|
|
64
|
+
| level | <code>string</code> | Log level |
|
|
65
|
+
| message | <code>string</code> \| <code>Object</code> | Log message |
|
|
66
|
+
| ...args | <code>\*</code> | Additional arguments |
|
|
67
|
+
|
|
68
|
+
<a name="MimLogger+error"></a>
|
|
69
|
+
|
|
70
|
+
### mimLogger.error(message, ...args)
|
|
71
|
+
Log error message
|
|
72
|
+
|
|
73
|
+
**Kind**: instance method of [<code>MimLogger</code>](#MimLogger)
|
|
74
|
+
|
|
75
|
+
| Param | Type | Description |
|
|
76
|
+
| --- | --- | --- |
|
|
77
|
+
| message | <code>string</code> \| <code>Object</code> | Log message |
|
|
78
|
+
| ...args | <code>\*</code> | Additional arguments |
|
|
79
|
+
|
|
80
|
+
<a name="MimLogger+warn"></a>
|
|
81
|
+
|
|
82
|
+
### mimLogger.warn(message, ...args)
|
|
83
|
+
Log warning message
|
|
84
|
+
|
|
85
|
+
**Kind**: instance method of [<code>MimLogger</code>](#MimLogger)
|
|
86
|
+
|
|
87
|
+
| Param | Type | Description |
|
|
88
|
+
| --- | --- | --- |
|
|
89
|
+
| message | <code>string</code> \| <code>Object</code> | Log message |
|
|
90
|
+
| ...args | <code>\*</code> | Additional arguments |
|
|
91
|
+
|
|
92
|
+
<a name="MimLogger+info"></a>
|
|
93
|
+
|
|
94
|
+
### mimLogger.info(message, ...args)
|
|
95
|
+
Log info message
|
|
96
|
+
|
|
97
|
+
**Kind**: instance method of [<code>MimLogger</code>](#MimLogger)
|
|
98
|
+
|
|
99
|
+
| Param | Type | Description |
|
|
100
|
+
| --- | --- | --- |
|
|
101
|
+
| message | <code>string</code> \| <code>Object</code> | Log message |
|
|
102
|
+
| ...args | <code>\*</code> | Additional arguments |
|
|
103
|
+
|
|
104
|
+
<a name="MimLogger+verbose"></a>
|
|
105
|
+
|
|
106
|
+
### mimLogger.verbose(message, ...args)
|
|
107
|
+
Log verbose message
|
|
108
|
+
|
|
109
|
+
**Kind**: instance method of [<code>MimLogger</code>](#MimLogger)
|
|
110
|
+
|
|
111
|
+
| Param | Type | Description |
|
|
112
|
+
| --- | --- | --- |
|
|
113
|
+
| message | <code>string</code> \| <code>Object</code> | Log message |
|
|
114
|
+
| ...args | <code>\*</code> | Additional arguments |
|
|
115
|
+
|
|
116
|
+
<a name="MimLogger+debug"></a>
|
|
117
|
+
|
|
118
|
+
### mimLogger.debug(message, ...args)
|
|
119
|
+
Log debug message
|
|
120
|
+
|
|
121
|
+
**Kind**: instance method of [<code>MimLogger</code>](#MimLogger)
|
|
122
|
+
|
|
123
|
+
| Param | Type | Description |
|
|
124
|
+
| --- | --- | --- |
|
|
125
|
+
| message | <code>string</code> \| <code>Object</code> | Log message |
|
|
126
|
+
| ...args | <code>\*</code> | Additional arguments |
|
|
127
|
+
|
|
128
|
+
<a name="MimLogger+silly"></a>
|
|
129
|
+
|
|
130
|
+
### mimLogger.silly(message, ...args)
|
|
131
|
+
Log silly message
|
|
132
|
+
|
|
133
|
+
**Kind**: instance method of [<code>MimLogger</code>](#MimLogger)
|
|
134
|
+
|
|
135
|
+
| Param | Type | Description |
|
|
136
|
+
| --- | --- | --- |
|
|
137
|
+
| message | <code>string</code> \| <code>Object</code> | Log message |
|
|
138
|
+
| ...args | <code>\*</code> | Additional arguments |
|
|
139
|
+
|
|
140
|
+
<a name="MimLogger+flush"></a>
|
|
141
|
+
|
|
142
|
+
### mimLogger.flush() ⇒ <code>Promise.<void></code>
|
|
143
|
+
Flush all transports (for graceful shutdown)
|
|
144
|
+
|
|
145
|
+
**Kind**: instance method of [<code>MimLogger</code>](#MimLogger)
|
|
146
|
+
<a name="MimLogger+flushAndExit"></a>
|
|
147
|
+
|
|
148
|
+
### mimLogger.flushAndExit(exitCode) ⇒ <code>Promise.<void></code>
|
|
149
|
+
Flush all transports and exit process
|
|
150
|
+
|
|
151
|
+
**Kind**: instance method of [<code>MimLogger</code>](#MimLogger)
|
|
152
|
+
|
|
153
|
+
| Param | Type | Default | Description |
|
|
154
|
+
| --- | --- | --- | --- |
|
|
155
|
+
| exitCode | <code>number</code> | <code>0</code> | Process exit code |
|
|
156
|
+
|
|
157
|
+
<a name="MimLogger+serializeError"></a>
|
|
158
|
+
|
|
159
|
+
### mimLogger.serializeError(err) ⇒ <code>Object</code>
|
|
160
|
+
Utility to serialize errors
|
|
161
|
+
|
|
162
|
+
**Kind**: instance method of [<code>MimLogger</code>](#MimLogger)
|
|
163
|
+
**Returns**: <code>Object</code> - Serialized error
|
|
164
|
+
|
|
165
|
+
| Param | Type | Description |
|
|
166
|
+
| --- | --- | --- |
|
|
167
|
+
| err | <code>Error</code> | Error to serialize |
|
|
168
|
+
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration management for mim-logger
|
|
3
|
+
* Supports both direct env object and context-based env access
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { LEVELS, LEVEL_PRIORITY } = require('../lib/common');
|
|
7
|
+
|
|
8
|
+
const DEFAULT_CONFIG = {
|
|
9
|
+
level: 'debug',
|
|
10
|
+
enabled: true,
|
|
11
|
+
format: 'text',
|
|
12
|
+
useColors: true,
|
|
13
|
+
includeStack: false,
|
|
14
|
+
serverType: null,
|
|
15
|
+
serverId: null,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Parses a boolean environment variable
|
|
20
|
+
* @param {string} value - Environment variable value
|
|
21
|
+
* @param {boolean} defaultValue - Default if not set
|
|
22
|
+
* @returns {boolean}
|
|
23
|
+
*/
|
|
24
|
+
const parseBoolean = (value, defaultValue) => {
|
|
25
|
+
if (value === undefined || value === null || value === '') {
|
|
26
|
+
return defaultValue;
|
|
27
|
+
}
|
|
28
|
+
const lower = String(value).toLowerCase();
|
|
29
|
+
return lower === 'true' || lower === '1' || lower === 'yes';
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Creates configuration from environment object
|
|
34
|
+
* @param {Object} env - Environment variables object
|
|
35
|
+
* @returns {Object} Logger configuration
|
|
36
|
+
*/
|
|
37
|
+
const createConfig = (env = {}) => {
|
|
38
|
+
const level = env.LOG_LEVEL || DEFAULT_CONFIG.level;
|
|
39
|
+
|
|
40
|
+
// Validate level
|
|
41
|
+
if (!LEVELS.includes(level)) {
|
|
42
|
+
console.warn(`[mim-logger] Invalid LOG_LEVEL "${level}", using default "${DEFAULT_CONFIG.level}"`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
level: LEVELS.includes(level) ? level : DEFAULT_CONFIG.level,
|
|
47
|
+
enabled: parseBoolean(env.LOG_ENABLED, DEFAULT_CONFIG.enabled),
|
|
48
|
+
format: env.LOG_FORMAT === 'json' ? 'json' : DEFAULT_CONFIG.format,
|
|
49
|
+
useColors: parseBoolean(env.LOG_COLORS, DEFAULT_CONFIG.useColors),
|
|
50
|
+
includeStack: parseBoolean(env.LOG_STACK, DEFAULT_CONFIG.includeStack),
|
|
51
|
+
serverType: env.SERVER_TYPE || globalThis.serverType || DEFAULT_CONFIG.serverType,
|
|
52
|
+
serverId: env.SERVER_ID || globalThis.serverId || DEFAULT_CONFIG.serverId,
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Checks if a log level should be logged based on configured minimum level
|
|
58
|
+
* @param {string} level - Level to check
|
|
59
|
+
* @param {string} minLevel - Minimum configured level
|
|
60
|
+
* @returns {boolean}
|
|
61
|
+
*/
|
|
62
|
+
const shouldLog = (level, minLevel) => {
|
|
63
|
+
const levelPriority = LEVEL_PRIORITY[level];
|
|
64
|
+
const minPriority = LEVEL_PRIORITY[minLevel];
|
|
65
|
+
|
|
66
|
+
if (levelPriority === undefined || minPriority === undefined) {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return levelPriority <= minPriority;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
module.exports = {
|
|
74
|
+
DEFAULT_CONFIG,
|
|
75
|
+
createConfig,
|
|
76
|
+
shouldLog,
|
|
77
|
+
parseBoolean,
|
|
78
|
+
};
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const loadPlugin = async (name) => {
|
|
2
|
+
const mod = await import(name);
|
|
3
|
+
return mod.default ?? mod;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
module.exports = (async () => {
|
|
7
|
+
const js = await loadPlugin('@eslint/js');
|
|
8
|
+
const importPlugin = await loadPlugin('eslint-plugin-import');
|
|
9
|
+
const stylistic = await loadPlugin('@stylistic/eslint-plugin');
|
|
10
|
+
const processDoc = await loadPlugin('@mimik/eslint-plugin-document-env');
|
|
11
|
+
|
|
12
|
+
const MAX_LENGTH_LINE = 180;
|
|
13
|
+
const MAX_FUNCTION_PARAMETERS = 6;
|
|
14
|
+
const MAX_LINES_IN_FILES = 600;
|
|
15
|
+
const MAX_LINES_IN_FUNCTION = 150;
|
|
16
|
+
const MAX_STATEMENTS_IN_FUNCTION = 45;
|
|
17
|
+
const MIN_KEYS_IN_OBJECT = 10;
|
|
18
|
+
const MAX_COMPLEXITY = 30;
|
|
19
|
+
|
|
20
|
+
return [
|
|
21
|
+
{
|
|
22
|
+
ignores: ['mochawesome-report/**', '**/node_modules', 'node_modules/**', 'dist/**', '**/eslint.config.js', '**/webpack.config.js'],
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
// Baseline best-practices
|
|
26
|
+
js.configs.recommended,
|
|
27
|
+
importPlugin.flatConfigs.recommended,
|
|
28
|
+
stylistic.configs.recommended,
|
|
29
|
+
|
|
30
|
+
{
|
|
31
|
+
plugins: { processDoc },
|
|
32
|
+
languageOptions: {
|
|
33
|
+
ecmaVersion: 'latest',
|
|
34
|
+
sourceType: 'commonjs',
|
|
35
|
+
globals: {
|
|
36
|
+
mimikModule: true,
|
|
37
|
+
console: 'readonly',
|
|
38
|
+
describe: 'readonly',
|
|
39
|
+
it: 'readonly',
|
|
40
|
+
require: 'readonly',
|
|
41
|
+
module: 'readonly',
|
|
42
|
+
__dirname: 'readonly',
|
|
43
|
+
process: 'readonly',
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
linterOptions: {
|
|
47
|
+
reportUnusedDisableDirectives: true,
|
|
48
|
+
},
|
|
49
|
+
rules: {
|
|
50
|
+
'@stylistic/brace-style': ['warn', 'stroustrup', { allowSingleLine: true }],
|
|
51
|
+
'@stylistic/line-comment-position': 'off',
|
|
52
|
+
'@stylistic/semi': ['error', 'always'],
|
|
53
|
+
|
|
54
|
+
'capitalized-comments': 'off',
|
|
55
|
+
'complexity': ['error', MAX_COMPLEXITY],
|
|
56
|
+
'curly': 'off',
|
|
57
|
+
'id-length': ['error', { exceptions: ['x', 'y', 'z', 'i', 'j', 'k'] }],
|
|
58
|
+
'import/no-extraneous-dependencies': ['error', { devDependencies: true }],
|
|
59
|
+
'import/no-unresolved': ['error', { amd: true, caseSensitiveStrict: true, commonjs: true }],
|
|
60
|
+
'init-declarations': 'off',
|
|
61
|
+
'linebreak-style': 'off',
|
|
62
|
+
'max-len': ['warn', MAX_LENGTH_LINE, { ignoreComments: true }],
|
|
63
|
+
'max-lines': ['warn', { max: MAX_LINES_IN_FILES, skipComments: true }],
|
|
64
|
+
'max-lines-per-function': ['warn', { max: MAX_LINES_IN_FUNCTION, skipComments: true }],
|
|
65
|
+
'max-params': ['error', MAX_FUNCTION_PARAMETERS],
|
|
66
|
+
'max-statements': ['warn', MAX_STATEMENTS_IN_FUNCTION],
|
|
67
|
+
'no-confusing-arrow': 'off',
|
|
68
|
+
'no-inline-comments': 'off',
|
|
69
|
+
'no-process-env': 'error',
|
|
70
|
+
'no-ternary': 'off',
|
|
71
|
+
'no-undefined': 'off',
|
|
72
|
+
'one-var': ['error', 'never'],
|
|
73
|
+
'processDoc/validate-document-env': 'error',
|
|
74
|
+
'quotes': ['warn', 'single'],
|
|
75
|
+
'sort-keys': ['error', 'asc', { caseSensitive: true, minKeys: MIN_KEYS_IN_OBJECT, natural: false }],
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
})();
|
package/index.js
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mim-logger - Lightweight logging library for mimOE runtime environment
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* const logger = require('@mimik/mim-logger');
|
|
6
|
+
*
|
|
7
|
+
* // Initialize with context (recommended for mimOE)
|
|
8
|
+
* logger.init(context);
|
|
9
|
+
*
|
|
10
|
+
* // Or initialize with env object directly
|
|
11
|
+
* logger.init({ env: { LOG_LEVEL: 'debug' } });
|
|
12
|
+
*
|
|
13
|
+
* // Log messages
|
|
14
|
+
* logger.info('message');
|
|
15
|
+
* logger.error('message', { meta: 'data' });
|
|
16
|
+
* logger.debug('message', { meta: 'data' }, 'correlationId');
|
|
17
|
+
*
|
|
18
|
+
* Environment Variables:
|
|
19
|
+
* LOG_ENABLED - Enable/disable logging ('true'/'false', default: 'true')
|
|
20
|
+
* LOG_LEVEL - Minimum log level (error/warn/info/verbose/debug/silly, default: 'debug')
|
|
21
|
+
* LOG_FORMAT - Output format ('text'/'json', default: 'text')
|
|
22
|
+
* LOG_COLORS - Use ANSI colors ('true'/'false', default: 'true')
|
|
23
|
+
* LOG_STACK - Include stack traces ('true'/'false', default: 'false')
|
|
24
|
+
* SERVER_TYPE - Server type identifier
|
|
25
|
+
* SERVER_ID - Server instance identifier
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
const { LEVELS, serializeData, serializeError } = require('./lib/common');
|
|
29
|
+
const { parseCorrelationId, getStackInfo } = require('./lib/formatLib');
|
|
30
|
+
const { createConfig, shouldLog, DEFAULT_CONFIG } = require('./configuration/config');
|
|
31
|
+
const ConsoleTransport = require('./lib/consoleTransport');
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Logger instance
|
|
35
|
+
*/
|
|
36
|
+
class MimLogger {
|
|
37
|
+
constructor() {
|
|
38
|
+
this.config = { ...DEFAULT_CONFIG };
|
|
39
|
+
this.transport = new ConsoleTransport();
|
|
40
|
+
this.initialized = false;
|
|
41
|
+
this.context = null;
|
|
42
|
+
|
|
43
|
+
// Expose LEVELS constant
|
|
44
|
+
this.LEVELS = LEVELS;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Initializes the logger with context or configuration
|
|
49
|
+
* @param {Object} contextOrConfig - mimik context object or config with env
|
|
50
|
+
* @returns {MimLogger} Logger instance for chaining
|
|
51
|
+
*/
|
|
52
|
+
init(contextOrConfig = {}) {
|
|
53
|
+
this.context = contextOrConfig;
|
|
54
|
+
|
|
55
|
+
// Extract env from context or use directly
|
|
56
|
+
const env = contextOrConfig.env || contextOrConfig || {};
|
|
57
|
+
|
|
58
|
+
this.config = createConfig(env);
|
|
59
|
+
this.transport = new ConsoleTransport({
|
|
60
|
+
format: this.config.format,
|
|
61
|
+
useColors: this.config.useColors,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
this.initialized = true;
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Gets current configuration, refreshing from context if available
|
|
70
|
+
* @returns {Object} Current configuration
|
|
71
|
+
*/
|
|
72
|
+
getConfig() {
|
|
73
|
+
// If we have a context with env, refresh config each time
|
|
74
|
+
// This allows dynamic config changes
|
|
75
|
+
if (this.context && this.context.env) {
|
|
76
|
+
this.config = createConfig(this.context.env);
|
|
77
|
+
this.transport = new ConsoleTransport({
|
|
78
|
+
format: this.config.format,
|
|
79
|
+
useColors: this.config.useColors,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return this.config;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Internal log method
|
|
87
|
+
* @param {string} level - Log level
|
|
88
|
+
* @param {string|Object} message - Log message or object with message property
|
|
89
|
+
* @param {...*} args - Additional arguments (metadata, correlationId)
|
|
90
|
+
*/
|
|
91
|
+
_log(level, message, ...args) {
|
|
92
|
+
const config = this.getConfig();
|
|
93
|
+
|
|
94
|
+
// Check if logging is enabled
|
|
95
|
+
if (!config.enabled) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Check if this level should be logged
|
|
100
|
+
if (!shouldLog(level, config.level)) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Parse arguments
|
|
105
|
+
let meta = null;
|
|
106
|
+
let correlationId = null;
|
|
107
|
+
|
|
108
|
+
// Last string argument is correlation ID
|
|
109
|
+
if (args.length > 0 && typeof args[args.length - 1] === 'string') {
|
|
110
|
+
correlationId = args.pop();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Remaining arguments are metadata
|
|
114
|
+
if (args.length > 0) {
|
|
115
|
+
// Merge all metadata objects
|
|
116
|
+
meta = args.reduce((acc, arg) => {
|
|
117
|
+
if (arg && typeof arg === 'object') {
|
|
118
|
+
return { ...acc, ...serializeData(arg) };
|
|
119
|
+
}
|
|
120
|
+
return acc;
|
|
121
|
+
}, {});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Handle message as object
|
|
125
|
+
let messageStr = message;
|
|
126
|
+
if (message && typeof message === 'object') {
|
|
127
|
+
if (message.message) {
|
|
128
|
+
messageStr = message.message;
|
|
129
|
+
// Merge remaining properties into meta
|
|
130
|
+
// eslint-disable-next-line id-length, no-unused-vars
|
|
131
|
+
const { message: _, ...rest } = message;
|
|
132
|
+
meta = { ...meta, ...serializeData(rest) };
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
messageStr = JSON.stringify(serializeData(message));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Build log info object
|
|
140
|
+
const info = {
|
|
141
|
+
level,
|
|
142
|
+
message: String(messageStr),
|
|
143
|
+
timestamp: new Date().toISOString(),
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
// Add correlation ID if present
|
|
147
|
+
if (correlationId) {
|
|
148
|
+
info.correlationId = correlationId;
|
|
149
|
+
info.correlationParsed = parseCorrelationId(correlationId);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Add metadata
|
|
153
|
+
if (meta && Object.keys(meta).length > 0) {
|
|
154
|
+
info.meta = meta;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Add stack info for errors or if configured
|
|
158
|
+
if (level === 'error' || config.includeStack) {
|
|
159
|
+
const stackInfo = getStackInfo();
|
|
160
|
+
if (stackInfo) {
|
|
161
|
+
info.stack = stackInfo;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Add server identification
|
|
166
|
+
if (config.serverType) {
|
|
167
|
+
info.serverType = config.serverType;
|
|
168
|
+
}
|
|
169
|
+
if (config.serverId) {
|
|
170
|
+
info.serverId = config.serverId;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Send to transport
|
|
174
|
+
this.transport.log(info);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Generic log method with explicit level
|
|
179
|
+
* @param {string} level - Log level
|
|
180
|
+
* @param {string|Object} message - Log message
|
|
181
|
+
* @param {...*} args - Additional arguments
|
|
182
|
+
*/
|
|
183
|
+
log(level, message, ...args) {
|
|
184
|
+
if (!LEVELS.includes(level)) {
|
|
185
|
+
console.warn(`[mim-logger] Invalid log level "${level}"`);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
this._log(level, message, ...args);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Log error message
|
|
193
|
+
* @param {string|Object} message - Log message
|
|
194
|
+
* @param {...*} args - Additional arguments
|
|
195
|
+
*/
|
|
196
|
+
error(message, ...args) {
|
|
197
|
+
this._log('error', message, ...args);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Log warning message
|
|
202
|
+
* @param {string|Object} message - Log message
|
|
203
|
+
* @param {...*} args - Additional arguments
|
|
204
|
+
*/
|
|
205
|
+
warn(message, ...args) {
|
|
206
|
+
this._log('warn', message, ...args);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Log info message
|
|
211
|
+
* @param {string|Object} message - Log message
|
|
212
|
+
* @param {...*} args - Additional arguments
|
|
213
|
+
*/
|
|
214
|
+
info(message, ...args) {
|
|
215
|
+
this._log('info', message, ...args);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Log verbose message
|
|
220
|
+
* @param {string|Object} message - Log message
|
|
221
|
+
* @param {...*} args - Additional arguments
|
|
222
|
+
*/
|
|
223
|
+
verbose(message, ...args) {
|
|
224
|
+
this._log('verbose', message, ...args);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Log debug message
|
|
229
|
+
* @param {string|Object} message - Log message
|
|
230
|
+
* @param {...*} args - Additional arguments
|
|
231
|
+
*/
|
|
232
|
+
debug(message, ...args) {
|
|
233
|
+
this._log('debug', message, ...args);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Log silly message
|
|
238
|
+
* @param {string|Object} message - Log message
|
|
239
|
+
* @param {...*} args - Additional arguments
|
|
240
|
+
*/
|
|
241
|
+
silly(message, ...args) {
|
|
242
|
+
this._log('silly', message, ...args);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Flush all transports (for graceful shutdown)
|
|
247
|
+
* @returns {Promise<void>}
|
|
248
|
+
*/
|
|
249
|
+
flush() {
|
|
250
|
+
return this.transport.flush();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Flush all transports and exit process
|
|
255
|
+
* @param {number} exitCode - Process exit code
|
|
256
|
+
* @returns {Promise<void>}
|
|
257
|
+
*/
|
|
258
|
+
flushAndExit(exitCode = 0) {
|
|
259
|
+
return this.flush().then(() => {
|
|
260
|
+
if (typeof process !== 'undefined' && typeof process.exit === 'function') {
|
|
261
|
+
process.exit(exitCode);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Utility to serialize errors
|
|
268
|
+
* @param {Error} err - Error to serialize
|
|
269
|
+
* @returns {Object} Serialized error
|
|
270
|
+
*/
|
|
271
|
+
serializeError(err) {
|
|
272
|
+
return serializeError(err);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Export singleton instance
|
|
277
|
+
const logger = new MimLogger();
|
|
278
|
+
|
|
279
|
+
module.exports = logger;
|
package/lib/common.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common constants and utilities for mim-logger
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const LEVELS = ['error', 'warn', 'info', 'verbose', 'debug', 'silly'];
|
|
6
|
+
|
|
7
|
+
const LEVEL_PRIORITY = {
|
|
8
|
+
error: 0,
|
|
9
|
+
warn: 1,
|
|
10
|
+
info: 2,
|
|
11
|
+
verbose: 3,
|
|
12
|
+
debug: 4,
|
|
13
|
+
silly: 5,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const LEVEL_COLORS = {
|
|
17
|
+
error: '\x1b[31m', // Red
|
|
18
|
+
warn: '\x1b[33m', // Yellow
|
|
19
|
+
info: '\x1b[36m', // Cyan
|
|
20
|
+
verbose: '\x1b[35m', // Magenta
|
|
21
|
+
debug: '\x1b[32m', // Green
|
|
22
|
+
silly: '\x1b[90m', // Gray
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const RESET_COLOR = '\x1b[0m';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Serializes an error object into a plain object
|
|
29
|
+
* @param {Error} err - Error to serialize
|
|
30
|
+
* @returns {Object} Serialized error
|
|
31
|
+
*/
|
|
32
|
+
const serializeError = (err) => {
|
|
33
|
+
if (!err) return err;
|
|
34
|
+
if (!(err instanceof Error)) return err;
|
|
35
|
+
|
|
36
|
+
const serialized = {
|
|
37
|
+
message: err.message,
|
|
38
|
+
name: err.name,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
if (err.stack) {
|
|
42
|
+
serialized.stack = err.stack;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (err.code) {
|
|
46
|
+
serialized.code = err.code;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (err.status) {
|
|
50
|
+
serialized.status = err.status;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (err.statusCode) {
|
|
54
|
+
serialized.statusCode = err.statusCode;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Copy any additional enumerable properties
|
|
58
|
+
Object.keys(err).forEach((key) => {
|
|
59
|
+
if (!(key in serialized)) {
|
|
60
|
+
serialized[key] = err[key];
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
return serialized;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Deep clones and serializes data, handling errors and circular references
|
|
69
|
+
* Uses array-based tracking for mimOE runtime compatibility (no WeakSet)
|
|
70
|
+
* @param {*} data - Data to serialize
|
|
71
|
+
* @param {Array} seen - Array of seen objects for circular reference detection
|
|
72
|
+
* @returns {*} Serialized data
|
|
73
|
+
*/
|
|
74
|
+
const serializeData = (data, seen) => {
|
|
75
|
+
if (data === null || data === undefined) return data;
|
|
76
|
+
if (typeof data !== 'object') return data;
|
|
77
|
+
|
|
78
|
+
if (data instanceof Error) {
|
|
79
|
+
return serializeError(data);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Initialize seen array on first call
|
|
83
|
+
var seenArr = seen || [];
|
|
84
|
+
|
|
85
|
+
// Check for circular reference using indexOf (works in ES5)
|
|
86
|
+
if (seenArr.indexOf(data) !== -1) {
|
|
87
|
+
return '[Circular]';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
seenArr.push(data);
|
|
91
|
+
|
|
92
|
+
if (Array.isArray(data)) {
|
|
93
|
+
return data.map(function (item) {
|
|
94
|
+
return serializeData(item, seenArr);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (data instanceof Date) {
|
|
99
|
+
return data.toISOString();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Handle Mongoose-like objects with toObject method
|
|
103
|
+
if (typeof data.toObject === 'function') {
|
|
104
|
+
return serializeData(data.toObject(), seenArr);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
var result = {};
|
|
108
|
+
Object.keys(data).forEach(function (key) {
|
|
109
|
+
result[key] = serializeData(data[key], seenArr);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
return result;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
module.exports = {
|
|
116
|
+
LEVELS,
|
|
117
|
+
LEVEL_PRIORITY,
|
|
118
|
+
LEVEL_COLORS,
|
|
119
|
+
RESET_COLOR,
|
|
120
|
+
serializeError,
|
|
121
|
+
serializeData,
|
|
122
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Console transport for mim-logger
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { formatText, formatJson } = require('./formatLib');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Console transport class
|
|
9
|
+
*/
|
|
10
|
+
class ConsoleTransport {
|
|
11
|
+
/**
|
|
12
|
+
* Creates a new ConsoleTransport
|
|
13
|
+
* @param {Object} options - Transport options
|
|
14
|
+
* @param {string} options.format - Output format ('text' or 'json')
|
|
15
|
+
* @param {boolean} options.useColors - Use ANSI colors (text format only)
|
|
16
|
+
*/
|
|
17
|
+
constructor(options = {}) {
|
|
18
|
+
this.format = options.format || 'text';
|
|
19
|
+
this.useColors = options.useColors !== false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Logs a message to console
|
|
24
|
+
* @param {Object} info - Log info object
|
|
25
|
+
*/
|
|
26
|
+
log(info) {
|
|
27
|
+
const formatted = this.format === 'json'
|
|
28
|
+
? formatJson(info)
|
|
29
|
+
: formatText(info, this.useColors);
|
|
30
|
+
|
|
31
|
+
const { level } = info;
|
|
32
|
+
|
|
33
|
+
// Use appropriate console method based on level
|
|
34
|
+
switch (level) {
|
|
35
|
+
case 'error':
|
|
36
|
+
console.error(formatted);
|
|
37
|
+
break;
|
|
38
|
+
case 'warn':
|
|
39
|
+
console.warn(formatted);
|
|
40
|
+
break;
|
|
41
|
+
case 'debug':
|
|
42
|
+
case 'verbose':
|
|
43
|
+
case 'silly':
|
|
44
|
+
// console.debug may not be available in all environments
|
|
45
|
+
if (typeof console.debug === 'function') {
|
|
46
|
+
console.debug(formatted);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
console.log(formatted);
|
|
50
|
+
}
|
|
51
|
+
break;
|
|
52
|
+
default:
|
|
53
|
+
console.log(formatted);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Flush method (no-op for console, included for interface compatibility)
|
|
59
|
+
* @returns {Promise<void>}
|
|
60
|
+
*/
|
|
61
|
+
flush() {
|
|
62
|
+
return Promise.resolve();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = ConsoleTransport;
|
package/lib/formatLib.js
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Log formatting utilities for mim-logger
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
LEVEL_COLORS,
|
|
7
|
+
RESET_COLOR,
|
|
8
|
+
serializeData,
|
|
9
|
+
} = require('./common');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Pads a string to a target length (ES5 compatible replacement for padEnd)
|
|
13
|
+
* @param {string} str - String to pad
|
|
14
|
+
* @param {number} targetLength - Desired length
|
|
15
|
+
* @param {string} padChar - Character to pad with (default: space)
|
|
16
|
+
* @returns {string} Padded string
|
|
17
|
+
*/
|
|
18
|
+
function padEnd(str, targetLength, padChar) {
|
|
19
|
+
var padS = String(str);
|
|
20
|
+
var pad = padChar || ' ';
|
|
21
|
+
if (padS.length >= targetLength) {
|
|
22
|
+
return padS;
|
|
23
|
+
}
|
|
24
|
+
var padding = '';
|
|
25
|
+
for (var i = padS.length; i < targetLength; i++) {
|
|
26
|
+
padding += pad;
|
|
27
|
+
}
|
|
28
|
+
return padS + padding;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Parses correlation ID string into components
|
|
33
|
+
* Format: correlationId/timestamp or correlationId@step/timestamp
|
|
34
|
+
* @param {string} correlationId - Correlation ID string
|
|
35
|
+
* @returns {Object} Parsed correlation components
|
|
36
|
+
*/
|
|
37
|
+
const parseCorrelationId = (correlationId) => {
|
|
38
|
+
if (!correlationId || typeof correlationId !== 'string') {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const result = {
|
|
43
|
+
correlationId: null,
|
|
44
|
+
step: null,
|
|
45
|
+
timestamp: null,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Check for step separator (@)
|
|
49
|
+
const atIndex = correlationId.indexOf('@');
|
|
50
|
+
const slashIndex = correlationId.lastIndexOf('/');
|
|
51
|
+
|
|
52
|
+
if (slashIndex > 0) {
|
|
53
|
+
result.timestamp = correlationId.substring(slashIndex + 1);
|
|
54
|
+
const beforeSlash = correlationId.substring(0, slashIndex);
|
|
55
|
+
|
|
56
|
+
if (atIndex > 0 && atIndex < slashIndex) {
|
|
57
|
+
result.correlationId = beforeSlash.substring(0, atIndex);
|
|
58
|
+
result.step = beforeSlash.substring(atIndex + 1);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
result.correlationId = beforeSlash;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else if (atIndex > 0) {
|
|
65
|
+
result.correlationId = correlationId.substring(0, atIndex);
|
|
66
|
+
result.step = correlationId.substring(atIndex + 1);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
result.correlationId = correlationId;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return result;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Extracts stack trace info from the call site
|
|
77
|
+
* @param {number} depth - Stack depth to extract from
|
|
78
|
+
* @returns {Object|null} Stack info object
|
|
79
|
+
*/
|
|
80
|
+
const getStackInfo = (depth = 4) => {
|
|
81
|
+
const stackLines = new Error().stack.split('\n');
|
|
82
|
+
|
|
83
|
+
if (stackLines.length <= depth) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const line = stackLines[depth];
|
|
88
|
+
// Match: at functionName (path:line:col) or at path:line:col
|
|
89
|
+
const match = line.match(/at\s+(?:(.+?)\s+\()?(.+?):(\d+):(\d+)\)?/);
|
|
90
|
+
|
|
91
|
+
if (!match) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const [, method, path, lineNum, pos] = match;
|
|
96
|
+
const file = path.split('/').pop();
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
method: method || '<anonymous>',
|
|
100
|
+
path,
|
|
101
|
+
line: parseInt(lineNum, 10),
|
|
102
|
+
pos: parseInt(pos, 10),
|
|
103
|
+
file,
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Formats a log entry as text
|
|
109
|
+
* @param {Object} info - Log info object
|
|
110
|
+
* @param {boolean} useColors - Whether to use ANSI colors
|
|
111
|
+
* @returns {string} Formatted log string
|
|
112
|
+
*/
|
|
113
|
+
const formatText = (info, useColors = true) => {
|
|
114
|
+
const { level, message, timestamp, meta, correlationId, stack } = info;
|
|
115
|
+
|
|
116
|
+
const parts = [];
|
|
117
|
+
|
|
118
|
+
// Timestamp
|
|
119
|
+
parts.push(`[${timestamp}]`);
|
|
120
|
+
|
|
121
|
+
// Level with optional color
|
|
122
|
+
var levelStr = padEnd(level.toUpperCase(), 7);
|
|
123
|
+
if (useColors) {
|
|
124
|
+
var color = LEVEL_COLORS[level] || '';
|
|
125
|
+
parts.push(color + levelStr + RESET_COLOR);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
parts.push(levelStr);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Correlation ID if present
|
|
132
|
+
if (correlationId) {
|
|
133
|
+
parts.push(`[${correlationId}]`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Message
|
|
137
|
+
parts.push(message);
|
|
138
|
+
|
|
139
|
+
// Stack info for errors
|
|
140
|
+
if (stack && stack.file) {
|
|
141
|
+
parts.push(`(${stack.file}:${stack.line})`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
let result = parts.join(' ');
|
|
145
|
+
|
|
146
|
+
// Metadata on separate line if present
|
|
147
|
+
if (meta && Object.keys(meta).length > 0) {
|
|
148
|
+
result += `\n ${JSON.stringify(meta)}`;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return result;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Formats a log entry as JSON
|
|
156
|
+
* @param {Object} info - Log info object
|
|
157
|
+
* @returns {string} JSON formatted log string
|
|
158
|
+
*/
|
|
159
|
+
const formatJson = (info) => {
|
|
160
|
+
const output = {
|
|
161
|
+
timestamp: info.timestamp,
|
|
162
|
+
level: info.level,
|
|
163
|
+
message: info.message,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
if (info.correlationId) {
|
|
167
|
+
output.correlationId = info.correlationId;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (info.correlationParsed) {
|
|
171
|
+
output.correlation = info.correlationParsed;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (info.stack) {
|
|
175
|
+
output.stack = info.stack;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (info.meta && Object.keys(info.meta).length > 0) {
|
|
179
|
+
output.meta = serializeData(info.meta);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (info.serverType) {
|
|
183
|
+
output.serverType = info.serverType;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (info.serverId) {
|
|
187
|
+
output.serverId = info.serverId;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return JSON.stringify(output);
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
module.exports = {
|
|
194
|
+
parseCorrelationId,
|
|
195
|
+
getStackInfo,
|
|
196
|
+
formatText,
|
|
197
|
+
formatJson,
|
|
198
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mimik/mim-logger",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Lightweight logging library for mimOE runtime environment",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"docs": "jsdoc2md index.js > README.md",
|
|
8
|
+
"lint": "eslint . --no-error-on-unmatched-pattern",
|
|
9
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"mimik",
|
|
13
|
+
"logger",
|
|
14
|
+
"edge",
|
|
15
|
+
"microservice",
|
|
16
|
+
"mim"
|
|
17
|
+
],
|
|
18
|
+
"author": "mimik",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=14.0.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@eslint/js": "9.39.2",
|
|
25
|
+
"@mimik/eslint-plugin-document-env": "2.0.8",
|
|
26
|
+
"@stylistic/eslint-plugin": "5.7.0",
|
|
27
|
+
"eslint": "9.39.2",
|
|
28
|
+
"eslint-plugin-import": "2.32.0",
|
|
29
|
+
"jsdoc-to-markdown": "9.1.3"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Basic tests for mim-logger
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const logger = require('../index');
|
|
6
|
+
|
|
7
|
+
// Test initialization
|
|
8
|
+
console.log('\n=== Test 1: Default initialization ===');
|
|
9
|
+
logger.init();
|
|
10
|
+
logger.info('Logger initialized with defaults');
|
|
11
|
+
|
|
12
|
+
// Test all log levels
|
|
13
|
+
console.log('\n=== Test 2: All log levels ===');
|
|
14
|
+
logger.error('This is an error message');
|
|
15
|
+
logger.warn('This is a warning message');
|
|
16
|
+
logger.info('This is an info message');
|
|
17
|
+
logger.verbose('This is a verbose message');
|
|
18
|
+
logger.debug('This is a debug message');
|
|
19
|
+
logger.silly('This is a silly message');
|
|
20
|
+
|
|
21
|
+
// Test with metadata
|
|
22
|
+
console.log('\n=== Test 3: With metadata ===');
|
|
23
|
+
logger.info('User logged in', { userId: '123', email: 'test@example.com' });
|
|
24
|
+
logger.error('Request failed', { status: 500, endpoint: '/api/data' });
|
|
25
|
+
|
|
26
|
+
// Test with correlation ID
|
|
27
|
+
console.log('\n=== Test 4: With correlation ID ===');
|
|
28
|
+
logger.info('Processing request', { action: 'create' }, 'req-abc123/1704067200');
|
|
29
|
+
logger.debug('Cache hit', { key: 'user:123' }, 'req-abc123@step2/1704067200');
|
|
30
|
+
|
|
31
|
+
// Test with Error object
|
|
32
|
+
console.log('\n=== Test 5: With Error object ===');
|
|
33
|
+
const testError = new Error('Something went wrong');
|
|
34
|
+
testError.code = 'ERR_TEST';
|
|
35
|
+
logger.error('Operation failed', { error: testError });
|
|
36
|
+
|
|
37
|
+
// Test log level filtering
|
|
38
|
+
console.log('\n=== Test 6: Log level filtering (LOG_LEVEL=warn) ===');
|
|
39
|
+
logger.init({ LOG_LEVEL: 'warn' });
|
|
40
|
+
logger.debug('This should NOT appear (debug < warn)');
|
|
41
|
+
logger.info('This should NOT appear (info < warn)');
|
|
42
|
+
logger.warn('This SHOULD appear');
|
|
43
|
+
logger.error('This SHOULD appear');
|
|
44
|
+
|
|
45
|
+
// Test disabled logging
|
|
46
|
+
console.log('\n=== Test 7: Disabled logging ===');
|
|
47
|
+
logger.init({ LOG_ENABLED: 'false' });
|
|
48
|
+
logger.error('This should NOT appear (logging disabled)');
|
|
49
|
+
logger.init({ LOG_ENABLED: 'true' }); // Re-enable
|
|
50
|
+
|
|
51
|
+
// Test JSON format
|
|
52
|
+
console.log('\n=== Test 8: JSON format ===');
|
|
53
|
+
logger.init({ LOG_FORMAT: 'json', LOG_LEVEL: 'debug' });
|
|
54
|
+
logger.info('JSON formatted message', { data: { nested: true } });
|
|
55
|
+
logger.error('JSON error with stack');
|
|
56
|
+
|
|
57
|
+
// Test with mimik context pattern
|
|
58
|
+
console.log('\n=== Test 9: mimik context pattern ===');
|
|
59
|
+
const mockContext = {
|
|
60
|
+
env: {
|
|
61
|
+
LOG_LEVEL: 'info',
|
|
62
|
+
LOG_FORMAT: 'text',
|
|
63
|
+
SERVER_TYPE: 'mEHR',
|
|
64
|
+
SERVER_ID: 'edge-001',
|
|
65
|
+
},
|
|
66
|
+
http: {}, // Mock http client
|
|
67
|
+
};
|
|
68
|
+
logger.init(mockContext);
|
|
69
|
+
logger.info('Using mimik context', { feature: 'EHR' });
|
|
70
|
+
|
|
71
|
+
// Test generic log method
|
|
72
|
+
console.log('\n=== Test 10: Generic log method ===');
|
|
73
|
+
logger.log('debug', 'Using generic log method', { custom: true });
|
|
74
|
+
logger.log('invalid', 'This should show warning'); // Invalid level
|
|
75
|
+
|
|
76
|
+
// Test LEVELS constant
|
|
77
|
+
console.log('\n=== Test 11: LEVELS constant ===');
|
|
78
|
+
console.log('Available levels:', logger.LEVELS);
|
|
79
|
+
|
|
80
|
+
console.log('\n=== All tests completed ===\n');
|