logixia 1.2.1 → 1.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 (59) hide show
  1. package/dist/{index-BDRSTjUt.d.ts → index-CHIsdA9n.d.ts} +53 -2
  2. package/dist/index-CHIsdA9n.d.ts.map +1 -0
  3. package/dist/{index-Drrzn-Yg.d.mts → index-iDTW2-eY.d.mts} +53 -2
  4. package/dist/index-iDTW2-eY.d.mts.map +1 -0
  5. package/dist/index.d.mts +89 -3
  6. package/dist/index.d.mts.map +1 -1
  7. package/dist/index.d.ts +89 -3
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +5 -2
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.mjs +3 -3
  12. package/dist/index.mjs.map +1 -1
  13. package/dist/{logitron-logger.module-iO8DPE7_.mjs → logitron-logger.module-2AzkadqZ.mjs} +226 -8
  14. package/dist/logitron-logger.module-2AzkadqZ.mjs.map +1 -0
  15. package/dist/{logitron-logger.module-X6nGDVGC.js → logitron-logger.module-BqNKp0Fs.js} +240 -4
  16. package/dist/logitron-logger.module-BqNKp0Fs.js.map +1 -0
  17. package/dist/{logitron-logger.module-CY3t8yK6.d.mts → logitron-logger.module-C0G8JGVf.d.ts} +6 -4
  18. package/dist/logitron-logger.module-C0G8JGVf.d.ts.map +1 -0
  19. package/dist/{logitron-logger.module-DgEldK9V.d.ts → logitron-logger.module-DQKaZTJL.d.mts} +6 -4
  20. package/dist/logitron-logger.module-DQKaZTJL.d.mts.map +1 -0
  21. package/dist/middleware.d.mts +83 -0
  22. package/dist/middleware.d.mts.map +1 -0
  23. package/dist/middleware.d.ts +83 -0
  24. package/dist/middleware.d.ts.map +1 -0
  25. package/dist/middleware.js +132 -0
  26. package/dist/middleware.js.map +1 -0
  27. package/dist/middleware.mjs +130 -0
  28. package/dist/middleware.mjs.map +1 -0
  29. package/dist/nest.d.mts +2 -2
  30. package/dist/nest.d.ts +2 -2
  31. package/dist/nest.js +2 -2
  32. package/dist/nest.mjs +2 -2
  33. package/dist/testing.d.mts +67 -0
  34. package/dist/testing.d.mts.map +1 -0
  35. package/dist/testing.d.ts +67 -0
  36. package/dist/testing.d.ts.map +1 -0
  37. package/dist/testing.js +164 -0
  38. package/dist/testing.js.map +1 -0
  39. package/dist/testing.mjs +163 -0
  40. package/dist/testing.mjs.map +1 -0
  41. package/dist/{transport.manager-Vi__pagn.mjs → transport.manager-5VVdqS3o.mjs} +51 -2
  42. package/dist/transport.manager-5VVdqS3o.mjs.map +1 -0
  43. package/dist/{transport.manager-C3Xr7Tvi.js → transport.manager-DCOm4uIQ.js} +51 -2
  44. package/dist/transport.manager-DCOm4uIQ.js.map +1 -0
  45. package/dist/transports.d.mts +38 -1
  46. package/dist/transports.d.mts.map +1 -1
  47. package/dist/transports.d.ts +38 -1
  48. package/dist/transports.d.ts.map +1 -1
  49. package/dist/transports.js +1 -1
  50. package/dist/transports.mjs +1 -1
  51. package/package.json +21 -1
  52. package/dist/index-BDRSTjUt.d.ts.map +0 -1
  53. package/dist/index-Drrzn-Yg.d.mts.map +0 -1
  54. package/dist/logitron-logger.module-CY3t8yK6.d.mts.map +0 -1
  55. package/dist/logitron-logger.module-DgEldK9V.d.ts.map +0 -1
  56. package/dist/logitron-logger.module-X6nGDVGC.js.map +0 -1
  57. package/dist/logitron-logger.module-iO8DPE7_.mjs.map +0 -1
  58. package/dist/transport.manager-C3Xr7Tvi.js.map +0 -1
  59. package/dist/transport.manager-Vi__pagn.mjs.map +0 -1
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require('./chunk-BTgCAUrQ.js');
2
- const require_transport_manager = require('./transport.manager-C3Xr7Tvi.js');
2
+ const require_transport_manager = require('./transport.manager-DCOm4uIQ.js');
3
3
  let fast_json_stringify = require("fast-json-stringify");
