-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: added new tab for infra metrics in logs detailed page (#5771)
* feat: added new tab for infra metrics in logs detailed page * feat: added yaxis unit for the charts * chore: cleanup query_range params * fix: clusterName, podName variables not working * feat: added skeleton for each charts in infra metrics tab * change card height to 300px * fix: updated the test cases * feat: added new sub-tabs node and pod for infra metrics tab * feat: added new components for node and pod metrics * feat: added card titles for host metrics and handled empty state * fix: updated the constant for host name * feat: added vertical dotted line to all panels and updated y axis units for all panels * feat: removed other panel types other than graph from host metrics query payload * fix: updated the query payload for node metrics * feat: moved the label of vertical dotted line to top * feat: added console statement to check query payload * fix: added pod name instead of node name in pod query payload * fix: added key as pod name instead of node name in file system usage * fix: updated query payload for file system usage in pod metrics and removed label from dotted line * fix: updated the y axis units for network io * fix: custom date time issue while plotting the graph * feat: compare end time and current time update the end time accordingly * feat: added the start and end time in query payloads * refactor: removed the comments and unused variables * chore: added a todo to make common component for sub-tabs * fix: addressed review comments --------- Co-authored-by: Ankit Nayan <ankit@signoz.io>
- Loading branch information
1 parent
f3c01a5
commit c5b5bfe
Showing
25 changed files
with
3,697 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 34 additions & 0 deletions
34
frontend/src/container/LogDetailedView/InfraMetrics/InfraMetrics.styles.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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%); | ||
} | ||
} |
94 changes: 94 additions & 0 deletions
94
frontend/src/container/LogDetailedView/InfraMetrics/InfraMetrics.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) { | ||
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
140
frontend/src/container/LogDetailedView/InfraMetrics/NodeMetrics.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
import { Card, Col, Row, Skeleton, Typography } from 'antd'; | ||
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; |
Oops, something went wrong.