logixia 1.12.5 → 1.12.6
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 +65 -14
- package/dist/.tsbuildinfo +1 -1
- package/dist/async-context-CSHWoHMB.js +88 -0
- package/dist/async-context-CSHWoHMB.js.map +1 -0
- package/dist/async-context-CwBNMm2E.mjs +68 -0
- package/dist/async-context-CwBNMm2E.mjs.map +1 -0
- package/dist/azure-monitor.transport-CG7ClkYj.d.ts +62 -0
- package/dist/azure-monitor.transport-CG7ClkYj.d.ts.map +1 -0
- package/dist/azure-monitor.transport-CHnjVRF-.mjs +152 -0
- package/dist/azure-monitor.transport-CHnjVRF-.mjs.map +1 -0
- package/dist/azure-monitor.transport-Cm74kXZS.d.mts +62 -0
- package/dist/azure-monitor.transport-Cm74kXZS.d.mts.map +1 -0
- package/dist/azure-monitor.transport-CrvfHd9C.js +157 -0
- package/dist/azure-monitor.transport-CrvfHd9C.js.map +1 -0
- package/dist/azure.d.mts +3 -0
- package/dist/azure.d.ts +3 -0
- package/dist/azure.js +6 -0
- package/dist/azure.mjs +6 -0
- package/dist/batch-buffer-BY-mRWIK.js +170 -0
- package/dist/batch-buffer-BY-mRWIK.js.map +1 -0
- package/dist/batch-buffer-PGvSgbmd.mjs +132 -0
- package/dist/batch-buffer-PGvSgbmd.mjs.map +1 -0
- package/dist/browser.d.mts +427 -0
- package/dist/browser.d.mts.map +1 -0
- package/dist/browser.d.ts +427 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +590 -0
- package/dist/browser.js.map +1 -0
- package/dist/browser.mjs +586 -0
- package/dist/browser.mjs.map +1 -0
- package/dist/chunk-DLSkYcPk.mjs +7 -0
- package/dist/chunk-nOFOJqeH.js +30 -0
- package/dist/cloudwatch.d.mts +3 -0
- package/dist/cloudwatch.d.ts +3 -0
- package/dist/cloudwatch.js +6 -0
- package/dist/cloudwatch.mjs +6 -0
- package/dist/cloudwatch.transport-CGHY4buE.d.mts +81 -0
- package/dist/cloudwatch.transport-CGHY4buE.d.mts.map +1 -0
- package/dist/cloudwatch.transport-DK_8cKd3.d.ts +81 -0
- package/dist/cloudwatch.transport-DK_8cKd3.d.ts.map +1 -0
- package/dist/cloudwatch.transport-pNBNWJLt.js +370 -0
- package/dist/cloudwatch.transport-pNBNWJLt.js.map +1 -0
- package/dist/cloudwatch.transport-yBwm4OeO.mjs +360 -0
- package/dist/cloudwatch.transport-yBwm4OeO.mjs.map +1 -0
- package/dist/coerce.utils-BN980Y9V.js +44 -0
- package/dist/coerce.utils-BN980Y9V.js.map +1 -0
- package/dist/coerce.utils-Dxv4_ikC.mjs +38 -0
- package/dist/coerce.utils-Dxv4_ikC.mjs.map +1 -0
- package/dist/correlation.d.mts +238 -0
- package/dist/correlation.d.mts.map +1 -0
- package/dist/correlation.d.ts +238 -0
- package/dist/correlation.d.ts.map +1 -0
- package/dist/correlation.js +272 -0
- package/dist/correlation.js.map +1 -0
- package/dist/correlation.mjs +261 -0
- package/dist/correlation.mjs.map +1 -0
- package/dist/gcp.d.mts +3 -0
- package/dist/gcp.d.ts +3 -0
- package/dist/gcp.js +6 -0
- package/dist/gcp.mjs +6 -0
- package/dist/gcp.transport-BJuNRFe_.mjs +192 -0
- package/dist/gcp.transport-BJuNRFe_.mjs.map +1 -0
- package/dist/gcp.transport-BOhkVKlI.d.ts +71 -0
- package/dist/gcp.transport-BOhkVKlI.d.ts.map +1 -0
- package/dist/gcp.transport-Bi6CUqZR.d.mts +71 -0
- package/dist/gcp.transport-Bi6CUqZR.d.mts.map +1 -0
- package/dist/gcp.transport-SHdtc1n0.js +198 -0
- package/dist/gcp.transport-SHdtc1n0.js.map +1 -0
- package/dist/{index-CwFww7VC.d.ts → index-BPKwwERo.d.mts} +18 -174
- package/dist/index-BPKwwERo.d.mts.map +1 -0
- package/dist/{index-DnyeaoZU.d.mts → index-C1X-TE5I.d.ts} +18 -174
- package/dist/index-C1X-TE5I.d.ts.map +1 -0
- package/dist/index.d.mts +41 -11
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +40 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +87 -147
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +62 -132
- package/dist/index.mjs.map +1 -1
- package/dist/internal-log-BUJYMZbE.js +70 -0
- package/dist/internal-log-BUJYMZbE.js.map +1 -0
- package/dist/internal-log-Cm8SOWSV.mjs +52 -0
- package/dist/internal-log-Cm8SOWSV.mjs.map +1 -0
- package/dist/{logitron-logger.module-BIGTm2V6.mjs → logitron-logger.module-Bs3XYZgF.mjs} +266 -166
- package/dist/logitron-logger.module-Bs3XYZgF.mjs.map +1 -0
- package/dist/{logitron-logger.module-BTxDpK6u.js → logitron-logger.module-BsDZR5BH.js} +297 -203
- package/dist/logitron-logger.module-BsDZR5BH.js.map +1 -0
- package/dist/{logitron-logger.module-DvqtI43C.d.ts → logitron-logger.module-CxT1F6Cm.d.ts} +25 -12
- package/dist/logitron-logger.module-CxT1F6Cm.d.ts.map +1 -0
- package/dist/{logitron-logger.module-CpffEHec.d.mts → logitron-logger.module-zDKVd_o0.d.mts} +25 -12
- package/dist/logitron-logger.module-zDKVd_o0.d.mts.map +1 -0
- package/dist/middleware.d.mts +5 -2
- package/dist/middleware.d.mts.map +1 -1
- package/dist/middleware.d.ts +5 -2
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +13 -4
- package/dist/middleware.js.map +1 -1
- package/dist/middleware.mjs +14 -4
- package/dist/middleware.mjs.map +1 -1
- package/dist/nest.d.mts +3 -2
- package/dist/nest.d.mts.map +1 -1
- package/dist/nest.d.ts +3 -2
- package/dist/nest.d.ts.map +1 -1
- package/dist/nest.js +6 -2
- package/dist/nest.mjs +6 -2
- package/dist/safe-stringify-DJ66tW5I.mjs +118 -0
- package/dist/safe-stringify-DJ66tW5I.mjs.map +1 -0
- package/dist/safe-stringify-DWo3aZp_.js +142 -0
- package/dist/safe-stringify-DWo3aZp_.js.map +1 -0
- package/dist/search-CFQRISUv.mjs +1596 -0
- package/dist/search-CFQRISUv.mjs.map +1 -0
- package/dist/search-CQOo-Iq6.d.mts +816 -0
- package/dist/search-CQOo-Iq6.d.mts.map +1 -0
- package/dist/search-DujhR94O.d.ts +816 -0
- package/dist/search-DujhR94O.d.ts.map +1 -0
- package/dist/search-njTI2hdo.js +1631 -0
- package/dist/search-njTI2hdo.js.map +1 -0
- package/dist/search.d.mts +4 -1
- package/dist/search.d.ts +4 -1
- package/dist/search.js +9 -1
- package/dist/search.mjs +3 -2
- package/dist/testing.d.mts +2 -1
- package/dist/testing.d.mts.map +1 -1
- package/dist/testing.d.ts +2 -1
- package/dist/testing.d.ts.map +1 -1
- package/dist/{transport.manager-ciG4r5uz.js → transport.manager-BivyUTtU.js} +497 -437
- package/dist/transport.manager-BivyUTtU.js.map +1 -0
- package/dist/{transport.manager-DETpXuaY.mjs → transport.manager-D6KL3t9V.mjs} +473 -364
- package/dist/transport.manager-D6KL3t9V.mjs.map +1 -0
- package/dist/transport.types-B5vcyv2k.d.mts +187 -0
- package/dist/transport.types-B5vcyv2k.d.mts.map +1 -0
- package/dist/transport.types-BuY84TwO.d.ts +187 -0
- package/dist/transport.types-BuY84TwO.d.ts.map +1 -0
- package/dist/transports.d.mts +90 -29
- package/dist/transports.d.mts.map +1 -1
- package/dist/transports.d.ts +90 -29
- package/dist/transports.d.ts.map +1 -1
- package/dist/transports.js +14 -2
- package/dist/transports.mjs +10 -2
- package/dist/worker-entry.js +1241 -0
- package/dist/worker.d.mts +3 -0
- package/dist/worker.d.ts +3 -0
- package/dist/worker.js +4 -0
- package/dist/worker.mjs +4 -0
- package/dist/worker.transport-BP8Q28Z2.d.mts +76 -0
- package/dist/worker.transport-BP8Q28Z2.d.mts.map +1 -0
- package/dist/worker.transport-BhMyvqQ2.mjs +167 -0
- package/dist/worker.transport-BhMyvqQ2.mjs.map +1 -0
- package/dist/worker.transport-CfH6z37Z.js +168 -0
- package/dist/worker.transport-CfH6z37Z.js.map +1 -0
- package/dist/worker.transport-mKXgNK0L.d.ts +76 -0
- package/dist/worker.transport-mKXgNK0L.d.ts.map +1 -0
- package/package.json +23 -18
- package/dist/index-CwFww7VC.d.ts.map +0 -1
- package/dist/index-DnyeaoZU.d.mts.map +0 -1
- package/dist/logitron-logger.module-BIGTm2V6.mjs.map +0 -1
- package/dist/logitron-logger.module-BTxDpK6u.js.map +0 -1
- package/dist/logitron-logger.module-CpffEHec.d.mts.map +0 -1
- package/dist/logitron-logger.module-DvqtI43C.d.ts.map +0 -1
- package/dist/search-Cg_OasF-.d.ts +0 -1
- package/dist/search-DN676Dnz.d.mts +0 -1
- package/dist/search-DOvSI-mb.mjs +0 -1
- package/dist/search-DoZF3RZj.js +0 -0
- package/dist/transport.manager-DETpXuaY.mjs.map +0 -1
- package/dist/transport.manager-ciG4r5uz.js.map +0 -1
|
@@ -1,102 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
1
|
+
import { t as __require } from "./chunk-DLSkYcPk.mjs";
|
|
2
|
+
import { n as internalLog, r as internalWarn, t as internalError } from "./internal-log-Cm8SOWSV.mjs";
|
|
3
|
+
import { i as stringifyForLog } from "./safe-stringify-DJ66tW5I.mjs";
|
|
4
|
+
import { t as safeToString } from "./coerce.utils-Dxv4_ikC.mjs";
|
|
4
5
|
import * as fs from "node:fs";
|
|
5
|
-
import * as path from "node:path";
|
|
6
|
+
import * as path$1 from "node:path";
|
|
7
|
+
import { EventEmitter } from "node:events";
|
|
6
8
|
import { pipeline } from "node:stream/promises";
|
|
7
9
|
import { promisify } from "node:util";
|
|
8
10
|
import { createGzip } from "node:zlib";
|
|
9
11
|
|
|
10
|
-
//#region rolldown:runtime
|
|
11
|
-
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
12
|
-
|
|
13
|
-
//#endregion
|
|
14
|
-
//#region src/utils/internal-log.ts
|
|
15
|
-
/**
|
|
16
|
-
* Internal logging helpers for use within the logixia library itself.
|
|
17
|
-
*
|
|
18
|
-
* These functions write directly to stderr/stdout without going through
|
|
19
|
-
* LogixiaLogger — this avoids circular dependencies and ensures internal
|
|
20
|
-
* diagnostics still surface even when the logger is misconfigured.
|
|
21
|
-
*
|
|
22
|
-
* Library code must NEVER call console.log/warn/error directly. Use these
|
|
23
|
-
* helpers instead so that internal output can be silenced in tests by setting
|
|
24
|
-
* the LOGIXIA_SILENT_INTERNAL=1 environment variable.
|
|
25
|
-
*/
|
|
26
|
-
/**
|
|
27
|
-
* Read the silence flag on each call rather than caching it at import time, so
|
|
28
|
-
* setting LOGIXIA_SILENT_INTERNAL=1 AFTER the module is first imported (e.g. in
|
|
29
|
-
* a test setup file) still takes effect — the documented test-silencing
|
|
30
|
-
* behavior was previously unreliable because the value was frozen at load.
|
|
31
|
-
*/
|
|
32
|
-
function isSilent() {
|
|
33
|
-
return process.env.LOGIXIA_SILENT_INTERNAL === "1";
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Emit an internal debug/info message to stderr.
|
|
37
|
-
* Use for field-enable/disable notifications that a developer might want to see
|
|
38
|
-
* during development but should not appear in production log streams.
|
|
39
|
-
*/
|
|
40
|
-
function internalLog(message) {
|
|
41
|
-
if (!isSilent()) process.stderr.write(`[logixia] ${message}\n`);
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Emit an internal warning to stderr.
|
|
45
|
-
* Use when something is misconfigured but logixia can continue operating.
|
|
46
|
-
*/
|
|
47
|
-
function internalWarn(message) {
|
|
48
|
-
if (!isSilent()) process.stderr.write(`[logixia:warn] ${message}\n`);
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Emit an internal error to stderr.
|
|
52
|
-
* Use when a transport write fails or a serious internal error occurs.
|
|
53
|
-
*/
|
|
54
|
-
function internalError(message, error) {
|
|
55
|
-
if (!isSilent()) {
|
|
56
|
-
let errStr = "";
|
|
57
|
-
if (error instanceof Error) errStr = ` — ${error.message}`;
|
|
58
|
-
else if (error != null) errStr = ` — ${String(error)}`;
|
|
59
|
-
process.stderr.write(`[logixia:error] ${message}${errStr}\n`);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
//#endregion
|
|
64
|
-
//#region src/utils/coerce.utils.ts
|
|
65
|
-
/**
|
|
66
|
-
* Defensive value coercion helpers for Logixia.
|
|
67
|
-
*
|
|
68
|
-
* Logger inputs come from third-party code (NestJS framework internals,
|
|
69
|
-
* user payloads, error objects) where the runtime type is not always what
|
|
70
|
-
* the TypeScript signature promises. The formatter / transport hot paths
|
|
71
|
-
* call `.replace()` on string fields and crash if the field is anything
|
|
72
|
-
* else. Use these helpers anywhere a downstream consumer assumes a string.
|
|
73
|
-
*/
|
|
74
|
-
/**
|
|
75
|
-
* Coerce any value to a string without throwing.
|
|
76
|
-
*
|
|
77
|
-
* - `undefined` / `null` → `''` (so the field is rendered as empty rather
|
|
78
|
-
* than the literal string "undefined")
|
|
79
|
-
* - strings → returned as-is (zero allocation on the hot path)
|
|
80
|
-
* - Errors → `error.message` (matches what users would expect to see)
|
|
81
|
-
* - everything else → `JSON.stringify` if possible, falling back to
|
|
82
|
-
* `String(value)` for circular refs / values without a stringifier.
|
|
83
|
-
*/
|
|
84
|
-
function safeToString(value) {
|
|
85
|
-
var _constructor;
|
|
86
|
-
if (typeof value === "string") return value;
|
|
87
|
-
if (value === void 0 || value === null) return "";
|
|
88
|
-
if (value instanceof Error) return value.message;
|
|
89
|
-
if (typeof value === "number" || typeof value === "bigint" || typeof value === "boolean") return String(value);
|
|
90
|
-
if (typeof value === "symbol") return value.toString();
|
|
91
|
-
if (typeof value === "function") return `[Function: ${value.name || "anonymous"}]`;
|
|
92
|
-
try {
|
|
93
|
-
const json = JSON.stringify(value);
|
|
94
|
-
if (json !== void 0) return json;
|
|
95
|
-
} catch {}
|
|
96
|
-
return `[${((_constructor = value.constructor) === null || _constructor === void 0 ? void 0 : _constructor.name) ?? "object"}]`;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
//#endregion
|
|
100
12
|
//#region src/transports/console.transport.ts
|
|
101
13
|
/**
|
|
102
14
|
* Writes log entries to stdout (info/debug/verbose) and stderr (error/warn).
|
|
@@ -142,7 +54,7 @@ var ConsoleTransport = class ConsoleTransport {
|
|
|
142
54
|
(lvl === "error" || lvl === "warn" ? process.stderr : process.stdout).write(formatted);
|
|
143
55
|
}
|
|
144
56
|
formatEntry(entry) {
|
|
145
|
-
if (this.config.format === "json") return
|
|
57
|
+
if (this.config.format === "json") return stringifyForLog({
|
|
146
58
|
timestamp: this.config.timestamp !== false ? entry.timestamp.toISOString() : void 0,
|
|
147
59
|
level: entry.level,
|
|
148
60
|
message: entry.message,
|
|
@@ -170,7 +82,7 @@ var ConsoleTransport = class ConsoleTransport {
|
|
|
170
82
|
}
|
|
171
83
|
parts.push(ConsoleTransport.sanitize(entry.message));
|
|
172
84
|
if (entry.data && Object.keys(entry.data).length > 0) {
|
|
173
|
-
const data =
|
|
85
|
+
const data = stringifyForLog(entry.data);
|
|
174
86
|
parts.push(this.colorize(data, "blue"));
|
|
175
87
|
}
|
|
176
88
|
return parts.join(" ");
|
|
@@ -205,6 +117,15 @@ var ConsoleTransport = class ConsoleTransport {
|
|
|
205
117
|
* Validates that a SQL identifier (table/index name) contains only safe characters.
|
|
206
118
|
* Prevents SQL injection via user-supplied table names.
|
|
207
119
|
*/
|
|
120
|
+
const POSTGRES_MAX_PARAMS = 65535;
|
|
121
|
+
const POSTGRES_COLUMNS = 8;
|
|
122
|
+
const POSTGRES_ROWS_PER_INSERT = Math.floor(POSTGRES_MAX_PARAMS / POSTGRES_COLUMNS);
|
|
123
|
+
const MYSQL_ROWS_PER_INSERT = 5e3;
|
|
124
|
+
function chunk(items, size) {
|
|
125
|
+
const chunks = [];
|
|
126
|
+
for (let i = 0; i < items.length; i += size) chunks.push(items.slice(i, i + size));
|
|
127
|
+
return chunks;
|
|
128
|
+
}
|
|
208
129
|
function validateSqlIdentifier(name, label = "table") {
|
|
209
130
|
if (!/^[a-zA-Z_]\w*$/.test(name)) throw new Error(`Invalid ${label} name "${name}". Only letters, digits, and underscores are allowed, and the name must start with a letter or underscore.`);
|
|
210
131
|
}
|
|
@@ -406,22 +327,38 @@ var DatabaseTransport = class {
|
|
|
406
327
|
}
|
|
407
328
|
async flushToPostgreSQL(entries) {
|
|
408
329
|
const tableName = this.config.table || "logs";
|
|
409
|
-
const
|
|
330
|
+
const chunks = chunk(entries.map((entry) => [
|
|
410
331
|
entry.timestamp,
|
|
411
332
|
entry.level,
|
|
412
333
|
entry.message,
|
|
413
|
-
|
|
334
|
+
stringifyForLog(entry.data || {}),
|
|
414
335
|
entry.context,
|
|
415
336
|
entry.traceId,
|
|
416
337
|
entry.appName,
|
|
417
338
|
entry.environment
|
|
418
|
-
]);
|
|
419
|
-
const
|
|
339
|
+
]), POSTGRES_ROWS_PER_INSERT);
|
|
340
|
+
const insertChunk = async (rows) => {
|
|
341
|
+
const query = `
|
|
420
342
|
INSERT INTO ${tableName}
|
|
421
343
|
(timestamp, level, message, payload, context, trace_id, app_name, environment)
|
|
422
|
-
VALUES ${
|
|
344
|
+
VALUES ${rows.map((_, i) => {
|
|
345
|
+
return `(${Array.from({ length: POSTGRES_COLUMNS }, (_unused, c) => `$${i * POSTGRES_COLUMNS + c + 1}`).join(", ")})`;
|
|
346
|
+
}).join(", ")}
|
|
423
347
|
`;
|
|
424
|
-
|
|
348
|
+
await this.connection.query(query, rows.flat());
|
|
349
|
+
};
|
|
350
|
+
if (chunks.length === 1) {
|
|
351
|
+
await insertChunk(chunks[0]);
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
await this.connection.query("BEGIN");
|
|
355
|
+
try {
|
|
356
|
+
for (const rows of chunks) await insertChunk(rows);
|
|
357
|
+
await this.connection.query("COMMIT");
|
|
358
|
+
} catch (error) {
|
|
359
|
+
await this.connection.query("ROLLBACK").catch(() => {});
|
|
360
|
+
throw error;
|
|
361
|
+
}
|
|
425
362
|
}
|
|
426
363
|
async flushToMySQL(entries) {
|
|
427
364
|
const tableName = this.config.table || "logs";
|
|
@@ -429,7 +366,7 @@ var DatabaseTransport = class {
|
|
|
429
366
|
entry.timestamp,
|
|
430
367
|
entry.level,
|
|
431
368
|
entry.message,
|
|
432
|
-
|
|
369
|
+
stringifyForLog(entry.data || {}),
|
|
433
370
|
entry.context,
|
|
434
371
|
entry.traceId,
|
|
435
372
|
entry.appName,
|
|
@@ -440,7 +377,19 @@ var DatabaseTransport = class {
|
|
|
440
377
|
(timestamp, level, message, payload, context, trace_id, app_name, environment)
|
|
441
378
|
VALUES ?
|
|
442
379
|
`;
|
|
443
|
-
|
|
380
|
+
const chunks = chunk(values, MYSQL_ROWS_PER_INSERT);
|
|
381
|
+
if (chunks.length === 1) {
|
|
382
|
+
await this.connection.query(query, [values]);
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
await this.connection.beginTransaction();
|
|
386
|
+
try {
|
|
387
|
+
for (const rows of chunks) await this.connection.query(query, [rows]);
|
|
388
|
+
await this.connection.commit();
|
|
389
|
+
} catch (error) {
|
|
390
|
+
await this.connection.rollback().catch(() => {});
|
|
391
|
+
throw error;
|
|
392
|
+
}
|
|
444
393
|
}
|
|
445
394
|
async flushToSQLite(entries) {
|
|
446
395
|
const tableName = this.config.table || "logs";
|
|
@@ -449,8 +398,16 @@ var DatabaseTransport = class {
|
|
|
449
398
|
(timestamp, level, message, payload, context, trace_id, app_name, environment)
|
|
450
399
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
451
400
|
`);
|
|
452
|
-
|
|
453
|
-
|
|
401
|
+
await this.connection.exec("BEGIN");
|
|
402
|
+
try {
|
|
403
|
+
for (const entry of entries) await stmt.run(entry.timestamp instanceof Date ? entry.timestamp.toISOString() : entry.timestamp, entry.level, entry.message, stringifyForLog(entry.data || {}), entry.context, entry.traceId, entry.appName, entry.environment);
|
|
404
|
+
await this.connection.exec("COMMIT");
|
|
405
|
+
} catch (error) {
|
|
406
|
+
await this.connection.exec("ROLLBACK").catch(() => {});
|
|
407
|
+
throw error;
|
|
408
|
+
} finally {
|
|
409
|
+
await stmt.finalize().catch(() => {});
|
|
410
|
+
}
|
|
454
411
|
}
|
|
455
412
|
async createPostgreSQLTable() {
|
|
456
413
|
const tableName = this.config.table || "logs";
|
|
@@ -518,11 +475,13 @@ var DatabaseTransport = class {
|
|
|
518
475
|
await this.connection.exec(`CREATE INDEX IF NOT EXISTS idx_${tableName}_trace_id ON ${tableName} (trace_id)`);
|
|
519
476
|
}
|
|
520
477
|
setupFlushTimer() {
|
|
478
|
+
var _this$flushTimer$unre, _this$flushTimer;
|
|
521
479
|
this.flushTimer = setInterval(() => {
|
|
522
480
|
this.flush().catch((error) => {
|
|
523
481
|
internalError("Scheduled database flush error", error);
|
|
524
482
|
});
|
|
525
483
|
}, this.flushInterval);
|
|
484
|
+
(_this$flushTimer$unre = (_this$flushTimer = this.flushTimer).unref) === null || _this$flushTimer$unre === void 0 || _this$flushTimer$unre.call(_this$flushTimer);
|
|
526
485
|
}
|
|
527
486
|
async close() {
|
|
528
487
|
if (this.flushTimer) {
|
|
@@ -530,6 +489,7 @@ var DatabaseTransport = class {
|
|
|
530
489
|
this.flushTimer = void 0;
|
|
531
490
|
}
|
|
532
491
|
const MAX_CLOSE_FLUSH_ATTEMPTS = 3;
|
|
492
|
+
if (this.flushPromise) await this.flushPromise.catch(() => {});
|
|
533
493
|
for (let attempt = 0; attempt < MAX_CLOSE_FLUSH_ATTEMPTS && this.batch.length > 0; attempt += 1) try {
|
|
534
494
|
await this.flush();
|
|
535
495
|
} catch (error) {
|
|
@@ -565,14 +525,22 @@ var DatabaseTransport = class {
|
|
|
565
525
|
try {
|
|
566
526
|
switch (this.config.type) {
|
|
567
527
|
case "mongodb": return await this.connection.db(this.config.database).collection(this.config.collection || "logs").countDocuments();
|
|
568
|
-
case "postgresql":
|
|
569
|
-
|
|
570
|
-
case "sqlite": {
|
|
571
|
-
var _result$rows, _result$;
|
|
528
|
+
case "postgresql": {
|
|
529
|
+
var _result$rows;
|
|
572
530
|
const tableName = this.config.table || "logs";
|
|
573
531
|
const result = await this.connection.query(`SELECT COUNT(*) as count FROM ${tableName}`);
|
|
574
|
-
|
|
575
|
-
|
|
532
|
+
return Number((_result$rows = result.rows) === null || _result$rows === void 0 || (_result$rows = _result$rows[0]) === null || _result$rows === void 0 ? void 0 : _result$rows.count) || 0;
|
|
533
|
+
}
|
|
534
|
+
case "mysql": {
|
|
535
|
+
var _rows$;
|
|
536
|
+
const tableName = this.config.table || "logs";
|
|
537
|
+
const [rows] = await this.connection.query(`SELECT COUNT(*) as count FROM ${tableName}`);
|
|
538
|
+
return Number(rows === null || rows === void 0 || (_rows$ = rows[0]) === null || _rows$ === void 0 ? void 0 : _rows$.count) || 0;
|
|
539
|
+
}
|
|
540
|
+
case "sqlite": {
|
|
541
|
+
const tableName = this.config.table || "logs";
|
|
542
|
+
const row = await this.connection.get(`SELECT COUNT(*) as count FROM ${tableName}`);
|
|
543
|
+
return Number(row === null || row === void 0 ? void 0 : row.count) || 0;
|
|
576
544
|
}
|
|
577
545
|
default: return 0;
|
|
578
546
|
}
|
|
@@ -599,10 +567,9 @@ var AnalyticsTransport = class {
|
|
|
599
567
|
this.level = config.level;
|
|
600
568
|
this.batchSize = this.config.batchSize || 50;
|
|
601
569
|
this.flushInterval = this.config.flushInterval || 1e4;
|
|
602
|
-
this.initialize();
|
|
603
570
|
}
|
|
604
571
|
async write(entry) {
|
|
605
|
-
if (!this.isReady) await this.
|
|
572
|
+
if (!this.isReady) await this.ensureReady();
|
|
606
573
|
if (this.shouldSkipEntry(entry)) return;
|
|
607
574
|
if (this.config.batchSize && this.config.batchSize > 1) this.addToBatch(entry);
|
|
608
575
|
else await this.sendEntry(entry);
|
|
@@ -610,9 +577,13 @@ var AnalyticsTransport = class {
|
|
|
610
577
|
addToBatch(entry) {
|
|
611
578
|
this.batch.push(entry);
|
|
612
579
|
if (this.batch.length >= (this.config.batchSize || 50)) this.flush().catch((err) => internalError(`${this.name} batch flush failed`, err));
|
|
613
|
-
else if (!this.batchTimer && this.config.flushInterval)
|
|
614
|
-
|
|
615
|
-
|
|
580
|
+
else if (!this.batchTimer && this.config.flushInterval) {
|
|
581
|
+
var _this$batchTimer$unre, _this$batchTimer;
|
|
582
|
+
this.batchTimer = setTimeout(() => {
|
|
583
|
+
this.flush().catch((err) => internalError(`${this.name} interval flush failed`, err));
|
|
584
|
+
}, this.config.flushInterval);
|
|
585
|
+
(_this$batchTimer$unre = (_this$batchTimer = this.batchTimer).unref) === null || _this$batchTimer$unre === void 0 || _this$batchTimer$unre.call(_this$batchTimer);
|
|
586
|
+
}
|
|
616
587
|
}
|
|
617
588
|
async flush() {
|
|
618
589
|
if (this.batchTimer) {
|
|
@@ -671,10 +642,20 @@ var AnalyticsTransport = class {
|
|
|
671
642
|
if (this.config.customProperties) Object.assign(transformed, this.config.customProperties);
|
|
672
643
|
return transformed;
|
|
673
644
|
}
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
645
|
+
/**
|
|
646
|
+
* Runs initialize() once. A bad config (say, a missing API key) fails every
|
|
647
|
+
* write with the same error instead of an unhandled rejection at startup.
|
|
648
|
+
*/
|
|
649
|
+
ensureReady() {
|
|
650
|
+
this.initPromise ??= (async () => {
|
|
651
|
+
await this.initialize();
|
|
652
|
+
this.isReady = true;
|
|
653
|
+
})();
|
|
654
|
+
return this.initPromise;
|
|
655
|
+
}
|
|
656
|
+
/** @deprecated Use ensureReady(); kept for subclasses that call it. */
|
|
657
|
+
async waitForReady() {
|
|
658
|
+
await this.ensureReady();
|
|
678
659
|
}
|
|
679
660
|
};
|
|
680
661
|
|
|
@@ -700,7 +681,6 @@ var DataDogTransport = class extends AnalyticsTransport {
|
|
|
700
681
|
async initialize() {
|
|
701
682
|
try {
|
|
702
683
|
if (!this.datadogConfig.apiKey) throw new Error("DataDog API key is required");
|
|
703
|
-
await this.testConnection();
|
|
704
684
|
this.isReady = true;
|
|
705
685
|
} catch (error) {
|
|
706
686
|
internalError("DataDog transport initialization failed", error);
|
|
@@ -751,11 +731,11 @@ var DataDogTransport = class extends AnalyticsTransport {
|
|
|
751
731
|
timestamp: entry.timestamp.toISOString(),
|
|
752
732
|
level: this.mapLogLevel(entry.level),
|
|
753
733
|
message: entry.message,
|
|
754
|
-
service: this.datadogConfig.service || "
|
|
734
|
+
service: this.datadogConfig.service || "logixia",
|
|
755
735
|
version: this.datadogConfig.version,
|
|
756
736
|
env: this.datadogConfig.env || "production",
|
|
757
737
|
logger: {
|
|
758
|
-
name: "
|
|
738
|
+
name: "logixia",
|
|
759
739
|
thread_name: "main"
|
|
760
740
|
},
|
|
761
741
|
...transformed,
|
|
@@ -773,7 +753,7 @@ var DataDogTransport = class extends AnalyticsTransport {
|
|
|
773
753
|
host: this.getHostname(),
|
|
774
754
|
tags: [
|
|
775
755
|
`level:${entry.level}`,
|
|
776
|
-
`service:${this.datadogConfig.service || "
|
|
756
|
+
`service:${this.datadogConfig.service || "logixia"}`,
|
|
777
757
|
`env:${this.datadogConfig.env || "production"}`,
|
|
778
758
|
...entry.context ? [`context:${entry.context}`] : [],
|
|
779
759
|
...entry.appName ? [`app:${entry.appName}`] : []
|
|
@@ -786,7 +766,7 @@ var DataDogTransport = class extends AnalyticsTransport {
|
|
|
786
766
|
span_id: this.generateSpanId(),
|
|
787
767
|
parent_id: null,
|
|
788
768
|
name: "log.entry",
|
|
789
|
-
service: this.datadogConfig.service || "
|
|
769
|
+
service: this.datadogConfig.service || "logixia",
|
|
790
770
|
resource: entry.message,
|
|
791
771
|
type: "log",
|
|
792
772
|
start: entry.timestamp.getTime() * 1e6,
|
|
@@ -823,7 +803,7 @@ var DataDogTransport = class extends AnalyticsTransport {
|
|
|
823
803
|
"Content-Type": "application/json",
|
|
824
804
|
"DD-API-KEY": this.datadogConfig.apiKey
|
|
825
805
|
},
|
|
826
|
-
body:
|
|
806
|
+
body: stringifyForLog(data)
|
|
827
807
|
});
|
|
828
808
|
if (!response.ok) throw new Error(`DataDog Logs API error: ${response.status} ${response.statusText}`);
|
|
829
809
|
} catch (error) {
|
|
@@ -839,23 +819,13 @@ var DataDogTransport = class extends AnalyticsTransport {
|
|
|
839
819
|
"Content-Type": "application/json",
|
|
840
820
|
"DD-API-KEY": this.datadogConfig.apiKey
|
|
841
821
|
},
|
|
842
|
-
body:
|
|
822
|
+
body: stringifyForLog(data)
|
|
843
823
|
});
|
|
844
824
|
if (!response.ok) throw new Error(`DataDog Metrics API error: ${response.status} ${response.statusText}`);
|
|
845
825
|
} catch (error) {
|
|
846
826
|
throw new Error(`Failed to send metrics to DataDog: ${error.message}`, { cause: error });
|
|
847
827
|
}
|
|
848
828
|
}
|
|
849
|
-
async testConnection() {
|
|
850
|
-
const testMetric = { series: [{
|
|
851
|
-
metric: "logitron.test",
|
|
852
|
-
points: [[Math.floor(Date.now() / 1e3), 1]],
|
|
853
|
-
type: "count",
|
|
854
|
-
host: this.getHostname(),
|
|
855
|
-
tags: ["test:true"]
|
|
856
|
-
}] };
|
|
857
|
-
await this.makeMetricsRequest("/api/v1/series", testMetric);
|
|
858
|
-
}
|
|
859
829
|
generateSpanId() {
|
|
860
830
|
return Math.floor(Math.random() * 0xffffffffffff).toString(16);
|
|
861
831
|
}
|
|
@@ -883,11 +853,11 @@ var DataDogTransport = class extends AnalyticsTransport {
|
|
|
883
853
|
date_happened: Math.floor(Date.now() / 1e3),
|
|
884
854
|
priority: "normal",
|
|
885
855
|
tags: [
|
|
886
|
-
`service:${this.datadogConfig.service || "
|
|
856
|
+
`service:${this.datadogConfig.service || "logixia"}`,
|
|
887
857
|
`env:${this.datadogConfig.env || "production"}`,
|
|
888
858
|
...tags
|
|
889
859
|
],
|
|
890
|
-
source_type_name: "
|
|
860
|
+
source_type_name: "logixia"
|
|
891
861
|
};
|
|
892
862
|
const url = `${this.baseUrl}/api/v1/events`;
|
|
893
863
|
try {
|
|
@@ -897,7 +867,7 @@ var DataDogTransport = class extends AnalyticsTransport {
|
|
|
897
867
|
"Content-Type": "application/json",
|
|
898
868
|
"DD-API-KEY": this.datadogConfig.apiKey
|
|
899
869
|
},
|
|
900
|
-
body:
|
|
870
|
+
body: stringifyForLog(payload)
|
|
901
871
|
});
|
|
902
872
|
if (!response.ok) throw new Error(`DataDog Events API error: ${response.status} ${response.statusText}`);
|
|
903
873
|
} catch (error) {
|
|
@@ -908,16 +878,46 @@ var DataDogTransport = class extends AnalyticsTransport {
|
|
|
908
878
|
|
|
909
879
|
//#endregion
|
|
910
880
|
//#region src/transports/file.transport.ts
|
|
911
|
-
const
|
|
912
|
-
const
|
|
881
|
+
const appendFile = promisify(fs.appendFile);
|
|
882
|
+
const rename = promisify(fs.rename);
|
|
913
883
|
const unlink = promisify(fs.unlink);
|
|
914
884
|
const readdir = promisify(fs.readdir);
|
|
915
885
|
const stat = promisify(fs.stat);
|
|
886
|
+
const DROP_WARN_INTERVAL_MS = 5e3;
|
|
887
|
+
const SIZE_UNITS = {
|
|
888
|
+
"": 1,
|
|
889
|
+
k: 1024,
|
|
890
|
+
m: 1024 ** 2,
|
|
891
|
+
g: 1024 ** 3,
|
|
892
|
+
t: 1024 ** 4
|
|
893
|
+
};
|
|
894
|
+
/**
|
|
895
|
+
* Parses a size like `50MB`, `1.5g`, `512 KiB` or a plain byte count.
|
|
896
|
+
* Units are binary (1KB = 1024 bytes). Returns undefined for anything else.
|
|
897
|
+
*/
|
|
898
|
+
function parseSize(value) {
|
|
899
|
+
if (value === void 0) return void 0;
|
|
900
|
+
if (typeof value === "number") return Number.isFinite(value) && value > 0 ? Math.floor(value) : void 0;
|
|
901
|
+
const match = /^(\d+(?:\.\d+)?) ?([kmgt]?)(?:i?b)?$/i.exec(value.trim());
|
|
902
|
+
if (!match) return void 0;
|
|
903
|
+
const bytes = Math.floor(Number(match[1]) * (SIZE_UNITS[(match[2] ?? "").toLowerCase()] ?? 1));
|
|
904
|
+
return bytes > 0 ? bytes : void 0;
|
|
905
|
+
}
|
|
906
|
+
/** Formats a date with the YYYY, MM, DD, HH, mm and ss tokens, in local time. */
|
|
907
|
+
function formatDatePattern(date, pattern) {
|
|
908
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
909
|
+
return pattern.replace(/YYYY/g, String(date.getFullYear())).replace(/MM/g, pad(date.getMonth() + 1)).replace(/DD/g, pad(date.getDate())).replace(/HH/g, pad(date.getHours())).replace(/mm/g, pad(date.getMinutes())).replace(/ss/g, pad(date.getSeconds()));
|
|
910
|
+
}
|
|
916
911
|
/**
|
|
917
912
|
* Appends log entries to a local file with automatic rotation.
|
|
918
913
|
*
|
|
919
|
-
*
|
|
920
|
-
*
|
|
914
|
+
* Rotates by size (`maxSize`), by date (`datePattern`), by interval
|
|
915
|
+
* (`rotation.interval`) or by a custom `rotation.shouldRotate` check. Rotated
|
|
916
|
+
* files can be gzipped (`zippedArchive`) and pruned to the newest `maxFiles`.
|
|
917
|
+
*
|
|
918
|
+
* Without a `rotation` block the live file keeps its name (`app.log`) and
|
|
919
|
+
* rotated files are renamed beside it (`app-2026-10-22T09-30-00-000.log`).
|
|
920
|
+
* With a `rotation` block every file, the live one included, is timestamped.
|
|
921
921
|
*
|
|
922
922
|
* @example
|
|
923
923
|
* transports: { file: { filename: 'app.log', dirname: './logs', maxSize: '50MB' } }
|
|
@@ -927,7 +927,17 @@ var FileTransport = class {
|
|
|
927
927
|
this.name = "file";
|
|
928
928
|
this.batch = [];
|
|
929
929
|
this.lastRotation = /* @__PURE__ */ new Date();
|
|
930
|
-
this.
|
|
930
|
+
this.bytesWritten = 0;
|
|
931
|
+
this.rotateRequested = false;
|
|
932
|
+
this.rotationWaiters = [];
|
|
933
|
+
this.enqueued = 0;
|
|
934
|
+
this.settled = 0;
|
|
935
|
+
this.flushWaiters = [];
|
|
936
|
+
this.spaceWaiters = [];
|
|
937
|
+
this.inFlight = 0;
|
|
938
|
+
this.dropped = 0;
|
|
939
|
+
this.lastDropWarning = 0;
|
|
940
|
+
this.housekeeping = Promise.resolve();
|
|
931
941
|
this.config = {
|
|
932
942
|
format: "json",
|
|
933
943
|
batchSize: 100,
|
|
@@ -938,50 +948,171 @@ var FileTransport = class {
|
|
|
938
948
|
this.batchSize = this.config.batchSize || 100;
|
|
939
949
|
this.flushInterval = this.config.flushInterval || 5e3;
|
|
940
950
|
if (config.filter) this.filter = config.filter;
|
|
941
|
-
|
|
942
|
-
|
|
951
|
+
const rotation = config.rotation;
|
|
952
|
+
const rawMaxSize = (rotation === null || rotation === void 0 ? void 0 : rotation.maxSize) ?? config.maxSize;
|
|
953
|
+
this.maxBytes = parseSize(rawMaxSize);
|
|
954
|
+
if (rawMaxSize !== void 0 && this.maxBytes === void 0) internalWarn(`FileTransport: maxSize ${JSON.stringify(rawMaxSize)} is not a valid size (try '50MB'); size rotation is off`);
|
|
955
|
+
const maxQueue = config.maxQueue;
|
|
956
|
+
if (maxQueue !== void 0 && !(Number.isInteger(maxQueue) && maxQueue > 0)) internalWarn(`FileTransport: maxQueue ${String(maxQueue)} is not a positive integer; ignored`);
|
|
957
|
+
else this.maxQueue = maxQueue;
|
|
958
|
+
this.queuePolicy = config.queuePolicy ?? "drop-oldest";
|
|
959
|
+
this.maxFiles = (rotation === null || rotation === void 0 ? void 0 : rotation.maxFiles) ?? config.maxFiles;
|
|
960
|
+
this.compress = (rotation === null || rotation === void 0 ? void 0 : rotation.compress) ?? config.zippedArchive ?? false;
|
|
961
|
+
this.intervalMs = (rotation === null || rotation === void 0 ? void 0 : rotation.interval) ? this.parseInterval(rotation.interval) : void 0;
|
|
962
|
+
this.timestampedActive = rotation !== void 0;
|
|
963
|
+
this.dir = this.resolveDir();
|
|
964
|
+
this.ext = path$1.extname(config.filename);
|
|
965
|
+
this.base = path$1.basename(config.filename, this.ext);
|
|
966
|
+
try {
|
|
967
|
+
fs.mkdirSync(this.dir, { recursive: true });
|
|
968
|
+
} catch (error) {
|
|
969
|
+
internalError(`FileTransport could not create ${this.dir}`, error);
|
|
970
|
+
}
|
|
971
|
+
this.currentFilePath = this.timestampedActive ? this.uniquePath(this.timestamp(/* @__PURE__ */ new Date())) : path$1.join(this.dir, `${this.base}${this.ext}`);
|
|
972
|
+
let existing;
|
|
973
|
+
try {
|
|
974
|
+
existing = fs.statSync(this.currentFilePath);
|
|
975
|
+
} catch {
|
|
976
|
+
existing = void 0;
|
|
977
|
+
}
|
|
978
|
+
this.bytesWritten = (existing === null || existing === void 0 ? void 0 : existing.size) ?? 0;
|
|
979
|
+
if (config.datePattern) this.period = formatDatePattern((existing === null || existing === void 0 ? void 0 : existing.mtime) ?? /* @__PURE__ */ new Date(), config.datePattern);
|
|
943
980
|
}
|
|
944
981
|
async write(entry) {
|
|
945
982
|
try {
|
|
946
|
-
|
|
947
|
-
this.isRotating = true;
|
|
948
|
-
try {
|
|
949
|
-
await this.rotate();
|
|
950
|
-
} finally {
|
|
951
|
-
this.isRotating = false;
|
|
952
|
-
}
|
|
953
|
-
}
|
|
983
|
+
while (this.queuePolicy === "block" && this.queueFull()) await this.roomAvailable();
|
|
954
984
|
if (this.config.batchSize && this.config.batchSize > 1) this.addToBatch(entry);
|
|
955
|
-
else
|
|
985
|
+
else {
|
|
986
|
+
this.enqueue(entry);
|
|
987
|
+
await this.flush();
|
|
988
|
+
}
|
|
956
989
|
} catch (error) {
|
|
957
990
|
throw new Error(`File transport write failed: ${error.message}`, { cause: error });
|
|
958
991
|
}
|
|
959
992
|
}
|
|
993
|
+
/**
|
|
994
|
+
* Resolves once every entry queued before the call is on disk. It does not
|
|
995
|
+
* wait for entries queued afterwards, so under steady traffic it still returns
|
|
996
|
+
* instead of waiting for a quiet moment that never comes.
|
|
997
|
+
*/
|
|
960
998
|
async flush() {
|
|
961
999
|
if (this.batchTimer) {
|
|
962
1000
|
clearTimeout(this.batchTimer);
|
|
963
1001
|
this.batchTimer = void 0;
|
|
964
1002
|
}
|
|
965
|
-
if (
|
|
966
|
-
|
|
1003
|
+
if (this.settled >= this.enqueued) return;
|
|
1004
|
+
const done = new Promise((resolve, reject) => {
|
|
1005
|
+
this.flushWaiters.push({
|
|
1006
|
+
upTo: this.enqueued,
|
|
1007
|
+
resolve,
|
|
1008
|
+
reject
|
|
1009
|
+
});
|
|
1010
|
+
});
|
|
1011
|
+
this.startDrain();
|
|
1012
|
+
await done;
|
|
1013
|
+
}
|
|
1014
|
+
/** Writes out anything pending, then starts a new file. */
|
|
1015
|
+
async rotate() {
|
|
1016
|
+
await this.flush();
|
|
1017
|
+
const done = new Promise((resolve) => this.rotationWaiters.push(resolve));
|
|
1018
|
+
this.rotateRequested = true;
|
|
1019
|
+
this.startDrain();
|
|
1020
|
+
await done;
|
|
1021
|
+
}
|
|
1022
|
+
enqueue(entry) {
|
|
1023
|
+
if (this.queueFull()) {
|
|
1024
|
+
if (this.queuePolicy === "drop-oldest" && this.batch.length > 0) {
|
|
1025
|
+
this.batch.shift();
|
|
1026
|
+
this.batch.push(entry);
|
|
1027
|
+
}
|
|
1028
|
+
this.noteDropped();
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
this.batch.push(entry);
|
|
1032
|
+
this.enqueued += 1;
|
|
1033
|
+
}
|
|
1034
|
+
queueFull() {
|
|
1035
|
+
return this.maxQueue !== void 0 && this.batch.length + this.inFlight >= this.maxQueue;
|
|
1036
|
+
}
|
|
1037
|
+
roomAvailable() {
|
|
1038
|
+
const room = new Promise((resolve) => this.spaceWaiters.push(resolve));
|
|
1039
|
+
this.startDrain();
|
|
1040
|
+
return room;
|
|
1041
|
+
}
|
|
1042
|
+
releaseWaiters() {
|
|
1043
|
+
const waiting = this.spaceWaiters;
|
|
1044
|
+
this.spaceWaiters = [];
|
|
1045
|
+
for (const resolve of waiting) resolve();
|
|
1046
|
+
}
|
|
1047
|
+
noteDropped() {
|
|
1048
|
+
this.dropped += 1;
|
|
1049
|
+
this.reportDropped(DROP_WARN_INTERVAL_MS);
|
|
1050
|
+
}
|
|
1051
|
+
reportDropped(minGapMs) {
|
|
1052
|
+
const now = Date.now();
|
|
1053
|
+
if (this.dropped === 0 || now - this.lastDropWarning < minGapMs) return;
|
|
1054
|
+
internalWarn(`FileTransport: dropped ${this.dropped} log line${this.dropped === 1 ? "" : "s"} (queue full at maxQueue ${this.maxQueue}, policy ${this.queuePolicy})`);
|
|
1055
|
+
this.dropped = 0;
|
|
1056
|
+
this.lastDropWarning = now;
|
|
1057
|
+
}
|
|
1058
|
+
startDrain() {
|
|
1059
|
+
if (this.draining) return;
|
|
1060
|
+
this.draining = this.drain().finally(() => {
|
|
1061
|
+
this.draining = void 0;
|
|
967
1062
|
});
|
|
968
|
-
await this.flushPromise;
|
|
969
1063
|
}
|
|
970
1064
|
/**
|
|
971
|
-
*
|
|
972
|
-
*
|
|
973
|
-
*
|
|
1065
|
+
* Writes the batch one snapshot at a time. Entries queued while a write is in
|
|
1066
|
+
* flight are picked up by the next iteration, so one drain empties the batch
|
|
1067
|
+
* however much is logged meanwhile.
|
|
974
1068
|
*/
|
|
975
1069
|
async drain() {
|
|
976
|
-
while (this.batch.length > 0) {
|
|
1070
|
+
while (this.batch.length > 0 || this.rotateRequested) {
|
|
1071
|
+
if (this.rotateRequested) {
|
|
1072
|
+
this.rotateRequested = false;
|
|
1073
|
+
const waiting = this.rotationWaiters;
|
|
1074
|
+
this.rotationWaiters = [];
|
|
1075
|
+
try {
|
|
1076
|
+
await this.rotateFile();
|
|
1077
|
+
} catch (error) {
|
|
1078
|
+
internalError("FileTransport rotation failed", error);
|
|
1079
|
+
}
|
|
1080
|
+
for (const resolve of waiting) resolve();
|
|
1081
|
+
continue;
|
|
1082
|
+
}
|
|
977
1083
|
const pending = this.batch;
|
|
978
1084
|
this.batch = [];
|
|
979
|
-
|
|
1085
|
+
this.inFlight = pending.length;
|
|
1086
|
+
let failure;
|
|
1087
|
+
try {
|
|
1088
|
+
await this.writeBatch(pending);
|
|
1089
|
+
} catch (error) {
|
|
1090
|
+
failure = error;
|
|
1091
|
+
}
|
|
1092
|
+
this.inFlight = 0;
|
|
1093
|
+
this.settled += pending.length;
|
|
1094
|
+
this.settle(failure);
|
|
1095
|
+
this.releaseWaiters();
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
/**
|
|
1099
|
+
* Resolves the flush() calls whose entries are now written. A failed batch
|
|
1100
|
+
* fails every flush still waiting, since each one covers that batch.
|
|
1101
|
+
*/
|
|
1102
|
+
settle(failure) {
|
|
1103
|
+
if (failure !== void 0) {
|
|
1104
|
+
if (this.flushWaiters.length === 0) internalError("FileTransport write failed", failure);
|
|
1105
|
+
for (const waiter of this.flushWaiters) waiter.error ??= failure;
|
|
980
1106
|
}
|
|
1107
|
+
const waiting = [];
|
|
1108
|
+
for (const waiter of this.flushWaiters) if (waiter.upTo > this.settled) waiting.push(waiter);
|
|
1109
|
+
else if (waiter.error === void 0) waiter.resolve();
|
|
1110
|
+
else waiter.reject(waiter.error);
|
|
1111
|
+
this.flushWaiters = waiting;
|
|
981
1112
|
}
|
|
982
1113
|
formatEntry(entry) {
|
|
983
1114
|
switch (this.config.format) {
|
|
984
|
-
case "json": return
|
|
1115
|
+
case "json": return stringifyForLog({
|
|
985
1116
|
timestamp: entry.timestamp.toISOString(),
|
|
986
1117
|
level: entry.level,
|
|
987
1118
|
message: entry.message,
|
|
@@ -995,43 +1126,79 @@ var FileTransport = class {
|
|
|
995
1126
|
`"${safeMessage.replace(/"/g, "\"\"")}"`,
|
|
996
1127
|
safeToString(entry.context),
|
|
997
1128
|
safeToString(entry.traceId),
|
|
998
|
-
|
|
1129
|
+
stringifyForLog(entry.data || {})
|
|
999
1130
|
].join(",");
|
|
1000
1131
|
}
|
|
1001
1132
|
case "text":
|
|
1002
|
-
default: return `${entry.timestamp.toISOString()} [${entry.level.toUpperCase()}] ${safeToString(entry.message)}${entry.data ? " " +
|
|
1133
|
+
default: return `${entry.timestamp.toISOString()} [${entry.level.toUpperCase()}] ${safeToString(entry.message)}${entry.data ? " " + stringifyForLog(entry.data) : ""}`;
|
|
1003
1134
|
}
|
|
1004
1135
|
}
|
|
1005
1136
|
addToBatch(entry) {
|
|
1006
|
-
this.
|
|
1007
|
-
|
|
1137
|
+
this.enqueue(entry);
|
|
1138
|
+
const threshold = Math.min(this.config.batchSize || 100, this.maxQueue ?? Infinity);
|
|
1139
|
+
if (this.batch.length >= threshold && !this.draining) {
|
|
1008
1140
|
if (this.batchTimer) {
|
|
1009
1141
|
clearTimeout(this.batchTimer);
|
|
1010
1142
|
this.batchTimer = void 0;
|
|
1011
1143
|
}
|
|
1012
|
-
this.
|
|
1144
|
+
this.startDrain();
|
|
1013
1145
|
} else if (!this.batchTimer && this.config.flushInterval) this.batchTimer = setTimeout(() => {
|
|
1014
1146
|
this.flush().catch((err) => internalError("FileTransport interval flush failed", err));
|
|
1015
1147
|
}, this.config.flushInterval);
|
|
1016
1148
|
}
|
|
1149
|
+
/** Only ever called from drain(), so writes and rotations never overlap. */
|
|
1017
1150
|
async writeBatch(entries) {
|
|
1018
1151
|
if (entries.length === 0) return;
|
|
1019
|
-
|
|
1020
|
-
if (this.
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1152
|
+
if (this.timeToRotate()) await this.rotateFile();
|
|
1153
|
+
if (this.maxBytes === void 0) {
|
|
1154
|
+
const content = entries.map((entry) => this.formatEntry(entry)).join("\n") + "\n";
|
|
1155
|
+
await this.append(content);
|
|
1156
|
+
return;
|
|
1157
|
+
}
|
|
1158
|
+
let chunk$1 = "";
|
|
1159
|
+
let chunkBytes = 0;
|
|
1160
|
+
for (const entry of entries) {
|
|
1161
|
+
const line = this.formatEntry(entry) + "\n";
|
|
1162
|
+
const lineBytes = Buffer.byteLength(line);
|
|
1163
|
+
const used = this.bytesWritten + chunkBytes;
|
|
1164
|
+
if (used > 0 && used + lineBytes > this.maxBytes) {
|
|
1165
|
+
if (chunk$1) await this.append(chunk$1, chunkBytes);
|
|
1166
|
+
chunk$1 = "";
|
|
1167
|
+
chunkBytes = 0;
|
|
1168
|
+
await this.rotateFile();
|
|
1169
|
+
}
|
|
1170
|
+
chunk$1 += line;
|
|
1171
|
+
chunkBytes += lineBytes;
|
|
1172
|
+
}
|
|
1173
|
+
if (chunk$1) await this.append(chunk$1, chunkBytes);
|
|
1174
|
+
}
|
|
1175
|
+
async append(content, bytes = Buffer.byteLength(content)) {
|
|
1176
|
+
await appendFile(this.currentFilePath, content);
|
|
1177
|
+
this.bytesWritten += bytes;
|
|
1027
1178
|
}
|
|
1028
|
-
|
|
1179
|
+
timeToRotate() {
|
|
1029
1180
|
var _this$config$rotation;
|
|
1030
|
-
if (
|
|
1031
|
-
|
|
1181
|
+
if (this.intervalMs !== void 0 && Date.now() - this.lastRotation.getTime() >= this.intervalMs) return true;
|
|
1182
|
+
if (this.config.datePattern && this.period !== void 0) {
|
|
1183
|
+
if (formatDatePattern(/* @__PURE__ */ new Date(), this.config.datePattern) !== this.period) return true;
|
|
1184
|
+
}
|
|
1185
|
+
const shouldRotate = (_this$config$rotation = this.config.rotation) === null || _this$config$rotation === void 0 ? void 0 : _this$config$rotation.shouldRotate;
|
|
1186
|
+
if (shouldRotate) {
|
|
1187
|
+
const stats = {
|
|
1188
|
+
size: this.bytesWritten,
|
|
1189
|
+
created: this.lastRotation,
|
|
1190
|
+
modified: /* @__PURE__ */ new Date()
|
|
1191
|
+
};
|
|
1192
|
+
try {
|
|
1193
|
+
return shouldRotate(this.currentFilePath, stats);
|
|
1194
|
+
} catch (error) {
|
|
1195
|
+
internalError("FileTransport rotation.shouldRotate threw", error);
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
return false;
|
|
1032
1199
|
}
|
|
1033
1200
|
parseInterval(interval) {
|
|
1034
|
-
const match = (
|
|
1201
|
+
const match = /^(\d{1,6})([hdwmy])$/i.exec(interval.trim());
|
|
1035
1202
|
if (!match) return 1440 * 60 * 1e3;
|
|
1036
1203
|
const value = Number.parseInt(match[1] || "1", 10);
|
|
1037
1204
|
switch ((match[2] || "h").toLowerCase()) {
|
|
@@ -1043,40 +1210,54 @@ var FileTransport = class {
|
|
|
1043
1210
|
default: return value * 60 * 60 * 1e3;
|
|
1044
1211
|
}
|
|
1045
1212
|
}
|
|
1046
|
-
async
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
if (this.
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
this.
|
|
1213
|
+
async rotateFile() {
|
|
1214
|
+
const now = /* @__PURE__ */ new Date();
|
|
1215
|
+
let archived;
|
|
1216
|
+
if (this.timestampedActive) {
|
|
1217
|
+
archived = this.currentFilePath;
|
|
1218
|
+
this.currentFilePath = this.uniquePath(this.timestamp(now));
|
|
1219
|
+
} else {
|
|
1220
|
+
archived = this.uniquePath(this.period ?? this.timestamp(now));
|
|
1221
|
+
try {
|
|
1222
|
+
await rename(this.currentFilePath, archived);
|
|
1223
|
+
} catch (error) {
|
|
1224
|
+
if (error.code !== "ENOENT") {
|
|
1225
|
+
internalError(`FileTransport could not rotate ${this.currentFilePath}`, error);
|
|
1226
|
+
this.bytesWritten = 0;
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1229
|
+
archived = void 0;
|
|
1230
|
+
}
|
|
1054
1231
|
}
|
|
1055
|
-
|
|
1056
|
-
this.
|
|
1057
|
-
this.
|
|
1058
|
-
if (
|
|
1059
|
-
|
|
1232
|
+
this.bytesWritten = 0;
|
|
1233
|
+
this.lastRotation = now;
|
|
1234
|
+
if (this.config.datePattern) this.period = formatDatePattern(now, this.config.datePattern);
|
|
1235
|
+
if (archived === void 0 || !fs.existsSync(archived)) return;
|
|
1236
|
+
const file = archived;
|
|
1237
|
+
this.housekeeping = this.housekeeping.then(async () => {
|
|
1238
|
+
if (this.compress) await this.compressFile(file);
|
|
1239
|
+
if (this.maxFiles) await this.cleanupOldFiles();
|
|
1240
|
+
});
|
|
1241
|
+
}
|
|
1242
|
+
timestamp(date) {
|
|
1243
|
+
return date.toISOString().replace(/[:.]/g, "-").slice(0, 23);
|
|
1244
|
+
}
|
|
1245
|
+
/** `<base>-<stamp><ext>`, with `.1`, `.2`… added if that name (or its .gz) is taken. */
|
|
1246
|
+
uniquePath(stamp) {
|
|
1247
|
+
const taken = (p) => fs.existsSync(p) || fs.existsSync(`${p}.gz`);
|
|
1248
|
+
let candidate = path$1.join(this.dir, `${this.base}-${stamp}${this.ext}`);
|
|
1249
|
+
let n = 0;
|
|
1250
|
+
while (taken(candidate) || candidate === this.currentFilePath) {
|
|
1251
|
+
n += 1;
|
|
1252
|
+
candidate = path$1.join(this.dir, `${this.base}-${stamp}.${n}${this.ext}`);
|
|
1253
|
+
}
|
|
1254
|
+
return candidate;
|
|
1060
1255
|
}
|
|
1061
1256
|
resolveDir() {
|
|
1062
1257
|
if (this.config.dirname) return this.config.dirname;
|
|
1063
|
-
const fromFilename = path.dirname(this.config.filename);
|
|
1258
|
+
const fromFilename = path$1.dirname(this.config.filename);
|
|
1064
1259
|
return fromFilename === "." ? process.cwd() : fromFilename;
|
|
1065
1260
|
}
|
|
1066
|
-
generateFilePath() {
|
|
1067
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
1068
|
-
const dir = this.resolveDir();
|
|
1069
|
-
const ext = path.extname(this.config.filename);
|
|
1070
|
-
const base = path.basename(this.config.filename, ext);
|
|
1071
|
-
if (this.config.rotation) return path.join(dir, `${base}-${timestamp}${ext}`);
|
|
1072
|
-
return path.join(dir, `${base}${ext}`);
|
|
1073
|
-
}
|
|
1074
|
-
async ensureDirectoryExists() {
|
|
1075
|
-
const dir = this.resolveDir();
|
|
1076
|
-
try {
|
|
1077
|
-
await mkdir(dir, { recursive: true });
|
|
1078
|
-
} catch {}
|
|
1079
|
-
}
|
|
1080
1261
|
/**
|
|
1081
1262
|
* Gzip the rotated file to `<file>.gz` and remove the original. Streams the
|
|
1082
1263
|
* data so large log files don't have to be buffered in memory. Best-effort:
|
|
@@ -1095,33 +1276,42 @@ var FileTransport = class {
|
|
|
1095
1276
|
} catch {}
|
|
1096
1277
|
}
|
|
1097
1278
|
}
|
|
1279
|
+
/**
|
|
1280
|
+
* True for this transport's rotated files only: `<base>-<stamp><ext>` where the
|
|
1281
|
+
* stamp is digits and separators, optionally `.gz`. Another transport's
|
|
1282
|
+
* `app-error.log` must not count as one of `app.log`'s rotations.
|
|
1283
|
+
*/
|
|
1284
|
+
isOwnRotatedFile(file) {
|
|
1285
|
+
const name = file.endsWith(".gz") ? file.slice(0, -3) : file;
|
|
1286
|
+
if (!name.startsWith(`${this.base}-`) || !name.endsWith(this.ext)) return false;
|
|
1287
|
+
const stamp = name.slice(this.base.length + 1, name.length - this.ext.length);
|
|
1288
|
+
return /^\d[\d\-_.T]*$/.test(stamp);
|
|
1289
|
+
}
|
|
1098
1290
|
async cleanupOldFiles() {
|
|
1099
|
-
|
|
1100
|
-
if (!((_this$config$rotation4 = this.config.rotation) === null || _this$config$rotation4 === void 0 ? void 0 : _this$config$rotation4.maxFiles)) return;
|
|
1291
|
+
if (!this.maxFiles) return;
|
|
1101
1292
|
try {
|
|
1102
|
-
const
|
|
1103
|
-
const
|
|
1104
|
-
const
|
|
1105
|
-
|
|
1106
|
-
const isOwnRotatedFile = (file) => file.startsWith(`${base}-`) && (file.endsWith(ext) || file.endsWith(`${ext}.gz`));
|
|
1107
|
-
const logFiles = files.filter(isOwnRotatedFile).map(async (file) => {
|
|
1108
|
-
const filePath = path.join(dir, file);
|
|
1293
|
+
const files = await readdir(this.dir);
|
|
1294
|
+
const active = path$1.basename(this.currentFilePath);
|
|
1295
|
+
const newestFirst = (await Promise.all(files.filter((file) => file !== active && this.isOwnRotatedFile(file)).map(async (file) => {
|
|
1296
|
+
const filePath = path$1.join(this.dir, file);
|
|
1109
1297
|
return {
|
|
1110
1298
|
path: filePath,
|
|
1111
|
-
mtime: (await stat(filePath)).
|
|
1299
|
+
mtime: (await stat(filePath)).mtimeMs
|
|
1112
1300
|
};
|
|
1113
|
-
});
|
|
1114
|
-
const
|
|
1115
|
-
for (const file of filesToDelete) await unlink(file.path);
|
|
1301
|
+
}))).sort((a, b) => b.mtime - a.mtime || b.path.localeCompare(a.path));
|
|
1302
|
+
for (const file of newestFirst.slice(this.maxFiles)) await unlink(file.path);
|
|
1116
1303
|
} catch (error) {
|
|
1117
1304
|
internalError("Failed to cleanup old log files", error);
|
|
1118
1305
|
}
|
|
1119
1306
|
}
|
|
1120
1307
|
async close() {
|
|
1308
|
+
if (this.batchTimer) {
|
|
1309
|
+
clearTimeout(this.batchTimer);
|
|
1310
|
+
this.batchTimer = void 0;
|
|
1311
|
+
}
|
|
1121
1312
|
await this.flush();
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
});
|
|
1313
|
+
await this.housekeeping;
|
|
1314
|
+
this.reportDropped(0);
|
|
1125
1315
|
}
|
|
1126
1316
|
};
|
|
1127
1317
|
|
|
@@ -1146,7 +1336,6 @@ var GoogleAnalyticsTransport = class extends AnalyticsTransport {
|
|
|
1146
1336
|
try {
|
|
1147
1337
|
if (!this.gaConfig.measurementId) throw new Error("Google Analytics Measurement ID is required");
|
|
1148
1338
|
if (!this.gaConfig.apiSecret) throw new Error("Google Analytics API Secret is required");
|
|
1149
|
-
await this.testConnection();
|
|
1150
1339
|
this.isReady = true;
|
|
1151
1340
|
} catch (error) {
|
|
1152
1341
|
internalError("Google Analytics transport initialization failed", error);
|
|
@@ -1215,10 +1404,10 @@ var GoogleAnalyticsTransport = class extends AnalyticsTransport {
|
|
|
1215
1404
|
}
|
|
1216
1405
|
async sendEvents(events) {
|
|
1217
1406
|
const chunks = this.chunkArray(events, 25);
|
|
1218
|
-
for (const chunk of chunks) {
|
|
1407
|
+
for (const chunk$1 of chunks) {
|
|
1219
1408
|
const payload = {
|
|
1220
1409
|
client_id: this.gaConfig.clientId || this.generateClientId(),
|
|
1221
|
-
events: chunk
|
|
1410
|
+
events: chunk$1
|
|
1222
1411
|
};
|
|
1223
1412
|
await this.makeRequest(payload);
|
|
1224
1413
|
}
|
|
@@ -1233,7 +1422,7 @@ var GoogleAnalyticsTransport = class extends AnalyticsTransport {
|
|
|
1233
1422
|
const response = await fetch(`${url}?${params}`, {
|
|
1234
1423
|
method: "POST",
|
|
1235
1424
|
headers: { "Content-Type": "application/json" },
|
|
1236
|
-
body:
|
|
1425
|
+
body: stringifyForLog(payload)
|
|
1237
1426
|
});
|
|
1238
1427
|
if (!response.ok) {
|
|
1239
1428
|
const errorText = await response.text();
|
|
@@ -1247,22 +1436,6 @@ var GoogleAnalyticsTransport = class extends AnalyticsTransport {
|
|
|
1247
1436
|
throw new Error(`Failed to send data to Google Analytics: ${error.message}`, { cause: error });
|
|
1248
1437
|
}
|
|
1249
1438
|
}
|
|
1250
|
-
async testConnection() {
|
|
1251
|
-
const testEvent = {
|
|
1252
|
-
name: "logitron_test",
|
|
1253
|
-
params: {
|
|
1254
|
-
event_category: "Test",
|
|
1255
|
-
event_label: "Connection Test",
|
|
1256
|
-
test: true,
|
|
1257
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1258
|
-
}
|
|
1259
|
-
};
|
|
1260
|
-
const payload = {
|
|
1261
|
-
client_id: this.gaConfig.clientId || this.generateClientId(),
|
|
1262
|
-
events: [testEvent]
|
|
1263
|
-
};
|
|
1264
|
-
await this.makeRequest(payload, true);
|
|
1265
|
-
}
|
|
1266
1439
|
generateClientId() {
|
|
1267
1440
|
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
|
|
1268
1441
|
const r = Math.random() * 16 | 0;
|
|
@@ -1373,7 +1546,6 @@ var MixpanelTransport = class extends AnalyticsTransport {
|
|
|
1373
1546
|
async initialize() {
|
|
1374
1547
|
try {
|
|
1375
1548
|
if (!this.mixpanelConfig.token) throw new Error("Mixpanel token is required");
|
|
1376
|
-
await this.testConnection();
|
|
1377
1549
|
this.isReady = true;
|
|
1378
1550
|
} catch (error) {
|
|
1379
1551
|
internalError("Mixpanel transport initialization failed", error);
|
|
@@ -1432,7 +1604,7 @@ var MixpanelTransport = class extends AnalyticsTransport {
|
|
|
1432
1604
|
"Content-Type": "application/json",
|
|
1433
1605
|
Accept: "text/plain"
|
|
1434
1606
|
},
|
|
1435
|
-
body:
|
|
1607
|
+
body: stringifyForLog(data)
|
|
1436
1608
|
});
|
|
1437
1609
|
if (!response.ok) throw new Error(`Mixpanel API error: ${response.status} ${response.statusText}`);
|
|
1438
1610
|
const result = await response.text();
|
|
@@ -1441,18 +1613,6 @@ var MixpanelTransport = class extends AnalyticsTransport {
|
|
|
1441
1613
|
throw new Error(`Failed to send data to Mixpanel: ${error.message}`, { cause: error });
|
|
1442
1614
|
}
|
|
1443
1615
|
}
|
|
1444
|
-
async testConnection() {
|
|
1445
|
-
const testEvent = {
|
|
1446
|
-
event: "logitron_test",
|
|
1447
|
-
properties: {
|
|
1448
|
-
token: this.mixpanelConfig.token,
|
|
1449
|
-
distinct_id: "test_user",
|
|
1450
|
-
test: true,
|
|
1451
|
-
time: Date.now()
|
|
1452
|
-
}
|
|
1453
|
-
};
|
|
1454
|
-
await this.makeRequest("/track", [testEvent]);
|
|
1455
|
-
}
|
|
1456
1616
|
generateInsertId(entry) {
|
|
1457
1617
|
return `${entry.timestamp.getTime()}_${this.simpleHash(entry.message + (entry.traceId || ""))}`;
|
|
1458
1618
|
}
|
|
@@ -1499,6 +1659,17 @@ var MixpanelTransport = class extends AnalyticsTransport {
|
|
|
1499
1659
|
}
|
|
1500
1660
|
};
|
|
1501
1661
|
|
|
1662
|
+
//#endregion
|
|
1663
|
+
//#region src/version.ts
|
|
1664
|
+
/**
|
|
1665
|
+
* The published version of this package.
|
|
1666
|
+
*
|
|
1667
|
+
* Kept in sync with `version` in package.json by the release process. Importing
|
|
1668
|
+
* from here (instead of reading package.json at runtime) keeps the built `dist`
|
|
1669
|
+
* free of a JSON import that bundlers and consumers would have to resolve.
|
|
1670
|
+
*/
|
|
1671
|
+
const LOGIXIA_VERSION = "1.12.0";
|
|
1672
|
+
|
|
1502
1673
|
//#endregion
|
|
1503
1674
|
//#region src/transports/segment.transport.ts
|
|
1504
1675
|
/**
|
|
@@ -1522,7 +1693,6 @@ var SegmentTransport = class extends AnalyticsTransport {
|
|
|
1522
1693
|
async initialize() {
|
|
1523
1694
|
try {
|
|
1524
1695
|
if (!this.segmentConfig.writeKey) throw new Error("Segment Write Key is required");
|
|
1525
|
-
await this.testConnection();
|
|
1526
1696
|
this.isReady = true;
|
|
1527
1697
|
} catch (error) {
|
|
1528
1698
|
internalError("Segment transport initialization failed", error);
|
|
@@ -1563,7 +1733,7 @@ var SegmentTransport = class extends AnalyticsTransport {
|
|
|
1563
1733
|
context: {
|
|
1564
1734
|
library: {
|
|
1565
1735
|
name: "logixia",
|
|
1566
|
-
version:
|
|
1736
|
+
version: LOGIXIA_VERSION
|
|
1567
1737
|
},
|
|
1568
1738
|
app: {
|
|
1569
1739
|
name: entry.appName || "unknown",
|
|
@@ -1583,8 +1753,8 @@ var SegmentTransport = class extends AnalyticsTransport {
|
|
|
1583
1753
|
}
|
|
1584
1754
|
async batchTrack(events) {
|
|
1585
1755
|
const chunks = this.chunkArray(events, this.segmentConfig.maxBatchSize || 100);
|
|
1586
|
-
for (const chunk of chunks) {
|
|
1587
|
-
const payload = { batch: chunk };
|
|
1756
|
+
for (const chunk$1 of chunks) {
|
|
1757
|
+
const payload = { batch: chunk$1 };
|
|
1588
1758
|
await this.makeRequest("/v1/batch", payload);
|
|
1589
1759
|
}
|
|
1590
1760
|
}
|
|
@@ -1597,9 +1767,9 @@ var SegmentTransport = class extends AnalyticsTransport {
|
|
|
1597
1767
|
headers: {
|
|
1598
1768
|
"Content-Type": "application/json",
|
|
1599
1769
|
Authorization: `Basic ${auth}`,
|
|
1600
|
-
"User-Agent":
|
|
1770
|
+
"User-Agent": `logixia/${LOGIXIA_VERSION}`
|
|
1601
1771
|
},
|
|
1602
|
-
body:
|
|
1772
|
+
body: stringifyForLog(data)
|
|
1603
1773
|
});
|
|
1604
1774
|
if (!response.ok) {
|
|
1605
1775
|
const errorText = await response.text();
|
|
@@ -1609,24 +1779,6 @@ var SegmentTransport = class extends AnalyticsTransport {
|
|
|
1609
1779
|
throw new Error(`Failed to send data to Segment: ${error.message}`, { cause: error });
|
|
1610
1780
|
}
|
|
1611
1781
|
}
|
|
1612
|
-
async testConnection() {
|
|
1613
|
-
const testEvent = {
|
|
1614
|
-
type: "track",
|
|
1615
|
-
event: "Logixia Test",
|
|
1616
|
-
userId: "test-user",
|
|
1617
|
-
anonymousId: this.generateAnonymousId(),
|
|
1618
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1619
|
-
properties: {
|
|
1620
|
-
test: true,
|
|
1621
|
-
source: "logixia"
|
|
1622
|
-
},
|
|
1623
|
-
context: { library: {
|
|
1624
|
-
name: "logixia",
|
|
1625
|
-
version: "1.0.0"
|
|
1626
|
-
} }
|
|
1627
|
-
};
|
|
1628
|
-
await this.makeRequest("/v1/track", testEvent);
|
|
1629
|
-
}
|
|
1630
1782
|
extractUserId(entry) {
|
|
1631
1783
|
if (entry.data) {
|
|
1632
1784
|
const d = entry.data;
|
|
@@ -1656,8 +1808,8 @@ var SegmentTransport = class extends AnalyticsTransport {
|
|
|
1656
1808
|
...user.traits
|
|
1657
1809
|
},
|
|
1658
1810
|
context: { library: {
|
|
1659
|
-
name: "
|
|
1660
|
-
version:
|
|
1811
|
+
name: "logixia",
|
|
1812
|
+
version: LOGIXIA_VERSION
|
|
1661
1813
|
} }
|
|
1662
1814
|
};
|
|
1663
1815
|
await this.makeRequest("/v1/identify", payload);
|
|
@@ -1675,8 +1827,8 @@ var SegmentTransport = class extends AnalyticsTransport {
|
|
|
1675
1827
|
},
|
|
1676
1828
|
context: {
|
|
1677
1829
|
library: {
|
|
1678
|
-
name: "
|
|
1679
|
-
version:
|
|
1830
|
+
name: "logixia",
|
|
1831
|
+
version: LOGIXIA_VERSION
|
|
1680
1832
|
},
|
|
1681
1833
|
page: {
|
|
1682
1834
|
url: properties.url || `/${name.toLowerCase().replace(/\s+/g, "-")}`,
|
|
@@ -1695,8 +1847,8 @@ var SegmentTransport = class extends AnalyticsTransport {
|
|
|
1695
1847
|
properties: { ...properties },
|
|
1696
1848
|
context: {
|
|
1697
1849
|
library: {
|
|
1698
|
-
name: "
|
|
1699
|
-
version:
|
|
1850
|
+
name: "logixia",
|
|
1851
|
+
version: LOGIXIA_VERSION
|
|
1700
1852
|
},
|
|
1701
1853
|
screen: { name }
|
|
1702
1854
|
}
|
|
@@ -1712,8 +1864,8 @@ var SegmentTransport = class extends AnalyticsTransport {
|
|
|
1712
1864
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1713
1865
|
traits,
|
|
1714
1866
|
context: { library: {
|
|
1715
|
-
name: "
|
|
1716
|
-
version:
|
|
1867
|
+
name: "logixia",
|
|
1868
|
+
version: LOGIXIA_VERSION
|
|
1717
1869
|
} }
|
|
1718
1870
|
};
|
|
1719
1871
|
await this.makeRequest("/v1/group", payload);
|
|
@@ -1725,8 +1877,8 @@ var SegmentTransport = class extends AnalyticsTransport {
|
|
|
1725
1877
|
previousId,
|
|
1726
1878
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1727
1879
|
context: { library: {
|
|
1728
|
-
name: "
|
|
1729
|
-
version:
|
|
1880
|
+
name: "logixia",
|
|
1881
|
+
version: LOGIXIA_VERSION
|
|
1730
1882
|
} }
|
|
1731
1883
|
};
|
|
1732
1884
|
await this.makeRequest("/v1/alias", payload);
|
|
@@ -1740,8 +1892,8 @@ var SegmentTransport = class extends AnalyticsTransport {
|
|
|
1740
1892
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1741
1893
|
properties,
|
|
1742
1894
|
context: { library: {
|
|
1743
|
-
name: "
|
|
1744
|
-
version:
|
|
1895
|
+
name: "logixia",
|
|
1896
|
+
version: LOGIXIA_VERSION
|
|
1745
1897
|
} }
|
|
1746
1898
|
};
|
|
1747
1899
|
await this.makeRequest("/v1/track", payload);
|
|
@@ -1759,8 +1911,8 @@ var SegmentTransport = class extends AnalyticsTransport {
|
|
|
1759
1911
|
products
|
|
1760
1912
|
},
|
|
1761
1913
|
context: { library: {
|
|
1762
|
-
name: "
|
|
1763
|
-
version:
|
|
1914
|
+
name: "logixia",
|
|
1915
|
+
version: LOGIXIA_VERSION
|
|
1764
1916
|
} }
|
|
1765
1917
|
};
|
|
1766
1918
|
await this.makeRequest("/v1/track", payload);
|
|
@@ -1773,8 +1925,8 @@ var SegmentTransport = class extends AnalyticsTransport {
|
|
|
1773
1925
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1774
1926
|
properties: product,
|
|
1775
1927
|
context: { library: {
|
|
1776
|
-
name: "
|
|
1777
|
-
version:
|
|
1928
|
+
name: "logixia",
|
|
1929
|
+
version: LOGIXIA_VERSION
|
|
1778
1930
|
} }
|
|
1779
1931
|
};
|
|
1780
1932
|
await this.makeRequest("/v1/track", payload);
|
|
@@ -1793,7 +1945,7 @@ var TransportManager = class extends EventEmitter {
|
|
|
1793
1945
|
this.metrics = /* @__PURE__ */ new Map();
|
|
1794
1946
|
this.isShuttingDown = false;
|
|
1795
1947
|
this.transportLevelPreferences = /* @__PURE__ */ new Map();
|
|
1796
|
-
this.
|
|
1948
|
+
this.warnedPromptDeprecated = false;
|
|
1797
1949
|
this.setupTransports(config);
|
|
1798
1950
|
}
|
|
1799
1951
|
setupTransports(config) {
|
|
@@ -1893,17 +2045,24 @@ var TransportManager = class extends EventEmitter {
|
|
|
1893
2045
|
if (entry.context !== void 0) transportEntry.context = entry.context;
|
|
1894
2046
|
if (entry.traceId !== void 0) transportEntry.traceId = entry.traceId;
|
|
1895
2047
|
if (entry.environment !== void 0) transportEntry.environment = entry.environment;
|
|
1896
|
-
if (this.promptForLevels) return this._writeAsyncWithPrompt(transportEntry);
|
|
1897
2048
|
let pending;
|
|
2049
|
+
let syncFailure;
|
|
1898
2050
|
for (const [id, transport] of this.transports) {
|
|
1899
2051
|
if (!this.shouldTransportHandle(transport, transportEntry.level, id)) continue;
|
|
1900
2052
|
if (transport.filter && !transport.filter(transportEntry)) continue;
|
|
1901
|
-
|
|
2053
|
+
let result;
|
|
2054
|
+
try {
|
|
2055
|
+
result = this.writeToTransport(id, transport, transportEntry);
|
|
2056
|
+
} catch (error) {
|
|
2057
|
+
syncFailure ??= { error };
|
|
2058
|
+
continue;
|
|
2059
|
+
}
|
|
1902
2060
|
if (result && typeof result.then === "function") {
|
|
1903
2061
|
if (pending === void 0) pending = [];
|
|
1904
2062
|
pending.push(result);
|
|
1905
2063
|
}
|
|
1906
2064
|
}
|
|
2065
|
+
if (syncFailure) throw syncFailure.error;
|
|
1907
2066
|
if (pending === void 0) return;
|
|
1908
2067
|
return Promise.allSettled(pending).then((results) => {
|
|
1909
2068
|
const failures = results.filter((r) => r.status === "rejected");
|
|
@@ -1913,22 +2072,6 @@ var TransportManager = class extends EventEmitter {
|
|
|
1913
2072
|
}
|
|
1914
2073
|
});
|
|
1915
2074
|
}
|
|
1916
|
-
/** Slow path used only when interactive level-prompting is enabled. */
|
|
1917
|
-
async _writeAsyncWithPrompt(transportEntry) {
|
|
1918
|
-
const writePromises = [];
|
|
1919
|
-
for (const [id, transport] of this.transports) {
|
|
1920
|
-
if (!this.transportLevelPreferences.has(id)) await this.configureTransportLevels(id);
|
|
1921
|
-
if (!this.shouldTransportHandle(transport, transportEntry.level, id)) continue;
|
|
1922
|
-
if (transport.filter && !transport.filter(transportEntry)) continue;
|
|
1923
|
-
const result = this.writeToTransport(id, transport, transportEntry);
|
|
1924
|
-
if (result && typeof result.then === "function") writePromises.push(result);
|
|
1925
|
-
}
|
|
1926
|
-
const failures = (await Promise.allSettled(writePromises)).filter((r) => r.status === "rejected");
|
|
1927
|
-
if (failures.length > 0) {
|
|
1928
|
-
const errors = failures.map((f) => f.reason);
|
|
1929
|
-
this.emit("error", /* @__PURE__ */ new Error(`Transport write failures: ${errors.join(", ")}`), "multiple");
|
|
1930
|
-
}
|
|
1931
|
-
}
|
|
1932
2075
|
writeToTransport(id, transport, entry) {
|
|
1933
2076
|
const startTime = Date.now();
|
|
1934
2077
|
const metrics = this.metrics.get(id);
|
|
@@ -2115,52 +2258,18 @@ var TransportManager = class extends EventEmitter {
|
|
|
2115
2258
|
details
|
|
2116
2259
|
};
|
|
2117
2260
|
}
|
|
2261
|
+
/** @deprecated Library code never prompts on stdin. Use setTransportLevels(). No-op. */
|
|
2118
2262
|
enableLevelPrompting() {
|
|
2119
|
-
this.
|
|
2120
|
-
internalLog("Transport level prompting enabled");
|
|
2263
|
+
this.warnPromptingRemoved();
|
|
2121
2264
|
}
|
|
2265
|
+
/** @deprecated Library code never prompts on stdin. No-op. */
|
|
2122
2266
|
disableLevelPrompting() {
|
|
2123
|
-
this.
|
|
2124
|
-
internalLog("Transport level prompting disabled");
|
|
2267
|
+
this.warnPromptingRemoved();
|
|
2125
2268
|
}
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
"info",
|
|
2131
|
-
"debug",
|
|
2132
|
-
"trace",
|
|
2133
|
-
"verbose"
|
|
2134
|
-
];
|
|
2135
|
-
return new Promise((resolve) => {
|
|
2136
|
-
const rl = readline.createInterface({
|
|
2137
|
-
input: process.stdin,
|
|
2138
|
-
output: process.stdout
|
|
2139
|
-
});
|
|
2140
|
-
process.stdout.write(`\nConfigure log levels for transport '${transportId}'\n`);
|
|
2141
|
-
process.stdout.write(`Available levels: ${availableLevels.join(", ")}\n`);
|
|
2142
|
-
process.stdout.write("Enter levels separated by commas (e.g., error,warn,info) or \"all\" for all levels:\n");
|
|
2143
|
-
rl.question("> ", (answer) => {
|
|
2144
|
-
rl.close();
|
|
2145
|
-
if (answer.toLowerCase().trim() === "all") resolve(availableLevels);
|
|
2146
|
-
else {
|
|
2147
|
-
const selectedLevels = answer.split(",").map((level) => level.trim().toLowerCase()).filter((level) => availableLevels.includes(level));
|
|
2148
|
-
if (selectedLevels.length === 0) {
|
|
2149
|
-
internalWarn("No valid levels selected, using all levels");
|
|
2150
|
-
resolve(availableLevels);
|
|
2151
|
-
} else {
|
|
2152
|
-
internalLog(`Selected levels for ${transportId}: ${selectedLevels.join(", ")}`);
|
|
2153
|
-
resolve(selectedLevels);
|
|
2154
|
-
}
|
|
2155
|
-
}
|
|
2156
|
-
});
|
|
2157
|
-
});
|
|
2158
|
-
}
|
|
2159
|
-
async configureTransportLevels(transportId) {
|
|
2160
|
-
if (this.promptForLevels && !this.transportLevelPreferences.has(transportId)) {
|
|
2161
|
-
const selectedLevels = await this.promptUserForTransportLevels(transportId);
|
|
2162
|
-
this.transportLevelPreferences.set(transportId, selectedLevels);
|
|
2163
|
-
}
|
|
2269
|
+
warnPromptingRemoved() {
|
|
2270
|
+
if (this.warnedPromptDeprecated) return;
|
|
2271
|
+
this.warnedPromptDeprecated = true;
|
|
2272
|
+
internalWarn("Transport level prompting was removed: logixia never reads stdin. Use setTransportLevels(id, levels) instead.");
|
|
2164
2273
|
}
|
|
2165
2274
|
setTransportLevels(transportId, levels) {
|
|
2166
2275
|
this.transportLevelPreferences.set(transportId, levels);
|
|
@@ -2176,5 +2285,5 @@ var TransportManager = class extends EventEmitter {
|
|
|
2176
2285
|
};
|
|
2177
2286
|
|
|
2178
2287
|
//#endregion
|
|
2179
|
-
export { FileTransport as a, DatabaseTransport as c,
|
|
2180
|
-
//# sourceMappingURL=transport.manager-
|
|
2288
|
+
export { FileTransport as a, DatabaseTransport as c, GoogleAnalyticsTransport as i, ConsoleTransport as l, SegmentTransport as n, DataDogTransport as o, MixpanelTransport as r, AnalyticsTransport as s, TransportManager as t };
|
|
2289
|
+
//# sourceMappingURL=transport.manager-D6KL3t9V.mjs.map
|