Skip to content

Commit

Permalink
Support hierarchical error span highlighting (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
tiffon authored and yurishkuro committed May 13, 2017
1 parent 4429225 commit 5376087
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 6 deletions.
15 changes: 9 additions & 6 deletions src/components/TracePage/TraceTimelineViewer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
formatDuration,
findServerChildSpan,
isErrorSpan,
spanContainsErredSpan,
} from './utils';
import { transformTrace } from './transforms';
import colorGenerator from '../../../utils/color-generator';
Expand Down Expand Up @@ -323,12 +324,9 @@ function TraceView(props) {
};
}

let backgroundColor;
if (isErrorSpan(span)) {
backgroundColor = '#ffe6e6';
} else if (showSpanDetails) {
backgroundColor = 'whitesmoke';
}
const showErrorIcon = isErrorSpan(span) ||
(spanIsCollapsed && spanContainsErredSpan(trace.spans, i));
const backgroundColor = showSpanDetails ? 'whitesmoke' : null;
arr.push(
<TimelineRow
key={spanID}
Expand Down Expand Up @@ -378,6 +376,11 @@ function TraceView(props) {
: undefined,
}}
>
{showErrorIcon &&
<i
aria-hidden="true"
className="icon warning circle red"
/>}
{span.process.serviceName} {childServerSpan &&
spanIsCollapsed &&
<span>
Expand Down
22 changes: 22 additions & 0 deletions src/components/TracePage/TraceTimelineViewer/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,28 @@ export const isClientSpan = span => hasTagKey(span.tags, 'span.kind', 'client');
export const isServerSpan = span => hasTagKey(span.tags, 'span.kind', 'server');
export const isErrorSpan = span => hasTagKey(span.tags, 'error', true);

/**
* Returns `true` if at least one of the descendants of the `parentSpanIndex`
* span contains an error tag.
*
* @param {Span[]} spans The spans for a trace - should be
* sorted with children following parents.
* @param {number} parentSpanIndex The index of the parent span - only
* subsequent spans with depth less than
* the parent span will be checked.
* @return {boolean} Returns `true` if a descendant contains an error tag.
*/
export function spanContainsErredSpan(spans, parentSpanIndex) {
const { depth } = spans[parentSpanIndex];
let i = parentSpanIndex + 1;
for (; i < spans.length && spans[i].depth > depth; i++) {
if (isErrorSpan(spans[i])) {
return true;
}
}
return false;
}

/**
* Expects the first span to be the parent span.
*/
Expand Down
63 changes: 63 additions & 0 deletions src/components/TracePage/TraceTimelineViewer/utils.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import { spanContainsErredSpan } from './utils';
import traceGenerator from '../../../demo/trace-generators';

it('spanContainsErredSpan(...) is true only when a descendant has an error tag', () => {
const errorTag = { key: 'error', type: 'bool', value: true };
const getTags = withError =>
withError ? traceGenerator.tags().concat(errorTag) : traceGenerator.tags();

// Using a string to generate the test spans. Each line results in a span. The
// left number indicates whether or not the generated span has a descendant
// with an error tag (the expectation). The length of the line indicates the
// depth of the span (i.e. further right is higher depth). The right number
// indicates whether or not the span has an error tag.
const config = `
1 0
1 0
0 1
0 0
1 0
1 1
0 1
0 0
1 0
0 1
0 0
`
.trim()
.split('\n')
.map(s => s.trim());
// Get the expectation, str -> number -> bool
const expectations = config.map(s => Boolean(Number(s[0])));
const spans = config.map(line => ({
depth: line.length,
tags: getTags(+line.slice(-1)),
}));

expectations.forEach((target, i) => {
// include the index in the expect condition to know which span failed
// (if there is a failure, that is)
const result = [i, spanContainsErredSpan(spans, i)];
expect(result).toEqual([i, target]);
});
});

0 comments on commit 5376087

Please sign in to comment.