@zintrust/trace 0.4.92 → 0.4.94
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/dist/build-manifest.json +17 -9
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/register.js +2 -1
- package/dist/storage/TraceContentBudget.d.ts +4 -0
- package/dist/storage/TraceContentBudget.js +114 -0
- package/package.json +2 -2
- package/src/index.ts +1 -0
- package/src/register.ts +6 -3
- package/src/storage/TraceContentBudget.ts +145 -0
package/dist/build-manifest.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zintrust/trace",
|
|
3
|
-
"version": "0.4.
|
|
4
|
-
"buildDate": "2026-04-
|
|
3
|
+
"version": "0.4.94",
|
|
4
|
+
"buildDate": "2026-04-11T09:24:47.024Z",
|
|
5
5
|
"buildEnvironment": {
|
|
6
6
|
"node": "v20.20.2",
|
|
7
7
|
"platform": "linux",
|
|
8
8
|
"arch": "x64"
|
|
9
9
|
},
|
|
10
10
|
"git": {
|
|
11
|
-
"commit": "
|
|
11
|
+
"commit": "785d0351",
|
|
12
12
|
"branch": "master"
|
|
13
13
|
},
|
|
14
14
|
"package": {
|
|
@@ -70,12 +70,12 @@
|
|
|
70
70
|
"sha256": "2903901d8c0c5076118aa691727daa79be7abe87fdb393c5b389a2b1a8fce170"
|
|
71
71
|
},
|
|
72
72
|
"index.d.ts": {
|
|
73
|
-
"size":
|
|
74
|
-
"sha256": "
|
|
73
|
+
"size": 2537,
|
|
74
|
+
"sha256": "1707d26322dbad17f6bf85938ae6fe2477e84c7fed3333760ce8d6eadfaaffd2"
|
|
75
75
|
},
|
|
76
76
|
"index.js": {
|
|
77
|
-
"size":
|
|
78
|
-
"sha256": "
|
|
77
|
+
"size": 3325,
|
|
78
|
+
"sha256": "0926788b00fc68f513ae20a508f2ef3c2f4e7e907fa32d70f146ce6e8b0ec812"
|
|
79
79
|
},
|
|
80
80
|
"migrations/20260331000001_create_zin_trace_entries_table.d.ts": {
|
|
81
81
|
"size": 304,
|
|
@@ -130,8 +130,16 @@
|
|
|
130
130
|
"sha256": "71d366165dd36f1675aa253a76262b226fb6c62e5ab632746b8aea61c0c625fc"
|
|
131
131
|
},
|
|
132
132
|
"register.js": {
|
|
133
|
-
"size":
|
|
134
|
-
"sha256": "
|
|
133
|
+
"size": 14327,
|
|
134
|
+
"sha256": "efc9bb131b9eef7e81a6f85ea3338fd8ac74257f794aa0fc7c76efed636f99d8"
|
|
135
|
+
},
|
|
136
|
+
"storage/TraceContentBudget.d.ts": {
|
|
137
|
+
"size": 159,
|
|
138
|
+
"sha256": "d899a615e6cf2a5eea51f6200347cb81fbaae11979b801859f57135083aaf85b"
|
|
139
|
+
},
|
|
140
|
+
"storage/TraceContentBudget.js": {
|
|
141
|
+
"size": 4022,
|
|
142
|
+
"sha256": "4b1d4f0ad7da15caeaa1f9fe8b4edcd3000b0000c5bdc270601cd6db2eedce2e"
|
|
135
143
|
},
|
|
136
144
|
"storage/TraceContentRedaction.d.ts": {
|
|
137
145
|
"size": 207,
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
export { TraceConfig } from './config';
|
|
9
9
|
export { TraceStorage } from './storage';
|
|
10
10
|
export type { ITraceStorage } from './storage';
|
|
11
|
+
export { TraceContentBudget } from './storage/TraceContentBudget';
|
|
11
12
|
export { TraceContentRedaction } from './storage/TraceContentRedaction';
|
|
12
13
|
export { TraceContext } from './context';
|
|
13
14
|
export { registerTraceDashboard, registerTraceRoutes } from './dashboard/routes';
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,7 @@ export { TraceConfig } from './config.js';
|
|
|
14
14
|
// Storage
|
|
15
15
|
// ---------------------------------------------------------------------------
|
|
16
16
|
export { TraceStorage } from './storage/index.js';
|
|
17
|
+
export { TraceContentBudget } from './storage/TraceContentBudget.js';
|
|
17
18
|
export { TraceContentRedaction } from './storage/TraceContentRedaction.js';
|
|
18
19
|
// ---------------------------------------------------------------------------
|
|
19
20
|
// Context
|
package/dist/register.js
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
import { TraceConfig } from './config.js';
|
|
23
23
|
import { TraceContext } from './context.js';
|
|
24
24
|
import { TraceStorage } from './storage/index.js';
|
|
25
|
+
import { TraceContentBudget } from './storage/TraceContentBudget.js';
|
|
25
26
|
import { TraceContentRedaction } from './storage/TraceContentRedaction.js';
|
|
26
27
|
import { TraceEntryFiltering } from './storage/TraceEntryFiltering.js';
|
|
27
28
|
import { TraceWriteDiagnostics } from './storage/TraceWriteDiagnostics.js';
|
|
@@ -256,7 +257,7 @@ if (!traceAlreadyInitialized && Env) {
|
|
|
256
257
|
envKey: 'TRACE_QUERY_CONNECTION',
|
|
257
258
|
});
|
|
258
259
|
await assertTraceStorageReady(core, storageDb, resolvedConnectionName);
|
|
259
|
-
const storage = TraceWriteDiagnostics.wrapStorage(TraceContentRedaction.wrapStorage(TraceEntryFiltering.wrapStorage(TraceStorage.resolveStorage(storageDb), config), config.redaction), {
|
|
260
|
+
const storage = TraceWriteDiagnostics.wrapStorage(TraceContentBudget.wrapStorage(TraceContentRedaction.wrapStorage(TraceEntryFiltering.wrapStorage(TraceStorage.resolveStorage(storageDb), config), config.redaction)), {
|
|
260
261
|
connectionName: resolvedConnectionName,
|
|
261
262
|
logger: core.Logger,
|
|
262
263
|
});
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
const DEFAULT_MAX_ENTRY_BYTES = 64 * 1024;
|
|
2
|
+
const DEFAULT_MAX_STRING_BYTES = 16 * 1024;
|
|
3
|
+
const DEFAULT_MAX_ARRAY_ITEMS = 25;
|
|
4
|
+
const DEFAULT_MAX_OBJECT_ENTRIES = 40;
|
|
5
|
+
const DEFAULT_MAX_DEPTH = 6;
|
|
6
|
+
const DROPPED_FIELD_MESSAGE = '[trace] Value dropped because the field exceeded the trace storage size limit.';
|
|
7
|
+
const COMPACTED_CONTENT_MESSAGE = '[trace] Trace content was compacted because it exceeded the trace storage size limit.';
|
|
8
|
+
const encoder = new TextEncoder();
|
|
9
|
+
const serializedSize = (value) => {
|
|
10
|
+
try {
|
|
11
|
+
return encoder.encode(JSON.stringify(value)).length;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return Number.MAX_SAFE_INTEGER;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
const describeValueType = (value) => {
|
|
18
|
+
if (Array.isArray(value))
|
|
19
|
+
return 'array';
|
|
20
|
+
if (value === null)
|
|
21
|
+
return 'null';
|
|
22
|
+
return typeof value;
|
|
23
|
+
};
|
|
24
|
+
const compactValue = (value, depth) => {
|
|
25
|
+
if (depth >= DEFAULT_MAX_DEPTH) {
|
|
26
|
+
return DROPPED_FIELD_MESSAGE;
|
|
27
|
+
}
|
|
28
|
+
if (typeof value === 'string') {
|
|
29
|
+
return serializedSize(value) > DEFAULT_MAX_STRING_BYTES ? DROPPED_FIELD_MESSAGE : value;
|
|
30
|
+
}
|
|
31
|
+
if (Array.isArray(value)) {
|
|
32
|
+
const next = value
|
|
33
|
+
.slice(0, DEFAULT_MAX_ARRAY_ITEMS)
|
|
34
|
+
.map((item) => compactValue(item, depth + 1));
|
|
35
|
+
if (value.length > DEFAULT_MAX_ARRAY_ITEMS) {
|
|
36
|
+
next.push(`[trace] ${String(value.length - DEFAULT_MAX_ARRAY_ITEMS)} additional items were dropped.`);
|
|
37
|
+
}
|
|
38
|
+
return next;
|
|
39
|
+
}
|
|
40
|
+
if (typeof value !== 'object' || value === null) {
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
const entries = Object.entries(value);
|
|
44
|
+
const compactedEntries = entries
|
|
45
|
+
.slice(0, DEFAULT_MAX_OBJECT_ENTRIES)
|
|
46
|
+
.map(([key, entryValue]) => [key, compactValue(entryValue, depth + 1)]);
|
|
47
|
+
if (entries.length > DEFAULT_MAX_OBJECT_ENTRIES) {
|
|
48
|
+
compactedEntries.push([
|
|
49
|
+
'__traceNotice',
|
|
50
|
+
`[trace] ${String(entries.length - DEFAULT_MAX_OBJECT_ENTRIES)} additional fields were dropped.`,
|
|
51
|
+
]);
|
|
52
|
+
}
|
|
53
|
+
return Object.fromEntries(compactedEntries);
|
|
54
|
+
};
|
|
55
|
+
const compactTopLevelObjectToBudget = (value) => {
|
|
56
|
+
const compacted = {
|
|
57
|
+
...value,
|
|
58
|
+
__traceNotice: COMPACTED_CONTENT_MESSAGE,
|
|
59
|
+
};
|
|
60
|
+
const keysByDescendingSize = Object.keys(compacted)
|
|
61
|
+
.filter((key) => key !== '__traceNotice')
|
|
62
|
+
.sort((left, right) => serializedSize(compacted[right]) - serializedSize(compacted[left]));
|
|
63
|
+
for (const key of keysByDescendingSize) {
|
|
64
|
+
if (serializedSize(compacted) <= DEFAULT_MAX_ENTRY_BYTES)
|
|
65
|
+
break;
|
|
66
|
+
compacted[key] = DROPPED_FIELD_MESSAGE;
|
|
67
|
+
}
|
|
68
|
+
return compacted;
|
|
69
|
+
};
|
|
70
|
+
const fitContentToBudget = (content) => {
|
|
71
|
+
if (serializedSize(content) <= DEFAULT_MAX_ENTRY_BYTES) {
|
|
72
|
+
return content;
|
|
73
|
+
}
|
|
74
|
+
const compacted = compactValue(content, 0);
|
|
75
|
+
if (serializedSize(compacted) <= DEFAULT_MAX_ENTRY_BYTES) {
|
|
76
|
+
return compacted;
|
|
77
|
+
}
|
|
78
|
+
if (typeof compacted === 'object' && compacted !== null && !Array.isArray(compacted)) {
|
|
79
|
+
const topLevelCompacted = compactTopLevelObjectToBudget(compacted);
|
|
80
|
+
if (serializedSize(topLevelCompacted) <= DEFAULT_MAX_ENTRY_BYTES) {
|
|
81
|
+
return topLevelCompacted;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
__traceNotice: COMPACTED_CONTENT_MESSAGE,
|
|
86
|
+
dropped: true,
|
|
87
|
+
valueType: describeValueType(content),
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
const fitEntryToBudget = (entry) => ({
|
|
91
|
+
...entry,
|
|
92
|
+
content: fitContentToBudget(entry.content),
|
|
93
|
+
});
|
|
94
|
+
const fitPatchToBudget = (patch) => {
|
|
95
|
+
if (patch.content === undefined)
|
|
96
|
+
return patch;
|
|
97
|
+
return {
|
|
98
|
+
...patch,
|
|
99
|
+
content: fitContentToBudget(patch.content),
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
export const TraceContentBudget = Object.freeze({
|
|
103
|
+
wrapStorage(storage) {
|
|
104
|
+
return Object.freeze({
|
|
105
|
+
...storage,
|
|
106
|
+
writeEntry: async (entry) => {
|
|
107
|
+
await storage.writeEntry(fitEntryToBudget(entry));
|
|
108
|
+
},
|
|
109
|
+
updateEntry: async (uuid, patch) => {
|
|
110
|
+
await storage.updateEntry(uuid, fitPatchToBudget(patch));
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
},
|
|
114
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zintrust/trace",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.94",
|
|
4
4
|
"description": "Trace assistant for ZinTrust: logs requests, queries, exceptions, jobs, and more.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"node": ">=20.0.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"@zintrust/core": "^0.4.
|
|
43
|
+
"@zintrust/core": "^0.4.93"
|
|
44
44
|
},
|
|
45
45
|
"publishConfig": {
|
|
46
46
|
"access": "public"
|
package/src/index.ts
CHANGED
|
@@ -18,6 +18,7 @@ export { TraceConfig } from './config';
|
|
|
18
18
|
// ---------------------------------------------------------------------------
|
|
19
19
|
export { TraceStorage } from './storage';
|
|
20
20
|
export type { ITraceStorage } from './storage';
|
|
21
|
+
export { TraceContentBudget } from './storage/TraceContentBudget';
|
|
21
22
|
export { TraceContentRedaction } from './storage/TraceContentRedaction';
|
|
22
23
|
|
|
23
24
|
// ---------------------------------------------------------------------------
|
package/src/register.ts
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
import { TraceConfig } from './config';
|
|
23
23
|
import { TraceContext } from './context';
|
|
24
24
|
import { TraceStorage } from './storage';
|
|
25
|
+
import { TraceContentBudget } from './storage/TraceContentBudget';
|
|
25
26
|
import { TraceContentRedaction } from './storage/TraceContentRedaction';
|
|
26
27
|
import { TraceEntryFiltering } from './storage/TraceEntryFiltering';
|
|
27
28
|
import { TraceWriteDiagnostics } from './storage/TraceWriteDiagnostics';
|
|
@@ -379,9 +380,11 @@ if (!traceAlreadyInitialized && Env) {
|
|
|
379
380
|
await assertTraceStorageReady(core, storageDb, resolvedConnectionName);
|
|
380
381
|
|
|
381
382
|
const storage = TraceWriteDiagnostics.wrapStorage(
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
383
|
+
TraceContentBudget.wrapStorage(
|
|
384
|
+
TraceContentRedaction.wrapStorage(
|
|
385
|
+
TraceEntryFiltering.wrapStorage(TraceStorage.resolveStorage(storageDb), config),
|
|
386
|
+
config.redaction
|
|
387
|
+
)
|
|
385
388
|
),
|
|
386
389
|
{
|
|
387
390
|
connectionName: resolvedConnectionName,
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import type { ITraceEntry, ITraceStorage } from '../types';
|
|
2
|
+
|
|
3
|
+
const DEFAULT_MAX_ENTRY_BYTES = 64 * 1024;
|
|
4
|
+
const DEFAULT_MAX_STRING_BYTES = 16 * 1024;
|
|
5
|
+
const DEFAULT_MAX_ARRAY_ITEMS = 25;
|
|
6
|
+
const DEFAULT_MAX_OBJECT_ENTRIES = 40;
|
|
7
|
+
const DEFAULT_MAX_DEPTH = 6;
|
|
8
|
+
|
|
9
|
+
const DROPPED_FIELD_MESSAGE =
|
|
10
|
+
'[trace] Value dropped because the field exceeded the trace storage size limit.';
|
|
11
|
+
const COMPACTED_CONTENT_MESSAGE =
|
|
12
|
+
'[trace] Trace content was compacted because it exceeded the trace storage size limit.';
|
|
13
|
+
|
|
14
|
+
const encoder = new TextEncoder();
|
|
15
|
+
|
|
16
|
+
const serializedSize = (value: unknown): number => {
|
|
17
|
+
try {
|
|
18
|
+
return encoder.encode(JSON.stringify(value)).length;
|
|
19
|
+
} catch {
|
|
20
|
+
return Number.MAX_SAFE_INTEGER;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const describeValueType = (value: unknown): string => {
|
|
25
|
+
if (Array.isArray(value)) return 'array';
|
|
26
|
+
if (value === null) return 'null';
|
|
27
|
+
return typeof value;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const compactValue = (value: unknown, depth: number): unknown => {
|
|
31
|
+
if (depth >= DEFAULT_MAX_DEPTH) {
|
|
32
|
+
return DROPPED_FIELD_MESSAGE;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (typeof value === 'string') {
|
|
36
|
+
return serializedSize(value) > DEFAULT_MAX_STRING_BYTES ? DROPPED_FIELD_MESSAGE : value;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (Array.isArray(value)) {
|
|
40
|
+
const next = value
|
|
41
|
+
.slice(0, DEFAULT_MAX_ARRAY_ITEMS)
|
|
42
|
+
.map((item) => compactValue(item, depth + 1));
|
|
43
|
+
|
|
44
|
+
if (value.length > DEFAULT_MAX_ARRAY_ITEMS) {
|
|
45
|
+
next.push(
|
|
46
|
+
`[trace] ${String(value.length - DEFAULT_MAX_ARRAY_ITEMS)} additional items were dropped.`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return next;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (typeof value !== 'object' || value === null) {
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const entries = Object.entries(value);
|
|
58
|
+
const compactedEntries = entries
|
|
59
|
+
.slice(0, DEFAULT_MAX_OBJECT_ENTRIES)
|
|
60
|
+
.map(([key, entryValue]) => [key, compactValue(entryValue, depth + 1)]);
|
|
61
|
+
|
|
62
|
+
if (entries.length > DEFAULT_MAX_OBJECT_ENTRIES) {
|
|
63
|
+
compactedEntries.push([
|
|
64
|
+
'__traceNotice',
|
|
65
|
+
`[trace] ${String(entries.length - DEFAULT_MAX_OBJECT_ENTRIES)} additional fields were dropped.`,
|
|
66
|
+
]);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return Object.fromEntries(compactedEntries);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const compactTopLevelObjectToBudget = (value: Record<string, unknown>): Record<string, unknown> => {
|
|
73
|
+
const compacted: Record<string, unknown> = {
|
|
74
|
+
...value,
|
|
75
|
+
__traceNotice: COMPACTED_CONTENT_MESSAGE,
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const keysByDescendingSize = Object.keys(compacted)
|
|
79
|
+
.filter((key) => key !== '__traceNotice')
|
|
80
|
+
.sort((left, right) => serializedSize(compacted[right]) - serializedSize(compacted[left]));
|
|
81
|
+
|
|
82
|
+
for (const key of keysByDescendingSize) {
|
|
83
|
+
if (serializedSize(compacted) <= DEFAULT_MAX_ENTRY_BYTES) break;
|
|
84
|
+
compacted[key] = DROPPED_FIELD_MESSAGE;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return compacted;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const fitContentToBudget = (content: unknown): unknown => {
|
|
91
|
+
if (serializedSize(content) <= DEFAULT_MAX_ENTRY_BYTES) {
|
|
92
|
+
return content;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const compacted = compactValue(content, 0);
|
|
96
|
+
if (serializedSize(compacted) <= DEFAULT_MAX_ENTRY_BYTES) {
|
|
97
|
+
return compacted;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (typeof compacted === 'object' && compacted !== null && !Array.isArray(compacted)) {
|
|
101
|
+
const topLevelCompacted = compactTopLevelObjectToBudget(compacted as Record<string, unknown>);
|
|
102
|
+
if (serializedSize(topLevelCompacted) <= DEFAULT_MAX_ENTRY_BYTES) {
|
|
103
|
+
return topLevelCompacted;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
__traceNotice: COMPACTED_CONTENT_MESSAGE,
|
|
109
|
+
dropped: true,
|
|
110
|
+
valueType: describeValueType(content),
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const fitEntryToBudget = (entry: ITraceEntry): ITraceEntry => ({
|
|
115
|
+
...entry,
|
|
116
|
+
content: fitContentToBudget(entry.content),
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const fitPatchToBudget = (
|
|
120
|
+
patch: Partial<Pick<ITraceEntry, 'content' | 'isLatest'>>
|
|
121
|
+
): Partial<Pick<ITraceEntry, 'content' | 'isLatest'>> => {
|
|
122
|
+
if (patch.content === undefined) return patch;
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
...patch,
|
|
126
|
+
content: fitContentToBudget(patch.content),
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export const TraceContentBudget = Object.freeze({
|
|
131
|
+
wrapStorage(storage: ITraceStorage): ITraceStorage {
|
|
132
|
+
return Object.freeze({
|
|
133
|
+
...storage,
|
|
134
|
+
writeEntry: async (entry: ITraceEntry): Promise<void> => {
|
|
135
|
+
await storage.writeEntry(fitEntryToBudget(entry));
|
|
136
|
+
},
|
|
137
|
+
updateEntry: async (
|
|
138
|
+
uuid: string,
|
|
139
|
+
patch: Partial<Pick<ITraceEntry, 'content' | 'isLatest'>>
|
|
140
|
+
): Promise<void> => {
|
|
141
|
+
await storage.updateEntry(uuid, fitPatchToBudget(patch));
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
},
|
|
145
|
+
});
|