mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Tempo / Trace Viewer: Implement deep linking to spans
This commit is contained in:
@@ -1,4 +1,15 @@
|
||||
import { DataFrame, DataFrameView, SplitOpen, TraceSpanRow } from '@grafana/data';
|
||||
import {
|
||||
DataFrame,
|
||||
DataFrameView,
|
||||
DataLink,
|
||||
DataSourceApi,
|
||||
Field,
|
||||
LinkModel,
|
||||
mapInternalLinkToExplore,
|
||||
SplitOpen,
|
||||
TraceSpanRow,
|
||||
} from '@grafana/data';
|
||||
import { getTemplateSrv } from '@grafana/runtime';
|
||||
import {
|
||||
Trace,
|
||||
TracePageHeader,
|
||||
@@ -15,7 +26,8 @@ import { getTimeZone } from 'app/features/profile/state/selectors';
|
||||
import { StoreState } from 'app/types';
|
||||
import { ExploreId } from 'app/types/explore';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { changePanelState } from '../state/explorePane';
|
||||
import { createSpanLinkFactory } from './createSpanLink';
|
||||
import { UIElements } from './uiElements';
|
||||
import { useChildrenState } from './useChildrenState';
|
||||
@@ -66,10 +78,16 @@ export function TraceView(props: Props) {
|
||||
|
||||
const traceProp = useMemo(() => transformDataFrames(frame), [frame]);
|
||||
const { search, setSearch, spanFindMatches } = useSearch(traceProp?.spans);
|
||||
const dataSourceName = useSelector((state: StoreState) => state.explore[props.exploreId]?.datasourceInstance?.name);
|
||||
const traceToLogsOptions = (getDatasourceSrv().getInstanceSettings(dataSourceName)?.jsonData as TraceToLogsData)
|
||||
?.tracesToLogs;
|
||||
const timeZone = useSelector((state: StoreState) => getTimeZone(state.user));
|
||||
|
||||
const datasource = useSelector(
|
||||
(state: StoreState) => state.explore[props.exploreId]?.datasourceInstance ?? undefined
|
||||
);
|
||||
|
||||
const [focusedSpanId, createFocusSpanLink] = useFocusSpanLink({
|
||||
refId: frame?.refId,
|
||||
exploreId: props.exploreId,
|
||||
datasource,
|
||||
});
|
||||
|
||||
const traceTimeline: TTraceTimeline = useMemo(
|
||||
() => ({
|
||||
@@ -83,11 +101,14 @@ export function TraceView(props: Props) {
|
||||
[childrenHiddenIDs, detailStates, hoverIndentGuideIds, spanNameColumnWidth, traceProp?.traceID]
|
||||
);
|
||||
|
||||
const traceToLogsOptions = (getDatasourceSrv().getInstanceSettings(datasource?.name)?.jsonData as TraceToLogsData)
|
||||
?.tracesToLogs;
|
||||
const createSpanLink = useMemo(
|
||||
() => createSpanLinkFactory({ splitOpenFn: props.splitOpenFn, traceToLogsOptions, dataFrame: frame }),
|
||||
[props.splitOpenFn, traceToLogsOptions, frame]
|
||||
);
|
||||
const onSlimViewClicked = useCallback(() => setSlim(!slim), [slim]);
|
||||
const timeZone = useSelector((state: StoreState) => getTimeZone(state.user));
|
||||
|
||||
if (!props.dataFrames?.length || !traceProp) {
|
||||
return null;
|
||||
@@ -151,6 +172,8 @@ export function TraceView(props: Props) {
|
||||
uiFind={search}
|
||||
createSpanLink={createSpanLink}
|
||||
scrollElement={props.scrollElement}
|
||||
focusedSpanId={focusedSpanId}
|
||||
createFocusSpanLink={createFocusSpanLink}
|
||||
/>
|
||||
</UIElementsContext.Provider>
|
||||
);
|
||||
@@ -198,3 +221,59 @@ function transformTraceDataFrame(frame: DataFrame): TraceResponse {
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles focusing a span. Returns the span id to focus to based on what is in current explore state and also a
|
||||
* function to change the focused span id.
|
||||
* @param options
|
||||
*/
|
||||
function useFocusSpanLink(options: {
|
||||
exploreId: ExploreId;
|
||||
refId?: string;
|
||||
datasource?: DataSourceApi;
|
||||
}): [string | undefined, (traceId: string, spanId: string) => LinkModel<Field>] {
|
||||
const panelState = useSelector((state: StoreState) => state.explore[options.exploreId]?.panelsState.trace);
|
||||
const focusedSpanId = panelState?.spanId;
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const setFocusedSpanId = (spanId?: string) =>
|
||||
dispatch(
|
||||
changePanelState(options.exploreId, 'trace', {
|
||||
...panelState,
|
||||
spanId,
|
||||
})
|
||||
);
|
||||
|
||||
const query = useSelector((state: StoreState) =>
|
||||
state.explore[options.exploreId]?.queries.find((query) => query.refId === options.refId)
|
||||
);
|
||||
|
||||
const createFocusSpanLink = (traceId: string, spanId: string) => {
|
||||
const link: DataLink = {
|
||||
title: 'Deep link to this span',
|
||||
url: '',
|
||||
internal: {
|
||||
datasourceUid: options.datasource?.uid!,
|
||||
datasourceName: options.datasource?.name!,
|
||||
query: query,
|
||||
panelsState: {
|
||||
trace: {
|
||||
spanId,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return mapInternalLinkToExplore({
|
||||
link,
|
||||
internalLink: link.internal!,
|
||||
scopedVars: {},
|
||||
range: {} as any,
|
||||
field: {} as Field,
|
||||
onClickFn: () => setFocusedSpanId(focusedSpanId === spanId ? undefined : spanId),
|
||||
replaceVariables: getTemplateSrv().replace.bind(getTemplateSrv()),
|
||||
});
|
||||
};
|
||||
|
||||
return [focusedSpanId, createFocusSpanLink];
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ describe('createSpanLinkFactory', () => {
|
||||
const linkDef = createLink!(createTraceSpan());
|
||||
expect(linkDef!.href).toBe(
|
||||
`/explore?left=${encodeURIComponent(
|
||||
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"loki1","queries":[{"expr":"{cluster=\\"cluster1\\", hostname=\\"hostname1\\"}","refId":""}]}'
|
||||
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"loki1","queries":[{"expr":"{cluster=\\"cluster1\\", hostname=\\"hostname1\\"}","refId":""}],"panelsState":{}}'
|
||||
)}`
|
||||
);
|
||||
});
|
||||
@@ -54,7 +54,7 @@ describe('createSpanLinkFactory', () => {
|
||||
);
|
||||
expect(linkDef!.href).toBe(
|
||||
`/explore?left=${encodeURIComponent(
|
||||
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"loki1","queries":[{"expr":"{ip=\\"192.168.0.1\\"}","refId":""}]}'
|
||||
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"loki1","queries":[{"expr":"{ip=\\"192.168.0.1\\"}","refId":""}],"panelsState":{}}'
|
||||
)}`
|
||||
);
|
||||
});
|
||||
@@ -77,7 +77,7 @@ describe('createSpanLinkFactory', () => {
|
||||
);
|
||||
expect(linkDef!.href).toBe(
|
||||
`/explore?left=${encodeURIComponent(
|
||||
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"loki1","queries":[{"expr":"{ip=\\"192.168.0.1\\", host=\\"host\\"}","refId":""}]}'
|
||||
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"loki1","queries":[{"expr":"{ip=\\"192.168.0.1\\", host=\\"host\\"}","refId":""}],"panelsState":{}}'
|
||||
)}`
|
||||
);
|
||||
});
|
||||
@@ -101,7 +101,7 @@ describe('createSpanLinkFactory', () => {
|
||||
);
|
||||
expect(linkDef!.href).toBe(
|
||||
`/explore?left=${encodeURIComponent(
|
||||
'{"range":{"from":"2020-10-14T01:01:00.000Z","to":"2020-10-14T01:01:01.000Z"},"datasource":"loki1","queries":[{"expr":"{hostname=\\"hostname1\\"}","refId":""}]}'
|
||||
'{"range":{"from":"2020-10-14T01:01:00.000Z","to":"2020-10-14T01:01:01.000Z"},"datasource":"loki1","queries":[{"expr":"{hostname=\\"hostname1\\"}","refId":""}],"panelsState":{}}'
|
||||
)}`
|
||||
);
|
||||
});
|
||||
@@ -116,7 +116,7 @@ describe('createSpanLinkFactory', () => {
|
||||
|
||||
expect(linkDef!.href).toBe(
|
||||
`/explore?left=${encodeURIComponent(
|
||||
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"loki1","queries":[{"expr":"{cluster=\\"cluster1\\", hostname=\\"hostname1\\"} |=\\"7946b05c2e2e4e5a\\" |=\\"6605c7b08e715d6c\\"","refId":""}]}'
|
||||
'{"range":{"from":"2020-10-14T01:00:00.000Z","to":"2020-10-14T01:00:01.000Z"},"datasource":"loki1","queries":[{"expr":"{cluster=\\"cluster1\\", hostname=\\"hostname1\\"} |=\\"7946b05c2e2e4e5a\\" |=\\"6605c7b08e715d6c\\"","refId":""}],"panelsState":{}}'
|
||||
)}`
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user