logixia 1.13.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. package/README.md +81 -16
  2. package/dist/{async-context-CwBNMm2E.mjs → async-context-BzJrbfQs.mjs} +8 -4
  3. package/dist/async-context-BzJrbfQs.mjs.map +1 -0
  4. package/dist/{async-context-CSHWoHMB.js → async-context-DyQQYq3I.js} +8 -4
  5. package/dist/async-context-DyQQYq3I.js.map +1 -0
  6. package/dist/{azure-monitor.transport-CG7ClkYj.d.ts → azure-monitor.transport-Cn_3Usvu.d.ts} +2 -2
  7. package/dist/{azure-monitor.transport-CG7ClkYj.d.ts.map → azure-monitor.transport-Cn_3Usvu.d.ts.map} +1 -1
  8. package/dist/{azure-monitor.transport-Cm74kXZS.d.mts → azure-monitor.transport-D7Da4RAb.d.mts} +2 -2
  9. package/dist/{azure-monitor.transport-Cm74kXZS.d.mts.map → azure-monitor.transport-D7Da4RAb.d.mts.map} +1 -1
  10. package/dist/azure.d.mts +2 -2
  11. package/dist/azure.d.ts +2 -2
  12. package/dist/browser.d.mts +9 -0
  13. package/dist/browser.d.mts.map +1 -1
  14. package/dist/browser.d.ts +9 -0
  15. package/dist/browser.d.ts.map +1 -1
  16. package/dist/browser.js +43 -12
  17. package/dist/browser.js.map +1 -1
  18. package/dist/browser.mjs +43 -12
  19. package/dist/browser.mjs.map +1 -1
  20. package/dist/{logitron-logger.module-Bs3XYZgF.mjs → builder-0gEzRopq.mjs} +383 -1021
  21. package/dist/builder-0gEzRopq.mjs.map +1 -0
  22. package/dist/{logitron-logger.module-BsDZR5BH.js → builder-D44QktIo.js} +411 -1155
  23. package/dist/builder-D44QktIo.js.map +1 -0
  24. package/dist/cli/index.js +130 -65
  25. package/dist/cloudwatch.d.mts +2 -2
  26. package/dist/cloudwatch.d.ts +2 -2
  27. package/dist/cloudwatch.js +2 -1
  28. package/dist/cloudwatch.mjs +2 -1
  29. package/dist/{cloudwatch.transport-pNBNWJLt.js → cloudwatch.transport-BO1ahxw-.js} +26 -19
  30. package/dist/cloudwatch.transport-BO1ahxw-.js.map +1 -0
  31. package/dist/{cloudwatch.transport-yBwm4OeO.mjs → cloudwatch.transport-DZ6ies7g.mjs} +26 -19
  32. package/dist/cloudwatch.transport-DZ6ies7g.mjs.map +1 -0
  33. package/dist/{cloudwatch.transport-CGHY4buE.d.mts → cloudwatch.transport-DsEq633o.d.ts} +9 -2
  34. package/dist/cloudwatch.transport-DsEq633o.d.ts.map +1 -0
  35. package/dist/{cloudwatch.transport-DK_8cKd3.d.ts → cloudwatch.transport-UlyZFWMn.d.mts} +9 -2
  36. package/dist/cloudwatch.transport-UlyZFWMn.d.mts.map +1 -0
  37. package/dist/{coerce.utils-BN980Y9V.js → coerce.utils-B-LilukN.js} +1 -1
  38. package/dist/{coerce.utils-BN980Y9V.js.map → coerce.utils-B-LilukN.js.map} +1 -1
  39. package/dist/{coerce.utils-Dxv4_ikC.mjs → coerce.utils-Cu6IUTgX.mjs} +1 -1
  40. package/dist/{coerce.utils-Dxv4_ikC.mjs.map → coerce.utils-Cu6IUTgX.mjs.map} +1 -1
  41. package/dist/correlation.d.mts +24 -6
  42. package/dist/correlation.d.mts.map +1 -1
  43. package/dist/correlation.d.ts +24 -6
  44. package/dist/correlation.d.ts.map +1 -1
  45. package/dist/correlation.js +41 -16
  46. package/dist/correlation.js.map +1 -1
  47. package/dist/correlation.mjs +41 -16
  48. package/dist/correlation.mjs.map +1 -1
  49. package/dist/entry-fields-BJkuPnIg.mjs +30 -0
  50. package/dist/entry-fields-BJkuPnIg.mjs.map +1 -0
  51. package/dist/entry-fields-BvOLFlJ4.js +36 -0
  52. package/dist/entry-fields-BvOLFlJ4.js.map +1 -0
  53. package/dist/gcp.d.mts +2 -2
  54. package/dist/gcp.d.ts +2 -2
  55. package/dist/gcp.js +2 -1
  56. package/dist/gcp.mjs +2 -1
  57. package/dist/{gcp.transport-BOhkVKlI.d.ts → gcp.transport-B7oc97sG.d.ts} +2 -2
  58. package/dist/gcp.transport-B7oc97sG.d.ts.map +1 -0
  59. package/dist/{gcp.transport-SHdtc1n0.js → gcp.transport-CHV8MGgz.js} +5 -5
  60. package/dist/gcp.transport-CHV8MGgz.js.map +1 -0
  61. package/dist/{gcp.transport-BJuNRFe_.mjs → gcp.transport-DBX5TPIy.mjs} +5 -5
  62. package/dist/gcp.transport-DBX5TPIy.mjs.map +1 -0
  63. package/dist/{gcp.transport-Bi6CUqZR.d.mts → gcp.transport-GrJurKo8.d.mts} +2 -2
  64. package/dist/gcp.transport-GrJurKo8.d.mts.map +1 -0
  65. package/dist/{index-BPKwwERo.d.mts → index-3mw5gaCw.d.mts} +2 -2
  66. package/dist/{index-BPKwwERo.d.mts.map → index-3mw5gaCw.d.mts.map} +1 -1
  67. package/dist/{index-C1X-TE5I.d.ts → index-DhIoploR.d.ts} +2 -2
  68. package/dist/{index-C1X-TE5I.d.ts.map → index-DhIoploR.d.ts.map} +1 -1
  69. package/dist/index.d.mts +99 -137
  70. package/dist/index.d.mts.map +1 -1
  71. package/dist/index.d.ts +99 -137
  72. package/dist/index.d.ts.map +1 -1
  73. package/dist/index.js +146 -510
  74. package/dist/index.js.map +1 -1
  75. package/dist/index.mjs +99 -431
  76. package/dist/index.mjs.map +1 -1
  77. package/dist/logitron-logger-BFDSO8Io.d.mts +257 -0
  78. package/dist/logitron-logger-BFDSO8Io.d.mts.map +1 -0
  79. package/dist/logitron-logger-UVeN-mQm.d.ts +257 -0
  80. package/dist/logitron-logger-UVeN-mQm.d.ts.map +1 -0
  81. package/dist/middleware.d.mts +11 -4
  82. package/dist/middleware.d.mts.map +1 -1
  83. package/dist/middleware.d.ts +11 -4
  84. package/dist/middleware.d.ts.map +1 -1
  85. package/dist/middleware.js +148 -24
  86. package/dist/middleware.js.map +1 -1
  87. package/dist/middleware.mjs +148 -24
  88. package/dist/middleware.mjs.map +1 -1
  89. package/dist/nest.d.mts +481 -6
  90. package/dist/nest.d.mts.map +1 -1
  91. package/dist/nest.d.ts +481 -6
  92. package/dist/nest.d.ts.map +1 -1
  93. package/dist/nest.js +884 -13
  94. package/dist/nest.js.map +1 -0
  95. package/dist/nest.mjs +863 -6
  96. package/dist/nest.mjs.map +1 -0
  97. package/dist/{search-CFQRISUv.mjs → search-BFbu_oVn.mjs} +35 -12
  98. package/dist/search-BFbu_oVn.mjs.map +1 -0
  99. package/dist/{search-njTI2hdo.js → search-C4V0OMEn.js} +35 -12
  100. package/dist/search-C4V0OMEn.js.map +1 -0
  101. package/dist/{search-DujhR94O.d.ts → search-CNh9uVwR.d.ts} +2 -3
  102. package/dist/search-CNh9uVwR.d.ts.map +1 -0
  103. package/dist/{search-CQOo-Iq6.d.mts → search-DXANsQ2n.d.mts} +2 -3
  104. package/dist/search-DXANsQ2n.d.mts.map +1 -0
  105. package/dist/search.d.mts +3 -3
  106. package/dist/search.d.ts +3 -3
  107. package/dist/search.js +2 -2
  108. package/dist/search.mjs +2 -2
  109. package/dist/slack.transport-BZzoXDPn.d.mts +112 -0
  110. package/dist/slack.transport-BZzoXDPn.d.mts.map +1 -0
  111. package/dist/slack.transport-BfJRouB2.js +654 -0
  112. package/dist/slack.transport-BfJRouB2.js.map +1 -0
  113. package/dist/slack.transport-Bzw7sucI.mjs +643 -0
  114. package/dist/slack.transport-Bzw7sucI.mjs.map +1 -0
  115. package/dist/slack.transport-CXjQYOes.d.ts +112 -0
  116. package/dist/slack.transport-CXjQYOes.d.ts.map +1 -0
  117. package/dist/testing.d.mts +2 -2
  118. package/dist/testing.d.ts +2 -2
  119. package/dist/trace.utils-CvXgaiQT.mjs +278 -0
  120. package/dist/trace.utils-CvXgaiQT.mjs.map +1 -0
  121. package/dist/trace.utils-DpBOLDtq.js +358 -0
  122. package/dist/trace.utils-DpBOLDtq.js.map +1 -0
  123. package/dist/{transport.manager-D6KL3t9V.mjs → transport.manager-BiuCAJd2.mjs} +526 -160
  124. package/dist/transport.manager-BiuCAJd2.mjs.map +1 -0
  125. package/dist/{transport.manager-BivyUTtU.js → transport.manager-CXbKKdVE.js} +537 -159
  126. package/dist/transport.manager-CXbKKdVE.js.map +1 -0
  127. package/dist/{transport.types-B5vcyv2k.d.mts → transport.types-BMnurPcc.d.mts} +23 -3
  128. package/dist/transport.types-BMnurPcc.d.mts.map +1 -0
  129. package/dist/{transport.types-BuY84TwO.d.ts → transport.types-CGoHpWQR.d.ts} +23 -3
  130. package/dist/transport.types-CGoHpWQR.d.ts.map +1 -0
  131. package/dist/transports.d.mts +50 -20
  132. package/dist/transports.d.mts.map +1 -1
  133. package/dist/transports.d.ts +50 -20
  134. package/dist/transports.d.ts.map +1 -1
  135. package/dist/transports.js +9 -5
  136. package/dist/transports.mjs +8 -6
  137. package/dist/worker-entry.js +307 -55
  138. package/dist/worker.d.mts +2 -2
  139. package/dist/worker.d.ts +2 -2
  140. package/dist/worker.js +1 -1
  141. package/dist/worker.mjs +1 -1
  142. package/dist/{worker.transport-CfH6z37Z.js → worker.transport-BdPxu3PN.js} +1 -1
  143. package/dist/{worker.transport-CfH6z37Z.js.map → worker.transport-BdPxu3PN.js.map} +1 -1
  144. package/dist/{worker.transport-mKXgNK0L.d.ts → worker.transport-CdzSZJmw.d.ts} +2 -2
  145. package/dist/{worker.transport-mKXgNK0L.d.ts.map → worker.transport-CdzSZJmw.d.ts.map} +1 -1
  146. package/dist/{worker.transport-BP8Q28Z2.d.mts → worker.transport-JvGPog3C.d.mts} +2 -2
  147. package/dist/{worker.transport-BP8Q28Z2.d.mts.map → worker.transport-JvGPog3C.d.mts.map} +1 -1
  148. package/dist/{worker.transport-BhMyvqQ2.mjs → worker.transport-lPxO2MVL.mjs} +1 -1
  149. package/dist/{worker.transport-BhMyvqQ2.mjs.map → worker.transport-lPxO2MVL.mjs.map} +1 -1
  150. package/package.json +27 -14
  151. package/dist/.tsbuildinfo +0 -1
  152. package/dist/async-context-CSHWoHMB.js.map +0 -1
  153. package/dist/async-context-CwBNMm2E.mjs.map +0 -1
  154. package/dist/cloudwatch.transport-CGHY4buE.d.mts.map +0 -1
  155. package/dist/cloudwatch.transport-DK_8cKd3.d.ts.map +0 -1
  156. package/dist/cloudwatch.transport-pNBNWJLt.js.map +0 -1
  157. package/dist/cloudwatch.transport-yBwm4OeO.mjs.map +0 -1
  158. package/dist/gcp.transport-BJuNRFe_.mjs.map +0 -1
  159. package/dist/gcp.transport-BOhkVKlI.d.ts.map +0 -1
  160. package/dist/gcp.transport-Bi6CUqZR.d.mts.map +0 -1
  161. package/dist/gcp.transport-SHdtc1n0.js.map +0 -1
  162. package/dist/logitron-logger.module-BdG1Jnpk.d.mts +0 -618
  163. package/dist/logitron-logger.module-BdG1Jnpk.d.mts.map +0 -1
  164. package/dist/logitron-logger.module-Bs3XYZgF.mjs.map +0 -1
  165. package/dist/logitron-logger.module-BsDZR5BH.js.map +0 -1
  166. package/dist/logitron-logger.module-CbXy2gfW.d.ts +0 -618
  167. package/dist/logitron-logger.module-CbXy2gfW.d.ts.map +0 -1
  168. package/dist/search-CFQRISUv.mjs.map +0 -1
  169. package/dist/search-CQOo-Iq6.d.mts.map +0 -1
  170. package/dist/search-DujhR94O.d.ts.map +0 -1
  171. package/dist/search-njTI2hdo.js.map +0 -1
  172. package/dist/transport.manager-BivyUTtU.js.map +0 -1
  173. package/dist/transport.manager-D6KL3t9V.mjs.map +0 -1
  174. package/dist/transport.types-B5vcyv2k.d.mts.map +0 -1
  175. package/dist/transport.types-BuY84TwO.d.ts.map +0 -1
  176. package/scripts/postinstall.js +0 -61
