@zintrust/trace 1.6.1 → 1.6.2
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/register.js +14 -10
- package/package.json +2 -3
- package/src/TraceConnection.ts +0 -182
- package/src/cli-register.ts +0 -63
- package/src/config.ts +0 -383
- package/src/context.ts +0 -101
- package/src/dashboard/handlers.ts +0 -353
- package/src/dashboard/routes.ts +0 -114
- package/src/dashboard/ui.ts +0 -1262
- package/src/dashboard/zintrust-debuger.svg +0 -30
- package/src/index.ts +0 -102
- package/src/ingest/TraceIngestGateway.ts +0 -414
- package/src/plugin.ts +0 -9
- package/src/register.ts +0 -659
- package/src/storage/ProxyTraceStorage.ts +0 -190
- package/src/storage/TraceContentBudget.ts +0 -491
- package/src/storage/TraceContentRedaction.ts +0 -44
- package/src/storage/TraceEntryFiltering.ts +0 -92
- package/src/storage/TraceServiceTag.ts +0 -56
- package/src/storage/TraceStorage.ts +0 -543
- package/src/storage/TraceWriteDiagnostics.ts +0 -289
- package/src/storage/index.ts +0 -4
- package/src/types.ts +0 -430
- package/src/ui.ts +0 -9
- package/src/utils/authTag.ts +0 -20
- package/src/utils/entryFilter.ts +0 -131
- package/src/utils/familyHash.ts +0 -8
- package/src/utils/redact.ts +0 -112
- package/src/utils/requestFilter.ts +0 -79
- package/src/utils/stackFrame.ts +0 -44
- package/src/watchers/AuthWatcher.ts +0 -53
- package/src/watchers/BatchWatcher.ts +0 -55
- package/src/watchers/CacheWatcher.ts +0 -72
- package/src/watchers/CommandWatcher.ts +0 -58
- package/src/watchers/DumpWatcher.ts +0 -45
- package/src/watchers/EventWatcher.ts +0 -46
- package/src/watchers/ExceptionWatcher.ts +0 -130
- package/src/watchers/GateWatcher.ts +0 -53
- package/src/watchers/HttpClientWatcher.ts +0 -219
- package/src/watchers/HttpWatcher.ts +0 -220
- package/src/watchers/JobWatcher.ts +0 -124
- package/src/watchers/LogWatcher.ts +0 -120
- package/src/watchers/MailWatcher.ts +0 -65
- package/src/watchers/MiddlewareWatcher.ts +0 -54
- package/src/watchers/ModelWatcher.ts +0 -60
- package/src/watchers/NotificationWatcher.ts +0 -60
- package/src/watchers/QueryWatcher.ts +0 -107
- package/src/watchers/RedisWatcher.ts +0 -42
- package/src/watchers/ScheduleWatcher.ts +0 -57
- package/src/watchers/ViewWatcher.ts +0 -40
package/src/ui.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Lightweight UI/dashboard entrypoint for @zintrust/trace.
|
|
3
|
-
*
|
|
4
|
-
* Import this subpath when you only need trace dashboard registration
|
|
5
|
-
* without pulling in the package root re-export surface.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export { registerTraceDashboard, registerTraceRoutes } from './dashboard/routes';
|
|
9
|
-
export type { TraceDashboardOptions, TraceDashboardRegistrationOptions } from './dashboard/routes';
|
package/src/utils/authTag.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { TraceContext } from '../context';
|
|
2
|
-
|
|
3
|
-
const resolveAuthTag = (): string | undefined => {
|
|
4
|
-
const userId = TraceContext.getUserId();
|
|
5
|
-
if (userId === undefined || userId === '') return undefined;
|
|
6
|
-
return `Auth:${userId}`;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
const appendAuthTag = (tags: string[]): string[] => {
|
|
10
|
-
const authTag = resolveAuthTag();
|
|
11
|
-
if (authTag === undefined || tags.includes(authTag)) return tags;
|
|
12
|
-
return [...tags, authTag];
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export const AuthTag = Object.freeze({
|
|
16
|
-
append: appendAuthTag,
|
|
17
|
-
resolve: resolveAuthTag,
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
export default AuthTag;
|
package/src/utils/entryFilter.ts
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
ITraceConfig,
|
|
3
|
-
ITraceEntry,
|
|
4
|
-
TraceClientRequestWatcherConfig,
|
|
5
|
-
TraceFilterRule,
|
|
6
|
-
TraceRequestWatcherConfig,
|
|
7
|
-
WatcherToggles,
|
|
8
|
-
} from '../types';
|
|
9
|
-
import { EntryType } from '../types';
|
|
10
|
-
|
|
11
|
-
const isObjectValue = (value: unknown): value is Record<string, unknown> => {
|
|
12
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const normalizeTerms = (terms?: string[]): string[] => {
|
|
16
|
-
if (!Array.isArray(terms)) return [];
|
|
17
|
-
|
|
18
|
-
return terms
|
|
19
|
-
.filter((term): term is string => typeof term === 'string')
|
|
20
|
-
.map((term) => term.trim().toLowerCase())
|
|
21
|
-
.filter((term) => term !== '');
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const matchesRule = (haystack: string, rule?: TraceFilterRule): boolean => {
|
|
25
|
-
if (!rule) return true;
|
|
26
|
-
if (rule.enabled === false) return false;
|
|
27
|
-
|
|
28
|
-
const include = normalizeTerms(rule.include);
|
|
29
|
-
const exclude = normalizeTerms(rule.exclude);
|
|
30
|
-
|
|
31
|
-
if (exclude.some((term) => haystack.includes(term))) return false;
|
|
32
|
-
if (include.length === 0) return true;
|
|
33
|
-
|
|
34
|
-
return include.some((term) => haystack.includes(term));
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const toSearchableText = (entry: ITraceEntry): string => {
|
|
38
|
-
const sections = [entry.type, entry.batchId, ...(entry.tags ?? [])];
|
|
39
|
-
|
|
40
|
-
try {
|
|
41
|
-
sections.push(JSON.stringify(entry.content) ?? '');
|
|
42
|
-
} catch {
|
|
43
|
-
sections.push(String(entry.content ?? ''));
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return sections.join(' ').toLowerCase();
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const watcherKeyByEntryType: Record<ITraceEntry['type'], keyof WatcherToggles> = {
|
|
50
|
-
[EntryType.REQUEST]: 'request',
|
|
51
|
-
[EntryType.QUERY]: 'query',
|
|
52
|
-
[EntryType.EXCEPTION]: 'exception',
|
|
53
|
-
[EntryType.LOG]: 'log',
|
|
54
|
-
[EntryType.JOB]: 'job',
|
|
55
|
-
[EntryType.CACHE]: 'cache',
|
|
56
|
-
[EntryType.SCHEDULE]: 'schedule',
|
|
57
|
-
[EntryType.MAIL]: 'mail',
|
|
58
|
-
[EntryType.AUTH]: 'auth',
|
|
59
|
-
[EntryType.EVENT]: 'event',
|
|
60
|
-
[EntryType.MODEL]: 'model',
|
|
61
|
-
[EntryType.NOTIFICATION]: 'notification',
|
|
62
|
-
[EntryType.REDIS]: 'redis',
|
|
63
|
-
[EntryType.GATE]: 'gate',
|
|
64
|
-
[EntryType.MIDDLEWARE]: 'middleware',
|
|
65
|
-
[EntryType.COMMAND]: 'command',
|
|
66
|
-
[EntryType.BATCH]: 'batch',
|
|
67
|
-
[EntryType.DUMP]: 'dump',
|
|
68
|
-
[EntryType.VIEW]: 'view',
|
|
69
|
-
[EntryType.CLIENT_REQUEST]: 'clientRequest',
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
const getRequestMethodRule = (
|
|
73
|
-
watcher: TraceRequestWatcherConfig,
|
|
74
|
-
entry: ITraceEntry
|
|
75
|
-
): TraceFilterRule | undefined => {
|
|
76
|
-
if (entry.type !== EntryType.REQUEST) return undefined;
|
|
77
|
-
|
|
78
|
-
const content = isObjectValue(entry.content) ? entry.content : undefined;
|
|
79
|
-
const methodValue = content?.['method'];
|
|
80
|
-
const method = typeof methodValue === 'string' ? methodValue.trim().toLowerCase() : '';
|
|
81
|
-
|
|
82
|
-
if (method === 'get') return watcher.get;
|
|
83
|
-
if (method === 'post') return watcher.post;
|
|
84
|
-
if (method === 'put') return watcher.put;
|
|
85
|
-
if (method === 'patch') return watcher.patch;
|
|
86
|
-
if (method === 'delete' || method === 'del') return watcher.delete;
|
|
87
|
-
|
|
88
|
-
return watcher.all;
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
const getClientRequestSourceRule = (
|
|
92
|
-
watcher: TraceClientRequestWatcherConfig,
|
|
93
|
-
entry: ITraceEntry
|
|
94
|
-
): TraceFilterRule | undefined => {
|
|
95
|
-
if (entry.type !== EntryType.CLIENT_REQUEST) return undefined;
|
|
96
|
-
|
|
97
|
-
const content = isObjectValue(entry.content) ? entry.content : undefined;
|
|
98
|
-
const sourceValue = content?.['source'];
|
|
99
|
-
const source = typeof sourceValue === 'string' ? sourceValue.trim().toLowerCase() : '';
|
|
100
|
-
|
|
101
|
-
if (source === '') return undefined;
|
|
102
|
-
return watcher.sources?.[source];
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
export const TraceEntryFilter = Object.freeze({
|
|
106
|
-
shouldCapture(entry: ITraceEntry, config: ITraceConfig): boolean {
|
|
107
|
-
const watcherKey = watcherKeyByEntryType[entry.type];
|
|
108
|
-
const watcher = config.watchers[watcherKey];
|
|
109
|
-
if (watcher === false) return false;
|
|
110
|
-
if (!isObjectValue(watcher)) return true;
|
|
111
|
-
|
|
112
|
-
const haystack = toSearchableText(entry);
|
|
113
|
-
if (!matchesRule(haystack, watcher)) return false;
|
|
114
|
-
|
|
115
|
-
if (watcherKey === 'request') {
|
|
116
|
-
const requestWatcher = watcher as TraceRequestWatcherConfig;
|
|
117
|
-
const methodRule = getRequestMethodRule(requestWatcher, entry);
|
|
118
|
-
if (!matchesRule(haystack, requestWatcher.all)) return false;
|
|
119
|
-
if (!matchesRule(haystack, methodRule)) return false;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (watcherKey === 'clientRequest') {
|
|
123
|
-
const clientRequestWatcher = watcher as TraceClientRequestWatcherConfig;
|
|
124
|
-
const sourceRule = getClientRequestSourceRule(clientRequestWatcher, entry);
|
|
125
|
-
if (sourceRule?.enabled === false) return false;
|
|
126
|
-
if (!matchesRule(haystack, sourceRule)) return false;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return true;
|
|
130
|
-
},
|
|
131
|
-
});
|
package/src/utils/familyHash.ts
DELETED
package/src/utils/redact.ts
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Redaction helpers for @zintrust/trace watchers.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
const REDACTED = '****';
|
|
6
|
-
|
|
7
|
-
const isArrayValue = (value: unknown): value is unknown[] => Array.isArray(value);
|
|
8
|
-
|
|
9
|
-
const isObjectValue = (value: unknown): value is Record<string, unknown> => {
|
|
10
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const normalizeFields = (fields: string[]): Set<string> => {
|
|
14
|
-
const normalized = new Set<string>();
|
|
15
|
-
|
|
16
|
-
for (const field of fields) {
|
|
17
|
-
if (typeof field !== 'string') continue;
|
|
18
|
-
const key = field.trim().toLowerCase();
|
|
19
|
-
if (key !== '') normalized.add(key);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return normalized;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const redactUnknownValue = (
|
|
26
|
-
value: unknown,
|
|
27
|
-
fields: Set<string>,
|
|
28
|
-
seen: WeakSet<object>
|
|
29
|
-
): unknown => {
|
|
30
|
-
if (isArrayValue(value)) {
|
|
31
|
-
return value.map((item) => redactUnknownValue(item, fields, seen));
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (!isObjectValue(value)) {
|
|
35
|
-
return value;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (seen.has(value)) {
|
|
39
|
-
return '[Circular]';
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
seen.add(value);
|
|
43
|
-
const out: Record<string, unknown> = {};
|
|
44
|
-
|
|
45
|
-
for (const [key, entryValue] of Object.entries(value)) {
|
|
46
|
-
out[key] = fields.has(key.toLowerCase())
|
|
47
|
-
? REDACTED
|
|
48
|
-
: redactUnknownValue(entryValue, fields, seen);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
seen.delete(value);
|
|
52
|
-
return out;
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const redactQuerySegment = (segment: string, fields: Set<string>): string => {
|
|
56
|
-
const separatorIndex = segment.indexOf('=');
|
|
57
|
-
if (separatorIndex <= 0) return segment;
|
|
58
|
-
|
|
59
|
-
const key = segment.slice(0, separatorIndex);
|
|
60
|
-
const value = segment.slice(separatorIndex + 1);
|
|
61
|
-
if (!fields.has(key.toLowerCase())) return `${key}=${value}`;
|
|
62
|
-
|
|
63
|
-
return `${key}=${REDACTED}`;
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
export const redactHeaders = (
|
|
67
|
-
headers: Record<string, string>,
|
|
68
|
-
fields: string[]
|
|
69
|
-
): Record<string, string> => {
|
|
70
|
-
const lower = normalizeFields(fields);
|
|
71
|
-
const out: Record<string, string> = {};
|
|
72
|
-
for (const [k, v] of Object.entries(headers)) {
|
|
73
|
-
out[k] = lower.has(k.toLowerCase()) ? REDACTED : v;
|
|
74
|
-
}
|
|
75
|
-
return out;
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
export const redactUnknown = (value: unknown, fields: string[]): unknown => {
|
|
79
|
-
return redactUnknownValue(value, normalizeFields(fields), new WeakSet<object>());
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
export const redactObject = (
|
|
83
|
-
obj: Record<string, unknown>,
|
|
84
|
-
fields: string[]
|
|
85
|
-
): Record<string, unknown> => {
|
|
86
|
-
const redacted = redactUnknown(obj, fields);
|
|
87
|
-
return isObjectValue(redacted) ? redacted : {};
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
export const redactString = (value: string, fields: string[]): string => {
|
|
91
|
-
const lower = normalizeFields(fields);
|
|
92
|
-
if (value === '') return value;
|
|
93
|
-
|
|
94
|
-
let output = '';
|
|
95
|
-
let segmentStart = 0;
|
|
96
|
-
|
|
97
|
-
for (let index = 0; index <= value.length; index += 1) {
|
|
98
|
-
const isBoundary = index === value.length || value[index] === '&';
|
|
99
|
-
if (!isBoundary) continue;
|
|
100
|
-
|
|
101
|
-
const segment = value.slice(segmentStart, index);
|
|
102
|
-
output += redactQuerySegment(segment, lower);
|
|
103
|
-
|
|
104
|
-
if (index < value.length) {
|
|
105
|
-
output += '&';
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
segmentStart = index + 1;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return output;
|
|
112
|
-
};
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { TraceContext } from '../context';
|
|
2
|
-
|
|
3
|
-
type RequestIgnoreRules = {
|
|
4
|
-
ignoreRoutes?: string[];
|
|
5
|
-
ignorePaths?: string[];
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
const normalizePath = (input: string): string => {
|
|
9
|
-
const trimmed = input.trim();
|
|
10
|
-
const [pathOnly] = trimmed.split('?');
|
|
11
|
-
if (!pathOnly || pathOnly === '') return '/';
|
|
12
|
-
return pathOnly.startsWith('/') ? pathOnly : `/${pathOnly}`;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const normalizeContainsPattern = (input: string): string => {
|
|
16
|
-
const trimmed = input.trim();
|
|
17
|
-
const [pathOnly] = trimmed.split('?');
|
|
18
|
-
return pathOnly ?? '';
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const resolveRules = (
|
|
22
|
-
ignoreRoutesOrRules: string[] | RequestIgnoreRules,
|
|
23
|
-
ignorePaths?: string[]
|
|
24
|
-
): Required<RequestIgnoreRules> => {
|
|
25
|
-
if (Array.isArray(ignoreRoutesOrRules)) {
|
|
26
|
-
return {
|
|
27
|
-
ignoreRoutes: ignoreRoutesOrRules,
|
|
28
|
-
ignorePaths: ignorePaths ?? [],
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return {
|
|
33
|
-
ignoreRoutes: ignoreRoutesOrRules.ignoreRoutes ?? [],
|
|
34
|
-
ignorePaths: ignoreRoutesOrRules.ignorePaths ?? [],
|
|
35
|
-
};
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const matchesIgnoredPath = (
|
|
39
|
-
path: string,
|
|
40
|
-
ignoreRoutesOrRules: string[] | RequestIgnoreRules,
|
|
41
|
-
ignorePaths?: string[]
|
|
42
|
-
): boolean => {
|
|
43
|
-
const normalizedPath = normalizePath(path);
|
|
44
|
-
const rules = resolveRules(ignoreRoutesOrRules, ignorePaths);
|
|
45
|
-
|
|
46
|
-
if (
|
|
47
|
-
rules.ignoreRoutes.some((route) => {
|
|
48
|
-
const normalizedRoute = normalizePath(route);
|
|
49
|
-
return (
|
|
50
|
-
normalizedPath === normalizedRoute ||
|
|
51
|
-
normalizedPath.startsWith(
|
|
52
|
-
normalizedRoute.endsWith('/') ? normalizedRoute : `${normalizedRoute}/`
|
|
53
|
-
)
|
|
54
|
-
);
|
|
55
|
-
})
|
|
56
|
-
) {
|
|
57
|
-
return true;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return rules.ignorePaths.some((route) => {
|
|
61
|
-
const containsPattern = normalizeContainsPattern(route);
|
|
62
|
-
if (containsPattern === '') return false;
|
|
63
|
-
return normalizedPath.includes(containsPattern);
|
|
64
|
-
});
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
const shouldIgnoreCurrentRequest = (
|
|
68
|
-
ignoreRoutesOrRules: string[] | RequestIgnoreRules,
|
|
69
|
-
ignorePaths?: string[]
|
|
70
|
-
): boolean => {
|
|
71
|
-
const currentPath = TraceContext.getRequestPath();
|
|
72
|
-
if (typeof currentPath !== 'string' || currentPath === '') return false;
|
|
73
|
-
return matchesIgnoredPath(currentPath, ignoreRoutesOrRules, ignorePaths);
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
export const RequestFilter = Object.freeze({
|
|
77
|
-
matchesIgnoredPath,
|
|
78
|
-
shouldIgnoreCurrentRequest,
|
|
79
|
-
});
|
package/src/utils/stackFrame.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
type StackFrame = { file: string; line: number };
|
|
2
|
-
|
|
3
|
-
const FRAME_PREFIX = 'at ';
|
|
4
|
-
|
|
5
|
-
const parsePositiveInt = (value: string): number | null => {
|
|
6
|
-
if (value === '') return null;
|
|
7
|
-
|
|
8
|
-
for (const char of value) {
|
|
9
|
-
if (char < '0' || char > '9') return null;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const parsed = Number.parseInt(value, 10);
|
|
13
|
-
return Number.isNaN(parsed) ? null : parsed;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const parseFrameLocation = (value: string): StackFrame | null => {
|
|
17
|
-
const columnSeparatorIndex = value.lastIndexOf(':');
|
|
18
|
-
if (columnSeparatorIndex <= 0) return null;
|
|
19
|
-
|
|
20
|
-
const lineSeparatorIndex = value.lastIndexOf(':', columnSeparatorIndex - 1);
|
|
21
|
-
if (lineSeparatorIndex <= 0) return null;
|
|
22
|
-
|
|
23
|
-
const file = value.slice(0, lineSeparatorIndex).trim();
|
|
24
|
-
const line = parsePositiveInt(value.slice(lineSeparatorIndex + 1, columnSeparatorIndex));
|
|
25
|
-
const column = parsePositiveInt(value.slice(columnSeparatorIndex + 1));
|
|
26
|
-
|
|
27
|
-
if (file === '' || line === null || column === null) return null;
|
|
28
|
-
return { file, line };
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
export const parseStackFrameLine = (line: string): StackFrame | null => {
|
|
32
|
-
const trimmed = line.trim();
|
|
33
|
-
if (!trimmed.startsWith(FRAME_PREFIX)) return null;
|
|
34
|
-
|
|
35
|
-
const body = trimmed.slice(FRAME_PREFIX.length).trim();
|
|
36
|
-
if (body === '') return null;
|
|
37
|
-
|
|
38
|
-
const wrappedStartIndex = body.lastIndexOf(' (');
|
|
39
|
-
if (wrappedStartIndex !== -1 && body.endsWith(')')) {
|
|
40
|
-
return parseFrameLocation(body.slice(wrappedStartIndex + 2, -1));
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return parseFrameLocation(body);
|
|
44
|
-
};
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AuthWatcher — records login/logout/failed auth events.
|
|
3
|
-
* Credentials are never stored; only the outcome.
|
|
4
|
-
*/
|
|
5
|
-
import { TraceContext } from '../context';
|
|
6
|
-
import type { AuthContent, ITraceWatcher, ITraceWatcherConfig } from '../types';
|
|
7
|
-
import { EntryType } from '../types';
|
|
8
|
-
import { RequestFilter } from '../utils/requestFilter';
|
|
9
|
-
|
|
10
|
-
let _storage: ITraceWatcherConfig['storage'] | null = null;
|
|
11
|
-
let _ignoreRoutes: string[] = [];
|
|
12
|
-
let _ignorePaths: string[] = [];
|
|
13
|
-
|
|
14
|
-
const emit = (event: AuthContent['event'], userId?: string): void => {
|
|
15
|
-
if (!_storage) return;
|
|
16
|
-
if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes, _ignorePaths)) return;
|
|
17
|
-
const content: AuthContent = {
|
|
18
|
-
event,
|
|
19
|
-
userId,
|
|
20
|
-
hostname: TraceContext.getHostname(),
|
|
21
|
-
};
|
|
22
|
-
const tags: string[] = [];
|
|
23
|
-
if (userId) tags.push(`Auth:${userId}`);
|
|
24
|
-
if (event === 'failed') tags.push('failed');
|
|
25
|
-
|
|
26
|
-
_storage
|
|
27
|
-
.writeEntry({
|
|
28
|
-
uuid: crypto.randomUUID(),
|
|
29
|
-
batchId: TraceContext.getBatchId(),
|
|
30
|
-
type: EntryType.AUTH,
|
|
31
|
-
content,
|
|
32
|
-
tags,
|
|
33
|
-
isLatest: true,
|
|
34
|
-
createdAt: TraceContext.now(),
|
|
35
|
-
})
|
|
36
|
-
.catch(() => undefined);
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
export const AuthWatcher: ITraceWatcher & { emit: typeof emit } = Object.freeze({
|
|
40
|
-
emit,
|
|
41
|
-
|
|
42
|
-
register({ storage, config }: ITraceWatcherConfig): () => void {
|
|
43
|
-
if (config.watchers.auth === false) return () => undefined;
|
|
44
|
-
_storage = storage;
|
|
45
|
-
_ignoreRoutes = config.ignoreRoutes;
|
|
46
|
-
_ignorePaths = config.ignorePaths;
|
|
47
|
-
return () => {
|
|
48
|
-
_storage = null;
|
|
49
|
-
_ignoreRoutes = [];
|
|
50
|
-
_ignorePaths = [];
|
|
51
|
-
};
|
|
52
|
-
},
|
|
53
|
-
});
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { TraceContext } from '../context';
|
|
2
|
-
import type { BatchContent, ITraceWatcher, ITraceWatcherConfig } from '../types';
|
|
3
|
-
import { EntryType } from '../types';
|
|
4
|
-
import { RequestFilter } from '../utils/requestFilter';
|
|
5
|
-
|
|
6
|
-
let _storage: ITraceWatcherConfig['storage'] | null = null;
|
|
7
|
-
let _ignoreRoutes: string[] = [];
|
|
8
|
-
let _ignorePaths: string[] = [];
|
|
9
|
-
|
|
10
|
-
const emit = (
|
|
11
|
-
name: string,
|
|
12
|
-
total: number,
|
|
13
|
-
processed: number,
|
|
14
|
-
failed: number,
|
|
15
|
-
status: BatchContent['status']
|
|
16
|
-
): void => {
|
|
17
|
-
if (!_storage) return;
|
|
18
|
-
if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes, _ignorePaths)) return;
|
|
19
|
-
const tags = [name];
|
|
20
|
-
if (failed > 0) tags.push('failed');
|
|
21
|
-
const content: BatchContent = {
|
|
22
|
-
name,
|
|
23
|
-
total,
|
|
24
|
-
processed,
|
|
25
|
-
failed,
|
|
26
|
-
status,
|
|
27
|
-
hostname: TraceContext.getHostname(),
|
|
28
|
-
};
|
|
29
|
-
_storage
|
|
30
|
-
.writeEntry({
|
|
31
|
-
uuid: crypto.randomUUID(),
|
|
32
|
-
batchId: TraceContext.getBatchId(),
|
|
33
|
-
type: EntryType.BATCH,
|
|
34
|
-
content,
|
|
35
|
-
tags,
|
|
36
|
-
isLatest: true,
|
|
37
|
-
createdAt: TraceContext.now(),
|
|
38
|
-
})
|
|
39
|
-
.catch(() => undefined);
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
export const BatchWatcher: ITraceWatcher & { emit: typeof emit } = Object.freeze({
|
|
43
|
-
emit,
|
|
44
|
-
register({ storage, config }: ITraceWatcherConfig): () => void {
|
|
45
|
-
if (config.watchers.batch === false) return () => undefined;
|
|
46
|
-
_storage = storage;
|
|
47
|
-
_ignoreRoutes = config.ignoreRoutes;
|
|
48
|
-
_ignorePaths = config.ignorePaths;
|
|
49
|
-
return () => {
|
|
50
|
-
_storage = null;
|
|
51
|
-
_ignoreRoutes = [];
|
|
52
|
-
_ignorePaths = [];
|
|
53
|
-
};
|
|
54
|
-
},
|
|
55
|
-
});
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CacheWatcher — records cache operations.
|
|
3
|
-
* Call CacheWatcher.emit() from within your cache driver instrumentation.
|
|
4
|
-
*/
|
|
5
|
-
import { TraceContext } from '../context';
|
|
6
|
-
import type { CacheContent, ITraceWatcher, ITraceWatcherConfig } from '../types';
|
|
7
|
-
import { EntryType } from '../types';
|
|
8
|
-
import { AuthTag } from '../utils/authTag';
|
|
9
|
-
import { redactString, redactUnknown } from '../utils/redact';
|
|
10
|
-
import { RequestFilter } from '../utils/requestFilter';
|
|
11
|
-
|
|
12
|
-
let _storage: ITraceWatcherConfig['storage'] | null = null;
|
|
13
|
-
let _config: ITraceWatcherConfig['config'] | null = null;
|
|
14
|
-
let _redactionFields: string[] = [];
|
|
15
|
-
let _ignoreRoutes: string[] = [];
|
|
16
|
-
let _ignorePaths: string[] = [];
|
|
17
|
-
|
|
18
|
-
const emit = (
|
|
19
|
-
operation: CacheContent['operation'],
|
|
20
|
-
key: string,
|
|
21
|
-
duration: number,
|
|
22
|
-
hit?: boolean,
|
|
23
|
-
payload?: unknown,
|
|
24
|
-
store?: string,
|
|
25
|
-
ttl?: number
|
|
26
|
-
): void => {
|
|
27
|
-
if (!_storage) return;
|
|
28
|
-
if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes, _ignorePaths)) return;
|
|
29
|
-
const safeKey = redactString(key, _redactionFields);
|
|
30
|
-
const shouldLogPayload = _config?.captureCachePayloads === true;
|
|
31
|
-
const content: CacheContent = {
|
|
32
|
-
operation,
|
|
33
|
-
key: safeKey,
|
|
34
|
-
hit,
|
|
35
|
-
...(typeof store === 'string' && store !== '' ? { store } : {}),
|
|
36
|
-
...(typeof ttl === 'number' ? { ttl } : {}),
|
|
37
|
-
payloadLogged: shouldLogPayload,
|
|
38
|
-
...(shouldLogPayload ? { payload: redactUnknown(payload, _redactionFields) } : {}),
|
|
39
|
-
duration,
|
|
40
|
-
hostname: TraceContext.getHostname(),
|
|
41
|
-
};
|
|
42
|
-
_storage
|
|
43
|
-
.writeEntry({
|
|
44
|
-
uuid: crypto.randomUUID(),
|
|
45
|
-
batchId: TraceContext.getBatchId(),
|
|
46
|
-
type: EntryType.CACHE,
|
|
47
|
-
content,
|
|
48
|
-
tags: AuthTag.append([]),
|
|
49
|
-
isLatest: true,
|
|
50
|
-
createdAt: TraceContext.now(),
|
|
51
|
-
})
|
|
52
|
-
.catch(() => undefined);
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
export const CacheWatcher: ITraceWatcher & { emit: typeof emit } = Object.freeze({
|
|
56
|
-
emit,
|
|
57
|
-
|
|
58
|
-
register({ storage, config }: ITraceWatcherConfig): () => void {
|
|
59
|
-
if (config.watchers.cache === false) return () => undefined;
|
|
60
|
-
_storage = storage;
|
|
61
|
-
_config = config;
|
|
62
|
-
_redactionFields = config.redaction.query;
|
|
63
|
-
_ignoreRoutes = config.ignoreRoutes;
|
|
64
|
-
_ignorePaths = config.ignorePaths;
|
|
65
|
-
return () => {
|
|
66
|
-
_storage = null;
|
|
67
|
-
_config = null;
|
|
68
|
-
_ignoreRoutes = [];
|
|
69
|
-
_ignorePaths = [];
|
|
70
|
-
};
|
|
71
|
-
},
|
|
72
|
-
});
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { TraceContext } from '../context';
|
|
2
|
-
import type { CommandContent, ITraceWatcher, ITraceWatcherConfig } from '../types';
|
|
3
|
-
import { EntryType } from '../types';
|
|
4
|
-
import { redactObject } from '../utils/redact';
|
|
5
|
-
import { RequestFilter } from '../utils/requestFilter';
|
|
6
|
-
|
|
7
|
-
let _storage: ITraceWatcherConfig['storage'] | null = null;
|
|
8
|
-
let _redactKeys: string[] = [];
|
|
9
|
-
let _ignoreRoutes: string[] = [];
|
|
10
|
-
let _ignorePaths: string[] = [];
|
|
11
|
-
|
|
12
|
-
const emit = (
|
|
13
|
-
name: string,
|
|
14
|
-
args: Record<string, unknown>,
|
|
15
|
-
exitCode: number,
|
|
16
|
-
duration: number,
|
|
17
|
-
output?: string
|
|
18
|
-
): void => {
|
|
19
|
-
if (!_storage) return;
|
|
20
|
-
if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes, _ignorePaths)) return;
|
|
21
|
-
const tags = [name];
|
|
22
|
-
if (exitCode !== 0) tags.push('failed');
|
|
23
|
-
const content: CommandContent = {
|
|
24
|
-
name,
|
|
25
|
-
arguments: redactObject(args, _redactKeys),
|
|
26
|
-
exitCode,
|
|
27
|
-
duration,
|
|
28
|
-
output,
|
|
29
|
-
hostname: TraceContext.getHostname(),
|
|
30
|
-
};
|
|
31
|
-
_storage
|
|
32
|
-
.writeEntry({
|
|
33
|
-
uuid: crypto.randomUUID(),
|
|
34
|
-
batchId: TraceContext.getBatchId(),
|
|
35
|
-
type: EntryType.COMMAND,
|
|
36
|
-
content,
|
|
37
|
-
tags,
|
|
38
|
-
isLatest: true,
|
|
39
|
-
createdAt: TraceContext.now(),
|
|
40
|
-
})
|
|
41
|
-
.catch(() => undefined);
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
export const CommandWatcher: ITraceWatcher & { emit: typeof emit } = Object.freeze({
|
|
45
|
-
emit,
|
|
46
|
-
register({ storage, config }: ITraceWatcherConfig): () => void {
|
|
47
|
-
if (config.watchers.command === false) return () => undefined;
|
|
48
|
-
_storage = storage;
|
|
49
|
-
_redactKeys = [...(config.redaction?.keys ?? []), ...(config.redaction?.body ?? [])];
|
|
50
|
-
_ignoreRoutes = config.ignoreRoutes;
|
|
51
|
-
_ignorePaths = config.ignorePaths;
|
|
52
|
-
return () => {
|
|
53
|
-
_storage = null;
|
|
54
|
-
_ignoreRoutes = [];
|
|
55
|
-
_ignorePaths = [];
|
|
56
|
-
};
|
|
57
|
-
},
|
|
58
|
-
});
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { TraceContext } from '../context';
|
|
2
|
-
import type { DumpContent, ITraceWatcher, ITraceWatcherConfig } from '../types';
|
|
3
|
-
import { EntryType } from '../types';
|
|
4
|
-
import { RequestFilter } from '../utils/requestFilter';
|
|
5
|
-
|
|
6
|
-
let _storage: ITraceWatcherConfig['storage'] | null = null;
|
|
7
|
-
let _enabled = false;
|
|
8
|
-
let _ignoreRoutes: string[] = [];
|
|
9
|
-
let _ignorePaths: string[] = [];
|
|
10
|
-
|
|
11
|
-
/** Explicitly opt-in (enabled only when config.watchers.dump === true, not just non-false). */
|
|
12
|
-
const emit = (value: unknown, file?: string, line?: number): void => {
|
|
13
|
-
if (!_storage || !_enabled) return;
|
|
14
|
-
if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes, _ignorePaths)) return;
|
|
15
|
-
const content: DumpContent = { value, file, line, hostname: TraceContext.getHostname() };
|
|
16
|
-
_storage
|
|
17
|
-
.writeEntry({
|
|
18
|
-
uuid: crypto.randomUUID(),
|
|
19
|
-
batchId: TraceContext.getBatchId(),
|
|
20
|
-
type: EntryType.DUMP,
|
|
21
|
-
content,
|
|
22
|
-
tags: [],
|
|
23
|
-
isLatest: true,
|
|
24
|
-
createdAt: TraceContext.now(),
|
|
25
|
-
})
|
|
26
|
-
.catch(() => undefined);
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
export const DumpWatcher: ITraceWatcher & { emit: typeof emit } = Object.freeze({
|
|
30
|
-
emit,
|
|
31
|
-
register({ storage, config }: ITraceWatcherConfig): () => void {
|
|
32
|
-
// DumpWatcher requires explicit opt-in (=== true), not just absence of false
|
|
33
|
-
if (config.watchers.dump !== true) return () => undefined;
|
|
34
|
-
_storage = storage;
|
|
35
|
-
_enabled = true;
|
|
36
|
-
_ignoreRoutes = config.ignoreRoutes;
|
|
37
|
-
_ignorePaths = config.ignorePaths;
|
|
38
|
-
return () => {
|
|
39
|
-
_storage = null;
|
|
40
|
-
_enabled = false;
|
|
41
|
-
_ignoreRoutes = [];
|
|
42
|
-
_ignorePaths = [];
|
|
43
|
-
};
|
|
44
|
-
},
|
|
45
|
-
});
|