mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Chore: Refactor span.js, trace.js & trace.fixture.js to TypeScript (#58006)
This commit is contained in:
parent
ce630b2dc5
commit
5073839f8f
@ -12,11 +12,14 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { TraceResponse } from 'src/types';
|
||||
import { TraceSpan, TraceSpanData } from 'src/types/trace';
|
||||
|
||||
import traceGenerator from '../demo/trace-generators';
|
||||
|
||||
import * as spanSelectors from './span';
|
||||
|
||||
const generatedTrace = traceGenerator.trace({ numberOfSpans: 45 });
|
||||
const generatedTrace: TraceResponse = traceGenerator.trace({ numberOfSpans: 45 });
|
||||
|
||||
it('getSpanId() should return the name of the span', () => {
|
||||
const span = generatedTrace.spans[0];
|
||||
@ -46,8 +49,10 @@ it('getSpanReferences() should return the span reference array', () => {
|
||||
expect(spanSelectors.getSpanReferences(generatedTrace.spans[0])).toEqual(generatedTrace.spans[0].references);
|
||||
});
|
||||
|
||||
it('getSpanReferences() should return empty array for null references', () => {
|
||||
expect(spanSelectors.getSpanReferences({ references: null })).toEqual([]);
|
||||
it('getSpanReferences() should return an empty array when references is undefined', () => {
|
||||
const span = generatedTrace.spans[0];
|
||||
span.references = undefined;
|
||||
expect(spanSelectors.getSpanReferences(span)).toEqual([]);
|
||||
});
|
||||
|
||||
it('getSpanReferenceByType() should return the span reference requested', () => {
|
||||
@ -55,7 +60,7 @@ it('getSpanReferenceByType() should return the span reference requested', () =>
|
||||
spanSelectors.getSpanReferenceByType({
|
||||
span: generatedTrace.spans[1],
|
||||
type: 'CHILD_OF',
|
||||
}).refType
|
||||
})?.refType
|
||||
).toBe('CHILD_OF');
|
||||
});
|
||||
|
||||
@ -70,7 +75,7 @@ it('getSpanReferenceByType() should return undefined if one does not exist', ()
|
||||
|
||||
it('getSpanParentId() should return the spanID of the parent span', () => {
|
||||
expect(spanSelectors.getSpanParentId(generatedTrace.spans[1])).toBe(
|
||||
generatedTrace.spans[1].references.find(({ refType }) => refType === 'CHILD_OF').spanID
|
||||
generatedTrace.spans[1].references!.find(({ refType }: { refType: string }) => refType === 'CHILD_OF')!.spanID
|
||||
);
|
||||
});
|
||||
|
||||
@ -85,16 +90,17 @@ it('getSpanProcessId() should return the processID of the span', () => {
|
||||
});
|
||||
|
||||
it('getSpanProcess() should return the process of the span', () => {
|
||||
const serviceName = 'bagel';
|
||||
const span = {
|
||||
...generatedTrace.spans[0],
|
||||
process: {},
|
||||
};
|
||||
process: { serviceName },
|
||||
} as TraceSpan;
|
||||
|
||||
expect(spanSelectors.getSpanProcess(span)).toBe(span.process);
|
||||
});
|
||||
|
||||
it('getSpanProcess() should throw if no process exists', () => {
|
||||
expect(() => spanSelectors.getSpanProcess(generatedTrace.spans[0])).toThrow();
|
||||
expect(() => spanSelectors.getSpanProcess(generatedTrace.spans[0] as TraceSpan)).toThrow();
|
||||
});
|
||||
|
||||
it('getSpanServiceName() should return the service name of the span', () => {
|
||||
@ -102,7 +108,7 @@ it('getSpanServiceName() should return the service name of the span', () => {
|
||||
const span = {
|
||||
...generatedTrace.spans[0],
|
||||
process: { serviceName },
|
||||
};
|
||||
} as TraceSpan;
|
||||
|
||||
expect(spanSelectors.getSpanServiceName(span)).toBe(serviceName);
|
||||
});
|
||||
@ -112,17 +118,17 @@ it('filterSpansForTimestamps() should return a filtered list of spans between th
|
||||
const spans = [
|
||||
{
|
||||
startTime: now - 1000,
|
||||
id: 'start-time-1',
|
||||
spanID: 'start-time-1',
|
||||
},
|
||||
{
|
||||
startTime: now,
|
||||
id: 'start-time-2',
|
||||
spanID: 'start-time-2',
|
||||
},
|
||||
{
|
||||
startTime: now + 1000,
|
||||
id: 'start-time-3',
|
||||
spanID: 'start-time-3',
|
||||
},
|
||||
];
|
||||
] as TraceSpanData[];
|
||||
|
||||
expect(
|
||||
spanSelectors.filterSpansForTimestamps({
|
||||
@ -164,23 +170,23 @@ it('filterSpansForText() should return a filtered list of spans between the time
|
||||
process: {
|
||||
serviceName: 'alpha',
|
||||
},
|
||||
id: 'start-time-1',
|
||||
spanID: 'start-time-1',
|
||||
},
|
||||
{
|
||||
operationName: 'GET /another',
|
||||
process: {
|
||||
serviceName: 'beta',
|
||||
},
|
||||
id: 'start-time-1',
|
||||
spanID: 'start-time-1',
|
||||
},
|
||||
{
|
||||
operationName: 'POST /mything',
|
||||
process: {
|
||||
serviceName: 'alpha',
|
||||
},
|
||||
id: 'start-time-1',
|
||||
spanID: 'start-time-1',
|
||||
},
|
||||
];
|
||||
] as TraceSpan[];
|
||||
|
||||
expect(
|
||||
spanSelectors.filterSpansForText({
|
@ -15,25 +15,27 @@
|
||||
import fuzzy from 'fuzzy';
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
import { TraceSpan, TraceSpanData, TraceSpanReference } from '../types/trace';
|
||||
|
||||
import { getProcessServiceName } from './process';
|
||||
|
||||
export const getSpanId = (span) => span.spanID;
|
||||
export const getSpanName = (span) => span.operationName;
|
||||
export const getSpanDuration = (span) => span.duration;
|
||||
export const getSpanTimestamp = (span) => span.startTime;
|
||||
export const getSpanProcessId = (span) => span.processID;
|
||||
export const getSpanReferences = (span) => span.references || [];
|
||||
export const getSpanId = (span: TraceSpanData) => span.spanID;
|
||||
export const getSpanName = (span: TraceSpanData) => span.operationName;
|
||||
export const getSpanDuration = (span: TraceSpanData) => span.duration;
|
||||
export const getSpanTimestamp = (span: TraceSpanData) => span.startTime;
|
||||
export const getSpanProcessId = (span: TraceSpanData) => span.processID;
|
||||
export const getSpanReferences = (span: TraceSpanData) => span.references || [];
|
||||
export const getSpanReferenceByType = createSelector(
|
||||
createSelector(({ span }) => span, getSpanReferences),
|
||||
({ type }) => type,
|
||||
(references, type) => references.find((ref) => ref.refType === type)
|
||||
createSelector(({ span }: { span: TraceSpanData }) => span, getSpanReferences),
|
||||
({ type }: { type: string }) => type,
|
||||
(references, type) => references.find((ref: TraceSpanReference) => ref.refType === type)
|
||||
);
|
||||
export const getSpanParentId = createSelector(
|
||||
(span) => getSpanReferenceByType({ span, type: 'CHILD_OF' }),
|
||||
(span: TraceSpanData) => getSpanReferenceByType({ span, type: 'CHILD_OF' }),
|
||||
(childOfRef) => (childOfRef ? childOfRef.spanID : null)
|
||||
);
|
||||
|
||||
export const getSpanProcess = (span) => {
|
||||
export const getSpanProcess = (span: TraceSpan) => {
|
||||
if (!span.process) {
|
||||
throw new Error(
|
||||
`
|
||||
@ -42,23 +44,22 @@ export const getSpanProcess = (span) => {
|
||||
`
|
||||
);
|
||||
}
|
||||
|
||||
return span.process;
|
||||
};
|
||||
|
||||
export const getSpanServiceName = createSelector(getSpanProcess, getProcessServiceName);
|
||||
|
||||
export const filterSpansForTimestamps = createSelector(
|
||||
({ spans }) => spans,
|
||||
({ leftBound }) => leftBound,
|
||||
({ rightBound }) => rightBound,
|
||||
({ spans }: { spans: TraceSpanData[] }) => spans,
|
||||
({ leftBound }: { leftBound: number }) => leftBound,
|
||||
({ rightBound }: { rightBound: number }) => rightBound,
|
||||
(spans, leftBound, rightBound) =>
|
||||
spans.filter((span) => getSpanTimestamp(span) >= leftBound && getSpanTimestamp(span) <= rightBound)
|
||||
);
|
||||
|
||||
export const filterSpansForText = createSelector(
|
||||
({ spans }) => spans,
|
||||
({ text }) => text,
|
||||
({ spans }: { spans: TraceSpan[] }) => spans,
|
||||
({ text }: { text: string }) => text,
|
||||
(spans, text) =>
|
||||
fuzzy
|
||||
.filter(text, spans, {
|
||||
@ -67,7 +68,7 @@ export const filterSpansForText = createSelector(
|
||||
.map(({ original }) => original)
|
||||
);
|
||||
|
||||
const getTextFilterdSpansAsMap = createSelector(filterSpansForText, (matchingSpans) =>
|
||||
const getTextFilteredSpansAsMap = createSelector(filterSpansForText, (matchingSpans) =>
|
||||
matchingSpans.reduce(
|
||||
(obj, span) => ({
|
||||
...obj,
|
||||
@ -77,11 +78,12 @@ const getTextFilterdSpansAsMap = createSelector(filterSpansForText, (matchingSpa
|
||||
)
|
||||
);
|
||||
|
||||
// TODO: delete this function as it is not used?
|
||||
export const highlightSpansForTextFilter = createSelector(
|
||||
({ spans }) => spans,
|
||||
getTextFilterdSpansAsMap,
|
||||
(spans, textFilteredSpansMap) =>
|
||||
spans.map((span) => ({
|
||||
({ spans }: { spans: TraceSpanData[] }) => spans,
|
||||
getTextFilteredSpansAsMap,
|
||||
(spans, textFilteredSpansMap: { [key: string]: TraceSpanData }) =>
|
||||
spans.map((span: TraceSpanData) => ({
|
||||
...span,
|
||||
muted: !textFilteredSpansMap[getSpanId(span)],
|
||||
}))
|
@ -14,6 +14,16 @@
|
||||
|
||||
// See https://github.com/jaegertracing/jaeger-ui/issues/115 for details.
|
||||
|
||||
import { TraceSpanReference } from '../types/trace';
|
||||
|
||||
const references: TraceSpanReference[] = [
|
||||
{
|
||||
refType: 'FOLLOWS_FROM',
|
||||
spanID: 'ea7cfaca83f0724b',
|
||||
traceID: '2992f2a5b5d037a8aabffd08ef384237',
|
||||
},
|
||||
];
|
||||
|
||||
export const followsFromRef = {
|
||||
processes: {
|
||||
p1: {
|
||||
@ -28,13 +38,7 @@ export const followsFromRef = {
|
||||
logs: [],
|
||||
operationName: 'thread',
|
||||
processID: 'p1',
|
||||
references: [
|
||||
{
|
||||
refType: 'FOLLOWS_FROM',
|
||||
spanID: 'ea7cfaca83f0724b',
|
||||
traceID: '2992f2a5b5d037a8aabffd08ef384237',
|
||||
},
|
||||
],
|
||||
references: references,
|
||||
spanID: '1bdf4201221bb2ac',
|
||||
startTime: 1509533706521220,
|
||||
tags: [],
|
@ -13,8 +13,10 @@
|
||||
// limitations under the License.
|
||||
|
||||
import { values as _values } from 'lodash';
|
||||
import TreeNode from 'src/utils/TreeNode';
|
||||
|
||||
import traceGenerator from '../demo/trace-generators';
|
||||
import { TraceResponse, TraceSpan, TraceSpanData } from '../types/trace';
|
||||
import { numberSortComparator } from '../utils/sort';
|
||||
|
||||
import {
|
||||
@ -29,7 +31,7 @@ import {
|
||||
import * as traceSelectors from './trace';
|
||||
import { followsFromRef } from './trace.fixture';
|
||||
|
||||
const generatedTrace = traceGenerator.trace({ numberOfSpans: 45 });
|
||||
const generatedTrace: TraceResponse = traceGenerator.trace({ numberOfSpans: 45 });
|
||||
|
||||
it('getTraceId() should return the traceID', () => {
|
||||
expect(traceSelectors.getTraceId(generatedTrace)).toBe(generatedTrace.traceID);
|
||||
@ -39,7 +41,7 @@ it('hydrateSpansWithProcesses() should return the trace with processes on each s
|
||||
const hydratedTrace = traceSelectors.hydrateSpansWithProcesses(generatedTrace);
|
||||
|
||||
hydratedTrace.spans.forEach((span) =>
|
||||
expect(getSpanProcess(span)).toBe(generatedTrace.processes[getSpanProcessId(span)])
|
||||
expect(getSpanProcess(span as TraceSpan)).toBe(generatedTrace.processes[getSpanProcessId(span)])
|
||||
);
|
||||
});
|
||||
|
||||
@ -55,7 +57,7 @@ describe('getTraceSpanIdsAsTree()', () => {
|
||||
const tree = traceSelectors.getTraceSpanIdsAsTree(generatedTrace);
|
||||
const spanMap = traceSelectors.getTraceSpansAsMap(generatedTrace);
|
||||
|
||||
tree.walk((value, node) => {
|
||||
tree.walk((value: string | number | undefined, node: TreeNode) => {
|
||||
const expectedParentValue = value === traceSelectors.TREE_ROOT_ID ? null : value;
|
||||
node.children.forEach((childNode) => {
|
||||
expect(getSpanParentId(spanMap.get(childNode.value))).toBe(expectedParentValue);
|
||||
@ -98,7 +100,7 @@ it('getParentSpan() should return the first span if there are multiple parents',
|
||||
references: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
} as unknown as TraceResponse;
|
||||
|
||||
expect(traceSelectors.getParentSpan(trace)).toBe(firstSpan);
|
||||
});
|
||||
@ -131,14 +133,14 @@ it('getTraceDepth() should determine the total depth of the trace tree', () => {
|
||||
});
|
||||
|
||||
it('getSpanDepthForTrace() should determine the depth of a given span in the parent', () => {
|
||||
function testDepthCalc(span) {
|
||||
function testDepthCalc(span: TraceSpanData) {
|
||||
let depth = 2;
|
||||
let currentId = getSpanParentId(span);
|
||||
|
||||
const findCurrentSpanById = (item) => getSpanId(item) === currentId;
|
||||
const findCurrentSpanById = (item: TraceSpanData) => getSpanId(item) === currentId;
|
||||
while (currentId !== getSpanId(generatedTrace.spans[0])) {
|
||||
depth++;
|
||||
currentId = getSpanParentId(generatedTrace.spans.find(findCurrentSpanById));
|
||||
currentId = getSpanParentId(generatedTrace.spans.find(findCurrentSpanById)!);
|
||||
}
|
||||
|
||||
// console.log('hypothetical depth', depth);
|
||||
@ -183,10 +185,13 @@ it('formatDurationForUnit() should use the formatters to return the proper value
|
||||
});
|
||||
|
||||
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: {
|
||||
spans: [{ duration: 600000 }],
|
||||
...generatedTrace,
|
||||
spans: [firstSpan],
|
||||
},
|
||||
duration: 302000,
|
||||
})
|
||||
@ -266,14 +271,17 @@ it('getTreeSizeForTraceSpan() should return the size for a child span', () => {
|
||||
trace: generatedTrace,
|
||||
span: generatedTrace.spans[1],
|
||||
})
|
||||
).toBe(traceSelectors.getTraceSpanIdsAsTree(generatedTrace).find(generatedTrace.spans[1].spanID).size - 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: { spanID: 'whatever' },
|
||||
span: absentSpan,
|
||||
})
|
||||
).toBe(-1);
|
||||
});
|
||||
@ -287,7 +295,7 @@ it('getTraceName() should return the trace name based on the parentSpan', () =>
|
||||
|
||||
it('omitCollapsedSpans() should filter out collapsed spans', () => {
|
||||
const span = generatedTrace.spans[1];
|
||||
const size = traceSelectors.getTraceSpanIdsAsTree(generatedTrace).find(span.spanID).size - 1;
|
||||
const size = traceSelectors.getTraceSpanIdsAsTree(generatedTrace).find(span.spanID)!.size - 1;
|
||||
|
||||
expect(
|
||||
traceSelectors.omitCollapsedSpans({
|
||||
@ -299,15 +307,13 @@ it('omitCollapsedSpans() should filter out collapsed spans', () => {
|
||||
});
|
||||
|
||||
it('getTicksForTrace() should return a list of ticks given interval parameters', () => {
|
||||
const trace = generatedTrace;
|
||||
const timestamp = new Date().getTime() * 1000;
|
||||
const trace = {
|
||||
spans: [
|
||||
{
|
||||
startTime: timestamp,
|
||||
duration: 3000000,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
trace.spans.forEach((span) => {
|
||||
span.duration = 3000000;
|
||||
span.startTime = timestamp;
|
||||
});
|
||||
|
||||
expect(
|
||||
traceSelectors.getTicksForTrace({
|
||||
@ -325,14 +331,20 @@ it('getTicksForTrace() should return a list of ticks given interval parameters',
|
||||
|
||||
it('getTicksForTrace() should use defaults', () => {
|
||||
const timestamp = new Date().getTime() * 1000;
|
||||
const trace = {
|
||||
spans: [
|
||||
{
|
||||
startTime: timestamp,
|
||||
duration: 4000000,
|
||||
},
|
||||
],
|
||||
};
|
||||
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 },
|
@ -14,6 +14,7 @@
|
||||
|
||||
import { createSelector, createStructuredSelector } from 'reselect';
|
||||
|
||||
import { Trace, TraceData, TraceProcess, TraceResponse, TraceSpan, TraceSpanData } from '../types/trace';
|
||||
import TreeNode from '../utils/TreeNode';
|
||||
import { formatMillisecondTime, formatSecondTime, ONE_SECOND } from '../utils/date';
|
||||
import { numberSortComparator } from '../utils/sort';
|
||||
@ -28,15 +29,13 @@ import {
|
||||
getSpanProcessId,
|
||||
} from './span';
|
||||
|
||||
export const getTraceId = (trace) => trace.traceID;
|
||||
|
||||
export const getTraceSpans = (trace) => trace.spans;
|
||||
|
||||
const getTraceProcesses = (trace) => trace.processes;
|
||||
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) => state.span,
|
||||
(state) => state.processes,
|
||||
(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)],
|
||||
@ -44,7 +43,7 @@ const getSpanWithProcess = createSelector(
|
||||
);
|
||||
|
||||
export const getTraceSpansAsMap = createSelector(getTraceSpans, (spans) =>
|
||||
spans.reduce((map, span) => map.set(getSpanId(span), span), new Map())
|
||||
spans.reduce((map, span: TraceSpanData) => map.set(getSpanId(span), span), new Map())
|
||||
);
|
||||
|
||||
export const TREE_ROOT_ID = '__root__';
|
||||
@ -63,17 +62,17 @@ export const TREE_ROOT_ID = '__root__';
|
||||
* @return {TreeNode} A tree of spanIDs derived from the relationships
|
||||
* between spans in the trace.
|
||||
*/
|
||||
export function getTraceSpanIdsAsTree(trace) {
|
||||
const nodesById = new Map(trace.spans.map((span) => [span.spanID, new TreeNode(span.spanID)]));
|
||||
const spansById = new Map(trace.spans.map((span) => [span.spanID, span]));
|
||||
export function getTraceSpanIdsAsTree(trace: TraceResponse) {
|
||||
const nodesById = new Map(trace.spans.map((span: TraceSpanData) => [span.spanID, new TreeNode(span.spanID)]));
|
||||
const spansById = new Map(trace.spans.map((span: TraceSpanData) => [span.spanID, span]));
|
||||
const root = new TreeNode(TREE_ROOT_ID);
|
||||
trace.spans.forEach((span) => {
|
||||
const node = nodesById.get(span.spanID);
|
||||
trace.spans.forEach((span: TraceSpanData) => {
|
||||
const node = nodesById.get(span.spanID)!;
|
||||
if (Array.isArray(span.references) && span.references.length) {
|
||||
const { refType, spanID: parentID } = span.references[0];
|
||||
if (refType === 'CHILD_OF' || refType === 'FOLLOWS_FROM') {
|
||||
const parent = nodesById.get(parentID) || root;
|
||||
parent.children.push(node);
|
||||
parent.children?.push(node);
|
||||
} else {
|
||||
throw new Error(`Unrecognized ref type: ${refType}`);
|
||||
}
|
||||
@ -81,15 +80,15 @@ export function getTraceSpanIdsAsTree(trace) {
|
||||
root.children.push(node);
|
||||
}
|
||||
});
|
||||
const comparator = (nodeA, nodeB) => {
|
||||
const a = spansById.get(nodeA.value);
|
||||
const b = spansById.get(nodeB.value);
|
||||
return +(a.startTime > b.startTime) || +(a.startTime === b.startTime) - 1;
|
||||
const comparator = (nodeA: TreeNode | undefined, nodeB: TreeNode | undefined) => {
|
||||
const a: TraceSpanData | undefined = nodeA?.value ? spansById.get(nodeA.value.toString()) : undefined;
|
||||
const b: TraceSpanData | undefined = nodeB?.value ? spansById.get(nodeB.value.toString()) : undefined;
|
||||
return +(a?.startTime! > b?.startTime!) || +(a?.startTime === b?.startTime) - 1;
|
||||
};
|
||||
trace.spans.forEach((span) => {
|
||||
const node = nodesById.get(span.spanID);
|
||||
if (node.children.length > 1) {
|
||||
node.children.sort(comparator);
|
||||
trace.spans.forEach((span: TraceSpanData) => {
|
||||
const node: TreeNode | undefined = nodesById.get(span.spanID);
|
||||
if (node!.children.length > 1) {
|
||||
node?.children.sort(comparator);
|
||||
}
|
||||
});
|
||||
root.children.sort(comparator);
|
||||
@ -97,13 +96,13 @@ export function getTraceSpanIdsAsTree(trace) {
|
||||
}
|
||||
|
||||
// attach "process" as an object to each span.
|
||||
export const hydrateSpansWithProcesses = (trace) => {
|
||||
export const hydrateSpansWithProcesses = (trace: TraceResponse) => {
|
||||
const spans = getTraceSpans(trace);
|
||||
const processes = getTraceProcesses(trace);
|
||||
|
||||
return {
|
||||
...trace,
|
||||
spans: spans.map((span) => getSpanWithProcess({ span, processes })),
|
||||
spans: spans.map((span: TraceSpanData) => getSpanWithProcess({ span, processes })),
|
||||
};
|
||||
};
|
||||
|
||||
@ -111,25 +110,26 @@ export const getTraceSpanCount = createSelector(getTraceSpans, (spans) => spans.
|
||||
|
||||
export const getTraceTimestamp = createSelector(getTraceSpans, (spans) =>
|
||||
spans.reduce(
|
||||
(prevTimestamp, span) => (prevTimestamp ? Math.min(prevTimestamp, getSpanTimestamp(span)) : getSpanTimestamp(span)),
|
||||
null
|
||||
(prevTimestamp: number, span: TraceSpanData) =>
|
||||
prevTimestamp ? Math.min(prevTimestamp, getSpanTimestamp(span)) : getSpanTimestamp(span),
|
||||
0
|
||||
)
|
||||
);
|
||||
|
||||
export const getTraceDuration = createSelector(getTraceSpans, getTraceTimestamp, (spans, timestamp) =>
|
||||
spans.reduce(
|
||||
(prevDuration, span) =>
|
||||
(prevDuration: number, span: TraceSpanData) =>
|
||||
prevDuration
|
||||
? Math.max(getSpanTimestamp(span) - timestamp + getSpanDuration(span), prevDuration)
|
||||
? Math.max(getSpanTimestamp(span) - timestamp! + getSpanDuration(span), prevDuration)
|
||||
: getSpanDuration(span),
|
||||
null
|
||||
0
|
||||
)
|
||||
);
|
||||
|
||||
export const getTraceEndTimestamp = createSelector(
|
||||
getTraceTimestamp,
|
||||
getTraceDuration,
|
||||
(timestamp, duration) => timestamp + duration
|
||||
(timestamp: number, duration: number) => timestamp! + duration
|
||||
);
|
||||
|
||||
export const getParentSpan = createSelector(
|
||||
@ -137,16 +137,18 @@ export const getParentSpan = createSelector(
|
||||
getTraceSpansAsMap,
|
||||
(tree, spanMap) =>
|
||||
tree.children
|
||||
.map((node) => spanMap.get(node.value))
|
||||
.sort((spanA, spanB) => numberSortComparator(getSpanTimestamp(spanA), getSpanTimestamp(spanB)))[0]
|
||||
.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) => state.trace, getTraceSpanIdsAsTree),
|
||||
createSelector((state) => state.span, getSpanId),
|
||||
(node, spanID) => node.getPath(spanID).length - 1
|
||||
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) =>
|
||||
@ -165,26 +167,34 @@ export const DURATION_FORMATTERS = {
|
||||
s: formatSecondTime,
|
||||
};
|
||||
|
||||
const getDurationFormatterForTrace = createSelector(getTraceDuration, (totalDuration) =>
|
||||
const getDurationFormatterForTrace = createSelector(getTraceDuration, (totalDuration: number) =>
|
||||
totalDuration >= ONE_SECOND ? DURATION_FORMATTERS.s : DURATION_FORMATTERS.ms
|
||||
);
|
||||
|
||||
export const formatDurationForUnit = createSelector(
|
||||
({ duration }) => duration,
|
||||
({ unit }) => DURATION_FORMATTERS[unit],
|
||||
({ duration }: { duration: number }) => duration,
|
||||
({ unit }: { unit: 'ms' | 's' }) => DURATION_FORMATTERS[unit],
|
||||
(duration, formatter) => formatter(duration)
|
||||
);
|
||||
|
||||
export const formatDurationForTrace = createSelector(
|
||||
({ duration }) => duration,
|
||||
createSelector(({ trace }) => trace, getDurationFormatterForTrace),
|
||||
({ duration }: { duration: number }) => duration,
|
||||
createSelector(({ trace }: { trace: TraceResponse }) => trace, getDurationFormatterForTrace),
|
||||
(duration, formatter) => formatter(duration)
|
||||
);
|
||||
|
||||
export const getSortedSpans = createSelector(
|
||||
({ trace }) => trace,
|
||||
({ spans }) => spans,
|
||||
({ sort }) => sort,
|
||||
({ 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)))
|
||||
);
|
||||
@ -192,13 +202,13 @@ export const getSortedSpans = createSelector(
|
||||
const getTraceSpansByHierarchyPosition = createSelector(getTraceSpanIdsAsTree, (tree) => {
|
||||
const hierarchyPositionMap = new Map();
|
||||
let i = 0;
|
||||
tree.walk((spanID) => hierarchyPositionMap.set(spanID, i++));
|
||||
tree.walk((spanID: string | number | undefined) => hierarchyPositionMap.set(spanID, i++));
|
||||
return hierarchyPositionMap;
|
||||
});
|
||||
|
||||
export const getTreeSizeForTraceSpan = createSelector(
|
||||
createSelector((state) => state.trace, getTraceSpanIdsAsTree),
|
||||
createSelector((state) => state.span, getSpanId),
|
||||
createSelector((state: { trace: TraceResponse }) => state.trace, getTraceSpanIdsAsTree),
|
||||
createSelector((state: { span: TraceSpanData }) => state.span, getSpanId),
|
||||
(tree, spanID) => {
|
||||
const node = tree.find(spanID);
|
||||
if (!node) {
|
||||
@ -209,8 +219,8 @@ export const getTreeSizeForTraceSpan = createSelector(
|
||||
);
|
||||
|
||||
export const getSpanHierarchySortPositionForTrace = createSelector(
|
||||
createSelector(({ trace }) => trace, getTraceSpansByHierarchyPosition),
|
||||
({ span }) => span,
|
||||
createSelector(({ trace }: { trace: Trace }) => trace, getTraceSpansByHierarchyPosition),
|
||||
({ span }: { span: TraceSpan }) => span,
|
||||
(hierarchyPositionMap, span) => hierarchyPositionMap.get(getSpanId(span))
|
||||
);
|
||||
|
||||
@ -222,16 +232,16 @@ export const getTraceName = createSelector(
|
||||
serviceName: getSpanServiceName,
|
||||
})
|
||||
),
|
||||
({ name, serviceName }) => `${serviceName}: ${name}`
|
||||
({ name, serviceName }: { name: string; serviceName: string }) => `${serviceName}: ${name}`
|
||||
);
|
||||
|
||||
export const omitCollapsedSpans = createSelector(
|
||||
({ spans }) => spans,
|
||||
createSelector(({ trace }) => trace, getTraceSpanIdsAsTree),
|
||||
({ collapsed }) => collapsed,
|
||||
({ spans }: { spans: TraceSpanData[] }) => spans,
|
||||
createSelector(({ trace }: { trace: TraceResponse }) => trace, getTraceSpanIdsAsTree),
|
||||
({ collapsed }: { collapsed: string[] }) => collapsed,
|
||||
(spans, tree, collapse) => {
|
||||
const hiddenSpanIds = collapse.reduce((result, collapsedSpanId) => {
|
||||
tree.find(collapsedSpanId).walk((id) => id !== collapsedSpanId && result.add(id));
|
||||
tree.find(collapsedSpanId)!.walk((id: string | number | undefined) => id !== collapsedSpanId && result.add(id));
|
||||
return result;
|
||||
}, new Set());
|
||||
|
||||
@ -242,13 +252,13 @@ export const omitCollapsedSpans = createSelector(
|
||||
export const DEFAULT_TICK_INTERVAL = 4;
|
||||
export const DEFAULT_TICK_WIDTH = 3;
|
||||
export const getTicksForTrace = createSelector(
|
||||
({ trace }) => trace,
|
||||
({ interval = DEFAULT_TICK_INTERVAL }) => interval,
|
||||
({ width = DEFAULT_TICK_WIDTH }) => width,
|
||||
({ trace }: { trace: TraceResponse }) => trace,
|
||||
({ interval = DEFAULT_TICK_INTERVAL }: { interval?: number }) => interval,
|
||||
({ width = DEFAULT_TICK_WIDTH }: { width?: number }) => width,
|
||||
(
|
||||
trace,
|
||||
interval,
|
||||
width
|
||||
interval: number,
|
||||
width: number
|
||||
// timestamps will be spaced over the interval, starting from the initial timestamp
|
||||
) =>
|
||||
[...Array(interval + 1).keys()].map((num) => ({
|
||||
@ -260,14 +270,16 @@ export const getTicksForTrace = createSelector(
|
||||
// TODO: delete this when the backend can ensure uniqueness
|
||||
/* istanbul ignore next */
|
||||
export const enforceUniqueSpanIds = createSelector(
|
||||
/* istanbul ignore next */ (trace) => trace,
|
||||
/* istanbul ignore next */ (trace: Trace) => trace,
|
||||
getTraceSpans,
|
||||
/* istanbul ignore next */ (trace, spans) => {
|
||||
const map = new Map();
|
||||
|
||||
const spanArray: TraceSpanData[] = [];
|
||||
|
||||
return {
|
||||
...trace,
|
||||
spans: spans.reduce((result, span) => {
|
||||
spans: spans.reduce((result: TraceSpanData[], span: TraceSpanData) => {
|
||||
const spanID = map.has(getSpanId(span)) ? `${getSpanId(span)}_${map.get(getSpanId(span))}` : getSpanId(span);
|
||||
const updatedSpan = { ...span, spanID };
|
||||
|
||||
@ -280,17 +292,17 @@ export const enforceUniqueSpanIds = createSelector(
|
||||
map.set(getSpanId(span), (map.get(getSpanId(span)) || 0) + 1);
|
||||
|
||||
return result.concat([updatedSpan]);
|
||||
}, []),
|
||||
}, spanArray),
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// TODO: delete this when the backend can ensure uniqueness
|
||||
export const dropEmptyStartTimeSpans = createSelector(
|
||||
/* istanbul ignore next */ (trace) => trace,
|
||||
/* istanbul ignore next */ (trace: Trace) => trace,
|
||||
getTraceSpans,
|
||||
/* istanbul ignore next */ (trace, spans) => ({
|
||||
...trace,
|
||||
spans: spans.filter((span) => !!getSpanTimestamp(span)),
|
||||
spans: spans.filter((span: TraceSpanData) => !!getSpanTimestamp(span)),
|
||||
})
|
||||
);
|
@ -22,7 +22,7 @@ export default class TreeNode {
|
||||
return (node: TreeNode) => fn(node.value, node, depth);
|
||||
}
|
||||
|
||||
static searchFunction(search: TreeNode | number | SearchFn) {
|
||||
static searchFunction(search: TreeNode | number | SearchFn | string) {
|
||||
if (typeof search === 'function') {
|
||||
return search;
|
||||
}
|
||||
@ -51,7 +51,7 @@ export default class TreeNode {
|
||||
return this;
|
||||
}
|
||||
|
||||
find(search: TreeNode | number | SearchFn): TreeNode | null {
|
||||
find(search: TreeNode | number | SearchFn | string): TreeNode | null {
|
||||
const searchFn = TreeNode.iterFunction(TreeNode.searchFunction(search));
|
||||
if (searchFn(this)) {
|
||||
return this;
|
||||
@ -65,7 +65,7 @@ export default class TreeNode {
|
||||
return null;
|
||||
}
|
||||
|
||||
getPath(search: TreeNode) {
|
||||
getPath(search: TreeNode | string) {
|
||||
const searchFn = TreeNode.iterFunction(TreeNode.searchFunction(search));
|
||||
|
||||
const findPath = (currentNode: TreeNode, currentPath: TreeNode[]): TreeNode[] | null => {
|
||||
|
Loading…
Reference in New Issue
Block a user