@morojs/moro 1.5.17 → 1.6.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 +48 -65
- package/dist/core/auth/morojs-adapter.js +12 -16
- package/dist/core/auth/morojs-adapter.js.map +1 -1
- package/dist/core/config/file-loader.d.ts +5 -0
- package/dist/core/config/file-loader.js +171 -0
- package/dist/core/config/file-loader.js.map +1 -1
- package/dist/core/config/index.d.ts +10 -39
- package/dist/core/config/index.js +29 -66
- package/dist/core/config/index.js.map +1 -1
- package/dist/core/config/loader.d.ts +7 -0
- package/dist/core/config/loader.js +269 -0
- package/dist/core/config/loader.js.map +1 -0
- package/dist/core/config/schema.js +31 -41
- package/dist/core/config/schema.js.map +1 -1
- package/dist/core/config/utils.d.ts +2 -9
- package/dist/core/config/utils.js +32 -19
- package/dist/core/config/utils.js.map +1 -1
- package/dist/core/config/validation.d.ts +17 -0
- package/dist/core/config/validation.js +131 -0
- package/dist/core/config/validation.js.map +1 -0
- package/dist/core/database/adapters/mongodb.d.ts +0 -10
- package/dist/core/database/adapters/mongodb.js +2 -23
- package/dist/core/database/adapters/mongodb.js.map +1 -1
- package/dist/core/database/adapters/mysql.d.ts +0 -11
- package/dist/core/database/adapters/mysql.js +0 -1
- package/dist/core/database/adapters/mysql.js.map +1 -1
- package/dist/core/database/adapters/postgresql.d.ts +1 -9
- package/dist/core/database/adapters/postgresql.js +1 -1
- package/dist/core/database/adapters/postgresql.js.map +1 -1
- package/dist/core/database/adapters/redis.d.ts +0 -9
- package/dist/core/database/adapters/redis.js +4 -14
- package/dist/core/database/adapters/redis.js.map +1 -1
- package/dist/core/framework.d.ts +7 -6
- package/dist/core/framework.js +16 -131
- package/dist/core/framework.js.map +1 -1
- package/dist/core/http/http-server.d.ts +0 -12
- package/dist/core/http/http-server.js +23 -151
- package/dist/core/http/http-server.js.map +1 -1
- package/dist/core/http/router.d.ts +0 -12
- package/dist/core/http/router.js +36 -114
- package/dist/core/http/router.js.map +1 -1
- package/dist/core/logger/filters.js +4 -12
- package/dist/core/logger/filters.js.map +1 -1
- package/dist/core/logger/index.d.ts +1 -1
- package/dist/core/logger/index.js +1 -2
- package/dist/core/logger/index.js.map +1 -1
- package/dist/core/logger/logger.d.ts +13 -29
- package/dist/core/logger/logger.js +203 -380
- package/dist/core/logger/logger.js.map +1 -1
- package/dist/core/logger/outputs.js +2 -0
- package/dist/core/logger/outputs.js.map +1 -1
- package/dist/core/middleware/built-in/auth.js +17 -88
- package/dist/core/middleware/built-in/auth.js.map +1 -1
- package/dist/core/middleware/built-in/cache.js +1 -3
- package/dist/core/middleware/built-in/cache.js.map +1 -1
- package/dist/core/middleware/built-in/index.d.ts +0 -1
- package/dist/core/middleware/built-in/index.js +1 -6
- package/dist/core/middleware/built-in/index.js.map +1 -1
- package/dist/core/middleware/built-in/request-logger.js +2 -3
- package/dist/core/middleware/built-in/request-logger.js.map +1 -1
- package/dist/core/middleware/built-in/sse.js +7 -9
- package/dist/core/middleware/built-in/sse.js.map +1 -1
- package/dist/core/modules/auto-discovery.d.ts +0 -17
- package/dist/core/modules/auto-discovery.js +12 -367
- package/dist/core/modules/auto-discovery.js.map +1 -1
- package/dist/core/modules/modules.js +2 -12
- package/dist/core/modules/modules.js.map +1 -1
- package/dist/core/networking/adapters/ws-adapter.d.ts +1 -1
- package/dist/core/networking/adapters/ws-adapter.js +2 -2
- package/dist/core/networking/adapters/ws-adapter.js.map +1 -1
- package/dist/core/networking/service-discovery.js +7 -7
- package/dist/core/networking/service-discovery.js.map +1 -1
- package/dist/core/routing/index.d.ts +0 -20
- package/dist/core/routing/index.js +13 -178
- package/dist/core/routing/index.js.map +1 -1
- package/dist/core/runtime/node-adapter.js +6 -12
- package/dist/core/runtime/node-adapter.js.map +1 -1
- package/dist/moro.d.ts +0 -48
- package/dist/moro.js +148 -456
- package/dist/moro.js.map +1 -1
- package/dist/types/config.d.ts +2 -58
- package/dist/types/core.d.ts +40 -34
- package/dist/types/http.d.ts +1 -16
- package/dist/types/logger.d.ts +0 -7
- package/dist/types/module.d.ts +0 -11
- package/package.json +2 -2
- package/src/core/auth/morojs-adapter.ts +13 -18
- package/src/core/config/file-loader.ts +233 -0
- package/src/core/config/index.ts +32 -77
- package/src/core/config/loader.ts +633 -0
- package/src/core/config/schema.ts +31 -41
- package/src/core/config/utils.ts +29 -22
- package/src/core/config/validation.ts +140 -0
- package/src/core/database/README.md +16 -26
- package/src/core/database/adapters/mongodb.ts +2 -30
- package/src/core/database/adapters/mysql.ts +0 -14
- package/src/core/database/adapters/postgresql.ts +2 -12
- package/src/core/database/adapters/redis.ts +4 -27
- package/src/core/framework.ts +23 -163
- package/src/core/http/http-server.ts +36 -176
- package/src/core/http/router.ts +38 -127
- package/src/core/logger/filters.ts +4 -12
- package/src/core/logger/index.ts +0 -1
- package/src/core/logger/logger.ts +216 -427
- package/src/core/logger/outputs.ts +2 -0
- package/src/core/middleware/built-in/auth.ts +17 -98
- package/src/core/middleware/built-in/cache.ts +1 -3
- package/src/core/middleware/built-in/index.ts +0 -8
- package/src/core/middleware/built-in/request-logger.ts +1 -3
- package/src/core/middleware/built-in/sse.ts +7 -9
- package/src/core/modules/auto-discovery.ts +13 -476
- package/src/core/modules/modules.ts +9 -20
- package/src/core/networking/adapters/ws-adapter.ts +5 -2
- package/src/core/networking/service-discovery.ts +7 -6
- package/src/core/routing/index.ts +14 -198
- package/src/core/runtime/node-adapter.ts +6 -12
- package/src/moro.ts +166 -554
- package/src/types/config.ts +2 -59
- package/src/types/core.ts +45 -47
- package/src/types/http.ts +1 -23
- package/src/types/logger.ts +0 -9
- package/src/types/module.ts +0 -12
- package/dist/core/config/config-manager.d.ts +0 -44
- package/dist/core/config/config-manager.js +0 -114
- package/dist/core/config/config-manager.js.map +0 -1
- package/dist/core/config/config-sources.d.ts +0 -21
- package/dist/core/config/config-sources.js +0 -502
- package/dist/core/config/config-sources.js.map +0 -1
- package/dist/core/config/config-validator.d.ts +0 -21
- package/dist/core/config/config-validator.js +0 -765
- package/dist/core/config/config-validator.js.map +0 -1
- package/dist/core/middleware/built-in/jwt-helpers.d.ts +0 -118
- package/dist/core/middleware/built-in/jwt-helpers.js +0 -221
- package/dist/core/middleware/built-in/jwt-helpers.js.map +0 -1
- package/src/core/config/config-manager.ts +0 -133
- package/src/core/config/config-sources.ts +0 -596
- package/src/core/config/config-validator.ts +0 -1078
- package/src/core/middleware/built-in/jwt-helpers.ts +0 -240
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Moro Logger - Beautiful, Fast, Feature-Rich
|
|
2
2
|
import { performance } from 'perf_hooks';
|
|
3
|
-
|
|
3
|
+
// import { format } from 'util'; // Not currently used
|
|
4
4
|
import {
|
|
5
5
|
LogLevel,
|
|
6
6
|
LogEntry,
|
|
@@ -41,27 +41,13 @@ export class MoroLogger implements Logger {
|
|
|
41
41
|
private lastTimestamp = 0;
|
|
42
42
|
private timestampCacheInterval = 100; // 100ms for better precision
|
|
43
43
|
|
|
44
|
-
//
|
|
45
|
-
private static readonly ENTRY_POOL: LogEntry[] = [];
|
|
46
|
-
private static readonly MAX_POOL_SIZE = 100;
|
|
47
|
-
private static poolIndex = 0;
|
|
48
|
-
|
|
49
|
-
// String builder for efficient concatenation
|
|
50
|
-
private static stringBuilder: string[] = [];
|
|
51
|
-
private static stringBuilderIndex = 0;
|
|
52
|
-
|
|
53
|
-
// Buffered output for performance
|
|
44
|
+
// Buffered output for micro-batching
|
|
54
45
|
private outputBuffer: string[] = [];
|
|
55
46
|
private bufferSize = 0;
|
|
56
|
-
private maxBufferSize =
|
|
47
|
+
private maxBufferSize = 1024; // 1KB buffer
|
|
57
48
|
private flushTimeout: NodeJS.Timeout | null = null;
|
|
58
49
|
private flushInterval = 1; // 1ms micro-batching
|
|
59
50
|
|
|
60
|
-
// Buffer overflow protection
|
|
61
|
-
private bufferOverflowThreshold: number;
|
|
62
|
-
private emergencyFlushInProgress = false;
|
|
63
|
-
private isDestroyed = false;
|
|
64
|
-
|
|
65
51
|
// High-performance output methods
|
|
66
52
|
|
|
67
53
|
private static readonly LEVELS: Record<LogLevel, number> = {
|
|
@@ -72,6 +58,84 @@ export class MoroLogger implements Logger {
|
|
|
72
58
|
fatal: 4,
|
|
73
59
|
};
|
|
74
60
|
|
|
61
|
+
// Static pre-allocated strings for maximum performance
|
|
62
|
+
private static readonly LEVEL_STRINGS: Record<LogLevel, string> = {
|
|
63
|
+
debug: 'DEBUG',
|
|
64
|
+
info: 'INFO',
|
|
65
|
+
warn: 'WARN',
|
|
66
|
+
error: 'ERROR',
|
|
67
|
+
fatal: 'FATAL',
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// Pre-allocated ANSI color codes
|
|
71
|
+
private static readonly ANSI_COLORS = {
|
|
72
|
+
reset: '\x1b[0m',
|
|
73
|
+
bold: '\x1b[1m',
|
|
74
|
+
dim: '\x1b[2m',
|
|
75
|
+
red: '\x1b[31m',
|
|
76
|
+
green: '\x1b[32m',
|
|
77
|
+
yellow: '\x1b[33m',
|
|
78
|
+
blue: '\x1b[34m',
|
|
79
|
+
magenta: '\x1b[35m',
|
|
80
|
+
cyan: '\x1b[36m',
|
|
81
|
+
white: '\x1b[37m',
|
|
82
|
+
gray: '\x1b[90m',
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// Object pool for LogEntry reuse
|
|
86
|
+
private static readonly ENTRY_POOL: LogEntry[] = [];
|
|
87
|
+
private static readonly MAX_POOL_SIZE = 100;
|
|
88
|
+
private static poolIndex = 0;
|
|
89
|
+
|
|
90
|
+
// Object pool management
|
|
91
|
+
private static getPooledEntry(): LogEntry {
|
|
92
|
+
if (MoroLogger.poolIndex > 0) {
|
|
93
|
+
return MoroLogger.ENTRY_POOL[--MoroLogger.poolIndex];
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
timestamp: new Date(),
|
|
97
|
+
level: 'info',
|
|
98
|
+
message: '',
|
|
99
|
+
context: undefined,
|
|
100
|
+
metadata: undefined,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private static returnPooledEntry(entry: LogEntry): void {
|
|
105
|
+
if (MoroLogger.poolIndex < MoroLogger.MAX_POOL_SIZE) {
|
|
106
|
+
// Reset the entry
|
|
107
|
+
entry.timestamp = new Date();
|
|
108
|
+
entry.level = 'info';
|
|
109
|
+
entry.message = '';
|
|
110
|
+
entry.context = undefined;
|
|
111
|
+
entry.metadata = undefined;
|
|
112
|
+
MoroLogger.ENTRY_POOL[MoroLogger.poolIndex++] = entry;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// String builder for efficient concatenation
|
|
117
|
+
private static stringBuilder: string[] = [];
|
|
118
|
+
private static stringBuilderIndex = 0;
|
|
119
|
+
|
|
120
|
+
private static resetStringBuilder(): void {
|
|
121
|
+
MoroLogger.stringBuilderIndex = 0;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private static appendToBuilder(str: string): void {
|
|
125
|
+
if (MoroLogger.stringBuilderIndex < MoroLogger.stringBuilder.length) {
|
|
126
|
+
MoroLogger.stringBuilder[MoroLogger.stringBuilderIndex++] = str;
|
|
127
|
+
} else {
|
|
128
|
+
MoroLogger.stringBuilder.push(str);
|
|
129
|
+
MoroLogger.stringBuilderIndex++;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private static buildString(): string {
|
|
134
|
+
const result = MoroLogger.stringBuilder.slice(0, MoroLogger.stringBuilderIndex).join('');
|
|
135
|
+
MoroLogger.resetStringBuilder();
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
|
|
75
139
|
private static readonly COLORS: ColorScheme = {
|
|
76
140
|
debug: '\x1b[36m', // Cyan
|
|
77
141
|
info: '\x1b[32m', // Green
|
|
@@ -82,23 +146,13 @@ export class MoroLogger implements Logger {
|
|
|
82
146
|
context: '\x1b[34m', // Blue
|
|
83
147
|
metadata: '\x1b[37m', // White
|
|
84
148
|
performance: '\x1b[36m', // Cyan
|
|
85
|
-
reset: '\x1b[0m', // Reset
|
|
86
149
|
};
|
|
87
150
|
|
|
88
151
|
private static readonly RESET = '\x1b[0m';
|
|
89
152
|
private static readonly BOLD = '\x1b[1m';
|
|
90
153
|
|
|
91
|
-
// Static pre-allocated strings for performance
|
|
92
|
-
private static readonly LEVEL_STRINGS: Record<LogLevel, string> = {
|
|
93
|
-
debug: 'DEBUG',
|
|
94
|
-
info: 'INFO ',
|
|
95
|
-
warn: 'WARN ',
|
|
96
|
-
error: 'ERROR',
|
|
97
|
-
fatal: 'FATAL',
|
|
98
|
-
};
|
|
99
|
-
|
|
100
154
|
constructor(options: LoggerOptions = {}) {
|
|
101
|
-
this.options =
|
|
155
|
+
this.options = {
|
|
102
156
|
level: 'info',
|
|
103
157
|
enableColors: true,
|
|
104
158
|
enableTimestamp: true,
|
|
@@ -109,18 +163,11 @@ export class MoroLogger implements Logger {
|
|
|
109
163
|
outputs: [],
|
|
110
164
|
filters: [],
|
|
111
165
|
maxEntries: 1000,
|
|
112
|
-
maxBufferSize: 1000,
|
|
113
166
|
...options,
|
|
114
|
-
}
|
|
167
|
+
};
|
|
115
168
|
|
|
116
169
|
this.level = this.options.level || 'info';
|
|
117
170
|
|
|
118
|
-
// Initialize buffer size from options
|
|
119
|
-
this.maxBufferSize = this.options.maxBufferSize || 1000;
|
|
120
|
-
|
|
121
|
-
// Initialize buffer overflow protection
|
|
122
|
-
this.bufferOverflowThreshold = this.maxBufferSize * 2;
|
|
123
|
-
|
|
124
171
|
// Add default console output
|
|
125
172
|
this.addOutput({
|
|
126
173
|
name: 'console',
|
|
@@ -133,58 +180,6 @@ export class MoroLogger implements Logger {
|
|
|
133
180
|
this.options.filters?.forEach(filter => this.addFilter(filter));
|
|
134
181
|
}
|
|
135
182
|
|
|
136
|
-
// Object pooling methods
|
|
137
|
-
private static getPooledEntry(): LogEntry {
|
|
138
|
-
if (MoroLogger.ENTRY_POOL.length > 0) {
|
|
139
|
-
const entry = MoroLogger.ENTRY_POOL.pop()!;
|
|
140
|
-
// Properly reset ALL properties to prevent memory leaks
|
|
141
|
-
entry.timestamp = new Date();
|
|
142
|
-
entry.level = 'info';
|
|
143
|
-
entry.message = '';
|
|
144
|
-
entry.context = undefined;
|
|
145
|
-
entry.metadata = undefined;
|
|
146
|
-
entry.performance = undefined;
|
|
147
|
-
entry.moduleId = undefined;
|
|
148
|
-
return entry;
|
|
149
|
-
}
|
|
150
|
-
return MoroLogger.createFreshEntry();
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// ADD this new method:
|
|
154
|
-
private static createFreshEntry(): LogEntry {
|
|
155
|
-
return {
|
|
156
|
-
timestamp: new Date(),
|
|
157
|
-
level: 'info',
|
|
158
|
-
message: '',
|
|
159
|
-
context: undefined,
|
|
160
|
-
metadata: undefined,
|
|
161
|
-
performance: undefined,
|
|
162
|
-
moduleId: undefined,
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
private static returnPooledEntry(entry: LogEntry): void {
|
|
167
|
-
if (MoroLogger.ENTRY_POOL.length < MoroLogger.MAX_POOL_SIZE) {
|
|
168
|
-
MoroLogger.ENTRY_POOL.push(entry);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// String builder methods
|
|
173
|
-
private static resetStringBuilder(): void {
|
|
174
|
-
MoroLogger.stringBuilder.length = 0;
|
|
175
|
-
MoroLogger.stringBuilderIndex = 0;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
private static appendToBuilder(str: string): void {
|
|
179
|
-
MoroLogger.stringBuilder[MoroLogger.stringBuilderIndex++] = str;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
private static buildString(): string {
|
|
183
|
-
const result = MoroLogger.stringBuilder.join('');
|
|
184
|
-
MoroLogger.resetStringBuilder();
|
|
185
|
-
return result;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
183
|
debug(message: string, context?: string, metadata?: Record<string, any>): void {
|
|
189
184
|
this.log('debug', message, context, metadata);
|
|
190
185
|
}
|
|
@@ -227,9 +222,7 @@ export class MoroLogger implements Logger {
|
|
|
227
222
|
}
|
|
228
223
|
|
|
229
224
|
child(context: string, metadata?: Record<string, any>): Logger {
|
|
230
|
-
|
|
231
|
-
const childOptions = { ...this.options, level: this.level };
|
|
232
|
-
const childLogger = new MoroLogger(childOptions);
|
|
225
|
+
const childLogger = new MoroLogger(this.options);
|
|
233
226
|
childLogger.contextPrefix = this.contextPrefix ? `${this.contextPrefix}:${context}` : context;
|
|
234
227
|
childLogger.contextMetadata = { ...this.contextMetadata, ...metadata };
|
|
235
228
|
childLogger.outputs = this.outputs;
|
|
@@ -245,10 +238,6 @@ export class MoroLogger implements Logger {
|
|
|
245
238
|
this.level = level;
|
|
246
239
|
}
|
|
247
240
|
|
|
248
|
-
getLevel(): LogLevel {
|
|
249
|
-
return this.level;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
241
|
addOutput(output: LogOutput): void {
|
|
253
242
|
this.outputs.set(output.name, output);
|
|
254
243
|
}
|
|
@@ -298,17 +287,6 @@ export class MoroLogger implements Logger {
|
|
|
298
287
|
return this.cachedTimestamp;
|
|
299
288
|
}
|
|
300
289
|
|
|
301
|
-
// Cached timestamp generation (updates once per second)
|
|
302
|
-
private getFastCachedTimestamp(): string {
|
|
303
|
-
const now = Date.now();
|
|
304
|
-
if (now - this.lastTimestamp > 1000) {
|
|
305
|
-
// Update every second
|
|
306
|
-
this.lastTimestamp = now;
|
|
307
|
-
this.cachedTimestamp = new Date(now).toISOString().slice(0, 19).replace('T', ' ');
|
|
308
|
-
}
|
|
309
|
-
return this.cachedTimestamp;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
290
|
getMetrics(): LogMetrics {
|
|
313
291
|
const now = Date.now();
|
|
314
292
|
const uptime = (now - this.startTime) / 1000; // seconds
|
|
@@ -336,49 +314,62 @@ export class MoroLogger implements Logger {
|
|
|
336
314
|
};
|
|
337
315
|
}
|
|
338
316
|
|
|
339
|
-
// Optimized logging method
|
|
317
|
+
// Optimized logging method with aggressive level checking
|
|
340
318
|
private log(
|
|
341
319
|
level: LogLevel,
|
|
342
320
|
message: string,
|
|
343
321
|
context?: string,
|
|
344
322
|
metadata?: Record<string, any>
|
|
345
323
|
): void {
|
|
346
|
-
//
|
|
347
|
-
const
|
|
348
|
-
|
|
349
|
-
|
|
324
|
+
// AGGRESSIVE LEVEL CHECK - numeric comparison for maximum speed
|
|
325
|
+
const levelNum = MoroLogger.LEVELS[level];
|
|
326
|
+
const effectiveLevelNum = this.parent
|
|
327
|
+
? MoroLogger.LEVELS[this.parent.level]
|
|
328
|
+
: MoroLogger.LEVELS[this.level];
|
|
329
|
+
|
|
330
|
+
if (levelNum < effectiveLevelNum) {
|
|
331
|
+
return; // Exit immediately if level is too low
|
|
350
332
|
}
|
|
351
333
|
|
|
352
|
-
//
|
|
334
|
+
// ULTRA-FAST PATH: Just message, no context, no metadata
|
|
353
335
|
if (!metadata && !context && !this.contextPrefix && !this.contextMetadata) {
|
|
354
|
-
|
|
336
|
+
const levelStr = MoroLogger.LEVEL_STRINGS[level];
|
|
337
|
+
this.output(`${levelStr} ${message}\n`, level);
|
|
355
338
|
return;
|
|
356
339
|
}
|
|
357
340
|
|
|
358
|
-
//
|
|
341
|
+
// FAST PATH: Message + context, no metadata
|
|
359
342
|
if (!metadata && !this.contextMetadata) {
|
|
360
|
-
|
|
343
|
+
const levelStr = MoroLogger.LEVEL_STRINGS[level];
|
|
344
|
+
if (context) {
|
|
345
|
+
this.output(`${levelStr} [${context}] ${message}\n`, level);
|
|
346
|
+
} else {
|
|
347
|
+
this.output(`${levelStr} ${message}\n`, level);
|
|
348
|
+
}
|
|
361
349
|
return;
|
|
362
350
|
}
|
|
363
351
|
|
|
364
|
-
//
|
|
365
|
-
if (metadata && Object.keys(metadata).length
|
|
366
|
-
|
|
352
|
+
// MEDIUM PATH: Message + context + simple metadata
|
|
353
|
+
if (metadata && Object.keys(metadata).length <= 3 && !this.contextMetadata) {
|
|
354
|
+
const levelStr = MoroLogger.LEVEL_STRINGS[level];
|
|
355
|
+
const contextStr = context ? `[${context}] ` : '';
|
|
356
|
+
const metaStr = this.stringify(metadata);
|
|
357
|
+
this.output(`${levelStr} ${contextStr}${message} ${metaStr}\n`, level);
|
|
367
358
|
return;
|
|
368
359
|
}
|
|
369
360
|
|
|
370
|
-
//
|
|
361
|
+
// FULL PATH: All features enabled
|
|
371
362
|
this.fullLog(level, message, context, metadata);
|
|
372
363
|
}
|
|
373
364
|
|
|
374
|
-
// Full logging with all features
|
|
365
|
+
// Full logging with all features using object pooling
|
|
375
366
|
private fullLog(
|
|
376
367
|
level: LogLevel,
|
|
377
368
|
message: string,
|
|
378
369
|
context?: string,
|
|
379
370
|
metadata?: Record<string, any>
|
|
380
371
|
): void {
|
|
381
|
-
//
|
|
372
|
+
// Get pooled entry to avoid allocation
|
|
382
373
|
const entry = MoroLogger.getPooledEntry();
|
|
383
374
|
const now = Date.now();
|
|
384
375
|
|
|
@@ -397,7 +388,6 @@ export class MoroLogger implements Logger {
|
|
|
397
388
|
if (this.filters.size > 0) {
|
|
398
389
|
for (const filter of this.filters.values()) {
|
|
399
390
|
if (!filter.filter(entry)) {
|
|
400
|
-
MoroLogger.returnPooledEntry(entry);
|
|
401
391
|
return;
|
|
402
392
|
}
|
|
403
393
|
}
|
|
@@ -412,89 +402,8 @@ export class MoroLogger implements Logger {
|
|
|
412
402
|
// Write to outputs with batched processing
|
|
413
403
|
this.writeToOutputs(entry, level);
|
|
414
404
|
|
|
415
|
-
// Return entry to pool
|
|
416
|
-
MoroLogger.returnPooledEntry(entry);
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
// Absolute minimal logging - pure speed, no overhead
|
|
420
|
-
private complexLog(
|
|
421
|
-
level: LogLevel,
|
|
422
|
-
message: string,
|
|
423
|
-
context?: string,
|
|
424
|
-
metadata?: Record<string, any>
|
|
425
|
-
): void {
|
|
426
|
-
// Use object pooling for LogEntry (Pino's technique)
|
|
427
|
-
const entry = MoroLogger.getPooledEntry();
|
|
428
|
-
const now = Date.now();
|
|
429
|
-
|
|
430
|
-
entry.timestamp = new Date(now);
|
|
431
|
-
entry.level = level;
|
|
432
|
-
entry.message = message;
|
|
433
|
-
entry.context = this.contextPrefix
|
|
434
|
-
? context
|
|
435
|
-
? `${this.contextPrefix}:${context}`
|
|
436
|
-
: this.contextPrefix
|
|
437
|
-
: context;
|
|
438
|
-
entry.metadata = this.createMetadata(metadata);
|
|
439
|
-
entry.performance = this.options.enablePerformance ? this.getPerformanceData(now) : undefined;
|
|
440
|
-
|
|
441
|
-
// Write to outputs with batched processing
|
|
442
|
-
this.writeToOutputs(entry, level);
|
|
443
|
-
|
|
444
|
-
// Return entry to pool
|
|
445
|
-
MoroLogger.returnPooledEntry(entry);
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
// Simple log writer with colors for minimal overhead cases
|
|
449
|
-
private writeSimpleLog(level: LogLevel, message: string, context?: string): void {
|
|
450
|
-
const colors = this.options.enableColors !== false;
|
|
451
|
-
const levelReset = colors ? MoroLogger.RESET : '';
|
|
452
|
-
|
|
453
|
-
MoroLogger.resetStringBuilder();
|
|
454
|
-
|
|
455
|
-
// Timestamp with caching optimization
|
|
456
|
-
if (this.options.enableTimestamp !== false) {
|
|
457
|
-
const timestamp = this.getFastCachedTimestamp();
|
|
458
|
-
if (colors) {
|
|
459
|
-
MoroLogger.appendToBuilder(MoroLogger.COLORS.timestamp);
|
|
460
|
-
MoroLogger.appendToBuilder(timestamp);
|
|
461
|
-
MoroLogger.appendToBuilder(levelReset);
|
|
462
|
-
} else {
|
|
463
|
-
MoroLogger.appendToBuilder(timestamp);
|
|
464
|
-
}
|
|
465
|
-
MoroLogger.appendToBuilder(' ');
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
// Level with pre-allocated strings
|
|
469
|
-
const levelStr = MoroLogger.LEVEL_STRINGS[level];
|
|
470
|
-
if (colors) {
|
|
471
|
-
MoroLogger.appendToBuilder(MoroLogger.COLORS[level]);
|
|
472
|
-
MoroLogger.appendToBuilder(MoroLogger.BOLD);
|
|
473
|
-
MoroLogger.appendToBuilder(levelStr);
|
|
474
|
-
MoroLogger.appendToBuilder(levelReset);
|
|
475
|
-
} else {
|
|
476
|
-
MoroLogger.appendToBuilder(levelStr);
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
// Context
|
|
480
|
-
if (context && this.options.enableContext !== false) {
|
|
481
|
-
MoroLogger.appendToBuilder(' ');
|
|
482
|
-
if (colors) {
|
|
483
|
-
MoroLogger.appendToBuilder(MoroLogger.COLORS.context);
|
|
484
|
-
MoroLogger.appendToBuilder(`[${context}]`);
|
|
485
|
-
MoroLogger.appendToBuilder(levelReset);
|
|
486
|
-
} else {
|
|
487
|
-
MoroLogger.appendToBuilder(`[${context}]`);
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
// Message
|
|
492
|
-
MoroLogger.appendToBuilder(' ');
|
|
493
|
-
MoroLogger.appendToBuilder(message);
|
|
494
|
-
|
|
495
|
-
// Output main log line with high-performance method
|
|
496
|
-
const finalMessage = MoroLogger.buildString();
|
|
497
|
-
this.output(`${finalMessage}\n`, level);
|
|
405
|
+
// Return entry to pool after a short delay to allow async operations
|
|
406
|
+
setTimeout(() => MoroLogger.returnPooledEntry(entry), 0);
|
|
498
407
|
}
|
|
499
408
|
|
|
500
409
|
private updateMetrics(entry: LogEntry): void {
|
|
@@ -548,39 +457,24 @@ export class MoroLogger implements Logger {
|
|
|
548
457
|
private writeToOutputs(entry: LogEntry, level: LogLevel): void {
|
|
549
458
|
if (this.outputs.size === 0) return;
|
|
550
459
|
|
|
551
|
-
let successCount = 0;
|
|
552
|
-
const errors: Array<{ outputName: string; error: any }> = [];
|
|
553
|
-
|
|
554
460
|
for (const output of this.outputs.values()) {
|
|
555
461
|
if (!output.level || MoroLogger.LEVELS[level] >= MoroLogger.LEVELS[output.level]) {
|
|
556
462
|
try {
|
|
557
463
|
output.write(entry);
|
|
558
|
-
successCount++;
|
|
559
464
|
} catch (error) {
|
|
560
|
-
|
|
561
|
-
|
|
465
|
+
// Fallback to console.error for logger errors
|
|
466
|
+
// eslint-disable-next-line no-console
|
|
467
|
+
console.error('Logger output error:', error);
|
|
562
468
|
}
|
|
563
469
|
}
|
|
564
470
|
}
|
|
565
|
-
|
|
566
|
-
// If all outputs fail, use emergency console
|
|
567
|
-
if (successCount === 0 && this.outputs.size > 0) {
|
|
568
|
-
this.emergencyConsoleWrite(entry);
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
// Log output errors (but avoid infinite loops)
|
|
572
|
-
if (errors.length > 0 && level !== 'error') {
|
|
573
|
-
this.error(`Logger output errors: ${errors.length} failed`, 'MoroLogger', {
|
|
574
|
-
errors: errors.map(e => e.outputName),
|
|
575
|
-
});
|
|
576
|
-
}
|
|
577
471
|
}
|
|
578
472
|
|
|
579
473
|
private writeToConsole(entry: LogEntry): void {
|
|
580
474
|
const format = this.options.format || 'pretty';
|
|
581
475
|
|
|
582
476
|
if (format === 'json') {
|
|
583
|
-
this.output(
|
|
477
|
+
this.output(JSON.stringify(entry) + '\n', entry.level);
|
|
584
478
|
return;
|
|
585
479
|
}
|
|
586
480
|
|
|
@@ -597,8 +491,6 @@ export class MoroLogger implements Logger {
|
|
|
597
491
|
|
|
598
492
|
private writePrettyLog(entry: LogEntry): void {
|
|
599
493
|
const colors = this.options.enableColors !== false;
|
|
600
|
-
const levelReset = colors ? MoroLogger.RESET : '';
|
|
601
|
-
|
|
602
494
|
MoroLogger.resetStringBuilder();
|
|
603
495
|
|
|
604
496
|
// Timestamp with caching optimization
|
|
@@ -607,29 +499,37 @@ export class MoroLogger implements Logger {
|
|
|
607
499
|
if (colors) {
|
|
608
500
|
MoroLogger.appendToBuilder(MoroLogger.COLORS.timestamp);
|
|
609
501
|
MoroLogger.appendToBuilder(timestamp);
|
|
610
|
-
MoroLogger.appendToBuilder(
|
|
502
|
+
MoroLogger.appendToBuilder(MoroLogger.RESET);
|
|
611
503
|
} else {
|
|
612
504
|
MoroLogger.appendToBuilder(timestamp);
|
|
613
505
|
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// Level with color using pre-allocated strings
|
|
509
|
+
const levelColor = colors ? MoroLogger.COLORS[entry.level] : '';
|
|
510
|
+
const levelReset = colors ? MoroLogger.RESET : '';
|
|
511
|
+
const levelText = MoroLogger.LEVEL_STRINGS[entry.level];
|
|
512
|
+
|
|
513
|
+
// Add space after timestamp if present
|
|
514
|
+
if (this.options.enableTimestamp !== false) {
|
|
614
515
|
MoroLogger.appendToBuilder(' ');
|
|
615
516
|
}
|
|
616
517
|
|
|
617
|
-
// Level with pre-allocated strings
|
|
618
|
-
const levelStr = MoroLogger.LEVEL_STRINGS[entry.level];
|
|
619
518
|
if (colors) {
|
|
620
|
-
MoroLogger.appendToBuilder(
|
|
519
|
+
MoroLogger.appendToBuilder(levelColor);
|
|
621
520
|
MoroLogger.appendToBuilder(MoroLogger.BOLD);
|
|
622
|
-
MoroLogger.appendToBuilder(
|
|
521
|
+
MoroLogger.appendToBuilder(levelText);
|
|
623
522
|
MoroLogger.appendToBuilder(levelReset);
|
|
624
523
|
} else {
|
|
625
|
-
MoroLogger.appendToBuilder(
|
|
524
|
+
MoroLogger.appendToBuilder(levelText);
|
|
626
525
|
}
|
|
627
526
|
|
|
628
527
|
// Context
|
|
629
528
|
if (entry.context && this.options.enableContext !== false) {
|
|
630
|
-
MoroLogger.
|
|
529
|
+
const contextColor = colors ? MoroLogger.COLORS.context : '';
|
|
530
|
+
MoroLogger.appendToBuilder(' '); // Space before context
|
|
631
531
|
if (colors) {
|
|
632
|
-
MoroLogger.appendToBuilder(
|
|
532
|
+
MoroLogger.appendToBuilder(contextColor);
|
|
633
533
|
MoroLogger.appendToBuilder(`[${entry.context}]`);
|
|
634
534
|
MoroLogger.appendToBuilder(levelReset);
|
|
635
535
|
} else {
|
|
@@ -638,7 +538,7 @@ export class MoroLogger implements Logger {
|
|
|
638
538
|
}
|
|
639
539
|
|
|
640
540
|
// Message
|
|
641
|
-
MoroLogger.appendToBuilder(' ');
|
|
541
|
+
MoroLogger.appendToBuilder(' '); // Space before message
|
|
642
542
|
MoroLogger.appendToBuilder(entry.message);
|
|
643
543
|
|
|
644
544
|
// Performance info
|
|
@@ -654,7 +554,7 @@ export class MoroLogger implements Logger {
|
|
|
654
554
|
}
|
|
655
555
|
|
|
656
556
|
if (perfParts.length > 0) {
|
|
657
|
-
MoroLogger.appendToBuilder(' ');
|
|
557
|
+
MoroLogger.appendToBuilder(' '); // Space before performance info
|
|
658
558
|
if (colors) {
|
|
659
559
|
MoroLogger.appendToBuilder(perfColor);
|
|
660
560
|
MoroLogger.appendToBuilder(`(${perfParts.join(', ')})`);
|
|
@@ -675,20 +575,19 @@ export class MoroLogger implements Logger {
|
|
|
675
575
|
const cleanMetadata = this.cleanMetadata(entry.metadata);
|
|
676
576
|
|
|
677
577
|
if (Object.keys(cleanMetadata).length > 0) {
|
|
678
|
-
MoroLogger.appendToBuilder(' ');
|
|
679
578
|
if (colors) {
|
|
680
579
|
MoroLogger.appendToBuilder(metaColor);
|
|
681
|
-
MoroLogger.appendToBuilder(this.
|
|
580
|
+
MoroLogger.appendToBuilder(this.stringify(cleanMetadata));
|
|
682
581
|
MoroLogger.appendToBuilder(levelReset);
|
|
683
582
|
} else {
|
|
684
|
-
MoroLogger.appendToBuilder(this.
|
|
583
|
+
MoroLogger.appendToBuilder(this.stringify(cleanMetadata));
|
|
685
584
|
}
|
|
686
585
|
}
|
|
687
586
|
}
|
|
688
587
|
|
|
689
588
|
// Output main log line with high-performance method
|
|
690
589
|
const finalMessage = MoroLogger.buildString();
|
|
691
|
-
this.output(
|
|
590
|
+
this.output(finalMessage + '\n', entry.level);
|
|
692
591
|
|
|
693
592
|
// Stack trace for errors
|
|
694
593
|
if (entry.metadata?.stack && (entry.level === 'error' || entry.level === 'fatal')) {
|
|
@@ -708,196 +607,79 @@ export class MoroLogger implements Logger {
|
|
|
708
607
|
return clean;
|
|
709
608
|
}
|
|
710
609
|
|
|
711
|
-
//
|
|
712
|
-
private
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
) {
|
|
718
|
-
this.emergencyFlushInProgress = true;
|
|
719
|
-
this.forceFlushBuffer();
|
|
720
|
-
this.emergencyFlushInProgress = false;
|
|
610
|
+
// Fast JSON stringify with error handling
|
|
611
|
+
private stringify(obj: any): string {
|
|
612
|
+
try {
|
|
613
|
+
return JSON.stringify(obj);
|
|
614
|
+
} catch {
|
|
615
|
+
return '[Circular Reference]';
|
|
721
616
|
}
|
|
617
|
+
}
|
|
722
618
|
|
|
619
|
+
// High-performance output with micro-batching
|
|
620
|
+
private output(message: string, level: LogLevel = 'info'): void {
|
|
621
|
+
// Add to buffer
|
|
723
622
|
this.outputBuffer.push(message);
|
|
724
|
-
this.bufferSize
|
|
623
|
+
this.bufferSize += message.length;
|
|
725
624
|
|
|
726
|
-
//
|
|
727
|
-
if (
|
|
625
|
+
// Flush immediately if buffer is full or for errors
|
|
626
|
+
if (this.bufferSize >= this.maxBufferSize || level === 'error' || level === 'fatal') {
|
|
728
627
|
this.flushBuffer();
|
|
729
628
|
} else {
|
|
629
|
+
// Schedule flush with micro-batching
|
|
730
630
|
this.scheduleFlush();
|
|
731
631
|
}
|
|
732
632
|
}
|
|
733
633
|
|
|
734
634
|
private scheduleFlush(): void {
|
|
735
|
-
if (this.flushTimeout
|
|
736
|
-
return; // Already scheduled or destroyed
|
|
737
|
-
}
|
|
635
|
+
if (this.flushTimeout) return; // Already scheduled
|
|
738
636
|
|
|
739
637
|
this.flushTimeout = setTimeout(() => {
|
|
740
638
|
this.flushBuffer();
|
|
639
|
+
this.flushTimeout = null;
|
|
741
640
|
}, this.flushInterval);
|
|
742
641
|
}
|
|
743
642
|
|
|
744
|
-
|
|
745
|
-
if (this.outputBuffer.length === 0)
|
|
746
|
-
return;
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
// Group messages by stream type
|
|
750
|
-
const stdoutMessages: string[] = [];
|
|
751
|
-
const stderrMessages: string[] = [];
|
|
643
|
+
private flushBuffer(): void {
|
|
644
|
+
if (this.outputBuffer.length === 0) return;
|
|
752
645
|
|
|
753
|
-
|
|
754
|
-
//
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
646
|
+
try {
|
|
647
|
+
// Group by stream type for efficiency
|
|
648
|
+
const stdoutMessages: string[] = [];
|
|
649
|
+
const stderrMessages: string[] = [];
|
|
650
|
+
|
|
651
|
+
for (const message of this.outputBuffer) {
|
|
652
|
+
// Determine stream based on message content (simple heuristic)
|
|
653
|
+
if (message.includes('ERROR') || message.includes('FATAL')) {
|
|
654
|
+
stderrMessages.push(message);
|
|
655
|
+
} else {
|
|
656
|
+
stdoutMessages.push(message);
|
|
657
|
+
}
|
|
759
658
|
}
|
|
760
|
-
}
|
|
761
659
|
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
if (stdoutMessages.length > 0 && process.stdout.writable) {
|
|
660
|
+
// Write to streams
|
|
661
|
+
if (stdoutMessages.length > 0) {
|
|
765
662
|
process.stdout.write(stdoutMessages.join(''));
|
|
766
663
|
}
|
|
767
|
-
if (stderrMessages.length > 0
|
|
664
|
+
if (stderrMessages.length > 0) {
|
|
768
665
|
process.stderr.write(stderrMessages.join(''));
|
|
769
666
|
}
|
|
770
667
|
} catch {
|
|
771
|
-
// Fallback to console if
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
668
|
+
// Fallback to console methods if stream write fails
|
|
669
|
+
for (const message of this.outputBuffer) {
|
|
670
|
+
if (message.includes('ERROR') || message.includes('FATAL')) {
|
|
671
|
+
// eslint-disable-next-line no-console
|
|
672
|
+
console.error(message.trim());
|
|
673
|
+
} else {
|
|
674
|
+
// eslint-disable-next-line no-console
|
|
675
|
+
console.log(message.trim());
|
|
676
|
+
}
|
|
777
677
|
}
|
|
778
678
|
}
|
|
779
679
|
|
|
780
|
-
//
|
|
680
|
+
// Reset buffer
|
|
781
681
|
this.outputBuffer.length = 0;
|
|
782
682
|
this.bufferSize = 0;
|
|
783
|
-
|
|
784
|
-
// Clear timeout
|
|
785
|
-
if (this.flushTimeout) {
|
|
786
|
-
clearTimeout(this.flushTimeout);
|
|
787
|
-
this.flushTimeout = null;
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
// Emergency flush for buffer overflow protection
|
|
792
|
-
private forceFlushBuffer(): void {
|
|
793
|
-
if (this.outputBuffer.length === 0) return;
|
|
794
|
-
|
|
795
|
-
try {
|
|
796
|
-
const message = this.outputBuffer.join('');
|
|
797
|
-
process.stdout.write(message);
|
|
798
|
-
} catch (error) {
|
|
799
|
-
// Emergency fallback - write individual messages
|
|
800
|
-
for (const msg of this.outputBuffer) {
|
|
801
|
-
try {
|
|
802
|
-
process.stdout.write(msg);
|
|
803
|
-
} catch {
|
|
804
|
-
// If even this fails, give up on this batch
|
|
805
|
-
break;
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
} finally {
|
|
809
|
-
this.outputBuffer.length = 0;
|
|
810
|
-
this.bufferSize = 0;
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
// Safe stringify with circular reference detection
|
|
815
|
-
private safeStringify(obj: any, maxDepth = 3): string {
|
|
816
|
-
const seen = new WeakSet();
|
|
817
|
-
|
|
818
|
-
const stringify = (value: any, depth: number): any => {
|
|
819
|
-
if (depth > maxDepth) return '[Max Depth Reached]';
|
|
820
|
-
if (value === null || typeof value !== 'object') return value;
|
|
821
|
-
if (seen.has(value)) return '[Circular Reference]';
|
|
822
|
-
|
|
823
|
-
seen.add(value);
|
|
824
|
-
|
|
825
|
-
if (Array.isArray(value)) {
|
|
826
|
-
return value.map(item => stringify(item, depth + 1));
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
const result: any = {};
|
|
830
|
-
for (const [key, val] of Object.entries(value)) {
|
|
831
|
-
if (typeof val !== 'function') {
|
|
832
|
-
// Skip functions
|
|
833
|
-
result[key] = stringify(val, depth + 1);
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
return result;
|
|
837
|
-
};
|
|
838
|
-
|
|
839
|
-
try {
|
|
840
|
-
return JSON.stringify(stringify(obj, 0));
|
|
841
|
-
} catch (error) {
|
|
842
|
-
return '[Stringify Error]';
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
// Configuration validation
|
|
847
|
-
private validateOptions(options: LoggerOptions): LoggerOptions {
|
|
848
|
-
const validated = { ...options };
|
|
849
|
-
|
|
850
|
-
// Validate log level
|
|
851
|
-
const validLevels = ['debug', 'info', 'warn', 'error', 'fatal'];
|
|
852
|
-
if (validated.level && !validLevels.includes(validated.level)) {
|
|
853
|
-
console.warn(`[MoroLogger] Invalid log level: ${validated.level}, defaulting to 'info'`);
|
|
854
|
-
validated.level = 'info';
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
// Validate max entries
|
|
858
|
-
if (validated.maxEntries !== undefined) {
|
|
859
|
-
if (validated.maxEntries < 1 || validated.maxEntries > 100000) {
|
|
860
|
-
console.warn(
|
|
861
|
-
`[MoroLogger] Invalid maxEntries: ${validated.maxEntries}, defaulting to 1000`
|
|
862
|
-
);
|
|
863
|
-
validated.maxEntries = 1000;
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
// Validate buffer size
|
|
868
|
-
if (validated.maxBufferSize !== undefined) {
|
|
869
|
-
if (validated.maxBufferSize < 10 || validated.maxBufferSize > 10000) {
|
|
870
|
-
console.warn(
|
|
871
|
-
`[MoroLogger] Invalid maxBufferSize: ${validated.maxBufferSize}, defaulting to 1000`
|
|
872
|
-
);
|
|
873
|
-
validated.maxBufferSize = 1000;
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
|
|
877
|
-
return validated;
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
// Error handling methods
|
|
881
|
-
private handleOutputError(outputName: string, error: any): void {
|
|
882
|
-
// Could implement output retry logic, circuit breaker, etc.
|
|
883
|
-
// For now, just track the error
|
|
884
|
-
if (!this.metrics.outputErrors) {
|
|
885
|
-
this.metrics.outputErrors = {};
|
|
886
|
-
}
|
|
887
|
-
this.metrics.outputErrors[outputName] = (this.metrics.outputErrors[outputName] || 0) + 1;
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
private emergencyConsoleWrite(entry: LogEntry): void {
|
|
891
|
-
const message = `${entry.timestamp.toISOString()} ${entry.level.toUpperCase()} ${entry.message}`;
|
|
892
|
-
try {
|
|
893
|
-
if (entry.level === 'error' || entry.level === 'fatal') {
|
|
894
|
-
process.stderr.write(`[EMERGENCY] ${message}\n`);
|
|
895
|
-
} else {
|
|
896
|
-
process.stdout.write(`[EMERGENCY] ${message}\n`);
|
|
897
|
-
}
|
|
898
|
-
} catch {
|
|
899
|
-
// If even emergency write fails, there's nothing more we can do
|
|
900
|
-
}
|
|
901
683
|
}
|
|
902
684
|
|
|
903
685
|
// Force flush streams (useful for shutdown)
|
|
@@ -912,40 +694,28 @@ export class MoroLogger implements Logger {
|
|
|
912
694
|
this.flushBuffer();
|
|
913
695
|
|
|
914
696
|
try {
|
|
915
|
-
// Force flush streams
|
|
697
|
+
// Force flush streams
|
|
916
698
|
if (process.stdout.writable) {
|
|
917
|
-
process.stdout.
|
|
699
|
+
process.stdout.end();
|
|
918
700
|
}
|
|
919
701
|
if (process.stderr.writable) {
|
|
920
|
-
process.stderr.
|
|
702
|
+
process.stderr.end();
|
|
921
703
|
}
|
|
922
704
|
} catch {
|
|
923
705
|
// Ignore flush errors
|
|
924
706
|
}
|
|
925
707
|
}
|
|
926
708
|
|
|
927
|
-
//
|
|
928
|
-
public
|
|
929
|
-
//
|
|
930
|
-
this.isDestroyed = true;
|
|
931
|
-
|
|
932
|
-
// Clear any remaining timeouts
|
|
709
|
+
// Cleanup method to clear all timeouts and handles
|
|
710
|
+
public cleanup(): void {
|
|
711
|
+
// Clear any pending flush timeout
|
|
933
712
|
if (this.flushTimeout) {
|
|
934
713
|
clearTimeout(this.flushTimeout);
|
|
935
714
|
this.flushTimeout = null;
|
|
936
715
|
}
|
|
937
716
|
|
|
938
|
-
// Flush any remaining
|
|
717
|
+
// Flush any remaining output
|
|
939
718
|
this.flushBuffer();
|
|
940
|
-
|
|
941
|
-
// Clear outputs and filters
|
|
942
|
-
this.outputs.clear();
|
|
943
|
-
this.filters.clear();
|
|
944
|
-
|
|
945
|
-
// Clear history
|
|
946
|
-
this.history.length = 0;
|
|
947
|
-
this.historyIndex = 0;
|
|
948
|
-
this.historySize = 0;
|
|
949
719
|
}
|
|
950
720
|
}
|
|
951
721
|
|
|
@@ -961,6 +731,33 @@ export const logger = new MoroLogger({
|
|
|
961
731
|
format: (process.env.LOG_FORMAT as any) || 'pretty',
|
|
962
732
|
});
|
|
963
733
|
|
|
734
|
+
// Add cleanup handlers for Jest and other test runners
|
|
735
|
+
if (typeof process !== 'undefined') {
|
|
736
|
+
// Cleanup on process exit
|
|
737
|
+
process.on('beforeExit', () => {
|
|
738
|
+
logger.cleanup();
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
process.on('SIGINT', () => {
|
|
742
|
+
logger.cleanup();
|
|
743
|
+
process.exit(0);
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
process.on('SIGTERM', () => {
|
|
747
|
+
logger.cleanup();
|
|
748
|
+
process.exit(0);
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
// For Jest and other test runners - cleanup on uncaught exceptions
|
|
752
|
+
process.on('uncaughtException', () => {
|
|
753
|
+
logger.cleanup();
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
process.on('unhandledRejection', () => {
|
|
757
|
+
logger.cleanup();
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
|
|
964
761
|
/**
|
|
965
762
|
* Configure the global logger with new settings
|
|
966
763
|
* This allows runtime configuration of the logger
|
|
@@ -973,14 +770,6 @@ export function configureGlobalLogger(options: Partial<LoggerOptions>): void {
|
|
|
973
770
|
// For now, focusing on level which is the most critical
|
|
974
771
|
}
|
|
975
772
|
|
|
976
|
-
/**
|
|
977
|
-
* Destroy the global logger and clean up resources (for testing)
|
|
978
|
-
* @internal
|
|
979
|
-
*/
|
|
980
|
-
export function destroyGlobalLogger(): void {
|
|
981
|
-
logger.destroy();
|
|
982
|
-
}
|
|
983
|
-
|
|
984
773
|
/**
|
|
985
774
|
* Apply logging configuration from the config system and/or createApp options
|
|
986
775
|
*/
|