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 <david.kaltschmidt@gmail.com>
This commit is contained in:
Andrej Ocenas
2020-04-15 16:04:01 +02:00
committed by GitHub
parent f48d444a14
commit 1864807b15
10 changed files with 178 additions and 142 deletions

View File

@@ -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

View File

@@ -50,18 +50,22 @@ export function ThemeConsumer(props: ThemeConsumerProps) {
return (
<ThemeContext.Consumer>
{(value: ThemeOptions | undefined) => {
const mergedTheme: Theme = value
? {
...defaultTheme,
...value,
}
: defaultTheme;
return props.children(mergedTheme);
const theme = memoizedThemeMerge(value);
return props.children(theme);
}}
</ThemeContext.Consumer>
);
}
const memoizedThemeMerge = memoizeOne(value => {
return value
? {
...defaultTheme,
...value,
}
: defaultTheme;
});
type WrappedWithThemeComponent<Props> = React.ComponentType<Omit<Props, 'theme'>> & {
wrapped: React.ComponentType<Props>;
};

View File

@@ -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 &&

View File

@@ -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) {
</UIInputGroup>
</div>
);
}
});

View File

@@ -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 (
<ThemeProvider
value={{
type: theme.isDark ? ThemeType.Dark : ThemeType.Light,
}}
>
<ThemeProvider value={traceTheme}>
<UIElementsContext.Provider value={UIElements}>
<TracePageHeader
canCollapse={true}
clearSearch={() => {}}
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}
/>
<TraceTimelineViewer
registerAccessors={() => {}}
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}
/>
</UIElementsContext.Provider>

View File

@@ -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<string>());
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<string>());
}
}, []);
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<string>());
const newChildrenHiddenIDs = spans.reduce((res, s) => {
if (s.hasChildren) {
res.add(s.spanID);
}
return res;
}, new Set<string>());
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,

View File

@@ -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<string, DetailState>());
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]),
};
}

View File

@@ -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<string>());
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 };
}

View File

@@ -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<string> | undefined;
if (search && spans) {
spanFindMatches = filterSpans(search, spans);
}
const spanFindMatches: Set<string> | undefined = useMemo(() => {
return search && spans ? filterSpans(search, spans) : undefined;
}, [search, spans]);
return { search, setSearch, spanFindMatches };
}

View File

@@ -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 };
}