4
4
  fast_json_stringify = require_chunk.__toESM(fast_json_stringify);
5
5
  let node_async_hooks = require("node:async_hooks");
@@ -9,6 +9,75 @@ crypto = require_chunk.__toESM(crypto);
9
9
  let __nestjs_common = require("@nestjs/common");
10
10
  __nestjs_common = require_chunk.__toESM(__nestjs_common);
11
11
 
12
+ //#region src/context/async-context.ts
13
+ const _storage = new node_async_hooks.AsyncLocalStorage();
14
+ const LogixiaContext = {
15
+ run(store, callback) {
16
+ const parent = _storage.getStore() ?? {};
17
+ return _storage.run({
18
+ ...parent,
19
+ ...store
20
+ }, callback);
21
+ },
22
+ get() {
23
+ return _storage.getStore();
24
+ },
25
+ set(fields) {
26
+ const store = _storage.getStore();
27
+ if (!store) return;
28
+ Object.assign(store, fields);
29
+ },
30
+ getStorage() {
31
+ return _storage;
32
+ }
33
+ };
34
+ /**
35
+ * Create an Express/Connect-compatible middleware that wraps each request in
36
+ * a `LogixiaContext.run()` scope populated with common request fields.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * import { createExpressContextMiddleware } from 'logixia';
41
+ * app.use(createExpressContextMiddleware());
42
+ * ```
43
+ */
44
+ function createExpressContextMiddleware(options = {}) {
45
+ const { enrich, requestIdHeader = "x-request-id", traceIdHeader = "x-trace-id" } = options;
46
+ return function logixiaContextMiddleware(req, _res, next) {
47
+ const headers = req["headers"] ?? {};
48
+ const traceId = headers[traceIdHeader];
49
+ const base = {
50
+ requestId: headers[requestIdHeader] ?? crypto.randomUUID().slice(0, 8),
51
+ ...traceId !== void 0 ? { traceId } : {}
52
+ };
53
+ LogixiaContext.run({
54
+ ...base,
55
+ ...enrich ? enrich(req) : {}
56
+ }, next);
57
+ };
58
+ }
59
+ /**
60
+ * Create a Fastify lifecycle hook that wraps each request in a context scope.
61
+ *
62
+ * Register with `fastify.addHook('onRequest', createFastifyContextHook())`.
63
+ */
64
+ function createFastifyContextHook(options = {}) {
65
+ const { enrich, requestIdHeader = "x-request-id", traceIdHeader = "x-trace-id" } = options;
66
+ return function logixiaFastifyHook(request, _reply, done) {
67
+ const headers = request["headers"] ?? {};
68
+ const traceId = headers[traceIdHeader];
69
+ const base = {
70
+ requestId: headers[requestIdHeader] ?? request["id"] ?? crypto.randomUUID().slice(0, 8),
71
+ ...traceId !== void 0 ? { traceId } : {}
72
+ };
73
+ LogixiaContext.run({
74
+ ...base,
75
+ ...enrich ? enrich(request) : {}
76
+ }, done);
77
+ };
78
+ }
79
+
80
+ //#endregion
12
81
  //#region src/types/index.ts
13
82
  const LogLevel = {
14
83
  ERROR: "error",
@@ -227,6 +296,137 @@ function isPlainObject(value) {
227
296
  return value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof Error) && !(value instanceof RegExp);
228
297
  }
229
298
 
299
+ //#endregion
300
+ //#region src/utils/sampling.utils.ts
301
+ const ALWAYS_EMIT_LEVELS = new Set(["error", "fatal"]);
302
+ var Sampler = class {
303
+ constructor(config, onStats) {
304
+ this.sampledTraces = /* @__PURE__ */ new Set();
305
+ this.droppedTraces = /* @__PURE__ */ new Set();
306
+ this._tokenBucket = 0;
307
+ this._lastRefillMs = Date.now();
308
+ this._stats = {
309
+ evaluated: 0,
310
+ emitted: 0,
311
+ dropped: 0,
312
+ byLevel: {},
313
+ windowStart: Date.now()
314
+ };
315
+ this.config = config;
316
+ if (config.maxLogsPerSecond && config.maxLogsPerSecond > 0) this._tokenBucket = config.maxLogsPerSecond;
317
+ const interval$1 = config.statsIntervalMs ?? 6e4;
318
+ if (interval$1 > 0 && onStats) {
319
+ this._statsTimer = setInterval(() => {
320
+ onStats(this.getStats());
321
+ this.resetStats();
322
+ }, interval$1);
323
+ if (this._statsTimer.unref) this._statsTimer.unref();
324
+ }
325
+ }
326
+ /**
327
+ * Decide whether a given log entry should be emitted.
328
+ *
329
+ * @param level Log level string (lowercase)
330
+ * @param traceId Active trace ID, if any
331
+ * @returns true → emit, false → drop
332
+ */
333
+ shouldEmit(level, traceId) {
334
+ var _this$config$perLevel;
335
+ const lvl = level.toLowerCase();
336
+ this._trackEvaluated(lvl);
337
+ if (ALWAYS_EMIT_LEVELS.has(lvl) && ((_this$config$perLevel = this.config.perLevel) === null || _this$config$perLevel === void 0 ? void 0 : _this$config$perLevel[lvl]) === void 0) {
338
+ this._trackEmitted(lvl);
339
+ return true;
340
+ }
341
+ if (this.config.traceConsistent && traceId) {
342
+ if (this.sampledTraces.has(traceId)) {
343
+ this._trackEmitted(lvl);
344
+ return true;
345
+ }
346
+ if (this.droppedTraces.has(traceId)) {
347
+ this._trackDropped(lvl);
348
+ return false;
349
+ }
350
+ const emit = this._sampleByRate(lvl);
351
+ if (emit) this.sampledTraces.add(traceId);
352
+ else this.droppedTraces.add(traceId);
353
+ if (emit) this._trackEmitted(lvl);
354
+ else this._trackDropped(lvl);
355
+ return emit;
356
+ }
357
+ if (!this._sampleByRate(lvl)) {
358
+ this._trackDropped(lvl);
359
+ return false;
360
+ }
361
+ if (this.config.maxLogsPerSecond && this.config.maxLogsPerSecond > 0) {
362
+ if (!this._consumeToken()) {
363
+ this._trackDropped(lvl);
364
+ return false;
365
+ }
366
+ }
367
+ this._trackEmitted(lvl);
368
+ return true;
369
+ }
370
+ getStats() {
371
+ return {
372
+ ...this._stats,
373
+ byLevel: { ...this._stats.byLevel }
374
+ };
375
+ }
376
+ resetStats() {
377
+ this._stats = {
378
+ evaluated: 0,
379
+ emitted: 0,
380
+ dropped: 0,
381
+ byLevel: {},
382
+ windowStart: Date.now()
383
+ };
384
+ this.sampledTraces.clear();
385
+ this.droppedTraces.clear();
386
+ }
387
+ destroy() {
388
+ if (this._statsTimer) clearInterval(this._statsTimer);
389
+ }
390
+ _sampleByRate(level) {
391
+ var _this$config$perLevel2, _this$config$perLevel3;
392
+ const rate = ((_this$config$perLevel2 = this.config.perLevel) === null || _this$config$perLevel2 === void 0 ? void 0 : _this$config$perLevel2[level]) ?? ((_this$config$perLevel3 = this.config.perLevel) === null || _this$config$perLevel3 === void 0 ? void 0 : _this$config$perLevel3["*"]) ?? this.config.rate ?? 1;
393
+ if (rate >= 1) return true;
394
+ if (rate <= 0) return false;
395
+ return Math.random() < rate;
396
+ }
397
+ _consumeToken() {
398
+ const now = Date.now();
399
+ const elapsed = (now - this._lastRefillMs) / 1e3;
400
+ const max$1 = this.config.maxLogsPerSecond;
401
+ this._tokenBucket = Math.min(max$1, this._tokenBucket + elapsed * max$1);
402
+ this._lastRefillMs = now;
403
+ if (this._tokenBucket >= 1) {
404
+ this._tokenBucket -= 1;
405
+ return true;
406
+ }
407
+ return false;
408
+ }
409
+ _ensure(level) {
410
+ if (!this._stats.byLevel[level]) this._stats.byLevel[level] = {
411
+ evaluated: 0,
412
+ emitted: 0
413
+ };
414
+ }
415
+ _trackEvaluated(level) {
416
+ this._stats.evaluated++;
417
+ this._ensure(level);
418
+ this._stats.byLevel[level].evaluated++;
419
+ }
420
+ _trackEmitted(level) {
421
+ this._stats.emitted++;
422
+ this._ensure(level);
423
+ this._stats.byLevel[level].emitted++;
424
+ }
425
+ _trackDropped(_level) {
426
+ this._stats.dropped++;
427
+ }
428
+ };
429
+
230
430
  //#endregion
231
431
  //#region src/utils/shutdown.utils.ts
232
432
  /** Module-level registry of all logger instances that have opted into graceful shutdown */
