@loggerjs/node 0.0.2 → 0.3.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 (40) hide show
  1. package/README.md +102 -21
  2. package/dist/bullmq-integration.d.ts +9 -0
  3. package/dist/bullmq-integration.d.ts.map +1 -0
  4. package/dist/file-transport.d.ts +6 -2
  5. package/dist/file-transport.d.ts.map +1 -1
  6. package/dist/hapi-integration.d.ts +44 -0
  7. package/dist/hapi-integration.d.ts.map +1 -0
  8. package/dist/http-transport.d.ts +2 -1
  9. package/dist/http-transport.d.ts.map +1 -1
  10. package/dist/index.cjs +857 -261
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.ts +8 -0
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +852 -264
  15. package/dist/index.js.map +1 -1
  16. package/dist/internal-types.d.ts +2 -0
  17. package/dist/internal-types.d.ts.map +1 -1
  18. package/dist/koa-integration.d.ts +26 -0
  19. package/dist/koa-integration.d.ts.map +1 -0
  20. package/dist/logger-diagnostics.d.ts +14 -0
  21. package/dist/logger-diagnostics.d.ts.map +1 -0
  22. package/dist/nest-integration.d.ts +5 -0
  23. package/dist/nest-integration.d.ts.map +1 -0
  24. package/dist/node-destination.d.ts +32 -0
  25. package/dist/node-destination.d.ts.map +1 -0
  26. package/dist/payload-transforms.d.ts +12 -0
  27. package/dist/payload-transforms.d.ts.map +1 -0
  28. package/dist/prisma-integration.d.ts +6 -0
  29. package/dist/prisma-integration.d.ts.map +1 -0
  30. package/dist/process-integration.d.ts +4 -0
  31. package/dist/process-integration.d.ts.map +1 -1
  32. package/dist/redis-integration.d.ts +7 -0
  33. package/dist/redis-integration.d.ts.map +1 -0
  34. package/dist/rotating-file-transport.d.ts +2 -0
  35. package/dist/rotating-file-transport.d.ts.map +1 -1
  36. package/dist/stdout-transport.d.ts +2 -0
  37. package/dist/stdout-transport.d.ts.map +1 -1
  38. package/dist/worker-transport.d.ts +16 -2
  39. package/dist/worker-transport.d.ts.map +1 -1
  40. package/package.json +42 -2
package/dist/index.cjs CHANGED
@@ -1,72 +1,124 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  let _loggerjs_core = require("@loggerjs/core");
3
3
  let fs = require("fs");
4
+ let path = require("path");
5
+ let node_zlib = require("node:zlib");
4
6
  let dgram = require("dgram");
5
7
  let net = require("net");
6
8
  let os = require("os");
7
9
  let node_worker_threads = require("node:worker_threads");
8
10
  let node_async_hooks = require("node:async_hooks");
9
11
  let node_module = require("node:module");
10
- //#region src/stdout-transport.ts
12
+ //#region src/node-destination.ts
13
+ const encoder = new TextEncoder();
11
14
  const normalizeError = (error) => error instanceof Error ? error : new Error(String(error));
