@zintrust/trace 0.4.95 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build-manifest.json +23 -23
- package/dist/config.js +83 -6
- package/dist/dashboard/handlers.js +107 -4
- package/dist/dashboard/ui.js +4 -0
- package/dist/storage/TraceContentBudget.js +67 -15
- package/dist/types.d.ts +13 -1
- package/dist/utils/entryFilter.js +20 -0
- package/dist/watchers/HttpClientWatcher.d.ts +2 -2
- package/dist/watchers/HttpClientWatcher.js +118 -20
- package/package.json +2 -2
- package/src/config.ts +136 -6
- package/src/dashboard/handlers.ts +134 -5
- package/src/dashboard/ui.ts +4 -0
- package/src/storage/TraceContentBudget.ts +98 -17
- package/src/types.ts +15 -1
- package/src/utils/entryFilter.ts +23 -0
- package/src/watchers/HttpClientWatcher.ts +165 -26
|
@@ -27,6 +27,87 @@ const describeValueType = (value: unknown): string => {
|
|
|
27
27
|
return typeof value;
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
+
type TracePathSegment = string | number;
|
|
31
|
+
|
|
32
|
+
type TracePathCandidate = {
|
|
33
|
+
path: TracePathSegment[];
|
|
34
|
+
size: number;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const chooseLargerCandidate = (
|
|
38
|
+
left: TracePathCandidate | null,
|
|
39
|
+
right: TracePathCandidate | null
|
|
40
|
+
): TracePathCandidate | null => {
|
|
41
|
+
if (left === null) return right;
|
|
42
|
+
if (right === null) return left;
|
|
43
|
+
return right.size > left.size ? right : left;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const fallbackCandidate = (value: unknown, path: TracePathSegment[]): TracePathCandidate | null => {
|
|
47
|
+
return path.length === 0 ? null : { path, size: serializedSize(value) };
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const findLargestDroppablePathInArray = (
|
|
51
|
+
value: unknown[],
|
|
52
|
+
path: TracePathSegment[]
|
|
53
|
+
): TracePathCandidate | null => {
|
|
54
|
+
let best: TracePathCandidate | null = null;
|
|
55
|
+
|
|
56
|
+
for (const [index, item] of value.entries()) {
|
|
57
|
+
best = chooseLargerCandidate(best, findLargestDroppablePath(item, [...path, index]));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return best ?? fallbackCandidate(value, path);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const findLargestDroppablePathInObject = (
|
|
64
|
+
value: Record<string, unknown>,
|
|
65
|
+
path: TracePathSegment[]
|
|
66
|
+
): TracePathCandidate | null => {
|
|
67
|
+
let best: TracePathCandidate | null = null;
|
|
68
|
+
|
|
69
|
+
for (const [key, entryValue] of Object.entries(value)) {
|
|
70
|
+
if (key === '__traceNotice') continue;
|
|
71
|
+
best = chooseLargerCandidate(best, findLargestDroppablePath(entryValue, [...path, key]));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return best ?? fallbackCandidate(value, path);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const findLargestDroppablePath = (
|
|
78
|
+
value: unknown,
|
|
79
|
+
path: TracePathSegment[] = []
|
|
80
|
+
): TracePathCandidate | null => {
|
|
81
|
+
if (Array.isArray(value)) return findLargestDroppablePathInArray(value, path);
|
|
82
|
+
if (typeof value === 'object' && value !== null) {
|
|
83
|
+
return findLargestDroppablePathInObject(value as Record<string, unknown>, path);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return fallbackCandidate(value, path);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const replaceAtPath = (value: unknown, path: TracePathSegment[], replacement: unknown): unknown => {
|
|
90
|
+
if (path.length === 0) return replacement;
|
|
91
|
+
|
|
92
|
+
const [segment, ...rest] = path;
|
|
93
|
+
|
|
94
|
+
if (Array.isArray(value) && typeof segment === 'number') {
|
|
95
|
+
const next = value.slice();
|
|
96
|
+
next[segment] = replaceAtPath(next[segment], rest, replacement);
|
|
97
|
+
return next;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (typeof value === 'object' && value !== null && typeof segment === 'string') {
|
|
101
|
+
const current = value as Record<string, unknown>;
|
|
102
|
+
return {
|
|
103
|
+
...current,
|
|
104
|
+
[segment]: replaceAtPath(current[segment], rest, replacement),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return value;
|
|
109
|
+
};
|
|
110
|
+
|
|
30
111
|
const compactValue = (value: unknown, depth: number): unknown => {
|
|
31
112
|
if (depth >= DEFAULT_MAX_DEPTH) {
|
|
32
113
|
return DROPPED_FIELD_MESSAGE;
|
|
@@ -69,19 +150,19 @@ const compactValue = (value: unknown, depth: number): unknown => {
|
|
|
69
150
|
return Object.fromEntries(compactedEntries);
|
|
70
151
|
};
|
|
71
152
|
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (
|
|
84
|
-
compacted
|
|
153
|
+
const compactStructuredValueToBudget = (value: unknown): unknown => {
|
|
154
|
+
let compacted: unknown =
|
|
155
|
+
typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
156
|
+
? {
|
|
157
|
+
...(value as Record<string, unknown>),
|
|
158
|
+
__traceNotice: COMPACTED_CONTENT_MESSAGE,
|
|
159
|
+
}
|
|
160
|
+
: value;
|
|
161
|
+
|
|
162
|
+
while (serializedSize(compacted) > DEFAULT_MAX_ENTRY_BYTES) {
|
|
163
|
+
const candidate = findLargestDroppablePath(compacted);
|
|
164
|
+
if (candidate === null) break;
|
|
165
|
+
compacted = replaceAtPath(compacted, candidate.path, DROPPED_FIELD_MESSAGE);
|
|
85
166
|
}
|
|
86
167
|
|
|
87
168
|
return compacted;
|
|
@@ -97,10 +178,10 @@ const fitContentToBudget = (content: unknown): unknown => {
|
|
|
97
178
|
return compacted;
|
|
98
179
|
}
|
|
99
180
|
|
|
100
|
-
if (typeof compacted === 'object' && compacted !== null
|
|
101
|
-
const
|
|
102
|
-
if (serializedSize(
|
|
103
|
-
return
|
|
181
|
+
if (typeof compacted === 'object' && compacted !== null) {
|
|
182
|
+
const budgetCompacted = compactStructuredValueToBudget(compacted);
|
|
183
|
+
if (serializedSize(budgetCompacted) <= DEFAULT_MAX_ENTRY_BYTES) {
|
|
184
|
+
return budgetCompacted;
|
|
104
185
|
}
|
|
105
186
|
}
|
|
106
187
|
|
package/src/types.ts
CHANGED
|
@@ -209,6 +209,7 @@ export interface ViewContent {
|
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
export interface ClientRequestContent {
|
|
212
|
+
source?: string;
|
|
212
213
|
method: string;
|
|
213
214
|
url: string;
|
|
214
215
|
requestHeaders: Record<string, string>;
|
|
@@ -222,6 +223,7 @@ export interface ClientRequestContent {
|
|
|
222
223
|
}
|
|
223
224
|
|
|
224
225
|
export interface ClientRequestTraceInput {
|
|
226
|
+
source?: string;
|
|
225
227
|
method: string;
|
|
226
228
|
url: string;
|
|
227
229
|
requestHeaders: Record<string, string>;
|
|
@@ -315,6 +317,13 @@ export type TraceFilterRule = {
|
|
|
315
317
|
exclude?: string[];
|
|
316
318
|
};
|
|
317
319
|
|
|
320
|
+
export type TraceClientRequestCaptureRule = TraceFilterRule & {
|
|
321
|
+
requestHeaders?: boolean;
|
|
322
|
+
requestBody?: boolean;
|
|
323
|
+
responseHeaders?: boolean;
|
|
324
|
+
responseBody?: boolean;
|
|
325
|
+
};
|
|
326
|
+
|
|
318
327
|
export type TraceRequestWatcherConfig = TraceFilterRule & {
|
|
319
328
|
all?: TraceFilterRule;
|
|
320
329
|
get?: TraceFilterRule;
|
|
@@ -324,8 +333,13 @@ export type TraceRequestWatcherConfig = TraceFilterRule & {
|
|
|
324
333
|
delete?: TraceFilterRule;
|
|
325
334
|
};
|
|
326
335
|
|
|
336
|
+
export type TraceClientRequestWatcherConfig = TraceClientRequestCaptureRule & {
|
|
337
|
+
sources?: Record<string, TraceClientRequestCaptureRule>;
|
|
338
|
+
};
|
|
339
|
+
|
|
327
340
|
export type TraceWatcherToggle = boolean | TraceFilterRule;
|
|
328
341
|
export type TraceRequestWatcherToggle = boolean | TraceRequestWatcherConfig;
|
|
342
|
+
export type TraceClientRequestWatcherToggle = boolean | TraceClientRequestWatcherConfig;
|
|
329
343
|
|
|
330
344
|
export type WatcherToggles = {
|
|
331
345
|
request?: TraceRequestWatcherToggle;
|
|
@@ -347,7 +361,7 @@ export type WatcherToggles = {
|
|
|
347
361
|
batch?: TraceWatcherToggle;
|
|
348
362
|
dump?: TraceWatcherToggle;
|
|
349
363
|
view?: TraceWatcherToggle;
|
|
350
|
-
clientRequest?:
|
|
364
|
+
clientRequest?: TraceClientRequestWatcherToggle;
|
|
351
365
|
};
|
|
352
366
|
|
|
353
367
|
export interface ITraceConfig {
|
package/src/utils/entryFilter.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
ITraceConfig,
|
|
3
3
|
ITraceEntry,
|
|
4
|
+
TraceClientRequestWatcherConfig,
|
|
4
5
|
TraceFilterRule,
|
|
5
6
|
TraceRequestWatcherConfig,
|
|
6
7
|
WatcherToggles,
|
|
@@ -22,6 +23,7 @@ const normalizeTerms = (terms?: string[]): string[] => {
|
|
|
22
23
|
|
|
23
24
|
const matchesRule = (haystack: string, rule?: TraceFilterRule): boolean => {
|
|
24
25
|
if (!rule) return true;
|
|
26
|
+
if (rule.enabled === false) return false;
|
|
25
27
|
|
|
26
28
|
const include = normalizeTerms(rule.include);
|
|
27
29
|
const exclude = normalizeTerms(rule.exclude);
|
|
@@ -86,6 +88,20 @@ const getRequestMethodRule = (
|
|
|
86
88
|
return watcher.all;
|
|
87
89
|
};
|
|
88
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
|
+
|
|
89
105
|
export const TraceEntryFilter = Object.freeze({
|
|
90
106
|
shouldCapture(entry: ITraceEntry, config: ITraceConfig): boolean {
|
|
91
107
|
const watcherKey = watcherKeyByEntryType[entry.type];
|
|
@@ -103,6 +119,13 @@ export const TraceEntryFilter = Object.freeze({
|
|
|
103
119
|
if (!matchesRule(haystack, methodRule)) return false;
|
|
104
120
|
}
|
|
105
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
|
+
|
|
106
129
|
return true;
|
|
107
130
|
},
|
|
108
131
|
});
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
import { generateUuid } from '@zintrust/core';
|
|
1
2
|
import { TraceContext } from '../context';
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
import {
|
|
4
|
+
EntryType,
|
|
5
|
+
type ClientRequestContent,
|
|
6
|
+
type ClientRequestTraceInput,
|
|
7
|
+
type ITraceWatcher,
|
|
8
|
+
type ITraceWatcherConfig,
|
|
9
|
+
type TraceClientRequestCaptureRule,
|
|
10
|
+
type TraceClientRequestWatcherConfig,
|
|
7
11
|
} from '../types';
|
|
8
|
-
import { EntryType } from '../types';
|
|
9
12
|
import { AuthTag } from '../utils/authTag';
|
|
10
13
|
import { redactHeaders, redactUnknown } from '../utils/redact';
|
|
11
14
|
import { RequestFilter } from '../utils/requestFilter';
|
|
@@ -14,8 +17,137 @@ let _storage: ITraceWatcherConfig['storage'] | null = null;
|
|
|
14
17
|
let _redactHeaderNames: string[] = [];
|
|
15
18
|
let _redactBodyFields: string[] = [];
|
|
16
19
|
let _ignoreRoutes: string[] = [];
|
|
20
|
+
let _clientRequestWatcher: TraceClientRequestWatcherConfig | undefined;
|
|
21
|
+
|
|
22
|
+
const isObjectValue = (value: unknown): value is Record<string, unknown> => {
|
|
23
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const resolveSource = (value: unknown): string | undefined => {
|
|
27
|
+
if (typeof value !== 'string') return undefined;
|
|
28
|
+
const normalized = value.trim().toLowerCase();
|
|
29
|
+
return normalized === '' ? undefined : normalized;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const resolveSourceRule = (
|
|
33
|
+
source: string | undefined
|
|
34
|
+
): TraceClientRequestCaptureRule | undefined => {
|
|
35
|
+
if (source === undefined) return undefined;
|
|
36
|
+
return _clientRequestWatcher?.sources?.[source];
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const shouldCaptureField = (
|
|
40
|
+
field: keyof Pick<
|
|
41
|
+
TraceClientRequestCaptureRule,
|
|
42
|
+
'requestHeaders' | 'requestBody' | 'responseHeaders' | 'responseBody'
|
|
43
|
+
>,
|
|
44
|
+
sourceRule: TraceClientRequestCaptureRule | undefined
|
|
45
|
+
): boolean => {
|
|
46
|
+
const scoped = sourceRule?.[field];
|
|
47
|
+
if (typeof scoped === 'boolean') return scoped;
|
|
48
|
+
const global = _clientRequestWatcher?.[field];
|
|
49
|
+
if (typeof global === 'boolean') return global;
|
|
50
|
+
return true;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const buildRequestHeaders = (
|
|
54
|
+
requestHeaders: Record<string, string>,
|
|
55
|
+
sourceRule: TraceClientRequestCaptureRule | undefined
|
|
56
|
+
): Pick<ClientRequestContent, 'requestHeaders'> => {
|
|
57
|
+
return shouldCaptureField('requestHeaders', sourceRule)
|
|
58
|
+
? { requestHeaders: redactHeaders(requestHeaders, _redactHeaderNames) }
|
|
59
|
+
: { requestHeaders: {} };
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const buildRequestBody = (
|
|
63
|
+
requestBody: unknown,
|
|
64
|
+
sourceRule: TraceClientRequestCaptureRule | undefined
|
|
65
|
+
): Partial<Pick<ClientRequestContent, 'requestBody'>> => {
|
|
66
|
+
if (requestBody === undefined) return {};
|
|
67
|
+
if (!shouldCaptureField('requestBody', sourceRule)) return {};
|
|
68
|
+
return { requestBody: redactUnknown(requestBody, _redactBodyFields) };
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const buildResponseHeaders = (
|
|
72
|
+
responseHeaders: Record<string, string> | undefined,
|
|
73
|
+
sourceRule: TraceClientRequestCaptureRule | undefined
|
|
74
|
+
): Partial<Pick<ClientRequestContent, 'responseHeaders'>> => {
|
|
75
|
+
if (responseHeaders === undefined) return {};
|
|
76
|
+
if (!shouldCaptureField('responseHeaders', sourceRule)) return {};
|
|
77
|
+
return { responseHeaders: redactHeaders(responseHeaders, _redactHeaderNames) };
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const buildResponseBody = (
|
|
81
|
+
responseBody: unknown,
|
|
82
|
+
sourceRule: TraceClientRequestCaptureRule | undefined
|
|
83
|
+
): Partial<Pick<ClientRequestContent, 'responseBody'>> => {
|
|
84
|
+
if (responseBody === undefined) return {};
|
|
85
|
+
if (!shouldCaptureField('responseBody', sourceRule)) return {};
|
|
86
|
+
return { responseBody: redactUnknown(responseBody, _redactBodyFields) };
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const applySource = (content: ClientRequestContent, normalizedSource: string | undefined): void => {
|
|
90
|
+
if (normalizedSource !== undefined) {
|
|
91
|
+
content.source = normalizedSource;
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const applyResponseStatus = (
|
|
96
|
+
content: ClientRequestContent,
|
|
97
|
+
responseStatus: number | undefined
|
|
98
|
+
): void => {
|
|
99
|
+
if (responseStatus !== undefined) {
|
|
100
|
+
content.responseStatus = responseStatus;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const applyError = (content: ClientRequestContent, error: unknown): void => {
|
|
105
|
+
if (typeof error === 'string' && error !== '') {
|
|
106
|
+
content.error = error;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const mergePartialContent = (
|
|
111
|
+
content: ClientRequestContent,
|
|
112
|
+
partial: Partial<ClientRequestContent>
|
|
113
|
+
): void => {
|
|
114
|
+
Object.assign(content, partial);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const buildClientRequestContent = (
|
|
118
|
+
input: ClientRequestTraceInput,
|
|
119
|
+
sourceRule: TraceClientRequestCaptureRule | undefined,
|
|
120
|
+
normalizedSource: string | undefined
|
|
121
|
+
): ClientRequestContent => {
|
|
122
|
+
const content: ClientRequestContent = {
|
|
123
|
+
method: input.method.toUpperCase(),
|
|
124
|
+
url: input.url,
|
|
125
|
+
requestHeaders: {},
|
|
126
|
+
duration: input.duration,
|
|
127
|
+
hostname: TraceContext.getHostname(),
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
applySource(content, normalizedSource);
|
|
131
|
+
mergePartialContent(content, buildRequestHeaders(input.requestHeaders, sourceRule));
|
|
132
|
+
mergePartialContent(content, buildRequestBody(input.requestBody, sourceRule));
|
|
133
|
+
applyResponseStatus(content, input.responseStatus);
|
|
134
|
+
mergePartialContent(content, buildResponseHeaders(input.responseHeaders, sourceRule));
|
|
135
|
+
mergePartialContent(content, buildResponseBody(input.responseBody, sourceRule));
|
|
136
|
+
applyError(content, input.error);
|
|
137
|
+
|
|
138
|
+
return content;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const isWatcherEnabled = (
|
|
142
|
+
value: ITraceWatcherConfig['config']['watchers']['clientRequest']
|
|
143
|
+
): boolean => {
|
|
144
|
+
if (value === false) return false;
|
|
145
|
+
if (isObjectValue(value) && value.enabled === false) return false;
|
|
146
|
+
return true;
|
|
147
|
+
};
|
|
17
148
|
|
|
18
149
|
const emit = ({
|
|
150
|
+
source,
|
|
19
151
|
method,
|
|
20
152
|
url,
|
|
21
153
|
requestHeaders,
|
|
@@ -28,29 +160,31 @@ const emit = ({
|
|
|
28
160
|
}: ClientRequestTraceInput): void => {
|
|
29
161
|
if (!_storage) return;
|
|
30
162
|
if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes)) return;
|
|
163
|
+
const normalizedSource = resolveSource(source);
|
|
164
|
+
const sourceRule = resolveSourceRule(normalizedSource);
|
|
165
|
+
if (sourceRule?.enabled === false) return;
|
|
31
166
|
const tags = AuthTag.append([method.toUpperCase()]);
|
|
32
167
|
if ((responseStatus ?? 0) >= 400 || error) tags.push('failed');
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
};
|
|
168
|
+
if (normalizedSource !== undefined) tags.push(normalizedSource);
|
|
169
|
+
const content = buildClientRequestContent(
|
|
170
|
+
{
|
|
171
|
+
source,
|
|
172
|
+
method,
|
|
173
|
+
url,
|
|
174
|
+
requestHeaders,
|
|
175
|
+
responseStatus,
|
|
176
|
+
duration,
|
|
177
|
+
requestBody,
|
|
178
|
+
responseHeaders,
|
|
179
|
+
responseBody,
|
|
180
|
+
error,
|
|
181
|
+
},
|
|
182
|
+
sourceRule,
|
|
183
|
+
normalizedSource
|
|
184
|
+
);
|
|
51
185
|
_storage
|
|
52
186
|
.writeEntry({
|
|
53
|
-
uuid:
|
|
187
|
+
uuid: generateUuid(),
|
|
54
188
|
batchId: TraceContext.getBatchId(),
|
|
55
189
|
type: EntryType.CLIENT_REQUEST,
|
|
56
190
|
content,
|
|
@@ -64,13 +198,18 @@ const emit = ({
|
|
|
64
198
|
export const HttpClientWatcher: ITraceWatcher & { emit: typeof emit } = Object.freeze({
|
|
65
199
|
emit,
|
|
66
200
|
register({ storage, config }: ITraceWatcherConfig): () => void {
|
|
67
|
-
if (config.watchers.clientRequest
|
|
201
|
+
if (!isWatcherEnabled(config.watchers.clientRequest)) return () => undefined;
|
|
68
202
|
_storage = storage;
|
|
203
|
+
_clientRequestWatcher =
|
|
204
|
+
typeof config.watchers.clientRequest === 'object' && config.watchers.clientRequest !== null
|
|
205
|
+
? config.watchers.clientRequest
|
|
206
|
+
: undefined;
|
|
69
207
|
_redactHeaderNames = [...(config.redaction?.keys ?? []), ...(config.redaction?.headers ?? [])];
|
|
70
208
|
_redactBodyFields = [...(config.redaction?.keys ?? []), ...(config.redaction?.body ?? [])];
|
|
71
209
|
_ignoreRoutes = config.ignoreRoutes;
|
|
72
210
|
return () => {
|
|
73
211
|
_storage = null;
|
|
212
|
+
_clientRequestWatcher = undefined;
|
|
74
213
|
_redactBodyFields = [];
|
|
75
214
|
_ignoreRoutes = [];
|
|
76
215
|
};
|