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 3 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
34 changes: 24 additions & 10 deletions static/app/views/performance/newTraceDetails/trace.tsx
Expand Up @@ -487,14 +487,15 @@ function RenderRow(props: {
}}
>
<div
className="TraceLeftColumnInner"
className={`TraceLeftColumnInner ${props.node.errored ? 'Errored' : ''}`}
style={{
paddingLeft: props.node.depth * props.viewManager.row_depth_padding,
}}
>
<div className="TraceChildrenCountWrapper">
<Connectors node={props.node} viewManager={props.viewManager} />
<ChildrenCountButton
errored={props.node.errored}
expanded={!props.node.expanded}
onClick={e => props.onExpand(e, props.node, !props.node.expanded)}
>
Expand Down Expand Up @@ -544,6 +545,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 @@ -571,19 +575,21 @@ 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 ? (
<ChildrenCountButton
errored={errored}
expanded={props.node.expanded || props.node.zoomedIn}
onClick={e => props.onExpand(e, props.node, !props.node.expanded)}
>
Expand All @@ -595,12 +601,12 @@ function RenderRow(props: {
<span className="TraceOperation">{props.node.value['transaction.op']}</span>
<strong className="TraceEmDash"> — </strong>
<span>{props.node.value.transaction}</span>
{props.node.canFetchData ? (
<button onClick={e => props.onZoomIn(e, props.node, !props.node.zoomedIn)}>
{props.node.zoomedIn ? 'Zoom Out' : 'Zoom In'}
</button>
) : null}
</div>
{props.node.canFetchData ? (
<button onClick={e => props.onZoomIn(e, props.node, !props.node.zoomedIn)}>
{props.node.zoomedIn ? 'Zoom Out' : 'Zoom In'}
</button>
) : null}
</div>
<div
ref={r =>
Expand Down Expand Up @@ -630,6 +636,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 @@ -657,7 +664,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 @@ -1094,9 +1101,13 @@ function ChildrenCountButton(props: {
children: React.ReactNode;
expanded: boolean;
onClick: (e: React.MouseEvent) => void;
errored?: boolean;
}) {
return (
<button className="TraceChildrenCount" onClick={props.onClick}>
<button
className={`TraceChildrenCount ${props.errored ? 'Errored' : ''}`}
onClick={props.onClick}
>
{props.children}
<IconChevron
size="xs"
Expand Down Expand Up @@ -1448,6 +1459,9 @@ const TraceStylingWrapper = styled('div')`
box-shadow: ${p => p.theme.dropShadowLight};
margin-right: ${space(1)};

&.Errored {
border: 2px solid ${p => p.theme.error};
}
svg {
width: 7px;
transition: none;
Expand Down
77 changes: 65 additions & 12 deletions static/app/views/performance/newTraceDetails/traceTree.tsx
Expand Up @@ -411,7 +411,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 @@ -1219,6 +1221,24 @@ export class ParentAutogroupNode extends TraceTreeNode<TraceTree.ChildrenAutogro
}
return this.tail.children;
}

get errored(): boolean {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

has_error since this property is static, we should cache it avoid iterating the entire tree. I would probably go with having a TraceTreeNode[] property on the autogrouped node where we push the errored references at the time of autogrouping. This also gives us the ability to show to the user all errors or use that list to iterate and show 🔥 icons.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great idea @JonasBa , I'll move forward with this.

// We mark the node as errored if any child from head to and including tail has an error.
let currentNode: TraceTreeNode<TraceTree.Span> = this.head;
while (currentNode && currentNode !== this.tail.children[0]) {
if (currentNode.value.relatedErrors.length > 0) {
return true;
}

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

currentNode = currentNode.children[0];
}

return false;
}
}

export class SiblingAutogroupNode extends TraceTreeNode<TraceTree.SiblingAutogroup> {
Expand All @@ -1232,6 +1252,20 @@ export class SiblingAutogroupNode extends TraceTreeNode<TraceTree.SiblingAutogro
super(parent, node, metadata);
this.expanded = false;
}

get errored(): boolean {
for (const child of this.children) {
if (!isSpanNode(child)) {
throw new TypeError('Expected child of autogrouped node to be a span node.');
}

if (child.value.relatedErrors.length > 0) {
return true;
}
}

return false;
}
}

function partialTransaction(
Expand Down Expand Up @@ -1346,20 +1380,39 @@ function nodeToId(n: TraceTreeNode<TraceTree.NodeValue>): TraceTree.NodePath {
throw new Error('Not implemented');
}

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