diff --git a/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/ResultItem.css b/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/ResultItem.css
index f53f6b91ec..fa0d1ece08 100644
--- a/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/ResultItem.css
+++ b/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/ResultItem.css
@@ -27,4 +27,15 @@ limitations under the License.
.ResultItem--serviceTag {
border-left-width: 15px;
margin: 0;
+ display: flex;
+ align-items: center;
+}
+
+.ResultItem--errorIcon {
+ background: #db2828;
+ border-radius: 6.5px;
+ color: #fff;
+ font-size: 0.85em;
+ margin-right: 0.25rem;
+ padding: 1px;
}
diff --git a/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/ResultItem.test.js b/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/ResultItem.test.js
index 2d03716c98..9ad0828aef 100644
--- a/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/ResultItem.test.js
+++ b/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/ResultItem.test.js
@@ -40,3 +40,10 @@ it(' should not render any ServiceTags when there are no services'
const serviceTags = wrapper.find(`[data-test="${markers.SERVICE_TAGS}"]`).find(Tag);
expect(serviceTags).toHaveLength(0);
});
+
+it(' should render error icon on ServiceTags that have an error tag', () => {
+ trace.spans[0].tags.push({ key: 'error', value: true });
+ const wrapper = shallow();
+ const errorServiceTags = wrapper.find('.ResultItem--errorIcon').getElements();
+ expect(errorServiceTags).toHaveLength(1);
+});
diff --git a/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/ResultItem.tsx b/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/ResultItem.tsx
index 3f8f300a9e..93d6077903 100644
--- a/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/ResultItem.tsx
+++ b/packages/jaeger-ui/src/components/SearchTracePage/SearchResults/ResultItem.tsx
@@ -19,6 +19,8 @@ import { Link } from 'react-router-dom';
import { sortBy } from 'lodash';
import moment from 'moment';
+import IoAlert from 'react-icons/lib/io/alert';
+
import { trackConversions, EAltViewActions } from './index.track';
import * as markers from './ResultItem.markers';
import ResultItemTitle from './ResultItemTitle';
@@ -38,10 +40,43 @@ type Props = {
disableComparision: boolean;
};
+type State = {
+ erroredServices: Set;
+ numSpans: number;
+ numErredSpans: number;
+ timeStr: string;
+ fromNow: string | boolean;
+};
+
const isErrorTag = ({ key, value }: KeyValuePair) => key === 'error' && (value === true || value === 'true');
const trackTraceConversions = () => trackConversions(EAltViewActions.Traces);
-export default class ResultItem extends React.PureComponent {
+export default class ResultItem extends React.PureComponent {
+ constructor(props: Props, state: State) {
+ super(props, state);
+ const { startTime, spans } = props.trace;
+
+ const mDate = moment(startTime / 1000);
+
+ const erroredServices: Set = new Set();
+
+ const numErredSpans = spans.filter(sp => {
+ const hasError = sp.tags.some(isErrorTag);
+ if (hasError) {
+ erroredServices.add(sp.process.serviceName);
+ }
+ return hasError;
+ }).length;
+
+ this.state = {
+ numSpans: spans.length,
+ timeStr: mDate.format('h:mm:ss a'),
+ fromNow: mDate.fromNow(),
+ numErredSpans,
+ erroredServices,
+ };
+ }
+
render() {
const {
disableComparision,
@@ -51,12 +86,7 @@ export default class ResultItem extends React.PureComponent {
toggleComparison,
trace,
} = this.props;
- const { duration, services, startTime, spans, traceName, traceID } = trace;
- const mDate = moment(startTime / 1000);
- const timeStr = mDate.format('h:mm:ss a');
- const fromNow = mDate.fromNow();
- const numSpans = spans.length;
- const numErredSpans = spans.filter(sp => sp.tags.some(isErrorTag)).length;
+ const { duration, services, startTime, traceName, traceID } = trace;
return (
{
- {numSpans} Span{numSpans > 1 && 's'}
+ {this.state.numSpans} Span{this.state.numSpans > 1 && 's'}
- {Boolean(numErredSpans) && (
+ {Boolean(this.state.numErredSpans) && (
- {numErredSpans} Error{numErredSpans > 1 && 's'}
+ {this.state.numErredSpans} Error{this.state.numErredSpans > 1 && 's'}
)}
@@ -91,6 +121,9 @@ export default class ResultItem extends React.PureComponent {
className="ResultItem--serviceTag"
style={{ borderLeftColor: colorGenerator.getColorByKey(name) }}
>
+ {this.state.erroredServices.has(name) && (
+
+ )}
{name} ({count})
@@ -101,9 +134,9 @@ export default class ResultItem extends React.PureComponent {
{formatRelativeDate(startTime / 1000)}
- {timeStr.slice(0, -3)} {timeStr.slice(-2)}
+ {this.state.timeStr.slice(0, -3)} {this.state.timeStr.slice(-2)}
- {fromNow}
+ {this.state.fromNow}