TraceView: Remove unused trace selectors (#70527)

Remove unused selectors
This commit is contained in:
Joey 2023-06-27 09:01:35 +01:00 committed by GitHub
parent da54fe8ae8
commit 5b13b71ed0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 5 additions and 493 deletions

View File

@ -17,35 +17,14 @@ import { values as _values } from 'lodash';
import TreeNode from 'app/features/explore/TraceView/components/utils/TreeNode';
import traceGenerator from '../demo/trace-generators';
import { TraceResponse, TraceSpan, TraceSpanData } from '../types/trace';
import { numberSortComparator } from '../utils/sort';
import { TraceResponse } from '../types/trace';
import {
getSpanId,
getSpanName,
getSpanParentId,
getSpanProcess,
getSpanProcessId,
getSpanServiceName,
getSpanTimestamp,
} from './span';
import { getSpanId, getSpanParentId } from './span';
import * as traceSelectors from './trace';
import { followsFromRef } from './trace.fixture';
const generatedTrace: TraceResponse = traceGenerator.trace({ numberOfSpans: 45 });
it('getTraceId() should return the traceID', () => {
expect(traceSelectors.getTraceId(generatedTrace)).toBe(generatedTrace.traceID);
});
it('hydrateSpansWithProcesses() should return the trace with processes on each span', () => {
const hydratedTrace = traceSelectors.hydrateSpansWithProcesses(generatedTrace);
hydratedTrace.spans.forEach((span) =>
expect(getSpanProcess(span as TraceSpan)).toBe(generatedTrace.processes[getSpanProcessId(span)])
);
});
it('getTraceSpansAsMap() should return a map of all of the spans', () => {
const spanMap = traceSelectors.getTraceSpansAsMap(generatedTrace);
[...spanMap.entries()].forEach((pair) => {
@ -70,300 +49,3 @@ describe('getTraceSpanIdsAsTree()', () => {
expect(() => traceSelectors.getTraceSpanIdsAsTree(followsFromRef)).not.toThrow();
});
});
it('getParentSpan() should return the parent span of the tree', () => {
expect(traceSelectors.getParentSpan(generatedTrace)).toBe(
traceSelectors
.getTraceSpansAsMap(generatedTrace)
.get(traceSelectors.getTraceSpanIdsAsTree(generatedTrace).children[0].value)
);
});
it('getParentSpan() should return the first span if there are multiple parents', () => {
const initialTimestamp = new Date().getTime() * 1000;
const firstSpan = {
startTime: initialTimestamp,
spanID: 'my-span-1',
references: [],
};
const trace = {
spans: [
{
startTime: initialTimestamp + 2000,
spanID: 'my-span-3',
references: [],
},
firstSpan,
{
startTime: initialTimestamp + 1000,
spanID: 'my-span-2',
references: [],
},
],
} as unknown as TraceResponse;
expect(traceSelectors.getParentSpan(trace)).toBe(firstSpan);
});
it('getTraceName() should return a formatted name for the first span', () => {
const hydratedTrace = traceSelectors.hydrateSpansWithProcesses(generatedTrace);
const parentSpan = traceSelectors.getParentSpan(hydratedTrace);
expect(traceSelectors.getTraceName(hydratedTrace)).toBe(
`${getSpanServiceName(parentSpan)}: ${getSpanName(parentSpan)}`
);
});
it('getTraceSpanCount() should return the length of the spans array', () => {
expect(traceSelectors.getTraceSpanCount(generatedTrace)).toBe(generatedTrace.spans.length);
});
it('getTraceDuration() should return the duration for the span', () => {
expect(traceSelectors.getTraceDuration(generatedTrace)).toBe(generatedTrace.spans[0].duration);
});
it('getTraceTimestamp() should return the first timestamp for the conventional trace', () => {
expect(traceSelectors.getTraceTimestamp(generatedTrace)).toBe(generatedTrace.spans[0].startTime);
});
it('getTraceDepth() should determine the total depth of the trace tree', () => {
expect(traceSelectors.getTraceDepth(generatedTrace)).toBe(
traceSelectors.getTraceSpanIdsAsTree(generatedTrace).depth - 1
);
});
it('getSpanDepthForTrace() should determine the depth of a given span in the parent', () => {
function testDepthCalc(span: TraceSpanData) {
let depth = 2;
let currentId = getSpanParentId(span);
const findCurrentSpanById = (item: TraceSpanData) => getSpanId(item) === currentId;
while (currentId !== getSpanId(generatedTrace.spans[0])) {
depth++;
currentId = getSpanParentId(generatedTrace.spans.find(findCurrentSpanById)!);
}
// console.log('hypothetical depth', depth);
expect(
traceSelectors.getSpanDepthForTrace({
trace: generatedTrace,
span,
})
).toBe(depth);
}
// test depth calculations for a few random spans
testDepthCalc(generatedTrace.spans[1]);
testDepthCalc(generatedTrace.spans[Math.floor(generatedTrace.spans.length / 2)]);
testDepthCalc(generatedTrace.spans[Math.floor(generatedTrace.spans.length / 4)]);
testDepthCalc(generatedTrace.spans[Math.floor(generatedTrace.spans.length * 0.75)]);
});
it('getTraceServices() should return an unique array of all services in the trace', () => {
const svcs = [...traceSelectors.getTraceServices(generatedTrace)].sort();
const set = new Set(_values(generatedTrace.processes).map((v) => v.serviceName));
const setSvcs = [...set.values()].sort();
expect(svcs).toEqual(setSvcs);
});
it('getTraceServiceCount() should return the length of the service list', () => {
expect(traceSelectors.getTraceServiceCount(generatedTrace)).toBe(
Object.values(generatedTrace.processes).reduce((results, process) => results.add(process.serviceName), new Set())
.size
);
});
it('formatDurationForUnit() should use the formatters to return the proper value', () => {
expect(traceSelectors.formatDurationForUnit({ duration: 302000, unit: 'ms' })).toBe('302ms');
expect(traceSelectors.formatDurationForUnit({ duration: 1302000, unit: 'ms' })).toBe('1302ms');
expect(traceSelectors.formatDurationForUnit({ duration: 1302000, unit: 's' })).toBe('1.302s');
expect(traceSelectors.formatDurationForUnit({ duration: 90000, unit: 's' })).toBe('0.09s');
});
it('formatDurationForTrace() should return a ms value for traces shorter than a second', () => {
const firstSpan = generatedTrace.spans[0];
firstSpan.duration = 600000;
expect(
traceSelectors.formatDurationForTrace({
trace: {
...generatedTrace,
spans: [firstSpan],
},
duration: 302000,
})
).toBe('302ms');
});
it('formatDurationForTrace() should return a s value for traces longer than a second', () => {
expect(
traceSelectors.formatDurationForTrace({
trace: {
...generatedTrace,
spans: generatedTrace.spans.concat([
{
...generatedTrace.spans[0],
duration: 1000000,
},
]),
},
duration: 302000,
})
).toBe('0.302s');
expect(
traceSelectors.formatDurationForTrace({
trace: {
...generatedTrace,
spans: generatedTrace.spans.concat([
{
...generatedTrace.spans[0],
duration: 1200000,
},
]),
},
duration: 302000,
})
).toBe('0.302s');
});
it('getSortedSpans() should sort spans given a sort object', () => {
expect(
traceSelectors.getSortedSpans({
trace: generatedTrace,
spans: generatedTrace.spans,
sort: {
dir: 1,
comparator: numberSortComparator,
selector: getSpanTimestamp,
},
})
).toEqual([...generatedTrace.spans].sort((spanA, spanB) => spanA.startTime - spanB.startTime));
expect(
traceSelectors.getSortedSpans({
trace: generatedTrace,
spans: generatedTrace.spans,
sort: {
dir: -1,
comparator: numberSortComparator,
selector: getSpanTimestamp,
},
})
).toEqual([...generatedTrace.spans].sort((spanA, spanB) => spanB.startTime - spanA.startTime));
});
it('getTreeSizeForTraceSpan() should return the size for the parent span', () => {
expect(
traceSelectors.getTreeSizeForTraceSpan({
trace: generatedTrace,
span: generatedTrace.spans[0],
})
).toBe(generatedTrace.spans.length - 1);
});
it('getTreeSizeForTraceSpan() should return the size for a child span', () => {
expect(
traceSelectors.getTreeSizeForTraceSpan({
trace: generatedTrace,
span: generatedTrace.spans[1],
})
).toBe(traceSelectors.getTraceSpanIdsAsTree(generatedTrace).find(generatedTrace.spans[1].spanID)!.size - 1);
});
it('getTreeSizeForTraceSpan() should return -1 for an absent span', () => {
const absentSpan = generatedTrace.spans[0];
absentSpan.spanID = 'whatever';
expect(
traceSelectors.getTreeSizeForTraceSpan({
trace: generatedTrace,
span: absentSpan,
})
).toBe(-1);
});
it('getTraceName() should return the trace name based on the parentSpan', () => {
const serviceName = generatedTrace.processes[generatedTrace.spans[0].processID].serviceName;
const operationName = generatedTrace.spans[0].operationName;
expect(traceSelectors.getTraceName(generatedTrace)).toBe(`${serviceName}: ${operationName}`);
});
it('omitCollapsedSpans() should filter out collapsed spans', () => {
const span = generatedTrace.spans[1];
const size = traceSelectors.getTraceSpanIdsAsTree(generatedTrace).find(span.spanID)!.size - 1;
expect(
traceSelectors.omitCollapsedSpans({
trace: generatedTrace,
spans: generatedTrace.spans,
collapsed: [span.spanID],
}).length
).toBe(generatedTrace.spans.length - size);
});
it('getTicksForTrace() should return a list of ticks given interval parameters', () => {
const trace = generatedTrace;
const timestamp = new Date().getTime() * 1000;
trace.spans.forEach((span) => {
span.duration = 3000000;
span.startTime = timestamp;
});
expect(
traceSelectors.getTicksForTrace({
trace,
interval: 3,
width: 10,
})
).toEqual([
{ timestamp, width: 10 },
{ timestamp: timestamp + 1000000, width: 10 },
{ timestamp: timestamp + 2000000, width: 10 },
{ timestamp: timestamp + 3000000, width: 10 },
]);
});
it('getTicksForTrace() should use defaults', () => {
const timestamp = new Date().getTime() * 1000;
const trace = traceGenerator.trace({ numberOfSpans: 1 });
trace.spans = [
{
traceID: '5031233a-d0b5-5d41-9b4b-4c072bcf5020',
processID: 'b5f4e0ff-7318-5017-a3f3-9c7b423a82aa',
spanID: 'e871771f-f1b4-54af-9e9d-826259c2915e',
flags: 0,
logs: [],
operationName: 'POST',
startTime: timestamp,
duration: 4000000,
},
];
expect(traceSelectors.getTicksForTrace({ trace })).toEqual([
{ timestamp, width: traceSelectors.DEFAULT_TICK_WIDTH },
{
timestamp: timestamp + 1000000,
width: traceSelectors.DEFAULT_TICK_WIDTH,
},
{
timestamp: timestamp + 2000000,
width: traceSelectors.DEFAULT_TICK_WIDTH,
},
{
timestamp: timestamp + 3000000,
width: traceSelectors.DEFAULT_TICK_WIDTH,
},
{
timestamp: timestamp + 4000000,
width: traceSelectors.DEFAULT_TICK_WIDTH,
},
]);
});

View File

@ -12,35 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { createSelector, createStructuredSelector } from 'reselect';
import { createSelector } from 'reselect';
import { Trace, TraceData, TraceProcess, TraceResponse, TraceSpanData } from '../types/trace';
import { TraceResponse, TraceSpanData } from '../types/trace';
import TreeNode from '../utils/TreeNode';
import { formatMillisecondTime, formatSecondTime, ONE_SECOND } from '../utils/date';
import { numberSortComparator } from '../utils/sort';
import { getProcessServiceName } from './process';
import {
getSpanId,
getSpanName,
getSpanServiceName,
getSpanTimestamp,
getSpanDuration,
getSpanProcessId,
} from './span';
import { getSpanId } from './span';
export const getTraceId = (trace: TraceData) => trace.traceID;
export const getTraceSpans = (trace: TraceResponse) => trace.spans;
const getTraceProcesses = (trace: TraceData | Trace) => trace.processes;
const getSpanWithProcess = createSelector(
(state: { span: TraceSpanData; processes: Record<string, TraceProcess> }) => state.span,
(state: { span: TraceSpanData; processes: Record<string, TraceProcess> }) => state.processes,
(span, processes) => ({
...span,
process: processes[getSpanProcessId(span)],
})
);
export const getTraceSpansAsMap = createSelector(getTraceSpans, (spans) =>
spans.reduce((map, span: TraceSpanData) => map.set(getSpanId(span), span), new Map())
@ -95,127 +74,6 @@ export function getTraceSpanIdsAsTree(trace: TraceResponse) {
return root;
}
// attach "process" as an object to each span.
export const hydrateSpansWithProcesses = (trace: TraceResponse) => {
const spans = getTraceSpans(trace);
const processes = getTraceProcesses(trace);
return {
...trace,
spans: spans.map((span: TraceSpanData) => getSpanWithProcess({ span, processes })),
};
};
export const getTraceSpanCount = createSelector(getTraceSpans, (spans) => spans.length);
export const getTraceTimestamp = createSelector(getTraceSpans, (spans) =>
spans.reduce(
(prevTimestamp: number, span: TraceSpanData) =>
prevTimestamp ? Math.min(prevTimestamp, getSpanTimestamp(span)) : getSpanTimestamp(span),
0
)
);
export const getTraceDuration = createSelector(getTraceSpans, getTraceTimestamp, (spans, timestamp) =>
spans.reduce(
(prevDuration: number, span: TraceSpanData) =>
prevDuration
? Math.max(getSpanTimestamp(span) - timestamp! + getSpanDuration(span), prevDuration)
: getSpanDuration(span),
0
)
);
export const getParentSpan = createSelector(
getTraceSpanIdsAsTree,
getTraceSpansAsMap,
(tree, spanMap) =>
tree.children
.map((node: TreeNode) => spanMap.get(node.value))
.sort((spanA: TraceSpanData, spanB: TraceSpanData) =>
numberSortComparator(getSpanTimestamp(spanA), getSpanTimestamp(spanB))
)[0]
);
export const getTraceDepth = createSelector(getTraceSpanIdsAsTree, (spanTree) => spanTree.depth - 1);
export const getSpanDepthForTrace = createSelector(
createSelector((state: { trace: TraceResponse }) => state.trace, getTraceSpanIdsAsTree),
createSelector((state: { span: TraceSpanData }) => state.span, getSpanId),
(node, spanID) => node.getPath(spanID)!.length - 1
);
export const getTraceServices = createSelector(getTraceProcesses, (processes) =>
Object.keys(processes).reduce(
(services, processID) => services.add(getProcessServiceName(processes[processID])),
new Set()
)
);
export const getTraceServiceCount = createSelector(getTraceServices, (services) => services.size);
// establish constants to determine how math should be handled
// for nanosecond-to-millisecond conversions.
export const DURATION_FORMATTERS = {
ms: formatMillisecondTime,
s: formatSecondTime,
};
const getDurationFormatterForTrace = createSelector(getTraceDuration, (totalDuration: number) =>
totalDuration >= ONE_SECOND ? DURATION_FORMATTERS.s : DURATION_FORMATTERS.ms
);
export const formatDurationForUnit = createSelector(
({ duration }: { duration: number }) => duration,
({ unit }: { unit: 'ms' | 's' }) => DURATION_FORMATTERS[unit],
(duration, formatter) => formatter(duration)
);
export const formatDurationForTrace = createSelector(
({ duration }: { duration: number }) => duration,
createSelector(({ trace }: { trace: TraceResponse }) => trace, getDurationFormatterForTrace),
(duration, formatter) => formatter(duration)
);
export const getSortedSpans = createSelector(
({ trace }: { trace: TraceResponse }) => trace,
({ spans }: { spans: TraceSpanData[] }) => spans,
({
sort,
}: {
sort: {
dir: number;
comparator: (itemA: number, itemB: number) => number;
selector: (itemA: TraceSpanData, itemB: TraceResponse) => number;
};
}) => sort,
(trace, spans, { dir, comparator, selector }) =>
[...spans].sort((spanA, spanB) => dir * comparator(selector(spanA, trace), selector(spanB, trace)))
);
export const getTreeSizeForTraceSpan = createSelector(
createSelector((state: { trace: TraceResponse }) => state.trace, getTraceSpanIdsAsTree),
createSelector((state: { span: TraceSpanData }) => state.span, getSpanId),
(tree, spanID) => {
const node = tree.find(spanID);
if (!node) {
return -1;
}
return node.size - 1;
}
);
export const getTraceName = createSelector(
createSelector(
createSelector(hydrateSpansWithProcesses, getParentSpan),
createStructuredSelector({
name: getSpanName,
serviceName: getSpanServiceName,
})
),
({ name, serviceName }: { name: string; serviceName: string }) => `${serviceName}: ${name}`
);
export const omitCollapsedSpans = createSelector(
({ spans }: { spans: TraceSpanData[] }) => spans,
createSelector(({ trace }: { trace: TraceResponse }) => trace, getTraceSpanIdsAsTree),
@ -229,21 +87,3 @@ export const omitCollapsedSpans = createSelector(
return hiddenSpanIds.size > 0 ? spans.filter((span) => !hiddenSpanIds.has(getSpanId(span))) : spans;
}
);
export const DEFAULT_TICK_INTERVAL = 4;
export const DEFAULT_TICK_WIDTH = 3;
export const getTicksForTrace = createSelector(
({ trace }: { trace: TraceResponse }) => trace,
({ interval = DEFAULT_TICK_INTERVAL }: { interval?: number }) => interval,
({ width = DEFAULT_TICK_WIDTH }: { width?: number }) => width,
(
trace,
interval: number,
width: number
// timestamps will be spaced over the interval, starting from the initial timestamp
) =>
[...Array(interval + 1).keys()].map((num) => ({
timestamp: getTraceTimestamp(trace) + getTraceDuration(trace) * (num / interval),
width,
}))
);

View File

@ -36,12 +36,6 @@ it('localeStringComparator() should properly sort a list of strings', () => {
]);
});
it('numberSortComparator() should properly sort a list of numbers', () => {
const arr = [3, -1.1, 4, -1, 9, 4, 2, Infinity, 0, 0];
expect(arr.sort(sortUtils.numberSortComparator)).toEqual([-1.1, -1, 0, 0, 2, 3, 4, 4, 9, Infinity]);
});
it('classNameForSortDir() should return the proper asc classes', () => {
expect(sortUtils.classNameForSortDir(1)).toBe('sorted ascending');
});

View File

@ -16,10 +16,6 @@ export function localeStringComparator(itemA: string, itemB: string) {
return itemA.localeCompare(itemB);
}
export function numberSortComparator(itemA: number, itemB: number) {
return itemA - itemB;
}
export function classNameForSortDir(dir: number) {
return `sorted ${dir === 1 ? 'ascending' : 'descending'}`;
}