From 0e4f06d4528d52516ef1b6cf259f3adb0918ef39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edvard=20Falksk=C3=A4r?= <457523+edvard-falkskar@users.noreply.github.com> Date: Mon, 2 Jun 2025 15:38:25 +0200 Subject: [PATCH] TraceView: Resource attributes links extension point (#104680) * TraceView: Resource attributes links extension point * Add data source info to context * Remove console log * Removing unused linkGetters and more * More tests * Fixing last todos * Fixing * Fixing * Change link style * Rename extension context * Fix lint error --- packages/grafana-data/src/index.ts | 1 + .../src/types/pluginExtensions.ts | 10 ++++ .../features/explore/TraceView/TraceView.tsx | 3 +- .../SpanDetail/AccordianKeyValues.tsx | 6 +- .../SpanDetail/AccordianLogs.tsx | 7 +-- .../SpanDetail/AccordianReferences.tsx | 1 - .../SpanDetail/KeyValuesTable.test.tsx | 16 +++--- .../SpanDetail/KeyValuesTable.tsx | 33 ++++++----- .../SpanDetail/index.test.tsx | 23 ++++++++ .../TraceTimelineViewer/SpanDetail/index.tsx | 56 ++++++++++++++++--- .../TraceTimelineViewer/SpanDetailRow.tsx | 14 ++--- .../VirtualizedTraceView.tsx | 10 ++-- .../components/TraceTimelineViewer/index.tsx | 7 ++- 13 files changed, 129 insertions(+), 58 deletions(-) diff --git a/packages/grafana-data/src/index.ts b/packages/grafana-data/src/index.ts index 016fb013123..28cb92c47d3 100644 --- a/packages/grafana-data/src/index.ts +++ b/packages/grafana-data/src/index.ts @@ -563,6 +563,7 @@ export { type PluginExtensionAddedComponentConfig, type PluginExtensionAddedLinkConfig, type PluginExtensionAddedFunctionConfig, + type PluginExtensionResourceAttributesContext, } from './types/pluginExtensions'; export { type ScopeDashboardBindingSpec, diff --git a/packages/grafana-data/src/types/pluginExtensions.ts b/packages/grafana-data/src/types/pluginExtensions.ts index c7a1df01b23..36ddc74ab22 100644 --- a/packages/grafana-data/src/types/pluginExtensions.ts +++ b/packages/grafana-data/src/types/pluginExtensions.ts @@ -192,6 +192,7 @@ export enum PluginExtensionPoints { UserProfileTab = 'grafana/user/profile/tab', TraceViewDetails = 'grafana/traceview/details', QueryEditorRowAdaptiveTelemetryV1 = 'grafana/query-editor-row/adaptivetelemetry/v1', + TraceViewResourceAttributes = 'grafana/traceview/resource-attributes', } export type PluginExtensionPanelContext = { @@ -236,6 +237,15 @@ export type PluginExtensionDataSourceConfigContext< export type PluginExtensionCommandPaletteContext = {}; +export type PluginExtensionResourceAttributesContext = { + // Key-value pairs of resource attributes, attribute name is the key + attributes: Record; + datasource: { + type: string; + uid: string; + }; +}; + type Dashboard = { uid: string; title: string; diff --git a/public/app/features/explore/TraceView/TraceView.tsx b/public/app/features/explore/TraceView/TraceView.tsx index 891d060ef2e..3fc0199156c 100644 --- a/public/app/features/explore/TraceView/TraceView.tsx +++ b/public/app/features/explore/TraceView/TraceView.tsx @@ -169,6 +169,7 @@ export function TraceView(props: Props) { ); const timeZone = useSelector((state) => getTimeZone(state.user)); const datasourceType = datasource ? datasource?.type : 'unknown'; + const datasourceUid = datasource ? datasource?.uid : ''; const scrollElement = props.scrollElement ? props.scrollElement : document.getElementsByClassName(props.scrollElementClass ?? '')[0]; @@ -204,6 +205,7 @@ export function TraceView(props: Props) { trace={traceProp} traceToProfilesOptions={traceToProfilesOptions} datasourceType={datasourceType} + datasourceUid={datasourceUid} spanBarOptions={spanBarOptions?.spanBar} traceTimeline={traceTimeline} updateNextViewRangeTime={updateNextViewRangeTime} @@ -227,7 +229,6 @@ export function TraceView(props: Props) { detailToggle={toggleDetail} addHoverIndentGuideId={addHoverIndentGuideId} removeHoverIndentGuideId={removeHoverIndentGuideId} - linksGetter={() => []} createSpanLink={createSpanLink} scrollElement={scrollElement} focusedSpanId={focusedSpanId} diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianKeyValues.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianKeyValues.tsx index f41478005d4..1932b8d2c96 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianKeyValues.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianKeyValues.tsx @@ -20,10 +20,10 @@ import { GrafanaTheme2, TraceKeyValuePair } from '@grafana/data'; import { Icon, useStyles2 } from '@grafana/ui'; import { autoColor } from '../../Theme'; -import { TraceLink, TNil } from '../../types'; +import { TNil } from '../../types'; import * as markers from './AccordianKeyValues.markers'; -import KeyValuesTable from './KeyValuesTable'; +import KeyValuesTable, { KeyValuesTableLink } from './KeyValuesTable'; import { alignIcon } from '.'; @@ -94,7 +94,7 @@ export type AccordianKeyValuesProps = { interactive?: boolean; isOpen: boolean; label: string | React.ReactNode; - linksGetter?: ((pairs: TraceKeyValuePair[], index: number) => TraceLink[]) | TNil; + linksGetter?: ((pairs: TraceKeyValuePair[], index: number) => KeyValuesTableLink[]) | TNil; onToggle?: null | (() => void); }; diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianLogs.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianLogs.tsx index cceb414dfdb..1de9660cbb6 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianLogs.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianLogs.tsx @@ -16,13 +16,11 @@ import { css } from '@emotion/css'; import { sortBy as _sortBy } from 'lodash'; import * as React from 'react'; -import { GrafanaTheme2, TraceKeyValuePair, TraceLog } from '@grafana/data'; +import { GrafanaTheme2, TraceLog } from '@grafana/data'; import { Trans } from '@grafana/i18n'; import { Icon, useStyles2 } from '@grafana/ui'; import { autoColor } from '../../Theme'; -import { TNil } from '../../types'; -import { TraceLink } from '../../types/trace'; import { formatDuration } from '../utils'; import AccordianKeyValues from './AccordianKeyValues'; @@ -69,7 +67,6 @@ const getStyles = (theme: GrafanaTheme2) => { export type AccordianLogsProps = { interactive?: boolean; isOpen: boolean; - linksGetter?: ((pairs: TraceKeyValuePair[], index: number) => TraceLink[]) | TNil; logs: TraceLog[]; onItemToggle?: (log: TraceLog) => void; onToggle?: () => void; @@ -80,7 +77,6 @@ export type AccordianLogsProps = { export default function AccordianLogs({ interactive = true, isOpen, - linksGetter, logs, openedItems, onItemToggle, @@ -138,7 +134,6 @@ export default function AccordianLogs({ interactive={interactive} isOpen={openedItems ? openedItems.has(log) : false} label={label} - linksGetter={linksGetter} onToggle={interactive && onItemToggle ? () => onItemToggle(log) : null} /> ); diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianReferences.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianReferences.tsx index 14f43d49676..a2949cdf9c4 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianReferences.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/AccordianReferences.tsx @@ -176,7 +176,6 @@ export function References(props: ReferenceItemProps) { interactive={interactive} isOpen={openedItems ? openedItems.has(reference) : false} label={t('explore.references.label-attributes', 'attributes')} - linksGetter={null} onToggle={interactive && onItemToggle ? () => onItemToggle(reference) : null} /> diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/KeyValuesTable.test.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/KeyValuesTable.test.tsx index ac89bdf22b7..f168569d8df 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/KeyValuesTable.test.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/KeyValuesTable.test.tsx @@ -33,14 +33,12 @@ const setup = (propOverrides?: Partial) => { describe('LinkValue', () => { it('renders as expected', () => { - const title = 'titleValue'; - const href = 'hrefValue'; + const link = { + title: 'titleValue', + path: 'hrefValue', + }; const childrenText = 'childrenTextValue'; - render( - - {childrenText} - - ); + render({childrenText}); expect(screen.getByRole('link', { name: 'titleValue' })).toBeInTheDocument(); expect(screen.getByText(/^childrenTextValue$/)).toBeInTheDocument(); }); @@ -73,8 +71,8 @@ describe('KeyValuesTable tests', () => { array[i].key === 'span.kind' ? [ { - url: `http://example.com/?kind=${encodeURIComponent(array[i].value)}`, - text: `More info about ${array[i].value}`, + path: `http://example.com/?kind=${encodeURIComponent(array[i].value)}`, + title: `More info about ${array[i].value}`, }, ] : [], diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/KeyValuesTable.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/KeyValuesTable.tsx index d6a3f1eca93..60c00367373 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/KeyValuesTable.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/KeyValuesTable.tsx @@ -15,14 +15,13 @@ import { css } from '@emotion/css'; import cx from 'classnames'; import { PropsWithChildren } from 'react'; -import * as React from 'react'; -import { GrafanaTheme2, TraceKeyValuePair } from '@grafana/data'; +import { GrafanaTheme2, PluginExtensionLink, TraceKeyValuePair } from '@grafana/data'; import { Icon, useStyles2 } from '@grafana/ui'; import { autoColor } from '../../Theme'; import CopyIcon from '../../common/CopyIcon'; -import { TraceLink, TNil } from '../../types'; +import { TNil } from '../../types'; import jsonMarkup from './jsonMarkup'; @@ -57,6 +56,12 @@ export const getStyles = (theme: GrafanaTheme2) => { [`&:not(:hover) .${copyIconClassName}`]: { visibility: 'hidden', }, + 'a span': { + color: `${theme.colors.text.link} !important`, + }, + 'a:hover span': { + textDecoration: 'underline', + }, }), keyColumn: css({ label: 'keyColumn', @@ -94,23 +99,25 @@ function parseIfComplexJson(value: unknown) { return value; } +export type KeyValuesTableLink = Pick; + interface LinkValueProps { - href: string; - title?: string; - children: React.ReactNode; + link: KeyValuesTableLink; } -export const LinkValue = ({ href, title = '', children }: PropsWithChildren) => { +export const LinkValue = ({ link, children }: PropsWithChildren) => { + const { path, title = '', onClick, icon = 'external-link-alt' } = link; + return ( - - {children} + + {children} ); }; export type KeyValuesTableProps = { data: TraceKeyValuePair[]; - linksGetter?: ((pairs: TraceKeyValuePair[], index: number) => TraceLink[]) | TNil; + linksGetter?: ((pairs: TraceKeyValuePair[], index: number) => KeyValuesTableLink[]) | TNil; }; export default function KeyValuesTable(props: KeyValuesTableProps) { @@ -125,15 +132,13 @@ export default function KeyValuesTable(props: KeyValuesTableProps) { __html: jsonMarkup(parseIfComplexJson(row.value)), }; const jsonTable =
; - const links = linksGetter ? linksGetter(data, i) : null; + const links = linksGetter?.(data, i); let valueMarkup; if (links && links.length) { // TODO: handle multiple items valueMarkup = (
- - {jsonTable} - + {jsonTable}
); } else { diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.test.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.test.tsx index b326a7aee79..330b043ac82 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.test.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.test.tsx @@ -76,6 +76,8 @@ describe('', () => { to: 1000000000000, }, }, + datasourceType: 'tempo', + datasourceUid: 'grafanacloud-traces', }; span.tags = [ @@ -258,4 +260,25 @@ describe('', () => { expect(screen.getByText(/(Count)/)).toBeInTheDocument(); }); }); + + it('should load plugin links for resource attributes', () => { + const usePluginLinksMock = jest.fn().mockReturnValue({ links: [] }); + setPluginLinksHook(usePluginLinksMock); + jest.requireMock('@grafana/runtime').usePluginLinks = usePluginLinksMock; + + render(); + expect(usePluginLinksMock).toHaveBeenCalledWith( + expect.objectContaining({ + context: expect.objectContaining({ + attributes: expect.objectContaining({ + 'http.url': expect.arrayContaining([expect.any(String)]), + }), + datasource: { + type: 'tempo', + uid: 'grafanacloud-traces', + }, + }), + }) + ); + }); }); diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.tsx index 02aa0932375..9ad1de2b701 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/index.tsx @@ -15,6 +15,7 @@ import { css } from '@emotion/css'; import { SpanStatusCode } from '@opentelemetry/api'; import cx from 'classnames'; +import { useCallback, useMemo } from 'react'; import { CoreApp, @@ -25,9 +26,12 @@ import { TimeRange, TraceKeyValuePair, TraceLog, + PluginExtensionResourceAttributesContext, + PluginExtensionPoints, } from '@grafana/data'; import { Trans, useTranslate } from '@grafana/i18n'; import { TraceToProfilesOptions } from '@grafana/o11y-ds-frontend'; +import { usePluginLinks } from '@grafana/runtime'; import { TimeZone } from '@grafana/schema'; import { Divider, Icon, TextArea, useStyles2 } from '@grafana/ui'; @@ -35,8 +39,8 @@ import { pyroscopeProfileIdTagKey } from '../../../createSpanLink'; import { autoColor } from '../../Theme'; import LabeledList from '../../common/LabeledList'; import { KIND, LIBRARY_NAME, LIBRARY_VERSION, STATUS, STATUS_MESSAGE, TRACE_STATE } from '../../constants/span'; -import { SpanLinkFunc, TNil } from '../../types'; -import { TraceLink, TraceSpan, TraceSpanReference } from '../../types/trace'; +import { SpanLinkFunc } from '../../types'; +import { TraceProcess, TraceSpan, TraceSpanReference } from '../../types/trace'; import { formatDuration } from '../utils'; import AccordianKeyValues from './AccordianKeyValues'; @@ -47,6 +51,44 @@ import DetailState from './DetailState'; import { getSpanDetailLinkButtons } from './SpanDetailLinkButtons'; import SpanFlameGraph from './SpanFlameGraph'; +const useResourceAttributesExtensionLinks = (process: TraceProcess, datasourceType: string, datasourceUid: string) => { + // Stable context for useMemo inside usePluginLinks + const context: PluginExtensionResourceAttributesContext = useMemo(() => { + const attributes = (process.tags ?? []).reduce>((acc, tag) => { + if (acc[tag.key]) { + acc[tag.key].push(tag.value); + } else { + acc[tag.key] = [tag.value]; + } + return acc; + }, {}); + + return { + attributes, + datasource: { + type: datasourceType, + uid: datasourceUid, + }, + }; + }, [process.tags, datasourceType, datasourceUid]); + + const { links } = usePluginLinks({ + extensionPointId: PluginExtensionPoints.TraceViewResourceAttributes, + limitPerPlugin: 10, + context, + }); + + const resourceLinksGetter = useCallback( + (pairs: TraceKeyValuePair[], index: number) => { + const { key } = pairs[index] ?? {}; + return links.filter((link) => link.category === key); + }, + [links] + ); + + return resourceLinksGetter; +}; + const getStyles = (theme: GrafanaTheme2) => { return { header: css({ @@ -145,7 +187,6 @@ export type TraceFlameGraphs = { export type SpanDetailProps = { detailState: DetailState; - linksGetter: ((links: TraceKeyValuePair[], index: number) => TraceLink[]) | TNil; logItemToggle: (spanID: string, log: TraceLog) => void; logsToggle: (spanID: string) => void; processToggle: (spanID: string) => void; @@ -164,6 +205,7 @@ export type SpanDetailProps = { focusedSpanId?: string; createFocusSpanLink: (traceId: string, spanId: string) => LinkModel; datasourceType: string; + datasourceUid: string; traceFlameGraphs: TraceFlameGraphs; setTraceFlameGraphs: (flameGraphs: TraceFlameGraphs) => void; setRedrawListView: (redraw: {}) => void; @@ -174,7 +216,6 @@ export type SpanDetailProps = { export default function SpanDetail(props: SpanDetailProps) { const { detailState, - linksGetter, logItemToggle, logsToggle, processToggle, @@ -190,6 +231,7 @@ export default function SpanDetail(props: SpanDetailProps) { createSpanLink, createFocusSpanLink, datasourceType, + datasourceUid, traceFlameGraphs, setTraceFlameGraphs, traceToProfilesOptions, @@ -302,6 +344,8 @@ export default function SpanDetail(props: SpanDetailProps) { }); const focusSpanLink = createFocusSpanLink(traceID, spanID); + const resourceLinksGetter = useResourceAttributesExtensionLinks(process, datasourceType, datasourceUid); + return (
@@ -319,7 +363,6 @@ export default function SpanDetail(props: SpanDetailProps) { tagsToggle(spanID)} /> @@ -328,7 +371,7 @@ export default function SpanDetail(props: SpanDetailProps) { className={styles.AccordianKeyValuesItem} data={process.tags} label={t('explore.span-detail.label-resource-attributes', 'Resource attributes')} - linksGetter={linksGetter} + linksGetter={resourceLinksGetter} isOpen={isProcessOpen} onToggle={() => processToggle(spanID)} /> @@ -336,7 +379,6 @@ export default function SpanDetail(props: SpanDetailProps) {
{logs && logs.length > 0 && ( void; - linksGetter: (span: TraceSpan, links: TraceKeyValuePair[], index: number) => TraceLink[]; logItemToggle: (spanID: string, log: TraceLog) => void; logsToggle: (spanID: string) => void; processToggle: (spanID: string) => void; @@ -99,6 +98,7 @@ export type SpanDetailRowProps = { focusedSpanId?: string; createFocusSpanLink: (traceId: string, spanId: string) => LinkModel; datasourceType: string; + datasourceUid: string; visibleSpanIds: string[]; traceFlameGraphs: TraceFlameGraphs; setTraceFlameGraphs: (flameGraphs: TraceFlameGraphs) => void; @@ -112,11 +112,6 @@ export class UnthemedSpanDetailRow extends PureComponent { this.props.onDetailToggled(this.props.span.spanID); }; - _linksGetter = (items: TraceKeyValuePair[], itemIndex: number) => { - const { linksGetter, span } = this.props; - return linksGetter(span, items, itemIndex); - }; - render() { const { color, @@ -144,6 +139,7 @@ export class UnthemedSpanDetailRow extends PureComponent { focusedSpanId, createFocusSpanLink, datasourceType, + datasourceUid, visibleSpanIds, traceFlameGraphs, setTraceFlameGraphs, @@ -175,7 +171,6 @@ export class UnthemedSpanDetailRow extends PureComponent {
{ focusedSpanId={focusedSpanId} createFocusSpanLink={createFocusSpanLink} datasourceType={datasourceType} + datasourceUid={datasourceUid} traceFlameGraphs={traceFlameGraphs} setTraceFlameGraphs={setTraceFlameGraphs} setRedrawListView={setRedrawListView} diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/VirtualizedTraceView.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/VirtualizedTraceView.tsx index 4e9fe6c0b98..22124159efb 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/VirtualizedTraceView.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/VirtualizedTraceView.tsx @@ -18,7 +18,7 @@ import memoizeOne from 'memoize-one'; import * as React from 'react'; import { RefObject } from 'react'; -import { CoreApp, GrafanaTheme2, LinkModel, TimeRange, TraceKeyValuePair, TraceLog } from '@grafana/data'; +import { CoreApp, GrafanaTheme2, LinkModel, TimeRange, TraceLog } from '@grafana/data'; import { t } from '@grafana/i18n/internal'; import { TraceToProfilesOptions } from '@grafana/o11y-ds-frontend'; import { config, reportInteraction } from '@grafana/runtime'; @@ -28,7 +28,7 @@ import { stylesFactory, withTheme2, ToolbarButton } from '@grafana/ui'; import { PEER_SERVICE } from '../constants/tag-keys'; import { CriticalPathSection, SpanBarOptions, SpanLinkFunc, TNil } from '../types'; import TTraceTimeline from '../types/TTraceTimeline'; -import { TraceSpan, Trace, TraceLink, TraceSpanReference } from '../types/trace'; +import { TraceSpan, Trace, TraceSpanReference } from '../types/trace'; import { getColorByKey } from '../utils/color-generator'; import ListView from './ListView'; @@ -79,7 +79,6 @@ type TVirtualizedTraceViewOwnProps = { trace: Trace; traceToProfilesOptions?: TraceToProfilesOptions; spanBarOptions: SpanBarOptions | undefined; - linksGetter: (span: TraceSpan, items: TraceKeyValuePair[], itemIndex: number) => TraceLink[]; childrenToggle: (spanID: string) => void; detailLogItemToggle: (spanID: string, log: TraceLog) => void; detailLogsToggle: (spanID: string) => void; @@ -104,6 +103,7 @@ type TVirtualizedTraceViewOwnProps = { createFocusSpanLink: (traceId: string, spanId: string) => LinkModel; topOfViewRef?: RefObject; datasourceType: string; + datasourceUid: string; headerHeight: number; criticalPath: CriticalPathSection[]; traceFlameGraphs: TraceFlameGraphs; @@ -551,12 +551,12 @@ export class UnthemedVirtualizedTraceView extends React.Component void; updateViewRangeTime: TUpdateViewRangeTimeFunction; @@ -96,7 +97,6 @@ export type TProps = { detailToggle: (spanID: string) => void; addHoverIndentGuideId: (spanID: string) => void; removeHoverIndentGuideId: (spanID: string) => void; - linksGetter: (span: TraceSpan, items: TraceKeyValuePair[], itemIndex: number) => TraceLink[]; theme: GrafanaTheme2; createSpanLink?: SpanLinkFunc; scrollElement?: Element; @@ -222,6 +222,7 @@ export class UnthemedTraceTimelineViewer extends PureComponent { topOfViewRef={topOfViewRef} focusedSpanIdForSearch={focusedSpanIdForSearch} datasourceType={this.props.datasourceType} + datasourceUid={this.props.datasourceUid} />
);