[APM] Fix bucket size in alert previews (#111304)
* fixing preview charts * using metrics * fixing ts issues * refactoring * refactoring useServiceTransactionTypesFetcher * addressing pr comments * addressing pr comments * addressing pr comments
This commit is contained in:
parent
6e9b1b57b8
commit
c51c92cd7a
|
@ -15,7 +15,7 @@ import { useEnvironmentsFetcher } from '../../../hooks/use_environments_fetcher'
|
|||
import { useFetcher } from '../../../hooks/use_fetcher';
|
||||
import { ChartPreview } from '../chart_preview';
|
||||
import { EnvironmentField, IsAboveField, ServiceField } from '../fields';
|
||||
import { AlertMetadata, getAbsoluteTimeRange } from '../helper';
|
||||
import { AlertMetadata, getIntervalAndTimeRange, TimeUnit } from '../helper';
|
||||
import { ServiceAlertTrigger } from '../service_alert_trigger';
|
||||
|
||||
export interface AlertParams {
|
||||
|
@ -54,14 +54,20 @@ export function ErrorCountAlertTrigger(props: Props) {
|
|||
|
||||
const { data } = useFetcher(
|
||||
(callApmApi) => {
|
||||
if (params.windowSize && params.windowUnit) {
|
||||
const { interval, start, end } = getIntervalAndTimeRange({
|
||||
windowSize: params.windowSize,
|
||||
windowUnit: params.windowUnit as TimeUnit,
|
||||
});
|
||||
if (interval && start && end) {
|
||||
return callApmApi({
|
||||
endpoint: 'GET /api/apm/alerts/chart_preview/transaction_error_count',
|
||||
params: {
|
||||
query: {
|
||||
...getAbsoluteTimeRange(params.windowSize, params.windowUnit),
|
||||
environment: params.environment,
|
||||
serviceName: params.serviceName,
|
||||
interval,
|
||||
start,
|
||||
end,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import datemath from '@elastic/datemath';
|
||||
import moment from 'moment';
|
||||
|
||||
export interface AlertMetadata {
|
||||
environment: string;
|
||||
|
@ -15,12 +14,25 @@ export interface AlertMetadata {
|
|||
end?: string;
|
||||
}
|
||||
|
||||
export function getAbsoluteTimeRange(windowSize: number, windowUnit: string) {
|
||||
const now = new Date().toISOString();
|
||||
export type TimeUnit = 's' | 'm' | 'h' | 'd';
|
||||
|
||||
const BUCKET_SIZE = 20;
|
||||
|
||||
export function getIntervalAndTimeRange({
|
||||
windowSize,
|
||||
windowUnit,
|
||||
}: {
|
||||
windowSize: number;
|
||||
windowUnit: TimeUnit;
|
||||
}) {
|
||||
const end = Date.now();
|
||||
const start =
|
||||
end -
|
||||
moment.duration(windowSize, windowUnit).asMilliseconds() * BUCKET_SIZE;
|
||||
|
||||
return {
|
||||
start:
|
||||
datemath.parse(`now-${windowSize}${windowUnit}`)?.toISOString() ?? now,
|
||||
end: now,
|
||||
interval: `${windowSize}${windowUnit}`,
|
||||
start: new Date(start).toISOString(),
|
||||
end: new Date(end).toISOString(),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ import {
|
|||
ServiceField,
|
||||
TransactionTypeField,
|
||||
} from '../fields';
|
||||
import { AlertMetadata, getAbsoluteTimeRange } from '../helper';
|
||||
import { AlertMetadata, getIntervalAndTimeRange, TimeUnit } from '../helper';
|
||||
import { ServiceAlertTrigger } from '../service_alert_trigger';
|
||||
import { PopoverExpression } from '../service_alert_trigger/popover_expression';
|
||||
|
||||
|
@ -77,9 +77,11 @@ export function TransactionDurationAlertTrigger(props: Props) {
|
|||
|
||||
createCallApmApi(services as CoreStart);
|
||||
|
||||
const transactionTypes = useServiceTransactionTypesFetcher(
|
||||
metadata?.serviceName
|
||||
);
|
||||
const transactionTypes = useServiceTransactionTypesFetcher({
|
||||
serviceName: metadata?.serviceName,
|
||||
start: metadata?.start,
|
||||
end: metadata?.end,
|
||||
});
|
||||
|
||||
const params = defaults(
|
||||
{
|
||||
|
@ -104,16 +106,22 @@ export function TransactionDurationAlertTrigger(props: Props) {
|
|||
|
||||
const { data } = useFetcher(
|
||||
(callApmApi) => {
|
||||
if (params.windowSize && params.windowUnit) {
|
||||
const { interval, start, end } = getIntervalAndTimeRange({
|
||||
windowSize: params.windowSize,
|
||||
windowUnit: params.windowUnit as TimeUnit,
|
||||
});
|
||||
if (interval && start && end) {
|
||||
return callApmApi({
|
||||
endpoint: 'GET /api/apm/alerts/chart_preview/transaction_duration',
|
||||
params: {
|
||||
query: {
|
||||
...getAbsoluteTimeRange(params.windowSize, params.windowUnit),
|
||||
aggregationType: params.aggregationType,
|
||||
environment: params.environment,
|
||||
serviceName: params.serviceName,
|
||||
transactionType: params.transactionType,
|
||||
interval,
|
||||
start,
|
||||
end,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -48,9 +48,11 @@ interface Props {
|
|||
export function TransactionDurationAnomalyAlertTrigger(props: Props) {
|
||||
const { alertParams, metadata, setAlertParams, setAlertProperty } = props;
|
||||
|
||||
const transactionTypes = useServiceTransactionTypesFetcher(
|
||||
metadata?.serviceName
|
||||
);
|
||||
const transactionTypes = useServiceTransactionTypesFetcher({
|
||||
serviceName: metadata?.serviceName,
|
||||
start: metadata?.start,
|
||||
end: metadata?.end,
|
||||
});
|
||||
|
||||
const params = defaults(
|
||||
{
|
||||
|
|
|
@ -23,7 +23,7 @@ import {
|
|||
ServiceField,
|
||||
TransactionTypeField,
|
||||
} from '../fields';
|
||||
import { AlertMetadata, getAbsoluteTimeRange } from '../helper';
|
||||
import { AlertMetadata, getIntervalAndTimeRange, TimeUnit } from '../helper';
|
||||
import { ServiceAlertTrigger } from '../service_alert_trigger';
|
||||
|
||||
interface AlertParams {
|
||||
|
@ -47,10 +47,11 @@ export function TransactionErrorRateAlertTrigger(props: Props) {
|
|||
const { alertParams, metadata, setAlertParams, setAlertProperty } = props;
|
||||
|
||||
createCallApmApi(services as CoreStart);
|
||||
|
||||
const transactionTypes = useServiceTransactionTypesFetcher(
|
||||
metadata?.serviceName
|
||||
);
|
||||
const transactionTypes = useServiceTransactionTypesFetcher({
|
||||
serviceName: metadata?.serviceName,
|
||||
start: metadata?.start,
|
||||
end: metadata?.end,
|
||||
});
|
||||
|
||||
const params = defaults(
|
||||
{ ...omit(metadata, ['start', 'end']), ...alertParams },
|
||||
|
@ -72,15 +73,21 @@ export function TransactionErrorRateAlertTrigger(props: Props) {
|
|||
|
||||
const { data } = useFetcher(
|
||||
(callApmApi) => {
|
||||
if (params.windowSize && params.windowUnit) {
|
||||
const { interval, start, end } = getIntervalAndTimeRange({
|
||||
windowSize: params.windowSize,
|
||||
windowUnit: params.windowUnit as TimeUnit,
|
||||
});
|
||||
if (interval && start && end) {
|
||||
return callApmApi({
|
||||
endpoint: 'GET /api/apm/alerts/chart_preview/transaction_error_rate',
|
||||
params: {
|
||||
query: {
|
||||
...getAbsoluteTimeRange(params.windowSize, params.windowUnit),
|
||||
environment: params.environment,
|
||||
serviceName: params.serviceName,
|
||||
transactionType: params.transactionType,
|
||||
interval,
|
||||
start,
|
||||
end,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -51,7 +51,11 @@ export function ApmServiceContextProvider({
|
|||
end,
|
||||
});
|
||||
|
||||
const transactionTypes = useServiceTransactionTypesFetcher(serviceName);
|
||||
const transactionTypes = useServiceTransactionTypesFetcher({
|
||||
serviceName,
|
||||
start,
|
||||
end,
|
||||
});
|
||||
|
||||
const transactionType = getTransactionType({
|
||||
transactionType: query.transactionType,
|
||||
|
|
|
@ -5,19 +5,19 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useApmParams } from '../../hooks/use_apm_params';
|
||||
import { useFetcher } from '../../hooks/use_fetcher';
|
||||
import { useTimeRange } from '../../hooks/use_time_range';
|
||||
|
||||
const INITIAL_DATA = { transactionTypes: [] };
|
||||
|
||||
export function useServiceTransactionTypesFetcher(serviceName?: string) {
|
||||
const {
|
||||
query: { rangeFrom, rangeTo },
|
||||
} = useApmParams('/services/{serviceName}');
|
||||
|
||||
const { start, end } = useTimeRange({ rangeFrom, rangeTo });
|
||||
|
||||
export function useServiceTransactionTypesFetcher({
|
||||
serviceName,
|
||||
start,
|
||||
end,
|
||||
}: {
|
||||
serviceName?: string;
|
||||
start?: string;
|
||||
end?: string;
|
||||
}) {
|
||||
const { data = INITIAL_DATA } = useFetcher(
|
||||
(callApmApi) => {
|
||||
if (serviceName && start && end) {
|
||||
|
|
|
@ -6,17 +6,19 @@
|
|||
*/
|
||||
|
||||
import { QueryDslQueryContainer } from '@elastic/elasticsearch/api/types';
|
||||
import { rangeQuery } from '../../../../../observability/server';
|
||||
import {
|
||||
PROCESSOR_EVENT,
|
||||
SERVICE_NAME,
|
||||
TRANSACTION_DURATION,
|
||||
TRANSACTION_TYPE,
|
||||
} from '../../../../common/elasticsearch_fieldnames';
|
||||
import { ProcessorEvent } from '../../../../common/processor_event';
|
||||
import { rangeQuery } from '../../../../../observability/server';
|
||||
import { environmentQuery } from '../../../../common/utils/environment_query';
|
||||
import { AlertParams } from '../../../routes/alerts/chart_preview';
|
||||
import { getBucketSize } from '../../helpers/get_bucket_size';
|
||||
import {
|
||||
getDocumentTypeFilterForAggregatedTransactions,
|
||||
getProcessorEventForAggregatedTransactions,
|
||||
getSearchAggregatedTransactions,
|
||||
getTransactionDurationFieldForAggregatedTransactions,
|
||||
} from '../../helpers/aggregated_transactions';
|
||||
import { Setup, SetupTimeRange } from '../../helpers/setup_request';
|
||||
|
||||
export async function getTransactionDurationChartPreview({
|
||||
|
@ -26,43 +28,58 @@ export async function getTransactionDurationChartPreview({
|
|||
alertParams: AlertParams;
|
||||
setup: Setup & SetupTimeRange;
|
||||
}) {
|
||||
const searchAggregatedTransactions = await getSearchAggregatedTransactions({
|
||||
...setup,
|
||||
kuery: '',
|
||||
});
|
||||
|
||||
const { apmEventClient, start, end } = setup;
|
||||
const {
|
||||
aggregationType,
|
||||
environment,
|
||||
serviceName,
|
||||
transactionType,
|
||||
interval,
|
||||
} = alertParams;
|
||||
|
||||
const query = {
|
||||
bool: {
|
||||
filter: [
|
||||
{ term: { [PROCESSOR_EVENT]: ProcessorEvent.transaction } },
|
||||
...(serviceName ? [{ term: { [SERVICE_NAME]: serviceName } }] : []),
|
||||
...(transactionType
|
||||
? [{ term: { [TRANSACTION_TYPE]: transactionType } }]
|
||||
: []),
|
||||
...rangeQuery(start, end),
|
||||
...environmentQuery(environment),
|
||||
...getDocumentTypeFilterForAggregatedTransactions(
|
||||
searchAggregatedTransactions
|
||||
),
|
||||
] as QueryDslQueryContainer[],
|
||||
},
|
||||
};
|
||||
|
||||
const { intervalString } = getBucketSize({ start, end, numBuckets: 20 });
|
||||
const transactionDurationField = getTransactionDurationFieldForAggregatedTransactions(
|
||||
searchAggregatedTransactions
|
||||
);
|
||||
|
||||
const aggs = {
|
||||
timeseries: {
|
||||
date_histogram: {
|
||||
field: '@timestamp',
|
||||
fixed_interval: intervalString,
|
||||
fixed_interval: interval,
|
||||
min_doc_count: 0,
|
||||
extended_bounds: {
|
||||
min: start,
|
||||
max: end,
|
||||
},
|
||||
},
|
||||
aggs: {
|
||||
agg:
|
||||
aggregationType === 'avg'
|
||||
? { avg: { field: TRANSACTION_DURATION } }
|
||||
? { avg: { field: transactionDurationField } }
|
||||
: {
|
||||
percentiles: {
|
||||
field: TRANSACTION_DURATION,
|
||||
field: transactionDurationField,
|
||||
percents: [aggregationType === '95th' ? 95 : 99],
|
||||
},
|
||||
},
|
||||
|
@ -70,7 +87,13 @@ export async function getTransactionDurationChartPreview({
|
|||
},
|
||||
};
|
||||
const params = {
|
||||
apm: { events: [ProcessorEvent.transaction] },
|
||||
apm: {
|
||||
events: [
|
||||
getProcessorEventForAggregatedTransactions(
|
||||
searchAggregatedTransactions
|
||||
),
|
||||
],
|
||||
},
|
||||
body: { size: 0, query, aggs },
|
||||
};
|
||||
const resp = await apmEventClient.search(
|
||||
|
|
|
@ -10,7 +10,6 @@ import { ProcessorEvent } from '../../../../common/processor_event';
|
|||
import { AlertParams } from '../../../routes/alerts/chart_preview';
|
||||
import { rangeQuery } from '../../../../../observability/server';
|
||||
import { environmentQuery } from '../../../../common/utils/environment_query';
|
||||
import { getBucketSize } from '../../helpers/get_bucket_size';
|
||||
import { Setup, SetupTimeRange } from '../../helpers/setup_request';
|
||||
|
||||
export async function getTransactionErrorCountChartPreview({
|
||||
|
@ -21,7 +20,7 @@ export async function getTransactionErrorCountChartPreview({
|
|||
alertParams: AlertParams;
|
||||
}) {
|
||||
const { apmEventClient, start, end } = setup;
|
||||
const { serviceName, environment } = alertParams;
|
||||
const { serviceName, environment, interval } = alertParams;
|
||||
|
||||
const query = {
|
||||
bool: {
|
||||
|
@ -33,13 +32,15 @@ export async function getTransactionErrorCountChartPreview({
|
|||
},
|
||||
};
|
||||
|
||||
const { intervalString } = getBucketSize({ start, end, numBuckets: 20 });
|
||||
|
||||
const aggs = {
|
||||
timeseries: {
|
||||
date_histogram: {
|
||||
field: '@timestamp',
|
||||
fixed_interval: intervalString,
|
||||
fixed_interval: interval,
|
||||
extended_bounds: {
|
||||
min: start,
|
||||
max: end,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -5,16 +5,18 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { rangeQuery } from '../../../../../observability/server';
|
||||
import {
|
||||
PROCESSOR_EVENT,
|
||||
SERVICE_NAME,
|
||||
TRANSACTION_TYPE,
|
||||
} from '../../../../common/elasticsearch_fieldnames';
|
||||
import { ProcessorEvent } from '../../../../common/processor_event';
|
||||
import { AlertParams } from '../../../routes/alerts/chart_preview';
|
||||
import { rangeQuery } from '../../../../../observability/server';
|
||||
import { environmentQuery } from '../../../../common/utils/environment_query';
|
||||
import { getBucketSize } from '../../helpers/get_bucket_size';
|
||||
import { AlertParams } from '../../../routes/alerts/chart_preview';
|
||||
import {
|
||||
getDocumentTypeFilterForAggregatedTransactions,
|
||||
getProcessorEventForAggregatedTransactions,
|
||||
getSearchAggregatedTransactions,
|
||||
} from '../../helpers/aggregated_transactions';
|
||||
import { Setup, SetupTimeRange } from '../../helpers/setup_request';
|
||||
import {
|
||||
calculateFailedTransactionRate,
|
||||
|
@ -28,41 +30,56 @@ export async function getTransactionErrorRateChartPreview({
|
|||
setup: Setup & SetupTimeRange;
|
||||
alertParams: AlertParams;
|
||||
}) {
|
||||
const { apmEventClient, start, end } = setup;
|
||||
const { serviceName, environment, transactionType } = alertParams;
|
||||
const searchAggregatedTransactions = await getSearchAggregatedTransactions({
|
||||
...setup,
|
||||
kuery: '',
|
||||
});
|
||||
|
||||
const query = {
|
||||
bool: {
|
||||
filter: [
|
||||
{ term: { [PROCESSOR_EVENT]: ProcessorEvent.transaction } },
|
||||
...(serviceName ? [{ term: { [SERVICE_NAME]: serviceName } }] : []),
|
||||
...(transactionType
|
||||
? [{ term: { [TRANSACTION_TYPE]: transactionType } }]
|
||||
: []),
|
||||
...rangeQuery(start, end),
|
||||
...environmentQuery(environment),
|
||||
],
|
||||
},
|
||||
};
|
||||
const { apmEventClient, start, end } = setup;
|
||||
const { serviceName, environment, transactionType, interval } = alertParams;
|
||||
|
||||
const outcomes = getOutcomeAggregation();
|
||||
|
||||
const { intervalString } = getBucketSize({ start, end, numBuckets: 20 });
|
||||
|
||||
const aggs = {
|
||||
outcomes,
|
||||
timeseries: {
|
||||
date_histogram: {
|
||||
field: '@timestamp',
|
||||
fixed_interval: intervalString,
|
||||
},
|
||||
aggs: { outcomes },
|
||||
},
|
||||
};
|
||||
|
||||
const params = {
|
||||
apm: { events: [ProcessorEvent.transaction] },
|
||||
body: { size: 0, query, aggs },
|
||||
apm: {
|
||||
events: [
|
||||
getProcessorEventForAggregatedTransactions(
|
||||
searchAggregatedTransactions
|
||||
),
|
||||
],
|
||||
},
|
||||
body: {
|
||||
size: 0,
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
...(serviceName ? [{ term: { [SERVICE_NAME]: serviceName } }] : []),
|
||||
...(transactionType
|
||||
? [{ term: { [TRANSACTION_TYPE]: transactionType } }]
|
||||
: []),
|
||||
...rangeQuery(start, end),
|
||||
...environmentQuery(environment),
|
||||
...getDocumentTypeFilterForAggregatedTransactions(
|
||||
searchAggregatedTransactions
|
||||
),
|
||||
],
|
||||
},
|
||||
},
|
||||
aggs: {
|
||||
outcomes,
|
||||
timeseries: {
|
||||
date_histogram: {
|
||||
field: '@timestamp',
|
||||
fixed_interval: interval,
|
||||
extended_bounds: {
|
||||
min: start,
|
||||
max: end,
|
||||
},
|
||||
},
|
||||
aggs: { outcomes },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const resp = await apmEventClient.search(
|
||||
|
|
|
@ -26,6 +26,9 @@ const alertParamsRt = t.intersection([
|
|||
}),
|
||||
environmentRt,
|
||||
rangeRt,
|
||||
t.type({
|
||||
interval: t.string,
|
||||
}),
|
||||
]);
|
||||
|
||||
export type AlertParams = t.TypeOf<typeof alertParamsRt>;
|
||||
|
|
|
@ -24,6 +24,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
serviceName: 'opbeans-java',
|
||||
transactionType: 'request' as string | undefined,
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
interval: '5m',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue