logixia 1.7.0 → 1.7.1

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.
@@ -812,51 +812,116 @@ var v4_default = v4;
812
812
 
813
813
  //#endregion
814
814
  //#region src/utils/trace.utils.ts
815
- /** The key under which the trace ID is stored in AsyncLocalStorage. */
815
+ /** Default key used to store the trace ID in AsyncLocalStorage. */
816
816
  const TRACE_CONTEXT_KEY = "traceId";
817
- const traceStorage = new node_async_hooks.AsyncLocalStorage();
818
817
  /**
819
- * Default trace ID generator using UUID v4
818
+ * Singleton that owns the AsyncLocalStorage and the active contextKey.
819
+ *
820
+ * Why a class instead of bare module-level variables?
821
+ * - The contextKey is user-configurable; encapsulating it here means there is
822
+ * exactly one place that reads and writes it.
823
+ * - All helpers (getCurrentTraceId, runWithTraceId, etc.) delegate to this
824
+ * instance, so the key is always in sync without hidden global state.
825
+ * - Easier to test: you can reset the singleton between test cases.
826
+ *
827
+ * Usage (advanced):
828
+ * import { TraceContext } from 'logixia';
829
+ * TraceContext.instance.contextKey // → 'traceId' (or custom)
830
+ * TraceContext.instance.getCurrentTraceId() // same as standalone fn
831
+ */
832
+ var TraceContext = class TraceContext {
833
+ static #_ = this._instance = null;
834
+ constructor() {
835
+ this.storage = new node_async_hooks.AsyncLocalStorage();
836
+ this._contextKey = TRACE_CONTEXT_KEY;
837
+ }
838
+ /** The process-wide singleton. */
839
+ static get instance() {
840
+ if (!TraceContext._instance) TraceContext._instance = new TraceContext();
841
+ return TraceContext._instance;
842
+ }
843
+ /** @internal Reset the singleton (useful in tests). */
844
+ static _reset() {
845
+ TraceContext._instance = null;
846
+ }
847
+ /** The AsyncLocalStorage key that holds the trace ID value. */
848
+ get contextKey() {
849
+ return this._contextKey;
850
+ }
851
+ /** @internal Called by TraceMiddleware when it boots to register the user's key. */
852
+ setContextKey(key) {
853
+ this._contextKey = key;
854
+ }
855
+ /** UUID v4 generator (default). */
856
+ generate() {
857
+ return v4_default();
858
+ }
859
+ /** Read the trace ID from the current async context. */
860
+ getCurrentTraceId() {
861
+ var _this$storage$getStor;
862
+ return (_this$storage$getStor = this.storage.getStore()) === null || _this$storage$getStor === void 0 ? void 0 : _this$storage$getStor[this._contextKey];
863
+ }
864
+ /**
865
+ * Mutate the CURRENT async context in-place.
866
+ *
867
+ * ⚠️ Uses `enterWith()` — prefer `run()` when you can wrap the operation
868
+ * in a callback, as it scopes the context to the callback only.
869
+ */
870
+ setTraceId(traceId, data) {
871
+ const current = this.storage.getStore() ?? {};
872
+ this.storage.enterWith({
873
+ ...current,
874
+ [this._contextKey]: traceId,
875
+ ...data
876
+ });
877
+ }
878
+ /** Run `fn` inside a new async context that carries `traceId`. */
879
+ run(traceId, fn, data) {
880
+ return this.storage.run({
881
+ [this._contextKey]: traceId,
882
+ ...data
883
+ }, fn);
884
+ }
885
+ };
886
+ /**
887
+ * The shared AsyncLocalStorage instance.
888
+ * @deprecated Prefer `TraceContext.instance.storage` for new code.
820
889
  */
890
+ const traceStorage = TraceContext.instance.storage;
891
+ /** Returns the key currently used to store the trace ID in AsyncLocalStorage. */
892
+ function getTraceContextKey() {
893
+ return TraceContext.instance.contextKey;
894
+ }
895
+ /** @internal Called by TraceMiddleware / traceMiddleware() when they boot. */
896
+ function _setActiveContextKey(key) {
897
+ TraceContext.instance.setContextKey(key);
898
+ }
899
+ /** Generate a UUID v4 trace ID. */
821
900
  function generateTraceId() {
822
- return v4_default();
901
+ return TraceContext.instance.generate();
823
902
  }
824
903
  /**
825
- * Get current trace ID from async context
904
+ * Get the current trace ID from async context.
905
+ * Returns `undefined` when called outside a traced request.
826
906
  */
827
907
  function getCurrentTraceId() {
828
- const store = traceStorage.getStore();
829
- return store === null || store === void 0 ? void 0 : store.traceId;
908
+ return TraceContext.instance.getCurrentTraceId();
830
909
  }
831
910
  /**
832
911
  * Set trace ID in the CURRENT async context without starting a new one.
833
912
  *
834
- * ⚠️ Uses `enterWith()` which mutates the context for the current async
835
- * execution and ALL futures spawned from it. Prefer `runWithTraceId()` when
836
- * you can wrap the operation in a callback, as it creates a properly-scoped
837
- * child context. Use `setTraceId()` only when you cannot use `runWithTraceId()`
838
- * (e.g., inside a class constructor or a non-callback async entry point).
913
+ * ⚠️ Uses `enterWith()` mutates the context for the current async execution
914
+ * and all futures spawned from it. Prefer `runWithTraceId()` when you can wrap
915
+ * the operation in a callback.
839
916
  */
840
917
  function setTraceId(traceId, data) {
841
- const currentStore = traceStorage.getStore() ?? {};
842
- traceStorage.enterWith({
843
- ...currentStore,
844
- traceId,
845
- ...data
846
- });
918
+ TraceContext.instance.setTraceId(traceId, data);
847
919
  }
848
- /**
849
- * Run function with trace ID context
850
- */
920
+ /** Run `fn` inside a new async context carrying `traceId`. */
851
921
  function runWithTraceId(traceId, fn, data) {
852
- return traceStorage.run({
853
- traceId,
854
- ...data
855
- }, fn);
922
+ return TraceContext.instance.run(traceId, fn, data);
856
923
  }
857
- /**
858
- * Extract trace ID from request using configuration
859
- */
924
+ /** Extract trace ID from request using configuration (header → query → body → params). */
860
925
  function extractTraceId(request, config) {
861
926
  const req = request;
862
927
  if (config.header) {
@@ -896,7 +961,7 @@ function extractTraceId(request, config) {
896
961
  * Default headers checked for incoming trace ID propagation, in priority order:
897
962
  * traceparent (W3C/OTel) → x-trace-id → x-request-id → x-correlation-id → trace-id
898
963
  */
899
- const DEFAULT_TRACE_HEADERS$1 = [
964
+ const DEFAULT_TRACE_HEADERS = [
900
965
  "traceparent",
901
966
  "x-trace-id",
902
967
  "x-request-id",
@@ -908,7 +973,7 @@ const DEFAULT_TRACE_HEADERS$1 = [
908
973
  */
909
974
  function createTraceMiddleware(config) {
910
975
  const defaultExtractor = {
911
- header: DEFAULT_TRACE_HEADERS$1,
976
+ header: DEFAULT_TRACE_HEADERS,
912
977
  query: ["traceId", "trace_id"]
913
978
  };
914
979
  const resolvedConfig = {
@@ -918,15 +983,14 @@ function createTraceMiddleware(config) {
918
983
  ...config.extractor
919
984
  } : defaultExtractor
920
985
  };
986
+ _setActiveContextKey(resolvedConfig.contextKey ?? TRACE_CONTEXT_KEY);
921
987
  return (req, res, next) => {
922
988
  let traceId;
923
989
  if (resolvedConfig.extractor) traceId = extractTraceId(req, resolvedConfig.extractor);
924
990
  if (!traceId) traceId = resolvedConfig.generator ? resolvedConfig.generator() : generateTraceId();
925
991
  req.traceId = traceId;
926
992
  res.setHeader("X-Trace-Id", traceId);
927
- runWithTraceId(traceId, () => {
928
- next();
929
- }, { requestId: req["id"] || req["requestId"] || generateTraceId() });
993
+ runWithTraceId(traceId, () => next(), { requestId: req["id"] || req["requestId"] || generateTraceId() });
930
994
  };
931
995
  }
932
996
 
@@ -985,7 +1049,7 @@ var LogixiaLogger = class LogixiaLogger {
985
1049
  this.timers = /* @__PURE__ */ new Map();
986
1050
  this.contextData = {};
987
1051
  this.fieldState = /* @__PURE__ */ new Map();
988
- this.fallbackTraceId = generateTraceId();
1052
+ this.fallbackTraceId = TraceContext.instance.generate();
989
1053
  this._levelValues = /* @__PURE__ */ new Map();
990
1054
  this._minLevelValue = 2;
991
1055
  this._colorMap = /* @__PURE__ */ new Map();
@@ -1371,7 +1435,7 @@ var LogixiaLogger = class LogixiaLogger {
1371
1435
  if (this.config.silent) return;
1372
1436
  if (!this.shouldLog(level)) return;
1373
1437
  if (this._sampler) {
1374
- const traceId$1 = this.config.traceId ? getCurrentTraceId() ?? this.fallbackTraceId : void 0;
1438
+ const traceId$1 = this.config.traceId ? TraceContext.instance.getCurrentTraceId() ?? this.fallbackTraceId : void 0;
1375
1439
  if (!this._sampler.shouldEmit(level, traceId$1)) return;
1376
1440
  }
1377
1441
  const alsContext = LogixiaContext.get();
@@ -1394,7 +1458,7 @@ var LogixiaLogger = class LogixiaLogger {
1394
1458
  } : mergedData;
1395
1459
  let payload;
1396
1460
  if (rawPayload !== void 0 && rawPayload !== null) payload = this._hasRedact ? applyRedaction(rawPayload, this.config.redact) ?? rawPayload : rawPayload;
1397
- const traceId = this.config.traceId ? getCurrentTraceId() ?? this.fallbackTraceId : void 0;
1461
+ const traceId = this.config.traceId ? TraceContext.instance.getCurrentTraceId() ?? this.fallbackTraceId : void 0;
1398
1462
  const entry = {
1399
1463
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1400
1464
  level,
@@ -1645,11 +1709,11 @@ let LogixiaLoggerService = _LogixiaLoggerService = class LogixiaLoggerService$1
1645
1709
  return childService;
1646
1710
  }
1647
1711
  getCurrentTraceId() {
1648
- return getCurrentTraceId();
1712
+ return TraceContext.instance.getCurrentTraceId();
1649
1713
  }
1650
- /** The AsyncLocalStorage key under which the trace ID is stored ('traceId'). */
1714
+ /** Returns the AsyncLocalStorage key currently used to store the trace ID. */
1651
1715
  get traceContextKey() {
1652
- return TRACE_CONTEXT_KEY;
1716
+ return getTraceContextKey();
1653
1717
  }
1654
1718
  async close() {
1655
1719
  return this.logger.close();
@@ -10533,6 +10597,7 @@ var import_cjs$1 = /* @__PURE__ */ require_chunk.__toESM(require_cjs());
10533
10597
  let KafkaTraceInterceptor = class KafkaTraceInterceptor$1 {
10534
10598
  constructor(config) {
10535
10599
  this.config = config;
10600
+ this.ctx = TraceContext.instance;
10536
10601
  this.config = {
10537
10602
  enabled: true,
10538
10603
  contextKey: "traceId",
@@ -10560,7 +10625,7 @@ let KafkaTraceInterceptor = class KafkaTraceInterceptor$1 {
10560
10625
  query: {},
10561
10626
  params: {}
10562
10627
  }, this.config.extractor);
10563
- if (!traceId) traceId = getCurrentTraceId();
10628
+ if (!traceId) traceId = this.ctx.getCurrentTraceId();
10564
10629
  if (!traceId) return next.handle();
10565
10630
  const kafkaContext = {
10566
10631
  messageType: "kafka",
@@ -10571,7 +10636,7 @@ let KafkaTraceInterceptor = class KafkaTraceInterceptor$1 {
10571
10636
  timestamp: rpcData === null || rpcData === void 0 ? void 0 : rpcData.timestamp
10572
10637
  };
10573
10638
  return new import_cjs$1.Observable((subscriber) => {
10574
- runWithTraceId(traceId, () => {
10639
+ this.ctx.run(traceId, () => {
10575
10640
  next.handle().subscribe({
10576
10641
  next: (value) => subscriber.next(value),
10577
10642
  error: (err) => subscriber.error(err),
@@ -10593,36 +10658,17 @@ function __decorateParam(paramIndex, decorator) {
10593
10658
 
10594
10659
  //#endregion
10595
10660
  //#region src/core/trace.middleware.ts
10596
- /**
10597
- * Default headers checked when extracting an incoming trace ID, in priority order:
10598
- * 1. traceparent — W3C Trace Context (OpenTelemetry standard)
10599
- * 2. x-trace-id — common custom header
10600
- * 3. x-request-id — used by AWS ALB, NGINX, etc.
10601
- * 4. x-correlation-id — used by Azure / enterprise ESBs
10602
- * 5. trace-id — legacy shorthand
10603
- *
10604
- * NOTE: for `traceparent` the format is `00-<traceId>-<spanId>-<flags>`.
10605
- * We store the full header value as-is so downstream systems can forward it
10606
- * unmodified. If you need only the 32-char traceId segment, configure a
10607
- * custom extractor via TraceIdConfig.extractor.
10608
- */
10609
- const DEFAULT_TRACE_HEADERS = [
10610
- "traceparent",
10611
- "x-trace-id",
10612
- "x-request-id",
10613
- "x-correlation-id",
10614
- "trace-id"
10615
- ];
10616
10661
  let TraceMiddleware = class TraceMiddleware$1 {
10617
10662
  constructor(config) {
10618
10663
  this.config = config;
10664
+ this.ctx = TraceContext.instance;
10619
10665
  const defaultExtractor = {
10620
10666
  header: DEFAULT_TRACE_HEADERS,
10621
10667
  query: ["traceId", "trace_id"]
10622
10668
  };
10623
10669
  this.config = {
10624
10670
  enabled: true,
10625
- generator: generateTraceId,
10671
+ generator: () => this.ctx.generate(),
10626
10672
  contextKey: "traceId",
10627
10673
  ...config,
10628
10674
  extractor: (config === null || config === void 0 ? void 0 : config.extractor) ? {
@@ -10630,20 +10676,19 @@ let TraceMiddleware = class TraceMiddleware$1 {
10630
10676
  ...config.extractor
10631
10677
  } : defaultExtractor
10632
10678
  };
10679
+ this.ctx.setContextKey(this.config.contextKey ?? "traceId");
10633
10680
  }
10634
10681
  use(req, res, next) {
10635
10682
  var _this$config;
10636
10683
  if (!((_this$config = this.config) === null || _this$config === void 0 ? void 0 : _this$config.enabled)) return next();
10637
10684
  let traceId;
10638
10685
  if (this.config.extractor) traceId = extractTraceId(req, this.config.extractor);
10639
- if (!traceId) traceId = this.config.generator ? this.config.generator() : generateTraceId();
10686
+ if (!traceId) traceId = this.config.generator ? this.config.generator() : this.ctx.generate();
10640
10687
  req.traceId = traceId;
10641
- req.requestId = req.requestId || generateTraceId();
10688
+ req.requestId = req.requestId || this.ctx.generate();
10642
10689
  res.setHeader("X-Trace-Id", traceId);
10643
10690
  res.setHeader("X-Request-Id", req.requestId);
10644
- runWithTraceId(traceId, () => {
10645
- next();
10646
- }, {
10691
+ this.ctx.run(traceId, () => next(), {
10647
10692
  requestId: req.requestId,
10648
10693
  method: req.method,
10649
10694
  url: req.url,
@@ -10664,6 +10709,7 @@ var import_cjs = /* @__PURE__ */ require_chunk.__toESM(require_cjs());
10664
10709
  let WebSocketTraceInterceptor = class WebSocketTraceInterceptor$1 {
10665
10710
  constructor(config) {
10666
10711
  this.config = config;
10712
+ this.ctx = TraceContext.instance;
10667
10713
  this.config = {
10668
10714
  enabled: true,
10669
10715
  contextKey: "traceId",
@@ -10695,7 +10741,7 @@ let WebSocketTraceInterceptor = class WebSocketTraceInterceptor$1 {
10695
10741
  params: {}
10696
10742
  }, this.config.extractor);
10697
10743
  }
10698
- if (!traceId) traceId = getCurrentTraceId();
10744
+ if (!traceId) traceId = this.ctx.getCurrentTraceId();
10699
10745
  if (!traceId) return next.handle();
10700
10746
  const wsContextData = {
10701
10747
  messageType: "websocket",
@@ -10705,7 +10751,7 @@ let WebSocketTraceInterceptor = class WebSocketTraceInterceptor$1 {
10705
10751
  clientAddress: client === null || client === void 0 || (_client$handshake3 = client.handshake) === null || _client$handshake3 === void 0 ? void 0 : _client$handshake3.address
10706
10752
  };
10707
10753
  return new import_cjs.Observable((observer) => {
10708
- runWithTraceId(traceId, () => {
10754
+ this.ctx.run(traceId, () => {
10709
10755
  next.handle().subscribe({
10710
10756
  next: (value) => observer.next(value),
10711
10757
  error: (err) => observer.error(err),
@@ -10742,7 +10788,7 @@ let LogixiaLoggerModule = class LogixiaLoggerModule$1 {
10742
10788
  else if (_LogixiaLoggerModule.loggerConfig.traceId === true) traceConfig = {
10743
10789
  enabled: true,
10744
10790
  contextKey: "traceId",
10745
- generator: () => generateTraceId()
10791
+ generator: () => TraceContext.instance.generate()
10746
10792
  };
10747
10793
  else traceConfig = void 0;
10748
10794
  return new TraceMiddleware(traceConfig).use(req, res, next);
@@ -10944,7 +10990,7 @@ Object.defineProperty(exports, 'DEFAULT_LOG_LEVELS', {
10944
10990
  Object.defineProperty(exports, 'DEFAULT_TRACE_HEADERS', {
10945
10991
  enumerable: true,
10946
10992
  get: function () {
10947
- return DEFAULT_TRACE_HEADERS$1;
10993
+ return DEFAULT_TRACE_HEADERS;
10948
10994
  }
10949
10995
  });
10950
10996
  Object.defineProperty(exports, 'KafkaTraceInterceptor', {
@@ -11007,6 +11053,12 @@ Object.defineProperty(exports, 'TRACE_CONTEXT_KEY', {
11007
11053
  return TRACE_CONTEXT_KEY;
11008
11054
  }
11009
11055
  });
11056
+ Object.defineProperty(exports, 'TraceContext', {
11057
+ enumerable: true,
11058
+ get: function () {
11059
+ return TraceContext;
11060
+ }
11061
+ });
11010
11062
  Object.defineProperty(exports, 'TraceMiddleware', {
11011
11063
  enumerable: true,
11012
11064
  get: function () {
@@ -11037,6 +11089,12 @@ Object.defineProperty(exports, '__decorateParam', {
11037
11089
  return __decorateParam;
11038
11090
  }
11039
11091
  });
11092
+ Object.defineProperty(exports, '_setActiveContextKey', {
11093
+ enumerable: true,
11094
+ get: function () {
11095
+ return _setActiveContextKey;
11096
+ }
11097
+ });
11040
11098
  Object.defineProperty(exports, 'applyRedaction', {
11041
11099
  enumerable: true,
11042
11100
  get: function () {
@@ -11115,6 +11173,12 @@ Object.defineProperty(exports, 'getOtelMetaFields', {
11115
11173
  return getOtelMetaFields;
11116
11174
  }
11117
11175
  });
11176
+ Object.defineProperty(exports, 'getTraceContextKey', {
11177
+ enumerable: true,
11178
+ get: function () {
11179
+ return getTraceContextKey;
11180
+ }
11181
+ });
11118
11182
  Object.defineProperty(exports, 'globalPluginRegistry', {
11119
11183
  enumerable: true,
11120
11184
  get: function () {
@@ -11187,4 +11251,4 @@ Object.defineProperty(exports, 'usePlugin', {
11187
11251
  return usePlugin;
11188
11252
  }
11189
11253
  });
11190
- //# sourceMappingURL=logitron-logger.module-BcCtbMEe.js.map
11254
+ //# sourceMappingURL=logitron-logger.module-BI5eI4X9.js.map