[Infra UI] Add Docker section to node details page (#43627) (#46825)

* [Infra UI] Add Docker section to node detail page

* Add chart with terms aggregation for top 5 CPU and Memory

- Add label to series returned from metrics endpoint
- Honor seriesOverride name or use label
- Fix chart tooltip for long names
- Add name to seriesOverrides for current layouts

* Fixing types

* removing point changes, handled in seperate pr

* Changing infMetricsExplorerChart className to infrastructureChart

* updating comment with new functionality

* updating id to human readable string
This commit is contained in:
Chris Cowan 2019-09-27 14:42:18 -07:00 committed by GitHub
parent a86b6b3d51
commit 6d5824ed31
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 837 additions and 246 deletions

View file

@ -309,6 +309,8 @@ export interface InfraMetricData {
export interface InfraDataSeries {
id: string;
label: string;
data: InfraDataPoint[];
}
@ -574,6 +576,10 @@ export enum InfraMetric {
hostLoad = 'hostLoad',
hostMemoryUsage = 'hostMemoryUsage',
hostNetworkTraffic = 'hostNetworkTraffic',
hostDockerOverview = 'hostDockerOverview',
hostDockerInfo = 'hostDockerInfo',
hostDockerTop5ByCpu = 'hostDockerTop5ByCpu',
hostDockerTop5ByMemory = 'hostDockerTop5ByMemory',
podOverview = 'podOverview',
podCpuUsage = 'podCpuUsage',
podMemoryUsage = 'podMemoryUsage',
@ -839,6 +845,8 @@ export namespace MetricsQuery {
id: string;
label: string;
data: Data[];
};

View file

@ -5,7 +5,7 @@
*/
import React, { useCallback } from 'react';
import moment from 'moment';
import { InjectedIntl, injectI18n } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { get } from 'lodash';
import {
Axis,
@ -40,109 +40,107 @@ interface Props {
onChangeRangeTime?: (time: MetricsTimeInput) => void;
isLiveStreaming?: boolean;
stopLiveStreaming?: () => void;
intl: InjectedIntl;
}
export const ChartSection = injectI18n(
({ onChangeRangeTime, section, metric, intl, stopLiveStreaming, isLiveStreaming }: Props) => {
const { visConfig } = section;
const [dateFormat] = useKibanaUiSetting('dateFormat');
const formatter = get(visConfig, 'formatter', InfraFormatterType.number);
const formatterTemplate = get(visConfig, 'formatterTemplate', '{{value}}');
const valueFormatter = useCallback(getFormatter(formatter, formatterTemplate), [
formatter,
formatterTemplate,
]);
const dateFormatter = useCallback(niceTimeFormatter(getMaxMinTimestamp(metric)), [metric]);
const handleTimeChange = useCallback(
(from: number, to: number) => {
if (onChangeRangeTime) {
if (isLiveStreaming && stopLiveStreaming) {
stopLiveStreaming();
}
onChangeRangeTime({
from: moment(from).toISOString(),
to: moment(to).toISOString(),
interval: '>=1m',
});
export const ChartSection = ({
onChangeRangeTime,
section,
metric,
stopLiveStreaming,
isLiveStreaming,
}: Props) => {
const { visConfig } = section;
const [dateFormat] = useKibanaUiSetting('dateFormat');
const formatter = get(visConfig, 'formatter', InfraFormatterType.number);
const formatterTemplate = get(visConfig, 'formatterTemplate', '{{value}}');
const valueFormatter = useCallback(getFormatter(formatter, formatterTemplate), [
formatter,
formatterTemplate,
]);
const dateFormatter = useCallback(niceTimeFormatter(getMaxMinTimestamp(metric)), [metric]);
const handleTimeChange = useCallback(
(from: number, to: number) => {
if (onChangeRangeTime) {
if (isLiveStreaming && stopLiveStreaming) {
stopLiveStreaming();
}
},
[onChangeRangeTime, isLiveStreaming, stopLiveStreaming]
);
const tooltipProps = {
headerFormatter: useCallback(
(data: TooltipValue) => moment(data.value).format(dateFormat || 'Y-MM-DD HH:mm:ss.SSS'),
[dateFormat]
),
};
if (!metric) {
return (
<ErrorMessage
title={intl.formatMessage({
id: 'xpack.infra.chartSection.missingMetricDataText',
defaultMessage: 'Missing Data',
})}
body={intl.formatMessage({
id: 'xpack.infra.chartSection.missingMetricDataBody',
defaultMessage: 'The data for this chart is missing.',
})}
/>
);
}
if (metric.series.some(seriesHasLessThen2DataPoints)) {
return (
<ErrorMessage
title={intl.formatMessage({
id: 'xpack.infra.chartSection.notEnoughDataPointsToRenderTitle',
defaultMessage: 'Not Enough Data',
})}
body={intl.formatMessage({
id: 'xpack.infra.chartSection.notEnoughDataPointsToRenderText',
defaultMessage:
'Not enough data points to render chart, try increasing the time range.',
})}
/>
);
}
onChangeRangeTime({
from: moment(from).toISOString(),
to: moment(to).toISOString(),
interval: '>=1m',
});
}
},
[onChangeRangeTime, isLiveStreaming, stopLiveStreaming]
);
const tooltipProps = {
headerFormatter: useCallback(
(data: TooltipValue) => moment(data.value).format(dateFormat || 'Y-MM-DD HH:mm:ss.SSS'),
[dateFormat]
),
};
if (!metric) {
return (
<EuiPageContentBody>
<EuiTitle size="xs">
<h3 id={section.id}>{section.label}</h3>
</EuiTitle>
<div style={{ height: 250, marginBottom: 16 }}>
<Chart>
<Axis
id={getAxisId('timestamp')}
position={Position.Bottom}
showOverlappingTicks={true}
tickFormat={dateFormatter}
/>
<Axis id={getAxisId('values')} position={Position.Left} tickFormat={valueFormatter} />
{metric &&
metric.series.map(series => (
<SeriesChart
key={`series-${section.id}-${series.id}`}
id={`series-${section.id}-${series.id}`}
series={series}
name={getChartName(section, series.id)}
type={getChartType(section, series.id)}
color={getChartColor(section, series.id)}
stack={visConfig.stacked}
/>
))}
<Settings
tooltip={tooltipProps}
onBrushEnd={handleTimeChange}
theme={getChartTheme()}
showLegend={true}
legendPosition="right"
/>
</Chart>
</div>
</EuiPageContentBody>
<ErrorMessage
title={i18n.translate('xpack.infra.chartSection.missingMetricDataText', {
defaultMessage: 'Missing Data',
})}
body={i18n.translate('xpack.infra.chartSection.missingMetricDataBody', {
defaultMessage: 'The data for this chart is missing.',
})}
/>
);
}
);
if (metric.series.some(seriesHasLessThen2DataPoints)) {
return (
<ErrorMessage
title={i18n.translate('xpack.infra.chartSection.notEnoughDataPointsToRenderTitle', {
defaultMessage: 'Not Enough Data',
})}
body={i18n.translate('xpack.infra.chartSection.notEnoughDataPointsToRenderText', {
defaultMessage: 'Not enough data points to render chart, try increasing the time range.',
})}
/>
);
}
return (
<EuiPageContentBody>
<EuiTitle size="xs">
<h3 id={section.id}>{section.label}</h3>
</EuiTitle>
<div className="infrastructureChart" style={{ height: 250, marginBottom: 16 }}>
<Chart>
<Axis
id={getAxisId('timestamp')}
position={Position.Bottom}
showOverlappingTicks={true}
tickFormat={dateFormatter}
/>
<Axis id={getAxisId('values')} position={Position.Left} tickFormat={valueFormatter} />
{metric &&
metric.series.map(series => (
<SeriesChart
key={`series-${section.id}-${series.id}`}
id={`series-${section.id}-${series.id}`}
series={series}
name={getChartName(section, series.id, series.label)}
type={getChartType(section, series.id)}
color={getChartColor(section, series.id)}
stack={visConfig.stacked}
/>
))}
<Settings
tooltip={tooltipProps}
onBrushEnd={handleTimeChange}
theme={getChartTheme()}
showLegend={true}
legendPosition="right"
/>
</Chart>
</div>
</EuiPageContentBody>
);
};

View file

@ -53,18 +53,24 @@ export const getMaxMinTimestamp = (metric: InfraMetricData): [number, number] =>
* Returns the chart name from the visConfig based on the series id, otherwise it
* just returns the seriesId
*/
export const getChartName = (section: InfraMetricLayoutSection, seriesId: string) => {
return get(section, ['visConfig', 'seriesOverrides', seriesId, 'name'], seriesId);
export const getChartName = (
section: InfraMetricLayoutSection,
seriesId: string,
label: string
) => {
return get(section, ['visConfig', 'seriesOverrides', seriesId, 'name'], label);
};
/**
* Returns the chart color from the visConfig based on the series id, otherwise it
* just returns a default color of #999
* just returns null if the color doesn't exists in the overrides.
*/
export const getChartColor = (section: InfraMetricLayoutSection, seriesId: string) => {
const color = new Color(
get(section, ['visConfig', 'seriesOverrides', seriesId, 'color'], '#999')
);
const rawColor: string | null = get(section, ['visConfig', 'seriesOverrides', seriesId, 'color']);
if (!rawColor) {
return null;
}
const color = new Color(rawColor);
return color.hex().toString();
};

View file

@ -22,7 +22,7 @@ import { InfraDataSeries } from '../../../graphql/types';
interface Props {
id: string;
name: string;
color: string;
color: string | null;
series: InfraDataSeries;
type: InfraMetricLayoutVisualizationType;
stack: boolean | undefined;
@ -57,7 +57,7 @@ export const AreaChart = ({ id, color, series, name, type, stack }: Props) => {
specId: getSpecId(id),
};
const customColors: CustomSeriesColorsMap = new Map();
customColors.set(colors, color);
customColors.set(colors, color || '#999');
return (
<AreaSeries
id={getSpecId(id)}
@ -68,7 +68,7 @@ export const AreaChart = ({ id, color, series, name, type, stack }: Props) => {
yAccessors={['value']}
data={series.data}
areaSeriesStyle={style}
customSeriesColors={customColors}
customSeriesColors={color ? customColors : void 0}
stackAccessors={stack ? ['timestamp'] : void 0}
/>
);
@ -77,7 +77,7 @@ export const AreaChart = ({ id, color, series, name, type, stack }: Props) => {
export const BarChart = ({ id, color, series, name, type, stack }: Props) => {
const style: RecursivePartial<BarSeriesStyle> = {
rectBorder: {
stroke: color,
stroke: color || void 0,
strokeWidth: 1,
visible: true,
},
@ -90,7 +90,7 @@ export const BarChart = ({ id, color, series, name, type, stack }: Props) => {
specId: getSpecId(id),
};
const customColors: CustomSeriesColorsMap = new Map();
customColors.set(colors, color);
customColors.set(colors, color || '#999');
return (
<BarSeries
id={getSpecId(id)}

View file

@ -5,7 +5,6 @@
*/
import React, { useCallback, useMemo } from 'react';
import { InjectedIntl, injectI18n } from '@kbn/i18n/react';
import { EuiTitle, EuiToolTip, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import {
Axis,
@ -39,7 +38,6 @@ import { useKibanaUiSetting } from '../../utils/use_kibana_ui_setting';
import { calculateDomain } from './helpers/calculate_domain';
interface Props {
intl: InjectedIntl;
title?: string | null;
onFilter: (query: string) => void;
width?: number | string;
@ -54,122 +52,120 @@ interface Props {
}
export const MetricsExplorerChart = injectUICapabilities(
injectI18n(
({
source,
options,
chartOptions,
series,
title,
onFilter,
height = 200,
width = '100%',
timeRange,
onTimeChange,
uiCapabilities,
}: Props) => {
const { metrics } = options;
const [dateFormat] = useKibanaUiSetting('dateFormat');
const handleTimeChange = (from: number, to: number) => {
onTimeChange(moment(from).toISOString(), moment(to).toISOString());
};
const dateFormatter = useMemo(
() =>
series.rows.length > 0
? niceTimeFormatter([first(series.rows).timestamp, last(series.rows).timestamp])
: (value: number) => `${value}`,
[series.rows]
);
const tooltipProps = {
headerFormatter: useCallback(
(data: TooltipValue) => moment(data.value).format(dateFormat || 'Y-MM-DD HH:mm:ss.SSS'),
[dateFormat]
),
};
const yAxisFormater = useCallback(createFormatterForMetric(first(metrics)), [options]);
const dataDomain = calculateDomain(series, metrics, chartOptions.stack);
const domain =
chartOptions.yAxisMode === MetricsExplorerYAxisMode.fromZero
? { ...dataDomain, min: 0 }
: dataDomain;
return (
<div style={{ padding: 24 }}>
{options.groupBy ? (
<EuiTitle size="xs">
<EuiFlexGroup alignItems="center">
<ChartTitle>
<EuiToolTip content={title}>
<span>{title}</span>
</EuiToolTip>
</ChartTitle>
<EuiFlexItem grow={false}>
<MetricsExplorerChartContextMenu
timeRange={timeRange}
options={options}
chartOptions={chartOptions}
series={series}
onFilter={onFilter}
source={source}
uiCapabilities={uiCapabilities}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiTitle>
) : (
<EuiFlexGroup justifyContent="flexEnd">
({
source,
options,
chartOptions,
series,
title,
onFilter,
height = 200,
width = '100%',
timeRange,
onTimeChange,
uiCapabilities,
}: Props) => {
const { metrics } = options;
const [dateFormat] = useKibanaUiSetting('dateFormat');
const handleTimeChange = (from: number, to: number) => {
onTimeChange(moment(from).toISOString(), moment(to).toISOString());
};
const dateFormatter = useMemo(
() =>
series.rows.length > 0
? niceTimeFormatter([first(series.rows).timestamp, last(series.rows).timestamp])
: (value: number) => `${value}`,
[series.rows]
);
const tooltipProps = {
headerFormatter: useCallback(
(data: TooltipValue) => moment(data.value).format(dateFormat || 'Y-MM-DD HH:mm:ss.SSS'),
[dateFormat]
),
};
const yAxisFormater = useCallback(createFormatterForMetric(first(metrics)), [options]);
const dataDomain = calculateDomain(series, metrics, chartOptions.stack);
const domain =
chartOptions.yAxisMode === MetricsExplorerYAxisMode.fromZero
? { ...dataDomain, min: 0 }
: dataDomain;
return (
<div style={{ padding: 24 }}>
{options.groupBy ? (
<EuiTitle size="xs">
<EuiFlexGroup alignItems="center">
<ChartTitle>
<EuiToolTip content={title}>
<span>{title}</span>
</EuiToolTip>
</ChartTitle>
<EuiFlexItem grow={false}>
<MetricsExplorerChartContextMenu
timeRange={timeRange}
options={options}
chartOptions={chartOptions}
series={series}
onFilter={onFilter}
source={source}
timeRange={timeRange}
uiCapabilities={uiCapabilities}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiTitle>
) : (
<EuiFlexGroup justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<MetricsExplorerChartContextMenu
options={options}
chartOptions={chartOptions}
series={series}
source={source}
timeRange={timeRange}
uiCapabilities={uiCapabilities}
/>
</EuiFlexItem>
</EuiFlexGroup>
)}
<div className="infrastructureChart" style={{ height, width }}>
{series.rows.length > 0 ? (
<Chart>
{metrics.map((metric, id) => (
<MetricExplorerSeriesChart
type={chartOptions.type}
key={id}
metric={metric}
id={id}
series={series}
stack={chartOptions.stack}
/>
))}
<Axis
id={getAxisId('timestamp')}
position={Position.Bottom}
showOverlappingTicks={true}
tickFormat={dateFormatter}
/>
<Axis
id={getAxisId('values')}
position={Position.Left}
tickFormat={yAxisFormater}
domain={domain}
/>
<Settings
tooltip={tooltipProps}
onBrushEnd={handleTimeChange}
theme={getChartTheme()}
/>
</Chart>
) : options.metrics.length > 0 ? (
<MetricsExplorerEmptyChart />
) : (
<MetricsExplorerNoMetrics />
)}
<div className="infMetricsExplorerChart" style={{ height, width }}>
{series.rows.length > 0 ? (
<Chart>
{metrics.map((metric, id) => (
<MetricExplorerSeriesChart
type={chartOptions.type}
key={id}
metric={metric}
id={id}
series={series}
stack={chartOptions.stack}
/>
))}
<Axis
id={getAxisId('timestamp')}
position={Position.Bottom}
showOverlappingTicks={true}
tickFormat={dateFormatter}
/>
<Axis
id={getAxisId('values')}
position={Position.Left}
tickFormat={yAxisFormater}
domain={domain}
/>
<Settings
tooltip={tooltipProps}
onBrushEnd={handleTimeChange}
theme={getChartTheme()}
/>
</Chart>
) : options.metrics.length > 0 ? (
<MetricsExplorerEmptyChart />
) : (
<MetricsExplorerNoMetrics />
)}
</div>
</div>
);
}
)
</div>
);
}
);
const ChartTitle = euiStyled.div`

View file

@ -26,6 +26,7 @@ export const metricsQuery = gql`
id
series {
id
label
data {
timestamp
value

View file

@ -2429,6 +2429,30 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "hostDockerOverview",
"description": "",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "hostDockerInfo",
"description": "",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "hostDockerTop5ByCpu",
"description": "",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "hostDockerTop5ByMemory",
"description": "",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "podOverview",
"description": "",
@ -2621,6 +2645,18 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "label",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": { "kind": "SCALAR", "name": "String", "ofType": null }
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "data",
"description": "",

View file

@ -309,6 +309,8 @@ export interface InfraMetricData {
export interface InfraDataSeries {
id: string;
label: string;
data: InfraDataPoint[];
}
@ -574,6 +576,10 @@ export enum InfraMetric {
hostLoad = 'hostLoad',
hostMemoryUsage = 'hostMemoryUsage',
hostNetworkTraffic = 'hostNetworkTraffic',
hostDockerOverview = 'hostDockerOverview',
hostDockerInfo = 'hostDockerInfo',
hostDockerTop5ByCpu = 'hostDockerTop5ByCpu',
hostDockerTop5ByMemory = 'hostDockerTop5ByMemory',
podOverview = 'podOverview',
podCpuUsage = 'podCpuUsage',
podMemoryUsage = 'podMemoryUsage',
@ -839,6 +845,8 @@ export namespace MetricsQuery {
id: string;
label: string;
data: Data[];
};

View file

@ -30,6 +30,12 @@
// This is a temporary workaround for https://github.com/elastic/kibana/issues/39808
// A real fix will most likely depend on changes in elastic-charts.
.infMetricsExplorerChart .echTooltip {
.infrastructureChart .echTooltip {
max-width: 90vw;
}
.infrastructureChart .echTooltip__label {
overflow-x: hidden;
white-space: no-wrap;
text-overflow: ellipsis;
}

View file

@ -95,7 +95,13 @@ export const containerLayoutCreator: InfraMetricLayoutCreator = theme => [
type: InfraMetricLayoutVisualizationType.area,
formatter: InfraFormatterType.percent,
seriesOverrides: {
cpu: { color: theme.eui.euiColorVis1 },
cpu: {
color: theme.eui.euiColorVis1,
name: i18n.translate(
'xpack.infra.metricDetailPage.containerMetricsLayout.cpuUsageSection.seriesLabel.cpu',
{ defaultMessage: 'cpu' }
),
},
},
},
},
@ -114,7 +120,13 @@ export const containerLayoutCreator: InfraMetricLayoutCreator = theme => [
type: InfraMetricLayoutVisualizationType.area,
formatter: InfraFormatterType.percent,
seriesOverrides: {
memory: { color: theme.eui.euiColorVis1 },
memory: {
color: theme.eui.euiColorVis1,
name: i18n.translate(
'xpack.infra.metricDetailPage.containerMetricsLayout.memoryUsageSection.seriesLabel.memory',
{ defaultMessage: 'memory' }
),
},
},
},
},

View file

@ -106,13 +106,55 @@ export const hostLayoutCreator: InfraMetricLayoutCreator = theme => [
type: InfraMetricLayoutVisualizationType.area,
formatter: InfraFormatterType.percent,
seriesOverrides: {
user: { color: theme.eui.euiColorVis0 },
system: { color: theme.eui.euiColorVis2 },
steal: { color: theme.eui.euiColorVis9 },
irq: { color: theme.eui.euiColorVis4 },
softirq: { color: theme.eui.euiColorVis6 },
iowait: { color: theme.eui.euiColorVis7 },
nice: { color: theme.eui.euiColorVis5 },
user: {
color: theme.eui.euiColorVis0,
name: i18n.translate(
'xpack.infra.metricDetailPage.hostMetricsLayout.cpuUsageSection.seriesLabel.user',
{ defaultMessage: 'user' }
),
},
system: {
color: theme.eui.euiColorVis2,
name: i18n.translate(
'xpack.infra.metricDetailPage.hostMetricsLayout.cpuUsageSection.seriesLabel.system',
{ defaultMessage: 'system' }
),
},
steal: {
color: theme.eui.euiColorVis9,
name: i18n.translate(
'xpack.infra.metricDetailPage.hostMetricsLayout.cpuUsageSection.seriesLabel.steal',
{ defaultMessage: 'steal' }
),
},
irq: {
color: theme.eui.euiColorVis4,
name: i18n.translate(
'xpack.infra.metricDetailPage.hostMetricsLayout.cpuUsageSection.seriesLabel.irq',
{ defaultMessage: 'irq' }
),
},
softirq: {
color: theme.eui.euiColorVis6,
name: i18n.translate(
'xpack.infra.metricDetailPage.hostMetricsLayout.cpuUsageSection.seriesLabel.softirq',
{ defaultMessage: 'softirq' }
),
},
iowait: {
color: theme.eui.euiColorVis7,
name: i18n.translate(
'xpack.infra.metricDetailPage.hostMetricsLayout.cpuUsageSection.seriesLabel.iowait',
{ defaultMessage: 'iowait' }
),
},
nice: {
color: theme.eui.euiColorVis5,
name: i18n.translate(
'xpack.infra.metricDetailPage.hostMetricsLayout.cpuUsageSection.seriesLabel.nice',
{ defaultMessage: 'nice' }
),
},
},
},
},
@ -173,9 +215,27 @@ export const hostLayoutCreator: InfraMetricLayoutCreator = theme => [
formatter: InfraFormatterType.bytes,
type: InfraMetricLayoutVisualizationType.area,
seriesOverrides: {
used: { color: theme.eui.euiColorVis2 },
free: { color: theme.eui.euiColorVis0 },
cache: { color: theme.eui.euiColorVis1 },
used: {
color: theme.eui.euiColorVis2,
name: i18n.translate(
'xpack.infra.metricDetailPage.hostMetricsLayout.memoryUsageSection.seriesLabel.used',
{ defaultMessage: 'Used' }
),
},
free: {
color: theme.eui.euiColorVis0,
name: i18n.translate(
'xpack.infra.metricDetailPage.hostMetricsLayout.memoryUsageSection.seriesLabel.free',
{ defaultMessage: 'Free' }
),
},
cache: {
color: theme.eui.euiColorVis1,
name: i18n.translate(
'xpack.infra.metricDetailPage.hostMetricsLayout.memoryUsageSection.seriesLabel.cache',
{ defaultMessage: 'Cache' }
),
},
},
},
},
@ -199,7 +259,7 @@ export const hostLayoutCreator: InfraMetricLayoutCreator = theme => [
name: i18n.translate(
'xpack.infra.metricDetailPage.hostMetricsLayout.networkTrafficSection.networkRxRateSeriesLabel',
{
defaultMessage: 'in',
defaultMessage: 'In',
}
),
},
@ -208,7 +268,7 @@ export const hostLayoutCreator: InfraMetricLayoutCreator = theme => [
name: i18n.translate(
'xpack.infra.metricDetailPage.hostMetricsLayout.networkTrafficSection.networkTxRateSeriesLabel',
{
defaultMessage: 'out',
defaultMessage: 'Out',
}
),
},
@ -303,8 +363,21 @@ export const hostLayoutCreator: InfraMetricLayoutCreator = theme => [
visConfig: {
formatter: InfraFormatterType.abbreviatedNumber,
seriesOverrides: {
capacity: { color: theme.eui.euiColorVis2 },
used: { color: theme.eui.euiColorVis1, type: InfraMetricLayoutVisualizationType.area },
capacity: {
color: theme.eui.euiColorVis2,
name: i18n.translate(
'xpack.infra.metricDetailPage.kubernetesMetricsLayout.nodeCpuCapacitySection.seriesLabel.capacity',
{ defaultMessage: 'Capacity' }
),
},
used: {
color: theme.eui.euiColorVis1,
name: i18n.translate(
'xpack.infra.metricDetailPage.kubernetesMetricsLayout.nodeCpuCapacitySection.seriesLabel.used',
{ defaultMessage: 'Used' }
),
type: InfraMetricLayoutVisualizationType.area,
},
},
},
},
@ -321,8 +394,21 @@ export const hostLayoutCreator: InfraMetricLayoutCreator = theme => [
visConfig: {
formatter: InfraFormatterType.bytes,
seriesOverrides: {
capacity: { color: theme.eui.euiColorVis2 },
used: { color: theme.eui.euiColorVis1, type: InfraMetricLayoutVisualizationType.area },
capacity: {
color: theme.eui.euiColorVis2,
name: i18n.translate(
'xpack.infra.metricDetailPage.kubernetesMetricsLayout.nodeMemoryCapacitySection.seriesLabel.capacity',
{ defaultMessage: 'Capacity' }
),
},
used: {
color: theme.eui.euiColorVis1,
name: i18n.translate(
'xpack.infra.metricDetailPage.kubernetesMetricsLayout.nodeMemoryCapacitySection.seriesLabel.used',
{ defaultMessage: 'Used' }
),
type: InfraMetricLayoutVisualizationType.area,
},
},
},
},
@ -339,8 +425,21 @@ export const hostLayoutCreator: InfraMetricLayoutCreator = theme => [
visConfig: {
formatter: InfraFormatterType.bytes,
seriesOverrides: {
capacity: { color: theme.eui.euiColorVis2 },
used: { color: theme.eui.euiColorVis1, type: InfraMetricLayoutVisualizationType.area },
capacity: {
color: theme.eui.euiColorVis2,
name: i18n.translate(
'xpack.infra.metricDetailPage.kubernetesMetricsLayout.nodeDiskCapacitySection.seriesLabel.capacity',
{ defaultMessage: 'Capacity' }
),
},
used: {
color: theme.eui.euiColorVis1,
name: i18n.translate(
'xpack.infra.metricDetailPage.kubernetesMetricsLayout.nodeDiskCapacitySection.seriesLabel.used',
{ defaultMessage: 'Used' }
),
type: InfraMetricLayoutVisualizationType.area,
},
},
},
},
@ -357,13 +456,155 @@ export const hostLayoutCreator: InfraMetricLayoutCreator = theme => [
visConfig: {
formatter: InfraFormatterType.number,
seriesOverrides: {
capacity: { color: theme.eui.euiColorVis2 },
used: { color: theme.eui.euiColorVis1, type: InfraMetricLayoutVisualizationType.area },
capacity: {
color: theme.eui.euiColorVis2,
name: i18n.translate(
'xpack.infra.metricDetailPage.kubernetesMetricsLayout.nodePodCapacitySection.seriesLabel.capacity',
{ defaultMessage: 'Capacity' }
),
},
used: {
color: theme.eui.euiColorVis1,
name: i18n.translate(
'xpack.infra.metricDetailPage.kubernetesMetricsLayout.nodePodCapacitySection.seriesLabel.used',
{ defaultMessage: 'Used' }
),
type: InfraMetricLayoutVisualizationType.area,
},
},
},
},
],
},
{
id: 'dockerOverview',
label: 'Docker',
sections: [
{
id: InfraMetric.hostDockerOverview,
linkToId: 'dockerOverview',
label: i18n.translate(
'xpack.infra.metricDetailPage.dockerMetricsLayout.overviewSection.sectionLabel',
{
defaultMessage: 'Overview',
}
),
requires: ['docker.info'],
type: InfraMetricLayoutSectionType.gauges,
visConfig: {
seriesOverrides: {
total: {
name: i18n.translate(
'xpack.infra.metricDetailPage.dockerMetricsLayout.overviewSection.totalLabel',
{
defaultMessage: 'Total',
}
),
color: 'secondary',
},
running: {
name: i18n.translate(
'xpack.infra.metricDetailPage.dockerMetricsLayout.overviewSection.runningLabel',
{
defaultMessage: 'Running',
}
),
color: 'secondary',
},
paused: {
name: i18n.translate(
'xpack.infra.metricDetailPage.dockerMetricsLayout.overviewSection.pausedLabel',
{
defaultMessage: 'Paused',
}
),
color: 'secondary',
},
stopped: {
name: i18n.translate(
'xpack.infra.metricDetailPage.dockerMetricsLayout.overviewSection.stoppedLabel',
{
defaultMessage: 'Stopped',
}
),
color: 'secondary',
},
},
},
},
{
id: InfraMetric.hostDockerInfo,
label: i18n.translate(
'xpack.infra.metricDetailPage.dockerMetricsLayout.containerStates.sectionLabel',
{
defaultMessage: 'Container States',
}
),
requires: ['docker.info'],
type: InfraMetricLayoutSectionType.chart,
visConfig: {
formatter: InfraFormatterType.abbreviatedNumber,
stacked: true,
seriesOverrides: {
running: {
color: theme.eui.euiColorVis2,
type: InfraMetricLayoutVisualizationType.bar,
name: i18n.translate(
'xpack.infra.metricDetailPage.containerStates.seriesLabel.running',
{ defaultMessage: 'Running' }
),
},
stopped: {
color: theme.eui.euiColorVis1,
type: InfraMetricLayoutVisualizationType.bar,
name: i18n.translate(
'xpack.infra.metricDetailPage.containerStates.seriesLabel.stopped',
{ defaultMessage: 'Stopped' }
),
},
paused: {
color: theme.eui.euiColorVis7,
type: InfraMetricLayoutVisualizationType.bar,
name: i18n.translate(
'xpack.infra.metricDetailPage.containerStates.seriesLabel.paused',
{ defaultMessage: 'Paused' }
),
},
},
},
},
{
id: InfraMetric.hostDockerTop5ByCpu,
label: i18n.translate(
'xpack.infra.metricDetailPage.dockerMetricsLayout.top5Cpu.sectionLabel',
{
defaultMessage: 'Top 5 Containers by CPU',
}
),
requires: ['docker.cpu'],
type: InfraMetricLayoutSectionType.chart,
visConfig: {
formatter: InfraFormatterType.percent,
seriesOverrides: {},
},
},
{
id: InfraMetric.hostDockerTop5ByMemory,
label: i18n.translate(
'xpack.infra.metricDetailPage.dockerMetricsLayout.top5Memory.sectionLabel',
{
defaultMessage: 'Top 5 Containers by Memory',
}
),
requires: ['docker.memory'],
type: InfraMetricLayoutSectionType.chart,
visConfig: {
formatter: InfraFormatterType.percent,
seriesOverrides: {},
},
},
],
},
...nginxLayoutCreator(theme),
...awsLayoutCreator(theme),
];

View file

@ -32,10 +32,26 @@ export const nginxLayoutCreator: InfraMetricLayoutCreator = theme => [
formatter: InfraFormatterType.abbreviatedNumber,
stacked: true,
seriesOverrides: {
'200s': { color: theme.eui.euiColorVis1, type: InfraMetricLayoutVisualizationType.bar },
'300s': { color: theme.eui.euiColorVis5, type: InfraMetricLayoutVisualizationType.bar },
'400s': { color: theme.eui.euiColorVis2, type: InfraMetricLayoutVisualizationType.bar },
'500s': { color: theme.eui.euiColorVis9, type: InfraMetricLayoutVisualizationType.bar },
'200s': {
color: theme.eui.euiColorVis1,
type: InfraMetricLayoutVisualizationType.bar,
name: '200s',
},
'300s': {
color: theme.eui.euiColorVis5,
type: InfraMetricLayoutVisualizationType.bar,
name: '300s',
},
'400s': {
color: theme.eui.euiColorVis2,
type: InfraMetricLayoutVisualizationType.bar,
name: '400s',
},
'500s': {
color: theme.eui.euiColorVis9,
type: InfraMetricLayoutVisualizationType.bar,
name: '500s',
},
},
},
},
@ -53,7 +69,14 @@ export const nginxLayoutCreator: InfraMetricLayoutCreator = theme => [
formatter: InfraFormatterType.abbreviatedNumber,
formatterTemplate: '{{value}}/s',
seriesOverrides: {
rate: { color: theme.eui.euiColorVis1, type: InfraMetricLayoutVisualizationType.area },
rate: {
color: theme.eui.euiColorVis1,
type: InfraMetricLayoutVisualizationType.area,
name: i18n.translate(
'xpack.infra.metricDetailPage.nginxMetricsLayout.requestRateSection.seriesLabel.rate',
{ defaultMessage: 'rate' }
),
},
},
},
},
@ -73,6 +96,10 @@ export const nginxLayoutCreator: InfraMetricLayoutCreator = theme => [
connections: {
color: theme.eui.euiColorVis1,
type: InfraMetricLayoutVisualizationType.bar,
name: i18n.translate(
'xpack.infra.metricDetailPage.nginxMetricsLayout.activeConnectionsSection.seriesLabel.connections',
{ defaultMessage: 'connections' }
),
},
},
},

View file

@ -93,7 +93,14 @@ export const podLayoutCreator: InfraMetricLayoutCreator = theme => [
visConfig: {
formatter: InfraFormatterType.percent,
seriesOverrides: {
cpu: { color: theme.eui.euiColorVis1, type: InfraMetricLayoutVisualizationType.area },
cpu: {
color: theme.eui.euiColorVis1,
name: i18n.translate(
'xpack.infra.metricDetailPage.podMetricsLayout.cpuUsageSection.seriesLabel.cpu',
{ defaultMessage: 'cpu' }
),
type: InfraMetricLayoutVisualizationType.area,
},
},
},
},
@ -112,6 +119,10 @@ export const podLayoutCreator: InfraMetricLayoutCreator = theme => [
seriesOverrides: {
memory: {
color: theme.eui.euiColorVis1,
name: i18n.translate(
'xpack.infra.metricDetailPage.podMetricsLayout.memoryUsageSection.seriesLabel.memory',
{ defaultMessage: 'memory' }
),
type: InfraMetricLayoutVisualizationType.area,
},
},

View file

@ -19,6 +19,10 @@ export const metricsSchema: any = gql`
hostLoad
hostMemoryUsage
hostNetworkTraffic
hostDockerOverview
hostDockerInfo
hostDockerTop5ByCpu
hostDockerTop5ByMemory
podOverview
podCpuUsage
podMemoryUsage
@ -51,6 +55,7 @@ export const metricsSchema: any = gql`
type InfraDataSeries {
id: ID!
label: String!
data: [InfraDataPoint!]!
}

View file

@ -337,6 +337,8 @@ export interface InfraMetricData {
export interface InfraDataSeries {
id: string;
label: string;
data: InfraDataPoint[];
}
@ -602,6 +604,10 @@ export enum InfraMetric {
hostLoad = 'hostLoad',
hostMemoryUsage = 'hostMemoryUsage',
hostNetworkTraffic = 'hostNetworkTraffic',
hostDockerOverview = 'hostDockerOverview',
hostDockerInfo = 'hostDockerInfo',
hostDockerTop5ByCpu = 'hostDockerTop5ByCpu',
hostDockerTop5ByMemory = 'hostDockerTop5ByMemory',
podOverview = 'podOverview',
podCpuUsage = 'podCpuUsage',
podMemoryUsage = 'podMemoryUsage',
@ -1693,6 +1699,8 @@ export namespace InfraDataSeriesResolvers {
export interface Resolvers<Context = InfraContext, TypeParent = InfraDataSeries> {
id?: IdResolver<string, TypeParent, Context>;
label?: LabelResolver<string, TypeParent, Context>;
data?: DataResolver<InfraDataPoint[], TypeParent, Context>;
}
@ -1701,6 +1709,11 @@ export namespace InfraDataSeriesResolvers {
Parent,
Context
>;
export type LabelResolver<
R = string,
Parent = InfraDataSeries,
Context = InfraContext
> = Resolver<R, Parent, Context>;
export type DataResolver<
R = InfraDataPoint[],
Parent = InfraDataSeries,

View file

@ -204,6 +204,7 @@ export interface InfraTSVBPanel {
export interface InfraTSVBSeries {
id: string;
label: string;
data: InfraTSVBDataPoint[];
}

View file

@ -100,6 +100,7 @@ export class KibanaMetricsAdapter implements InfraMetricsAdapter {
series: panel.series.map(series => {
return {
id: series.id,
label: series.label,
data: series.data.map(point => ({ timestamp: point[0], value: point[1] })),
};
}),

View file

@ -0,0 +1,60 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import {
InfraMetricModelCreator,
InfraMetricModelMetricType,
InfraMetricModel,
} from '../../adapter_types';
import { InfraMetric } from '../../../../../graphql/types';
export const hostDockerInfo: InfraMetricModelCreator = (
timeField,
indexPattern,
interval
): InfraMetricModel => ({
id: InfraMetric.hostDockerInfo,
requires: ['docker.info'],
index_pattern: indexPattern,
interval,
time_field: timeField,
type: 'timeseries',
series: [
{
id: 'running',
metrics: [
{
field: 'docker.info.containers.running',
id: 'max-running',
type: InfraMetricModelMetricType.max,
},
],
split_mode: 'everything',
},
{
id: 'paused',
metrics: [
{
field: 'docker.info.containers.paused',
id: 'max-paused',
type: InfraMetricModelMetricType.max,
},
],
split_mode: 'everything',
},
{
id: 'stopped',
metrics: [
{
field: 'docker.info.containers.stopped',
id: 'max-stopped',
type: InfraMetricModelMetricType.max,
},
],
split_mode: 'everything',
},
],
});

View file

@ -0,0 +1,71 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import {
InfraMetricModelCreator,
InfraMetricModelMetricType,
InfraMetricModel,
} from '../../adapter_types';
import { InfraMetric } from '../../../../../graphql/types';
export const hostDockerOverview: InfraMetricModelCreator = (
timeField,
indexPattern,
interval
): InfraMetricModel => ({
id: InfraMetric.hostDockerOverview,
requires: ['docker.info'],
index_pattern: indexPattern,
interval,
time_field: timeField,
type: 'gauge',
series: [
{
id: 'total',
metrics: [
{
field: 'docker.info.containers.total',
id: 'max-total',
type: InfraMetricModelMetricType.max,
},
],
split_mode: 'everything',
},
{
id: 'running',
metrics: [
{
field: 'docker.info.containers.running',
id: 'max-running',
type: InfraMetricModelMetricType.max,
},
],
split_mode: 'everything',
},
{
id: 'paused',
metrics: [
{
field: 'docker.info.containers.paused',
id: 'max-paused',
type: InfraMetricModelMetricType.max,
},
],
split_mode: 'everything',
},
{
id: 'stopped',
metrics: [
{
field: 'docker.info.containers.stopped',
id: 'max-stopped',
type: InfraMetricModelMetricType.max,
},
],
split_mode: 'everything',
},
],
});

View file

@ -0,0 +1,41 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import {
InfraMetricModelCreator,
InfraMetricModelMetricType,
InfraMetricModel,
} from '../../adapter_types';
import { InfraMetric } from '../../../../../graphql/types';
export const hostDockerTop5ByCpu: InfraMetricModelCreator = (
timeField,
indexPattern,
interval
): InfraMetricModel => ({
id: InfraMetric.hostDockerTop5ByCpu,
requires: ['docker.cpu'],
index_pattern: indexPattern,
interval,
time_field: timeField,
type: 'timeseries',
series: [
{
id: 'avg-cpu',
metrics: [
{
field: 'docker.cpu.total.pct',
id: 'avg-cpu-metric',
type: InfraMetricModelMetricType.avg,
},
],
split_mode: 'terms',
terms_field: 'container.name',
terms_order_by: 'avg-cpu',
terms_size: 5,
},
],
});

View file

@ -0,0 +1,41 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import {
InfraMetricModelCreator,
InfraMetricModelMetricType,
InfraMetricModel,
} from '../../adapter_types';
import { InfraMetric } from '../../../../../graphql/types';
export const hostDockerTop5ByMemory: InfraMetricModelCreator = (
timeField,
indexPattern,
interval
): InfraMetricModel => ({
id: InfraMetric.hostDockerTop5ByMemory,
requires: ['docker.memory'],
index_pattern: indexPattern,
interval,
time_field: timeField,
type: 'timeseries',
series: [
{
id: 'avg-memory',
metrics: [
{
field: 'docker.memory.usage.pct',
id: 'avg-memory-metric',
type: InfraMetricModelMetricType.avg,
},
],
split_mode: 'terms',
terms_field: 'container.name',
terms_order_by: 'avg-memory',
terms_size: 5,
},
],
});

View file

@ -18,6 +18,10 @@ import { hostLoad } from './host/host_load';
import { hostMemoryUsage } from './host/host_memory_usage';
import { hostNetworkTraffic } from './host/host_network_traffic';
import { hostSystemOverview } from './host/host_system_overview';
import { hostDockerOverview } from './host/host_docker_overview';
import { hostDockerInfo } from './host/host_docker_info';
import { hostDockerTop5ByCpu } from './host/host_docker_top_5_by_cpu';
import { hostDockerTop5ByMemory } from './host/host_docker_top_5_by_memory';
import { podCpuUsage } from './pod/pod_cpu_usage';
import { podLogUsage } from './pod/pod_log_usage';
@ -60,6 +64,10 @@ export const metricModels: InfraMetricModels = {
[InfraMetric.hostLoad]: hostLoad,
[InfraMetric.hostMemoryUsage]: hostMemoryUsage,
[InfraMetric.hostNetworkTraffic]: hostNetworkTraffic,
[InfraMetric.hostDockerOverview]: hostDockerOverview,
[InfraMetric.hostDockerInfo]: hostDockerInfo,
[InfraMetric.hostDockerTop5ByCpu]: hostDockerTop5ByCpu,
[InfraMetric.hostDockerTop5ByMemory]: hostDockerTop5ByMemory,
[InfraMetric.podOverview]: podOverview,
[InfraMetric.podCpuUsage]: podCpuUsage,