mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-13 18:46:01 +08:00
feat: add span links support (#2415)
* feat: add span links support * fix: handle an edge case * chore: test is fixed * chore: some of the refactoring is updated --------- Co-authored-by: Palash Gupta <palashgdev@gmail.com>
This commit is contained in:
parent
b99d7009a1
commit
2a5cb78964
@ -1,4 +1,6 @@
|
|||||||
import { Input, Typography } from 'antd';
|
import { Input, List, Typography } from 'antd';
|
||||||
|
import ROUTES from 'constants/routes';
|
||||||
|
import { formUrlParams } from 'container/TraceDetail/utils';
|
||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { ITraceTag } from 'types/api/trace/getTraceItem';
|
import { ITraceTag } from 'types/api/trace/getTraceItem';
|
||||||
@ -7,7 +9,12 @@ import { ModalText } from '..';
|
|||||||
import { Container } from './styles';
|
import { Container } from './styles';
|
||||||
import Tag from './Tag';
|
import Tag from './Tag';
|
||||||
|
|
||||||
function Tags({ tags, onToggleHandler, setText }: TagsProps): JSX.Element {
|
function Tags({
|
||||||
|
tags,
|
||||||
|
linkedSpans,
|
||||||
|
onToggleHandler,
|
||||||
|
setText,
|
||||||
|
}: TagsProps): JSX.Element {
|
||||||
const { t } = useTranslation(['traceDetails']);
|
const { t } = useTranslation(['traceDetails']);
|
||||||
const [allRenderedTags, setAllRenderedTags] = useState(tags);
|
const [allRenderedTags, setAllRenderedTags] = useState(tags);
|
||||||
const isSearchVisible = useMemo(() => tags.length > 5, [tags]);
|
const isSearchVisible = useMemo(() => tags.length > 5, [tags]);
|
||||||
@ -16,6 +23,16 @@ function Tags({ tags, onToggleHandler, setText }: TagsProps): JSX.Element {
|
|||||||
setAllRenderedTags(tags);
|
setAllRenderedTags(tags);
|
||||||
}, [tags]);
|
}, [tags]);
|
||||||
|
|
||||||
|
const getLink = useCallback(
|
||||||
|
(item: Record<string, string>) =>
|
||||||
|
`${ROUTES.TRACE}/${item.TraceId}${formUrlParams({
|
||||||
|
spanId: item.SpanId,
|
||||||
|
levelUp: 0,
|
||||||
|
levelDown: 0,
|
||||||
|
})}`,
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
const onChangeHandler = useCallback(
|
const onChangeHandler = useCallback(
|
||||||
(e: React.ChangeEvent<HTMLInputElement>): void => {
|
(e: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
const { value } = e.target;
|
const { value } = e.target;
|
||||||
@ -38,7 +55,6 @@ function Tags({ tags, onToggleHandler, setText }: TagsProps): JSX.Element {
|
|||||||
onChange={onChangeHandler}
|
onChange={onChangeHandler}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{allRenderedTags.map((tag) => (
|
{allRenderedTags.map((tag) => (
|
||||||
<Tag
|
<Tag
|
||||||
key={JSON.stringify(tag)}
|
key={JSON.stringify(tag)}
|
||||||
@ -49,12 +65,24 @@ function Tags({ tags, onToggleHandler, setText }: TagsProps): JSX.Element {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
{linkedSpans && linkedSpans.length > 0 && (
|
||||||
|
<List
|
||||||
|
header={<Typography.Title level={5}>Linked Spans</Typography.Title>}
|
||||||
|
dataSource={linkedSpans}
|
||||||
|
renderItem={(item): JSX.Element => (
|
||||||
|
<List.Item>
|
||||||
|
<Typography.Link href={getLink(item)}>{item.SpanId}</Typography.Link>
|
||||||
|
</List.Item>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TagsProps extends CommonTagsProps {
|
interface TagsProps extends CommonTagsProps {
|
||||||
tags: ITraceTag[];
|
tags: ITraceTag[];
|
||||||
|
linkedSpans?: Record<string, string>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CommonTagsProps {
|
export interface CommonTagsProps {
|
||||||
@ -62,4 +90,8 @@ export interface CommonTagsProps {
|
|||||||
setText: React.Dispatch<React.SetStateAction<ModalText>>;
|
setText: React.Dispatch<React.SetStateAction<ModalText>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Tags.defaultProps = {
|
||||||
|
linkedSpans: [],
|
||||||
|
};
|
||||||
|
|
||||||
export default Tags;
|
export default Tags;
|
||||||
|
@ -42,7 +42,7 @@ function SelectedSpanDetails(props: SelectedSpanDetailsProps): JSX.Element {
|
|||||||
return <div />;
|
return <div />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { tags } = tree;
|
const { tags, nonChildReferences } = tree;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CardContainer>
|
<CardContainer>
|
||||||
@ -83,7 +83,12 @@ function SelectedSpanDetails(props: SelectedSpanDetailsProps): JSX.Element {
|
|||||||
|
|
||||||
<Tabs defaultActiveKey="1">
|
<Tabs defaultActiveKey="1">
|
||||||
<TabPane tab="Tags" key="1">
|
<TabPane tab="Tags" key="1">
|
||||||
<Tags onToggleHandler={onToggleHandler} setText={setText} tags={tags} />
|
<Tags
|
||||||
|
onToggleHandler={onToggleHandler}
|
||||||
|
setText={setText}
|
||||||
|
tags={tags}
|
||||||
|
linkedSpans={nonChildReferences}
|
||||||
|
/>
|
||||||
</TabPane>
|
</TabPane>
|
||||||
<TabPane tab="Events" key="2">
|
<TabPane tab="Events" key="2">
|
||||||
<Events
|
<Events
|
||||||
|
@ -46,9 +46,10 @@ export interface ITraceTree {
|
|||||||
hasError?: boolean;
|
hasError?: boolean;
|
||||||
event?: ITraceEvents[];
|
event?: ITraceEvents[];
|
||||||
isMissing?: boolean;
|
isMissing?: boolean;
|
||||||
|
childReferences?: Record<string, string>[];
|
||||||
|
nonChildReferences?: Record<string, string>[];
|
||||||
// For internal use
|
// For internal use
|
||||||
isProcessed?: boolean;
|
isProcessed?: boolean;
|
||||||
references?: Record<string, string>[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ITraceTag {
|
export interface ITraceTag {
|
||||||
|
@ -5,10 +5,31 @@ Object {
|
|||||||
"missingSpanTree": Array [],
|
"missingSpanTree": Array [],
|
||||||
"spanTree": Array [
|
"spanTree": Array [
|
||||||
Object {
|
Object {
|
||||||
|
"childReferences": Array [
|
||||||
|
Object {
|
||||||
|
"RefType": "CHILD_OF",
|
||||||
|
"SpanId": "",
|
||||||
|
"TraceId": "0000000000000000span_1",
|
||||||
|
},
|
||||||
|
],
|
||||||
"children": Array [
|
"children": Array [
|
||||||
Object {
|
Object {
|
||||||
|
"childReferences": Array [
|
||||||
|
Object {
|
||||||
|
"RefType": "CHILD_OF",
|
||||||
|
"SpanId": "span_1",
|
||||||
|
"TraceId": "0000000000000000span_1",
|
||||||
|
},
|
||||||
|
],
|
||||||
"children": Array [
|
"children": Array [
|
||||||
Object {
|
Object {
|
||||||
|
"childReferences": Array [
|
||||||
|
Object {
|
||||||
|
"RefType": "CHILD_OF",
|
||||||
|
"SpanId": "span_2",
|
||||||
|
"TraceId": "0000000000000000span_1",
|
||||||
|
},
|
||||||
|
],
|
||||||
"children": Array [],
|
"children": Array [],
|
||||||
"event": Array [
|
"event": Array [
|
||||||
Object {
|
Object {
|
||||||
@ -25,13 +46,7 @@ Object {
|
|||||||
"id": "span_3",
|
"id": "span_3",
|
||||||
"isProcessed": true,
|
"isProcessed": true,
|
||||||
"name": "HTTP GET SPAN 3",
|
"name": "HTTP GET SPAN 3",
|
||||||
"references": Array [
|
"nonChildReferences": Array [],
|
||||||
Object {
|
|
||||||
"RefType": "CHILD_OF",
|
|
||||||
"SpanId": "span_2",
|
|
||||||
"TraceId": "0000000000000000span_1",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"serviceColour": "",
|
"serviceColour": "",
|
||||||
"serviceName": "frontend",
|
"serviceName": "frontend",
|
||||||
"startTime": 1657275433246,
|
"startTime": 1657275433246,
|
||||||
@ -60,13 +75,7 @@ Object {
|
|||||||
"id": "span_2",
|
"id": "span_2",
|
||||||
"isProcessed": true,
|
"isProcessed": true,
|
||||||
"name": "HTTP GET SPAN 2",
|
"name": "HTTP GET SPAN 2",
|
||||||
"references": Array [
|
"nonChildReferences": Array [],
|
||||||
Object {
|
|
||||||
"RefType": "CHILD_OF",
|
|
||||||
"SpanId": "span_1",
|
|
||||||
"TraceId": "0000000000000000span_1",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"serviceColour": "",
|
"serviceColour": "",
|
||||||
"serviceName": "frontend",
|
"serviceName": "frontend",
|
||||||
"startTime": 1657275433246,
|
"startTime": 1657275433246,
|
||||||
@ -94,13 +103,7 @@ Object {
|
|||||||
"hasError": false,
|
"hasError": false,
|
||||||
"id": "span_1",
|
"id": "span_1",
|
||||||
"name": "HTTP GET SPAN 1",
|
"name": "HTTP GET SPAN 1",
|
||||||
"references": Array [
|
"nonChildReferences": Array [],
|
||||||
Object {
|
|
||||||
"RefType": "CHILD_OF",
|
|
||||||
"SpanId": "",
|
|
||||||
"TraceId": "0000000000000000span_1",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"serviceColour": "",
|
"serviceColour": "",
|
||||||
"serviceName": "frontend",
|
"serviceName": "frontend",
|
||||||
"startTime": 1657275433246,
|
"startTime": 1657275433246,
|
||||||
@ -123,6 +126,13 @@ Object {
|
|||||||
Object {
|
Object {
|
||||||
"children": Array [
|
"children": Array [
|
||||||
Object {
|
Object {
|
||||||
|
"childReferences": Array [
|
||||||
|
Object {
|
||||||
|
"RefType": "CHILD_OF",
|
||||||
|
"SpanId": "span_2",
|
||||||
|
"TraceId": "0000000000000000span_1",
|
||||||
|
},
|
||||||
|
],
|
||||||
"children": Array [],
|
"children": Array [],
|
||||||
"event": Array [
|
"event": Array [
|
||||||
Object {
|
Object {
|
||||||
@ -139,13 +149,7 @@ Object {
|
|||||||
"id": "span_3",
|
"id": "span_3",
|
||||||
"isProcessed": true,
|
"isProcessed": true,
|
||||||
"name": "HTTP GET SPAN 3",
|
"name": "HTTP GET SPAN 3",
|
||||||
"references": Array [
|
"nonChildReferences": Array [],
|
||||||
Object {
|
|
||||||
"RefType": "CHILD_OF",
|
|
||||||
"SpanId": "span_2",
|
|
||||||
"TraceId": "0000000000000000span_1",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"serviceColour": "",
|
"serviceColour": "",
|
||||||
"serviceName": "frontend",
|
"serviceName": "frontend",
|
||||||
"startTime": 1657275433246,
|
"startTime": 1657275433246,
|
||||||
@ -172,6 +176,13 @@ Object {
|
|||||||
],
|
],
|
||||||
"spanTree": Array [
|
"spanTree": Array [
|
||||||
Object {
|
Object {
|
||||||
|
"childReferences": Array [
|
||||||
|
Object {
|
||||||
|
"RefType": "CHILD_OF",
|
||||||
|
"SpanId": "",
|
||||||
|
"TraceId": "0000000000000000span_1",
|
||||||
|
},
|
||||||
|
],
|
||||||
"children": Array [],
|
"children": Array [],
|
||||||
"event": Array [
|
"event": Array [
|
||||||
Object {
|
Object {
|
||||||
@ -187,13 +198,7 @@ Object {
|
|||||||
"hasError": false,
|
"hasError": false,
|
||||||
"id": "span_1",
|
"id": "span_1",
|
||||||
"name": "HTTP GET SPAN 1",
|
"name": "HTTP GET SPAN 1",
|
||||||
"references": Array [
|
"nonChildReferences": Array [],
|
||||||
Object {
|
|
||||||
"RefType": "CHILD_OF",
|
|
||||||
"SpanId": "",
|
|
||||||
"TraceId": "0000000000000000span_1",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"serviceColour": "",
|
"serviceColour": "",
|
||||||
"serviceName": "frontend",
|
"serviceName": "frontend",
|
||||||
"startTime": 1657275433246,
|
"startTime": 1657275433246,
|
||||||
|
@ -4,8 +4,20 @@ import { ITraceForest, ITraceTree, Span } from 'types/api/trace/getTraceItem';
|
|||||||
|
|
||||||
const getSpanReferences = (
|
const getSpanReferences = (
|
||||||
rawReferences: string[] = [],
|
rawReferences: string[] = [],
|
||||||
): Record<string, string>[] =>
|
isChildReference: boolean,
|
||||||
rawReferences.map((rawRef) => {
|
): Record<string, string>[] => {
|
||||||
|
let filteredReferences = [];
|
||||||
|
if (isChildReference) {
|
||||||
|
filteredReferences = rawReferences.filter((value) =>
|
||||||
|
value.includes('CHILD_OF'),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
filteredReferences = rawReferences.filter(
|
||||||
|
(value) => !value.includes('CHILD_OF'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return filteredReferences.map((rawRef) => {
|
||||||
const refObject: Record<string, string> = {};
|
const refObject: Record<string, string> = {};
|
||||||
rawRef
|
rawRef
|
||||||
.replaceAll('{', '')
|
.replaceAll('{', '')
|
||||||
@ -19,6 +31,7 @@ const getSpanReferences = (
|
|||||||
|
|
||||||
return refObject;
|
return refObject;
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// This getSpanTags is migrated from the previous implementation.
|
// This getSpanTags is migrated from the previous implementation.
|
||||||
const getSpanTags = (spanData: Span): { key: string; value: string }[] => {
|
const getSpanTags = (spanData: Span): { key: string; value: string }[] => {
|
||||||
@ -41,7 +54,7 @@ export const spanToTreeUtil = (inputSpanList: Span[]): ITraceForest => {
|
|||||||
const traceIdSet: Set<string> = new Set();
|
const traceIdSet: Set<string> = new Set();
|
||||||
const spanMap: Record<string, ITraceTree> = {};
|
const spanMap: Record<string, ITraceTree> = {};
|
||||||
|
|
||||||
const createTarceRootSpan = (
|
const createTraceRootSpan = (
|
||||||
spanReferences: Record<string, string>[],
|
spanReferences: Record<string, string>[],
|
||||||
): void => {
|
): void => {
|
||||||
spanReferences.forEach(({ SpanId, TraceId }) => {
|
spanReferences.forEach(({ SpanId, TraceId }) => {
|
||||||
@ -64,7 +77,8 @@ export const spanToTreeUtil = (inputSpanList: Span[]): ITraceForest => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
spanList.forEach((span) => {
|
spanList.forEach((span) => {
|
||||||
const spanReferences = getSpanReferences(span[9] as string[]);
|
const childReferences = getSpanReferences(span[9] as string[], true);
|
||||||
|
const nonChildReferences = getSpanReferences(span[9] as string[], false);
|
||||||
const spanObject = {
|
const spanObject = {
|
||||||
id: span[1],
|
id: span[1],
|
||||||
name: span[4],
|
name: span[4],
|
||||||
@ -76,16 +90,17 @@ export const spanToTreeUtil = (inputSpanList: Span[]): ITraceForest => {
|
|||||||
serviceName: span[3],
|
serviceName: span[3],
|
||||||
hasError: !!span[11],
|
hasError: !!span[11],
|
||||||
serviceColour: '',
|
serviceColour: '',
|
||||||
event: span[10].map((e) => JSON.parse(e || '{}') || {}),
|
event: span[10]?.map((e) => JSON.parse(e || '{}') || {}),
|
||||||
references: spanReferences,
|
childReferences,
|
||||||
|
nonChildReferences,
|
||||||
};
|
};
|
||||||
spanMap[span[1]] = spanObject;
|
spanMap[span[1]] = spanObject;
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const [, spanData] of Object.entries(spanMap)) {
|
for (const [, spanData] of Object.entries(spanMap)) {
|
||||||
if (spanData.references) {
|
if (spanData.childReferences) {
|
||||||
createTarceRootSpan(spanData.references);
|
createTraceRootSpan(spanData.childReferences);
|
||||||
spanData.references.forEach(({ SpanId: parentSpanId }) => {
|
spanData.childReferences.forEach(({ SpanId: parentSpanId }) => {
|
||||||
if (spanMap[parentSpanId]) {
|
if (spanMap[parentSpanId]) {
|
||||||
spanData.isProcessed = true;
|
spanData.isProcessed = true;
|
||||||
spanMap[parentSpanId].children.push(spanData);
|
spanMap[parentSpanId].children.push(spanData);
|
||||||
@ -103,7 +118,9 @@ export const spanToTreeUtil = (inputSpanList: Span[]): ITraceForest => {
|
|||||||
const missingSpanTree: ITraceTree[] = [];
|
const missingSpanTree: ITraceTree[] = [];
|
||||||
const referencedTraceIds: string[] = Array.from(traceIdSet);
|
const referencedTraceIds: string[] = Array.from(traceIdSet);
|
||||||
Object.keys(spanMap).forEach((spanId) => {
|
Object.keys(spanMap).forEach((spanId) => {
|
||||||
const isRoot = spanMap[spanId].references?.some((refs) => refs.SpanId === '');
|
const isRoot = spanMap[spanId].childReferences?.some(
|
||||||
|
(refs) => refs.SpanId === '',
|
||||||
|
);
|
||||||
if (isRoot) {
|
if (isRoot) {
|
||||||
spanTree.push(spanMap[spanId]);
|
spanTree.push(spanMap[spanId]);
|
||||||
return;
|
return;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user