Skip to content

Commit

Permalink
[APM] Show badge for failed spans in waterfall (#109812)
Browse files Browse the repository at this point in the history
Co-authored-by: Casper Hübertz <casper@formgeist.com>
  • Loading branch information
sorenlouv and formgeist committed Sep 8, 2021
1 parent 5464af6 commit d3f6303
Show file tree
Hide file tree
Showing 42 changed files with 2,478 additions and 2,524 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,7 @@ export function TransactionDistribution({

const { urlParams } = useUrlParams();

const {
waterfall,
exceedsMax,
status: waterfallStatus,
} = useWaterfallFetcher();
const { waterfall, status: waterfallStatus } = useWaterfallFetcher();

const markerCurrentTransaction =
waterfall.entryWaterfallTransaction?.doc.transaction.duration.us;
Expand Down Expand Up @@ -215,7 +211,6 @@ export function TransactionDistribution({
urlParams={urlParams}
waterfall={waterfall}
isLoading={waterfallStatus === FETCH_STATUS.LOADING}
exceedsMax={exceedsMax}
traceSamples={traceSamples}
/>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import { useTimeRange } from '../../../hooks/use_time_range';
import { getWaterfall } from './waterfall_with_summary/waterfall_container/Waterfall/waterfall_helpers/waterfall_helpers';

const INITIAL_DATA = {
root: undefined,
trace: { items: [], exceedsMax: false, errorDocs: [] },
errorsPerTransaction: {},
errorDocs: [],
traceDocs: [],
exceedsMax: false,
};

export function useWaterfallFetcher() {
Expand Down Expand Up @@ -51,5 +51,5 @@ export function useWaterfallFetcher() {
transactionId,
]);

return { waterfall, status, error, exceedsMax: data.trace.exceedsMax };
return { waterfall, status, error };
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,9 @@ interface Props {
transaction: Transaction;
urlParams: ApmUrlParams;
waterfall: IWaterfall;
exceedsMax: boolean;
}

export function TransactionTabs({
transaction,
urlParams,
waterfall,
exceedsMax,
}: Props) {
export function TransactionTabs({ transaction, urlParams, waterfall }: Props) {
const history = useHistory();
const tabs = [timelineTab, metadataTab, logsTab];
const currentTab =
Expand Down Expand Up @@ -65,7 +59,6 @@ export function TransactionTabs({
<TabContent
urlParams={urlParams}
waterfall={waterfall}
exceedsMax={exceedsMax}
transaction={transaction}
/>
</React.Fragment>
Expand Down Expand Up @@ -99,19 +92,11 @@ const logsTab = {
function TimelineTabContent({
urlParams,
waterfall,
exceedsMax,
}: {
urlParams: ApmUrlParams;
waterfall: IWaterfall;
exceedsMax: boolean;
}) {
return (
<WaterfallContainer
urlParams={urlParams}
waterfall={waterfall}
exceedsMax={exceedsMax}
/>
);
return <WaterfallContainer urlParams={urlParams} waterfall={waterfall} />;
}

function MetadataTabContent({ transaction }: { transaction: Transaction }) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,13 @@ import { useApmParams } from '../../../../hooks/use_apm_params';
interface Props {
urlParams: ApmUrlParams;
waterfall: IWaterfall;
exceedsMax: boolean;
isLoading: boolean;
traceSamples: TraceSample[];
}

export function WaterfallWithSummary({
urlParams,
waterfall,
exceedsMax,
isLoading,
traceSamples,
}: Props) {
Expand Down Expand Up @@ -125,7 +123,7 @@ export function WaterfallWithSummary({
<EuiSpacer size="s" />

<TransactionSummary
errorCount={waterfall.errorsCount}
errorCount={waterfall.apiResponse.errorDocs.length}
totalDuration={waterfall.rootTransaction?.transaction.duration.us}
transaction={entryTransaction}
/>
Expand All @@ -135,7 +133,6 @@ export function WaterfallWithSummary({
transaction={entryTransaction}
urlParams={urlParams}
waterfall={waterfall}
exceedsMax={exceedsMax}
/>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ interface AccordionWaterfallProps {
level: number;
duration: IWaterfall['duration'];
waterfallItemId?: string;
errorsPerTransaction: IWaterfall['errorsPerTransaction'];
childrenByParentId: Record<string, IWaterfallSpanOrTransaction[]>;
waterfall: IWaterfall;
onToggleEntryTransaction?: () => void;
timelineMargins: Margins;
onClickWaterfallItem: (item: IWaterfallSpanOrTransaction) => void;
Expand Down Expand Up @@ -96,22 +95,17 @@ export function AccordionWaterfall(props: AccordionWaterfallProps) {
item,
level,
duration,
childrenByParentId,
waterfall,
waterfallItemId,
errorsPerTransaction,
timelineMargins,
onClickWaterfallItem,
onToggleEntryTransaction,
} = props;

const nextLevel = level + 1;

const errorCount =
item.docType === 'transaction'
? errorsPerTransaction[item.doc.transaction.id]
: 0;

const children = childrenByParentId[item.id] || [];
const children = waterfall.childrenByParentId[item.id] || [];
const errorCount = waterfall.getErrorCount(item.id);

// To indent the items creating the parent/child tree
const marginLeftLevel = 8 * level;
Expand All @@ -121,7 +115,7 @@ export function AccordionWaterfall(props: AccordionWaterfallProps) {
buttonClassName={`button_${item.id}`}
key={item.id}
id={item.id}
hasError={errorCount > 0}
hasError={item.doc.event?.outcome === 'failure'}
marginLeftLevel={marginLeftLevel}
childrenCount={children.length}
buttonContent={
Expand Down Expand Up @@ -152,16 +146,11 @@ export function AccordionWaterfall(props: AccordionWaterfallProps) {
>
{children.map((child) => (
<AccordionWaterfall
{...props}
key={child.id}
isOpen={isOpen}
item={child}
level={nextLevel}
waterfallItemId={waterfallItemId}
errorsPerTransaction={errorsPerTransaction}
duration={duration}
childrenByParentId={childrenByParentId}
timelineMargins={timelineMargins}
onClickWaterfallItem={onClickWaterfallItem}
item={child}
/>
))}
</StyledAccordion>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import { EuiBadge, EuiToolTip } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useTheme } from '../../../../../../hooks/use_theme';

import { euiStyled } from '../../../../../../../../../../src/plugins/kibana_react/common';

const ResetLineHeight = euiStyled.span`
line-height: initial;
`;

export function FailureBadge({ outcome }: { outcome?: 'success' | 'failure' }) {
const theme = useTheme();

if (outcome !== 'failure') {
return null;
}

return (
<ResetLineHeight>
<EuiToolTip
content={i18n.translate('xpack.apm.failure_badge.tooltip', {
defaultMessage: 'event.outcome = failure',
})}
>
<EuiBadge color={theme.eui.euiColorDanger}>failure</EuiBadge>
</EuiToolTip>
</ResetLineHeight>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import { WaterfallFlyout } from './waterfall_flyout';
import {
IWaterfall,
IWaterfallItem,
IWaterfallSpanOrTransaction,
} from './waterfall_helpers/waterfall_helpers';

const Container = euiStyled.div`
Expand Down Expand Up @@ -61,9 +60,8 @@ const WaterfallItemsContainer = euiStyled.div`
interface Props {
waterfallItemId?: string;
waterfall: IWaterfall;
exceedsMax: boolean;
}
export function Waterfall({ waterfall, exceedsMax, waterfallItemId }: Props) {
export function Waterfall({ waterfall, waterfallItemId }: Props) {
const history = useHistory();
const [isAccordionOpen, setIsAccordionOpen] = useState(true);
const itemContainerHeight = 58; // TODO: This is a nasty way to calculate the height of the svg element. A better approach should be found
Expand All @@ -74,37 +72,10 @@ export function Waterfall({ waterfall, exceedsMax, waterfallItemId }: Props) {
const agentMarks = getAgentMarks(waterfall.entryWaterfallTransaction?.doc);
const errorMarks = getErrorMarks(waterfall.errorItems);

function renderItems(
childrenByParentId: Record<string | number, IWaterfallSpanOrTransaction[]>
) {
const { entryWaterfallTransaction } = waterfall;
if (!entryWaterfallTransaction) {
return null;
}
return (
<AccordionWaterfall
// used to recreate the entire tree when `isAccordionOpen` changes, collapsing or expanding all elements.
key={`accordion_state_${isAccordionOpen}`}
isOpen={isAccordionOpen}
item={entryWaterfallTransaction}
level={0}
waterfallItemId={waterfallItemId}
errorsPerTransaction={waterfall.errorsPerTransaction}
duration={duration}
childrenByParentId={childrenByParentId}
timelineMargins={TIMELINE_MARGINS}
onClickWaterfallItem={(item: IWaterfallItem) =>
toggleFlyout({ history, item })
}
onToggleEntryTransaction={() => setIsAccordionOpen((isOpen) => !isOpen)}
/>
);
}

return (
<HeightRetainer>
<Container>
{exceedsMax && (
{waterfall.apiResponse.exceedsMax && (
<EuiCallOut
color="warning"
size="s"
Expand Down Expand Up @@ -132,7 +103,25 @@ export function Waterfall({ waterfall, exceedsMax, waterfallItemId }: Props) {
/>
</div>
<WaterfallItemsContainer>
{renderItems(waterfall.childrenByParentId)}
{!waterfall.entryWaterfallTransaction ? null : (
<AccordionWaterfall
// used to recreate the entire tree when `isAccordionOpen` changes, collapsing or expanding all elements.
key={`accordion_state_${isAccordionOpen}`}
isOpen={isAccordionOpen}
item={waterfall.entryWaterfallTransaction}
level={0}
waterfallItemId={waterfallItemId}
duration={duration}
waterfall={waterfall}
timelineMargins={TIMELINE_MARGINS}
onClickWaterfallItem={(item: IWaterfallItem) =>
toggleFlyout({ history, item })
}
onToggleEntryTransaction={() =>
setIsAccordionOpen((isOpen) => !isOpen)
}
/>
)}
</WaterfallItemsContainer>
</div>

Expand Down
Loading

0 comments on commit d3f6303

Please sign in to comment.