@@ -548,6 +748,13 @@ var LogixiaLogger = class LogixiaLogger {
548
748
  };
549
749
  this.context = context$1 ?? "";
550
750
  if (this.config.transports) this.transportManager = new require_transport_manager.TransportManager(this.config.transports);
751
+ if (this.config.sampling) this._sampler = new Sampler(this.config.sampling, (stats) => {
752
+ process.stdout.write(JSON.stringify({
753
+ level: "info",
754
+ message: "[logixia/sampling] stats",
755
+ ...stats
756
+ }) + "\n");
757
+ });
551
758
  this.setupGracefulShutdown();
552
759
  this.createCustomLevelMethods();
553
760
  this._buildPerfCaches();
@@ -787,6 +994,7 @@ var LogixiaLogger = class LogixiaLogger {
787
994
  return this.transportManager.healthCheck();
788
995
  }
789
996
  async close() {
997
+ var _this$_sampler;
790
998
  for (const [label, timer$1] of this.timers) await this.warn(`Timer '${label}' was not ended properly`, {
791
999
  startTime: new Date(timer$1.startTime).toISOString(),
792
1000
  duration: `${Date.now() - timer$1.startTime}ms (incomplete)`
@@ -796,15 +1004,25 @@ var LogixiaLogger = class LogixiaLogger {
796
1004
  await this.transportManager.flush();
797
1005
  await this.transportManager.close();
798
1006
  }
1007
+ (_this$_sampler = this._sampler) === null || _this$_sampler === void 0 || _this$_sampler.destroy();
799
1008
  deregisterFromShutdown(this);
800
1009
  }
801
1010
  async log(level, message, data) {
802
1011
  if (this.config.silent) return;
803
1012
  if (!this.shouldLog(level)) return;
804
- const rawPayload = this._hasContextData ? {
805
- ...this.contextData,
1013
+ if (this._sampler) {
1014
+ const traceId$1 = this.config.traceId ? getCurrentTraceId() ?? this.fallbackTraceId : void 0;
1015
+ if (!this._sampler.shouldEmit(level, traceId$1)) return;
1016
+ }
1017
+ const alsContext = LogixiaContext.get();
1018
+ const mergedData = alsContext && Object.keys(alsContext).length > 0 ? {
1019
+ ...alsContext,
806
1020
  ...data
807
1021
  } : data;
1022
+ const rawPayload = this._hasContextData ? {
1023
+ ...this.contextData,
1024
+ ...mergedData
1025
+ } : mergedData;
808
1026
  let payload;
809
1027
  if (rawPayload !== void 0 && rawPayload !== null) payload = this._hasRedact ? applyRedaction(rawPayload, this.config.redact) ?? rawPayload : rawPayload;
810
1028
  const traceId = this.config.traceId ? getCurrentTraceId() ?? this.fallbackTraceId : void 0;
@@ -10349,6 +10567,12 @@ Object.defineProperty(exports, 'LogLevel', {
10349
10567
  return LogLevel;
10350
10568
  }
10351
10569
  });
10570
+ Object.defineProperty(exports, 'LogixiaContext', {
10571
+ enumerable: true,
10572
+ get: function () {
10573
+ return LogixiaContext;
10574
+ }
10575
+ });
10352
10576
  Object.defineProperty(exports, 'LogixiaLogger', {
10353
10577
  enumerable: true,
10354
10578
  get: function () {
@@ -10385,6 +10609,18 @@ Object.defineProperty(exports, 'applyRedaction', {
10385
10609
  return applyRedaction;
10386
10610
  }
10387
10611
  });
10612
+ Object.defineProperty(exports, 'createExpressContextMiddleware', {
10613
+ enumerable: true,
10614
+ get: function () {
10615
+ return createExpressContextMiddleware;
10616
+ }
10617
+ });
10618
+ Object.defineProperty(exports, 'createFastifyContextHook', {
10619
+ enumerable: true,
10620
+ get: function () {
10621
+ return createFastifyContextHook;
10622
+ }
10623
+ });
10388
10624
  Object.defineProperty(exports, 'createLogger', {
10389
10625
  enumerable: true,
10390
10626
  get: function () {
@@ -10481,4 +10717,4 @@ Object.defineProperty(exports, 'traceStorage', {
10481
10717
  return traceStorage;
10482
10718
  }
10483
10719
  });
10484
- //# sourceMappingURL=logitron-logger.module-X6nGDVGC.js.map
10720
+ //# sourceMappingURL=logitron-logger.module-BqNKp0Fs.js.map