Skip to content

Commit

Permalink
[APM] One-line trace summary
Browse files Browse the repository at this point in the history
Replace the `StickyTransactionProperties` with a trace summary that puts everything on one line.

Fixes elastic#43247.
  • Loading branch information
smith committed Sep 9, 2019
1 parent bb8b2f1 commit 03f58bf
Show file tree
Hide file tree
Showing 17 changed files with 594 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@ import { EuiBadge } from '@elastic/eui';
import euiThemeLight from '@elastic/eui/dist/eui_theme_light.json';
import React from 'react';

type Props = React.ComponentProps<typeof EuiBadge> & {
errorCount: number;
};
type Props = React.ComponentProps<typeof EuiBadge>;

export const ErrorCountBadge = ({ errorCount = 0, ...rest }: Props) => (
export const ErrorCountBadge = ({ children, ...rest }: Props) => (
<EuiBadge color={euiThemeLight.euiColorDanger} {...rest}>
{errorCount}
{children}
</EuiBadge>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import React from 'react';
import { i18n } from '@kbn/i18n';
import { EuiToolTip } from '@elastic/eui';
import { asPercent } from '../../../../utils/formatters';

interface PercentOfTraceProps {
duration: number;
totalDuration?: number;
}

export function PercentOfTrace({
duration,
totalDuration
}: PercentOfTraceProps) {
totalDuration = totalDuration || duration;
const isOver100 = duration > totalDuration;
const percentOfTrace = isOver100
? '>100%'
: asPercent(duration, totalDuration, '');

const percentOfTraceText = i18n.translate(
'xpack.apm.transactionDetails.percentOfTrace',
{
defaultMessage: '{value} of trace',
values: { value: percentOfTrace }
}
);

return (
<>
{isOver100 ? (
<EuiToolTip
content={i18n.translate(
'xpack.apm.transactionDetails.percentOfTraceLabelExplanation',
{
defaultMessage:
'The % of trace exceeds 100% because this transaction takes longer than the root transaction.'
}
)}
>
<>{percentOfTraceText}</>
</EuiToolTip>
) : (
`(${percentOfTraceText})`
)}
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import { i18n } from '@kbn/i18n';
import React from 'react';
import { idx } from '@kbn/elastic-idx';
import { EuiToolTip } from '@elastic/eui';
import styled from 'styled-components';
import {
TRANSACTION_DURATION,
Expand All @@ -18,14 +17,15 @@ import {
} from '../../../../../common/elasticsearch_fieldnames';
import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n';
import { Transaction } from '../../../../../typings/es_schemas/ui/Transaction';
import { asPercent, asTime } from '../../../../utils/formatters';
import { asTime } from '../../../../utils/formatters';
import {
IStickyProperty,
StickyProperties
} from '../../../shared/StickyProperties';
import { ErrorCountBadge } from './ErrorCountBadge';
import { isRumAgentName } from '../../../../../common/agent_name';
import { fontSize } from '../../../../style/variables';
import { PercentOfTrace } from './PercentOfTrace';

interface Props {
transaction: Transaction;
Expand Down Expand Up @@ -96,22 +96,7 @@ export function StickyTransactionProperties({
defaultMessage: '% of trace'
}
),
val:
totalDuration !== undefined && duration > totalDuration ? (
<EuiToolTip
content={i18n.translate(
'xpack.apm.transactionDetails.percentOfTraceLabelExplanation',
{
defaultMessage:
'The % of trace exceeds 100% because this transaction takes longer than the root transaction.'
}
)}
>
<span>&gt;100%</span>
</EuiToolTip>
) : (
asPercent(duration, totalDuration, NOT_AVAILABLE_LABEL)
),
val: <PercentOfTrace duration={duration} totalDuration={totalDuration} />,
width: '25%'
},
{
Expand All @@ -131,7 +116,7 @@ export function StickyTransactionProperties({
),
val: errorCount ? (
<>
<ErrorCountBadge errorCount={errorCount} />
<ErrorCountBadge>{errorCount}</ErrorCountBadge>
<ErrorTitle>
&nbsp;
{i18n.translate('xpack.apm.transactionDetails.errorsOverviewLink', {
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;
* you may not use this file except in compliance with the Elastic License.
*/

import React from 'react';
import { i18n } from '@kbn/i18n';
import { EuiToolTip, EuiText } from '@elastic/eui';
import styled from 'styled-components';
import { asTime } from '../../../../../utils/formatters';
import { PercentOfTrace } from '../PercentOfTrace';

interface DurationProps {
duration: number;
totalDuration?: number;
}

const Span = styled('span')`
white-space: nowrap;
`;

export function Duration({ duration, totalDuration }: DurationProps) {
totalDuration = totalDuration || duration;
const label = i18n.translate('xpack.apm.transactionDetails.durationLabel', {
defaultMessage: 'Duration'
});

return (
<Span>
<EuiToolTip content={label}>
<EuiText>{asTime(duration)}</EuiText>
</EuiToolTip>{' '}
<PercentOfTrace duration={duration} totalDuration={totalDuration} />
</Span>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import React from 'react';
import { shallow, mount } from 'enzyme';
import { HttpInfo } from './HttpInfo';
import * as exampleTransactions from './__fixtures__/transactions';
import { palettes } from '@elastic/eui';
import { cloneDeep, set } from 'lodash';

describe('HttpInfo', () => {
describe('render', () => {
const transaction = exampleTransactions.httpOk;
const url = 'https://example.com';
const props = { transaction, url };

it('renders', () => {
expect(() => shallow(<HttpInfo {...props} />)).not.toThrowError();
});

describe('with status code 200', () => {
it('shows a success color', () => {
const wrapper = mount(<HttpInfo {...props} />);

expect(wrapper.find('HttpStatusBadge EuiBadge').prop('color')).toEqual(
palettes.euiPaletteForStatus.colors[0]
);
});
});

describe('with status code 301', () => {
it('shows a warning color', () => {
const p = cloneDeep(props);
set(p, 'transaction.http.response.status_code', 301);

const wrapper = mount(<HttpInfo {...p} />);

expect(wrapper.find('HttpStatusBadge EuiBadge').prop('color')).toEqual(
palettes.euiPaletteForStatus.colors[4]
);
});
});

describe('with status code 404', () => {
it('shows a error color', () => {
const p = cloneDeep(props);
set(p, 'transaction.http.response.status_code', 404);

const wrapper = mount(<HttpInfo {...p} />);

expect(wrapper.find('HttpStatusBadge EuiBadge').prop('color')).toEqual(
palettes.euiPaletteForStatus.colors[9]
);
});
});

describe('with status code 502', () => {
it('shows a error color', () => {
const p = cloneDeep(props);
set(p, 'transaction.http.response.status_code', 502);

const wrapper = mount(<HttpInfo {...p} />);

expect(wrapper.find('HttpStatusBadge EuiBadge').prop('color')).toEqual(
palettes.euiPaletteForStatus.colors[9]
);
});
});

describe('with some other status code', () => {
it('shows the default color', () => {
const p = cloneDeep(props);
set(p, 'transaction.http.response.status_code', 700);

const wrapper = mount(<HttpInfo {...p} />);

expect(wrapper.find('HttpStatusBadge EuiBadge').prop('color')).toEqual(
'default'
);
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import React from 'react';
import { EuiToolTip, EuiBadge } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import styled from 'styled-components';
import { idx } from '@kbn/elastic-idx/target';
import { palettes } from '@elastic/eui';
import { Transaction } from '../../../../../../typings/es_schemas/ui/Transaction';
import { units, px, truncate, unit } from '../../../../../style/variables';
import { statusCodes } from './statusCodes';

const statusColors = {
success: palettes.euiPaletteForStatus.colors[0],
warning: palettes.euiPaletteForStatus.colors[4],
error: palettes.euiPaletteForStatus.colors[9]
};

function getStatusColor(status: number) {
const colors: { [key: string]: string } = {
2: statusColors.success,
3: statusColors.warning,
4: statusColors.error,
5: statusColors.error
};

return colors[status.toString().substr(0, 1)] || 'default';
}

interface HttpStatusBadgeProps {
status: number;
}
function HttpStatusBadge({ status }: HttpStatusBadgeProps) {
const label = i18n.translate('xpack.apm.transactionDetails.statusCode', {
defaultMessage: 'Status code'
});

return (
<EuiToolTip content={label}>
<EuiBadge color={getStatusColor(status)}>
{status} {statusCodes[status.toString()]}
</EuiBadge>
</EuiToolTip>
);
}

const HttpInfoBadge = styled(EuiBadge)`
margin-right: ${px(units.quarter)};
`;

const Url = styled('span')`
display: inline-block;
vertical-align: bottom;
${truncate(px(unit * 24))};
`;
interface HttpInfoProps {
transaction: Transaction;
url: string;
}

const Span = styled('span')`
white-space: nowrap;
`;

export function HttpInfo({ transaction, url }: HttpInfoProps) {
const method = (
idx(transaction, _ => _.http.request.method) || ''
).toUpperCase();
const status = idx(transaction, _ => _.http.response.status_code);

const methodLabel = i18n.translate(
'xpack.apm.transactionDetails.requestMethodLabel',
{
defaultMessage: 'Request method'
}
);

return (
<Span>
<HttpInfoBadge title={undefined}>
<EuiToolTip content={methodLabel}>
<strong>{method}</strong>
</EuiToolTip>{' '}
<EuiToolTip content={url}>
<Url>{url}</Url>
</EuiToolTip>
</HttpInfoBadge>
{status && <HttpStatusBadge status={status} />}
</Span>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

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

interface ResultProps {
result: string;
}

export function Result({ result }: ResultProps) {
return (
<EuiToolTip
content={i18n.translate('xpack.apm.transactionDetails.resultLabel', {
defaultMessage: 'Result'
})}
>
<EuiBadge color="default" title={undefined}>
{result}
</EuiBadge>
</EuiToolTip>
);
}

0 comments on commit 03f58bf

Please sign in to comment.