From 1864807b150b6aa85fb8d01eaf4a26a554fc864f Mon Sep 17 00:00:00 2001 From: Andrej Ocenas Date: Wed, 15 Apr 2020 16:04:01 +0200 Subject: [PATCH] Tracing: Performance optimization (#23474) * Add integration with Jeager Add Jaeger datasource and modify derived fields in loki to allow for opening a trace in Jager in separate split. Modifies build so that this branch docker images are pushed to docker hub Add a traceui dir with docker-compose and provision files for demoing.:wq * Enable docker logger plugin to send logs to loki * Add placeholder zipkin datasource * Fixed rebase issues, added enhanceDataFrame to non-legacy code path * Trace selector for jaeger query field * Fix logs default mode for Loki * Fix loading jaeger query field services on split * Updated grafana image in traceui/compose file * Fix prettier error * Hide behind feature flag, clean up unused code. * Fix tests * Fix tests * Cleanup code and review feedback * Remove traceui directory * Remove circle build changes * Fix feature toggles object * Fix merge issues * Add trace ui in Explore * WIP * WIP * WIP * Make jaeger datasource return trace data instead of link * Allow js in jest tests * Return data from Jaeger datasource * Take yarn.lock from master * Fix missing component * Update yarn lock * Fix some ts and lint errors * Fix merge * Fix type errors * Make tests pass again * Add tests * Fix es5 compatibility * Add header with minimap * Fix sizing issue due to column resizer handle * Fix issues with sizing, search functionality, duplicate react, tests * Refactor TraceView component, fix tests * Fix type errors * Add dark theme styling * Add tests for hooks * More color changes * Fix tests to deal with additional theme wrappers. * Add memoization * Fix duplicate identifier Co-authored-by: David Kaltschmidt --- conf/provisioning/datasources/loki_test.yaml | 10 +- packages/jaeger-ui-components/src/Theme.tsx | 18 ++- .../src/TracePageHeader/TracePageHeader.tsx | 3 +- .../TracePageHeader/TracePageSearchBar.tsx | 5 +- .../features/explore/TraceView/TraceView.tsx | 60 ++++---- .../explore/TraceView/useChildrenState.ts | 132 ++++++++++-------- .../explore/TraceView/useDetailState.ts | 62 ++++---- .../explore/TraceView/useHoverIndentGuide.ts | 10 +- .../features/explore/TraceView/useSearch.ts | 10 +- .../explore/TraceView/useViewRange.ts | 10 +- 10 files changed, 178 insertions(+), 142 deletions(-) diff --git a/conf/provisioning/datasources/loki_test.yaml b/conf/provisioning/datasources/loki_test.yaml index 70f43d0ee92..7b561346d54 100644 --- a/conf/provisioning/datasources/loki_test.yaml +++ b/conf/provisioning/datasources/loki_test.yaml @@ -10,8 +10,14 @@ datasources: jsonData: derivedFields: - name: "traceID" - matcherRegex: "traceID=(\\w+)" + matcherRegex: "trace_id=(\\w+)" url: "$${__value.raw}" - datasourceName: "Jaeger" + datasourceUid: jaeger1234 + - name: jaeger-derived-test + type: jaeger + uid: jaeger1234 + access: proxy + url: http://localhost:16686 + editable: false diff --git a/packages/jaeger-ui-components/src/Theme.tsx b/packages/jaeger-ui-components/src/Theme.tsx index 8888fc7d6e8..4a6f248d2d5 100644 --- a/packages/jaeger-ui-components/src/Theme.tsx +++ b/packages/jaeger-ui-components/src/Theme.tsx @@ -50,18 +50,22 @@ export function ThemeConsumer(props: ThemeConsumerProps) { return ( {(value: ThemeOptions | undefined) => { - const mergedTheme: Theme = value - ? { - ...defaultTheme, - ...value, - } - : defaultTheme; - return props.children(mergedTheme); + const theme = memoizedThemeMerge(value); + return props.children(theme); }} ); } +const memoizedThemeMerge = memoizeOne(value => { + return value + ? { + ...defaultTheme, + ...value, + } + : defaultTheme; +}); + type WrappedWithThemeComponent = React.ComponentType> & { wrapped: React.ComponentType; }; diff --git a/packages/jaeger-ui-components/src/TracePageHeader/TracePageHeader.tsx b/packages/jaeger-ui-components/src/TracePageHeader/TracePageHeader.tsx index 55663948b4c..55ded1a4eba 100644 --- a/packages/jaeger-ui-components/src/TracePageHeader/TracePageHeader.tsx +++ b/packages/jaeger-ui-components/src/TracePageHeader/TracePageHeader.tsx @@ -34,6 +34,7 @@ import { getTraceLinks } from '../model/link-patterns'; import ExternalLinks from '../common/ExternalLinks'; import { createStyle } from '../Theme'; import { uTxMuted } from '../uberUtilityStyles'; +import { useMemo } from 'react'; const getStyles = createStyle((theme: Theme) => { const TracePageHeaderOverviewItemValueDetail = css` @@ -222,7 +223,7 @@ export default function TracePageHeader(props: TracePageHeaderEmbedProps) { return null; } - const links = getTraceLinks(trace); + const links = useMemo(() => getTraceLinks(trace), [trace]); const summaryItems = !hideSummary && diff --git a/packages/jaeger-ui-components/src/TracePageHeader/TracePageSearchBar.tsx b/packages/jaeger-ui-components/src/TracePageHeader/TracePageSearchBar.tsx index 22d77ef7c67..d3e923a4e1a 100644 --- a/packages/jaeger-ui-components/src/TracePageHeader/TracePageSearchBar.tsx +++ b/packages/jaeger-ui-components/src/TracePageHeader/TracePageSearchBar.tsx @@ -24,6 +24,7 @@ import { TNil } from '../types'; import { UIButton, UIInputGroup } from '../uiElementsContext'; import { createStyle } from '../Theme'; import { ubFlexAuto, ubJustifyEnd } from '../uberUtilityStyles'; +import { memo } from 'react'; export const getStyles = createStyle(() => { return { @@ -71,7 +72,7 @@ type TracePageSearchBarProps = { hideSearchButtons?: boolean; }; -export default function TracePageSearchBar(props: TracePageSearchBarProps) { +export default memo(function TracePageSearchBar(props: TracePageSearchBarProps) { const { clearSearch, focusUiFindMatches, @@ -141,4 +142,4 @@ export default function TracePageSearchBar(props: TracePageSearchBarProps) { ); -} +}); diff --git a/public/app/features/explore/TraceView/TraceView.tsx b/public/app/features/explore/TraceView/TraceView.tsx index 761664c0595..8828c8a2554 100644 --- a/public/app/features/explore/TraceView/TraceView.tsx +++ b/public/app/features/explore/TraceView/TraceView.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import { KeyValuePair, Link, @@ -50,27 +50,36 @@ export function TraceView(props: Props) { */ const [slim, setSlim] = useState(false); - const traceProp = transformTraceData(props.trace); + const traceProp = useMemo(() => transformTraceData(props.trace), [props.trace]); const { search, setSearch, spanFindMatches } = useSearch(traceProp?.spans); + const theme = useTheme(); + const traceTheme = useMemo(() => ({ type: theme.isDark ? ThemeType.Dark : ThemeType.Light }), [theme]); + const traceTimeline: TTraceTimeline = useMemo( + () => ({ + childrenHiddenIDs, + detailStates, + hoverIndentGuideIds, + shouldScrollToFirstUiFindMatch: false, + spanNameColumnWidth, + traceID: traceProp.traceID, + }), + [childrenHiddenIDs, detailStates, hoverIndentGuideIds, spanNameColumnWidth, traceProp.traceID] + ); return ( - + {}} - focusUiFindMatches={() => {}} + clearSearch={useCallback(() => {}, [])} + focusUiFindMatches={useCallback(() => {}, [])} hideMap={false} hideSummary={false} - nextResult={() => {}} - onSlimViewClicked={() => setSlim(!slim)} - onTraceGraphViewClicked={() => {}} - prevResult={() => {}} + nextResult={useCallback(() => {}, [])} + onSlimViewClicked={useCallback(() => setSlim(!slim), [])} + onTraceGraphViewClicked={useCallback(() => {}, [])} + prevResult={useCallback(() => {}, [])} resultCount={0} slimView={slim} textFilter={null} @@ -84,32 +93,23 @@ export function TraceView(props: Props) { hideSearchButtons={true} /> {}} - scrollToFirstVisibleSpan={() => {}} + registerAccessors={useCallback(() => {}, [])} + scrollToFirstVisibleSpan={useCallback(() => {}, [])} findMatchesIDs={spanFindMatches} trace={traceProp} - traceTimeline={ - { - childrenHiddenIDs, - detailStates, - hoverIndentGuideIds, - shouldScrollToFirstUiFindMatch: false, - spanNameColumnWidth, - traceID: '50b96206cf81dd64', - } as TTraceTimeline - } + traceTimeline={traceTimeline} updateNextViewRangeTime={updateNextViewRangeTime} updateViewRangeTime={updateViewRangeTime} viewRange={viewRange} - focusSpan={() => {}} - createLinkToExternalSpan={() => ''} + focusSpan={useCallback(() => {}, [])} + createLinkToExternalSpan={useCallback(() => '', [])} setSpanNameColumnWidth={setSpanNameColumnWidth} collapseAll={collapseAll} collapseOne={collapseOne} expandAll={expandAll} expandOne={expandOne} childrenToggle={childrenToggle} - clearShouldScrollToFirstUiFindMatch={() => {}} + clearShouldScrollToFirstUiFindMatch={useCallback(() => {}, [])} detailLogItemToggle={detailLogItemToggle} detailLogsToggle={detailLogsToggle} detailWarningsToggle={detailWarningsToggle} @@ -117,10 +117,10 @@ export function TraceView(props: Props) { detailProcessToggle={detailProcessToggle} detailTagsToggle={detailTagsToggle} detailToggle={toggleDetail} - setTrace={(trace: Trace | null, uiFind: string | null) => {}} + setTrace={useCallback((trace: Trace | null, uiFind: string | null) => {}, [])} addHoverIndentGuideId={addHoverIndentGuideId} removeHoverIndentGuideId={removeHoverIndentGuideId} - linksGetter={(span: Span, items: KeyValuePair[], itemIndex: number) => [] as Link[]} + linksGetter={useCallback((span: Span, items: KeyValuePair[], itemIndex: number) => [] as Link[], [])} uiFind={search} /> diff --git a/public/app/features/explore/TraceView/useChildrenState.ts b/public/app/features/explore/TraceView/useChildrenState.ts index 1315682b6f7..c1558182ab6 100644 --- a/public/app/features/explore/TraceView/useChildrenState.ts +++ b/public/app/features/explore/TraceView/useChildrenState.ts @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useCallback, useState } from 'react'; import { Span } from '@jaegertracing/jaeger-ui-components'; /** @@ -7,76 +7,88 @@ import { Span } from '@jaegertracing/jaeger-ui-components'; export function useChildrenState() { const [childrenHiddenIDs, setChildrenHiddenIDs] = useState(new Set()); - function expandOne(spans: Span[]) { - if (childrenHiddenIDs.size === 0) { - return; - } - let prevExpandedDepth = -1; - let expandNextHiddenSpan = true; - const newChildrenHiddenIDs = spans.reduce((res, s) => { - if (s.depth <= prevExpandedDepth) { - expandNextHiddenSpan = true; + const expandOne = useCallback( + function expandOne(spans: Span[]) { + if (childrenHiddenIDs.size === 0) { + return; } - if (expandNextHiddenSpan && res.has(s.spanID)) { - res.delete(s.spanID); - expandNextHiddenSpan = false; - prevExpandedDepth = s.depth; - } - return res; - }, new Set(childrenHiddenIDs)); - setChildrenHiddenIDs(newChildrenHiddenIDs); - } + let prevExpandedDepth = -1; + let expandNextHiddenSpan = true; + const newChildrenHiddenIDs = spans.reduce((res, s) => { + if (s.depth <= prevExpandedDepth) { + expandNextHiddenSpan = true; + } + if (expandNextHiddenSpan && res.has(s.spanID)) { + res.delete(s.spanID); + expandNextHiddenSpan = false; + prevExpandedDepth = s.depth; + } + return res; + }, new Set(childrenHiddenIDs)); + setChildrenHiddenIDs(newChildrenHiddenIDs); + }, + [childrenHiddenIDs] + ); - function collapseOne(spans: Span[]) { - if (shouldDisableCollapse(spans, childrenHiddenIDs)) { - return; - } - let nearestCollapsedAncestor: Span | undefined; - const newChildrenHiddenIDs = spans.reduce((res, curSpan) => { - if (nearestCollapsedAncestor && curSpan.depth <= nearestCollapsedAncestor.depth) { - res.add(nearestCollapsedAncestor.spanID); - if (curSpan.hasChildren) { + const collapseOne = useCallback( + function collapseOne(spans: Span[]) { + if (shouldDisableCollapse(spans, childrenHiddenIDs)) { + return; + } + let nearestCollapsedAncestor: Span | undefined; + const newChildrenHiddenIDs = spans.reduce((res, curSpan) => { + if (nearestCollapsedAncestor && curSpan.depth <= nearestCollapsedAncestor.depth) { + res.add(nearestCollapsedAncestor.spanID); + if (curSpan.hasChildren) { + nearestCollapsedAncestor = curSpan; + } + } else if (curSpan.hasChildren && !res.has(curSpan.spanID)) { nearestCollapsedAncestor = curSpan; } - } else if (curSpan.hasChildren && !res.has(curSpan.spanID)) { - nearestCollapsedAncestor = curSpan; + return res; + }, new Set(childrenHiddenIDs)); + // The last one + if (nearestCollapsedAncestor) { + newChildrenHiddenIDs.add(nearestCollapsedAncestor.spanID); } - return res; - }, new Set(childrenHiddenIDs)); - // The last one - if (nearestCollapsedAncestor) { - newChildrenHiddenIDs.add(nearestCollapsedAncestor.spanID); - } - setChildrenHiddenIDs(newChildrenHiddenIDs); - } + setChildrenHiddenIDs(newChildrenHiddenIDs); + }, + [childrenHiddenIDs] + ); - function expandAll() { + const expandAll = useCallback(function expandAll() { setChildrenHiddenIDs(new Set()); - } + }, []); - function collapseAll(spans: Span[]) { - if (shouldDisableCollapse(spans, childrenHiddenIDs)) { - return; - } - const newChildrenHiddenIDs = spans.reduce((res, s) => { - if (s.hasChildren) { - res.add(s.spanID); + const collapseAll = useCallback( + function collapseAll(spans: Span[]) { + if (shouldDisableCollapse(spans, childrenHiddenIDs)) { + return; } - return res; - }, new Set()); + const newChildrenHiddenIDs = spans.reduce((res, s) => { + if (s.hasChildren) { + res.add(s.spanID); + } + return res; + }, new Set()); - setChildrenHiddenIDs(newChildrenHiddenIDs); - } + setChildrenHiddenIDs(newChildrenHiddenIDs); + }, + [childrenHiddenIDs] + ); - function childrenToggle(spanID: string) { - const newChildrenHiddenIDs = new Set(childrenHiddenIDs); - if (childrenHiddenIDs.has(spanID)) { - newChildrenHiddenIDs.delete(spanID); - } else { - newChildrenHiddenIDs.add(spanID); - } - setChildrenHiddenIDs(newChildrenHiddenIDs); - } + const childrenToggle = useCallback( + function childrenToggle(spanID: string) { + const newChildrenHiddenIDs = new Set(childrenHiddenIDs); + if (childrenHiddenIDs.has(spanID)) { + newChildrenHiddenIDs.delete(spanID); + } else { + newChildrenHiddenIDs.add(spanID); + } + setChildrenHiddenIDs(newChildrenHiddenIDs); + }, + [childrenHiddenIDs] + ); return { childrenHiddenIDs, diff --git a/public/app/features/explore/TraceView/useDetailState.ts b/public/app/features/explore/TraceView/useDetailState.ts index cf725a1f370..4e10874515e 100644 --- a/public/app/features/explore/TraceView/useDetailState.ts +++ b/public/app/features/explore/TraceView/useDetailState.ts @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useCallback, useState } from 'react'; import { DetailState, Log } from '@jaegertracing/jaeger-ui-components'; /** @@ -8,36 +8,48 @@ import { DetailState, Log } from '@jaegertracing/jaeger-ui-components'; export function useDetailState() { const [detailStates, setDetailStates] = useState(new Map()); - function toggleDetail(spanID: string) { - const newDetailStates = new Map(detailStates); - if (newDetailStates.has(spanID)) { - newDetailStates.delete(spanID); - } else { - newDetailStates.set(spanID, new DetailState()); - } - setDetailStates(newDetailStates); - } + const toggleDetail = useCallback( + function toggleDetail(spanID: string) { + const newDetailStates = new Map(detailStates); + if (newDetailStates.has(spanID)) { + newDetailStates.delete(spanID); + } else { + newDetailStates.set(spanID, new DetailState()); + } + setDetailStates(newDetailStates); + }, + [detailStates] + ); - function detailLogItemToggle(spanID: string, log: Log) { - const old = detailStates.get(spanID); - if (!old) { - return; - } - const detailState = old.toggleLogItem(log); - const newDetailStates = new Map(detailStates); - newDetailStates.set(spanID, detailState); - return setDetailStates(newDetailStates); - } + const detailLogItemToggle = useCallback( + function detailLogItemToggle(spanID: string, log: Log) { + const old = detailStates.get(spanID); + if (!old) { + return; + } + const detailState = old.toggleLogItem(log); + const newDetailStates = new Map(detailStates); + newDetailStates.set(spanID, detailState); + return setDetailStates(newDetailStates); + }, + [detailStates] + ); return { detailStates, toggleDetail, detailLogItemToggle, - detailLogsToggle: makeDetailSubsectionToggle('logs', detailStates, setDetailStates), - detailWarningsToggle: makeDetailSubsectionToggle('warnings', detailStates, setDetailStates), - detailReferencesToggle: makeDetailSubsectionToggle('references', detailStates, setDetailStates), - detailProcessToggle: makeDetailSubsectionToggle('process', detailStates, setDetailStates), - detailTagsToggle: makeDetailSubsectionToggle('tags', detailStates, setDetailStates), + detailLogsToggle: useCallback(makeDetailSubsectionToggle('logs', detailStates, setDetailStates), [detailStates]), + detailWarningsToggle: useCallback(makeDetailSubsectionToggle('warnings', detailStates, setDetailStates), [ + detailStates, + ]), + detailReferencesToggle: useCallback(makeDetailSubsectionToggle('references', detailStates, setDetailStates), [ + detailStates, + ]), + detailProcessToggle: useCallback(makeDetailSubsectionToggle('process', detailStates, setDetailStates), [ + detailStates, + ]), + detailTagsToggle: useCallback(makeDetailSubsectionToggle('tags', detailStates, setDetailStates), [detailStates]), }; } diff --git a/public/app/features/explore/TraceView/useHoverIndentGuide.ts b/public/app/features/explore/TraceView/useHoverIndentGuide.ts index d628b779927..0dfb0128358 100644 --- a/public/app/features/explore/TraceView/useHoverIndentGuide.ts +++ b/public/app/features/explore/TraceView/useHoverIndentGuide.ts @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useCallback, useState } from 'react'; /** * This is used internally to handle hover state of indent guide. As indent guides are separate @@ -10,21 +10,21 @@ import { useState } from 'react'; export function useHoverIndentGuide() { const [hoverIndentGuideIds, setHoverIndentGuideIds] = useState(new Set()); - function addHoverIndentGuideId(spanID: string) { + const addHoverIndentGuideId = useCallback(function addHoverIndentGuideId(spanID: string) { setHoverIndentGuideIds(prevState => { const newHoverIndentGuideIds = new Set(prevState); newHoverIndentGuideIds.add(spanID); return newHoverIndentGuideIds; }); - } + }, []); - function removeHoverIndentGuideId(spanID: string) { + const removeHoverIndentGuideId = useCallback(function removeHoverIndentGuideId(spanID: string) { setHoverIndentGuideIds(prevState => { const newHoverIndentGuideIds = new Set(prevState); newHoverIndentGuideIds.delete(spanID); return newHoverIndentGuideIds; }); - } + }, []); return { hoverIndentGuideIds, addHoverIndentGuideId, removeHoverIndentGuideId }; } diff --git a/public/app/features/explore/TraceView/useSearch.ts b/public/app/features/explore/TraceView/useSearch.ts index eb15b3ff811..a2f7bd614dd 100644 --- a/public/app/features/explore/TraceView/useSearch.ts +++ b/public/app/features/explore/TraceView/useSearch.ts @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useMemo, useState } from 'react'; import { Span, filterSpans } from '@jaegertracing/jaeger-ui-components'; /** @@ -7,9 +7,9 @@ import { Span, filterSpans } from '@jaegertracing/jaeger-ui-components'; */ export function useSearch(spans?: Span[]) { const [search, setSearch] = useState(''); - let spanFindMatches: Set | undefined; - if (search && spans) { - spanFindMatches = filterSpans(search, spans); - } + const spanFindMatches: Set | undefined = useMemo(() => { + return search && spans ? filterSpans(search, spans) : undefined; + }, [search, spans]); + return { search, setSearch, spanFindMatches }; } diff --git a/public/app/features/explore/TraceView/useViewRange.ts b/public/app/features/explore/TraceView/useViewRange.ts index 84254fd11af..82498ae3af7 100644 --- a/public/app/features/explore/TraceView/useViewRange.ts +++ b/public/app/features/explore/TraceView/useViewRange.ts @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useCallback, useState } from 'react'; import { ViewRangeTimeUpdate, ViewRange } from '@jaegertracing/jaeger-ui-components'; /** @@ -12,16 +12,16 @@ export function useViewRange() { }, }); - function updateNextViewRangeTime(update: ViewRangeTimeUpdate) { + const updateNextViewRangeTime = useCallback(function updateNextViewRangeTime(update: ViewRangeTimeUpdate) { setViewRange( (prevRange): ViewRange => { const time = { ...prevRange.time, ...update }; return { ...prevRange, time }; } ); - } + }, []); - function updateViewRangeTime(start: number, end: number) { + const updateViewRangeTime = useCallback(function updateViewRangeTime(start: number, end: number) { const current: [number, number] = [start, end]; const time = { current }; setViewRange( @@ -29,7 +29,7 @@ export function useViewRange() { return { ...prevRange, time }; } ); - } + }, []); return { viewRange, updateViewRangeTime, updateNextViewRangeTime }; }