* [APM] Always get the root transaction when fetching trace items Fixes #33210 * code tweaks * displays message notifying user that trace items exceeds maximum displayed * remove getTraceRoot query by adjusting the score and order of trace items with no parent.id * add `apm_oss.maxTraceItems` config options to control the number of displayed trace items * changed config `apm_oss.maxTraceItems` to `xpack.apm.ui.maxTraceItems` * added missing configs to apm settings doc and docker template * minor code tweak
This commit is contained in:
parent
4535f2d686
commit
4301be4821
|
@ -21,6 +21,10 @@ xpack.apm.ui.enabled:: Set to `false` to hide the APM plugin {kib} from the menu
|
|||
|
||||
xpack.apm.ui.transactionGroupBucketSize:: Number of top transaction groups displayed in APM plugin in Kibana. Defaults to `100`.
|
||||
|
||||
xpack.apm.ui.maxTraceItems:: Max number of child items displayed when viewing trace details. Defaults to `1000`.
|
||||
|
||||
apm_oss.apmAgentConfigurationIndex:: Index containing agent configuration settings. Defaults to `.apm-agent-configuration`.
|
||||
|
||||
apm_oss.indexPattern:: Index pattern is used for integrations with Machine Learning and Kuery Bar. It must match all apm indices. Defaults to `apm-*`.
|
||||
|
||||
apm_oss.errorIndices:: Matcher for indices containing error documents. Defaults to `apm-*`.
|
||||
|
|
|
@ -82,6 +82,14 @@ kibana_vars=(
|
|||
vega.enableExternalUrls
|
||||
xpack.apm.enabled
|
||||
xpack.apm.ui.enabled
|
||||
xpack.apm.ui.maxTraceItems
|
||||
apm_oss.apmAgentConfigurationIndex
|
||||
apm_oss.indexPattern
|
||||
apm_oss.errorIndices
|
||||
apm_oss.onboardingIndices
|
||||
apm_oss.spanIndices
|
||||
apm_oss.transactionIndices
|
||||
apm_oss.metricsIndices
|
||||
xpack.canvas.enabled
|
||||
xpack.graph.enabled
|
||||
xpack.grokdebugger.enabled
|
||||
|
|
|
@ -38,7 +38,7 @@ export default function apmOss(kibana) {
|
|||
spanIndices: Joi.string().default('apm-*'),
|
||||
metricsIndices: Joi.string().default('apm-*'),
|
||||
onboardingIndices: Joi.string().default('apm-*'),
|
||||
apmAgentConfigurationIndex: Joi.string().default('.apm-agent-configuration')
|
||||
apmAgentConfigurationIndex: Joi.string().default('.apm-agent-configuration'),
|
||||
}).default();
|
||||
},
|
||||
|
||||
|
@ -49,7 +49,7 @@ export default function apmOss(kibana) {
|
|||
'transactionIndices',
|
||||
'spanIndices',
|
||||
'metricsIndices',
|
||||
'onboardingIndices'
|
||||
'onboardingIndices',
|
||||
].map(type => server.config().get(`apm_oss.${type}`))));
|
||||
}
|
||||
});
|
||||
|
|
|
@ -59,7 +59,8 @@ export const apm: LegacyPluginInitializer = kibana => {
|
|||
// display menu item
|
||||
ui: Joi.object({
|
||||
enabled: Joi.boolean().default(true),
|
||||
transactionGroupBucketSize: Joi.number().default(100)
|
||||
transactionGroupBucketSize: Joi.number().default(100),
|
||||
maxTraceItems: Joi.number().default(1000)
|
||||
}).default(),
|
||||
|
||||
// enable plugin
|
||||
|
|
|
@ -35,13 +35,15 @@ interface Props {
|
|||
transaction: Transaction;
|
||||
urlParams: IUrlParams;
|
||||
waterfall: IWaterfall;
|
||||
exceedsMax: boolean;
|
||||
}
|
||||
|
||||
export function TransactionTabs({
|
||||
location,
|
||||
transaction,
|
||||
urlParams,
|
||||
waterfall
|
||||
waterfall,
|
||||
exceedsMax
|
||||
}: Props) {
|
||||
const tabs = [timelineTab, metadataTab];
|
||||
const currentTab =
|
||||
|
@ -79,6 +81,7 @@ export function TransactionTabs({
|
|||
location={location}
|
||||
urlParams={urlParams}
|
||||
waterfall={waterfall}
|
||||
exceedsMax={exceedsMax}
|
||||
/>
|
||||
) : (
|
||||
<TransactionMetadata transaction={transaction} />
|
||||
|
|
|
@ -9,6 +9,8 @@ import React, { Component } from 'react';
|
|||
// @ts-ignore
|
||||
import { StickyContainer } from 'react-sticky';
|
||||
import styled from 'styled-components';
|
||||
import { EuiCallOut } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { IUrlParams } from '../../../../../../context/UrlParamsContext/types';
|
||||
// @ts-ignore
|
||||
import Timeline from '../../../../../shared/charts/Timeline';
|
||||
|
@ -47,6 +49,7 @@ interface Props {
|
|||
waterfall: IWaterfall;
|
||||
location: Location;
|
||||
serviceColors: IServiceColors;
|
||||
exceedsMax: boolean;
|
||||
}
|
||||
|
||||
export class Waterfall extends Component<Props> {
|
||||
|
@ -126,12 +129,23 @@ export class Waterfall extends Component<Props> {
|
|||
};
|
||||
|
||||
public render() {
|
||||
const { waterfall } = this.props;
|
||||
const { waterfall, exceedsMax } = this.props;
|
||||
const itemContainerHeight = 58; // TODO: This is a nasty way to calculate the height of the svg element. A better approach should be found
|
||||
const waterfallHeight = itemContainerHeight * waterfall.orderedItems.length;
|
||||
|
||||
return (
|
||||
<Container>
|
||||
{exceedsMax ? (
|
||||
<EuiCallOut
|
||||
color="warning"
|
||||
size="s"
|
||||
iconType="alert"
|
||||
title={i18n.translate('xpack.apm.waterfall.exceedsMax', {
|
||||
defaultMessage:
|
||||
'Number of items in this trace exceed what is displayed'
|
||||
})}
|
||||
/>
|
||||
) : null}
|
||||
<StickyContainer>
|
||||
<Timeline
|
||||
agentMarks={this.props.agentMarks}
|
||||
|
|
|
@ -17,6 +17,17 @@ import {
|
|||
describe('waterfall_helpers', () => {
|
||||
describe('getWaterfall', () => {
|
||||
const hits = [
|
||||
{
|
||||
processor: { event: 'transaction' },
|
||||
trace: { id: 'myTraceId' },
|
||||
service: { name: 'opbeans-node' },
|
||||
transaction: {
|
||||
duration: { us: 49660 },
|
||||
name: 'GET /api',
|
||||
id: 'myTransactionId1'
|
||||
},
|
||||
timestamp: { us: 1549324795784006 }
|
||||
} as Transaction,
|
||||
{
|
||||
parent: { id: 'mySpanIdA' },
|
||||
processor: { event: 'span' },
|
||||
|
@ -80,17 +91,6 @@ describe('waterfall_helpers', () => {
|
|||
id: 'myTransactionId2'
|
||||
},
|
||||
timestamp: { us: 1549324795823304 }
|
||||
} as Transaction,
|
||||
{
|
||||
processor: { event: 'transaction' },
|
||||
trace: { id: 'myTraceId' },
|
||||
service: { name: 'opbeans-node' },
|
||||
transaction: {
|
||||
duration: { us: 49660 },
|
||||
name: 'GET /api',
|
||||
id: 'myTransactionId1'
|
||||
},
|
||||
timestamp: { us: 1549324795784006 }
|
||||
} as Transaction
|
||||
];
|
||||
|
||||
|
@ -101,7 +101,10 @@ describe('waterfall_helpers', () => {
|
|||
myTransactionId2: 3
|
||||
};
|
||||
const waterfall = getWaterfall(
|
||||
{ trace: hits, errorsPerTransaction },
|
||||
{
|
||||
trace: { items: hits, exceedsMax: false },
|
||||
errorsPerTransaction
|
||||
},
|
||||
entryTransactionId
|
||||
);
|
||||
expect(waterfall.orderedItems.length).toBe(6);
|
||||
|
@ -116,7 +119,10 @@ describe('waterfall_helpers', () => {
|
|||
myTransactionId2: 3
|
||||
};
|
||||
const waterfall = getWaterfall(
|
||||
{ trace: hits, errorsPerTransaction },
|
||||
{
|
||||
trace: { items: hits, exceedsMax: false },
|
||||
errorsPerTransaction
|
||||
},
|
||||
entryTransactionId
|
||||
);
|
||||
expect(waterfall.orderedItems.length).toBe(4);
|
||||
|
@ -131,7 +137,10 @@ describe('waterfall_helpers', () => {
|
|||
myTransactionId2: 3
|
||||
};
|
||||
const waterfall = getWaterfall(
|
||||
{ trace: hits, errorsPerTransaction },
|
||||
{
|
||||
trace: { items: hits, exceedsMax: false },
|
||||
errorsPerTransaction
|
||||
},
|
||||
entryTransactionId
|
||||
);
|
||||
const transaction = waterfall.getTransactionById('myTransactionId2');
|
||||
|
|
|
@ -6,14 +6,14 @@
|
|||
|
||||
import theme from '@elastic/eui/dist/eui_theme_light.json';
|
||||
import {
|
||||
first,
|
||||
flatten,
|
||||
groupBy,
|
||||
indexBy,
|
||||
isEmpty,
|
||||
sortBy,
|
||||
uniq,
|
||||
zipObject
|
||||
zipObject,
|
||||
isEmpty,
|
||||
first
|
||||
} from 'lodash';
|
||||
import { idx } from '@kbn/elastic-idx';
|
||||
import { TraceAPIResponse } from '../../../../../../../../server/lib/traces/get_trace';
|
||||
|
@ -22,7 +22,7 @@ import { Span } from '../../../../../../../../typings/es_schemas/ui/Span';
|
|||
import { Transaction } from '../../../../../../../../typings/es_schemas/ui/Transaction';
|
||||
|
||||
interface IWaterfallIndex {
|
||||
[key: string]: IWaterfallItem;
|
||||
[key: string]: IWaterfallItem | undefined;
|
||||
}
|
||||
|
||||
interface IWaterfallGroup {
|
||||
|
@ -234,7 +234,7 @@ export function getWaterfall(
|
|||
{ trace, errorsPerTransaction }: TraceAPIResponse,
|
||||
entryTransactionId?: Transaction['transaction']['id']
|
||||
): IWaterfall {
|
||||
if (isEmpty(trace) || !entryTransactionId) {
|
||||
if (isEmpty(trace.items) || !entryTransactionId) {
|
||||
return {
|
||||
services: [],
|
||||
duration: 0,
|
||||
|
@ -246,7 +246,7 @@ export function getWaterfall(
|
|||
};
|
||||
}
|
||||
|
||||
const waterfallItems = trace.map(traceItem => {
|
||||
const waterfallItems = trace.items.map(traceItem => {
|
||||
const docType = traceItem.processor.event;
|
||||
switch (docType) {
|
||||
case 'span':
|
||||
|
|
|
@ -18,13 +18,15 @@ interface Props {
|
|||
transaction: Transaction;
|
||||
location: Location;
|
||||
waterfall: IWaterfall;
|
||||
exceedsMax: boolean;
|
||||
}
|
||||
|
||||
export function WaterfallContainer({
|
||||
location,
|
||||
urlParams,
|
||||
transaction,
|
||||
waterfall
|
||||
waterfall,
|
||||
exceedsMax
|
||||
}: Props) {
|
||||
const agentMarks = getAgentMarks(transaction);
|
||||
if (!waterfall) {
|
||||
|
@ -40,6 +42,7 @@ export function WaterfallContainer({
|
|||
serviceColors={waterfall.serviceColors}
|
||||
urlParams={urlParams}
|
||||
waterfall={waterfall}
|
||||
exceedsMax={exceedsMax}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -97,13 +97,15 @@ interface Props {
|
|||
urlParams: IUrlParams;
|
||||
location: Location;
|
||||
waterfall: IWaterfall;
|
||||
exceedsMax: boolean;
|
||||
}
|
||||
|
||||
export const Transaction: React.SFC<Props> = ({
|
||||
transaction,
|
||||
urlParams,
|
||||
location,
|
||||
waterfall
|
||||
waterfall,
|
||||
exceedsMax
|
||||
}) => {
|
||||
return (
|
||||
<EuiPanel paddingSize="m">
|
||||
|
@ -149,6 +151,7 @@ export const Transaction: React.SFC<Props> = ({
|
|||
location={location}
|
||||
urlParams={urlParams}
|
||||
waterfall={waterfall}
|
||||
exceedsMax={exceedsMax}
|
||||
/>
|
||||
</EuiPanel>
|
||||
);
|
||||
|
|
|
@ -31,7 +31,7 @@ export function TransactionDetails() {
|
|||
|
||||
const { data: transactionChartsData } = useTransactionCharts();
|
||||
|
||||
const { data: waterfall } = useWaterfall(urlParams);
|
||||
const { data: waterfall, exceedsMax } = useWaterfall(urlParams);
|
||||
const transaction = waterfall.getTransactionById(urlParams.transactionId);
|
||||
|
||||
const { transactionName } = urlParams;
|
||||
|
@ -81,6 +81,7 @@ export function TransactionDetails() {
|
|||
transaction={transaction}
|
||||
urlParams={urlParams}
|
||||
waterfall={waterfall}
|
||||
exceedsMax={exceedsMax}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -10,7 +10,11 @@ import { loadTrace } from '../services/rest/apm/traces';
|
|||
import { IUrlParams } from '../context/UrlParamsContext/types';
|
||||
import { useFetcher } from './useFetcher';
|
||||
|
||||
const INITIAL_DATA = { trace: [], errorsPerTransaction: {} };
|
||||
const INITIAL_DATA = {
|
||||
root: undefined,
|
||||
trace: { items: [], exceedsMax: false },
|
||||
errorsPerTransaction: {}
|
||||
};
|
||||
|
||||
export function useWaterfall(urlParams: IUrlParams) {
|
||||
const { traceId, start, end, transactionId } = urlParams;
|
||||
|
@ -25,5 +29,5 @@ export function useWaterfall(urlParams: IUrlParams) {
|
|||
transactionId
|
||||
]);
|
||||
|
||||
return { data: waterfall, status, error };
|
||||
return { data: waterfall, status, error, exceedsMax: data.trace.exceedsMax };
|
||||
}
|
||||
|
|
|
@ -7,7 +7,10 @@
|
|||
import { SearchParams } from 'elasticsearch';
|
||||
import {
|
||||
PROCESSOR_EVENT,
|
||||
TRACE_ID
|
||||
TRACE_ID,
|
||||
PARENT_ID,
|
||||
TRANSACTION_DURATION,
|
||||
SPAN_DURATION
|
||||
} from '../../../common/elasticsearch_fieldnames';
|
||||
import { Span } from '../../../typings/es_schemas/ui/Span';
|
||||
import { Transaction } from '../../../typings/es_schemas/ui/Transaction';
|
||||
|
@ -16,6 +19,7 @@ import { Setup } from '../helpers/setup_request';
|
|||
|
||||
export async function getTraceItems(traceId: string, setup: Setup) {
|
||||
const { start, end, client, config } = setup;
|
||||
const maxTraceItems = config.get<number>('xpack.apm.ui.maxTraceItems');
|
||||
|
||||
const params: SearchParams = {
|
||||
index: [
|
||||
|
@ -23,20 +27,31 @@ export async function getTraceItems(traceId: string, setup: Setup) {
|
|||
config.get('apm_oss.transactionIndices')
|
||||
],
|
||||
body: {
|
||||
size: 1000,
|
||||
size: maxTraceItems,
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
{ term: { [TRACE_ID]: traceId } },
|
||||
{ terms: { [PROCESSOR_EVENT]: ['span', 'transaction'] } },
|
||||
{ range: rangeFilter(start, end) }
|
||||
]
|
||||
],
|
||||
should: {
|
||||
exists: { field: PARENT_ID }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
sort: [
|
||||
{ _score: { order: 'asc' } },
|
||||
{ [TRANSACTION_DURATION]: { order: 'desc' } },
|
||||
{ [SPAN_DURATION]: { order: 'desc' } }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const resp = await client.search<Transaction | Span>(params);
|
||||
|
||||
return resp.hits.hits.map(hit => hit._source);
|
||||
return {
|
||||
items: resp.hits.hits.map(hit => hit._source),
|
||||
exceedsMax: resp.hits.total > maxTraceItems
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue