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: added new tab for infra metrics in logs detailed page #5771

Merged
merged 40 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
917aef0
feat: added new tab for infra metrics in logs detailed page
rahulkeswani101 Aug 26, 2024
247cd78
feat: added yaxis unit for the charts
rahulkeswani101 Aug 26, 2024
6e3910a
chore: cleanup query_range params
ankitnayan Aug 26, 2024
7cea836
Merge branch 'SIG-5730' of https://github.com/SigNoz/signoz into SIG-…
ankitnayan Aug 26, 2024
1bf30ce
fix: clusterName, podName variables not working
ankitnayan Aug 26, 2024
d562ff4
Merge branch 'develop' into SIG-5730
rahulkeswani101 Aug 26, 2024
0faa5e3
feat: added skeleton for each charts in infra metrics tab
rahulkeswani101 Aug 26, 2024
42f7be6
change card height to 300px
ankitnayan Aug 27, 2024
9d74ad8
fix: updated the test cases
rahulkeswani101 Aug 27, 2024
cf0bf86
Merge branch 'SIG-5730' of https://github.com/SigNoz/signoz into SIG-…
rahulkeswani101 Aug 27, 2024
1d11559
Merge branch 'develop' into SIG-5730
rahulkeswani101 Sep 4, 2024
20ff952
feat: added new sub-tabs node and pod for infra metrics tab
rahulkeswani101 Sep 6, 2024
836b627
Merge branch 'SIG-5730' of https://github.com/SigNoz/signoz into SIG-…
rahulkeswani101 Sep 6, 2024
88cc6c0
feat: added new components for node and pod metrics
rahulkeswani101 Sep 6, 2024
d09d1c6
feat: added card titles for host metrics and handled empty state
rahulkeswani101 Sep 7, 2024
152fc22
Merge branch 'develop' into SIG-5730
rahulkeswani101 Sep 7, 2024
228d7b6
fix: updated the constant for host name
rahulkeswani101 Sep 7, 2024
947eec2
Merge branch 'SIG-5730' of https://github.com/SigNoz/signoz into SIG-…
rahulkeswani101 Sep 7, 2024
985fa96
feat: added vertical dotted line to all panels and updated y axis uni…
rahulkeswani101 Sep 9, 2024
52e19da
Merge branch 'develop' into SIG-5730
rahulkeswani101 Sep 9, 2024
ec14cd5
feat: removed other panel types other than graph from host metrics qu…
rahulkeswani101 Sep 9, 2024
576af63
fix: updated the query payload for node metrics
rahulkeswani101 Sep 9, 2024
a9f6f73
feat: moved the label of vertical dotted line to top
rahulkeswani101 Sep 9, 2024
1e2b126
feat: added console statement to check query payload
rahulkeswani101 Sep 9, 2024
789c073
fix: added pod name instead of node name in pod query payload
rahulkeswani101 Sep 9, 2024
78ff18e
fix: added key as pod name instead of node name in file system usage
rahulkeswani101 Sep 9, 2024
2a08957
fix: updated query payload for file system usage in pod metrics and r…
rahulkeswani101 Sep 9, 2024
03adb40
fix: updated the y axis units for network io
rahulkeswani101 Sep 10, 2024
a66c78a
fix: custom date time issue while plotting the graph
rahulkeswani101 Sep 10, 2024
065d362
feat: compare end time and current time update the end time accordingly
rahulkeswani101 Sep 10, 2024
2e6219a
feat: added the start and end time in query payloads
rahulkeswani101 Sep 11, 2024
e0a49f1
Merge branch 'develop' into SIG-5730
rahulkeswani101 Sep 11, 2024
1e5a8b2
refactor: removed the comments and unused variables
rahulkeswani101 Sep 16, 2024
567467b
Merge branch 'SIG-5730' of https://github.com/SigNoz/signoz into SIG-…
rahulkeswani101 Sep 16, 2024
ce0a16d
Merge branch 'develop' into SIG-5730
rahulkeswani101 Sep 16, 2024
9ef27be
chore: added a todo to make common component for sub-tabs
rahulkeswani101 Sep 16, 2024
331274a
Merge branch 'develop' into SIG-5730
rahulkeswani101 Sep 16, 2024
41eaed7
Merge branch 'develop' into SIG-5730
rahulkeswani101 Sep 20, 2024
f459e1d
fix: addressed review comments
rahulkeswani101 Sep 20, 2024
d424dc4
Merge branch 'develop' into SIG-5730
rahulkeswani101 Sep 20, 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
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,20 @@ beforeAll(() => {
matchMedia();
});

jest.mock('uplot', () => {
const paths = {
spline: jest.fn(),
bars: jest.fn(),
};
const uplotMock = jest.fn(() => ({
paths,
}));
return {
paths,
default: uplotMock,
};
});

