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,