node-logy 0.2.1 → 0.2.3
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/dist/index.d.ts +3 -266
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -716
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +251 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +656 -0
- package/dist/logger.js.map +1 -0
- package/package.json +5 -2
package/dist/index.js
CHANGED
|
@@ -1,717 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import { Worker } from "node:worker_threads";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
-
const __dirname = dirname(__filename);
|
|
8
|
-
/**
|
|
9
|
-
* Numeric priority for log levels (higher = more severe)
|
|
10
|
-
*/
|
|
11
|
-
const LogLevelPriority = {
|
|
12
|
-
[LOG_LEVEL.DEBUG]: 0,
|
|
13
|
-
[LOG_LEVEL.INFO]: 1,
|
|
14
|
-
[LOG_LEVEL.WARN]: 2,
|
|
15
|
-
[LOG_LEVEL.ERROR]: 3,
|
|
16
|
-
[LOG_LEVEL.FATAL]: 4,
|
|
17
|
-
};
|
|
18
|
-
// ANSI color codes
|
|
19
|
-
const Colors = {
|
|
20
|
-
reset: "\x1b[0m",
|
|
21
|
-
bright: "\x1b[1m",
|
|
22
|
-
dim: "\x1b[2m",
|
|
23
|
-
red: "\x1b[31m",
|
|
24
|
-
green: "\x1b[32m",
|
|
25
|
-
yellow: "\x1b[33m",
|
|
26
|
-
blue: "\x1b[34m",
|
|
27
|
-
magenta: "\x1b[35m",
|
|
28
|
-
cyan: "\x1b[36m",
|
|
29
|
-
white: "\x1b[37m",
|
|
30
|
-
gray: "\x1b[90m",
|
|
31
|
-
};
|
|
32
|
-
const defaultLoggerOptions = {
|
|
33
|
-
basePath: "./logs",
|
|
34
|
-
outputToConsole: true,
|
|
35
|
-
saveToLogFiles: false,
|
|
36
|
-
useColoredOutput: true,
|
|
37
|
-
colorMap: {
|
|
38
|
-
[LOG_LEVEL.INFO]: Colors.cyan,
|
|
39
|
-
[LOG_LEVEL.WARN]: Colors.yellow,
|
|
40
|
-
[LOG_LEVEL.ERROR]: Colors.red,
|
|
41
|
-
[LOG_LEVEL.DEBUG]: Colors.gray,
|
|
42
|
-
[LOG_LEVEL.FATAL]: Colors.magenta,
|
|
43
|
-
},
|
|
44
|
-
showTimestamps: true,
|
|
45
|
-
timestampType: "iso",
|
|
46
|
-
showLogLevel: true,
|
|
47
|
-
logLevelMap: {
|
|
48
|
-
[LOG_LEVEL.INFO]: "INFO",
|
|
49
|
-
[LOG_LEVEL.WARN]: "WARN",
|
|
50
|
-
[LOG_LEVEL.ERROR]: "ERROR",
|
|
51
|
-
[LOG_LEVEL.DEBUG]: "DEBUG",
|
|
52
|
-
[LOG_LEVEL.FATAL]: "FATAL",
|
|
53
|
-
},
|
|
54
|
-
};
|
|
55
|
-
/**
|
|
56
|
-
* Custom error for logger initialization failures
|
|
57
|
-
*/
|
|
58
|
-
export class LoggerInitializationError extends Error {
|
|
59
|
-
constructor(message) {
|
|
60
|
-
super(message);
|
|
61
|
-
this.name = "LoggerInitializationError";
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Used to log to console and also the log files
|
|
66
|
-
*/
|
|
67
|
-
export class Logger {
|
|
68
|
-
/**
|
|
69
|
-
* Local reference to options passed
|
|
70
|
-
*/
|
|
71
|
-
_options;
|
|
72
|
-
/**
|
|
73
|
-
* Holds the worker thread
|
|
74
|
-
*/
|
|
75
|
-
_worker = null;
|
|
76
|
-
/**
|
|
77
|
-
* A request's id
|
|
78
|
-
*/
|
|
79
|
-
_id = 1;
|
|
80
|
-
/**
|
|
81
|
-
* Gets the next ID; if it exceeds 1 million then rolls back to zero
|
|
82
|
-
*/
|
|
83
|
-
_getNextId = () => {
|
|
84
|
-
if (this._id > 1000000) {
|
|
85
|
-
this._id = 1;
|
|
86
|
-
}
|
|
87
|
-
return this._id++;
|
|
88
|
-
};
|
|
89
|
-
/**
|
|
90
|
-
* Holds batch of LOG requests only (fire-and-forget)
|
|
91
|
-
*/
|
|
92
|
-
_logBatch = [];
|
|
93
|
-
/**
|
|
94
|
-
* How long it will wait until it flushes / sends the logs to the worker
|
|
95
|
-
*/
|
|
96
|
-
_logRequestFlushMs = 100;
|
|
97
|
-
/**
|
|
98
|
-
* Holds the timeout for log batch flushing
|
|
99
|
-
*/
|
|
100
|
-
_logBatchTimeout = null;
|
|
101
|
-
/**
|
|
102
|
-
* How large we want the batch array to get before we send it
|
|
103
|
-
*/
|
|
104
|
-
_logBatchMaxSize = 250;
|
|
105
|
-
/**
|
|
106
|
-
* Holds pending requests that expect a response (FLUSH, RELOAD, SHUTDOWN)
|
|
107
|
-
*/
|
|
108
|
-
_pending = new Map();
|
|
109
|
-
constructor(options = {}) {
|
|
110
|
-
const mergedColorMap = {
|
|
111
|
-
...defaultLoggerOptions.colorMap,
|
|
112
|
-
...options.colorMap,
|
|
113
|
-
};
|
|
114
|
-
const mergedLogLevelMap = {
|
|
115
|
-
...defaultLoggerOptions.logLevelMap,
|
|
116
|
-
...options.logLevelMap,
|
|
117
|
-
};
|
|
118
|
-
this._options = {
|
|
119
|
-
...defaultLoggerOptions,
|
|
120
|
-
...options,
|
|
121
|
-
colorMap: mergedColorMap,
|
|
122
|
-
logLevelMap: mergedLogLevelMap,
|
|
123
|
-
};
|
|
124
|
-
this._validateBasePath();
|
|
125
|
-
this._initializeDirectory();
|
|
126
|
-
this._initWorker();
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Get the path to the worker
|
|
130
|
-
* @returns Path to the worker
|
|
131
|
-
*/
|
|
132
|
-
_getWorkerPath() {
|
|
133
|
-
return path.join(__dirname, "worker.js");
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Inits the worker thread
|
|
137
|
-
*/
|
|
138
|
-
_initWorker() {
|
|
139
|
-
if (!this._options.saveToLogFiles)
|
|
140
|
-
return;
|
|
141
|
-
try {
|
|
142
|
-
const workerPath = this._getWorkerPath();
|
|
143
|
-
if (!fs.existsSync(workerPath)) {
|
|
144
|
-
throw new Error("Worker file not found");
|
|
145
|
-
}
|
|
146
|
-
this._worker = new Worker(workerPath);
|
|
147
|
-
this._worker.on("message", (response) => {
|
|
148
|
-
this._handleResponse(response);
|
|
149
|
-
});
|
|
150
|
-
this._worker.stderr.on("data", (chunk) => {
|
|
151
|
-
process.stderr.write(`Sidecar error: ${chunk.toString()}`);
|
|
152
|
-
});
|
|
153
|
-
this._worker.on("error", (err) => {
|
|
154
|
-
this._clearPending();
|
|
155
|
-
process.stderr.write(`Sidecar error: ${this._stringify(err)}`);
|
|
156
|
-
});
|
|
157
|
-
this._worker.on("exit", () => {
|
|
158
|
-
this._clearPending();
|
|
159
|
-
this._worker = null;
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
catch (error) {
|
|
163
|
-
process.stderr.write(`Failed to spawn sidecar: ${this._stringify(error)}`);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* Flush the current log batch to worker immediately
|
|
168
|
-
*/
|
|
169
|
-
_flushLogBatch() {
|
|
170
|
-
if (!this._worker || this._logBatch.length === 0) {
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
this._worker.postMessage(this._logBatch);
|
|
174
|
-
this._logBatch = [];
|
|
175
|
-
this._stopLogBatchTimer();
|
|
176
|
-
}
|
|
177
|
-
/**
|
|
178
|
-
* Starts the timer to flush log batch after delay
|
|
179
|
-
*/
|
|
180
|
-
_startLogBatchTimer() {
|
|
181
|
-
if (this._logBatchTimeout)
|
|
182
|
-
return;
|
|
183
|
-
this._logBatchTimeout = setTimeout(() => {
|
|
184
|
-
this._flushLogBatch();
|
|
185
|
-
}, this._logRequestFlushMs);
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* Stops the log batch flush timer
|
|
189
|
-
*/
|
|
190
|
-
_stopLogBatchTimer() {
|
|
191
|
-
if (this._logBatchTimeout) {
|
|
192
|
-
clearTimeout(this._logBatchTimeout);
|
|
193
|
-
this._logBatchTimeout = null;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
/**
|
|
197
|
-
* Adds a LOG request to the batch (fire-and-forget)
|
|
198
|
-
*/
|
|
199
|
-
_addToLogBatch(request) {
|
|
200
|
-
this._logBatch.push(request);
|
|
201
|
-
if (this._logBatch.length >= this._logBatchMaxSize) {
|
|
202
|
-
this._flushLogBatch();
|
|
203
|
-
}
|
|
204
|
-
else {
|
|
205
|
-
this._startLogBatchTimer();
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
/**
|
|
209
|
-
* Clears pending requests on process exit/error
|
|
210
|
-
*/
|
|
211
|
-
_clearPending() {
|
|
212
|
-
const error = new Error("Sidecar process terminated");
|
|
213
|
-
for (const [_, { reject }] of this._pending) {
|
|
214
|
-
reject(error);
|
|
215
|
-
}
|
|
216
|
-
this._pending.clear();
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* Handle a decoded response from worker
|
|
220
|
-
*/
|
|
221
|
-
_handleResponse(response) {
|
|
222
|
-
this._resolvePending(response);
|
|
223
|
-
if (!response.success) {
|
|
224
|
-
process.stderr.write(`Log operation failed: id=${response.id}, method=${response.method}, level=${response.level}\n`);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
/**
|
|
228
|
-
* Resolve any pending requests that expected a response
|
|
229
|
-
*/
|
|
230
|
-
_resolvePending(response) {
|
|
231
|
-
const pending = this._pending.get(response.id);
|
|
232
|
-
if (!pending)
|
|
233
|
-
return;
|
|
234
|
-
if (response.success) {
|
|
235
|
-
pending.resolve();
|
|
236
|
-
}
|
|
237
|
-
else {
|
|
238
|
-
pending.reject(new Error("Request failed"));
|
|
239
|
-
}
|
|
240
|
-
this._pending.delete(response.id);
|
|
241
|
-
}
|
|
242
|
-
/**
|
|
243
|
-
* Send a request that expects a response (FLUSH, RELOAD, SHUTDOWN)
|
|
244
|
-
* These are sent immediately, not batched
|
|
245
|
-
*/
|
|
246
|
-
_sendControlRequest(request) {
|
|
247
|
-
const id = request.id;
|
|
248
|
-
if (!id)
|
|
249
|
-
throw new Error("Request must contain and ID");
|
|
250
|
-
return new Promise((resolve, reject) => {
|
|
251
|
-
const timeout = setTimeout(() => {
|
|
252
|
-
if (this._pending.has(id)) {
|
|
253
|
-
this._pending
|
|
254
|
-
.get(id)
|
|
255
|
-
?.reject(new Error(`Request timed out: ${this._stringify(request)}`));
|
|
256
|
-
this._pending.delete(id);
|
|
257
|
-
}
|
|
258
|
-
}, 4000);
|
|
259
|
-
this._pending.set(id, {
|
|
260
|
-
reject: (reason) => {
|
|
261
|
-
clearTimeout(timeout);
|
|
262
|
-
reject(reason);
|
|
263
|
-
},
|
|
264
|
-
resolve: (value) => {
|
|
265
|
-
clearTimeout(timeout);
|
|
266
|
-
resolve(value);
|
|
267
|
-
},
|
|
268
|
-
});
|
|
269
|
-
this._worker?.postMessage([request]);
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
/**
|
|
273
|
-
* Validates the basePath option
|
|
274
|
-
*/
|
|
275
|
-
_validateBasePath() {
|
|
276
|
-
const { basePath } = this._options;
|
|
277
|
-
if (basePath === undefined || basePath === null) {
|
|
278
|
-
throw new LoggerInitializationError("basePath is required and cannot be null or undefined");
|
|
279
|
-
}
|
|
280
|
-
if (typeof basePath !== "string") {
|
|
281
|
-
throw new LoggerInitializationError(`basePath must be a string, received ${typeof basePath}`);
|
|
282
|
-
}
|
|
283
|
-
if (basePath.trim().length === 0) {
|
|
284
|
-
throw new LoggerInitializationError("basePath cannot be an empty string");
|
|
285
|
-
}
|
|
286
|
-
this._options.basePath = path.resolve(basePath);
|
|
287
|
-
}
|
|
288
|
-
/**
|
|
289
|
-
* Creates the log directory if file logging is enabled
|
|
290
|
-
*/
|
|
291
|
-
_initializeDirectory() {
|
|
292
|
-
if (!this._options.saveToLogFiles) {
|
|
293
|
-
return;
|
|
294
|
-
}
|
|
295
|
-
try {
|
|
296
|
-
fs.mkdirSync(this._options.basePath, { recursive: true });
|
|
297
|
-
}
|
|
298
|
-
catch (error) {
|
|
299
|
-
const nodeError = error;
|
|
300
|
-
throw new LoggerInitializationError(`Failed to create log directory at "${this._options.basePath}": ${nodeError.message}`);
|
|
301
|
-
}
|
|
302
|
-
this._verifyWritable();
|
|
303
|
-
}
|
|
304
|
-
/**
|
|
305
|
-
* Extract call site information (file:line:column) from stack trace
|
|
306
|
-
*/
|
|
307
|
-
_getCallSite() {
|
|
308
|
-
const originalPrepareStackTrace = Error.prepareStackTrace;
|
|
309
|
-
try {
|
|
310
|
-
Error.prepareStackTrace = (_, stack) => stack;
|
|
311
|
-
const err = new Error();
|
|
312
|
-
const stack = err.stack;
|
|
313
|
-
// Base index: 0 = _getCallSite, 1 = _formatMessage, 2 = log, 3 = actual caller
|
|
314
|
-
let frameIndex = 3;
|
|
315
|
-
// Skip internal logger methods to find actual caller
|
|
316
|
-
// Use path.basename to compare just the filename, not full paths
|
|
317
|
-
// This handles path format differences between compiled and source
|
|
318
|
-
const thisFileName = path.basename(__filename);
|
|
319
|
-
while (frameIndex < stack.length) {
|
|
320
|
-
const frame = stack[frameIndex];
|
|
321
|
-
const frameFileName = frame?.getFileName();
|
|
322
|
-
// Stop if we hit a frame without a filename or outside this logger file
|
|
323
|
-
if (!frameFileName)
|
|
324
|
-
break;
|
|
325
|
-
// Check if this frame is still in the logger file
|
|
326
|
-
const isLoggerFile = frameFileName === __filename ||
|
|
327
|
-
path.basename(frameFileName) === thisFileName ||
|
|
328
|
-
(frameFileName.includes("node-logger") &&
|
|
329
|
-
frameFileName.includes("index.js"));
|
|
330
|
-
if (!isLoggerFile) {
|
|
331
|
-
break;
|
|
332
|
-
}
|
|
333
|
-
frameIndex++;
|
|
334
|
-
}
|
|
335
|
-
const frame = stack[frameIndex];
|
|
336
|
-
if (!frame) {
|
|
337
|
-
return "unknown";
|
|
338
|
-
}
|
|
339
|
-
const fileName = frame.getFileName() || "unknown";
|
|
340
|
-
const lineNumber = frame.getLineNumber() || 0;
|
|
341
|
-
const columnNumber = frame.getColumnNumber() || 0;
|
|
342
|
-
// Use full path or short name based on option
|
|
343
|
-
const useFullPath = this._options.callSiteOptions?.fullFilePath ?? false;
|
|
344
|
-
const displayFileName = useFullPath ? fileName : path.basename(fileName);
|
|
345
|
-
return `${displayFileName}:${lineNumber}:${columnNumber}`;
|
|
346
|
-
}
|
|
347
|
-
catch {
|
|
348
|
-
return "unknown";
|
|
349
|
-
}
|
|
350
|
-
finally {
|
|
351
|
-
Error.prepareStackTrace = originalPrepareStackTrace;
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
/**
|
|
355
|
-
* Check if a log level should be processed based on filtering rules
|
|
356
|
-
*/
|
|
357
|
-
_shouldLog(level) {
|
|
358
|
-
const { minLevel, maxLevel, includeLevels, excludeLevels, filter } = this._options;
|
|
359
|
-
// Check whitelist (if specified, only these levels pass)
|
|
360
|
-
if (includeLevels && includeLevels.length > 0) {
|
|
361
|
-
if (!includeLevels.includes(level)) {
|
|
362
|
-
return false;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
// Check blacklist
|
|
366
|
-
if (excludeLevels && excludeLevels.length > 0) {
|
|
367
|
-
if (excludeLevels.includes(level)) {
|
|
368
|
-
return false;
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
// Check min level (filter out lower severity)
|
|
372
|
-
if (minLevel !== undefined) {
|
|
373
|
-
if (LogLevelPriority[level] < LogLevelPriority[minLevel]) {
|
|
374
|
-
return false;
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
// Check max level (filter out higher severity)
|
|
378
|
-
if (maxLevel !== undefined) {
|
|
379
|
-
if (LogLevelPriority[level] > LogLevelPriority[maxLevel]) {
|
|
380
|
-
return false;
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
// Check custom filter function
|
|
384
|
-
if (filter) {
|
|
385
|
-
// Note: filter function is checked in log() with message available
|
|
386
|
-
return true; // Defer to log() method for filter check
|
|
387
|
-
}
|
|
388
|
-
return true;
|
|
389
|
-
}
|
|
390
|
-
/**
|
|
391
|
-
* Verifies the log directory is writable
|
|
392
|
-
*/
|
|
393
|
-
_verifyWritable() {
|
|
394
|
-
try {
|
|
395
|
-
const testFile = path.join(this._options.basePath, ".write-test");
|
|
396
|
-
fs.writeFileSync(testFile, "", { flag: "wx" });
|
|
397
|
-
fs.unlinkSync(testFile);
|
|
398
|
-
}
|
|
399
|
-
catch (error) {
|
|
400
|
-
const nodeError = error;
|
|
401
|
-
throw new LoggerInitializationError(`Log directory "${this._options.basePath}" is not writable: ${nodeError.message}`);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
/**
|
|
405
|
-
* Get the string representation of a log level
|
|
406
|
-
*/
|
|
407
|
-
_getLevelString(level) {
|
|
408
|
-
return this._options.logLevelMap[level] ?? "UNKNOWN";
|
|
409
|
-
}
|
|
410
|
-
/**
|
|
411
|
-
* Format timestamp based on the configured timestampType
|
|
412
|
-
*/
|
|
413
|
-
_formatTimestamp(date) {
|
|
414
|
-
const { timestampType, customTimestampFormat } = this._options;
|
|
415
|
-
switch (timestampType) {
|
|
416
|
-
case "iso":
|
|
417
|
-
return date.toISOString();
|
|
418
|
-
case "locale":
|
|
419
|
-
return date.toLocaleString();
|
|
420
|
-
case "utc":
|
|
421
|
-
return date.toUTCString();
|
|
422
|
-
case "unix":
|
|
423
|
-
return Math.floor(date.getTime() / 1000).toString();
|
|
424
|
-
case "unix_ms":
|
|
425
|
-
return date.getTime().toString();
|
|
426
|
-
case "date":
|
|
427
|
-
return date.toISOString().split("T")[0];
|
|
428
|
-
case "time":
|
|
429
|
-
return date.toTimeString().split(" ")[0];
|
|
430
|
-
case "datetime":
|
|
431
|
-
return date.toISOString().replace("T", " ").replace("Z", "");
|
|
432
|
-
case "short":
|
|
433
|
-
const d = date;
|
|
434
|
-
const day = d.getDate().toString().padStart(2, "0");
|
|
435
|
-
const month = (d.getMonth() + 1).toString().padStart(2, "0");
|
|
436
|
-
const year = d.getFullYear();
|
|
437
|
-
const hours = d.getHours().toString().padStart(2, "0");
|
|
438
|
-
const minutes = d.getMinutes().toString().padStart(2, "0");
|
|
439
|
-
return `${day}/${month}/${year} ${hours}:${minutes}`;
|
|
440
|
-
case "custom":
|
|
441
|
-
if (!customTimestampFormat) {
|
|
442
|
-
return date.toISOString();
|
|
443
|
-
}
|
|
444
|
-
return this._applyCustomFormat(date, customTimestampFormat);
|
|
445
|
-
default:
|
|
446
|
-
return date.toISOString();
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
/**
|
|
450
|
-
* Apply custom format string to date
|
|
451
|
-
* Tokens: YYYY=year, MM=month, DD=day, HH=hour, mm=minute, ss=second, ms=millisecond
|
|
452
|
-
*/
|
|
453
|
-
_applyCustomFormat(date, format) {
|
|
454
|
-
const tokens = {
|
|
455
|
-
YYYY: date.getFullYear().toString(),
|
|
456
|
-
MM: (date.getMonth() + 1).toString().padStart(2, "0"),
|
|
457
|
-
DD: date.getDate().toString().padStart(2, "0"),
|
|
458
|
-
HH: date.getHours().toString().padStart(2, "0"),
|
|
459
|
-
mm: date.getMinutes().toString().padStart(2, "0"),
|
|
460
|
-
ss: date.getSeconds().toString().padStart(2, "0"),
|
|
461
|
-
ms: date.getMilliseconds().toString().padStart(3, "0"),
|
|
462
|
-
};
|
|
463
|
-
return format.replace(/YYYY|MM|DD|HH|mm|ss|ms/g, (match) => tokens[match] || match);
|
|
464
|
-
}
|
|
465
|
-
/**
|
|
466
|
-
* Convert any value to string representation
|
|
467
|
-
*/
|
|
468
|
-
_stringify(value) {
|
|
469
|
-
const valueType = typeof value;
|
|
470
|
-
if (value === null)
|
|
471
|
-
return "null";
|
|
472
|
-
if (value === undefined)
|
|
473
|
-
return "undefined";
|
|
474
|
-
if (valueType === "string")
|
|
475
|
-
return value;
|
|
476
|
-
if (valueType === "number")
|
|
477
|
-
return String(value);
|
|
478
|
-
if (valueType === "boolean")
|
|
479
|
-
return String(value);
|
|
480
|
-
if (valueType === "bigint")
|
|
481
|
-
return `${value}n`;
|
|
482
|
-
if (valueType === "symbol")
|
|
483
|
-
return value.toString();
|
|
484
|
-
if (valueType === "function")
|
|
485
|
-
return `[Function: ${value.name || "anonymous"}]`;
|
|
486
|
-
// Check if value is an Error instance (must be object, so check after primitives)
|
|
487
|
-
if (value instanceof Error) {
|
|
488
|
-
const errorParts = [
|
|
489
|
-
`name: ${value.name}`,
|
|
490
|
-
`message: ${value.message}`,
|
|
491
|
-
];
|
|
492
|
-
if (value.stack)
|
|
493
|
-
errorParts.push(`stack: ${value.stack}`);
|
|
494
|
-
if ("code" in value && value.code !== undefined)
|
|
495
|
-
errorParts.push(`code: ${value.code}`);
|
|
496
|
-
if ("errno" in value && value.errno !== undefined)
|
|
497
|
-
errorParts.push(`errno: ${value.errno}`);
|
|
498
|
-
if ("syscall" in value && value.syscall !== undefined)
|
|
499
|
-
errorParts.push(`syscall: ${value.syscall}`);
|
|
500
|
-
if ("path" in value && value.path !== undefined)
|
|
501
|
-
errorParts.push(`path: ${value.path}`);
|
|
502
|
-
// Capture custom properties
|
|
503
|
-
const standardProps = new Set([
|
|
504
|
-
"name",
|
|
505
|
-
"message",
|
|
506
|
-
"stack",
|
|
507
|
-
"code",
|
|
508
|
-
"errno",
|
|
509
|
-
"syscall",
|
|
510
|
-
"path",
|
|
511
|
-
]);
|
|
512
|
-
for (const prop of Object.getOwnPropertyNames(value)) {
|
|
513
|
-
if (standardProps.has(prop))
|
|
514
|
-
continue;
|
|
515
|
-
try {
|
|
516
|
-
const propValue = value[prop];
|
|
517
|
-
errorParts.push(`${prop}: ${typeof propValue === "object" && propValue !== null ? "[object]" : propValue}`);
|
|
518
|
-
}
|
|
519
|
-
catch {
|
|
520
|
-
errorParts.push(`${prop}: [unreadable]`);
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
return `Error { ${errorParts.join(", ")} }`;
|
|
524
|
-
}
|
|
525
|
-
if (valueType === "object") {
|
|
526
|
-
const keys = Object.keys(value);
|
|
527
|
-
if (keys.length === 0)
|
|
528
|
-
return "{}";
|
|
529
|
-
const entries = keys.map((key) => {
|
|
530
|
-
try {
|
|
531
|
-
const val = value[key];
|
|
532
|
-
// Show type for nested objects/arrays, value for primitives
|
|
533
|
-
const display = val === null
|
|
534
|
-
? "null"
|
|
535
|
-
: Array.isArray(val)
|
|
536
|
-
? "[Array]"
|
|
537
|
-
: typeof val === "object"
|
|
538
|
-
? "[Object]"
|
|
539
|
-
: typeof val === "function"
|
|
540
|
-
? "[Function]"
|
|
541
|
-
: typeof val === "symbol"
|
|
542
|
-
? "[Symbol]"
|
|
543
|
-
: String(val);
|
|
544
|
-
return `${key}: ${display}`;
|
|
545
|
-
}
|
|
546
|
-
catch {
|
|
547
|
-
return `${key}: [unreadable]`;
|
|
548
|
-
}
|
|
549
|
-
});
|
|
550
|
-
return `{ ${entries.join(", ")} }`;
|
|
551
|
-
}
|
|
552
|
-
// Fallback (shouldn't reach here with standard JS types)
|
|
553
|
-
return String(value);
|
|
554
|
-
}
|
|
555
|
-
/**
|
|
556
|
-
* Format a log message with optional fields
|
|
557
|
-
*/
|
|
558
|
-
_formatMessage(level, message, additionalMessages) {
|
|
559
|
-
const parts = [];
|
|
560
|
-
// Add call site if enabled
|
|
561
|
-
if (this._options.showCallSite) {
|
|
562
|
-
const callSite = this._getCallSite();
|
|
563
|
-
parts.push(`[${callSite}]`);
|
|
564
|
-
}
|
|
565
|
-
// Add timestamp if enabled
|
|
566
|
-
if (this._options.showTimestamps) {
|
|
567
|
-
const timestamp = this._formatTimestamp(new Date());
|
|
568
|
-
parts.push(`[${timestamp}]`);
|
|
569
|
-
}
|
|
570
|
-
// Add log level if enabled
|
|
571
|
-
if (this._options.showLogLevel) {
|
|
572
|
-
const levelStr = this._getLevelString(level);
|
|
573
|
-
parts.push(`[${levelStr}]`);
|
|
574
|
-
}
|
|
575
|
-
// Add user prefixes
|
|
576
|
-
const addPrefixes = this._options.additionalPrefixes;
|
|
577
|
-
if (addPrefixes) {
|
|
578
|
-
for (let i = 0; i < addPrefixes.length; i++) {
|
|
579
|
-
parts.push(addPrefixes[i]);
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
// Build the message content
|
|
583
|
-
const mainMessage = this._stringify(message);
|
|
584
|
-
const additionalStr = additionalMessages
|
|
585
|
-
.map((msg) => this._stringify(msg))
|
|
586
|
-
.join(" ");
|
|
587
|
-
const fullMessage = additionalStr
|
|
588
|
-
? `${mainMessage} ${additionalStr}`
|
|
589
|
-
: mainMessage;
|
|
590
|
-
// Combine parts with message
|
|
591
|
-
if (parts.length > 0) {
|
|
592
|
-
return `${parts.join(" ")}: ${fullMessage}`;
|
|
593
|
-
}
|
|
594
|
-
else {
|
|
595
|
-
return fullMessage;
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
/**
|
|
599
|
-
* Apply color to the entire message if colored output is enabled
|
|
600
|
-
*/
|
|
601
|
-
_colorize(level, message) {
|
|
602
|
-
if (!this._options.useColoredOutput) {
|
|
603
|
-
return message;
|
|
604
|
-
}
|
|
605
|
-
const color = this._options.colorMap[level] || Colors.reset;
|
|
606
|
-
return `${color}${message}${Colors.reset}`;
|
|
607
|
-
}
|
|
608
|
-
/**
|
|
609
|
-
* Log a specific level and content
|
|
610
|
-
* @param level The specific level to log
|
|
611
|
-
* @param message The content of the message
|
|
612
|
-
* @param messages Any additional messages
|
|
613
|
-
*/
|
|
614
|
-
log(level, message, ...messages) {
|
|
615
|
-
if (!this._shouldLog(level)) {
|
|
616
|
-
return;
|
|
617
|
-
}
|
|
618
|
-
if (this._options.filter) {
|
|
619
|
-
const fullMessage = messages.length > 0
|
|
620
|
-
? [message, ...messages].map((m) => this._stringify(m)).join(" ")
|
|
621
|
-
: this._stringify(message);
|
|
622
|
-
if (!this._options.filter(level, fullMessage)) {
|
|
623
|
-
return;
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
const formattedMessage = this._formatMessage(level, message, messages);
|
|
627
|
-
if (this._options.saveToLogFiles) {
|
|
628
|
-
this._addToLogBatch({
|
|
629
|
-
// we don't need ID and level
|
|
630
|
-
method: METHOD.LOG,
|
|
631
|
-
payload: formattedMessage,
|
|
632
|
-
});
|
|
633
|
-
}
|
|
634
|
-
if (this._options.outputToConsole) {
|
|
635
|
-
const coloredMessage = this._colorize(level, formattedMessage);
|
|
636
|
-
if (level === LOG_LEVEL.ERROR || level === LOG_LEVEL.FATAL) {
|
|
637
|
-
process.stderr.write(coloredMessage + "\n");
|
|
638
|
-
}
|
|
639
|
-
else {
|
|
640
|
-
process.stdout.write(coloredMessage + "\n");
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
/**
|
|
645
|
-
* Convenience method for INFO level
|
|
646
|
-
*/
|
|
647
|
-
info(message, ...messages) {
|
|
648
|
-
this.log(LOG_LEVEL.INFO, message, ...messages);
|
|
649
|
-
}
|
|
650
|
-
/**
|
|
651
|
-
* Convenience method for WARN level
|
|
652
|
-
*/
|
|
653
|
-
warn(message, ...messages) {
|
|
654
|
-
this.log(LOG_LEVEL.WARN, message, ...messages);
|
|
655
|
-
}
|
|
656
|
-
/**
|
|
657
|
-
* Convenience method for ERROR level
|
|
658
|
-
*/
|
|
659
|
-
error(message, ...messages) {
|
|
660
|
-
this.log(LOG_LEVEL.ERROR, message, ...messages);
|
|
661
|
-
}
|
|
662
|
-
/**
|
|
663
|
-
* Convenience method for DEBUG level
|
|
664
|
-
*/
|
|
665
|
-
debug(message, ...messages) {
|
|
666
|
-
this.log(LOG_LEVEL.DEBUG, message, ...messages);
|
|
667
|
-
}
|
|
668
|
-
/**
|
|
669
|
-
* Convenience method for FATAL level
|
|
670
|
-
*/
|
|
671
|
-
fatal(message, ...messages) {
|
|
672
|
-
this.log(LOG_LEVEL.FATAL, message, ...messages);
|
|
673
|
-
}
|
|
674
|
-
/**
|
|
675
|
-
* Flush remaining buffer to log files
|
|
676
|
-
*/
|
|
677
|
-
flush() {
|
|
678
|
-
this._flushLogBatch();
|
|
679
|
-
return this._sendControlRequest({
|
|
680
|
-
id: this._getNextId(),
|
|
681
|
-
level: LOG_LEVEL.INFO,
|
|
682
|
-
method: METHOD.FLUSH,
|
|
683
|
-
payload: "",
|
|
684
|
-
});
|
|
685
|
-
}
|
|
686
|
-
/**
|
|
687
|
-
* Used to reload / refresh the process
|
|
688
|
-
*/
|
|
689
|
-
reload() {
|
|
690
|
-
this._flushLogBatch();
|
|
691
|
-
return this._sendControlRequest({
|
|
692
|
-
id: this._getNextId(),
|
|
693
|
-
level: LOG_LEVEL.INFO,
|
|
694
|
-
method: METHOD.RELOAD,
|
|
695
|
-
payload: "",
|
|
696
|
-
});
|
|
697
|
-
}
|
|
698
|
-
/**
|
|
699
|
-
* Used to shut down the child process and clean up
|
|
700
|
-
*/
|
|
701
|
-
shutdown() {
|
|
702
|
-
this._flushLogBatch();
|
|
703
|
-
return this._sendControlRequest({
|
|
704
|
-
id: this._getNextId(),
|
|
705
|
-
level: LOG_LEVEL.INFO,
|
|
706
|
-
method: METHOD.SHUTDOWN,
|
|
707
|
-
payload: "",
|
|
708
|
-
});
|
|
709
|
-
}
|
|
710
|
-
/**
|
|
711
|
-
* Get current logger options (read-only copy)
|
|
712
|
-
*/
|
|
713
|
-
get options() {
|
|
714
|
-
return { ...this._options };
|
|
715
|
-
}
|
|
716
|
-
}
|
|
1
|
+
export * from "./logger.js";
|
|
2
|
+
export * from "./protocol.js";
|
|
3
|
+
export * from "./worker.js";
|
|
717
4
|
//# sourceMappingURL=index.js.map
|