logixia 1.10.3 → 1.11.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.
- package/README.md +121 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/{index-Co47qPnq.d.mts → index-CSFeEGLb.d.ts} +32 -2
- package/dist/index-CSFeEGLb.d.ts.map +1 -0
- package/dist/{index-F-A7hg1u.d.ts → index-Cw-sN_0_.d.mts} +32 -2
- package/dist/index-Cw-sN_0_.d.mts.map +1 -0
- package/dist/index.d.mts +251 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +251 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +499 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +488 -7
- package/dist/index.mjs.map +1 -1
- package/dist/{logitron-logger.module-BLT1y5Iq.d.ts → logitron-logger.module-DGwNfjBX.d.mts} +21 -2
- package/dist/logitron-logger.module-DGwNfjBX.d.mts.map +1 -0
- package/dist/{logitron-logger.module-Dlf5GwJ9.js → logitron-logger.module-DHFampon.js} +69 -2
- package/dist/logitron-logger.module-DHFampon.js.map +1 -0
- package/dist/{logitron-logger.module-B8NklSC4.d.mts → logitron-logger.module-DfyBsT_K.d.ts} +21 -2
- package/dist/logitron-logger.module-DfyBsT_K.d.ts.map +1 -0
- package/dist/{logitron-logger.module-BBC9nO5q.mjs → logitron-logger.module-QYBy_Kkq.mjs} +69 -2
- package/dist/logitron-logger.module-QYBy_Kkq.mjs.map +1 -0
- package/dist/middleware.d.mts +1 -1
- package/dist/middleware.d.ts +1 -1
- package/dist/nest.d.mts +2 -2
- package/dist/nest.d.ts +2 -2
- package/dist/nest.js +1 -1
- package/dist/nest.mjs +1 -1
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/transports.d.mts +1 -1
- package/dist/transports.d.mts.map +1 -1
- package/dist/transports.d.ts +1 -1
- package/package.json +1 -1
- package/dist/index-Co47qPnq.d.mts.map +0 -1
- package/dist/index-F-A7hg1u.d.ts.map +0 -1
- package/dist/logitron-logger.module-B8NklSC4.d.mts.map +0 -1
- package/dist/logitron-logger.module-BBC9nO5q.mjs.map +0 -1
- package/dist/logitron-logger.module-BLT1y5Iq.d.ts.map +0 -1
- package/dist/logitron-logger.module-Dlf5GwJ9.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
const require_transport_manager = require('./transport.manager-B9LF9uDd.js');
|
|
2
|
-
const require_logitron_logger_module = require('./logitron-logger.module-
|
|
2
|
+
const require_logitron_logger_module = require('./logitron-logger.module-DHFampon.js');
|
|
3
3
|
require('./search-DoZF3RZj.js');
|
|
4
|
+
let node_async_hooks = require("node:async_hooks");
|
|
5
|
+
node_async_hooks = require_transport_manager.__toESM(node_async_hooks);
|
|
4
6
|
let __nestjs_common = require("@nestjs/common");
|
|
5
7
|
__nestjs_common = require_transport_manager.__toESM(__nestjs_common);
|
|
6
8
|
|
|
@@ -253,7 +255,7 @@ function LogMethod(options = {}) {
|
|
|
253
255
|
const reportLogFailure = (phase, err) => {
|
|
254
256
|
process.stderr.write(`[logixia] @LogMethod(${label}) ${phase} log failed: ${String(err)}\n`);
|
|
255
257
|
};
|
|
256
|
-
const emit = (logger$1, phase, message, data) => {
|
|
258
|
+
const emit$1 = (logger$1, phase, message, data) => {
|
|
257
259
|
const logFnRaw = logger$1[level];
|
|
258
260
|
const p = (typeof logFnRaw === "function" ? logFnRaw : logger$1.debug).bind(logger$1)(message, data);
|
|
259
261
|
if (p && typeof p.catch === "function") p.catch((e) => reportLogFailure(phase, e));
|
|
@@ -275,7 +277,7 @@ function LogMethod(options = {}) {
|
|
|
275
277
|
const start = Date.now();
|
|
276
278
|
const entry = { method: label };
|
|
277
279
|
if (logArgs && args.length > 0) entry["args"] = args;
|
|
278
|
-
if (logger$1) emit(logger$1, "entry", `→ ${label}`, entry);
|
|
280
|
+
if (logger$1) emit$1(logger$1, "entry", `→ ${label}`, entry);
|
|
279
281
|
const buildExit = (result$1) => {
|
|
280
282
|
const exit = {
|
|
281
283
|
method: label,
|
|
@@ -292,13 +294,13 @@ function LogMethod(options = {}) {
|
|
|
292
294
|
throw error;
|
|
293
295
|
}
|
|
294
296
|
if (result && typeof result.then === "function") return result.then((resolved) => {
|
|
295
|
-
if (logger$1) emit(logger$1, "exit", `← ${label}`, buildExit(resolved));
|
|
297
|
+
if (logger$1) emit$1(logger$1, "exit", `← ${label}`, buildExit(resolved));
|
|
296
298
|
return resolved;
|
|
297
299
|
}, (error) => {
|
|
298
300
|
if (logger$1 && logErrors) emitError(logger$1, error, start);
|
|
299
301
|
throw error;
|
|
300
302
|
});
|
|
301
|
-
if (logger$1) emit(logger$1, "exit", `← ${label}`, buildExit(result));
|
|
303
|
+
if (logger$1) emit$1(logger$1, "exit", `← ${label}`, buildExit(result));
|
|
302
304
|
return result;
|
|
303
305
|
};
|
|
304
306
|
return descriptor;
|
|
@@ -546,6 +548,234 @@ var TextFormatter = class TextFormatter {
|
|
|
546
548
|
}
|
|
547
549
|
};
|
|
548
550
|
|
|
551
|
+
//#endregion
|
|
552
|
+
//#region src/utils/runtime-control.ts
|
|
553
|
+
const DEFAULT_CYCLE = [
|
|
554
|
+
"error",
|
|
555
|
+
"warn",
|
|
556
|
+
"info",
|
|
557
|
+
"debug",
|
|
558
|
+
"trace",
|
|
559
|
+
"verbose"
|
|
560
|
+
];
|
|
561
|
+
/**
|
|
562
|
+
* Register an OS-signal handler that cycles the logger's global level on each
|
|
563
|
+
* signal. Returns a dispose function that removes the listener.
|
|
564
|
+
*
|
|
565
|
+
* Cycling (rather than jumping straight to a fixed level) means a single,
|
|
566
|
+
* memorizable command (`kill -USR2 <pid>`) is enough to ratchet verbosity up
|
|
567
|
+
* while chasing a bug and back down again — no value to remember.
|
|
568
|
+
*/
|
|
569
|
+
function registerLevelSignal(logger$1, options = {}) {
|
|
570
|
+
const signal = options.signal ?? "SIGUSR2";
|
|
571
|
+
const cycle = options.cycle && options.cycle.length > 0 ? options.cycle : [...DEFAULT_CYCLE];
|
|
572
|
+
const handler = () => {
|
|
573
|
+
const current = logger$1.getLevel();
|
|
574
|
+
const next = cycle[(cycle.indexOf(current) + 1) % cycle.length];
|
|
575
|
+
logger$1.setLevel(next);
|
|
576
|
+
require_transport_manager.internalLog(`runtime level changed via ${signal}: ${current} → ${next}`);
|
|
577
|
+
};
|
|
578
|
+
process.on(signal, handler);
|
|
579
|
+
return () => {
|
|
580
|
+
process.removeListener(signal, handler);
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
const VALID_LEVELS = new Set([
|
|
584
|
+
"error",
|
|
585
|
+
"warn",
|
|
586
|
+
"info",
|
|
587
|
+
"debug",
|
|
588
|
+
"trace",
|
|
589
|
+
"verbose",
|
|
590
|
+
"fatal"
|
|
591
|
+
]);
|
|
592
|
+
/**
|
|
593
|
+
* Create an HTTP handler (Node `http`/Express-compatible) that reads and sets
|
|
594
|
+
* the logger's level at runtime.
|
|
595
|
+
*
|
|
596
|
+
* - `GET` → `{ level, namespaceLevels }`
|
|
597
|
+
* - `POST` → body `{ level?, namespaceLevels? }` applies them, returns the new state
|
|
598
|
+
*
|
|
599
|
+
* Custom levels are accepted too: any level the logger already knows passes
|
|
600
|
+
* through. Unknown levels are rejected with 400 so a typo can't silently mute
|
|
601
|
+
* logging. Mount behind your own auth — this intentionally has none.
|
|
602
|
+
*/
|
|
603
|
+
function createLevelControlHandler(logger$1, options = {}) {
|
|
604
|
+
const allowed = options.allowedLevels && options.allowedLevels.length > 0 ? new Set(options.allowedLevels.map((l) => l.toLowerCase())) : VALID_LEVELS;
|
|
605
|
+
const snapshot = () => {
|
|
606
|
+
var _logger$getNamespaceL;
|
|
607
|
+
return {
|
|
608
|
+
level: logger$1.getLevel(),
|
|
609
|
+
namespaceLevels: ((_logger$getNamespaceL = logger$1.getNamespaceLevels) === null || _logger$getNamespaceL === void 0 ? void 0 : _logger$getNamespaceL.call(logger$1)) ?? {}
|
|
610
|
+
};
|
|
611
|
+
};
|
|
612
|
+
const send = (res, status, body) => {
|
|
613
|
+
var _res$setHeader;
|
|
614
|
+
res.statusCode = status;
|
|
615
|
+
(_res$setHeader = res.setHeader) === null || _res$setHeader === void 0 || _res$setHeader.call(res, "Content-Type", "application/json");
|
|
616
|
+
res.end(JSON.stringify(body));
|
|
617
|
+
};
|
|
618
|
+
const applyBody = (res, raw) => {
|
|
619
|
+
let parsed;
|
|
620
|
+
try {
|
|
621
|
+
parsed = raw ? JSON.parse(raw) : {};
|
|
622
|
+
} catch {
|
|
623
|
+
send(res, 400, { error: "invalid JSON body" });
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
if (parsed.level !== void 0) {
|
|
627
|
+
const lvl = String(parsed.level).toLowerCase();
|
|
628
|
+
if (!allowed.has(lvl)) {
|
|
629
|
+
send(res, 400, {
|
|
630
|
+
error: `unknown level "${parsed.level}"`,
|
|
631
|
+
allowed: [...allowed]
|
|
632
|
+
});
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
logger$1.setLevel(lvl);
|
|
636
|
+
}
|
|
637
|
+
if (parsed.namespaceLevels !== void 0) {
|
|
638
|
+
if (typeof parsed.namespaceLevels !== "object" || parsed.namespaceLevels === null || Array.isArray(parsed.namespaceLevels)) {
|
|
639
|
+
send(res, 400, { error: "namespaceLevels must be an object" });
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
const nl = parsed.namespaceLevels;
|
|
643
|
+
for (const [pat, lvl] of Object.entries(nl)) if (!allowed.has(String(lvl).toLowerCase())) {
|
|
644
|
+
send(res, 400, { error: `unknown level "${String(lvl)}" for namespace "${pat}"` });
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
if (logger$1.setNamespaceLevels) {
|
|
648
|
+
const coerced = {};
|
|
649
|
+
for (const [pat, lvl] of Object.entries(nl)) coerced[pat] = String(lvl).toLowerCase();
|
|
650
|
+
logger$1.setNamespaceLevels(coerced);
|
|
651
|
+
} else require_transport_manager.internalWarn("level control: logger does not support setNamespaceLevels — ignored");
|
|
652
|
+
}
|
|
653
|
+
send(res, 200, snapshot());
|
|
654
|
+
};
|
|
655
|
+
return function levelControlHandler(req, res) {
|
|
656
|
+
const method = (req.method ?? "GET").toUpperCase();
|
|
657
|
+
if (method === "GET") {
|
|
658
|
+
send(res, 200, snapshot());
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
if (method === "POST" || method === "PUT" || method === "PATCH") {
|
|
662
|
+
const maybeBody = req.body;
|
|
663
|
+
if (maybeBody !== void 0 && typeof req.on !== "function") {
|
|
664
|
+
applyBody(res, typeof maybeBody === "string" ? maybeBody : JSON.stringify(maybeBody));
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
if (typeof req.on === "function") {
|
|
668
|
+
let raw = "";
|
|
669
|
+
req.on("data", (chunk) => {
|
|
670
|
+
raw += String(chunk ?? "");
|
|
671
|
+
});
|
|
672
|
+
req.on("end", () => applyBody(res, raw));
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
applyBody(res, "");
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
send(res, 405, { error: `method ${method} not allowed` });
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
//#endregion
|
|
683
|
+
//#region src/utils/safe-stringify.ts
|
|
684
|
+
/** Build a JSONPath like `$["a"][0]["b"]` for a decycle pointer. */
|
|
685
|
+
function jsonPath(parts) {
|
|
686
|
+
let path = "$";
|
|
687
|
+
for (const p of parts) path += typeof p === "number" ? `[${p}]` : `[${JSON.stringify(p)}]`;
|
|
688
|
+
return path;
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Serialize any value to JSON without throwing on circular references or BigInt.
|
|
692
|
+
* Circular refs become `"[Circular]"` (or `{ $ref }` pointers when `decycle`).
|
|
693
|
+
*/
|
|
694
|
+
function safeStringify(value, options = {}) {
|
|
695
|
+
const { indent, deterministic = false, decycle = false, bigint = "string" } = options;
|
|
696
|
+
if (decycle) return JSON.stringify(decycleValue(value, bigint), void 0, indent);
|
|
697
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
698
|
+
const transform = (val) => {
|
|
699
|
+
if (typeof val === "bigint") return bigint === "number" ? Number(val) : val.toString();
|
|
700
|
+
if (typeof val === "function") return `[Function: ${val.name || "anonymous"}]`;
|
|
701
|
+
if (typeof val === "symbol") return val.toString();
|
|
702
|
+
if (val === null || typeof val !== "object") return val;
|
|
703
|
+
if (seen.has(val)) return "[Circular]";
|
|
704
|
+
seen.add(val);
|
|
705
|
+
let out;
|
|
706
|
+
if (Array.isArray(val)) out = val.map((item) => transform(item));
|
|
707
|
+
else if (val instanceof Date) out = val.toISOString();
|
|
708
|
+
else {
|
|
709
|
+
const rec = val;
|
|
710
|
+
const keys = deterministic ? Object.keys(rec).sort() : Object.keys(rec);
|
|
711
|
+
const obj = {};
|
|
712
|
+
for (const k of keys) {
|
|
713
|
+
if (k === "__proto__" || k === "constructor" || k === "prototype") continue;
|
|
714
|
+
obj[k] = transform(rec[k]);
|
|
715
|
+
}
|
|
716
|
+
out = obj;
|
|
717
|
+
}
|
|
718
|
+
seen.delete(val);
|
|
719
|
+
return out;
|
|
720
|
+
};
|
|
721
|
+
return JSON.stringify(transform(value), void 0, indent);
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Replace repeated object references with round-trippable `{ "$ref": "$..." }`
|
|
725
|
+
* JSONPath pointers (the classic Crockford decycle). Pair with {@link retrocycle}
|
|
726
|
+
* to reconstruct the original shared/circular graph.
|
|
727
|
+
*/
|
|
728
|
+
function decycleValue(value, bigint = "string") {
|
|
729
|
+
const paths = /* @__PURE__ */ new WeakMap();
|
|
730
|
+
const walk = (val, path) => {
|
|
731
|
+
if (typeof val === "bigint") return bigint === "number" ? Number(val) : val.toString();
|
|
732
|
+
if (val === null || typeof val !== "object") return val;
|
|
733
|
+
if (val instanceof Date) return val.toISOString();
|
|
734
|
+
const existing = paths.get(val);
|
|
735
|
+
if (existing !== void 0) return { $ref: existing };
|
|
736
|
+
paths.set(val, jsonPath(path));
|
|
737
|
+
if (Array.isArray(val)) return val.map((item, i) => walk(item, [...path, i]));
|
|
738
|
+
const rec = val;
|
|
739
|
+
const obj = {};
|
|
740
|
+
for (const k of Object.keys(rec)) {
|
|
741
|
+
if (k === "__proto__" || k === "constructor" || k === "prototype") continue;
|
|
742
|
+
obj[k] = walk(rec[k], [...path, k]);
|
|
743
|
+
}
|
|
744
|
+
return obj;
|
|
745
|
+
};
|
|
746
|
+
return walk(value, []);
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* Inverse of {@link decycleValue}: resolve `{ "$ref": "$..." }` pointers back
|
|
750
|
+
* into the live object graph (mutates and returns the parsed input).
|
|
751
|
+
*/
|
|
752
|
+
function retrocycle(root) {
|
|
753
|
+
const refRe = /^\$(?:\[(?:\d+|"(?:[^"\\]|\\.)*")\])*$/;
|
|
754
|
+
const resolve = (path) => {
|
|
755
|
+
const segs = [];
|
|
756
|
+
const partRe = /\[(\d+|"(?:[^"\\]|\\.)*")\]/g;
|
|
757
|
+
let m;
|
|
758
|
+
while ((m = partRe.exec(path)) !== null) {
|
|
759
|
+
const raw = m[1];
|
|
760
|
+
segs.push(raw.startsWith("\"") ? JSON.parse(raw) : Number(raw));
|
|
761
|
+
}
|
|
762
|
+
let node = root;
|
|
763
|
+
for (const s of segs) node = node[s];
|
|
764
|
+
return node;
|
|
765
|
+
};
|
|
766
|
+
const walk = (val) => {
|
|
767
|
+
if (val === null || typeof val !== "object") return;
|
|
768
|
+
const rec = val;
|
|
769
|
+
for (const k of Object.keys(rec)) {
|
|
770
|
+
const child = rec[k];
|
|
771
|
+
if (child !== null && typeof child === "object" && typeof child.$ref === "string" && refRe.test(child.$ref)) rec[k] = resolve(child.$ref);
|
|
772
|
+
else walk(child);
|
|
773
|
+
}
|
|
774
|
+
};
|
|
775
|
+
walk(root);
|
|
776
|
+
return root;
|
|
777
|
+
}
|
|
778
|
+
|
|
549
779
|
//#endregion
|
|
550
780
|
//#region src/utils/typed-logger.ts
|
|
551
781
|
/**
|
|
@@ -606,6 +836,258 @@ function createTypedLogger(logger$1, schema) {
|
|
|
606
836
|
};
|
|
607
837
|
}
|
|
608
838
|
|
|
839
|
+
//#endregion
|
|
840
|
+
//#region src/wide-events.ts
|
|
841
|
+
const _storage = new node_async_hooks.AsyncLocalStorage();
|
|
842
|
+
/**
|
|
843
|
+
* Merge fields into the wide event for the current async scope. No-op (with no
|
|
844
|
+
* throw) when called outside a `withWideEvent` / middleware scope, so business
|
|
845
|
+
* code can call it unconditionally.
|
|
846
|
+
*/
|
|
847
|
+
function addEventFields(fields) {
|
|
848
|
+
const state = _storage.getStore();
|
|
849
|
+
if (!state || state.emitted) return;
|
|
850
|
+
Object.assign(state.fields, fields);
|
|
851
|
+
}
|
|
852
|
+
/** Set a single field on the current wide event. */
|
|
853
|
+
function setEventField(key, value) {
|
|
854
|
+
addEventFields({ [key]: value });
|
|
855
|
+
}
|
|
856
|
+
/** Read a shallow copy of the wide event accumulated so far, or undefined. */
|
|
857
|
+
function getEventFields() {
|
|
858
|
+
const state = _storage.getStore();
|
|
859
|
+
return state ? { ...state.fields } : void 0;
|
|
860
|
+
}
|
|
861
|
+
function emit(logger$1, state, options, extra) {
|
|
862
|
+
if (state.emitted) return;
|
|
863
|
+
state.emitted = true;
|
|
864
|
+
const level = options.level ?? "info";
|
|
865
|
+
const message = options.message ?? "request";
|
|
866
|
+
const durationField = options.durationField ?? "durationMs";
|
|
867
|
+
const includeTrace = options.includeTrace ?? true;
|
|
868
|
+
const payload = { ...state.fields };
|
|
869
|
+
if (extra) Object.assign(payload, extra);
|
|
870
|
+
payload[durationField] = Date.now() - state.startMs;
|
|
871
|
+
if (includeTrace) {
|
|
872
|
+
const traceId = require_logitron_logger_module.getCurrentTraceId();
|
|
873
|
+
if (traceId !== void 0 && payload["traceId"] === void 0) payload["traceId"] = traceId;
|
|
874
|
+
}
|
|
875
|
+
const p = logger$1.logLevel(level, message, payload);
|
|
876
|
+
if (p && typeof p.catch === "function") p.catch(() => {});
|
|
877
|
+
}
|
|
878
|
+
/**
|
|
879
|
+
* Run `callback` inside a wide-event scope. `addEventFields` calls anywhere in
|
|
880
|
+
* the (async) call tree accumulate onto one event, which is emitted exactly once
|
|
881
|
+
* when the callback settles — on success OR error (the canonical "emit in
|
|
882
|
+
* finally" guarantee). On error, `error` + `errorMessage` fields are added.
|
|
883
|
+
*/
|
|
884
|
+
async function withWideEvent(logger$1, initialFields, callback, options = {}) {
|
|
885
|
+
const state = {
|
|
886
|
+
fields: { ...initialFields },
|
|
887
|
+
startMs: Date.now(),
|
|
888
|
+
emitted: false
|
|
889
|
+
};
|
|
890
|
+
return _storage.run(state, async () => {
|
|
891
|
+
try {
|
|
892
|
+
const result = await callback();
|
|
893
|
+
emit(logger$1, state, options);
|
|
894
|
+
return result;
|
|
895
|
+
} catch (error) {
|
|
896
|
+
emit(logger$1, state, options, {
|
|
897
|
+
error: true,
|
|
898
|
+
errorMessage: error instanceof Error ? error.message : String(error)
|
|
899
|
+
});
|
|
900
|
+
throw error;
|
|
901
|
+
}
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* Express/Connect middleware that opens a wide-event scope per request and
|
|
906
|
+
* emits ONE canonical event on response `finish`/`close` — even if the handler
|
|
907
|
+
* throws or the client disconnects. Pre-populates method/url/ip; handlers add
|
|
908
|
+
* more via `addEventFields`. The completion event includes `statusCode` and the
|
|
909
|
+
* request duration.
|
|
910
|
+
*/
|
|
911
|
+
function wideEventMiddleware(logger$1, options = {}) {
|
|
912
|
+
return function logixiaWideEventMiddleware(req, res, next) {
|
|
913
|
+
var _options$skip, _req$socket, _res$once, _res$once2;
|
|
914
|
+
if ((_options$skip = options.skip) === null || _options$skip === void 0 ? void 0 : _options$skip.call(options, req)) {
|
|
915
|
+
next();
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
const state = {
|
|
919
|
+
fields: {
|
|
920
|
+
method: req.method,
|
|
921
|
+
url: req.originalUrl ?? req.url,
|
|
922
|
+
ip: req.ip ?? ((_req$socket = req.socket) === null || _req$socket === void 0 ? void 0 : _req$socket.remoteAddress),
|
|
923
|
+
...options.enrich ? options.enrich(req) : {}
|
|
924
|
+
},
|
|
925
|
+
startMs: Date.now(),
|
|
926
|
+
emitted: false
|
|
927
|
+
};
|
|
928
|
+
const finalize = () => {
|
|
929
|
+
emit(logger$1, state, options, { statusCode: res.statusCode ?? 0 });
|
|
930
|
+
};
|
|
931
|
+
(_res$once = res.once) === null || _res$once === void 0 || _res$once.call(res, "finish", finalize);
|
|
932
|
+
(_res$once2 = res.once) === null || _res$once2 === void 0 || _res$once2.call(res, "close", finalize);
|
|
933
|
+
_storage.run(state, () => next());
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
//#endregion
|
|
938
|
+
//#region src/transports/otlp.transport.ts
|
|
939
|
+
/**
|
|
940
|
+
* Map a logixia level name to an OTel SeverityNumber (1–24) and text.
|
|
941
|
+
* Per the OTel logs SDK spec: TRACE=1, DEBUG=5, INFO=9, WARN=13, ERROR=17,
|
|
942
|
+
* FATAL=21. Custom/unknown levels fall back to INFO (9).
|
|
943
|
+
*/
|
|
944
|
+
function toOtelSeverity(level) {
|
|
945
|
+
switch (level.toLowerCase()) {
|
|
946
|
+
case "trace": return {
|
|
947
|
+
number: 1,
|
|
948
|
+
text: "TRACE"
|
|
949
|
+
};
|
|
950
|
+
case "verbose": return {
|
|
951
|
+
number: 5,
|
|
952
|
+
text: "DEBUG"
|
|
953
|
+
};
|
|
954
|
+
case "debug": return {
|
|
955
|
+
number: 5,
|
|
956
|
+
text: "DEBUG"
|
|
957
|
+
};
|
|
958
|
+
case "info": return {
|
|
959
|
+
number: 9,
|
|
960
|
+
text: "INFO"
|
|
961
|
+
};
|
|
962
|
+
case "warn":
|
|
963
|
+
case "warning": return {
|
|
964
|
+
number: 13,
|
|
965
|
+
text: "WARN"
|
|
966
|
+
};
|
|
967
|
+
case "error": return {
|
|
968
|
+
number: 17,
|
|
969
|
+
text: "ERROR"
|
|
970
|
+
};
|
|
971
|
+
case "fatal": return {
|
|
972
|
+
number: 21,
|
|
973
|
+
text: "FATAL"
|
|
974
|
+
};
|
|
975
|
+
default: return {
|
|
976
|
+
number: 9,
|
|
977
|
+
text: "INFO"
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
/** Coerce a JS value into an OTLP AnyValue. */
|
|
982
|
+
function toAnyValue(value) {
|
|
983
|
+
if (typeof value === "string") return { stringValue: value };
|
|
984
|
+
if (typeof value === "boolean") return { boolValue: value };
|
|
985
|
+
if (typeof value === "number") return Number.isInteger(value) ? { intValue: value } : { doubleValue: value };
|
|
986
|
+
if (typeof value === "bigint") return { stringValue: value.toString() };
|
|
987
|
+
if (value === null || value === void 0) return { stringValue: "" };
|
|
988
|
+
try {
|
|
989
|
+
return { stringValue: JSON.stringify(value) };
|
|
990
|
+
} catch {
|
|
991
|
+
return { stringValue: String(value) };
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
/** Build the OTLP KeyValue attribute list from a flat record. */
|
|
995
|
+
function toAttributes(rec) {
|
|
996
|
+
return Object.entries(rec).map(([key, value]) => ({
|
|
997
|
+
key,
|
|
998
|
+
value: toAnyValue(value)
|
|
999
|
+
}));
|
|
1000
|
+
}
|
|
1001
|
+
var OtlpLogTransport = class {
|
|
1002
|
+
constructor(config) {
|
|
1003
|
+
this.name = "otlp";
|
|
1004
|
+
this.batch = [];
|
|
1005
|
+
this.flushTimer = null;
|
|
1006
|
+
this.url = config.url;
|
|
1007
|
+
this.headers = config.headers ?? {};
|
|
1008
|
+
this.batchSize = config.batchSize ?? 100;
|
|
1009
|
+
this.flushIntervalMs = config.flushIntervalMs ?? 5e3;
|
|
1010
|
+
this.level = config.level;
|
|
1011
|
+
this.resourceAttrs = toAttributes({
|
|
1012
|
+
"service.name": config.serviceName ?? "logixia",
|
|
1013
|
+
...config.serviceVersion ? { "service.version": config.serviceVersion } : {},
|
|
1014
|
+
...config.environment ? { "deployment.environment": config.environment } : {},
|
|
1015
|
+
...config.resourceAttributes ?? {}
|
|
1016
|
+
});
|
|
1017
|
+
this.flushTimer = setInterval(() => {
|
|
1018
|
+
this.flush().catch(() => {});
|
|
1019
|
+
}, this.flushIntervalMs);
|
|
1020
|
+
if (this.flushTimer.unref) this.flushTimer.unref();
|
|
1021
|
+
}
|
|
1022
|
+
write(entry) {
|
|
1023
|
+
this.batch.push(entry);
|
|
1024
|
+
if (this.batch.length >= this.batchSize) this.flush().catch(() => {});
|
|
1025
|
+
}
|
|
1026
|
+
/** Convert one entry into an OTLP LogRecord. */
|
|
1027
|
+
toLogRecord(entry) {
|
|
1028
|
+
const sev = toOtelSeverity(entry.level);
|
|
1029
|
+
const tsNanos = String(entry.timestamp.getTime() * 1e6);
|
|
1030
|
+
const attrs = { ...entry.data ?? {} };
|
|
1031
|
+
if (entry.context !== void 0) attrs["context"] = entry.context;
|
|
1032
|
+
if (entry.appName !== void 0) attrs["app.name"] = entry.appName;
|
|
1033
|
+
if (entry.environment !== void 0) attrs["deployment.environment"] = entry.environment;
|
|
1034
|
+
const record = {
|
|
1035
|
+
timeUnixNano: tsNanos,
|
|
1036
|
+
observedTimeUnixNano: tsNanos,
|
|
1037
|
+
severityNumber: sev.number,
|
|
1038
|
+
severityText: sev.text,
|
|
1039
|
+
body: { stringValue: entry.message },
|
|
1040
|
+
attributes: toAttributes(attrs)
|
|
1041
|
+
};
|
|
1042
|
+
if (entry.traceId) record["traceId"] = entry.traceId;
|
|
1043
|
+
return record;
|
|
1044
|
+
}
|
|
1045
|
+
buildPayload(entries) {
|
|
1046
|
+
return JSON.stringify({ resourceLogs: [{
|
|
1047
|
+
resource: { attributes: this.resourceAttrs },
|
|
1048
|
+
scopeLogs: [{
|
|
1049
|
+
scope: { name: "logixia" },
|
|
1050
|
+
logRecords: entries.map((e) => this.toLogRecord(e))
|
|
1051
|
+
}]
|
|
1052
|
+
}] });
|
|
1053
|
+
}
|
|
1054
|
+
async flush() {
|
|
1055
|
+
while (this.batch.length > 0) {
|
|
1056
|
+
const entries = this.batch.splice(0, this.batchSize);
|
|
1057
|
+
try {
|
|
1058
|
+
await this.send(entries);
|
|
1059
|
+
} catch (err) {
|
|
1060
|
+
require_transport_manager.internalError("OtlpLogTransport flush error", err);
|
|
1061
|
+
this.batch.unshift(...entries);
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
async send(entries) {
|
|
1067
|
+
if (typeof fetch !== "function") {
|
|
1068
|
+
require_transport_manager.internalWarn("OtlpLogTransport: global fetch unavailable — cannot export logs");
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
const res = await fetch(this.url, {
|
|
1072
|
+
method: "POST",
|
|
1073
|
+
headers: {
|
|
1074
|
+
"Content-Type": "application/json",
|
|
1075
|
+
...this.headers
|
|
1076
|
+
},
|
|
1077
|
+
body: this.buildPayload(entries)
|
|
1078
|
+
});
|
|
1079
|
+
if (!res.ok) throw new Error(`OTLP export failed: HTTP ${res.status}`);
|
|
1080
|
+
}
|
|
1081
|
+
async close() {
|
|
1082
|
+
if (this.flushTimer) {
|
|
1083
|
+
clearInterval(this.flushTimer);
|
|
1084
|
+
this.flushTimer = null;
|
|
1085
|
+
}
|
|
1086
|
+
for (let attempt = 0; attempt < 3 && this.batch.length > 0; attempt += 1) await this.flush();
|
|
1087
|
+
if (this.batch.length > 0) require_transport_manager.internalError(`OtlpLogTransport closing with ${this.batch.length} undelivered record(s)`);
|
|
1088
|
+
}
|
|
1089
|
+
};
|
|
1090
|
+
|
|
609
1091
|
//#endregion
|
|
610
1092
|
//#region src/metrics.ts
|
|
611
1093
|
const DEFAULT_BUCKETS = [
|
|
@@ -971,6 +1453,7 @@ Object.defineProperty(exports, 'LogixiaLoggerService', {
|
|
|
971
1453
|
}
|
|
972
1454
|
});
|
|
973
1455
|
exports.MetricsPlugin = MetricsPlugin;
|
|
1456
|
+
exports.OtlpLogTransport = OtlpLogTransport;
|
|
974
1457
|
exports.PluginRegistry = require_logitron_logger_module.PluginRegistry;
|
|
975
1458
|
exports.TRACE_CONTEXT_KEY = require_logitron_logger_module.TRACE_CONTEXT_KEY;
|
|
976
1459
|
exports.TextFormatter = TextFormatter;
|
|
@@ -982,14 +1465,17 @@ Object.defineProperty(exports, 'WebSocketTraceInterceptor', {
|
|
|
982
1465
|
}
|
|
983
1466
|
});
|
|
984
1467
|
exports._setActiveContextKey = require_logitron_logger_module._setActiveContextKey;
|
|
1468
|
+
exports.addEventFields = addEventFields;
|
|
985
1469
|
exports.applyRedaction = require_logitron_logger_module.applyRedaction;
|
|
986
1470
|
exports.createExpressContextMiddleware = require_logitron_logger_module.createExpressContextMiddleware;
|
|
987
1471
|
exports.createFastifyContextHook = require_logitron_logger_module.createFastifyContextHook;
|
|
1472
|
+
exports.createLevelControlHandler = createLevelControlHandler;
|
|
988
1473
|
exports.createLogger = createLogger;
|
|
989
1474
|
exports.createLoggerService = createLoggerService;
|
|
990
1475
|
exports.createMetricsPlugin = createMetricsPlugin;
|
|
991
1476
|
exports.createTraceMiddleware = require_logitron_logger_module.createTraceMiddleware;
|
|
992
1477
|
exports.createTypedLogger = createTypedLogger;
|
|
1478
|
+
exports.decycleValue = decycleValue;
|
|
993
1479
|
exports.defineLogSchema = defineLogSchema;
|
|
994
1480
|
exports.deregisterFromShutdown = require_logitron_logger_module.deregisterFromShutdown;
|
|
995
1481
|
exports.disableOtelBridge = require_logitron_logger_module.disableOtelBridge;
|
|
@@ -999,6 +1485,7 @@ exports.generateRequestId = generateRequestId;
|
|
|
999
1485
|
exports.generateTraceId = generateTraceId;
|
|
1000
1486
|
exports.getActiveOtelContext = require_logitron_logger_module.getActiveOtelContext;
|
|
1001
1487
|
exports.getCurrentTraceId = require_logitron_logger_module.getCurrentTraceId;
|
|
1488
|
+
exports.getEventFields = getEventFields;
|
|
1002
1489
|
exports.getOtelMetaFields = require_logitron_logger_module.getOtelMetaFields;
|
|
1003
1490
|
exports.getTraceContextKey = require_logitron_logger_module.getTraceContextKey;
|
|
1004
1491
|
exports.globalPluginRegistry = require_logitron_logger_module.globalPluginRegistry;
|
|
@@ -1009,10 +1496,17 @@ exports.logger = logger;
|
|
|
1009
1496
|
exports.normalizeError = require_logitron_logger_module.normalizeError;
|
|
1010
1497
|
exports.redactObject = require_logitron_logger_module.redactObject;
|
|
1011
1498
|
exports.registerForShutdown = require_logitron_logger_module.registerForShutdown;
|
|
1499
|
+
exports.registerLevelSignal = registerLevelSignal;
|
|
1012
1500
|
exports.resetShutdownHandlers = require_logitron_logger_module.resetShutdownHandlers;
|
|
1501
|
+
exports.retrocycle = retrocycle;
|
|
1013
1502
|
exports.runWithTraceId = require_logitron_logger_module.runWithTraceId;
|
|
1503
|
+
exports.safeStringify = safeStringify;
|
|
1014
1504
|
exports.serializeError = require_logitron_logger_module.serializeError;
|
|
1505
|
+
exports.setEventField = setEventField;
|
|
1015
1506
|
exports.setTraceId = require_logitron_logger_module.setTraceId;
|
|
1507
|
+
exports.toOtelSeverity = toOtelSeverity;
|
|
1016
1508
|
exports.traceStorage = require_logitron_logger_module.traceStorage;
|
|
1017
1509
|
exports.usePlugin = require_logitron_logger_module.usePlugin;
|
|
1510
|
+
exports.wideEventMiddleware = wideEventMiddleware;
|
|
1511
|
+
exports.withWideEvent = withWideEvent;
|
|
1018
1512
|
//# sourceMappingURL=index.js.map
|