mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
* Filters * Service/span/duration filters * Renames for focused span and matches * Tag filters and new component * Tag filtering * Multiple tags and enable next/prev appropriately * Enum, renames, fixes * Clean up unecessary props * setFocusedSearchMatch * Faster options * Perf enhancements and cleanup * General improvements to tags etc * Updates to filtering * Add datasourceType in next/prev * Integrate TracePageSearchBar with NewTracePageSearchBar * Design tweaks * Update sticky elem and header design * Fix tests * Self review * Enhancements * More enhancements * Update tests * tests * More tests * Add span filters to docs * Update image link * Update docs * Update buttonEnabled and text * PR review * Update sticky header * Doc updates * Set values for service/span name * Buffer and dash update
192 lines
5.6 KiB
TypeScript
192 lines
5.6 KiB
TypeScript
// 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 React, { memo, useEffect, useMemo } from 'react';
|
|
|
|
import { GrafanaTheme2 } from '@grafana/data';
|
|
import { TimeZone } from '@grafana/schema';
|
|
import { Badge, BadgeColor, Tooltip, useStyles2 } from '@grafana/ui';
|
|
|
|
import { SearchProps } from '../../useSearch';
|
|
import ExternalLinks from '../common/ExternalLinks';
|
|
import TraceName from '../common/TraceName';
|
|
import { getTraceLinks } from '../model/link-patterns';
|
|
import { getHeaderTags, getTraceName } from '../model/trace-viewer';
|
|
import { Trace } from '../types';
|
|
import { formatDuration } from '../utils/date';
|
|
|
|
import TracePageActions from './Actions/TracePageActions';
|
|
import { SpanFilters } from './SpanFilters/SpanFilters';
|
|
import { timestamp, getStyles } from './TracePageHeader';
|
|
|
|
export type TracePageHeaderProps = {
|
|
trace: Trace | null;
|
|
timeZone: TimeZone;
|
|
search: SearchProps;
|
|
setSearch: React.Dispatch<React.SetStateAction<SearchProps>>;
|
|
showSpanFilters: boolean;
|
|
setShowSpanFilters: (isOpen: boolean) => void;
|
|
focusedSpanIdForSearch: string;
|
|
setFocusedSpanIdForSearch: React.Dispatch<React.SetStateAction<string>>;
|
|
spanFilterMatches: Set<string> | undefined;
|
|
datasourceType: string;
|
|
setHeaderHeight: (height: number) => void;
|
|
};
|
|
|
|
export const NewTracePageHeader = memo((props: TracePageHeaderProps) => {
|
|
const {
|
|
trace,
|
|
timeZone,
|
|
search,
|
|
setSearch,
|
|
showSpanFilters,
|
|
setShowSpanFilters,
|
|
focusedSpanIdForSearch,
|
|
setFocusedSpanIdForSearch,
|
|
spanFilterMatches,
|
|
datasourceType,
|
|
setHeaderHeight,
|
|
} = props;
|
|
const styles = { ...useStyles2(getStyles), ...useStyles2(getNewStyles) };
|
|
|
|
useEffect(() => {
|
|
setHeaderHeight(document.querySelector('.' + styles.header)?.scrollHeight ?? 0);
|
|
}, [setHeaderHeight, showSpanFilters, styles.header]);
|
|
|
|
const links = 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.title)}>
|
|
<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) {
|
|
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.header}>
|
|
<div className={styles.titleRow}>
|
|
{links && links.length > 0 && <ExternalLinks links={links} className={styles.TracePageHeaderBack} />}
|
|
{title}
|
|
<TracePageActions traceId={trace.traceID} />
|
|
</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>
|
|
|
|
<SpanFilters
|
|
trace={trace}
|
|
showSpanFilters={showSpanFilters}
|
|
setShowSpanFilters={setShowSpanFilters}
|
|
search={search}
|
|
setSearch={setSearch}
|
|
spanFilterMatches={spanFilterMatches}
|
|
focusedSpanIdForSearch={focusedSpanIdForSearch}
|
|
setFocusedSpanIdForSearch={setFocusedSpanIdForSearch}
|
|
datasourceType={datasourceType}
|
|
/>
|
|
</header>
|
|
);
|
|
});
|
|
|
|
NewTracePageHeader.displayName = 'NewTracePageHeader';
|
|
|
|
const getNewStyles = (theme: GrafanaTheme2) => {
|
|
return {
|
|
header: css`
|
|
label: TracePageHeader;
|
|
background-color: ${theme.colors.background.primary};
|
|
padding: 0.5em 0 0 0;
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 5;
|
|
`,
|
|
titleRow: css`
|
|
align-items: center;
|
|
display: flex;
|
|
padding: 0 8px;
|
|
`,
|
|
title: css`
|
|
color: inherit;
|
|
flex: 1;
|
|
font-size: 1.7em;
|
|
line-height: 1em;
|
|
`,
|
|
subtitle: css`
|
|
flex: 1;
|
|
line-height: 1em;
|
|
margin: -0.5em 8px 0.75em 8px;
|
|
`,
|
|
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;
|
|
`,
|
|
};
|
|
};
|