From eefc1ee0d7e95dd0e96e95906e1bf967145d7ad6 Mon Sep 17 00:00:00 2001 From: Athou Date: Sun, 14 Aug 2022 18:15:40 +0200 Subject: [PATCH] add metrics page --- commafeed-client/src/App.tsx | 2 + commafeed-client/src/app/client.ts | 2 + commafeed-client/src/app/slices/redirect.ts | 3 + commafeed-client/src/app/types.ts | 76 +++++++++++++++---- .../src/components/header/ProfileMenu.tsx | 13 +++- .../src/components/metrics/Gauge.tsx | 9 +++ .../src/components/metrics/Meter.tsx | 19 +++++ .../metrics/MetricAccordionItem.tsx | 22 ++++++ .../src/components/metrics/Timer.tsx | 19 +++++ .../src/pages/admin/MetricsPage.tsx | 75 ++++++++++++++++++ 10 files changed, 222 insertions(+), 18 deletions(-) create mode 100644 commafeed-client/src/components/metrics/Gauge.tsx create mode 100644 commafeed-client/src/components/metrics/Meter.tsx create mode 100644 commafeed-client/src/components/metrics/MetricAccordionItem.tsx create mode 100644 commafeed-client/src/components/metrics/Timer.tsx create mode 100644 commafeed-client/src/pages/admin/MetricsPage.tsx diff --git a/commafeed-client/src/App.tsx b/commafeed-client/src/App.tsx index f55c1872..61b71733 100644 --- a/commafeed-client/src/App.tsx +++ b/commafeed-client/src/App.tsx @@ -14,6 +14,7 @@ import { Header } from "components/header/Header" import { Tree } from "components/sidebar/Tree" import { useI18n } from "i18n" import { AdminUsersPage } from "pages/admin/AdminUsersPage" +import { MetricsPage } from "pages/admin/MetricsPage" import { AddPage } from "pages/app/AddPage" import { CategoryDetailsPage } from "pages/app/CategoryDetailsPage" import { FeedDetailsPage } from "pages/app/FeedDetailsPage" @@ -78,6 +79,7 @@ function AppRoutes() { } /> } /> + } /> } /> diff --git a/commafeed-client/src/app/client.ts b/commafeed-client/src/app/client.ts index 9d766638..0504b026 100644 --- a/commafeed-client/src/app/client.ts +++ b/commafeed-client/src/app/client.ts @@ -12,6 +12,7 @@ import { IDRequest, LoginRequest, MarkRequest, + Metrics, PasswordResetRequest, ProfileModificationRequest, RegistrationRequest, @@ -79,6 +80,7 @@ export const client = { getAllUsers: () => axiosInstance.get("admin/user/getAll"), saveUser: (req: UserModel) => axiosInstance.post("admin/user/save", req), deleteUser: (req: IDRequest) => axiosInstance.post("admin/user/delete", req), + getMetrics: () => axiosInstance.get("admin/metrics"), }, } diff --git a/commafeed-client/src/app/slices/redirect.ts b/commafeed-client/src/app/slices/redirect.ts index fc41da38..c138d470 100644 --- a/commafeed-client/src/app/slices/redirect.ts +++ b/commafeed-client/src/app/slices/redirect.ts @@ -37,6 +37,9 @@ export const redirectToSettings = createAsyncThunk("redirect/settings", (_, thun export const redirectToAdminUsers = createAsyncThunk("redirect/admin/users", (_, thunkApi) => thunkApi.dispatch(redirectTo("/app/admin/users")) ) +export const redirectToMetrics = createAsyncThunk("redirect/admin/metrics", (_, thunkApi) => + thunkApi.dispatch(redirectTo("/app/admin/metrics")) +) export const redirectSlice = createSlice({ name: "redirect", diff --git a/commafeed-client/src/app/types.ts b/commafeed-client/src/app/types.ts index 17dcc5c0..6683b4ce 100644 --- a/commafeed-client/src/app/types.ts +++ b/commafeed-client/src/app/types.ts @@ -115,6 +115,22 @@ export interface FeedModificationRequest { filter?: string } +export interface GetEntriesRequest { + id: string + readType?: ReadingMode + newerThan?: number + order?: ReadingOrder + keywords?: string + onlyIds?: boolean + excludedSubscriptionIds?: string + tag?: string +} + +export interface GetEntriesPaginatedRequest extends GetEntriesRequest { + offset: number + limit: number +} + export interface IDRequest { id: number } @@ -132,6 +148,50 @@ export interface MarkRequest { excludedSubscriptions?: number[] } +export interface MetricCounter { + count: number +} + +export interface MetricGauge { + value: number +} + +export interface MetricMeter { + count: number + m15_rate: number + m1_rate: number + m5_rate: number + mean_rate: number + units: string +} + +export type MetricTimer = { + count: number + max: number + mean: number + min: number + p50: number + p75: number + p95: number + p98: number + p99: number + p999: number + stddev: number + m15_rate: number + m1_rate: number + m5_rate: number + mean_rate: number + duration_units: string + rate_units: string +} + +export interface Metrics { + counters: { [key: string]: MetricCounter } + gauges: { [key: string]: MetricGauge } + meters: { [key: string]: MetricMeter } + timers: { [key: string]: MetricTimer } +} + export interface MultipleMarkRequest { requests: MarkRequest[] } @@ -219,22 +279,6 @@ export interface TagRequest { tags: string[] } -export interface GetEntriesRequest { - id: string - readType?: ReadingMode - newerThan?: number - order?: ReadingOrder - keywords?: string - onlyIds?: boolean - excludedSubscriptionIds?: string - tag?: string -} - -export interface GetEntriesPaginatedRequest extends GetEntriesRequest { - offset: number - limit: number -} - export interface UnreadCount { feedId?: number unreadCount?: number diff --git a/commafeed-client/src/components/header/ProfileMenu.tsx b/commafeed-client/src/components/header/ProfileMenu.tsx index 897583f3..4c8f088a 100644 --- a/commafeed-client/src/components/header/ProfileMenu.tsx +++ b/commafeed-client/src/components/header/ProfileMenu.tsx @@ -1,9 +1,9 @@ import { Trans } from "@lingui/macro" import { Divider, Menu, useMantineColorScheme } from "@mantine/core" -import { redirectToAdminUsers, redirectToSettings } from "app/slices/redirect" +import { redirectToAdminUsers, redirectToMetrics, redirectToSettings } from "app/slices/redirect" import { useAppDispatch, useAppSelector } from "app/store" import { useState } from "react" -import { TbMoon, TbPower, TbSettings, TbSun, TbUsers } from "react-icons/tb" +import { TbChartLine, TbMoon, TbPower, TbSettings, TbSun, TbUsers } from "react-icons/tb" interface ProfileMenuProps { control: React.ReactElement @@ -52,6 +52,15 @@ export function ProfileMenu(props: ProfileMenuProps) { > Manage users + } + onClick={() => { + dispatch(redirectToMetrics()) + setOpened(false) + }} + > + Metrics + )} diff --git a/commafeed-client/src/components/metrics/Gauge.tsx b/commafeed-client/src/components/metrics/Gauge.tsx new file mode 100644 index 00000000..26fe9b83 --- /dev/null +++ b/commafeed-client/src/components/metrics/Gauge.tsx @@ -0,0 +1,9 @@ +import { MetricGauge } from "app/types" + +interface MeterProps { + gauge: MetricGauge +} + +export function Gauge(props: MeterProps) { + return {props.gauge.value} +} diff --git a/commafeed-client/src/components/metrics/Meter.tsx b/commafeed-client/src/components/metrics/Meter.tsx new file mode 100644 index 00000000..d8dc1978 --- /dev/null +++ b/commafeed-client/src/components/metrics/Meter.tsx @@ -0,0 +1,19 @@ +import { Box } from "@mantine/core" +import { MetricMeter } from "app/types" + +interface MeterProps { + meter: MetricMeter +} + +export function Meter(props: MeterProps) { + return ( + + Mean: {props.meter.mean_rate.toFixed(2)} + Last minute: {props.meter.m1_rate.toFixed(2)} + Last 5 minutes: {props.meter.m5_rate.toFixed(2)} + Last 15 minutes: {props.meter.m15_rate.toFixed(2)} + Units: {props.meter.units} + Total: {props.meter.count} + + ) +} diff --git a/commafeed-client/src/components/metrics/MetricAccordionItem.tsx b/commafeed-client/src/components/metrics/MetricAccordionItem.tsx new file mode 100644 index 00000000..658f3f29 --- /dev/null +++ b/commafeed-client/src/components/metrics/MetricAccordionItem.tsx @@ -0,0 +1,22 @@ +import { Accordion, Box, Group } from "@mantine/core" + +interface MetricAccordionItemProps { + metricKey: string + name: string + headerValue: number + children: React.ReactNode +} + +export function MetricAccordionItem({ metricKey, name, headerValue, children }: MetricAccordionItemProps) { + return ( + + + + {name} + {headerValue} + + + {children} + + ) +} diff --git a/commafeed-client/src/components/metrics/Timer.tsx b/commafeed-client/src/components/metrics/Timer.tsx new file mode 100644 index 00000000..2ce8b3d0 --- /dev/null +++ b/commafeed-client/src/components/metrics/Timer.tsx @@ -0,0 +1,19 @@ +import { Box } from "@mantine/core" +import { MetricTimer } from "app/types" + +interface MetricTimerProps { + timer: MetricTimer +} + +export function Timer(props: MetricTimerProps) { + return ( + + Mean: {props.timer.mean_rate.toFixed(2)} + Last minute: {props.timer.m1_rate.toFixed(2)} + Last 5 minutes: {props.timer.m5_rate.toFixed(2)} + Last 15 minutes: {props.timer.m15_rate.toFixed(2)} + Units: {props.timer.rate_units} + Total: {props.timer.count} + + ) +} diff --git a/commafeed-client/src/pages/admin/MetricsPage.tsx b/commafeed-client/src/pages/admin/MetricsPage.tsx new file mode 100644 index 00000000..15b024d2 --- /dev/null +++ b/commafeed-client/src/pages/admin/MetricsPage.tsx @@ -0,0 +1,75 @@ +import { Accordion, Box, Tabs } from "@mantine/core" +import { client } from "app/client" +import { Loader } from "components/Loader" +import { Gauge } from "components/metrics/Gauge" +import { Meter } from "components/metrics/Meter" +import { MetricAccordionItem } from "components/metrics/MetricAccordionItem" +import { Timer } from "components/metrics/Timer" +import { useAsync } from "react-async-hook" +import { TbChartAreaLine, TbClock } from "react-icons/tb" + +const shownMeters: { [key: string]: string } = { + "com.commafeed.backend.feed.FeedQueues.refill": "Refresh queue refill rate", + "com.commafeed.backend.feed.FeedRefreshTaskGiver.feedRefreshed": "Feed refreshed", + "com.commafeed.backend.feed.FeedRefreshUpdater.feedUpdated": "Feed updated", + "com.commafeed.backend.feed.FeedRefreshUpdater.entryCacheHit": "Entry cache hit", + "com.commafeed.backend.feed.FeedRefreshUpdater.entryCacheMiss": "Entry cache miss", +} + +const shownGauges: { [key: string]: string } = { + "com.commafeed.backend.feed.FeedRefreshExecutor.feed-refresh-updater.active": "Feed Updater active", + "com.commafeed.backend.feed.FeedRefreshExecutor.feed-refresh-updater.pending": "Feed Updater queued", + "com.commafeed.backend.feed.FeedRefreshExecutor.feed-refresh-worker.active": "Feed Worker active", + "com.commafeed.backend.feed.FeedRefreshExecutor.feed-refresh-worker.pending": "Feed Worker queued", + "com.commafeed.backend.feed.FeedQueues.addQueue": "Task Giver Add Queue", + "com.commafeed.backend.feed.FeedQueues.takeQueue": "Task Giver Take Queue", + "com.commafeed.backend.feed.FeedQueues.giveBackQueue": "Task Giver Give Back Queue", +} + +export function MetricsPage() { + const query = useAsync(() => client.admin.getMetrics(), []) + + if (!query.result) return + const { meters, gauges, timers } = query.result.data + return ( + + + }> + Stats + + }> + Timers + + + + + + {Object.keys(shownMeters).map(m => ( + + + + ))} + + + + {Object.keys(shownGauges).map(g => ( + + {shownGauges[g]}  + + + ))} + + + + + + {Object.keys(timers).map(key => ( + + + + ))} + + + + ) +}