Skip to content

Commit

Permalink
Visualize multiple parent span
Browse files Browse the repository at this point in the history
Signed-off-by: Ruben Vargas <ruben.vp8510@gmail.com>
  • Loading branch information
rubenvp8510 committed Nov 5, 2019
1 parent 7580270 commit 5bddd3d
Show file tree
Hide file tree
Showing 13 changed files with 311 additions and 116 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Copyright (c) 2019 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.

import * as React from 'react';
import { Button, Dropdown, Menu, Tooltip } from 'antd';
import { bindActionCreators } from 'redux';
import { connect, Dispatch } from 'react-redux';
import { History as RouterHistory, Location } from 'history';
import { withRouter } from 'react-router-dom';

import { FetchedTrace, ReduxState, TNil } from '../../../types';
import { actions as timelineActions } from './duck';
import { extractUiFindFromState } from '../../common/UiFindInput';
import { SpanReference, Trace } from '../../../types/trace';
import updateUiFind from '../../../utils/update-ui-find';

type TDispatchProps = {
focusUiFindMatches: (trace: Trace, uiFind: string | TNil) => void;
};

type TReduxProps = {
uiFind: string | TNil;
trace: FetchedTrace | TNil;
};

type TOwnProps = {
references: SpanReference[];
traceID: string;
history: RouterHistory;
location: Location;
match: any;
};

type MultiParentIconProps = TDispatchProps & TReduxProps & TOwnProps;

const linkValueList = (links: SpanReference[], focusUiFindMatches: (spanID: string) => void) => (
<Menu>
{links.map(({ spanID, span }, index) => {
let text = spanID;
if (span) {
text = `${span.operationName} (${spanID})`;
}
return (
// `index` is necessary in the key because url can repeat
// eslint-disable-next-line react/no-array-index-key
<Menu.Item key={`${spanID}-${index}`}>
<a role="button" onClick={() => focusUiFindMatches(spanID)}>
{' '}
{text}{' '}
</a>
</Menu.Item>
);
})}
</Menu>
);

class MultiParentIconImpl extends React.PureComponent<MultiParentIconProps> {
focusUiFindMatches = (uiFind: string) => {
const { trace, focusUiFindMatches, location, history } = this.props;
if (trace && trace.data) {
updateUiFind({
location,
history,
uiFind,
});
focusUiFindMatches(trace.data, uiFind);
}
};

render() {
const { references } = this.props;
return (
<Tooltip arrowPointAtCenter mouseLeaveDelay={0.5} placement="left" title="Contains multiple references">
<Dropdown
overlay={linkValueList(references, this.focusUiFindMatches)}
placement="bottomRight"
trigger={['click']}
>
<Button className="MultiParentIcon" htmlType="button" icon="fork" />
</Dropdown>
</Tooltip>
);
}
}

export function mapDispatchToProps(dispatch: Dispatch<ReduxState>): TDispatchProps {
const { focusUiFindMatches } = bindActionCreators(timelineActions, dispatch);
return { focusUiFindMatches };
}

// export for tests
export function mapStateToProps(state: ReduxState, ownProps: TOwnProps): TReduxProps {
const { traces } = state.trace;
const trace = ownProps.traceID ? traces[ownProps.traceID] : null;
return {
trace,
...extractUiFindFromState(state),
};
}

