Skip to content

Commit

Permalink
Visualizing composite spans.
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexanderWert committed Jul 27, 2021
1 parent 12c8d2d commit 8712101
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import {
EuiBadge,
EuiButtonEmpty,
EuiCallOut,
EuiFlexGroup,
EuiFlexItem,
EuiFlyoutBody,
Expand All @@ -21,6 +22,7 @@ import {
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { Fragment } from 'react';
import { CompositeSpanDurationSummaryItem } from '../../../../../../shared/Summary/CompositeSpanDurationSummaryItem';
import { euiStyled } from '../../../../../../../../../../../src/plugins/kibana_react/common';
import { Span } from '../../../../../../../../typings/es_schemas/ui/span';
import { Transaction } from '../../../../../../../../typings/es_schemas/ui/transaction';
Expand Down Expand Up @@ -63,7 +65,7 @@ function formatSubtype(subtype: string | undefined) {

function getSpanTypes(span: Span) {
const { type, subtype, action } = span.span;

return {
spanType: formatType(type),
spanSubtype: formatSubtype(subtype),
Expand Down Expand Up @@ -123,7 +125,6 @@ export function SpanFlyout({
</h2>
</EuiTitle>
</EuiFlexItem>

<EuiFlexItem grow={false}>
<DiscoverSpanLink span={span}>
<EuiButtonEmpty iconType="discoverApp">
Expand All @@ -137,18 +138,40 @@ export function SpanFlyout({
</DiscoverSpanLink>
</EuiFlexItem>
</EuiFlexGroup>
{span.span.composite && (
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiCallOut color="warning" iconType="gear" size="s">
{i18n.translate(
'xpack.apm.transactionDetails.spanFlyout.compositeExampleWarning',
{
defaultMessage: 'This is a sample document for a group of consecutive, similar spans!',
}
)}
</EuiCallOut>
</EuiFlexItem>
</EuiFlexGroup>
)}
</EuiFlyoutHeader>
<EuiFlyoutBody>
<StickySpanProperties span={span} transaction={parentTransaction} />
<EuiSpacer size="m" />
<Summary
items={[
<TimestampTooltip time={span.timestamp.us / 1000} />,
<DurationSummaryItem
duration={span.span.duration.us}
totalDuration={totalDuration}
parentType="transaction"
/>,
<DurationSummaryItem
duration={span.span.duration.us}
totalDuration={totalDuration}
parentType="transaction"
/>,
<>
{span.span.composite && (
<CompositeSpanDurationSummaryItem
count={span.span.composite.count}
durationSum={span.span.composite.sum.us}
/>
)}
</>,
<>
{spanHttpUrl && (
<HttpInfoContainer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { Margins } from '../../../../../shared/charts/Timeline';
import { ErrorOverviewLink } from '../../../../../shared/Links/apm/ErrorOverviewLink';
import { ErrorCount } from '../../ErrorCount';
import { SyncBadge } from './sync_badge';
import { IWaterfallSpanOrTransaction } from './waterfall_helpers/waterfall_helpers';
import { IWaterfallSpanOrTransaction, IWaterfallSpan } from './waterfall_helpers/waterfall_helpers';

type ItemType = 'transaction' | 'span' | 'error';

Expand Down Expand Up @@ -147,7 +147,12 @@ function HttpStatusCode({ item }: { item: IWaterfallSpanOrTransaction }) {
function NameLabel({ item }: { item: IWaterfallSpanOrTransaction }) {
switch (item.docType) {
case 'span':
return <EuiText size="s">{item.doc.span.name}</EuiText>;
var name = item.doc.span.name;
if(item.doc.span.composite !== undefined){
const compositePrefix = item.doc.span.composite.compression_strategy === 'exact_match' ? 'x' : '';
name = item.doc.span.composite.count + compositePrefix + ' ' + name;
}
return <EuiText size="s">{name}</EuiText>;
case 'transaction':
return (
<EuiTitle size="xxs">
Expand Down Expand Up @@ -182,6 +187,9 @@ export function WaterfallItem({
}
);

const isCompositeSpan = item.docType === 'span' && (item as IWaterfallSpan).doc.span.composite !== undefined;
const itemBarStyle = getItemBarStyle(item, color, width, left);

return (
<Container
type={item.docType}
Expand All @@ -193,8 +201,8 @@ export function WaterfallItem({
}}
>
<ItemBar // using inline styles instead of props to avoid generating a css class for each item
style={{ left: `${left}%`, width: `${width}%` }}
color={color}
style={itemBarStyle}
color={isCompositeSpan ? 'transparent' : color}
type={item.docType}
/>
<ItemText // using inline styles instead of props to avoid generating a css class for each item
Expand Down Expand Up @@ -227,3 +235,29 @@ export function WaterfallItem({
</Container>
);
}

function getItemBarStyle(item: IWaterfallSpanOrTransaction, color: string, width: number, left: number): React.CSSProperties {
var itemBarStyle = { left: `${left}%`, width: `${width}%` };

if(item.docType === 'span'){
const spanItem = item as IWaterfallSpan;

if(spanItem.doc.span.composite !== undefined){
const composite = spanItem.doc.span.composite;
const percNumItems = 100.0 / composite.count;
const percDuration = percNumItems * (composite.sum.us / spanItem.doc.span.duration.us);

itemBarStyle = {
...itemBarStyle,
...{
backgroundImage: `repeating-linear-gradient(90deg, ${color},`+
` ${color} max(${percDuration}%,3px),` +
` transparent max(${percDuration}%,3px),` +
` transparent max(${percNumItems}%,max(${percDuration}%,3px) + 3px))`
}
};
}
}

return itemBarStyle;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* 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 { i18n } from '@kbn/i18n';
import { EuiToolTip, EuiText } from '@elastic/eui';
import { asDuration } from '../../../../common/utils/formatters';

interface Props {
count: number;
durationSum: number;
}

function CompositeSpanDurationSummaryItem({ count, durationSum }: Props) {
const avgDuration = durationSum / count;

const label = i18n.translate('xpack.apm.compositeSpanDurationLabel', {
defaultMessage: 'Average duration',
});
const callsLabel = i18n.translate('xpack.apm.compositeSpanCallsLabel', {
defaultMessage: 'calls',
});
return (
<>
<EuiToolTip content={label}>
<EuiText>{count} {callsLabel}, &#8709; {asDuration(avgDuration)}</EuiText>
</EuiToolTip>
</>
);
}

export { CompositeSpanDurationSummaryItem };
5 changes: 5 additions & 0 deletions x-pack/plugins/apm/typings/es_schemas/raw/span_raw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ export interface SpanRaw extends APMBaseDoc {
body?: string;
headers?: Record<string, unknown>;
};
composite?: {
count: number;
sum: { us: number };
compression_strategy: string;
};
};
timestamp: TimestampUs;
transaction?: {
Expand Down

0 comments on commit 8712101

Please sign in to comment.