Skip to content

Commit

Permalink
[#7716] Config > Admin > Agent Statistic
Browse files Browse the repository at this point in the history
  • Loading branch information
jihea-park committed Nov 25, 2024
1 parent e3c4cdd commit 2b30132
Show file tree
Hide file tree
Showing 16 changed files with 502 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ export const CONFIG_MENU_MAP = {
path: APP_PATH.CONFIG_AGENT_MANAGEMENT,
href: APP_PATH.CONFIG_AGENT_MANAGEMENT,
},
{
name: 'Agent statistic',
path: APP_PATH.CONFIG_AGENT_STATISTIC,
href: APP_PATH.CONFIG_AGENT_STATISTIC,
},
],
},
PERSONAL_SETTINGS: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useAtomValue } from 'jotai';
import { getLayoutWithConfiguration, getLayoutWithSideNavigation } from '@/components/Layout';
import { AgentStatisticPage as CommonAgentStatisticPage, withInitialFetch } from '@pinpoint-fe/ui';
import { configurationAtom } from '@pinpoint-fe/atoms';

export interface AgentStatisticPageProps {}
const AgentStatisticPage = () => {
const configuration = useAtomValue(configurationAtom);

return <CommonAgentStatisticPage configuration={configuration} />;
};

export default withInitialFetch((props: AgentStatisticPageProps) =>
getLayoutWithSideNavigation(getLayoutWithConfiguration(<AgentStatisticPage {...props} />)),
);
5 changes: 5 additions & 0 deletions web-frontend/src/main/v3/apps/web/src/routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import Users from '@/pages/config/Users';
import Alarm from '@/pages/config/Alarm';
import Webhook from '@/pages/config/Webhook';
import AgentManagement from '@/pages/config/AgentManagement';
import AgentStatistic from '@/pages/config/AgentStatistic';
import { threadDumpRouteLoader } from './loader/threadDump';
import { openTelemetryRouteLoader } from './loader/openTelemetry';
import { handleV2RouteLoader } from './loader/handleV2';
Expand Down Expand Up @@ -169,6 +170,10 @@ const router = createBrowserRouter(
path: `${APP_PATH.CONFIG_AGENT_MANAGEMENT}`,
element: <AgentManagement />,
},
{
path: `${APP_PATH.CONFIG_AGENT_STATISTIC}`,
element: <AgentStatistic />,
},
{
path: '*',
element: <NotFound />,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export const APDEX_SCORE = `${LOCAL_API_PATH}/getApdexScore`;
// '/getAgentStat/apdexScore/chart',
// '/agents/search-all',
export const SEARCH_APPLICATION = `${LOCAL_API_PATH}/agents/search-application`;
export const AGENT_STATISTICS = `${LOCAL_API_PATH}/agents/statistics`;
// '/agents/statistics',
export const ERROR_ANALYSIS_GROUPS = `${LOCAL_API_PATH}/errors/groups`;
export const ERROR_ANALYSIS_ERROR_LIST = `${LOCAL_API_PATH}/errors/errorList`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,12 @@
"REMOVE_APPLICATION": "Remove application"
}
},
"AGENT_STATISTIC": {
"ZOOM_GUIDE": "You can zoom in/out the chart through the mouse.",
"LOAD_GUIDE": "It takes a long time to load the agent's statistical data.",
"LOADING": "Load",
"RELOAD": "Reload"
},
"USERS": {
"USER_ADD_TITLE": "Add user",
"USER_INFO_TITLE": "User info",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,12 @@
"REMOVE_APPLICATION": "Application 삭제"
}
},
"AGENT_STATISTIC": {
"ZOOM_GUIDE": "마우스를 통해 차트를 Zoom In/Out 할 수 있습니다.",
"LOAD_GUIDE": "Agent의 통계 정보를 가져오는 작업은 많은 시간을 소요합니다.",
"LOADING": "데이터 가져오기",
"RELOAD": "데이터 다시 가져오기"
},
"USERS": {
"USER_ADD_TITLE": "사용자 등록",
"USER_INFO_TITLE": "사용자 정보",
Expand Down
1 change: 1 addition & 0 deletions web-frontend/src/main/v3/packages/constants/src/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const APP_PATH = {
CONFIG_USERS: '/config/users',
CONFIG_WEBHOOK: '/config/webhook',
CONFIG_AGENT_MANAGEMENT: '/config/agentManagement',
CONFIG_AGENT_STATISTIC: '/config/agentStatistic',
CONFIG: '/config',
ERROR_ANALYSIS: '/errorAnalysis',
FILTERED_MAP: '/filteredMap',
Expand Down
1 change: 1 addition & 0 deletions web-frontend/src/main/v3/packages/hooks/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from './useAdmin';
export * from './useAlarmRuleMutation';
export * from './useAlarmRuleQuery';
export * from './useAgentsSearchApplication';
export * from './useAgentsStatistics';
export * from './useConfigUsers';
export * from './useDeleteConfigGroupMember';
export * from './useDeleteConfigUserGroup';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { useQuery } from '@tanstack/react-query';
import { END_POINTS, SearchApplication } from '@pinpoint-fe/constants';
import { queryFn } from './reactQueryHelper';

export const useGetAgentsStatistics = (load: boolean) => {
const { data, isLoading, refetch } = useQuery<SearchApplication.Response>({
queryKey: [END_POINTS.AGENT_STATISTICS],
queryFn: queryFn(`${END_POINTS.AGENT_STATISTICS}`),
enabled: !!load,
});

return { data, isLoading, refetch };
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { colors } from '../../../constant';
import {
ChartConfig,
ChartContainer,
ChartLegend,
ChartLegendContent,
} from '../../../components/ui';
import { XAxis, YAxis, LabelList, Bar, BarChart } from 'recharts';

export type ChartData = {
vmVersion?: string;
agentVersion?: string;
value: number;
};

export function AgentStatisticChart({
type,
chartData,
}: {
type: 'vmVersion' | 'agentVersion';
chartData?: ChartData[];
}) {
const chartConfig = {
value: {
label: type === 'vmVersion' ? 'JVM' : 'Agent',
color: type === 'vmVersion' ? colors.blue[600] : colors.orange[500],
},
} satisfies ChartConfig;

return (
<ChartContainer config={chartConfig} className="w-full h-full">
<BarChart
accessibilityLayer
data={chartData}
layout="vertical"
margin={{
right: 50,
left: 15,
}}
>
<XAxis type="number" dataKey={'value'} />
<YAxis
dataKey={type}
type="category"
width={type === 'vmVersion' ? 60 : 120}
tickLine={false}
/>
<ChartLegend content={<ChartLegendContent />} />
<Bar dataKey="value" fill="var(--color-value)">
<LabelList
dataKey="value"
position="right"
offset={8}
className="fill-foreground"
fontSize={12}
/>
</Bar>
</BarChart>
</ChartContainer>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';
import { AgentStatisticChart, ChartData } from './AgentStatisticChart';
import { SearchApplication } from '@pinpoint-fe/constants';

/* eslint-disable @typescript-eslint/no-explicit-any */
const useVersionChartData = (data: any, versionKey: 'vmVersion' | 'agentVersion') =>
React.useMemo(() => {
const instances = data?.flatMap((group: SearchApplication.Application) => group.instancesList);

const versionCounts = instances?.reduce((acc: any, instance: SearchApplication.Instance) => {
const version = instance[versionKey];
if (version) {
acc[version] = (acc[version] || 0) + 1;
}
return acc;
}, {} as any);

return Object.entries(versionCounts || {}).map(([version, value]) => ({
[versionKey]: version,
value,
}));
}, [data, versionKey]);

export function AgentStatisticContainer({ data }: { data?: SearchApplication.Application[] }) {
const vmVersionChartData = useVersionChartData(data, 'vmVersion');
const agentVersionChartData = useVersionChartData(data, 'agentVersion');

return (
<div className="flex min-w-full gap-5 max-h-[40%]">
<AgentStatisticChart type="vmVersion" chartData={vmVersionChartData as ChartData[]} />
<AgentStatisticChart type="agentVersion" chartData={agentVersionChartData as ChartData[]} />
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React from 'react';
import { HiOutlineRefresh } from 'react-icons/hi';
import { useTranslation } from 'react-i18next';
import { Configuration } from '@pinpoint-fe/constants';
import { Button, Separator } from '../../../components';
import { useGetAgentsStatistics } from '@pinpoint-fe/hooks';
import { CgSpinner } from 'react-icons/cg';
import { cn } from '../../../lib';
import { AgentStatisticContainer } from './AgentStatisticChartContainer';
import { AgentStatisticTable } from './AgentStatisticTable';
import { format } from 'date-fns';

export interface AgentStatisticFetcherProps {
configuration?: Configuration;
}

export const AgentStatisticFetcher = ({ configuration }: AgentStatisticFetcherProps) => {
void configuration; // Not use configuration

const { t } = useTranslation();
const [load, setLoad] = React.useState(false);
const [loadDate, setLoadDate] = React.useState<Date>();

const { data, isLoading, refetch } = useGetAgentsStatistics(load);

function handleClickLoad() {
setLoad(true);
}

function handleReload() {
refetch();
}

React.useEffect(() => {
setLoadDate(new Date());
}, [data]);

return (
<div className="flex flex-col h-full">
<div className="flex gap-10">
<h3 className="text-lg font-semibold">Agent statistic</h3>
</div>
<Separator className="my-6" />
<div className="bg-red-300"></div>
<div className="h-[-webkit-fill-available] relative overflow-hidden">
{isLoading && (
<div className="absolute flex items-center justify-center w-full h-full">
<CgSpinner className="absolute opacity-100 animate-spin" size={100} />
<div className="w-full h-full bg-gray-400 opacity-20"></div>
</div>
)}
{data ? (
<div className="flex flex-col h-full gap-5">
<div className="flex flex-row items-center justify-end gap-1">
{loadDate && format(loadDate, 'yyyy.MM.dd HH:mm:ss')}
<Button onClick={handleReload} size="sm">
<HiOutlineRefresh size={18} />
</Button>
</div>
<AgentStatisticContainer data={data} />
<AgentStatisticTable data={data} />
</div>
) : (
<div
className={cn('flex flex-col items-center justify-center h-full gap-3', {
'opacity-20': isLoading,
})}
>
{t('CONFIGURATION.AGENT_STATISTIC.LOAD_GUIDE')}
<Button className="w-max" onClick={handleClickLoad} disabled={isLoading}>
{t('CONFIGURATION.AGENT_STATISTIC.LOADING')}
</Button>
</div>
)}
</div>
</div>
);
};
Loading

0 comments on commit 2b30132

Please sign in to comment.