@oneuptime/common 10.0.34 → 10.0.35
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/Models/DatabaseModels/Index.ts +3 -0
- package/Models/DatabaseModels/KubernetesCluster.ts +640 -0
- package/Server/API/IPWhitelistAPI.ts +31 -0
- package/Server/API/Index.ts +2 -0
- package/Server/EnvironmentConfig.ts +2 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1773761409952-MigrationName.ts +137 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1774000000000-MigrationName.ts +80 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Services/KubernetesClusterService.ts +109 -0
- package/Server/Services/UserService.ts +6 -0
- package/Server/Services/UserSessionService.ts +23 -0
- package/Server/Utils/Express.ts +0 -6
- package/Server/Utils/StartServer.ts +7 -3
- package/Server/Utils/VM/VMRunner.ts +3 -0
- package/Types/Icon/IconProp.ts +1 -0
- package/Types/Permission.ts +42 -0
- package/UI/Components/Icon/Icon.tsx +51 -0
- package/UI/Components/LogsViewer/LogsViewer.tsx +2 -0
- package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +25 -0
- package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +85 -53
- package/UI/Components/Navbar/NavBarMenu.tsx +2 -2
- package/Utils/Traces/CriticalPath.ts +348 -0
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js +661 -0
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -0
- package/build/dist/Server/API/IPWhitelistAPI.js +24 -0
- package/build/dist/Server/API/IPWhitelistAPI.js.map +1 -0
- package/build/dist/Server/API/Index.js +2 -0
- package/build/dist/Server/API/Index.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +1 -0
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773761409952-MigrationName.js +52 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773761409952-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1774000000000-MigrationName.js +35 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1774000000000-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterService.js +117 -0
- package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -0
- package/build/dist/Server/Services/UserService.js +5 -0
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Services/UserSessionService.js +20 -0
- package/build/dist/Server/Services/UserSessionService.js.map +1 -1
- package/build/dist/Server/Utils/Express.js +1 -42
- package/build/dist/Server/Utils/Express.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +4 -3
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/VM/VMRunner.js +3 -0
- package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
- package/build/dist/Types/Icon/IconProp.js +1 -0
- package/build/dist/Types/Icon/IconProp.js.map +1 -1
- package/build/dist/Types/Permission.js +36 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +27 -0
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +4 -0
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +50 -43
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBarMenu.js +2 -2
- package/build/dist/Utils/Traces/CriticalPath.js +240 -0
- package/build/dist/Utils/Traces/CriticalPath.js.map +1 -0
- package/package.json +1 -1
|
@@ -208,9 +208,9 @@ const CodeBlock: FunctionComponent<{
|
|
|
208
208
|
(language ? language.charAt(0).toUpperCase() + language.slice(1) : "");
|
|
209
209
|
|
|
210
210
|
return (
|
|
211
|
-
<div className="relative rounded-lg mt-
|
|
211
|
+
<div className="relative rounded-lg mt-3 mb-3 overflow-hidden border border-gray-200 shadow-sm">
|
|
212
212
|
{/* Header bar */}
|
|
213
|
-
<div className="flex items-center justify-between px-3 py-1.5 bg-gray-800
|
|
213
|
+
<div className="flex items-center justify-between px-3 py-1.5 bg-gray-800 border-b border-gray-700">
|
|
214
214
|
<span className="text-[11px] font-medium uppercase tracking-wider text-gray-400 select-none">
|
|
215
215
|
{displayLang}
|
|
216
216
|
</span>
|
|
@@ -261,7 +261,7 @@ const CodeBlock: FunctionComponent<{
|
|
|
261
261
|
children={content}
|
|
262
262
|
language={language}
|
|
263
263
|
style={vscDarkPlus}
|
|
264
|
-
className="!rounded-none !mt-0 !mb-0 !bg-gray-900 !pt-
|
|
264
|
+
className="!rounded-none !mt-0 !mb-0 !bg-gray-900 !pt-3 !pb-3 !px-4 text-sm !border-0"
|
|
265
265
|
codeTagProps={{ className: "font-mono" }}
|
|
266
266
|
/>
|
|
267
267
|
</div>
|
|
@@ -279,7 +279,7 @@ const MarkdownViewer: FunctionComponent<ComponentProps> = (
|
|
|
279
279
|
h1: ({ ...props }: any) => {
|
|
280
280
|
return (
|
|
281
281
|
<h1
|
|
282
|
-
className="text-
|
|
282
|
+
className="text-lg mt-6 mb-3 text-gray-900 font-bold"
|
|
283
283
|
{...props}
|
|
284
284
|
/>
|
|
285
285
|
);
|
|
@@ -287,7 +287,7 @@ const MarkdownViewer: FunctionComponent<ComponentProps> = (
|
|
|
287
287
|
h2: ({ ...props }: any) => {
|
|
288
288
|
return (
|
|
289
289
|
<h2
|
|
290
|
-
className="text-
|
|
290
|
+
className="text-base mt-6 mb-2 text-gray-900 font-semibold"
|
|
291
291
|
{...props}
|
|
292
292
|
/>
|
|
293
293
|
);
|
|
@@ -295,7 +295,7 @@ const MarkdownViewer: FunctionComponent<ComponentProps> = (
|
|
|
295
295
|
h3: ({ ...props }: any) => {
|
|
296
296
|
return (
|
|
297
297
|
<h3
|
|
298
|
-
className="text-
|
|
298
|
+
className="text-base mt-4 mb-2 text-gray-900 font-semibold"
|
|
299
299
|
{...props}
|
|
300
300
|
/>
|
|
301
301
|
);
|
|
@@ -303,7 +303,7 @@ const MarkdownViewer: FunctionComponent<ComponentProps> = (
|
|
|
303
303
|
h4: ({ ...props }: any) => {
|
|
304
304
|
return (
|
|
305
305
|
<h4
|
|
306
|
-
className="text-
|
|
306
|
+
className="text-sm mt-3 mb-2 text-gray-900 font-semibold"
|
|
307
307
|
{...props}
|
|
308
308
|
/>
|
|
309
309
|
);
|
|
@@ -311,7 +311,7 @@ const MarkdownViewer: FunctionComponent<ComponentProps> = (
|
|
|
311
311
|
h5: ({ ...props }: any) => {
|
|
312
312
|
return (
|
|
313
313
|
<h5
|
|
314
|
-
className="text-
|
|
314
|
+
className="text-sm mt-3 mb-1 text-gray-900 font-medium"
|
|
315
315
|
{...props}
|
|
316
316
|
/>
|
|
317
317
|
);
|
|
@@ -319,7 +319,7 @@ const MarkdownViewer: FunctionComponent<ComponentProps> = (
|
|
|
319
319
|
h6: ({ ...props }: any) => {
|
|
320
320
|
return (
|
|
321
321
|
<h6
|
|
322
|
-
className="text-
|
|
322
|
+
className="text-sm mt-2 mb-1 text-gray-700 font-medium"
|
|
323
323
|
{...props}
|
|
324
324
|
/>
|
|
325
325
|
);
|
|
@@ -327,7 +327,7 @@ const MarkdownViewer: FunctionComponent<ComponentProps> = (
|
|
|
327
327
|
p: ({ ...props }: any) => {
|
|
328
328
|
return (
|
|
329
329
|
<p
|
|
330
|
-
className="text-
|
|
330
|
+
className="text-sm mt-2 mb-3 text-gray-700 leading-relaxed"
|
|
331
331
|
{...props}
|
|
332
332
|
/>
|
|
333
333
|
);
|
|
@@ -352,24 +352,24 @@ const MarkdownViewer: FunctionComponent<ComponentProps> = (
|
|
|
352
352
|
return <>{children}</>;
|
|
353
353
|
}
|
|
354
354
|
|
|
355
|
-
|
|
356
|
-
|
|
355
|
+
/*
|
|
356
|
+
* If the child is a custom component (CodeBlock, MermaidDiagram, etc.)
|
|
357
|
+
* rather than a plain HTML element like <code>, skip pre styling.
|
|
358
|
+
* Checking typeof type !== "string" is minification-safe unlike checking type.name.
|
|
359
|
+
*/
|
|
360
|
+
const isCustomComponent: boolean =
|
|
357
361
|
React.isValidElement(children) &&
|
|
358
|
-
|
|
359
|
-
(((children as any).type &&
|
|
360
|
-
((children as any).type.name === "SyntaxHighlighter" ||
|
|
361
|
-
(children as any).type.displayName ===
|
|
362
|
-
"SyntaxHighlighter")) ||
|
|
363
|
-
(children as any).props?.className?.includes(
|
|
364
|
-
"syntax-highlighter",
|
|
365
|
-
));
|
|
362
|
+
typeof (children as any).type !== "string";
|
|
366
363
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
364
|
+
if (isCustomComponent) {
|
|
365
|
+
return <>{children}</>;
|
|
366
|
+
}
|
|
370
367
|
|
|
371
368
|
return (
|
|
372
|
-
<pre
|
|
369
|
+
<pre
|
|
370
|
+
className="bg-gray-900 text-gray-100 mt-3 mb-3 p-3 rounded-md text-sm overflow-x-auto border border-gray-700"
|
|
371
|
+
{...rest}
|
|
372
|
+
>
|
|
373
373
|
{children}
|
|
374
374
|
</pre>
|
|
375
375
|
);
|
|
@@ -377,7 +377,7 @@ const MarkdownViewer: FunctionComponent<ComponentProps> = (
|
|
|
377
377
|
strong: ({ ...props }: any) => {
|
|
378
378
|
return (
|
|
379
379
|
<strong
|
|
380
|
-
className="text-
|
|
380
|
+
className="text-sm font-semibold text-gray-900"
|
|
381
381
|
{...props}
|
|
382
382
|
/>
|
|
383
383
|
);
|
|
@@ -385,56 +385,76 @@ const MarkdownViewer: FunctionComponent<ComponentProps> = (
|
|
|
385
385
|
li: ({ ...props }: any) => {
|
|
386
386
|
return (
|
|
387
387
|
<li
|
|
388
|
-
className="text-
|
|
388
|
+
className="text-sm mt-1 mb-1 text-gray-700 leading-relaxed"
|
|
389
389
|
{...props}
|
|
390
390
|
/>
|
|
391
391
|
);
|
|
392
392
|
},
|
|
393
393
|
ul: ({ ...props }: any) => {
|
|
394
|
-
return <ul className="list-disc pl-
|
|
394
|
+
return <ul className="list-disc pl-6 mt-1 mb-3" {...props} />;
|
|
395
395
|
},
|
|
396
396
|
ol: ({ ...props }: any) => {
|
|
397
|
-
return <ol className="list-decimal pl-
|
|
397
|
+
return <ol className="list-decimal pl-6 mt-1 mb-3" {...props} />;
|
|
398
398
|
},
|
|
399
|
-
blockquote: ({ ...props }: any) => {
|
|
399
|
+
blockquote: ({ children, ...props }: any) => {
|
|
400
400
|
return (
|
|
401
401
|
<blockquote
|
|
402
|
-
className="
|
|
402
|
+
className="rounded-lg border border-amber-200 bg-amber-50/50 my-4 not-italic overflow-hidden"
|
|
403
403
|
{...props}
|
|
404
|
-
|
|
404
|
+
>
|
|
405
|
+
<div className="flex items-start gap-3 px-4 py-3">
|
|
406
|
+
<svg
|
|
407
|
+
className="h-5 w-5 flex-shrink-0 text-amber-500 mt-0.5"
|
|
408
|
+
fill="none"
|
|
409
|
+
stroke="currentColor"
|
|
410
|
+
strokeWidth={2}
|
|
411
|
+
viewBox="0 0 24 24"
|
|
412
|
+
>
|
|
413
|
+
<path
|
|
414
|
+
strokeLinecap="round"
|
|
415
|
+
strokeLinejoin="round"
|
|
416
|
+
d="M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z"
|
|
417
|
+
/>
|
|
418
|
+
</svg>
|
|
419
|
+
<div className="text-sm text-gray-700 leading-relaxed [&>p]:mt-0 [&>p]:mb-0 [&>p>strong:first-child]:text-amber-700 [&>p>strong:first-child]:mr-1">
|
|
420
|
+
{children}
|
|
421
|
+
</div>
|
|
422
|
+
</div>
|
|
423
|
+
</blockquote>
|
|
405
424
|
);
|
|
406
425
|
},
|
|
407
426
|
table: ({ ...props }: any) => {
|
|
408
427
|
return (
|
|
409
|
-
<
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
428
|
+
<div className="overflow-hidden rounded-lg border border-gray-200 mt-4 mb-4 shadow-sm">
|
|
429
|
+
<table
|
|
430
|
+
className="min-w-full table-auto border-collapse text-sm"
|
|
431
|
+
{...props}
|
|
432
|
+
/>
|
|
433
|
+
</div>
|
|
413
434
|
);
|
|
414
435
|
},
|
|
415
436
|
thead: ({ ...props }: any) => {
|
|
416
|
-
return <thead className="bg-gray-
|
|
437
|
+
return <thead className="bg-gray-50" {...props} />;
|
|
417
438
|
},
|
|
418
439
|
tbody: ({ ...props }: any) => {
|
|
419
|
-
return <tbody {...props} />;
|
|
440
|
+
return <tbody className="divide-y divide-gray-100" {...props} />;
|
|
420
441
|
},
|
|
421
442
|
tr: ({ ...props }: any) => {
|
|
422
|
-
return
|
|
443
|
+
return (
|
|
444
|
+
<tr className="hover:bg-gray-50 transition-colors" {...props} />
|
|
445
|
+
);
|
|
423
446
|
},
|
|
424
447
|
th: ({ ...props }: any) => {
|
|
425
448
|
return (
|
|
426
449
|
<th
|
|
427
|
-
className="px-4 py-2 text-left text-
|
|
450
|
+
className="px-4 py-2.5 text-left text-xs font-semibold uppercase tracking-wider text-gray-500 border-b border-gray-200"
|
|
428
451
|
{...props}
|
|
429
452
|
/>
|
|
430
453
|
);
|
|
431
454
|
},
|
|
432
455
|
td: ({ ...props }: any) => {
|
|
433
456
|
return (
|
|
434
|
-
<td
|
|
435
|
-
className="px-4 py-2 text-sm text-gray-700 border border-gray-300"
|
|
436
|
-
{...props}
|
|
437
|
-
/>
|
|
457
|
+
<td className="px-4 py-2.5 text-sm text-gray-700" {...props} />
|
|
438
458
|
);
|
|
439
459
|
},
|
|
440
460
|
hr: ({ ...props }: any) => {
|
|
@@ -457,19 +477,31 @@ const MarkdownViewer: FunctionComponent<ComponentProps> = (
|
|
|
457
477
|
return <MermaidDiagram chart={content} />;
|
|
458
478
|
}
|
|
459
479
|
|
|
460
|
-
const
|
|
461
|
-
|
|
462
|
-
|
|
480
|
+
const isMultiline: boolean = content.includes("\n");
|
|
481
|
+
const hasLanguage: boolean = Boolean(
|
|
482
|
+
match &&
|
|
463
483
|
match?.filter((item: string) => {
|
|
464
484
|
return item.includes("language-");
|
|
465
|
-
}).length > 0
|
|
466
|
-
|
|
467
|
-
: "text-sm px-2 py-1 bg-gray-200 rounded text-gray-900 font-mono";
|
|
485
|
+
}).length > 0,
|
|
486
|
+
);
|
|
468
487
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
488
|
+
// Multiline code blocks (with or without language) get the full CodeBlock treatment
|
|
489
|
+
if (hasLanguage || isMultiline) {
|
|
490
|
+
return (
|
|
491
|
+
<CodeBlock
|
|
492
|
+
language={match ? match[1]! : "text"}
|
|
493
|
+
content={content}
|
|
494
|
+
rest={rest}
|
|
495
|
+
/>
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Inline code
|
|
500
|
+
return (
|
|
501
|
+
<code
|
|
502
|
+
className="text-xs px-1.5 py-0.5 bg-gray-100 border border-gray-200 rounded text-gray-800 font-mono"
|
|
503
|
+
{...rest}
|
|
504
|
+
>
|
|
473
505
|
{children}
|
|
474
506
|
</code>
|
|
475
507
|
);
|
|
@@ -39,7 +39,7 @@ const NavBarMenu: FunctionComponent<ComponentProps> = (
|
|
|
39
39
|
);
|
|
40
40
|
|
|
41
41
|
return (
|
|
42
|
-
<div className="absolute left-0 z-
|
|
42
|
+
<div className="absolute left-0 z-50 mt-8 w-screen max-w-5xl transform px-2 sm:px-0">
|
|
43
43
|
<div className="overflow-hidden rounded-2xl shadow-xl ring-1 ring-black ring-opacity-5 bg-white">
|
|
44
44
|
{/* Sections */}
|
|
45
45
|
<div className="p-6">
|
|
@@ -127,7 +127,7 @@ const NavBarMenu: FunctionComponent<ComponentProps> = (
|
|
|
127
127
|
|
|
128
128
|
return (
|
|
129
129
|
<div
|
|
130
|
-
className={`absolute left-1/2 z-
|
|
130
|
+
className={`absolute left-1/2 z-50 mt-8 w-screen max-w-md -translate-x-1/2 transform px-2 sm:px-0 ${maxWidthClass}`}
|
|
131
131
|
>
|
|
132
132
|
<div className="overflow-hidden rounded-2xl shadow-xl ring-1 ring-black ring-opacity-5 bg-white">
|
|
133
133
|
{/* Menu Items */}
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Critical Path Analysis for distributed traces
|
|
3
|
+
* Computes self-time, critical path, and bottleneck identification
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface SpanData {
|
|
7
|
+
spanId: string;
|
|
8
|
+
parentSpanId: string | undefined;
|
|
9
|
+
startTimeUnixNano: number;
|
|
10
|
+
endTimeUnixNano: number;
|
|
11
|
+
durationUnixNano: number;
|
|
12
|
+
serviceId: string | undefined;
|
|
13
|
+
name: string | undefined;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface SpanSelfTime {
|
|
17
|
+
spanId: string;
|
|
18
|
+
selfTimeUnixNano: number;
|
|
19
|
+
childTimeUnixNano: number;
|
|
20
|
+
totalTimeUnixNano: number;
|
|
21
|
+
selfTimePercent: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface CriticalPathResult {
|
|
25
|
+
criticalPathSpanIds: string[];
|
|
26
|
+
totalTraceDurationUnixNano: number;
|
|
27
|
+
criticalPathDurationUnixNano: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface ServiceBreakdown {
|
|
31
|
+
serviceId: string;
|
|
32
|
+
totalDurationUnixNano: number;
|
|
33
|
+
selfTimeUnixNano: number;
|
|
34
|
+
spanCount: number;
|
|
35
|
+
percentOfTrace: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default class CriticalPathUtil {
|
|
39
|
+
/**
|
|
40
|
+
* Compute self-time for each span.
|
|
41
|
+
* Self-time = span duration minus the time covered by direct children,
|
|
42
|
+
* accounting for overlapping children.
|
|
43
|
+
*/
|
|
44
|
+
public static computeSelfTimes(spans: SpanData[]): Map<string, SpanSelfTime> {
|
|
45
|
+
const result: Map<string, SpanSelfTime> = new Map();
|
|
46
|
+
|
|
47
|
+
// Build parent -> children index
|
|
48
|
+
const childrenMap: Map<string, SpanData[]> = new Map();
|
|
49
|
+
for (const span of spans) {
|
|
50
|
+
if (span.parentSpanId) {
|
|
51
|
+
const children: SpanData[] = childrenMap.get(span.parentSpanId) || [];
|
|
52
|
+
children.push(span);
|
|
53
|
+
childrenMap.set(span.parentSpanId, children);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
for (const span of spans) {
|
|
58
|
+
const children: SpanData[] = childrenMap.get(span.spanId) || [];
|
|
59
|
+
const childTimeUnixNano: number =
|
|
60
|
+
CriticalPathUtil.computeMergedChildDuration(
|
|
61
|
+
children,
|
|
62
|
+
span.startTimeUnixNano,
|
|
63
|
+
span.endTimeUnixNano,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const selfTimeUnixNano: number = Math.max(
|
|
67
|
+
0,
|
|
68
|
+
span.durationUnixNano - childTimeUnixNano,
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
result.set(span.spanId, {
|
|
72
|
+
spanId: span.spanId,
|
|
73
|
+
selfTimeUnixNano,
|
|
74
|
+
childTimeUnixNano,
|
|
75
|
+
totalTimeUnixNano: span.durationUnixNano,
|
|
76
|
+
selfTimePercent:
|
|
77
|
+
span.durationUnixNano > 0
|
|
78
|
+
? (selfTimeUnixNano / span.durationUnixNano) * 100
|
|
79
|
+
: 0,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Compute the merged duration of child spans within the parent's time window.
|
|
88
|
+
* Handles overlapping children by merging intervals.
|
|
89
|
+
*/
|
|
90
|
+
private static computeMergedChildDuration(
|
|
91
|
+
children: SpanData[],
|
|
92
|
+
parentStart: number,
|
|
93
|
+
parentEnd: number,
|
|
94
|
+
): number {
|
|
95
|
+
if (children.length === 0) {
|
|
96
|
+
return 0;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Clamp children to parent boundaries and create intervals
|
|
100
|
+
const intervals: Array<{ start: number; end: number }> = children
|
|
101
|
+
.map((child: SpanData) => {
|
|
102
|
+
return {
|
|
103
|
+
start: Math.max(child.startTimeUnixNano, parentStart),
|
|
104
|
+
end: Math.min(child.endTimeUnixNano, parentEnd),
|
|
105
|
+
};
|
|
106
|
+
})
|
|
107
|
+
.filter((interval: { start: number; end: number }) => {
|
|
108
|
+
return interval.end > interval.start;
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
if (intervals.length === 0) {
|
|
112
|
+
return 0;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Sort by start time
|
|
116
|
+
intervals.sort(
|
|
117
|
+
(
|
|
118
|
+
a: { start: number; end: number },
|
|
119
|
+
b: { start: number; end: number },
|
|
120
|
+
) => {
|
|
121
|
+
return a.start - b.start;
|
|
122
|
+
},
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
// Merge overlapping intervals
|
|
126
|
+
let mergedDuration: number = 0;
|
|
127
|
+
let currentStart: number = intervals[0]!.start;
|
|
128
|
+
let currentEnd: number = intervals[0]!.end;
|
|
129
|
+
|
|
130
|
+
for (let i: number = 1; i < intervals.length; i++) {
|
|
131
|
+
const interval: { start: number; end: number } = intervals[i]!;
|
|
132
|
+
if (interval.start <= currentEnd) {
|
|
133
|
+
// Overlapping - extend
|
|
134
|
+
currentEnd = Math.max(currentEnd, interval.end);
|
|
135
|
+
} else {
|
|
136
|
+
// Non-overlapping - add previous and start new
|
|
137
|
+
mergedDuration += currentEnd - currentStart;
|
|
138
|
+
currentStart = interval.start;
|
|
139
|
+
currentEnd = interval.end;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
mergedDuration += currentEnd - currentStart;
|
|
144
|
+
|
|
145
|
+
return mergedDuration;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Compute the critical path through the trace.
|
|
150
|
+
* The critical path is the longest sequential chain of spans,
|
|
151
|
+
* accounting for parallelism (parallel children don't add to the critical path together).
|
|
152
|
+
*/
|
|
153
|
+
public static computeCriticalPath(spans: SpanData[]): CriticalPathResult {
|
|
154
|
+
if (spans.length === 0) {
|
|
155
|
+
return {
|
|
156
|
+
criticalPathSpanIds: [],
|
|
157
|
+
totalTraceDurationUnixNano: 0,
|
|
158
|
+
criticalPathDurationUnixNano: 0,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Find total trace duration
|
|
163
|
+
let traceStart: number = spans[0]!.startTimeUnixNano;
|
|
164
|
+
let traceEnd: number = spans[0]!.endTimeUnixNano;
|
|
165
|
+
for (const span of spans) {
|
|
166
|
+
if (span.startTimeUnixNano < traceStart) {
|
|
167
|
+
traceStart = span.startTimeUnixNano;
|
|
168
|
+
}
|
|
169
|
+
if (span.endTimeUnixNano > traceEnd) {
|
|
170
|
+
traceEnd = span.endTimeUnixNano;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Build parent -> children index
|
|
175
|
+
const childrenMap: Map<string, SpanData[]> = new Map();
|
|
176
|
+
const spanMap: Map<string, SpanData> = new Map();
|
|
177
|
+
const allSpanIds: Set<string> = new Set();
|
|
178
|
+
|
|
179
|
+
for (const span of spans) {
|
|
180
|
+
spanMap.set(span.spanId, span);
|
|
181
|
+
allSpanIds.add(span.spanId);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
for (const span of spans) {
|
|
185
|
+
if (span.parentSpanId && allSpanIds.has(span.parentSpanId)) {
|
|
186
|
+
const children: SpanData[] = childrenMap.get(span.parentSpanId) || [];
|
|
187
|
+
children.push(span);
|
|
188
|
+
childrenMap.set(span.parentSpanId, children);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Find root spans
|
|
193
|
+
const rootSpans: SpanData[] = spans.filter((span: SpanData) => {
|
|
194
|
+
return !span.parentSpanId || !allSpanIds.has(span.parentSpanId);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
if (rootSpans.length === 0) {
|
|
198
|
+
return {
|
|
199
|
+
criticalPathSpanIds: [],
|
|
200
|
+
totalTraceDurationUnixNano: traceEnd - traceStart,
|
|
201
|
+
criticalPathDurationUnixNano: 0,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// For each span, compute the critical path weight (longest path through this span and descendants)
|
|
206
|
+
const criticalPathCache: Map<string, { weight: number; path: string[] }> =
|
|
207
|
+
new Map();
|
|
208
|
+
|
|
209
|
+
const computeWeight: (spanId: string) => {
|
|
210
|
+
weight: number;
|
|
211
|
+
path: string[];
|
|
212
|
+
} = (spanId: string): { weight: number; path: string[] } => {
|
|
213
|
+
const cached: { weight: number; path: string[] } | undefined =
|
|
214
|
+
criticalPathCache.get(spanId);
|
|
215
|
+
if (cached) {
|
|
216
|
+
return cached;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const span: SpanData | undefined = spanMap.get(spanId);
|
|
220
|
+
if (!span) {
|
|
221
|
+
return { weight: 0, path: [] };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const children: SpanData[] = childrenMap.get(spanId) || [];
|
|
225
|
+
|
|
226
|
+
if (children.length === 0) {
|
|
227
|
+
const result: { weight: number; path: string[] } = {
|
|
228
|
+
weight: span.durationUnixNano,
|
|
229
|
+
path: [spanId],
|
|
230
|
+
};
|
|
231
|
+
criticalPathCache.set(spanId, result);
|
|
232
|
+
return result;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Find the child with the longest critical path
|
|
236
|
+
let maxChildWeight: number = 0;
|
|
237
|
+
let maxChildPath: string[] = [];
|
|
238
|
+
|
|
239
|
+
for (const child of children) {
|
|
240
|
+
const childResult: { weight: number; path: string[] } = computeWeight(
|
|
241
|
+
child.spanId,
|
|
242
|
+
);
|
|
243
|
+
if (childResult.weight > maxChildWeight) {
|
|
244
|
+
maxChildWeight = childResult.weight;
|
|
245
|
+
maxChildPath = childResult.path;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Self time contribution
|
|
250
|
+
const selfTimes: Map<string, SpanSelfTime> =
|
|
251
|
+
CriticalPathUtil.computeSelfTimes([span, ...children]);
|
|
252
|
+
const selfTime: SpanSelfTime | undefined = selfTimes.get(spanId);
|
|
253
|
+
const selfTimeValue: number = selfTime ? selfTime.selfTimeUnixNano : 0;
|
|
254
|
+
|
|
255
|
+
const result: { weight: number; path: string[] } = {
|
|
256
|
+
weight: selfTimeValue + maxChildWeight,
|
|
257
|
+
path: [spanId, ...maxChildPath],
|
|
258
|
+
};
|
|
259
|
+
criticalPathCache.set(spanId, result);
|
|
260
|
+
return result;
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
// Find the root with the longest critical path
|
|
264
|
+
let maxWeight: number = 0;
|
|
265
|
+
let criticalPath: string[] = [];
|
|
266
|
+
|
|
267
|
+
for (const rootSpan of rootSpans) {
|
|
268
|
+
const result: { weight: number; path: string[] } = computeWeight(
|
|
269
|
+
rootSpan.spanId,
|
|
270
|
+
);
|
|
271
|
+
if (result.weight > maxWeight) {
|
|
272
|
+
maxWeight = result.weight;
|
|
273
|
+
criticalPath = result.path;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return {
|
|
278
|
+
criticalPathSpanIds: criticalPath,
|
|
279
|
+
totalTraceDurationUnixNano: traceEnd - traceStart,
|
|
280
|
+
criticalPathDurationUnixNano: maxWeight,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Compute latency breakdown by service.
|
|
286
|
+
*/
|
|
287
|
+
public static computeServiceBreakdown(spans: SpanData[]): ServiceBreakdown[] {
|
|
288
|
+
const selfTimes: Map<string, SpanSelfTime> =
|
|
289
|
+
CriticalPathUtil.computeSelfTimes(spans);
|
|
290
|
+
|
|
291
|
+
// Find total trace duration
|
|
292
|
+
let traceStart: number = Number.MAX_SAFE_INTEGER;
|
|
293
|
+
let traceEnd: number = 0;
|
|
294
|
+
for (const span of spans) {
|
|
295
|
+
if (span.startTimeUnixNano < traceStart) {
|
|
296
|
+
traceStart = span.startTimeUnixNano;
|
|
297
|
+
}
|
|
298
|
+
if (span.endTimeUnixNano > traceEnd) {
|
|
299
|
+
traceEnd = span.endTimeUnixNano;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
const totalDuration: number = traceEnd - traceStart;
|
|
303
|
+
|
|
304
|
+
// Aggregate by service
|
|
305
|
+
const serviceMap: Map<
|
|
306
|
+
string,
|
|
307
|
+
{ totalDuration: number; selfTime: number; spanCount: number }
|
|
308
|
+
> = new Map();
|
|
309
|
+
|
|
310
|
+
for (const span of spans) {
|
|
311
|
+
const serviceId: string = span.serviceId || "unknown";
|
|
312
|
+
const entry: {
|
|
313
|
+
totalDuration: number;
|
|
314
|
+
selfTime: number;
|
|
315
|
+
spanCount: number;
|
|
316
|
+
} = serviceMap.get(serviceId) || {
|
|
317
|
+
totalDuration: 0,
|
|
318
|
+
selfTime: 0,
|
|
319
|
+
spanCount: 0,
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
entry.totalDuration += span.durationUnixNano;
|
|
323
|
+
const selfTime: SpanSelfTime | undefined = selfTimes.get(span.spanId);
|
|
324
|
+
entry.selfTime += selfTime ? selfTime.selfTimeUnixNano : 0;
|
|
325
|
+
entry.spanCount += 1;
|
|
326
|
+
serviceMap.set(serviceId, entry);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const result: ServiceBreakdown[] = [];
|
|
330
|
+
for (const [serviceId, data] of serviceMap.entries()) {
|
|
331
|
+
result.push({
|
|
332
|
+
serviceId,
|
|
333
|
+
totalDurationUnixNano: data.totalDuration,
|
|
334
|
+
selfTimeUnixNano: data.selfTime,
|
|
335
|
+
spanCount: data.spanCount,
|
|
336
|
+
percentOfTrace:
|
|
337
|
+
totalDuration > 0 ? (data.selfTime / totalDuration) * 100 : 0,
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Sort by self-time descending (biggest contributors first)
|
|
342
|
+
result.sort((a: ServiceBreakdown, b: ServiceBreakdown) => {
|
|
343
|
+
return b.selfTimeUnixNano - a.selfTimeUnixNano;
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
return result;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import AcmeCertificate from "./AcmeCertificate";
|
|
2
2
|
import AcmeChallenge from "./AcmeChallenge";
|
|
3
|
+
import KubernetesCluster from "./KubernetesCluster";
|
|
3
4
|
// API Keys
|
|
4
5
|
import ApiKey from "./ApiKey";
|
|
5
6
|
import ApiKeyPermission from "./ApiKeyPermission";
|
|
@@ -430,6 +431,7 @@ const AllModelTypes = [
|
|
|
430
431
|
ProjectSCIM,
|
|
431
432
|
ProjectSCIMLog,
|
|
432
433
|
StatusPageSCIMLog,
|
|
434
|
+
KubernetesCluster,
|
|
433
435
|
];
|
|
434
436
|
const modelTypeMap = {};
|
|
435
437
|
export const getModelTypeByName = (tableName) => {
|