diff --git a/docs/settings/apm-settings.asciidoc b/docs/settings/apm-settings.asciidoc
index 615dc98c066b..e47326a1d206 100644
--- a/docs/settings/apm-settings.asciidoc
+++ b/docs/settings/apm-settings.asciidoc
@@ -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-*`.
diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker
index ce64d5fbf842..6e2cc28495e1 100755
--- a/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker
+++ b/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker
@@ -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
diff --git a/src/legacy/core_plugins/apm_oss/index.js b/src/legacy/core_plugins/apm_oss/index.js
index 6c0c6d0e5fe5..0c281ec939bb 100644
--- a/src/legacy/core_plugins/apm_oss/index.js
+++ b/src/legacy/core_plugins/apm_oss/index.js
@@ -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}`))));
}
});
diff --git a/x-pack/legacy/plugins/apm/index.ts b/x-pack/legacy/plugins/apm/index.ts
index aac946a36b45..d39df6084935 100644
--- a/x-pack/legacy/plugins/apm/index.ts
+++ b/x-pack/legacy/plugins/apm/index.ts
@@ -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
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Transaction/TransactionTabs.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Transaction/TransactionTabs.tsx
index d3852014c373..e5be12509e3c 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Transaction/TransactionTabs.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Transaction/TransactionTabs.tsx
@@ -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}
/>
) : (
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Transaction/WaterfallContainer/Waterfall/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Transaction/WaterfallContainer/Waterfall/index.tsx
index 1c89cbf9f6e6..d53b4077d975 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Transaction/WaterfallContainer/Waterfall/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Transaction/WaterfallContainer/Waterfall/index.tsx
@@ -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 {
@@ -126,12 +129,23 @@ export class Waterfall extends Component {
};
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 (
+ {exceedsMax ? (
+
+ ) : null}
{
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');
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Transaction/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Transaction/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts
index 80a8474c2930..7835d47468b7 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Transaction/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Transaction/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts
@@ -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':
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Transaction/WaterfallContainer/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Transaction/WaterfallContainer/index.tsx
index cd437325e57f..2f34cc86c5cf 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Transaction/WaterfallContainer/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Transaction/WaterfallContainer/index.tsx
@@ -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}
/>
);
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Transaction/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Transaction/index.tsx
index 659eca2c06f0..d9e024c5560e 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Transaction/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Transaction/index.tsx
@@ -97,13 +97,15 @@ interface Props {
urlParams: IUrlParams;
location: Location;
waterfall: IWaterfall;
+ exceedsMax: boolean;
}
export const Transaction: React.SFC = ({
transaction,
urlParams,
location,
- waterfall
+ waterfall,
+ exceedsMax
}) => {
return (
@@ -149,6 +151,7 @@ export const Transaction: React.SFC = ({
location={location}
urlParams={urlParams}
waterfall={waterfall}
+ exceedsMax={exceedsMax}
/>
);
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/index.tsx
index e08285ae9b96..f242690beab0 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/index.tsx
@@ -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}
/>
)}
diff --git a/x-pack/legacy/plugins/apm/public/hooks/useWaterfall.ts b/x-pack/legacy/plugins/apm/public/hooks/useWaterfall.ts
index 050041724a90..fd2ed152c79f 100644
--- a/x-pack/legacy/plugins/apm/public/hooks/useWaterfall.ts
+++ b/x-pack/legacy/plugins/apm/public/hooks/useWaterfall.ts
@@ -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 };
}
diff --git a/x-pack/legacy/plugins/apm/server/lib/traces/get_trace_items.ts b/x-pack/legacy/plugins/apm/server/lib/traces/get_trace_items.ts
index 11599d09c1d6..00eeefb4b4fc 100644
--- a/x-pack/legacy/plugins/apm/server/lib/traces/get_trace_items.ts
+++ b/x-pack/legacy/plugins/apm/server/lib/traces/get_trace_items.ts
@@ -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('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(params);
- return resp.hits.hits.map(hit => hit._source);
+ return {
+ items: resp.hits.hits.map(hit => hit._source),
+ exceedsMax: resp.hits.total > maxTraceItems
+ };
}