diff --git a/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanBar.css b/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanBar.css index 3a0150dd30..adc08aa338 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanBar.css +++ b/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanBar.css @@ -49,6 +49,32 @@ limitations under the License. top: 45%; height: 11%; z-index: 2; + overflow: hidden; +} + +.SpanBar--criticalPath::before { + content: ''; + position: absolute; + top: 0; + width: 10%; + height: 100%; + visibility: hidden; + background-color: white; + border: 1px solid white; +} + +.SpanBar--criticalPath:hover::before { + visibility: visible; + animation: runOnce 2s forwards; +} + +@keyframes runOnce { + 0% { + left: 0; + } + 100% { + left: 100%; + } } .SpanBar--label { @@ -85,6 +111,8 @@ limitations under the License. .SpanBar--logMarker:hover { background-color: #000; + transform: scale(1.2); + transition: transform 0.3s ease; } .SpanBar--logMarker::before, @@ -94,7 +122,7 @@ limitations under the License. top: 0; bottom: 0; right: 0; - border: 1px solid transparent; + border: 3px solid transparent; } .SpanBar--logMarker::after { diff --git a/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanBar.tsx b/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanBar.tsx index 5d08301040..7f9447ef8a 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanBar.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/SpanBar.tsx @@ -13,7 +13,7 @@ // limitations under the License. import React, { useState } from 'react'; -import { Popover } from 'antd'; +import { Popover, Tooltip } from 'antd'; import _groupBy from 'lodash/groupBy'; import AccordianLogs from './SpanDetail/AccordianLogs'; @@ -124,7 +124,7 @@ function SpanBar(props: TCommonProps) {
))} @@ -146,16 +146,25 @@ function SpanBar(props: TCommonProps) { const criticalPathViewEnd = critcalPathViewBounds.end; const key = `${each.spanId}-${index}`; return ( -
+ + A segment on the critical path of the overall trace/request/workflow. +
+ } + > +
+ ); })}
diff --git a/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/VirtualizedTraceView.test.js b/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/VirtualizedTraceView.test.js index 34038f55f3..16c767ca35 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/VirtualizedTraceView.test.js +++ b/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/VirtualizedTraceView.test.js @@ -13,6 +13,7 @@ // limitations under the License. import React from 'react'; import { shallow, mount } from 'enzyme'; +import { render, screen } from '@testing-library/react'; import ListView from './ListView'; import SpanBarRow from './SpanBarRow'; @@ -25,6 +26,8 @@ import updateUiFindSpy from '../../../utils/update-ui-find'; import * as linkPatterns from '../../../model/link-patterns'; import memoizedTraceCriticalPath from '../CriticalPath/index'; +import criticalPathTest from '../CriticalPath/testCases/test2'; + jest.mock('./SpanTreeOffset'); jest.mock('../../../utils/update-ui-find'); @@ -326,6 +329,26 @@ describe('', () => { ).toBe(true); }); + it('renders Critical Path segments when row is not collapsed', () => { + wrapper.setProps({ + trace: criticalPathTest.trace, + criticalPath: criticalPathTest.criticalPathSections, + }); + render(instance.renderRow('some-key', {}, 0, {})); + expect(screen.getAllByTestId('SpanBar--criticalPath').length).toBe(2); + }); + + it('renders Critical Path segments are merged if consecutive when row is collapased', () => { + const childrenHiddenIDs = new Set([criticalPathTest.trace.spans[0].spanID]); + wrapper.setProps({ + childrenHiddenIDs, + trace: criticalPathTest.trace, + criticalPath: criticalPathTest.criticalPathSections, + }); + render(instance.renderRow('some-key', {}, 0, {})); + expect(screen.getAllByTestId('SpanBar--criticalPath').length).toBe(1); + }); + it('renders a SpanBarRow with a RPC span if the row is collapsed and a client span', () => { const clientTags = [{ key: 'span.kind', value: 'client' }, ...trace.spans[0].tags]; const serverTags = [{ key: 'span.kind', value: 'server' }, ...trace.spans[1].tags]; diff --git a/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/VirtualizedTraceView.tsx b/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/VirtualizedTraceView.tsx index 0b1e5c21ea..e0cfbeecba 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/VirtualizedTraceView.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TraceTimelineViewer/VirtualizedTraceView.tsx @@ -158,6 +158,41 @@ function getCssClasses(currentViewRange: [number, number]) { }); } +function mergeChildrenCriticalPath( + trace: Trace, + spanID: string, + criticalPath: criticalPathSection[] +): criticalPathSection[] { + // Define an array to store the IDs of the span and its descendants (if the span is collapsed) + const allRequiredSpanIds = [spanID]; + + // If the span is collapsed, recursively find all of its descendants. + const findAllDescendants = (currentChildSpanIds: string[]) => { + currentChildSpanIds.forEach(eachId => { + const currentChildSpan = trace.spans.find(a => a.spanID === eachId)!; + if (currentChildSpan.hasChildren) { + allRequiredSpanIds.push(...currentChildSpan.childSpanIds); + findAllDescendants(currentChildSpan.childSpanIds); + } + }); + }; + findAllDescendants(allRequiredSpanIds); + + const criticalPathSections: criticalPathSection[] = []; + criticalPath.forEach(each => { + if (allRequiredSpanIds.includes(each.spanId)) { + if (criticalPathSections.length !== 0 && each.section_end === criticalPathSections[0].section_start) { + // Merge Critical Paths if they are consecutive + criticalPathSections[0].section_start = each.section_start; + } else { + criticalPathSections.unshift({ ...each }); + } + } + }); + + return criticalPathSections; +} + const memoizedGenerateRowStates = memoizeOne(generateRowStatesFromTrace); const memoizedViewBoundsFunc = memoizeOne(createViewedBoundsFunc, _isEqual); const memoizedGetCssClasses = memoizeOne(getCssClasses, _isEqual); @@ -358,24 +393,9 @@ export class VirtualizedTraceViewImpl extends React.Component { - if (isCollapsed) { - const allChildSpanIds = [spanID, ...childSpanIds]; - // This function called recursively to find all descendants of a span - const findAllDescendants = (currentChildSpanIds: string[]) => { - currentChildSpanIds.forEach(eachId => { - const currentChildSpan = trace.spans.find(a => a.spanID === eachId)!; - if (currentChildSpan.hasChildren) { - allChildSpanIds.push(...currentChildSpan.childSpanIds); - findAllDescendants(currentChildSpan.childSpanIds); - } - }); - }; - findAllDescendants(childSpanIds); - return allChildSpanIds.includes(each.spanId); - } - return each.spanId === spanID; - }); + const criticalPathSections = isCollapsed + ? mergeChildrenCriticalPath(trace, spanID, criticalPath) + : criticalPath.filter(each => each.spanId === spanID); // Check for direct child "server" span if the span is a "client" span. let rpc = null; if (isCollapsed) {