[APM] Use apmEventClient for querying APM event indices (#73449)

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Dario Gieselaar 2020-07-31 15:22:04 +02:00 committed by GitHub
parent dbb603f979
commit c66ea65ec1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
110 changed files with 1358 additions and 1588 deletions

View file

@ -8,4 +8,18 @@ export enum ProcessorEvent {
transaction = 'transaction',
error = 'error',
metric = 'metric',
span = 'span',
onboarding = 'onboarding',
sourcemap = 'sourcemap',
}
/**
* Processor events that are searchable in the UI via the query bar.
*
* Some client-sideroutes will define 1 or more processor events that
* will be used to fetch the dynamic index pattern for the query bar.
*/
export type UIProcessorEvent =
| ProcessorEvent.transaction
| ProcessorEvent.error
| ProcessorEvent.metric;

View file

@ -0,0 +1,16 @@
/*
* 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.
*/
export enum Projection {
services = 'services',
transactionGroups = 'transactionGroups',
traces = 'traces',
transactions = 'transactions',
metrics = 'metrics',
errorGroups = 'errorGroups',
serviceNodes = 'serviceNodes',
rumOverview = 'rumOverview',
}

View file

@ -1,64 +0,0 @@
/*
* 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 {
Setup,
SetupUIFilters,
SetupTimeRange,
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
} from '../../server/lib/helpers/setup_request';
import { SERVICE_NAME, PROCESSOR_EVENT } from '../elasticsearch_fieldnames';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { rangeFilter } from '../utils/range_filter';
export function getServicesProjection({
setup,
noEvents,
}: {
setup: Setup & SetupTimeRange & SetupUIFilters;
noEvents?: boolean;
}) {
const { start, end, uiFiltersES, indices } = setup;
return {
...(noEvents
? {}
: {
index: [
indices['apm_oss.metricsIndices'],
indices['apm_oss.errorIndices'],
indices['apm_oss.transactionIndices'],
],
}),
body: {
size: 0,
query: {
bool: {
filter: [
...(noEvents
? []
: [
{
terms: {
[PROCESSOR_EVENT]: ['transaction', 'error', 'metric'],
},
},
]),
{ range: rangeFilter(start, end) },
...uiFiltersES,
],
},
},
aggs: {
services: {
terms: {
field: SERVICE_NAME,
},
},
},
},
};
}

View file

@ -14,7 +14,7 @@ import {
import { i18n } from '@kbn/i18n';
import React, { useMemo } from 'react';
import { useTrackPageview } from '../../../../../observability/public';
import { PROJECTION } from '../../../../common/projections/typings';
import { Projection } from '../../../../common/projections';
import { useFetcher } from '../../../hooks/useFetcher';
import { useUrlParams } from '../../../hooks/useUrlParams';
import { callApmApi } from '../../../services/rest/createCallApmApi';
@ -79,7 +79,7 @@ function ErrorGroupOverview() {
params: {
serviceName,
},
projection: PROJECTION.ERROR_GROUPS,
projection: Projection.errorGroups,
};
return config;

View file

@ -13,7 +13,7 @@ import {
} from '@elastic/eui';
import { useTrackPageview } from '../../../../../observability/public';
import { LocalUIFilters } from '../../shared/LocalUIFilters';
import { PROJECTION } from '../../../../common/projections/typings';
import { Projection } from '../../../../common/projections';
import { RumDashboard } from './RumDashboard';
import { ServiceNameFilter } from '../../shared/LocalUIFilters/ServiceNameFilter';
import { useUrlParams } from '../../../hooks/useUrlParams';
@ -28,7 +28,7 @@ export function RumOverview() {
const localUIFiltersConfig = useMemo(() => {
const config: React.ComponentProps<typeof LocalUIFilters> = {
filterNames: ['transactionUrl', 'location', 'device', 'os', 'browser'],
projection: PROJECTION.RUM_OVERVIEW,
projection: Projection.rumOverview,
};
return config;

View file

@ -16,7 +16,7 @@ import { useServiceMetricCharts } from '../../../hooks/useServiceMetricCharts';
import { MetricsChart } from '../../shared/charts/MetricsChart';
import { useUrlParams } from '../../../hooks/useUrlParams';
import { ChartsSyncContextProvider } from '../../../context/ChartsSyncContext';
import { PROJECTION } from '../../../../common/projections/typings';
import { Projection } from '../../../../common/projections';
import { LocalUIFilters } from '../../shared/LocalUIFilters';
interface ServiceMetricsProps {
@ -36,7 +36,7 @@ export function ServiceMetrics({ agentName }: ServiceMetricsProps) {
serviceName,
serviceNodeName,
},
projection: PROJECTION.METRICS,
projection: Projection.metrics,
showCount: false,
}),
[serviceName, serviceNodeName]

View file

@ -15,7 +15,7 @@ import { i18n } from '@kbn/i18n';
import styled from 'styled-components';
import { UNIDENTIFIED_SERVICE_NODES_LABEL } from '../../../../common/i18n';
import { SERVICE_NODE_NAME_MISSING } from '../../../../common/service_nodes';
import { PROJECTION } from '../../../../common/projections/typings';
import { Projection } from '../../../../common/projections';
import { LocalUIFilters } from '../../shared/LocalUIFilters';
import { useUrlParams } from '../../../hooks/useUrlParams';
import { ManagedTable, ITableColumn } from '../../shared/ManagedTable';
@ -46,7 +46,7 @@ function ServiceNodeOverview() {
params: {
serviceName,
},
projection: PROJECTION.SERVICE_NODES,
projection: Projection.serviceNodes,
}),
[serviceName]
);

View file

@ -15,7 +15,7 @@ import { NoServicesMessage } from './NoServicesMessage';
import { ServiceList } from './ServiceList';
import { useUrlParams } from '../../../hooks/useUrlParams';
import { useTrackPageview } from '../../../../../observability/public';
import { PROJECTION } from '../../../../common/projections/typings';
import { Projection } from '../../../../common/projections';
import { LocalUIFilters } from '../../shared/LocalUIFilters';
import { useApmPluginContext } from '../../../hooks/useApmPluginContext';
@ -88,7 +88,7 @@ export function ServiceOverview() {
const localFiltersConfig: React.ComponentProps<typeof LocalUIFilters> = useMemo(
() => ({
filterNames: ['host', 'agentName'],
projection: PROJECTION.SERVICES,
projection: Projection.services,
}),
[]
);

View file

@ -11,7 +11,7 @@ import { TraceList } from './TraceList';
import { useUrlParams } from '../../../hooks/useUrlParams';
import { useTrackPageview } from '../../../../../observability/public';
import { LocalUIFilters } from '../../shared/LocalUIFilters';
import { PROJECTION } from '../../../../common/projections/typings';
import { Projection } from '../../../../common/projections';
import { APIReturnType } from '../../../services/rest/createCallApmApi';
type TracesAPIResponse = APIReturnType<'/api/apm/traces'>;
@ -48,7 +48,7 @@ export function TraceOverview() {
const localUIFiltersConfig = useMemo(() => {
const config: React.ComponentProps<typeof LocalUIFilters> = {
filterNames: ['transactionResult', 'host', 'containerId', 'podName'],
projection: PROJECTION.TRACES,
projection: Projection.traces,
};
return config;

View file

@ -27,7 +27,7 @@ import { FETCH_STATUS } from '../../../hooks/useFetcher';
import { TransactionBreakdown } from '../../shared/TransactionBreakdown';
import { ChartsSyncContextProvider } from '../../../context/ChartsSyncContext';
import { useTrackPageview } from '../../../../../observability/public';
import { PROJECTION } from '../../../../common/projections/typings';
import { Projection } from '../../../../common/projections';
import { LocalUIFilters } from '../../shared/LocalUIFilters';
import { HeightRetainer } from '../../shared/HeightRetainer';
import { ErroneousTransactionsRateChart } from '../../shared/charts/ErroneousTransactionsRateChart';
@ -52,7 +52,7 @@ export function TransactionDetails() {
const localUIFiltersConfig = useMemo(() => {
const config: React.ComponentProps<typeof LocalUIFilters> = {
filterNames: ['transactionResult', 'serviceVersion'],
projection: PROJECTION.TRANSACTIONS,
projection: Projection.transactions,
params: {
transactionName,
transactionType,

View file

@ -35,7 +35,7 @@ import { ChartsSyncContextProvider } from '../../../context/ChartsSyncContext';
import { useTrackPageview } from '../../../../../observability/public';
import { fromQuery, toQuery } from '../../shared/Links/url_helpers';
import { LocalUIFilters } from '../../shared/LocalUIFilters';
import { PROJECTION } from '../../../../common/projections/typings';
import { Projection } from '../../../../common/projections';
import { useUrlParams } from '../../../hooks/useUrlParams';
import { useServiceTransactionTypes } from '../../../hooks/useServiceTransactionTypes';
import { TransactionTypeFilter } from '../../shared/LocalUIFilters/TransactionTypeFilter';
@ -103,7 +103,7 @@ export function TransactionOverview() {
serviceName,
transactionType,
},
projection: PROJECTION.TRANSACTION_GROUPS,
projection: Projection.transactionGroups,
}),
[serviceName, transactionType]
);

View file

@ -9,7 +9,7 @@ import { uniqueId, startsWith } from 'lodash';
import styled from 'styled-components';
import { i18n } from '@kbn/i18n';
import { fromQuery, toQuery } from '../Links/url_helpers';
// @ts-ignore
// @ts-expect-error
import { Typeahead } from './Typeahead';
import { getBoolFilter } from './get_bool_filter';
import { useLocation } from '../../../hooks/useLocation';

View file

@ -17,10 +17,10 @@ import styled from 'styled-components';
import { LocalUIFilterName } from '../../../../server/lib/ui_filters/local_ui_filters/config';
import { Filter } from './Filter';
import { useLocalUIFilters } from '../../../hooks/useLocalUIFilters';
import { PROJECTION } from '../../../../common/projections/typings';
import { Projection } from '../../../../common/projections';
interface Props {
projection: PROJECTION;
projection: Projection;
filterNames: LocalUIFilterName[];
params?: Record<string, string | number | boolean | undefined>;
showCount?: boolean;

View file

@ -7,7 +7,7 @@ import { EuiTitle } from '@elastic/eui';
import React from 'react';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { GenericMetricsChart } from '../../../../../server/lib/metrics/transform_metrics_chart';
// @ts-ignore
// @ts-expect-error
import CustomPlot from '../CustomPlot';
import {
asDecimal,

View file

@ -7,10 +7,13 @@
import { compact, pickBy } from 'lodash';
import datemath from '@elastic/datemath';
import { IUrlParams } from './types';
import { ProcessorEvent } from '../../../common/processor_event';
import {
ProcessorEvent,
UIProcessorEvent,
} from '../../../common/processor_event';
interface PathParams {
processorEvent?: ProcessorEvent;
processorEvent?: UIProcessorEvent;
serviceName?: string;
errorGroupId?: string;
serviceNodeName?: string;

View file

@ -6,7 +6,7 @@
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { LocalUIFilterName } from '../../../server/lib/ui_filters/local_ui_filters/config';
import { ProcessorEvent } from '../../../common/processor_event';
import { UIProcessorEvent } from '../../../common/processor_event';
export type IUrlParams = {
detailTab?: string;
@ -32,6 +32,6 @@ export type IUrlParams = {
pageSize?: number;
serviceNodeName?: string;
searchTerm?: string;
processorEvent?: ProcessorEvent;
processorEvent?: UIProcessorEvent;
traceIdLink?: string;
} & Partial<Record<LocalUIFilterName, string>>;

View file

@ -5,10 +5,10 @@
*/
import { useFetcher } from './useFetcher';
import { ProcessorEvent } from '../../common/processor_event';
import { UIProcessorEvent } from '../../common/processor_event';
export function useDynamicIndexPattern(
processorEvent: ProcessorEvent | undefined
processorEvent: UIProcessorEvent | undefined
) {
const { data, status } = useFetcher(
(callApmApi) => {

View file

@ -17,7 +17,7 @@ import {
import { history } from '../utils/history';
import { toQuery, fromQuery } from '../components/shared/Links/url_helpers';
import { removeUndefinedProps } from '../context/UrlParamsContext/helpers';
import { PROJECTION } from '../../common/projections/typings';
import { Projection } from '../../common/projections';
import { pickKeys } from '../../common/utils/pick_keys';
import { useCallApi } from './useCallApi';
@ -35,7 +35,7 @@ export function useLocalUIFilters({
filterNames,
params,
}: {
projection: PROJECTION;
projection: Projection;
filterNames: LocalUIFilterName[];
params?: Record<string, string | number | boolean | undefined>;
}) {

View file

@ -99,10 +99,9 @@ export function expectTextsInDocument(output: any, texts: string[]) {
}
interface MockSetup {
dynamicIndexPattern: any;
start: number;
end: number;
client: any;
apmEventClient: any;
internalClient: any;
config: APMConfig;
uiFiltersES: ESFilter[];
@ -148,7 +147,7 @@ export async function inspectSearchParams(
const mockSetup = {
start: 1528113600000,
end: 1528977600000,
client: { search: spy } as any,
apmEventClient: { search: spy } as any,
internalClient: { search: spy } as any,
config: new Proxy({}, { get: () => 'myIndex' }) as APMConfig,
uiFiltersES: [{ term: { 'my.custom.ui.filter': 'foo-bar' } }],

View file

@ -2,6 +2,13 @@
exports[`getAllEnvironments fetches all environments 1`] = `
Object {
"apm": Object {
"events": Array [
"transaction",
"error",
"metric",
],
},
"body": Object {
"aggs": Object {
"environments": Object {
@ -15,15 +22,6 @@ Object {
"query": Object {
"bool": Object {
"filter": Array [
Object {
"terms": Object {
"processor.event": Array [
"transaction",
"error",
"metric",
],
},
},
Object {
"term": Object {
"service.name": "test",
@ -34,16 +32,18 @@ Object {
},
"size": 0,
},
"index": Array [
"myIndex",
"myIndex",
"myIndex",
],
}
`;
exports[`getAllEnvironments fetches all environments with includeMissing 1`] = `
Object {
"apm": Object {
"events": Array [
"transaction",
"error",
"metric",
],
},
"body": Object {
"aggs": Object {
"environments": Object {
@ -57,15 +57,6 @@ Object {
"query": Object {
"bool": Object {
"filter": Array [
Object {
"terms": Object {
"processor.event": Array [
"transaction",
"error",
"metric",
],
},
},
Object {
"term": Object {
"service.name": "test",
@ -76,10 +67,5 @@ Object {
},
"size": 0,
},
"index": Array [
"myIndex",
"myIndex",
"myIndex",
],
}
`;

View file

@ -4,9 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { ProcessorEvent } from '../../../common/processor_event';
import { Setup } from '../helpers/setup_request';
import {
PROCESSOR_EVENT,
SERVICE_NAME,
SERVICE_ENVIRONMENT,
} from '../../../common/elasticsearch_fieldnames';
@ -21,7 +21,7 @@ export async function getAllEnvironments({
setup: Setup;
includeMissing?: boolean;
}) {
const { client, indices } = setup;
const { apmEventClient } = setup;
// omit filter for service.name if "All" option is selected
const serviceNameFilter = serviceName
@ -29,21 +29,18 @@ export async function getAllEnvironments({
: [];
const params = {
index: [
indices['apm_oss.metricsIndices'],
indices['apm_oss.errorIndices'],
indices['apm_oss.transactionIndices'],
],
apm: {
events: [
ProcessorEvent.transaction,
ProcessorEvent.error,
ProcessorEvent.metric,
],
},
body: {
size: 0,
query: {
bool: {
filter: [
{
terms: { [PROCESSOR_EVENT]: ['transaction', 'error', 'metric'] },
},
...serviceNameFilter,
],
filter: [...serviceNameFilter],
},
},
aggs: {
@ -58,7 +55,7 @@ export async function getAllEnvironments({
},
};
const resp = await client.search(params);
const resp = await apmEventClient.search(params);
const environments =
resp.aggregations?.environments.buckets.map(
(bucket) => bucket.key as string

View file

@ -2,6 +2,11 @@
exports[`error queries fetches a single error group 1`] = `
Object {
"apm": Object {
"events": Array [
"error",
],
},
"body": Object {
"query": Object {
"bool": Object {
@ -11,11 +16,6 @@ Object {
"service.name": "serviceName",
},
},
Object {
"term": Object {
"processor.event": "error",
},
},
Object {
"term": Object {
"error.grouping_key": "groupId",
@ -57,12 +57,16 @@ Object {
},
],
},
"index": "myIndex",
}
`;
exports[`error queries fetches multiple error groups 1`] = `
Object {
"apm": Object {
"events": Array [
"error",
],
},
"body": Object {
"aggs": Object {
"error_groups": Object {
@ -104,11 +108,6 @@ Object {
"service.name": "serviceName",
},
},
Object {
"term": Object {
"processor.event": "error",
},
},
Object {
"range": Object {
"@timestamp": Object {
@ -128,12 +127,16 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;
exports[`error queries fetches multiple error groups when sortField = latestOccurrenceAt 1`] = `
Object {
"apm": Object {
"events": Array [
"error",
],
},
"body": Object {
"aggs": Object {
"error_groups": Object {
@ -180,11 +183,6 @@ Object {
"service.name": "serviceName",
},
},
Object {
"term": Object {
"processor.event": "error",
},
},
Object {
"range": Object {
"@timestamp": Object {
@ -204,6 +202,5 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;

View file

@ -2,6 +2,11 @@
exports[`error distribution queries fetches an error distribution 1`] = `
Object {
"apm": Object {
"events": Array [
"error",
],
},
"body": Object {
"aggs": Object {
"distribution": Object {
@ -19,11 +24,6 @@ Object {
"query": Object {
"bool": Object {
"filter": Array [
Object {
"term": Object {
"processor.event": "error",
},
},
Object {
"term": Object {
"service.name": "serviceName",
@ -48,12 +48,16 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;
exports[`error distribution queries fetches an error distribution with a group id 1`] = `
Object {
"apm": Object {
"events": Array [
"error",
],
},
"body": Object {
"aggs": Object {
"distribution": Object {
@ -71,11 +75,6 @@ Object {
"query": Object {
"bool": Object {
"filter": Array [
Object {
"term": Object {
"processor.event": "error",
},
},
Object {
"term": Object {
"service.name": "serviceName",
@ -105,6 +104,5 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;

View file

@ -4,6 +4,11 @@ exports[`timeseriesFetcher should make the correct query 1`] = `
Array [
Array [
Object {
"apm": Object {
"events": Array [
"error",
],
},
"body": Object {
"aggs": Object {
"distribution": Object {
@ -21,11 +26,6 @@ Array [
"query": Object {
"bool": Object {
"filter": Array [
Object {
"term": Object {
"processor.event": "error",
},
},
Object {
"term": Object {
"service.name": "myServiceName",
@ -50,7 +50,6 @@ Array [
},
"size": 0,
},
"index": "apm-*",
},
],
]

View file

@ -4,9 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { PROCESSOR_EVENT } from '../../../../../common/elasticsearch_fieldnames';
import { getBuckets } from '../get_buckets';
import { APMConfig } from '../../../..';
import { ProcessorEvent } from '../../../../../common/processor_event';
describe('timeseriesFetcher', () => {
let clientSpy: jest.Mock;
@ -29,7 +29,7 @@ describe('timeseriesFetcher', () => {
setup: {
start: 1528113600000,
end: 1528977600000,
client: {
apmEventClient: {
search: clientSpy,
} as any,
internalClient: {
@ -66,8 +66,6 @@ describe('timeseriesFetcher', () => {
it('should limit query results to error documents', () => {
const query = clientSpy.mock.calls[0][0];
expect(query.body.query.bool.filter).toEqual(
expect.arrayContaining([{ term: { [PROCESSOR_EVENT]: 'error' } }])
);
expect(query.apm.events).toEqual([ProcessorEvent.error]);
});
});

View file

@ -4,10 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { ProcessorEvent } from '../../../../common/processor_event';
import { ESFilter } from '../../../../typings/elasticsearch';
import {
ERROR_GROUP_ID,
PROCESSOR_EVENT,
SERVICE_NAME,
} from '../../../../common/elasticsearch_fieldnames';
import { rangeFilter } from '../../../../common/utils/range_filter';
@ -28,9 +28,8 @@ export async function getBuckets({
bucketSize: number;
setup: Setup & SetupTimeRange & SetupUIFilters;
}) {
const { start, end, uiFiltersES, client, indices } = setup;
const { start, end, uiFiltersES, apmEventClient } = setup;
const filter: ESFilter[] = [
{ term: { [PROCESSOR_EVENT]: 'error' } },
{ term: { [SERVICE_NAME]: serviceName } },
{ range: rangeFilter(start, end) },
...uiFiltersES,
@ -41,7 +40,9 @@ export async function getBuckets({
}
const params = {
index: indices['apm_oss.errorIndices'],
apm: {
events: [ProcessorEvent.error],
},
body: {
size: 0,
query: {
@ -65,7 +66,7 @@ export async function getBuckets({
},
};
const resp = await client.search(params);
const resp = await apmEventClient.search(params);
const buckets = (resp.aggregations?.distribution.buckets || []).map(
(bucket) => ({

View file

@ -4,14 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { ProcessorEvent } from '../../../common/processor_event';
import {
ERROR_GROUP_ID,
PROCESSOR_EVENT,
SERVICE_NAME,
TRANSACTION_SAMPLED,
} from '../../../common/elasticsearch_fieldnames';
import { PromiseReturnType } from '../../../typings/common';
import { APMError } from '../../../typings/es_schemas/ui/apm_error';
import { rangeFilter } from '../../../common/utils/range_filter';
import {
Setup,
@ -32,17 +31,18 @@ export async function getErrorGroup({
groupId: string;
setup: Setup & SetupTimeRange & SetupUIFilters;
}) {
const { start, end, uiFiltersES, client, indices } = setup;
const { start, end, uiFiltersES, apmEventClient } = setup;
const params = {
index: indices['apm_oss.errorIndices'],
apm: {
events: [ProcessorEvent.error as const],
},
body: {
size: 1,
query: {
bool: {
filter: [
{ term: { [SERVICE_NAME]: serviceName } },
{ term: { [PROCESSOR_EVENT]: 'error' } },
{ term: { [ERROR_GROUP_ID]: groupId } },
{ range: rangeFilter(start, end) },
...uiFiltersES,
@ -57,7 +57,7 @@ export async function getErrorGroup({
},
};
const resp = await client.search<APMError>(params);
const resp = await apmEventClient.search(params);
const error = resp.hits.hits[0]?._source;
const transactionId = error?.transaction?.id;
const traceId = error?.trace?.id;

View file

@ -13,14 +13,13 @@ import {
ERROR_LOG_MESSAGE,
} from '../../../common/elasticsearch_fieldnames';
import { PromiseReturnType } from '../../../typings/common';
import { APMError } from '../../../typings/es_schemas/ui/apm_error';
import {
Setup,
SetupTimeRange,
SetupUIFilters,
} from '../helpers/setup_request';
import { getErrorGroupsProjection } from '../../../common/projections/errors';
import { mergeProjection } from '../../../common/projections/util/merge_projection';
import { getErrorGroupsProjection } from '../../projections/errors';
import { mergeProjection } from '../../projections/util/merge_projection';
import { SortOptions } from '../../../typings/elasticsearch/aggregations';
export type ErrorGroupListAPIResponse = PromiseReturnType<
@ -38,7 +37,7 @@ export async function getErrorGroups({
sortDirection?: 'asc' | 'desc';
setup: Setup & SetupTimeRange & SetupUIFilters;
}) {
const { client } = setup;
const { apmEventClient } = setup;
// sort buckets by last occurrence of error
const sortByLatestOccurrence = sortField === 'latestOccurrenceAt';
@ -92,23 +91,7 @@ export async function getErrorGroups({
},
});
interface SampleError {
'@timestamp': APMError['@timestamp'];
error: {
log?: {
message: string;
};
exception?: Array<{
handled?: boolean;
message?: string;
type?: string;
}>;
culprit: APMError['error']['culprit'];
grouping_key: APMError['error']['grouping_key'];
};
}
const resp = await client.search<SampleError, typeof params>(params);
const resp = await apmEventClient.search(params);
// aggregations can be undefined when no matching indices are found.
// this is an exception rather than the rule so the ES type does not account for this.

View file

@ -0,0 +1,72 @@
/*
* 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.
*/
/* eslint-disable no-console */
import chalk from 'chalk';
import {
LegacyAPICaller,
KibanaRequest,
} from '../../../../../../../src/core/server';
function formatObj(obj: Record<string, any>) {
return JSON.stringify(obj, null, 2);
}
export async function callClientWithDebug({
apiCaller,
operationName,
params,
debug,
request,
}: {
apiCaller: LegacyAPICaller;
operationName: string;
params: Record<string, any>;
debug: boolean;
request: KibanaRequest;
}) {
const startTime = process.hrtime();
let res: any;
let esError = null;
try {
res = apiCaller(operationName, params);
} catch (e) {
// catch error and throw after outputting debug info
esError = e;
}
if (debug) {
const highlightColor = esError ? 'bgRed' : 'inverse';
const diff = process.hrtime(startTime);
const duration = `${Math.round(diff[0] * 1000 + diff[1] / 1e6)}ms`;
const routeInfo = `${request.route.method.toUpperCase()} ${
request.route.path
}`;
console.log(
chalk.bold[highlightColor](`=== Debug: ${routeInfo} (${duration}) ===`)
);
if (operationName === 'search') {
console.log(`GET ${params.index}/_${operationName}`);
console.log(formatObj(params.body));
} else {
console.log(chalk.bold('ES operation:'), operationName);
console.log(chalk.bold('ES query:'));
console.log(formatObj(params));
}
console.log(`\n`);
}
if (esError) {
throw esError;
}
return res;
}

View file

@ -0,0 +1,31 @@
/*
* 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 { cloneDeep } from 'lodash';
import { OBSERVER_VERSION_MAJOR } from '../../../../../common/elasticsearch_fieldnames';
import {
ESSearchRequest,
ESFilter,
} from '../../../../../typings/elasticsearch';
/*
Adds a range query to the ES request to exclude legacy data
*/
export function addFilterToExcludeLegacyData(
params: ESSearchRequest & {
body: { query: { bool: { filter: ESFilter[] } } };
}
) {
const nextParams = cloneDeep(params);
// add filter for omitting pre-7.x data
nextParams.body.query.bool.filter.push({
range: { [OBSERVER_VERSION_MAJOR]: { gte: 7 } },
});
return nextParams;
}

View file

@ -0,0 +1,91 @@
/*
* 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 { ValuesType } from 'utility-types';
import { APMBaseDoc } from '../../../../../typings/es_schemas/raw/apm_base_doc';
import { APMError } from '../../../../../typings/es_schemas/ui/apm_error';
import { KibanaRequest } from '../../../../../../../../src/core/server';
import { ProcessorEvent } from '../../../../../common/processor_event';
import {
ESSearchRequest,
ESSearchResponse,
} from '../../../../../typings/elasticsearch';
import { ApmIndicesConfig } from '../../../settings/apm_indices/get_apm_indices';
import { APMRequestHandlerContext } from '../../../../routes/typings';
import { addFilterToExcludeLegacyData } from './add_filter_to_exclude_legacy_data';
import { callClientWithDebug } from '../call_client_with_debug';
import { Transaction } from '../../../../../typings/es_schemas/ui/transaction';
import { Span } from '../../../../../typings/es_schemas/ui/span';
import { unpackProcessorEvents } from './unpack_processor_events';
export type APMEventESSearchRequest = Omit<ESSearchRequest, 'index'> & {
apm: {
events: ProcessorEvent[];
};
};
type TypeOfProcessorEvent<T extends ProcessorEvent> = {
[ProcessorEvent.error]: APMError;
[ProcessorEvent.transaction]: Transaction;
[ProcessorEvent.span]: Span;
[ProcessorEvent.metric]: APMBaseDoc;
[ProcessorEvent.onboarding]: unknown;
[ProcessorEvent.sourcemap]: unknown;
}[T];
type ESSearchRequestOf<TParams extends APMEventESSearchRequest> = Omit<
TParams,
'apm'
> & { index: string[] | string };
type TypedSearchResponse<
TParams extends APMEventESSearchRequest
> = ESSearchResponse<
TypeOfProcessorEvent<ValuesType<TParams['apm']['events']>>,
ESSearchRequestOf<TParams>
>;
export type APMEventClient = ReturnType<typeof createApmEventClient>;
export function createApmEventClient({
context,
request,
indices,
options: { includeFrozen } = { includeFrozen: false },
}: {
context: APMRequestHandlerContext;
request: KibanaRequest;
indices: ApmIndicesConfig;
options: {
includeFrozen: boolean;
};
}) {
const client = context.core.elasticsearch.legacy.client;
return {
search<TParams extends APMEventESSearchRequest>(
params: TParams,
{ includeLegacyData } = { includeLegacyData: false }
): Promise<TypedSearchResponse<TParams>> {
const withProcessorEventFilter = unpackProcessorEvents(params, indices);
const withPossibleLegacyDataFilter = !includeLegacyData
? addFilterToExcludeLegacyData(withProcessorEventFilter)
: withProcessorEventFilter;
return callClientWithDebug({
apiCaller: client.callAsCurrentUser,
operationName: 'search',
params: {
...withPossibleLegacyDataFilter,
ignore_throttled: !includeFrozen,
},
request,
debug: context.params.query._debug,
});
},
};
}

View file

@ -0,0 +1,61 @@
/*
* 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 { uniq, defaultsDeep, cloneDeep } from 'lodash';
import { PROCESSOR_EVENT } from '../../../../../common/elasticsearch_fieldnames';
import { ProcessorEvent } from '../../../../../common/processor_event';
import {
ESSearchRequest,
ESFilter,
} from '../../../../../typings/elasticsearch';
import { APMEventESSearchRequest } from '.';
import {
ApmIndicesConfig,
ApmIndicesName,
} from '../../../settings/apm_indices/get_apm_indices';
export const processorEventIndexMap: Record<ProcessorEvent, ApmIndicesName> = {
[ProcessorEvent.transaction]: 'apm_oss.transactionIndices',
[ProcessorEvent.span]: 'apm_oss.spanIndices',
[ProcessorEvent.metric]: 'apm_oss.metricsIndices',
[ProcessorEvent.error]: 'apm_oss.errorIndices',
[ProcessorEvent.sourcemap]: 'apm_oss.sourcemapIndices',
[ProcessorEvent.onboarding]: 'apm_oss.onboardingIndices',
};
export function unpackProcessorEvents(
request: APMEventESSearchRequest,
indices: ApmIndicesConfig
) {
const { apm, ...params } = request;
const index = uniq(
apm.events.map((event) => indices[processorEventIndexMap[event]])
);
const withFilterForProcessorEvent: ESSearchRequest & {
body: { query: { bool: { filter: ESFilter[] } } };
} = defaultsDeep(cloneDeep(params), {
body: {
query: {
bool: {
filter: [],
},
},
},
});
withFilterForProcessorEvent.body.query.bool.filter.push({
terms: {
[PROCESSOR_EVENT]: apm.events,
},
});
return {
index,
...withFilterForProcessorEvent,
};
}

View file

@ -0,0 +1,66 @@
/*
* 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 {
IndexDocumentParams,
IndicesCreateParams,
DeleteDocumentResponse,
DeleteDocumentParams,
} from 'elasticsearch';
import { KibanaRequest } from 'src/core/server';
import { APMRequestHandlerContext } from '../../../../routes/typings';
import {
ESSearchResponse,
ESSearchRequest,
} from '../../../../../typings/elasticsearch';
import { callClientWithDebug } from '../call_client_with_debug';
// `type` was deprecated in 7.0
export type APMIndexDocumentParams<T> = Omit<IndexDocumentParams<T>, 'type'>;
export type APMInternalClient = ReturnType<typeof createInternalESClient>;
export function createInternalESClient({
context,
request,
}: {
context: APMRequestHandlerContext;
request: KibanaRequest;
}) {
const { callAsInternalUser } = context.core.elasticsearch.legacy.client;
const callEs = (operationName: string, params: Record<string, any>) => {
return callClientWithDebug({
apiCaller: callAsInternalUser,
operationName,
params,
request,
debug: context.params.query._debug,
});
};
return {
search: async <
TDocument = unknown,
TSearchRequest extends ESSearchRequest = ESSearchRequest
>(
params: TSearchRequest
): Promise<ESSearchResponse<TDocument, TSearchRequest>> => {
return callEs('search', params);
},
index: <Body>(params: APMIndexDocumentParams<Body>) => {
return callEs('index', params);
},
delete: (
params: Omit<DeleteDocumentParams, 'type'>
): Promise<DeleteDocumentResponse> => {
return callEs('delete', params);
},
indicesCreate: (params: IndicesCreateParams) => {
return callEs('indices.create', params);
},
};
}

View file

@ -1,48 +0,0 @@
/*
* 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 { isApmIndex } from './es_client';
describe('isApmIndex', () => {
const apmIndices = [
'apm-*-metric-*',
'apm-*-onboarding-*',
'apm-*-span-*',
'apm-*-transaction-*',
'apm-*-error-*',
];
describe('when indexParam is a string', () => {
it('should return true if it matches any of the items in apmIndices', () => {
const indexParam = 'apm-*-transaction-*';
expect(isApmIndex(apmIndices, indexParam)).toBe(true);
});
it('should return false if it does not match any of the items in `apmIndices`', () => {
const indexParam = '.ml-anomalies-*';
expect(isApmIndex(apmIndices, indexParam)).toBe(false);
});
});
describe('when indexParam is an array', () => {
it('should return true if all values in `indexParam` matches values in `apmIndices`', () => {
const indexParam = ['apm-*-transaction-*', 'apm-*-span-*'];
expect(isApmIndex(apmIndices, indexParam)).toBe(true);
});
it("should return false if some of the values don't match with `apmIndices`", () => {
const indexParam = ['apm-*-transaction-*', '.ml-anomalies-*'];
expect(isApmIndex(apmIndices, indexParam)).toBe(false);
});
});
describe('when indexParam is neither a string or an array', () => {
it('should return false', () => {
[true, false, undefined].forEach((indexParam) => {
expect(isApmIndex(apmIndices, indexParam)).toBe(false);
});
});
});
});

View file

@ -1,228 +0,0 @@
/*
* 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.
*/
/* eslint-disable no-console */
import {
IndexDocumentParams,
SearchParams,
IndicesCreateParams,
DeleteDocumentResponse,
DeleteDocumentParams,
} from 'elasticsearch';
import { cloneDeep, isString, merge } from 'lodash';
import { KibanaRequest } from 'src/core/server';
import chalk from 'chalk';
import {
ESSearchRequest,
ESSearchResponse,
} from '../../../typings/elasticsearch';
import { OBSERVER_VERSION_MAJOR } from '../../../common/elasticsearch_fieldnames';
import { pickKeys } from '../../../common/utils/pick_keys';
import { APMRequestHandlerContext } from '../../routes/typings';
import { ApmIndicesConfig } from '../settings/apm_indices/get_apm_indices';
// `type` was deprecated in 7.0
export type APMIndexDocumentParams<T> = Omit<IndexDocumentParams<T>, 'type'>;
export interface IndexPrivileges {
has_all_requested: boolean;
index: Record<string, { read: boolean }>;
}
interface IndexPrivilegesParams {
index: Array<{
names: string[] | string;
privileges: string[];
}>;
}
export function isApmIndex(
apmIndices: string[],
indexParam: SearchParams['index']
) {
if (isString(indexParam)) {
return apmIndices.includes(indexParam);
} else if (Array.isArray(indexParam)) {
// return false if at least one of the indices is not an APM index
return indexParam.every((index) => apmIndices.includes(index));
}
return false;
}
function addFilterForLegacyData(
apmIndices: string[],
params: ESSearchRequest,
{ includeLegacyData = false } = {}
): SearchParams {
// search across all data (including data)
if (includeLegacyData || !isApmIndex(apmIndices, params.index)) {
return params;
}
const nextParams = merge(
{
body: {
query: {
bool: {
filter: [],
},
},
},
},
cloneDeep(params)
);
// add filter for omitting pre-7.x data
nextParams.body.query.bool.filter.push({
range: { [OBSERVER_VERSION_MAJOR]: { gte: 7 } },
});
return nextParams;
}
// add additional params for search (aka: read) requests
function getParamsForSearchRequest({
context,
params,
indices,
includeFrozen,
includeLegacyData,
}: {
context: APMRequestHandlerContext;
params: ESSearchRequest;
indices: ApmIndicesConfig;
includeFrozen: boolean;
includeLegacyData?: boolean;
}) {
// Get indices for legacy data filter (only those which apply)
const apmIndices = Object.values(
pickKeys(
indices,
'apm_oss.sourcemapIndices',
'apm_oss.errorIndices',
'apm_oss.onboardingIndices',
'apm_oss.spanIndices',
'apm_oss.transactionIndices',
'apm_oss.metricsIndices'
)
);
return {
...addFilterForLegacyData(apmIndices, params, { includeLegacyData }), // filter out pre-7.0 data
ignore_throttled: !includeFrozen, // whether to query frozen indices or not
};
}
interface APMOptions {
includeLegacyData: boolean;
}
interface ClientCreateOptions {
clientAsInternalUser?: boolean;
indices: ApmIndicesConfig;
includeFrozen: boolean;
}
export type ESClient = ReturnType<typeof getESClient>;
function formatObj(obj: Record<string, any>) {
return JSON.stringify(obj, null, 2);
}
export function getESClient(
context: APMRequestHandlerContext,
request: KibanaRequest,
{ clientAsInternalUser = false, indices, includeFrozen }: ClientCreateOptions
) {
const {
callAsCurrentUser,
callAsInternalUser,
} = context.core.elasticsearch.legacy.client;
async function callEs(operationName: string, params: Record<string, any>) {
const startTime = process.hrtime();
let res: any;
let esError = null;
try {
res = clientAsInternalUser
? await callAsInternalUser(operationName, params)
: await callAsCurrentUser(operationName, params);
} catch (e) {
// catch error and throw after outputting debug info
esError = e;
}
if (context.params.query._debug) {
const highlightColor = esError ? 'bgRed' : 'inverse';
const diff = process.hrtime(startTime);
const duration = `${Math.round(diff[0] * 1000 + diff[1] / 1e6)}ms`;
const routeInfo = `${request.route.method.toUpperCase()} ${
request.route.path
}`;
console.log(
chalk.bold[highlightColor](`=== Debug: ${routeInfo} (${duration}) ===`)
);
if (operationName === 'search') {
console.log(`GET ${params.index}/_${operationName}`);
console.log(formatObj(params.body));
} else {
console.log(chalk.bold('ES operation:'), operationName);
console.log(chalk.bold('ES query:'));
console.log(formatObj(params));
}
console.log(`\n`);
}
if (esError) {
throw esError;
}
return res;
}
return {
search: async <
TDocument = unknown,
TSearchRequest extends ESSearchRequest = {}
>(
params: TSearchRequest,
apmOptions?: APMOptions
): Promise<ESSearchResponse<TDocument, TSearchRequest>> => {
const nextParams = await getParamsForSearchRequest({
context,
params,
indices,
includeFrozen,
...apmOptions,
});
return callEs('search', nextParams);
},
index: <Body>(params: APMIndexDocumentParams<Body>) => {
return callEs('index', params);
},
delete: (
params: Omit<DeleteDocumentParams, 'type'>
): Promise<DeleteDocumentResponse> => {
return callEs('delete', params);
},
indicesCreate: (params: IndicesCreateParams) => {
return callEs('indices.create', params);
},
hasPrivileges: (
params: IndexPrivilegesParams
): Promise<IndexPrivileges> => {
return callEs('transport.request', {
method: 'POST',
path: '/_security/user/_has_privileges',
body: params,
});
},
};
}

View file

@ -7,6 +7,8 @@ import { setupRequest } from './setup_request';
import { APMConfig } from '../..';
import { APMRequestHandlerContext } from '../../routes/typings';
import { KibanaRequest } from '../../../../../../src/core/server';
import { ProcessorEvent } from '../../../common/processor_event';
import { PROCESSOR_EVENT } from '../../../common/elasticsearch_fieldnames';
jest.mock('../settings/apm_indices/get_apm_indices', () => ({
getApmIndices: async () => ({
@ -93,163 +95,175 @@ function getMockRequest() {
}
describe('setupRequest', () => {
it('should call callWithRequest with default args', async () => {
const { mockContext, mockRequest } = getMockRequest();
const { client } = await setupRequest(mockContext, mockRequest);
await client.search({ index: 'apm-*', body: { foo: 'bar' } } as any);
expect(
mockContext.core.elasticsearch.legacy.client.callAsCurrentUser
).toHaveBeenCalledWith('search', {
index: 'apm-*',
body: {
foo: 'bar',
query: {
bool: {
filter: [{ range: { 'observer.version_major': { gte: 7 } } }],
},
},
},
ignore_throttled: true,
});
});
it('should call callWithInternalUser with default args', async () => {
const { mockContext, mockRequest } = getMockRequest();
const { internalClient } = await setupRequest(mockContext, mockRequest);
await internalClient.search({
index: 'apm-*',
body: { foo: 'bar' },
} as any);
expect(
mockContext.core.elasticsearch.legacy.client.callAsInternalUser
).toHaveBeenCalledWith('search', {
index: 'apm-*',
body: {
foo: 'bar',
query: {
bool: {
filter: [{ range: { 'observer.version_major': { gte: 7 } } }],
},
},
},
ignore_throttled: true,
});
});
describe('observer.version_major filter', () => {
describe('if index is apm-*', () => {
it('should merge `observer.version_major` filter with existing boolean filters', async () => {
const { mockContext, mockRequest } = getMockRequest();
const { client } = await setupRequest(mockContext, mockRequest);
await client.search({
index: 'apm-*',
body: { query: { bool: { filter: [{ term: 'someTerm' }] } } },
});
const params =
mockContext.core.elasticsearch.legacy.client.callAsCurrentUser.mock
.calls[0][1];
expect(params.body).toEqual({
describe('with default args', () => {
it('calls callWithRequest', async () => {
const { mockContext, mockRequest } = getMockRequest();
const { apmEventClient } = await setupRequest(mockContext, mockRequest);
await apmEventClient.search({
apm: { events: [ProcessorEvent.transaction] },
body: { foo: 'bar' },
});
expect(
mockContext.core.elasticsearch.legacy.client.callAsCurrentUser
).toHaveBeenCalledWith('search', {
index: ['apm-*'],
body: {
foo: 'bar',
query: {
bool: {
filter: [
{ term: 'someTerm' },
{ terms: { 'processor.event': ['transaction'] } },
{ range: { 'observer.version_major': { gte: 7 } } },
],
},
},
});
});
it('should add `observer.version_major` filter if none exists', async () => {
const { mockContext, mockRequest } = getMockRequest();
const { client } = await setupRequest(mockContext, mockRequest);
await client.search({ index: 'apm-*' });
const params =
mockContext.core.elasticsearch.legacy.client.callAsCurrentUser.mock
.calls[0][1];
expect(params.body).toEqual({
query: {
bool: {
filter: [{ range: { 'observer.version_major': { gte: 7 } } }],
},
},
});
});
it('should not add `observer.version_major` filter if `includeLegacyData=true`', async () => {
const { mockContext, mockRequest } = getMockRequest();
const { client } = await setupRequest(mockContext, mockRequest);
await client.search(
{
index: 'apm-*',
body: { query: { bool: { filter: [{ term: 'someTerm' }] } } },
},
{
includeLegacyData: true,
}
);
const params =
mockContext.core.elasticsearch.legacy.client.callAsCurrentUser.mock
.calls[0][1];
expect(params.body).toEqual({
query: { bool: { filter: [{ term: 'someTerm' }] } },
});
},
ignore_throttled: true,
});
});
it('if index is not an APM index, it should not add `observer.version_major` filter', async () => {
it('calls callWithInternalUser', async () => {
const { mockContext, mockRequest } = getMockRequest();
const { client } = await setupRequest(mockContext, mockRequest);
await client.search({
index: '.ml-*',
const { internalClient } = await setupRequest(mockContext, mockRequest);
await internalClient.search({
index: ['apm-*'],
body: { foo: 'bar' },
} as any);
expect(
mockContext.core.elasticsearch.legacy.client.callAsInternalUser
).toHaveBeenCalledWith('search', {
index: ['apm-*'],
body: {
query: { bool: { filter: [{ term: 'someTerm' }] } },
foo: 'bar',
},
});
});
});
describe('with a bool filter', () => {
it('adds a range filter for `observer.version_major` to the existing filter', async () => {
const { mockContext, mockRequest } = getMockRequest();
const { apmEventClient } = await setupRequest(mockContext, mockRequest);
await apmEventClient.search({
apm: {
events: [ProcessorEvent.transaction],
},
body: { query: { bool: { filter: [{ term: 'someTerm' }] } } },
});
const params =
mockContext.core.elasticsearch.legacy.client.callAsCurrentUser.mock
.calls[0][1];
expect(params.body).toEqual({
query: {
bool: {
filter: [{ term: 'someTerm' }],
filter: [
{ term: 'someTerm' },
{ terms: { [PROCESSOR_EVENT]: ['transaction'] } },
{ range: { 'observer.version_major': { gte: 7 } } },
],
},
},
});
});
it('does not add a range filter for `observer.version_major` if includeLegacyData=true', async () => {
const { mockContext, mockRequest } = getMockRequest();
const { apmEventClient } = await setupRequest(mockContext, mockRequest);
await apmEventClient.search(
{
apm: {
events: [ProcessorEvent.error],
},
body: { query: { bool: { filter: [{ term: 'someTerm' }] } } },
},
{
includeLegacyData: true,
}
);
const params =
mockContext.core.elasticsearch.legacy.client.callAsCurrentUser.mock
.calls[0][1];
expect(params.body).toEqual({
query: {
bool: {
filter: [
{ term: 'someTerm' },
{
terms: {
[PROCESSOR_EVENT]: ['error'],
},
},
],
},
},
});
});
});
});
describe('ignore_throttled', () => {
it('should set `ignore_throttled=true` if `includeFrozen=false`', async () => {
const { mockContext, mockRequest } = getMockRequest();
// mock includeFrozen to return false
mockContext.core.uiSettings.client.get.mockResolvedValue(false);
const { client } = await setupRequest(mockContext, mockRequest);
await client.search({});
const params =
mockContext.core.elasticsearch.legacy.client.callAsCurrentUser.mock
.calls[0][1];
expect(params.ignore_throttled).toBe(true);
describe('without a bool filter', () => {
it('adds a range filter for `observer.version_major`', async () => {
const { mockContext, mockRequest } = getMockRequest();
const { apmEventClient } = await setupRequest(mockContext, mockRequest);
await apmEventClient.search({
apm: {
events: [ProcessorEvent.error],
},
});
it('should set `ignore_throttled=false` if `includeFrozen=true`', async () => {
const { mockContext, mockRequest } = getMockRequest();
// mock includeFrozen to return true
mockContext.core.uiSettings.client.get.mockResolvedValue(true);
const { client } = await setupRequest(mockContext, mockRequest);
await client.search({});
const params =
mockContext.core.elasticsearch.legacy.client.callAsCurrentUser.mock
.calls[0][1];
expect(params.ignore_throttled).toBe(false);
const params =
mockContext.core.elasticsearch.legacy.client.callAsCurrentUser.mock
.calls[0][1];
expect(params.body).toEqual({
query: {
bool: {
filter: [
{ terms: { [PROCESSOR_EVENT]: ['error'] } },
{ range: { 'observer.version_major': { gte: 7 } } },
],
},
},
});
});
});
describe('with includeFrozen=false', () => {
it('sets `ignore_throttled=true`', async () => {
const { mockContext, mockRequest } = getMockRequest();
// mock includeFrozen to return false
mockContext.core.uiSettings.client.get.mockResolvedValue(false);
const { apmEventClient } = await setupRequest(mockContext, mockRequest);
await apmEventClient.search({
apm: {
events: [],
},
});
const params =
mockContext.core.elasticsearch.legacy.client.callAsCurrentUser.mock
.calls[0][1];
expect(params.ignore_throttled).toBe(true);
});
});
describe('with includeFrozen=true', () => {
it('sets `ignore_throttled=false`', async () => {
const { mockContext, mockRequest } = getMockRequest();
// mock includeFrozen to return true
mockContext.core.uiSettings.client.get.mockResolvedValue(true);
const { apmEventClient } = await setupRequest(mockContext, mockRequest);
await apmEventClient.search({
apm: { events: [] },
});
const params =
mockContext.core.elasticsearch.legacy.client.callAsCurrentUser.mock
.calls[0][1];
expect(params.ignore_throttled).toBe(false);
});
});

View file

@ -13,11 +13,17 @@ import {
ApmIndicesConfig,
} from '../settings/apm_indices/get_apm_indices';
import { ESFilter } from '../../../typings/elasticsearch';
import { ESClient } from './es_client';
import { getUiFiltersES } from './convert_ui_filters/get_ui_filters_es';
import { APMRequestHandlerContext } from '../../routes/typings';
import { getESClient } from './es_client';
import { ProcessorEvent } from '../../../common/processor_event';
import {
APMEventClient,
createApmEventClient,
} from './create_es_client/create_apm_event_client';
import {
APMInternalClient,
createInternalESClient,
} from './create_es_client/create_internal_es_client';
function decodeUiFilters(uiFiltersEncoded?: string) {
if (!uiFiltersEncoded) {
@ -30,8 +36,8 @@ function decodeUiFilters(uiFiltersEncoded?: string) {
// https://github.com/microsoft/TypeScript/issues/34933
export interface Setup {
client: ESClient;
internalClient: ESClient;
apmEventClient: APMEventClient;
internalClient: APMInternalClient;
ml?: ReturnType<typeof getMlSetup>;
config: APMConfig;
indices: ApmIndicesConfig;
@ -78,22 +84,19 @@ export async function setupRequest<TParams extends SetupRequestParams>(
context.core.uiSettings.client.get(UI_SETTINGS.SEARCH_INCLUDE_FROZEN),
]);
const createClientOptions = {
indices,
includeFrozen,
};
const uiFiltersES = decodeUiFilters(query.uiFilters);
const coreSetupRequest = {
indices,
client: getESClient(context, request, {
clientAsInternalUser: false,
...createClientOptions,
apmEventClient: createApmEventClient({
context,
request,
indices,
options: { includeFrozen },
}),
internalClient: getESClient(context, request, {
clientAsInternalUser: true,
...createClientOptions,
internalClient: createInternalESClient({
context,
request,
}),
ml: getMlSetup(context, request),
config,

View file

@ -11,7 +11,10 @@ import {
IIndexPattern,
} from '../../../../../../src/plugins/data/server';
import { ApmIndicesConfig } from '../settings/apm_indices/get_apm_indices';
import { ProcessorEvent } from '../../../common/processor_event';
import {
ProcessorEvent,
UIProcessorEvent,
} from '../../../common/processor_event';
import { APMRequestHandlerContext } from '../../routes/typings';
const cache = new LRU<string, IIndexPattern | undefined>({
@ -27,7 +30,7 @@ export const getDynamicIndexPattern = async ({
}: {
context: APMRequestHandlerContext;
indices: ApmIndicesConfig;
processorEvent?: ProcessorEvent;
processorEvent?: UIProcessorEvent;
}) => {
const patternIndices = getPatternIndices(indices, processorEvent);
const indexPatternTitle = patternIndices.join(',');
@ -75,17 +78,17 @@ export const getDynamicIndexPattern = async ({
function getPatternIndices(
indices: ApmIndicesConfig,
processorEvent?: ProcessorEvent
processorEvent?: UIProcessorEvent
) {
const indexNames = processorEvent
? [processorEvent]
: ['transaction' as const, 'metric' as const, 'error' as const];
: [ProcessorEvent.transaction, ProcessorEvent.metric, ProcessorEvent.error];
const indicesMap = {
transaction: indices['apm_oss.transactionIndices'],
metric: indices['apm_oss.metricsIndices'],
error: indices['apm_oss.errorIndices'],
[ProcessorEvent.transaction]: indices['apm_oss.transactionIndices'],
[ProcessorEvent.metric]: indices['apm_oss.metricsIndices'],
[ProcessorEvent.error]: indices['apm_oss.errorIndices'],
};
return indexNames.map((name) => indicesMap[name]);
return indexNames.map((name) => indicesMap[name as UIProcessorEvent]);
}

View file

@ -2,6 +2,11 @@
exports[`metrics queries with a service node name fetches cpu chart data 1`] = `
Object {
"apm": Object {
"events": Array [
"metric",
],
},
"body": Object {
"aggs": Object {
"processCPUAverage": Object {
@ -66,11 +71,6 @@ Object {
"service.name": "foo",
},
},
Object {
"term": Object {
"processor.event": "metric",
},
},
Object {
"range": Object {
"@timestamp": Object {
@ -95,12 +95,16 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;
exports[`metrics queries with a service node name fetches heap memory chart data 1`] = `
Object {
"apm": Object {
"events": Array [
"metric",
],
},
"body": Object {
"aggs": Object {
"heapMemoryCommitted": Object {
@ -155,11 +159,6 @@ Object {
"service.name": "foo",
},
},
Object {
"term": Object {
"processor.event": "metric",
},
},
Object {
"range": Object {
"@timestamp": Object {
@ -189,12 +188,16 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;
exports[`metrics queries with a service node name fetches memory chart data 1`] = `
Object {
"apm": Object {
"events": Array [
"metric",
],
},
"body": Object {
"aggs": Object {
"memoryUsedAvg": Object {
@ -251,11 +254,6 @@ Object {
"service.name": "foo",
},
},
Object {
"term": Object {
"processor.event": "metric",
},
},
Object {
"range": Object {
"@timestamp": Object {
@ -290,12 +288,16 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;
exports[`metrics queries with a service node name fetches non heap memory chart data 1`] = `
Object {
"apm": Object {
"events": Array [
"metric",
],
},
"body": Object {
"aggs": Object {
"nonHeapMemoryCommitted": Object {
@ -350,11 +352,6 @@ Object {
"service.name": "foo",
},
},
Object {
"term": Object {
"processor.event": "metric",
},
},
Object {
"range": Object {
"@timestamp": Object {
@ -384,12 +381,16 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;
exports[`metrics queries with a service node name fetches thread count chart data 1`] = `
Object {
"apm": Object {
"events": Array [
"metric",
],
},
"body": Object {
"aggs": Object {
"threadCount": Object {
@ -434,11 +435,6 @@ Object {
"service.name": "foo",
},
},
Object {
"term": Object {
"processor.event": "metric",
},
},
Object {
"range": Object {
"@timestamp": Object {
@ -468,12 +464,16 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;
exports[`metrics queries with service_node_name_missing fetches cpu chart data 1`] = `
Object {
"apm": Object {
"events": Array [
"metric",
],
},
"body": Object {
"aggs": Object {
"processCPUAverage": Object {
@ -538,11 +538,6 @@ Object {
"service.name": "foo",
},
},
Object {
"term": Object {
"processor.event": "metric",
},
},
Object {
"range": Object {
"@timestamp": Object {
@ -573,12 +568,16 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;
exports[`metrics queries with service_node_name_missing fetches heap memory chart data 1`] = `
Object {
"apm": Object {
"events": Array [
"metric",
],
},
"body": Object {
"aggs": Object {
"heapMemoryCommitted": Object {
@ -633,11 +632,6 @@ Object {
"service.name": "foo",
},
},
Object {
"term": Object {
"processor.event": "metric",
},
},
Object {
"range": Object {
"@timestamp": Object {
@ -673,12 +667,16 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;
exports[`metrics queries with service_node_name_missing fetches memory chart data 1`] = `
Object {
"apm": Object {
"events": Array [
"metric",
],
},
"body": Object {
"aggs": Object {
"memoryUsedAvg": Object {
@ -735,11 +733,6 @@ Object {
"service.name": "foo",
},
},
Object {
"term": Object {
"processor.event": "metric",
},
},
Object {
"range": Object {
"@timestamp": Object {
@ -780,12 +773,16 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;
exports[`metrics queries with service_node_name_missing fetches non heap memory chart data 1`] = `
Object {
"apm": Object {
"events": Array [
"metric",
],
},
"body": Object {
"aggs": Object {
"nonHeapMemoryCommitted": Object {
@ -840,11 +837,6 @@ Object {
"service.name": "foo",
},
},
Object {
"term": Object {
"processor.event": "metric",
},
},
Object {
"range": Object {
"@timestamp": Object {
@ -880,12 +872,16 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;
exports[`metrics queries with service_node_name_missing fetches thread count chart data 1`] = `
Object {
"apm": Object {
"events": Array [
"metric",
],
},
"body": Object {
"aggs": Object {
"threadCount": Object {
@ -930,11 +926,6 @@ Object {
"service.name": "foo",
},
},
Object {
"term": Object {
"processor.event": "metric",
},
},
Object {
"range": Object {
"@timestamp": Object {
@ -970,12 +961,16 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;
exports[`metrics queries without a service node name fetches cpu chart data 1`] = `
Object {
"apm": Object {
"events": Array [
"metric",
],
},
"body": Object {
"aggs": Object {
"processCPUAverage": Object {
@ -1040,11 +1035,6 @@ Object {
"service.name": "foo",
},
},
Object {
"term": Object {
"processor.event": "metric",
},
},
Object {
"range": Object {
"@timestamp": Object {
@ -1064,12 +1054,16 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;
exports[`metrics queries without a service node name fetches heap memory chart data 1`] = `
Object {
"apm": Object {
"events": Array [
"metric",
],
},
"body": Object {
"aggs": Object {
"heapMemoryCommitted": Object {
@ -1124,11 +1118,6 @@ Object {
"service.name": "foo",
},
},
Object {
"term": Object {
"processor.event": "metric",
},
},
Object {
"range": Object {
"@timestamp": Object {
@ -1153,12 +1142,16 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;
exports[`metrics queries without a service node name fetches memory chart data 1`] = `
Object {
"apm": Object {
"events": Array [
"metric",
],
},
"body": Object {
"aggs": Object {
"memoryUsedAvg": Object {
@ -1215,11 +1208,6 @@ Object {
"service.name": "foo",
},
},
Object {
"term": Object {
"processor.event": "metric",
},
},
Object {
"range": Object {
"@timestamp": Object {
@ -1249,12 +1237,16 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;
exports[`metrics queries without a service node name fetches non heap memory chart data 1`] = `
Object {
"apm": Object {
"events": Array [
"metric",
],
},
"body": Object {
"aggs": Object {
"nonHeapMemoryCommitted": Object {
@ -1309,11 +1301,6 @@ Object {
"service.name": "foo",
},
},
Object {
"term": Object {
"processor.event": "metric",
},
},
Object {
"range": Object {
"@timestamp": Object {
@ -1338,12 +1325,16 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;
exports[`metrics queries without a service node name fetches thread count chart data 1`] = `
Object {
"apm": Object {
"events": Array [
"metric",
],
},
"body": Object {
"aggs": Object {
"threadCount": Object {
@ -1388,11 +1379,6 @@ Object {
"service.name": "foo",
},
},
Object {
"term": Object {
"processor.event": "metric",
},
},
Object {
"range": Object {
"@timestamp": Object {
@ -1417,6 +1403,5 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;

View file

@ -18,8 +18,8 @@ import {
} from '../../../../helpers/setup_request';
import { getMetricsDateHistogramParams } from '../../../../helpers/metrics';
import { ChartBase } from '../../../types';
import { getMetricsProjection } from '../../../../../../common/projections/metrics';
import { mergeProjection } from '../../../../../../common/projections/util/merge_projection';
import { getMetricsProjection } from '../../../../../projections/metrics';
import { mergeProjection } from '../../../../../projections/util/merge_projection';
import {
AGENT_NAME,
LABEL_NAME,
@ -42,7 +42,7 @@ export async function fetchAndTransformGcMetrics({
chartBase: ChartBase;
fieldName: typeof METRIC_JAVA_GC_COUNT | typeof METRIC_JAVA_GC_TIME;
}) {
const { start, end, client } = setup;
const { start, end, apmEventClient } = setup;
const { bucketSize } = getBucketSize(start, end, 'auto');
@ -105,7 +105,7 @@ export async function fetchAndTransformGcMetrics({
},
});
const response = await client.search(params);
const response = await apmEventClient.search(params);
const { aggregations } = response;

View file

@ -5,7 +5,6 @@
*/
import { Unionize, Overwrite } from 'utility-types';
import { ESSearchRequest } from '../../../typings/elasticsearch';
import {
Setup,
SetupTimeRange,
@ -14,9 +13,10 @@ import {
import { getMetricsDateHistogramParams } from '../helpers/metrics';
import { ChartBase } from './types';
import { transformDataToMetricsChart } from './transform_metrics_chart';
import { getMetricsProjection } from '../../../common/projections/metrics';
import { mergeProjection } from '../../../common/projections/util/merge_projection';
import { getMetricsProjection } from '../../projections/metrics';
import { mergeProjection } from '../../projections/util/merge_projection';
import { AggregationOptionsByType } from '../../../typings/elasticsearch/aggregations';
import { APMEventESSearchRequest } from '../helpers/create_es_client/create_apm_event_client';
type MetricsAggregationMap = Unionize<{
min: AggregationOptionsByType['min'];
@ -28,7 +28,7 @@ type MetricsAggregationMap = Unionize<{
type MetricAggs = Record<string, MetricsAggregationMap>;
export type GenericMetricsRequest = Overwrite<
ESSearchRequest,
APMEventESSearchRequest,
{
body: {
aggs: {
@ -65,7 +65,7 @@ export async function fetchAndTransformMetrics<T extends MetricAggs>({
aggs: T;
additionalFilters?: Filter[];
}) {
const { start, end, client } = setup;
const { start, end, apmEventClient } = setup;
const projection = getMetricsProjection({
setup,
@ -91,7 +91,7 @@ export async function fetchAndTransformMetrics<T extends MetricAggs>({
},
});
const response = await client.search(params);
const response = await apmEventClient.search(params);
return transformDataToMetricsChart(response, chartBase);
}

View file

@ -6,10 +6,7 @@
import { ProcessorEvent } from '../../../common/processor_event';
import { rangeFilter } from '../../../common/utils/range_filter';
import {
SERVICE_NAME,
PROCESSOR_EVENT,
} from '../../../common/elasticsearch_fieldnames';
import { SERVICE_NAME } from '../../../common/elasticsearch_fieldnames';
import { Setup, SetupTimeRange } from '../helpers/setup_request';
export async function getServiceCount({
@ -17,36 +14,27 @@ export async function getServiceCount({
}: {
setup: Setup & SetupTimeRange;
}) {
const { client, indices, start, end } = setup;
const { apmEventClient, start, end } = setup;
const params = {
index: [
indices['apm_oss.transactionIndices'],
indices['apm_oss.errorIndices'],
indices['apm_oss.metricsIndices'],
],
apm: {
events: [
ProcessorEvent.transaction,
ProcessorEvent.error,
ProcessorEvent.metric,
],
},
body: {
size: 0,
query: {
bool: {
filter: [
{ range: rangeFilter(start, end) },
{
terms: {
[PROCESSOR_EVENT]: [
ProcessorEvent.error,
ProcessorEvent.transaction,
ProcessorEvent.metric,
],
},
},
],
filter: [{ range: rangeFilter(start, end) }],
},
},
aggs: { serviceCount: { cardinality: { field: SERVICE_NAME } } },
},
};
const { aggregations } = await client.search(params);
const { aggregations } = await apmEventClient.search(params);
return aggregations?.serviceCount.value || 0;
}

View file

@ -10,7 +10,6 @@
*/
import { rangeFilter } from '../../../common/utils/range_filter';
import { Coordinates } from '../../../../observability/public';
import { PROCESSOR_EVENT } from '../../../common/elasticsearch_fieldnames';
import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { ProcessorEvent } from '../../../common/processor_event';
@ -21,18 +20,17 @@ export async function getTransactionCoordinates({
setup: Setup & SetupTimeRange;
bucketSize: string;
}): Promise<Coordinates[]> {
const { client, indices, start, end } = setup;
const { apmEventClient, start, end } = setup;
const { aggregations } = await client.search({
index: indices['apm_oss.transactionIndices'],
const { aggregations } = await apmEventClient.search({
apm: {
events: [ProcessorEvent.transaction],
},
body: {
size: 0,
query: {
bool: {
filter: [
{ term: { [PROCESSOR_EVENT]: ProcessorEvent.transaction } },
{ range: rangeFilter(start, end) },
],
filter: [{ range: rangeFilter(start, end) }],
},
},
aggs: {

View file

@ -3,41 +3,27 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { PROCESSOR_EVENT } from '../../../common/elasticsearch_fieldnames';
import { ProcessorEvent } from '../../../common/processor_event';
import { Setup } from '../helpers/setup_request';
export async function hasData({ setup }: { setup: Setup }) {
const { client, indices } = setup;
const { apmEventClient } = setup;
try {
const params = {
index: [
indices['apm_oss.transactionIndices'],
indices['apm_oss.errorIndices'],
indices['apm_oss.metricsIndices'],
],
apm: {
events: [
ProcessorEvent.transaction,
ProcessorEvent.error,
ProcessorEvent.metric,
],
},
terminateAfter: 1,
body: {
size: 0,
query: {
bool: {
filter: [
{
terms: {
[PROCESSOR_EVENT]: [
ProcessorEvent.error,
ProcessorEvent.metric,
ProcessorEvent.transaction,
],
},
},
],
},
},
},
};
const response = await client.search(params);
const response = await apmEventClient.search(params);
return response.hits.total.value > 0;
} catch (e) {
return false;

View file

@ -2,6 +2,11 @@
exports[`rum client dashboard queries fetches client metrics 1`] = `
Object {
"apm": Object {
"events": Array [
"transaction",
],
},
"body": Object {
"aggs": Object {
"backEnd": Object {
@ -34,11 +39,6 @@ Object {
},
},
},
Object {
"term": Object {
"processor.event": "transaction",
},
},
Object {
"term": Object {
"transaction.type": "page-load",
@ -59,12 +59,16 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;
exports[`rum client dashboard queries fetches page load distribution 1`] = `
Object {
"apm": Object {
"events": Array [
"transaction",
],
},
"body": Object {
"aggs": Object {
"durPercentiles": Object {
@ -101,11 +105,6 @@ Object {
},
},
},
Object {
"term": Object {
"processor.event": "transaction",
},
},
Object {
"term": Object {
"transaction.type": "page-load",
@ -126,12 +125,16 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;
exports[`rum client dashboard queries fetches page view trends 1`] = `
Object {
"apm": Object {
"events": Array [
"transaction",
],
},
"body": Object {
"aggs": Object {
"pageViews": Object {
@ -154,11 +157,6 @@ Object {
},
},
},
Object {
"term": Object {
"processor.event": "transaction",
},
},
Object {
"term": Object {
"transaction.type": "page-load",
@ -179,12 +177,16 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;
exports[`rum client dashboard queries fetches rum services 1`] = `
Object {
"apm": Object {
"events": Array [
"transaction",
],
},
"body": Object {
"aggs": Object {
"services": Object {
@ -206,11 +208,6 @@ Object {
},
},
},
Object {
"term": Object {
"processor.event": "transaction",
},
},
Object {
"term": Object {
"transaction.type": "page-load",
@ -231,6 +228,5 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;

View file

@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { getRumOverviewProjection } from '../../../common/projections/rum_overview';
import { mergeProjection } from '../../../common/projections/util/merge_projection';
import { getRumOverviewProjection } from '../../projections/rum_overview';
import { mergeProjection } from '../../projections/util/merge_projection';
import {
Setup,
SetupTimeRange,
@ -45,9 +45,9 @@ export async function getClientMetrics({
},
});
const { client } = setup;
const { apmEventClient } = setup;
const response = await client.search(params);
const response = await apmEventClient.search(params);
const { backEnd, domInteractive, pageViews } = response.aggregations!;
// Divide by 1000 to convert ms into seconds

View file

@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { getRumOverviewProjection } from '../../../common/projections/rum_overview';
import { mergeProjection } from '../../../common/projections/util/merge_projection';
import { getRumOverviewProjection } from '../../projections/rum_overview';
import { mergeProjection } from '../../projections/util/merge_projection';
import {
Setup,
SetupTimeRange,
@ -57,12 +57,12 @@ export async function getPageLoadDistribution({
},
});
const { client } = setup;
const { apmEventClient } = setup;
const {
aggregations,
hits: { total },
} = await client.search(params);
} = await apmEventClient.search(params);
if (total.value === 0) {
return null;
@ -130,9 +130,9 @@ const getPercentilesDistribution = async (
},
});
const { client } = setup;
const { apmEventClient } = setup;
const { aggregations } = await client.search(params);
const { aggregations } = await apmEventClient.search(params);
const pageDist = aggregations?.loadDistribution.values ?? [];

View file

@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { getRumOverviewProjection } from '../../../common/projections/rum_overview';
import { mergeProjection } from '../../../common/projections/util/merge_projection';
import { getRumOverviewProjection } from '../../projections/rum_overview';
import { mergeProjection } from '../../projections/util/merge_projection';
import {
Setup,
SetupTimeRange,
@ -56,9 +56,9 @@ export async function getPageViewTrends({
},
});
const { client } = setup;
const { apmEventClient } = setup;
const response = await client.search(params);
const response = await apmEventClient.search(params);
const result = response.aggregations?.pageViews.buckets ?? [];

View file

@ -4,8 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { getRumOverviewProjection } from '../../../common/projections/rum_overview';
import { mergeProjection } from '../../../common/projections/util/merge_projection';
import { ProcessorEvent } from '../../../common/processor_event';
import { getRumOverviewProjection } from '../../projections/rum_overview';
import { mergeProjection } from '../../projections/util/merge_projection';
import {
Setup,
SetupTimeRange,
@ -16,6 +17,7 @@ import {
USER_AGENT_DEVICE,
USER_AGENT_NAME,
USER_AGENT_OS,
TRANSACTION_DURATION,
} from '../../../common/elasticsearch_fieldnames';
import { MICRO_TO_SEC, microToSec } from './get_page_load_distribution';
@ -53,11 +55,11 @@ export const getPageLoadDistBreakdown = async (
});
const params = mergeProjection(projection, {
apm: {
events: [ProcessorEvent.transaction],
},
body: {
size: 0,
query: {
bool: projection.body.query.bool,
},
aggs: {
breakdowns: {
terms: {
@ -67,7 +69,7 @@ export const getPageLoadDistBreakdown = async (
aggs: {
page_dist: {
percentile_ranks: {
field: 'transaction.duration.us',
field: TRANSACTION_DURATION,
values: stepValues,
keyed: false,
hdr: {
@ -81,9 +83,9 @@ export const getPageLoadDistBreakdown = async (
},
});
const { client } = setup;
const { apmEventClient } = setup;
const { aggregations } = await client.search(params);
const { aggregations } = await apmEventClient.search(params);
const pageDistBreakdowns = aggregations?.breakdowns.buckets;

View file

@ -4,13 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { getRumOverviewProjection } from '../../../common/projections/rum_overview';
import { mergeProjection } from '../../../common/projections/util/merge_projection';
import {
Setup,
SetupTimeRange,
SetupUIFilters,
} from '../helpers/setup_request';
import { getRumOverviewProjection } from '../../projections/rum_overview';
import { mergeProjection } from '../../projections/util/merge_projection';
export async function getRumServices({
setup,
@ -38,9 +38,9 @@ export async function getRumServices({
},
});
const { client } = setup;
const { apmEventClient } = setup;
const response = await client.search(params);
const response = await apmEventClient.search(params);
const result = response.aggregations?.services.buckets ?? [];

View file

@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { getRumOverviewProjection } from '../../../common/projections/rum_overview';
import { mergeProjection } from '../../../common/projections/util/merge_projection';
import { getRumOverviewProjection } from '../../projections/rum_overview';
import { mergeProjection } from '../../projections/util/merge_projection';
import {
Setup,
SetupTimeRange,
@ -55,9 +55,9 @@ export async function getVisitorBreakdown({
},
});
const { client } = setup;
const { apmEventClient } = setup;
const response = await client.search(params);
const response = await apmEventClient.search(params);
const { browsers, os, devices } = response.aggregations!;
return {

View file

@ -3,10 +3,8 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import {
PROCESSOR_EVENT,
TRACE_ID,
} from '../../../common/elasticsearch_fieldnames';
import { ProcessorEvent } from '../../../common/processor_event';
import { TRACE_ID } from '../../../common/elasticsearch_fieldnames';
import {
ConnectionNode,
ExternalConnectionNode,
@ -18,23 +16,17 @@ export async function fetchServicePathsFromTraceIds(
setup: Setup,
traceIds: string[]
) {
const { indices, client } = setup;
const { apmEventClient } = setup;
const serviceMapParams = {
index: [
indices['apm_oss.spanIndices'],
indices['apm_oss.transactionIndices'],
],
apm: {
events: [ProcessorEvent.span, ProcessorEvent.transaction],
},
body: {
size: 0,
query: {
bool: {
filter: [
{
terms: {
[PROCESSOR_EVENT]: ['span', 'transaction'],
},
},
{
terms: {
[TRACE_ID]: traceIds,
@ -212,7 +204,7 @@ export async function fetchServicePathsFromTraceIds(
},
};
const serviceMapFromTraceIdsScriptResponse = await client.search(
const serviceMapFromTraceIdsScriptResponse = await apmEventClient.search(
serviceMapParams
);

View file

@ -10,8 +10,8 @@ import {
SERVICE_ENVIRONMENT,
SERVICE_NAME,
} from '../../../common/elasticsearch_fieldnames';
import { getServicesProjection } from '../../../common/projections/services';
import { mergeProjection } from '../../../common/projections/util/merge_projection';
import { getServicesProjection } from '../../projections/services';
import { mergeProjection } from '../../projections/util/merge_projection';
import { PromiseReturnType } from '../../../typings/common';
import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { transformServiceMapResponses } from './transform_service_map_responses';
@ -118,9 +118,9 @@ async function getServicesData(options: IEnvOptions) {
},
});
const { client } = setup;
const { apmEventClient } = setup;
const response = await client.search(params);
const response = await apmEventClient.search(params);
return (
response.aggregations?.services.buckets.map((bucket) => {

View file

@ -12,7 +12,7 @@ describe('getServiceMapServiceNodeInfo', () => {
describe('with no results', () => {
it('returns null data', async () => {
const setup = ({
client: {
apmEventClient: {
search: () =>
Promise.resolve({
hits: { total: { value: 0 } },
@ -49,7 +49,7 @@ describe('getServiceMapServiceNodeInfo', () => {
});
const setup = ({
client: {
apmEventClient: {
search: () =>
Promise.resolve({
hits: { total: { value: 1 } },

View file

@ -6,13 +6,12 @@
import { UIFilters } from '../../../typings/ui_filters';
import {
SERVICE_NAME,
TRANSACTION_DURATION,
TRANSACTION_TYPE,
METRIC_SYSTEM_CPU_PERCENT,
METRIC_SYSTEM_FREE_MEMORY,
METRIC_SYSTEM_TOTAL_MEMORY,
PROCESSOR_EVENT,
SERVICE_NAME,
TRANSACTION_DURATION,
} from '../../../common/elasticsearch_fieldnames';
import { ProcessorEvent } from '../../../common/processor_event';
import { rangeFilter } from '../../../common/utils/range_filter';
@ -109,17 +108,18 @@ async function getTransactionStats({
avgTransactionDuration: number | null;
avgRequestsPerMinute: number | null;
}> {
const { indices, client } = setup;
const { apmEventClient } = setup;
const params = {
index: indices['apm_oss.transactionIndices'],
apm: {
events: [ProcessorEvent.transaction],
},
body: {
size: 0,
query: {
bool: {
filter: [
...filter,
{ term: { [PROCESSOR_EVENT]: ProcessorEvent.transaction } },
{
terms: {
[TRANSACTION_TYPE]: [
@ -135,8 +135,9 @@ async function getTransactionStats({
aggs: { duration: { avg: { field: TRANSACTION_DURATION } } },
},
};
const response = await client.search(params);
const response = await apmEventClient.search(params);
const docCount = response.hits.total.value;
return {
avgTransactionDuration: response.aggregations?.duration.value ?? null,
avgRequestsPerMinute: docCount > 0 ? docCount / minutes : null,
@ -147,18 +148,17 @@ async function getCpuStats({
setup,
filter,
}: TaskParameters): Promise<{ avgCpuUsage: number | null }> {
const { indices, client } = setup;
const { apmEventClient } = setup;
const response = await client.search({
index: indices['apm_oss.metricsIndices'],
const response = await apmEventClient.search({
apm: {
events: [ProcessorEvent.metric],
},
body: {
size: 0,
query: {
bool: {
filter: filter.concat([
{ term: { [PROCESSOR_EVENT]: ProcessorEvent.metric } },
{ exists: { field: METRIC_SYSTEM_CPU_PERCENT } },
]),
filter: [...filter, { exists: { field: METRIC_SYSTEM_CPU_PERCENT } }],
},
},
aggs: { avgCpuUsage: { avg: { field: METRIC_SYSTEM_CPU_PERCENT } } },
@ -172,17 +172,19 @@ async function getMemoryStats({
setup,
filter,
}: TaskParameters): Promise<{ avgMemoryUsage: number | null }> {
const { client, indices } = setup;
const response = await client.search({
index: indices['apm_oss.metricsIndices'],
const { apmEventClient } = setup;
const response = await apmEventClient.search({
apm: {
events: [ProcessorEvent.metric],
},
body: {
query: {
bool: {
filter: filter.concat([
{ term: { [PROCESSOR_EVENT]: 'metric' } },
filter: [
...filter,
{ exists: { field: METRIC_SYSTEM_FREE_MEMORY } },
{ exists: { field: METRIC_SYSTEM_TOTAL_MEMORY } },
]),
],
},
},
aggs: { avgMemoryUsage: { avg: { script: percentMemoryUsedScript } } },

View file

@ -4,11 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { uniq, take, sortBy } from 'lodash';
import { ProcessorEvent } from '../../../common/processor_event';
import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { rangeFilter } from '../../../common/utils/range_filter';
import { ESFilter } from '../../../typings/elasticsearch';
import {
PROCESSOR_EVENT,
SERVICE_NAME,
SERVICE_ENVIRONMENT,
TRACE_ID,
@ -26,18 +26,13 @@ export async function getTraceSampleIds({
environment?: string;
setup: Setup & SetupTimeRange;
}) {
const { start, end, client, indices, config } = setup;
const { start, end, apmEventClient, config } = setup;
const rangeQuery = { range: rangeFilter(start, end) };
const query = {
bool: {
filter: [
{
term: {
[PROCESSOR_EVENT]: 'span',
},
},
{
exists: {
field: SPAN_DESTINATION_SERVICE_RESOURCE,
@ -67,7 +62,9 @@ export async function getTraceSampleIds({
const samplerShardSize = traceIdBucketSize * 10;
const params = {
index: [indices['apm_oss.spanIndices']],
apm: {
events: [ProcessorEvent.span],
},
body: {
size: 0,
query,
@ -126,9 +123,7 @@ export async function getTraceSampleIds({
},
};
const tracesSampleResponse = await client.search<unknown, typeof params>(
params
);
const tracesSampleResponse = await apmEventClient.search(params);
// make sure at least one trace per composite/connection bucket
// is queried

View file

@ -2,6 +2,11 @@
exports[`service node queries fetches metadata for a service node 1`] = `
Object {
"apm": Object {
"events": Array [
"metric",
],
},
"body": Object {
"aggs": Object {
"containerId": Object {
@ -30,11 +35,6 @@ Object {
"service.name": "foo",
},
},
Object {
"term": Object {
"processor.event": "metric",
},
},
Object {
"range": Object {
"@timestamp": Object {
@ -59,12 +59,16 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;
exports[`service node queries fetches metadata for unidentified service nodes 1`] = `
Object {
"apm": Object {
"events": Array [
"metric",
],
},
"body": Object {
"aggs": Object {
"containerId": Object {
@ -93,11 +97,6 @@ Object {
"service.name": "foo",
},
},
Object {
"term": Object {
"processor.event": "metric",
},
},
Object {
"range": Object {
"@timestamp": Object {
@ -128,12 +127,16 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;
exports[`service node queries fetches services nodes 1`] = `
Object {
"apm": Object {
"events": Array [
"metric",
],
},
"body": Object {
"aggs": Object {
"nodes": Object {
@ -174,11 +177,6 @@ Object {
"service.name": "foo",
},
},
Object {
"term": Object {
"processor.event": "metric",
},
},
Object {
"range": Object {
"@timestamp": Object {
@ -197,6 +195,5 @@ Object {
},
},
},
"index": "myIndex",
}
`;

View file

@ -9,8 +9,8 @@ import {
SetupTimeRange,
SetupUIFilters,
} from '../helpers/setup_request';
import { getServiceNodesProjection } from '../../../common/projections/service_nodes';
import { mergeProjection } from '../../../common/projections/util/merge_projection';
import { getServiceNodesProjection } from '../../projections/service_nodes';
import { mergeProjection } from '../../projections/util/merge_projection';
import { SERVICE_NODE_NAME_MISSING } from '../../../common/service_nodes';
import {
METRIC_PROCESS_CPU_PERCENT,
@ -26,7 +26,7 @@ const getServiceNodes = async ({
setup: Setup & SetupTimeRange & SetupUIFilters;
serviceName: string;
}) => {
const { client } = setup;
const { apmEventClient } = setup;
const projection = getServiceNodesProjection({ setup, serviceName });
@ -66,7 +66,7 @@ const getServiceNodes = async ({
},
});
const response = await client.search(params);
const response = await apmEventClient.search(params);
if (!response.aggregations) {
return [];

View file

@ -2,48 +2,32 @@
exports[`services queries fetches the agent status 1`] = `
Object {
"apm": Object {
"events": Array [
"error",
"metric",
"sourcemap",
"transaction",
],
},
"body": Object {
"query": Object {
"bool": Object {
"filter": Array [
Object {
"terms": Object {
"processor.event": Array [
"error",
"metric",
"sourcemap",
"transaction",
],
},
},
],
},
},
"size": 0,
},
"index": Array [
"myIndex",
"myIndex",
"myIndex",
"myIndex",
],
"terminateAfter": 1,
}
`;
exports[`services queries fetches the legacy data status 1`] = `
Object {
"apm": Object {
"events": Array [
"transaction",
],
},
"body": Object {
"query": Object {
"bool": Object {
"filter": Array [
Object {
"terms": Object {
"processor.event": Array [
"transaction",
],
},
},
Object {
"range": Object {
"observer.version_major": Object {
@ -56,13 +40,19 @@ Object {
},
"size": 0,
},
"index": "myIndex",
"terminateAfter": 1,
}
`;
exports[`services queries fetches the service agent name 1`] = `
Object {
"apm": Object {
"events": Array [
"error",
"transaction",
"metric",
],
},
"body": Object {
"aggs": Object {
"agents": Object {
@ -80,15 +70,6 @@ Object {
"service.name": "foo",
},
},
Object {
"terms": Object {
"processor.event": Array [
"error",
"transaction",
"metric",
],
},
},
Object {
"range": Object {
"@timestamp": Object {
@ -103,11 +84,6 @@ Object {
},
"size": 0,
},
"index": Array [
"myIndex",
"myIndex",
"myIndex",
],
"terminateAfter": 1,
}
`;
@ -115,6 +91,11 @@ Object {
exports[`services queries fetches the service items 1`] = `
Array [
Object {
"apm": Object {
"events": Array [
"transaction",
],
},
"body": Object {
"aggs": Object {
"services": Object {
@ -148,20 +129,20 @@ Array [
"my.custom.ui.filter": "foo-bar",
},
},
Object {
"term": Object {
"processor.event": "transaction",
},
},
],
},
},
"size": 0,
},
"index": "myIndex",
"size": 0,
},
Object {
"apm": Object {
"events": Array [
"metric",
"error",
"transaction",
],
},
"body": Object {
"aggs": Object {
"services": Object {
@ -198,27 +179,18 @@ Array [
"my.custom.ui.filter": "foo-bar",
},
},
Object {
"terms": Object {
"processor.event": Array [
"metric",
"error",
"transaction",
],
},
},
],
},
},
"size": 0,
},
"index": Array [
"myIndex",
"myIndex",
"myIndex",
],
},
Object {
"apm": Object {
"events": Array [
"transaction",
],
},
"body": Object {
"aggs": Object {
"services": Object {
@ -245,19 +217,18 @@ Array [
"my.custom.ui.filter": "foo-bar",
},
},
Object {
"term": Object {
"processor.event": "transaction",
},
},
],
},
},
"size": 0,
},
"index": "myIndex",
},
Object {
"apm": Object {
"events": Array [
"error",
],
},
"body": Object {
"aggs": Object {
"services": Object {
@ -284,19 +255,20 @@ Array [
"my.custom.ui.filter": "foo-bar",
},
},
Object {
"term": Object {
"processor.event": "error",
},
},
],
},
},
"size": 0,
},
"index": "myIndex",
},
Object {
"apm": Object {
"events": Array [
"metric",
"transaction",
"error",
],
},
"body": Object {
"aggs": Object {
"services": Object {
@ -330,31 +302,22 @@ Array [
"my.custom.ui.filter": "foo-bar",
},
},
Object {
"terms": Object {
"processor.event": Array [
"transaction",
"error",
"metric",
],
},
},
],
},
},
"size": 0,
},
"index": Array [
"myIndex",
"myIndex",
"myIndex",
],
},
]
`;
exports[`services queries fetches the service transaction types 1`] = `
Object {
"apm": Object {
"events": Array [
"transaction",
],
},
"body": Object {
"aggs": Object {
"types": Object {
@ -372,13 +335,6 @@ Object {
"service.name": "foo",
},
},
Object {
"terms": Object {
"processor.event": Array [
"transaction",
],
},
},
Object {
"range": Object {
"@timestamp": Object {
@ -393,6 +349,5 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;

View file

@ -4,12 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { isNumber } from 'lodash';
import { ProcessorEvent } from '../../../../common/processor_event';
import { Annotation, AnnotationType } from '../../../../common/annotations';
import { SetupTimeRange, Setup } from '../../helpers/setup_request';
import { ESFilter } from '../../../../typings/elasticsearch';
import { rangeFilter } from '../../../../common/utils/range_filter';
import {
PROCESSOR_EVENT,
SERVICE_NAME,
SERVICE_VERSION,
} from '../../../../common/elasticsearch_fieldnames';
@ -24,23 +24,24 @@ export async function getDerivedServiceAnnotations({
environment?: string;
setup: Setup & SetupTimeRange;
}) {
const { start, end, client, indices } = setup;
const { start, end, apmEventClient } = setup;
const filter: ESFilter[] = [
{ term: { [PROCESSOR_EVENT]: 'transaction' } },
{ term: { [SERVICE_NAME]: serviceName } },
...getEnvironmentUiFilterES(environment),
];
const versions =
(
await client.search({
index: indices['apm_oss.transactionIndices'],
await apmEventClient.search({
apm: {
events: [ProcessorEvent.transaction],
},
body: {
size: 0,
query: {
bool: {
filter: filter.concat({ range: rangeFilter(start, end) }),
filter: [...filter, { range: rangeFilter(start, end) }],
},
},
aggs: {
@ -59,17 +60,15 @@ export async function getDerivedServiceAnnotations({
}
const annotations = await Promise.all(
versions.map(async (version) => {
const response = await client.search({
index: indices['apm_oss.transactionIndices'],
const response = await apmEventClient.search({
apm: {
events: [ProcessorEvent.transaction],
},
body: {
size: 0,
query: {
bool: {
filter: filter.concat({
term: {
[SERVICE_VERSION]: version,
},
}),
filter: [...filter, { term: { [SERVICE_VERSION]: version } }],
},
},
aggs: {

View file

@ -3,8 +3,8 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { ProcessorEvent } from '../../../common/processor_event';
import {
PROCESSOR_EVENT,
AGENT_NAME,
SERVICE_NAME,
} from '../../../common/elasticsearch_fieldnames';
@ -15,24 +15,23 @@ export async function getServiceAgentName(
serviceName: string,
setup: Setup & SetupTimeRange
) {
const { start, end, client, indices } = setup;
const { start, end, apmEventClient } = setup;
const params = {
terminateAfter: 1,
index: [
indices['apm_oss.errorIndices'],
indices['apm_oss.transactionIndices'],
indices['apm_oss.metricsIndices'],
],
apm: {
events: [
ProcessorEvent.error,
ProcessorEvent.transaction,
ProcessorEvent.metric,
],
},
body: {
size: 0,
query: {
bool: {
filter: [
{ term: { [SERVICE_NAME]: serviceName } },
{
terms: { [PROCESSOR_EVENT]: ['error', 'transaction', 'metric'] },
},
{ range: rangeFilter(start, end) },
],
},
@ -45,7 +44,7 @@ export async function getServiceAgentName(
},
};
const { aggregations } = await client.search(params);
const { aggregations } = await apmEventClient.search(params);
const agentName = aggregations?.agents.buckets[0]?.key as string | undefined;
return { agentName };
}

View file

@ -14,8 +14,8 @@ import {
CONTAINER_ID,
} from '../../../common/elasticsearch_fieldnames';
import { NOT_AVAILABLE_LABEL } from '../../../common/i18n';
import { mergeProjection } from '../../../common/projections/util/merge_projection';
import { getServiceNodesProjection } from '../../../common/projections/service_nodes';
import { mergeProjection } from '../../projections/util/merge_projection';
import { getServiceNodesProjection } from '../../projections/service_nodes';
export async function getServiceNodeMetadata({
serviceName,
@ -26,7 +26,7 @@ export async function getServiceNodeMetadata({
serviceNodeName: string;
setup: Setup & SetupTimeRange & SetupUIFilters;
}) {
const { client } = setup;
const { apmEventClient } = setup;
const query = mergeProjection(
getServiceNodesProjection({
@ -55,7 +55,7 @@ export async function getServiceNodeMetadata({
}
);
const response = await client.search(query);
const response = await apmEventClient.search(query);
return {
host: response.aggregations?.host.buckets[0]?.key || NOT_AVAILABLE_LABEL,

View file

@ -3,8 +3,8 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { ProcessorEvent } from '../../../common/processor_event';
import {
PROCESSOR_EVENT,
SERVICE_NAME,
TRANSACTION_TYPE,
} from '../../../common/elasticsearch_fieldnames';
@ -15,17 +15,18 @@ export async function getServiceTransactionTypes(
serviceName: string,
setup: Setup & SetupTimeRange
) {
const { start, end, client, indices } = setup;
const { start, end, apmEventClient } = setup;
const params = {
index: indices['apm_oss.transactionIndices'],
apm: {
events: [ProcessorEvent.transaction],
},
body: {
size: 0,
query: {
bool: {
filter: [
{ term: { [SERVICE_NAME]: serviceName } },
{ terms: { [PROCESSOR_EVENT]: ['transaction'] } },
{ range: rangeFilter(start, end) },
],
},
@ -38,7 +39,7 @@ export async function getServiceTransactionTypes(
},
};
const { aggregations } = await client.search(params);
const { aggregations } = await apmEventClient.search(params);
const transactionTypes =
aggregations?.types.buckets.map((bucket) => bucket.key as string) || [];
return { transactionTypes };

View file

@ -4,33 +4,30 @@
* you may not use this file except in compliance with the Elastic License.
*/
import {
OBSERVER_VERSION_MAJOR,
PROCESSOR_EVENT,
} from '../../../../common/elasticsearch_fieldnames';
import { ProcessorEvent } from '../../../../common/processor_event';
import { OBSERVER_VERSION_MAJOR } from '../../../../common/elasticsearch_fieldnames';
import { Setup } from '../../helpers/setup_request';
// returns true if 6.x data is found
export async function getLegacyDataStatus(setup: Setup) {
const { client, indices } = setup;
const { apmEventClient } = setup;
const params = {
terminateAfter: 1,
index: indices['apm_oss.transactionIndices'],
apm: {
events: [ProcessorEvent.transaction],
},
body: {
size: 0,
query: {
bool: {
filter: [
{ terms: { [PROCESSOR_EVENT]: ['transaction'] } },
{ range: { [OBSERVER_VERSION_MAJOR]: { lt: 7 } } },
],
filter: [{ range: { [OBSERVER_VERSION_MAJOR]: { lt: 7 } } }],
},
},
},
};
const resp = await client.search(params, { includeLegacyData: true });
const resp = await apmEventClient.search(params, { includeLegacyData: true });
const hasLegacyData = resp.hits.total.value > 0;
return hasLegacyData;
}

View file

@ -10,7 +10,7 @@ import {
SetupTimeRange,
SetupUIFilters,
} from '../../helpers/setup_request';
import { getServicesProjection } from '../../../../common/projections/services';
import { getServicesProjection } from '../../../projections/services';
import {
getTransactionDurationAverages,
getAgentNames,
@ -25,7 +25,7 @@ export type ServicesItemsProjection = ReturnType<typeof getServicesProjection>;
export async function getServicesItems(setup: ServicesItemsSetup) {
const params = {
projection: getServicesProjection({ setup, noEvents: true }),
projection: getServicesProjection({ setup }),
setup,
};

View file

@ -5,12 +5,11 @@
*/
import {
PROCESSOR_EVENT,
TRANSACTION_DURATION,
AGENT_NAME,
SERVICE_ENVIRONMENT,
} from '../../../../common/elasticsearch_fieldnames';
import { mergeProjection } from '../../../../common/projections/util/merge_projection';
import { mergeProjection } from '../../../projections/util/merge_projection';
import { ProcessorEvent } from '../../../../common/processor_event';
import {
ServicesItemsSetup,
@ -31,22 +30,15 @@ export const getTransactionDurationAverages = async ({
setup,
projection,
}: AggregationParams) => {
const { client, indices } = setup;
const { apmEventClient } = setup;
const response = await client.search(
const response = await apmEventClient.search(
mergeProjection(projection, {
size: 0,
index: indices['apm_oss.transactionIndices'],
apm: {
events: [ProcessorEvent.transaction],
},
body: {
query: {
bool: {
filter: projection.body.query.bool.filter.concat({
term: {
[PROCESSOR_EVENT]: ProcessorEvent.transaction,
},
}),
},
},
size: 0,
aggs: {
services: {
terms: {
@ -82,32 +74,18 @@ export const getAgentNames = async ({
setup,
projection,
}: AggregationParams) => {
const { client, indices } = setup;
const response = await client.search(
const { apmEventClient } = setup;
const response = await apmEventClient.search(
mergeProjection(projection, {
index: [
indices['apm_oss.metricsIndices'],
indices['apm_oss.errorIndices'],
indices['apm_oss.transactionIndices'],
],
apm: {
events: [
ProcessorEvent.metric,
ProcessorEvent.error,
ProcessorEvent.transaction,
],
},
body: {
size: 0,
query: {
bool: {
filter: [
...projection.body.query.bool.filter,
{
terms: {
[PROCESSOR_EVENT]: [
ProcessorEvent.metric,
ProcessorEvent.error,
ProcessorEvent.transaction,
],
},
},
],
},
},
aggs: {
services: {
terms: {
@ -136,11 +114,7 @@ export const getAgentNames = async ({
return aggregations.services.buckets.map((bucket) => ({
serviceName: bucket.key as string,
agentName: (bucket.agent_name.hits.hits[0]?._source as {
agent: {
name: string;
};
}).agent.name,
agentName: bucket.agent_name.hits.hits[0]?._source.agent.name,
}));
};
@ -148,24 +122,14 @@ export const getTransactionRates = async ({
setup,
projection,
}: AggregationParams) => {
const { client, indices } = setup;
const response = await client.search(
const { apmEventClient } = setup;
const response = await apmEventClient.search(
mergeProjection(projection, {
index: indices['apm_oss.transactionIndices'],
apm: {
events: [ProcessorEvent.transaction],
},
body: {
size: 0,
query: {
bool: {
filter: [
...projection.body.query.bool.filter,
{
term: {
[PROCESSOR_EVENT]: ProcessorEvent.transaction,
},
},
],
},
},
aggs: {
services: {
terms: {
@ -199,24 +163,14 @@ export const getErrorRates = async ({
setup,
projection,
}: AggregationParams) => {
const { client, indices } = setup;
const response = await client.search(
const { apmEventClient } = setup;
const response = await apmEventClient.search(
mergeProjection(projection, {
index: indices['apm_oss.errorIndices'],
apm: {
events: [ProcessorEvent.error],
},
body: {
size: 0,
query: {
bool: {
filter: [
...projection.body.query.bool.filter,
{
term: {
[PROCESSOR_EVENT]: ProcessorEvent.error,
},
},
],
},
},
aggs: {
services: {
terms: {
@ -250,32 +204,18 @@ export const getEnvironments = async ({
setup,
projection,
}: AggregationParams) => {
const { client, indices } = setup;
const response = await client.search(
const { apmEventClient } = setup;
const response = await apmEventClient.search(
mergeProjection(projection, {
index: [
indices['apm_oss.metricsIndices'],
indices['apm_oss.errorIndices'],
indices['apm_oss.transactionIndices'],
],
apm: {
events: [
ProcessorEvent.metric,
ProcessorEvent.transaction,
ProcessorEvent.error,
],
},
body: {
size: 0,
query: {
bool: {
filter: [
...projection.body.query.bool.filter,
{
terms: {
[PROCESSOR_EVENT]: [
ProcessorEvent.transaction,
ProcessorEvent.error,
ProcessorEvent.metric,
],
},
},
],
},
},
aggs: {
services: {
terms: {

View file

@ -4,43 +4,28 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { PROCESSOR_EVENT } from '../../../../common/elasticsearch_fieldnames';
import { ProcessorEvent } from '../../../../common/processor_event';
import { Setup } from '../../helpers/setup_request';
// Note: this logic is duplicated in tutorials/apm/envs/on_prem
export async function hasHistoricalAgentData(setup: Setup) {
const { client, indices } = setup;
const { apmEventClient } = setup;
const params = {
terminateAfter: 1,
index: [
indices['apm_oss.errorIndices'],
indices['apm_oss.metricsIndices'],
indices['apm_oss.sourcemapIndices'],
indices['apm_oss.transactionIndices'],
],
apm: {
events: [
ProcessorEvent.error,
ProcessorEvent.metric,
ProcessorEvent.sourcemap,
ProcessorEvent.transaction,
],
},
body: {
size: 0,
query: {
bool: {
filter: [
{
terms: {
[PROCESSOR_EVENT]: [
'error',
'metric',
'sourcemap',
'transaction',
],
},
},
],
},
},
},
};
const resp = await client.search(params);
const hasHistorialAgentData = resp.hits.total.value > 0;
return hasHistorialAgentData;
const resp = await apmEventClient.search(params);
return resp.hits.total.value > 0;
}

View file

@ -115,6 +115,13 @@ Object {
exports[`agent configuration queries getServiceNames fetches service names 1`] = `
Object {
"apm": Object {
"events": Array [
"transaction",
"error",
"metric",
],
},
"body": Object {
"aggs": Object {
"services": Object {
@ -124,28 +131,8 @@ Object {
},
},
},
"query": Object {
"bool": Object {
"filter": Array [
Object {
"terms": Object {
"processor.event": Array [
"transaction",
"error",
"metric",
],
},
},
],
},
},
"size": 0,
},
"index": Array [
"myIndex",
"myIndex",
"myIndex",
],
}
`;

View file

@ -10,7 +10,7 @@ import {
AgentConfiguration,
AgentConfigurationIntake,
} from '../../../../common/agent_configuration/configuration_types';
import { APMIndexDocumentParams } from '../../helpers/es_client';
import { APMIndexDocumentParams } from '../../helpers/create_es_client/create_internal_es_client';
export async function createOrUpdateConfiguration({
configurationId,

View file

@ -4,11 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { ProcessorEvent } from '../../../../common/processor_event';
import { Setup } from '../../helpers/setup_request';
import {
PROCESSOR_EVENT,
SERVICE_NAME,
} from '../../../../common/elasticsearch_fieldnames';
import { SERVICE_NAME } from '../../../../common/elasticsearch_fieldnames';
import { AGENT_NAME } from '../../../../common/elasticsearch_fieldnames';
export async function getAgentNameByService({
@ -18,25 +16,22 @@ export async function getAgentNameByService({
serviceName: string;
setup: Setup;
}) {
const { client, indices } = setup;
const { apmEventClient } = setup;
const params = {
terminateAfter: 1,
index: [
indices['apm_oss.metricsIndices'],
indices['apm_oss.errorIndices'],
indices['apm_oss.transactionIndices'],
],
apm: {
events: [
ProcessorEvent.transaction,
ProcessorEvent.error,
ProcessorEvent.metric,
],
},
body: {
size: 0,
query: {
bool: {
filter: [
{
terms: { [PROCESSOR_EVENT]: ['transaction', 'error', 'metric'] },
},
{ term: { [SERVICE_NAME]: serviceName } },
],
filter: [{ term: { [SERVICE_NAME]: serviceName } }],
},
},
aggs: {
@ -47,7 +42,7 @@ export async function getAgentNameByService({
},
};
const { aggregations } = await client.search(params);
const { aggregations } = await apmEventClient.search(params);
const agentName = aggregations?.agent_names.buckets[0]?.key;
return agentName as string | undefined;
}

View file

@ -4,37 +4,28 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { ProcessorEvent } from '../../../../common/processor_event';
import { Setup } from '../../helpers/setup_request';
import { PromiseReturnType } from '../../../../../observability/typings/common';
import {
PROCESSOR_EVENT,
SERVICE_NAME,
} from '../../../../common/elasticsearch_fieldnames';
import { SERVICE_NAME } from '../../../../common/elasticsearch_fieldnames';
import { ALL_OPTION_VALUE } from '../../../../common/agent_configuration/all_option';
export type AgentConfigurationServicesAPIResponse = PromiseReturnType<
typeof getServiceNames
>;
export async function getServiceNames({ setup }: { setup: Setup }) {
const { client, indices } = setup;
const { apmEventClient } = setup;
const params = {
index: [
indices['apm_oss.metricsIndices'],
indices['apm_oss.errorIndices'],
indices['apm_oss.transactionIndices'],
],
apm: {
events: [
ProcessorEvent.transaction,
ProcessorEvent.error,
ProcessorEvent.metric,
],
},
body: {
size: 0,
query: {
bool: {
filter: [
{
terms: { [PROCESSOR_EVENT]: ['transaction', 'error', 'metric'] },
},
],
},
},
aggs: {
services: {
terms: {
@ -46,7 +37,7 @@ export async function getServiceNames({ setup }: { setup: Setup }) {
},
};
const resp = await client.search(params);
const resp = await apmEventClient.search(params);
const serviceNames =
resp.aggregations?.services.buckets
.map((bucket) => bucket.key as string)

View file

@ -2,15 +2,15 @@
exports[`custom link get transaction fetches with all filter 1`] = `
Object {
"apm": Object {
"events": Array [
"transaction",
],
},
"body": Object {
"query": Object {
"bool": Object {
"filter": Array [
Object {
"term": Object {
"processor.event": "transaction",
},
},
Object {
"terms": Object {
"service.name": Array [
@ -43,7 +43,6 @@ Object {
},
},
},
"index": "myIndex",
"size": 1,
"terminateAfter": 1,
}
@ -51,20 +50,18 @@ Object {
exports[`custom link get transaction fetches without filter 1`] = `
Object {
"apm": Object {
"events": Array [
"transaction",
],
},
"body": Object {
"query": Object {
"bool": Object {
"filter": Array [
Object {
"term": Object {
"processor.event": "transaction",
},
},
],
"filter": Array [],
},
},
},
"index": "myIndex",
"size": 1,
"terminateAfter": 1,
}

View file

@ -8,9 +8,9 @@ import {
CustomLink,
CustomLinkES,
} from '../../../../common/custom_link/custom_link_types';
import { APMIndexDocumentParams } from '../../helpers/es_client';
import { Setup } from '../../helpers/setup_request';
import { toESFormat } from './helper';
import { APMIndexDocumentParams } from '../../helpers/create_es_client/create_internal_es_client';
export async function createOrUpdateCustomLink({
customLinkId,

View file

@ -4,8 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
import * as t from 'io-ts';
import { PROCESSOR_EVENT } from '../../../../common/elasticsearch_fieldnames';
import { Transaction } from '../../../../typings/es_schemas/ui/transaction';
import { Setup } from '../../helpers/setup_request';
import { ProcessorEvent } from '../../../../common/processor_event';
import { filterOptionsRt } from './custom_link_types';
@ -18,7 +16,7 @@ export async function getTransaction({
setup: Setup;
filters?: t.TypeOf<typeof filterOptionsRt>;
}) {
const { client, indices } = setup;
const { apmEventClient } = setup;
const esFilters = Object.entries(filters)
// loops through the filters splitting the value by comma and removing white spaces
@ -32,19 +30,18 @@ export async function getTransaction({
const params = {
terminateAfter: 1,
index: indices['apm_oss.transactionIndices'],
apm: {
events: [ProcessorEvent.transaction as const],
},
size: 1,
body: {
query: {
bool: {
filter: [
{ term: { [PROCESSOR_EVENT]: ProcessorEvent.transaction } },
...esFilters,
],
filter: esFilters,
},
},
},
};
const resp = await client.search<Transaction>(params);
const resp = await apmEventClient.search(params);
return resp.hits.hits[0]?._source;
}

View file

@ -2,6 +2,11 @@
exports[`trace queries fetches a trace 1`] = `
Object {
"apm": Object {
"events": Array [
"error",
],
},
"body": Object {
"aggs": Object {
"by_transaction_id": Object {
@ -20,11 +25,6 @@ Object {
"trace.id": "foo",
},
},
Object {
"term": Object {
"processor.event": "error",
},
},
Object {
"range": Object {
"@timestamp": Object {
@ -48,6 +48,5 @@ Object {
},
"size": "myIndex",
},
"index": "myIndex",
}
`;

View file

@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { ProcessorEvent } from '../../../common/processor_event';
import {
PROCESSOR_EVENT,
TRACE_ID,
PARENT_ID,
TRANSACTION_DURATION,
@ -13,8 +13,6 @@ import {
TRANSACTION_ID,
ERROR_LOG_LEVEL,
} from '../../../common/elasticsearch_fieldnames';
import { Span } from '../../../typings/es_schemas/ui/span';
import { Transaction } from '../../../typings/es_schemas/ui/transaction';
import { APMError } from '../../../typings/es_schemas/ui/apm_error';
import { rangeFilter } from '../../../common/utils/range_filter';
import { Setup, SetupTimeRange } from '../helpers/setup_request';
@ -28,19 +26,20 @@ export async function getTraceItems(
traceId: string,
setup: Setup & SetupTimeRange
) {
const { start, end, client, config, indices } = setup;
const { start, end, apmEventClient, config } = setup;
const maxTraceItems = config['xpack.apm.ui.maxTraceItems'];
const excludedLogLevels = ['debug', 'info', 'warning'];
const errorResponsePromise = client.search({
index: indices['apm_oss.errorIndices'],
const errorResponsePromise = apmEventClient.search({
apm: {
events: [ProcessorEvent.error],
},
body: {
size: maxTraceItems,
query: {
bool: {
filter: [
{ term: { [TRACE_ID]: traceId } },
{ term: { [PROCESSOR_EVENT]: 'error' } },
{ range: rangeFilter(start, end) },
],
must_not: { terms: { [ERROR_LOG_LEVEL]: excludedLogLevels } },
@ -59,18 +58,16 @@ export async function getTraceItems(
},
});
const traceResponsePromise = client.search({
index: [
indices['apm_oss.spanIndices'],
indices['apm_oss.transactionIndices'],
],
const traceResponsePromise = apmEventClient.search({
apm: {
events: [ProcessorEvent.span, ProcessorEvent.transaction],
},
body: {
size: maxTraceItems,
query: {
bool: {
filter: [
{ term: { [TRACE_ID]: traceId } },
{ terms: { [PROCESSOR_EVENT]: ['span', 'transaction'] } },
{ range: rangeFilter(start, end) },
],
should: {
@ -91,22 +88,17 @@ export async function getTraceItems(
// explicit intermediary types to avoid TS "excessively deep" error
PromiseValueType<typeof errorResponsePromise>,
PromiseValueType<typeof traceResponsePromise>
// @ts-ignore
] = await Promise.all([errorResponsePromise, traceResponsePromise]);
const exceedsMax = traceResponse.hits.total.value > maxTraceItems;
const items = (traceResponse.hits.hits as Array<{
_source: Transaction | Span;
}>).map((hit) => hit._source);
const items = traceResponse.hits.hits.map((hit) => hit._source);
const errorFrequencies: {
errorsPerTransaction: ErrorsPerTransaction;
errorDocs: APMError[];
} = {
errorDocs: errorResponse.hits.hits.map(
({ _source }) => _source as APMError
),
errorDocs: errorResponse.hits.hits.map(({ _source }) => _source),
errorsPerTransaction:
errorResponse.aggregations?.by_transaction_id.buckets.reduce(
(acc, current) => {

View file

@ -3,6 +3,11 @@
exports[`transaction group queries fetches top traces 1`] = `
Array [
Object {
"apm": Object {
"events": Array [
"transaction",
],
},
"body": Object {
"aggs": Object {
"transaction_groups": Object {
@ -46,11 +51,6 @@ Array [
},
},
},
Object {
"term": Object {
"processor.event": "transaction",
},
},
Object {
"term": Object {
"my.custom.ui.filter": "foo-bar",
@ -84,10 +84,14 @@ Array [
},
],
},
"index": "myIndex",
"size": 0,
},
Object {
"apm": Object {
"events": Array [
"transaction",
],
},
"body": Object {
"aggs": Object {
"transaction_groups": Object {
@ -131,11 +135,6 @@ Array [
},
},
},
Object {
"term": Object {
"processor.event": "transaction",
},
},
Object {
"term": Object {
"my.custom.ui.filter": "foo-bar",
@ -152,10 +151,14 @@ Array [
},
},
},
"index": "myIndex",
"size": 0,
},
Object {
"apm": Object {
"events": Array [
"transaction",
],
},
"body": Object {
"aggs": Object {
"transaction_groups": Object {
@ -199,11 +202,6 @@ Array [
},
},
},
Object {
"term": Object {
"processor.event": "transaction",
},
},
Object {
"term": Object {
"my.custom.ui.filter": "foo-bar",
@ -220,7 +218,6 @@ Array [
},
},
},
"index": "myIndex",
"size": 0,
},
]
@ -229,6 +226,11 @@ Array [
exports[`transaction group queries fetches top transactions 1`] = `
Array [
Object {
"apm": Object {
"events": Array [
"transaction",
],
},
"body": Object {
"aggs": Object {
"transaction_groups": Object {
@ -257,11 +259,6 @@ Array [
},
},
},
Object {
"term": Object {
"processor.event": "transaction",
},
},
Object {
"term": Object {
"transaction.type": "bar",
@ -298,10 +295,14 @@ Array [
},
],
},
"index": "myIndex",
"size": 0,
},
Object {
"apm": Object {
"events": Array [
"transaction",
],
},
"body": Object {
"aggs": Object {
"transaction_groups": Object {
@ -330,11 +331,6 @@ Array [
},
},
},
Object {
"term": Object {
"processor.event": "transaction",
},
},
Object {
"term": Object {
"transaction.type": "bar",
@ -354,10 +350,14 @@ Array [
},
},
},
"index": "myIndex",
"size": 0,
},
Object {
"apm": Object {
"events": Array [
"transaction",
],
},
"body": Object {
"aggs": Object {
"transaction_groups": Object {
@ -386,11 +386,6 @@ Array [
},
},
},
Object {
"term": Object {
"processor.event": "transaction",
},
},
Object {
"term": Object {
"transaction.type": "bar",
@ -410,10 +405,14 @@ Array [
},
},
},
"index": "myIndex",
"size": 0,
},
Object {
"apm": Object {
"events": Array [
"transaction",
],
},
"body": Object {
"aggs": Object {
"transaction_groups": Object {
@ -448,11 +447,6 @@ Array [
},
},
},
Object {
"term": Object {
"processor.event": "transaction",
},
},
Object {
"term": Object {
"transaction.type": "bar",
@ -472,7 +466,6 @@ Array [
},
},
},
"index": "myIndex",
"size": 0,
},
]

View file

@ -7,13 +7,12 @@ import { take, sortBy } from 'lodash';
import { Unionize } from 'utility-types';
import moment from 'moment';
import { joinByKey } from '../../../common/utils/join_by_key';
import { ESSearchRequest } from '../../../typings/elasticsearch';
import {
SERVICE_NAME,
TRANSACTION_NAME,
} from '../../../common/elasticsearch_fieldnames';
import { getTransactionGroupsProjection } from '../../../common/projections/transaction_groups';
import { mergeProjection } from '../../../common/projections/util/merge_projection';
import { getTransactionGroupsProjection } from '../../projections/transaction_groups';
import { mergeProjection } from '../../projections/util/merge_projection';
import { PromiseReturnType } from '../../../../observability/typings/common';
import { AggregationOptionsByType } from '../../../typings/elasticsearch/aggregations';
import { Transaction } from '../../../typings/es_schemas/ui/transaction';
@ -45,7 +44,9 @@ export type Options = TopTransactionOptions | TopTraceOptions;
export type ESResponse = PromiseReturnType<typeof transactionGroupsFetcher>;
export type TransactionGroupRequestBase = ESSearchRequest & {
export type TransactionGroupRequestBase = ReturnType<
typeof getTransactionGroupsProjection
> & {
body: {
aggs: {
transaction_groups: Unionize<

View file

@ -5,7 +5,6 @@
*/
import { mean } from 'lodash';
import {
PROCESSOR_EVENT,
HTTP_RESPONSE_STATUS_CODE,
TRANSACTION_NAME,
TRANSACTION_TYPE,
@ -31,7 +30,7 @@ export async function getErrorRate({
transactionName?: string;
setup: Setup & SetupTimeRange & SetupUIFilters;
}) {
const { start, end, uiFiltersES, client, indices } = setup;
const { start, end, uiFiltersES, apmEventClient } = setup;
const transactionNamefilter = transactionName
? [{ term: { [TRANSACTION_NAME]: transactionName } }]
@ -42,7 +41,6 @@ export async function getErrorRate({
const filter = [
{ term: { [SERVICE_NAME]: serviceName } },
{ term: { [PROCESSOR_EVENT]: ProcessorEvent.transaction } },
{ range: rangeFilter(start, end) },
{ exists: { field: HTTP_RESPONSE_STATUS_CODE } },
...transactionNamefilter,
@ -51,7 +49,9 @@ export async function getErrorRate({
];
const params = {
index: indices['apm_oss.transactionIndices'],
apm: {
events: [ProcessorEvent.transaction],
},
body: {
size: 0,
query: { bool: { filter } },
@ -68,7 +68,7 @@ export async function getErrorRate({
},
};
const resp = await client.search(params);
const resp = await apmEventClient.search(params);
const noHits = resp.hits.total.value === 0;

View file

@ -5,7 +5,6 @@
*/
import { merge } from 'lodash';
import { arrayUnionToCallable } from '../../../common/utils/array_union_to_callable';
import { Transaction } from '../../../typings/es_schemas/ui/transaction';
import {
TRANSACTION_SAMPLED,
TRANSACTION_DURATION,
@ -52,7 +51,7 @@ export async function getSamples({ request, setup }: MetricParams) {
{ '@timestamp': { order: 'desc' as const } },
];
const response = await setup.client.search({
const response = await setup.apmEventClient.search({
...params,
body: {
...params.body,
@ -73,7 +72,7 @@ export async function getSamples({ request, setup }: MetricParams) {
return {
key: bucket.key as BucketKey,
count: bucket.doc_count,
sample: bucket.sample.hits.hits[0]._source as Transaction,
sample: bucket.sample.hits.hits[0]._source,
};
});
}
@ -87,7 +86,7 @@ export async function getAverages({ request, setup }: MetricParams) {
},
});
const response = await setup.client.search(params);
const response = await setup.apmEventClient.search(params);
return arrayUnionToCallable(
response.aggregations?.transaction_groups.buckets ?? []
@ -108,7 +107,7 @@ export async function getSums({ request, setup }: MetricParams) {
},
});
const response = await setup.client.search(params);
const response = await setup.apmEventClient.search(params);
return arrayUnionToCallable(
response.aggregations?.transaction_groups.buckets ?? []
@ -131,7 +130,7 @@ export async function getPercentiles({ request, setup }: MetricParams) {
},
});
const response = await setup.client.search(params);
const response = await setup.apmEventClient.search(params);
return arrayUnionToCallable(
response.aggregations?.transaction_groups.buckets ?? []

View file

@ -2,15 +2,15 @@
exports[`transaction queries fetches a transaction 1`] = `
Object {
"apm": Object {
"events": Array [
"transaction",
],
},
"body": Object {
"query": Object {
"bool": Object {
"filter": Array [
Object {
"term": Object {
"processor.event": "transaction",
},
},
Object {
"term": Object {
"transaction.id": "foo",
@ -35,12 +35,16 @@ Object {
},
"size": 1,
},
"index": "myIndex",
}
`;
exports[`transaction queries fetches breakdown data for transactions 1`] = `
Object {
"apm": Object {
"events": Array [
"metric",
],
},
"body": Object {
"aggs": Object {
"by_date": Object {
@ -146,11 +150,6 @@ Object {
"transaction.type": "bar",
},
},
Object {
"term": Object {
"processor.event": "metric",
},
},
Object {
"range": Object {
"@timestamp": Object {
@ -170,12 +169,16 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;
exports[`transaction queries fetches breakdown data for transactions for a transaction name 1`] = `
Object {
"apm": Object {
"events": Array [
"metric",
],
},
"body": Object {
"aggs": Object {
"by_date": Object {
@ -281,11 +284,6 @@ Object {
"transaction.type": "bar",
},
},
Object {
"term": Object {
"processor.event": "metric",
},
},
Object {
"range": Object {
"@timestamp": Object {
@ -310,12 +308,16 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;
exports[`transaction queries fetches transaction charts 1`] = `
Object {
"apm": Object {
"events": Array [
"transaction",
],
},
"body": Object {
"aggs": Object {
"overall_avg_duration": Object {
@ -376,11 +378,6 @@ Object {
"query": Object {
"bool": Object {
"filter": Array [
Object {
"term": Object {
"processor.event": "transaction",
},
},
Object {
"term": Object {
"service.name": "foo",
@ -405,12 +402,16 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;
exports[`transaction queries fetches transaction charts for a transaction type 1`] = `
Object {
"apm": Object {
"events": Array [
"transaction",
],
},
"body": Object {
"aggs": Object {
"overall_avg_duration": Object {
@ -471,11 +472,6 @@ Object {
"query": Object {
"bool": Object {
"filter": Array [
Object {
"term": Object {
"processor.event": "transaction",
},
},
Object {
"term": Object {
"service.name": "foo",
@ -505,12 +501,16 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;
exports[`transaction queries fetches transaction charts for a transaction type and transaction name 1`] = `
Object {
"apm": Object {
"events": Array [
"transaction",
],
},
"body": Object {
"aggs": Object {
"overall_avg_duration": Object {
@ -571,11 +571,6 @@ Object {
"query": Object {
"bool": Object {
"filter": Array [
Object {
"term": Object {
"processor.event": "transaction",
},
},
Object {
"term": Object {
"service.name": "foo",
@ -610,12 +605,16 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;
exports[`transaction queries fetches transaction distribution 1`] = `
Object {
"apm": Object {
"events": Array [
"transaction",
],
},
"body": Object {
"aggs": Object {
"stats": Object {
@ -632,11 +631,6 @@ Object {
"service.name": "foo",
},
},
Object {
"term": Object {
"processor.event": "transaction",
},
},
Object {
"term": Object {
"transaction.type": "baz",
@ -666,6 +660,5 @@ Object {
},
"size": 0,
},
"index": "myIndex",
}
`;

View file

@ -15,7 +15,7 @@ describe('fetcher', () => {
it('performs a search', async () => {
const search = jest.fn();
const setup = ({
client: { search },
apmEventClient: { search },
indices: {},
uiFiltersES: [],
} as unknown) as Setup & SetupTimeRange & SetupUIFilters;

View file

@ -7,7 +7,6 @@
import { ESFilter } from '../../../../typings/elasticsearch';
import { PromiseReturnType } from '../../../../../observability/typings/common';
import {
PROCESSOR_EVENT,
SERVICE_NAME,
TRANSACTION_TYPE,
USER_AGENT_NAME,
@ -23,7 +22,7 @@ import { ProcessorEvent } from '../../../../common/processor_event';
export type ESResponse = PromiseReturnType<typeof fetcher>;
export function fetcher(options: Options) {
const { end, client, indices, start, uiFiltersES } = options.setup;
const { end, apmEventClient, start, uiFiltersES } = options.setup;
const { serviceName, transactionName } = options;
const { intervalString } = getBucketSize(start, end, 'auto');
@ -32,7 +31,6 @@ export function fetcher(options: Options) {
: [];
const filter: ESFilter[] = [
{ term: { [PROCESSOR_EVENT]: ProcessorEvent.transaction } },
{ term: { [SERVICE_NAME]: serviceName } },
{ term: { [TRANSACTION_TYPE]: TRANSACTION_PAGE_LOAD } },
{ range: rangeFilter(start, end) },
@ -41,7 +39,9 @@ export function fetcher(options: Options) {
];
const params = {
index: indices['apm_oss.transactionIndices'],
apm: {
events: [ProcessorEvent.transaction],
},
body: {
size: 0,
query: { bool: { filter } },
@ -80,5 +80,5 @@ export function fetcher(options: Options) {
},
};
return client.search(params);
return apmEventClient.search(params);
}

View file

@ -4,9 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { ProcessorEvent } from '../../../../common/processor_event';
import {
CLIENT_GEO_COUNTRY_ISO_CODE,
PROCESSOR_EVENT,
SERVICE_NAME,
TRANSACTION_DURATION,
TRANSACTION_TYPE,
@ -29,12 +29,14 @@ export async function getTransactionAvgDurationByCountry({
serviceName: string;
transactionName?: string;
}) {
const { uiFiltersES, client, start, end, indices } = setup;
const { uiFiltersES, apmEventClient, start, end } = setup;
const transactionNameFilter = transactionName
? [{ term: { [TRANSACTION_NAME]: transactionName } }]
: [];
const params = {
index: indices['apm_oss.transactionIndices'],
apm: {
events: [ProcessorEvent.transaction],
},
body: {
size: 0,
query: {
@ -42,7 +44,6 @@ export async function getTransactionAvgDurationByCountry({
filter: [
{ term: { [SERVICE_NAME]: serviceName } },
...transactionNameFilter,
{ term: { [PROCESSOR_EVENT]: 'transaction' } },
{ term: { [TRANSACTION_TYPE]: TRANSACTION_PAGE_LOAD } },
{ exists: { field: CLIENT_GEO_COUNTRY_ISO_CODE } },
{ range: rangeFilter(start, end) },
@ -66,7 +67,7 @@ export async function getTransactionAvgDurationByCountry({
},
};
const resp = await client.search(params);
const resp = await apmEventClient.search(params);
if (!resp.aggregations) {
return [];

View file

@ -26,7 +26,7 @@ function getMockSetup(esResponse: any) {
return {
start: 0,
end: 500000,
client: { search: clientSpy } as any,
apmEventClient: { search: clientSpy } as any,
internalClient: { search: clientSpy } as any,
config: new Proxy(
{},

View file

@ -5,6 +5,7 @@
*/
import { flatten, orderBy, last } from 'lodash';
import { ProcessorEvent } from '../../../../common/processor_event';
import {
SERVICE_NAME,
SPAN_SUBTYPE,
@ -13,7 +14,6 @@ import {
TRANSACTION_TYPE,
TRANSACTION_NAME,
TRANSACTION_BREAKDOWN_COUNT,
PROCESSOR_EVENT,
} from '../../../../common/elasticsearch_fieldnames';
import {
Setup,
@ -36,7 +36,7 @@ export async function getTransactionBreakdown({
transactionName?: string;
transactionType: string;
}) {
const { uiFiltersES, client, start, end, indices } = setup;
const { uiFiltersES, apmEventClient, start, end } = setup;
const subAggs = {
sum_all_self_times: {
@ -82,7 +82,6 @@ export async function getTransactionBreakdown({
const filters = [
{ term: { [SERVICE_NAME]: serviceName } },
{ term: { [TRANSACTION_TYPE]: transactionType } },
{ term: { [PROCESSOR_EVENT]: 'metric' } },
{ range: rangeFilter(start, end) },
...uiFiltersES,
];
@ -92,7 +91,9 @@ export async function getTransactionBreakdown({
}
const params = {
index: indices['apm_oss.metricsIndices'],
apm: {
events: [ProcessorEvent.metric],
},
body: {
size: 0,
query: {
@ -110,7 +111,7 @@ export async function getTransactionBreakdown({
},
};
const resp = await client.search(params);
const resp = await apmEventClient.search(params);
const formatBucket = (
aggs:

View file

@ -4,6 +4,11 @@ exports[`timeseriesFetcher should call client with correct query 1`] = `
Array [
Array [
Object {
"apm": Object {
"events": Array [
"transaction",
],
},
"body": Object {
"aggs": Object {
"overall_avg_duration": Object {
@ -64,11 +69,6 @@ Array [
"query": Object {
"bool": Object {
"filter": Array [
Object {
"term": Object {
"processor.event": "transaction",
},
},
Object {
"term": Object {
"service.name": "myServiceName",
@ -98,7 +98,6 @@ Array [
},
"size": 0,
},
"index": "myIndex",
},
],
]

View file

@ -4,9 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { PROCESSOR_EVENT } from '../../../../../common/elasticsearch_fieldnames';
import { ESResponse, timeseriesFetcher } from './fetcher';
import { APMConfig } from '../../../../../server';
import { ProcessorEvent } from '../../../../../common/processor_event';
describe('timeseriesFetcher', () => {
let res: ESResponse;
@ -21,7 +21,7 @@ describe('timeseriesFetcher', () => {
setup: {
start: 1528113600000,
end: 1528977600000,
client: { search: clientSpy } as any,
apmEventClient: { search: clientSpy } as any,
internalClient: { search: clientSpy } as any,
config: new Proxy(
{},
@ -54,15 +54,7 @@ describe('timeseriesFetcher', () => {
it('should restrict results to only transaction documents', () => {
const query = clientSpy.mock.calls[0][0];
expect(query.body.query.bool.filter).toEqual(
expect.arrayContaining([
{
term: {
[PROCESSOR_EVENT]: 'transaction',
},
} as any,
])
);
expect(query.apm.events).toEqual([ProcessorEvent.transaction]);
});
it('should return correct response', () => {

View file

@ -4,9 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { ProcessorEvent } from '../../../../../common/processor_event';
import { ESFilter } from '../../../../../typings/elasticsearch';
import {
PROCESSOR_EVENT,
SERVICE_NAME,
TRANSACTION_DURATION,
TRANSACTION_NAME,
@ -34,11 +34,10 @@ export function timeseriesFetcher({
transactionName: string | undefined;
setup: Setup & SetupTimeRange & SetupUIFilters;
}) {
const { start, end, uiFiltersES, client, indices } = setup;
const { start, end, uiFiltersES, apmEventClient } = setup;
const { intervalString } = getBucketSize(start, end, 'auto');
const filter: ESFilter[] = [
{ term: { [PROCESSOR_EVENT]: 'transaction' } },
{ term: { [SERVICE_NAME]: serviceName } },
{ range: rangeFilter(start, end) },
...uiFiltersES,
@ -54,7 +53,9 @@ export function timeseriesFetcher({
}
const params = {
index: indices['apm_oss.transactionIndices'],
apm: {
events: [ProcessorEvent.transaction as const],
},
body: {
size: 0,
query: { bool: { filter } },
@ -95,5 +96,5 @@ export function timeseriesFetcher({
},
};
return client.search(params);
return apmEventClient.search(params);
}

View file

@ -4,9 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { Transaction } from '../../../../../typings/es_schemas/ui/transaction';
import { ProcessorEvent } from '../../../../../common/processor_event';
import {
PROCESSOR_EVENT,
SERVICE_NAME,
TRACE_ID,
TRANSACTION_DURATION,
@ -32,17 +31,18 @@ export async function bucketFetcher(
bucketSize: number,
setup: Setup & SetupTimeRange & SetupUIFilters
) {
const { start, end, uiFiltersES, client, indices } = setup;
const { start, end, uiFiltersES, apmEventClient } = setup;
const params = {
index: indices['apm_oss.transactionIndices'],
apm: {
events: [ProcessorEvent.transaction as const],
},
body: {
size: 0,
query: {
bool: {
filter: [
{ term: { [SERVICE_NAME]: serviceName } },
{ term: { [PROCESSOR_EVENT]: 'transaction' } },
{ term: { [TRANSACTION_TYPE]: transactionType } },
{ term: { [TRANSACTION_NAME]: transactionName } },
{ range: rangeFilter(start, end) },
@ -85,7 +85,7 @@ export async function bucketFetcher(
},
};
const response = await client.search<Transaction, typeof params>(params);
const response = await apmEventClient.search(params);
return response;
}

View file

@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { ProcessorEvent } from '../../../../common/processor_event';
import {
PROCESSOR_EVENT,
SERVICE_NAME,
TRANSACTION_DURATION,
TRANSACTION_NAME,
@ -23,17 +23,18 @@ export async function getDistributionMax(
transactionType: string,
setup: Setup & SetupTimeRange & SetupUIFilters
) {
const { start, end, uiFiltersES, client, indices } = setup;
const { start, end, uiFiltersES, apmEventClient } = setup;
const params = {
index: indices['apm_oss.transactionIndices'],
apm: {
events: [ProcessorEvent.transaction],
},
body: {
size: 0,
query: {
bool: {
filter: [
{ term: { [SERVICE_NAME]: serviceName } },
{ term: { [PROCESSOR_EVENT]: 'transaction' } },
{ term: { [TRANSACTION_TYPE]: transactionType } },
{ term: { [TRANSACTION_NAME]: transactionName } },
{
@ -59,6 +60,6 @@ export async function getDistributionMax(
},
};
const resp = await client.search(params);
const resp = await apmEventClient.search(params);
return resp.aggregations ? resp.aggregations.stats.max : null;
}

View file

@ -5,11 +5,9 @@
*/
import {
PROCESSOR_EVENT,
TRACE_ID,
TRANSACTION_ID,
} from '../../../../common/elasticsearch_fieldnames';
import { Transaction } from '../../../../typings/es_schemas/ui/transaction';
import { rangeFilter } from '../../../../common/utils/range_filter';
import {
Setup,
@ -27,16 +25,17 @@ export async function getTransaction({
traceId: string;
setup: Setup & SetupTimeRange & SetupUIFilters;
}) {
const { start, end, client, indices } = setup;
const { start, end, apmEventClient } = setup;
const params = {
index: indices['apm_oss.transactionIndices'],
const resp = await apmEventClient.search({
apm: {
events: [ProcessorEvent.transaction],
},
body: {
size: 1,
query: {
bool: {
filter: [
{ term: { [PROCESSOR_EVENT]: ProcessorEvent.transaction } },
{ term: { [TRANSACTION_ID]: transactionId } },
{ term: { [TRACE_ID]: traceId } },
{ range: rangeFilter(start, end) },
@ -44,8 +43,7 @@ export async function getTransaction({
},
},
},
};
});
const resp = await client.search<Transaction>(params);
return resp.hits.hits[0]?._source;
}

View file

@ -5,11 +5,9 @@
*/
import {
PROCESSOR_EVENT,
TRACE_ID,
PARENT_ID,
} from '../../../../common/elasticsearch_fieldnames';
import { Transaction } from '../../../../typings/es_schemas/ui/transaction';
import { Setup } from '../../helpers/setup_request';
import { ProcessorEvent } from '../../../../common/processor_event';
@ -17,9 +15,12 @@ export async function getRootTransactionByTraceId(
traceId: string,
setup: Setup
) {
const { client, indices } = setup;
const { apmEventClient } = setup;
const params = {
index: indices['apm_oss.transactionIndices'],
apm: {
events: [ProcessorEvent.transaction as const],
},
body: {
size: 1,
query: {
@ -35,16 +36,13 @@ export async function getRootTransactionByTraceId(
},
},
],
filter: [
{ term: { [TRACE_ID]: traceId } },
{ term: { [PROCESSOR_EVENT]: ProcessorEvent.transaction } },
],
filter: [{ term: { [TRACE_ID]: traceId } }],
},
},
},
};
const resp = await client.search<Transaction>(params);
const resp = await apmEventClient.search(params);
return {
transaction: resp.hits.hits[0]?._source,
};

View file

@ -2,6 +2,13 @@
exports[`ui filter queries fetches environments 1`] = `
Object {
"apm": Object {
"events": Array [
"transaction",
"metric",
"error",
],
},
"body": Object {
"aggs": Object {
"environments": Object {
@ -14,15 +21,6 @@ Object {
"query": Object {
"bool": Object {
"filter": Array [
Object {
"terms": Object {
"processor.event": Array [
"transaction",
"error",
"metric",
],
},
},
Object {
"range": Object {
"@timestamp": Object {
@ -42,16 +40,18 @@ Object {
},
"size": 0,
},
"index": Array [
"myIndex",
"myIndex",
"myIndex",
],
}
`;
exports[`ui filter queries fetches environments without a service name 1`] = `
Object {
"apm": Object {
"events": Array [
"transaction",
"metric",
"error",
],
},
"body": Object {
"aggs": Object {
"environments": Object {
@ -64,15 +64,6 @@ Object {
"query": Object {
"bool": Object {
"filter": Array [
Object {
"terms": Object {
"processor.event": Array [
"transaction",
"error",
"metric",
],
},
},
Object {
"range": Object {
"@timestamp": Object {
@ -87,10 +78,5 @@ Object {
},
"size": 0,
},
"index": Array [
"myIndex",
"myIndex",
"myIndex",
],
}
`;

View file

@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { ProcessorEvent } from '../../../common/processor_event';
import {
PROCESSOR_EVENT,
SERVICE_ENVIRONMENT,
SERVICE_NAME,
} from '../../../common/elasticsearch_fieldnames';
@ -18,12 +18,9 @@ export async function getEnvironments(
setup: Setup & SetupTimeRange,
serviceName?: string
) {
const { start, end, client, indices } = setup;
const { start, end, apmEventClient } = setup;
const filter: ESFilter[] = [
{ terms: { [PROCESSOR_EVENT]: ['transaction', 'error', 'metric'] } },
{ range: rangeFilter(start, end) },
];
const filter: ESFilter[] = [{ range: rangeFilter(start, end) }];
if (serviceName) {
filter.push({
@ -32,11 +29,13 @@ export async function getEnvironments(
}
const params = {
index: [
indices['apm_oss.metricsIndices'],
indices['apm_oss.errorIndices'],
indices['apm_oss.transactionIndices'],
],
apm: {
events: [
ProcessorEvent.transaction,
ProcessorEvent.metric,
ProcessorEvent.error,
],
},
body: {
size: 0,
query: {
@ -55,7 +54,7 @@ export async function getEnvironments(
},
};
const resp = await client.search(params);
const resp = await apmEventClient.search(params);
const aggs = resp.aggregations;
const environmentsBuckets = aggs?.environments.buckets || [];

View file

@ -2,6 +2,13 @@
exports[`local ui filter queries fetches local ui filter aggregations 1`] = `
Object {
"apm": Object {
"events": Array [
"transaction",
"metric",
"error",
],
},
"body": Object {
"aggs": Object {
"by_terms": Object {
@ -28,15 +35,6 @@ Object {
"query": Object {
"bool": Object {
"filter": Array [
Object {
"terms": Object {
"processor.event": Array [
"transaction",
"error",
"metric",
],
},
},
Object {
"range": Object {
"@timestamp": Object {
@ -56,10 +54,5 @@ Object {
},
"size": 0,
},
"index": Array [
"myIndex",
"myIndex",
"myIndex",
],
}
`;

View file

@ -5,8 +5,8 @@
*/
import { omit } from 'lodash';
import { mergeProjection } from '../../../../common/projections/util/merge_projection';
import { Projection } from '../../../../common/projections/typings';
import { mergeProjection } from '../../../projections/util/merge_projection';
import { Projection } from '../../../projections/typings';
import { UIFilters } from '../../../../typings/ui_filters';
import { getUiFiltersES } from '../../helpers/convert_ui_filters/get_ui_filters_es';
import { localUIFilters, LocalUIFilterName } from './config';

View file

@ -5,7 +5,7 @@
*/
import { cloneDeep, orderBy } from 'lodash';
import { UIFilters } from '../../../../typings/ui_filters';
import { Projection } from '../../../../common/projections/typings';
import { Projection } from '../../../projections/typings';
import { PromiseReturnType } from '../../../../../observability/typings/common';
import { getLocalFilterQuery } from './get_local_filter_query';
import { Setup } from '../../helpers/setup_request';
@ -26,7 +26,7 @@ export async function getLocalUIFilters({
uiFilters: UIFilters;
localFilterNames: LocalUIFilterName[];
}) {
const { client } = setup;
const { apmEventClient } = setup;
const projectionWithoutAggs = cloneDeep(projection);
@ -40,7 +40,7 @@ export async function getLocalUIFilters({
localUIFilterName: name,
});
const response = await client.search(query);
const response = await apmEventClient.search(query);
const filter = localUIFilters[name];

View file

@ -9,7 +9,7 @@ import {
SearchParamsMock,
inspectSearchParams,
} from '../../../../public/utils/testHelpers';
import { getServicesProjection } from '../../../../common/projections/services';
import { getServicesProjection } from '../../../projections/services';
describe('local ui filter queries', () => {
let mock: SearchParamsMock;

View file

@ -8,15 +8,13 @@ import {
Setup,
SetupTimeRange,
SetupUIFilters,
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
} from '../../server/lib/helpers/setup_request';
import {
PROCESSOR_EVENT,
SERVICE_NAME,
ERROR_GROUP_ID,
} from '../elasticsearch_fieldnames';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { rangeFilter } from '../utils/range_filter';
} from '../../common/elasticsearch_fieldnames';
import { rangeFilter } from '../../common/utils/range_filter';
import { ProcessorEvent } from '../../common/processor_event';
export function getErrorGroupsProjection({
setup,
@ -25,16 +23,17 @@ export function getErrorGroupsProjection({
setup: Setup & SetupTimeRange & SetupUIFilters;
serviceName: string;
}) {
const { start, end, uiFiltersES, indices } = setup;
const { start, end, uiFiltersES } = setup;
return {
index: indices['apm_oss.errorIndices'],
apm: {
events: [ProcessorEvent.error as const],
},
body: {
query: {
bool: {
filter: [
{ term: { [SERVICE_NAME]: serviceName } },
{ term: { [PROCESSOR_EVENT]: 'error' } },
{ range: rangeFilter(start, end) },
...uiFiltersES,
],

Some files were not shown because too many files have changed in this diff Show more