Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/console virtualization #224

Merged
merged 32 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
cc0503e
wip: rough draft calls virtualization
johann-crabnebula Jan 31, 2024
00f8495
wip: basic virtualization for console
johann-crabnebula Feb 5, 2024
73b8503
fix: odd row coloring for console
johann-crabnebula Feb 5, 2024
23cec21
fix: add dynamic height support for autoscroll window
johann-crabnebula Feb 7, 2024
04974e9
fix: update tanstack-virtual
johann-crabnebula Feb 7, 2024
1bddff2
feat: improve console filter to not run when there is no filter selected
johann-crabnebula Feb 7, 2024
1a5e09a
fix: remove unecessary dependencies
johann-crabnebula Feb 7, 2024
057d20f
fix: invert odd row coloring
johann-crabnebula Feb 7, 2024
f21226a
fix: revert changes to calls
johann-crabnebula Feb 7, 2024
54c559a
fix: no scroll when there is no items
johann-crabnebula Feb 7, 2024
0d3bcb5
fix: add z index for toolbar
johann-crabnebula Feb 7, 2024
415c348
fix: set reasonable overscan value
johann-crabnebula Feb 7, 2024
db9ef40
fix: remove unnecessary filter cycles on scroll / prevent filter from…
johann-crabnebula Feb 7, 2024
6fd60b8
fix: improve filtering performance for console tab
johann-crabnebula Feb 7, 2024
91d2bdd
fix: catch rare error with scrolling when list gets voided
johann-crabnebula Feb 7, 2024
3eb0abc
fix: linter error
johann-crabnebula Feb 7, 2024
529361e
fix: remove produce syntax from spans stream to drastically improve p…
johann-crabnebula Feb 8, 2024
949b000
fix: simplify virtualizer code
johann-crabnebula Feb 8, 2024
145a758
Merge branch 'main' into feat/console-virtualization
CrabNejonas Feb 8, 2024
e785fd2
Merge branch 'main' into feat/console-virtualization
CrabNejonas Feb 8, 2024
a4942ed
Merge branch 'main' into feat/console-virtualization
CrabNejonas Feb 8, 2024
9af1fba
fix: update from main
CrabNejonas Feb 8, 2024
3bf6a40
Merge remote-tracking branch 'origin/main' into feat/console-virtuali…
johann-crabnebula Feb 20, 2024
269ec4f
fix: prettier
johann-crabnebula Feb 20, 2024
a3a9091
fix: simplify log-event-entry template
johann-crabnebula Feb 20, 2024
f4eb1c0
fix: prettier
johann-crabnebula Feb 20, 2024
a62152c
fix: revert change of span updates to unblock PR
johann-crabnebula Mar 4, 2024
0d23e8c
fix: remove console log
johann-crabnebula Mar 4, 2024
512b7b2
fix: remove try catch and fix reactivity for auto scroll pane
johann-crabnebula Mar 4, 2024
4d0f08c
fix: cleanup log event entry by moving processing code into a view fu…
johann-crabnebula Mar 4, 2024
58592e6
fix: remove unnecessary type from transport
johann-crabnebula Mar 4, 2024
a2df0f5
fix: log event entry change
johann-crabnebula Mar 4, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions clients/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,27 @@
"devDependencies": {
"@protobuf-ts/protoc": "^2.9.1",
"@sentry/netlify-build-plugin": "^1.1.1",
"@solidjs/testing-library": "^0.8.0",
"@testing-library/jest-dom": "^6.0.0",
"@solidjs/testing-library": "^0.8.5",
"@testing-library/jest-dom": "^6.2.0",
"@types/testing-library__jest-dom": "^5.14.9",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"@typescript-eslint/eslint-plugin": "^6.19.0",
"@typescript-eslint/parser": "^6.19.0",
"@vitest/coverage-v8": "^0.34.6",
"autoprefixer": "^10.4.16",
"cross-env": "^7.0.3",
"eslint": "^8.53.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-solid": "^0.13.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-solid": "^0.13.1",
"jsdom": "^24.0.0",
"postcss": "^8.4.31",
"prettier": "^2.8.8",
"tailwindcss": "^3.3.5",
"typescript": "^5.2.2",
"vite": "^4.5.0",
"vite": "^4.5.2",
"vite-plugin-solid": "^2.7.2",
"vite-plugin-static-copy": "^0.17.0",
"vite-plugin-static-copy": "^0.17.1",
"vite-plugin-wasm": "^3.2.2",
"vitest": "^0.34.0"
"vitest": "^0.34.6"
},
"dependencies": {
"@crabnebula/file-icons": "^0.1.0",
Expand All @@ -52,7 +52,8 @@
"@sentry/browser": "^7.83.0",
"@sentry/vite-plugin": "^2.10.2",
"@solid-primitives/map": "^0.4.8",
"@solidjs/router": "^0.11.0",
"@solidjs/router": "^0.10.5",
"@tanstack/solid-virtual": "^3.0.4",
"clsx": "^2.0.0",
"csp_evaluator": "^1.1.1",
"json-schema-library": "^9.1.2",
Expand Down
48 changes: 32 additions & 16 deletions clients/web/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

93 changes: 93 additions & 0 deletions clients/web/src/components/auto-scroll-pane.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { Show, Accessor, JSXElement, For, createEffect } from "solid-js";
import { createVirtualizer } from "@tanstack/solid-virtual";

type AutoScrollPaneProps<AutoScrollItem> = {
dataStream: AutoScrollItem[];
displayComponent: (props: {
event: AutoScrollItem;
[key: string]: unknown;
}) => JSXElement;
displayOptions: Record<string, unknown>;
shouldAutoScroll: Accessor<boolean>;
fallback: JSXElement;
};

export function AutoScrollPane<AutoScrollItem>(
props: AutoScrollPaneProps<AutoScrollItem>
) {
let logPanel: HTMLDivElement | undefined;

const virtualizer = createVirtualizer({
get count() {
return props.dataStream.length;
},
getScrollElement: () => logPanel ?? null,
estimateSize: () => 28,
overscan: 25,
});

createEffect(() => {
if (props.shouldAutoScroll() && props.dataStream.length > 0) {
// When updating the filter really quick (fast typing for example) it is possible to void the virtual items
// before the scroll can be performed, which will lead to an error
try {
virtualizer.scrollToIndex(virtualizer.options.count - 1);
} catch (e) {
/* intentionally ignore */
}
}
});

return (
<div
ref={logPanel}
class="overflow-y-auto h-[calc(100%-var(--toolbar-height))] relative"
style={{
contain: "strict",
}}
>
<Show when={props.dataStream.length === 0 || !props.dataStream}>
{props.fallback}
</Show>
<div
style={{
height: `${virtualizer.getTotalSize()}px`,
width: "100%",
position: "relative",
}}
>
<ul
class=""
style={{
position: "absolute",
top: 0,
left: 0,
width: "100%",
transform: `translateY(${
virtualizer.getVirtualItems()[0]?.start ?? 0
}px)`,
}}
>
<For each={virtualizer.getVirtualItems()}>
{(virtualRow) => {
return (
<li
data-index={virtualRow.index}
ref={(el) =>
queueMicrotask(() => virtualizer.measureElement(el))
}
>
<props.displayComponent
event={props.dataStream[virtualRow.index]}
{...props.displayOptions}
odd={virtualRow.index & 1}
/>
</li>
);
}}
</For>
</ul>
</div>
</div>
);
}
42 changes: 0 additions & 42 deletions clients/web/src/components/autoscroll-pane.tsx

This file was deleted.

75 changes: 75 additions & 0 deletions clients/web/src/components/console/log-event-entry.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Show, untrack } from "solid-js";
import { getLogMetadata } from "~/lib/console/get-log-metadata";
import { timestampToDate, formatTimestamp } from "~/lib/formatters";
import { getLevelClasses } from "~/lib/console/get-level-classes";
import { useMonitor } from "~/context/monitor-provider";
import { processFieldValue } from "~/lib/span/process-field-value";
import type { LogEvent } from "~/lib/proto/logs";
import clsx from "clsx";
import { getFileNameFromPath } from "~/lib/console/get-file-name-from-path";

export function LogEventEntry(props: {
event: LogEvent;
showTimestamp?: boolean;
odd?: boolean;
}) {
const { monitorData } = useMonitor();
const logEvent = untrack(() => props.event);
const { message, at } = logEvent;
if (!at) return null;

Check warning on line 19 in clients/web/src/components/console/log-event-entry.tsx

View workflow job for this annotation

GitHub Actions / test

Solid components run once, so an early return breaks reactivity. Move the condition inside a JSX element, such as a fragment or <Show />

const metadata = getLogMetadata(monitorData, logEvent);
const timeDate = timestampToDate(at);
const levelStyle = getLevelClasses(metadata?.level);

let target = metadata?.target;
if (target === "log") {
const field = logEvent.fields.find((field) => field.name === "log.target");
if (field) {
target = processFieldValue(field.value);
}
}

return (
<div
class={clsx(
"p-1 font-mono text-sm items-center flex gap-4 group",
levelStyle ? levelStyle : "border-b-gray-800 text-white",
props.odd ? "" : "bg-slate-900"
)}
>
<Show when={props.showTimestamp}>
<time
dateTime={timeDate.toISOString()}
class={clsx(
levelStyle
? levelStyle
: "text-slate-400 group-hover:text-slate-100",
"font-mono text-xs transition-colors"
)}
>
{formatTimestamp(timeDate)}
</time>
</Show>
<span class="group-hover:text-white text-slate-300 transition-colors">
{message}
</span>
<span class="ml-auto flex gap-2 items-center text-xs">
<Show when={target}>
{(logTarget) => (
<span class="text-slate-400 group-hover:text-slate-100 transition-colors">
{logTarget()}
</span>
)}
</Show>
<Show when={metadata?.location?.file}>
{(filePath) => (
<>
{getFileNameFromPath(filePath())}:{metadata?.location?.line ?? ""}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this would be better to handle the non-existing line within getFileNameFromPath and omitting the DOM node completely instead of printing an empty string. So the method returns the line or a falsy value (null, perhaps).

So the signature looks like:

<Show when={getFileNameFromPath(metadata?.location?.file)>
  {line => (<span>{line()</span>)}
</Show>

wdyt?

Copy link
Contributor Author

@johann-crabnebula johann-crabnebula Feb 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will fix this, taking your suggestion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrote a getFileLineFunction that either returns a fully fledged line or null:

import { Location } from "../proto/common";
import { getFileNameFromPath } from "./get-file-name-from-path";

export function getFileLineFromLocation(location: Location | undefined) {
  if (!location || !location.file) return null;

  let line = getFileNameFromPath(location.file);

  if (location.line) line += `:${location.line}`;

  return line;
}

Template now looks like this:

<Show when={getFileLineFromLocation(metadata?.location)}>
  {(line) => <span>{line()}</span>}
</Show>

</>
)}
</Show>
</span>
</div>
);
}
2 changes: 1 addition & 1 deletion clients/web/src/components/toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ type Props = {
};

export const Toolbar = (props: Props) => (
<div class="sticky items-center h-toolbar text-sm text-gray-400 p-2 top-0 flex justify-end border-b gap-4 border-gray-800">
<div class="sticky items-center h-toolbar text-sm text-gray-400 p-2 top-0 flex justify-end border-b gap-4 border-gray-800 z-10">
{props.children}
</div>
);
Loading
Loading