export default withRouter(
connect(
mapStateToProps,
mapDispatchToProps
)(MultiParentIconImpl)
);
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ type SpanBarRowProps = {
isChildrenExpanded: boolean;
isDetailExpanded: boolean;
isMatchingFilter: boolean;
onDetailToggled: (spanID: string) => void;
onChildrenToggled: (spanID: string) => void;
onDetailToggled: (UiID: string) => void;
onChildrenToggled: (UiID: string) => void;
numTicks: number;
rpc?:
| {
Expand Down Expand Up @@ -67,11 +67,11 @@ export default class SpanBarRow extends React.PureComponent<SpanBarRowProps> {
};

_detailToggle = () => {
this.props.onDetailToggled(this.props.span.spanID);
this.props.onDetailToggled(this.props.span.uiID);
};

_childrenToggle = () => {
this.props.onChildrenToggled(this.props.span.spanID);
this.props.onChildrenToggled(this.props.span.uiID);
};

render() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ import CopyIcon from '../../../common/CopyIcon';
import LabeledList from '../../../common/LabeledList';

import { TNil } from '../../../../types';
import { KeyValuePair, Link, Log, Span } from '../../../../types/trace';
import { KeyValuePair, Link, Log, Span, SpanReference } from '../../../../types/trace';

import './index.css';
import MultiParentIcon from '../MultiParentIcon';

type SpanDetailProps = {
detailState: DetailState;
Expand All @@ -38,6 +39,7 @@ type SpanDetailProps = {
tagsToggle: (spanID: string) => void;
traceStartTime: number;
warningsToggle: (spanID: string) => void;
references: SpanReference[];
};

export default function SpanDetail(props: SpanDetailProps) {
Expand All @@ -51,9 +53,10 @@ export default function SpanDetail(props: SpanDetailProps) {
tagsToggle,
traceStartTime,
warningsToggle,
references,
} = props;
const { isTagsOpen, isProcessOpen, logs: logsState, isWarningsOpen } = detailState;
const { operationName, process, duration, relativeStartTime, spanID, logs, tags, warnings } = span;
const { operationName, process, duration, relativeStartTime, spanID, logs, tags, warnings, uiID } = span;
const overviewItems = [
{
key: 'svc',
Expand All @@ -72,11 +75,13 @@ export default function SpanDetail(props: SpanDetailProps) {
},
];
const deepLinkCopyText = `${window.location.origin}${window.location.pathname}?uiFind=${spanID}`;

return (
<div>
<div className="ub-flex ub-items-center">
<h2 className="ub-flex-auto ub-m0">{operationName}</h2>
<h2 className="ub-flex-auto ub-m0">
{operationName}
{references.length > 1 && <MultiParentIcon references={references} traceID={span.traceID} />}
</h2>
<LabeledList
className="ub-tx-right-align"
dividerClassName="SpanDetail--divider"
Expand All @@ -91,7 +96,7 @@ export default function SpanDetail(props: SpanDetailProps) {
label="Tags"
linksGetter={linksGetter}
isOpen={isTagsOpen}
onToggle={() => tagsToggle(spanID)}
onToggle={() => tagsToggle(uiID)}
/>
{process.tags && (
<AccordianKeyValues
Expand All @@ -100,7 +105,7 @@ export default function SpanDetail(props: SpanDetailProps) {
label="Process"
linksGetter={linksGetter}
isOpen={isProcessOpen}
onToggle={() => processToggle(spanID)}
onToggle={() => processToggle(uiID)}
/>
)}
</div>
Expand All @@ -111,7 +116,7 @@ export default function SpanDetail(props: SpanDetailProps) {
isOpen={logsState.isOpen}
openedItems={logsState.openedItems}
onToggle={() => logsToggle(spanID)}
onItemToggle={logItem => logItemToggle(spanID, logItem)}
onItemToggle={logItem => logItemToggle(uiID, logItem)}
timestamp={traceStartTime}
/>
)}
Expand All @@ -122,7 +127,7 @@ export default function SpanDetail(props: SpanDetailProps) {
label={<span className="AccordianWarnings--label">Warnings</span>}
data={warnings}
isOpen={isWarningsOpen}
onToggle={() => warningsToggle(spanID)}
onToggle={() => warningsToggle(uiID)}
/>
)}
<small className="SpanDetail--debugInfo">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,29 @@ import DetailState from './SpanDetail/DetailState';
import SpanTreeOffset from './SpanTreeOffset';
import TimelineRow from './TimelineRow';

import { Log, Span, KeyValuePair, Link } from '../../../types/trace';
import { Log, Span, KeyValuePair, Link, SpanReference } from '../../../types/trace';

import './SpanDetailRow.css';

type SpanDetailRowProps = {
color: string;
columnDivision: number;
detailState: DetailState;
onDetailToggled: (spanID: string) => void;
onDetailToggled: (UiID: string) => void;
linksGetter: (span: Span, links: KeyValuePair[], index: number) => Link[];
logItemToggle: (spanID: string, log: Log) => void;
logsToggle: (spanID: string) => void;
processToggle: (spanID: string) => void;
warningsToggle: (spanID: string) => void;
logItemToggle: (UiID: string, log: Log) => void;
logsToggle: (UiID: string) => void;
processToggle: (UiID: string) => void;
warningsToggle: (UiID: string) => void;
span: Span;
tagsToggle: (spanID: string) => void;
tagsToggle: (UiID: string) => void;
traceStartTime: number;
references: SpanReference[];
};

export default class SpanDetailRow extends React.PureComponent<SpanDetailRowProps> {
_detailToggle = () => {
this.props.onDetailToggled(this.props.span.spanID);
this.props.onDetailToggled(this.props.span.uiID);
};

_linksGetter = (items: KeyValuePair[], itemIndex: number) => {
Expand All @@ -60,6 +61,7 @@ export default class SpanDetailRow extends React.PureComponent<SpanDetailRowProp
span,
tagsToggle,
traceStartTime,
references,
} = this.props;
return (
<TimelineRow className="detail-row">
Expand Down Expand Up @@ -87,6 +89,7 @@ export default class SpanDetailRow extends React.PureComponent<SpanDetailRowProp
span={span}
tagsToggle={tagsToggle}
traceStartTime={traceStartTime}
references={references}
/>
</div>
</TimelineRow.Cell>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ type TDispatchProps = {
detailLogItemToggle: (spanID: string, log: Log) => void;
detailLogsToggle: (spanID: string) => void;
detailWarningsToggle: (spanID: string) => void;
detailReferencesToggle: (UiID: string) => void;
detailProcessToggle: (spanID: string) => void;
detailTagsToggle: (spanID: string) => void;
detailToggle: (spanID: string) => void;
Expand Down Expand Up @@ -96,7 +97,7 @@ function generateRowStates(
const rowStates = [];
for (let i = 0; i < spans.length; i++) {
const span = spans[i];
const { spanID, depth } = span;
const { uiID, depth } = span;
let hidden = false;
if (collapseDepth != null) {
if (depth >= collapseDepth) {
Expand All @@ -108,15 +109,15 @@ function generateRowStates(
if (hidden) {
continue;
}
if (childrenHiddenIDs.has(spanID)) {
if (childrenHiddenIDs.has(uiID)) {
collapseDepth = depth + 1;
}
rowStates.push({
span,
isDetail: false,
spanIndex: i,
});
if (detailStates.has(spanID)) {
if (detailStates.has(uiID)) {
rowStates.push({
span,
isDetail: true,
Expand Down Expand Up @@ -156,7 +157,6 @@ export class VirtualizedTraceViewImpl extends React.Component<VirtualizedTraceVi
viewEnd: zoomEnd,
});
this.rowStates = generateRowStates(trace.spans, childrenHiddenIDs, detailStates);

setTrace(trace, uiFind);
}

Expand Down Expand Up @@ -269,17 +269,17 @@ export class VirtualizedTraceViewImpl extends React.Component<VirtualizedTraceVi
// https://github.com/facebook/flow/issues/3076#issuecomment-290944051
getKeyFromIndex = (index: number) => {
const { isDetail, span } = this.rowStates[index];
return `${span.spanID}--${isDetail ? 'detail' : 'bar'}`;
return `${span.uiID}--${isDetail ? 'detail' : 'bar'}`;
};

getIndexFromKey = (key: string) => {
const parts = key.split('--');
const _spanID = parts[0];
const _UiID = parts[0];
const _isDetail = parts[1] === 'detail';
const max = this.rowStates.length;
for (let i = 0; i < max; i++) {
const { span, isDetail } = this.rowStates[i];
if (span.spanID === _spanID && isDetail === _isDetail) {
if (span.uiID === _UiID && isDetail === _isDetail) {
return i;
}
}
Expand Down Expand Up @@ -307,7 +307,7 @@ export class VirtualizedTraceViewImpl extends React.Component<VirtualizedTraceVi
};

renderSpanBarRow(span: Span, spanIndex: number, key: string, style: React.CSSProperties, attrs: {}) {
const { spanID } = span;
const { uiID, spanID } = span;
const { serviceName } = span.process;
const {
childrenHiddenIDs,
Expand All @@ -323,8 +323,8 @@ export class VirtualizedTraceViewImpl extends React.Component<VirtualizedTraceVi
return null;
}
const color = colorGenerator.getColorByKey(serviceName);
const isCollapsed = childrenHiddenIDs.has(spanID);
const isDetailExpanded = detailStates.has(spanID);
const isCollapsed = childrenHiddenIDs.has(uiID);
const isDetailExpanded = detailStates.has(uiID);
const isMatchingFilter = findMatchesIDs ? findMatchesIDs.has(spanID) : false;
const showErrorIcon = isErrorSpan(span) || (isCollapsed && spanContainsErredSpan(trace.spans, spanIndex));

Expand Down Expand Up @@ -366,7 +366,7 @@ export class VirtualizedTraceViewImpl extends React.Component<VirtualizedTraceVi
}

renderSpanDetailRow(span: Span, key: string, style: React.CSSProperties, attrs: {}) {
const { spanID } = span;
const { uiID } = span;
const { serviceName } = span.process;
const {
detailLogItemToggle,
Expand All @@ -379,7 +379,7 @@ export class VirtualizedTraceViewImpl extends React.Component<VirtualizedTraceVi
spanNameColumnWidth,
trace,
} = this.props;
const detailState = detailStates.get(spanID);
const detailState = detailStates.get(uiID);
if (!trace || !detailState) {
return null;
}
Expand All @@ -399,6 +399,7 @@ export class VirtualizedTraceViewImpl extends React.Component<VirtualizedTraceVi
span={span}
tagsToggle={detailTagsToggle}
traceStartTime={trace.startTime}
references={span.references}
/>
</div>
);
Expand Down
Loading

0 comments on commit 5bddd3d

Please sign in to comment.