@@ -1,20 +1,39 @@
1
1
  const require_chunk = require('./chunk-nOFOJqeH.js');
2
2
  const require_internal_log = require('./internal-log-BUJYMZbE.js');
3
3
  const require_safe_stringify = require('./safe-stringify-DWo3aZp_.js');
4
- const require_coerce_utils = require('./coerce.utils-BN980Y9V.js');
4
+ const require_entry_fields = require('./entry-fields-BvOLFlJ4.js');
5
+ const require_coerce_utils = require('./coerce.utils-B-LilukN.js');
5
6
  let node_fs = require("node:fs");
6
7
  node_fs = require_chunk.__toESM(node_fs);
7
8
  let node_path = require("node:path");
8
9
  node_path = require_chunk.__toESM(node_path);
9
10
  let node_events = require("node:events");
10
11
  node_events = require_chunk.__toESM(node_events);
11
- let node_stream_promises = require("node:stream/promises");
12
- node_stream_promises = require_chunk.__toESM(node_stream_promises);
13
12
  let node_util = require("node:util");
14
13
  node_util = require_chunk.__toESM(node_util);
15
14
  let node_zlib = require("node:zlib");
16
15
  node_zlib = require_chunk.__toESM(node_zlib);
16
+ let node_stream_promises = require("node:stream/promises");
17
+ node_stream_promises = require_chunk.__toESM(node_stream_promises);
18
+
19
+ //#region src/utils/sanitize.utils.ts
20
+ /**
21
+ * Control-character stripping for text-mode log output.
22
+ *
23
+ * Line breaks become the visible escapes `\\n` / `\\r`; other C0 characters
24
+ * (except tab), DEL and C1 characters are removed, so user-supplied text cannot
25
+ * inject ANSI escapes, overwrite lines, or forge extra log entries.
26
+ */
27
+ const CONTROL_CHARS_RE = /[\x00-\x08\x0A-\x1F\x7F-\x9F]/g;
28
+ const VISIBLE_ESCAPES = {
29
+ "\n": "\\n",
30
+ "\r": "\\r"
31
+ };
32
+ function stripControlChars(value) {
33
+ return value.replace(CONTROL_CHARS_RE, (ch) => VISIBLE_ESCAPES[ch] ?? "");
34
+ }
17
35
 
36
+ //#endregion
18
37
  //#region src/transports/console.transport.ts