12
- function stdoutTransport(options = {}) {
13
- const codec = options.codec ?? (0, _loggerjs_core.ndjsonCodec)();
14
- const stream = options.stream ?? process.stdout;
15
- const transportName = options.name ?? "stdout";
15
+ function byteLength(payload) {
16
+ return typeof payload === "string" ? encoder.encode(payload).byteLength : payload.byteLength;
17
+ }
18
+ function coalescePayloads(payloads) {
19
+ if (payloads.every((payload) => typeof payload === "string")) return payloads.join("");
20
+ const buffers = payloads.map((payload) => typeof payload === "string" ? Buffer.from(payload) : Buffer.from(payload));
21
+ return Buffer.concat(buffers);
22
+ }
23
+ function isEpipe(error) {
24
+ return Boolean(error) && typeof error === "object" && error.code === "EPIPE";
25
+ }
26
+ function normalizeMinLength(value) {
27
+ if (value === void 0 || !Number.isFinite(value) || value <= 0) return 0;
28
+ return Math.floor(value);
29
+ }
30
+ function createNodeStreamDestination(options) {
31
+ const minLength = normalizeMinLength(options.minLength);
32
+ const ignoreEpipe = options.ignoreEpipe ?? false;
16
33
  const waiters = [];
34
+ const buffer = [];
35
+ const pendingPayloads = [];
36
+ let bufferedBytes = 0;
17
37
  let pendingWrites = 0;
18
38
  let pendingDrains = 0;
19
39
  let lastError;
20
40
  let lastContext;
41
+ let closed = false;
21
42
  const reportInternalError = (error, operation) => {
43
+ if (ignoreEpipe && isEpipe(error)) return;
22
44
  lastContext?.reportInternalError(error, {
23
45
  phase: "transport",
24
- transport: transportName,
46
+ transport: options.name,
25
47
  operation
26
48
  });
27
49
  };
28
50
  const settleWaitersIfIdle = () => {
29
51
  if (pendingWrites + pendingDrains > 0) return;
52
+ if (waiters.length === 0) return;
30
53
  const error = lastError;
31
54
  lastError = void 0;
32
55
  for (const waiter of waiters.splice(0)) if (error) waiter.reject(error);
33
56
  else waiter.resolve();
34
57
  };
35
58
  const recordError = (error, operation) => {
59
+ if (ignoreEpipe && isEpipe(error)) {
60
+ closed = true;
61
+ return;
62
+ }
36
63
  lastError = normalizeError(error);
37
64
  reportInternalError(lastError, operation);
38
65
  };
66
+ const onStreamError = (error) => {
67
+ recordError(error, "stream-error");
68
+ settleWaitersIfIdle();
69
+ };
70
+ const offError = () => options.stream.off?.("error", onStreamError);
71
+ options.stream.on?.("error", onStreamError);
39
72
  const waitForDrain = () => {
40
- if (!stream.once) return;
73
+ if (!options.stream.once) return;
41
74
  pendingDrains += 1;
42
- stream.once("drain", () => {
75
+ options.stream.once("drain", () => {
43
76
  pendingDrains -= 1;
44
77
  settleWaitersIfIdle();
45
78
  });
46
79
  };
47
- const writePayload = (payload) => {
80
+ const removePending = (payload) => {
81
+ const index = pendingPayloads.indexOf(payload);
82
+ if (index >= 0) pendingPayloads.splice(index, 1);
83
+ };
84
+ const writeNow = (payload) => {
85
+ if (closed) return;
86
+ pendingPayloads.push(payload);
48
87
  pendingWrites += 1;
49
88
  try {
50
- if (stream.write(payload, (error) => {
89
+ if (options.stream.write(payload, (error) => {
90
+ removePending(payload);
51
91
  pendingWrites -= 1;
52
- if (error) recordError(error, "write");
92
+ if (error) recordError(error, options.reportOperation ?? "write");
53
93
  settleWaitersIfIdle();
54
94
  }) === false) waitForDrain();
55
95
  } catch (error) {
96
+ removePending(payload);
56
97
  pendingWrites -= 1;
57
- recordError(error, "write");
98
+ recordError(error, options.reportOperation ?? "write");
58
99
  settleWaitersIfIdle();
59
100
  }
60
101
  };
61
- return {
62
- name: transportName,
63
- minLevel: options.minLevel,
64
- log(event, context) {
65
- if (options.minLevel !== void 0 && event.level < (0, _loggerjs_core.toLevelValue)(options.minLevel)) return;
102
+ const flushBuffered = () => {
103
+ if (buffer.length === 0) return;
104
+ const payload = coalescePayloads(buffer.splice(0));
105
+ bufferedBytes = 0;
106
+ writeNow(payload);
107
+ };
108
+ const destination = {
109
+ write(payload, context) {
110
+ if (closed) return;
66
111
  lastContext = context;
67
- writePayload(codec.encode(event));
112
+ if (minLength > 0) {
113
+ buffer.push(payload);
114
+ bufferedBytes += byteLength(payload);
115
+ if (bufferedBytes >= minLength) flushBuffered();
116
+ return;
117
+ }
118
+ writeNow(payload);
68
119
  },
69
120
  flush() {
121
+ flushBuffered();
70
122
  if (pendingWrites + pendingDrains === 0) {
71
123
  const error = lastError;
72
124
  lastError = void 0;
@@ -79,8 +131,133 @@ function stdoutTransport(options = {}) {
79
131
  reject
80
132
  });
81
133
  });
134
+ },
135
+ flushSync() {
136
+ if (!options.syncWrite) {
137
+ flushBuffered();
138
+ return;
139
+ }
140
+ for (const payload of buffer.splice(0)) options.syncWrite(payload);
141
+ bufferedBytes = 0;
142
+ for (const payload of pendingPayloads.splice(0)) options.syncWrite(payload);
143
+ },
144
+ close() {
145
+ return destination.flush().then(() => new Promise((resolve, reject) => {
146
+ if (!options.stream.end) {
147
+ closed = true;
148
+ offError();
149
+ resolve();
150
+ return;
151
+ }
152
+ options.stream.end((error) => {
153
+ closed = true;
154
+ offError();
155
+ if (error && !(ignoreEpipe && isEpipe(error))) reject(error);
156
+ else resolve();
157
+ });
158
+ }), (error) => {
159
+ offError();
160
+ return Promise.reject(error);
161
+ });
162
+ },
163
+ closeSync() {
164
+ destination.flushSync();
165
+ closed = true;
166
+ offError();
167
+ }
168
+ };
169
+ return destination;
170
+ }
171
+ function createNodeFileDestination(options) {
172
+ const flags = options.flags ?? (options.append === false ? "w" : "a");
173
+ if (options.mkdir) (0, fs.mkdirSync)((0, path.dirname)(options.path), { recursive: true });
174
+ let fd;
175
+ const getFd = () => {
176
+ fd ??= (0, fs.openSync)(options.path, flags);
177
+ return fd;
178
+ };
179
+ const syncWrite = (payload) => {
180
+ if (typeof payload === "string") (0, fs.writeSync)(getFd(), payload);
181
+ else (0, fs.writeSync)(getFd(), payload);
182
+ };
183
+ if (options.sync) {
184
+ let closed = false;
185
+ const releaseSync = () => {
186
+ if (fd !== void 0) {
187
+ (0, fs.closeSync)(fd);
188
+ fd = void 0;
189
+ }
190
+ };
191
+ return {
192
+ write(payload) {
193
+ if (closed) return;
194
+ syncWrite(payload);
195
+ },
196
+ flush() {
197
+ return Promise.resolve();
198
+ },
199
+ flushSync() {},
200
+ close() {
201
+ releaseSync();
202
+ closed = true;
203
+ return Promise.resolve();
204
+ },
205
+ closeSync() {
206
+ releaseSync();
207
+ closed = true;
208
+ },
209
+ releaseSync
210
+ };
211
+ }
212
+ const stream = (0, fs.createWriteStream)(options.path, { flags });
213
+ const destination = createNodeStreamDestination({
214
+ name: options.name,
215
+ stream,
216
+ minLength: options.minLength,
217
+ syncWrite
218
+ });
219
+ const releaseSync = () => {
220
+ if (fd !== void 0) {
221
+ (0, fs.closeSync)(fd);
222
+ fd = void 0;
82
223
  }
83
224
  };
225
+ return {
226
+ ...destination,
227
+ stream,
228
+ close() {
229
+ return destination.close().finally(releaseSync);
230
+ },
231
+ closeSync() {
232
+ destination.closeSync();
233
+ releaseSync();
234
+ },
235
+ releaseSync
236
+ };
237
+ }
238
+ //#endregion
239
+ //#region src/stdout-transport.ts
240
+ function stdoutTransport(options = {}) {
241
+ const codec = options.codec ?? (0, _loggerjs_core.ndjsonCodec)();
242
+ const stream = options.stream ?? process.stdout;
243
+ const transportName = options.name ?? "stdout";
244
+ const destination = createNodeStreamDestination({
245
+ name: transportName,
246
+ stream,
247
+ minLength: options.minLength,
248
+ ignoreEpipe: options.ignoreEpipe ?? true
249
+ });
250
+ return {
251
+ name: transportName,
252
+ minLevel: options.minLevel,
253
+ log(event, context) {
254
+ if (options.minLevel !== void 0 && event.level < (0, _loggerjs_core.toLevelValue)(options.minLevel)) return;
255
+ destination.write(codec.encode(event), context);
256
+ },
257
+ flush: destination.flush,
258
+ flushSync: destination.flushSync,
259
+ close: destination.close
260
+ };
84
261
  }
85
262
  function stderrTransport(options = {}) {
86
263
  return stdoutTransport({
@@ -93,60 +270,26 @@ function stderrTransport(options = {}) {
93
270
  //#region src/file-transport.ts
94
271
  function fileTransport(options) {
95
272
  const codec = options.codec ?? (0, _loggerjs_core.ndjsonCodec)();
96
- const flags = options.flags ?? "a";
97
- const stream = (0, fs.createWriteStream)(options.path, { flags });
98
- let syncFd;
99
- const pending = [];
100
- const removePending = (payload) => {
101
- const index = pending.indexOf(payload);
102
- if (index >= 0) pending.splice(index, 1);
103
- };
104
- const getSyncFd = () => {
105
- syncFd ??= (0, fs.openSync)(options.path, flags);
106
- return syncFd;
107
- };
273
+ const destination = createNodeFileDestination({
274
+ name: options.name ?? "file",
275
+ path: options.path,
276
+ flags: options.flags,
277
+ append: options.append,
278
+ mkdir: options.mkdir,
279
+ sync: options.sync,
280
+ minLength: options.minLength
281
+ });
108
282
  return {
109
283
  name: options.name ?? "file",
110
284
  minLevel: options.minLevel,
111
- stream,
112
- log(event) {
285
+ stream: destination.stream,
286
+ log(event, context) {
113
287
  if (options.minLevel !== void 0 && event.level < (0, _loggerjs_core.toLevelValue)(options.minLevel)) return;
114
- const payload = codec.encode(event);
115
- pending.push(payload);
116
- stream.write(payload, (error) => {
117
- if (!error) removePending(payload);
118
- });
119
- },
120
- flush() {
121
- return new Promise((resolve, reject) => {
122
- stream.write("", (error) => {
123
- if (error) {
124
- reject(error);
125
- return;
126
- }
127
- pending.splice(0, pending.length);
128
- resolve();
129
- });
130
- });
131
- },
132
- flushSync() {
133
- if (pending.length === 0) return;
134
- const fd = getSyncFd();
135
- for (const payload of pending.splice(0)) if (typeof payload === "string") (0, fs.writeSync)(fd, payload);
136
- else (0, fs.writeSync)(fd, payload);
288
+ destination.write(codec.encode(event), context);
137
289
  },
138
- close() {
139
- return new Promise((resolve, reject) => {
140
- stream.end?.((error) => {
141
- if (syncFd !== void 0) {
142
- (0, fs.closeSync)(syncFd);
143
- syncFd = void 0;
144
- }
145
- if (error) reject(error);
146
- else resolve();
147
- });
148
- });
149
- }
290
+ flush: destination.flush,
291
+ flushSync: destination.flushSync,
292
+ close: destination.close
150
293
  };
151
294
  }
152
295
  //#endregion
@@ -175,23 +318,20 @@ function fileSize(path) {
175
318
  }
176
319
  function rotatingFileTransport(options) {
177
320
  const codec = options.codec ?? (0, _loggerjs_core.ndjsonCodec)();
178
- const flags = options.flags ?? "a";
179
321
  const maxBytes = normalizePositiveInteger(options.maxBytes, 10 * 1024 * 1024);
180
322
  const maxFiles = normalizeNonNegativeInteger(options.maxFiles, 5);
181
323
  const archivePath = options.archivePath ?? defaultArchivePath;
182
- let fd;
324
+ const destination = createNodeFileDestination({
325
+ name: options.name ?? "rotating-file",
326
+ path: options.path,
327
+ flags: options.flags,
328
+ append: options.append,
329
+ mkdir: options.mkdir,
330
+ sync: true
331
+ });
183
332
  let bytes = fileSize(options.path);
184
- const closeFd = () => {
185
- if (fd === void 0) return;
186
- (0, fs.closeSync)(fd);
187
- fd = void 0;
188
- };
189
- const getFd = () => {
190
- fd ??= (0, fs.openSync)(options.path, flags);
191
- return fd;
192
- };
193
333
  const rotate = () => {
194
- closeFd();
334
+ destination.releaseSync?.();
195
335
  if (maxFiles === 0) {
196
336
  if ((0, fs.existsSync)(options.path)) (0, fs.unlinkSync)(options.path);
197
337
  bytes = 0;
@@ -209,8 +349,7 @@ function rotatingFileTransport(options) {
209
349
  const writePayload = (payload) => {
210
350
  const size = payloadBytes(payload);
211
351
  if (bytes > 0 && bytes + size > maxBytes) rotate();
212
- if (typeof payload === "string") (0, fs.writeSync)(getFd(), payload);
213
- else (0, fs.writeSync)(getFd(), payload);
352
+ destination.write(payload);
214
353
  bytes += size;
215
354
  };
216
355
  return {
@@ -227,12 +366,11 @@ function rotatingFileTransport(options) {
227
366
  }
228
367
  },
229
368
  flush() {
230
- return Promise.resolve();
369
+ return destination.flush();
231
370
  },
232
- flushSync() {},
371
+ flushSync: destination.flushSync,
233
372
  close() {
234
- closeFd();
235
- return Promise.resolve();
373
+ return destination.close();
236
374
  },
237
375
  rotate,
238
376
  currentBytes: () => bytes
@@ -252,13 +390,19 @@ function nodeHttpTransport(options) {
252
390
  minLevel: options.minLevel,
253
391
  async logBatch(events) {
254
392
  if (!fetchFn) throw new Error("fetch is not available. Use Node.js 18+ or pass fetchFn.");
393
+ const transformed = await (0, _loggerjs_core.applyPayloadTransforms)(codec.encode(events), {
394
+ contentType: codec.contentType,
395
+ events,
396
+ transport: options.name ?? "node-http"
397
+ }, options.transformPayload);
255
398
  const response = await fetchFn(options.url, {
256
399
  method: options.method ?? "POST",
257
400
  headers: {
258
- "content-type": codec.contentType,
401
+ "content-type": transformed.contentType,
402
+ ...transformed.headers,
259
403
  ...options.headers
260
404
  },
261
- body: payloadToBody(codec.encode(events))
405
+ body: payloadToBody(transformed.payload)
262
406
  });
263
407
  if (!response.ok) throw new Error(`nodeHttpTransport failed with status ${response.status}`);
264
408
  }
@@ -283,6 +427,29 @@ function nodeHttpTransport(options) {
283
427
  });
284
428
  }
285
429
  //#endregion
430
+ //#region src/payload-transforms.ts
431
+ const contentEncodingByFormat = {
432
+ gzip: "gzip",
433
+ brotli: "br",
434
+ deflate: "deflate"
435
+ };
436
+ function compressPayload(payload, format, options) {
437
+ const input = (0, _loggerjs_core.encodedPayloadToUint8Array)(payload);
438
+ if (format === "brotli") return Uint8Array.from((0, node_zlib.brotliCompressSync)(input, options.brotliOptions));
439
+ if (format === "deflate") return Uint8Array.from((0, node_zlib.deflateSync)(input, options.deflateOptions));
440
+ return Uint8Array.from((0, node_zlib.gzipSync)(input, options.gzipOptions));
441
+ }
442
+ function nodeCompressionPayloadTransform(options = {}) {
443
+ const format = options.format ?? "gzip";
444
+ return (payload) => ({
445
+ payload: compressPayload(payload, format, options),
446
+ headers: {
447
+ "content-encoding": contentEncodingByFormat[format],
448
+ ...options.headers
449
+ }
450
+ });
451
+ }
452
+ //#endregion
286
453
  //#region src/syslog-transport.ts
287
454
  const severityByLevel = {
288
455
  trace: 7,
@@ -425,13 +592,58 @@ function transferablePayload(payload) {
425
592
  transferList: [copy.buffer]
426
593
  };
427
594
  }
595
+ function normalizePositiveTimeout(value) {
596
+ if (value === void 0 || !Number.isFinite(value) || value <= 0) return 0;
597
+ return Math.floor(value);
598
+ }
599
+ function protocolMessage(value) {
600
+ if (!value || typeof value !== "object") return void 0;
601
+ const message = value;
602
+ if (message.type === "loggerjs:ready") return { type: "loggerjs:ready" };
603
+ if (message.type === "loggerjs:batch:ack") {
604
+ const id = value.id;
605
+ if (typeof id === "number") return {
606
+ type: "loggerjs:batch:ack",
607
+ id
608
+ };
609
+ }
610
+ if (message.type === "loggerjs:error") {
611
+ const item = value;
612
+ return {
613
+ type: "loggerjs:error",
614
+ error: item.error,
615
+ message: typeof item.message === "string" ? item.message : void 0
616
+ };
617
+ }
618
+ }
619
+ function dropBatch(events, reason) {
620
+ (0, _loggerjs_core.incrementLoggerMetaCounter)("transport.dropped", events.length);
621
+ (0, _loggerjs_core.incrementLoggerMetaCounter)(`transport.dropped.${reason}`, events.length);
622
+ }
428
623
  function workerTransport(options = {}) {
429
624
  const codec = options.codec ?? (0, _loggerjs_core.safeJsonCodec)();
430
625
  const transportName = options.name ?? "worker";
431
626
  const fallback = options.fallback;
627
+ const readyTimeoutMs = normalizePositiveTimeout(options.readyTimeoutMs);
628
+ const ackTimeoutMs = normalizePositiveTimeout(options.ackTimeoutMs);
629
+ const autoEnd = options.autoEnd ?? true;
432
630
  let worker;
433
631
  let failed = false;
632
+ let failedError;
434
633
  let lastContext;
634
+ let ready = readyTimeoutMs === 0;
635
+ let readyPromise;
636
+ let readyTimer;
637
+ let readyResolve;
638
+ let readyReject;
639
+ let nextBatchId = 1;
640
+ const pendingBatches = /* @__PURE__ */ new Map();
641
+ const setPendingGauge = () => {
642
+ (0, _loggerjs_core.setLoggerMetaGauge)(`transport.queue.depth.${transportName}`, pendingBatches.size);
643
+ };
644
+ const setReadyGauge = (value) => {
645
+ (0, _loggerjs_core.setLoggerMetaGauge)(`transport.ready.${transportName}`, value);
646
+ };
435
647
  const reportInternalError = (error, operation) => {
436
648
  lastContext?.reportInternalError(error, {
437
649
  phase: "transport",
@@ -440,14 +652,82 @@ function workerTransport(options = {}) {
440
652
  });
441
653
  };
442
654
  const markFailed = (error, operation) => {
655
+ if (failed) return;
443
656
  failed = true;
657
+ failedError = error instanceof Error ? error : new Error(String(error));
658
+ setReadyGauge(0);
444
659
  (0, _loggerjs_core.incrementLoggerMetaCounter)("transport.worker.failed");
660
+ (0, _loggerjs_core.emitLoggerDiagnostic)({
661
+ stage: "worker",
662
+ phase: "error",
663
+ transport: transportName,
664
+ operation,
665
+ error
666
+ });
445
667
  reportInternalError(error, operation);
668
+ readyReject?.(failedError);
669
+ failPendingBatches(operation);
446
670
  };
447
671
  const onWorkerError = (error) => markFailed(error, "worker-error");
448
672
  const onWorkerExit = (code) => {
449
673
  if (typeof code === "number" && code !== 0) markFailed(code, "worker-exit");
450
674
  };
675
+ const onWorkerMessage = (value) => {
676
+ const message = protocolMessage(value);
677
+ if (!message) return;
678
+ if (message.type === "loggerjs:ready") {
679
+ ready = true;
680
+ setReadyGauge(1);
681
+ (0, _loggerjs_core.emitLoggerDiagnostic)({
682
+ stage: "worker",
683
+ phase: "end",
684
+ transport: transportName,
685
+ operation: "ready"
686
+ });
687
+ if (readyTimer) clearTimeout(readyTimer);
688
+ readyResolve?.();
689
+ return;
690
+ }
691
+ if (message.type === "loggerjs:batch:ack") {
692
+ const pending = pendingBatches.get(message.id);
693
+ if (!pending) return;
694
+ pendingBatches.delete(message.id);
695
+ setPendingGauge();
696
+ if (pending.timer) clearTimeout(pending.timer);
697
+ pending.resolve();
698
+ (0, _loggerjs_core.incrementLoggerMetaCounter)("transport.worker.ack");
699
+ return;
700
+ }
701
+ markFailed(message.error ?? new Error(message.message ?? "Worker reported an error"), "worker-message");
702
+ };
703
+ const fallbackOrDropBatch = async (events, context, reason) => {
704
+ if (fallback) {
705
+ await fallbackBatch(events, context);
706
+ return;
707
+ }
708
+ dropBatch(events, reason);
709
+ };
710
+ const failPendingBatch = (pending, reason) => {
711
+ pendingBatches.delete(pending.id);
712
+ setPendingGauge();
713
+ if (pending.timer) clearTimeout(pending.timer);
714
+ (0, _loggerjs_core.incrementLoggerMetaCounter)("transport.worker.pending-dropped", pending.events.length);
715
+ fallbackOrDropBatch(pending.events, pending.context, reason).finally(pending.resolve);
716
+ };
717
+ function failPendingBatches(reason) {
718
+ for (const pending of Array.from(pendingBatches.values())) failPendingBatch(pending, reason);
719
+ }
720
+ const waitForReady = async () => {
721
+ if (ready) return;
722
+ readyPromise ??= new Promise((resolve, reject) => {
723
+ readyResolve = resolve;
724
+ readyReject = reject;
725
+ readyTimer = setTimeout(() => {
726
+ reject(/* @__PURE__ */ new Error(`workerTransport ready timeout after ${readyTimeoutMs}ms`));
727
+ }, readyTimeoutMs);
728
+ });
729
+ await readyPromise;
730
+ };
451
731
  const getWorker = () => {
452
732
  if (failed) return void 0;
453
733
  if (worker) return worker;
@@ -455,6 +735,8 @@ function workerTransport(options = {}) {
455
735
  worker = options.worker ?? options.workerFactory?.() ?? createDefaultWorker(options);
456
736
  worker.on?.("error", onWorkerError);
457
737
  worker.on?.("exit", onWorkerExit);
738
+ worker.on?.("message", onWorkerMessage);
739
+ if (ready) setReadyGauge(1);
458
740
  return worker;
459
741
  } catch (error) {
460
742
  markFailed(error, "create-worker");
@@ -472,7 +754,14 @@ function workerTransport(options = {}) {
472
754
  lastContext = context;
473
755
  const currentWorker = getWorker();
474
756
  if (!currentWorker) {
475
- await fallbackBatch(events, context);
757
+ await fallbackOrDropBatch(events, context, "worker-unavailable");
758
+ return;
759
+ }
760
+ try {
761
+ await waitForReady();
762
+ } catch (error) {
763
+ markFailed(error, "worker-ready-timeout");
764
+ await fallbackOrDropBatch(events, context, "worker-ready-timeout");
476
765
  return;
477
766
  }
478
767
  const encoded = payloadToBytes(codec.encode(events));
@@ -487,16 +776,53 @@ function workerTransport(options = {}) {
487
776
  count: events.length,
488
777
  payload: transferable.payload
489
778
  };
779
+ let pending;
780
+ if (ackTimeoutMs > 0) {
781
+ const id = nextBatchId;
782
+ nextBatchId += 1;
783
+ message.id = id;
784
+ pending = {
785
+ id,
786
+ events,
787
+ context,
788
+ resolve: () => {}
789
+ };
790
+ pending.timer = setTimeout(() => {
791
+ if (!pending) return;
792
+ markFailed(/* @__PURE__ */ new Error(`workerTransport ack timeout after ${ackTimeoutMs}ms`), "worker-ack-timeout");
793
+ }, ackTimeoutMs);
794
+ pendingBatches.set(id, pending);
795
+ setPendingGauge();
796
+ }
490
797
  try {
491
798
  currentWorker.postMessage(message, transferable.transferList);
492
799
  } catch (error) {
493
800
  markFailed(error, "post-message");
494
- await fallbackBatch(events, context);
801
+ if (!pending) await fallbackOrDropBatch(events, context, "worker-post-message");
495
802
  }
496
803
  };
497
- return {
804
+ const transport = {
498
805
  name: transportName,
499
806
  minLevel: options.minLevel,
807
+ async ready() {
808
+ if (!getWorker()) {
809
+ if (fallback) {
810
+ await fallback.ready?.();
811
+ return;
812
+ }
813
+ throw failedError ?? /* @__PURE__ */ new Error("workerTransport is unavailable");
814
+ }
815
+ try {
816
+ await waitForReady();
817
+ } catch (error) {
818
+ markFailed(error, "worker-ready-timeout");
819
+ if (fallback) {
820
+ await fallback.ready?.();
821
+ return;
822
+ }
823
+ throw error;
824
+ }
825
+ },
500
826
  log(event, context) {
501
827
  if (options.minLevel !== void 0 && event.level < (0, _loggerjs_core.toLevelValue)(options.minLevel)) return;
502
828
  return postBatch([event], context);
@@ -505,15 +831,28 @@ function workerTransport(options = {}) {
505
831
  return postBatch(events, context);
506
832
  },
507
833
  async flush() {
834
+ await Promise.all(Array.from(pendingBatches.values(), (pending) => new Promise((resolve) => {
835
+ const originalResolve = pending.resolve;
836
+ pending.resolve = () => {
837
+ originalResolve();
838
+ resolve();
839
+ };
840
+ })));
508
841
  await fallback?.flush?.();
509
842
  },
510
843
  async close() {
844
+ await transport.flush?.();
511
845
  worker?.off?.("error", onWorkerError);
512
846
  worker?.off?.("exit", onWorkerExit);
847
+ worker?.off?.("message", onWorkerMessage);
513
848
  await fallback?.close?.();
514
- await worker?.terminate?.();
849
+ if (autoEnd) await worker?.terminate?.();
850
+ if (readyTimer) clearTimeout(readyTimer);
851
+ readyReject = void 0;
852
+ readyResolve = void 0;
515
853
  }
516
854
  };
855
+ return transport;
517
856
  }
518
857
  //#endregion
519
858
  //#region src/context.ts
@@ -546,6 +885,170 @@ function installAsyncLocalStorageContext(manager = createAsyncLocalStorageContex
546
885
  };
547
886
  }
548
887
  //#endregion
888
+ //#region src/queue-integration.ts
889
+ const defaultMethods$1 = [
890
+ "add",
891
+ "ack",
892
+ "consume",
893
+ "dequeue",
894
+ "nack",
895
+ "process",
896
+ "publish",
897
+ "receive",
898
+ "send",
899
+ "sendToQueue"
900
+ ];
901
+ const defaultRandom$6 = () => Math.random();
902
+ function defaultLevel$6(_durationMs, error) {
903
+ return error ? "error" : "debug";
904
+ }
905
+ function isPromiseLike$2(value) {
906
+ return typeof value === "object" && value !== null && typeof value.then === "function";
907
+ }
908
+ function defaultOperation$1(method) {
909
+ switch (method) {
910
+ case "add":
911
+ case "publish":
912
+ case "send":
913
+ case "sendToQueue": return "publish";
914
+ case "consume":
915
+ case "dequeue":
916
+ case "process":
917
+ case "receive": return "consume";
918
+ case "ack": return "ack";
919
+ case "nack": return "nack";
920
+ default: return "other";
921
+ }
922
+ }
923
+ function defaultPayload(args) {
924
+ return typeof args[0] === "string" && args.length > 1 ? args[1] : args[0];
925
+ }
926
+ function defaultMessageId(args) {
927
+ const payload = defaultPayload(args);
928
+ if (typeof payload !== "object" || payload === null) return void 0;
929
+ const value = payload.messageId ?? payload.id;
930
+ return value === void 0 ? void 0 : String(value);
931
+ }
932
+ function payloadFor(args, method, options) {
933
+ if (!options.capturePayload) return void 0;
934
+ const payload = options.getPayload?.(args, method) ?? defaultPayload(args);
935
+ return payload === void 0 ? void 0 : (0, _loggerjs_core.normalizeValue)(payload, {
936
+ maxDepth: 4,
937
+ maxObjectKeys: 80
938
+ });
939
+ }
940
+ function queueIntegration(options = {}) {
941
+ const targets = options.targets ?? (options.client ? [{
942
+ client: options.client,
943
+ name: options.name,
944
+ system: options.system,
945
+ queueName: options.queueName,
946
+ methods: options.methods
947
+ }] : []);
948
+ const captureAll = options.captureAll ?? options.captureSuccessful ?? false;
949
+ const sampleRate = options.sampleRate ?? 1;
950
+ const random = options.random ?? defaultRandom$6;
951
+ const levelFor = options.level ?? defaultLevel$6;
952
+ const shouldCapture = (durationMs, error) => {
953
+ if (error) return true;
954
+ if (options.minDurationMs !== void 0 && durationMs >= options.minDurationMs) return true;
955
+ if (!captureAll) return false;
956
+ return sampleRate >= 1 || random() < sampleRate;
957
+ };
958
+ return {
959
+ name: "queue",
960
+ setup(api) {
961
+ const capture = api.guard((input) => api.capture(input));
962
+ const disposers = [];
963
+ let disposed = false;
964
+ const captureResult = (info, started, args, error) => {
965
+ const durationMs = Date.now() - started;
966
+ if (disposed || !shouldCapture(durationMs, error)) return;
967
+ capture({
968
+ level: levelFor(durationMs, error, info),
969
+ message: error ? `Queue error ${info.operation} ${info.queueName ?? info.method}` : `Queue ${info.operation} ${info.queueName ?? info.method}`,
970
+ error,
971
+ props: { queue: {
972
+ kind: info.target,
973
+ system: info.system,
974
+ queueName: info.queueName,
975
+ method: info.method,
976
+ operation: info.operation,
977
+ durationMs,
978
+ messageId: options.getMessageId?.(args, info.method) ?? defaultMessageId(args),
979
+ payload: payloadFor(args, info.method, options)
980
+ } }
981
+ });
982
+ };
983
+ for (const target of targets) {
984
+ const methods = target.methods ?? options.methods ?? defaultMethods$1;
985
+ for (const method of methods) {
986
+ const original = target.client[method];
987
+ if (typeof original !== "function") continue;
988
+ const originalMethod = original;
989
+ const targetName = target.name ?? options.name ?? "queue";
990
+ const system = target.system ?? options.system;
991
+ target.client[method] = function wrappedQueueMethod(...args) {
992
+ const started = Date.now();
993
+ const operation = options.getOperation?.(method, args) ?? defaultOperation$1(method);
994
+ const info = {
995
+ target: targetName,
996
+ system,
997
+ queueName: options.getQueueName?.(args, method) ?? target.queueName ?? options.queueName ?? (typeof args[0] === "string" ? args[0] : void 0),
998
+ method,
999
+ operation
1000
+ };
1001
+ try {
1002
+ const result = originalMethod.apply(this, args);
1003
+ if (isPromiseLike$2(result)) return result.then((value) => {
1004
+ captureResult(info, started, args, void 0);
1005
+ return value;
1006
+ }, (error) => {
1007
+ captureResult(info, started, args, error);
1008
+ throw error;
1009
+ });
1010
+ captureResult(info, started, args, void 0);
1011
+ return result;
1012
+ } catch (error) {
1013
+ captureResult(info, started, args, error);
1014
+ throw error;
1015
+ }
1016
+ };
1017
+ disposers.push(() => {
1018
+ target.client[method] = originalMethod;
1019
+ });
1020
+ }
1021
+ }
1022
+ return () => {
1023
+ if (disposed) return;
1024
+ disposed = true;
1025
+ for (const dispose of disposers) dispose();
1026
+ };
1027
+ }
1028
+ };
1029
+ }
1030
+ //#endregion
1031
+ //#region src/bullmq-integration.ts
1032
+ const bullMqMethods = [
1033
+ "add",
1034
+ "addBulk",
1035
+ "process"
1036
+ ];
1037
+ function queueName(args, fallback) {
1038
+ const first = args[0];
1039
+ return typeof first === "string" ? first : fallback;
1040
+ }
1041
+ function bullMqIntegration(options) {
1042
+ return queueIntegration({
1043
+ ...options,
1044
+ name: options.name ?? "bullmq",
1045
+ system: "bullmq",
1046
+ methods: options.methods ?? bullMqMethods,
1047
+ queueName: options.queueName ?? options.client.name,
1048
+ getQueueName: (args) => queueName(args, options.queueName ?? options.client.name)
1049
+ });
1050
+ }
1051
+ //#endregion
549
1052
  //#region src/cli-integration.ts
550
1053
  const defaultSignals = ["SIGINT", "SIGTERM"];
551
1054
  function defaultProcess() {
@@ -558,7 +1061,7 @@ function defaultSanitizeArg(arg) {
558
1061
  function defaultCommand(argv) {
559
1062
  return argv[1] ?? argv[0];
560
1063
  }
561
- function levelFor(level, event, code) {
1064
+ function levelFor$2(level, event, code) {
562
1065
  if (typeof level === "function") return level(event, code);
563
1066
  if (level) return level;
564
1067
  if (event === "signal") return "warn";
@@ -611,7 +1114,7 @@ function captureCliIntegration(options = {}) {
611
1114
  env: pickEnv(processLike.env, options.captureEnv)
612
1115
  };
613
1116
  if (captureStart) capture({
614
- level: levelFor(options.level, "start"),
1117
+ level: levelFor$2(options.level, "start"),
615
1118
  message: `CLI start${command ? ` ${command}` : ""}`,
616
1119
  props: { cli: {
617
1120
  ...cliBase,
@@ -622,7 +1125,7 @@ function captureCliIntegration(options = {}) {
622
1125
  const onExit = (code) => {
623
1126
  const exitCode = code ?? processLike.exitCode ?? 0;
624
1127
  capture({
625
- level: levelFor(options.level, "exit", exitCode),
1128
+ level: levelFor$2(options.level, "exit", exitCode),
626
1129
  message: `CLI exit ${String(exitCode)}`,
627
1130
  props: { cli: {
628
1131
  ...cliBase,
@@ -637,7 +1140,7 @@ function captureCliIntegration(options = {}) {
637
1140
  if (captureSignals && processLike.on) for (const signal of signals) {
638
1141
  const onSignal = () => {
639
1142
  capture({
640
- level: levelFor(options.level, "signal", signal),
1143
+ level: levelFor$2(options.level, "signal", signal),
641
1144
  message: `CLI signal ${signal}`,
642
1145
  props: { cli: {
643
1146
  ...cliBase,
@@ -659,18 +1162,18 @@ function captureCliIntegration(options = {}) {
659
1162
  }
660
1163
  //#endregion
661
1164
  //#region src/database-integration.ts
662
- const defaultMethods$1 = [
1165
+ const defaultMethods = [
663
1166
  "query",
664
1167
  "execute",
665
1168
  "run",
666
1169
  "all",
667
1170
  "get"
668
1171
  ];
669
- const defaultRandom$6 = () => Math.random();
670
- function defaultLevel$6(_durationMs, error) {
1172
+ const defaultRandom$5 = () => Math.random();
1173
+ function defaultLevel$5(_durationMs, error) {
671
1174
  return error ? "error" : "debug";
672
1175
  }
673
- function isPromiseLike$2(value) {
1176
+ function isPromiseLike$1(value) {
674
1177
  return typeof value === "object" && value !== null && typeof value.then === "function";
675
1178
  }
676
1179
  function defaultStatement(args) {
@@ -704,8 +1207,8 @@ function databaseIntegration(options = {}) {
704
1207
  }] : []);
705
1208
  const captureAll = options.captureAll ?? options.captureSuccessful ?? false;
706
1209
  const sampleRate = options.sampleRate ?? 1;
707
- const random = options.random ?? defaultRandom$6;
708
- const levelFor = options.level ?? defaultLevel$6;
1210
+ const random = options.random ?? defaultRandom$5;
1211
+ const levelFor = options.level ?? defaultLevel$5;
709
1212
  const shouldCapture = (durationMs, error) => {
710
1213
  if (error) return true;
711
1214
  if (options.minDurationMs !== void 0 && durationMs >= options.minDurationMs) return true;
@@ -736,7 +1239,7 @@ function databaseIntegration(options = {}) {
736
1239
  });
737
1240
  };
738
1241
  for (const target of targets) {
739
- const methods = target.methods ?? options.methods ?? defaultMethods$1;
1242
+ const methods = target.methods ?? options.methods ?? defaultMethods;
740
1243
  for (const method of methods) {
741
1244
  const original = target.client[method];
742
1245
  if (typeof original !== "function") continue;
@@ -753,7 +1256,7 @@ function databaseIntegration(options = {}) {
753
1256
  };
754
1257
  try {
755
1258
  const result = originalMethod.apply(this, args);
756
- if (isPromiseLike$2(result)) return result.then((value) => {
1259
+ if (isPromiseLike$1(result)) return result.then((value) => {
757
1260
  captureResult(info, started, args, void 0);
758
1261
  return value;
759
1262
  }, (error) => {
@@ -782,14 +1285,14 @@ function databaseIntegration(options = {}) {
782
1285
  }
783
1286
  //#endregion
784
1287
  //#region src/express-integration.ts
785
- const defaultRandom$5 = () => Math.random();
1288
+ const defaultRandom$4 = () => Math.random();
786
1289
  function defaultSanitizeUrl$3(url) {
787
1290
  const queryIndex = url.indexOf("?");
788
1291
  const hashIndex = url.indexOf("#");
789
1292
  const end = Math.min(queryIndex >= 0 ? queryIndex : url.length, hashIndex >= 0 ? hashIndex : url.length);
790
1293
  return url.slice(0, end);
791
1294
  }
792
- function defaultLevel$5(status, _req, _res, aborted) {
1295
+ function defaultLevel$4(status, _req, _res, aborted) {
793
1296
  if (aborted) return "warn";
794
1297
  if (status >= 500) return "error";
795
1298
  if (status >= 400) return "warn";
@@ -844,9 +1347,9 @@ function expressIntegration(logger, options = {}) {
844
1347
  const captureAll = options.captureAll ?? options.captureSuccessful ?? false;
845
1348
  const captureAborted = options.captureAborted ?? true;
846
1349
  const sampleRate = options.sampleRate ?? 1;
847
- const random = options.random ?? defaultRandom$5;
1350
+ const random = options.random ?? defaultRandom$4;
848
1351
  const sanitizeUrl = options.sanitizeUrl ?? defaultSanitizeUrl$3;
849
- const levelFor = options.level ?? defaultLevel$5;
1352
+ const levelFor = options.level ?? defaultLevel$4;
850
1353
  const routeFor = options.getRoute ?? defaultRoute$1;
851
1354
  const bindContext = options.bindContext ?? true;
852
1355
  return (req, res, next) => {
@@ -907,14 +1410,14 @@ function expressIntegration(logger, options = {}) {
907
1410
  }
908
1411
  //#endregion
909
1412
  //#region src/fastify-integration.ts
910
- const defaultRandom$4 = () => Math.random();
1413
+ const defaultRandom$3 = () => Math.random();
911
1414
  function defaultSanitizeUrl$2(url) {
912
1415
  const queryIndex = url.indexOf("?");
913
1416
  const hashIndex = url.indexOf("#");
914
1417
  const end = Math.min(queryIndex >= 0 ? queryIndex : url.length, hashIndex >= 0 ? hashIndex : url.length);
915
1418
  return url.slice(0, end);
916
1419
  }
917
- function defaultLevel$4(status, _request, _reply, error) {
1420
+ function defaultLevel$3(status, _request, _reply, error) {
918
1421
  if (error || status >= 500) return "error";
919
1422
  if (status >= 400) return "warn";
920
1423
  return "info";
@@ -949,9 +1452,9 @@ function fastifyIntegration(logger, options = {}) {
949
1452
  const minStatus = options.minStatus ?? 400;
950
1453
  const captureAll = options.captureAll ?? options.captureSuccessful ?? false;
951
1454
  const sampleRate = options.sampleRate ?? 1;
952
- const random = options.random ?? defaultRandom$4;
1455
+ const random = options.random ?? defaultRandom$3;
953
1456
  const sanitizeUrl = options.sanitizeUrl ?? defaultSanitizeUrl$2;
954
- const levelFor = options.level ?? defaultLevel$4;
1457
+ const levelFor = options.level ?? defaultLevel$3;
955
1458
  const routeFor = options.getRoute ?? defaultRoute;
956
1459
  const bindContext = options.bindContext ?? true;
957
1460
  const states = /* @__PURE__ */ new WeakMap();
@@ -1021,15 +1524,131 @@ function fastifyIntegration(logger, options = {}) {
1021
1524
  };
1022
1525
  }
1023
1526
  //#endregion
1527
+ //#region src/hapi-integration.ts
1528
+ function responseStatus(response) {
1529
+ if (!response) return 200;
1530
+ if ("statusCode" in response && typeof response.statusCode === "number") return response.statusCode;
1531
+ if ("output" in response && typeof response.output?.statusCode === "number") return response.output.statusCode;
1532
+ return 200;
1533
+ }
1534
+ function levelFor$1(status) {
1535
+ if (status >= 500) return "error";
1536
+ if (status >= 400) return "warn";
1537
+ return "info";
1538
+ }
1539
+ function hapiIntegration(logger, options = {}) {
1540
+ const minStatus = options.minStatus ?? 400;
1541
+ const captureAll = options.captureAll ?? false;
1542
+ const bindContext = options.bindContext ?? true;
1543
+ const getLevel = options.level ?? levelFor$1;
1544
+ return {
1545
+ name: "loggerjs-hapi",
1546
+ register(server) {
1547
+ server.ext("onRequest", (request, h) => {
1548
+ const context = {
1549
+ httpMethod: (request.method ?? "GET").toUpperCase(),
1550
+ httpUrl: request.url?.pathname ?? request.path ?? "/",
1551
+ requestId: options.getRequestId?.(request) ?? request.info?.id,
1552
+ ...options.context?.(request)
1553
+ };
1554
+ request.app = {
1555
+ ...request.app,
1556
+ loggerjsStartedAt: Date.now()
1557
+ };
1558
+ if (bindContext) return (0, _loggerjs_core.withContext)(context, () => h.continue);
1559
+ return h.continue;
1560
+ });
1561
+ server.events?.on?.("response", (request) => {
1562
+ const status = responseStatus(request.response);
1563
+ if (!captureAll && status < minStatus) return;
1564
+ const method = (request.method ?? "GET").toUpperCase();
1565
+ const url = request.url?.pathname ?? request.path ?? "/";
1566
+ const requestId = options.getRequestId?.(request) ?? request.info?.id;
1567
+ const started = request.app?.loggerjsStartedAt;
1568
+ logger.log(getLevel(status, request), `Hapi ${status} ${method} ${url}`, { http: {
1569
+ framework: "hapi",
1570
+ kind: "hapi",
1571
+ method,
1572
+ url,
1573
+ status,
1574
+ durationMs: started ? Date.now() - started : void 0,
1575
+ requestId,
1576
+ remoteAddress: request.info?.remoteAddress
1577
+ } });
1578
+ });
1579
+ }
1580
+ };
1581
+ }
1582
+ //#endregion
1583
+ //#region src/koa-integration.ts
1584
+ function sanitizeUrl(url) {
1585
+ return url.split(/[?#]/, 1)[0] || "/";
1586
+ }
1587
+ function levelFor(status, _ctx, error) {
1588
+ if (error || status >= 500) return "error";
1589
+ if (status >= 400) return "warn";
1590
+ return "info";
1591
+ }
1592
+ function koaIntegration(logger, options = {}) {
1593
+ const minStatus = options.minStatus ?? 400;
1594
+ const captureAll = options.captureAll ?? false;
1595
+ const bindContext = options.bindContext ?? true;
1596
+ const sanitize = options.sanitizeUrl ?? sanitizeUrl;
1597
+ const getLevel = options.level ?? levelFor;
1598
+ return async (ctx, next) => {
1599
+ const started = Date.now();
1600
+ const method = (ctx.method ?? "GET").toUpperCase();
1601
+ const url = sanitize(ctx.originalUrl ?? ctx.url ?? ctx.path ?? "/");
1602
+ const requestId = options.getRequestId?.(ctx) ?? ctx.state?.requestId;
1603
+ const context = {
1604
+ httpMethod: method,
1605
+ httpUrl: url,
1606
+ requestId,
1607
+ ...options.context?.(ctx)
1608
+ };
1609
+ let error;
1610
+ try {
1611
+ if (bindContext) await (0, _loggerjs_core.withContext)(context, next);
1612
+ else await next();
1613
+ } catch (caught) {
1614
+ error = caught;
1615
+ throw caught;
1616
+ } finally {
1617
+ const status = ctx.status ?? (error ? 500 : 200);
1618
+ if (error || captureAll || status >= minStatus) logger.log(getLevel(status, ctx, error), `Koa ${status} ${method} ${url}`, {
1619
+ http: {
1620
+ framework: "koa",
1621
+ kind: "koa",
1622
+ method,
1623
+ url,
1624
+ status,
1625
+ durationMs: Date.now() - started,
1626
+ requestId,
1627
+ remoteAddress: ctx.ip
1628
+ },
1629
+ error
1630
+ });
1631
+ }
1632
+ };
1633
+ }
1634
+ //#endregion
1635
+ //#region src/nest-integration.ts
1636
+ function nestMiddlewareIntegration(logger, options = {}) {
1637
+ return expressIntegration(logger, {
1638
+ name: "nestjs",
1639
+ ...options
1640
+ });
1641
+ }
1642
+ //#endregion
1024
1643
  //#region src/node-fetch-integration.ts
1025
- const defaultRandom$3 = () => Math.random();
1644
+ const defaultRandom$2 = () => Math.random();
1026
1645
  function defaultSanitizeUrl$1(url) {
1027
1646
  const queryIndex = url.indexOf("?");
1028
1647
  const hashIndex = url.indexOf("#");
1029
1648
  const end = Math.min(queryIndex >= 0 ? queryIndex : url.length, hashIndex >= 0 ? hashIndex : url.length);
1030
1649
  return url.slice(0, end);
1031
1650
  }
1032
- function defaultLevel$3(status, error) {
1651
+ function defaultLevel$2(status, error) {
1033
1652
  if (error || status === void 0 || status >= 500) return "error";
1034
1653
  if (status >= 400) return "warn";
1035
1654
  return "info";
@@ -1072,9 +1691,9 @@ function nodeFetchIntegration(options = {}) {
1072
1691
  const minStatus = options.minStatus ?? 400;
1073
1692
  const captureAll = options.captureAll ?? options.captureSuccessful ?? false;
1074
1693
  const sampleRate = options.sampleRate ?? 1;
1075
- const random = options.random ?? defaultRandom$3;
1694
+ const random = options.random ?? defaultRandom$2;
1076
1695
  const sanitizeUrl = options.sanitizeUrl ?? defaultSanitizeUrl$1;
1077
- const levelFor = options.level ?? defaultLevel$3;
1696
+ const levelFor = options.level ?? defaultLevel$2;
1078
1697
  const shouldCapture = (status, error) => {
1079
1698
  if (error) return true;
1080
1699
  if (status !== void 0 && status >= minStatus) return true;
@@ -1131,16 +1750,16 @@ function nodeFetchIntegration(options = {}) {
1131
1750
  }
1132
1751
  //#endregion
1133
1752
  //#region src/node-http-client-integration.ts
1134
- const requireBuiltin$1 = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);
1135
- const defaultRandom$2 = () => Math.random();
1753
+ const requireBuiltin$2 = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);
1754
+ const defaultRandom$1 = () => Math.random();
1136
1755
  let cachedHttpModule;
1137
1756
  let cachedHttpsModule;
1138
1757
  function loadBuiltin(name, fallback) {
1139
1758
  try {
1140
- return requireBuiltin$1(name);
1759
+ return requireBuiltin$2(name);
1141
1760
  } catch {
1142
1761
  try {
1143
- return requireBuiltin$1(fallback);
1762
+ return requireBuiltin$2(fallback);
1144
1763
  } catch {
1145
1764
  return null;
1146
1765
  }
@@ -1162,7 +1781,7 @@ function defaultSanitizeUrl(url) {
1162
1781
  const end = Math.min(queryIndex >= 0 ? queryIndex : url.length, hashIndex >= 0 ? hashIndex : url.length);
1163
1782
  return url.slice(0, end);
1164
1783
  }
1165
- function defaultLevel$2(status, error) {
1784
+ function defaultLevel$1(status, error) {
1166
1785
  if (error || status === void 0 || status >= 500) return "error";
1167
1786
  if (status >= 400) return "warn";
1168
1787
  return "info";
@@ -1247,9 +1866,9 @@ function nodeHttpClientIntegration(options = {}) {
1247
1866
  const minStatus = options.minStatus ?? 400;
1248
1867
  const captureAll = options.captureAll ?? options.captureSuccessful ?? false;
1249
1868
  const sampleRate = options.sampleRate ?? 1;
1250
- const random = options.random ?? defaultRandom$2;
1869
+ const random = options.random ?? defaultRandom$1;
1251
1870
  const sanitizeUrl = options.sanitizeUrl ?? defaultSanitizeUrl;
1252
- const levelFor = options.level ?? defaultLevel$2;
1871
+ const levelFor = options.level ?? defaultLevel$1;
1253
1872
  const shouldCapture = (status, error) => {
1254
1873
  if (error) return true;
1255
1874
  if (status !== void 0 && status >= minStatus) return true;
@@ -1319,12 +1938,19 @@ function nodeHttpClientIntegration(options = {}) {
1319
1938
  }
1320
1939
  //#endregion
1321
1940
  //#region src/process-integration.ts
1941
+ const signalExitCodes = {
1942
+ SIGINT: 130,
1943
+ SIGTERM: 143
1944
+ };
1322
1945
  function captureProcessIntegration(options = {}) {
1323
1946
  const uncaughtException = options.uncaughtException ?? true;
1324
1947
  const unhandledRejection = options.unhandledRejection ?? true;
1325
1948
  const warning = options.warning ?? true;
1326
1949
  const beforeExitFlush = options.beforeExitFlush ?? true;
1327
1950
  const exitFlush = options.exitFlush ?? true;
1951
+ const signalFlush = options.signalFlush ?? true;
1952
+ const signals = options.signals ?? ["SIGTERM"];
1953
+ const exitOnSignal = options.exitOnSignal ?? true;
1328
1954
  const exitOnUncaught = options.exitOnUncaught ?? true;
1329
1955
  const flushTimeoutMs = options.flushTimeoutMs ?? 250;
1330
1956
  const exitFn = options.exitFn ?? ((code) => process.exit(code));
@@ -1414,6 +2040,23 @@ function captureProcessIntegration(options = {}) {
1414
2040
  process.on("exit", onExit);
1415
2041
  disposers.push(() => process.off("exit", onExit));
1416
2042
  }
2043
+ if (signalFlush) for (const signal of signals) {
2044
+ const onSignal = () => {
2045
+ capture({
2046
+ level: "fatal",
2047
+ message: `Process signal ${signal}`,
2048
+ props: { process: {
2049
+ kind: "signal",
2050
+ signal
2051
+ } }
2052
+ });
2053
+ flushSync();
2054
+ const flushed = flushBounded();
2055
+ if (exitOnSignal) flushed.finally(() => exitFn(signalExitCodes[signal] ?? 1));
2056
+ };
2057
+ process.on(signal, onSignal);
2058
+ disposers.push(() => process.off(signal, onSignal));
2059
+ }
1417
2060
  return () => {
1418
2061
  if (disposed) return;
1419
2062
  disposed = true;
@@ -1423,148 +2066,53 @@ function captureProcessIntegration(options = {}) {
1423
2066
  };
1424
2067
  }
1425
2068
  //#endregion
1426
- //#region src/queue-integration.ts
1427
- const defaultMethods = [
1428
- "add",
1429
- "ack",
1430
- "consume",
1431
- "dequeue",
1432
- "nack",
1433
- "process",
1434
- "publish",
1435
- "receive",
1436
- "send",
1437
- "sendToQueue"
2069
+ //#region src/prisma-integration.ts
2070
+ const prismaMethods = [
2071
+ "$executeRaw",
2072
+ "$executeRawUnsafe",
2073
+ "$queryRaw",
2074
+ "$queryRawUnsafe"
1438
2075
  ];
1439
- const defaultRandom$1 = () => Math.random();
1440
- function defaultLevel$1(_durationMs, error) {
1441
- return error ? "error" : "debug";
1442
- }
1443
- function isPromiseLike$1(value) {
1444
- return typeof value === "object" && value !== null && typeof value.then === "function";
1445
- }
1446
- function defaultOperation$1(method) {
1447
- switch (method) {
1448
- case "add":
1449
- case "publish":
1450
- case "send":
1451
- case "sendToQueue": return "publish";
1452
- case "consume":
1453
- case "dequeue":
1454
- case "process":
1455
- case "receive": return "consume";
1456
- case "ack": return "ack";
1457
- case "nack": return "nack";
1458
- default: return "other";
1459
- }
2076
+ function prismaStatement(args) {
2077
+ const first = args[0];
2078
+ if (typeof first === "string") return first;
2079
+ if (Array.isArray(first) && "raw" in first) return first.raw?.join("?");
1460
2080
  }
1461
- function defaultPayload(args) {
1462
- return typeof args[0] === "string" && args.length > 1 ? args[1] : args[0];
2081
+ function prismaIntegration(options) {
2082
+ return databaseIntegration({
2083
+ ...options,
2084
+ name: options.name ?? "prisma",
2085
+ system: "prisma",
2086
+ methods: prismaMethods,
2087
+ getStatement: prismaStatement
2088
+ });
1463
2089
  }
1464
- function defaultMessageId(args) {
1465
- const payload = defaultPayload(args);
1466
- if (typeof payload !== "object" || payload === null) return void 0;
1467
- const value = payload.messageId ?? payload.id;
1468
- return value === void 0 ? void 0 : String(value);
2090
+ //#endregion
2091
+ //#region src/redis-integration.ts
2092
+ const redisMethods = [
2093
+ "del",
2094
+ "get",
2095
+ "hget",
2096
+ "hset",
2097
+ "lpush",
2098
+ "publish",
2099
+ "rpush",
2100
+ "set",
2101
+ "xadd"
2102
+ ];
2103
+ function redisStatement(args, method) {
2104
+ const key = args[0] === void 0 ? "" : ` ${String(args[0])}`;
2105
+ return `${method.toUpperCase()}${key}`;
1469
2106
  }
1470
- function payloadFor(args, method, options) {
1471
- if (!options.capturePayload) return void 0;
1472
- const payload = options.getPayload?.(args, method) ?? defaultPayload(args);
1473
- return payload === void 0 ? void 0 : (0, _loggerjs_core.normalizeValue)(payload, {
1474
- maxDepth: 4,
1475
- maxObjectKeys: 80
2107
+ function redisIntegration(options) {
2108
+ return databaseIntegration({
2109
+ ...options,
2110
+ name: options.name ?? "redis",
2111
+ system: "redis",
2112
+ methods: options.methods ?? redisMethods,
2113
+ getStatement: redisStatement
1476
2114
  });
1477
2115
  }
1478
- function queueIntegration(options = {}) {
1479
- const targets = options.targets ?? (options.client ? [{
1480
- client: options.client,
1481
- name: options.name,
1482
- system: options.system,
1483
- queueName: options.queueName,
1484
- methods: options.methods
1485
- }] : []);
1486
- const captureAll = options.captureAll ?? options.captureSuccessful ?? false;
1487
- const sampleRate = options.sampleRate ?? 1;
1488
- const random = options.random ?? defaultRandom$1;
1489
- const levelFor = options.level ?? defaultLevel$1;
1490
- const shouldCapture = (durationMs, error) => {
1491
- if (error) return true;
1492
- if (options.minDurationMs !== void 0 && durationMs >= options.minDurationMs) return true;
1493
- if (!captureAll) return false;
1494
- return sampleRate >= 1 || random() < sampleRate;
1495
- };
1496
- return {
1497
- name: "queue",
1498
- setup(api) {
1499
- const capture = api.guard((input) => api.capture(input));
1500
- const disposers = [];
1501
- let disposed = false;
1502
- const captureResult = (info, started, args, error) => {
1503
- const durationMs = Date.now() - started;
1504
- if (disposed || !shouldCapture(durationMs, error)) return;
1505
- capture({
1506
- level: levelFor(durationMs, error, info),
1507
- message: error ? `Queue error ${info.operation} ${info.queueName ?? info.method}` : `Queue ${info.operation} ${info.queueName ?? info.method}`,
1508
- error,
1509
- props: { queue: {
1510
- kind: info.target,
1511
- system: info.system,
1512
- queueName: info.queueName,
1513
- method: info.method,
1514
- operation: info.operation,
1515
- durationMs,
1516
- messageId: options.getMessageId?.(args, info.method) ?? defaultMessageId(args),
1517
- payload: payloadFor(args, info.method, options)
1518
- } }
1519
- });
1520
- };
1521
- for (const target of targets) {
1522
- const methods = target.methods ?? options.methods ?? defaultMethods;
1523
- for (const method of methods) {
1524
- const original = target.client[method];
1525
- if (typeof original !== "function") continue;
1526
- const originalMethod = original;
1527
- const targetName = target.name ?? options.name ?? "queue";
1528
- const system = target.system ?? options.system;
1529
- target.client[method] = function wrappedQueueMethod(...args) {
1530
- const started = Date.now();
1531
- const operation = options.getOperation?.(method, args) ?? defaultOperation$1(method);
1532
- const info = {
1533
- target: targetName,
1534
- system,
1535
- queueName: options.getQueueName?.(args, method) ?? target.queueName ?? options.queueName ?? (typeof args[0] === "string" ? args[0] : void 0),
1536
- method,
1537
- operation
1538
- };
1539
- try {
1540
- const result = originalMethod.apply(this, args);
1541
- if (isPromiseLike$1(result)) return result.then((value) => {
1542
- captureResult(info, started, args, void 0);
1543
- return value;
1544
- }, (error) => {
1545
- captureResult(info, started, args, error);
1546
- throw error;
1547
- });
1548
- captureResult(info, started, args, void 0);
1549
- return result;
1550
- } catch (error) {
1551
- captureResult(info, started, args, error);
1552
- throw error;
1553
- }
1554
- };
1555
- disposers.push(() => {
1556
- target.client[method] = originalMethod;
1557
- });
1558
- }
1559
- }
1560
- return () => {
1561
- if (disposed) return;
1562
- disposed = true;
1563
- for (const dispose of disposers) dispose();
1564
- };
1565
- }
1566
- };
1567
- }
1568
2116
  //#endregion
1569
2117
  //#region src/serverless-integration.ts
1570
2118
  const defaultRandom = () => Math.random();
@@ -1676,15 +2224,15 @@ const defaultChannels = [
1676
2224
  "undici:request:trailers",
1677
2225
  "undici:request:error"
1678
2226
  ];
1679
- const requireBuiltin = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);
1680
- let cachedDiagnosticsChannel;
2227
+ const requireBuiltin$1 = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);
2228
+ let cachedDiagnosticsChannel$1;
1681
2229
  function diagnosticsChannelIntegration(options = {}) {
1682
2230
  const channels = options.channels ?? defaultChannels;
1683
2231
  const captureMessage = options.captureMessage ?? false;
1684
2232
  return {
1685
2233
  name: "diagnostics-channel",
1686
2234
  setup(api) {
1687
- const diagnosticsChannel = options.diagnosticsChannel === void 0 ? loadDiagnosticsChannel() : options.diagnosticsChannel;
2235
+ const diagnosticsChannel = options.diagnosticsChannel === void 0 ? loadDiagnosticsChannel$1() : options.diagnosticsChannel;
1688
2236
  if (!diagnosticsChannel) return;
1689
2237
  const disposers = [];
1690
2238
  let disposed = false;
@@ -1711,6 +2259,23 @@ function diagnosticsChannelIntegration(options = {}) {
1711
2259
  }
1712
2260
  };
1713
2261
  }
2262
+ function loadDiagnosticsChannel$1() {
2263
+ if (cachedDiagnosticsChannel$1 !== void 0) return cachedDiagnosticsChannel$1;
2264
+ try {
2265
+ cachedDiagnosticsChannel$1 = requireBuiltin$1("node:diagnostics_channel");
2266
+ } catch {
2267
+ try {
2268
+ cachedDiagnosticsChannel$1 = requireBuiltin$1("diagnostics_channel");
2269
+ } catch {
2270
+ cachedDiagnosticsChannel$1 = null;
2271
+ }
2272
+ }
2273
+ return cachedDiagnosticsChannel$1;
2274
+ }
2275
+ //#endregion
2276
+ //#region src/logger-diagnostics.ts
2277
+ const requireBuiltin = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);
2278
+ let cachedDiagnosticsChannel;
1714
2279
  function loadDiagnosticsChannel() {
1715
2280
  if (cachedDiagnosticsChannel !== void 0) return cachedDiagnosticsChannel;
1716
2281
  try {
@@ -1724,7 +2289,31 @@ function loadDiagnosticsChannel() {
1724
2289
  }
1725
2290
  return cachedDiagnosticsChannel;
1726
2291
  }
2292
+ function installLoggerDiagnosticsChannel(options = {}) {
2293
+ const diagnosticsChannel = options.diagnosticsChannel === void 0 ? loadDiagnosticsChannel() : options.diagnosticsChannel;
2294
+ if (!diagnosticsChannel) return () => {};
2295
+ const prefix = options.prefix ?? "loggerjs";
2296
+ const channels = /* @__PURE__ */ new Map();
2297
+ const channelFor = (stage) => {
2298
+ const name = `${prefix}.${stage}`;
2299
+ let channel = channels.get(name);
2300
+ if (!channel) {
2301
+ channel = diagnosticsChannel.channel(name);
2302
+ channels.set(name, channel);
2303
+ }
2304
+ return channel;
2305
+ };
2306
+ const sink = (event) => {
2307
+ channelFor(event.stage).publish(event);
2308
+ };
2309
+ sink.enabled = (stage) => channelFor(stage).hasSubscribers !== false;
2310
+ const previous = (0, _loggerjs_core.setLoggerDiagnosticSink)(sink);
2311
+ return () => {
2312
+ (0, _loggerjs_core.setLoggerDiagnosticSink)(previous);
2313
+ };
2314
+ }
1727
2315
  //#endregion
2316
+ exports.bullMqIntegration = bullMqIntegration;
1728
2317
  exports.captureCliIntegration = captureCliIntegration;
1729
2318
  exports.captureProcessIntegration = captureProcessIntegration;
1730
2319
  exports.createAsyncLocalStorageContextManager = createAsyncLocalStorageContextManager;
@@ -1734,12 +2323,19 @@ exports.expressIntegration = expressIntegration;
1734
2323
  exports.fastifyIntegration = fastifyIntegration;
1735
2324
  exports.fileTransport = fileTransport;
1736
2325
  exports.formatSyslogMessage = formatSyslogMessage;
2326
+ exports.hapiIntegration = hapiIntegration;
1737
2327
  exports.installAsyncLocalStorageContext = installAsyncLocalStorageContext;
2328
+ exports.installLoggerDiagnosticsChannel = installLoggerDiagnosticsChannel;
2329
+ exports.koaIntegration = koaIntegration;
2330
+ exports.nestMiddlewareIntegration = nestMiddlewareIntegration;
2331
+ exports.nodeCompressionPayloadTransform = nodeCompressionPayloadTransform;
1738
2332
  exports.nodeFetchIntegration = nodeFetchIntegration;
1739
2333
  exports.nodeHttpClientIntegration = nodeHttpClientIntegration;
1740
2334
  exports.nodeHttpTransport = nodeHttpTransport;
1741
2335
  exports.nodeSyslogTransport = nodeSyslogTransport;
2336
+ exports.prismaIntegration = prismaIntegration;
1742
2337
  exports.queueIntegration = queueIntegration;
2338
+ exports.redisIntegration = redisIntegration;
1743
2339
  exports.rotatingFileTransport = rotatingFileTransport;
1744
2340
  exports.serverlessIntegration = serverlessIntegration;
1745
2341
  exports.stderrTransport = stderrTransport;