Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
CrabNejonas committed Dec 20, 2023
2 parents 862eaa3 + 3544880 commit ba6a068
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 39 deletions.
149 changes: 123 additions & 26 deletions web-client/src/components/span/span-detail-trace.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { For } from "solid-js";
import { For, JSXElement, Show } from "solid-js";
import { UiSpan } from "~/lib/span/format-spans-for-ui";
import { Span } from "~/lib/connection/monitor";
import {
computeWaterfallStyle,
computeSlices,
} from "~/lib/span/normalize-spans";
import { Popover } from "@kobalte/core";
import { getDetailedTime } from "~/lib/formatters.ts";

export function SpanDetailTrace(props: {
span: UiSpan;
Expand All @@ -15,33 +18,127 @@ export function SpanDetailTrace(props: {
};
}) {
return (
<tr class="even:bg-nearly-invisible cursor-pointer hover:bg-[#ffffff05] even:hover:bg-[#ffffff10]">
<td class="py-1 px-4">{props.span.name}</td>
<td class="py-1 px-4 relative w-[60%]">
<div class="relative w-[90%]">
<div class="bg-gray-800 w-full absolute rounded-sm h-2" />
<div
class="relative h-2"
style={computeWaterfallStyle(
props.span,
props.durations.start,
props.durations.end,
props.durations.shortest,
props.durations.longest
)}
>
{/* Slices is "time slices" as in multiple entry points to a given span */}
<For each={computeSlices(props.span.original)}>
{(slice) => (
<div
class="absolute bg-teal-500 top-0 left-0 h-full"
style={slice}
/>
<Popover.Root>
<Popover.Trigger
class="even:bg-nearly-invisible cursor-pointer hover:bg-[#ffffff05] even:hover:bg-[#ffffff10]"
as="tr"
>
<td class="py-1 px-4">{props.span.name}</td>
<td class="py-1 px-4 relative w-[70%]">
<div class="relative">
<div class="bg-gray-800 w-full absolute rounded-sm h-2" />
<div
class="relative h-2"
style={computeWaterfallStyle(
props.span,
props.durations.start,
props.durations.end,
props.durations.shortest,
props.durations.longest
)}
</For>
>
{/* Slices is "time slices" as in multiple entry points to a given span */}
<For each={computeSlices(props.span.original)}>
{(slice) => <SpanDetailSlice slice={slice} />}
</For>
</div>
</div>
</div>
</td>
</td>
<td class="py-1 px-4">{props.span.time.toFixed(2)}ms</td>
</Popover.Trigger>
<Popover.Portal>
<SpanDetailPopOverContent span={props.span} />
</Popover.Portal>
</Popover.Root>
);
}

function SpanDetailPopOverContent(props: { span: UiSpan }) {
const busy = (span: Span) =>
span.enters.reduce((acc, enter, i) => {
const exit = span.exits[i];

return acc + (exit.timestamp - enter.timestamp);
}, 0);

return (
<Popover.Content
class="z-50 bg-gray-700 rounded-sm drop-shadow-2xl px-2 py-1 focus:outline-none"
style={{ "max-width": "min(calc(100vw - 16px), 380px)" }}
>
<ToolTipContent>
<ToolTipRow title={props.span.name} />
<ToolTipRow title="Busy">
{(busy(props.span.original) / 1e6).toFixed(3)}ms
</ToolTipRow>
<ToolTipRow title="Idle">
{(props.span.time - busy(props.span.original) / 1e6).toFixed(3)}ms
</ToolTipRow>
</ToolTipContent>
</Popover.Content>
);
}

function SpanDetailSlice(props: {
slice: {
entered: number;
exited: number;
threadID: number;
width: number;
marginLeft: number;
};
}) {
return (
<Popover.Root>
<Popover.Trigger
class="absolute bg-teal-500 top-0 left-0 h-full hover:bg-teal-300"
style={{
width: `${props.slice.width}%`,
"margin-left": `${props.slice.marginLeft}%`,
}}
/>
<Popover.Portal>
<Popover.Content
class="z-50 bg-gray-700 rounded-sm drop-shadow-2xl px-2 py-1 focus:outline-none"
style={{
"max-width": "min(calc(100vw - 16px), 380px)",
}}
>
<ToolTipContent>
<ToolTipRow title="Time">
{((props.slice.exited - props.slice.entered) / 1e6).toFixed(3)}
ms
</ToolTipRow>
<ToolTipRow title="Thread">{props.slice.threadID}</ToolTipRow>
<ToolTipRow title="Start">
{getDetailedTime(new Date(props.slice.entered / 1e6))}
</ToolTipRow>
<ToolTipRow title="End">
{getDetailedTime(new Date(props.slice.exited / 1e6))}
</ToolTipRow>
</ToolTipContent>
</Popover.Content>
</Popover.Portal>
</Popover.Root>
);
}

function ToolTipContent(props: { children: JSXElement }) {
return (
<>
<Popover.Arrow />
<table>{props.children}</table>
</>
);
}

function ToolTipRow(props: { title: string; children?: JSXElement }) {
return (
<tr class="grid grid-cols-2 text-left">
<th>{props.title}</th>
<Show when={props.children}>
<td>{props.children}</td>
</Show>
</tr>
);
}
4 changes: 2 additions & 2 deletions web-client/src/lib/connection/monitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ export type Span = {
metadataId: bigint;
fields: Field[];
createdAt: number;
enters: number[];
exits: number[];
enters: { timestamp: number; threadID: number }[];
exits: { timestamp: number; threadID: number }[];
closedAt: number;
duration: number;
};
Expand Down
10 changes: 10 additions & 0 deletions web-client/src/lib/formatters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,13 @@ export function getTime(date: Date) {
second: "2-digit",
}).format(date);
}

export function getDetailedTime(date: Date) {
return Intl.DateTimeFormat("en", {
hour: "2-digit",
minute: "2-digit",
hour12: false,
second: "2-digit",
fractionalSecondDigits: 3,
}).format(date);
}
27 changes: 18 additions & 9 deletions web-client/src/lib/span/normalize-spans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,20 +54,29 @@ export function computeWaterfallStyle(
}

export function computeSlices(span: Span) {
const allExits = span.exits.reduce((acc, e) => acc + e, 0);
const allEnters = span.enters.reduce((acc, e) => acc + e, 0);
const allExits = span.exits.reduce((acc, e) => acc + e.timestamp, 0);
const allEnters = span.enters.reduce((acc, e) => acc + e.timestamp, 0);

const slices = span.enters.map((enter, i) => {
const width = scaleToMax([span.exits[i] - enter], allExits - allEnters)[0];
const offset = scaleNumbers([enter], span.createdAt, span.closedAt)[0];
return span.enters.map((entered, i) => {
const exited = span.exits[i].timestamp;

const width = scaleToMax(
[exited - entered.timestamp],
allExits - allEnters
)[0];
const offset = scaleNumbers(
[entered.timestamp],
span.createdAt,
span.closedAt
)[0];
const marginLeft = offset - (offset * width) / 100;

return {
entered: entered.timestamp,
exited,
threadID: entered.threadID,
width,
marginLeft,
};
});

return slices.map(
(slice) => `width:${slice.width}%;margin-left:${slice.marginLeft}%;`
);
}
10 changes: 8 additions & 2 deletions web-client/src/lib/span/update-spans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ export function updatedSpans(currentSpans: Span[], spanEvents: SpanEvent[]) {
? convertTimestampToNanoseconds(event.event.enterSpan.at)
: -1;
if (span) {
span.enters.push(enteredAt);
span.enters.push({
timestamp: enteredAt,
threadID: Number(event.event.enterSpan.threadId),
});
}

break;
Expand All @@ -43,7 +46,10 @@ export function updatedSpans(currentSpans: Span[], spanEvents: SpanEvent[]) {
? convertTimestampToNanoseconds(event.event.exitSpan.at)
: -1;
if (span) {
span.exits.push(exitedAt);
span.exits.push({
timestamp: exitedAt,
threadID: Number(event.event.exitSpan.threadId),
});
}
break;
}
Expand Down

0 comments on commit ba6a068

Please sign in to comment.