logixia 2.0.9 → 2.0.11
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/{async-context-COuvmwsS.mjs → async-context-BGUCdYAa.mjs} +3 -3
- package/dist/{async-context-COuvmwsS.mjs.map → async-context-BGUCdYAa.mjs.map} +1 -1
- package/dist/{async-context-Dv0bmPC-.js → async-context-D8um9I5V.js} +3 -3
- package/dist/{async-context-Dv0bmPC-.js.map → async-context-D8um9I5V.js.map} +1 -1
- package/dist/{azure-monitor.transport-D6ryi8rA.js → azure-monitor.transport-C8KdmsSw.js} +19 -8
- package/dist/{azure-monitor.transport-D6ryi8rA.js.map → azure-monitor.transport-C8KdmsSw.js.map} +1 -1
- package/dist/{azure-monitor.transport-Coz5Gesu.mjs → azure-monitor.transport-CTnfwA0F.mjs} +19 -8
- package/dist/{azure-monitor.transport-Coz5Gesu.mjs.map → azure-monitor.transport-CTnfwA0F.mjs.map} +1 -1
- package/dist/azure.js +1 -1
- package/dist/azure.mjs +1 -1
- package/dist/{batch-buffer-CQUWDMYq.mjs → batch-buffer-BJqzpfpT.mjs} +11 -7
- package/dist/{batch-buffer-CQUWDMYq.mjs.map → batch-buffer-BJqzpfpT.mjs.map} +1 -1
- package/dist/{batch-buffer-CFzKnuNZ.js → batch-buffer-BTQdAXwC.js} +11 -7
- package/dist/{batch-buffer-CFzKnuNZ.js.map → batch-buffer-BTQdAXwC.js.map} +1 -1
- package/dist/browser.js +24 -9
- package/dist/browser.js.map +1 -1
- package/dist/browser.mjs +24 -9
- package/dist/browser.mjs.map +1 -1
- package/dist/{builder-BlIJNIuk.mjs → builder-C6kKEVrU.mjs} +154 -52
- package/dist/builder-C6kKEVrU.mjs.map +1 -0
- package/dist/{builder-Bs0qkfkY.js → builder-Up4YxX7l.js} +157 -55
- package/dist/builder-Up4YxX7l.js.map +1 -0
- package/dist/cli/index.js +21 -16
- package/dist/cloudwatch.js +1 -1
- package/dist/cloudwatch.mjs +1 -1
- package/dist/{cloudwatch.transport-Cr1aeTGj.mjs → cloudwatch.transport-Bl1qzdBx.mjs} +24 -9
- package/dist/{cloudwatch.transport-Cr1aeTGj.mjs.map → cloudwatch.transport-Bl1qzdBx.mjs.map} +1 -1
- package/dist/{cloudwatch.transport-D4G5xYfG.js → cloudwatch.transport-BoCyxNKO.js} +24 -9
- package/dist/{cloudwatch.transport-D4G5xYfG.js.map → cloudwatch.transport-BoCyxNKO.js.map} +1 -1
- package/dist/correlation.js +3 -3
- package/dist/correlation.mjs +3 -3
- package/dist/{coerce.utils-C1C5sIX0.js → csv.utils-CbVHY1pd.js} +24 -1
- package/dist/csv.utils-CbVHY1pd.js.map +1 -0
- package/dist/{coerce.utils-B_rEudCn.mjs → csv.utils-ZdxSod5g.mjs} +19 -2
- package/dist/csv.utils-ZdxSod5g.mjs.map +1 -0
- package/dist/{entry-fields-CVwoQplO.js → entry-fields-CJbeaoBs.js} +2 -2
- package/dist/{entry-fields-CVwoQplO.js.map → entry-fields-CJbeaoBs.js.map} +1 -1
- package/dist/{entry-fields-DNTKQxoU.mjs → entry-fields-D_aNnjg0.mjs} +2 -2
- package/dist/{entry-fields-DNTKQxoU.mjs.map → entry-fields-D_aNnjg0.mjs.map} +1 -1
- package/dist/gcp.js +1 -1
- package/dist/gcp.mjs +1 -1
- package/dist/{gcp.transport-LLK2bw2-.mjs → gcp.transport-DOH4bpWM.mjs} +18 -9
- package/dist/{gcp.transport-LLK2bw2-.mjs.map → gcp.transport-DOH4bpWM.mjs.map} +1 -1
- package/dist/{gcp.transport-CVuRpchl.js → gcp.transport-D_NHVZrw.js} +18 -9
- package/dist/{gcp.transport-CVuRpchl.js.map → gcp.transport-D_NHVZrw.js.map} +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +32 -16
- package/dist/index.mjs.map +1 -1
- package/dist/logitron-logger-DVUdgmBY.d.ts.map +1 -1
- package/dist/logitron-logger-DoWmMjYB.d.mts.map +1 -1
- package/dist/middleware.d.mts.map +1 -1
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +10 -6
- package/dist/middleware.js.map +1 -1
- package/dist/middleware.mjs +10 -6
- package/dist/middleware.mjs.map +1 -1
- package/dist/nest.d.mts +4 -3
- package/dist/nest.d.mts.map +1 -1
- package/dist/nest.d.ts +4 -3
- package/dist/nest.d.ts.map +1 -1
- package/dist/nest.js +41 -20
- package/dist/nest.js.map +1 -1
- package/dist/nest.mjs +39 -18
- package/dist/nest.mjs.map +1 -1
- package/dist/{otel-DOuwGPCd.mjs → otel-DAVxpArV.mjs} +135 -56
- package/dist/otel-DAVxpArV.mjs.map +1 -0
- package/dist/{otel-ChnVYRLA.js → otel-DH6-G6DR.js} +137 -58
- package/dist/otel-DH6-G6DR.js.map +1 -0
- package/dist/{request-trace-ByujsUZk.js → request-trace-C7kXMjx5.js} +5 -6
- package/dist/{request-trace-ByujsUZk.js.map → request-trace-C7kXMjx5.js.map} +1 -1
- package/dist/{request-trace-BgNykH9M.mjs → request-trace-Dekcnenb.mjs} +5 -6
- package/dist/{request-trace-BgNykH9M.mjs.map → request-trace-Dekcnenb.mjs.map} +1 -1
- package/dist/{safe-stringify-CN7icp8S.js → safe-stringify-CJisLyZ3.js} +2 -2
- package/dist/{safe-stringify-CN7icp8S.js.map → safe-stringify-CJisLyZ3.js.map} +1 -1
- package/dist/{safe-stringify--7gbe95n.mjs → safe-stringify-pZOnzGRJ.mjs} +2 -2
- package/dist/{safe-stringify--7gbe95n.mjs.map → safe-stringify-pZOnzGRJ.mjs.map} +1 -1
- package/dist/{search-DPwTKPve.d.ts → search-B4eEzrhM.d.ts} +3 -6
- package/dist/{search-DPwTKPve.d.ts.map → search-B4eEzrhM.d.ts.map} +1 -1
- package/dist/{search-CRjhHxe9.js → search-CJw_xoQl.js} +44 -35
- package/dist/search-CJw_xoQl.js.map +1 -0
- package/dist/{search-DAXKE-P9.mjs → search-CjYl-mo5.mjs} +41 -32
- package/dist/search-CjYl-mo5.mjs.map +1 -0
- package/dist/{search-D-8HJADE.d.mts → search-Crf2IEoA.d.mts} +3 -6
- package/dist/{search-D-8HJADE.d.mts.map → search-Crf2IEoA.d.mts.map} +1 -1
- package/dist/search.d.mts +1 -1
- package/dist/search.d.ts +1 -1
- package/dist/search.js +1 -1
- package/dist/search.mjs +1 -1
- package/dist/{secret-config-BvFl2dC8.js → secret-config-6hdODxBb.js} +2 -2
- package/dist/{secret-config-BvFl2dC8.js.map → secret-config-6hdODxBb.js.map} +1 -1
- package/dist/{secret-config-ByGq7kJd.mjs → secret-config-D1F4MW69.mjs} +2 -2
- package/dist/{secret-config-ByGq7kJd.mjs.map → secret-config-D1F4MW69.mjs.map} +1 -1
- package/dist/{sensitive-names-CHMowujD.js → sensitive-names-CNgHaqv9.js} +7 -1
- package/dist/{sensitive-names-CHMowujD.js.map → sensitive-names-CNgHaqv9.js.map} +1 -1
- package/dist/{sensitive-names-o__1PUUR.mjs → sensitive-names-wv27qBQC.mjs} +2 -2
- package/dist/{sensitive-names-o__1PUUR.mjs.map → sensitive-names-wv27qBQC.mjs.map} +1 -1
- package/dist/{slack.transport-zLQ9MGxS.js → slack.transport-CEYBVzUj.js} +46 -18
- package/dist/{slack.transport-zLQ9MGxS.js.map → slack.transport-CEYBVzUj.js.map} +1 -1
- package/dist/{slack.transport-CSj1j0_X.mjs → slack.transport-YvaLJxNv.mjs} +46 -18
- package/dist/{slack.transport-CSj1j0_X.mjs.map → slack.transport-YvaLJxNv.mjs.map} +1 -1
- package/dist/transports.d.mts.map +1 -1
- package/dist/transports.d.ts.map +1 -1
- package/dist/transports.js +6 -6
- package/dist/transports.mjs +6 -6
- package/dist/worker-entry.js +109 -41
- package/dist/worker.js +1 -1
- package/dist/worker.mjs +1 -1
- package/dist/{worker.transport-DQDv1nAJ.js → worker.transport-CwNpH98o.js} +21 -13
- package/dist/{worker.transport-DQDv1nAJ.js.map → worker.transport-CwNpH98o.js.map} +1 -1
- package/dist/{worker.transport-D78xhNhB.mjs → worker.transport-DgK_ZXlB.mjs} +21 -13
- package/dist/{worker.transport-D78xhNhB.mjs.map → worker.transport-DgK_ZXlB.mjs.map} +1 -1
- package/package.json +10 -3
- package/dist/builder-BlIJNIuk.mjs.map +0 -1
- package/dist/builder-Bs0qkfkY.js.map +0 -1
- package/dist/coerce.utils-B_rEudCn.mjs.map +0 -1
- package/dist/coerce.utils-C1C5sIX0.js.map +0 -1
- package/dist/otel-ChnVYRLA.js.map +0 -1
- package/dist/otel-DOuwGPCd.mjs.map +0 -1
- package/dist/search-CRjhHxe9.js.map +0 -1
- package/dist/search-DAXKE-P9.mjs.map +0 -1
package/dist/worker-entry.js
CHANGED
|
@@ -243,12 +243,35 @@ function mergeCoreFields(core, data, extraReserved = []) {
|
|
|
243
243
|
* transports: { console: { format: 'json' } }
|
|
244
244
|
*/
|
|
245
245
|
var ConsoleTransport = class ConsoleTransport {
|
|
246
|
+
config;
|
|
247
|
+
name = "console";
|
|
248
|
+
level;
|
|
249
|
+
filter;
|
|
250
|
+
static COLORS = /* @__PURE__ */ new Map([
|
|
251
|
+
["black", "\x1B[30m"],
|
|
252
|
+
["red", "\x1B[31m"],
|
|
253
|
+
["green", "\x1B[32m"],
|
|
254
|
+
["yellow", "\x1B[33m"],
|
|
255
|
+
["blue", "\x1B[34m"],
|
|
256
|
+
["magenta", "\x1B[35m"],
|
|
257
|
+
["cyan", "\x1B[36m"],
|
|
258
|
+
["white", "\x1B[37m"],
|
|
259
|
+
["gray", "\x1B[90m"],
|
|
260
|
+
["grey", "\x1B[90m"],
|
|
261
|
+
["brightred", "\x1B[91m"],
|
|
262
|
+
["brightgreen", "\x1B[92m"],
|
|
263
|
+
["brightyellow", "\x1B[93m"],
|
|
264
|
+
["brightblue", "\x1B[94m"],
|
|
265
|
+
["brightmagenta", "\x1B[95m"],
|
|
266
|
+
["brightcyan", "\x1B[96m"],
|
|
267
|
+
["brightwhite", "\x1B[97m"],
|
|
268
|
+
["reset", "\x1B[0m"]
|
|
269
|
+
]);
|
|
246
270
|
static sanitize(value) {
|
|
247
271
|
return stripControlChars(safeToString(value));
|
|
248
272
|
}
|
|
249
273
|
constructor(config = {}) {
|
|
250
274
|
this.config = config;
|
|
251
|
-
this.name = "console";
|
|
252
275
|
this.level = config.level;
|
|
253
276
|
if (config.filter) this.filter = config.filter;
|
|
254
277
|
}
|
|
@@ -312,26 +335,6 @@ var ConsoleTransport = class ConsoleTransport {
|
|
|
312
335
|
close() {
|
|
313
336
|
return Promise.resolve();
|
|
314
337
|
}
|
|
315
|
-
static #_ = () => this.COLORS = /* @__PURE__ */ new Map([
|
|
316
|
-
["black", "\x1B[30m"],
|
|
317
|
-
["red", "\x1B[31m"],
|
|
318
|
-
["green", "\x1B[32m"],
|
|
319
|
-
["yellow", "\x1B[33m"],
|
|
320
|
-
["blue", "\x1B[34m"],
|
|
321
|
-
["magenta", "\x1B[35m"],
|
|
322
|
-
["cyan", "\x1B[36m"],
|
|
323
|
-
["white", "\x1B[37m"],
|
|
324
|
-
["gray", "\x1B[90m"],
|
|
325
|
-
["grey", "\x1B[90m"],
|
|
326
|
-
["brightred", "\x1B[91m"],
|
|
327
|
-
["brightgreen", "\x1B[92m"],
|
|
328
|
-
["brightyellow", "\x1B[93m"],
|
|
329
|
-
["brightblue", "\x1B[94m"],
|
|
330
|
-
["brightmagenta", "\x1B[95m"],
|
|
331
|
-
["brightcyan", "\x1B[96m"],
|
|
332
|
-
["brightwhite", "\x1B[97m"],
|
|
333
|
-
["reset", "\x1B[0m"]
|
|
334
|
-
]);
|
|
335
338
|
};
|
|
336
339
|
// src/utils/internal-log.ts
|
|
337
340
|
/**
|
|
@@ -396,10 +399,12 @@ const DROP_WARN_INTERVAL_MS$1 = 5e3;
|
|
|
396
399
|
* A cap below `batchSize` is raised to `batchSize` so one batch always fits.
|
|
397
400
|
*/
|
|
398
401
|
var BufferCap = class {
|
|
402
|
+
name;
|
|
403
|
+
max;
|
|
404
|
+
dropped = 0;
|
|
405
|
+
lastWarning = 0;
|
|
399
406
|
constructor(name, requested, batchSize, fallback = DEFAULT_BUFFER_CAP) {
|
|
400
407
|
this.name = name;
|
|
401
|
-
this.dropped = 0;
|
|
402
|
-
this.lastWarning = 0;
|
|
403
408
|
let max = fallback;
|
|
404
409
|
if (requested !== void 0) {
|
|
405
410
|
if (Number.isInteger(requested) && requested >= 1) max = requested;
|
|
@@ -777,6 +782,7 @@ function isDataError(type, error) {
|
|
|
777
782
|
}
|
|
778
783
|
/** A write that stored part of a batch: only `remaining` still needs to be written. */
|
|
779
784
|
var PartialWriteError = class extends Error {
|
|
785
|
+
remaining;
|
|
780
786
|
constructor(message, remaining, cause) {
|
|
781
787
|
super(message, { cause });
|
|
782
788
|
this.remaining = remaining;
|
|
@@ -803,11 +809,28 @@ function validateSqlIdentifier(name, label = "table") {
|
|
|
803
809
|
* }
|
|
804
810
|
*/
|
|
805
811
|
var DatabaseTransport = class {
|
|
812
|
+
config;
|
|
813
|
+
name = "database";
|
|
814
|
+
batchSize;
|
|
815
|
+
flushInterval;
|
|
816
|
+
level;
|
|
817
|
+
filter;
|
|
818
|
+
batch = [];
|
|
819
|
+
flushTimer;
|
|
820
|
+
/**
|
|
821
|
+
* The in-flight drain promise, or undefined when idle. Concurrent flush()
|
|
822
|
+
* calls (the un-awaited threshold flush write() fires, plus the interval
|
|
823
|
+
* timer) all await this single promise instead of starting their own drain,
|
|
824
|
+
* so a batch is never snapshotted and written twice. Mirrors the FileTransport
|
|
825
|
+
* fix for the overlapping-batch-flush duplication bug.
|
|
826
|
+
*/
|
|
827
|
+
flushPromise;
|
|
828
|
+
connection;
|
|
829
|
+
isConnected = false;
|
|
830
|
+
connectionPromise;
|
|
831
|
+
cap;
|
|
806
832
|
constructor(config) {
|
|
807
833
|
this.config = config;
|
|
808
|
-
this.name = "database";
|
|
809
|
-
this.batch = [];
|
|
810
|
-
this.isConnected = false;
|
|
811
834
|
protectSecrets(this, ["config", "connection"]);
|
|
812
835
|
this.batchSize = config.batchSize || 100;
|
|
813
836
|
this.flushInterval = config.flushInterval || 5e3;
|
|
@@ -1308,6 +1331,23 @@ var DatabaseTransport = class {
|
|
|
1308
1331
|
}
|
|
1309
1332
|
}
|
|
1310
1333
|
};
|
|
1334
|
+
// src/utils/csv.utils.ts
|
|
1335
|
+
const CSV_FORMULA_PREFIX = /^[=+\-@\t\r]/;
|
|
1336
|
+
/**
|
|
1337
|
+
* Render one CSV field. String values that a spreadsheet would treat as a
|
|
1338
|
+
* formula (leading `=`, `+`, `-`, `@`, tab or CR) are prefixed with `'`.
|
|
1339
|
+
* Objects are JSON-encoded. Fields containing a comma, quote, CR or LF are quoted;
|
|
1340
|
+
* `alwaysQuote` quotes every field. `null` / `undefined` render as empty.
|
|
1341
|
+
*/
|
|
1342
|
+
function csvField$1(value, alwaysQuote = false) {
|
|
1343
|
+
if (value === void 0 || value === null) return alwaysQuote ? "\"\"" : "";
|
|
1344
|
+
let str;
|
|
1345
|
+
if (typeof value === "string") str = CSV_FORMULA_PREFIX.test(value) ? `'${value}` : value;
|
|
1346
|
+
else if (typeof value === "object") str = JSON.stringify(value);
|
|
1347
|
+
else str = String(value);
|
|
1348
|
+
if (alwaysQuote || /[",\r\n]/.test(str)) return `"${str.replace(/"/g, "\"\"")}"`;
|
|
1349
|
+
return str;
|
|
1350
|
+
}
|
|
1311
1351
|
// src/transports/file.transport.ts
|
|
1312
1352
|
const appendFile = (0, node_util.promisify)(node_fs.appendFile);
|
|
1313
1353
|
const rename = (0, node_util.promisify)(node_fs.rename);
|
|
@@ -1319,7 +1359,7 @@ const DEFAULT_MAX_QUEUE = 1e4;
|
|
|
1319
1359
|
const DEFAULT_FILE_MODE = 384;
|
|
1320
1360
|
const DEFAULT_DIR_MODE = 448;
|
|
1321
1361
|
function csvField(value) {
|
|
1322
|
-
return
|
|
1362
|
+
return csvField$1(stripControlChars(safeToString(value)), true);
|
|
1323
1363
|
}
|
|
1324
1364
|
const SIZE_UNITS = {
|
|
1325
1365
|
"": 1,
|
|
@@ -1364,21 +1404,49 @@ function formatDatePattern(date, pattern) {
|
|
|
1364
1404
|
* transports: { file: { filename: 'app.log', dirname: './logs', maxSize: '50MB' } }
|
|
1365
1405
|
*/
|
|
1366
1406
|
var FileTransport = class {
|
|
1407
|
+
name = "file";
|
|
1408
|
+
level;
|
|
1409
|
+
batchSize;
|
|
1410
|
+
flushInterval;
|
|
1411
|
+
filter;
|
|
1412
|
+
config;
|
|
1413
|
+
maxBytes;
|
|
1414
|
+
maxFiles;
|
|
1415
|
+
compress;
|
|
1416
|
+
intervalMs;
|
|
1417
|
+
timestampedActive;
|
|
1418
|
+
dir;
|
|
1419
|
+
base;
|
|
1420
|
+
ext;
|
|
1421
|
+
batch = [];
|
|
1422
|
+
batchTimer;
|
|
1423
|
+
lastRotation = /* @__PURE__ */ new Date();
|
|
1424
|
+
currentFilePath;
|
|
1425
|
+
bytesWritten = 0;
|
|
1426
|
+
period;
|
|
1427
|
+
rotateRequested = false;
|
|
1428
|
+
rotationWaiters = [];
|
|
1429
|
+
/**
|
|
1430
|
+
* The one drain loop, or undefined when idle. Only one ever runs, so the batch
|
|
1431
|
+
* is never snapshotted and written twice, and rotation (which runs inside it)
|
|
1432
|
+
* never races a write.
|
|
1433
|
+
*/
|
|
1434
|
+
draining;
|
|
1435
|
+
/** Entries queued and entries written (or failed) so far; flush() waits on these. */
|
|
1436
|
+
enqueued = 0;
|
|
1437
|
+
settled = 0;
|
|
1438
|
+
flushWaiters = [];
|
|
1439
|
+
maxQueue;
|
|
1440
|
+
fileMode;
|
|
1441
|
+
dirMode;
|
|
1442
|
+
queuePolicy;
|
|
1443
|
+
spaceWaiters = [];
|
|
1444
|
+
inFlight = 0;
|
|
1445
|
+
dropped = 0;
|
|
1446
|
+
lastDropWarning = 0;
|
|
1447
|
+
/** Gzip and pruning of rotated files run off the write path, one at a time. */
|
|
1448
|
+
housekeeping = Promise.resolve();
|
|
1367
1449
|
constructor(config) {
|
|
1368
|
-
this.name = "file";
|
|
1369
|
-
this.batch = [];
|
|
1370
|
-
this.lastRotation = /* @__PURE__ */ new Date();
|
|
1371
|
-
this.bytesWritten = 0;
|
|
1372
|
-
this.rotateRequested = false;
|
|
1373
|
-
this.rotationWaiters = [];
|
|
1374
|
-
this.enqueued = 0;
|
|
1375
|
-
this.settled = 0;
|
|
1376
|
-
this.flushWaiters = [];
|
|
1377
|
-
this.spaceWaiters = [];
|
|
1378
|
-
this.inFlight = 0;
|
|
1379
|
-
this.dropped = 0;
|
|
1380
|
-
this.lastDropWarning = 0;
|
|
1381
|
-
this.housekeeping = Promise.resolve();
|
|
1382
1450
|
this.config = {
|
|
1383
1451
|
format: "json",
|
|
1384
1452
|
batchSize: 100,
|
package/dist/worker.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_worker_transport = require("./worker.transport-
|
|
2
|
+
const require_worker_transport = require("./worker.transport-CwNpH98o.js");
|
|
3
3
|
exports.WorkerTransport = require_worker_transport.WorkerTransport;
|
package/dist/worker.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { t as WorkerTransport } from "./worker.transport-
|
|
1
|
+
export { t as WorkerTransport } from "./worker.transport-DgK_ZXlB.mjs";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_secret_config = require("./secret-config-
|
|
1
|
+
const require_secret_config = require("./secret-config-6hdODxBb.js");
|
|
2
2
|
const require_internal_log = require("./internal-log-DwqTjhpb.js");
|
|
3
3
|
let node_path = require("node:path");
|
|
4
4
|
node_path = require_secret_config.__toESM(node_path);
|
|
@@ -43,18 +43,26 @@ let node_worker_threads = require("node:worker_threads");
|
|
|
43
43
|
* thread, completely removing JSON serialization and I/O from the main event loop.
|
|
44
44
|
*/
|
|
45
45
|
var WorkerTransport = class {
|
|
46
|
+
name = "worker";
|
|
47
|
+
level;
|
|
48
|
+
batchSize = 1;
|
|
49
|
+
flushInterval = 0;
|
|
50
|
+
worker = null;
|
|
51
|
+
ready = false;
|
|
52
|
+
buffer = [];
|
|
53
|
+
bufferSize;
|
|
54
|
+
restarts = 0;
|
|
55
|
+
maxRestarts;
|
|
56
|
+
config;
|
|
57
|
+
/** Pending restart-backoff timer, so close() can cancel a queued restart. */
|
|
58
|
+
restartTimer = null;
|
|
59
|
+
/** Set once close() begins, so a worker 'exit' no longer triggers a restart. */
|
|
60
|
+
closing = false;
|
|
61
|
+
/** Max time to wait for the worker's 'flushed' ack before giving up. */
|
|
62
|
+
FLUSH_TIMEOUT_MS = 5e3;
|
|
63
|
+
/** Max time to wait for a graceful worker exit before force-terminating. */
|
|
64
|
+
SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
46
65
|
constructor(config) {
|
|
47
|
-
this.name = "worker";
|
|
48
|
-
this.batchSize = 1;
|
|
49
|
-
this.flushInterval = 0;
|
|
50
|
-
this.worker = null;
|
|
51
|
-
this.ready = false;
|
|
52
|
-
this.buffer = [];
|
|
53
|
-
this.restarts = 0;
|
|
54
|
-
this.restartTimer = null;
|
|
55
|
-
this.closing = false;
|
|
56
|
-
this.FLUSH_TIMEOUT_MS = 5e3;
|
|
57
|
-
this.SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
58
66
|
this.config = config;
|
|
59
67
|
this.bufferSize = config.bufferSize ?? 1e3;
|
|
60
68
|
this.maxRestarts = config.maxRestarts ?? 3;
|
|
@@ -198,4 +206,4 @@ Object.defineProperty(exports, "WorkerTransport", {
|
|
|
198
206
|
}
|
|
199
207
|
});
|
|
200
208
|
|
|
201
|
-
//# sourceMappingURL=worker.transport-
|
|
209
|
+
//# sourceMappingURL=worker.transport-CwNpH98o.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.transport-DQDv1nAJ.js","names":["Worker","path"],"sources":["../src/transports/worker.transport.ts"],"sourcesContent":["/**\n * logixia — Worker Thread Transport\n *\n * Offloads JSON serialization and all I/O to a Node.js worker thread,\n * completely eliminating any event-loop impact from logging. The main thread\n * only posts a raw message to the worker — it never calls JSON.stringify or\n * any I/O syscalls on the hot path.\n *\n * Architecture:\n * Main thread → MessageChannel → Worker thread → Wrapped transport\n *\n * The worker runs an ordinary logixia transport (file, database, console…)\n * inside its own event loop. If the worker crashes it is restarted automatically\n * (up to `maxRestarts` times) and buffered messages are replayed.\n *\n * @example\n * ```ts\n * import { WorkerTransport } from 'logixia/worker';\n *\n * const logger = createLogger({\n * transports: {\n * // Run file transport inside a worker thread — zero event-loop impact\n * custom: [\n * new WorkerTransport({\n * transportType: 'file',\n * transportConfig: { filename: 'app.log', dirname: './logs' },\n * bufferSize: 500,\n * maxRestarts: 3,\n * }),\n * ],\n * },\n * });\n * ```\n */\n\nimport path from 'node:path';\nimport { Worker } from 'node:worker_threads';\n\nimport type { ITransport, TransportLogEntry } from '../types/transport.types';\nimport { internalError, internalWarn } from '../utils/internal-log';\nimport { protectSecrets } from './secret-config';\n\nexport interface WorkerTransportConfig {\n /**\n * Which built-in logixia transport to run inside the worker thread.\n * The worker resolves the correct module automatically.\n */\n transportType: 'file' | 'database' | 'console';\n /** Config forwarded verbatim to the wrapped transport constructor. */\n transportConfig: Record<string, unknown>;\n /**\n * Max log entries to buffer in the main thread while the worker is starting\n * or restarting. Entries beyond this limit are dropped with a warning.\n * @default 1000\n */\n bufferSize?: number;\n /**\n * How many times to restart the worker if it crashes unexpectedly.\n * @default 3\n */\n maxRestarts?: number;\n /**\n * Custom level severities (`levelOptions.levels`) the wrapped transport's own\n * `level` is compared against inside the worker. Built-in levels need no entry.\n */\n levels?: Record<string, number>;\n /**\n * Log level filter — only entries at or above this level are forwarded.\n * Useful for routing only errors to an expensive transport in a worker.\n */\n level?: string;\n}\n\n/**\n * Transport that runs a wrapped transport inside a dedicated Node.js worker\n * thread, completely removing JSON serialization and I/O from the main event loop.\n */\nexport class WorkerTransport implements ITransport {\n public readonly name = 'worker';\n public readonly level: string | undefined;\n public readonly batchSize = 1;\n public readonly flushInterval = 0;\n\n private worker: Worker | null = null;\n private ready = false;\n private readonly buffer: TransportLogEntry[] = [];\n private readonly bufferSize: number;\n private restarts = 0;\n private readonly maxRestarts: number;\n private readonly config: WorkerTransportConfig;\n /** Pending restart-backoff timer, so close() can cancel a queued restart. */\n private restartTimer: NodeJS.Timeout | null = null;\n /** Set once close() begins, so a worker 'exit' no longer triggers a restart. */\n private closing = false;\n /** Max time to wait for the worker's 'flushed' ack before giving up. */\n private readonly FLUSH_TIMEOUT_MS = 5000;\n /** Max time to wait for a graceful worker exit before force-terminating. */\n private readonly SHUTDOWN_TIMEOUT_MS = 5000;\n\n constructor(config: WorkerTransportConfig) {\n this.config = config;\n this.bufferSize = config.bufferSize ?? 1000;\n this.maxRestarts = config.maxRestarts ?? 3;\n this.level = config.level;\n protectSecrets(this, ['config']);\n this.startWorker();\n }\n\n private startWorker(): void {\n try {\n this.worker = new Worker(this.workerFile(), {\n workerData: {\n transportType: this.config.transportType,\n transportConfig: this.config.transportConfig,\n levels: this.config.levels,\n },\n });\n\n this.worker.on('message', (msg: { type: string; error?: string }) => {\n if (msg.type === 'ready') {\n this.ready = true;\n this.drainBuffer();\n } else if (msg.type === 'error' || msg.type === 'write-error') {\n internalError(\n `WorkerTransport [${this.config.transportType}]: ${msg.error ?? 'unknown'}`\n );\n }\n });\n\n this.worker.on('error', (err) => {\n internalError('WorkerTransport worker error', err);\n this.handleWorkerExit();\n });\n\n this.worker.on('exit', (code) => {\n if (code !== 0) {\n internalWarn(`WorkerTransport worker exited with code ${code}`);\n this.handleWorkerExit();\n }\n });\n } catch (err) {\n internalError('WorkerTransport failed to start', err);\n }\n }\n\n /** The worker entry is always built as CommonJS, next to this module's output. */\n protected workerFile(): string {\n return path.join(__dirname, 'worker-entry.js');\n }\n\n private handleWorkerExit(): void {\n this.ready = false;\n // Don't resurrect a worker we're intentionally shutting down — close() sets\n // `closing` and nulls the worker, so a late 'exit'/'error' event must not\n // queue a restart (which would spawn a fresh leaked thread post-shutdown).\n if (this.closing) return;\n if (this.restarts < this.maxRestarts) {\n this.restarts++;\n internalWarn(`WorkerTransport restarting (attempt ${this.restarts}/${this.maxRestarts})`);\n // Small backoff before restart. unref() so a perpetually-failing worker's\n // pending restart never keeps the process alive on its own.\n this.restartTimer = setTimeout(() => {\n this.restartTimer = null;\n this.startWorker();\n }, 500 * this.restarts);\n if (this.restartTimer.unref) this.restartTimer.unref();\n } else {\n internalError(\n `WorkerTransport exhausted ${this.maxRestarts} restart attempts — transport disabled`\n );\n }\n }\n\n private drainBuffer(): void {\n while (this.buffer.length > 0 && this.ready && this.worker) {\n const entry = this.buffer.shift();\n if (entry) {\n this.worker.postMessage({ type: 'write', entry });\n }\n }\n }\n\n write(entry: TransportLogEntry): void {\n if (this.ready && this.worker) {\n this.worker.postMessage({ type: 'write', entry });\n } else {\n if (this.buffer.length >= this.bufferSize) {\n internalWarn('WorkerTransport buffer full — dropping oldest entry');\n this.buffer.shift();\n }\n this.buffer.push(entry);\n }\n }\n\n async flush(): Promise<void> {\n if (!this.ready || !this.worker) return;\n const worker = this.worker;\n return new Promise<void>((resolve) => {\n // Time-box the flush: if the worker dies or restarts before answering,\n // the 'flushed' message would never arrive and this promise would hang\n // forever, blocking shutdown until the force-exit timer kills the process\n // (losing everything). Resolve on timeout so close() can still terminate.\n const timer = setTimeout(() => {\n worker.off('message', handler);\n resolve();\n }, this.FLUSH_TIMEOUT_MS);\n if (timer.unref) timer.unref();\n\n const handler = (msg: { type: string }) => {\n if (msg.type === 'flushed') {\n clearTimeout(timer);\n worker.off('message', handler);\n resolve();\n }\n };\n worker.on('message', handler);\n worker.postMessage({ type: 'flush' });\n });\n }\n\n /**\n * Flush the worker's buffer and terminate the thread on shutdown.\n *\n * This MUST be named close() — TransportManager.close() invokes\n * transport.close(), so a method named only shutdown() was never called on\n * graceful exit, leaking the worker thread and dropping its buffered logs.\n */\n async close(): Promise<void> {\n // Stop the restart loop first: cancel any queued backoff restart and flag\n // that further worker exits are expected (so handleWorkerExit no-ops).\n this.closing = true;\n if (this.restartTimer) {\n clearTimeout(this.restartTimer);\n this.restartTimer = null;\n }\n\n if (!this.worker) return;\n const worker = this.worker;\n this.worker = null;\n\n // Push any locally-buffered (pre-ready) entries first: the worker's port\n // queues them until its listener attaches, so they are handled before the\n // shutdown message, which flushes the wrapped transport and exits.\n this.drainBufferTo(worker);\n worker.postMessage({ type: 'shutdown' });\n\n await new Promise<void>((resolve) => {\n // Don't wait forever for a wedged worker — terminate() it after a timeout.\n const timer = setTimeout(() => {\n worker.terminate().finally(() => resolve());\n }, this.SHUTDOWN_TIMEOUT_MS);\n if (timer.unref) timer.unref();\n\n worker.once('exit', () => {\n clearTimeout(timer);\n resolve();\n });\n });\n }\n\n /** Back-compat alias — prefer close(). */\n async shutdown(): Promise<void> {\n await this.close();\n }\n\n private drainBufferTo(worker: Worker): void {\n while (this.buffer.length > 0) {\n const entry = this.buffer.shift();\n if (entry) worker.postMessage({ type: 'write', entry });\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"worker.transport-CwNpH98o.js","names":["Worker","path"],"sources":["../src/transports/worker.transport.ts"],"sourcesContent":["/**\n * logixia — Worker Thread Transport\n *\n * Offloads JSON serialization and all I/O to a Node.js worker thread,\n * completely eliminating any event-loop impact from logging. The main thread\n * only posts a raw message to the worker — it never calls JSON.stringify or\n * any I/O syscalls on the hot path.\n *\n * Architecture:\n * Main thread → MessageChannel → Worker thread → Wrapped transport\n *\n * The worker runs an ordinary logixia transport (file, database, console…)\n * inside its own event loop. If the worker crashes it is restarted automatically\n * (up to `maxRestarts` times) and buffered messages are replayed.\n *\n * @example\n * ```ts\n * import { WorkerTransport } from 'logixia/worker';\n *\n * const logger = createLogger({\n * transports: {\n * // Run file transport inside a worker thread — zero event-loop impact\n * custom: [\n * new WorkerTransport({\n * transportType: 'file',\n * transportConfig: { filename: 'app.log', dirname: './logs' },\n * bufferSize: 500,\n * maxRestarts: 3,\n * }),\n * ],\n * },\n * });\n * ```\n */\n\nimport path from 'node:path';\nimport { Worker } from 'node:worker_threads';\n\nimport type { ITransport, TransportLogEntry } from '../types/transport.types';\nimport { internalError, internalWarn } from '../utils/internal-log';\nimport { protectSecrets } from './secret-config';\n\nexport interface WorkerTransportConfig {\n /**\n * Which built-in logixia transport to run inside the worker thread.\n * The worker resolves the correct module automatically.\n */\n transportType: 'file' | 'database' | 'console';\n /** Config forwarded verbatim to the wrapped transport constructor. */\n transportConfig: Record<string, unknown>;\n /**\n * Max log entries to buffer in the main thread while the worker is starting\n * or restarting. Entries beyond this limit are dropped with a warning.\n * @default 1000\n */\n bufferSize?: number;\n /**\n * How many times to restart the worker if it crashes unexpectedly.\n * @default 3\n */\n maxRestarts?: number;\n /**\n * Custom level severities (`levelOptions.levels`) the wrapped transport's own\n * `level` is compared against inside the worker. Built-in levels need no entry.\n */\n levels?: Record<string, number>;\n /**\n * Log level filter — only entries at or above this level are forwarded.\n * Useful for routing only errors to an expensive transport in a worker.\n */\n level?: string;\n}\n\n/**\n * Transport that runs a wrapped transport inside a dedicated Node.js worker\n * thread, completely removing JSON serialization and I/O from the main event loop.\n */\nexport class WorkerTransport implements ITransport {\n public readonly name = 'worker';\n public readonly level: string | undefined;\n public readonly batchSize = 1;\n public readonly flushInterval = 0;\n\n private worker: Worker | null = null;\n private ready = false;\n private readonly buffer: TransportLogEntry[] = [];\n private readonly bufferSize: number;\n private restarts = 0;\n private readonly maxRestarts: number;\n private readonly config: WorkerTransportConfig;\n /** Pending restart-backoff timer, so close() can cancel a queued restart. */\n private restartTimer: NodeJS.Timeout | null = null;\n /** Set once close() begins, so a worker 'exit' no longer triggers a restart. */\n private closing = false;\n /** Max time to wait for the worker's 'flushed' ack before giving up. */\n private readonly FLUSH_TIMEOUT_MS = 5000;\n /** Max time to wait for a graceful worker exit before force-terminating. */\n private readonly SHUTDOWN_TIMEOUT_MS = 5000;\n\n constructor(config: WorkerTransportConfig) {\n this.config = config;\n this.bufferSize = config.bufferSize ?? 1000;\n this.maxRestarts = config.maxRestarts ?? 3;\n this.level = config.level;\n protectSecrets(this, ['config']);\n this.startWorker();\n }\n\n private startWorker(): void {\n try {\n this.worker = new Worker(this.workerFile(), {\n workerData: {\n transportType: this.config.transportType,\n transportConfig: this.config.transportConfig,\n levels: this.config.levels,\n },\n });\n\n this.worker.on('message', (msg: { type: string; error?: string }) => {\n if (msg.type === 'ready') {\n this.ready = true;\n this.drainBuffer();\n } else if (msg.type === 'error' || msg.type === 'write-error') {\n internalError(\n `WorkerTransport [${this.config.transportType}]: ${msg.error ?? 'unknown'}`\n );\n }\n });\n\n this.worker.on('error', (err) => {\n internalError('WorkerTransport worker error', err);\n this.handleWorkerExit();\n });\n\n this.worker.on('exit', (code) => {\n if (code !== 0) {\n internalWarn(`WorkerTransport worker exited with code ${code}`);\n this.handleWorkerExit();\n }\n });\n } catch (err) {\n internalError('WorkerTransport failed to start', err);\n }\n }\n\n /** The worker entry is always built as CommonJS, next to this module's output. */\n protected workerFile(): string {\n return path.join(__dirname, 'worker-entry.js');\n }\n\n private handleWorkerExit(): void {\n this.ready = false;\n // Don't resurrect a worker we're intentionally shutting down — close() sets\n // `closing` and nulls the worker, so a late 'exit'/'error' event must not\n // queue a restart (which would spawn a fresh leaked thread post-shutdown).\n if (this.closing) return;\n if (this.restarts < this.maxRestarts) {\n this.restarts++;\n internalWarn(`WorkerTransport restarting (attempt ${this.restarts}/${this.maxRestarts})`);\n // Small backoff before restart. unref() so a perpetually-failing worker's\n // pending restart never keeps the process alive on its own.\n this.restartTimer = setTimeout(() => {\n this.restartTimer = null;\n this.startWorker();\n }, 500 * this.restarts);\n if (this.restartTimer.unref) this.restartTimer.unref();\n } else {\n internalError(\n `WorkerTransport exhausted ${this.maxRestarts} restart attempts — transport disabled`\n );\n }\n }\n\n private drainBuffer(): void {\n while (this.buffer.length > 0 && this.ready && this.worker) {\n const entry = this.buffer.shift();\n if (entry) {\n this.worker.postMessage({ type: 'write', entry });\n }\n }\n }\n\n write(entry: TransportLogEntry): void {\n if (this.ready && this.worker) {\n this.worker.postMessage({ type: 'write', entry });\n } else {\n if (this.buffer.length >= this.bufferSize) {\n internalWarn('WorkerTransport buffer full — dropping oldest entry');\n this.buffer.shift();\n }\n this.buffer.push(entry);\n }\n }\n\n async flush(): Promise<void> {\n if (!this.ready || !this.worker) return;\n const worker = this.worker;\n return new Promise<void>((resolve) => {\n // Time-box the flush: if the worker dies or restarts before answering,\n // the 'flushed' message would never arrive and this promise would hang\n // forever, blocking shutdown until the force-exit timer kills the process\n // (losing everything). Resolve on timeout so close() can still terminate.\n const timer = setTimeout(() => {\n worker.off('message', handler);\n resolve();\n }, this.FLUSH_TIMEOUT_MS);\n if (timer.unref) timer.unref();\n\n const handler = (msg: { type: string }) => {\n if (msg.type === 'flushed') {\n clearTimeout(timer);\n worker.off('message', handler);\n resolve();\n }\n };\n worker.on('message', handler);\n worker.postMessage({ type: 'flush' });\n });\n }\n\n /**\n * Flush the worker's buffer and terminate the thread on shutdown.\n *\n * This MUST be named close() — TransportManager.close() invokes\n * transport.close(), so a method named only shutdown() was never called on\n * graceful exit, leaking the worker thread and dropping its buffered logs.\n */\n async close(): Promise<void> {\n // Stop the restart loop first: cancel any queued backoff restart and flag\n // that further worker exits are expected (so handleWorkerExit no-ops).\n this.closing = true;\n if (this.restartTimer) {\n clearTimeout(this.restartTimer);\n this.restartTimer = null;\n }\n\n if (!this.worker) return;\n const worker = this.worker;\n this.worker = null;\n\n // Push any locally-buffered (pre-ready) entries first: the worker's port\n // queues them until its listener attaches, so they are handled before the\n // shutdown message, which flushes the wrapped transport and exits.\n this.drainBufferTo(worker);\n worker.postMessage({ type: 'shutdown' });\n\n await new Promise<void>((resolve) => {\n // Don't wait forever for a wedged worker — terminate() it after a timeout.\n const timer = setTimeout(() => {\n worker.terminate().finally(() => resolve());\n }, this.SHUTDOWN_TIMEOUT_MS);\n if (timer.unref) timer.unref();\n\n worker.once('exit', () => {\n clearTimeout(timer);\n resolve();\n });\n });\n }\n\n /** Back-compat alias — prefer close(). */\n async shutdown(): Promise<void> {\n await this.close();\n }\n\n private drainBufferTo(worker: Worker): void {\n while (this.buffer.length > 0) {\n const entry = this.buffer.shift();\n if (entry) worker.postMessage({ type: 'write', entry });\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as protectSecrets } from "./secret-config-
|
|
1
|
+
import { n as protectSecrets } from "./secret-config-D1F4MW69.mjs";
|
|
2
2
|
import { r as internalWarn, t as internalError } from "./internal-log-Cy5nF1ZL.mjs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
@@ -47,18 +47,26 @@ const __dirname = /* @__PURE__ */ getDirname();
|
|
|
47
47
|
* thread, completely removing JSON serialization and I/O from the main event loop.
|
|
48
48
|
*/
|
|
49
49
|
var WorkerTransport = class {
|
|
50
|
+
name = "worker";
|
|
51
|
+
level;
|
|
52
|
+
batchSize = 1;
|
|
53
|
+
flushInterval = 0;
|
|
54
|
+
worker = null;
|
|
55
|
+
ready = false;
|
|
56
|
+
buffer = [];
|
|
57
|
+
bufferSize;
|
|
58
|
+
restarts = 0;
|
|
59
|
+
maxRestarts;
|
|
60
|
+
config;
|
|
61
|
+
/** Pending restart-backoff timer, so close() can cancel a queued restart. */
|
|
62
|
+
restartTimer = null;
|
|
63
|
+
/** Set once close() begins, so a worker 'exit' no longer triggers a restart. */
|
|
64
|
+
closing = false;
|
|
65
|
+
/** Max time to wait for the worker's 'flushed' ack before giving up. */
|
|
66
|
+
FLUSH_TIMEOUT_MS = 5e3;
|
|
67
|
+
/** Max time to wait for a graceful worker exit before force-terminating. */
|
|
68
|
+
SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
50
69
|
constructor(config) {
|
|
51
|
-
this.name = "worker";
|
|
52
|
-
this.batchSize = 1;
|
|
53
|
-
this.flushInterval = 0;
|
|
54
|
-
this.worker = null;
|
|
55
|
-
this.ready = false;
|
|
56
|
-
this.buffer = [];
|
|
57
|
-
this.restarts = 0;
|
|
58
|
-
this.restartTimer = null;
|
|
59
|
-
this.closing = false;
|
|
60
|
-
this.FLUSH_TIMEOUT_MS = 5e3;
|
|
61
|
-
this.SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
62
70
|
this.config = config;
|
|
63
71
|
this.bufferSize = config.bufferSize ?? 1e3;
|
|
64
72
|
this.maxRestarts = config.maxRestarts ?? 3;
|
|
@@ -197,4 +205,4 @@ var WorkerTransport = class {
|
|
|
197
205
|
};
|
|
198
206
|
export { WorkerTransport as t };
|
|
199
207
|
|
|
200
|
-
//# sourceMappingURL=worker.transport-
|
|
208
|
+
//# sourceMappingURL=worker.transport-DgK_ZXlB.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.transport-D78xhNhB.mjs","names":[],"sources":["../node_modules/tsdown/esm-shims.js","../src/transports/worker.transport.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","/**\n * logixia — Worker Thread Transport\n *\n * Offloads JSON serialization and all I/O to a Node.js worker thread,\n * completely eliminating any event-loop impact from logging. The main thread\n * only posts a raw message to the worker — it never calls JSON.stringify or\n * any I/O syscalls on the hot path.\n *\n * Architecture:\n * Main thread → MessageChannel → Worker thread → Wrapped transport\n *\n * The worker runs an ordinary logixia transport (file, database, console…)\n * inside its own event loop. If the worker crashes it is restarted automatically\n * (up to `maxRestarts` times) and buffered messages are replayed.\n *\n * @example\n * ```ts\n * import { WorkerTransport } from 'logixia/worker';\n *\n * const logger = createLogger({\n * transports: {\n * // Run file transport inside a worker thread — zero event-loop impact\n * custom: [\n * new WorkerTransport({\n * transportType: 'file',\n * transportConfig: { filename: 'app.log', dirname: './logs' },\n * bufferSize: 500,\n * maxRestarts: 3,\n * }),\n * ],\n * },\n * });\n * ```\n */\n\nimport path from 'node:path';\nimport { Worker } from 'node:worker_threads';\n\nimport type { ITransport, TransportLogEntry } from '../types/transport.types';\nimport { internalError, internalWarn } from '../utils/internal-log';\nimport { protectSecrets } from './secret-config';\n\nexport interface WorkerTransportConfig {\n /**\n * Which built-in logixia transport to run inside the worker thread.\n * The worker resolves the correct module automatically.\n */\n transportType: 'file' | 'database' | 'console';\n /** Config forwarded verbatim to the wrapped transport constructor. */\n transportConfig: Record<string, unknown>;\n /**\n * Max log entries to buffer in the main thread while the worker is starting\n * or restarting. Entries beyond this limit are dropped with a warning.\n * @default 1000\n */\n bufferSize?: number;\n /**\n * How many times to restart the worker if it crashes unexpectedly.\n * @default 3\n */\n maxRestarts?: number;\n /**\n * Custom level severities (`levelOptions.levels`) the wrapped transport's own\n * `level` is compared against inside the worker. Built-in levels need no entry.\n */\n levels?: Record<string, number>;\n /**\n * Log level filter — only entries at or above this level are forwarded.\n * Useful for routing only errors to an expensive transport in a worker.\n */\n level?: string;\n}\n\n/**\n * Transport that runs a wrapped transport inside a dedicated Node.js worker\n * thread, completely removing JSON serialization and I/O from the main event loop.\n */\nexport class WorkerTransport implements ITransport {\n public readonly name = 'worker';\n public readonly level: string | undefined;\n public readonly batchSize = 1;\n public readonly flushInterval = 0;\n\n private worker: Worker | null = null;\n private ready = false;\n private readonly buffer: TransportLogEntry[] = [];\n private readonly bufferSize: number;\n private restarts = 0;\n private readonly maxRestarts: number;\n private readonly config: WorkerTransportConfig;\n /** Pending restart-backoff timer, so close() can cancel a queued restart. */\n private restartTimer: NodeJS.Timeout | null = null;\n /** Set once close() begins, so a worker 'exit' no longer triggers a restart. */\n private closing = false;\n /** Max time to wait for the worker's 'flushed' ack before giving up. */\n private readonly FLUSH_TIMEOUT_MS = 5000;\n /** Max time to wait for a graceful worker exit before force-terminating. */\n private readonly SHUTDOWN_TIMEOUT_MS = 5000;\n\n constructor(config: WorkerTransportConfig) {\n this.config = config;\n this.bufferSize = config.bufferSize ?? 1000;\n this.maxRestarts = config.maxRestarts ?? 3;\n this.level = config.level;\n protectSecrets(this, ['config']);\n this.startWorker();\n }\n\n private startWorker(): void {\n try {\n this.worker = new Worker(this.workerFile(), {\n workerData: {\n transportType: this.config.transportType,\n transportConfig: this.config.transportConfig,\n levels: this.config.levels,\n },\n });\n\n this.worker.on('message', (msg: { type: string; error?: string }) => {\n if (msg.type === 'ready') {\n this.ready = true;\n this.drainBuffer();\n } else if (msg.type === 'error' || msg.type === 'write-error') {\n internalError(\n `WorkerTransport [${this.config.transportType}]: ${msg.error ?? 'unknown'}`\n );\n }\n });\n\n this.worker.on('error', (err) => {\n internalError('WorkerTransport worker error', err);\n this.handleWorkerExit();\n });\n\n this.worker.on('exit', (code) => {\n if (code !== 0) {\n internalWarn(`WorkerTransport worker exited with code ${code}`);\n this.handleWorkerExit();\n }\n });\n } catch (err) {\n internalError('WorkerTransport failed to start', err);\n }\n }\n\n /** The worker entry is always built as CommonJS, next to this module's output. */\n protected workerFile(): string {\n return path.join(__dirname, 'worker-entry.js');\n }\n\n private handleWorkerExit(): void {\n this.ready = false;\n // Don't resurrect a worker we're intentionally shutting down — close() sets\n // `closing` and nulls the worker, so a late 'exit'/'error' event must not\n // queue a restart (which would spawn a fresh leaked thread post-shutdown).\n if (this.closing) return;\n if (this.restarts < this.maxRestarts) {\n this.restarts++;\n internalWarn(`WorkerTransport restarting (attempt ${this.restarts}/${this.maxRestarts})`);\n // Small backoff before restart. unref() so a perpetually-failing worker's\n // pending restart never keeps the process alive on its own.\n this.restartTimer = setTimeout(() => {\n this.restartTimer = null;\n this.startWorker();\n }, 500 * this.restarts);\n if (this.restartTimer.unref) this.restartTimer.unref();\n } else {\n internalError(\n `WorkerTransport exhausted ${this.maxRestarts} restart attempts — transport disabled`\n );\n }\n }\n\n private drainBuffer(): void {\n while (this.buffer.length > 0 && this.ready && this.worker) {\n const entry = this.buffer.shift();\n if (entry) {\n this.worker.postMessage({ type: 'write', entry });\n }\n }\n }\n\n write(entry: TransportLogEntry): void {\n if (this.ready && this.worker) {\n this.worker.postMessage({ type: 'write', entry });\n } else {\n if (this.buffer.length >= this.bufferSize) {\n internalWarn('WorkerTransport buffer full — dropping oldest entry');\n this.buffer.shift();\n }\n this.buffer.push(entry);\n }\n }\n\n async flush(): Promise<void> {\n if (!this.ready || !this.worker) return;\n const worker = this.worker;\n return new Promise<void>((resolve) => {\n // Time-box the flush: if the worker dies or restarts before answering,\n // the 'flushed' message would never arrive and this promise would hang\n // forever, blocking shutdown until the force-exit timer kills the process\n // (losing everything). Resolve on timeout so close() can still terminate.\n const timer = setTimeout(() => {\n worker.off('message', handler);\n resolve();\n }, this.FLUSH_TIMEOUT_MS);\n if (timer.unref) timer.unref();\n\n const handler = (msg: { type: string }) => {\n if (msg.type === 'flushed') {\n clearTimeout(timer);\n worker.off('message', handler);\n resolve();\n }\n };\n worker.on('message', handler);\n worker.postMessage({ type: 'flush' });\n });\n }\n\n /**\n * Flush the worker's buffer and terminate the thread on shutdown.\n *\n * This MUST be named close() — TransportManager.close() invokes\n * transport.close(), so a method named only shutdown() was never called on\n * graceful exit, leaking the worker thread and dropping its buffered logs.\n */\n async close(): Promise<void> {\n // Stop the restart loop first: cancel any queued backoff restart and flag\n // that further worker exits are expected (so handleWorkerExit no-ops).\n this.closing = true;\n if (this.restartTimer) {\n clearTimeout(this.restartTimer);\n this.restartTimer = null;\n }\n\n if (!this.worker) return;\n const worker = this.worker;\n this.worker = null;\n\n // Push any locally-buffered (pre-ready) entries first: the worker's port\n // queues them until its listener attaches, so they are handled before the\n // shutdown message, which flushes the wrapped transport and exits.\n this.drainBufferTo(worker);\n worker.postMessage({ type: 'shutdown' });\n\n await new Promise<void>((resolve) => {\n // Don't wait forever for a wedged worker — terminate() it after a timeout.\n const timer = setTimeout(() => {\n worker.terminate().finally(() => resolve());\n }, this.SHUTDOWN_TIMEOUT_MS);\n if (timer.unref) timer.unref();\n\n worker.once('exit', () => {\n clearTimeout(timer);\n resolve();\n });\n });\n }\n\n /** Back-compat alias — prefer close(). */\n async shutdown(): Promise<void> {\n await this.close();\n }\n\n private drainBufferTo(worker: Worker): void {\n while (this.buffer.length > 0) {\n const entry = this.buffer.shift();\n if (entry) worker.postMessage({ type: 'write', entry });\n }\n }\n}\n"],"ignoreList":[0],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"worker.transport-DgK_ZXlB.mjs","names":[],"sources":["../node_modules/tsdown/esm-shims.js","../src/transports/worker.transport.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","/**\n * logixia — Worker Thread Transport\n *\n * Offloads JSON serialization and all I/O to a Node.js worker thread,\n * completely eliminating any event-loop impact from logging. The main thread\n * only posts a raw message to the worker — it never calls JSON.stringify or\n * any I/O syscalls on the hot path.\n *\n * Architecture:\n * Main thread → MessageChannel → Worker thread → Wrapped transport\n *\n * The worker runs an ordinary logixia transport (file, database, console…)\n * inside its own event loop. If the worker crashes it is restarted automatically\n * (up to `maxRestarts` times) and buffered messages are replayed.\n *\n * @example\n * ```ts\n * import { WorkerTransport } from 'logixia/worker';\n *\n * const logger = createLogger({\n * transports: {\n * // Run file transport inside a worker thread — zero event-loop impact\n * custom: [\n * new WorkerTransport({\n * transportType: 'file',\n * transportConfig: { filename: 'app.log', dirname: './logs' },\n * bufferSize: 500,\n * maxRestarts: 3,\n * }),\n * ],\n * },\n * });\n * ```\n */\n\nimport path from 'node:path';\nimport { Worker } from 'node:worker_threads';\n\nimport type { ITransport, TransportLogEntry } from '../types/transport.types';\nimport { internalError, internalWarn } from '../utils/internal-log';\nimport { protectSecrets } from './secret-config';\n\nexport interface WorkerTransportConfig {\n /**\n * Which built-in logixia transport to run inside the worker thread.\n * The worker resolves the correct module automatically.\n */\n transportType: 'file' | 'database' | 'console';\n /** Config forwarded verbatim to the wrapped transport constructor. */\n transportConfig: Record<string, unknown>;\n /**\n * Max log entries to buffer in the main thread while the worker is starting\n * or restarting. Entries beyond this limit are dropped with a warning.\n * @default 1000\n */\n bufferSize?: number;\n /**\n * How many times to restart the worker if it crashes unexpectedly.\n * @default 3\n */\n maxRestarts?: number;\n /**\n * Custom level severities (`levelOptions.levels`) the wrapped transport's own\n * `level` is compared against inside the worker. Built-in levels need no entry.\n */\n levels?: Record<string, number>;\n /**\n * Log level filter — only entries at or above this level are forwarded.\n * Useful for routing only errors to an expensive transport in a worker.\n */\n level?: string;\n}\n\n/**\n * Transport that runs a wrapped transport inside a dedicated Node.js worker\n * thread, completely removing JSON serialization and I/O from the main event loop.\n */\nexport class WorkerTransport implements ITransport {\n public readonly name = 'worker';\n public readonly level: string | undefined;\n public readonly batchSize = 1;\n public readonly flushInterval = 0;\n\n private worker: Worker | null = null;\n private ready = false;\n private readonly buffer: TransportLogEntry[] = [];\n private readonly bufferSize: number;\n private restarts = 0;\n private readonly maxRestarts: number;\n private readonly config: WorkerTransportConfig;\n /** Pending restart-backoff timer, so close() can cancel a queued restart. */\n private restartTimer: NodeJS.Timeout | null = null;\n /** Set once close() begins, so a worker 'exit' no longer triggers a restart. */\n private closing = false;\n /** Max time to wait for the worker's 'flushed' ack before giving up. */\n private readonly FLUSH_TIMEOUT_MS = 5000;\n /** Max time to wait for a graceful worker exit before force-terminating. */\n private readonly SHUTDOWN_TIMEOUT_MS = 5000;\n\n constructor(config: WorkerTransportConfig) {\n this.config = config;\n this.bufferSize = config.bufferSize ?? 1000;\n this.maxRestarts = config.maxRestarts ?? 3;\n this.level = config.level;\n protectSecrets(this, ['config']);\n this.startWorker();\n }\n\n private startWorker(): void {\n try {\n this.worker = new Worker(this.workerFile(), {\n workerData: {\n transportType: this.config.transportType,\n transportConfig: this.config.transportConfig,\n levels: this.config.levels,\n },\n });\n\n this.worker.on('message', (msg: { type: string; error?: string }) => {\n if (msg.type === 'ready') {\n this.ready = true;\n this.drainBuffer();\n } else if (msg.type === 'error' || msg.type === 'write-error') {\n internalError(\n `WorkerTransport [${this.config.transportType}]: ${msg.error ?? 'unknown'}`\n );\n }\n });\n\n this.worker.on('error', (err) => {\n internalError('WorkerTransport worker error', err);\n this.handleWorkerExit();\n });\n\n this.worker.on('exit', (code) => {\n if (code !== 0) {\n internalWarn(`WorkerTransport worker exited with code ${code}`);\n this.handleWorkerExit();\n }\n });\n } catch (err) {\n internalError('WorkerTransport failed to start', err);\n }\n }\n\n /** The worker entry is always built as CommonJS, next to this module's output. */\n protected workerFile(): string {\n return path.join(__dirname, 'worker-entry.js');\n }\n\n private handleWorkerExit(): void {\n this.ready = false;\n // Don't resurrect a worker we're intentionally shutting down — close() sets\n // `closing` and nulls the worker, so a late 'exit'/'error' event must not\n // queue a restart (which would spawn a fresh leaked thread post-shutdown).\n if (this.closing) return;\n if (this.restarts < this.maxRestarts) {\n this.restarts++;\n internalWarn(`WorkerTransport restarting (attempt ${this.restarts}/${this.maxRestarts})`);\n // Small backoff before restart. unref() so a perpetually-failing worker's\n // pending restart never keeps the process alive on its own.\n this.restartTimer = setTimeout(() => {\n this.restartTimer = null;\n this.startWorker();\n }, 500 * this.restarts);\n if (this.restartTimer.unref) this.restartTimer.unref();\n } else {\n internalError(\n `WorkerTransport exhausted ${this.maxRestarts} restart attempts — transport disabled`\n );\n }\n }\n\n private drainBuffer(): void {\n while (this.buffer.length > 0 && this.ready && this.worker) {\n const entry = this.buffer.shift();\n if (entry) {\n this.worker.postMessage({ type: 'write', entry });\n }\n }\n }\n\n write(entry: TransportLogEntry): void {\n if (this.ready && this.worker) {\n this.worker.postMessage({ type: 'write', entry });\n } else {\n if (this.buffer.length >= this.bufferSize) {\n internalWarn('WorkerTransport buffer full — dropping oldest entry');\n this.buffer.shift();\n }\n this.buffer.push(entry);\n }\n }\n\n async flush(): Promise<void> {\n if (!this.ready || !this.worker) return;\n const worker = this.worker;\n return new Promise<void>((resolve) => {\n // Time-box the flush: if the worker dies or restarts before answering,\n // the 'flushed' message would never arrive and this promise would hang\n // forever, blocking shutdown until the force-exit timer kills the process\n // (losing everything). Resolve on timeout so close() can still terminate.\n const timer = setTimeout(() => {\n worker.off('message', handler);\n resolve();\n }, this.FLUSH_TIMEOUT_MS);\n if (timer.unref) timer.unref();\n\n const handler = (msg: { type: string }) => {\n if (msg.type === 'flushed') {\n clearTimeout(timer);\n worker.off('message', handler);\n resolve();\n }\n };\n worker.on('message', handler);\n worker.postMessage({ type: 'flush' });\n });\n }\n\n /**\n * Flush the worker's buffer and terminate the thread on shutdown.\n *\n * This MUST be named close() — TransportManager.close() invokes\n * transport.close(), so a method named only shutdown() was never called on\n * graceful exit, leaking the worker thread and dropping its buffered logs.\n */\n async close(): Promise<void> {\n // Stop the restart loop first: cancel any queued backoff restart and flag\n // that further worker exits are expected (so handleWorkerExit no-ops).\n this.closing = true;\n if (this.restartTimer) {\n clearTimeout(this.restartTimer);\n this.restartTimer = null;\n }\n\n if (!this.worker) return;\n const worker = this.worker;\n this.worker = null;\n\n // Push any locally-buffered (pre-ready) entries first: the worker's port\n // queues them until its listener attaches, so they are handled before the\n // shutdown message, which flushes the wrapped transport and exits.\n this.drainBufferTo(worker);\n worker.postMessage({ type: 'shutdown' });\n\n await new Promise<void>((resolve) => {\n // Don't wait forever for a wedged worker — terminate() it after a timeout.\n const timer = setTimeout(() => {\n worker.terminate().finally(() => resolve());\n }, this.SHUTDOWN_TIMEOUT_MS);\n if (timer.unref) timer.unref();\n\n worker.once('exit', () => {\n clearTimeout(timer);\n resolve();\n });\n });\n }\n\n /** Back-compat alias — prefer close(). */\n async shutdown(): Promise<void> {\n await this.close();\n }\n\n private drainBufferTo(worker: Worker): void {\n while (this.buffer.length > 0) {\n const entry = this.buffer.shift();\n if (entry) worker.postMessage({ type: 'write', entry });\n }\n }\n}\n"],"ignoreList":[0],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "logixia",
|
|
3
|
-
"version": "2.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.11",
|
|
4
|
+
"description": "TypeScript logger for Node.js, NestJS and edge runtimes: structured JSON logs, request trace IDs, redaction, OpenTelemetry/OTLP and cloud transports. Pino/Winston alternative.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -171,6 +171,7 @@
|
|
|
171
171
|
"dev:intelligent-search": "ts-node -P tsconfig.examples.json --transpile-only -r ./examples/register.js examples/intelligent-search.ts",
|
|
172
172
|
"benchmark": "npm run build && node benchmarks/run.mjs",
|
|
173
173
|
"check:browser": "node --experimental-vm-modules --no-warnings scripts/check-browser-entry.mjs",
|
|
174
|
+
"check:dist": "node scripts/check-dist-runtime.mjs",
|
|
174
175
|
"check:exports": "node scripts/check-exports.mjs",
|
|
175
176
|
"check:optional-peers": "node scripts/check-optional-peers.mjs",
|
|
176
177
|
"size": "size-limit",
|
|
@@ -216,7 +217,13 @@
|
|
|
216
217
|
"log-rotation",
|
|
217
218
|
"log-search",
|
|
218
219
|
"prometheus",
|
|
219
|
-
"observability"
|
|
220
|
+
"observability",
|
|
221
|
+
"nodejs-logger",
|
|
222
|
+
"json-logger",
|
|
223
|
+
"log-redaction",
|
|
224
|
+
"pii",
|
|
225
|
+
"bun",
|
|
226
|
+
"nodejs"
|
|
220
227
|
],
|
|
221
228
|
"author": {
|
|
222
229
|
"name": "Sanjeev Sharma",
|