Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(new-trace): Added logic for red errored rows. #66194

Merged
merged 6 commits into from Mar 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 21 additions & 5 deletions static/app/views/performance/newTraceDetails/trace.tsx
Expand Up @@ -516,7 +516,7 @@ function RenderRow(props: {
}}
>
<div
className="TraceLeftColumnInner"
className={`TraceLeftColumnInner ${props.node.has_error ? 'Errored' : ''}`}
style={{
paddingLeft: props.node.depth * props.viewManager.row_depth_padding,
}}
Expand All @@ -528,6 +528,7 @@ function RenderRow(props: {
status={props.node.fetchStatus}
expanded={!props.node.expanded}
onClick={e => props.onExpand(e, props.node, !props.node.expanded)}
errored={props.node.has_error}
>
{COUNT_FORMATTER.format(props.node.groupCount)}
</ChildrenButton>
Expand Down Expand Up @@ -566,6 +567,9 @@ function RenderRow(props: {
}

if (isTransactionNode(props.node)) {
const errored =
props.node.value.errors.length > 0 ||
props.node.value.performance_issues.length > 0;
return (
<div
key={props.index}
Expand Down Expand Up @@ -593,15 +597,16 @@ function RenderRow(props: {
}}
>
<div
className="TraceLeftColumnInner"
className={`TraceLeftColumnInner ${errored ? 'Errored' : ''}`}
style={{
paddingLeft: props.node.depth * props.viewManager.row_depth_padding,
}}
>
<div
className={`TraceChildrenCountWrapper ${
props.node.isOrphaned ? 'Orphaned' : ''
}`}
}
`}
>
<Connectors node={props.node} viewManager={props.viewManager} />
{props.node.children.length > 0 || props.node.canFetch ? (
Expand All @@ -622,6 +627,7 @@ function RenderRow(props: {
? props.onZoomIn(e, props.node, !props.node.zoomedIn)
: props.onExpand(e, props.node, !props.node.expanded)
}
errored={errored}
>
{props.node.children.length > 0
? COUNT_FORMATTER.format(props.node.children.length)
Expand Down Expand Up @@ -663,6 +669,7 @@ function RenderRow(props: {
}

if (isSpanNode(props.node)) {
const errored = props.node.value.relatedErrors.length > 0;
return (
<div
key={props.index}
Expand Down Expand Up @@ -690,7 +697,7 @@ function RenderRow(props: {
}}
>
<div
className="TraceLeftColumnInner"
className={`TraceLeftColumnInner ${errored ? 'Errored' : ''}`}
style={{
paddingLeft: props.node.depth * props.viewManager.row_depth_padding,
}}
Expand Down Expand Up @@ -719,6 +726,7 @@ function RenderRow(props: {
? props.onZoomIn(e, props.node, !props.node.zoomedIn)
: props.onExpand(e, props.node, !props.node.expanded)
}
errored={errored}
>
{props.node.children.length > 0
? COUNT_FORMATTER.format(props.node.children.length)
Expand Down Expand Up @@ -1142,9 +1150,13 @@ function ChildrenButton(props: {
icon: React.ReactNode;
onClick: (e: React.MouseEvent) => void;
status: TraceTreeNode<any>['fetchStatus'] | undefined;
errored?: boolean;
}) {
return (
<button className="TraceChildrenCount" onClick={props.onClick}>
<button
className={`TraceChildrenCount ${props.errored ? 'Errored' : ''}`}
onClick={props.onClick}
>
<div className="TraceChildrenCountContent">{props.children}</div>
<div className="TraceChildrenCountAction">
{props.icon}
Expand Down Expand Up @@ -1525,6 +1537,10 @@ const TraceStylingWrapper = styled('div')`
box-shadow: ${p => p.theme.dropShadowLight};
margin-right: ${space(1)};

&.Errored {
border: 2px solid ${p => p.theme.error};
}

.TraceChildrenCountContent {
+ .TraceChildrenCountAction {
margin-left: 2px;
Expand Down
78 changes: 78 additions & 0 deletions static/app/views/performance/newTraceDetails/traceTree.spec.tsx
Expand Up @@ -1718,6 +1718,31 @@ describe('TraceTree', () => {
expect(root.children.length).toBe(1);
});

it('collects errored children for sibling autogrouped node', () => {
const root = new TraceTreeNode(null, makeSpan({description: 'span1'}), {
project_slug: '',
event_id: '',
});

for (let i = 0; i < 5; i++) {
const node = new TraceTreeNode(root, makeSpan({description: 'span', op: 'db'}), {
project_slug: '',
event_id: '',
});
node.value.relatedErrors = [makeTraceError()];
root.children.push(node);
}

expect(root.children.length).toBe(5);

TraceTree.AutogroupSiblingSpanNodes(root);

expect(root.children.length).toBe(1);
assertAutogroupedNode(root.children[0]);
expect(root.children[0].has_error).toBe(true);
expect(root.children[0].errored_children).toHaveLength(5);
});

it('adds autogrouped siblings as children under autogrouped node', () => {
const root = new TraceTreeNode(null, makeSpan({description: 'span1'}), {
project_slug: '',
Expand Down Expand Up @@ -1825,6 +1850,59 @@ describe('TraceTree', () => {
);
});

it('collects errored children for parent autogrouped node', () => {
// db db db
// http -> parent autogroup (3) -> parent autogroup (3)
// http http
// http http
// http

const root: TraceTreeNode<TraceTree.Span> = new TraceTreeNode(
null,
makeSpan({
description: `span1`,
span_id: `1`,
op: 'db',
}),
{project_slug: '', event_id: ''}
);

let last: TraceTreeNode<any> = root;

for (let i = 0; i < 3; i++) {
const node = new TraceTreeNode(
last,
makeSpan({
description: `span${i}`,
span_id: `${i}`,
op: 'http',
}),
{
project_slug: '',
event_id: '',
}
);
node.value.relatedErrors = [makeTraceError()];
last.children.push(node);
last = node;
}

if (!root) {
throw new Error('root is null');
}

expect(root.children.length).toBe(1);
expect(root.children[0].children.length).toBe(1);

TraceTree.AutogroupDirectChildrenSpanNodes(root);

expect(root.children.length).toBe(1);

assertAutogroupedNode(root.children[0]);
expect(root.children[0].has_error).toBe(true);
expect(root.children[0].errored_children).toHaveLength(3);
});

it('autogrouping direct children skips rendering intermediary nodes', () => {
// db db db
// http autogrouped (3) autogrouped (3)
Expand Down
77 changes: 65 additions & 12 deletions static/app/views/performance/newTraceDetails/traceTree.tsx
Expand Up @@ -413,7 +413,9 @@ export class TraceTree {
const spanNodeValue: TraceTree.Span = {
...span,
event: data as EventTransaction,
relatedErrors: childTxn ? getRelatedErrorsOrIssues(span, childTxn.value) : [],
relatedErrors: childTxn
? getSpanErrorsOrIssuesFromTransaction(span, childTxn.value)
: [],
childTxn: childTxn?.value,
};
const node: TraceTreeNode<TraceTree.Span> = new TraceTreeNode(null, spanNodeValue, {
Expand Down Expand Up @@ -478,17 +480,28 @@ export class TraceTree {
const head = node;
let tail = node;
let groupMatchCount = 0;
const erroredChildren: TraceTreeNode<TraceTree.NodeValue>[] = [];

while (
tail &&
tail.children.length === 1 &&
isSpanNode(tail.children[0]) &&
tail.children[0].value.op === head.value.op
) {
if (tail.value?.relatedErrors.length > 0) {
erroredChildren.push(tail);
}

groupMatchCount++;
tail = tail.children[0];
}

// Checking the tail node for errors as it is not included in the grouping
// while loop, but is hidden when the autogrouped node is collapsed
if (tail.value?.relatedErrors.length > 0) {
erroredChildren.push(tail);
}

if (groupMatchCount < 1) {
for (const child of head.children) {
queue.push(child);
Expand Down Expand Up @@ -517,6 +530,7 @@ export class TraceTree {
}

autoGroupedNode.groupCount = groupMatchCount + 1;
autoGroupedNode.errored_children = erroredChildren;
autoGroupedNode.space = [
Math.min(head.value.start_timestamp, tail.value.timestamp) *
autoGroupedNode.multiplier,
Expand Down Expand Up @@ -611,6 +625,16 @@ export class TraceTree {
start_timestamp = child.value.start_timestamp;
}

if (!isSpanNode(child)) {
throw new TypeError(
'Expected child of autogrouped node to be a span node.'
);
}

if (child.value?.relatedErrors.length > 0) {
autoGroupedNode.errored_children.push(child);
}

autoGroupedNode.children.push(node.children[j]);
autoGroupedNode.children[autoGroupedNode.children.length - 1].parent =
autoGroupedNode;
Expand Down Expand Up @@ -1257,6 +1281,7 @@ export class MissingInstrumentationNode extends TraceTreeNode<TraceTree.MissingI
export class ParentAutogroupNode extends TraceTreeNode<TraceTree.ChildrenAutogroup> {
head: TraceTreeNode<TraceTree.Span>;
tail: TraceTreeNode<TraceTree.Span>;
errored_children: TraceTreeNode<TraceTree.NodeValue>[] = [];
groupCount: number = 0;

private _autogroupedSegments: [number, number][] | undefined;
Expand All @@ -1282,6 +1307,10 @@ export class ParentAutogroupNode extends TraceTreeNode<TraceTree.ChildrenAutogro
return this.tail.children;
}

get has_error(): boolean {
return this.errored_children.length > 0;
}

get autogroupedSegments(): [number, number][] {
if (this._autogroupedSegments) {
return this._autogroupedSegments;
Expand All @@ -1304,6 +1333,7 @@ export class ParentAutogroupNode extends TraceTreeNode<TraceTree.ChildrenAutogro

export class SiblingAutogroupNode extends TraceTreeNode<TraceTree.SiblingAutogroup> {
groupCount: number = 0;
errored_children: TraceTreeNode<TraceTree.NodeValue>[] = [];
private _autogroupedSegments: [number, number][] | undefined;

constructor(
Expand All @@ -1315,6 +1345,10 @@ export class SiblingAutogroupNode extends TraceTreeNode<TraceTree.SiblingAutogro
this.expanded = false;
}

get has_error(): boolean {
return this.errored_children.length > 0;
}

get autogroupedSegments(): [number, number][] {
if (this._autogroupedSegments) {
return this._autogroupedSegments;
Expand Down Expand Up @@ -1505,20 +1539,39 @@ export function computeAutogroupedBarSegments(
return segments;
}

function getRelatedErrorsOrIssues(
// Returns a list of errors or performance issues related to the txn
// with ids matching the span id
function getSpanErrorsOrIssuesFromTransaction(
span: RawSpanType,
currentEvent: TraceTree.Transaction
txn: TraceTree.Transaction
): TraceErrorOrIssue[] {
const performanceIssues = currentEvent.performance_issues.filter(
issue =>
issue.span.some(id => id === span.span_id) ||
issue.suspect_spans.some(suspectSpanId => suspectSpanId === span.span_id)
);
if (!txn.performance_issues.length && !txn.errors.length) {
return [];
}

const errorsOrIssues: TraceErrorOrIssue[] = [];

for (const perfIssue of txn.performance_issues) {
for (const s of perfIssue.span) {
if (s === span.span_id) {
errorsOrIssues.push(perfIssue);
}
}

for (const suspect of perfIssue.suspect_spans) {
if (suspect === span.span_id) {
errorsOrIssues.push(perfIssue);
}
}
}

for (const error of txn.errors) {
if (error.span === span.span_id) {
errorsOrIssues.push(error);
}
}

return [
...currentEvent.errors.filter(error => error.span === span.span_id),
...performanceIssues, // Spans can be shown when embedded in performance issues
];
return errorsOrIssues;
}

function printNode(t: TraceTreeNode<TraceTree.NodeValue>, offset: number): string {
Expand Down