diff --git a/packages/jaeger-ui/src/model/find-trace-name.test.js b/packages/jaeger-ui/src/model/find-trace-name.test.js new file mode 100644 index 0000000000..b9963555dd --- /dev/null +++ b/packages/jaeger-ui/src/model/find-trace-name.test.js @@ -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 { 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); + }); +}); diff --git a/packages/jaeger-ui/src/model/trace-viewer.js b/packages/jaeger-ui/src/model/trace-viewer.js deleted file mode 100644 index cbc756014a..0000000000 --- a/packages/jaeger-ui/src/model/trace-viewer.js +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2017 Uber Technologies, Inc. -// -// 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. - -// eslint-disable-next-line import/prefer-default-export -export function getTraceName(spans) { - const span = spans.filter(sp => !sp.references || !sp.references.length)[0]; - return span ? `${span.process.serviceName}: ${span.operationName}` : ''; -} diff --git a/packages/jaeger-ui/src/model/trace-viewer.tsx b/packages/jaeger-ui/src/model/trace-viewer.tsx new file mode 100644 index 0000000000..049d6dcf6a --- /dev/null +++ b/packages/jaeger-ui/src/model/trace-viewer.tsx @@ -0,0 +1,40 @@ +// 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 { Span } from '../types/trace'; + +type spansDict = { [index: string]: Span }; + +// eslint-disable-next-line import/prefer-default-export +export function getTraceName(spans: Span[]) { + const allTraceSpans: spansDict = spans.reduce((dict, span) => ({ ...dict, [span.spanID]: span }), {}); + const rootSpan = spans + .filter(sp => { + if (!sp.references || !sp.references.length) { + return true; + } + const parentIDs = sp.references.filter(r => r.traceID === sp.traceID).map(r => r.spanID); + + // returns true if no parent from this trace found + return !parentIDs.some(pID => Boolean(allTraceSpans[pID])); + }) + .sort((sp1, sp2) => { + const sp1ParentsNum = sp1.references ? sp1.references.length : 0; + const sp2ParentsNum = sp2.references ? sp2.references.length : 0; + + return sp1ParentsNum - sp2ParentsNum || sp1.startTime - sp2.startTime; + })[0]; + + return rootSpan ? `${rootSpan.process.serviceName}: ${rootSpan.operationName}` : ''; +} diff --git a/packages/jaeger-ui/tsconfig.lint.json b/packages/jaeger-ui/tsconfig.lint.json index 738684b0ab..562e1172d2 100644 --- a/packages/jaeger-ui/tsconfig.lint.json +++ b/packages/jaeger-ui/tsconfig.lint.json @@ -30,7 +30,6 @@ "src/api/jaeger.js", "src/components/SearchTracePage/SearchResults/ResultItem.markers.js", "src/model/order-by.js", - "src/model/trace-viewer.js", "src/selectors/process.js", "src/selectors/span.js", "src/selectors/trace.js",