From 410df3d06500452196080b0b908740e4d6066e4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Jamr=C3=B3z?= Date: Mon, 29 Sep 2025 17:00:07 +0200 Subject: [PATCH] Trace View: Use data links post processor for legacy data links (#111304) --- .../features/explore/TraceView/TraceView.tsx | 5 ++ .../explore/TraceView/TraceViewContainer.tsx | 26 +++++---- .../SpanDetail/SpanDetailLinkButtons.tsx | 1 + .../TraceView/components/types/links.ts | 3 +- .../explore/TraceView/createSpanLink.tsx | 53 +++++++++++++------ 5 files changed, 62 insertions(+), 26 deletions(-) diff --git a/public/app/features/explore/TraceView/TraceView.tsx b/public/app/features/explore/TraceView/TraceView.tsx index 28a47518b81..b9d185aab75 100644 --- a/public/app/features/explore/TraceView/TraceView.tsx +++ b/public/app/features/explore/TraceView/TraceView.tsx @@ -15,6 +15,7 @@ import { SplitOpen, TimeRange, TraceSearchProps, + useDataLinksContext, } from '@grafana/data'; import { Trans, t } from '@grafana/i18n'; import { getTraceToLogsOptions, TraceToMetricsData, TraceToProfilesData } from '@grafana/o11y-ds-frontend'; @@ -142,6 +143,8 @@ export function TraceView(props: Props) { const traceToProfilesOptions = traceToProfilesData?.tracesToProfiles; const spanBarOptions: SpanBarOptionsData | undefined = instanceSettings?.jsonData; + const dataLinksContext = useDataLinksContext(); + const createSpanLink = useMemo( () => createSpanLinkFromProps ?? @@ -153,6 +156,7 @@ export function TraceView(props: Props) { dataFrame: props.dataFrames[0], createFocusSpanLink, trace: traceProp, + dataLinkPostProcessor: dataLinksContext?.dataLinkPostProcessor, }), [ props.splitOpenFn, @@ -163,6 +167,7 @@ export function TraceView(props: Props) { createFocusSpanLink, traceProp, createSpanLinkFromProps, + dataLinksContext?.dataLinkPostProcessor, ] ); const timeZone = useSelector((state) => getTimeZone(state.user)); diff --git a/public/app/features/explore/TraceView/TraceViewContainer.tsx b/public/app/features/explore/TraceView/TraceViewContainer.tsx index 9eff3de76f2..fa1f7360720 100644 --- a/public/app/features/explore/TraceView/TraceViewContainer.tsx +++ b/public/app/features/explore/TraceView/TraceViewContainer.tsx @@ -1,10 +1,12 @@ import { useMemo } from 'react'; -import { DataFrame, SplitOpen, TimeRange } from '@grafana/data'; +import { DataFrame, DataLinksContext, SplitOpen, TimeRange } from '@grafana/data'; import { t } from '@grafana/i18n'; import { PanelChrome } from '@grafana/ui'; import { StoreState, useSelector } from 'app/types/store'; +import { useExploreDataLinkPostProcessor } from '../hooks/useExploreDataLinkPostProcessor'; + import { TraceView } from './TraceView'; import { transformDataFrames } from './utils/transform'; @@ -25,21 +27,25 @@ export function TraceViewContainer(props: Props) { (state: StoreState) => state.explore.panes[props.exploreId]?.datasourceInstance ?? undefined ); + const dataLinkPostProcessor = useExploreDataLinkPostProcessor(splitOpenFn, timeRange); + if (!traceProp) { return null; } return ( - + + + ); } diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/SpanDetailLinkButtons.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/SpanDetailLinkButtons.tsx index 36810a370df..605280c292a 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/SpanDetailLinkButtons.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/SpanDetailLinkButtons.tsx @@ -196,6 +196,7 @@ const createLinkModel = ( className, type, linkModel: { + ...link.linkModel, ...link, title: title, target: '_blank', diff --git a/public/app/features/explore/TraceView/components/types/links.ts b/public/app/features/explore/TraceView/components/types/links.ts index 2610e9511cf..f0b172092f6 100644 --- a/public/app/features/explore/TraceView/components/types/links.ts +++ b/public/app/features/explore/TraceView/components/types/links.ts @@ -1,6 +1,6 @@ import * as React from 'react'; -import { Field, LinkTarget } from '@grafana/data'; +import { Field, LinkModel, LinkTarget } from '@grafana/data'; import { TraceSpan } from './trace'; @@ -22,6 +22,7 @@ export type SpanLinkDef = { field: Field; type: SpanLinkType; target?: LinkTarget; + linkModel?: LinkModel; }; export type SpanLinkFunc = (span: TraceSpan) => SpanLinkDef[] | undefined; diff --git a/public/app/features/explore/TraceView/createSpanLink.tsx b/public/app/features/explore/TraceView/createSpanLink.tsx index 09376d61fd4..4feed49e07d 100644 --- a/public/app/features/explore/TraceView/createSpanLink.tsx +++ b/public/app/features/explore/TraceView/createSpanLink.tsx @@ -1,6 +1,7 @@ import { DataFrame, DataLink, + DataLinkPostProcessor, DataSourceInstanceSettings, DataSourceJsonData, dateTime, @@ -44,6 +45,7 @@ export function createSpanLinkFactory({ dataFrame, createFocusSpanLink, trace, + dataLinkPostProcessor, }: { splitOpenFn: SplitOpen; traceToLogsOptions?: TraceToLogsOptionsV2; @@ -52,6 +54,7 @@ export function createSpanLinkFactory({ dataFrame?: DataFrame; createFocusSpanLink?: (traceId: string, spanId: string) => LinkModel; trace: Trace; + dataLinkPostProcessor?: DataLinkPostProcessor; }): SpanLinkFunc | undefined { if (!dataFrame) { return undefined; @@ -67,7 +70,9 @@ export function createSpanLinkFactory({ traceToLogsOptions, traceToMetricsOptions, createFocusSpanLink, - scopedVars + scopedVars, + dataFrame, + dataLinkPostProcessor ); return function SpanLink(span: TraceSpan): SpanLinkDef[] | undefined { @@ -147,7 +152,9 @@ function legacyCreateSpanLinkFactory( traceToLogsOptions?: TraceToLogsOptionsV2, traceToMetricsOptions?: TraceToMetricsOptions, createFocusSpanLink?: (traceId: string, spanId: string) => LinkModel, - scopedVars?: ScopedVars + scopedVars?: ScopedVars, + dataFrame?: DataFrame, + dataLinkPostProcessor?: DataLinkPostProcessor ) { let logsDataSourceSettings: DataSourceInstanceSettings | undefined; if (traceToLogsOptions?.datasourceUid) { @@ -215,6 +222,18 @@ function legacyCreateSpanLinkFactory( datasourceUid: logsDataSourceSettings.uid, datasourceName: logsDataSourceSettings.name, query, + range: getTimeRangeFromSpan( + span, + { + startMs: traceToLogsOptions.spanStartTimeShift + ? rangeUtil.intervalToMs(traceToLogsOptions.spanStartTimeShift) + : 0, + endMs: traceToLogsOptions.spanEndTimeShift + ? rangeUtil.intervalToMs(traceToLogsOptions.spanEndTimeShift) + : 0, + }, + isSplunkDS + ), }, }; @@ -229,29 +248,32 @@ function legacyCreateSpanLinkFactory( // Check if all variables are defined and don't show if they aren't. This is usually handled by the // getQueryFor* functions but this is for case of custom query supplied by the user. if (getVariableUsageInfo(dataLink.internal!.query, scopedVars).allVariablesDefined) { - const link = mapInternalLinkToExplore({ + let link = mapInternalLinkToExplore({ link: dataLink, internalLink: dataLink.internal!, scopedVars: scopedVars, - range: getTimeRangeFromSpan( - span, - { - startMs: traceToLogsOptions.spanStartTimeShift - ? rangeUtil.intervalToMs(traceToLogsOptions.spanStartTimeShift) - : 0, - endMs: traceToLogsOptions.spanEndTimeShift - ? rangeUtil.intervalToMs(traceToLogsOptions.spanEndTimeShift) - : 0, - }, - isSplunkDS - ), + range: dataLink.internal!.range, field: {} as Field, onClickFn: splitOpenFn, replaceVariables: getTemplateSrv().replace.bind(getTemplateSrv()), }); + link = + (dataFrame && + dataLinkPostProcessor?.({ + frame: dataFrame, + field: field, + dataLinkScopedVars: scopedVars, + replaceVariables: getTemplateSrv().replace.bind(getTemplateSrv()), + config: {}, + link: dataLink, + linkModel: link, + })) || + link; + links.push({ href: link.href, + linkModel: link, title: t('explore.legacy-create-span-link-factory.title.related-logs', 'Related logs'), onClick: link.onClick, content: ( @@ -351,6 +373,7 @@ function legacyCreateSpanLinkFactory( links!.push({ href: link.href, + linkModel: link, title, content: , onClick: link.onClick,