mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Infrastructure UI] KPI charts with Lens Embeddable (#154903)
## Summary
This PR replaces all but Host Count metric charts with Lens and aligns
the formulas used in Lens with the formulas used in the Snapshot API.
The Hosts Count was not converted yet, because Snapshot API
[post-process the results and filters out hosts that don't have metrics
and come from
APM](47c71b3025/x-pack/plugins/infra/server/routes/snapshot/lib/transform_metrics_ui_response.ts (L61)
).
I decided to keep it unchanged until we start using a new API, which
won't have that step.
In order to avoid multiple requests going to the server simultaneously,
I introduced an intersection observer in the Lens chart component. It
will make them only trigger a request once the user has scrolled over a
chart component. This aims to prevent the occurrence of
`circuit_breaking_exception` exceptions when async_search has to process
too much data.
### Differences between Lens and current KPI charts
<img width="533" alt="image"
src="https://user-images.githubusercontent.com/2767137/232526591-4812059e-ba1f-4e59-a060-820570230084.png">
Currently, Lens doesn't provide an option for adding a suffix to the
metric value nor set a max number of decimal places. (e.g (e.g 3.8 Mbit
**/s**)
### For reviewer
Unfortunately, it's a big PR. Everything in the `lens/formulas` folder
is just related to the metric formulas that are reused between KPIs and
Metrics Charts.
The core of the changes is in the `lens/visualization_types` folder. It
contains the details on how to build different objects to render either
a metric type of chart or a lineXY one.
I have also aligned `cpu` and `memory` formulas with what we have in the
Inventory Model. When comparing the current KPI with the new ones in
Lens Embeddable, the results were not matching.
---------
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
0283b7abd3
commit
fce3664397
47 changed files with 1301 additions and 1227 deletions
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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 {
|
||||
cpu,
|
||||
diskIORead,
|
||||
diskIOWrite,
|
||||
load,
|
||||
memory,
|
||||
memoryAvailable,
|
||||
rx,
|
||||
tx,
|
||||
hostCount,
|
||||
} from './lens/formulas/host';
|
||||
import { LineChart, MetricChart } from './lens/visualization_types';
|
||||
|
||||
export const hostLensFormulas = {
|
||||
cpu,
|
||||
diskIORead,
|
||||
diskIOWrite,
|
||||
hostCount,
|
||||
load,
|
||||
memory,
|
||||
memoryAvailable,
|
||||
rx,
|
||||
tx,
|
||||
};
|
||||
|
||||
export const visualizationTypes = {
|
||||
lineChart: LineChart,
|
||||
metricChart: MetricChart,
|
||||
};
|
|
@ -5,19 +5,18 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { CPU, Load, Memory, MemoryAvailable, RX, TX, DiskIORead, DiskIOWrite } from './lens/hosts';
|
||||
export type {
|
||||
HostsLensFormulas,
|
||||
LineChartOptions,
|
||||
LensChartConfig,
|
||||
LensLineChartConfig,
|
||||
MetricChartOptions,
|
||||
HostsLensMetricChartFormulas,
|
||||
HostsLensLineChartFormulas,
|
||||
LensOptions,
|
||||
LensAttributes,
|
||||
} from './types';
|
||||
|
||||
export { buildLensAttributes } from './lens/lens_visualization';
|
||||
export { hostLensFormulas, visualizationTypes } from './constants';
|
||||
|
||||
export const hostMetricsLensAttributes = {
|
||||
cpu: CPU,
|
||||
load: Load,
|
||||
memory: Memory,
|
||||
memoryAvailable: MemoryAvailable,
|
||||
rx: RX,
|
||||
tx: TX,
|
||||
diskIORead: DiskIORead,
|
||||
diskIOWrite: DiskIOWrite,
|
||||
};
|
||||
|
||||
export type HostLensAttributesTypes = keyof typeof hostMetricsLensAttributes;
|
||||
export { buildLensAttributes } from './lens/build_lens_attributes';
|
||||
|
|
|
@ -4,10 +4,11 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import type { LensAttributes } from '../../../types';
|
||||
import type { ILensVisualization } from './types';
|
||||
import type { LensAttributes, TVisualization, VisualizationAttributes } from '../types';
|
||||
|
||||
export const buildLensAttributes = (visualization: ILensVisualization): LensAttributes => {
|
||||
export const buildLensAttributes = <T extends VisualizationAttributes<TVisualization>>(
|
||||
visualization: T
|
||||
): LensAttributes => {
|
||||
return {
|
||||
title: visualization.getTitle(),
|
||||
visualizationType: visualization.getVisualizationType(),
|
||||
|
@ -18,7 +19,8 @@ export const buildLensAttributes = (visualization: ILensVisualization): LensAttr
|
|||
layers: visualization.getLayers(),
|
||||
},
|
||||
},
|
||||
filters: [],
|
||||
internalReferences: visualization.getReferences(),
|
||||
filters: visualization.getFilters(),
|
||||
query: { language: 'kuery', query: '' },
|
||||
visualization: visualization.getVisualizationState(),
|
||||
adHocDataViews: visualization.getAdhocDataView(),
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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 type { LensChartConfig, LensLineChartConfig } from '../../../types';
|
||||
import { getFilters } from './utils';
|
||||
|
||||
export const cpuLineChart: LensLineChartConfig = {
|
||||
extraVisualizationState: {
|
||||
yLeftExtent: {
|
||||
mode: 'custom',
|
||||
lowerBound: 0,
|
||||
upperBound: 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const cpu: LensChartConfig = {
|
||||
title: 'CPU Usage',
|
||||
formula: {
|
||||
formula:
|
||||
'(average(system.cpu.user.pct) + average(system.cpu.system.pct)) / max(system.cpu.cores)',
|
||||
format: {
|
||||
id: 'percent',
|
||||
params: {
|
||||
decimals: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
getFilters,
|
||||
|
||||
lineChartConfig: cpuLineChart,
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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 type { LensChartConfig } from '../../../types';
|
||||
import { getFilters } from './utils';
|
||||
|
||||
export const diskIORead: LensChartConfig = {
|
||||
title: 'Disk Read IOPS',
|
||||
formula: {
|
||||
formula: "counter_rate(max(system.diskio.read.bytes), kql='system.diskio.read.bytes: *')",
|
||||
format: {
|
||||
id: 'bytes',
|
||||
params: {
|
||||
decimals: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
getFilters,
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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 type { LensChartConfig } from '../../../types';
|
||||
import { getFilters } from './utils';
|
||||
|
||||
export const diskIOWrite: LensChartConfig = {
|
||||
title: 'Disk Write IOPS',
|
||||
formula: {
|
||||
formula: "counter_rate(max(system.diskio.write.bytes), kql='system.diskio.write.bytes: *')",
|
||||
format: {
|
||||
id: 'bytes',
|
||||
params: {
|
||||
decimals: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
getFilters,
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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 type { LensChartConfig } from '../../../types';
|
||||
import { getFilters } from './utils';
|
||||
|
||||
export const hostCount: LensChartConfig = {
|
||||
title: 'Hosts',
|
||||
formula: {
|
||||
formula: 'unique_count(host.name)',
|
||||
format: {
|
||||
id: 'number',
|
||||
params: {
|
||||
decimals: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
getFilters,
|
||||
};
|
|
@ -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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export { cpu } from './cpu';
|
||||
export { diskIORead } from './diskio_read';
|
||||
export { diskIOWrite } from './diskio_write';
|
||||
export { hostCount } from './host_count';
|
||||
export { load } from './load';
|
||||
export { memory } from './memory';
|
||||
export { memoryAvailable } from './memory_available';
|
||||
export { rx } from './rx';
|
||||
export { tx } from './tx';
|
|
@ -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 type { ReferenceBasedIndexPatternColumn } from '@kbn/lens-plugin/public/datasources/form_based/operations/definitions/column_types';
|
||||
import type { LensChartConfig, LensLineChartConfig } from '../../../types';
|
||||
import { getFilters } from './utils';
|
||||
|
||||
const REFERENCE_LAYER = 'referenceLayer';
|
||||
|
||||
export const loadLineChart: LensLineChartConfig = {
|
||||
extraLayers: {
|
||||
[REFERENCE_LAYER]: {
|
||||
linkToLayers: [],
|
||||
columnOrder: ['referenceColumn'],
|
||||
columns: {
|
||||
referenceColumn: {
|
||||
label: 'Reference',
|
||||
dataType: 'number',
|
||||
operationType: 'static_value',
|
||||
isStaticValue: true,
|
||||
isBucketed: false,
|
||||
scale: 'ratio',
|
||||
params: {
|
||||
value: 1,
|
||||
format: {
|
||||
id: 'percent',
|
||||
params: {
|
||||
decimals: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
references: [],
|
||||
customLabel: true,
|
||||
} as ReferenceBasedIndexPatternColumn,
|
||||
},
|
||||
sampling: 1,
|
||||
incompleteColumns: {},
|
||||
},
|
||||
},
|
||||
extraVisualizationState: {
|
||||
layers: [
|
||||
{
|
||||
layerId: REFERENCE_LAYER,
|
||||
layerType: 'referenceLine',
|
||||
accessors: ['referenceColumn'],
|
||||
yConfig: [
|
||||
{
|
||||
forAccessor: 'referenceColumn',
|
||||
axisMode: 'left',
|
||||
color: '#6092c0',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
extraReference: REFERENCE_LAYER,
|
||||
};
|
||||
|
||||
export const load: LensChartConfig = {
|
||||
title: 'Normalized Load',
|
||||
formula: {
|
||||
formula: 'average(system.load.1) / max(system.load.cores)',
|
||||
format: {
|
||||
id: 'percent',
|
||||
params: {
|
||||
decimals: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
getFilters,
|
||||
lineChartConfig: loadLineChart,
|
||||
};
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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 type { LensChartConfig, LensLineChartConfig } from '../../../types';
|
||||
import { getFilters } from './utils';
|
||||
|
||||
const memoryLineChart: LensLineChartConfig = {
|
||||
extraVisualizationState: {
|
||||
yLeftExtent: {
|
||||
mode: 'custom',
|
||||
lowerBound: 0,
|
||||
upperBound: 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const memory: LensChartConfig = {
|
||||
title: 'Memory',
|
||||
formula: {
|
||||
formula: 'average(system.memory.actual.used.pct)',
|
||||
format: {
|
||||
id: 'percent',
|
||||
params: {
|
||||
decimals: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
lineChartConfig: memoryLineChart,
|
||||
getFilters,
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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 type { LensChartConfig } from '../../../types';
|
||||
import { getFilters } from './utils';
|
||||
|
||||
export const memoryAvailable: LensChartConfig = {
|
||||
title: 'Memory Available',
|
||||
formula: {
|
||||
formula: 'max(system.memory.total) - average(system.memory.actual.used.bytes)',
|
||||
format: {
|
||||
id: 'bytes',
|
||||
params: {
|
||||
decimals: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
getFilters,
|
||||
};
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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 type { LensChartConfig } from '../../../types';
|
||||
import { getFilters } from './utils';
|
||||
|
||||
export const rx: LensChartConfig = {
|
||||
title: 'Network Inbound (RX)',
|
||||
formula: {
|
||||
formula:
|
||||
"average(host.network.ingress.bytes) * 8 / (max(metricset.period, kql='host.network.ingress.bytes: *') / 1000)",
|
||||
format: {
|
||||
id: 'bits',
|
||||
params: {
|
||||
decimals: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
getFilters,
|
||||
};
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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 type { LensChartConfig } from '../../../types';
|
||||
import { getFilters } from './utils';
|
||||
|
||||
export const tx: LensChartConfig = {
|
||||
title: 'Network Outbound (TX)',
|
||||
formula: {
|
||||
formula:
|
||||
"average(host.network.egress.bytes) * 8 / (max(metricset.period, kql='host.network.egress.bytes: *') / 1000)",
|
||||
format: {
|
||||
id: 'bits',
|
||||
params: {
|
||||
decimals: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
getFilters,
|
||||
};
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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 { DataViewBase } from '@kbn/es-query';
|
||||
|
||||
export const getFilters = ({ id }: Pick<DataViewBase, 'id'>) => [
|
||||
{
|
||||
meta: {
|
||||
index: id,
|
||||
},
|
||||
query: {
|
||||
exists: {
|
||||
field: 'host.name',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
|
@ -1,97 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type {
|
||||
FormBasedLayer,
|
||||
FormulaPublicApi,
|
||||
PersistedIndexPatternLayer,
|
||||
XYState,
|
||||
} from '@kbn/lens-plugin/public';
|
||||
import type { DataView } from '@kbn/data-views-plugin/public';
|
||||
import {
|
||||
DEFAULT_LAYER_ID,
|
||||
getAdhocDataView,
|
||||
getBreakdownColumn,
|
||||
getDefaultReferences,
|
||||
getHistogramColumn,
|
||||
getXYVisualizationState,
|
||||
} from '../utils';
|
||||
import type { LensOptions } from '../../../../types';
|
||||
import type { ILensVisualization } from '../types';
|
||||
|
||||
const BREAKDOWN_COLUMN_NAME = 'hosts_aggs_breakdown';
|
||||
const HISTOGRAM_COLUMN_NAME = 'x_date_histogram';
|
||||
|
||||
export class CPU implements ILensVisualization {
|
||||
constructor(
|
||||
private dataView: DataView,
|
||||
private options: LensOptions,
|
||||
private formula: FormulaPublicApi
|
||||
) {}
|
||||
|
||||
getTitle(): string {
|
||||
return 'CPU Usage';
|
||||
}
|
||||
|
||||
getVisualizationType(): string {
|
||||
return 'lnsXY';
|
||||
}
|
||||
|
||||
getLayers = (): Record<string, Omit<FormBasedLayer, 'indexPatternId'>> => {
|
||||
const baseLayer: PersistedIndexPatternLayer = {
|
||||
columnOrder: [BREAKDOWN_COLUMN_NAME, HISTOGRAM_COLUMN_NAME],
|
||||
columns: {
|
||||
...getBreakdownColumn(BREAKDOWN_COLUMN_NAME, 'host.name', this.options.breakdownSize),
|
||||
...getHistogramColumn(HISTOGRAM_COLUMN_NAME, this.dataView.timeFieldName ?? '@timestamp'),
|
||||
},
|
||||
};
|
||||
|
||||
const dataLayer = this.formula.insertOrReplaceFormulaColumn(
|
||||
'y_cpu_usage',
|
||||
{
|
||||
formula: 'average(system.cpu.total.norm.pct)',
|
||||
format: {
|
||||
id: 'percent',
|
||||
params: {
|
||||
decimals: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
baseLayer,
|
||||
this.dataView
|
||||
);
|
||||
|
||||
if (!dataLayer) {
|
||||
throw new Error('Error generating the data layer for the chart');
|
||||
}
|
||||
|
||||
return { [DEFAULT_LAYER_ID]: dataLayer };
|
||||
};
|
||||
getVisualizationState = (): XYState => {
|
||||
return getXYVisualizationState({
|
||||
layers: [
|
||||
{
|
||||
layerId: DEFAULT_LAYER_ID,
|
||||
seriesType: 'line',
|
||||
accessors: ['y_cpu_usage'],
|
||||
yConfig: [],
|
||||
layerType: 'data',
|
||||
xAccessor: HISTOGRAM_COLUMN_NAME,
|
||||
splitAccessor: BREAKDOWN_COLUMN_NAME,
|
||||
},
|
||||
],
|
||||
yLeftExtent: {
|
||||
mode: 'custom',
|
||||
lowerBound: 0,
|
||||
upperBound: 1,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
getReferences = () => getDefaultReferences(this.dataView, DEFAULT_LAYER_ID);
|
||||
getAdhocDataView = () => getAdhocDataView(this.dataView);
|
||||
}
|
|
@ -1,93 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type {
|
||||
FormBasedLayer,
|
||||
FormulaPublicApi,
|
||||
PersistedIndexPatternLayer,
|
||||
XYState,
|
||||
} from '@kbn/lens-plugin/public';
|
||||
import type { DataView } from '@kbn/data-views-plugin/public';
|
||||
import type { LensOptions } from '../../../../types';
|
||||
import {
|
||||
DEFAULT_LAYER_ID,
|
||||
getAdhocDataView,
|
||||
getBreakdownColumn,
|
||||
getDefaultReferences,
|
||||
getHistogramColumn,
|
||||
getXYVisualizationState,
|
||||
} from '../utils';
|
||||
import type { ILensVisualization } from '../types';
|
||||
|
||||
const BREAKDOWN_COLUMN_NAME = 'hosts_aggs_breakdown';
|
||||
const HISTOGRAM_COLUMN_NAME = 'x_date_histogram';
|
||||
|
||||
export class DiskIORead implements ILensVisualization {
|
||||
constructor(
|
||||
private dataView: DataView,
|
||||
private options: LensOptions,
|
||||
private formula: FormulaPublicApi
|
||||
) {}
|
||||
|
||||
getTitle(): string {
|
||||
return 'Disk Read IOPS';
|
||||
}
|
||||
|
||||
getVisualizationType(): string {
|
||||
return 'lnsXY';
|
||||
}
|
||||
|
||||
getLayers = (): Record<string, Omit<FormBasedLayer, 'indexPatternId'>> => {
|
||||
const baseLayer: PersistedIndexPatternLayer = {
|
||||
columnOrder: [BREAKDOWN_COLUMN_NAME, HISTOGRAM_COLUMN_NAME],
|
||||
columns: {
|
||||
...getBreakdownColumn(BREAKDOWN_COLUMN_NAME, 'host.name', this.options.breakdownSize),
|
||||
...getHistogramColumn(HISTOGRAM_COLUMN_NAME, this.dataView.timeFieldName ?? '@timestamp'),
|
||||
},
|
||||
};
|
||||
|
||||
const dataLayer = this.formula.insertOrReplaceFormulaColumn(
|
||||
'y_diskio_read',
|
||||
{
|
||||
formula: "counter_rate(max(system.diskio.read.bytes), kql='system.diskio.read.bytes >= 0')",
|
||||
format: {
|
||||
id: 'bytes',
|
||||
params: {
|
||||
decimals: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
baseLayer,
|
||||
this.dataView
|
||||
);
|
||||
|
||||
if (!dataLayer) {
|
||||
throw new Error('Error generating the data layer for the chart');
|
||||
}
|
||||
|
||||
return { [DEFAULT_LAYER_ID]: dataLayer };
|
||||
};
|
||||
|
||||
getVisualizationState = (): XYState => {
|
||||
return getXYVisualizationState({
|
||||
layers: [
|
||||
{
|
||||
layerId: DEFAULT_LAYER_ID,
|
||||
seriesType: 'line',
|
||||
accessors: ['y_diskio_read'],
|
||||
yConfig: [],
|
||||
layerType: 'data',
|
||||
xAccessor: HISTOGRAM_COLUMN_NAME,
|
||||
splitAccessor: BREAKDOWN_COLUMN_NAME,
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
getReferences = () => getDefaultReferences(this.dataView, DEFAULT_LAYER_ID);
|
||||
getAdhocDataView = () => getAdhocDataView(this.dataView);
|
||||
}
|
|
@ -1,94 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type {
|
||||
FormBasedLayer,
|
||||
FormulaPublicApi,
|
||||
PersistedIndexPatternLayer,
|
||||
XYState,
|
||||
} from '@kbn/lens-plugin/public';
|
||||
import type { DataView } from '@kbn/data-views-plugin/public';
|
||||
import type { LensOptions } from '../../../../types';
|
||||
import {
|
||||
DEFAULT_LAYER_ID,
|
||||
getAdhocDataView,
|
||||
getBreakdownColumn,
|
||||
getDefaultReferences,
|
||||
getHistogramColumn,
|
||||
getXYVisualizationState,
|
||||
} from '../utils';
|
||||
import type { ILensVisualization } from '../types';
|
||||
|
||||
const BREAKDOWN_COLUMN_NAME = 'hosts_aggs_breakdown';
|
||||
const HISTOGRAM_COLUMN_NAME = 'x_date_histogram';
|
||||
|
||||
export class DiskIOWrite implements ILensVisualization {
|
||||
constructor(
|
||||
private dataView: DataView,
|
||||
private options: LensOptions,
|
||||
private formula: FormulaPublicApi
|
||||
) {}
|
||||
|
||||
getTitle(): string {
|
||||
return 'Disk Write IOPS';
|
||||
}
|
||||
|
||||
getVisualizationType(): string {
|
||||
return 'lnsXY';
|
||||
}
|
||||
|
||||
getLayers = (): Record<string, Omit<FormBasedLayer, 'indexPatternId'>> => {
|
||||
const baseLayer: PersistedIndexPatternLayer = {
|
||||
columnOrder: [BREAKDOWN_COLUMN_NAME, HISTOGRAM_COLUMN_NAME],
|
||||
columns: {
|
||||
...getBreakdownColumn(BREAKDOWN_COLUMN_NAME, 'host.name', this.options.breakdownSize),
|
||||
...getHistogramColumn(HISTOGRAM_COLUMN_NAME, this.dataView.timeFieldName ?? '@timestamp'),
|
||||
},
|
||||
};
|
||||
|
||||
const dataLayer = this.formula.insertOrReplaceFormulaColumn(
|
||||
'y_diskio_write',
|
||||
{
|
||||
formula:
|
||||
"counter_rate(max(system.diskio.write.bytes), kql='system.diskio.write.bytes>= 0')",
|
||||
format: {
|
||||
id: 'bytes',
|
||||
params: {
|
||||
decimals: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
baseLayer,
|
||||
this.dataView
|
||||
);
|
||||
|
||||
if (!dataLayer) {
|
||||
throw new Error('Error generating the data layer for the chart');
|
||||
}
|
||||
|
||||
return { [DEFAULT_LAYER_ID]: dataLayer };
|
||||
};
|
||||
|
||||
getVisualizationState = (): XYState => {
|
||||
return getXYVisualizationState({
|
||||
layers: [
|
||||
{
|
||||
layerId: DEFAULT_LAYER_ID,
|
||||
seriesType: 'line',
|
||||
accessors: ['y_diskio_write'],
|
||||
yConfig: [],
|
||||
layerType: 'data',
|
||||
xAccessor: HISTOGRAM_COLUMN_NAME,
|
||||
splitAccessor: BREAKDOWN_COLUMN_NAME,
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
getReferences = () => getDefaultReferences(this.dataView, DEFAULT_LAYER_ID);
|
||||
getAdhocDataView = () => getAdhocDataView(this.dataView);
|
||||
}
|
|
@ -1,15 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export { CPU } from './cpu';
|
||||
export { Load } from './load';
|
||||
export { Memory } from './memory';
|
||||
export { MemoryAvailable } from './memory_available';
|
||||
export { RX } from './rx';
|
||||
export { TX } from './tx';
|
||||
export { DiskIORead } from './diskio_read';
|
||||
export { DiskIOWrite } from './diskio_write';
|
|
@ -1,144 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type {
|
||||
PersistedIndexPatternLayer,
|
||||
FormulaPublicApi,
|
||||
XYState,
|
||||
FormBasedLayer,
|
||||
} from '@kbn/lens-plugin/public';
|
||||
import type { DataView } from '@kbn/data-views-plugin/public';
|
||||
import type { ReferenceBasedIndexPatternColumn } from '@kbn/lens-plugin/public/datasources/form_based/operations/definitions/column_types';
|
||||
import type { SavedObjectReference } from '@kbn/core-saved-objects-common';
|
||||
import {
|
||||
DEFAULT_AD_HOC_DATA_VIEW_ID,
|
||||
DEFAULT_LAYER_ID,
|
||||
getAdhocDataView,
|
||||
getBreakdownColumn,
|
||||
getDefaultReferences,
|
||||
getHistogramColumn,
|
||||
getXYVisualizationState,
|
||||
} from '../utils';
|
||||
import type { LensOptions } from '../../../../types';
|
||||
import type { ILensVisualization } from '../types';
|
||||
|
||||
const BREAKDOWN_COLUMN_NAME = 'hosts_aggs_breakdown';
|
||||
const HISTOGRAM_COLUMN_NAME = 'x_date_histogram';
|
||||
const REFERENCE_LAYER = 'referenceLayer';
|
||||
|
||||
export class Load implements ILensVisualization {
|
||||
constructor(
|
||||
private dataView: DataView,
|
||||
private options: LensOptions,
|
||||
private formula: FormulaPublicApi
|
||||
) {}
|
||||
|
||||
getTitle(): string {
|
||||
return 'Normalized Load';
|
||||
}
|
||||
|
||||
getVisualizationType(): string {
|
||||
return 'lnsXY';
|
||||
}
|
||||
|
||||
getLayers = (): Record<string, Omit<FormBasedLayer, 'indexPatternId'>> => {
|
||||
const baseLayer: PersistedIndexPatternLayer = {
|
||||
columnOrder: [BREAKDOWN_COLUMN_NAME, HISTOGRAM_COLUMN_NAME],
|
||||
columns: {
|
||||
...getBreakdownColumn(BREAKDOWN_COLUMN_NAME, 'host.name', this.options.breakdownSize),
|
||||
...getHistogramColumn(HISTOGRAM_COLUMN_NAME, this.dataView.timeFieldName ?? '@timestamp'),
|
||||
},
|
||||
};
|
||||
|
||||
const dataLayer = this.formula.insertOrReplaceFormulaColumn(
|
||||
'y_cpu_cores_usage',
|
||||
{
|
||||
formula: 'average(system.load.1) / max(system.load.cores)',
|
||||
format: {
|
||||
id: 'percent',
|
||||
params: {
|
||||
decimals: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
baseLayer,
|
||||
this.dataView
|
||||
);
|
||||
|
||||
if (!dataLayer) {
|
||||
throw new Error('Error generating the data layer for the chart');
|
||||
}
|
||||
|
||||
return {
|
||||
[DEFAULT_LAYER_ID]: dataLayer,
|
||||
referenceLayer: {
|
||||
linkToLayers: [],
|
||||
columnOrder: ['referenceColumn'],
|
||||
columns: {
|
||||
referenceColumn: {
|
||||
label: 'Reference',
|
||||
dataType: 'number',
|
||||
operationType: 'static_value',
|
||||
isStaticValue: true,
|
||||
isBucketed: false,
|
||||
scale: 'ratio',
|
||||
params: {
|
||||
value: 1,
|
||||
format: {
|
||||
id: 'percent',
|
||||
params: {
|
||||
decimals: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
references: [],
|
||||
customLabel: true,
|
||||
} as ReferenceBasedIndexPatternColumn,
|
||||
},
|
||||
sampling: 1,
|
||||
incompleteColumns: {},
|
||||
},
|
||||
};
|
||||
};
|
||||
getVisualizationState = (): XYState => {
|
||||
return getXYVisualizationState({
|
||||
layers: [
|
||||
{
|
||||
layerId: DEFAULT_LAYER_ID,
|
||||
seriesType: 'line',
|
||||
accessors: ['y_cpu_cores_usage'],
|
||||
yConfig: [],
|
||||
layerType: 'data',
|
||||
xAccessor: HISTOGRAM_COLUMN_NAME,
|
||||
splitAccessor: BREAKDOWN_COLUMN_NAME,
|
||||
},
|
||||
{
|
||||
layerId: REFERENCE_LAYER,
|
||||
layerType: 'referenceLine',
|
||||
accessors: ['referenceColumn'],
|
||||
yConfig: [
|
||||
{
|
||||
forAccessor: 'referenceColumn',
|
||||
axisMode: 'left',
|
||||
color: '#6092c0',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
getReferences = (): SavedObjectReference[] => [
|
||||
...getDefaultReferences(this.dataView, DEFAULT_LAYER_ID),
|
||||
{
|
||||
type: 'index-pattern',
|
||||
id: this.dataView.id ?? DEFAULT_AD_HOC_DATA_VIEW_ID,
|
||||
name: `indexpattern-datasource-layer-${REFERENCE_LAYER}`,
|
||||
},
|
||||
];
|
||||
getAdhocDataView = () => getAdhocDataView(this.dataView);
|
||||
}
|
|
@ -1,97 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type {
|
||||
FormBasedLayer,
|
||||
FormulaPublicApi,
|
||||
PersistedIndexPatternLayer,
|
||||
XYState,
|
||||
} from '@kbn/lens-plugin/public';
|
||||
import type { DataView } from '@kbn/data-views-plugin/public';
|
||||
import {
|
||||
DEFAULT_LAYER_ID,
|
||||
getAdhocDataView,
|
||||
getBreakdownColumn,
|
||||
getDefaultReferences,
|
||||
getHistogramColumn,
|
||||
getXYVisualizationState,
|
||||
} from '../utils';
|
||||
import type { LensOptions } from '../../../../types';
|
||||
import type { ILensVisualization } from '../types';
|
||||
|
||||
const BREAKDOWN_COLUMN_NAME = 'hosts_aggs_breakdown';
|
||||
const HISTOGRAM_COLUMN_NAME = 'x_date_histogram';
|
||||
|
||||
export class Memory implements ILensVisualization {
|
||||
constructor(
|
||||
private dataView: DataView,
|
||||
private options: LensOptions,
|
||||
private formula: FormulaPublicApi
|
||||
) {}
|
||||
|
||||
getTitle(): string {
|
||||
return 'Disk Writes IOPS';
|
||||
}
|
||||
|
||||
getVisualizationType(): string {
|
||||
return 'lnsXY';
|
||||
}
|
||||
|
||||
getLayers = (): Record<string, Omit<FormBasedLayer, 'indexPatternId'>> => {
|
||||
const baseLayer: PersistedIndexPatternLayer = {
|
||||
columnOrder: [BREAKDOWN_COLUMN_NAME, HISTOGRAM_COLUMN_NAME],
|
||||
columns: {
|
||||
...getBreakdownColumn(BREAKDOWN_COLUMN_NAME, 'host.name', this.options.breakdownSize),
|
||||
...getHistogramColumn(HISTOGRAM_COLUMN_NAME, this.dataView.timeFieldName ?? '@timestamp'),
|
||||
},
|
||||
};
|
||||
|
||||
const dataLayer = this.formula.insertOrReplaceFormulaColumn(
|
||||
'y_memory_usage',
|
||||
{
|
||||
formula: 'average(system.memory.actual.used.bytes) / max(system.memory.total)',
|
||||
format: {
|
||||
id: 'percent',
|
||||
params: {
|
||||
decimals: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
baseLayer,
|
||||
this.dataView
|
||||
);
|
||||
|
||||
if (!dataLayer) {
|
||||
throw new Error('Error generating the data layer for the chart');
|
||||
}
|
||||
|
||||
return { [DEFAULT_LAYER_ID]: dataLayer };
|
||||
};
|
||||
getVisualizationState = (): XYState => {
|
||||
return getXYVisualizationState({
|
||||
layers: [
|
||||
{
|
||||
layerId: DEFAULT_LAYER_ID,
|
||||
seriesType: 'line',
|
||||
accessors: ['y_memory_usage'],
|
||||
yConfig: [],
|
||||
layerType: 'data',
|
||||
xAccessor: HISTOGRAM_COLUMN_NAME,
|
||||
splitAccessor: BREAKDOWN_COLUMN_NAME,
|
||||
},
|
||||
],
|
||||
yLeftExtent: {
|
||||
mode: 'custom',
|
||||
lowerBound: 0,
|
||||
upperBound: 1,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
getReferences = () => getDefaultReferences(this.dataView, DEFAULT_LAYER_ID);
|
||||
getAdhocDataView = () => getAdhocDataView(this.dataView);
|
||||
}
|
|
@ -1,91 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type {
|
||||
FormBasedLayer,
|
||||
FormulaPublicApi,
|
||||
PersistedIndexPatternLayer,
|
||||
XYState,
|
||||
} from '@kbn/lens-plugin/public';
|
||||
import type { DataView } from '@kbn/data-views-plugin/public';
|
||||
import {
|
||||
DEFAULT_LAYER_ID,
|
||||
getAdhocDataView,
|
||||
getBreakdownColumn,
|
||||
getDefaultReferences,
|
||||
getHistogramColumn,
|
||||
getXYVisualizationState,
|
||||
} from '../utils';
|
||||
import type { LensOptions } from '../../../../types';
|
||||
import type { ILensVisualization } from '../types';
|
||||
|
||||
const BREAKDOWN_COLUMN_NAME = 'hosts_aggs_breakdown';
|
||||
const HISTOGRAM_COLUMN_NAME = 'x_date_histogram';
|
||||
|
||||
export class MemoryAvailable implements ILensVisualization {
|
||||
constructor(
|
||||
private dataView: DataView,
|
||||
private options: LensOptions,
|
||||
private formula: FormulaPublicApi
|
||||
) {}
|
||||
|
||||
getTitle(): string {
|
||||
return 'Memory Available';
|
||||
}
|
||||
|
||||
getVisualizationType(): string {
|
||||
return 'lnsXY';
|
||||
}
|
||||
|
||||
getLayers = (): Record<string, Omit<FormBasedLayer, 'indexPatternId'>> => {
|
||||
const baseLayer: PersistedIndexPatternLayer = {
|
||||
columnOrder: [BREAKDOWN_COLUMN_NAME, HISTOGRAM_COLUMN_NAME],
|
||||
columns: {
|
||||
...getBreakdownColumn(BREAKDOWN_COLUMN_NAME, 'host.name', this.options.breakdownSize),
|
||||
...getHistogramColumn(HISTOGRAM_COLUMN_NAME, this.dataView.timeFieldName ?? '@timestamp'),
|
||||
},
|
||||
};
|
||||
|
||||
const dataLayer = this.formula.insertOrReplaceFormulaColumn(
|
||||
'y_memory_available',
|
||||
{
|
||||
formula: 'max(system.memory.total) - average(system.memory.actual.used.bytes)',
|
||||
format: {
|
||||
id: 'bytes',
|
||||
params: {
|
||||
decimals: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
baseLayer,
|
||||
this.dataView
|
||||
);
|
||||
|
||||
if (!dataLayer) {
|
||||
throw new Error('Error generating the data layer for the chart');
|
||||
}
|
||||
|
||||
return { [DEFAULT_LAYER_ID]: dataLayer };
|
||||
};
|
||||
getVisualizationState = (): XYState => {
|
||||
return getXYVisualizationState({
|
||||
layers: [
|
||||
{
|
||||
layerId: DEFAULT_LAYER_ID,
|
||||
seriesType: 'line',
|
||||
accessors: ['y_memory_available'],
|
||||
yConfig: [],
|
||||
layerType: 'data',
|
||||
xAccessor: HISTOGRAM_COLUMN_NAME,
|
||||
splitAccessor: BREAKDOWN_COLUMN_NAME,
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
getReferences = () => getDefaultReferences(this.dataView, DEFAULT_LAYER_ID);
|
||||
getAdhocDataView = () => getAdhocDataView(this.dataView);
|
||||
}
|
|
@ -1,92 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type {
|
||||
FormBasedLayer,
|
||||
FormulaPublicApi,
|
||||
PersistedIndexPatternLayer,
|
||||
XYState,
|
||||
} from '@kbn/lens-plugin/public';
|
||||
import type { DataView } from '@kbn/data-views-plugin/public';
|
||||
import {
|
||||
DEFAULT_LAYER_ID,
|
||||
getAdhocDataView,
|
||||
getBreakdownColumn,
|
||||
getDefaultReferences,
|
||||
getHistogramColumn,
|
||||
getXYVisualizationState,
|
||||
} from '../utils';
|
||||
import type { LensOptions } from '../../../../types';
|
||||
import type { ILensVisualization } from '../types';
|
||||
|
||||
const BREAKDOWN_COLUMN_NAME = 'hosts_aggs_breakdown';
|
||||
const HISTOGRAM_COLUMN_NAME = 'x_date_histogram';
|
||||
|
||||
export class RX implements ILensVisualization {
|
||||
constructor(
|
||||
private dataView: DataView,
|
||||
private options: LensOptions,
|
||||
private formula: FormulaPublicApi
|
||||
) {}
|
||||
|
||||
getTitle(): string {
|
||||
return 'Network Inbound (RX)';
|
||||
}
|
||||
|
||||
getVisualizationType(): string {
|
||||
return 'lnsXY';
|
||||
}
|
||||
|
||||
getLayers = (): Record<string, Omit<FormBasedLayer, 'indexPatternId'>> => {
|
||||
const baseLayer: PersistedIndexPatternLayer = {
|
||||
columnOrder: [BREAKDOWN_COLUMN_NAME, HISTOGRAM_COLUMN_NAME],
|
||||
columns: {
|
||||
...getBreakdownColumn(BREAKDOWN_COLUMN_NAME, 'host.name', this.options.breakdownSize),
|
||||
...getHistogramColumn(HISTOGRAM_COLUMN_NAME, this.dataView.timeFieldName ?? '@timestamp'),
|
||||
},
|
||||
};
|
||||
|
||||
const dataLayer = this.formula.insertOrReplaceFormulaColumn(
|
||||
'y_network_in_bytes',
|
||||
{
|
||||
formula:
|
||||
"average(host.network.ingress.bytes) * 8 / (max(metricset.period, kql='host.network.ingress.bytes: *') / 1000)",
|
||||
format: {
|
||||
id: 'bits',
|
||||
params: {
|
||||
decimals: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
baseLayer,
|
||||
this.dataView
|
||||
);
|
||||
|
||||
if (!dataLayer) {
|
||||
throw new Error('Error generating the data layer for the chart');
|
||||
}
|
||||
|
||||
return { [DEFAULT_LAYER_ID]: dataLayer };
|
||||
};
|
||||
getVisualizationState = (): XYState => {
|
||||
return getXYVisualizationState({
|
||||
layers: [
|
||||
{
|
||||
layerId: DEFAULT_LAYER_ID,
|
||||
seriesType: 'line',
|
||||
accessors: ['y_network_in_bytes'],
|
||||
yConfig: [],
|
||||
layerType: 'data',
|
||||
xAccessor: HISTOGRAM_COLUMN_NAME,
|
||||
splitAccessor: BREAKDOWN_COLUMN_NAME,
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
getReferences = () => getDefaultReferences(this.dataView, DEFAULT_LAYER_ID);
|
||||
getAdhocDataView = () => getAdhocDataView(this.dataView);
|
||||
}
|
|
@ -1,92 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type {
|
||||
FormBasedLayer,
|
||||
FormulaPublicApi,
|
||||
PersistedIndexPatternLayer,
|
||||
XYState,
|
||||
} from '@kbn/lens-plugin/public';
|
||||
import type { DataView } from '@kbn/data-views-plugin/public';
|
||||
import {
|
||||
DEFAULT_LAYER_ID,
|
||||
getAdhocDataView,
|
||||
getBreakdownColumn,
|
||||
getDefaultReferences,
|
||||
getHistogramColumn,
|
||||
getXYVisualizationState,
|
||||
} from '../utils';
|
||||
import type { LensOptions } from '../../../../types';
|
||||
import type { ILensVisualization } from '../types';
|
||||
|
||||
const BREAKDOWN_COLUMN_NAME = 'hosts_aggs_breakdown';
|
||||
const HISTOGRAM_COLUMN_NAME = 'x_date_histogram';
|
||||
|
||||
export class TX implements ILensVisualization {
|
||||
constructor(
|
||||
private dataView: DataView,
|
||||
private options: LensOptions,
|
||||
private formula: FormulaPublicApi
|
||||
) {}
|
||||
|
||||
getTitle(): string {
|
||||
return 'Network Outbound (TX)';
|
||||
}
|
||||
|
||||
getVisualizationType(): string {
|
||||
return 'lnsXY';
|
||||
}
|
||||
|
||||
getLayers = (): Record<string, Omit<FormBasedLayer, 'indexPatternId'>> => {
|
||||
const baseLayer: PersistedIndexPatternLayer = {
|
||||
columnOrder: [BREAKDOWN_COLUMN_NAME, HISTOGRAM_COLUMN_NAME],
|
||||
columns: {
|
||||
...getBreakdownColumn(BREAKDOWN_COLUMN_NAME, 'host.name', this.options.breakdownSize),
|
||||
...getHistogramColumn(HISTOGRAM_COLUMN_NAME, this.dataView.timeFieldName ?? '@timestamp'),
|
||||
},
|
||||
};
|
||||
|
||||
const dataLayer = this.formula.insertOrReplaceFormulaColumn(
|
||||
'y_network_out_bytes',
|
||||
{
|
||||
formula:
|
||||
"average(host.network.egress.bytes) * 8 / (max(metricset.period, kql='host.network.egress.bytes: *') / 1000)",
|
||||
format: {
|
||||
id: 'bits',
|
||||
params: {
|
||||
decimals: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
baseLayer,
|
||||
this.dataView
|
||||
);
|
||||
|
||||
if (!dataLayer) {
|
||||
throw new Error('Error generating the data layer for the chart');
|
||||
}
|
||||
|
||||
return { [DEFAULT_LAYER_ID]: dataLayer };
|
||||
};
|
||||
getVisualizationState = (): XYState => {
|
||||
return getXYVisualizationState({
|
||||
layers: [
|
||||
{
|
||||
layerId: DEFAULT_LAYER_ID,
|
||||
seriesType: 'line',
|
||||
accessors: ['y_network_out_bytes'],
|
||||
yConfig: [],
|
||||
layerType: 'data',
|
||||
xAccessor: HISTOGRAM_COLUMN_NAME,
|
||||
splitAccessor: BREAKDOWN_COLUMN_NAME,
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
getReferences = () => getDefaultReferences(this.dataView, DEFAULT_LAYER_ID);
|
||||
getAdhocDataView = () => getAdhocDataView(this.dataView);
|
||||
}
|
|
@ -1,19 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { SavedObjectReference } from '@kbn/core-saved-objects-common';
|
||||
import type { DataViewSpec } from '@kbn/data-views-plugin/common';
|
||||
import type { FormBasedLayer, XYState } from '@kbn/lens-plugin/public';
|
||||
|
||||
export interface ILensVisualization {
|
||||
getTitle(): string;
|
||||
getVisualizationType(): string;
|
||||
getLayers(): Record<string, Omit<FormBasedLayer, 'indexPatternId'>>;
|
||||
getVisualizationState(): XYState;
|
||||
getReferences(): SavedObjectReference[];
|
||||
getAdhocDataView(): Record<string, DataViewSpec>;
|
||||
}
|
|
@ -8,33 +8,45 @@ import {
|
|||
DateHistogramIndexPatternColumn,
|
||||
PersistedIndexPatternLayer,
|
||||
TermsIndexPatternColumn,
|
||||
XYState,
|
||||
} from '@kbn/lens-plugin/public';
|
||||
import type { DataView, DataViewSpec } from '@kbn/data-views-plugin/public';
|
||||
import type { SavedObjectReference } from '@kbn/core-saved-objects-common';
|
||||
|
||||
export const DEFAULT_LAYER_ID = 'layer1';
|
||||
export const DEFAULT_AD_HOC_DATA_VIEW_ID = 'infra_lens_ad_hoc_default';
|
||||
const DEFAULT_BREAKDOWN_SIZE = 10;
|
||||
|
||||
export const getHistogramColumn = (columnName: string, sourceField: string) => {
|
||||
export const getHistogramColumn = ({
|
||||
columnName,
|
||||
overrides,
|
||||
}: {
|
||||
columnName: string;
|
||||
overrides?: Partial<Pick<DateHistogramIndexPatternColumn, 'sourceField' | 'params'>>;
|
||||
}) => {
|
||||
return {
|
||||
[columnName]: {
|
||||
dataType: 'date',
|
||||
isBucketed: true,
|
||||
label: '@timestamp',
|
||||
operationType: 'date_histogram',
|
||||
params: { interval: 'auto' },
|
||||
scale: 'interval',
|
||||
sourceField,
|
||||
sourceField: '@timestamp',
|
||||
...overrides,
|
||||
params: { interval: 'auto', ...overrides?.params },
|
||||
} as DateHistogramIndexPatternColumn,
|
||||
};
|
||||
};
|
||||
|
||||
export const getBreakdownColumn = (
|
||||
columnName: string,
|
||||
sourceField: string,
|
||||
breakdownSize: number
|
||||
): PersistedIndexPatternLayer['columns'] => {
|
||||
export const getBreakdownColumn = ({
|
||||
columnName,
|
||||
overrides,
|
||||
}: {
|
||||
columnName: string;
|
||||
overrides?: Partial<Pick<TermsIndexPatternColumn, 'sourceField'>> & {
|
||||
breakdownSize?: number;
|
||||
};
|
||||
}): PersistedIndexPatternLayer['columns'] => {
|
||||
const { breakdownSize = DEFAULT_BREAKDOWN_SIZE, sourceField } = overrides ?? {};
|
||||
return {
|
||||
[columnName]: {
|
||||
label: `Top ${breakdownSize} values of ${sourceField}`,
|
||||
|
@ -64,47 +76,6 @@ export const getBreakdownColumn = (
|
|||
};
|
||||
};
|
||||
|
||||
export const getXYVisualizationState = (
|
||||
custom: Omit<Partial<XYState>, 'layers'> & { layers: XYState['layers'] }
|
||||
): XYState => ({
|
||||
legend: {
|
||||
isVisible: false,
|
||||
position: 'right',
|
||||
showSingleSeries: false,
|
||||
},
|
||||
valueLabels: 'show',
|
||||
fittingFunction: 'Zero',
|
||||
curveType: 'LINEAR',
|
||||
yLeftScale: 'linear',
|
||||
axisTitlesVisibilitySettings: {
|
||||
x: false,
|
||||
yLeft: false,
|
||||
yRight: true,
|
||||
},
|
||||
tickLabelsVisibilitySettings: {
|
||||
x: true,
|
||||
yLeft: true,
|
||||
yRight: true,
|
||||
},
|
||||
labelsOrientation: {
|
||||
x: 0,
|
||||
yLeft: 0,
|
||||
yRight: 0,
|
||||
},
|
||||
gridlinesVisibilitySettings: {
|
||||
x: true,
|
||||
yLeft: true,
|
||||
yRight: true,
|
||||
},
|
||||
preferredSeriesType: 'line',
|
||||
valuesInLegend: false,
|
||||
emphasizeFitting: true,
|
||||
yTitle: '',
|
||||
xTitle: '',
|
||||
hideEndzones: true,
|
||||
...custom,
|
||||
});
|
||||
|
||||
export const getDefaultReferences = (
|
||||
dataView: DataView,
|
||||
dataLayerId: string
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { LineChart } from './line_chart';
|
||||
export { MetricChart } from './metric_chart';
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* 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 type {
|
||||
FormBasedPersistedState,
|
||||
FormulaPublicApi,
|
||||
PersistedIndexPatternLayer,
|
||||
XYState,
|
||||
} from '@kbn/lens-plugin/public';
|
||||
import type { SavedObjectReference } from '@kbn/core-saved-objects-common';
|
||||
import type { DataView, DataViewSpec } from '@kbn/data-views-plugin/public';
|
||||
import { Filter } from '@kbn/es-query';
|
||||
import {
|
||||
DEFAULT_LAYER_ID,
|
||||
getAdhocDataView,
|
||||
getBreakdownColumn,
|
||||
getDefaultReferences,
|
||||
getHistogramColumn,
|
||||
} from '../utils';
|
||||
import type { LensChartConfig, VisualizationAttributes, LineChartOptions } from '../../types';
|
||||
|
||||
const BREAKDOWN_COLUMN_NAME = 'hosts_aggs_breakdown';
|
||||
const HISTOGRAM_COLUMN_NAME = 'x_date_histogram';
|
||||
const ACCESSOR = 'formula_accessor';
|
||||
|
||||
export class LineChart implements VisualizationAttributes<XYState> {
|
||||
constructor(
|
||||
private chartConfig: LensChartConfig,
|
||||
private dataView: DataView,
|
||||
private formulaAPI: FormulaPublicApi,
|
||||
private options?: LineChartOptions
|
||||
) {}
|
||||
|
||||
getVisualizationType(): string {
|
||||
return 'lnsXY';
|
||||
}
|
||||
|
||||
getLayers(): FormBasedPersistedState['layers'] {
|
||||
const baseLayer: PersistedIndexPatternLayer = {
|
||||
columnOrder: [BREAKDOWN_COLUMN_NAME, HISTOGRAM_COLUMN_NAME],
|
||||
columns: {
|
||||
...getBreakdownColumn({
|
||||
columnName: BREAKDOWN_COLUMN_NAME,
|
||||
overrides: {
|
||||
sourceField: 'host.name',
|
||||
breakdownSize: this.options?.breakdownSize,
|
||||
},
|
||||
}),
|
||||
...getHistogramColumn({
|
||||
columnName: HISTOGRAM_COLUMN_NAME,
|
||||
overrides: {
|
||||
sourceField: this.dataView.timeFieldName,
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
const dataLayer = this.formulaAPI.insertOrReplaceFormulaColumn(
|
||||
ACCESSOR,
|
||||
this.chartConfig.formula,
|
||||
baseLayer,
|
||||
this.dataView
|
||||
);
|
||||
|
||||
if (!dataLayer) {
|
||||
throw new Error('Error generating the data layer for the chart');
|
||||
}
|
||||
|
||||
return { [DEFAULT_LAYER_ID]: dataLayer, ...this.chartConfig.lineChartConfig?.extraLayers };
|
||||
}
|
||||
|
||||
getVisualizationState(): XYState {
|
||||
const extraVisualizationState = this.chartConfig.lineChartConfig?.extraVisualizationState;
|
||||
|
||||
return getXYVisualizationState({
|
||||
...extraVisualizationState,
|
||||
layers: [
|
||||
{
|
||||
layerId: DEFAULT_LAYER_ID,
|
||||
seriesType: 'line',
|
||||
accessors: [ACCESSOR],
|
||||
yConfig: [],
|
||||
layerType: 'data',
|
||||
xAccessor: HISTOGRAM_COLUMN_NAME,
|
||||
splitAccessor: BREAKDOWN_COLUMN_NAME,
|
||||
},
|
||||
...(extraVisualizationState?.layers ? extraVisualizationState?.layers : []),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
getReferences(): SavedObjectReference[] {
|
||||
const extraReference = this.chartConfig.lineChartConfig?.extraReference;
|
||||
return [
|
||||
...getDefaultReferences(this.dataView, DEFAULT_LAYER_ID),
|
||||
...(extraReference ? getDefaultReferences(this.dataView, extraReference) : []),
|
||||
];
|
||||
}
|
||||
|
||||
getAdhocDataView(): Record<string, DataViewSpec> {
|
||||
return getAdhocDataView(this.dataView);
|
||||
}
|
||||
|
||||
getTitle(): string {
|
||||
return this.options?.title ?? this.chartConfig.title ?? '';
|
||||
}
|
||||
|
||||
getFilters(): Filter[] {
|
||||
return this.chartConfig.getFilters({ id: this.dataView.id ?? DEFAULT_LAYER_ID });
|
||||
}
|
||||
}
|
||||
|
||||
export const getXYVisualizationState = (
|
||||
custom: Omit<Partial<XYState>, 'layers'> & { layers: XYState['layers'] }
|
||||
): XYState => ({
|
||||
legend: {
|
||||
isVisible: false,
|
||||
position: 'right',
|
||||
showSingleSeries: false,
|
||||
},
|
||||
valueLabels: 'show',
|
||||
fittingFunction: 'Zero',
|
||||
curveType: 'LINEAR',
|
||||
yLeftScale: 'linear',
|
||||
axisTitlesVisibilitySettings: {
|
||||
x: false,
|
||||
yLeft: false,
|
||||
yRight: true,
|
||||
},
|
||||
tickLabelsVisibilitySettings: {
|
||||
x: true,
|
||||
yLeft: true,
|
||||
yRight: true,
|
||||
},
|
||||
labelsOrientation: {
|
||||
x: 0,
|
||||
yLeft: 0,
|
||||
yRight: 0,
|
||||
},
|
||||
gridlinesVisibilitySettings: {
|
||||
x: true,
|
||||
yLeft: true,
|
||||
yRight: true,
|
||||
},
|
||||
preferredSeriesType: 'line',
|
||||
valuesInLegend: false,
|
||||
emphasizeFitting: true,
|
||||
hideEndzones: true,
|
||||
...custom,
|
||||
});
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* 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 {
|
||||
FormBasedPersistedState,
|
||||
FormulaPublicApi,
|
||||
MetricVisualizationState,
|
||||
PersistedIndexPatternLayer,
|
||||
} from '@kbn/lens-plugin/public';
|
||||
import type { SavedObjectReference } from '@kbn/core-saved-objects-common';
|
||||
import type { DataView, DataViewSpec } from '@kbn/data-views-plugin/public';
|
||||
import type { Filter } from '@kbn/es-query';
|
||||
import {
|
||||
DEFAULT_LAYER_ID,
|
||||
getAdhocDataView,
|
||||
getDefaultReferences,
|
||||
getHistogramColumn,
|
||||
} from '../utils';
|
||||
|
||||
import type { VisualizationAttributes, LensChartConfig, MetricChartOptions } from '../../types';
|
||||
|
||||
const HISTOGRAM_COLUMN_NAME = 'x_date_histogram';
|
||||
const TRENDLINE_LAYER_ID = 'trendline_layer';
|
||||
const TRENDLINE_ACCESSOR = 'metric_trendline_formula_accessor';
|
||||
const ACCESSOR = 'metric_formula_accessor';
|
||||
|
||||
export class MetricChart implements VisualizationAttributes<MetricVisualizationState> {
|
||||
constructor(
|
||||
private chartConfig: LensChartConfig,
|
||||
private dataView: DataView,
|
||||
private formulaAPI: FormulaPublicApi,
|
||||
private options?: MetricChartOptions
|
||||
) {}
|
||||
|
||||
getVisualizationType(): string {
|
||||
return 'lnsMetric';
|
||||
}
|
||||
|
||||
getTrendLineLayer(baseLayer: PersistedIndexPatternLayer): FormBasedPersistedState['layers'] {
|
||||
const trendLineLayer = this.formulaAPI.insertOrReplaceFormulaColumn(
|
||||
TRENDLINE_ACCESSOR,
|
||||
this.chartConfig.formula,
|
||||
baseLayer,
|
||||
this.dataView
|
||||
);
|
||||
|
||||
if (!trendLineLayer) {
|
||||
throw new Error('Error generating the data layer for the chart');
|
||||
}
|
||||
|
||||
return {
|
||||
[TRENDLINE_LAYER_ID]: {
|
||||
linkToLayers: [DEFAULT_LAYER_ID],
|
||||
...trendLineLayer,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
getLayers(): FormBasedPersistedState['layers'] {
|
||||
const { showTrendLine = true } = this.options ?? {};
|
||||
const baseLayer: PersistedIndexPatternLayer = {
|
||||
columnOrder: [HISTOGRAM_COLUMN_NAME],
|
||||
columns: getHistogramColumn({
|
||||
columnName: HISTOGRAM_COLUMN_NAME,
|
||||
overrides: {
|
||||
sourceField: this.dataView.timeFieldName,
|
||||
params: {
|
||||
interval: 'auto',
|
||||
includeEmptyRows: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
sampling: 1,
|
||||
};
|
||||
|
||||
const baseLayerDetails = this.formulaAPI.insertOrReplaceFormulaColumn(
|
||||
ACCESSOR,
|
||||
{
|
||||
...this.chartConfig.formula,
|
||||
label: this.options?.title ?? this.chartConfig.title,
|
||||
},
|
||||
{ columnOrder: [], columns: {} },
|
||||
this.dataView
|
||||
);
|
||||
|
||||
if (!baseLayerDetails) {
|
||||
throw new Error('Error generating the data layer for the chart');
|
||||
}
|
||||
|
||||
return {
|
||||
[DEFAULT_LAYER_ID]: baseLayerDetails,
|
||||
...(showTrendLine ? this.getTrendLineLayer(baseLayer) : {}),
|
||||
};
|
||||
}
|
||||
|
||||
getVisualizationState(): MetricVisualizationState {
|
||||
const { subtitle, backgroundColor, showTrendLine = true } = this.options ?? {};
|
||||
return {
|
||||
layerId: DEFAULT_LAYER_ID,
|
||||
layerType: 'data',
|
||||
metricAccessor: ACCESSOR,
|
||||
color: backgroundColor,
|
||||
subtitle,
|
||||
showBar: false,
|
||||
...(showTrendLine
|
||||
? {
|
||||
trendlineLayerId: TRENDLINE_LAYER_ID,
|
||||
trendlineLayerType: 'metricTrendline',
|
||||
trendlineMetricAccessor: TRENDLINE_ACCESSOR,
|
||||
trendlineTimeAccessor: HISTOGRAM_COLUMN_NAME,
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
getReferences(): SavedObjectReference[] {
|
||||
const { showTrendLine = true } = this.options ?? {};
|
||||
return [
|
||||
...getDefaultReferences(this.dataView, DEFAULT_LAYER_ID),
|
||||
...(showTrendLine ? getDefaultReferences(this.dataView, TRENDLINE_LAYER_ID) : []),
|
||||
];
|
||||
}
|
||||
|
||||
getAdhocDataView(): Record<string, DataViewSpec> {
|
||||
return getAdhocDataView(this.dataView);
|
||||
}
|
||||
|
||||
getTitle(): string {
|
||||
return this.options?.showTitle ? this.options?.title ?? this.chartConfig.title : '';
|
||||
}
|
||||
|
||||
getFilters(): Filter[] {
|
||||
return this.chartConfig.getFilters({ id: this.dataView.id ?? DEFAULT_LAYER_ID });
|
||||
}
|
||||
}
|
63
x-pack/plugins/infra/public/common/visualizations/types.ts
Normal file
63
x-pack/plugins/infra/public/common/visualizations/types.ts
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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 type { SavedObjectReference } from '@kbn/core-saved-objects-common';
|
||||
import type { DataViewSpec } from '@kbn/data-views-plugin/common';
|
||||
import { DataViewBase, Filter } from '@kbn/es-query';
|
||||
import {
|
||||
FormBasedPersistedState,
|
||||
FormulaPublicApi,
|
||||
MetricVisualizationState,
|
||||
TypedLensByValueInput,
|
||||
XYState,
|
||||
} from '@kbn/lens-plugin/public';
|
||||
import { hostLensFormulas, visualizationTypes } from './constants';
|
||||
|
||||
export type LensAttributes = TypedLensByValueInput['attributes'];
|
||||
|
||||
export interface LensOptions {
|
||||
title: string;
|
||||
}
|
||||
export interface LineChartOptions extends LensOptions {
|
||||
breakdownSize?: number;
|
||||
}
|
||||
export interface MetricChartOptions extends LensOptions {
|
||||
subtitle?: string;
|
||||
showTitle?: boolean;
|
||||
showTrendLine?: boolean;
|
||||
backgroundColor?: string;
|
||||
}
|
||||
|
||||
export interface LensLineChartConfig {
|
||||
extraVisualizationState?: Partial<Omit<XYState, 'layers'> & { layers: XYState['layers'] }>;
|
||||
extraLayers?: FormBasedPersistedState['layers'];
|
||||
extraReference?: string;
|
||||
}
|
||||
export interface LensChartConfig {
|
||||
title: string;
|
||||
formula: Formula;
|
||||
lineChartConfig?: LensLineChartConfig;
|
||||
getFilters: ({ id }: Pick<DataViewBase, 'id'>) => Filter[];
|
||||
}
|
||||
|
||||
export type TVisualization = XYState | MetricVisualizationState;
|
||||
export interface VisualizationAttributes<T extends TVisualization> {
|
||||
getTitle(): string;
|
||||
getVisualizationType(): string;
|
||||
getLayers(): FormBasedPersistedState['layers'];
|
||||
getVisualizationState(): T;
|
||||
getReferences(): SavedObjectReference[];
|
||||
getFilters(): Filter[];
|
||||
getAdhocDataView(): Record<string, DataViewSpec>;
|
||||
}
|
||||
|
||||
export type Formula = Parameters<FormulaPublicApi['insertOrReplaceFormulaColumn']>[1];
|
||||
|
||||
export type VisualizationTypes = keyof typeof visualizationTypes;
|
||||
export type HostsLensFormulas = keyof typeof hostLensFormulas;
|
||||
export type HostsLensMetricChartFormulas = Exclude<HostsLensFormulas, 'diskIORead' | 'diskIOWrite'>;
|
||||
export type HostsLensLineChartFormulas = Exclude<HostsLensFormulas, 'hostCount'>;
|
25
x-pack/plugins/infra/public/hooks/use_intersection_once.ts
Normal file
25
x-pack/plugins/infra/public/hooks/use_intersection_once.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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 { RefObject, useEffect, useState } from 'react';
|
||||
import useIntersection from 'react-use/lib/useIntersection';
|
||||
|
||||
export const useIntersectedOnce = (
|
||||
ref: RefObject<HTMLElement>,
|
||||
options: IntersectionObserverInit
|
||||
) => {
|
||||
const [intersectedOnce, setIntersectedOnce] = useState(false);
|
||||
const intersection = useIntersection(ref, options);
|
||||
|
||||
useEffect(() => {
|
||||
if (!intersectedOnce && (intersection?.intersectionRatio ?? 0) > 0) {
|
||||
setIntersectedOnce(true);
|
||||
}
|
||||
}, [intersectedOnce, intersection?.intersectionRatio]);
|
||||
|
||||
return { intersectedOnce, intersection };
|
||||
};
|
|
@ -48,7 +48,11 @@ describe('useHostTable hook', () => {
|
|||
it('should return the basic lens attributes', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useLensAttributes({
|
||||
visualizationType: 'lineChart',
|
||||
type: 'load',
|
||||
options: {
|
||||
title: 'Injected Normalized Load',
|
||||
},
|
||||
dataView: mockDataView,
|
||||
})
|
||||
);
|
||||
|
@ -57,12 +61,12 @@ describe('useHostTable hook', () => {
|
|||
const { state, title } = result.current.attributes ?? {};
|
||||
const { datasourceStates, filters } = state ?? {};
|
||||
|
||||
expect(title).toBe('Normalized Load');
|
||||
expect(title).toBe('Injected Normalized Load');
|
||||
expect(datasourceStates).toEqual({
|
||||
formBased: {
|
||||
layers: {
|
||||
layer1: {
|
||||
columnOrder: ['hosts_aggs_breakdown', 'x_date_histogram', 'y_cpu_cores_usage'],
|
||||
columnOrder: ['hosts_aggs_breakdown', 'x_date_histogram', 'formula_accessor'],
|
||||
columns: {
|
||||
hosts_aggs_breakdown: {
|
||||
dataType: 'string',
|
||||
|
@ -100,7 +104,7 @@ describe('useHostTable hook', () => {
|
|||
scale: 'interval',
|
||||
sourceField: '@timestamp',
|
||||
},
|
||||
y_cpu_cores_usage: {
|
||||
formula_accessor: {
|
||||
customLabel: false,
|
||||
dataType: 'number',
|
||||
filter: undefined,
|
||||
|
@ -154,19 +158,36 @@ describe('useHostTable hook', () => {
|
|||
},
|
||||
},
|
||||
});
|
||||
expect(filters).toEqual([]);
|
||||
expect(filters).toEqual([
|
||||
{
|
||||
meta: {
|
||||
index: 'mock-id',
|
||||
},
|
||||
query: {
|
||||
exists: {
|
||||
field: 'host.name',
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return attributes with injected values', async () => {
|
||||
it('should return extra actions', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useLensAttributes({
|
||||
visualizationType: 'lineChart',
|
||||
type: 'load',
|
||||
dataView: mockDataView,
|
||||
})
|
||||
);
|
||||
await waitForNextUpdate();
|
||||
|
||||
const injectedData = {
|
||||
const extraActions = result.current.getExtraActions({
|
||||
timeRange: {
|
||||
from: 'now-15m',
|
||||
to: 'now',
|
||||
mode: 'relative',
|
||||
},
|
||||
query: {
|
||||
language: 'kuery',
|
||||
query: '{term: { host.name: "a"}}',
|
||||
|
@ -186,17 +207,8 @@ describe('useHostTable hook', () => {
|
|||
query: { range: { 'system.load.cores': { gte: 0 } } },
|
||||
},
|
||||
],
|
||||
title: 'Injected CPU Cores',
|
||||
};
|
||||
});
|
||||
|
||||
const injectedAttributes = result.current.injectData(injectedData);
|
||||
|
||||
const { state, title } = injectedAttributes ?? {};
|
||||
const { filters, query } = state ?? {};
|
||||
|
||||
expect(title).toEqual(injectedData.title);
|
||||
expect(query).toEqual(injectedData.query);
|
||||
expect(filters).toHaveLength(1);
|
||||
expect(filters).toContain(injectedData.filters[0]);
|
||||
expect(extraActions.openInLens).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -12,25 +12,45 @@ import { useKibana } from '@kbn/kibana-react-plugin/public';
|
|||
import { ActionExecutionContext } from '@kbn/ui-actions-plugin/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import useAsync from 'react-use/lib/useAsync';
|
||||
import { InfraClientSetupDeps, LensAttributes, LensOptions } from '../types';
|
||||
import { InfraClientSetupDeps } from '../types';
|
||||
import {
|
||||
buildLensAttributes,
|
||||
HostLensAttributesTypes,
|
||||
hostMetricsLensAttributes,
|
||||
HostsLensFormulas,
|
||||
HostsLensMetricChartFormulas,
|
||||
HostsLensLineChartFormulas,
|
||||
LineChartOptions,
|
||||
MetricChartOptions,
|
||||
LensAttributes,
|
||||
hostLensFormulas,
|
||||
visualizationTypes,
|
||||
} from '../common/visualizations';
|
||||
|
||||
interface UseLensAttributesParams {
|
||||
type: HostLensAttributesTypes;
|
||||
type Options = LineChartOptions | MetricChartOptions;
|
||||
interface UseLensAttributesBaseParams<T extends HostsLensFormulas, O extends Options> {
|
||||
dataView: DataView | undefined;
|
||||
options?: LensOptions;
|
||||
type: T;
|
||||
options?: O;
|
||||
}
|
||||
|
||||
interface UseLensAttributesLineChartParams
|
||||
extends UseLensAttributesBaseParams<HostsLensLineChartFormulas, LineChartOptions> {
|
||||
visualizationType: 'lineChart';
|
||||
}
|
||||
|
||||
interface UseLensAttributesMetricChartParams
|
||||
extends UseLensAttributesBaseParams<HostsLensMetricChartFormulas, MetricChartOptions> {
|
||||
visualizationType: 'metricChart';
|
||||
}
|
||||
|
||||
type UseLensAttributesParams =
|
||||
| UseLensAttributesLineChartParams
|
||||
| UseLensAttributesMetricChartParams;
|
||||
|
||||
export const useLensAttributes = ({
|
||||
type,
|
||||
dataView,
|
||||
options = {
|
||||
breakdownSize: 10,
|
||||
},
|
||||
options,
|
||||
visualizationType,
|
||||
}: UseLensAttributesParams) => {
|
||||
const {
|
||||
services: { lens },
|
||||
|
@ -39,31 +59,31 @@ export const useLensAttributes = ({
|
|||
const { value, error } = useAsync(lens.stateHelperApi, [lens]);
|
||||
const { formula: formulaAPI } = value ?? {};
|
||||
|
||||
const attributes: LensAttributes | null = useMemo(() => {
|
||||
const attributes = useMemo(() => {
|
||||
if (!dataView || !formulaAPI) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const VisualizationClass = hostMetricsLensAttributes[type];
|
||||
const lensChartConfig = hostLensFormulas[type];
|
||||
const VisualizationType = visualizationTypes[visualizationType];
|
||||
|
||||
const visualizationAttributes = buildLensAttributes(
|
||||
new VisualizationClass(dataView, options, formulaAPI)
|
||||
new VisualizationType(lensChartConfig, dataView, formulaAPI, options)
|
||||
);
|
||||
|
||||
return visualizationAttributes;
|
||||
}, [dataView, formulaAPI, options, type]);
|
||||
}, [dataView, formulaAPI, options, type, visualizationType]);
|
||||
|
||||
const injectData = (data: {
|
||||
const injectFilters = (data: {
|
||||
timeRange: TimeRange;
|
||||
filters: Filter[];
|
||||
query: Query;
|
||||
title?: string;
|
||||
}): LensAttributes | null => {
|
||||
if (!attributes) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
...attributes,
|
||||
...(!!data.title ? { title: data.title } : {}),
|
||||
state: {
|
||||
...attributes.state,
|
||||
query: data.query,
|
||||
|
@ -72,7 +92,15 @@ export const useLensAttributes = ({
|
|||
};
|
||||
};
|
||||
|
||||
const getExtraActions = (currentAttributes: LensAttributes | null, timeRange: TimeRange) => {
|
||||
const getExtraActions = ({
|
||||
timeRange,
|
||||
filters,
|
||||
query,
|
||||
}: {
|
||||
timeRange: TimeRange;
|
||||
filters: Filter[];
|
||||
query: Query;
|
||||
}) => {
|
||||
return {
|
||||
openInLens: {
|
||||
id: 'openInLens',
|
||||
|
@ -93,12 +121,13 @@ export const useLensAttributes = ({
|
|||
return true;
|
||||
},
|
||||
async execute(_context: ActionExecutionContext): Promise<void> {
|
||||
if (currentAttributes) {
|
||||
const injectedAttributes = injectFilters({ timeRange, filters, query });
|
||||
if (injectedAttributes) {
|
||||
navigateToPrefilledEditor(
|
||||
{
|
||||
id: '',
|
||||
timeRange,
|
||||
attributes: currentAttributes,
|
||||
attributes: injectedAttributes,
|
||||
},
|
||||
{
|
||||
openInNewTab: true,
|
||||
|
@ -111,5 +140,5 @@ export const useLensAttributes = ({
|
|||
};
|
||||
};
|
||||
|
||||
return { attributes, injectData, getExtraActions, error };
|
||||
return { attributes, getExtraActions, error };
|
||||
};
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* 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 React, { useEffect, useState } from 'react';
|
||||
|
||||
import { Action } from '@kbn/ui-actions-plugin/public';
|
||||
import { ViewMode } from '@kbn/embeddable-plugin/public';
|
||||
import { BrushTriggerEvent } from '@kbn/charts-plugin/public';
|
||||
import { EuiFlexGroup } from '@elastic/eui';
|
||||
import { EuiFlexItem } from '@elastic/eui';
|
||||
import { EuiLoadingChart } from '@elastic/eui';
|
||||
import { Filter, Query, TimeRange } from '@kbn/es-query';
|
||||
import { useKibanaContextForPlugin } from '../../../../../hooks/use_kibana';
|
||||
import { useIntersectedOnce } from '../../../../../hooks/use_intersection_once';
|
||||
import { LensAttributes } from '../../../../../common/visualizations';
|
||||
|
||||
export interface Props {
|
||||
id: string;
|
||||
attributes: LensAttributes | null;
|
||||
dateRange: TimeRange;
|
||||
query: Query;
|
||||
filters: Filter[];
|
||||
extraActions: Action[];
|
||||
lastReloadRequestTime?: number;
|
||||
style?: React.CSSProperties;
|
||||
onBrushEnd?: (data: BrushTriggerEvent['data']) => void;
|
||||
}
|
||||
|
||||
export const LensWrapper = ({
|
||||
attributes,
|
||||
dateRange,
|
||||
filters,
|
||||
id,
|
||||
query,
|
||||
extraActions,
|
||||
style,
|
||||
onBrushEnd,
|
||||
lastReloadRequestTime,
|
||||
}: Props) => {
|
||||
const intersectionRef = React.useRef(null);
|
||||
|
||||
const [currentLastReloadRequestTime, setCurrentLastReloadRequestTime] = useState<
|
||||
number | undefined
|
||||
>(lastReloadRequestTime);
|
||||
const {
|
||||
services: { lens },
|
||||
} = useKibanaContextForPlugin();
|
||||
const { intersectedOnce, intersection } = useIntersectedOnce(intersectionRef, {
|
||||
threshold: 1,
|
||||
});
|
||||
|
||||
const EmbeddableComponent = lens.EmbeddableComponent;
|
||||
|
||||
useEffect(() => {
|
||||
if ((intersection?.intersectionRatio ?? 0) === 1) {
|
||||
setCurrentLastReloadRequestTime(lastReloadRequestTime);
|
||||
}
|
||||
}, [intersection?.intersectionRatio, lastReloadRequestTime]);
|
||||
|
||||
const isReady = attributes && intersectedOnce;
|
||||
|
||||
return (
|
||||
<div ref={intersectionRef}>
|
||||
{!isReady ? (
|
||||
<EuiFlexGroup style={style} justifyContent="center" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiLoadingChart size="l" mono />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
) : (
|
||||
<EmbeddableComponent
|
||||
id={id}
|
||||
style={style}
|
||||
attributes={attributes}
|
||||
viewMode={ViewMode.VIEW}
|
||||
timeRange={dateRange}
|
||||
query={query}
|
||||
filters={filters}
|
||||
extraActions={extraActions}
|
||||
lastReloadRequestTime={currentLastReloadRequestTime}
|
||||
executionContext={{
|
||||
type: 'infrastructure_observability_hosts_view',
|
||||
name: id,
|
||||
}}
|
||||
onBrushEnd={onBrushEnd}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -4,7 +4,7 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import React, { useMemo } from 'react';
|
||||
import React, { useEffect, useMemo, useRef } from 'react';
|
||||
import {
|
||||
Chart,
|
||||
Metric,
|
||||
|
@ -12,13 +12,15 @@ import {
|
|||
type MetricWNumber,
|
||||
type MetricWTrend,
|
||||
} from '@elastic/charts';
|
||||
|
||||
import { EuiPanel } from '@elastic/eui';
|
||||
import styled from 'styled-components';
|
||||
import { EuiLoadingChart } from '@elastic/eui';
|
||||
import { EuiFlexGroup } from '@elastic/eui';
|
||||
import { EuiFlexItem } from '@elastic/eui';
|
||||
import { EuiToolTip } from '@elastic/eui';
|
||||
import { EuiProgress } from '@elastic/eui';
|
||||
import { css } from '@emotion/react';
|
||||
import { useEuiTheme } from '@elastic/eui';
|
||||
import type { SnapshotNode, SnapshotNodeMetric } from '../../../../../../common/http_api';
|
||||
import { createInventoryMetricFormatter } from '../../../inventory_view/lib/create_inventory_metric_formatter';
|
||||
import type { SnapshotMetricType } from '../../../../../../common/inventory_models/types';
|
||||
|
@ -47,7 +49,7 @@ interface Props extends ChartBaseProps {
|
|||
|
||||
const MIN_HEIGHT = 150;
|
||||
|
||||
export const KPIChart = ({
|
||||
export const MetricChartWrapper = ({
|
||||
color,
|
||||
extra,
|
||||
id,
|
||||
|
@ -63,12 +65,23 @@ export const KPIChart = ({
|
|||
type,
|
||||
...props
|
||||
}: Props) => {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
const loadedOnce = useRef(false);
|
||||
const metrics = useMemo(() => (nodes ?? [])[0]?.metrics ?? [], [nodes]);
|
||||
const metricsTimeseries = useMemo(
|
||||
() => (metrics ?? []).find((m) => m.name === type)?.timeseries,
|
||||
[metrics, type]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!loadedOnce.current && !loading) {
|
||||
loadedOnce.current = true;
|
||||
}
|
||||
return () => {
|
||||
loadedOnce.current = false;
|
||||
};
|
||||
}, [loading]);
|
||||
|
||||
const metricsValue = useMemo(() => {
|
||||
if (overrideValue) {
|
||||
return overrideValue;
|
||||
|
@ -96,24 +109,39 @@ export const KPIChart = ({
|
|||
|
||||
return (
|
||||
<EuiPanel hasShadow={false} paddingSize="none" {...props}>
|
||||
{loading ? (
|
||||
<EuiFlexGroup style={{ minHeight: MIN_HEIGHT }} justifyContent="center" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiLoadingChart />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
) : (
|
||||
<EuiToolTip
|
||||
className="eui-fullWidth"
|
||||
delay="long"
|
||||
content={toolTip}
|
||||
anchorClassName="eui-fullWidth"
|
||||
>
|
||||
<KPIChartStyled size={{ height: MIN_HEIGHT }}>
|
||||
<Metric id={id} data={[[metricsData]]} />
|
||||
</KPIChartStyled>
|
||||
</EuiToolTip>
|
||||
)}
|
||||
<div
|
||||
css={css`
|
||||
position: relative;
|
||||
border-radius: ${euiTheme.size.s};
|
||||
overflow: hidden;
|
||||
`}
|
||||
>
|
||||
{loading && (
|
||||
<EuiProgress size="xs" color="accent" position="absolute" style={{ zIndex: 1 }} />
|
||||
)}
|
||||
{loading && !loadedOnce.current ? (
|
||||
<EuiFlexGroup
|
||||
style={{ minHeight: MIN_HEIGHT }}
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiLoadingChart size="l" mono />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
) : (
|
||||
<EuiToolTip
|
||||
className="eui-fullWidth"
|
||||
delay="regular"
|
||||
content={toolTip}
|
||||
anchorClassName="eui-fullWidth"
|
||||
>
|
||||
<KPIChartStyled size={{ height: MIN_HEIGHT }}>
|
||||
<Metric id={id} data={[[metricsData]]} />
|
||||
</KPIChartStyled>
|
||||
</EuiToolTip>
|
||||
)}
|
||||
</div>
|
||||
</EuiPanel>
|
||||
);
|
||||
};
|
|
@ -13,9 +13,9 @@ import { useMetricsDataViewContext } from '../hooks/use_data_view';
|
|||
import { UnifiedSearchBar } from './unified_search_bar';
|
||||
import { HostsTable } from './hosts_table';
|
||||
import { HostsViewProvider } from '../hooks/use_hosts_view';
|
||||
import { KPICharts } from './kpi_charts/kpi_charts';
|
||||
import { Tabs } from './tabs/tabs';
|
||||
import { AlertsQueryProvider } from '../hooks/use_alerts_query';
|
||||
import { KPIGrid } from './kpis/kpi_grid';
|
||||
|
||||
export const HostContainer = () => {
|
||||
const { dataView, loading, hasError } = useMetricsDataViewContext();
|
||||
|
@ -40,7 +40,7 @@ export const HostContainer = () => {
|
|||
<HostsViewProvider>
|
||||
<EuiFlexGroup direction="column">
|
||||
<EuiFlexItem grow={false}>
|
||||
<KPICharts />
|
||||
<KPIGrid />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<HostsTable />
|
||||
|
|
|
@ -49,7 +49,7 @@ export const HostsTable = () => {
|
|||
if (loading) {
|
||||
return (
|
||||
<InfraLoadingPanel
|
||||
height="185px"
|
||||
height="400px"
|
||||
width="auto"
|
||||
text={i18n.translate('xpack.infra.waffle.loadingDataText', {
|
||||
defaultMessage: 'Loading data',
|
||||
|
|
|
@ -1,154 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import React from 'react';
|
||||
|
||||
import { EuiFlexGroup } from '@elastic/eui';
|
||||
import { EuiFlexItem } from '@elastic/eui';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { Tile } from './tile';
|
||||
import { HostsTile } from './hosts_tile';
|
||||
|
||||
export const KPICharts = () => {
|
||||
return (
|
||||
<EuiFlexGroup
|
||||
direction="row"
|
||||
gutterSize="s"
|
||||
style={{ flexGrow: 0 }}
|
||||
data-test-subj="hostsView-metricsTrend"
|
||||
>
|
||||
<EuiFlexItem>
|
||||
<HostsTile
|
||||
type="hostsCount"
|
||||
metricType="value"
|
||||
color="#6DCCB1"
|
||||
title={i18n.translate('xpack.infra.hostsViewPage.metricTrend.hostCount.title', {
|
||||
defaultMessage: 'Hosts',
|
||||
})}
|
||||
trendA11yTitle={i18n.translate(
|
||||
'xpack.infra.hostsViewPage.metricTrend.hostCount.a11y.title',
|
||||
{
|
||||
defaultMessage: 'CPU usage over time.',
|
||||
}
|
||||
)}
|
||||
toolTip={i18n.translate('xpack.infra.hostsViewPage.metricTrend.hostCount.tooltip', {
|
||||
defaultMessage: 'The number of hosts returned by your current search criteria.',
|
||||
})}
|
||||
data-test-subj="hostsView-metricsTrend-hosts"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<Tile
|
||||
type="cpu"
|
||||
metricType="avg"
|
||||
color="#F1D86F"
|
||||
title={i18n.translate('xpack.infra.hostsViewPage.metricTrend.cpu.title', {
|
||||
defaultMessage: 'CPU usage',
|
||||
})}
|
||||
subtitle={i18n.translate('xpack.infra.hostsViewPage.metricTrend.cpu.subtitle', {
|
||||
defaultMessage: 'Average',
|
||||
})}
|
||||
trendA11yTitle={i18n.translate('xpack.infra.hostsViewPage.metricTrend.cpu.a11y.title', {
|
||||
defaultMessage: 'CPU usage over time.',
|
||||
})}
|
||||
trendA11yDescription={i18n.translate(
|
||||
'xpack.infra.hostsViewPage.metricTrend.cpu.a11y.description',
|
||||
{
|
||||
defaultMessage: 'A line chart showing the trend of the primary metric over time.',
|
||||
}
|
||||
)}
|
||||
toolTip={i18n.translate('xpack.infra.hostsViewPage.metricTrend.cpu.tooltip', {
|
||||
defaultMessage:
|
||||
'Average of percentage of CPU time spent in states other than Idle and IOWait, normalized by the number of CPU cores. Includes both time spent on user space and kernel space. 100% means all CPUs of the host are busy.',
|
||||
})}
|
||||
data-test-subj="hostsView-metricsTrend-cpu"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<Tile
|
||||
type="memory"
|
||||
metricType="avg"
|
||||
color="#A987D1"
|
||||
title={i18n.translate('xpack.infra.hostsViewPage.metricTrend.memory.title', {
|
||||
defaultMessage: 'Memory usage',
|
||||
})}
|
||||
subtitle={i18n.translate('xpack.infra.hostsViewPage.metricTrend.memory.subtitle', {
|
||||
defaultMessage: 'Average',
|
||||
})}
|
||||
trendA11yTitle={i18n.translate('xpack.infra.hostsViewPage.metricTrend.memory.a11yTitle', {
|
||||
defaultMessage: 'Memory usage over time.',
|
||||
})}
|
||||
trendA11yDescription={i18n.translate(
|
||||
'xpack.infra.hostsViewPage.metricTrend.memory.a11yDescription',
|
||||
{
|
||||
defaultMessage: 'A line chart showing the trend of the primary metric over time.',
|
||||
}
|
||||
)}
|
||||
toolTip={i18n.translate('xpack.infra.hostsViewPage.metricTrend.memory.tooltip', {
|
||||
defaultMessage:
|
||||
"Average of percentage of main memory usage excluding page cache. This includes resident memory for all processes plus memory used by the kernel structures and code apart the page cache. A high level indicates a situation of memory saturation for a host. 100% means the main memory is entirely filled with memory that can't be reclaimed, except by swapping out.",
|
||||
})}
|
||||
data-test-subj="hostsView-metricsTrend-memory"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<Tile
|
||||
type="rx"
|
||||
metricType="avg"
|
||||
color="#79AAD9"
|
||||
title={i18n.translate('xpack.infra.hostsViewPage.metricTrend.rx.title', {
|
||||
defaultMessage: 'Network inbound (RX)',
|
||||
})}
|
||||
subtitle={i18n.translate('xpack.infra.hostsViewPage.metricTrend.rx.subtitle', {
|
||||
defaultMessage: 'Average',
|
||||
})}
|
||||
trendA11yTitle={i18n.translate('xpack.infra.hostsViewPage.metricTrend.rx.a11y.title', {
|
||||
defaultMessage: 'Network inbound (RX) over time.',
|
||||
})}
|
||||
trendA11yDescription={i18n.translate(
|
||||
'xpack.infra.hostsViewPage.metricTrend.rx.a11y.description',
|
||||
{
|
||||
defaultMessage: 'A line chart showing the trend of the primary metric over time.',
|
||||
}
|
||||
)}
|
||||
toolTip={i18n.translate('xpack.infra.hostsViewPage.metricTrend.rx.tooltip', {
|
||||
defaultMessage:
|
||||
'Number of bytes which have been received per second on the public interfaces of the hosts.',
|
||||
})}
|
||||
data-test-subj="hostsView-metricsTrend-rx"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<Tile
|
||||
type="tx"
|
||||
metricType="avg"
|
||||
color="#F5A35C"
|
||||
title={i18n.translate('xpack.infra.hostsViewPage.metricTrend.tx.title', {
|
||||
defaultMessage: 'Network outbound (TX)',
|
||||
})}
|
||||
subtitle={i18n.translate('xpack.infra.hostsViewPage.metricTrend.tx.subtitle', {
|
||||
defaultMessage: 'Average',
|
||||
})}
|
||||
trendA11yTitle={i18n.translate('xpack.infra.hostsViewPage.metricTrend.tx.a11.title', {
|
||||
defaultMessage: 'Network outbound (TX) usage over time.',
|
||||
})}
|
||||
trendA11yDescription={i18n.translate(
|
||||
'xpack.infra.hostsViewPage.metricTrend.tx.a11y.description',
|
||||
{
|
||||
defaultMessage: 'A line chart showing the trend of the primary metric over time.',
|
||||
}
|
||||
)}
|
||||
toolTip={i18n.translate('xpack.infra.hostsViewPage.metricTrend.tx.tooltip', {
|
||||
defaultMessage:
|
||||
'Number of bytes which have been sent per second on the public interfaces of the hosts',
|
||||
})}
|
||||
data-test-subj="hostsView-metricsTrend-tx"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
|
@ -1,32 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import React from 'react';
|
||||
import type { SnapshotMetricType } from '../../../../../../common/inventory_models/types';
|
||||
|
||||
import { useSnapshot } from '../../../inventory_view/hooks/use_snaphot';
|
||||
import { useHostsViewContext } from '../../hooks/use_hosts_view';
|
||||
import { type ChartBaseProps, KPIChart } from './kpi_chart';
|
||||
|
||||
interface Props extends Omit<ChartBaseProps, 'type'> {
|
||||
type: SnapshotMetricType;
|
||||
}
|
||||
export const Tile = ({ type, ...props }: Props) => {
|
||||
const { baseRequest } = useHostsViewContext();
|
||||
|
||||
const { nodes, loading } = useSnapshot(
|
||||
{
|
||||
...baseRequest,
|
||||
metrics: [{ type }],
|
||||
groupBy: null,
|
||||
includeTimeseries: true,
|
||||
dropPartialBuckets: false,
|
||||
},
|
||||
{ abortable: true }
|
||||
);
|
||||
|
||||
return <KPIChart id={`$metric-${type}`} type={type} nodes={nodes} loading={loading} {...props} />;
|
||||
};
|
|
@ -7,14 +7,14 @@
|
|||
import React from 'react';
|
||||
|
||||
import { useHostsViewContext } from '../../hooks/use_hosts_view';
|
||||
import { type ChartBaseProps, KPIChart } from './kpi_chart';
|
||||
import { type ChartBaseProps, MetricChartWrapper } from '../chart/metric_chart_wrapper';
|
||||
|
||||
export const HostsTile = ({ type, ...props }: ChartBaseProps) => {
|
||||
const { hostNodes, loading } = useHostsViewContext();
|
||||
|
||||
return (
|
||||
<KPIChart
|
||||
id={`$metric-${type}`}
|
||||
<MetricChartWrapper
|
||||
id={`metric-${type}`}
|
||||
type={type}
|
||||
nodes={[]}
|
||||
loading={loading}
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
|
||||
import { EuiFlexGroup } from '@elastic/eui';
|
||||
import { EuiFlexItem } from '@elastic/eui';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { KPIChartProps, Tile } from './tile';
|
||||
import { HostsTile } from './hosts_tile';
|
||||
import { ChartBaseProps } from '../chart/metric_chart_wrapper';
|
||||
|
||||
const KPI_CHARTS: KPIChartProps[] = [
|
||||
{
|
||||
type: 'cpu',
|
||||
trendLine: true,
|
||||
backgroundColor: '#F1D86F',
|
||||
title: i18n.translate('xpack.infra.hostsViewPage.metricTrend.cpu.title', {
|
||||
defaultMessage: 'CPU usage',
|
||||
}),
|
||||
subtitle: i18n.translate('xpack.infra.hostsViewPage.metricTrend.cpu.subtitle', {
|
||||
defaultMessage: 'Average',
|
||||
}),
|
||||
toolTip: i18n.translate('xpack.infra.hostsViewPage.metricTrend.cpu.tooltip', {
|
||||
defaultMessage:
|
||||
'Average of percentage of CPU time spent in states other than Idle and IOWait, normalized by the number of CPU cores. Includes both time spent on user space and kernel space. 100% means all CPUs of the host are busy.',
|
||||
}),
|
||||
},
|
||||
{
|
||||
type: 'memory',
|
||||
trendLine: true,
|
||||
backgroundColor: '#A987D1',
|
||||
title: i18n.translate('xpack.infra.hostsViewPage.metricTrend.memory.title', {
|
||||
defaultMessage: 'Memory usage',
|
||||
}),
|
||||
subtitle: i18n.translate('xpack.infra.hostsViewPage.metricTrend.memory.subtitle', {
|
||||
defaultMessage: 'Average',
|
||||
}),
|
||||
toolTip: i18n.translate('xpack.infra.hostsViewPage.metricTrend.memory.tooltip', {
|
||||
defaultMessage:
|
||||
"Average of percentage of main memory usage excluding page cache. This includes resident memory for all processes plus memory used by the kernel structures and code apart the page cache. A high level indicates a situation of memory saturation for a host. 100% means the main memory is entirely filled with memory that can't be reclaimed, except by swapping out.",
|
||||
}),
|
||||
},
|
||||
{
|
||||
type: 'rx',
|
||||
trendLine: true,
|
||||
backgroundColor: '#79AAD9',
|
||||
title: i18n.translate('xpack.infra.hostsViewPage.metricTrend.rx.title', {
|
||||
defaultMessage: 'Network inbound (RX)',
|
||||
}),
|
||||
subtitle: i18n.translate('xpack.infra.hostsViewPage.metricTrend.rx.subtitle', {
|
||||
defaultMessage: 'Average',
|
||||
}),
|
||||
toolTip: i18n.translate('xpack.infra.hostsViewPage.metricTrend.rx.tooltip', {
|
||||
defaultMessage:
|
||||
'Number of bytes which have been received per second on the public interfaces of the hosts.',
|
||||
}),
|
||||
},
|
||||
{
|
||||
type: 'tx',
|
||||
trendLine: true,
|
||||
backgroundColor: '#F5A35C',
|
||||
title: i18n.translate('xpack.infra.hostsViewPage.metricTrend.tx.title', {
|
||||
defaultMessage: 'Network outbound (TX)',
|
||||
}),
|
||||
subtitle: i18n.translate('xpack.infra.hostsViewPage.metricTrend.tx.subtitle', {
|
||||
defaultMessage: 'Average',
|
||||
}),
|
||||
toolTip: i18n.translate('xpack.infra.hostsViewPage.metricTrend.tx.tooltip', {
|
||||
defaultMessage:
|
||||
'Number of bytes which have been received per second on the public interfaces of the hosts.',
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
const HOSTS_CHART: ChartBaseProps = {
|
||||
type: 'hostsCount',
|
||||
color: '#6DCCB1',
|
||||
metricType: 'value',
|
||||
title: i18n.translate('xpack.infra.hostsViewPage.metricTrend.hostCount.title', {
|
||||
defaultMessage: 'Hosts',
|
||||
}),
|
||||
trendA11yTitle: i18n.translate('xpack.infra.hostsViewPage.metricTrend.hostCount.a11y.title', {
|
||||
defaultMessage: 'Hosts count.',
|
||||
}),
|
||||
toolTip: i18n.translate('xpack.infra.hostsViewPage.metricTrend.hostCount.tooltip', {
|
||||
defaultMessage: 'The number of hosts returned by your current search criteria.',
|
||||
}),
|
||||
['data-test-subj']: 'hostsView-metricsTrend-hosts',
|
||||
};
|
||||
|
||||
export const KPIGrid = () => {
|
||||
return (
|
||||
<EuiFlexGroup
|
||||
direction="row"
|
||||
gutterSize="s"
|
||||
style={{ flexGrow: 0 }}
|
||||
data-test-subj="hostsView-metricsTrend"
|
||||
>
|
||||
<EuiFlexItem>
|
||||
<HostsTile {...HOSTS_CHART} />
|
||||
</EuiFlexItem>
|
||||
{KPI_CHARTS.map(({ ...chartProp }) => (
|
||||
<EuiFlexItem key={chartProp.type}>
|
||||
<Tile {...chartProp} />
|
||||
</EuiFlexItem>
|
||||
))}
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
|
||||
import { Action } from '@kbn/ui-actions-plugin/public';
|
||||
import { BrushTriggerEvent } from '@kbn/charts-plugin/public';
|
||||
import { EuiIcon, EuiPanel } from '@elastic/eui';
|
||||
import { EuiFlexGroup } from '@elastic/eui';
|
||||
import { EuiFlexItem } from '@elastic/eui';
|
||||
import { EuiText } from '@elastic/eui';
|
||||
import { EuiI18n } from '@elastic/eui';
|
||||
import styled from 'styled-components';
|
||||
import { EuiToolTip } from '@elastic/eui';
|
||||
import { useLensAttributes } from '../../../../../hooks/use_lens_attributes';
|
||||
import { useMetricsDataViewContext } from '../../hooks/use_data_view';
|
||||
import { useUnifiedSearchContext } from '../../hooks/use_unified_search';
|
||||
import { HostsLensMetricChartFormulas } from '../../../../../common/visualizations';
|
||||
import { useHostsViewContext } from '../../hooks/use_hosts_view';
|
||||
import { LensWrapper } from '../chart/lens_wrapper';
|
||||
|
||||
export interface KPIChartProps {
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
trendLine?: boolean;
|
||||
backgroundColor: string;
|
||||
type: HostsLensMetricChartFormulas;
|
||||
toolTip: string;
|
||||
}
|
||||
|
||||
const MIN_HEIGHT = 150;
|
||||
|
||||
export const Tile = ({
|
||||
title,
|
||||
subtitle,
|
||||
type,
|
||||
backgroundColor,
|
||||
toolTip,
|
||||
trendLine = false,
|
||||
}: KPIChartProps) => {
|
||||
const { searchCriteria, onSubmit } = useUnifiedSearchContext();
|
||||
const { dataView } = useMetricsDataViewContext();
|
||||
const { baseRequest } = useHostsViewContext();
|
||||
|
||||
const { attributes, getExtraActions, error } = useLensAttributes({
|
||||
type,
|
||||
dataView,
|
||||
options: {
|
||||
title,
|
||||
subtitle,
|
||||
backgroundColor,
|
||||
showTrendLine: trendLine,
|
||||
showTitle: false,
|
||||
},
|
||||
visualizationType: 'metricChart',
|
||||
});
|
||||
|
||||
const filters = [...searchCriteria.filters, ...searchCriteria.panelFilters];
|
||||
const extraActionOptions = getExtraActions({
|
||||
timeRange: searchCriteria.dateRange,
|
||||
filters,
|
||||
query: searchCriteria.query,
|
||||
});
|
||||
|
||||
const extraActions: Action[] = [extraActionOptions.openInLens];
|
||||
|
||||
const handleBrushEnd = ({ range }: BrushTriggerEvent['data']) => {
|
||||
const [min, max] = range;
|
||||
onSubmit({
|
||||
dateRange: {
|
||||
from: new Date(min).toISOString(),
|
||||
to: new Date(max).toISOString(),
|
||||
mode: 'absolute',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<EuiPanelStyled
|
||||
hasShadow={false}
|
||||
paddingSize={error ? 'm' : 'none'}
|
||||
style={{ minHeight: MIN_HEIGHT }}
|
||||
data-test-subj={`hostsView-metricsTrend-${type}`}
|
||||
>
|
||||
{error ? (
|
||||
<EuiFlexGroup
|
||||
style={{ height: MIN_HEIGHT, alignContent: 'center' }}
|
||||
gutterSize="xs"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
direction="column"
|
||||
>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiIcon type="warning" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s" textAlign="center">
|
||||
<EuiI18n
|
||||
token="'xpack.infra.hostsViewPage.errorOnLoadingLensDependencies'"
|
||||
default="There was an error trying to load Lens Plugin."
|
||||
/>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
) : (
|
||||
<EuiToolTip
|
||||
className="eui-fullWidth"
|
||||
delay="regular"
|
||||
content={toolTip}
|
||||
anchorClassName="eui-fullWidth"
|
||||
>
|
||||
<LensWrapper
|
||||
id={`hostViewKPIChart-${type}`}
|
||||
attributes={attributes}
|
||||
style={{ height: MIN_HEIGHT }}
|
||||
extraActions={extraActions}
|
||||
lastReloadRequestTime={baseRequest.requestTs}
|
||||
dateRange={searchCriteria.dateRange}
|
||||
filters={filters}
|
||||
query={searchCriteria.query}
|
||||
onBrushEnd={handleBrushEnd}
|
||||
/>
|
||||
</EuiToolTip>
|
||||
)}
|
||||
</EuiPanelStyled>
|
||||
);
|
||||
};
|
||||
|
||||
const EuiPanelStyled = styled(EuiPanel)`
|
||||
.echMetric {
|
||||
border-radius: ${(p) => p.theme.eui.euiBorderRadius};
|
||||
pointer-events: none;
|
||||
}
|
||||
`;
|
|
@ -5,26 +5,25 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import { Action } from '@kbn/ui-actions-plugin/public';
|
||||
import { ViewMode } from '@kbn/embeddable-plugin/public';
|
||||
import { BrushTriggerEvent } from '@kbn/charts-plugin/public';
|
||||
import { EuiIcon, EuiPanel } from '@elastic/eui';
|
||||
import { EuiFlexGroup } from '@elastic/eui';
|
||||
import { EuiFlexItem } from '@elastic/eui';
|
||||
import { EuiText } from '@elastic/eui';
|
||||
import { EuiI18n } from '@elastic/eui';
|
||||
import { InfraClientSetupDeps } from '../../../../../../types';
|
||||
import { useLensAttributes } from '../../../../../../hooks/use_lens_attributes';
|
||||
import { useMetricsDataViewContext } from '../../../hooks/use_data_view';
|
||||
import { useUnifiedSearchContext } from '../../../hooks/use_unified_search';
|
||||
import { HostLensAttributesTypes } from '../../../../../../common/visualizations';
|
||||
import { HostsLensLineChartFormulas } from '../../../../../../common/visualizations';
|
||||
import { useHostsViewContext } from '../../../hooks/use_hosts_view';
|
||||
import { LensWrapper } from '../../chart/lens_wrapper';
|
||||
|
||||
export interface MetricChartProps {
|
||||
title: string;
|
||||
type: HostLensAttributesTypes;
|
||||
type: HostsLensLineChartFormulas;
|
||||
breakdownSize: number;
|
||||
render?: boolean;
|
||||
}
|
||||
|
||||
const MIN_HEIGHT = 300;
|
||||
|
@ -33,28 +32,25 @@ export const MetricChart = ({ title, type, breakdownSize }: MetricChartProps) =>
|
|||
const { searchCriteria, onSubmit } = useUnifiedSearchContext();
|
||||
const { dataView } = useMetricsDataViewContext();
|
||||
const { baseRequest } = useHostsViewContext();
|
||||
const {
|
||||
services: { lens },
|
||||
} = useKibana<InfraClientSetupDeps>();
|
||||
|
||||
const EmbeddableComponent = lens.EmbeddableComponent;
|
||||
|
||||
const { injectData, getExtraActions, error } = useLensAttributes({
|
||||
const { attributes, getExtraActions, error } = useLensAttributes({
|
||||
type,
|
||||
dataView,
|
||||
options: {
|
||||
title,
|
||||
breakdownSize,
|
||||
},
|
||||
visualizationType: 'lineChart',
|
||||
});
|
||||
|
||||
const injectedLensAttributes = injectData({
|
||||
filters: [...searchCriteria.filters, ...searchCriteria.panelFilters],
|
||||
const filters = [...searchCriteria.filters, ...searchCriteria.panelFilters];
|
||||
const extraActionOptions = getExtraActions({
|
||||
timeRange: searchCriteria.dateRange,
|
||||
filters,
|
||||
query: searchCriteria.query,
|
||||
title,
|
||||
});
|
||||
|
||||
const extraActionOptions = getExtraActions(injectedLensAttributes, searchCriteria.dateRange);
|
||||
const extraAction: Action[] = [extraActionOptions.openInLens];
|
||||
const extraActions: Action[] = [extraActionOptions.openInLens];
|
||||
|
||||
const handleBrushEnd = ({ range }: BrushTriggerEvent['data']) => {
|
||||
const [min, max] = range;
|
||||
|
@ -97,24 +93,17 @@ export const MetricChart = ({ title, type, breakdownSize }: MetricChartProps) =>
|
|||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
) : (
|
||||
injectedLensAttributes && (
|
||||
<EmbeddableComponent
|
||||
id={`hostsViewsmetricsChart-${type}`}
|
||||
style={{ height: MIN_HEIGHT }}
|
||||
attributes={injectedLensAttributes}
|
||||
viewMode={ViewMode.VIEW}
|
||||
timeRange={searchCriteria.dateRange}
|
||||
query={searchCriteria.query}
|
||||
filters={searchCriteria.filters}
|
||||
extraActions={extraAction}
|
||||
lastReloadRequestTime={baseRequest.requestTs}
|
||||
executionContext={{
|
||||
type: 'infrastructure_observability_hosts_view',
|
||||
name: `Hosts View ${type} Chart`,
|
||||
}}
|
||||
onBrushEnd={handleBrushEnd}
|
||||
/>
|
||||
)
|
||||
<LensWrapper
|
||||
id={`hostsViewsmetricsChart-${type}`}
|
||||
attributes={attributes}
|
||||
style={{ height: MIN_HEIGHT }}
|
||||
extraActions={extraActions}
|
||||
lastReloadRequestTime={baseRequest.requestTs}
|
||||
dateRange={searchCriteria.dateRange}
|
||||
filters={filters}
|
||||
query={searchCriteria.query}
|
||||
onBrushEnd={handleBrushEnd}
|
||||
/>
|
||||
)}
|
||||
</EuiPanel>
|
||||
);
|
||||
|
|
|
@ -34,7 +34,7 @@ import type {
|
|||
// import type { OsqueryPluginStart } from '../../osquery/public';
|
||||
import type { SpacesPluginStart } from '@kbn/spaces-plugin/public';
|
||||
import type { IStorageWrapper } from '@kbn/kibana-utils-plugin/public';
|
||||
import { type TypedLensByValueInput, LensPublicStart } from '@kbn/lens-plugin/public';
|
||||
import { LensPublicStart } from '@kbn/lens-plugin/public';
|
||||
import type { ChartsPluginStart } from '@kbn/charts-plugin/public';
|
||||
import { CasesUiStart } from '@kbn/cases-plugin/public';
|
||||
import type { UnwrapPromise } from '../common/utility_types';
|
||||
|
@ -112,12 +112,6 @@ export interface InfraHttpError extends IHttpFetchError {
|
|||
};
|
||||
}
|
||||
|
||||
export type LensAttributes = TypedLensByValueInput['attributes'];
|
||||
|
||||
export interface LensOptions {
|
||||
breakdownSize: number;
|
||||
}
|
||||
|
||||
export interface ExecutionTimeRange {
|
||||
gte: number;
|
||||
lte: number;
|
||||
|
|
|
@ -17427,26 +17427,18 @@
|
|||
"xpack.infra.hostsViewPage.landing.introTitle": "Présentation : Analyse de l'hôte",
|
||||
"xpack.infra.hostsViewPage.landing.learnMore": "En savoir plus",
|
||||
"xpack.infra.hostsViewPage.landing.tryTheFeatureMessage": "Il s'agit d'une version préliminaire de la fonctionnalité, et nous souhaiterions vivement connaître votre avis tandis que nous continuons\n à la développer et à l'améliorer. Pour accéder à cette fonctionnalité, il suffit de l'activer ci-dessous. Ne passez pas à côté\n de cette nouvelle et puissante fonctionnalité ajoutée à notre plateforme... Essayez-la aujourd'hui même !",
|
||||
"xpack.infra.hostsViewPage.metricTrend.cpu.a11y.description": "Graphique linéaire affichant la tendance de l'indicateur principal sur la durée.",
|
||||
"xpack.infra.hostsViewPage.metricTrend.cpu.a11y.title": "Utilisation CPU sur la durée.",
|
||||
"xpack.infra.hostsViewPage.metricTrend.cpu.subtitle": "Moyenne",
|
||||
"xpack.infra.hostsViewPage.metricTrend.cpu.title": "Utilisation CPU",
|
||||
"xpack.infra.hostsViewPage.metricTrend.cpu.tooltip": "Moyenne de pourcentage de temps CPU utilisé dans les états autres que Inactif et IOWait, normalisée par le nombre de cœurs de processeur. Inclut le temps passé à la fois sur l'espace utilisateur et sur l'espace du noyau. 100 % signifie que tous les processeurs de l'hôte sont occupés.",
|
||||
"xpack.infra.hostsViewPage.metricTrend.hostCount.a11y.title": "Utilisation CPU sur la durée.",
|
||||
"xpack.infra.hostsViewPage.metricTrend.hostCount.title": "Hôtes",
|
||||
"xpack.infra.hostsViewPage.metricTrend.hostCount.tooltip": "Nombre d'hôtes renvoyé par vos critères de recherche actuels.",
|
||||
"xpack.infra.hostsViewPage.metricTrend.memory.a11yDescription": "Graphique linéaire affichant la tendance de l'indicateur principal sur la durée.",
|
||||
"xpack.infra.hostsViewPage.metricTrend.memory.a11yTitle": "Utilisation de la mémoire sur la durée.",
|
||||
"xpack.infra.hostsViewPage.metricTrend.memory.subtitle": "Moyenne",
|
||||
"xpack.infra.hostsViewPage.metricTrend.memory.title": "Utilisation mémoire",
|
||||
"xpack.infra.hostsViewPage.metricTrend.memory.tooltip": "Moyenne de pourcentage d'utilisation de la mémoire principale, en excluant le cache de pages. Cela inclut la mémoire résidente pour tous les processus, plus la mémoire utilisée par les structures et le code du noyau, à l'exception du cache de pages. Un niveau élevé indique une situation de saturation de la mémoire pour un hôte. 100 % signifie que la mémoire principale est entièrement remplie par de la mémoire ne pouvant pas être récupérée, sauf en l'échangeant.",
|
||||
"xpack.infra.hostsViewPage.metricTrend.rx.a11y.description": "Graphique linéaire affichant la tendance de l'indicateur principal sur la durée.",
|
||||
"xpack.infra.hostsViewPage.metricTrend.rx.a11y.title": "Réseau entrant (RX) sur la durée.",
|
||||
"xpack.infra.hostsViewPage.metricTrend.rx.subtitle": "Moyenne",
|
||||
"xpack.infra.hostsViewPage.metricTrend.rx.title": "Réseau entrant (RX)",
|
||||
"xpack.infra.hostsViewPage.metricTrend.rx.tooltip": "Nombre d'octets qui ont été reçus par seconde sur les interfaces publiques des hôtes.",
|
||||
"xpack.infra.hostsViewPage.metricTrend.tx.a11.title": "Utilisation de réseau sortant (TX) sur la durée.",
|
||||
"xpack.infra.hostsViewPage.metricTrend.tx.a11y.description": "Graphique linéaire affichant la tendance de l'indicateur principal sur la durée.",
|
||||
"xpack.infra.hostsViewPage.metricTrend.tx.subtitle": "Moyenne",
|
||||
"xpack.infra.hostsViewPage.metricTrend.tx.title": "Réseau sortant (TX)",
|
||||
"xpack.infra.hostsViewPage.metricTrend.tx.tooltip": "Nombre d'octets qui ont été envoyés par seconde sur les interfaces publiques des hôtes",
|
||||
|
@ -38121,4 +38113,4 @@
|
|||
"xpack.painlessLab.title": "Painless Lab",
|
||||
"xpack.painlessLab.walkthroughButtonLabel": "Présentation"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17426,26 +17426,18 @@
|
|||
"xpack.infra.hostsViewPage.landing.introTitle": "導入:ホスト分析",
|
||||
"xpack.infra.hostsViewPage.landing.learnMore": "詳細",
|
||||
"xpack.infra.hostsViewPage.landing.tryTheFeatureMessage": "この機能は初期バージョンであり、今後継続する中で、開発、改善するうえで皆様からのフィードバックをお願いします\n 。機能にアクセスするには、以下を有効にします。プラットフォームに\n 追加されたこの強力な新機能をお見逃しなく。今すぐお試しください!",
|
||||
"xpack.infra.hostsViewPage.metricTrend.cpu.a11y.description": "主要なメトリックの経時的な傾向を示す折れ線グラフ。",
|
||||
"xpack.infra.hostsViewPage.metricTrend.cpu.a11y.title": "経時的なCPU使用状況。",
|
||||
"xpack.infra.hostsViewPage.metricTrend.cpu.subtitle": "平均",
|
||||
"xpack.infra.hostsViewPage.metricTrend.cpu.title": "CPU 使用状況",
|
||||
"xpack.infra.hostsViewPage.metricTrend.cpu.tooltip": "アイドルおよびIOWait以外の状態で費やされたCPU時間の割合の平均値を、CPUコア数で正規化したもの。ユーザースペースとカーネルスペースの両方で費やされた時間が含まれます。100%はホストのすべてのCPUがビジー状態であることを示します。",
|
||||
"xpack.infra.hostsViewPage.metricTrend.hostCount.a11y.title": "経時的なCPU使用状況。",
|
||||
"xpack.infra.hostsViewPage.metricTrend.hostCount.title": "ホスト",
|
||||
"xpack.infra.hostsViewPage.metricTrend.hostCount.tooltip": "現在の検索条件から返されたホストの数です。",
|
||||
"xpack.infra.hostsViewPage.metricTrend.memory.a11yDescription": "主要なメトリックの経時的な傾向を示す折れ線グラフ。",
|
||||
"xpack.infra.hostsViewPage.metricTrend.memory.a11yTitle": "経時的なメモリ使用状況。",
|
||||
"xpack.infra.hostsViewPage.metricTrend.memory.subtitle": "平均",
|
||||
"xpack.infra.hostsViewPage.metricTrend.memory.title": "メモリー使用状況",
|
||||
"xpack.infra.hostsViewPage.metricTrend.memory.tooltip": "ページキャッシュを除いたメインメモリの割合の平均値。これには、すべてのプロセスの常駐メモリと、ページキャッシュを離れてカーネル構造とコードによって使用されるメモリが含まれます。高レベルは、ホストのメモリが飽和状態にあることを示します。100%とは、メインメモリがすべてスワップアウト以外の、再利用不可能なメモリで満たされていることを意味します。",
|
||||
"xpack.infra.hostsViewPage.metricTrend.rx.a11y.description": "主要なメトリックの経時的な傾向を示す折れ線グラフ。",
|
||||
"xpack.infra.hostsViewPage.metricTrend.rx.a11y.title": "経時的なネットワーク受信(RX)。",
|
||||
"xpack.infra.hostsViewPage.metricTrend.rx.subtitle": "平均",
|
||||
"xpack.infra.hostsViewPage.metricTrend.rx.title": "ネットワーク受信(RX)",
|
||||
"xpack.infra.hostsViewPage.metricTrend.rx.tooltip": "ホストのパブリックインターフェースで1秒間に受信したバイト数。",
|
||||
"xpack.infra.hostsViewPage.metricTrend.tx.a11.title": "経時的なネットワーク送信(TX)。",
|
||||
"xpack.infra.hostsViewPage.metricTrend.tx.a11y.description": "主要なメトリックの経時的な傾向を示す折れ線グラフ。",
|
||||
"xpack.infra.hostsViewPage.metricTrend.tx.subtitle": "平均",
|
||||
"xpack.infra.hostsViewPage.metricTrend.tx.title": "ネットワーク送信(TX)",
|
||||
"xpack.infra.hostsViewPage.metricTrend.tx.tooltip": "ホストのパブリックインターフェースで1秒間に送信したバイト数",
|
||||
|
@ -38089,4 +38081,4 @@
|
|||
"xpack.painlessLab.title": "Painless Lab",
|
||||
"xpack.painlessLab.walkthroughButtonLabel": "実地検証"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17427,26 +17427,18 @@
|
|||
"xpack.infra.hostsViewPage.landing.introTitle": "即将引入:主机分析",
|
||||
"xpack.infra.hostsViewPage.landing.learnMore": "了解详情",
|
||||
"xpack.infra.hostsViewPage.landing.tryTheFeatureMessage": "这是早期版本的功能,我们需要您的反馈,\n 以便继续开发和改进该功能。要访问该功能,直接在下面启用即可。请抓紧时间,\n 了解新添加到我们平台中的这项强大功能 - 立即试用!",
|
||||
"xpack.infra.hostsViewPage.metricTrend.cpu.a11y.description": "显示一段时间的主要指标趋势的折线图。",
|
||||
"xpack.infra.hostsViewPage.metricTrend.cpu.a11y.title": "一段时间的 CPU 使用率。",
|
||||
"xpack.infra.hostsViewPage.metricTrend.cpu.subtitle": "平均值",
|
||||
"xpack.infra.hostsViewPage.metricTrend.cpu.title": "CPU 使用",
|
||||
"xpack.infra.hostsViewPage.metricTrend.cpu.tooltip": "CPU 在空闲和 IOWait 状态以外所花费时间的平均百分比,按 CPU 核心数进行标准化。包括在用户空间和内核空间上花费的时间。100% 表示主机的所有 CPU 都处于忙碌状态。",
|
||||
"xpack.infra.hostsViewPage.metricTrend.hostCount.a11y.title": "一段时间的 CPU 使用率。",
|
||||
"xpack.infra.hostsViewPage.metricTrend.hostCount.title": "主机",
|
||||
"xpack.infra.hostsViewPage.metricTrend.hostCount.tooltip": "当前搜索条件返回的主机数。",
|
||||
"xpack.infra.hostsViewPage.metricTrend.memory.a11yDescription": "显示一段时间的主要指标趋势的折线图。",
|
||||
"xpack.infra.hostsViewPage.metricTrend.memory.a11yTitle": "一段时间的内存使用率。",
|
||||
"xpack.infra.hostsViewPage.metricTrend.memory.subtitle": "平均值",
|
||||
"xpack.infra.hostsViewPage.metricTrend.memory.title": "内存使用",
|
||||
"xpack.infra.hostsViewPage.metricTrend.memory.tooltip": "主内存使用率(不包括页面缓存)的平均百分比。这包括所有进程的常驻内存,加上由内核结构和代码使用的内存,但不包括页面缓存。高比率表明主机出现内存饱和情况。100% 表示主内存被完全占用,除了进行换出外无法回收内存。",
|
||||
"xpack.infra.hostsViewPage.metricTrend.rx.a11y.description": "显示一段时间的主要指标趋势的折线图。",
|
||||
"xpack.infra.hostsViewPage.metricTrend.rx.a11y.title": "一段时间的网络入站数据 (RX)。",
|
||||
"xpack.infra.hostsViewPage.metricTrend.rx.subtitle": "平均值",
|
||||
"xpack.infra.hostsViewPage.metricTrend.rx.title": "网络入站数据 (RX)",
|
||||
"xpack.infra.hostsViewPage.metricTrend.rx.tooltip": "主机的公共接口上每秒接收的字节数。",
|
||||
"xpack.infra.hostsViewPage.metricTrend.tx.a11.title": "一段时间的网络出站数据 (TX) 使用量。",
|
||||
"xpack.infra.hostsViewPage.metricTrend.tx.a11y.description": "显示一段时间的主要指标趋势的折线图。",
|
||||
"xpack.infra.hostsViewPage.metricTrend.tx.subtitle": "平均值",
|
||||
"xpack.infra.hostsViewPage.metricTrend.tx.title": "网络出站数据 (TX)",
|
||||
"xpack.infra.hostsViewPage.metricTrend.tx.tooltip": "主机的公共接口上每秒发送的字节数",
|
||||
|
@ -38116,4 +38108,4 @@
|
|||
"xpack.painlessLab.title": "Painless 实验室",
|
||||
"xpack.painlessLab.walkthroughButtonLabel": "指导"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -367,9 +367,9 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
|||
[
|
||||
{ metric: 'hosts', value: '6' },
|
||||
{ metric: 'cpu', value: '0.8%' },
|
||||
{ metric: 'memory', value: '16.8%' },
|
||||
{ metric: 'tx', value: '0 bit/s' },
|
||||
{ metric: 'rx', value: '0 bit/s' },
|
||||
{ metric: 'memory', value: '16.81%' },
|
||||
{ metric: 'tx', value: 'N/A' },
|
||||
{ metric: 'rx', value: 'N/A' },
|
||||
].forEach(({ metric, value }) => {
|
||||
it(`${metric} tile should show ${value}`, async () => {
|
||||
const tileValue = await pageObjects.infraHostsView.getMetricsTrendTileValue(metric);
|
||||
|
@ -497,9 +497,9 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
|||
[
|
||||
{ metric: 'hosts', value: '3' },
|
||||
{ metric: 'cpu', value: '0.8%' },
|
||||
{ metric: 'memory', value: '16.2%' },
|
||||
{ metric: 'tx', value: '0 bit/s' },
|
||||
{ metric: 'rx', value: '0 bit/s' },
|
||||
{ metric: 'memory', value: '16.25%' },
|
||||
{ metric: 'tx', value: 'N/A' },
|
||||
{ metric: 'rx', value: 'N/A' },
|
||||
].map(async ({ metric, value }) => {
|
||||
const tileValue = await pageObjects.infraHostsView.getMetricsTrendTileValue(metric);
|
||||
expect(tileValue).to.eql(value);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue