Skip to content

Commit

Permalink
[Synthetics] Error timeline date range (#151965)
Browse files Browse the repository at this point in the history
  • Loading branch information
shahzad31 authored Mar 1, 2023
1 parent 570eb38 commit d051183
Show file tree
Hide file tree
Showing 17 changed files with 320 additions and 104 deletions.
16 changes: 14 additions & 2 deletions x-pack/plugins/synthetics/common/runtime_types/ping/error_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,26 @@
*/

import * as t from 'io-ts';
export const StateEndsCodec = t.type({
duration_ms: t.union([t.string, t.number]),
checks: t.number,
ends: t.union([t.string, t.null]),
started_at: t.string,
id: t.string,
up: t.number,
down: t.number,
status: t.string,
});

export const ErrorStateCodec = t.type({
duration_ms: t.string,
duration_ms: t.union([t.string, t.number]),
checks: t.number,
ends: t.union([t.string, t.null]),
ends: t.union([StateEndsCodec, t.null]),
started_at: t.string,
id: t.string,
up: t.number,
down: t.number,
status: t.string,
});

export type ErrorState = t.TypeOf<typeof ErrorStateCodec>;
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,6 @@ journey(`TestRunDetailsPage`, async ({ page, params }) => {

await page.waitForSelector('text=Test run details');
await page.waitForSelector('text=Go to https://www.google.com');
await page.waitForSelector('text=After 2.1 s');
await page.waitForSelector('text=After 2.12 s');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import { i18n } from '@kbn/i18n';
import React, { CSSProperties, ReactElement, useState } from 'react';
import React, { CSSProperties, ReactElement, useCallback, useEffect, useState } from 'react';
import {
EuiBasicTable,
EuiBasicTableColumn,
Expand Down Expand Up @@ -62,25 +62,38 @@ export const BrowserStepsList = ({
Record<string, ReactElement>
>({});

const toggleDetails = (item: JourneyStep) => {
const itemIdToExpandedRowMapValues = { ...itemIdToExpandedRowMap };
if (itemIdToExpandedRowMapValues[item._id]) {
delete itemIdToExpandedRowMapValues[item._id];
} else {
if (testNowMode) {
itemIdToExpandedRowMapValues[item._id] = (
<EuiFlexGroup>
<EuiFlexItem>
<StepTabs step={item} loading={false} stepsList={steps} />
</EuiFlexItem>
</EuiFlexGroup>
);
} else {
itemIdToExpandedRowMapValues[item._id] = <></>;
}
const toggleDetails = useCallback(
(item: JourneyStep) => {
setItemIdToExpandedRowMap((prevState) => {
const itemIdToExpandedRowMapValues = { ...prevState };
if (itemIdToExpandedRowMapValues[item._id]) {
delete itemIdToExpandedRowMapValues[item._id];
} else {
if (testNowMode) {
itemIdToExpandedRowMapValues[item._id] = (
<EuiFlexGroup>
<EuiFlexItem>
<StepTabs step={item} loading={false} stepsList={steps} />
</EuiFlexItem>
</EuiFlexGroup>
);
} else {
itemIdToExpandedRowMapValues[item._id] = <></>;
}
}
return itemIdToExpandedRowMapValues;
});
},
[steps, testNowMode]
);

const failedStep = stepEnds?.find((step) => step.synthetics.step?.status === 'failed');

useEffect(() => {
if (failedStep && showExpand) {
toggleDetails(failedStep);
}
setItemIdToExpandedRowMap(itemIdToExpandedRowMapValues);
};
}, [failedStep, showExpand, toggleDetails]);

const columns: Array<EuiBasicTableColumn<JourneyStep>> = [
...(showExpand
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,50 @@ import React from 'react';
import { EuiDescriptionList } from '@elastic/eui';
import { i18n } from '@kbn/i18n';

import moment from 'moment';
import moment, { Moment } from 'moment';
import { useFindMyKillerState } from '../hooks/use_find_my_killer_state';
import { useErrorFailedTests } from '../hooks/use_last_error_state';

export const ErrorDuration: React.FC = () => {
const { failedTests } = useErrorFailedTests();

const state = failedTests?.[0]?.state;

const duration = state ? moment().diff(moment(state?.started_at), 'minutes') : 0;
const { killerState } = useFindMyKillerState();

return (
<EuiDescriptionList listItems={[{ title: ERROR_DURATION, description: `${duration} min` }]} />
);
const endsAt = killerState?.timestamp ? moment(killerState?.timestamp) : moment();
const startedAt = moment(state?.started_at);

const duration = state ? getErrorDuration(startedAt, endsAt) : 0;

return <EuiDescriptionList listItems={[{ title: ERROR_DURATION, description: duration }]} />;
};

const ERROR_DURATION = i18n.translate('xpack.synthetics.errorDetails.errorDuration', {
defaultMessage: 'Error duration',
});

const getErrorDuration = (startedAt: Moment, endsAt: Moment) => {
// const endsAt = state.ends ? moment(state.ends) : moment();
// const startedAt = moment(state?.started_at);

const diffInDays = endsAt.diff(startedAt, 'days');
if (diffInDays > 1) {
return i18n.translate('xpack.synthetics.errorDetails.errorDuration.days', {
defaultMessage: '{value} days',
values: { value: diffInDays },
});
}
const diffInHours = endsAt.diff(startedAt, 'hours');
if (diffInHours > 1) {
return i18n.translate('xpack.synthetics.errorDetails.errorDuration.hours', {
defaultMessage: '{value} hours',
values: { value: diffInHours },
});
}
const diffInMinutes = endsAt.diff(startedAt, 'minutes');
return i18n.translate('xpack.synthetics.errorDetails.errorDuration.mins', {
defaultMessage: '{value} mins',
values: { value: diffInMinutes },
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,32 @@
* 2.0.
*/
import React from 'react';
import { EuiLoadingContent } from '@elastic/eui';
import moment from 'moment';
import { Ping } from '../../../../../../common/runtime_types';
import { MonitorFailedTests } from '../../monitor_details/monitor_errors/failed_tests';

export const ErrorTimeline = () => {
return <MonitorFailedTests time={{ from: 'now-1h', to: 'now' }} />;
export const ErrorTimeline = ({ lastTestRun }: { lastTestRun?: Ping }) => {
if (!lastTestRun) {
return <EuiLoadingContent lines={3} />;
}
const diff = moment(lastTestRun.monitor.timespan?.lt).diff(
moment(lastTestRun.monitor.timespan?.gte),
'minutes'
);
const startedAt = lastTestRun?.state?.started_at;

return (
<MonitorFailedTests
time={{
from: moment(startedAt)
.subtract(diff / 2, 'minutes')
.toISOString(),
to: moment(lastTestRun.timestamp)
.add(diff / 2, 'minutes')
.toISOString(),
}}
allowBrushing={false}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,13 @@ import React, { ReactElement } from 'react';
import { EuiDescriptionList } from '@elastic/eui';
import { i18n } from '@kbn/i18n';

import { useErrorFailedTests } from '../hooks/use_last_error_state';
import { useFormatTestRunAt } from '../../../utils/monitor_test_result/test_time_formats';
import { useFindMyKillerState } from '../hooks/use_find_my_killer_state';

export const ResolvedAt: React.FC = () => {
const { failedTests } = useErrorFailedTests();
const { killerState } = useFindMyKillerState();

const state = failedTests?.[0]?.state;

let endsAt: string | ReactElement = useFormatTestRunAt(state?.ends ?? '');
let endsAt: string | ReactElement = useFormatTestRunAt(killerState?.timestamp);

if (!endsAt) {
endsAt = 'N/A';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export function ErrorDetailsPage() {
return (
<div>
<PanelWithTitle title={TIMELINE_LABEL}>
<ErrorTimeline />
<ErrorTimeline lastTestRun={lastTestRun} />
</PanelWithTitle>
<EuiSpacer size="m" />
<EuiFlexGroup gutterSize="m">
Expand Down Expand Up @@ -71,13 +71,19 @@ export function ErrorDetailsPage() {
</EuiPanel>
</EuiFlexItem>
<EuiFlexItem grow={1} style={{ height: 'fit-content' }}>
<PanelWithTitle>
{data?.details?.journey && failedStep && (
<StepImage ping={data?.details?.journey} step={failedStep} isFailed={isFailedStep} />
)}
</PanelWithTitle>
{data?.details?.journey && failedStep && (
<>
<PanelWithTitle>
<StepImage
ping={data?.details?.journey}
step={failedStep}
isFailed={isFailedStep}
/>
</PanelWithTitle>
<EuiSpacer size="m" />
</>
)}

<EuiSpacer size="m" />
<StepDurationPanel doBreakdown={false} />
<EuiSpacer size="m" />
<MonitorDetailsPanelContainer hideLocations />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
import { i18n } from '@kbn/i18n';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import { useSelectedLocation } from '../../monitor_details/hooks/use_selected_location';
import { useTestRunDetailsBreadcrumbs } from '../../test_run_details/hooks/use_test_run_details_breadcrumbs';
import { useSelectedMonitor } from '../../monitor_details/hooks/use_selected_monitor';
import { ConfigKey } from '../../../../../../common/runtime_types';
Expand All @@ -19,10 +20,14 @@ export const useErrorDetailsBreadcrumbs = (

const { monitor } = useSelectedMonitor();

const selectedLocation = useSelectedLocation();

const errorsBreadcrumbs = [
{
text: ERRORS_CRUMB,
href: `${appPath}/monitor/${monitor?.[ConfigKey.CONFIG_ID]}/errors`,
href: `${appPath}/monitor/${monitor?.[ConfigKey.CONFIG_ID]}/errors?locationId=${
selectedLocation?.id
}`,
},
...(extraCrumbs ?? []),
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ export function useErrorFailedTests() {
return useMemo(() => {
const failedTests =
data?.hits.hits?.map((doc) => {
return doc._source as Ping;
const source = doc._source as any;
return { ...source, timestamp: source['@timestamp'] } as Ping;
}) ?? [];

return {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { useParams } from 'react-router-dom';
import { useMemo } from 'react';
import { useReduxEsSearch } from '../../../hooks/use_redux_es_search';
import { Ping } from '../../../../../../common/runtime_types';
import {
EXCLUDE_RUN_ONCE_FILTER,
SUMMARY_FILTER,
} from '../../../../../../common/constants/client_defaults';
import { SYNTHETICS_INDEX_PATTERN } from '../../../../../../common/constants';
import { useSyntheticsRefreshContext } from '../../../contexts';
import { useGetUrlParams } from '../../../hooks';

export function useFindMyKillerState() {
const { lastRefresh } = useSyntheticsRefreshContext();

const { errorStateId, monitorId } = useParams<{ errorStateId: string; monitorId: string }>();

const { dateRangeStart, dateRangeEnd } = useGetUrlParams();

const { data, loading } = useReduxEsSearch(
{
index: SYNTHETICS_INDEX_PATTERN,

body: {
// TODO: remove this once we have a better way to handle this mapping
runtime_mappings: {
'state.ends.id': {
type: 'keyword',
},
},
size: 1,
query: {
bool: {
filter: [
SUMMARY_FILTER,
EXCLUDE_RUN_ONCE_FILTER,
{
term: {
'state.ends.id': errorStateId,
},
},
{
term: {
config_id: monitorId,
},
},
],
},
},
sort: [{ '@timestamp': 'desc' }],
},
},
[lastRefresh, monitorId, dateRangeStart, dateRangeEnd],
{ name: 'getStateWhichEndTheState' }
);

return useMemo(() => {
const killerStates =
data?.hits.hits?.map((doc) => {
const source = doc._source as any;
return { ...source, timestamp: source['@timestamp'] } as Ping;
}) ?? [];

return {
loading,
killerState: killerStates?.[0],
};
}, [data, loading]);
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const getErrorDetailsRouteConfig = (
),
rightSideItems: [
<ErrorDuration />,
<MonitorDetailsLocation />,
<MonitorDetailsLocation isDisabled={true} />,
<ResolvedAt />,
<ErrorStartedAt />,
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,15 @@ export function useMonitorErrors(monitorIdArg?: string) {
},
},
},
[lastRefresh, monitorId, monitorIdArg, dateRangeStart, dateRangeEnd],
{ name: 'getMonitorErrors', isRequestReady: Boolean(selectedLocation?.label) }
[lastRefresh, monitorId, monitorIdArg, dateRangeStart, dateRangeEnd, selectedLocation?.label],
{
name: `getMonitorErrors/${dateRangeStart}/${dateRangeEnd}`,
isRequestReady: Boolean(selectedLocation?.label),
}
);

return useMemo(() => {
const errorStates = (data?.aggregations?.errorStates.buckets ?? []).map((loc) => {
const errorStates = data?.aggregations?.errorStates.buckets?.map((loc) => {
return loc.summary.hits.hits?.[0]._source as PingState;
});

Expand Down
Loading

0 comments on commit d051183

Please sign in to comment.