mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Jaeger: Sync getTraceName improvements from upstream (#37408)
* Improve trace name resolution * Use getTraceName in transformTraceData * Improve getTraceName performance * Change trace-viewer extension back to ts
This commit is contained in:
parent
0699a04dcd
commit
6906a74179
244
packages/jaeger-ui-components/src/model/find-trace-name.test.js
Normal file
244
packages/jaeger-ui-components/src/model/find-trace-name.test.js
Normal file
@ -0,0 +1,244 @@
|
||||
// Copyright (c) 2020 The Jaeger Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { _getTraceNameImpl as getTraceName } from './trace-viewer';
|
||||
|
||||
describe('getTraceName', () => {
|
||||
const firstSpanId = 'firstSpanId';
|
||||
const secondSpanId = 'secondSpanId';
|
||||
const thirdSpanId = 'thirdSpanId';
|
||||
const missingSpanId = 'missingSpanId';
|
||||
|
||||
const currentTraceId = 'currentTraceId';
|
||||
|
||||
const serviceName = 'serviceName';
|
||||
const operationName = 'operationName';
|
||||
|
||||
const t = 1583758670000;
|
||||
|
||||
// Note: this trace has a loop S1 <- S2 <- S3 <- S1, which is the only way
|
||||
// to make the algorithm return an empty string as trace name.
|
||||
const spansWithNoRoots = [
|
||||
{
|
||||
spanID: firstSpanId,
|
||||
traceID: currentTraceId,
|
||||
startTime: t + 200,
|
||||
process: {},
|
||||
references: [
|
||||
{
|
||||
spanID: secondSpanId,
|
||||
traceID: currentTraceId,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
spanID: secondSpanId,
|
||||
traceID: currentTraceId,
|
||||
startTime: t + 100,
|
||||
process: {},
|
||||
references: [
|
||||
{
|
||||
spanID: thirdSpanId,
|
||||
traceID: currentTraceId,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
spanID: thirdSpanId,
|
||||
traceID: currentTraceId,
|
||||
startTime: t,
|
||||
process: {},
|
||||
references: [
|
||||
{
|
||||
spanID: firstSpanId,
|
||||
traceID: currentTraceId,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
const spansWithMultipleRootsDifferentByStartTime = [
|
||||
{
|
||||
spanID: firstSpanId,
|
||||
traceID: currentTraceId,
|
||||
startTime: t + 200,
|
||||
process: {},
|
||||
references: [
|
||||
{
|
||||
spanID: thirdSpanId,
|
||||
traceID: currentTraceId,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
spanID: secondSpanId, // may be a root span
|
||||
traceID: currentTraceId,
|
||||
startTime: t + 100,
|
||||
process: {},
|
||||
references: [
|
||||
{
|
||||
spanID: missingSpanId,
|
||||
traceID: currentTraceId,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
spanID: thirdSpanId, // root span (as the earliest)
|
||||
traceID: currentTraceId,
|
||||
startTime: t,
|
||||
operationName,
|
||||
process: {
|
||||
serviceName,
|
||||
},
|
||||
references: [
|
||||
{
|
||||
spanID: missingSpanId,
|
||||
traceID: currentTraceId,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
const spansWithMultipleRootsWithOneWithoutRefs = [
|
||||
{
|
||||
spanID: firstSpanId,
|
||||
traceID: currentTraceId,
|
||||
startTime: t + 200,
|
||||
process: {},
|
||||
references: [
|
||||
{
|
||||
spanID: thirdSpanId,
|
||||
traceID: currentTraceId,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
spanID: secondSpanId, // root span (as a span without any refs)
|
||||
traceID: currentTraceId,
|
||||
startTime: t + 100,
|
||||
operationName,
|
||||
process: {
|
||||
serviceName,
|
||||
},
|
||||
},
|
||||
{
|
||||
spanID: thirdSpanId, // may be a root span
|
||||
traceID: currentTraceId,
|
||||
startTime: t,
|
||||
process: {},
|
||||
references: [
|
||||
{
|
||||
spanID: missingSpanId,
|
||||
traceID: currentTraceId,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
const spansWithOneRootWithRemoteRef = [
|
||||
{
|
||||
spanID: firstSpanId,
|
||||
traceID: currentTraceId,
|
||||
startTime: t + 200,
|
||||
process: {},
|
||||
references: [
|
||||
{
|
||||
spanID: secondSpanId,
|
||||
traceID: currentTraceId,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
spanID: secondSpanId,
|
||||
traceID: currentTraceId,
|
||||
startTime: t + 100,
|
||||
process: {},
|
||||
references: [
|
||||
{
|
||||
spanID: thirdSpanId,
|
||||
traceID: currentTraceId,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
spanID: thirdSpanId, // effective root span, since its parent is missing
|
||||
traceID: currentTraceId,
|
||||
startTime: t,
|
||||
operationName,
|
||||
process: {
|
||||
serviceName,
|
||||
},
|
||||
references: [
|
||||
{
|
||||
spanID: missingSpanId,
|
||||
traceID: currentTraceId,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
const spansWithOneRootWithNoRefs = [
|
||||
{
|
||||
spanID: firstSpanId,
|
||||
traceID: currentTraceId,
|
||||
startTime: t + 200,
|
||||
process: {},
|
||||
references: [
|
||||
{
|
||||
spanID: thirdSpanId,
|
||||
traceID: currentTraceId,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
spanID: secondSpanId, // root span
|
||||
traceID: currentTraceId,
|
||||
startTime: t + 100,
|
||||
operationName,
|
||||
process: {
|
||||
serviceName,
|
||||
},
|
||||
},
|
||||
{
|
||||
spanID: thirdSpanId,
|
||||
traceID: currentTraceId,
|
||||
startTime: t,
|
||||
process: {},
|
||||
references: [
|
||||
{
|
||||
spanID: secondSpanId,
|
||||
traceID: currentTraceId,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const fullTraceName = `${serviceName}: ${operationName}`;
|
||||
|
||||
it('returns an empty string if given spans with no root among them', () => {
|
||||
expect(getTraceName(spansWithNoRoots)).toEqual('');
|
||||
});
|
||||
|
||||
it('returns an id of root span with the earliest startTime', () => {
|
||||
expect(getTraceName(spansWithMultipleRootsDifferentByStartTime)).toEqual(fullTraceName);
|
||||
});
|
||||
|
||||
it('returns an id of root span without any refs', () => {
|
||||
expect(getTraceName(spansWithMultipleRootsWithOneWithoutRefs)).toEqual(fullTraceName);
|
||||
});
|
||||
|
||||
it('returns an id of root span with remote ref', () => {
|
||||
expect(getTraceName(spansWithOneRootWithRemoteRef)).toEqual(fullTraceName);
|
||||
});
|
||||
|
||||
it('returns an id of root span with no refs', () => {
|
||||
expect(getTraceName(spansWithOneRootWithNoRefs)).toEqual(fullTraceName);
|
||||
});
|
||||
});
|
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2017 Uber Technologies, Inc.
|
||||
// Copyright (c) 2020 The Jaeger Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -12,9 +12,46 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { memoize } from 'lodash';
|
||||
|
||||
import { TraceSpan } from '../types/trace';
|
||||
|
||||
export function getTraceName(spans: TraceSpan[]): string {
|
||||
const span = spans.filter((sp) => !sp.references || !sp.references.length)[0];
|
||||
return span ? `${span.process.serviceName}: ${span.operationName}` : '';
|
||||
export function _getTraceNameImpl(spans: TraceSpan[]) {
|
||||
// Use a span with no references to another span in given array
|
||||
// prefering the span with the fewest references
|
||||
// using start time as a tie breaker
|
||||
let candidateSpan: TraceSpan | undefined;
|
||||
const allIDs: Set<string> = new Set(spans.map(({ spanID }) => spanID));
|
||||
|
||||
for (let i = 0; i < spans.length; i++) {
|
||||
const hasInternalRef =
|
||||
spans[i].references &&
|
||||
spans[i].references.some(({ traceID, spanID }) => traceID === spans[i].traceID && allIDs.has(spanID));
|
||||
if (hasInternalRef) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!candidateSpan) {
|
||||
candidateSpan = spans[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
const thisRefLength = (spans[i].references && spans[i].references.length) || 0;
|
||||
const candidateRefLength = (candidateSpan.references && candidateSpan.references.length) || 0;
|
||||
|
||||
if (
|
||||
thisRefLength < candidateRefLength ||
|
||||
(thisRefLength === candidateRefLength && spans[i].startTime < candidateSpan.startTime)
|
||||
) {
|
||||
candidateSpan = spans[i];
|
||||
}
|
||||
}
|
||||
return candidateSpan ? `${candidateSpan.process.serviceName}: ${candidateSpan.operationName}` : '';
|
||||
}
|
||||
|
||||
export const getTraceName = memoize(_getTraceNameImpl, (spans: TraceSpan[]) => {
|
||||
if (!spans.length) {
|
||||
return 0;
|
||||
}
|
||||
return spans[0].traceID;
|
||||
});
|
||||
|
@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { orderTags, deduplicateTags } from './transform-trace-data';
|
||||
import transformTraceData, { orderTags, deduplicateTags } from './transform-trace-data';
|
||||
|
||||
describe('orderTags()', () => {
|
||||
it('correctly orders tags', () => {
|
||||
@ -53,3 +53,111 @@ describe('deduplicateTags()', () => {
|
||||
expect(tagsInfo.warnings).toEqual(['Duplicate tag "b.ip:8.8.4.4"']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('transformTraceData()', () => {
|
||||
const startTime = 1586160015434000;
|
||||
const duration = 34000;
|
||||
const traceID = 'f77950feed55c1ce91dd8e87896623a6';
|
||||
const rootSpanID = 'd4dcb46e95b781f5';
|
||||
const rootOperationName = 'rootOperation';
|
||||
const serviceName = 'serviceName';
|
||||
|
||||
const spans = [
|
||||
{
|
||||
traceID,
|
||||
spanID: '41f71485ed2593e4',
|
||||
operationName: 'someOperationName',
|
||||
references: [
|
||||
{
|
||||
refType: 'CHILD_OF',
|
||||
traceID,
|
||||
spanID: rootSpanID,
|
||||
},
|
||||
],
|
||||
startTime,
|
||||
duration,
|
||||
tags: [],
|
||||
processID: 'p1',
|
||||
},
|
||||
{
|
||||
traceID,
|
||||
spanID: '4f623fd33c213cba',
|
||||
operationName: 'anotherOperationName',
|
||||
references: [
|
||||
{
|
||||
refType: 'CHILD_OF',
|
||||
traceID,
|
||||
spanID: rootSpanID,
|
||||
},
|
||||
],
|
||||
startTime: startTime + 100,
|
||||
duration,
|
||||
tags: [],
|
||||
processID: 'p1',
|
||||
},
|
||||
];
|
||||
|
||||
const rootSpanWithMissingRef = {
|
||||
traceID,
|
||||
spanID: rootSpanID,
|
||||
operationName: rootOperationName,
|
||||
references: [
|
||||
{
|
||||
refType: 'CHILD_OF',
|
||||
traceID,
|
||||
spanID: 'missingSpanId',
|
||||
},
|
||||
],
|
||||
startTime: startTime + 50,
|
||||
duration,
|
||||
tags: [],
|
||||
processID: 'p1',
|
||||
};
|
||||
|
||||
const rootSpanWithoutRefs = {
|
||||
traceID,
|
||||
spanID: rootSpanID,
|
||||
operationName: rootOperationName,
|
||||
startTime: startTime + 50,
|
||||
duration,
|
||||
tags: [],
|
||||
processID: 'p1',
|
||||
};
|
||||
|
||||
const processes = {
|
||||
p1: {
|
||||
serviceName,
|
||||
tags: [],
|
||||
},
|
||||
};
|
||||
|
||||
it('should return null for trace without traceID', () => {
|
||||
const traceData = {
|
||||
traceID: undefined,
|
||||
processes,
|
||||
spans,
|
||||
};
|
||||
|
||||
expect(transformTraceData(traceData)).toEqual(null);
|
||||
});
|
||||
|
||||
it('should return trace data with correct traceName based on root span with missing ref', () => {
|
||||
const traceData = {
|
||||
traceID,
|
||||
processes,
|
||||
spans: [...spans, rootSpanWithMissingRef],
|
||||
};
|
||||
|
||||
expect(transformTraceData(traceData).traceName).toEqual(`${serviceName}: ${rootOperationName}`);
|
||||
});
|
||||
|
||||
it('should return trace data with correct traceName based on root span without any refs', () => {
|
||||
const traceData = {
|
||||
traceID,
|
||||
processes,
|
||||
spans: [...spans, rootSpanWithoutRefs],
|
||||
};
|
||||
|
||||
expect(transformTraceData(traceData).traceName).toEqual(`${serviceName}: ${rootOperationName}`);
|
||||
});
|
||||
});
|
||||
|
@ -20,6 +20,7 @@ import { getConfigValue } from '../utils/config/get-config';
|
||||
import { TraceKeyValuePair, TraceSpan, Trace, TraceResponse } from '../types/trace';
|
||||
// @ts-ignore
|
||||
import TreeNode from '../utils/TreeNode';
|
||||
import { getTraceName } from './trace-viewer';
|
||||
|
||||
// exported for tests
|
||||
export function deduplicateTags(spanTags: TraceKeyValuePair[]) {
|
||||
@ -121,7 +122,6 @@ export default function transformTraceData(data: TraceResponse | undefined): Tra
|
||||
const tree = getTraceSpanIdsAsTree(data);
|
||||
const spans: TraceSpan[] = [];
|
||||
const svcCounts: Record<string, number> = {};
|
||||
let traceName = '';
|
||||
|
||||
// Eslint complains about number type not needed but then TS complains it is implicitly any.
|
||||
// eslint-disable-next-line @typescript-eslint/no-inferrable-types
|
||||
@ -135,9 +135,6 @@ export default function transformTraceData(data: TraceResponse | undefined): Tra
|
||||
}
|
||||
const { serviceName } = span.process;
|
||||
svcCounts[serviceName] = (svcCounts[serviceName] || 0) + 1;
|
||||
if (!span.references || !span.references.length) {
|
||||
traceName = `${serviceName}: ${span.operationName}`;
|
||||
}
|
||||
span.relativeStartTime = span.startTime - traceStartTime;
|
||||
span.depth = depth - 1;
|
||||
span.hasChildren = node.children.length > 0;
|
||||
@ -166,6 +163,7 @@ export default function transformTraceData(data: TraceResponse | undefined): Tra
|
||||
});
|
||||
spans.push(span);
|
||||
});
|
||||
const traceName = getTraceName(spans);
|
||||
const services = Object.keys(svcCounts).map((name) => ({ name, numberOfSpans: svcCounts[name] }));
|
||||
return {
|
||||
services,
|
||||
|
Loading…
Reference in New Issue
Block a user