@spinajs/log-common 2.0.481 → 2.0.484
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 +125 -4
- package/lib/cjs/BatchQueue.d.ts +127 -0
- package/lib/cjs/BatchQueue.d.ts.map +1 -0
- package/lib/cjs/BatchQueue.js +126 -0
- package/lib/cjs/BatchQueue.js.map +1 -0
- package/lib/cjs/filters/filter.d.ts +27 -0
- package/lib/cjs/filters/filter.d.ts.map +1 -0
- package/lib/cjs/filters/filter.js +20 -0
- package/lib/cjs/filters/filter.js.map +1 -0
- package/lib/cjs/filters/whenRepeated.d.ts +52 -0
- package/lib/cjs/filters/whenRepeated.d.ts.map +1 -0
- package/lib/cjs/filters/whenRepeated.js +98 -0
- package/lib/cjs/filters/whenRepeated.js.map +1 -0
- package/lib/cjs/format.d.ts +20 -0
- package/lib/cjs/format.d.ts.map +1 -0
- package/lib/cjs/format.js +75 -0
- package/lib/cjs/format.js.map +1 -0
- package/lib/cjs/index.d.ts +260 -21
- package/lib/cjs/index.d.ts.map +1 -1
- package/lib/cjs/index.js +211 -34
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/perf.d.ts +109 -0
- package/lib/cjs/perf.d.ts.map +1 -0
- package/lib/cjs/perf.js +162 -0
- package/lib/cjs/perf.js.map +1 -0
- package/lib/cjs/persistence.d.ts +19 -0
- package/lib/cjs/persistence.d.ts.map +1 -0
- package/lib/cjs/persistence.js +137 -0
- package/lib/cjs/persistence.js.map +1 -0
- package/lib/cjs/serializers.d.ts +77 -0
- package/lib/cjs/serializers.d.ts.map +1 -0
- package/lib/cjs/serializers.js +215 -0
- package/lib/cjs/serializers.js.map +1 -0
- package/lib/mjs/BatchQueue.d.ts +127 -0
- package/lib/mjs/BatchQueue.d.ts.map +1 -0
- package/lib/mjs/BatchQueue.js +122 -0
- package/lib/mjs/BatchQueue.js.map +1 -0
- package/lib/mjs/filters/filter.d.ts +27 -0
- package/lib/mjs/filters/filter.d.ts.map +1 -0
- package/lib/mjs/filters/filter.js +16 -0
- package/lib/mjs/filters/filter.js.map +1 -0
- package/lib/mjs/filters/whenRepeated.d.ts +52 -0
- package/lib/mjs/filters/whenRepeated.d.ts.map +1 -0
- package/lib/mjs/filters/whenRepeated.js +95 -0
- package/lib/mjs/filters/whenRepeated.js.map +1 -0
- package/lib/mjs/format.d.ts +20 -0
- package/lib/mjs/format.d.ts.map +1 -0
- package/lib/mjs/format.js +72 -0
- package/lib/mjs/format.js.map +1 -0
- package/lib/mjs/index.d.ts +260 -21
- package/lib/mjs/index.d.ts.map +1 -1
- package/lib/mjs/index.js +205 -11
- package/lib/mjs/index.js.map +1 -1
- package/lib/mjs/perf.d.ts +109 -0
- package/lib/mjs/perf.d.ts.map +1 -0
- package/lib/mjs/perf.js +157 -0
- package/lib/mjs/perf.js.map +1 -0
- package/lib/mjs/persistence.d.ts +19 -0
- package/lib/mjs/persistence.d.ts.map +1 -0
- package/lib/mjs/persistence.js +132 -0
- package/lib/mjs/persistence.js.map +1 -0
- package/lib/mjs/serializers.d.ts +77 -0
- package/lib/mjs/serializers.d.ts.map +1 -0
- package/lib/mjs/serializers.js +208 -0
- package/lib/mjs/serializers.js.map +1 -0
- package/lib/tsconfig.cjs.tsbuildinfo +1 -1
- package/lib/tsconfig.mjs.tsbuildinfo +1 -1
- package/package.json +8 -9
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-only runtime log-level persistence ( loglevel-style ). Lets a user's
|
|
3
|
+
* `setLevel(...)` choice survive page reloads via `localStorage`, with a
|
|
4
|
+
* `document.cookie` fallback for environments where storage throws ( eg. Safari
|
|
5
|
+
* private mode ). On Node ( no `window` ) every helper is a clean no-op.
|
|
6
|
+
*
|
|
7
|
+
* Kept dependency-free and free of any `node:*` imports so log-common stays
|
|
8
|
+
* browser-safe: the only globals touched are `window` / `document`, guarded by
|
|
9
|
+
* `typeof` checks.
|
|
10
|
+
*/
|
|
11
|
+
const KEY_PREFIX = "spinajs:log:level:";
|
|
12
|
+
function storageKey(loggerName) {
|
|
13
|
+
return `${KEY_PREFIX}${loggerName}`;
|
|
14
|
+
}
|
|
15
|
+
/** True only in a browser-like environment with a usable localStorage. */
|
|
16
|
+
function hasWindow() {
|
|
17
|
+
return typeof window !== "undefined";
|
|
18
|
+
}
|
|
19
|
+
function writeCookie(key, value) {
|
|
20
|
+
if (typeof document === "undefined") {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
// 1 year, path=/ so the value is visible across the app.
|
|
25
|
+
const maxAge = 60 * 60 * 24 * 365;
|
|
26
|
+
document.cookie = `${encodeURIComponent(key)}=${encodeURIComponent(value)};path=/;max-age=${maxAge}`;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// nothing else we can do - persistence is best-effort.
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function readCookie(key) {
|
|
33
|
+
if (typeof document === "undefined") {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
const enc = encodeURIComponent(key);
|
|
38
|
+
const parts = document.cookie ? document.cookie.split(";") : [];
|
|
39
|
+
for (const part of parts) {
|
|
40
|
+
const [k, ...rest] = part.trim().split("=");
|
|
41
|
+
if (k === enc) {
|
|
42
|
+
return decodeURIComponent(rest.join("="));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// ignore
|
|
48
|
+
}
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
function expireCookie(key) {
|
|
52
|
+
if (typeof document === "undefined") {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
document.cookie = `${encodeURIComponent(key)}=;path=/;max-age=0`;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// ignore
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Persist `level` for `loggerName`. Browser only - writes `String(level)` to
|
|
64
|
+
* localStorage under `spinajs:log:level:<loggerName>`, falling back to a cookie
|
|
65
|
+
* when storage throws. No-op on Node.
|
|
66
|
+
*/
|
|
67
|
+
export function persistLevel(loggerName, level) {
|
|
68
|
+
if (!hasWindow()) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const key = storageKey(loggerName);
|
|
72
|
+
const value = String(level);
|
|
73
|
+
try {
|
|
74
|
+
// window.localStorage access itself can throw ( disabled cookies / private mode ).
|
|
75
|
+
if (window.localStorage) {
|
|
76
|
+
window.localStorage.setItem(key, value);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// fall through to the cookie fallback below.
|
|
82
|
+
}
|
|
83
|
+
writeCookie(key, value);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Read back the persisted level for `loggerName` ( localStorage then cookie ).
|
|
87
|
+
* Returns a finite {@link LogLevel} number, or `undefined` when nothing valid is
|
|
88
|
+
* stored. Always `undefined` on Node.
|
|
89
|
+
*/
|
|
90
|
+
export function readPersistedLevel(loggerName) {
|
|
91
|
+
if (!hasWindow()) {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
const key = storageKey(loggerName);
|
|
95
|
+
let raw;
|
|
96
|
+
try {
|
|
97
|
+
if (window.localStorage) {
|
|
98
|
+
raw = window.localStorage.getItem(key);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
raw = undefined;
|
|
103
|
+
}
|
|
104
|
+
if (raw === null || raw === undefined) {
|
|
105
|
+
raw = readCookie(key);
|
|
106
|
+
}
|
|
107
|
+
if (raw === null || raw === undefined || raw === "") {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
const parsed = Number(raw);
|
|
111
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Remove any persisted level for `loggerName` ( localStorage + cookie ). No-op
|
|
115
|
+
* on Node.
|
|
116
|
+
*/
|
|
117
|
+
export function clearPersistedLevel(loggerName) {
|
|
118
|
+
if (!hasWindow()) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const key = storageKey(loggerName);
|
|
122
|
+
try {
|
|
123
|
+
if (window.localStorage) {
|
|
124
|
+
window.localStorage.removeItem(key);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// ignore - fall through to also expire the cookie.
|
|
129
|
+
}
|
|
130
|
+
expireCookie(key);
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=persistence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persistence.js","sourceRoot":"","sources":["../../src/persistence.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, dependency-free serialization helpers used by the log targets.
|
|
3
|
+
*
|
|
4
|
+
* These functions are intentionally browser-safe: they use no Node-only APIs
|
|
5
|
+
* ( no `util`, no `Buffer` ), only plain ECMAScript. They are also written to
|
|
6
|
+
* be defensive - neither of them may ever throw, because a logger that crashes
|
|
7
|
+
* while trying to log an error is worse than useless.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Structured representation of an Error suitable for JSON serialization.
|
|
11
|
+
*/
|
|
12
|
+
export interface ISerializedError {
|
|
13
|
+
name: string;
|
|
14
|
+
message: string;
|
|
15
|
+
stack?: string;
|
|
16
|
+
code?: string | number;
|
|
17
|
+
signal?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Turn an `Error` into a plain, JSON-friendly record.
|
|
21
|
+
*
|
|
22
|
+
* - Returns `undefined` for anything that is not an `Error` ( callers only
|
|
23
|
+
* serialize actual errors ).
|
|
24
|
+
* - `stack` walks the `.cause` chain and any `AggregateError.errors`, appending
|
|
25
|
+
* each nested error under a `Caused by:` line ( bunyan-style ).
|
|
26
|
+
* - `code` / `signal` are included only when present ( common on Node system
|
|
27
|
+
* errors such as `ECONNREFUSED` / `SIGTERM` ).
|
|
28
|
+
* - Never throws.
|
|
29
|
+
*/
|
|
30
|
+
export declare function serializeError(err: unknown): ISerializedError | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* A serializer turns a raw log-variable value into a structured, log-friendly
|
|
33
|
+
* form. It receives the value stored under a given field name and returns its
|
|
34
|
+
* replacement. Returning `undefined` means "leave the original value alone"
|
|
35
|
+
* ( e.g. `serializeError` returns `undefined` for a non-Error ).
|
|
36
|
+
*/
|
|
37
|
+
export type LogSerializer = (value: unknown) => unknown;
|
|
38
|
+
/**
|
|
39
|
+
* Registry of field-name -> serializer. Applied by {@link applySerializers} to
|
|
40
|
+
* every log entry's variables. Out of the box the SpinaJS `error` variable
|
|
41
|
+
* ( set by `createLogMessageObject` ) is serialized by {@link serializeError },
|
|
42
|
+
* so `error` becomes a plain `{ name, message, stack, code, signal }` record
|
|
43
|
+
* instead of an opaque `Error`.
|
|
44
|
+
*/
|
|
45
|
+
export declare const serializers: Map<string, LogSerializer>;
|
|
46
|
+
/**
|
|
47
|
+
* Register ( or override ) the serializer used for a given log-variable field.
|
|
48
|
+
*/
|
|
49
|
+
export declare function registerSerializer(field: string, fn: LogSerializer): void;
|
|
50
|
+
/**
|
|
51
|
+
* Apply the registered serializers to a log entry's variables, MUTATING `vars`
|
|
52
|
+
* in place.
|
|
53
|
+
*
|
|
54
|
+
* For each registered `[field, fn]`:
|
|
55
|
+
* - skip when `field` is absent or its value is `undefined` / `null`;
|
|
56
|
+
* - run `fn` inside a try/catch:
|
|
57
|
+
* - on success, overwrite `vars[field]` ONLY when the serializer returned
|
|
58
|
+
* a defined value, so a serializer that returns `undefined` for an
|
|
59
|
+
* unhandled value ( like `serializeError` on a non-Error ) leaves the
|
|
60
|
+
* original untouched;
|
|
61
|
+
* - on throw, replace the value with `{ serializerError: <message> }` so a
|
|
62
|
+
* broken serializer degrades gracefully and NEVER crashes the caller.
|
|
63
|
+
*/
|
|
64
|
+
export declare function applySerializers(vars: Record<string, unknown>): void;
|
|
65
|
+
/**
|
|
66
|
+
* JSON-stringify any value without ever throwing.
|
|
67
|
+
*
|
|
68
|
+
* Three tiers:
|
|
69
|
+
* 1. plain `JSON.stringify` ( the common, fast path );
|
|
70
|
+
* 2. on throw ( typically circular references ) retry with a replacer that
|
|
71
|
+
* swaps already-seen objects for the string `"[Circular]"`, producing
|
|
72
|
+
* VALID JSON;
|
|
73
|
+
* 3. if that still throws ( e.g. a getter that throws while enumerating ),
|
|
74
|
+
* fall back to `String(value)`.
|
|
75
|
+
*/
|
|
76
|
+
export declare function safeStringify(value: unknown, indent?: number): string;
|
|
77
|
+
//# sourceMappingURL=serializers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serializers.d.ts","sourceRoot":"","sources":["../../src/serializers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AA8ED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,gBAAgB,GAAG,SAAS,CAiCzE;AAED;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;AAExD;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,4BAA+E,CAAC;AAExG;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,aAAa,GAAG,IAAI,CAEzE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAoBpE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CA4BrE"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, dependency-free serialization helpers used by the log targets.
|
|
3
|
+
*
|
|
4
|
+
* These functions are intentionally browser-safe: they use no Node-only APIs
|
|
5
|
+
* ( no `util`, no `Buffer` ), only plain ECMAScript. They are also written to
|
|
6
|
+
* be defensive - neither of them may ever throw, because a logger that crashes
|
|
7
|
+
* while trying to log an error is worse than useless.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Max depth we walk down the `.cause` chain. A self-referential cause
|
|
11
|
+
* ( `err.cause = err` ) would otherwise loop forever, so we cap the walk.
|
|
12
|
+
*/
|
|
13
|
+
const MAX_CAUSE_DEPTH = 10;
|
|
14
|
+
/**
|
|
15
|
+
* Best-effort read of a property that might be defined via a throwing getter.
|
|
16
|
+
* Returns `undefined` instead of propagating the throw.
|
|
17
|
+
*/
|
|
18
|
+
function safeGet(obj, key) {
|
|
19
|
+
try {
|
|
20
|
+
return obj[key];
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Returns the most useful string form of an error for embedding in a stack:
|
|
28
|
+
* its `.stack` when available, otherwise its `.message`, otherwise `String()`.
|
|
29
|
+
*/
|
|
30
|
+
function errorText(err) {
|
|
31
|
+
const stack = safeGet(err, "stack");
|
|
32
|
+
if (typeof stack === "string" && stack.length > 0) {
|
|
33
|
+
return stack;
|
|
34
|
+
}
|
|
35
|
+
const message = safeGet(err, "message");
|
|
36
|
+
if (typeof message === "string" && message.length > 0) {
|
|
37
|
+
return message;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
return String(err);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return "[unserializable error]";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Builds a combined stack string that walks the `.cause` chain and, for
|
|
48
|
+
* `AggregateError`, its inner `errors`. Mirrors bunyan's `getFullErrorStack`:
|
|
49
|
+
* each nested error is appended under a `Caused by:` line.
|
|
50
|
+
*
|
|
51
|
+
* `seen` guards against cycles ( shared cause objects ) and `depth` caps how
|
|
52
|
+
* far down the chain we recurse, so a self-referential cause cannot hang.
|
|
53
|
+
*/
|
|
54
|
+
function buildFullStack(err, seen, depth) {
|
|
55
|
+
let out = errorText(err);
|
|
56
|
+
if (depth >= MAX_CAUSE_DEPTH || seen.has(err)) {
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
seen.add(err);
|
|
60
|
+
// Follow the standard `.cause` chain ( ES2022 error cause ).
|
|
61
|
+
const cause = safeGet(err, "cause");
|
|
62
|
+
if (cause instanceof Error && !seen.has(cause)) {
|
|
63
|
+
out += "\nCaused by: " + buildFullStack(cause, seen, depth + 1);
|
|
64
|
+
}
|
|
65
|
+
// AggregateError ( or any error carrying an `errors` array ) - surface each
|
|
66
|
+
// inner error under the same `Caused by:` style.
|
|
67
|
+
const errors = safeGet(err, "errors");
|
|
68
|
+
if (Array.isArray(errors)) {
|
|
69
|
+
for (const inner of errors) {
|
|
70
|
+
if (inner instanceof Error && !seen.has(inner)) {
|
|
71
|
+
out += "\nCaused by: " + buildFullStack(inner, seen, depth + 1);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Turn an `Error` into a plain, JSON-friendly record.
|
|
79
|
+
*
|
|
80
|
+
* - Returns `undefined` for anything that is not an `Error` ( callers only
|
|
81
|
+
* serialize actual errors ).
|
|
82
|
+
* - `stack` walks the `.cause` chain and any `AggregateError.errors`, appending
|
|
83
|
+
* each nested error under a `Caused by:` line ( bunyan-style ).
|
|
84
|
+
* - `code` / `signal` are included only when present ( common on Node system
|
|
85
|
+
* errors such as `ECONNREFUSED` / `SIGTERM` ).
|
|
86
|
+
* - Never throws.
|
|
87
|
+
*/
|
|
88
|
+
export function serializeError(err) {
|
|
89
|
+
if (!(err instanceof Error)) {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
const name = typeof err.name === "string" ? err.name : "Error";
|
|
94
|
+
const message = typeof err.message === "string" ? err.message : "";
|
|
95
|
+
const result = { name, message };
|
|
96
|
+
const stack = buildFullStack(err, new Set(), 0);
|
|
97
|
+
if (stack.length > 0) {
|
|
98
|
+
result.stack = stack;
|
|
99
|
+
}
|
|
100
|
+
// Node system errors carry a `code` ( e.g. 'ECONNREFUSED' ) and sometimes a
|
|
101
|
+
// `signal` ( e.g. 'SIGTERM' ). Include them only when actually present.
|
|
102
|
+
const code = safeGet(err, "code");
|
|
103
|
+
if (typeof code === "string" || typeof code === "number") {
|
|
104
|
+
result.code = code;
|
|
105
|
+
}
|
|
106
|
+
const signal = safeGet(err, "signal");
|
|
107
|
+
if (typeof signal === "string") {
|
|
108
|
+
result.signal = signal;
|
|
109
|
+
}
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// Absolute last-resort guard - serialization must never throw.
|
|
114
|
+
return { name: "Error", message: "[unserializable error]" };
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Registry of field-name -> serializer. Applied by {@link applySerializers} to
|
|
119
|
+
* every log entry's variables. Out of the box the SpinaJS `error` variable
|
|
120
|
+
* ( set by `createLogMessageObject` ) is serialized by {@link serializeError },
|
|
121
|
+
* so `error` becomes a plain `{ name, message, stack, code, signal }` record
|
|
122
|
+
* instead of an opaque `Error`.
|
|
123
|
+
*/
|
|
124
|
+
export const serializers = new Map([["error", serializeError]]);
|
|
125
|
+
/**
|
|
126
|
+
* Register ( or override ) the serializer used for a given log-variable field.
|
|
127
|
+
*/
|
|
128
|
+
export function registerSerializer(field, fn) {
|
|
129
|
+
serializers.set(field, fn);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Apply the registered serializers to a log entry's variables, MUTATING `vars`
|
|
133
|
+
* in place.
|
|
134
|
+
*
|
|
135
|
+
* For each registered `[field, fn]`:
|
|
136
|
+
* - skip when `field` is absent or its value is `undefined` / `null`;
|
|
137
|
+
* - run `fn` inside a try/catch:
|
|
138
|
+
* - on success, overwrite `vars[field]` ONLY when the serializer returned
|
|
139
|
+
* a defined value, so a serializer that returns `undefined` for an
|
|
140
|
+
* unhandled value ( like `serializeError` on a non-Error ) leaves the
|
|
141
|
+
* original untouched;
|
|
142
|
+
* - on throw, replace the value with `{ serializerError: <message> }` so a
|
|
143
|
+
* broken serializer degrades gracefully and NEVER crashes the caller.
|
|
144
|
+
*/
|
|
145
|
+
export function applySerializers(vars) {
|
|
146
|
+
for (const [field, fn] of serializers) {
|
|
147
|
+
if (!(field in vars)) {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
const value = vars[field];
|
|
151
|
+
if (value === undefined || value === null) {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
const s = fn(value);
|
|
156
|
+
if (s !== undefined) {
|
|
157
|
+
vars[field] = s;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch (e) {
|
|
161
|
+
vars[field] = { serializerError: e?.message ?? String(e) };
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* JSON-stringify any value without ever throwing.
|
|
167
|
+
*
|
|
168
|
+
* Three tiers:
|
|
169
|
+
* 1. plain `JSON.stringify` ( the common, fast path );
|
|
170
|
+
* 2. on throw ( typically circular references ) retry with a replacer that
|
|
171
|
+
* swaps already-seen objects for the string `"[Circular]"`, producing
|
|
172
|
+
* VALID JSON;
|
|
173
|
+
* 3. if that still throws ( e.g. a getter that throws while enumerating ),
|
|
174
|
+
* fall back to `String(value)`.
|
|
175
|
+
*/
|
|
176
|
+
export function safeStringify(value, indent) {
|
|
177
|
+
try {
|
|
178
|
+
// Tier 1: the fast, common path.
|
|
179
|
+
return JSON.stringify(value, undefined, indent);
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
// Tier 2: handle circular references with a WeakSet of seen objects.
|
|
183
|
+
try {
|
|
184
|
+
const seen = new WeakSet();
|
|
185
|
+
const replacer = (_key, val) => {
|
|
186
|
+
if (val !== null && typeof val === "object") {
|
|
187
|
+
if (seen.has(val)) {
|
|
188
|
+
return "[Circular]";
|
|
189
|
+
}
|
|
190
|
+
seen.add(val);
|
|
191
|
+
}
|
|
192
|
+
return val;
|
|
193
|
+
};
|
|
194
|
+
return JSON.stringify(value, replacer, indent);
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
// Tier 3: something else threw ( e.g. a throwing getter ). Give up on
|
|
198
|
+
// structured output but still return *a* string.
|
|
199
|
+
try {
|
|
200
|
+
return String(value);
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
return "[unserializable value]";
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=serializers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serializers.js","sourceRoot":"","sources":["../../src/serializers.ts"],"names":[],"mappings":"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"}
|