// 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>; showSpanFilters: boolean; setShowSpanFilters: (isOpen: boolean) => void; focusedSpanIdForSearch: string; setFocusedSpanIdForSearch: React.Dispatch>; spanFilterMatches: Set | 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 = (

| {formatDuration(trace.duration)}

); 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 (
{links && links.length > 0 && } {title}
{timestamp(trace, timeZone, styles)} {method || status || url ? | : undefined} {method && method.length > 0 && ( )} {status && status.length > 0 && ( )} {url && url.length > 0 && ( {url[0].value} )}
); }); 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; `, }; };