@spinajs/log-common 2.0.481 → 2.0.482
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 +207 -33
- 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 +201 -10
- 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,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.persistLevel = persistLevel;
|
|
4
|
+
exports.readPersistedLevel = readPersistedLevel;
|
|
5
|
+
exports.clearPersistedLevel = clearPersistedLevel;
|
|
6
|
+
/**
|
|
7
|
+
* Browser-only runtime log-level persistence ( loglevel-style ). Lets a user's
|
|
8
|
+
* `setLevel(...)` choice survive page reloads via `localStorage`, with a
|
|
9
|
+
* `document.cookie` fallback for environments where storage throws ( eg. Safari
|
|
10
|
+
* private mode ). On Node ( no `window` ) every helper is a clean no-op.
|
|
11
|
+
*
|
|
12
|
+
* Kept dependency-free and free of any `node:*` imports so log-common stays
|
|
13
|
+
* browser-safe: the only globals touched are `window` / `document`, guarded by
|
|
14
|
+
* `typeof` checks.
|
|
15
|
+
*/
|
|
16
|
+
const KEY_PREFIX = "spinajs:log:level:";
|
|
17
|
+
function storageKey(loggerName) {
|
|
18
|
+
return `${KEY_PREFIX}${loggerName}`;
|
|
19
|
+
}
|
|
20
|
+
/** True only in a browser-like environment with a usable localStorage. */
|
|
21
|
+
function hasWindow() {
|
|
22
|
+
return typeof window !== "undefined";
|
|
23
|
+
}
|
|
24
|
+
function writeCookie(key, value) {
|
|
25
|
+
if (typeof document === "undefined") {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
// 1 year, path=/ so the value is visible across the app.
|
|
30
|
+
const maxAge = 60 * 60 * 24 * 365;
|
|
31
|
+
document.cookie = `${encodeURIComponent(key)}=${encodeURIComponent(value)};path=/;max-age=${maxAge}`;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// nothing else we can do - persistence is best-effort.
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function readCookie(key) {
|
|
38
|
+
if (typeof document === "undefined") {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const enc = encodeURIComponent(key);
|
|
43
|
+
const parts = document.cookie ? document.cookie.split(";") : [];
|
|
44
|
+
for (const part of parts) {
|
|
45
|
+
const [k, ...rest] = part.trim().split("=");
|
|
46
|
+
if (k === enc) {
|
|
47
|
+
return decodeURIComponent(rest.join("="));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// ignore
|
|
53
|
+
}
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
function expireCookie(key) {
|
|
57
|
+
if (typeof document === "undefined") {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
document.cookie = `${encodeURIComponent(key)}=;path=/;max-age=0`;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// ignore
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Persist `level` for `loggerName`. Browser only - writes `String(level)` to
|
|
69
|
+
* localStorage under `spinajs:log:level:<loggerName>`, falling back to a cookie
|
|
70
|
+
* when storage throws. No-op on Node.
|
|
71
|
+
*/
|
|
72
|
+
function persistLevel(loggerName, level) {
|
|
73
|
+
if (!hasWindow()) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const key = storageKey(loggerName);
|
|
77
|
+
const value = String(level);
|
|
78
|
+
try {
|
|
79
|
+
// window.localStorage access itself can throw ( disabled cookies / private mode ).
|
|
80
|
+
if (window.localStorage) {
|
|
81
|
+
window.localStorage.setItem(key, value);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// fall through to the cookie fallback below.
|
|
87
|
+
}
|
|
88
|
+
writeCookie(key, value);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Read back the persisted level for `loggerName` ( localStorage then cookie ).
|
|
92
|
+
* Returns a finite {@link LogLevel} number, or `undefined` when nothing valid is
|
|
93
|
+
* stored. Always `undefined` on Node.
|
|
94
|
+
*/
|
|
95
|
+
function readPersistedLevel(loggerName) {
|
|
96
|
+
if (!hasWindow()) {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
const key = storageKey(loggerName);
|
|
100
|
+
let raw;
|
|
101
|
+
try {
|
|
102
|
+
if (window.localStorage) {
|
|
103
|
+
raw = window.localStorage.getItem(key);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
raw = undefined;
|
|
108
|
+
}
|
|
109
|
+
if (raw === null || raw === undefined) {
|
|
110
|
+
raw = readCookie(key);
|
|
111
|
+
}
|
|
112
|
+
if (raw === null || raw === undefined || raw === "") {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
const parsed = Number(raw);
|
|
116
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Remove any persisted level for `loggerName` ( localStorage + cookie ). No-op
|
|
120
|
+
* on Node.
|
|
121
|
+
*/
|
|
122
|
+
function clearPersistedLevel(loggerName) {
|
|
123
|
+
if (!hasWindow()) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const key = storageKey(loggerName);
|
|
127
|
+
try {
|
|
128
|
+
if (window.localStorage) {
|
|
129
|
+
window.localStorage.removeItem(key);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// ignore - fall through to also expire the cookie.
|
|
134
|
+
}
|
|
135
|
+
expireCookie(key);
|
|
136
|
+
}
|
|
137
|
+
//# 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,215 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Pure, dependency-free serialization helpers used by the log targets.
|
|
4
|
+
*
|
|
5
|
+
* These functions are intentionally browser-safe: they use no Node-only APIs
|
|
6
|
+
* ( no `util`, no `Buffer` ), only plain ECMAScript. They are also written to
|
|
7
|
+
* be defensive - neither of them may ever throw, because a logger that crashes
|
|
8
|
+
* while trying to log an error is worse than useless.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.serializers = void 0;
|
|
12
|
+
exports.serializeError = serializeError;
|
|
13
|
+
exports.registerSerializer = registerSerializer;
|
|
14
|
+
exports.applySerializers = applySerializers;
|
|
15
|
+
exports.safeStringify = safeStringify;
|
|
16
|
+
/**
|
|
17
|
+
* Max depth we walk down the `.cause` chain. A self-referential cause
|
|
18
|
+
* ( `err.cause = err` ) would otherwise loop forever, so we cap the walk.
|
|
19
|
+
*/
|
|
20
|
+
const MAX_CAUSE_DEPTH = 10;
|
|
21
|
+
/**
|
|
22
|
+
* Best-effort read of a property that might be defined via a throwing getter.
|
|
23
|
+
* Returns `undefined` instead of propagating the throw.
|
|
24
|
+
*/
|
|
25
|
+
function safeGet(obj, key) {
|
|
26
|
+
try {
|
|
27
|
+
return obj[key];
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Returns the most useful string form of an error for embedding in a stack:
|
|
35
|
+
* its `.stack` when available, otherwise its `.message`, otherwise `String()`.
|
|
36
|
+
*/
|
|
37
|
+
function errorText(err) {
|
|
38
|
+
const stack = safeGet(err, "stack");
|
|
39
|
+
if (typeof stack === "string" && stack.length > 0) {
|
|
40
|
+
return stack;
|
|
41
|
+
}
|
|
42
|
+
const message = safeGet(err, "message");
|
|
43
|
+
if (typeof message === "string" && message.length > 0) {
|
|
44
|
+
return message;
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
return String(err);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return "[unserializable error]";
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Builds a combined stack string that walks the `.cause` chain and, for
|
|
55
|
+
* `AggregateError`, its inner `errors`. Mirrors bunyan's `getFullErrorStack`:
|
|
56
|
+
* each nested error is appended under a `Caused by:` line.
|
|
57
|
+
*
|
|
58
|
+
* `seen` guards against cycles ( shared cause objects ) and `depth` caps how
|
|
59
|
+
* far down the chain we recurse, so a self-referential cause cannot hang.
|
|
60
|
+
*/
|
|
61
|
+
function buildFullStack(err, seen, depth) {
|
|
62
|
+
let out = errorText(err);
|
|
63
|
+
if (depth >= MAX_CAUSE_DEPTH || seen.has(err)) {
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
seen.add(err);
|
|
67
|
+
// Follow the standard `.cause` chain ( ES2022 error cause ).
|
|
68
|
+
const cause = safeGet(err, "cause");
|
|
69
|
+
if (cause instanceof Error && !seen.has(cause)) {
|
|
70
|
+
out += "\nCaused by: " + buildFullStack(cause, seen, depth + 1);
|
|
71
|
+
}
|
|
72
|
+
// AggregateError ( or any error carrying an `errors` array ) - surface each
|
|
73
|
+
// inner error under the same `Caused by:` style.
|
|
74
|
+
const errors = safeGet(err, "errors");
|
|
75
|
+
if (Array.isArray(errors)) {
|
|
76
|
+
for (const inner of errors) {
|
|
77
|
+
if (inner instanceof Error && !seen.has(inner)) {
|
|
78
|
+
out += "\nCaused by: " + buildFullStack(inner, seen, depth + 1);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Turn an `Error` into a plain, JSON-friendly record.
|
|
86
|
+
*
|
|
87
|
+
* - Returns `undefined` for anything that is not an `Error` ( callers only
|
|
88
|
+
* serialize actual errors ).
|
|
89
|
+
* - `stack` walks the `.cause` chain and any `AggregateError.errors`, appending
|
|
90
|
+
* each nested error under a `Caused by:` line ( bunyan-style ).
|
|
91
|
+
* - `code` / `signal` are included only when present ( common on Node system
|
|
92
|
+
* errors such as `ECONNREFUSED` / `SIGTERM` ).
|
|
93
|
+
* - Never throws.
|
|
94
|
+
*/
|
|
95
|
+
function serializeError(err) {
|
|
96
|
+
if (!(err instanceof Error)) {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
const name = typeof err.name === "string" ? err.name : "Error";
|
|
101
|
+
const message = typeof err.message === "string" ? err.message : "";
|
|
102
|
+
const result = { name, message };
|
|
103
|
+
const stack = buildFullStack(err, new Set(), 0);
|
|
104
|
+
if (stack.length > 0) {
|
|
105
|
+
result.stack = stack;
|
|
106
|
+
}
|
|
107
|
+
// Node system errors carry a `code` ( e.g. 'ECONNREFUSED' ) and sometimes a
|
|
108
|
+
// `signal` ( e.g. 'SIGTERM' ). Include them only when actually present.
|
|
109
|
+
const code = safeGet(err, "code");
|
|
110
|
+
if (typeof code === "string" || typeof code === "number") {
|
|
111
|
+
result.code = code;
|
|
112
|
+
}
|
|
113
|
+
const signal = safeGet(err, "signal");
|
|
114
|
+
if (typeof signal === "string") {
|
|
115
|
+
result.signal = signal;
|
|
116
|
+
}
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// Absolute last-resort guard - serialization must never throw.
|
|
121
|
+
return { name: "Error", message: "[unserializable error]" };
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Registry of field-name -> serializer. Applied by {@link applySerializers} to
|
|
126
|
+
* every log entry's variables. Out of the box the SpinaJS `error` variable
|
|
127
|
+
* ( set by `createLogMessageObject` ) is serialized by {@link serializeError },
|
|
128
|
+
* so `error` becomes a plain `{ name, message, stack, code, signal }` record
|
|
129
|
+
* instead of an opaque `Error`.
|
|
130
|
+
*/
|
|
131
|
+
exports.serializers = new Map([["error", serializeError]]);
|
|
132
|
+
/**
|
|
133
|
+
* Register ( or override ) the serializer used for a given log-variable field.
|
|
134
|
+
*/
|
|
135
|
+
function registerSerializer(field, fn) {
|
|
136
|
+
exports.serializers.set(field, fn);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Apply the registered serializers to a log entry's variables, MUTATING `vars`
|
|
140
|
+
* in place.
|
|
141
|
+
*
|
|
142
|
+
* For each registered `[field, fn]`:
|
|
143
|
+
* - skip when `field` is absent or its value is `undefined` / `null`;
|
|
144
|
+
* - run `fn` inside a try/catch:
|
|
145
|
+
* - on success, overwrite `vars[field]` ONLY when the serializer returned
|
|
146
|
+
* a defined value, so a serializer that returns `undefined` for an
|
|
147
|
+
* unhandled value ( like `serializeError` on a non-Error ) leaves the
|
|
148
|
+
* original untouched;
|
|
149
|
+
* - on throw, replace the value with `{ serializerError: <message> }` so a
|
|
150
|
+
* broken serializer degrades gracefully and NEVER crashes the caller.
|
|
151
|
+
*/
|
|
152
|
+
function applySerializers(vars) {
|
|
153
|
+
for (const [field, fn] of exports.serializers) {
|
|
154
|
+
if (!(field in vars)) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const value = vars[field];
|
|
158
|
+
if (value === undefined || value === null) {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
const s = fn(value);
|
|
163
|
+
if (s !== undefined) {
|
|
164
|
+
vars[field] = s;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
catch (e) {
|
|
168
|
+
vars[field] = { serializerError: e?.message ?? String(e) };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* JSON-stringify any value without ever throwing.
|
|
174
|
+
*
|
|
175
|
+
* Three tiers:
|
|
176
|
+
* 1. plain `JSON.stringify` ( the common, fast path );
|
|
177
|
+
* 2. on throw ( typically circular references ) retry with a replacer that
|
|
178
|
+
* swaps already-seen objects for the string `"[Circular]"`, producing
|
|
179
|
+
* VALID JSON;
|
|
180
|
+
* 3. if that still throws ( e.g. a getter that throws while enumerating ),
|
|
181
|
+
* fall back to `String(value)`.
|
|
182
|
+
*/
|
|
183
|
+
function safeStringify(value, indent) {
|
|
184
|
+
try {
|
|
185
|
+
// Tier 1: the fast, common path.
|
|
186
|
+
return JSON.stringify(value, undefined, indent);
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
// Tier 2: handle circular references with a WeakSet of seen objects.
|
|
190
|
+
try {
|
|
191
|
+
const seen = new WeakSet();
|
|
192
|
+
const replacer = (_key, val) => {
|
|
193
|
+
if (val !== null && typeof val === "object") {
|
|
194
|
+
if (seen.has(val)) {
|
|
195
|
+
return "[Circular]";
|
|
196
|
+
}
|
|
197
|
+
seen.add(val);
|
|
198
|
+
}
|
|
199
|
+
return val;
|
|
200
|
+
};
|
|
201
|
+
return JSON.stringify(value, replacer, indent);
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
// Tier 3: something else threw ( e.g. a throwing getter ). Give up on
|
|
205
|
+
// structured output but still return *a* string.
|
|
206
|
+
try {
|
|
207
|
+
return String(value);
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
return "[unserializable value]";
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=serializers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serializers.js","sourceRoot":"","sources":["../../src/serializers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A small, dependency-free, browser-safe buffered-batch queue.
|
|
3
|
+
*
|
|
4
|
+
* `BatchQueue<T>` owns ACCUMULATION and FLUSH TRIGGERING only; it delegates the
|
|
5
|
+
* actual I/O to the owner via `onFlush`. Ported from the essence of
|
|
6
|
+
* OpenTelemetry's BatchLogRecordProcessor.
|
|
7
|
+
*
|
|
8
|
+
* Triggers a flush when either:
|
|
9
|
+
* - the buffer reaches `maxBatch` items ( size trigger ), or
|
|
10
|
+
* - the periodic timer fires every `flushIntervalMs` ( interval trigger ).
|
|
11
|
+
*
|
|
12
|
+
* ## Failure model
|
|
13
|
+
*
|
|
14
|
+
* There are two, deliberately distinct, ways items can leave the queue:
|
|
15
|
+
*
|
|
16
|
+
* 1. **Overflow ( the ONLY silent drop ).** The buffer is hard-capped at
|
|
17
|
+
* `maxQueue`. When it grows past the cap the OLDEST items are dropped so
|
|
18
|
+
* memory stays bounded, and `onOverflow(droppedItems)` fires. This is the
|
|
19
|
+
* back-pressure release valve for a stuck/slow sink.
|
|
20
|
+
*
|
|
21
|
+
* 2. **Flush failure ( never a silent drop ).** `onFlush` may throw / reject.
|
|
22
|
+
* `BatchQueue` does NOT retry, requeue, or swallow the batch on its own —
|
|
23
|
+
* that is the owner's decision. The rejection PROPAGATES to any caller that
|
|
24
|
+
* awaits `flush()` / `forceFlush()`, so the owner can `catch` it and call
|
|
25
|
+
* `requeueFront(batch)` to put the batch back at the front ( never-drop
|
|
26
|
+
* semantics ) or drop it deliberately. The periodic timer intentionally
|
|
27
|
+
* ignores the rejection ( `void this.flush()` ) so a failing sink never
|
|
28
|
+
* kills the flush loop; the in-flight guard is always cleared in `finally`
|
|
29
|
+
* so the next flush can proceed regardless of outcome.
|
|
30
|
+
*
|
|
31
|
+
* ## Re-entrancy
|
|
32
|
+
*
|
|
33
|
+
* At most one `onFlush` runs at a time. If a flush is already in flight, calling
|
|
34
|
+
* `flush()` returns the SAME in-flight promise ( coalesced ) rather than
|
|
35
|
+
* starting a second `onFlush`. A flush snapshots and clears the buffer
|
|
36
|
+
* synchronously before awaiting the I/O, so items enqueued during a flush land
|
|
37
|
+
* in a fresh buffer and are picked up by the next flush.
|
|
38
|
+
*
|
|
39
|
+
* ## Runtime
|
|
40
|
+
*
|
|
41
|
+
* Uses only `setInterval` / `setTimeout` ( available in both Node and browsers ).
|
|
42
|
+
* The periodic timer ( and the optional per-flush timeout timer ) are `unref()`'d
|
|
43
|
+
* when that method exists, so they never keep a Node process alive; the `?.`
|
|
44
|
+
* guard makes the calls harmless in browsers.
|
|
45
|
+
*/
|
|
46
|
+
export interface IBatchQueueOptions<T> {
|
|
47
|
+
/** Flush automatically once the buffer reaches this many items. */
|
|
48
|
+
maxBatch: number;
|
|
49
|
+
/** Hard cap on buffered items. Beyond it the OLDEST are dropped ( onOverflow fires ). */
|
|
50
|
+
maxQueue: number;
|
|
51
|
+
/** Periodic flush tick in ms. The timer is unref()'d so it never blocks process exit. */
|
|
52
|
+
flushIntervalMs: number;
|
|
53
|
+
/** Optional per-flush timeout in ms. If onFlush does not settle in time, the flush rejects. */
|
|
54
|
+
exportTimeoutMs?: number;
|
|
55
|
+
/** Does the actual I/O for a batch. May throw/reject; the owner handles retry/requeue/drop. */
|
|
56
|
+
onFlush: (items: T[]) => Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Called when the queue cap is exceeded and the oldest items are dropped.
|
|
59
|
+
* Receives the DROPPED ITEMS themselves ( oldest first ) so an owner can route
|
|
60
|
+
* exactly what was dropped to a fallback ( see LogTarget.OnDropped ). Use
|
|
61
|
+
* `droppedItems.length` when only the count matters.
|
|
62
|
+
*/
|
|
63
|
+
onOverflow?: (droppedItems: T[]) => void;
|
|
64
|
+
}
|
|
65
|
+
export declare class BatchQueue<T> {
|
|
66
|
+
private options;
|
|
67
|
+
private buffer;
|
|
68
|
+
private timer;
|
|
69
|
+
private stopped;
|
|
70
|
+
/**
|
|
71
|
+
* The currently running flush, or null when idle. Used both as the
|
|
72
|
+
* re-entrancy guard ( coalesce concurrent flush() calls ) and to let
|
|
73
|
+
* forceFlush()/shutdown() wait for an in-flight flush before draining.
|
|
74
|
+
*/
|
|
75
|
+
private inFlight;
|
|
76
|
+
constructor(options: IBatchQueueOptions<T>);
|
|
77
|
+
/** Current number of buffered items. */
|
|
78
|
+
get size(): number;
|
|
79
|
+
/**
|
|
80
|
+
* Add an item to the buffer.
|
|
81
|
+
*
|
|
82
|
+
* If the queue has been shut down this is a no-op that resolves immediately.
|
|
83
|
+
* When the buffer reaches `maxBatch` a flush is triggered and its promise is
|
|
84
|
+
* returned, so a caller MAY await a size-triggered flush; otherwise resolves
|
|
85
|
+
* immediately.
|
|
86
|
+
*/
|
|
87
|
+
enqueue(item: T): Promise<void>;
|
|
88
|
+
/**
|
|
89
|
+
* Put items back at the FRONT of the buffer ( then enforce the cap ). Owners
|
|
90
|
+
* use this to re-queue a batch whose `onFlush` failed, without dropping it.
|
|
91
|
+
*/
|
|
92
|
+
requeueFront(items: T[]): void;
|
|
93
|
+
/**
|
|
94
|
+
* Enforce the hard `maxQueue` cap by dropping the OLDEST items. Fires
|
|
95
|
+
* `onOverflow(droppedItems)` with the spliced-out items when anything is dropped.
|
|
96
|
+
*/
|
|
97
|
+
private enforceCap;
|
|
98
|
+
/**
|
|
99
|
+
* Flush the entire buffer in a single `onFlush` call.
|
|
100
|
+
*
|
|
101
|
+
* Re-entrancy: if a flush is already in flight, that same promise is returned
|
|
102
|
+
* ( no second `onFlush` ). If the buffer is empty, resolves immediately.
|
|
103
|
+
* Otherwise the buffer is snapshotted and cleared synchronously, `onFlush`
|
|
104
|
+
* runs ( optionally raced against `exportTimeoutMs` ), and the in-flight guard
|
|
105
|
+
* is cleared in `finally` regardless of success or failure. The returned
|
|
106
|
+
* promise settles with `onFlush`'s outcome so an awaiting owner can catch a
|
|
107
|
+
* rejection and requeue.
|
|
108
|
+
*/
|
|
109
|
+
flush(): Promise<void>;
|
|
110
|
+
/**
|
|
111
|
+
* Race a flush promise against a timeout that rejects after `ms`. The timeout
|
|
112
|
+
* timer is cleared ( and unref()'d ) so it neither leaks nor keeps the loop alive.
|
|
113
|
+
*/
|
|
114
|
+
private withTimeout;
|
|
115
|
+
/**
|
|
116
|
+
* Flush any buffered items and await completion. If a flush is already in
|
|
117
|
+
* flight, wait for it first, then flush any remainder that accumulated. Since
|
|
118
|
+
* a single `flush()` drains the ENTIRE buffer, one follow-up flush suffices.
|
|
119
|
+
*/
|
|
120
|
+
forceFlush(): Promise<void>;
|
|
121
|
+
/**
|
|
122
|
+
* Stop the periodic timer and perform a best-effort final flush of buffered
|
|
123
|
+
* items. After shutdown, `enqueue` is a no-op that resolves.
|
|
124
|
+
*/
|
|
125
|
+
shutdown(): Promise<void>;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=BatchQueue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BatchQueue.d.ts","sourceRoot":"","sources":["../../src/BatchQueue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,MAAM,WAAW,kBAAkB,CAAC,CAAC;IACnC,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC;IACjB,yFAAyF;IACzF,QAAQ,EAAE,MAAM,CAAC;IACjB,yFAAyF;IACzF,eAAe,EAAE,MAAM,CAAC;IACxB,+FAA+F;IAC/F,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,+FAA+F;IAC/F,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,EAAE,KAAK,IAAI,CAAC;CAC1C;AAED,qBAAa,UAAU,CAAC,CAAC;IAcX,OAAO,CAAC,OAAO;IAb3B,OAAO,CAAC,MAAM,CAAW;IAEzB,OAAO,CAAC,KAAK,CAAiC;IAE9C,OAAO,CAAC,OAAO,CAAS;IAExB;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAA8B;gBAE1B,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC;IAMlD,wCAAwC;IACxC,IAAW,IAAI,IAAI,MAAM,CAExB;IAED;;;;;;;OAOG;IACI,OAAO,CAAC,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAetC;;;OAGG;IACI,YAAY,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI;IAKrC;;;OAGG;IACH,OAAO,CAAC,UAAU;IAQlB;;;;;;;;;;OAUG;IACI,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAwB7B;;;OAGG;IACH,OAAO,CAAC,WAAW;IAanB;;;;OAIG;IACU,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAQxC;;;OAGG;IACU,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAKvC"}
|