19
38
  /**
20
39
  * Writes log entries to stdout (info/debug/verbose) and stderr (error/warn).
@@ -45,9 +64,8 @@ var ConsoleTransport = class ConsoleTransport {
45
64
  ["brightwhite", "\x1B[97m"],
46
65
  ["reset", "\x1B[0m"]
47
66
  ]);
48
- static #_2 = this.CONTROL_CHARS_RE = /[\x00-\x08\x0B-\x1F\x7F-\x9F]/g;
49
67
  static sanitize(value) {
50
- return require_coerce_utils.safeToString(value).replace(ConsoleTransport.CONTROL_CHARS_RE, "");
68
+ return stripControlChars(require_coerce_utils.safeToString(value));
51
69
  }
52
70
  constructor(config = {}) {
53
71
  this.config = config;
@@ -60,16 +78,15 @@ var ConsoleTransport = class ConsoleTransport {
60
78
  (lvl === "error" || lvl === "warn" ? process.stderr : process.stdout).write(formatted);
61
79
  }
62
80
  formatEntry(entry) {
63
- if (this.config.format === "json") return require_safe_stringify.stringifyForLog({
81
+ if (this.config.format === "json") return require_safe_stringify.stringifyForLog(require_entry_fields.mergeCoreFields({
64
82
  timestamp: this.config.timestamp !== false ? entry.timestamp.toISOString() : void 0,
65
83
  level: entry.level,
66
84
  message: entry.message,
67
- ...entry.data || {},
68
85
  context: entry.context,
69
86
  traceId: entry.traceId,
70
87
  appName: entry.appName,
71
88
  environment: entry.environment
72
- });
89
+ }, entry.data));
73
90
  const parts = [];
74
91
  if (this.config.timestamp !== false) {
75
92
  const timestamp = entry.timestamp.toISOString();
@@ -117,6 +134,47 @@ var ConsoleTransport = class ConsoleTransport {
117
134
  }
118
135
  };
119
136
 
137
+ //#endregion
138
+ //#region src/transports/buffer-cap.ts
139
+ const DEFAULT_BUFFER_CAP = 1e4;
140
+ const DROP_WARN_INTERVAL_MS$1 = 5e3;
141
+ /**
142
+ * Keeps an in-memory retry buffer bounded. Past `max` the oldest items are
143
+ * dropped, counted, and reported through internalWarn at most every few seconds.
144
+ *
145
+ * `requested` must be an integer of at least 1, otherwise the default is used.
146
+ * A cap below `batchSize` is raised to `batchSize` so one batch always fits.
147
+ */
148
+ var BufferCap = class {
149
+ constructor(name, requested, batchSize, fallback = DEFAULT_BUFFER_CAP) {
150
+ this.name = name;
151
+ this.dropped = 0;
152
+ this.lastWarning = 0;
153
+ let max = fallback;
154
+ if (requested !== void 0) if (Number.isInteger(requested) && requested >= 1) max = requested;
155
+ else require_internal_log.internalWarn(`${name}: maxBufferSize ${String(requested)} is not a positive integer; using ${fallback}`);
156
+ this.max = Math.max(max, batchSize);
157
+ }
158
+ /** Drops the oldest items beyond the cap, in place. */
159
+ enforce(items) {
160
+ const excess = items.length - this.max;
161
+ if (excess <= 0) return;
162
+ items.splice(0, excess);
163
+ this.note(excess);
164
+ }
165
+ note(count) {
166
+ this.dropped += count;
167
+ this.report(DROP_WARN_INTERVAL_MS$1);
168
+ }
169
+ report(minGapMs) {
170
+ const now = Date.now();
171
+ if (this.dropped === 0 || now - this.lastWarning < minGapMs) return;
172
+ require_internal_log.internalWarn(`${this.name}: buffer full (${this.max}), dropped ${this.dropped} oldest entr${this.dropped === 1 ? "y" : "ies"}`);
173
+ this.dropped = 0;
174
+ this.lastWarning = now;
175
+ }
176
+ };
177
+
120
178
  //#endregion
121
179
  //#region src/transports/database.transport.ts
