mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
TraceView: Reworked header (#63105)
* Reworked header * Remove toggle from merge * Update test * Update how span is retrived * Tests * Update tests * Move new trace page header into its own component * Remove tests already covered in TracePageHeader.test.tsx * Update findHeaderTags * Tooltip updates
This commit is contained in:
parent
16503a719b
commit
82bcfb4928
@ -67,6 +67,7 @@ Alpha features might be changed or removed without prior notice.
|
||||
| `storage` | Configurable storage for dashboards, datasources, and resources |
|
||||
| `exploreMixedDatasource` | Enable mixed datasource in Explore |
|
||||
| `tracing` | Adds trace ID to error notifications |
|
||||
| `newTraceView` | Shows the new trace view design |
|
||||
| `correlations` | Correlations page |
|
||||
| `datasourceQueryMultiStatus` | Introduce HTTP 207 Multi Status for api/ds/query |
|
||||
| `traceToMetrics` | Enable trace to metrics links |
|
||||
|
@ -41,6 +41,7 @@ export interface FeatureToggles {
|
||||
export?: boolean;
|
||||
exploreMixedDatasource?: boolean;
|
||||
tracing?: boolean;
|
||||
newTraceView?: boolean;
|
||||
correlations?: boolean;
|
||||
cloudWatchDynamicLabels?: boolean;
|
||||
datasourceQueryMultiStatus?: boolean;
|
||||
|
@ -143,6 +143,12 @@ var (
|
||||
State: FeatureStateAlpha,
|
||||
FrontendOnly: true,
|
||||
},
|
||||
{
|
||||
Name: "newTraceView",
|
||||
Description: "Shows the new trace view design",
|
||||
State: FeatureStateAlpha,
|
||||
FrontendOnly: true,
|
||||
},
|
||||
{
|
||||
Name: "correlations",
|
||||
Description: "Correlations page",
|
||||
|
@ -107,6 +107,10 @@ const (
|
||||
// Adds trace ID to error notifications
|
||||
FlagTracing = "tracing"
|
||||
|
||||
// FlagNewTraceView
|
||||
// Shows the new trace view design
|
||||
FlagNewTraceView = "newTraceView"
|
||||
|
||||
// FlagCorrelations
|
||||
// Correlations page
|
||||
FlagCorrelations = "correlations"
|
||||
|
@ -13,7 +13,7 @@ import {
|
||||
PanelData,
|
||||
SplitOpen,
|
||||
} from '@grafana/data';
|
||||
import { getTemplateSrv } from '@grafana/runtime';
|
||||
import { config, getTemplateSrv } from '@grafana/runtime';
|
||||
import { DataQuery } from '@grafana/schema';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
import { getTraceToLogsOptions, TraceToLogsData } from 'app/core/components/TraceToLogs/TraceToLogsSettings';
|
||||
@ -26,7 +26,14 @@ import { ExploreId } from 'app/types/explore';
|
||||
|
||||
import { changePanelState } from '../state/explorePane';
|
||||
|
||||
import { SpanBarOptionsData, Trace, TracePageHeader, TraceTimelineViewer, TTraceTimeline } from './components';
|
||||
import {
|
||||
SpanBarOptionsData,
|
||||
Trace,
|
||||
TracePageHeader,
|
||||
NewTracePageHeader,
|
||||
TraceTimelineViewer,
|
||||
TTraceTimeline,
|
||||
} from './components';
|
||||
import { TopOfViewRefType } from './components/TraceTimelineViewer/VirtualizedTraceView';
|
||||
import { createSpanLinkFactory } from './createSpanLink';
|
||||
import { useChildrenState } from './useChildrenState';
|
||||
@ -134,13 +141,23 @@ export function TraceView(props: Props) {
|
||||
<>
|
||||
{props.dataFrames?.length && props.dataFrames[0]?.meta?.preferredVisualisationType === 'trace' && traceProp ? (
|
||||
<>
|
||||
<TracePageHeader
|
||||
trace={traceProp}
|
||||
updateNextViewRangeTime={updateNextViewRangeTime}
|
||||
updateViewRangeTime={updateViewRangeTime}
|
||||
viewRange={viewRange}
|
||||
timeZone={timeZone}
|
||||
/>
|
||||
{config.featureToggles.newTraceView ? (
|
||||
<NewTracePageHeader
|
||||
trace={traceProp}
|
||||
updateNextViewRangeTime={updateNextViewRangeTime}
|
||||
updateViewRangeTime={updateViewRangeTime}
|
||||
viewRange={viewRange}
|
||||
timeZone={timeZone}
|
||||
/>
|
||||
) : (
|
||||
<TracePageHeader
|
||||
trace={traceProp}
|
||||
updateNextViewRangeTime={updateNextViewRangeTime}
|
||||
updateViewRangeTime={updateViewRangeTime}
|
||||
viewRange={viewRange}
|
||||
timeZone={timeZone}
|
||||
/>
|
||||
)}
|
||||
<TraceTimelineViewer
|
||||
registerAccessors={noop}
|
||||
scrollToFirstVisibleSpan={noop}
|
||||
|
@ -0,0 +1,56 @@
|
||||
// Copyright (c) 2017 Uber Technologies, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { getAllByText, getByText, render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
import config from 'app/core/config';
|
||||
|
||||
import { NewTracePageHeader } from './NewTracePageHeader';
|
||||
import { TracePageHeaderEmbedProps } from './TracePageHeader';
|
||||
import { trace } from './TracePageHeader.test';
|
||||
|
||||
const setup = (propOverrides?: TracePageHeaderEmbedProps) => {
|
||||
const defaultProps = {
|
||||
trace,
|
||||
timeZone: '',
|
||||
viewRange: { time: { current: [10, 20] as [number, number] } },
|
||||
updateNextViewRangeTime: () => {},
|
||||
updateViewRangeTime: () => {},
|
||||
...propOverrides,
|
||||
};
|
||||
|
||||
return render(<NewTracePageHeader {...defaultProps} />);
|
||||
};
|
||||
|
||||
describe('NewTracePageHeader test', () => {
|
||||
it('should render the new trace header', () => {
|
||||
config.featureToggles.newTraceView = true;
|
||||
setup();
|
||||
|
||||
const header = document.querySelector('header');
|
||||
const method = getByText(header!, 'POST');
|
||||
const status = getByText(header!, '200');
|
||||
const url = getByText(header!, '/v2/gamma/792edh2w897y2huehd2h89');
|
||||
const duration = getAllByText(header!, '2.36s');
|
||||
const timestampPart1 = getByText(header!, '2023-02-05 08:50');
|
||||
const timestampPart2 = getByText(header!, ':56.289');
|
||||
expect(method).toBeInTheDocument();
|
||||
expect(status).toBeInTheDocument();
|
||||
expect(url).toBeInTheDocument();
|
||||
expect(duration.length).toBe(2);
|
||||
expect(timestampPart1).toBeInTheDocument();
|
||||
expect(timestampPart2).toBeInTheDocument();
|
||||
});
|
||||
});
|
@ -0,0 +1,130 @@
|
||||
// Copyright (c) 2017 Uber Technologies, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { css } from '@emotion/css';
|
||||
import cx from 'classnames';
|
||||
import { get as _get, maxBy as _maxBy, values as _values } from 'lodash';
|
||||
import * as React from 'react';
|
||||
|
||||
import { Badge, BadgeColor, Tooltip, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import ExternalLinks from '../common/ExternalLinks';
|
||||
import TraceName from '../common/TraceName';
|
||||
import { getTraceLinks } from '../model/link-patterns';
|
||||
import { getHeaderTags, getTraceName } from '../model/trace-viewer';
|
||||
import { formatDuration } from '../utils/date';
|
||||
|
||||
import SpanGraph from './SpanGraph';
|
||||
import { TracePageHeaderEmbedProps, timestamp, getStyles } from './TracePageHeader';
|
||||
|
||||
const getNewStyles = () => {
|
||||
return {
|
||||
subtitle: css`
|
||||
flex: 1;
|
||||
line-height: 1em;
|
||||
margin: -0.5em 0 1.5em 0.5em;
|
||||
`,
|
||||
tag: css`
|
||||
margin: 0 0.5em 0 0;
|
||||
`,
|
||||
url: css`
|
||||
margin: -2.5px 0.3em;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 30%;
|
||||
display: inline-block;
|
||||
`,
|
||||
divider: css`
|
||||
margin: 0 0.75em;
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export function NewTracePageHeader(props: TracePageHeaderEmbedProps) {
|
||||
const { trace, updateNextViewRangeTime, updateViewRangeTime, viewRange, timeZone } = props;
|
||||
|
||||
const styles = { ...useStyles2(getStyles), ...useStyles2(getNewStyles) };
|
||||
const links = React.useMemo(() => {
|
||||
if (!trace) {
|
||||
return [];
|
||||
}
|
||||
return getTraceLinks(trace);
|
||||
}, [trace]);
|
||||
|
||||
if (!trace) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { method, status, url } = getHeaderTags(trace.spans);
|
||||
|
||||
const title = (
|
||||
<h1 className={cx(styles.TracePageHeaderTitle)}>
|
||||
<TraceName traceName={getTraceName(trace.spans)} />
|
||||
<small>
|
||||
<span className={styles.divider}>|</span>
|
||||
{formatDuration(trace.duration)}
|
||||
</small>
|
||||
</h1>
|
||||
);
|
||||
|
||||
let statusColor: BadgeColor = 'green';
|
||||
if (status && status.length > 0 && Number.isInteger(status[0].value)) {
|
||||
if (status[0].value.toString().charAt(0) === '4') {
|
||||
statusColor = 'orange';
|
||||
} else if (status[0].value.toString().charAt(0) === '5') {
|
||||
statusColor = 'red';
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<header className={styles.TracePageHeader}>
|
||||
<div className={styles.TracePageHeaderTitleRow}>
|
||||
{links && links.length > 0 && <ExternalLinks links={links} className={styles.TracePageHeaderBack} />}
|
||||
{title}
|
||||
</div>
|
||||
|
||||
<div className={styles.subtitle}>
|
||||
{timestamp(trace, timeZone, styles)}
|
||||
{method || status || url ? <span className={styles.divider}>|</span> : undefined}
|
||||
{method && method.length > 0 && (
|
||||
<Tooltip content={'http.method'} interactive={true}>
|
||||
<span className={styles.tag}>
|
||||
<Badge text={method[0].value} color="blue" />
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
{status && status.length > 0 && (
|
||||
<Tooltip content={'http.status_code'} interactive={true}>
|
||||
<span className={styles.tag}>
|
||||
<Badge text={status[0].value} color={statusColor} />
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
{url && url.length > 0 && (
|
||||
<Tooltip content={'http.url or http.target or http.path'} interactive={true}>
|
||||
<span className={styles.url}>{url[0].value}</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<SpanGraph
|
||||
trace={trace}
|
||||
viewRange={viewRange}
|
||||
updateNextViewRangeTime={updateNextViewRangeTime}
|
||||
updateViewRangeTime={updateViewRangeTime}
|
||||
/>
|
||||
</header>
|
||||
);
|
||||
}
|
@ -15,22 +15,87 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
import traceGenerator from '../demo/trace-generators';
|
||||
import { getTraceName } from '../model/trace-viewer';
|
||||
import transformTraceData from '../model/transform-trace-data';
|
||||
|
||||
import TracePageHeader, { TracePageHeaderEmbedProps } from './TracePageHeader';
|
||||
|
||||
const trace = transformTraceData(traceGenerator.trace({}));
|
||||
export const trace = {
|
||||
services: [{ name: 'serviceA', numberOfSpans: 1 }],
|
||||
spans: [
|
||||
{
|
||||
traceID: '164afda25df92413',
|
||||
spanID: '164afda25df92413',
|
||||
operationName: 'HTTP Client',
|
||||
serviceName: 'serviceA',
|
||||
subsidiarilyReferencedBy: [],
|
||||
startTime: 1675602037286989,
|
||||
duration: 5685,
|
||||
logs: [],
|
||||
references: [],
|
||||
tags: [],
|
||||
processID: '164afda25df92413',
|
||||
flags: 0,
|
||||
process: {
|
||||
serviceName: 'lb',
|
||||
tags: [],
|
||||
},
|
||||
relativeStartTime: 0,
|
||||
depth: 0,
|
||||
hasChildren: false,
|
||||
childSpanCount: 0,
|
||||
warnings: [],
|
||||
},
|
||||
{
|
||||
traceID: '164afda25df92413',
|
||||
spanID: '164afda25df92413',
|
||||
operationName: 'HTTP Client',
|
||||
serviceName: 'serviceB',
|
||||
subsidiarilyReferencedBy: [],
|
||||
startTime: 1675602037286989,
|
||||
duration: 5685,
|
||||
logs: [],
|
||||
references: [],
|
||||
tags: [
|
||||
{
|
||||
key: 'http.url',
|
||||
type: 'String',
|
||||
value: `/v2/gamma/792edh2w897y2huehd2h89`,
|
||||
},
|
||||
{
|
||||
key: 'http.method',
|
||||
type: 'String',
|
||||
value: `POST`,
|
||||
},
|
||||
{
|
||||
key: 'http.status_code',
|
||||
type: 'String',
|
||||
value: `200`,
|
||||
},
|
||||
],
|
||||
processID: '164afda25df92413',
|
||||
flags: 0,
|
||||
process: {
|
||||
serviceName: 'lb',
|
||||
tags: [],
|
||||
},
|
||||
relativeStartTime: 0,
|
||||
depth: 0,
|
||||
hasChildren: false,
|
||||
childSpanCount: 0,
|
||||
warnings: [],
|
||||
},
|
||||
],
|
||||
traceID: '8bb35a31-eb64-512d-aaed-ddd61887bb2b',
|
||||
traceName: 'serviceA: GET',
|
||||
processes: {},
|
||||
duration: 2355515,
|
||||
startTime: 1675605056289000,
|
||||
endTime: 1675605058644515,
|
||||
};
|
||||
|
||||
const setup = (propOverrides?: TracePageHeaderEmbedProps) => {
|
||||
const defaultProps = {
|
||||
canCollapse: false,
|
||||
hideSummary: false,
|
||||
onSlimViewClicked: () => {},
|
||||
onTraceGraphViewClicked: () => {},
|
||||
slimView: false,
|
||||
trace,
|
||||
hideMap: false,
|
||||
timeZone: '',
|
||||
viewRange: { time: { current: [10, 20] as [number, number] } },
|
||||
updateNextViewRangeTime: () => {},
|
||||
@ -90,7 +155,7 @@ describe('TracePageHeader test', () => {
|
||||
{...({
|
||||
trace: trace,
|
||||
viewRange: { time: { current: [10, 20] } },
|
||||
} as TracePageHeaderEmbedProps)}
|
||||
} as unknown as TracePageHeaderEmbedProps)}
|
||||
/>
|
||||
);
|
||||
expect(screen.queryAllByRole('listitem')).toHaveLength(5);
|
||||
|
@ -32,17 +32,11 @@ import { formatDuration } from '../utils/date';
|
||||
|
||||
import SpanGraph from './SpanGraph';
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
export const getStyles = (theme: GrafanaTheme2) => {
|
||||
return {
|
||||
theme,
|
||||
TracePageHeader: css`
|
||||
label: TracePageHeader;
|
||||
& > :first-child {
|
||||
border-bottom: 1px solid ${autoColor(theme, '#e8e8e8')};
|
||||
}
|
||||
& > :nth-child(2) {
|
||||
background-color: ${autoColor(theme, '#eee')};
|
||||
border-bottom: 1px solid ${autoColor(theme, '#e4e4e4')};
|
||||
}
|
||||
& > :last-child {
|
||||
border-bottom: 1px solid ${autoColor(theme, '#ccc')};
|
||||
}
|
||||
@ -75,12 +69,13 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
flex: 1;
|
||||
font-size: 1.7em;
|
||||
line-height: 1em;
|
||||
margin: 0 0 0 0.5em;
|
||||
margin: 0 0 0 0.3em;
|
||||
padding-bottom: 0.5em;
|
||||
`,
|
||||
TracePageHeaderOverviewItems: css`
|
||||
label: TracePageHeaderOverviewItems;
|
||||
border-bottom: 1px solid #e4e4e4;
|
||||
background-color: ${autoColor(theme, '#eee')};
|
||||
border-bottom: 1px solid ${autoColor(theme, '#e4e4e4')};
|
||||
padding: 0.25rem 0.5rem !important;
|
||||
`,
|
||||
TracePageHeaderOverviewItemValueDetail: cx(
|
||||
@ -105,6 +100,9 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
label: TracePageHeaderTraceId;
|
||||
white-space: nowrap;
|
||||
`,
|
||||
titleBorderBottom: css`
|
||||
border-bottom: 1px solid ${autoColor(theme, '#e8e8e8')};
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
@ -116,23 +114,25 @@ export type TracePageHeaderEmbedProps = {
|
||||
timeZone: TimeZone;
|
||||
};
|
||||
|
||||
export const timestamp = (trace: Trace, timeZone: TimeZone, styles: ReturnType<typeof getStyles>) => {
|
||||
// Convert date from micro to milli seconds
|
||||
const dateStr = dateTimeFormat(trace.startTime / 1000, { timeZone, defaultWithMS: true });
|
||||
const match = dateStr.match(/^(.+)(:\d\d\.\d+)$/);
|
||||
return match ? (
|
||||
<span className={styles.TracePageHeaderOverviewItemValue}>
|
||||
{match[1]}
|
||||
<span className={styles.TracePageHeaderOverviewItemValueDetail}>{match[2]}</span>
|
||||
</span>
|
||||
) : (
|
||||
dateStr
|
||||
);
|
||||
};
|
||||
|
||||
export const HEADER_ITEMS = [
|
||||
{
|
||||
key: 'timestamp',
|
||||
label: 'Trace Start:',
|
||||
renderer(trace: Trace, timeZone: TimeZone, styles: ReturnType<typeof getStyles>) {
|
||||
// Convert date from micro to milli seconds
|
||||
const dateStr = dateTimeFormat(trace.startTime / 1000, { timeZone, defaultWithMS: true });
|
||||
const match = dateStr.match(/^(.+)(:\d\d\.\d+)$/);
|
||||
return match ? (
|
||||
<span className={styles.TracePageHeaderOverviewItemValue}>
|
||||
{match[1]}
|
||||
<span className={styles.TracePageHeaderOverviewItemValueDetail}>{match[2]}</span>
|
||||
</span>
|
||||
) : (
|
||||
dateStr
|
||||
);
|
||||
},
|
||||
renderer: timestamp,
|
||||
},
|
||||
{
|
||||
key: 'duration',
|
||||
@ -185,7 +185,7 @@ export default function TracePageHeader(props: TracePageHeaderEmbedProps) {
|
||||
|
||||
return (
|
||||
<header className={styles.TracePageHeader}>
|
||||
<div className={styles.TracePageHeaderTitleRow}>
|
||||
<div className={cx(styles.TracePageHeaderTitleRow, styles.titleBorderBottom)}>
|
||||
{links && links.length > 0 && <ExternalLinks links={links} className={styles.TracePageHeaderBack} />}
|
||||
{title}
|
||||
</div>
|
||||
|
@ -13,3 +13,4 @@
|
||||
// limitations under the License.
|
||||
|
||||
export { default } from './TracePageHeader';
|
||||
export { NewTracePageHeader } from './NewTracePageHeader';
|
||||
|
@ -1,5 +1,6 @@
|
||||
export { default as TraceTimelineViewer } from './TraceTimelineViewer';
|
||||
export { default as TracePageHeader } from './TracePageHeader';
|
||||
export { NewTracePageHeader } from './TracePageHeader';
|
||||
export { default as SpanBarSettings } from './settings/SpanBarSettings';
|
||||
export * from './types';
|
||||
export * from './TraceTimelineViewer/types';
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
import { TraceSpan } from '../types';
|
||||
|
||||
import { _getTraceNameImpl as getTraceName } from './trace-viewer';
|
||||
import { getHeaderTags, _getTraceNameImpl as getTraceName } from './trace-viewer';
|
||||
|
||||
describe('getTraceName', () => {
|
||||
const firstSpanId = 'firstSpanId';
|
||||
@ -221,6 +221,50 @@ describe('getTraceName', () => {
|
||||
],
|
||||
},
|
||||
];
|
||||
const spansWithHeaderTags = [
|
||||
{
|
||||
spanID: firstSpanId,
|
||||
traceID: currentTraceId,
|
||||
startTime: t + 200,
|
||||
process: {},
|
||||
references: [],
|
||||
tags: [],
|
||||
},
|
||||
{
|
||||
spanID: secondSpanId,
|
||||
traceID: currentTraceId,
|
||||
startTime: t + 100,
|
||||
process: {},
|
||||
references: [],
|
||||
tags: [
|
||||
{
|
||||
key: 'http.method',
|
||||
value: 'POST',
|
||||
},
|
||||
{
|
||||
key: 'http.status_code',
|
||||
value: '200',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
spanID: thirdSpanId,
|
||||
traceID: currentTraceId,
|
||||
startTime: t,
|
||||
process: {},
|
||||
references: [],
|
||||
tags: [
|
||||
{
|
||||
key: 'http.status_code',
|
||||
value: '400',
|
||||
},
|
||||
{
|
||||
key: 'http.url',
|
||||
value: '/test:80',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const fullTraceName = `${serviceName}: ${operationName}`;
|
||||
|
||||
@ -243,4 +287,22 @@ describe('getTraceName', () => {
|
||||
it('returns an id of root span with no refs', () => {
|
||||
expect(getTraceName(spansWithOneRootWithNoRefs as unknown as TraceSpan[])).toEqual(fullTraceName);
|
||||
});
|
||||
|
||||
it('returns span with header tags', () => {
|
||||
expect(getHeaderTags(spansWithHeaderTags as unknown as TraceSpan[])).toEqual({
|
||||
method: [
|
||||
{
|
||||
key: 'http.method',
|
||||
value: 'POST',
|
||||
},
|
||||
],
|
||||
status: [
|
||||
{
|
||||
key: 'http.status_code',
|
||||
value: '200',
|
||||
},
|
||||
],
|
||||
url: [],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -55,3 +55,31 @@ export const getTraceName = memoize(_getTraceNameImpl, (spans: TraceSpan[]) => {
|
||||
}
|
||||
return spans[0].traceID;
|
||||
});
|
||||
|
||||
export function findHeaderTags(spans: TraceSpan[]) {
|
||||
for (let i = 0; i < spans.length; i++) {
|
||||
const method = spans[i].tags.filter((tag) => {
|
||||
return tag.key === 'http.method';
|
||||
});
|
||||
|
||||
const status = spans[i].tags.filter((tag) => {
|
||||
return tag.key === 'http.status_code';
|
||||
});
|
||||
|
||||
const url = spans[i].tags.filter((tag) => {
|
||||
return tag.key === 'http.url' || tag.key === 'http.target' || tag.key === 'http.path';
|
||||
});
|
||||
|
||||
if (method.length > 0 || status.length > 0 || url.length > 0) {
|
||||
return { method, status, url };
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
export const getHeaderTags = memoize(findHeaderTags, (spans: TraceSpan[]) => {
|
||||
if (!spans.length) {
|
||||
return 0;
|
||||
}
|
||||
return spans[0].traceID;
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user