TraceView.tsx 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. import { css } from '@emotion/css';
  2. import { TopOfViewRefType } from '@jaegertracing/jaeger-ui-components/src/TraceTimelineViewer/VirtualizedTraceView';
  3. import React, { RefObject, useCallback, useMemo, useState } from 'react';
  4. import { useDispatch, useSelector } from 'react-redux';
  5. import {
  6. DataFrame,
  7. DataLink,
  8. DataQuery,
  9. DataSourceApi,
  10. DataSourceJsonData,
  11. Field,
  12. GrafanaTheme2,
  13. LinkModel,
  14. mapInternalLinkToExplore,
  15. PanelData,
  16. SplitOpen,
  17. } from '@grafana/data';
  18. import { getTemplateSrv } from '@grafana/runtime';
  19. import { useStyles2 } from '@grafana/ui';
  20. import { Trace, TracePageHeader, TraceTimelineViewer, TTraceTimeline } from '@jaegertracing/jaeger-ui-components';
  21. import { TraceToLogsData } from 'app/core/components/TraceToLogs/TraceToLogsSettings';
  22. import { TraceToMetricsData } from 'app/core/components/TraceToMetrics/TraceToMetricsSettings';
  23. import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
  24. import { getTimeZone } from 'app/features/profile/state/selectors';
  25. import { StoreState } from 'app/types';
  26. import { ExploreId } from 'app/types/explore';
  27. import { changePanelState } from '../state/explorePane';
  28. import { createSpanLinkFactory } from './createSpanLink';
  29. import { useChildrenState } from './useChildrenState';
  30. import { useDetailState } from './useDetailState';
  31. import { useHoverIndentGuide } from './useHoverIndentGuide';
  32. import { useViewRange } from './useViewRange';
  33. const getStyles = (theme: GrafanaTheme2) => ({
  34. noDataMsg: css`
  35. height: 100%;
  36. width: 100%;
  37. display: grid;
  38. place-items: center;
  39. font-size: ${theme.typography.h4.fontSize};
  40. color: ${theme.colors.text.secondary};
  41. `,
  42. });
  43. function noop(): {} {
  44. return {};
  45. }
  46. type Props = {
  47. dataFrames: DataFrame[];
  48. splitOpenFn?: SplitOpen;
  49. exploreId?: ExploreId;
  50. scrollElement?: Element;
  51. traceProp: Trace;
  52. spanFindMatches?: Set<string>;
  53. search: string;
  54. focusedSpanIdForSearch: string;
  55. queryResponse: PanelData;
  56. datasource: DataSourceApi<DataQuery, DataSourceJsonData, {}> | undefined;
  57. topOfViewRef: RefObject<HTMLDivElement>;
  58. topOfViewRefType: TopOfViewRefType;
  59. };
  60. export function TraceView(props: Props) {
  61. const { spanFindMatches, traceProp, datasource, topOfViewRef, topOfViewRefType } = props;
  62. const {
  63. detailStates,
  64. toggleDetail,
  65. detailLogItemToggle,
  66. detailLogsToggle,
  67. detailProcessToggle,
  68. detailReferencesToggle,
  69. detailReferenceItemToggle,
  70. detailTagsToggle,
  71. detailWarningsToggle,
  72. detailStackTracesToggle,
  73. } = useDetailState(props.dataFrames[0]);
  74. const { removeHoverIndentGuideId, addHoverIndentGuideId, hoverIndentGuideIds } = useHoverIndentGuide();
  75. const { viewRange, updateViewRangeTime, updateNextViewRangeTime } = useViewRange();
  76. const { expandOne, collapseOne, childrenToggle, collapseAll, childrenHiddenIDs, expandAll } = useChildrenState();
  77. const styles = useStyles2(getStyles);
  78. /**
  79. * Keeps state of resizable name column width
  80. */
  81. const [spanNameColumnWidth, setSpanNameColumnWidth] = useState(0.25);
  82. /**
  83. * State of the top minimap, slim means it is collapsed.
  84. */
  85. const [slim, setSlim] = useState(false);
  86. const [focusedSpanId, createFocusSpanLink] = useFocusSpanLink({
  87. refId: props.dataFrames[0]?.refId,
  88. exploreId: props.exploreId!,
  89. datasource,
  90. });
  91. const createLinkToExternalSpan = (traceId: string, spanId: string) => {
  92. const link = createFocusSpanLink(traceId, spanId);
  93. return link.href;
  94. };
  95. const traceTimeline: TTraceTimeline = useMemo(
  96. () => ({
  97. childrenHiddenIDs,
  98. detailStates,
  99. hoverIndentGuideIds,
  100. shouldScrollToFirstUiFindMatch: false,
  101. spanNameColumnWidth,
  102. traceID: props.traceProp?.traceID,
  103. }),
  104. [childrenHiddenIDs, detailStates, hoverIndentGuideIds, spanNameColumnWidth, props.traceProp?.traceID]
  105. );
  106. const instanceSettings = getDatasourceSrv().getInstanceSettings(datasource?.name);
  107. const traceToLogsOptions = (instanceSettings?.jsonData as TraceToLogsData)?.tracesToLogs;
  108. const traceToMetricsOptions = (instanceSettings?.jsonData as TraceToMetricsData)?.tracesToMetrics;
  109. const createSpanLink = useMemo(
  110. () =>
  111. createSpanLinkFactory({
  112. splitOpenFn: props.splitOpenFn!,
  113. traceToLogsOptions,
  114. traceToMetricsOptions,
  115. dataFrame: props.dataFrames[0],
  116. createFocusSpanLink,
  117. }),
  118. [props.splitOpenFn, traceToLogsOptions, traceToMetricsOptions, props.dataFrames, createFocusSpanLink]
  119. );
  120. const onSlimViewClicked = useCallback(() => setSlim(!slim), [slim]);
  121. const timeZone = useSelector((state: StoreState) => getTimeZone(state.user));
  122. const datasourceType = datasource ? datasource?.type : 'unknown';
  123. return (
  124. <>
  125. {props.dataFrames?.length && props.dataFrames[0]?.meta?.preferredVisualisationType === 'trace' && traceProp ? (
  126. <>
  127. <TracePageHeader
  128. canCollapse={false}
  129. hideMap={false}
  130. hideSummary={false}
  131. onSlimViewClicked={onSlimViewClicked}
  132. onTraceGraphViewClicked={noop}
  133. slimView={slim}
  134. trace={traceProp}
  135. updateNextViewRangeTime={updateNextViewRangeTime}
  136. updateViewRangeTime={updateViewRangeTime}
  137. viewRange={viewRange}
  138. timeZone={timeZone}
  139. />
  140. <TraceTimelineViewer
  141. registerAccessors={noop}
  142. scrollToFirstVisibleSpan={noop}
  143. findMatchesIDs={spanFindMatches}
  144. trace={traceProp}
  145. datasourceType={datasourceType}
  146. traceTimeline={traceTimeline}
  147. updateNextViewRangeTime={updateNextViewRangeTime}
  148. updateViewRangeTime={updateViewRangeTime}
  149. viewRange={viewRange}
  150. focusSpan={noop}
  151. createLinkToExternalSpan={createLinkToExternalSpan}
  152. setSpanNameColumnWidth={setSpanNameColumnWidth}
  153. collapseAll={collapseAll}
  154. collapseOne={collapseOne}
  155. expandAll={expandAll}
  156. expandOne={expandOne}
  157. childrenToggle={childrenToggle}
  158. clearShouldScrollToFirstUiFindMatch={noop}
  159. detailLogItemToggle={detailLogItemToggle}
  160. detailLogsToggle={detailLogsToggle}
  161. detailWarningsToggle={detailWarningsToggle}
  162. detailStackTracesToggle={detailStackTracesToggle}
  163. detailReferencesToggle={detailReferencesToggle}
  164. detailReferenceItemToggle={detailReferenceItemToggle}
  165. detailProcessToggle={detailProcessToggle}
  166. detailTagsToggle={detailTagsToggle}
  167. detailToggle={toggleDetail}
  168. setTrace={noop}
  169. addHoverIndentGuideId={addHoverIndentGuideId}
  170. removeHoverIndentGuideId={removeHoverIndentGuideId}
  171. linksGetter={noop as any}
  172. uiFind={props.search}
  173. createSpanLink={createSpanLink}
  174. scrollElement={props.scrollElement}
  175. focusedSpanId={focusedSpanId}
  176. focusedSpanIdForSearch={props.focusedSpanIdForSearch!}
  177. createFocusSpanLink={createFocusSpanLink}
  178. topOfViewRef={topOfViewRef}
  179. topOfViewRefType={topOfViewRefType}
  180. />
  181. </>
  182. ) : (
  183. <div className={styles.noDataMsg}>No data</div>
  184. )}
  185. </>
  186. );
  187. }
  188. /**
  189. * Handles focusing a span. Returns the span id to focus to based on what is in current explore state and also a
  190. * function to change the focused span id.
  191. * @param options
  192. */
  193. function useFocusSpanLink(options: {
  194. exploreId: ExploreId;
  195. refId?: string;
  196. datasource?: DataSourceApi;
  197. }): [string | undefined, (traceId: string, spanId: string) => LinkModel<Field>] {
  198. const panelState = useSelector((state: StoreState) => state.explore[options.exploreId]?.panelsState.trace);
  199. const focusedSpanId = panelState?.spanId;
  200. const dispatch = useDispatch();
  201. const setFocusedSpanId = (spanId?: string) =>
  202. dispatch(
  203. changePanelState(options.exploreId, 'trace', {
  204. ...panelState,
  205. spanId,
  206. })
  207. );
  208. const query = useSelector((state: StoreState) =>
  209. state.explore[options.exploreId]?.queries.find((query) => query.refId === options.refId)
  210. );
  211. const createFocusSpanLink = (traceId: string, spanId: string) => {
  212. const link: DataLink = {
  213. title: 'Deep link to this span',
  214. url: '',
  215. internal: {
  216. datasourceUid: options.datasource?.uid!,
  217. datasourceName: options.datasource?.name!,
  218. query: query,
  219. panelsState: {
  220. trace: {
  221. spanId,
  222. },
  223. },
  224. },
  225. };
  226. return mapInternalLinkToExplore({
  227. link,
  228. internalLink: link.internal!,
  229. scopedVars: {},
  230. range: {} as any,
  231. field: {} as Field,
  232. onClickFn: () => setFocusedSpanId(focusedSpanId === spanId ? undefined : spanId),
  233. replaceVariables: getTemplateSrv().replace.bind(getTemplateSrv()),
  234. });
  235. };
  236. return [focusedSpanId, createFocusSpanLink];
  237. }