mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-10-13 08:51:31 +08:00
287 lines
7.3 KiB
TypeScript
287 lines
7.3 KiB
TypeScript
import { ActiveElement, Chart, ChartData, ChartEvent } from 'chart.js';
|
|
import Graph from 'components/Graph';
|
|
import { METRICS_PAGE_QUERY_PARAM } from 'constants/query';
|
|
import ROUTES from 'constants/routes';
|
|
import FullView from 'container/GridGraphLayout/Graph/FullView/index.metricsBuilder';
|
|
import convertToNanoSecondsToSecond from 'lib/convertToNanoSecondsToSecond';
|
|
import { colors } from 'lib/getRandomColor';
|
|
import history from 'lib/history';
|
|
import {
|
|
convertRawQueriesToTraceSelectedTags,
|
|
resourceAttributesToTagFilterItems,
|
|
} from 'lib/resourceAttributes';
|
|
import React, { useCallback, useMemo, useState } from 'react';
|
|
import { useDispatch, useSelector } from 'react-redux';
|
|
import { useParams } from 'react-router-dom';
|
|
import { UpdateTimeInterval } from 'store/actions';
|
|
import { AppState } from 'store/reducers';
|
|
import { Widgets } from 'types/api/dashboard/getAll';
|
|
import MetricReducer from 'types/reducer/metrics';
|
|
|
|
import {
|
|
errorPercentage,
|
|
operationPerSec,
|
|
} from '../MetricsPageQueries/OverviewQueries';
|
|
import { Card, Col, GraphContainer, GraphTitle, Row } from '../styles';
|
|
import TopOperationsTable from '../TopOperationsTable';
|
|
import { Button } from './styles';
|
|
import { onGraphClickHandler, onViewTracePopupClick } from './util';
|
|
|
|
function Application({ getWidgetQueryBuilder }: DashboardProps): JSX.Element {
|
|
const { servicename } = useParams<{ servicename?: string }>();
|
|
const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0);
|
|
|
|
const handleSetTimeStamp = useCallback((selectTime: number) => {
|
|
setSelectedTimeStamp(selectTime);
|
|
}, []);
|
|
|
|
const dispatch = useDispatch();
|
|
const handleGraphClick = useCallback(
|
|
(type: string): ClickHandlerType => (
|
|
ChartEvent: ChartEvent,
|
|
activeElements: ActiveElement[],
|
|
chart: Chart,
|
|
data: ChartData,
|
|
): void => {
|
|
onGraphClickHandler(handleSetTimeStamp)(
|
|
ChartEvent,
|
|
activeElements,
|
|
chart,
|
|
data,
|
|
type,
|
|
);
|
|
},
|
|
[handleSetTimeStamp],
|
|
);
|
|
|
|
const {
|
|
topOperations,
|
|
serviceOverview,
|
|
resourceAttributeQueries,
|
|
topLevelOperations,
|
|
} = useSelector<AppState, MetricReducer>((state) => state.metrics);
|
|
|
|
const selectedTraceTags: string = JSON.stringify(
|
|
convertRawQueriesToTraceSelectedTags(resourceAttributeQueries) || [],
|
|
);
|
|
|
|
const tagFilterItems = useMemo(
|
|
() => resourceAttributesToTagFilterItems(resourceAttributeQueries) || [],
|
|
[resourceAttributeQueries],
|
|
);
|
|
|
|
const operationPerSecWidget = useMemo(
|
|
() =>
|
|
getWidgetQueryBuilder({
|
|
queryType: 1,
|
|
promQL: [],
|
|
metricsBuilder: operationPerSec({
|
|
servicename,
|
|
tagFilterItems,
|
|
topLevelOperations,
|
|
}),
|
|
clickHouse: [],
|
|
}),
|
|
[getWidgetQueryBuilder, servicename, topLevelOperations, tagFilterItems],
|
|
);
|
|
|
|
const errorPercentageWidget = useMemo(
|
|
() =>
|
|
getWidgetQueryBuilder({
|
|
queryType: 1,
|
|
promQL: [],
|
|
metricsBuilder: errorPercentage({
|
|
servicename,
|
|
tagFilterItems,
|
|
topLevelOperations,
|
|
}),
|
|
clickHouse: [],
|
|
}),
|
|
[servicename, topLevelOperations, tagFilterItems, getWidgetQueryBuilder],
|
|
);
|
|
|
|
const onDragSelect = useCallback(
|
|
(start: number, end: number) => {
|
|
const startTimestamp = Math.trunc(start);
|
|
const endTimestamp = Math.trunc(end);
|
|
|
|
if (startTimestamp !== endTimestamp) {
|
|
dispatch(UpdateTimeInterval('custom', [startTimestamp, endTimestamp]));
|
|
}
|
|
},
|
|
[dispatch],
|
|
);
|
|
|
|
const onErrorTrackHandler = (timestamp: number): void => {
|
|
const currentTime = timestamp;
|
|
const tPlusOne = timestamp + 60 * 1000;
|
|
|
|
const urlParams = new URLSearchParams();
|
|
urlParams.set(METRICS_PAGE_QUERY_PARAM.startTime, currentTime.toString());
|
|
urlParams.set(METRICS_PAGE_QUERY_PARAM.endTime, tPlusOne.toString());
|
|
|
|
history.replace(
|
|
`${
|
|
ROUTES.TRACE
|
|
}?${urlParams.toString()}&selected={"serviceName":["${servicename}"],"status":["error"]}&filterToFetchData=["duration","status","serviceName"]&spanAggregateCurrentPage=1&selectedTags=${selectedTraceTags}&isFilterExclude={"serviceName":false,"status":false}&userSelectedFilter={"serviceName":["${servicename}"],"status":["error"]}&spanAggregateCurrentPage=1`,
|
|
);
|
|
};
|
|
|
|
const generalChartDataProperties = useCallback(
|
|
(title: string, colorIndex: number) => ({
|
|
borderColor: colors[colorIndex],
|
|
label: title,
|
|
showLine: true,
|
|
borderWidth: 1.5,
|
|
spanGaps: true,
|
|
pointRadius: 2,
|
|
pointHoverRadius: 4,
|
|
}),
|
|
[],
|
|
);
|
|
|
|
const dataSets = useMemo(
|
|
() => [
|
|
{
|
|
data: serviceOverview.map((e) =>
|
|
parseFloat(convertToNanoSecondsToSecond(e.p99)),
|
|
),
|
|
...generalChartDataProperties('p99 Latency', 0),
|
|
},
|
|
{
|
|
data: serviceOverview.map((e) =>
|
|
parseFloat(convertToNanoSecondsToSecond(e.p95)),
|
|
),
|
|
...generalChartDataProperties('p95 Latency', 1),
|
|
},
|
|
{
|
|
data: serviceOverview.map((e) =>
|
|
parseFloat(convertToNanoSecondsToSecond(e.p50)),
|
|
),
|
|
...generalChartDataProperties('p50 Latency', 2),
|
|
},
|
|
],
|
|
[generalChartDataProperties, serviceOverview],
|
|
);
|
|
|
|
const data = useMemo(
|
|
() => ({
|
|
datasets: dataSets,
|
|
labels: serviceOverview.map(
|
|
(e) => new Date(parseFloat(convertToNanoSecondsToSecond(e.timestamp))),
|
|
),
|
|
}),
|
|
[serviceOverview, dataSets],
|
|
);
|
|
return (
|
|
<>
|
|
<Row gutter={24}>
|
|
<Col span={12}>
|
|
<Button
|
|
type="default"
|
|
size="small"
|
|
id="Service_button"
|
|
onClick={onViewTracePopupClick({
|
|
servicename,
|
|
selectedTraceTags,
|
|
timestamp: selectedTimeStamp,
|
|
})}
|
|
>
|
|
View Traces
|
|
</Button>
|
|
<Card>
|
|
<GraphTitle>Latency</GraphTitle>
|
|
<GraphContainer>
|
|
<Graph
|
|
animate={false}
|
|
onClickHandler={handleGraphClick('Service')}
|
|
name="service_latency"
|
|
type="line"
|
|
data={data}
|
|
yAxisUnit="ms"
|
|
onDragSelect={onDragSelect}
|
|
/>
|
|
</GraphContainer>
|
|
</Card>
|
|
</Col>
|
|
|
|
<Col span={12}>
|
|
<Button
|
|
type="default"
|
|
size="small"
|
|
id="Rate_button"
|
|
onClick={onViewTracePopupClick({
|
|
servicename,
|
|
selectedTraceTags,
|
|
timestamp: selectedTimeStamp,
|
|
})}
|
|
>
|
|
View Traces
|
|
</Button>
|
|
<Card>
|
|
<GraphTitle>Rate (ops/s)</GraphTitle>
|
|
<GraphContainer>
|
|
<FullView
|
|
name="operations_per_sec"
|
|
fullViewOptions={false}
|
|
onClickHandler={handleGraphClick('Rate')}
|
|
widget={operationPerSecWidget}
|
|
yAxisUnit="ops"
|
|
onDragSelect={onDragSelect}
|
|
/>
|
|
</GraphContainer>
|
|
</Card>
|
|
</Col>
|
|
</Row>
|
|
<Row gutter={24}>
|
|
<Col span={12}>
|
|
<Button
|
|
type="default"
|
|
size="small"
|
|
id="Error_button"
|
|
onClick={(): void => {
|
|
onErrorTrackHandler(selectedTimeStamp);
|
|
}}
|
|
>
|
|
View Traces
|
|
</Button>
|
|
|
|
<Card>
|
|
<GraphTitle>Error Percentage</GraphTitle>
|
|
<GraphContainer>
|
|
<FullView
|
|
name="error_percentage_%"
|
|
fullViewOptions={false}
|
|
onClickHandler={handleGraphClick('Error')}
|
|
widget={errorPercentageWidget}
|
|
yAxisUnit="%"
|
|
onDragSelect={onDragSelect}
|
|
/>
|
|
</GraphContainer>
|
|
</Card>
|
|
</Col>
|
|
|
|
<Col span={12}>
|
|
<Card>
|
|
<TopOperationsTable data={topOperations} />
|
|
</Card>
|
|
</Col>
|
|
</Row>
|
|
</>
|
|
);
|
|
}
|
|
|
|
interface DashboardProps {
|
|
getWidgetQueryBuilder: (query: Widgets['query']) => Widgets;
|
|
}
|
|
|
|
type ClickHandlerType = (
|
|
ChartEvent: ChartEvent,
|
|
activeElements: ActiveElement[],
|
|
chart: Chart,
|
|
data: ChartData,
|
|
type?: string,
|
|
) => void;
|
|
|
|
export default Application;
|