diff --git a/x-pack/plugins/infra/common/http_api/metadata_api.ts b/x-pack/plugins/infra/common/http_api/metadata_api.ts
index 5ee96b479be8..41b599c31041 100644
--- a/x-pack/plugins/infra/common/http_api/metadata_api.ts
+++ b/x-pack/plugins/infra/common/http_api/metadata_api.ts
@@ -29,10 +29,15 @@ export const InfraMetadataOSRT = rt.partial({
name: rt.string,
platform: rt.string,
version: rt.string,
+ build: rt.string,
});
export const InfraMetadataHostRT = rt.partial({
name: rt.string,
+ hostname: rt.string,
+ id: rt.string,
+ ip: rt.array(rt.string),
+ mac: rt.array(rt.string),
os: InfraMetadataOSRT,
architecture: rt.string,
containerized: rt.boolean,
@@ -43,25 +48,40 @@ export const InfraMetadataInstanceRT = rt.partial({
name: rt.string,
});
+export const InfraMetadataAccountRT = rt.partial({
+ id: rt.string,
+ name: rt.string,
+});
+
export const InfraMetadataProjectRT = rt.partial({
id: rt.string,
});
export const InfraMetadataMachineRT = rt.partial({
interface: rt.string,
+ type: rt.string,
});
export const InfraMetadataCloudRT = rt.partial({
instance: InfraMetadataInstanceRT,
provider: rt.string,
+ account: InfraMetadataAccountRT,
availability_zone: rt.string,
project: InfraMetadataProjectRT,
machine: InfraMetadataMachineRT,
+ region: rt.string,
+});
+
+export const InfraMetadataAgentRT = rt.partial({
+ id: rt.string,
+ version: rt.string,
+ policy: rt.string,
});
export const InfraMetadataInfoRT = rt.partial({
cloud: InfraMetadataCloudRT,
host: InfraMetadataHostRT,
+ agent: InfraMetadataAgentRT,
});
const InfraMetadataRequiredRT = rt.type({
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/overlay.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/overlay.tsx
index 0943ced5e5be..be953ded70d7 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/overlay.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/overlay.tsx
@@ -14,8 +14,11 @@ import { InventoryItemType } from '../../../../../../common/inventory_models/typ
import { MetricsTab } from './tabs/metrics/metrics';
import { LogsTab } from './tabs/logs';
import { ProcessesTab } from './tabs/processes';
-import { PropertiesTab } from './tabs/properties';
+import { PropertiesTab } from './tabs/properties/index';
import { OVERLAY_Y_START, OVERLAY_BOTTOM_MARGIN, OVERLAY_HEADER_SIZE } from './tabs/shared';
+import { useLinkProps } from '../../../../../hooks/use_link_props';
+import { getNodeDetailUrl } from '../../../../link_to';
+import { findInventoryModel } from '../../../../../../common/inventory_models';
interface Props {
isOpen: boolean;
@@ -35,6 +38,8 @@ export const NodeContextPopover = ({
}: Props) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
const tabConfigs = [MetricsTab, LogsTab, ProcessesTab, PropertiesTab];
+ const inventoryModel = findInventoryModel(nodeType);
+ const nodeDetailFrom = currentTime - inventoryModel.metrics.defaultTimeRangeInSeconds * 1000;
const tabs = useMemo(() => {
return tabConfigs.map((m) => {
@@ -50,6 +55,15 @@ export const NodeContextPopover = ({
const [selectedTab, setSelectedTab] = useState(0);
+ const nodeDetailMenuItemLinkProps = useLinkProps({
+ ...getNodeDetailUrl({
+ nodeType,
+ nodeId: node.id,
+ from: nodeDetailFrom,
+ to: currentTime,
+ }),
+ });
+
if (!isOpen) {
return null;
}
@@ -65,9 +79,28 @@ export const NodeContextPopover = ({
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/metrics/metrics.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/metrics/metrics.tsx
index b5628b0a7c9b..789658c06040 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/metrics/metrics.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/metrics/metrics.tsx
@@ -17,6 +17,7 @@ import {
PointerEvent,
} from '@elastic/charts';
import moment from 'moment';
+import { EuiLoadingChart } from '@elastic/eui';
import { TabContent, TabProps } from '../shared';
import { useSnapshot } from '../../../../hooks/use_snaphot';
import { useWaffleOptionsContext } from '../../../../hooks/use_waffle_options';
@@ -82,9 +83,9 @@ const TabComponent = (props: TabProps) => {
}
const buildCustomMetric = useCallback(
- (field: string, id: string) => ({
+ (field: string, id: string, aggregation: string = 'avg') => ({
type: 'custom' as SnapshotMetricType,
- aggregation: 'avg',
+ aggregation,
field,
id,
}),
@@ -110,6 +111,7 @@ const TabComponent = (props: TabProps) => {
buildCustomMetric('system.load.15', 'load15m'),
buildCustomMetric('system.memory.actual.used.bytes', 'usedMemory'),
buildCustomMetric('system.memory.actual.free', 'freeMemory'),
+ buildCustomMetric('system.cpu.cores', 'cores', 'max'),
],
[],
nodeType,
@@ -223,6 +225,7 @@ const TabComponent = (props: TabProps) => {
const load15mMetricsTs = useMemo(() => getTimeseries('load15m'), [getTimeseries]);
const usedMemoryMetricsTs = useMemo(() => getTimeseries('usedMemory'), [getTimeseries]);
const freeMemoryMetricsTs = useMemo(() => getTimeseries('freeMemory'), [getTimeseries]);
+ const coresMetricsTs = useMemo(() => getTimeseries('cores'), [getTimeseries]);
useEffect(() => {
reload();
@@ -239,7 +242,7 @@ const TabComponent = (props: TabProps) => {
!usedMemoryMetricsTs ||
!freeMemoryMetricsTs
) {
- return ;
+ return ;
}
const cpuChartMetrics = buildChartMetricLabels([SYSTEM_METRIC_NAME, USER_METRIC_NAME], 'avg');
@@ -253,6 +256,23 @@ const TabComponent = (props: TabProps) => {
'rate'
);
+ systemMetricsTs.rows = systemMetricsTs.rows.slice().map((r, idx) => {
+ const metric = r.metric_0 as number | undefined;
+ const cores = coresMetricsTs!.rows[idx].metric_0 as number | undefined;
+ if (metric && cores) {
+ r.metric_0 = metric / cores;
+ }
+ return r;
+ });
+
+ userMetricsTs.rows = userMetricsTs.rows.slice().map((r, idx) => {
+ const metric = r.metric_0 as number | undefined;
+ const cores = coresMetricsTs!.rows[idx].metric_0 as number | undefined;
+ if (metric && cores) {
+ r.metric_0 = metric / cores;
+ }
+ return r;
+ });
const cpuTimeseries = mergeTimeseries(systemMetricsTs, userMetricsTs);
const networkTimeseries = mergeTimeseries(rxMetricsTs, txMetricsTs);
const loadTimeseries = mergeTimeseries(load1mMetricsTs, load5mMetricsTs, load15mMetricsTs);
@@ -467,6 +487,23 @@ const ChartContainer: React.FC = ({ children }) => (
);
+const LoadingPlaceholder = () => {
+ return (
+
+
+
+ );
+};
+
export const MetricsTab = {
id: 'metrics',
name: i18n.translate('xpack.infra.nodeDetails.tabs.metrics', {
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties.tsx
deleted file mode 100644
index 8157aca9b141..000000000000
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties.tsx
+++ /dev/null
@@ -1,21 +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;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React from 'react';
-import { i18n } from '@kbn/i18n';
-import { TabContent, TabProps } from './shared';
-
-const TabComponent = (props: TabProps) => {
- return Properties Placeholder;
-};
-
-export const PropertiesTab = {
- id: 'properties',
- name: i18n.translate('xpack.infra.nodeDetails.tabs.properties', {
- defaultMessage: 'Properties',
- }),
- content: TabComponent,
-};
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/build_fields.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/build_fields.ts
new file mode 100644
index 000000000000..79610ba3eef0
--- /dev/null
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/build_fields.ts
@@ -0,0 +1,116 @@
+/*
+ * 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 { InfraMetadata } from '../../../../../../../../common/http_api';
+
+export const getFields = (metadata: InfraMetadata, group: 'cloud' | 'host' | 'agent') => {
+ switch (group) {
+ case 'host':
+ return prune([
+ {
+ name: 'host.architecture',
+ value: metadata.info?.host?.architecture,
+ },
+ {
+ name: 'host.hostname',
+ value: metadata.info?.host?.name,
+ },
+ {
+ name: 'host.id',
+ value: metadata.info?.host?.id,
+ },
+ {
+ name: 'host.ip',
+ value: metadata.info?.host?.ip,
+ },
+ {
+ name: 'host.mac',
+ value: metadata.info?.host?.mac,
+ },
+ {
+ name: 'host.name',
+ value: metadata.info?.host?.name,
+ },
+ {
+ name: 'host.os.build',
+ value: metadata.info?.host?.os?.build,
+ },
+ {
+ name: 'host.os.family',
+ value: metadata.info?.host?.os?.family,
+ },
+ {
+ name: 'host.os.name',
+ value: metadata.info?.host?.os?.name,
+ },
+ {
+ name: 'host.os.kernel',
+ value: metadata.info?.host?.os?.kernel,
+ },
+ {
+ name: 'host.os.platform',
+ value: metadata.info?.host?.os?.platform,
+ },
+ {
+ name: 'host.os.version',
+ value: metadata.info?.host?.os?.version,
+ },
+ ]);
+ case 'cloud':
+ return prune([
+ {
+ name: 'cloud.account.id',
+ value: metadata.info?.cloud?.account?.id,
+ },
+ {
+ name: 'cloud.account.name',
+ value: metadata.info?.cloud?.account?.name,
+ },
+ {
+ name: 'cloud.availability_zone',
+ value: metadata.info?.cloud?.availability_zone,
+ },
+ {
+ name: 'cloud.instance.id',
+ value: metadata.info?.cloud?.instance?.id,
+ },
+ {
+ name: 'cloud.instance.name',
+ value: metadata.info?.cloud?.instance?.name,
+ },
+ {
+ name: 'cloud.machine.type',
+ value: metadata.info?.cloud?.machine?.type,
+ },
+ {
+ name: 'cloud.provider',
+ value: metadata.info?.cloud?.provider,
+ },
+ {
+ name: 'cloud.region',
+ value: metadata.info?.cloud?.region,
+ },
+ ]);
+ case 'agent':
+ return prune([
+ {
+ name: 'agent.id',
+ value: metadata.info?.agent?.id,
+ },
+ {
+ name: 'agent.version',
+ value: metadata.info?.agent?.version,
+ },
+ {
+ name: 'agent.policy',
+ value: metadata.info?.agent?.policy,
+ },
+ ]);
+ }
+};
+
+const prune = (fields: Array<{ name: string; value: string | string[] | undefined }>) =>
+ fields.filter((f) => !!f.value);
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/index.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/index.tsx
new file mode 100644
index 000000000000..b901c3748438
--- /dev/null
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/index.tsx
@@ -0,0 +1,131 @@
+/*
+ * 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 React, { useCallback, useContext, useMemo } from 'react';
+import { i18n } from '@kbn/i18n';
+import { EuiLoadingChart } from '@elastic/eui';
+import { TabContent, TabProps } from '../shared';
+import { Source } from '../../../../../../../containers/source';
+import { findInventoryModel } from '../../../../../../../../common/inventory_models';
+import { InventoryItemType } from '../../../../../../../../common/inventory_models/types';
+import { useMetadata } from '../../../../../metric_detail/hooks/use_metadata';
+import { getFields } from './build_fields';
+import { useWaffleTimeContext } from '../../../../hooks/use_waffle_time';
+import { Table } from './table';
+import { euiStyled } from '../../../../../../../../../observability/public';
+import { useWaffleFiltersContext } from '../../../../hooks/use_waffle_filters';
+
+const TabComponent = (props: TabProps) => {
+ const nodeId = props.node.id;
+ const nodeType = props.nodeType as InventoryItemType;
+ const inventoryModel = findInventoryModel(nodeType);
+ const { sourceId } = useContext(Source.Context);
+ const { currentTimeRange } = useWaffleTimeContext();
+ const { applyFilterQuery } = useWaffleFiltersContext();
+ const { loading: metadataLoading, metadata } = useMetadata(
+ nodeId,
+ nodeType,
+ inventoryModel.requiredMetrics,
+ sourceId,
+ currentTimeRange
+ );
+
+ const hostFields = useMemo(() => {
+ if (!metadata) return null;
+ return getFields(metadata, 'host');
+ }, [metadata]);
+
+ const cloudFields = useMemo(() => {
+ if (!metadata) return null;
+ return getFields(metadata, 'cloud');
+ }, [metadata]);
+
+ const agentFields = useMemo(() => {
+ if (!metadata) return null;
+ return getFields(metadata, 'agent');
+ }, [metadata]);
+
+ const onFilter = useCallback(
+ (item: { name: string; value: string }) => {
+ applyFilterQuery({
+ kind: 'kuery',
+ expression: `${item.name}: "${item.value}"`,
+ });
+ },
+ [applyFilterQuery]
+ );
+
+ if (metadataLoading) {
+ return ;
+ }
+
+ return (
+
+ {hostFields && hostFields.length > 0 && (
+
+
+
+ )}
+ {cloudFields && cloudFields.length > 0 && (
+
+
+
+ )}
+ {agentFields && agentFields.length > 0 && (
+
+
+
+ )}
+
+ );
+};
+
+const TableWrapper = euiStyled.div`
+ margin-bottom: 20px
+`;
+
+const LoadingPlaceholder = () => {
+ return (
+
+
+
+ );
+};
+
+export const PropertiesTab = {
+ id: 'properties',
+ name: i18n.translate('xpack.infra.nodeDetails.tabs.metadata.title', {
+ defaultMessage: 'Metadata',
+ }),
+ content: TabComponent,
+};
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/table.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/table.tsx
new file mode 100644
index 000000000000..c3e47b6084eb
--- /dev/null
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/table.tsx
@@ -0,0 +1,158 @@
+/*
+ * 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 { EuiText } from '@elastic/eui';
+import { EuiToolTip } from '@elastic/eui';
+import { EuiButtonIcon } from '@elastic/eui';
+import { EuiFlexGroup } from '@elastic/eui';
+import { EuiFlexItem } from '@elastic/eui';
+import { EuiLink } from '@elastic/eui';
+import { EuiBasicTable } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { first } from 'lodash';
+import React, { useCallback, useMemo, useState } from 'react';
+import { FormattedMessage } from 'react-intl';
+import { euiStyled } from '../../../../../../../../../observability/public';
+
+interface Row {
+ name: string;
+ value: string | string[] | undefined;
+}
+
+interface Props {
+ rows: Row[];
+ title: string;
+ onClick(item: Row): void;
+}
+
+export const Table = (props: Props) => {
+ const { rows, title, onClick } = props;
+ const columns = useMemo(
+ () => [
+ {
+ field: 'name',
+ name: '',
+ width: '35%',
+ sortable: false,
+ render: (name: string, item: Row) => (
+
+ {item.name}
+
+ ),
+ },
+ {
+ field: 'value',
+ name: '',
+ width: '65%',
+ sortable: false,
+ render: (_name: string, item: Row) => {
+ return (
+
+
+
+
+ onClick(item)}
+ />
+
+
+ {!Array.isArray(item.value) && item.value}
+ {Array.isArray(item.value) && }
+
+
+
+
+ );
+ },
+ },
+ ],
+ [onClick]
+ );
+
+ return (
+ <>
+
+
+ {title}
+
+
+
+ >
+ );
+};
+
+const TitleWrapper = euiStyled.div`
+ margin-bottom: 10px
+`;
+
+class TableWithoutHeader extends EuiBasicTable {
+ renderTableHead() {
+ return <>>;
+ }
+}
+
+interface MoreProps {
+ values: string[];
+}
+const ArrayValue = (props: MoreProps) => {
+ const { values } = props;
+ const [isExpanded, setIsExpanded] = useState(false);
+ const expand = useCallback(() => {
+ setIsExpanded(true);
+ }, []);
+
+ const collapse = useCallback(() => {
+ setIsExpanded(false);
+ }, []);
+
+ return (
+ <>
+ {!isExpanded && (
+
+
+ {first(values)}
+ {' ... '}
+
+
+
+
+
+
+
+ )}
+ {isExpanded && (
+
+ {values.map((v) => (
+
{v}
+ ))}
+
+ {i18n.translate('xpack.infra.nodeDetails.tabs.metadata.seeLess', {
+ defaultMessage: 'See less',
+ })}
+
+
+ )}
+ >
+ );
+};
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node.tsx
index f2d9da960df8..03fb53898e31 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node.tsx
@@ -54,6 +54,7 @@ export const Node = class extends React.PureComponent {
defaultMessage: '{nodeName}, click to open menu',
values: { nodeName: node.name },
});
+
return (
<>
{
}
private togglePopover = () => {
- this.setState((prevState) => ({ isPopoverOpen: !prevState.isPopoverOpen }));
+ const { nodeType } = this.props;
+ if (nodeType === 'host') {
+ this.toggleNewOverlay();
+ } else {
+ this.setState((prevState) => ({ isPopoverOpen: !prevState.isPopoverOpen }));
+ }
};
private toggleNewOverlay = () => {
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx
index 91c6ad801000..3179d4aa0526 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx
@@ -161,14 +161,6 @@ export const NodeContextMenu: React.FC = withTheme
},
};
- const openNewOverlayMenuItem: SectionLinkProps = {
- label: i18n.translate('xpack.infra.nodeContextMenu.openNewOverlay', {
- defaultMessage: '**** [NEW] Overlay ***',
- }),
- style: { color: theme?.eui.euiLinkColor || '#006BB4', fontWeight: 500, padding: 0 },
- onClick: openNewOverlay,
- };
-
return (
<>
= withTheme
-
diff --git a/x-pack/plugins/infra/server/routes/metadata/lib/get_node_info.ts b/x-pack/plugins/infra/server/routes/metadata/lib/get_node_info.ts
index f1341c7ec810..b378b42e2ff5 100644
--- a/x-pack/plugins/infra/server/routes/metadata/lib/get_node_info.ts
+++ b/x-pack/plugins/infra/server/routes/metadata/lib/get_node_info.ts
@@ -58,7 +58,7 @@ export const getNodeInfo = async (
index: sourceConfiguration.metricAlias,
body: {
size: 1,
- _source: ['host.*', 'cloud.*'],
+ _source: ['host.*', 'cloud.*', 'agent.*'],
sort: [{ [timestampField]: 'desc' }],
query: {
bool: {
diff --git a/x-pack/test/api_integration/apis/metrics_ui/metadata.ts b/x-pack/test/api_integration/apis/metrics_ui/metadata.ts
index 349b0dcbd9cf..e319e59045d2 100644
--- a/x-pack/test/api_integration/apis/metrics_ui/metadata.ts
+++ b/x-pack/test/api_integration/apis/metrics_ui/metadata.ts
@@ -109,6 +109,13 @@ export default function ({ getService }: FtrProviderContext) {
machine: { type: 'n1-standard-4' },
project: { id: 'elastic-observability' },
},
+ agent: {
+ hostname: 'gke-observability-8--observability-8--bc1afd95-f0zc',
+ id: 'c91c0d2b-6483-46bb-9731-f06afd32bb59',
+ ephemeral_id: '7cb259b1-795c-4c76-beaf-2eb8f18f5b02',
+ type: 'metricbeat',
+ version: '8.0.0',
+ },
host: {
hostname: 'gke-observability-8--observability-8--bc1afd95-f0zc',
os: {
@@ -150,6 +157,13 @@ export default function ({ getService }: FtrProviderContext) {
region: 'us-east-2',
account: { id: '015351775590' },
},
+ agent: {
+ hostname: 'ip-172-31-47-9.us-east-2.compute.internal',
+ id: 'd0943b36-d0d3-426d-892b-7d79c071b44b',
+ ephemeral_id: '64c94244-88b8-4a37-adc0-30428fefaf53',
+ type: 'metricbeat',
+ version: '8.0.0',
+ },
host: {
hostname: 'ip-172-31-47-9.us-east-2.compute.internal',
os: {
@@ -197,6 +211,13 @@ export default function ({ getService }: FtrProviderContext) {
id: 'elastic-observability',
},
},
+ agent: {
+ hostname: 'gke-observability-8--observability-8--bc1afd95-ngmh',
+ id: '66dc19e6-da36-49d2-9471-2c9475503178',
+ ephemeral_id: 'a0c3a9ff-470a-41a0-bf43-d1af6b7a3b5b',
+ type: 'metricbeat',
+ version: '8.0.0',
+ },
host: {
hostname: 'gke-observability-8--observability-8--bc1afd95-ngmh',
name: 'gke-observability-8--observability-8--bc1afd95-ngmh',
@@ -244,6 +265,13 @@ export default function ({ getService }: FtrProviderContext) {
id: 'elastic-observability',
},
},
+ agent: {
+ hostname: 'gke-observability-8--observability-8--bc1afd95-nhhw',
+ id: 'c58a514c-e971-4590-8206-385400e184dd',
+ ephemeral_id: 'e9d46cb0-2e89-469d-bd3b-6f32d7c96cc0',
+ type: 'metricbeat',
+ version: '8.0.0',
+ },
host: {
hostname: 'gke-observability-8--observability-8--bc1afd95-nhhw',
name: 'gke-observability-8--observability-8--bc1afd95-nhhw',