mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Synthetics UI] Monitor history tab (#143516)
## Summary Closes https://github.com/elastic/kibana/issues/142997 Contents for the monitor history tab, minus the status widget. Co-authored-by: shahzad31 <shahzad.muhammad@elastic.co>
This commit is contained in:
parent
cbc7fe10f6
commit
a1128cf3cc
13 changed files with 345 additions and 19 deletions
|
@ -105,7 +105,19 @@ export function getSyntheticsKPIConfig({ dataView }: ConfigProps): SeriesConfig
|
|||
columnFilters: [
|
||||
{
|
||||
language: 'kuery',
|
||||
query: `state.id: * and state.up: 0`,
|
||||
query: `summary: * and summary.down > 0 and and monitor.status: "down"`,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Monitor Complete',
|
||||
id: 'state.up',
|
||||
field: 'state.up',
|
||||
columnType: OPERATION_COLUMN,
|
||||
columnFilters: [
|
||||
{
|
||||
language: 'kuery',
|
||||
query: `summary: * and summary.down: 0 and monitor.status: "up"`,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
@ -93,6 +93,30 @@ export function getSyntheticsSingleMetricConfig({ dataView }: ConfigProps): Seri
|
|||
titlePosition: 'bottom',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'monitor_total_runs',
|
||||
label: i18n.translate('xpack.observability.expView.totalRuns', {
|
||||
defaultMessage: 'Total Runs',
|
||||
}),
|
||||
metricStateOptions: {
|
||||
titlePosition: 'bottom',
|
||||
},
|
||||
columnType: FORMULA_COLUMN,
|
||||
formula: 'unique_count(monitor.check_group)',
|
||||
format: 'number',
|
||||
},
|
||||
{
|
||||
id: 'monitor_complete',
|
||||
label: i18n.translate('xpack.observability.expView.complete', {
|
||||
defaultMessage: 'Complete',
|
||||
}),
|
||||
metricStateOptions: {
|
||||
titlePosition: 'bottom',
|
||||
},
|
||||
columnType: FORMULA_COLUMN,
|
||||
formula: 'unique_count(monitor.check_group, kql=\'monitor.status: "up"\')',
|
||||
format: 'number',
|
||||
},
|
||||
{
|
||||
id: 'monitor_errors',
|
||||
label: i18n.translate('xpack.observability.expView.errors', {
|
||||
|
@ -104,7 +128,7 @@ export function getSyntheticsSingleMetricConfig({ dataView }: ConfigProps): Seri
|
|||
palette: getColorPalette('danger'),
|
||||
},
|
||||
columnType: FORMULA_COLUMN,
|
||||
formula: 'unique_count(state.id, kql=\'monitor.status: "down"\')',
|
||||
formula: 'unique_count(monitor.check_group, kql=\'monitor.status: "down"\')',
|
||||
format: 'number',
|
||||
},
|
||||
{
|
||||
|
|
|
@ -50,6 +50,7 @@ export interface ExploratoryEmbeddableProps {
|
|||
showCalculationMethod?: boolean;
|
||||
title?: string | JSX.Element;
|
||||
withActions?: boolean | ActionTypes[];
|
||||
align?: 'left' | 'right' | 'center';
|
||||
}
|
||||
|
||||
export interface ExploratoryEmbeddableComponentProps extends ExploratoryEmbeddableProps {
|
||||
|
@ -80,6 +81,7 @@ export default function Embeddable({
|
|||
withActions = true,
|
||||
lensFormulaHelper,
|
||||
hideTicks,
|
||||
align,
|
||||
}: ExploratoryEmbeddableComponentProps) {
|
||||
const LensComponent = lens?.EmbeddableComponent;
|
||||
const LensSaveModalComponent = lens?.SaveModalComponent;
|
||||
|
@ -168,7 +170,7 @@ export default function Embeddable({
|
|||
}
|
||||
|
||||
return (
|
||||
<Wrapper $customHeight={customHeight}>
|
||||
<Wrapper $customHeight={customHeight} align={align}>
|
||||
{(title || showCalculationMethod || appendTitle) && (
|
||||
<EuiFlexGroup alignItems="center" gutterSize="none">
|
||||
{title && (
|
||||
|
@ -226,6 +228,7 @@ export default function Embeddable({
|
|||
|
||||
const Wrapper = styled.div<{
|
||||
$customHeight?: string | number;
|
||||
align?: 'left' | 'right' | 'center';
|
||||
}>`
|
||||
height: ${(props) => (props.$customHeight ? `${props.$customHeight};` : `100%;`)};
|
||||
position: relative;
|
||||
|
@ -239,6 +242,14 @@ const Wrapper = styled.div<{
|
|||
}
|
||||
|
||||
.legacyMtrVis {
|
||||
> :first-child {
|
||||
justify-content: ${(props) =>
|
||||
props.align === 'left'
|
||||
? `flex-start;`
|
||||
: props.align === 'right'
|
||||
? `flex-end;`
|
||||
: 'center;'};
|
||||
}
|
||||
justify-content: flex-end;
|
||||
.legacyMtrVis__container {
|
||||
padding: 0;
|
||||
|
|
|
@ -4,17 +4,34 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EuiFlexGrid, EuiFlexGroup, EuiFlexItem, EuiPanel, EuiTitle } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useCallback } from 'react';
|
||||
import { EuiSpacer } from '@elastic/eui';
|
||||
import { useUrlParams } from '../../../hooks';
|
||||
import { useDimensions } from '../../../hooks';
|
||||
import { SyntheticsDatePicker } from '../../common/date_picker/synthetics_date_picker';
|
||||
import { AvailabilityPanel } from '../monitor_summary/availability_panel';
|
||||
import { DurationPanel } from '../monitor_summary/duration_panel';
|
||||
import { MonitorDurationTrend } from '../monitor_summary/duration_trend';
|
||||
import { TestRunsTable } from '../monitor_summary/test_runs_table';
|
||||
import { MonitorErrorsCount } from '../monitor_summary/monitor_errors_count';
|
||||
import { MonitorCompleteCount } from '../monitor_summary/monitor_complete_count';
|
||||
import { MonitorTotalRunsCount } from '../monitor_summary/monitor_total_runs_count';
|
||||
import { MonitorErrorSparklines } from '../monitor_summary/monitor_error_sparklines';
|
||||
import { AvailabilitySparklines } from '../monitor_summary/availability_sparklines';
|
||||
import { DurationSparklines } from '../monitor_summary/duration_sparklines';
|
||||
import { MonitorCompleteSparklines } from '../monitor_summary/monitor_complete_sparklines';
|
||||
import { MonitorStatusPanel } from '../monitor_status/monitor_status_panel';
|
||||
|
||||
const STATS_WIDTH_SINGLE_COLUMN_THRESHOLD = 360; // ✨ determined by trial and error
|
||||
|
||||
export const MonitorHistory = () => {
|
||||
const [useGetUrlParams, updateUrlParams] = useUrlParams();
|
||||
const { dateRangeStart, dateRangeEnd } = useGetUrlParams();
|
||||
|
||||
const { elementRef: statsRef, width: statsWidth } = useDimensions<HTMLDivElement>();
|
||||
const statsColumns = statsWidth && statsWidth < STATS_WIDTH_SINGLE_COLUMN_THRESHOLD ? 1 : 2;
|
||||
|
||||
const handleStatusChartBrushed = useCallback(
|
||||
({ fromUtc, toUtc }) => {
|
||||
updateUrlParams({ dateRangeStart: fromUtc, dateRangeEnd: toUtc });
|
||||
|
@ -23,18 +40,96 @@ export const MonitorHistory = () => {
|
|||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SyntheticsDatePicker fullWidth={true} />
|
||||
<EuiSpacer size="m" />
|
||||
<MonitorStatusPanel
|
||||
from={dateRangeStart}
|
||||
to={dateRangeEnd}
|
||||
showViewHistoryButton={false}
|
||||
periodCaption={''}
|
||||
brushable={true}
|
||||
onBrushed={handleStatusChartBrushed}
|
||||
/>
|
||||
<EuiSpacer size="m" />
|
||||
</>
|
||||
<EuiFlexGroup direction="column" gutterSize="m">
|
||||
<EuiFlexItem>
|
||||
<SyntheticsDatePicker fullWidth={true} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup gutterSize="m">
|
||||
<EuiFlexItem grow={1}>
|
||||
{/* @ts-expect-error Current @elastic/eui has the wrong types for the ref */}
|
||||
<EuiPanel hasShadow={false} hasBorder={true} panelRef={statsRef}>
|
||||
<EuiTitle size="xs">
|
||||
<h3>{STATS_LABEL}</h3>
|
||||
</EuiTitle>
|
||||
<EuiFlexGrid columns={statsColumns} gutterSize="s" responsive={false}>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup gutterSize="xs">
|
||||
<EuiFlexItem>
|
||||
<MonitorCompleteCount from={dateRangeStart} to={dateRangeEnd} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<MonitorCompleteSparklines from={dateRangeStart} to={dateRangeEnd} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup gutterSize="xs">
|
||||
<EuiFlexItem>
|
||||
<AvailabilityPanel from={dateRangeStart} to={dateRangeEnd} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<AvailabilitySparklines from={dateRangeStart} to={dateRangeEnd} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup gutterSize="xs">
|
||||
<EuiFlexItem>
|
||||
<MonitorErrorsCount from={dateRangeStart} to={dateRangeEnd} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<MonitorErrorSparklines from={dateRangeStart} to={dateRangeEnd} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup gutterSize="xs">
|
||||
<EuiFlexItem>
|
||||
<DurationPanel from={dateRangeStart} to={dateRangeEnd} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<DurationSparklines from={dateRangeStart} to={dateRangeEnd} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<MonitorTotalRunsCount from={dateRangeStart} to={dateRangeEnd} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGrid>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={2}>
|
||||
<EuiPanel hasShadow={false} hasBorder={true}>
|
||||
<EuiTitle size="xs">
|
||||
<h3>{DURATION_TREND_LABEL}</h3>
|
||||
</EuiTitle>
|
||||
<MonitorDurationTrend from={dateRangeStart} to={dateRangeEnd} />
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<MonitorStatusPanel
|
||||
from={dateRangeStart}
|
||||
to={dateRangeEnd}
|
||||
showViewHistoryButton={false}
|
||||
periodCaption={''}
|
||||
brushable={true}
|
||||
onBrushed={handleStatusChartBrushed}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<TestRunsTable from={dateRangeStart} to={dateRangeEnd} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
||||
|
||||
const STATS_LABEL = i18n.translate('xpack.synthetics.historyPanel.stats', {
|
||||
defaultMessage: 'Stats',
|
||||
});
|
||||
|
||||
const DURATION_TREND_LABEL = i18n.translate('xpack.synthetics.historyPanel.durationTrends', {
|
||||
defaultMessage: 'Duration trends',
|
||||
});
|
||||
|
|
|
@ -33,6 +33,7 @@ export const AvailabilityPanel = (props: AvailabilityPanelprops) => {
|
|||
|
||||
return (
|
||||
<ExploratoryViewEmbeddable
|
||||
align="left"
|
||||
customHeight="70px"
|
||||
reportType={ReportTypes.SINGLE_METRIC}
|
||||
attributes={[
|
||||
|
|
|
@ -33,6 +33,7 @@ export const DurationPanel = (props: DurationPanelProps) => {
|
|||
|
||||
return (
|
||||
<ExploratoryViewEmbeddable
|
||||
align="left"
|
||||
customHeight="70px"
|
||||
reportType={ReportTypes.SINGLE_METRIC}
|
||||
attributes={[
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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 { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import React from 'react';
|
||||
import { ReportTypes } from '@kbn/observability-plugin/public';
|
||||
import { ClientPluginsStart } from '../../../../../plugin';
|
||||
import { useMonitorQueryId } from '../hooks/use_monitor_query_id';
|
||||
|
||||
interface MonitorCompleteCountProps {
|
||||
from: string;
|
||||
to: string;
|
||||
}
|
||||
|
||||
export const MonitorCompleteCount = (props: MonitorCompleteCountProps) => {
|
||||
const { observability } = useKibana<ClientPluginsStart>().services;
|
||||
|
||||
const { ExploratoryViewEmbeddable } = observability;
|
||||
|
||||
const monitorId = useMonitorQueryId();
|
||||
|
||||
return (
|
||||
<ExploratoryViewEmbeddable
|
||||
align="left"
|
||||
reportType={ReportTypes.SINGLE_METRIC}
|
||||
attributes={[
|
||||
{
|
||||
time: props,
|
||||
reportDefinitions: { config_id: [monitorId] },
|
||||
dataType: 'synthetics',
|
||||
selectedMetricField: 'monitor_complete',
|
||||
name: 'synthetics-series-1',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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 { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import React from 'react';
|
||||
import { useEuiTheme } from '@elastic/eui';
|
||||
import { ClientPluginsStart } from '../../../../../plugin';
|
||||
import { useMonitorQueryId } from '../hooks/use_monitor_query_id';
|
||||
|
||||
interface Props {
|
||||
from: string;
|
||||
to: string;
|
||||
}
|
||||
export const MonitorCompleteSparklines = (props: Props) => {
|
||||
const { observability } = useKibana<ClientPluginsStart>().services;
|
||||
|
||||
const { ExploratoryViewEmbeddable } = observability;
|
||||
|
||||
const monitorId = useMonitorQueryId();
|
||||
|
||||
const { euiTheme } = useEuiTheme();
|
||||
|
||||
return (
|
||||
<ExploratoryViewEmbeddable
|
||||
reportType="kpi-over-time"
|
||||
axisTitlesVisibility={{ x: false, yRight: false, yLeft: false }}
|
||||
legendIsVisible={false}
|
||||
hideTicks={true}
|
||||
attributes={[
|
||||
{
|
||||
seriesType: 'area',
|
||||
time: props,
|
||||
reportDefinitions: { 'monitor.id': [monitorId] },
|
||||
dataType: 'synthetics',
|
||||
selectedMetricField: 'state.id',
|
||||
name: 'Monitor complete',
|
||||
color: euiTheme.colors.success,
|
||||
operationType: 'unique_count',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -46,7 +46,7 @@ export const MonitorErrorSparklines = (props: Props) => {
|
|||
'observer.geo.name': [selectedLocation?.label],
|
||||
},
|
||||
dataType: 'synthetics',
|
||||
selectedMetricField: 'state.id',
|
||||
selectedMetricField: 'state.up',
|
||||
name: 'Monitor errors',
|
||||
color: euiTheme.colors.danger,
|
||||
operationType: 'unique_count',
|
||||
|
|
|
@ -32,6 +32,7 @@ export const MonitorErrorsCount = (props: MonitorErrorsCountProps) => {
|
|||
|
||||
return (
|
||||
<ExploratoryViewEmbeddable
|
||||
align="left"
|
||||
customHeight="70px"
|
||||
reportType={ReportTypes.SINGLE_METRIC}
|
||||
attributes={[
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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 { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import React from 'react';
|
||||
import { ReportTypes } from '@kbn/observability-plugin/public';
|
||||
import { ClientPluginsStart } from '../../../../../plugin';
|
||||
import { useMonitorQueryId } from '../hooks/use_monitor_query_id';
|
||||
|
||||
interface MonitorTotalRunsCountProps {
|
||||
from: string;
|
||||
to: string;
|
||||
}
|
||||
|
||||
export const MonitorTotalRunsCount = (props: MonitorTotalRunsCountProps) => {
|
||||
const { observability } = useKibana<ClientPluginsStart>().services;
|
||||
|
||||
const { ExploratoryViewEmbeddable } = observability;
|
||||
|
||||
const monitorId = useMonitorQueryId();
|
||||
|
||||
return (
|
||||
<ExploratoryViewEmbeddable
|
||||
align="left"
|
||||
reportType={ReportTypes.SINGLE_METRIC}
|
||||
attributes={[
|
||||
{
|
||||
time: props,
|
||||
reportDefinitions: { config_id: [monitorId] },
|
||||
dataType: 'synthetics',
|
||||
selectedMetricField: 'monitor_total_runs',
|
||||
name: 'synthetics-series-1',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -15,3 +15,4 @@ export * from './use_last_50_duration_chart';
|
|||
export * from './use_location_name';
|
||||
export * from './use_status_by_location';
|
||||
export * from './use_composite_image';
|
||||
export * from './use_dimensions';
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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 { useEffect, useRef, useState } from 'react';
|
||||
|
||||
/** Returns a `ref` to attach to a DOM element, and its dimensions. */
|
||||
export function useDimensions<T extends Element>() {
|
||||
const [dimensions, setDimensions] = useState<{ width: number; height: number } | undefined>();
|
||||
const elementRef = useRef<T>();
|
||||
|
||||
const resizeObserverInstance = useRef<ResizeObserver>(
|
||||
new ResizeObserver((entries) => {
|
||||
if (entries && entries[0]) {
|
||||
setDimensions({
|
||||
width: entries[0].contentRect.width,
|
||||
height: entries[0].contentRect.height,
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// This makes ESLint happy. The cleanup function cannot point to the
|
||||
// `.current` property of a ref.
|
||||
const ref = elementRef.current;
|
||||
const resizeObserver = resizeObserverInstance.current;
|
||||
|
||||
if (ref) {
|
||||
resizeObserver.observe(ref);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (ref) {
|
||||
resizeObserver.unobserve(ref);
|
||||
}
|
||||
};
|
||||
|
||||
// ESlint complains about this dependencies not triggering `useEffect`
|
||||
// because they are mutable. This is not a problem for our case. We don't
|
||||
// care if the attached DOM node mutates. We only want to know when the node
|
||||
// gets attached to the ref.
|
||||
//
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [elementRef.current, resizeObserverInstance.current]);
|
||||
|
||||
return { elementRef, ...dimensions };
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue