Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ export function EngineContractSubscriptions({
return (
<div>
{/* Header */}
<div className="flex flex-row gap-2 justify-between mb-4">
<div className="flex flex-col md:flex-row gap-4 justify-between mb-4">
<div>
<h2 className="text-2xl font-semibold mb-1">
<h2 className="text-2xl font-semibold mb-1 tracking-tight">
Contract Subscriptions
</h2>
<p className="text-muted-foreground text-sm">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
"use client";

import { Card } from "chakra/card";
import { Heading } from "chakra/heading";
import { Text } from "chakra/text";
import { ChartAreaIcon, InfoIcon } from "lucide-react";
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { CircleAlertIcon } from "lucide-react";
import { GenericLoadingPage } from "@/components/blocks/skeletons/GenericLoadingPage";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { UnderlineLink } from "@/components/ui/UnderlineLink";
import {
type EngineInstance,
Expand All @@ -15,19 +13,17 @@ import { ErrorRate } from "./ErrorRate";
import { Healthcheck } from "./Healthcheck";
import { StatusCodes } from "./StatusCodes";

interface EngineStatusProps {
instance: EngineInstance;
teamSlug: string;
projectSlug: string;
authToken: string;
}

export const EngineSystemMetrics: React.FC<EngineStatusProps> = ({
export function EngineSystemMetrics({
instance,
teamSlug,
projectSlug,
authToken,
}) => {
}: {
instance: EngineInstance;
teamSlug: string;
projectSlug: string;
authToken: string;
}) {
const systemMetricsQuery = useEngineSystemMetrics(
instance.id,
teamSlug,
Expand All @@ -39,58 +35,54 @@ export const EngineSystemMetrics: React.FC<EngineStatusProps> = ({
pollInterval: 10_000,
});

let systemMetricsPanel = <Spinner className="h-4 w-4" />;
let systemMetricsPanel = <GenericLoadingPage />;
if (!systemMetricsQuery.data || systemMetricsQuery.isError) {
systemMetricsPanel = (
<Card p={8}>
<div className="flex flex-col gap-4">
<div className="flex flex-row items-center gap-2">
<InfoIcon className="size-4" />
<Heading size="title.xs">
System metrics are unavailable for self-hosted Engine.
</Heading>
</div>
<Text>
<div>
<h2 className="text-2xl font-semibold tracking-tight mb-4">
System Metrics
</h2>

<Alert variant="warning">
<CircleAlertIcon className="size-5" />
<AlertTitle>
System metrics are not available for self-hosted Engine
</AlertTitle>
<AlertDescription className="text-muted-foreground text-sm">
Upgrade to a{" "}
<UnderlineLink
color="blue.500"
href={`/team/${teamSlug}/${projectSlug}/engine/dedicated/create`}
rel="noopener noreferrer"
target="_blank"
>
Engine instance managed by thirdweb
</UnderlineLink>{" "}
to view these metrics.
</Text>
</div>
</Card>
to view system metrics
</AlertDescription>
</Alert>
</div>
);
} else {
systemMetricsPanel = (
<Card p={16}>
<div className="flex flex-col gap-4">
<div className="-mb-2 flex flex-row items-center gap-2">
<ChartAreaIcon className="size-4" />
<Heading size="title.md">System Metrics</Heading>
</div>
<div>
<div className="flex items-center gap-3 mb-4">
<h2 className="text-2xl font-semibold tracking-tight">
System Metrics
</h2>
<Healthcheck instance={instance} />
</div>

<div className="mt-10 grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
<Healthcheck instance={instance} />
</div>
<div className="space-y-8">
<StatusCodes
datapoints={systemMetricsQuery.data.result.statusCodes}
/>
<ErrorRate datapoints={systemMetricsQuery.data.result.errorRate} />
</div>
</Card>
</div>
);
}

let queueMetricsPanel = (
<div className="flex min-h-[200px] items-center justify-center rounded-lg border border-border">
<Spinner className="size-6" />
</div>
);
let queueMetricsPanel = <GenericLoadingPage />;

if (
!queueMetricsQuery.isPending &&
Expand All @@ -108,43 +100,39 @@ export const EngineSystemMetrics: React.FC<EngineStatusProps> = ({
const msToMine = queueMetricsQuery.data.result.latency?.msToMine;

queueMetricsPanel = (
<Card p={8}>
<div className="flex flex-col gap-6">
<div className="flex flex-row items-center gap-2">
<Heading size="title.md">Queue Metrics</Heading>
</div>
<div className="bg-card p-4 rounded-lg border lg:p-6">
<h2 className="text-lg tracking-tight font-semibold mb-3">
Queue Metrics
</h2>

<div className="flex flex-col gap-6 lg:flex-row lg:gap-12">
<div className="flex-col gap-y-4">
<h2 className="font-semibold"> Queued</h2>
<p className="text-muted-foreground">{numQueued}</p>
</div>
<div className="flex-col gap-y-4">
<h2 className="font-semibold">Pending</h2>
<p className="text-muted-foreground">{numPending}</p>
</div>
<div className="space-y-2">
<MetricRow label="Queued" value={numQueued} />
<MetricRow label="Pending" value={numPending} />

{msToSend && (
<div className="flex-col gap-y-4">
<h2 className="font-semibold">Time to send</h2>
<p className="text-muted-foreground">
{msToSend && (
<MetricRow
label="Time to send"
value={
<span>
p50 {(msToSend.p50 / 1000).toFixed(2)}s, p90{" "}
{(msToSend.p90 / 1000).toFixed(2)}s
</p>
</div>
)}
{msToMine && (
<div className="flex-col gap-y-4">
<h2 className="font-semibold">Time to mine</h2>
<p className="text-muted-foreground">
</span>
}
/>
)}
{msToMine && (
<MetricRow
label="Time to mine"
value={
<span>
p50 {(msToMine.p50 / 1000).toFixed(2)}s, p90{" "}
{(msToMine.p90 / 1000).toFixed(2)}s
</p>
</div>
)}
</div>
</span>
}
/>
)}
</div>
</Card>
</div>
);
}

Expand All @@ -154,4 +142,19 @@ export const EngineSystemMetrics: React.FC<EngineStatusProps> = ({
{queueMetricsPanel}
</div>
);
};
}

function MetricRow({
label,
value,
}: {
label: string;
value: React.ReactNode;
}) {
return (
<div className="grid grid-cols-2 w-[400px]">
<h3 className="text-sm font-medium">{label}</h3>
<p className="text-sm text-foreground">{value}</p>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"use client";

import { PrimaryInfoItem } from "app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item";
import { Skeleton } from "@/components/ui/skeleton";
import { ToolTipLabel } from "@/components/ui/tooltip";
import { type EngineInstance, useEngineSystemHealth } from "@/hooks/useEngine";
Expand All @@ -9,35 +8,25 @@ export function Healthcheck({ instance }: { instance: EngineInstance }) {
const query = useEngineSystemHealth(instance.url, 5_000);

return (
<>
{/* Engine Reachability */}
<PrimaryInfoItem title="Engine" titleIcon={<PulseDot />}>
<div className="flex items-center gap-1">
{query.isSuccess ? (
<ToolTipLabel label="Working">
<div className="text-lg text-success-text">Reachable</div>
</ToolTipLabel>
) : query.isError ? (
<ToolTipLabel label="Not Working">
<div className="text-destructive-text text-lg">Not Reachable</div>
</ToolTipLabel>
) : (
<div className="flex h-[28px] w-[70px] py-1">
<Skeleton className="h-full w-full" />
</div>
)}
</div>
</PrimaryInfoItem>
</>
<div className="rounded-full bg-card px-3 py-2 border flex items-center gap-2">
<PulseDot />
{query.isSuccess ? (
<div className="text-sm">Reachable</div>
) : query.isError ? (
<div className="text-destructive-text text-sm">Not Reachable</div>
) : (
<Skeleton className="h-4 w-16" />
)}
</div>
);
}

function PulseDot() {
return (
<ToolTipLabel label="Live Data">
<span className="relative flex size-3">
<span className="relative flex size-2">
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-sky-400 opacity-75" />
<span className="relative inline-flex size-3 rounded-full bg-primary" />
<span className="relative inline-flex size-2 rounded-full bg-primary" />
</span>
</ToolTipLabel>
);
Expand Down
Loading