@zintrust/trace 1.6.6 → 1.6.7
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/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 -702
- package/src/storage/ProxyTraceStorage.ts +0 -190
- package/src/storage/TraceContentBudget.ts +0 -493
- package/src/storage/TraceContentRedaction.ts +0 -44
- package/src/storage/TraceEntryFiltering.ts +0 -50
- 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 -249
- 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 -105
- package/src/watchers/RedisWatcher.ts +0 -42
- package/src/watchers/ScheduleWatcher.ts +0 -57
- package/src/watchers/ViewWatcher.ts +0 -40
|
@@ -1,289 +0,0 @@
|
|
|
1
|
-
import type { EntryTypeValue, ITraceEntry, ITraceStorage } from '../types';
|
|
2
|
-
|
|
3
|
-
type TraceLogger = {
|
|
4
|
-
warn: (message: string, context?: Record<string, unknown>) => void;
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
type TraceWriteFailureContext = {
|
|
8
|
-
connectionName: string;
|
|
9
|
-
error: unknown;
|
|
10
|
-
operation: string;
|
|
11
|
-
watcherType?: EntryTypeValue;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
type TraceWriteDiagnosticsSnapshot = {
|
|
15
|
-
degraded: boolean;
|
|
16
|
-
lastErrorMessage: string | null;
|
|
17
|
-
lastFailureAt: number | null;
|
|
18
|
-
totalFailures: number;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
type TraceWriteDiagnosticsState = TraceWriteDiagnosticsSnapshot & {
|
|
22
|
-
lastLoggedAtByFingerprint: Map<string, number>;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const LOG_WINDOW_MS = 30_000;
|
|
26
|
-
|
|
27
|
-
const diagnosticsState: TraceWriteDiagnosticsState = {
|
|
28
|
-
degraded: false,
|
|
29
|
-
lastErrorMessage: null,
|
|
30
|
-
lastFailureAt: null,
|
|
31
|
-
lastLoggedAtByFingerprint: new Map<string, number>(),
|
|
32
|
-
totalFailures: 0,
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const asRecord = (value: unknown): Record<string, unknown> | null => {
|
|
36
|
-
if (value === null || typeof value !== 'object') return null;
|
|
37
|
-
return value as Record<string, unknown>;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const getAttachedErrorDetails = (error: unknown): unknown => {
|
|
41
|
-
if (error instanceof Error && 'details' in error) {
|
|
42
|
-
const details = (error as Error & { details?: unknown }).details;
|
|
43
|
-
if (details !== undefined) return details;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const record = asRecord(error);
|
|
47
|
-
return record?.['details'];
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const getTextValue = (value: unknown): string | null => {
|
|
51
|
-
if (typeof value !== 'string') return null;
|
|
52
|
-
|
|
53
|
-
const trimmed = value.trim();
|
|
54
|
-
return trimmed === '' ? null : trimmed;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const describeBodyDetails = (body: Record<string, unknown>): string | null => {
|
|
58
|
-
const code = getTextValue(body['code']);
|
|
59
|
-
const message = getTextValue(body['message']);
|
|
60
|
-
|
|
61
|
-
if (code !== null && message !== null) return `${code}: ${message}`;
|
|
62
|
-
if (code !== null) return code;
|
|
63
|
-
if (message !== null) return message;
|
|
64
|
-
|
|
65
|
-
return null;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const describeRecordDetails = (record: Record<string, unknown>): string | null => {
|
|
69
|
-
const body = asRecord(record['body']);
|
|
70
|
-
if (body !== null) {
|
|
71
|
-
const describedBody = describeBodyDetails(body);
|
|
72
|
-
if (describedBody !== null) return describedBody;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const nested = describeErrorDetails(record['details']);
|
|
76
|
-
if (nested !== null) return nested;
|
|
77
|
-
|
|
78
|
-
const message = getTextValue(record['message']);
|
|
79
|
-
if (message !== null) return message;
|
|
80
|
-
|
|
81
|
-
const code = getTextValue(record['code']);
|
|
82
|
-
if (code !== null) return code;
|
|
83
|
-
|
|
84
|
-
return null;
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
const describeErrorDetails = (details: unknown): string | null => {
|
|
88
|
-
const text = getTextValue(details);
|
|
89
|
-
if (text !== null) return text;
|
|
90
|
-
|
|
91
|
-
const record = asRecord(details);
|
|
92
|
-
if (record === null) return null;
|
|
93
|
-
return describeRecordDetails(record);
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
const withOptionalDetail = (
|
|
97
|
-
context: Record<string, unknown>,
|
|
98
|
-
errorDetails: unknown
|
|
99
|
-
): Record<string, unknown> => {
|
|
100
|
-
if (errorDetails === undefined) return context;
|
|
101
|
-
return { ...context, errorDetails };
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
const TRACE_SKIP_LOG_CONTEXT_KEY = '__zintrustSkipTraceLog';
|
|
105
|
-
|
|
106
|
-
const withTraceSkipContext = (
|
|
107
|
-
logger: TraceLogger | undefined,
|
|
108
|
-
context: Record<string, unknown>
|
|
109
|
-
): Record<string, unknown> => {
|
|
110
|
-
const loggerWithTraceSkip = logger as TraceLogger & {
|
|
111
|
-
withTraceSkipContext?: (ctx?: Record<string, unknown>) => Record<string, unknown>;
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
return (
|
|
115
|
-
loggerWithTraceSkip.withTraceSkipContext?.(context) ?? {
|
|
116
|
-
...context,
|
|
117
|
-
[TRACE_SKIP_LOG_CONTEXT_KEY]: true,
|
|
118
|
-
}
|
|
119
|
-
);
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
const getErrorMessage = (error: unknown): string => {
|
|
123
|
-
let baseMessage = '';
|
|
124
|
-
if (error instanceof Error && error.message.trim() !== '') {
|
|
125
|
-
baseMessage = error.message;
|
|
126
|
-
} else if (typeof error === 'string' && error.trim() !== '') {
|
|
127
|
-
baseMessage = error;
|
|
128
|
-
} else {
|
|
129
|
-
try {
|
|
130
|
-
const serialized = JSON.stringify(error);
|
|
131
|
-
if (typeof serialized === 'string' && serialized !== '') baseMessage = serialized;
|
|
132
|
-
} catch {
|
|
133
|
-
// ignore serialization failures
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const detailsSummary = describeErrorDetails(getAttachedErrorDetails(error));
|
|
138
|
-
if (
|
|
139
|
-
detailsSummary !== null &&
|
|
140
|
-
detailsSummary !== '' &&
|
|
141
|
-
baseMessage !== '' &&
|
|
142
|
-
!baseMessage.includes(detailsSummary)
|
|
143
|
-
) {
|
|
144
|
-
return `${baseMessage} (${detailsSummary})`;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (baseMessage !== '') return baseMessage;
|
|
148
|
-
|
|
149
|
-
if (detailsSummary !== null && detailsSummary !== '') return detailsSummary;
|
|
150
|
-
|
|
151
|
-
return 'Unknown trace storage error';
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
const buildFingerprint = (context: TraceWriteFailureContext): string => {
|
|
155
|
-
return [
|
|
156
|
-
context.connectionName,
|
|
157
|
-
context.operation,
|
|
158
|
-
context.watcherType ?? 'unknown',
|
|
159
|
-
getErrorMessage(context.error),
|
|
160
|
-
].join('|');
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
const reportFailure = (
|
|
164
|
-
logger: TraceLogger | undefined,
|
|
165
|
-
context: TraceWriteFailureContext
|
|
166
|
-
): void => {
|
|
167
|
-
const now = Date.now();
|
|
168
|
-
const errorMessage = getErrorMessage(context.error);
|
|
169
|
-
const errorDetails = getAttachedErrorDetails(context.error);
|
|
170
|
-
const fingerprint = buildFingerprint(context);
|
|
171
|
-
const lastLoggedAt = diagnosticsState.lastLoggedAtByFingerprint.get(fingerprint);
|
|
172
|
-
|
|
173
|
-
diagnosticsState.degraded = true;
|
|
174
|
-
diagnosticsState.lastErrorMessage = errorMessage;
|
|
175
|
-
diagnosticsState.lastFailureAt = now;
|
|
176
|
-
diagnosticsState.totalFailures += 1;
|
|
177
|
-
|
|
178
|
-
if (logger === undefined) return;
|
|
179
|
-
if (typeof lastLoggedAt === 'number' && now - lastLoggedAt < LOG_WINDOW_MS) return;
|
|
180
|
-
|
|
181
|
-
diagnosticsState.lastLoggedAtByFingerprint.set(fingerprint, now);
|
|
182
|
-
logger.warn(
|
|
183
|
-
'[trace] Trace storage write degraded',
|
|
184
|
-
withTraceSkipContext(
|
|
185
|
-
logger,
|
|
186
|
-
withOptionalDetail(
|
|
187
|
-
{
|
|
188
|
-
connectionName: context.connectionName,
|
|
189
|
-
error: errorMessage,
|
|
190
|
-
lastFailureAt: now,
|
|
191
|
-
operation: context.operation,
|
|
192
|
-
totalFailures: diagnosticsState.totalFailures,
|
|
193
|
-
watcherType: context.watcherType ?? null,
|
|
194
|
-
},
|
|
195
|
-
errorDetails
|
|
196
|
-
)
|
|
197
|
-
)
|
|
198
|
-
);
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
const wrapStorageMethod = <TArgs extends unknown[], TResult>(
|
|
202
|
-
method: (...args: TArgs) => Promise<TResult>,
|
|
203
|
-
describeFailure: (...args: TArgs) => Omit<TraceWriteFailureContext, 'connectionName' | 'error'>,
|
|
204
|
-
connectionName: string,
|
|
205
|
-
logger?: TraceLogger
|
|
206
|
-
): ((...args: TArgs) => Promise<TResult>) => {
|
|
207
|
-
return async (...args: TArgs): Promise<TResult> => {
|
|
208
|
-
try {
|
|
209
|
-
return await method(...args);
|
|
210
|
-
} catch (error) {
|
|
211
|
-
reportFailure(logger, {
|
|
212
|
-
...describeFailure(...args),
|
|
213
|
-
connectionName,
|
|
214
|
-
error,
|
|
215
|
-
});
|
|
216
|
-
throw error;
|
|
217
|
-
}
|
|
218
|
-
};
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
export const TraceWriteDiagnostics = Object.freeze({
|
|
222
|
-
getSnapshot(): TraceWriteDiagnosticsSnapshot {
|
|
223
|
-
return {
|
|
224
|
-
degraded: diagnosticsState.degraded,
|
|
225
|
-
lastErrorMessage: diagnosticsState.lastErrorMessage,
|
|
226
|
-
lastFailureAt: diagnosticsState.lastFailureAt,
|
|
227
|
-
totalFailures: diagnosticsState.totalFailures,
|
|
228
|
-
};
|
|
229
|
-
},
|
|
230
|
-
|
|
231
|
-
reset(): void {
|
|
232
|
-
diagnosticsState.degraded = false;
|
|
233
|
-
diagnosticsState.lastErrorMessage = null;
|
|
234
|
-
diagnosticsState.lastFailureAt = null;
|
|
235
|
-
diagnosticsState.totalFailures = 0;
|
|
236
|
-
diagnosticsState.lastLoggedAtByFingerprint.clear();
|
|
237
|
-
},
|
|
238
|
-
|
|
239
|
-
wrapStorage(
|
|
240
|
-
storage: ITraceStorage,
|
|
241
|
-
options: { connectionName: string; logger?: TraceLogger }
|
|
242
|
-
): ITraceStorage {
|
|
243
|
-
return Object.freeze({
|
|
244
|
-
...storage,
|
|
245
|
-
writeEntry: wrapStorageMethod(
|
|
246
|
-
storage.writeEntry.bind(storage),
|
|
247
|
-
(entry: ITraceEntry) => ({ operation: 'writeEntry', watcherType: entry.type }),
|
|
248
|
-
options.connectionName,
|
|
249
|
-
options.logger
|
|
250
|
-
),
|
|
251
|
-
updateEntry: wrapStorageMethod(
|
|
252
|
-
storage.updateEntry.bind(storage),
|
|
253
|
-
(_uuid: string, _patch) => ({ operation: 'updateEntry' }),
|
|
254
|
-
options.connectionName,
|
|
255
|
-
options.logger
|
|
256
|
-
),
|
|
257
|
-
markFamilyStale: wrapStorageMethod(
|
|
258
|
-
storage.markFamilyStale.bind(storage),
|
|
259
|
-
(_familyHash: string, _exceptUuid: string) => ({ operation: 'markFamilyStale' }),
|
|
260
|
-
options.connectionName,
|
|
261
|
-
options.logger
|
|
262
|
-
),
|
|
263
|
-
prune: wrapStorageMethod(
|
|
264
|
-
storage.prune.bind(storage),
|
|
265
|
-
(_olderThanMs: number, _keepExceptions?: boolean) => ({ operation: 'prune' }),
|
|
266
|
-
options.connectionName,
|
|
267
|
-
options.logger
|
|
268
|
-
),
|
|
269
|
-
clear: wrapStorageMethod(
|
|
270
|
-
storage.clear.bind(storage),
|
|
271
|
-
() => ({ operation: 'clear' }),
|
|
272
|
-
options.connectionName,
|
|
273
|
-
options.logger
|
|
274
|
-
),
|
|
275
|
-
addMonitoring: wrapStorageMethod(
|
|
276
|
-
storage.addMonitoring.bind(storage),
|
|
277
|
-
(_tag: string) => ({ operation: 'addMonitoring' }),
|
|
278
|
-
options.connectionName,
|
|
279
|
-
options.logger
|
|
280
|
-
),
|
|
281
|
-
removeMonitoring: wrapStorageMethod(
|
|
282
|
-
storage.removeMonitoring.bind(storage),
|
|
283
|
-
(_tag: string) => ({ operation: 'removeMonitoring' }),
|
|
284
|
-
options.connectionName,
|
|
285
|
-
options.logger
|
|
286
|
-
),
|
|
287
|
-
});
|
|
288
|
-
},
|
|
289
|
-
});
|
package/src/storage/index.ts
DELETED
package/src/types.ts
DELETED
|
@@ -1,430 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Types for @zintrust/trace
|
|
3
|
-
* Sealed type definitions — no side effects.
|
|
4
|
-
*/
|
|
5
|
-
import type { IDatabase } from '@zintrust/core';
|
|
6
|
-
|
|
7
|
-
// ---------------------------------------------------------------------------
|
|
8
|
-
// Entry types used by the trace event stream.
|
|
9
|
-
// ---------------------------------------------------------------------------
|
|
10
|
-
|
|
11
|
-
export const EntryType = Object.freeze({
|
|
12
|
-
REQUEST: 'request',
|
|
13
|
-
QUERY: 'query',
|
|
14
|
-
EXCEPTION: 'exception',
|
|
15
|
-
LOG: 'log',
|
|
16
|
-
JOB: 'job',
|
|
17
|
-
CACHE: 'cache',
|
|
18
|
-
SCHEDULE: 'schedule',
|
|
19
|
-
MAIL: 'mail',
|
|
20
|
-
AUTH: 'auth',
|
|
21
|
-
EVENT: 'event',
|
|
22
|
-
MODEL: 'model',
|
|
23
|
-
NOTIFICATION: 'notification',
|
|
24
|
-
REDIS: 'redis',
|
|
25
|
-
GATE: 'gate',
|
|
26
|
-
MIDDLEWARE: 'middleware',
|
|
27
|
-
COMMAND: 'command',
|
|
28
|
-
BATCH: 'batch',
|
|
29
|
-
DUMP: 'dump',
|
|
30
|
-
VIEW: 'view',
|
|
31
|
-
CLIENT_REQUEST: 'client_request',
|
|
32
|
-
} as const);
|
|
33
|
-
|
|
34
|
-
export type EntryTypeValue = (typeof EntryType)[keyof typeof EntryType];
|
|
35
|
-
|
|
36
|
-
// ---------------------------------------------------------------------------
|
|
37
|
-
// Per-type content shapes
|
|
38
|
-
// ---------------------------------------------------------------------------
|
|
39
|
-
|
|
40
|
-
export interface RequestContent {
|
|
41
|
-
method: string;
|
|
42
|
-
uri: string;
|
|
43
|
-
headers: Record<string, string>;
|
|
44
|
-
payload: unknown;
|
|
45
|
-
responseStatus: number;
|
|
46
|
-
responseHeaders: Record<string, string>;
|
|
47
|
-
responseBody?: unknown;
|
|
48
|
-
duration: number;
|
|
49
|
-
memory: number | null;
|
|
50
|
-
middleware: string[];
|
|
51
|
-
hostname: string;
|
|
52
|
-
userId?: string;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface QueryContent {
|
|
56
|
-
connection: string;
|
|
57
|
-
sql: string;
|
|
58
|
-
statement?: string;
|
|
59
|
-
bindings?: unknown[];
|
|
60
|
-
bindingsIncluded?: boolean;
|
|
61
|
-
time: number;
|
|
62
|
-
duration: number;
|
|
63
|
-
slow: boolean;
|
|
64
|
-
hash: string;
|
|
65
|
-
hostname: string;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export interface ExceptionContent {
|
|
69
|
-
class: string;
|
|
70
|
-
file: string;
|
|
71
|
-
line: number;
|
|
72
|
-
message: string;
|
|
73
|
-
trace: Array<{ file: string; line: number; function?: string }>;
|
|
74
|
-
linePreview: Record<string, string>;
|
|
75
|
-
occurrences: number;
|
|
76
|
-
hostname: string;
|
|
77
|
-
userId?: string;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export interface LogContent {
|
|
81
|
-
level: string;
|
|
82
|
-
message: string;
|
|
83
|
-
context?: Record<string, unknown>;
|
|
84
|
-
hostname: string;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export interface JobContent {
|
|
88
|
-
status: 'pending' | 'processed' | 'failed';
|
|
89
|
-
connection: string;
|
|
90
|
-
queue: string;
|
|
91
|
-
name: string;
|
|
92
|
-
tries?: number;
|
|
93
|
-
timeout?: number;
|
|
94
|
-
data?: unknown;
|
|
95
|
-
exception?: { message: string; trace: Array<{ file: string; line: number }> };
|
|
96
|
-
hostname: string;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export interface CacheContent {
|
|
100
|
-
operation: 'get' | 'set' | 'delete' | 'clear' | 'has';
|
|
101
|
-
key: string;
|
|
102
|
-
hit?: boolean;
|
|
103
|
-
store?: string;
|
|
104
|
-
payload?: unknown;
|
|
105
|
-
payloadLogged?: boolean;
|
|
106
|
-
ttl?: number;
|
|
107
|
-
duration: number;
|
|
108
|
-
hostname: string;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export interface ScheduleContent {
|
|
112
|
-
name: string;
|
|
113
|
-
expression: string;
|
|
114
|
-
status: 'ran' | 'failed' | 'skipped';
|
|
115
|
-
duration: number;
|
|
116
|
-
output?: string;
|
|
117
|
-
hostname: string;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export interface MailContent {
|
|
121
|
-
to: string;
|
|
122
|
-
subject: string;
|
|
123
|
-
template?: string;
|
|
124
|
-
text?: string;
|
|
125
|
-
html?: string;
|
|
126
|
-
hostname: string;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export interface AuthContent {
|
|
130
|
-
event: 'login' | 'logout' | 'failed';
|
|
131
|
-
userId?: string;
|
|
132
|
-
hostname: string;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export interface EventContent {
|
|
136
|
-
name: string;
|
|
137
|
-
payload?: unknown;
|
|
138
|
-
listenerCount: number;
|
|
139
|
-
hostname: string;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export interface ModelContent {
|
|
143
|
-
action: 'create' | 'update' | 'delete';
|
|
144
|
-
model: string;
|
|
145
|
-
id?: string | number;
|
|
146
|
-
changes?: Record<string, unknown>;
|
|
147
|
-
hostname: string;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
export interface NotificationContent {
|
|
151
|
-
channels: string[];
|
|
152
|
-
notifiable?: string;
|
|
153
|
-
notification: string;
|
|
154
|
-
message?: string;
|
|
155
|
-
payload?: unknown;
|
|
156
|
-
hostname: string;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export interface RedisContent {
|
|
160
|
-
command: string;
|
|
161
|
-
duration: number;
|
|
162
|
-
hostname: string;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
export interface GateContent {
|
|
166
|
-
ability: string;
|
|
167
|
-
result: 'allowed' | 'denied';
|
|
168
|
-
userId?: string;
|
|
169
|
-
subject?: string;
|
|
170
|
-
hostname: string;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
export interface MiddlewareContent {
|
|
174
|
-
name: string;
|
|
175
|
-
event: 'before' | 'after';
|
|
176
|
-
duration?: number;
|
|
177
|
-
hostname: string;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
export interface CommandContent {
|
|
181
|
-
name: string;
|
|
182
|
-
arguments: Record<string, unknown>;
|
|
183
|
-
exitCode: number;
|
|
184
|
-
duration: number;
|
|
185
|
-
output?: string;
|
|
186
|
-
hostname: string;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
export interface BatchContent {
|
|
190
|
-
name: string;
|
|
191
|
-
total: number;
|
|
192
|
-
processed: number;
|
|
193
|
-
failed: number;
|
|
194
|
-
status: 'pending' | 'processing' | 'finished' | 'failed';
|
|
195
|
-
hostname: string;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
export interface DumpContent {
|
|
199
|
-
value: unknown;
|
|
200
|
-
file?: string;
|
|
201
|
-
line?: number;
|
|
202
|
-
hostname: string;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
export interface ViewContent {
|
|
206
|
-
template: string;
|
|
207
|
-
duration: number;
|
|
208
|
-
hostname: string;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
export interface ClientRequestContent {
|
|
212
|
-
source?: string;
|
|
213
|
-
method: string;
|
|
214
|
-
url: string;
|
|
215
|
-
requestHeaders: Record<string, string>;
|
|
216
|
-
requestBody?: unknown;
|
|
217
|
-
responseStatus?: number;
|
|
218
|
-
responseHeaders?: Record<string, string>;
|
|
219
|
-
responseBody?: unknown;
|
|
220
|
-
error?: string;
|
|
221
|
-
duration: number;
|
|
222
|
-
hostname: string;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
export interface ClientRequestTraceInput {
|
|
226
|
-
source?: string;
|
|
227
|
-
method: string;
|
|
228
|
-
url: string;
|
|
229
|
-
requestHeaders: Record<string, string>;
|
|
230
|
-
responseStatus?: number;
|
|
231
|
-
duration: number;
|
|
232
|
-
requestBody?: unknown;
|
|
233
|
-
responseHeaders?: Record<string, string>;
|
|
234
|
-
responseBody?: unknown;
|
|
235
|
-
error?: string;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// ---------------------------------------------------------------------------
|
|
239
|
-
// Core domain records
|
|
240
|
-
// ---------------------------------------------------------------------------
|
|
241
|
-
|
|
242
|
-
export interface ITraceEntry<T = unknown> {
|
|
243
|
-
uuid: string;
|
|
244
|
-
batchId: string;
|
|
245
|
-
familyHash?: string;
|
|
246
|
-
type: EntryTypeValue;
|
|
247
|
-
content: T;
|
|
248
|
-
tags: string[];
|
|
249
|
-
isLatest: boolean;
|
|
250
|
-
createdAt: number;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// ---------------------------------------------------------------------------
|
|
254
|
-
// Storage interface
|
|
255
|
-
// ---------------------------------------------------------------------------
|
|
256
|
-
|
|
257
|
-
export interface QueryEntriesOptions {
|
|
258
|
-
type?: EntryTypeValue;
|
|
259
|
-
tag?: string;
|
|
260
|
-
batchId?: string;
|
|
261
|
-
from?: number;
|
|
262
|
-
to?: number;
|
|
263
|
-
page?: number;
|
|
264
|
-
perPage?: number;
|
|
265
|
-
summary?: boolean;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
export interface QueryBatchEntriesOptions {
|
|
269
|
-
type?: EntryTypeValue;
|
|
270
|
-
excludeTypes?: EntryTypeValue[];
|
|
271
|
-
page?: number;
|
|
272
|
-
perPage?: number;
|
|
273
|
-
summary?: boolean;
|
|
274
|
-
countsOnly?: boolean;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
export interface QueryBatchEntriesResult {
|
|
278
|
-
entries: ITraceEntry[];
|
|
279
|
-
total: number;
|
|
280
|
-
counts: Partial<Record<EntryTypeValue, number>>;
|
|
281
|
-
page: number;
|
|
282
|
-
perPage: number;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
export interface ITraceStorage {
|
|
286
|
-
writeEntry(entry: ITraceEntry): Promise<void>;
|
|
287
|
-
updateEntry(
|
|
288
|
-
uuid: string,
|
|
289
|
-
patch: Partial<Pick<ITraceEntry, 'content' | 'isLatest'>>
|
|
290
|
-
): Promise<void>;
|
|
291
|
-
markFamilyStale(familyHash: string, exceptUuid: string): Promise<void>;
|
|
292
|
-
queryEntries(opts: QueryEntriesOptions): Promise<{ data: ITraceEntry[]; total: number }>;
|
|
293
|
-
getEntry(uuid: string): Promise<ITraceEntry | null>;
|
|
294
|
-
getBatch(batchId: string): Promise<ITraceEntry[]>;
|
|
295
|
-
queryBatchEntries(
|
|
296
|
-
batchId: string,
|
|
297
|
-
opts?: QueryBatchEntriesOptions
|
|
298
|
-
): Promise<QueryBatchEntriesResult>;
|
|
299
|
-
prune(olderThanMs: number, keepExceptions?: boolean): Promise<number>;
|
|
300
|
-
clear(): Promise<void>;
|
|
301
|
-
getMonitoring(): Promise<string[]>;
|
|
302
|
-
addMonitoring(tag: string): Promise<void>;
|
|
303
|
-
removeMonitoring(tag: string): Promise<void>;
|
|
304
|
-
stats(): Promise<Record<EntryTypeValue, number>>;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// ---------------------------------------------------------------------------
|
|
308
|
-
// Watcher interface
|
|
309
|
-
// ---------------------------------------------------------------------------
|
|
310
|
-
|
|
311
|
-
export interface ITraceWatcherConfig {
|
|
312
|
-
storage: ITraceStorage;
|
|
313
|
-
config: ITraceConfig;
|
|
314
|
-
db?: IDatabase;
|
|
315
|
-
/** Optional: provide to allow HttpWatcher to register as global middleware. */
|
|
316
|
-
registerMiddleware?: (
|
|
317
|
-
fn: (req: unknown, res: unknown, next: () => Promise<void>) => Promise<void>
|
|
318
|
-
) => void;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
export interface ITraceWatcher {
|
|
322
|
-
register(opts: ITraceWatcherConfig): () => void;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// ---------------------------------------------------------------------------
|
|
326
|
-
// Config interface
|
|
327
|
-
// ---------------------------------------------------------------------------
|
|
328
|
-
|
|
329
|
-
export type RedactionConfig = {
|
|
330
|
-
keys: string[];
|
|
331
|
-
headers: string[];
|
|
332
|
-
body: string[];
|
|
333
|
-
query: string[];
|
|
334
|
-
};
|
|
335
|
-
|
|
336
|
-
export type TraceFilterRule = {
|
|
337
|
-
enabled?: boolean;
|
|
338
|
-
include?: string[];
|
|
339
|
-
exclude?: string[];
|
|
340
|
-
};
|
|
341
|
-
|
|
342
|
-
export type TraceClientRequestCaptureRule = TraceFilterRule & {
|
|
343
|
-
requestHeaders?: boolean;
|
|
344
|
-
requestBody?: boolean;
|
|
345
|
-
responseHeaders?: boolean;
|
|
346
|
-
responseBody?: boolean;
|
|
347
|
-
};
|
|
348
|
-
|
|
349
|
-
export type TraceRequestWatcherConfig = TraceFilterRule & {
|
|
350
|
-
all?: TraceFilterRule;
|
|
351
|
-
get?: TraceFilterRule;
|
|
352
|
-
post?: TraceFilterRule;
|
|
353
|
-
put?: TraceFilterRule;
|
|
354
|
-
patch?: TraceFilterRule;
|
|
355
|
-
delete?: TraceFilterRule;
|
|
356
|
-
};
|
|
357
|
-
|
|
358
|
-
export type TraceClientRequestWatcherConfig = TraceClientRequestCaptureRule & {
|
|
359
|
-
sources?: Record<string, TraceClientRequestCaptureRule>;
|
|
360
|
-
};
|
|
361
|
-
|
|
362
|
-
export type TraceContentDispatchWorkerConfig = {
|
|
363
|
-
enabled: boolean;
|
|
364
|
-
intervalMs: number;
|
|
365
|
-
maxDurationMs: number;
|
|
366
|
-
concurrency: number;
|
|
367
|
-
};
|
|
368
|
-
|
|
369
|
-
export type TraceContentDispatchConfig = {
|
|
370
|
-
driver?: string;
|
|
371
|
-
queueName: string;
|
|
372
|
-
enqueueTimeoutMs: number;
|
|
373
|
-
worker: TraceContentDispatchWorkerConfig;
|
|
374
|
-
};
|
|
375
|
-
|
|
376
|
-
export type TraceProxyConfig = {
|
|
377
|
-
enabled: boolean;
|
|
378
|
-
url?: string;
|
|
379
|
-
path: string;
|
|
380
|
-
keyId?: string;
|
|
381
|
-
secret?: string;
|
|
382
|
-
timeoutMs: number;
|
|
383
|
-
};
|
|
384
|
-
|
|
385
|
-
export type TraceWatcherToggle = boolean | TraceFilterRule;
|
|
386
|
-
export type TraceRequestWatcherToggle = boolean | TraceRequestWatcherConfig;
|
|
387
|
-
export type TraceClientRequestWatcherToggle = boolean | TraceClientRequestWatcherConfig;
|
|
388
|
-
|
|
389
|
-
export type WatcherToggles = {
|
|
390
|
-
request?: TraceRequestWatcherToggle;
|
|
391
|
-
query?: TraceWatcherToggle;
|
|
392
|
-
exception?: TraceWatcherToggle;
|
|
393
|
-
log?: TraceWatcherToggle;
|
|
394
|
-
job?: TraceWatcherToggle;
|
|
395
|
-
cache?: TraceWatcherToggle;
|
|
396
|
-
schedule?: TraceWatcherToggle;
|
|
397
|
-
mail?: TraceWatcherToggle;
|
|
398
|
-
auth?: TraceWatcherToggle;
|
|
399
|
-
event?: TraceWatcherToggle;
|
|
400
|
-
model?: TraceWatcherToggle;
|
|
401
|
-
notification?: TraceWatcherToggle;
|
|
402
|
-
redis?: TraceWatcherToggle;
|
|
403
|
-
gate?: TraceWatcherToggle;
|
|
404
|
-
middleware?: TraceWatcherToggle;
|
|
405
|
-
command?: TraceWatcherToggle;
|
|
406
|
-
batch?: TraceWatcherToggle;
|
|
407
|
-
dump?: TraceWatcherToggle;
|
|
408
|
-
view?: TraceWatcherToggle;
|
|
409
|
-
clientRequest?: TraceClientRequestWatcherToggle;
|
|
410
|
-
};
|
|
411
|
-
|
|
412
|
-
export interface ITraceConfig {
|
|
413
|
-
enabled: boolean;
|
|
414
|
-
connection?: string;
|
|
415
|
-
observeConnection?: string;
|
|
416
|
-
serviceTag?: string;
|
|
417
|
-
proxy: TraceProxyConfig;
|
|
418
|
-
pruneAfterHours: number;
|
|
419
|
-
ignoreRoutes: string[];
|
|
420
|
-
ignorePaths: string[];
|
|
421
|
-
slowQueryThreshold: number;
|
|
422
|
-
captureCachePayloads: boolean;
|
|
423
|
-
captureQueryBindings: boolean;
|
|
424
|
-
logMinLevel: 'debug' | 'info' | 'warn' | 'error' | 'fatal';
|
|
425
|
-
contentDispatch: TraceContentDispatchConfig;
|
|
426
|
-
watchers: WatcherToggles;
|
|
427
|
-
redaction: RedactionConfig;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
export type TraceConfigOverrides = Partial<ITraceConfig>;
|