@localstack/appinspector-ui 1.0.176 → 1.0.178
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/index.cjs +7076 -6548
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +7117 -6588
- package/dist/index.js.map +1 -1
- package/dist/spans-worker.js +389 -149
- package/package.json +1 -1
package/dist/spans-worker.js
CHANGED
|
@@ -1,18 +1,120 @@
|
|
|
1
1
|
(() => {
|
|
2
|
-
// src/
|
|
3
|
-
var
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
2
|
+
// src/utils/query.ts
|
|
3
|
+
var EXTERNAL_PRODUCER = "external";
|
|
4
|
+
var QUERY_KEYS = ["service", "producer", "status", "op", "resource", "client", "region", "account", "dur", "trace"];
|
|
5
|
+
var isQueryKey = (value) => QUERY_KEYS.includes(value);
|
|
6
|
+
var TOKEN_PATTERN = /(?:[^\s"]+|"[^"]*"?)+/g;
|
|
7
|
+
var unquote = (value) => value.replaceAll('"', "");
|
|
8
|
+
var isExclusion = (token) => token.length > 1 && token.startsWith("-");
|
|
9
|
+
var splitTerm = (token) => {
|
|
10
|
+
const unquoted = unquote(isExclusion(token) ? token.slice(1) : token);
|
|
11
|
+
const separator = unquoted.indexOf(":");
|
|
12
|
+
if (separator <= 0) {
|
|
13
|
+
return void 0;
|
|
14
|
+
}
|
|
15
|
+
const key = unquoted.slice(0, separator).toLowerCase();
|
|
16
|
+
const value = unquoted.slice(separator + 1);
|
|
17
|
+
return isQueryKey(key) && value !== "" ? [key, value] : void 0;
|
|
18
|
+
};
|
|
19
|
+
var parseQuery = (query) => {
|
|
20
|
+
const parsed = { excludedTerms: {}, excludedWords: [], terms: {}, words: [] };
|
|
21
|
+
for (const token of query.match(TOKEN_PATTERN) ?? []) {
|
|
22
|
+
const excluded = isExclusion(token);
|
|
23
|
+
const term = splitTerm(token);
|
|
24
|
+
if (term) {
|
|
25
|
+
const [key, value] = term;
|
|
26
|
+
((excluded ? parsed.excludedTerms : parsed.terms)[key] ??= []).push(value);
|
|
27
|
+
} else {
|
|
28
|
+
const word = unquote(excluded ? token.slice(1) : token).toLowerCase();
|
|
29
|
+
if (word !== "") {
|
|
30
|
+
(excluded ? parsed.excludedWords : parsed.words).push(word);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return parsed;
|
|
35
|
+
};
|
|
36
|
+
var hasExclusions = (parsed) => parsed.excludedWords.length > 0 || Object.keys(parsed.excludedTerms).length > 0;
|
|
37
|
+
var isQueryEmpty = (parsed) => parsed.words.length === 0 && Object.keys(parsed.terms).length === 0 && !hasExclusions(parsed);
|
|
38
|
+
var UNIT_TO_MS = { m: 6e4, ms: 1, s: 1e3, us: 1e-3 };
|
|
39
|
+
var parseDuration = (value) => {
|
|
40
|
+
const match = /^(>=|<=|>|<|=)?\s*(\d+(?:\.\d+)?)\s*(us|ms|s|m)?$/i.exec(value.trim());
|
|
41
|
+
if (!match) {
|
|
42
|
+
return void 0;
|
|
43
|
+
}
|
|
44
|
+
const [, operator = ">=", amount = "0", unit = "ms"] = match;
|
|
45
|
+
const threshold = Number(amount) * (UNIT_TO_MS[unit.toLowerCase()] ?? 1);
|
|
46
|
+
switch (operator) {
|
|
47
|
+
case "<": {
|
|
48
|
+
return (durationMs) => durationMs < threshold;
|
|
49
|
+
}
|
|
50
|
+
case "<=": {
|
|
51
|
+
return (durationMs) => durationMs <= threshold;
|
|
52
|
+
}
|
|
53
|
+
case "=": {
|
|
54
|
+
return (durationMs) => Math.round(durationMs) === Math.round(threshold);
|
|
55
|
+
}
|
|
56
|
+
case ">": {
|
|
57
|
+
return (durationMs) => durationMs > threshold;
|
|
58
|
+
}
|
|
59
|
+
default: {
|
|
60
|
+
return (durationMs) => durationMs >= threshold;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
var includesIgnoreCase = (haystack, needle) => haystack.toLowerCase().includes(needle.toLowerCase());
|
|
65
|
+
var matchesTerm = (span, key, value) => {
|
|
66
|
+
switch (key) {
|
|
67
|
+
case "account": {
|
|
68
|
+
return span.accountId === value;
|
|
69
|
+
}
|
|
70
|
+
case "client": {
|
|
71
|
+
return span.triggerKind === value.toLowerCase();
|
|
72
|
+
}
|
|
73
|
+
case "dur": {
|
|
74
|
+
const predicate = parseDuration(value);
|
|
75
|
+
return predicate !== void 0 && span.durationMs !== void 0 && predicate(span.durationMs);
|
|
76
|
+
}
|
|
77
|
+
case "op": {
|
|
78
|
+
return includesIgnoreCase(span.operationName, value);
|
|
79
|
+
}
|
|
80
|
+
case "producer": {
|
|
81
|
+
return span.producer.toLowerCase() === value.toLowerCase();
|
|
82
|
+
}
|
|
83
|
+
case "region": {
|
|
84
|
+
return span.region.toLowerCase() === value.toLowerCase();
|
|
85
|
+
}
|
|
86
|
+
case "resource": {
|
|
87
|
+
return includesIgnoreCase(span.resourceName, value) || includesIgnoreCase(span.arn, value);
|
|
88
|
+
}
|
|
89
|
+
case "service": {
|
|
90
|
+
return span.serviceName.toLowerCase() === value.toLowerCase();
|
|
91
|
+
}
|
|
92
|
+
case "status": {
|
|
93
|
+
const status = value.toLowerCase();
|
|
94
|
+
if (status === "error") return span.error;
|
|
95
|
+
if (status === "warning" || status === "warn") return span.warning;
|
|
96
|
+
if (status === "ok") return !span.error && !span.warning;
|
|
97
|
+
return span.statusMessage?.includes(value) ?? false;
|
|
98
|
+
}
|
|
99
|
+
case "trace": {
|
|
100
|
+
return span.traceId.startsWith(value.toLowerCase());
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
var matchesExclusion = (span, parsed) => Object.entries(parsed.excludedTerms).some(([key, values]) => values.some((value) => matchesTerm(span, key, value))) || parsed.excludedWords.some((word) => span.haystack.includes(word));
|
|
105
|
+
var matchesQuery = (span, parsed, textMatches) => {
|
|
106
|
+
if (matchesExclusion(span, parsed)) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
for (const [key, values] of Object.entries(parsed.terms)) {
|
|
110
|
+
if (!values.some((value) => matchesTerm(span, key, value))) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (parsed.words.length > 0 && !textMatches?.has(span.spanId)) {
|
|
115
|
+
return parsed.words.every((word) => span.haystack.includes(word));
|
|
116
|
+
}
|
|
117
|
+
return true;
|
|
16
118
|
};
|
|
17
119
|
|
|
18
120
|
// src/utils/iam-utils.ts
|
|
@@ -71,161 +173,274 @@
|
|
|
71
173
|
}
|
|
72
174
|
}
|
|
73
175
|
|
|
74
|
-
// src/utils/
|
|
75
|
-
var
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
case "iam_explicit_deny":
|
|
82
|
-
case "iam_explicit_warn":
|
|
83
|
-
case "iam_implicit_deny":
|
|
84
|
-
case "iam_implicit_warn": {
|
|
85
|
-
return ErrorLevel.IAM_PERMISSION;
|
|
86
|
-
}
|
|
87
|
-
default: {
|
|
88
|
-
return ErrorLevel.OK;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
176
|
+
// src/utils/span-issues.ts
|
|
177
|
+
var firstText = (...values) => values.find((value) => !!value) ?? void 0;
|
|
178
|
+
var OTEL_STATUS_ERROR = 2;
|
|
179
|
+
var getIamDenials = (span) => span.events.flatMap((event) => {
|
|
180
|
+
if (!event.event_type?.toLowerCase().includes("iam") || typeof event.attributes?.payload !== "string") {
|
|
181
|
+
return [];
|
|
91
182
|
}
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
183
|
+
const permission = parseIAMEvent(event.attributes.payload)?.permission;
|
|
184
|
+
if (permission === "explicitly_denied") return ["explicit"];
|
|
185
|
+
if (permission === "implicitly_denied") return ["implicit"];
|
|
186
|
+
return [];
|
|
187
|
+
});
|
|
188
|
+
var getSpanIssues = (span) => {
|
|
189
|
+
const status = span.operation_status;
|
|
190
|
+
const iamDenied = status === "iam_explicit_deny" || status === "iam_implicit_deny";
|
|
191
|
+
const iamWarned = status === "iam_explicit_warn" || status === "iam_implicit_warn";
|
|
192
|
+
const errorMessages = (span.errors ?? []).filter((item) => item.level === "error");
|
|
193
|
+
const warningMessages = (span.errors ?? []).filter((item) => item.level === "warning");
|
|
194
|
+
const denials = getIamDenials(span);
|
|
195
|
+
const unenforcedDenial = !iamDenied && (denials.length > 0 || span.attributes?.["localstack.has_iam_deny_event"] === true);
|
|
196
|
+
const error = status === "error" || iamDenied || span.status_code === OTEL_STATUS_ERROR || errorMessages.length > 0;
|
|
197
|
+
const warning = iamWarned || warningMessages.length > 0 || unenforcedDenial;
|
|
198
|
+
const denialCount = unenforcedDenial ? Math.max(1, denials.length) : 0;
|
|
199
|
+
return {
|
|
200
|
+
error,
|
|
201
|
+
errorType: error ? firstText(span.status_message, errorMessages[0]?.message) ?? (iamDenied ? "AccessDenied" : "Error") : void 0,
|
|
202
|
+
warning,
|
|
203
|
+
warningCount: warning ? Math.max(1, warningMessages.length + denialCount) : 0,
|
|
204
|
+
warningType: warning ? firstText(warningMessages[0]?.message) ?? (unenforcedDenial ? `IAM ${denials[0] ?? "implicit"} deny` : iamWarned ? "IAM permission warning" : "Warning") : void 0
|
|
205
|
+
};
|
|
105
206
|
};
|
|
106
207
|
|
|
107
|
-
// src/utils/
|
|
108
|
-
var
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
return filters.consumerService.length === 0 && filters.producerService.length === 0 && filters.status.length === 0;
|
|
116
|
-
}
|
|
117
|
-
var collapseStatusValue = (status) => {
|
|
118
|
-
if (status === "iam_explicit_deny" || status === "iam_implicit_deny") return "iam_deny";
|
|
119
|
-
if (status === "iam_explicit_warn" || status === "iam_implicit_warn") return "iam_warn";
|
|
120
|
-
return status;
|
|
121
|
-
};
|
|
122
|
-
function applyFilters(spans, filters) {
|
|
123
|
-
if (isFiltersEmpty(filters)) return spans;
|
|
124
|
-
return spans.filter((span) => {
|
|
125
|
-
if (filters.consumerService.length > 0 && !filters.consumerService.includes(span.service_name)) {
|
|
126
|
-
return false;
|
|
127
|
-
}
|
|
128
|
-
if (filters.producerService.length > 0) {
|
|
129
|
-
const includesExternal = filters.producerService.includes(EXTERNAL_PRODUCER_VALUE);
|
|
130
|
-
const namedServices = filters.producerService.filter((value) => value !== EXTERNAL_PRODUCER_VALUE);
|
|
131
|
-
const matchesExternal = includesExternal && !span.parent_service_name;
|
|
132
|
-
const matchesNamed = namedServices.length > 0 && !!span.parent_service_name && namedServices.includes(span.parent_service_name);
|
|
133
|
-
if (!matchesExternal && !matchesNamed) return false;
|
|
208
|
+
// src/utils/span-parents.ts
|
|
209
|
+
var resolveParents = (spans) => {
|
|
210
|
+
const byId = new Map(spans.map((span) => [span.span_id, span]));
|
|
211
|
+
const parentOf = /* @__PURE__ */ new Map();
|
|
212
|
+
for (const span of spans) {
|
|
213
|
+
const parent = span.parent_span_id === null ? void 0 : byId.get(span.parent_span_id);
|
|
214
|
+
if (parent && parent.span_id !== span.span_id && parent.trace_id === span.trace_id) {
|
|
215
|
+
parentOf.set(span.span_id, parent.span_id);
|
|
134
216
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
217
|
+
}
|
|
218
|
+
const causes = /* @__PURE__ */ new Map();
|
|
219
|
+
const addCause = (spanId, causeId) => {
|
|
220
|
+
const list = causes.get(spanId);
|
|
221
|
+
if (list) list.push(causeId);
|
|
222
|
+
else causes.set(spanId, [causeId]);
|
|
223
|
+
};
|
|
224
|
+
for (const span of spans) {
|
|
225
|
+
for (const causeId of span.outgoing_link_span_ids ?? []) addCause(span.span_id, causeId);
|
|
226
|
+
for (const effectId of span.incoming_link_span_ids ?? []) addCause(effectId, span.span_id);
|
|
227
|
+
}
|
|
228
|
+
const isAncestorOrSelf = (candidateId, spanId) => {
|
|
229
|
+
let current = spanId;
|
|
230
|
+
for (let hops = 0; current !== void 0 && hops <= spans.length; hops++) {
|
|
231
|
+
if (current === candidateId) return true;
|
|
232
|
+
current = parentOf.get(current);
|
|
138
233
|
}
|
|
139
|
-
return
|
|
140
|
-
}
|
|
141
|
-
|
|
234
|
+
return false;
|
|
235
|
+
};
|
|
236
|
+
for (const span of spans) {
|
|
237
|
+
if (parentOf.has(span.span_id)) continue;
|
|
238
|
+
const cause = causes.get(span.span_id)?.find((causeId) => byId.has(causeId) && !isAncestorOrSelf(span.span_id, causeId));
|
|
239
|
+
if (cause !== void 0) parentOf.set(span.span_id, cause);
|
|
240
|
+
}
|
|
241
|
+
return parentOf;
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
// src/utils/trigger.ts
|
|
245
|
+
var getTriggerKind = (span) => span.isInternal ? "internal" : "client";
|
|
142
246
|
|
|
143
247
|
// src/workers/spans-reducer.ts
|
|
144
|
-
var
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
248
|
+
var nanosToMilliseconds = (start, end) => {
|
|
249
|
+
if (!end) return void 0;
|
|
250
|
+
return Number(BigInt(end) - BigInt(start)) / 1e6;
|
|
251
|
+
};
|
|
252
|
+
var toCorpusEntry = (projection, arrivedAt) => {
|
|
253
|
+
const issues = getSpanIssues(projection);
|
|
254
|
+
return {
|
|
255
|
+
accountId: projection.account_id,
|
|
256
|
+
arn: projection.arn,
|
|
257
|
+
arrivedAt,
|
|
258
|
+
durationMs: nanosToMilliseconds(projection.start_time_unix_nano, projection.end_time_unix_nano),
|
|
259
|
+
error: issues.error,
|
|
260
|
+
haystack: [
|
|
261
|
+
projection.service_name,
|
|
262
|
+
projection.operation_name,
|
|
263
|
+
projection.resource_name,
|
|
264
|
+
projection.arn,
|
|
265
|
+
projection.status_message,
|
|
266
|
+
projection.region,
|
|
267
|
+
projection.account_id,
|
|
268
|
+
projection.trace_id,
|
|
269
|
+
projection.span_id,
|
|
270
|
+
...(projection.errors ?? []).map((error) => error.message)
|
|
271
|
+
].filter(Boolean).join("\n").toLowerCase(),
|
|
272
|
+
issues,
|
|
273
|
+
operationName: projection.operation_name,
|
|
274
|
+
parents: projection,
|
|
275
|
+
producer: projection.parent_service_name ?? EXTERNAL_PRODUCER,
|
|
276
|
+
region: projection.region,
|
|
277
|
+
resourceName: projection.resource_name,
|
|
278
|
+
serviceName: projection.service_name,
|
|
279
|
+
spanId: projection.span_id,
|
|
280
|
+
startTime: projection.start_time_unix_nano,
|
|
281
|
+
statusMessage: projection.status_message,
|
|
282
|
+
traceId: projection.trace_id,
|
|
283
|
+
triggerKind: getTriggerKind({ isInternal: projection.is_internal }),
|
|
284
|
+
warning: issues.warning
|
|
285
|
+
};
|
|
286
|
+
};
|
|
287
|
+
var compareByStartTime = (left, right) => {
|
|
288
|
+
if (left.startTime.length !== right.startTime.length) {
|
|
289
|
+
return left.startTime.length - right.startTime.length;
|
|
174
290
|
}
|
|
175
|
-
|
|
291
|
+
return left.startTime.localeCompare(right.startTime, "en");
|
|
176
292
|
};
|
|
177
|
-
var
|
|
178
|
-
|
|
179
|
-
|
|
293
|
+
var countFacet = (counts, value) => {
|
|
294
|
+
counts.set(value, (counts.get(value) ?? 0) + 1);
|
|
295
|
+
};
|
|
296
|
+
var toFacetValues = (counts) => [...counts].map(([value, count]) => ({ count, value })).toSorted((left, right) => left.value.localeCompare(right.value));
|
|
297
|
+
var createSpansReducer = ({ now = Date.now, orphanGraceMs = 0 } = {}) => {
|
|
298
|
+
const corpus = /* @__PURE__ */ new Map();
|
|
299
|
+
let parsedQuery = parseQuery("");
|
|
300
|
+
let textMatches;
|
|
301
|
+
let nextReveal;
|
|
180
302
|
const computeResult = () => {
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
303
|
+
const sorted = [...corpus.values()].toSorted(compareByStartTime);
|
|
304
|
+
const queryActive = !isQueryEmpty(parsedQuery);
|
|
305
|
+
const parentOf = resolveParents(sorted.map((entry) => entry.parents));
|
|
306
|
+
const time = now();
|
|
307
|
+
nextReveal = void 0;
|
|
308
|
+
const waiting = /* @__PURE__ */ new Map();
|
|
309
|
+
const isOrphanWaiting = (root) => {
|
|
310
|
+
if (root.parents.parent_span_id === null && (root.parents.outgoing_link_span_ids?.length ?? 0) === 0) return false;
|
|
311
|
+
const revealAt = root.arrivedAt + orphanGraceMs;
|
|
312
|
+
if (time >= revealAt) return false;
|
|
313
|
+
nextReveal = Math.min(nextReveal ?? revealAt, revealAt);
|
|
314
|
+
return true;
|
|
315
|
+
};
|
|
316
|
+
const isWaiting = (entry) => {
|
|
317
|
+
const chain = [];
|
|
318
|
+
let current = entry;
|
|
319
|
+
let result = false;
|
|
320
|
+
while (current) {
|
|
321
|
+
const known = waiting.get(current.spanId);
|
|
322
|
+
if (known !== void 0) {
|
|
323
|
+
result = known;
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
chain.push(current);
|
|
327
|
+
const parent = corpus.get(parentOf.get(current.spanId) ?? "");
|
|
328
|
+
if (!parent) result = isOrphanWaiting(current);
|
|
329
|
+
current = parent;
|
|
330
|
+
}
|
|
331
|
+
for (const item of chain) waiting.set(item.spanId, result);
|
|
332
|
+
return result;
|
|
333
|
+
};
|
|
334
|
+
const entries = orphanGraceMs > 0 ? sorted.filter((entry) => !isWaiting(entry)) : sorted;
|
|
335
|
+
const children = /* @__PURE__ */ new Map();
|
|
336
|
+
const topLevel = [];
|
|
337
|
+
for (const entry of entries) {
|
|
338
|
+
const parentId = parentOf.get(entry.spanId);
|
|
339
|
+
if (parentId === void 0) {
|
|
340
|
+
topLevel.push(entry);
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
const siblings = children.get(parentId);
|
|
344
|
+
if (siblings) siblings.push(entry);
|
|
345
|
+
else children.set(parentId, [entry]);
|
|
346
|
+
}
|
|
347
|
+
const rows = {};
|
|
348
|
+
const visit = (root) => {
|
|
349
|
+
const stack = [{ entry: root, expanded: false }];
|
|
350
|
+
while (stack.length > 0) {
|
|
351
|
+
const frame = stack.at(-1);
|
|
352
|
+
const kids = children.get(frame.entry.spanId) ?? [];
|
|
353
|
+
if (!frame.expanded) {
|
|
354
|
+
frame.expanded = true;
|
|
355
|
+
for (const kid of kids) {
|
|
356
|
+
if (!(kid.spanId in rows)) stack.push({ entry: kid, expanded: false });
|
|
357
|
+
}
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
stack.pop();
|
|
361
|
+
const matches = !queryActive || matchesQuery(frame.entry, parsedQuery, textMatches);
|
|
362
|
+
const row = {
|
|
363
|
+
...frame.entry.issues,
|
|
364
|
+
downstream: { count: 0, errors: 0, warnings: 0 },
|
|
365
|
+
matches,
|
|
366
|
+
parentServiceName: corpus.get(parentOf.get(frame.entry.spanId) ?? "")?.serviceName,
|
|
367
|
+
rootSpanId: root.spanId,
|
|
368
|
+
treeMatches: matches ? 1 : 0,
|
|
369
|
+
treeSize: 1
|
|
370
|
+
};
|
|
371
|
+
for (const kid of kids) {
|
|
372
|
+
const kidRow = rows[kid.spanId];
|
|
373
|
+
if (!kidRow) continue;
|
|
374
|
+
row.downstream.count += kidRow.downstream.count + 1;
|
|
375
|
+
row.downstream.errors += kidRow.downstream.errors + (kidRow.error ? 1 : 0);
|
|
376
|
+
row.downstream.warnings += kidRow.downstream.warnings + (kidRow.warning ? 1 : 0);
|
|
377
|
+
row.treeMatches += kidRow.treeMatches;
|
|
378
|
+
row.treeSize += kidRow.treeSize;
|
|
379
|
+
}
|
|
380
|
+
rows[frame.entry.spanId] = row;
|
|
381
|
+
}
|
|
382
|
+
return rows[root.spanId]?.treeMatches ?? 0;
|
|
383
|
+
};
|
|
384
|
+
const topLevelIds = [];
|
|
385
|
+
for (const entry of topLevel) {
|
|
386
|
+
if (visit(entry) > 0) {
|
|
387
|
+
topLevelIds.push(entry.spanId);
|
|
191
388
|
}
|
|
192
389
|
}
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
390
|
+
const allIds = [];
|
|
391
|
+
let excludedCount = 0;
|
|
392
|
+
const excluding = hasExclusions(parsedQuery);
|
|
393
|
+
const producers = /* @__PURE__ */ new Map();
|
|
394
|
+
const services = /* @__PURE__ */ new Map();
|
|
395
|
+
const regions = /* @__PURE__ */ new Map();
|
|
396
|
+
const accounts = /* @__PURE__ */ new Map();
|
|
397
|
+
const clients = /* @__PURE__ */ new Map();
|
|
398
|
+
const issues = /* @__PURE__ */ new Map();
|
|
399
|
+
for (const entry of entries) {
|
|
400
|
+
if (rows[entry.spanId]?.matches) allIds.push(entry.spanId);
|
|
401
|
+
if (excluding && matchesExclusion(entry, parsedQuery)) excludedCount++;
|
|
402
|
+
countFacet(producers, entry.producer);
|
|
403
|
+
countFacet(services, entry.serviceName);
|
|
404
|
+
countFacet(regions, entry.region);
|
|
405
|
+
countFacet(accounts, entry.accountId);
|
|
406
|
+
countFacet(clients, entry.triggerKind);
|
|
407
|
+
if (entry.error) countFacet(issues, "error");
|
|
408
|
+
if (entry.warning) countFacet(issues, "warning");
|
|
409
|
+
if (!entry.error && !entry.warning) countFacet(issues, "ok");
|
|
202
410
|
}
|
|
203
411
|
return {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
412
|
+
allIds: allIds.toReversed(),
|
|
413
|
+
excludedCount,
|
|
414
|
+
facets: {
|
|
415
|
+
account: toFacetValues(accounts),
|
|
416
|
+
client: toFacetValues(clients),
|
|
417
|
+
issues: ["error", "warning", "ok"].map((value) => ({ count: issues.get(value) ?? 0, value })),
|
|
418
|
+
producer: toFacetValues(producers),
|
|
419
|
+
region: toFacetValues(regions),
|
|
420
|
+
service: toFacetValues(services)
|
|
421
|
+
},
|
|
422
|
+
queryActive,
|
|
423
|
+
rows,
|
|
424
|
+
topLevelIds: topLevelIds.toReversed(),
|
|
425
|
+
totalCount: entries.length
|
|
211
426
|
};
|
|
212
427
|
};
|
|
213
428
|
return {
|
|
214
|
-
addPage: (
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
for (const projection of sorted) {
|
|
218
|
-
insertSorted(corpus, { ...projection, errorLevel: calculateHighestErrorLevel(projection) }, compareByStartTime, hint);
|
|
429
|
+
addPage: (_direction, projections) => {
|
|
430
|
+
for (const projection of projections) {
|
|
431
|
+
corpus.set(projection.span_id, toCorpusEntry(projection, corpus.get(projection.span_id)?.arrivedAt ?? now()));
|
|
219
432
|
}
|
|
220
433
|
return computeResult();
|
|
221
434
|
},
|
|
222
435
|
clear: () => {
|
|
223
|
-
corpus
|
|
224
|
-
filters = EMPTY_FILTERS;
|
|
436
|
+
corpus.clear();
|
|
225
437
|
return computeResult();
|
|
226
438
|
},
|
|
227
|
-
|
|
228
|
-
|
|
439
|
+
nextReveal: () => nextReveal,
|
|
440
|
+
refresh: computeResult,
|
|
441
|
+
setQuery: (query, matches) => {
|
|
442
|
+
parsedQuery = parseQuery(query);
|
|
443
|
+
textMatches = matches ? new Set(matches) : void 0;
|
|
229
444
|
return computeResult();
|
|
230
445
|
}
|
|
231
446
|
};
|
|
@@ -233,10 +448,35 @@
|
|
|
233
448
|
|
|
234
449
|
// src/workers/spans-worker.worker.ts
|
|
235
450
|
var context = globalThis;
|
|
236
|
-
var reducer = createSpansReducer();
|
|
451
|
+
var reducer = createSpansReducer({ orphanGraceMs: 3e3 });
|
|
452
|
+
var revealTimer;
|
|
453
|
+
var post = (result, seq) => {
|
|
454
|
+
context.postMessage({ result, seq });
|
|
455
|
+
clearTimeout(revealTimer);
|
|
456
|
+
const revealAt = reducer.nextReveal();
|
|
457
|
+
if (revealAt !== void 0) {
|
|
458
|
+
revealTimer = setTimeout(() => {
|
|
459
|
+
post(reducer.refresh(), seq);
|
|
460
|
+
}, Math.max(0, revealAt - Date.now()));
|
|
461
|
+
}
|
|
462
|
+
};
|
|
237
463
|
context.addEventListener("message", (event) => {
|
|
238
464
|
const request = event.data;
|
|
239
|
-
|
|
240
|
-
|
|
465
|
+
let result;
|
|
466
|
+
switch (request.op) {
|
|
467
|
+
case "addPage": {
|
|
468
|
+
result = reducer.addPage(request.direction, request.projections);
|
|
469
|
+
break;
|
|
470
|
+
}
|
|
471
|
+
case "clear": {
|
|
472
|
+
result = reducer.clear();
|
|
473
|
+
break;
|
|
474
|
+
}
|
|
475
|
+
case "setQuery": {
|
|
476
|
+
result = reducer.setQuery(request.query, request.textMatches);
|
|
477
|
+
break;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
post(result, request.seq);
|
|
241
481
|
});
|
|
242
482
|
})();
|