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:
Vishal Sharma 2023-03-03 14:35:11 +05:30 committed by GitHub
parent b99d7009a1
commit 2a5cb78964
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 111 additions and 51 deletions

View File

@ -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;

View File

@ -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

View File

@ -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 {

View File

@ -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,

View File

@ -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;