jest.mock('react-dnd', () => ({
useDrop: jest.fn().mockImplementation(() => [jest.fn(), jest.fn(), jest.fn()]),
useDrag: jest.fn().mockImplementation(() => [jest.fn(), jest.fn(), jest.fn()]),
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/components/LogDetail/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@ export const VIEW_TYPES = {
OVERVIEW: 'OVERVIEW',
JSON: 'JSON',
CONTEXT: 'CONTEXT',
INFRAMETRICS: 'INFRAMETRICS',
} as const;

export type VIEWS = typeof VIEW_TYPES[keyof typeof VIEW_TYPES];

export const RESOURCE_KEYS = {
CLUSTER_NAME: 'k8s.cluster.name',
POD_NAME: 'k8s.pod.name',
NODE_NAME: 'k8s.node.name',
HOST_NAME: 'host.name',
} as const;
24 changes: 23 additions & 1 deletion frontend/src/components/LogDetail/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import cx from 'classnames';
import { LogType } from 'components/Logs/LogStateIndicator/LogStateIndicator';
import { LOCALSTORAGE } from 'constants/localStorage';
import ContextView from 'container/LogDetailedView/ContextView/ContextView';
import InfraMetrics from 'container/LogDetailedView/InfraMetrics/InfraMetrics';
import JSONView from 'container/LogDetailedView/JsonView';
import Overview from 'container/LogDetailedView/Overview';
import {
Expand All @@ -22,6 +23,7 @@ import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { useNotifications } from 'hooks/useNotifications';
import {
BarChart2,
Braces,
Copy,
Filter,
Expand All @@ -36,7 +38,7 @@ import { Query, TagFilter } from 'types/api/queryBuilder/queryBuilderData';
import { DataSource, StringOperators } from 'types/common/queryBuilder';
import { FORBID_DOM_PURIFY_TAGS } from 'utils/app';

import { VIEW_TYPES, VIEWS } from './constants';
import { RESOURCE_KEYS, VIEW_TYPES, VIEWS } from './constants';
import { LogDetailProps } from './LogDetail.interfaces';
import QueryBuilderSearchWrapper from './QueryBuilderSearchWrapper';

Expand Down Expand Up @@ -192,6 +194,17 @@ function LogDetail({
Context
</div>
</Radio.Button>
<Radio.Button
className={
selectedView === VIEW_TYPES.INFRAMETRICS ? 'selected_view tab' : 'tab'
}
value={VIEW_TYPES.INFRAMETRICS}
>
<div className="view-title">
<BarChart2 size={14} />
Metrics
</div>
</Radio.Button>
</Radio.Group>

{selectedView === VIEW_TYPES.JSON && (
Expand Down Expand Up @@ -246,6 +259,15 @@ function LogDetail({
isEdit={isEdit}
/>
)}
{selectedView === VIEW_TYPES.INFRAMETRICS && (
<InfraMetrics
clusterName={log.resources_string?.[RESOURCE_KEYS.CLUSTER_NAME] || ''}
podName={log.resources_string?.[RESOURCE_KEYS.POD_NAME] || ''}
nodeName={log.resources_string?.[RESOURCE_KEYS.NODE_NAME] || ''}
hostName={log.resources_string?.[RESOURCE_KEYS.HOST_NAME] || ''}
logLineTimestamp={log.timestamp.toString()}
/>
)}
</Drawer>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
.empty-container {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}

.infra-metrics-container {
.views-tabs {
margin-bottom: 1rem;
}
}

.infra-metrics-card {
margin: 1rem 0;
height: 300px;
padding: 10px;

.ant-card-body {
padding: 0;
}

.chart-container {
width: 100%;
height: 100%;
}

.no-data-container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import './InfraMetrics.styles.scss';

import { Empty, Radio } from 'antd';
import { RadioChangeEvent } from 'antd/lib';
import { History, Table } from 'lucide-react';
import { useState } from 'react';

import { VIEW_TYPES } from './constants';
import NodeMetrics from './NodeMetrics';
import PodMetrics from './PodMetrics';

interface MetricsDataProps {
podName: string;
nodeName: string;
hostName: string;
clusterName: string;
logLineTimestamp: string;
}

function InfraMetrics({
podName,
nodeName,
hostName,
clusterName,
logLineTimestamp,
}: MetricsDataProps): JSX.Element {
const [selectedView, setSelectedView] = useState<string>(() =>
podName ? VIEW_TYPES.POD : VIEW_TYPES.NODE,
);

const handleModeChange = (e: RadioChangeEvent): void => {
setSelectedView(e.target.value);
};

if (!podName && !nodeName && !hostName) {
vikrantgupta25 marked this conversation as resolved.
Show resolved Hide resolved
return (
<div className="empty-container">
<Empty
image={Empty.PRESENTED_IMAGE_SIMPLE}
description="No data available. Please select a valid log line containing a pod, node, or host attributes to view metrics."
/>
</div>
);
}

return (
<div className="infra-metrics-container">
<Radio.Group
className="views-tabs"
onChange={handleModeChange}
value={selectedView}
>
<Radio.Button
className={selectedView === VIEW_TYPES.NODE ? 'selected_view tab' : 'tab'}
value={VIEW_TYPES.NODE}
>
<div className="view-title">
<Table size={14} />
Node
</div>
</Radio.Button>
{podName && (
<Radio.Button
className={selectedView === VIEW_TYPES.POD ? 'selected_view tab' : 'tab'}
value={VIEW_TYPES.POD}
>
<div className="view-title">
<History size={14} />
Pod
</div>
</Radio.Button>
)}
</Radio.Group>
{/* TODO(Rahul): Make a common config driven component for this and other infra metrics components */}
{selectedView === VIEW_TYPES.NODE && (
<NodeMetrics
nodeName={nodeName}
clusterName={clusterName}
hostName={hostName}
logLineTimestamp={logLineTimestamp}
/>
)}
{selectedView === VIEW_TYPES.POD && podName && (
<PodMetrics
podName={podName}
clusterName={clusterName}
logLineTimestamp={logLineTimestamp}
/>
)}
</div>
);
}

export default InfraMetrics;
140 changes: 140 additions & 0 deletions frontend/src/container/LogDetailedView/InfraMetrics/NodeMetrics.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { Card, Col, Row, Skeleton, Typography } from 'antd';
vikrantgupta25 marked this conversation as resolved.
Show resolved Hide resolved
import cx from 'classnames';
import Uplot from 'components/Uplot';
import { ENTITY_VERSION_V4 } from 'constants/app';
import dayjs from 'dayjs';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { useResizeObserver } from 'hooks/useDimensions';
import { GetMetricQueryRange } from 'lib/dashboard/getQueryResults';
import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions';
import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData';
import { useMemo, useRef } from 'react';
import { useQueries, UseQueryResult } from 'react-query';
import { SuccessResponse } from 'types/api';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';

import {
getHostQueryPayload,
getNodeQueryPayload,
hostWidgetInfo,
nodeWidgetInfo,
} from './constants';

function NodeMetrics({
nodeName,
clusterName,
hostName,
logLineTimestamp,
}: {
nodeName: string;
clusterName: string;
hostName: string;
logLineTimestamp: string;
}): JSX.Element {
const { start, end, verticalLineTimestamp } = useMemo(() => {
const logTimestamp = dayjs(logLineTimestamp);
const now = dayjs();
const startTime = logTimestamp.subtract(3, 'hour');

const endTime = logTimestamp.add(3, 'hour').isBefore(now)
? logTimestamp.add(3, 'hour')
: now;

return {
start: startTime.unix(),
end: endTime.unix(),
verticalLineTimestamp: logTimestamp.unix(),
};
}, [logLineTimestamp]);

const queryPayloads = useMemo(() => {
if (nodeName) {
return getNodeQueryPayload(clusterName, nodeName, start, end);
}
return getHostQueryPayload(hostName, start, end);
}, [nodeName, hostName, clusterName, start, end]);

const widgetInfo = nodeName ? nodeWidgetInfo : hostWidgetInfo;
const queries = useQueries(
queryPayloads.map((payload) => ({
queryKey: ['metrics', payload, ENTITY_VERSION_V4, 'NODE'],
queryFn: (): Promise<SuccessResponse<MetricRangePayloadProps>> =>
GetMetricQueryRange(payload, ENTITY_VERSION_V4),
enabled: !!payload,
})),
);

const isDarkMode = useIsDarkMode();
const graphRef = useRef<HTMLDivElement>(null);
const dimensions = useResizeObserver(graphRef);

const chartData = useMemo(
() => queries.map(({ data }) => getUPlotChartData(data?.payload)),
[queries],
);

const options = useMemo(
() =>
queries.map(({ data }, idx) =>
getUPlotChartOptions({
apiResponse: data?.payload,
isDarkMode,
dimensions,
yAxisUnit: widgetInfo[idx].yAxisUnit,
softMax: null,
softMin: null,
minTimeScale: start,
maxTimeScale: end,
verticalLineTimestamp,
}),
),
[
queries,
isDarkMode,
dimensions,
widgetInfo,
start,
verticalLineTimestamp,
end,
],
);

const renderCardContent = (
query: UseQueryResult<SuccessResponse<MetricRangePayloadProps>, unknown>,
idx: number,
): JSX.Element => {
if (query.isLoading) {
return <Skeleton />;
}

if (query.error) {
const errorMessage =
(query.error as Error)?.message || 'Something went wrong';
return <div>{errorMessage}</div>;
}
return (
<div
className={cx('chart-container', {
'no-data-container':
!query.isLoading && !query?.data?.payload?.data?.result?.length,
})}
>
<Uplot options={options[idx]} data={chartData[idx]} />
</div>
);
};
return (
<Row gutter={24}>
{queries.map((query, idx) => (
<Col span={12} key={widgetInfo[idx].title}>
<Typography.Text>{widgetInfo[idx].title}</Typography.Text>
<Card bordered className="infra-metrics-card" ref={graphRef}>
{renderCardContent(query, idx)}
</Card>
</Col>
))}
</Row>
);
}

export default NodeMetrics;
Loading
Loading