mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 06:29:02 +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 { useTranslation } from 'react-i18next';
|
||||
import { ITraceTag } from 'types/api/trace/getTraceItem';
|
||||
@ -7,7 +9,12 @@ import { ModalText } from '..';
|
||||
import { Container } from './styles';
|
||||
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 [allRenderedTags, setAllRenderedTags] = useState(tags);
|
||||
const isSearchVisible = useMemo(() => tags.length > 5, [tags]);
|
||||
@ -16,6 +23,16 @@ function Tags({ tags, onToggleHandler, setText }: TagsProps): JSX.Element {
|
||||
setAllRenderedTags(tags);
|
||||
}, [tags]);
|
||||
|
||||
const getLink = useCallback(
|
||||
(item: Record<string, string>) =>
|
||||
`${ROUTES.TRACE}/${item.TraceId}${formUrlParams({
|
||||
spanId: item.SpanId,
|
||||
levelUp: 0,
|
||||
levelDown: 0,
|
||||
})}`,
|
||||
[],
|
||||
);
|
||||
|
||||
const onChangeHandler = useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
const { value } = e.target;
|
||||
@ -38,7 +55,6 @@ function Tags({ tags, onToggleHandler, setText }: TagsProps): JSX.Element {
|
||||
onChange={onChangeHandler}
|
||||
/>
|
||||
)}
|
||||
|
||||
{allRenderedTags.map((tag) => (
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
interface TagsProps extends CommonTagsProps {
|
||||
tags: ITraceTag[];
|
||||
linkedSpans?: Record<string, string>[];
|
||||
}
|
||||
|
||||
export interface CommonTagsProps {
|
||||
@ -62,4 +90,8 @@ export interface CommonTagsProps {
|
||||
setText: React.Dispatch<React.SetStateAction<ModalText>>;
|
||||
}
|
||||
|
||||
Tags.defaultProps = {
|
||||
linkedSpans: [],
|
||||
};
|
||||
|
||||
export default Tags;
|
||||
|
@ -42,7 +42,7 @@ function SelectedSpanDetails(props: SelectedSpanDetailsProps): JSX.Element {
|
||||
return <div />;
|
||||
}
|
||||
|
||||
const { tags } = tree;
|
||||
const { tags, nonChildReferences } = tree;
|
||||
|
||||
return (
|
||||
<CardContainer>
|
||||
@ -83,7 +83,12 @@ function SelectedSpanDetails(props: SelectedSpanDetailsProps): JSX.Element {
|
||||
|
||||
<Tabs defaultActiveKey="1">
|
||||
<TabPane tab="Tags" key="1">
|
||||
<Tags onToggleHandler={onToggleHandler} setText={setText} tags={tags} />
|
||||
<Tags
|
||||
onToggleHandler={onToggleHandler}
|
||||
setText={setText}
|
||||
tags={tags}
|
||||
linkedSpans={nonChildReferences}
|
||||
/>
|
||||
</TabPane>
|
||||
<TabPane tab="Events" key="2">
|
||||
<Events
|
||||
|
@ -46,9 +46,10 @@ export interface ITraceTree {
|
||||
hasError?: boolean;
|
||||
event?: ITraceEvents[];
|
||||
isMissing?: boolean;
|
||||
childReferences?: Record<string, string>[];
|
||||
nonChildReferences?: Record<string, string>[];
|
||||
// For internal use
|
||||
isProcessed?: boolean;
|
||||
references?: Record<string, string>[];
|
||||
}
|
||||
|
||||
export interface ITraceTag {
|
||||
|
@ -5,10 +5,31 @@ Object {
|
||||
"missingSpanTree": Array [],
|
||||
"spanTree": Array [
|
||||
Object {
|
||||
"childReferences": Array [
|
||||
Object {
|
||||
"RefType": "CHILD_OF",
|
||||
"SpanId": "",
|
||||
"TraceId": "0000000000000000span_1",
|
||||
},
|
||||
],
|
||||
"children": Array [
|
||||
Object {
|
||||
"childReferences": Array [
|
||||
Object {
|
||||
"RefType": "CHILD_OF",
|
||||
"SpanId": "span_1",
|
||||
"TraceId": "0000000000000000span_1",
|
||||
},
|
||||
],
|
||||
"children": Array [
|
||||
Object {
|
||||
"childReferences": Array [
|
||||
Object {
|
||||
"RefType": "CHILD_OF",
|
||||
"SpanId": "span_2",
|
||||
"TraceId": "0000000000000000span_1",
|
||||
},
|
||||
],
|
||||
"children": Array [],
|
||||
"event": Array [
|
||||
Object {
|
||||
@ -25,13 +46,7 @@ Object {
|
||||
"id": "span_3",
|
||||
"isProcessed": true,
|
||||
"name": "HTTP GET SPAN 3",
|
||||
"references": Array [
|
||||
Object {
|
||||
"RefType": "CHILD_OF",
|
||||
"SpanId": "span_2",
|
||||
"TraceId": "0000000000000000span_1",
|
||||
},
|
||||
],
|
||||
"nonChildReferences": Array [],
|
||||
"serviceColour": "",
|
||||
"serviceName": "frontend",
|
||||
"startTime": 1657275433246,
|
||||
@ -60,13 +75,7 @@ Object {
|
||||
"id": "span_2",
|
||||
"isProcessed": true,
|
||||
"name": "HTTP GET SPAN 2",
|
||||
"references": Array [
|
||||
Object {
|
||||
"RefType": "CHILD_OF",
|
||||
"SpanId": "span_1",
|
||||
"TraceId": "0000000000000000span_1",
|
||||
},
|
||||
],
|
||||
"nonChildReferences": Array [],
|
||||
"serviceColour": "",
|
||||
"serviceName": "frontend",
|
||||
"startTime": 1657275433246,
|
||||
@ -94,13 +103,7 @@ Object {
|
||||
"hasError": false,
|
||||
"id": "span_1",
|
||||
"name": "HTTP GET SPAN 1",
|
||||
"references": Array [
|
||||
Object {
|
||||
"RefType": "CHILD_OF",
|
||||
"SpanId": "",
|
||||
"TraceId": "0000000000000000span_1",
|
||||
},
|
||||
],
|
||||
"nonChildReferences": Array [],
|
||||
"serviceColour": "",
|
||||
"serviceName": "frontend",
|
||||
"startTime": 1657275433246,
|
||||
@ -123,6 +126,13 @@ Object {
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"childReferences": Array [
|
||||
Object {
|
||||
"RefType": "CHILD_OF",
|
||||
"SpanId": "span_2",
|
||||
"TraceId": "0000000000000000span_1",
|
||||
},
|
||||
],
|
||||
"children": Array [],
|
||||
"event": Array [
|
||||
Object {
|
||||
@ -139,13 +149,7 @@ Object {
|
||||
"id": "span_3",
|
||||
"isProcessed": true,
|
||||
"name": "HTTP GET SPAN 3",
|
||||
"references": Array [
|
||||
Object {
|
||||
"RefType": "CHILD_OF",
|
||||
"SpanId": "span_2",
|
||||
"TraceId": "0000000000000000span_1",
|
||||
},
|
||||
],
|
||||
"nonChildReferences": Array [],
|
||||
"serviceColour": "",
|
||||
"serviceName": "frontend",
|
||||
"startTime": 1657275433246,
|
||||
@ -172,6 +176,13 @@ Object {
|
||||
],
|
||||
"spanTree": Array [
|
||||
Object {
|
||||
"childReferences": Array [
|
||||
Object {
|
||||
"RefType": "CHILD_OF",
|
||||
"SpanId": "",
|
||||
"TraceId": "0000000000000000span_1",
|
||||
},
|
||||
],
|
||||
"children": Array [],
|
||||
"event": Array [
|
||||
Object {
|
||||
@ -187,13 +198,7 @@ Object {
|
||||
"hasError": false,
|
||||
"id": "span_1",
|
||||
"name": "HTTP GET SPAN 1",
|
||||
"references": Array [
|
||||
Object {
|
||||
"RefType": "CHILD_OF",
|
||||
"SpanId": "",
|
||||
"TraceId": "0000000000000000span_1",
|
||||
},
|
||||
],
|
||||
"nonChildReferences": Array [],
|
||||
"serviceColour": "",
|
||||
"serviceName": "frontend",
|
||||
"startTime": 1657275433246,
|
||||
|
@ -4,8 +4,20 @@ import { ITraceForest, ITraceTree, Span } from 'types/api/trace/getTraceItem';
|
||||
|
||||
const getSpanReferences = (
|
||||
rawReferences: string[] = [],
|
||||
): Record<string, string>[] =>
|
||||
rawReferences.map((rawRef) => {
|
||||
isChildReference: boolean,
|
||||
): 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> = {};
|
||||
rawRef
|
||||
.replaceAll('{', '')
|
||||
@ -19,6 +31,7 @@ const getSpanReferences = (
|
||||
|
||||
return refObject;
|
||||
});
|
||||
};
|
||||
|
||||
// This getSpanTags is migrated from the previous implementation.
|
||||
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 spanMap: Record<string, ITraceTree> = {};
|
||||
|
||||
const createTarceRootSpan = (
|
||||
const createTraceRootSpan = (
|
||||
spanReferences: Record<string, string>[],
|
||||
): void => {
|
||||
spanReferences.forEach(({ SpanId, TraceId }) => {
|
||||
@ -64,7 +77,8 @@ export const spanToTreeUtil = (inputSpanList: Span[]): ITraceForest => {
|
||||
};
|
||||
|
||||
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 = {
|
||||
id: span[1],
|
||||
name: span[4],
|
||||
@ -76,16 +90,17 @@ export const spanToTreeUtil = (inputSpanList: Span[]): ITraceForest => {
|
||||
serviceName: span[3],
|
||||
hasError: !!span[11],
|
||||
serviceColour: '',
|
||||
event: span[10].map((e) => JSON.parse(e || '{}') || {}),
|
||||
references: spanReferences,
|
||||
event: span[10]?.map((e) => JSON.parse(e || '{}') || {}),
|
||||
childReferences,
|
||||
nonChildReferences,
|
||||
};
|
||||
spanMap[span[1]] = spanObject;
|
||||
});
|
||||
|
||||
for (const [, spanData] of Object.entries(spanMap)) {
|
||||
if (spanData.references) {
|
||||
createTarceRootSpan(spanData.references);
|
||||
spanData.references.forEach(({ SpanId: parentSpanId }) => {
|
||||
if (spanData.childReferences) {
|
||||
createTraceRootSpan(spanData.childReferences);
|
||||
spanData.childReferences.forEach(({ SpanId: parentSpanId }) => {
|
||||
if (spanMap[parentSpanId]) {
|
||||
spanData.isProcessed = true;
|
||||
spanMap[parentSpanId].children.push(spanData);
|
||||
@ -103,7 +118,9 @@ export const spanToTreeUtil = (inputSpanList: Span[]): ITraceForest => {
|
||||
const missingSpanTree: ITraceTree[] = [];
|
||||
const referencedTraceIds: string[] = Array.from(traceIdSet);
|
||||
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) {
|
||||
spanTree.push(spanMap[spanId]);
|
||||
return;
|
||||
|
Loading…
x
Reference in New Issue
Block a user