122
180
  /**
@@ -127,6 +185,80 @@ const POSTGRES_MAX_PARAMS = 65535;
127
185
  const POSTGRES_COLUMNS = 8;
128
186
  const POSTGRES_ROWS_PER_INSERT = Math.floor(POSTGRES_MAX_PARAMS / POSTGRES_COLUMNS);
129
187
  const MYSQL_ROWS_PER_INSERT = 5e3;
188
+ /** Postgres rejects NUL in TEXT and JSONB. */
189
+ function stripNul(value) {
190
+ return typeof value === "string" ? value.split("\0").join("") : value;
191
+ }
192
+ /** JSONB also rejects the \u0000 escape, which JSON.stringify writes for a NUL. */
193
+ function stripJsonNulEscape(value) {
194
+ if (typeof value !== "string") return value;
195
+ return value.replace(/(?<!\\)((?:\\\\)*)\\u0000/g, "$1");
196
+ }
197
+ const PG_DATA_CLASSES = [
198
+ "22",
199
+ "23",
200
+ "54"
201
+ ];
202
+ const MYSQL_DATA_CODES = new Set([
203
+ "ER_DATA_TOO_LONG",
204
+ "ER_TRUNCATED_WRONG_VALUE",
205
+ "ER_TRUNCATED_WRONG_VALUE_FOR_FIELD",
206
+ "ER_BAD_NULL_ERROR",
207
+ "ER_WARN_DATA_OUT_OF_RANGE",
208
+ "ER_DUP_ENTRY",
209
+ "ER_INVALID_JSON_TEXT",
210
+ "ER_INVALID_CHARACTER_STRING"
211
+ ]);
212
+ const MYSQL_DATA_ERRNOS = new Set([
213
+ 1048,
214
+ 1062,
215
+ 1264,
216
+ 1292,
217
+ 1366,
218
+ 1406,
219
+ 3140
220
+ ]);
221
+ const SQLITE_DATA_CODES = new Set([
222
+ "SQLITE_CONSTRAINT",
223
+ "SQLITE_TOOBIG",
224
+ "SQLITE_MISMATCH"
225
+ ]);
226
+ const MONGO_DATA_CODES = new Set([
227
+ 2,
228
+ 121,
229
+ 10334,
230
+ 11e3
231
+ ]);
232
+ function isDataErrorLayer(type, e) {
233
+ const code = e.code;
234
+ switch (type) {
235
+ case "postgresql": return typeof code === "string" && PG_DATA_CLASSES.includes(code.slice(0, 2));
236
+ case "mysql": return typeof code === "string" && MYSQL_DATA_CODES.has(code) || typeof e.errno === "number" && MYSQL_DATA_ERRNOS.has(e.errno);
237
+ case "sqlite": return typeof code === "string" && (SQLITE_DATA_CODES.has(code) || code.startsWith("SQLITE_CONSTRAINT_"));
238
+ case "mongodb": return e.name === "BSONObjectTooLarge" || typeof code === "number" && MONGO_DATA_CODES.has(code);
239
+ default: return false;
240
+ }
241
+ }
242
+ /**
243
+ * True when the database refused the data itself (bad value, constraint, size),
244
+ * which retrying cannot fix. Connection, timeout, auth and unknown errors are
245
+ * not data errors and must be retried, never dropped.
246
+ */
247
+ function isDataError(type, error) {
248
+ let current = error;
249
+ for (let depth = 0; depth < 3 && current && typeof current === "object"; depth += 1) {
250
+ if (isDataErrorLayer(type, current)) return true;
251
+ current = current.cause;
252
+ }
253
+ return false;
254
+ }
255
+ /** A write that stored part of a batch: only `remaining` still needs to be written. */
256
+ var PartialWriteError = class extends Error {
257
+ constructor(message, remaining, cause) {
258
+ super(message, { cause });
259
+ this.remaining = remaining;
260
+ }
261
+ };
130
262
  function chunk(items, size) {
131
263
  const chunks = [];
132
264
  for (let i = 0; i < items.length; i += size) chunks.push(items.slice(i, i + size));
@@ -155,6 +287,7 @@ var DatabaseTransport = class {
155
287
  this.isConnected = false;
156
288
  this.batchSize = config.batchSize || 100;
157
289
  this.flushInterval = config.flushInterval || 5e3;
290
+ this.cap = new BufferCap("DatabaseTransport", config.maxBufferSize, this.batchSize);
158
291
  if (config.filter) this.filter = config.filter;
159
292
  if (config.table) validateSqlIdentifier(config.table, "table");
160
293
  this.setupFlushTimer();
@@ -166,6 +299,7 @@ var DatabaseTransport = class {
166
299
  }
167
300
  addToBatch(entry) {
168
301
  this.batch.push(entry);
302
+ this.cap.enforce(this.batch);
169
303
  }
170
304
  async flush() {
171
305
  if (!this.flushPromise) this.flushPromise = this.drain().finally(() => {
@@ -174,37 +308,97 @@ var DatabaseTransport = class {
174
308
  await this.flushPromise;
175
309
  }
176
310
  /**
177
- * Drains the batch to the database one snapshot at a time. The current batch
178
- * is detached SYNCHRONOUSLY before awaiting the write, so entries appended by
179
- * concurrent write() calls land in a fresh array and are never written twice.
180
- * On write failure the snapshot is restored to the front of the batch for the
181
- * next flush cycle and the loop stops, so a persistently failing DB does not
182
- * hot-spin retrying the same entries.
311
+ * Drains the buffer to the database one batch at a time. A batch is detached
312
+ * SYNCHRONOUSLY before awaiting the write, so entries appended by concurrent
313
+ * write() calls land in a fresh array and are never written twice.
314
+ *
315
+ * A failure that is not a data error (connection, timeout, auth, unknown) puts
316
+ * the batch back at the front of the buffer, bounded by `maxBufferSize`, and
317
+ * stops the loop so a down database is not hammered. A data error means the
318
+ * database refused some entry itself, so that batch alone is split to find and
319
+ * drop the refused entries; see isolate().
183
320
  */
184
321
  async drain() {
185
322
  while (this.batch.length > 0) {
186
- const entriesToFlush = this.batch;
187
- this.batch = [];
323
+ const entries = this.batch.splice(0, this.batchSize);
188
324
  try {
189
- switch (this.config.type) {
190
- case "mongodb":
191
- await this.flushToMongoDB(entriesToFlush);
192
- break;
193
- case "postgresql":
194
- await this.flushToPostgreSQL(entriesToFlush);
195
- break;
196
- case "mysql":
197
- await this.flushToMySQL(entriesToFlush);
198
- break;
199
- case "sqlite":
200
- await this.flushToSQLite(entriesToFlush);
201
- break;
202
- default: throw new Error(`Unsupported database type: ${this.config.type}`);
203
- }
325
+ await this.writeEntries(entries);
204
326
  } catch (error) {
205
327
  require_internal_log.internalError("Database flush error", error);
206
- this.batch.unshift(...entriesToFlush);
207
- throw error;
328
+ if (error instanceof PartialWriteError) {
329
+ this.requeue(error.remaining);
330
+ throw error.cause;
331
+ }
332
+ if (!isDataError(this.config.type, error)) {
333
+ this.requeue(entries);
334
+ throw error;
335
+ }
336
+ const state = {
337
+ budget: 2 * this.batchSize,
338
+ rejected: 0,
339
+ requeue: [],
340
+ stopped: false
341
+ };
342
+ await this.isolate(entries, state);
343
+ if (state.rejected > 0) require_internal_log.internalWarn(`DatabaseTransport: dropped ${state.rejected} log entr${state.rejected === 1 ? "y" : "ies"} the database rejected`);
344
+ this.requeue(state.requeue);
345
+ if (state.stopped) throw state.error;
346
+ }
347
+ }
348
+ }
349
+ requeue(entries) {
350
+ if (entries.length === 0) return;
351
+ this.batch = entries.concat(this.batch);
352
+ this.cap.enforce(this.batch);
353
+ }
354
+ async writeEntries(entries) {
355
+ switch (this.config.type) {
356
+ case "mongodb":
357
+ await this.flushToMongoDB(entries);
358
+ break;
359
+ case "postgresql":
360
+ await this.flushToPostgreSQL(entries);
361
+ break;
362
+ case "mysql":
363
+ await this.flushToMySQL(entries);
364
+ break;
365
+ case "sqlite":
366
+ await this.flushToSQLite(entries);
367
+ break;
368
+ default: throw new Error(`Unsupported database type: ${this.config.type}`);
369
+ }
370
+ }
371
+ /**
372
+ * `entries` failed with a data error. Splits them in half until the refused
373
+ * entries are alone and drops those. A write that fails with anything but a
374
+ * data error, or running out of the per-flush write budget, stops the search:
375
+ * the entries not yet written go back to the buffer.
376
+ */
377
+ async isolate(entries, state) {
378
+ if (entries.length === 1) {
379
+ state.rejected += 1;
380
+ return;
381
+ }
382
+ const mid = Math.ceil(entries.length / 2);
383
+ for (const half of [entries.slice(0, mid), entries.slice(mid)]) {
384
+ if (state.stopped || state.budget <= 0) {
385
+ state.requeue.push(...half);
386
+ continue;
387
+ }
388
+ state.budget -= 1;
389
+ try {
390
+ await this.writeEntries(half);
391
+ } catch (error) {
392
+ if (error instanceof PartialWriteError) {
393
+ state.requeue.push(...error.remaining);
394
+ state.stopped = true;
395
+ state.error = error.cause;
396
+ } else if (isDataError(this.config.type, error)) await this.isolate(half, state);
397
+ else {
398
+ state.requeue.push(...half);
399
+ state.stopped = true;
400
+ state.error = error;
401
+ }
208
402
  }
209
403
  }
210
404
  }
@@ -329,7 +523,26 @@ var DatabaseTransport = class {
329
523
  appName: entry.appName,
330
524
  environment: entry.environment
331
525
  }));
332
- await collection.insertMany(documents);
526
+ try {
527
+ await collection.insertMany(documents, { ordered: false });
528
+ } catch (error) {
529
+ const raw = error.writeErrors;
530
+ let list = [];
531
+ if (Array.isArray(raw)) list = raw;
532
+ else if (raw) list = [raw];
533
+ const writeErrors = list;
534
+ if (writeErrors.length === 0) throw error;
535
+ const remaining = [];
536
+ let rejected = 0;
537
+ for (const writeError of writeErrors) {
538
+ const entry = typeof writeError.index === "number" ? entries[writeError.index] : void 0;
539
+ if (!entry) throw error;
540
+ if (isDataError("mongodb", writeError)) rejected += 1;
541
+ else remaining.push(entry);
542
+ }
543
+ if (rejected > 0) require_internal_log.internalWarn(`DatabaseTransport: dropped ${rejected} log entr${rejected === 1 ? "y" : "ies"} the database rejected`);
544
+ if (remaining.length > 0) throw new PartialWriteError("MongoDB insert partially failed", remaining, error);
545
+ }
333
546
  }
334
547
  async flushToPostgreSQL(entries) {
335
548
  const tableName = this.config.table || "logs";
@@ -342,7 +555,7 @@ var DatabaseTransport = class {
342
555
  entry.traceId,
343
556
  entry.appName,
344
557
  entry.environment
345
- ]), POSTGRES_ROWS_PER_INSERT);
558
+ ].map((value, column) => stripNul(column === 3 ? stripJsonNulEscape(value) : value))), POSTGRES_ROWS_PER_INSERT);
346
559
  const insertChunk = async (rows) => {
347
560
  const query = `
348
561
  INSERT INTO ${tableName}
@@ -502,6 +715,7 @@ var DatabaseTransport = class {
502
715
  require_internal_log.internalError("Database flush during close failed; entries remain buffered", error);
503
716
  }
504
717
  if (this.batch.length > 0) require_internal_log.internalError(`Database transport closing with ${this.batch.length} unflushed log entr${this.batch.length === 1 ? "y" : "ies"} after ${MAX_CLOSE_FLUSH_ATTEMPTS} attempts`);
718
+ this.cap.report(0);
505
719
  if (this.connection) switch (this.config.type) {
506
720
  case "mongodb":
507
721
  await this.connection.close();
@@ -558,6 +772,17 @@ var DatabaseTransport = class {
558
772
 
559
773
  //#endregion
560
774
  //#region src/transports/analytics.transport.ts
775
+ /**
776
+ * Thrown by sendBatch() when part of a batch was already delivered. drain()
777
+ * requeues only `unsent`, so delivered entries are not sent twice.
778
+ */
779
+ var PartialBatchError = class extends Error {
780
+ constructor(message, unsent, options) {
781
+ super(message, options);
782
+ this.unsent = unsent;
783
+ this.name = "PartialBatchError";
784
+ }
785
+ };
561
786
  var AnalyticsTransport = class {
562
787
  constructor(name, config) {
563
788
  this.batch = [];
@@ -573,6 +798,7 @@ var AnalyticsTransport = class {
573
798
  this.level = config.level;
574
799
  this.batchSize = this.config.batchSize || 50;
575
800
  this.flushInterval = this.config.flushInterval || 1e4;
801
+ this.cap = new BufferCap(name, config.maxBufferSize, this.batchSize);
576
802
  }
577
803
  async write(entry) {
578
804
  if (!this.isReady) await this.ensureReady();
@@ -582,6 +808,7 @@ var AnalyticsTransport = class {
582
808
  }
583
809
  addToBatch(entry) {
584
810
  this.batch.push(entry);
811
+ this.cap.enforce(this.batch);
585
812
  if (this.batch.length >= (this.config.batchSize || 50)) this.flush().catch((err) => require_internal_log.internalError(`${this.name} batch flush failed`, err));
586
813
  else if (!this.batchTimer && this.config.flushInterval) {
587
814
  var _this$batchTimer$unre, _this$batchTimer;
@@ -616,7 +843,8 @@ var AnalyticsTransport = class {
616
843
  await this.sendBatch(entriesToSend);
617
844
  } catch (error) {
618
845
  require_internal_log.internalError(`Analytics transport ${this.name} flush failed`, error);
619
- this.batch.unshift(...entriesToSend);
846
+ this.batch.unshift(...error instanceof PartialBatchError ? error.unsent : entriesToSend);
847
+ this.cap.enforce(this.batch);
620
848
  return;
621
849
  }
622
850
  }
@@ -629,25 +857,36 @@ var AnalyticsTransport = class {
629
857
  const MAX_CLOSE_FLUSH_ATTEMPTS = 3;
630
858
  for (let attempt = 0; attempt < MAX_CLOSE_FLUSH_ATTEMPTS && this.batch.length > 0; attempt += 1) await this.flush();
631
859
  if (this.batch.length > 0) require_internal_log.internalError(`Analytics transport ${this.name} closing with ${this.batch.length} unflushed entr${this.batch.length === 1 ? "y" : "ies"} after ${MAX_CLOSE_FLUSH_ATTEMPTS} attempts`);
860
+ this.cap.report(0);
632
861
  await this.cleanup();
633
862
  }
634
863
  shouldSkipEntry(entry) {
635
864
  return ["debug", "trace"].includes(entry.level.toLowerCase());
636
865
  }
637
866
  transformEntry(entry) {
638
- const transformed = {
867
+ const core = {
639
868
  timestamp: entry.timestamp.toISOString(),
640
869
  level: entry.level,
641
870
  message: entry.message,
642
- ...entry.data
871
+ context: entry.context,
872
+ traceId: entry.traceId,
873
+ appName: entry.appName,
874
+ environment: entry.environment
643
875
  };
644
- if (entry.context) transformed.context = entry.context;
645
- if (entry.traceId) transformed.traceId = entry.traceId;
646
- if (entry.appName) transformed.appName = entry.appName;
647
- if (entry.environment) transformed.environment = entry.environment;
876
+ const transformed = require_entry_fields.mergeCoreFields(core, entry.data, this.reservedKeys());
877
+ for (const key of [
878
+ "context",
879
+ "traceId",
880
+ "appName",
881
+ "environment"
882
+ ]) if (!core[key]) delete transformed[key];
648
883
  if (this.config.customProperties) Object.assign(transformed, this.config.customProperties);
649
884
  return transformed;
650
885
  }
886
+ /** Provider-specific field names a payload key must not replace. */
887
+ reservedKeys() {
888
+ return [];
889
+ }
651
890
  /**
652
891
  * Runs initialize() once. A bad config (say, a missing API key) fails every
653
892
  * write with the same error instead of an unhandled rejection at startup.
@@ -667,8 +906,71 @@ var AnalyticsTransport = class {
667
906
 
668
907
  //#endregion
669
908
  //#region src/transports/datadog.transport.ts
909
+ const gzipAsync = (0, node_util.promisify)(node_zlib.gzip);
910
+ const MAX_LOGS_PER_REQUEST = 1e3;
911
+ const MAX_LOGS_PAYLOAD_BYTES = 5e6;
912
+ const MAX_LOG_BYTES = 1e6;
913
+ const MAX_TRUNCATED_MESSAGE_CHARS = 15e4;
914
+ const TRUNCATED_LOG_KEYS = [
915
+ "ddsource",
916
+ "ddtags",
917
+ "hostname",
918
+ "service",
919
+ "status",
920
+ "timestamp",
921
+ "level",
922
+ "context",
923
+ "traceId",
924
+ "appName",
925
+ "environment",
926
+ "env",
927
+ "version",
928
+ "logger",
929
+ "dd.trace_id",
930
+ "dd.span_id"
931
+ ];
932
+ const STATUS_MAP = {
933
+ fatal: "critical",
934
+ critical: "critical",
935
+ error: "error",
936
+ warn: "warn",
937
+ warning: "warn",
938
+ info: "info",
939
+ debug: "debug",
940
+ trace: "debug",
941
+ verbose: "debug"
942
+ };
943
+ const MAX_UINT64 = (1n << 64n) - 1n;
944
+ const DASHED_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
945
+ const HEX = /^[0-9a-f]+$/i;
946
+ const DIGITS = /^\d+$/;
947
+ /**
948
+ * Datadog correlates on a decimal 64-bit id. Accepts:
949
+ * - a dashed UUID (logixia's default trace id): dashes stripped, then 32-hex;
950
+ * - 32 hex chars (W3C trace id) or 16 hex chars containing a-f: low 64 bits as decimal;
951
+ * - an all-digit string: already decimal (dd-trace's native format), passed through.
952
+ * An all-digit string is ambiguous when it is 16 or 32 long: 32 is read as hex (a
953
+ * decimal id has at most 20 digits), 16 is read as decimal.
954
+ * Anything else yields the raw string for trace ids (`passthrough`) and nothing for span ids.
955
+ */
956
+ function toDatadogId(id, passthrough) {
957
+ if (typeof id !== "string" || id === "") return void 0;
958
+ const fallback = passthrough ? id : void 0;
959
+ const compact = DASHED_UUID.test(id) ? id.replaceAll("-", "") : id;
960
+ const isHexId = HEX.test(compact) && (compact.length === 32 || compact.length === 16);
961
+ if (DIGITS.test(compact) && compact.length !== 32) {
962
+ const value = BigInt(compact);
963
+ return value <= MAX_UINT64 ? value.toString() : fallback;
964
+ }
965
+ if (isHexId) return BigInt(`0x${compact.slice(-16)}`).toString();
966
+ return fallback;
967
+ }
968
+ /** Datadog tags: no commas or whitespace, lowercase, at most 200 chars. */
969
+ function tag(key, value) {
970
+ return `${key}:${value}`.replaceAll(/[\s,]+/g, "_").toLowerCase().slice(0, 200);
971
+ }
670
972
  /**
671
- * Sends log entries and metrics to Datadog via the Logs and Metrics HTTP APIs.
973
+ * Sends log entries and metrics to Datadog via the Logs (v2) and Metrics HTTP APIs.
672
974
  *
673
975
  * The API key is transmitted via the `DD-API-KEY` request header — never in the URL.
674
976
  *
@@ -694,29 +996,68 @@ var DataDogTransport = class extends AnalyticsTransport {
694
996
  }
695
997
  }
696
998
  async sendEntry(entry) {
697
- const promises = [];
698
- if (this.datadogConfig.enableLogs !== false) promises.push(this.sendLog(entry));
699
- if (this.datadogConfig.enableMetrics) promises.push(this.sendMetric(entry));
700
- if (this.datadogConfig.enableTraces && entry.traceId) promises.push(this.sendTrace(entry));
701
- await Promise.all(promises);
999
+ await this.sendBatch([entry]);
702
1000
  }
1001
+ /**
1002
+ * Logs go first, chunk by chunk; metrics follow once every log chunk is out.
1003
+ * A failure part-way throws PartialBatchError naming only the entries that were
1004
+ * not delivered, so a retry never re-posts a chunk Datadog already accepted.
1005
+ */
703
1006
  async sendBatch(entries) {
704
- const promises = [];
705
- if (this.datadogConfig.enableLogs !== false) promises.push(this.sendLogBatch(entries));
706
- if (this.datadogConfig.enableMetrics) promises.push(this.sendMetricBatch(entries));
707
- if (this.datadogConfig.enableTraces) {
708
- const entriesWithTraces = entries.filter((e) => e.traceId);
709
- if (entriesWithTraces.length > 0) promises.push(this.sendTraceBatch(entriesWithTraces));
1007
+ if (this.datadogConfig.enableLogs !== false) await this.sendLogBatch(entries);
1008
+ if (this.datadogConfig.enableMetrics) try {
1009
+ await this.sendMetricBatch(entries);
1010
+ } catch (error) {
1011
+ if (this.datadogConfig.enableLogs === false) throw error;
1012
+ throw new PartialBatchError(error.message, [], { cause: error });
710
1013
  }
711
- await Promise.all(promises);
712
1014
  }
713
1015
  async cleanup() {}
714
- async sendLog(entry) {
715
- await this.sendLogBatch([entry]);
716
- }
717
1016
  async sendLogBatch(entries) {
718
- const payload = { logs: entries.map((entry) => this.transformToDataDogLog(entry)) };
719
- await this.makeLogsRequest("/v1/input", payload);
1017
+ const items = entries.map((entry) => ({
1018
+ entry,
1019
+ json: this.serializeLog(entry)
1020
+ }));
1021
+ const chunks = this.chunkLogs(items);
1022
+ for (let i = 0; i < chunks.length; i += 1) {
1023
+ const chunk$1 = chunks[i];
1024
+ try {
1025
+ await this.makeLogsRequest("/api/v2/logs", `[${chunk$1.map((item) => item.json).join(",")}]`);
1026
+ } catch (error) {
1027
+ if (i === 0) throw error;
1028
+ const unsent = chunks.slice(i).flatMap((c) => c.map((item) => item.entry));
1029
+ throw new PartialBatchError(error.message, unsent, { cause: error });
1030
+ }
1031
+ }
1032
+ }
1033
+ /** Splits into requests of at most 1000 entries and about 5MB of JSON. */
1034
+ chunkLogs(items) {
1035
+ const chunks = [];
1036
+ let current = [];
1037
+ let currentBytes = 2;
1038
+ for (const item of items) {
1039
+ const bytes = Buffer.byteLength(item.json) + 1;
1040
+ if (current.length > 0 && (current.length >= MAX_LOGS_PER_REQUEST || currentBytes + bytes > MAX_LOGS_PAYLOAD_BYTES)) {
1041
+ chunks.push(current);
1042
+ current = [];
1043
+ currentBytes = 2;
1044
+ }
1045
+ current.push(item);
1046
+ currentBytes += bytes;
1047
+ }
1048
+ if (current.length > 0) chunks.push(current);
1049
+ return chunks;
1050
+ }
1051
+ /** Serializes once. A log over 1MB keeps its identity and correlation fields but loses its attributes and most of its message. */
1052
+ serializeLog(entry) {
1053
+ const log = this.transformToDataDogLog(entry);
1054
+ const json = require_safe_stringify.stringifyForLog(log);
1055
+ if (Buffer.byteLength(json) <= MAX_LOG_BYTES) return json;
1056
+ const kept = {};
1057
+ for (const key of TRUNCATED_LOG_KEYS) if (log[key] !== void 0) kept[key] = log[key];
1058
+ kept["message"] = String(log["message"]).slice(0, MAX_TRUNCATED_MESSAGE_CHARS);
1059
+ kept["truncated"] = true;
1060
+ return require_safe_stringify.stringifyForLog(kept);
720
1061
  }
721
1062
  async sendMetric(entry) {
722
1063
  await this.sendMetricBatch([entry]);
@@ -725,32 +1066,53 @@ var DataDogTransport = class extends AnalyticsTransport {
725
1066
  const payload = { series: entries.map((entry) => this.transformToDataDogMetric(entry)) };
726
1067
  await this.makeMetricsRequest("/api/v1/series", payload);
727
1068
  }
728
- async sendTrace(entry) {
729
- await this.sendLog(entry);
730
- }
731
- async sendTraceBatch(entries) {
732
- await this.sendLogBatch(entries);
733
- }
734
1069
  transformToDataDogLog(entry) {
1070
+ var _entry$data, _entry$data2;
735
1071
  const transformed = this.transformEntry(entry);
1072
+ const service = this.datadogConfig.service || "logixia";
1073
+ const env = this.datadogConfig.env || "production";
1074
+ const version = this.datadogConfig.version;
1075
+ const tags = [
1076
+ tag("env", env),
1077
+ tag("service", service),
1078
+ ...version ? [tag("version", version)] : []
1079
+ ];
1080
+ const spanId = toDatadogId(((_entry$data = entry.data) === null || _entry$data === void 0 ? void 0 : _entry$data["spanId"]) ?? ((_entry$data2 = entry.data) === null || _entry$data2 === void 0 ? void 0 : _entry$data2["span_id"]), false);
1081
+ const traceId = toDatadogId(entry.traceId, true);
736
1082
  return {
737
- timestamp: entry.timestamp.toISOString(),
738
- level: this.mapLogLevel(entry.level),
1083
+ ...transformed,
1084
+ ddsource: "nodejs",
1085
+ ddtags: tags.join(","),
1086
+ hostname: this.getHostname(),
1087
+ service,
1088
+ status: this.mapLogLevel(entry.level),
739
1089
  message: entry.message,
740
- service: this.datadogConfig.service || "logixia",
741
- version: this.datadogConfig.version,
742
- env: this.datadogConfig.env || "production",
1090
+ env,
1091
+ version,
743
1092
  logger: {
744
1093
  name: "logixia",
745
1094
  thread_name: "main"
746
1095
  },
747
- ...transformed,
748
- "dd.trace_id": entry.traceId,
749
- "dd.span_id": this.generateSpanId(),
750
- host: this.getHostname(),
751
- source: "nodejs"
1096
+ ...traceId && { "dd.trace_id": traceId },
1097
+ ...traceId && spanId && { "dd.span_id": spanId }
752
1098
  };
753
1099
  }
1100
+ reservedKeys() {
1101
+ return [
1102
+ "status",
1103
+ "ddtags",
1104
+ "ddsource",
1105
+ "hostname",
1106
+ "service",
1107
+ "version",
1108
+ "env",
1109
+ "logger",
1110
+ "host",
1111
+ "source",
1112
+ "dd.trace_id",
1113
+ "dd.span_id"
1114
+ ];
1115
+ }
754
1116
  transformToDataDogMetric(entry) {
755
1117
  return {
756
1118
  metric: `logitron.logs.${entry.level}`,
@@ -758,58 +1120,29 @@ var DataDogTransport = class extends AnalyticsTransport {
758
1120
  type: "count",
759
1121
  host: this.getHostname(),
760
1122
  tags: [
761
- `level:${entry.level}`,
762
- `service:${this.datadogConfig.service || "logixia"}`,
763
- `env:${this.datadogConfig.env || "production"}`,
764
- ...entry.context ? [`context:${entry.context}`] : [],
765
- ...entry.appName ? [`app:${entry.appName}`] : []
1123
+ tag("level", entry.level),
1124
+ tag("service", this.datadogConfig.service || "logixia"),
1125
+ tag("env", this.datadogConfig.env || "production"),
1126
+ ...entry.context ? [tag("context", entry.context)] : [],
1127
+ ...entry.appName ? [tag("app", entry.appName)] : []
766
1128
  ]
767
1129
  };
768
1130
  }
769
- transformToDataDogTrace(entry) {
770
- return {
771
- trace_id: entry.traceId,
772
- span_id: this.generateSpanId(),
773
- parent_id: null,
774
- name: "log.entry",
775
- service: this.datadogConfig.service || "logixia",
776
- resource: entry.message,
777
- type: "log",
778
- start: entry.timestamp.getTime() * 1e6,
779
- duration: 1e6,
780
- meta: {
781
- "log.level": entry.level,
782
- "log.message": entry.message,
783
- ...entry.context && { "log.context": entry.context },
784
- ...entry.data && Object.keys(entry.data).reduce((acc, key) => {
785
- acc[`log.${key}`] = String(entry.data[key]);
786
- return acc;
787
- }, {})
788
- },
789
- metrics: { _sampling_priority_v1: 1 }
790
- };
791
- }
792
1131
  mapLogLevel(level) {
793
- return {
794
- error: "error",
795
- warn: "warn",
796
- warning: "warn",
797
- info: "info",
798
- debug: "debug",
799
- trace: "trace",
800
- verbose: "debug"
801
- }[level.toLowerCase()] || "info";
802
- }
803
- async makeLogsRequest(endpoint, data) {
1132
+ return STATUS_MAP[level.toLowerCase()] || "info";
1133
+ }
1134
+ async makeLogsRequest(endpoint, json) {
804
1135
  const url = `${this.logsUrl}${endpoint}`;
805
1136
  try {
1137
+ const compress = this.datadogConfig.compress === true;
806
1138
  const response = await fetch(url, {
807
1139
  method: "POST",
808
1140
  headers: {
809
1141
  "Content-Type": "application/json",
810
- "DD-API-KEY": this.datadogConfig.apiKey
1142
+ "DD-API-KEY": this.datadogConfig.apiKey,
1143
+ ...compress && { "Content-Encoding": "gzip" }
811
1144
  },
812
- body: require_safe_stringify.stringifyForLog(data)
1145
+ body: compress ? await gzipAsync(json) : json
813
1146
  });
814
1147
  if (!response.ok) throw new Error(`DataDog Logs API error: ${response.status} ${response.statusText}`);
815
1148
  } catch (error) {
@@ -832,9 +1165,6 @@ var DataDogTransport = class extends AnalyticsTransport {
832
1165
  throw new Error(`Failed to send metrics to DataDog: ${error.message}`, { cause: error });
833
1166
  }
834
1167
  }
835
- generateSpanId() {
836
- return Math.floor(Math.random() * 0xffffffffffff).toString(16);
837
- }
838
1168
  getHostname() {
839
1169
  try {
840
1170
  return require("node:os").hostname();
@@ -890,6 +1220,12 @@ const unlink = (0, node_util.promisify)(node_fs.unlink);
890
1220
  const readdir = (0, node_util.promisify)(node_fs.readdir);
891
1221
  const stat = (0, node_util.promisify)(node_fs.stat);
892
1222
  const DROP_WARN_INTERVAL_MS = 5e3;
1223
+ const DEFAULT_MAX_QUEUE = 1e4;
1224
+ const DEFAULT_FILE_MODE = 384;
1225
+ const DEFAULT_DIR_MODE = 448;
1226
+ function csvField(value) {
1227
+ return `"${stripControlChars(require_coerce_utils.safeToString(value)).replace(/"/g, "\"\"")}"`;
1228
+ }
893
1229
  const SIZE_UNITS = {
894
1230
  "": 1,
895
1231
  k: 1024,
@@ -925,6 +1261,10 @@ function formatDatePattern(date, pattern) {
925
1261
  * rotated files are renamed beside it (`app-2026-10-22T09-30-00-000.log`).
926
1262
  * With a `rotation` block every file, the live one included, is timestamped.
927
1263
  *
1264
+ * Files are created with mode 0o600 and directories with 0o700; override with
1265
+ * `fileMode` and `dirMode`. At most `maxQueue` (default 10000) entries wait in
1266
+ * memory; the rest are dropped per `queuePolicy` and counted in a warning.
1267
+ *
928
1268
  * @example
929
1269
  * transports: { file: { filename: 'app.log', dirname: './logs', maxSize: '50MB' } }
930
1270
  */
@@ -959,8 +1299,10 @@ var FileTransport = class {
959
1299
  this.maxBytes = parseSize(rawMaxSize);
960
1300
  if (rawMaxSize !== void 0 && this.maxBytes === void 0) require_internal_log.internalWarn(`FileTransport: maxSize ${JSON.stringify(rawMaxSize)} is not a valid size (try '50MB'); size rotation is off`);
961
1301
  const maxQueue = config.maxQueue;
962
- if (maxQueue !== void 0 && !(Number.isInteger(maxQueue) && maxQueue > 0)) require_internal_log.internalWarn(`FileTransport: maxQueue ${String(maxQueue)} is not a positive integer; ignored`);
963
- else this.maxQueue = maxQueue;
1302
+ if (maxQueue !== void 0 && !(Number.isInteger(maxQueue) && maxQueue > 0)) require_internal_log.internalWarn(`FileTransport: maxQueue ${String(maxQueue)} is not a positive integer; using ${DEFAULT_MAX_QUEUE}`);
1303
+ this.maxQueue = maxQueue !== void 0 && Number.isInteger(maxQueue) && maxQueue > 0 ? maxQueue : DEFAULT_MAX_QUEUE;
1304
+ this.fileMode = config.fileMode ?? DEFAULT_FILE_MODE;
1305
+ this.dirMode = config.dirMode ?? DEFAULT_DIR_MODE;
964
1306
  this.queuePolicy = config.queuePolicy ?? "drop-oldest";
965
1307
  this.maxFiles = (rotation === null || rotation === void 0 ? void 0 : rotation.maxFiles) ?? config.maxFiles;
966
1308
  this.compress = (rotation === null || rotation === void 0 ? void 0 : rotation.compress) ?? config.zippedArchive ?? false;
@@ -970,7 +1312,10 @@ var FileTransport = class {
970
1312
  this.ext = node_path.extname(config.filename);
971
1313
  this.base = node_path.basename(config.filename, this.ext);
972
1314
  try {
973
- node_fs.mkdirSync(this.dir, { recursive: true });
1315
+ node_fs.mkdirSync(this.dir, {
1316
+ recursive: true,
1317
+ mode: this.dirMode
1318
+ });
974
1319
  } catch (error) {
975
1320
  require_internal_log.internalError(`FileTransport could not create ${this.dir}`, error);
976
1321
  }
@@ -1038,7 +1383,7 @@ var FileTransport = class {
1038
1383
  this.enqueued += 1;
1039
1384
  }
1040
1385
  queueFull() {
1041
- return this.maxQueue !== void 0 && this.batch.length + this.inFlight >= this.maxQueue;
1386
+ return this.batch.length + this.inFlight >= this.maxQueue;
1042
1387
  }
1043
1388
  roomAvailable() {
1044
1389
  const room = new Promise((resolve) => this.spaceWaiters.push(resolve));
@@ -1118,30 +1463,26 @@ var FileTransport = class {
1118
1463
  }
1119
1464
  formatEntry(entry) {
1120
1465
  switch (this.config.format) {
1121
- case "json": return require_safe_stringify.stringifyForLog({
1466
+ case "json": return require_safe_stringify.stringifyForLog(require_entry_fields.mergeCoreFields({
1122
1467
  timestamp: entry.timestamp.toISOString(),
1123
1468
  level: entry.level,
1124
- message: entry.message,
1125
- ...entry.data || {}
1126
- });
1127
- case "csv": {
1128
- const safeMessage = require_coerce_utils.safeToString(entry.message);
1129
- return [
1130
- entry.timestamp.toISOString(),
1131
- entry.level,
1132
- `"${safeMessage.replace(/"/g, "\"\"")}"`,
1133
- require_coerce_utils.safeToString(entry.context),
1134
- require_coerce_utils.safeToString(entry.traceId),
1135
- require_safe_stringify.stringifyForLog(entry.data || {})
1136
- ].join(",");
1137
- }
1469
+ message: entry.message
1470
+ }, entry.data));
1471
+ case "csv": return [
1472
+ entry.timestamp.toISOString(),
1473
+ csvField(entry.level),
1474
+ csvField(entry.message),
1475
+ csvField(entry.context),
1476
+ csvField(entry.traceId),
1477
+ csvField(require_safe_stringify.stringifyForLog(entry.data || {}))
1478
+ ].join(",");
1138
1479
  case "text":
1139
- default: return `${entry.timestamp.toISOString()} [${entry.level.toUpperCase()}] ${require_coerce_utils.safeToString(entry.message)}${entry.data ? " " + require_safe_stringify.stringifyForLog(entry.data) : ""}`;
1480
+ default: return `${entry.timestamp.toISOString()} [${stripControlChars(require_coerce_utils.safeToString(entry.level)).toUpperCase()}] ${stripControlChars(require_coerce_utils.safeToString(entry.message))}${entry.data ? " " + require_safe_stringify.stringifyForLog(entry.data) : ""}`;
1140
1481
  }
1141
1482
  }
1142
1483
  addToBatch(entry) {
1143
1484
  this.enqueue(entry);
1144
- const threshold = Math.min(this.config.batchSize || 100, this.maxQueue ?? Infinity);
1485
+ const threshold = Math.min(this.config.batchSize || 100, this.maxQueue);
1145
1486
  if (this.batch.length >= threshold && !this.draining) {
1146
1487
  if (this.batchTimer) {
1147
1488
  clearTimeout(this.batchTimer);
@@ -1179,7 +1520,7 @@ var FileTransport = class {
1179
1520
  if (chunk$1) await this.append(chunk$1, chunkBytes);
1180
1521
  }
1181
1522
  async append(content, bytes = Buffer.byteLength(content)) {
1182
- await appendFile(this.currentFilePath, content);
1523
+ await appendFile(this.currentFilePath, content, { mode: this.fileMode });
1183
1524
  this.bytesWritten += bytes;
1184
1525
  }
1185
1526
  timeToRotate() {
@@ -1273,7 +1614,7 @@ var FileTransport = class {
1273
1614
  async compressFile(filePath) {
1274
1615
  const gzPath = `${filePath}.gz`;
1275
1616
  try {
1276
- await (0, node_stream_promises.pipeline)(node_fs.createReadStream(filePath), (0, node_zlib.createGzip)(), node_fs.createWriteStream(gzPath));
1617
+ await (0, node_stream_promises.pipeline)(node_fs.createReadStream(filePath), (0, node_zlib.createGzip)(), node_fs.createWriteStream(gzPath, { mode: this.fileMode }));
1277
1618
  await unlink(filePath);
1278
1619
  } catch (error) {
1279
1620
  require_internal_log.internalError("Failed to compress rotated log file", error);
@@ -1357,12 +1698,27 @@ var GoogleAnalyticsTransport = class extends AnalyticsTransport {
1357
1698
  await this.sendEvents(events);
1358
1699
  }
1359
1700
  async cleanup() {}
1701
+ reservedKeys() {
1702
+ return [
1703
+ "event_category",
1704
+ "event_label",
1705
+ "log_level",
1706
+ "log_message",
1707
+ "trace_id",
1708
+ "app_name",
1709
+ "custom_dimension_1",
1710
+ "custom_dimension_2",
1711
+ "custom_dimension_3"
1712
+ ];
1713
+ }
1360
1714
  transformToGAEvent(entry) {
1361
1715
  const transformed = this.transformEntry(entry);
1716
+ const eventCategory = this.mapLogLevelToCategory(entry.level);
1362
1717
  return {
1363
1718
  name: "log_event",
1364
1719
  params: {
1365
- event_category: this.mapLogLevelToCategory(entry.level),
1720
+ ...this.flattenData(transformed),
1721
+ event_category: eventCategory,
1366
1722
  event_label: entry.message,
1367
1723
  log_level: entry.level,
1368
1724
  log_message: entry.message,
@@ -1371,7 +1727,6 @@ var GoogleAnalyticsTransport = class extends AnalyticsTransport {
1371
1727
  ...entry.traceId && { trace_id: entry.traceId },
1372
1728
  ...entry.appName && { app_name: entry.appName },
1373
1729
  ...entry.environment && { environment: entry.environment },
1374
- ...this.flattenData(transformed),
1375
1730
  custom_dimension_1: entry.level,
1376
1731
  custom_dimension_2: entry.context || "default",
1377
1732
  custom_dimension_3: entry.environment || "production"
@@ -1567,12 +1922,21 @@ var MixpanelTransport = class extends AnalyticsTransport {
1567
1922
  await this.trackEvents(events);
1568
1923
  }
1569
1924
  async cleanup() {}
1925
+ reservedKeys() {
1926
+ return [
1927
+ "token",
1928
+ "distinct_id",
1929
+ "time",
1930
+ "$insert_id"
1931
+ ];
1932
+ }
1570
1933
  transformToMixpanelEvent(entry) {
1571
1934
  const transformed = this.transformEntry(entry);
1572
1935
  return {
1573
1936
  name: `log_${entry.level}`,
1574
1937
  properties: {
1575
1938
  ...transformed,
1939
+ token: this.mixpanelConfig.token,
1576
1940
  distinct_id: this.mixpanelConfig.distinct_id || "anonymous",
1577
1941
  time: entry.timestamp.getTime(),
1578
1942
  $insert_id: this.generateInsertId(entry),
@@ -1585,8 +1949,8 @@ var MixpanelTransport = class extends AnalyticsTransport {
1585
1949
  const payload = {
1586
1950
  event: event.name,
1587
1951
  properties: {
1588
- token: this.mixpanelConfig.token,
1589
- ...event.properties
1952
+ ...event.properties,
1953
+ token: this.mixpanelConfig.token
1590
1954
  }
1591
1955
  };
1592
1956
  await this.makeRequest("/track", [payload]);
@@ -1595,8 +1959,8 @@ var MixpanelTransport = class extends AnalyticsTransport {
1595
1959
  const payload = events.map((event) => ({
1596
1960
  event: event.name,
1597
1961
  properties: {
1598
- token: this.mixpanelConfig.token,
1599
- ...event.properties
1962
+ ...event.properties,
1963
+ token: this.mixpanelConfig.token
1600
1964
  }
1601
1965
  }));
1602
1966
  await this.makeRequest("/track", payload);
@@ -1654,11 +2018,11 @@ var MixpanelTransport = class extends AnalyticsTransport {
1654
2018
  const payload = {
1655
2019
  event: eventName,
1656
2020
  properties: {
2021
+ ...properties,
2022
+ ...this.mixpanelConfig.enableSuperProperties ? this.mixpanelConfig.superProperties : {},
1657
2023
  token: this.mixpanelConfig.token,
1658
2024
  distinct_id: this.mixpanelConfig.distinct_id || "anonymous",
1659
- time: Date.now(),
1660
- ...properties,
1661
- ...this.mixpanelConfig.enableSuperProperties ? this.mixpanelConfig.superProperties : {}
2025
+ time: Date.now()
1662
2026
  }
1663
2027
  };
1664
2028
  await this.makeRequest("/track", [payload]);
@@ -1718,6 +2082,9 @@ var SegmentTransport = class extends AnalyticsTransport {
1718
2082
  await this.batchTrack(segmentEvents);
1719
2083
  }
1720
2084
  async cleanup() {}
2085
+ reservedKeys() {
2086
+ return ["logger"];
2087
+ }
1721
2088
  transformToSegmentEvent(entry) {
1722
2089
  const transformed = this.transformEntry(entry);
1723
2090
  return {
@@ -1727,10 +2094,10 @@ var SegmentTransport = class extends AnalyticsTransport {
1727
2094
  anonymousId: this.generateAnonymousId(),
1728
2095
  timestamp: entry.timestamp.toISOString(),
1729
2096
  properties: {
2097
+ ...transformed,
1730
2098
  level: entry.level,
1731
2099
  message: entry.message,
1732
2100
  logger: "logixia",
1733
- ...transformed,
1734
2101
  ...entry.context && { context: entry.context },
1735
2102
  ...entry.traceId && { traceId: entry.traceId },
1736
2103
  ...entry.appName && { appName: entry.appName },
@@ -1786,11 +2153,10 @@ var SegmentTransport = class extends AnalyticsTransport {
1786
2153
  }
1787
2154
  }
1788
2155
  extractUserId(entry) {
1789
- if (entry.data) {
1790
- const d = entry.data;
1791
- const id = d["userId"] ?? d["user_id"] ?? d["id"];
1792
- return typeof id === "string" ? id : void 0;
1793
- }
2156
+ var _entry$data;
2157
+ const field = this.segmentConfig.userIdField ?? "userId";
2158
+ const id = (_entry$data = entry.data) === null || _entry$data === void 0 ? void 0 : _entry$data[field];
2159
+ return typeof id === "string" ? id : void 0;
1794
2160
  }
1795
2161
  generateAnonymousId() {
1796
2162
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
@@ -2297,6 +2663,12 @@ Object.defineProperty(exports, 'AnalyticsTransport', {
2297
2663
  return AnalyticsTransport;
2298
2664
  }
2299
2665
  });
2666
+ Object.defineProperty(exports, 'BufferCap', {
2667
+ enumerable: true,
2668
+ get: function () {
2669
+ return BufferCap;
2670
+ }
2671
+ });
2300
2672
  Object.defineProperty(exports, 'ConsoleTransport', {
2301
2673
  enumerable: true,
2302
2674
  get: function () {
@@ -2345,4 +2717,10 @@ Object.defineProperty(exports, 'TransportManager', {
2345
2717
  return TransportManager;
2346
2718
  }
2347
2719
  });
2348
- //# sourceMappingURL=transport.manager-BivyUTtU.js.map
2720
+ Object.defineProperty(exports, 'stripControlChars', {
2721
+ enumerable: true,
2722
+ get: function () {
2723
+ return stripControlChars;
2724
+ }
2725
+ });
2726
+ //# sourceMappingURL=transport.manager-CXbKKdVE.js.map