mirror of
https://github.com/SigNoz/signoz.git
synced 2025-12-29 16:14:42 +00:00
201 lines
5.7 KiB
TypeScript
201 lines
5.7 KiB
TypeScript
|
|
import React, { useEffect, useMemo, useState } from 'react';
|
||
|
|
import { Col, Divider, Row, Typography, Space, Button } from 'antd';
|
||
|
|
import { FilterOutlined } from '@ant-design/icons';
|
||
|
|
import GanttChart from 'container/GantChart';
|
||
|
|
import { getNodeById } from 'container/GantChart/utils';
|
||
|
|
import Timeline from 'container/Timeline';
|
||
|
|
import TraceFlameGraph from 'container/TraceFlameGraph';
|
||
|
|
import dayjs from 'dayjs';
|
||
|
|
import { spanServiceNameToColorMapping } from 'lib/getRandomColor';
|
||
|
|
import { getSortedData } from './utils';
|
||
|
|
import { ITraceTree, PayloadProps } from 'types/api/trace/getTraceItem';
|
||
|
|
import { getSpanTreeMetadata } from 'utils/getSpanTreeMetadata';
|
||
|
|
import { spanToTreeUtil } from 'utils/spanToTree';
|
||
|
|
import SelectedSpanDetails from './SelectedSpanDetails';
|
||
|
|
import useUrlQuery from 'hooks/useUrlQuery';
|
||
|
|
import styles from './TraceGraph.module.css';
|
||
|
|
import history from 'lib/history';
|
||
|
|
import { SPAN_DETAILS_LEFT_COL_WIDTH } from 'pages/TraceDetail/constants';
|
||
|
|
import { INTERVAL_UNITS } from './utils';
|
||
|
|
|
||
|
|
const TraceDetail = ({ response }: TraceDetailProps): JSX.Element => {
|
||
|
|
const spanServiceColors = useMemo(
|
||
|
|
() => spanServiceNameToColorMapping(response[0].events),
|
||
|
|
[response],
|
||
|
|
);
|
||
|
|
|
||
|
|
const urlQuery = useUrlQuery();
|
||
|
|
const [spanId, _setSpanId] = useState<string | null>(urlQuery.get('spanId'));
|
||
|
|
|
||
|
|
const [intervalUnit, setIntervalUnit] = useState(INTERVAL_UNITS[0]);
|
||
|
|
const [searchSpanString, setSearchSpanString] = useState('');
|
||
|
|
const [activeHoverId, setActiveHoverId] = useState<string>('');
|
||
|
|
const [activeSelectedId, setActiveSelectedId] = useState<string>(spanId || '');
|
||
|
|
|
||
|
|
const [treeData, setTreeData] = useState<ITraceTree>(
|
||
|
|
spanToTreeUtil(response[0].events),
|
||
|
|
);
|
||
|
|
|
||
|
|
const { treeData: tree, ...traceMetaData } = useMemo(() => {
|
||
|
|
return getSpanTreeMetadata(getSortedData(treeData), spanServiceColors);
|
||
|
|
}, [treeData]);
|
||
|
|
|
||
|
|
const [globalTraceMetadata, _setGlobalTraceMetadata] = useState<object>({
|
||
|
|
...traceMetaData,
|
||
|
|
});
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (activeSelectedId) {
|
||
|
|
history.replace({
|
||
|
|
pathname: history.location.pathname,
|
||
|
|
search: `?spanId=${activeSelectedId}`,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}, [activeSelectedId]);
|
||
|
|
|
||
|
|
const getSelectedNode = useMemo(() => {
|
||
|
|
return getNodeById(activeSelectedId, treeData);
|
||
|
|
}, [activeSelectedId, treeData]);
|
||
|
|
|
||
|
|
const onSearchHandler = (value: string) => {
|
||
|
|
setSearchSpanString(value);
|
||
|
|
setTreeData(spanToTreeUtil(response[0].events));
|
||
|
|
};
|
||
|
|
const onFocusSelectedSpanHandler = () => {
|
||
|
|
const treeNode = getNodeById(activeSelectedId, tree);
|
||
|
|
if (treeNode) {
|
||
|
|
setTreeData(treeNode);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const onResetHandler = () => {
|
||
|
|
setTreeData(spanToTreeUtil(response[0].events));
|
||
|
|
};
|
||
|
|
|
||
|
|
return (
|
||
|
|
<Row style={{ flex: 1 }}>
|
||
|
|
<Col flex={'auto'} style={{ display: 'flex', flexDirection: 'column' }}>
|
||
|
|
<Row className={styles['trace-detail-content-spacing']}>
|
||
|
|
<Col
|
||
|
|
flex={`${SPAN_DETAILS_LEFT_COL_WIDTH}px`}
|
||
|
|
style={{ alignItems: 'center', display: 'flex', flexDirection: 'column' }}
|
||
|
|
>
|
||
|
|
<Typography.Title level={5} style={{ margin: 0 }}>
|
||
|
|
Trace Details
|
||
|
|
</Typography.Title>
|
||
|
|
<Typography.Text style={{ margin: 0 }}>
|
||
|
|
{traceMetaData.totalSpans} Span
|
||
|
|
</Typography.Text>
|
||
|
|
</Col>
|
||
|
|
<Col flex={'auto'}>
|
||
|
|
<TraceFlameGraph
|
||
|
|
treeData={tree}
|
||
|
|
traceMetaData={traceMetaData}
|
||
|
|
hoveredSpanId={activeHoverId}
|
||
|
|
selectedSpanId={activeSelectedId}
|
||
|
|
onSpanHover={setActiveHoverId}
|
||
|
|
onSpanSelect={setActiveSelectedId}
|
||
|
|
intervalUnit={intervalUnit}
|
||
|
|
/>
|
||
|
|
</Col>
|
||
|
|
</Row>
|
||
|
|
<Row style={{ marginTop: '2rem' }}>
|
||
|
|
<Col
|
||
|
|
flex={`${SPAN_DETAILS_LEFT_COL_WIDTH}px`}
|
||
|
|
style={{
|
||
|
|
display: 'flex',
|
||
|
|
alignItems: 'center',
|
||
|
|
justifyContent: 'center',
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
{dayjs(traceMetaData.globalStart / 1e6).format('hh:mm:ssa MM/DD')}
|
||
|
|
</Col>
|
||
|
|
<Col
|
||
|
|
flex="auto"
|
||
|
|
style={{ overflow: 'visible' }}
|
||
|
|
className={styles['trace-detail-content-spacing']}
|
||
|
|
>
|
||
|
|
<Timeline
|
||
|
|
globalTraceMetadata={globalTraceMetadata}
|
||
|
|
traceMetaData={traceMetaData}
|
||
|
|
intervalUnit={intervalUnit}
|
||
|
|
setIntervalUnit={setIntervalUnit}
|
||
|
|
/>
|
||
|
|
</Col>
|
||
|
|
<Divider style={{ height: '100%', margin: '0' }} />
|
||
|
|
</Row>
|
||
|
|
<Row
|
||
|
|
className={styles['trace-detail-content-spacing']}
|
||
|
|
style={{ margin: '1.5rem 1rem 0.5rem' }}
|
||
|
|
>
|
||
|
|
<Col flex={`${SPAN_DETAILS_LEFT_COL_WIDTH}px`}>
|
||
|
|
{/* <Search
|
||
|
|
placeholder="Type to filter.."
|
||
|
|
allowClear
|
||
|
|
onSearch={onSearchHandler}
|
||
|
|
style={{ width: 200 }}
|
||
|
|
/> */}
|
||
|
|
</Col>
|
||
|
|
<Col flex={'auto'}>
|
||
|
|
<Space
|
||
|
|
style={{
|
||
|
|
float: 'right',
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
<Button onClick={onFocusSelectedSpanHandler} icon={<FilterOutlined />}>
|
||
|
|
Focus on selected span
|
||
|
|
</Button>
|
||
|
|
<Button type="default" onClick={onResetHandler}>
|
||
|
|
Reset Focus
|
||
|
|
</Button>
|
||
|
|
</Space>
|
||
|
|
</Col>
|
||
|
|
</Row>
|
||
|
|
<div
|
||
|
|
className={styles['trace-detail-content-spacing']}
|
||
|
|
style={{
|
||
|
|
display: 'flex',
|
||
|
|
flexDirection: 'column',
|
||
|
|
position: 'relative',
|
||
|
|
flex: 1,
|
||
|
|
overflowY: 'auto',
|
||
|
|
overflowX: 'hidden',
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
<GanttChart
|
||
|
|
traceMetaData={traceMetaData}
|
||
|
|
data={tree}
|
||
|
|
activeSelectedId={activeSelectedId}
|
||
|
|
activeHoverId={activeHoverId}
|
||
|
|
setActiveHoverId={setActiveHoverId}
|
||
|
|
setActiveSelectedId={setActiveSelectedId}
|
||
|
|
spanId={spanId || ''}
|
||
|
|
intervalUnit={intervalUnit}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
</Col>
|
||
|
|
<Col>
|
||
|
|
<Divider style={{ height: '100%', margin: '0' }} type="vertical" />
|
||
|
|
</Col>
|
||
|
|
<Col
|
||
|
|
md={5}
|
||
|
|
sm={5}
|
||
|
|
style={{
|
||
|
|
height: '100%',
|
||
|
|
position: 'relative',
|
||
|
|
display: 'flex',
|
||
|
|
flexDirection: 'column',
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
<SelectedSpanDetails tree={getSelectedNode} />
|
||
|
|
</Col>
|
||
|
|
</Row>
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
interface TraceDetailProps {
|
||
|
|
response: PayloadProps;
|
||
|
|
}
|
||
|
|
|
||
|
|
export default TraceDetail;
|