mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
* [Monitoring] Introducing Logs UI (#31275) * Initial implementation * More logs UI work * Remove unnecessary code * Add support to build a logs url based on the cluster and/or node uuid * Deep link directly * Update link * Use CCS to access remote filebeat data * Fix existing tests * Add log specific api integration tests * Localization * Adding more localization * Adding unit tests for logs ui * Client side unit tests, configuration for log fetch count, adding visual callout for why we can't detect logs * Remove debug * Fix localization issue * Update tests * PR feedback * Update import * Format the count to avoid a huge string of numbers * Use @timestamp instead * Handle scenario where the time period is not right but the type exists * Update jest tests * Update api tests * Text changes * Add periods * Update tests * Update jest snapshot (#35082)
This commit is contained in:
parent
f3e982e02f
commit
e87682c5f8
46 changed files with 6470 additions and 40 deletions
|
@ -159,3 +159,5 @@ export const INDEX_PATTERN_LOGSTASH = '.monitoring-logstash-6-*,.monitoring-logs
|
|||
export const INDEX_PATTERN_BEATS = '.monitoring-beats-6-*,.monitoring-beats-7-*';
|
||||
export const INDEX_ALERTS = '.monitoring-alerts-6,.monitoring-alerts-7';
|
||||
export const INDEX_PATTERN_ELASTICSEARCH = '.monitoring-es-6-*,.monitoring-es-7-*';
|
||||
|
||||
export const INDEX_PATTERN_FILEBEAT = 'filebeat-*';
|
||||
|
|
|
@ -70,7 +70,8 @@ export const config = (Joi) => {
|
|||
keyPassphrase: Joi.string(),
|
||||
alwaysPresentCertificate: Joi.boolean().default(false),
|
||||
}).default(),
|
||||
apiVersion: Joi.string().default('master')
|
||||
apiVersion: Joi.string().default('master'),
|
||||
logFetchCount: Joi.number().default(10)
|
||||
}).default(),
|
||||
tests: Joi.object({
|
||||
cloud_detector: Joi.object({
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { get } from 'lodash';
|
||||
import React, { Fragment } from 'react';
|
||||
import { get, capitalize } from 'lodash';
|
||||
import { formatNumber } from 'plugins/monitoring/lib/format_number';
|
||||
import { ClusterItemContainer, HealthStatusIndicator, BytesUsage, BytesPercentageUsage } from './helpers';
|
||||
import {
|
||||
|
@ -18,9 +18,14 @@ import {
|
|||
EuiDescriptionListTitle,
|
||||
EuiDescriptionListDescription,
|
||||
EuiHorizontalRule,
|
||||
EuiBadge,
|
||||
EuiToolTip,
|
||||
EuiFlexGroup,
|
||||
} from '@elastic/eui';
|
||||
import { LicenseText } from './license_text';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage, injectI18n } from '@kbn/i18n/react';
|
||||
import { Reason } from '../../logs/reason';
|
||||
|
||||
const calculateShards = shards => {
|
||||
const total = get(shards, 'total', 0);
|
||||
|
@ -40,8 +45,97 @@ const calculateShards = shards => {
|
|||
};
|
||||
};
|
||||
|
||||
function ElasticsearchPanelUi(props) {
|
||||
function getBadgeColorFromLogLevel(level) {
|
||||
switch (level) {
|
||||
case 'warn':
|
||||
return 'warning';
|
||||
case 'debug':
|
||||
return 'hollow';
|
||||
case 'info':
|
||||
return 'default';
|
||||
case 'error':
|
||||
return 'danger';
|
||||
}
|
||||
}
|
||||
|
||||
function renderLogs(props) {
|
||||
if (!props.logs.enabled) {
|
||||
return (
|
||||
<EuiDescriptionList>
|
||||
<Reason reason={props.logs.reason}/>
|
||||
</EuiDescriptionList>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiDescriptionList type="column">
|
||||
{props.logs.types.map((log, index) => (
|
||||
<Fragment key={index}>
|
||||
<EuiDescriptionListTitle>
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.cluster.overview.logsPanel.logTypeTitle"
|
||||
defaultMessage="{type}"
|
||||
values={{
|
||||
type: capitalize(log.type),
|
||||
}}
|
||||
/>
|
||||
</EuiDescriptionListTitle>
|
||||
<EuiDescriptionListDescription>
|
||||
{renderLog(log)}
|
||||
</EuiDescriptionListDescription>
|
||||
</Fragment>
|
||||
))}
|
||||
{props.logs.types.length === 0
|
||||
? (
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.cluster.overview.logsPanel.noLogsFound"
|
||||
defaultMessage="No logs found."
|
||||
/>
|
||||
)
|
||||
: null
|
||||
}
|
||||
</EuiDescriptionList>
|
||||
);
|
||||
}
|
||||
|
||||
const logLevelText = {
|
||||
info: i18n.translate('xpack.monitoring.cluster.overview.esPanel.infoLogsTooltipText', {
|
||||
defaultMessage: 'The number of information logs'
|
||||
}),
|
||||
warn: i18n.translate('xpack.monitoring.cluster.overview.esPanel.warnLogsTooltipText', {
|
||||
defaultMessage: 'The number of warning logs'
|
||||
}),
|
||||
debug: i18n.translate('xpack.monitoring.cluster.overview.esPanel.debugLogsTooltipText', {
|
||||
defaultMessage: 'The number of debug logs'
|
||||
}),
|
||||
error: i18n.translate('xpack.monitoring.cluster.overview.esPanel.errorLogsTooltipText', {
|
||||
defaultMessage: 'The number of error logs'
|
||||
}),
|
||||
fatal: i18n.translate('xpack.monitoring.cluster.overview.esPanel.fatalLogsTooltipText', {
|
||||
defaultMessage: 'The number of fatal logs'
|
||||
}),
|
||||
};
|
||||
|
||||
function renderLog(log) {
|
||||
return (
|
||||
<EuiFlexGroup wrap responsive={false} gutterSize="xs">
|
||||
{log.levels.map((level, index) => (
|
||||
<EuiFlexItem grow={false} key={index}>
|
||||
<EuiToolTip
|
||||
position="top"
|
||||
content={logLevelText[level.level]}
|
||||
>
|
||||
<EuiBadge color={getBadgeColorFromLogLevel(level.level)}>
|
||||
{formatNumber(level.count, 'int_commas')}
|
||||
</EuiBadge>
|
||||
</EuiToolTip>
|
||||
</EuiFlexItem>
|
||||
))}
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
|
||||
function ElasticsearchPanelUi(props) {
|
||||
const clusterStats = props.cluster_stats || {};
|
||||
const nodes = clusterStats.nodes;
|
||||
const indices = clusterStats.indices;
|
||||
|
@ -239,6 +333,28 @@ function ElasticsearchPanelUi(props) {
|
|||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem>
|
||||
<EuiPanel paddingSize="m">
|
||||
<EuiTitle size="s">
|
||||
<h3>
|
||||
<EuiLink
|
||||
onClick={goToElasticsearch}
|
||||
aria-label={props.intl.formatMessage({
|
||||
id: 'xpack.monitoring.cluster.overview.esPanel.logsLinkAriaLabel', defaultMessage: 'Elasticsearch Logs' })}
|
||||
data-test-subj="esLogs"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.cluster.overview.esPanel.logsLinkLabel"
|
||||
defaultMessage="Logs"
|
||||
/>
|
||||
</EuiLink>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiHorizontalRule margin="m" />
|
||||
{renderLogs(props)}
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
|
||||
</EuiFlexGrid>
|
||||
</ClusterItemContainer>
|
||||
);
|
||||
|
|
|
@ -17,11 +17,15 @@ import {
|
|||
import { IndexDetailStatus } from '../index_detail_status';
|
||||
import { MonitoringTimeseriesContainer } from '../../chart';
|
||||
import { ShardAllocation } from '../shard_allocation/shard_allocation';
|
||||
import { Logs } from '../../logs';
|
||||
|
||||
export const Index = ({
|
||||
scope,
|
||||
indexSummary,
|
||||
metrics,
|
||||
clusterUuid,
|
||||
indexUuid,
|
||||
logs,
|
||||
kbnUrl,
|
||||
...props
|
||||
}) => {
|
||||
|
@ -54,6 +58,10 @@ export const Index = ({
|
|||
))}
|
||||
</EuiFlexGrid>
|
||||
<EuiSpacer size="m"/>
|
||||
<EuiPanel>
|
||||
<Logs logs={logs} indexUuid={indexUuid} clusterUuid={clusterUuid} />
|
||||
</EuiPanel>
|
||||
<EuiSpacer size="m"/>
|
||||
<ShardAllocation scope={scope} kbnUrl={kbnUrl} type="index" />
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
|
|
|
@ -15,12 +15,16 @@ import {
|
|||
EuiPanel,
|
||||
} from '@elastic/eui';
|
||||
import { NodeDetailStatus } from '../node_detail_status';
|
||||
import { Logs } from '../../logs/';
|
||||
import { MonitoringTimeseriesContainer } from '../../chart';
|
||||
import { ShardAllocation } from '../shard_allocation/shard_allocation';
|
||||
|
||||
export const Node = ({
|
||||
nodeSummary,
|
||||
metrics,
|
||||
logs,
|
||||
nodeId,
|
||||
clusterUuid,
|
||||
scope,
|
||||
kbnUrl,
|
||||
...props
|
||||
|
@ -53,9 +57,15 @@ export const Node = ({
|
|||
</EuiFlexItem>
|
||||
))}
|
||||
</EuiFlexGrid>
|
||||
<EuiSpacer size="m"/>
|
||||
<ShardAllocation scope={scope} kbnUrl={kbnUrl}/>
|
||||
</EuiPageContent>
|
||||
<EuiSpacer size="m"/>
|
||||
<EuiPanel>
|
||||
<Logs logs={logs} nodeId={nodeId} clusterUuid={clusterUuid} />
|
||||
</EuiPanel>
|
||||
<EuiSpacer size="m"/>
|
||||
<EuiPanel>
|
||||
<ShardAllocation scope={scope} kbnUrl={kbnUrl}/>
|
||||
</EuiPanel>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
);
|
||||
|
|
|
@ -9,10 +9,13 @@ import { ClusterStatus } from '../cluster_status';
|
|||
import { ShardActivity } from '../shard_activity';
|
||||
import { MonitoringTimeseriesContainer } from '../../chart';
|
||||
import { EuiPage, EuiFlexGrid, EuiFlexItem, EuiPanel, EuiSpacer, EuiPageBody, EuiPageContent } from '@elastic/eui';
|
||||
import { Logs } from '../../logs/logs';
|
||||
|
||||
export function ElasticsearchOverview({
|
||||
clusterStatus,
|
||||
metrics,
|
||||
logs,
|
||||
cluster,
|
||||
shardActivity,
|
||||
...props
|
||||
}) {
|
||||
|
@ -42,8 +45,15 @@ export function ElasticsearchOverview({
|
|||
</EuiFlexItem>
|
||||
))}
|
||||
</EuiFlexGrid>
|
||||
<ShardActivity data={shardActivity} {...props} />
|
||||
</EuiPageContent>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiPanel>
|
||||
<Logs logs={logs} clusterUuid={cluster.cluster_uuid}/>
|
||||
</EuiPanel>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiPanel>
|
||||
<ShardActivity data={shardActivity} {...props} />
|
||||
</EuiPanel>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
);
|
||||
|
|
319
x-pack/plugins/monitoring/public/components/logs/__snapshots__/logs.test.js.snap
generated
Normal file
319
x-pack/plugins/monitoring/public/components/logs/__snapshots__/logs.test.js.snap
generated
Normal file
|
@ -0,0 +1,319 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Logs should render a link to filter by cluster uuid 1`] = `
|
||||
<EuiCallOut
|
||||
color="primary"
|
||||
iconType="loggingApp"
|
||||
size="m"
|
||||
title="Want to see more logs?"
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="Visit {link} to dive deeper."
|
||||
id="xpack.monitoring.logs.listing.linkText"
|
||||
values={
|
||||
Object {
|
||||
"link": <EuiLink
|
||||
color="primary"
|
||||
href="/app/infra#/link-to/logs?filter=elasticsearch.cluster.uuid:12345"
|
||||
type="button"
|
||||
>
|
||||
Logs
|
||||
</EuiLink>,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
`;
|
||||
|
||||
exports[`Logs should render a link to filter by cluster uuid and index uuid 1`] = `
|
||||
<EuiCallOut
|
||||
color="primary"
|
||||
iconType="loggingApp"
|
||||
size="m"
|
||||
title="Want to see more logs?"
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="Visit {link} to dive deeper."
|
||||
id="xpack.monitoring.logs.listing.linkText"
|
||||
values={
|
||||
Object {
|
||||
"link": <EuiLink
|
||||
color="primary"
|
||||
href="/app/infra#/link-to/logs?filter=elasticsearch.cluster.uuid:12345 and elasticsearch.index.name:6789"
|
||||
type="button"
|
||||
>
|
||||
Logs
|
||||
</EuiLink>,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
`;
|
||||
|
||||
exports[`Logs should render a link to filter by cluster uuid and node uuid 1`] = `
|
||||
<EuiCallOut
|
||||
color="primary"
|
||||
iconType="loggingApp"
|
||||
size="m"
|
||||
title="Want to see more logs?"
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="Visit {link} to dive deeper."
|
||||
id="xpack.monitoring.logs.listing.linkText"
|
||||
values={
|
||||
Object {
|
||||
"link": <EuiLink
|
||||
color="primary"
|
||||
href="/app/infra#/link-to/logs?filter=elasticsearch.cluster.uuid:12345 and elasticsearch.node.id:6789"
|
||||
type="button"
|
||||
>
|
||||
Logs
|
||||
</EuiLink>,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
`;
|
||||
|
||||
exports[`Logs should render a reason if the logs are disabled 1`] = `
|
||||
<div>
|
||||
<EuiTitle
|
||||
size="m"
|
||||
textTransform="none"
|
||||
>
|
||||
<h1>
|
||||
Recent Logs
|
||||
</h1>
|
||||
</EuiTitle>
|
||||
<EuiText
|
||||
grow={true}
|
||||
size="s"
|
||||
>
|
||||
<p>
|
||||
Showing the most recent logs for this cluster, up to 15 total logs.
|
||||
</p>
|
||||
</EuiText>
|
||||
<EuiSpacer
|
||||
size="m"
|
||||
/>
|
||||
<Reason
|
||||
reason={Object {}}
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="m"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Logs should render fewer columns for node or index view 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"field": "timestamp",
|
||||
"name": "Timestamp",
|
||||
"render": [Function],
|
||||
"width": "12%",
|
||||
},
|
||||
Object {
|
||||
"field": "level",
|
||||
"name": "Level",
|
||||
"width": "5%",
|
||||
},
|
||||
Object {
|
||||
"field": "type",
|
||||
"name": "Type",
|
||||
"render": [Function],
|
||||
"width": "10%",
|
||||
},
|
||||
Object {
|
||||
"field": "message",
|
||||
"name": "Message",
|
||||
"width": "55%",
|
||||
},
|
||||
Object {
|
||||
"field": "component",
|
||||
"name": "Component",
|
||||
"width": "18%",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`Logs should render normally 1`] = `
|
||||
<div>
|
||||
<EuiTitle
|
||||
size="m"
|
||||
textTransform="none"
|
||||
>
|
||||
<h1>
|
||||
Recent Logs
|
||||
</h1>
|
||||
</EuiTitle>
|
||||
<EuiText
|
||||
grow={true}
|
||||
size="s"
|
||||
>
|
||||
<p>
|
||||
Showing the most recent logs for this cluster, up to 10 total logs.
|
||||
</p>
|
||||
</EuiText>
|
||||
<EuiSpacer
|
||||
size="m"
|
||||
/>
|
||||
<EuiBasicTable
|
||||
columns={
|
||||
Array [
|
||||
Object {
|
||||
"field": "timestamp",
|
||||
"name": "Timestamp",
|
||||
"render": [Function],
|
||||
"width": "12%",
|
||||
},
|
||||
Object {
|
||||
"field": "level",
|
||||
"name": "Level",
|
||||
"width": "5%",
|
||||
},
|
||||
Object {
|
||||
"field": "type",
|
||||
"name": "Type",
|
||||
"render": [Function],
|
||||
"width": "10%",
|
||||
},
|
||||
Object {
|
||||
"field": "message",
|
||||
"name": "Message",
|
||||
"width": "45%",
|
||||
},
|
||||
Object {
|
||||
"field": "component",
|
||||
"name": "Component",
|
||||
"width": "15%",
|
||||
},
|
||||
Object {
|
||||
"field": "node",
|
||||
"name": "Node",
|
||||
"width": "13%",
|
||||
},
|
||||
]
|
||||
}
|
||||
items={
|
||||
Array [
|
||||
Object {
|
||||
"component": "o.e.d.x.m.r.a.RestMonitoringBulkAction",
|
||||
"level": "WARN",
|
||||
"message": "[POST /_xpack/monitoring/_bulk] is deprecated! Use [POST /_monitoring/bulk] instead.",
|
||||
"node": "foobar",
|
||||
"timestamp": "2019-03-18T12:49:33.783Z",
|
||||
"type": "deprecation",
|
||||
},
|
||||
Object {
|
||||
"component": "o.e.d.x.m.r.a.RestMonitoringBulkAction",
|
||||
"level": "WARN",
|
||||
"message": "[POST /_xpack/monitoring/_bulk] is deprecated! Use [POST /_monitoring/bulk] instead.",
|
||||
"node": "foobar",
|
||||
"timestamp": "2019-03-18T12:49:26.781Z",
|
||||
"type": "deprecation",
|
||||
},
|
||||
Object {
|
||||
"component": "o.e.c.r.a.DiskThresholdMonitor",
|
||||
"level": "WARN",
|
||||
"message": "high disk watermark [90%] exceeded on [-pH5RhfsRl6FDeTPwD5vEw][Elastic-MBP.local][/Users/chris/Development/repos/kibana/.es/8.0.0/data/nodes/0] free: 29.5gb[6.3%], shards will be relocated away from this node",
|
||||
"node": "foobar2",
|
||||
"timestamp": "2019-03-18T12:49:24.414Z",
|
||||
"type": "server",
|
||||
},
|
||||
Object {
|
||||
"component": "o.e.c.r.a.DiskThresholdMonitor",
|
||||
"level": "INFO",
|
||||
"message": "rerouting shards: [high disk watermark exceeded on one or more nodes]",
|
||||
"node": "foobar",
|
||||
"timestamp": "2019-03-18T12:49:24.414Z",
|
||||
"type": "server",
|
||||
},
|
||||
Object {
|
||||
"component": "o.e.d.x.m.r.a.RestMonitoringBulkAction",
|
||||
"level": "WARN",
|
||||
"message": "[POST /_xpack/monitoring/_bulk] is deprecated! Use [POST /_monitoring/bulk] instead.",
|
||||
"node": "foobar",
|
||||
"timestamp": "2019-03-18T12:49:11.776Z",
|
||||
"type": "deprecation",
|
||||
},
|
||||
Object {
|
||||
"component": "o.e.d.x.m.r.a.RestMonitoringBulkAction",
|
||||
"level": "WARN",
|
||||
"message": "[POST /_xpack/monitoring/_bulk] is deprecated! Use [POST /_monitoring/bulk] instead.",
|
||||
"node": "foobar",
|
||||
"timestamp": "2019-03-18T12:49:08.770Z",
|
||||
"type": "deprecation",
|
||||
},
|
||||
Object {
|
||||
"component": "o.e.c.r.a.DiskThresholdMonitor",
|
||||
"level": "WARN",
|
||||
"message": "high disk watermark [90%] exceeded on [-pH5RhfsRl6FDeTPwD5vEw][Elastic-MBP.local][/Users/chris/Development/repos/kibana/.es/8.0.0/data/nodes/0] free: 29.3gb[6.2%], shards will be relocated away from this node",
|
||||
"node": "foobar",
|
||||
"timestamp": "2019-03-18T12:48:59.409Z",
|
||||
"type": "server",
|
||||
},
|
||||
Object {
|
||||
"component": "o.e.d.x.m.r.a.RestMonitoringBulkAction",
|
||||
"level": "WARN",
|
||||
"message": "[POST /_xpack/monitoring/_bulk] is deprecated! Use [POST /_monitoring/bulk] instead.",
|
||||
"node": "foobar",
|
||||
"timestamp": "2019-03-18T12:48:53.753Z",
|
||||
"type": "deprecation",
|
||||
},
|
||||
Object {
|
||||
"component": "o.e.d.x.m.r.a.RestMonitoringBulkAction",
|
||||
"level": "WARN",
|
||||
"message": "[POST /_xpack/monitoring/_bulk] is deprecated! Use [POST /_monitoring/bulk] instead.",
|
||||
"node": "foobar",
|
||||
"timestamp": "2019-03-18T12:48:53.753Z",
|
||||
"type": "deprecation",
|
||||
},
|
||||
Object {
|
||||
"component": "o.e.d.x.m.r.a.RestMonitoringBulkAction",
|
||||
"level": "WARN",
|
||||
"message": "[POST /_xpack/monitoring/_bulk] is deprecated! Use [POST /_monitoring/bulk] instead.",
|
||||
"node": "foobar2",
|
||||
"timestamp": "2019-03-18T12:48:46.745Z",
|
||||
"type": "deprecation",
|
||||
},
|
||||
]
|
||||
}
|
||||
noItemsMessage="No items found"
|
||||
responsive={true}
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="m"
|
||||
/>
|
||||
<EuiCallOut
|
||||
color="primary"
|
||||
iconType="loggingApp"
|
||||
size="m"
|
||||
title="Want to see more logs?"
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="Visit {link} to dive deeper."
|
||||
id="xpack.monitoring.logs.listing.linkText"
|
||||
values={
|
||||
Object {
|
||||
"link": <EuiLink
|
||||
color="primary"
|
||||
href="/app/infra#/link-to/logs"
|
||||
type="button"
|
||||
>
|
||||
Logs
|
||||
</EuiLink>,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
</div>
|
||||
`;
|
170
x-pack/plugins/monitoring/public/components/logs/__snapshots__/reason.test.js.snap
generated
Normal file
170
x-pack/plugins/monitoring/public/components/logs/__snapshots__/reason.test.js.snap
generated
Normal file
|
@ -0,0 +1,170 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Logs should render with a no cluster found reason 1`] = `
|
||||
<EuiCallOut
|
||||
color="warning"
|
||||
iconType="help"
|
||||
size="m"
|
||||
title="No logs for this cluster"
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="Check that your {link} is correct."
|
||||
id="xpack.monitoring.logs.reason.noClusterMessage"
|
||||
values={
|
||||
Object {
|
||||
"link": <EuiLink
|
||||
color="primary"
|
||||
href="https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-installation.html"
|
||||
type="button"
|
||||
>
|
||||
setup
|
||||
</EuiLink>,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
`;
|
||||
|
||||
exports[`Logs should render with a no index found reason 1`] = `
|
||||
<EuiCallOut
|
||||
color="warning"
|
||||
iconType="help"
|
||||
size="m"
|
||||
title="No logs for this index"
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="We found logs, but none for this index. If this problem continues, check that your {link} is correct."
|
||||
id="xpack.monitoring.logs.reason.noIndexMessage"
|
||||
values={
|
||||
Object {
|
||||
"link": <EuiLink
|
||||
color="primary"
|
||||
href="https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-installation.html"
|
||||
type="button"
|
||||
>
|
||||
setup
|
||||
</EuiLink>,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
`;
|
||||
|
||||
exports[`Logs should render with a no index pattern found reason 1`] = `
|
||||
<EuiCallOut
|
||||
color="warning"
|
||||
iconType="help"
|
||||
size="m"
|
||||
title="No log data found"
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="Set up {link}, then configure your Elasticsearch output to your monitoring cluster."
|
||||
id="xpack.monitoring.logs.reason.noIndexPatternMessage"
|
||||
values={
|
||||
Object {
|
||||
"link": <EuiLink
|
||||
color="primary"
|
||||
href="https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-installation.html"
|
||||
type="button"
|
||||
>
|
||||
Filebeat
|
||||
</EuiLink>,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
`;
|
||||
|
||||
exports[`Logs should render with a no node found reason 1`] = `
|
||||
<EuiCallOut
|
||||
color="warning"
|
||||
iconType="help"
|
||||
size="m"
|
||||
title="No logs for this Elasticsearch node"
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="Check that your {link} is correct."
|
||||
id="xpack.monitoring.logs.reason.noNodeMessage"
|
||||
values={
|
||||
Object {
|
||||
"link": <EuiLink
|
||||
color="primary"
|
||||
href="https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-installation.html"
|
||||
type="button"
|
||||
>
|
||||
setup
|
||||
</EuiLink>,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
`;
|
||||
|
||||
exports[`Logs should render with a no type found reason 1`] = `
|
||||
<EuiCallOut
|
||||
color="warning"
|
||||
iconType="help"
|
||||
size="m"
|
||||
title="No logs for Elasticsearch"
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="Follow {link} to set up Elasticsearch."
|
||||
id="xpack.monitoring.logs.reason.noTypeMessage"
|
||||
values={
|
||||
Object {
|
||||
"link": <EuiLink
|
||||
color="primary"
|
||||
href="https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-elasticsearch.html"
|
||||
type="button"
|
||||
>
|
||||
these directions
|
||||
</EuiLink>,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
`;
|
||||
|
||||
exports[`Logs should render with a time period reason 1`] = `
|
||||
<EuiCallOut
|
||||
color="warning"
|
||||
iconType="help"
|
||||
size="m"
|
||||
title="No logs for the selected time"
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="Use the time filter to adjust your timeframe."
|
||||
id="xpack.monitoring.logs.reason.noIndexPatternInTimePeriodMessage"
|
||||
values={Object {}}
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
`;
|
||||
|
||||
exports[`Logs should render with a time period reason for both scenarios 1`] = `
|
||||
<EuiCallOut
|
||||
color="warning"
|
||||
iconType="help"
|
||||
size="m"
|
||||
title="No logs for the selected time"
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="Use the time filter to adjust your timeframe."
|
||||
id="xpack.monitoring.logs.reason.noIndexPatternInTimePeriodMessage"
|
||||
values={Object {}}
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
`;
|
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { Logs } from './logs';
|
242
x-pack/plugins/monitoring/public/components/logs/logs.js
Normal file
242
x-pack/plugins/monitoring/public/components/logs/logs.js
Normal file
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
* 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, { PureComponent } from 'react';
|
||||
import { capitalize } from 'lodash';
|
||||
import chrome from 'ui/chrome';
|
||||
import {
|
||||
EuiBasicTable,
|
||||
EuiTitle,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
EuiCallOut,
|
||||
EuiLink,
|
||||
} from '@elastic/eui';
|
||||
import { formatDateTimeLocal } from '../../../common/formatting';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { Reason } from './reason';
|
||||
|
||||
const columnTimestampTitle = i18n.translate('xpack.monitoring.logs.listing.timestampTitle', {
|
||||
defaultMessage: 'Timestamp'
|
||||
});
|
||||
|
||||
const columnLevelTitle = i18n.translate('xpack.monitoring.logs.listing.levelTitle', {
|
||||
defaultMessage: 'Level'
|
||||
});
|
||||
|
||||
const columnTypeTitle = i18n.translate('xpack.monitoring.logs.listing.typeTitle', {
|
||||
defaultMessage: 'Type'
|
||||
});
|
||||
|
||||
const columnMessageTitle = i18n.translate('xpack.monitoring.logs.listing.messageTitle', {
|
||||
defaultMessage: 'Message'
|
||||
});
|
||||
|
||||
const columnComponentTitle = i18n.translate('xpack.monitoring.logs.listing.componentTitle', {
|
||||
defaultMessage: 'Component'
|
||||
});
|
||||
|
||||
const columnNodeTitle = i18n.translate('xpack.monitoring.logs.listing.nodeTitle', {
|
||||
defaultMessage: 'Node'
|
||||
});
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: 'timestamp',
|
||||
name: columnTimestampTitle,
|
||||
width: '12%',
|
||||
render: timestamp => formatDateTimeLocal(timestamp),
|
||||
},
|
||||
{
|
||||
field: 'level',
|
||||
name: columnLevelTitle,
|
||||
width: '5%',
|
||||
},
|
||||
{
|
||||
field: 'type',
|
||||
name: columnTypeTitle,
|
||||
width: '10%',
|
||||
render: type => capitalize(type),
|
||||
},
|
||||
{
|
||||
field: 'message',
|
||||
name: columnMessageTitle,
|
||||
width: '55%'
|
||||
},
|
||||
{
|
||||
field: 'component',
|
||||
name: columnComponentTitle,
|
||||
width: '18%'
|
||||
},
|
||||
];
|
||||
|
||||
const clusterColumns = [
|
||||
{
|
||||
field: 'timestamp',
|
||||
name: columnTimestampTitle,
|
||||
width: '12%',
|
||||
render: timestamp => formatDateTimeLocal(timestamp),
|
||||
},
|
||||
{
|
||||
field: 'level',
|
||||
name: columnLevelTitle,
|
||||
width: '5%',
|
||||
},
|
||||
{
|
||||
field: 'type',
|
||||
name: columnTypeTitle,
|
||||
width: '10%',
|
||||
render: type => capitalize(type),
|
||||
},
|
||||
{
|
||||
field: 'message',
|
||||
name: columnMessageTitle,
|
||||
width: '45%'
|
||||
},
|
||||
{
|
||||
field: 'component',
|
||||
name: columnComponentTitle,
|
||||
width: '15%'
|
||||
},
|
||||
{
|
||||
field: 'node',
|
||||
name: columnNodeTitle,
|
||||
width: '13%'
|
||||
},
|
||||
];
|
||||
|
||||
function getLogsUiLink(clusterUuid, nodeId, indexUuid) {
|
||||
const base = `${chrome.getBasePath()}/app/infra#/link-to/logs`;
|
||||
|
||||
const params = [];
|
||||
if (clusterUuid) {
|
||||
params.push(`elasticsearch.cluster.uuid:${clusterUuid}`);
|
||||
}
|
||||
if (nodeId) {
|
||||
params.push(`elasticsearch.node.id:${nodeId}`);
|
||||
}
|
||||
if (indexUuid) {
|
||||
params.push(`elasticsearch.index.name:${indexUuid}`);
|
||||
}
|
||||
|
||||
if (params.length === 0) {
|
||||
return base;
|
||||
}
|
||||
|
||||
return `${base}?filter=${params.join(' and ')}`;
|
||||
}
|
||||
|
||||
export class Logs extends PureComponent {
|
||||
renderLogs() {
|
||||
const { logs: { enabled, logs }, nodeId, indexUuid } = this.props;
|
||||
if (!enabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiBasicTable
|
||||
items={logs || []}
|
||||
columns={nodeId || indexUuid ? columns : clusterColumns}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderNoLogs() {
|
||||
const { logs: { enabled, reason } } = this.props;
|
||||
if (enabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <Reason reason={reason}/>;
|
||||
}
|
||||
|
||||
renderCallout() {
|
||||
const { logs: { enabled }, nodeId, clusterUuid, indexUuid } = this.props;
|
||||
|
||||
if (!enabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiCallOut
|
||||
size="m"
|
||||
title={i18n.translate('xpack.monitoring.logs.listing.calloutTitle', {
|
||||
defaultMessage: 'Want to see more logs?'
|
||||
})}
|
||||
iconType="loggingApp"
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.logs.listing.linkText"
|
||||
defaultMessage="Visit {link} to dive deeper."
|
||||
values={{
|
||||
link: (
|
||||
<EuiLink href={getLogsUiLink(clusterUuid, nodeId, indexUuid)}>
|
||||
{i18n.translate('xpack.monitoring.logs.listing.calloutLinkText', {
|
||||
defaultMessage: 'Logs'
|
||||
})}
|
||||
</EuiLink>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { nodeId, indexUuid, logs: { limit } } = this.props;
|
||||
|
||||
let description;
|
||||
|
||||
if (nodeId) {
|
||||
description = i18n.translate('xpack.monitoring.logs.listing.nodePageDescription', {
|
||||
defaultMessage: 'Showing the most recent logs for this node, up to {limit} total logs.',
|
||||
values: {
|
||||
limit,
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (indexUuid) {
|
||||
description = i18n.translate('xpack.monitoring.logs.listing.indexPageDescription', {
|
||||
defaultMessage: 'Showing the most recent logs for this index, up to {limit} total logs.',
|
||||
values: {
|
||||
limit,
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
description = i18n.translate('xpack.monitoring.logs.listing.clusterPageDescription', {
|
||||
defaultMessage: 'Showing the most recent logs for this cluster, up to {limit} total logs.',
|
||||
values: {
|
||||
limit,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<EuiTitle>
|
||||
<h1>
|
||||
{i18n.translate('xpack.monitoring.logs.listing.pageTitle', {
|
||||
defaultMessage: 'Recent Logs'
|
||||
})}
|
||||
</h1>
|
||||
</EuiTitle>
|
||||
<EuiText size="s">
|
||||
<p>
|
||||
{description}
|
||||
</p>
|
||||
</EuiText>
|
||||
<EuiSpacer size="m"/>
|
||||
{this.renderLogs()}
|
||||
{this.renderNoLogs()}
|
||||
<EuiSpacer size="m"/>
|
||||
{this.renderCallout()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
125
x-pack/plugins/monitoring/public/components/logs/logs.test.js
Normal file
125
x-pack/plugins/monitoring/public/components/logs/logs.test.js
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* 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 { shallow } from 'enzyme';
|
||||
import { Logs } from './logs';
|
||||
|
||||
jest.mock('ui/chrome', () => {
|
||||
return {
|
||||
getBasePath: () => ''
|
||||
};
|
||||
});
|
||||
|
||||
const logs = {
|
||||
enabled: true,
|
||||
limit: 10,
|
||||
logs: [
|
||||
{
|
||||
'timestamp': '2019-03-18T12:49:33.783Z',
|
||||
'component': 'o.e.d.x.m.r.a.RestMonitoringBulkAction',
|
||||
'level': 'WARN',
|
||||
'type': 'deprecation',
|
||||
'node': 'foobar',
|
||||
'message': '[POST /_xpack/monitoring/_bulk] is deprecated! Use [POST /_monitoring/bulk] instead.'
|
||||
}, {
|
||||
'timestamp': '2019-03-18T12:49:26.781Z',
|
||||
'component': 'o.e.d.x.m.r.a.RestMonitoringBulkAction',
|
||||
'level': 'WARN',
|
||||
'type': 'deprecation',
|
||||
'node': 'foobar',
|
||||
'message': '[POST /_xpack/monitoring/_bulk] is deprecated! Use [POST /_monitoring/bulk] instead.'
|
||||
}, {
|
||||
'timestamp': '2019-03-18T12:49:24.414Z',
|
||||
'component': 'o.e.c.r.a.DiskThresholdMonitor',
|
||||
'level': 'WARN',
|
||||
'type': 'server',
|
||||
'node': 'foobar2',
|
||||
'message': 'high disk watermark [90%] exceeded on [-pH5RhfsRl6FDeTPwD5vEw][Elastic-MBP.local][/Users/chris/Development/repos/kibana/.es/8.0.0/data/nodes/0] free: 29.5gb[6.3%], shards will be relocated away from this node' // eslint-disable-line max-len
|
||||
}, {
|
||||
'timestamp': '2019-03-18T12:49:24.414Z',
|
||||
'component': 'o.e.c.r.a.DiskThresholdMonitor',
|
||||
'level': 'INFO',
|
||||
'type': 'server',
|
||||
'node': 'foobar',
|
||||
'message': 'rerouting shards: [high disk watermark exceeded on one or more nodes]'
|
||||
}, {
|
||||
'timestamp': '2019-03-18T12:49:11.776Z',
|
||||
'component': 'o.e.d.x.m.r.a.RestMonitoringBulkAction',
|
||||
'level': 'WARN',
|
||||
'type': 'deprecation',
|
||||
'node': 'foobar',
|
||||
'message': '[POST /_xpack/monitoring/_bulk] is deprecated! Use [POST /_monitoring/bulk] instead.'
|
||||
}, {
|
||||
'timestamp': '2019-03-18T12:49:08.770Z',
|
||||
'component': 'o.e.d.x.m.r.a.RestMonitoringBulkAction',
|
||||
'level': 'WARN',
|
||||
'type': 'deprecation',
|
||||
'node': 'foobar',
|
||||
'message': '[POST /_xpack/monitoring/_bulk] is deprecated! Use [POST /_monitoring/bulk] instead.'
|
||||
}, {
|
||||
'timestamp': '2019-03-18T12:48:59.409Z',
|
||||
'component': 'o.e.c.r.a.DiskThresholdMonitor',
|
||||
'level': 'WARN',
|
||||
'type': 'server',
|
||||
'node': 'foobar',
|
||||
'message': 'high disk watermark [90%] exceeded on [-pH5RhfsRl6FDeTPwD5vEw][Elastic-MBP.local][/Users/chris/Development/repos/kibana/.es/8.0.0/data/nodes/0] free: 29.3gb[6.2%], shards will be relocated away from this node' // eslint-disable-line max-len
|
||||
}, {
|
||||
'timestamp': '2019-03-18T12:48:53.753Z',
|
||||
'component': 'o.e.d.x.m.r.a.RestMonitoringBulkAction',
|
||||
'level': 'WARN',
|
||||
'type': 'deprecation',
|
||||
'node': 'foobar',
|
||||
'message': '[POST /_xpack/monitoring/_bulk] is deprecated! Use [POST /_monitoring/bulk] instead.'
|
||||
}, {
|
||||
'timestamp': '2019-03-18T12:48:53.753Z',
|
||||
'component': 'o.e.d.x.m.r.a.RestMonitoringBulkAction',
|
||||
'level': 'WARN',
|
||||
'type': 'deprecation',
|
||||
'node': 'foobar',
|
||||
'message': '[POST /_xpack/monitoring/_bulk] is deprecated! Use [POST /_monitoring/bulk] instead.'
|
||||
}, {
|
||||
'timestamp': '2019-03-18T12:48:46.745Z',
|
||||
'component': 'o.e.d.x.m.r.a.RestMonitoringBulkAction',
|
||||
'level': 'WARN',
|
||||
'type': 'deprecation',
|
||||
'node': 'foobar2',
|
||||
'message': '[POST /_xpack/monitoring/_bulk] is deprecated! Use [POST /_monitoring/bulk] instead.'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
describe('Logs', () => {
|
||||
it('should render normally', () => {
|
||||
const component = shallow(<Logs logs={logs}/>);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render fewer columns for node or index view', () => {
|
||||
const component = shallow(<Logs logs={logs} nodeId="12345"/>);
|
||||
expect(component.find('EuiBasicTable').prop('columns')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render a link to filter by cluster uuid', () => {
|
||||
const component = shallow(<Logs logs={logs} clusterUuid="12345"/>);
|
||||
expect(component.find('EuiCallOut')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render a link to filter by cluster uuid and node uuid', () => {
|
||||
const component = shallow(<Logs logs={logs} clusterUuid="12345" nodeId="6789"/>);
|
||||
expect(component.find('EuiCallOut')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render a link to filter by cluster uuid and index uuid', () => {
|
||||
const component = shallow(<Logs logs={logs} clusterUuid="12345" indexUuid="6789"/>);
|
||||
expect(component.find('EuiCallOut')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render a reason if the logs are disabled', () => {
|
||||
const component = shallow(<Logs logs={{ enabled: false, limit: 15, reason: {} }}/>);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
140
x-pack/plugins/monitoring/public/components/logs/reason.js
Normal file
140
x-pack/plugins/monitoring/public/components/logs/reason.js
Normal file
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* 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 {
|
||||
EuiCallOut,
|
||||
EuiLink
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
export const Reason = ({ reason }) => {
|
||||
let title;
|
||||
let message;
|
||||
|
||||
if (false === reason.indexPatternExists) {
|
||||
title = i18n.translate('xpack.monitoring.logs.reason.noIndexPatternTitle', {
|
||||
defaultMessage: 'No log data found'
|
||||
});
|
||||
message = (
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.logs.reason.noIndexPatternMessage"
|
||||
defaultMessage="Set up {link}, then configure your Elasticsearch output to your monitoring cluster."
|
||||
values={{
|
||||
link: (
|
||||
<EuiLink href="https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-installation.html">
|
||||
{i18n.translate('xpack.monitoring.logs.reason.noIndexPatternLink', {
|
||||
defaultMessage: 'Filebeat'
|
||||
})}
|
||||
</EuiLink>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
else if (false === reason.indexPatternInTimeRangeExists || (false === reason.typeExists && reason.typeExistsAtAnyTime)) {
|
||||
title = i18n.translate('xpack.monitoring.logs.reason.noIndexPatternInTimePeriodTitle', {
|
||||
defaultMessage: 'No logs for the selected time'
|
||||
});
|
||||
message = (
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.logs.reason.noIndexPatternInTimePeriodMessage"
|
||||
defaultMessage="Use the time filter to adjust your timeframe."
|
||||
/>
|
||||
);
|
||||
}
|
||||
else if (false === reason.typeExists) {
|
||||
title = i18n.translate('xpack.monitoring.logs.reason.noTypeTitle', {
|
||||
defaultMessage: 'No logs for Elasticsearch'
|
||||
});
|
||||
message = (
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.logs.reason.noTypeMessage"
|
||||
defaultMessage="Follow {link} to set up Elasticsearch."
|
||||
values={{
|
||||
link: (
|
||||
<EuiLink href="https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-elasticsearch.html">
|
||||
{i18n.translate('xpack.monitoring.logs.reason.noTypeLink', {
|
||||
defaultMessage: 'these directions'
|
||||
})}
|
||||
</EuiLink>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
else if (false === reason.clusterExists) {
|
||||
title = i18n.translate('xpack.monitoring.logs.reason.noClusterTitle', {
|
||||
defaultMessage: 'No logs for this cluster'
|
||||
});
|
||||
message = (
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.logs.reason.noClusterMessage"
|
||||
defaultMessage="Check that your {link} is correct."
|
||||
values={{
|
||||
link: (
|
||||
<EuiLink href="https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-installation.html">
|
||||
{i18n.translate('xpack.monitoring.logs.reason.noClusterLink', {
|
||||
defaultMessage: 'setup'
|
||||
})}
|
||||
</EuiLink>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
else if (false === reason.nodeExists) {
|
||||
title = i18n.translate('xpack.monitoring.logs.reason.noNodeTitle', {
|
||||
defaultMessage: 'No logs for this Elasticsearch node'
|
||||
});
|
||||
message = (
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.logs.reason.noNodeMessage"
|
||||
defaultMessage="Check that your {link} is correct."
|
||||
values={{
|
||||
link: (
|
||||
<EuiLink href="https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-installation.html">
|
||||
{i18n.translate('xpack.monitoring.logs.reason.noNodeLink', {
|
||||
defaultMessage: 'setup'
|
||||
})}
|
||||
</EuiLink>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
else if (false === reason.indexExists) {
|
||||
title = i18n.translate('xpack.monitoring.logs.reason.noIndexTitle', {
|
||||
defaultMessage: 'No logs for this index'
|
||||
});
|
||||
message = (
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.logs.reason.noIndexMessage"
|
||||
defaultMessage="We found logs, but none for this index. If this problem continues, check that your {link} is correct."
|
||||
values={{
|
||||
link: (
|
||||
<EuiLink href="https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-installation.html">
|
||||
{i18n.translate('xpack.monitoring.logs.reason.noNodeLink', {
|
||||
defaultMessage: 'setup'
|
||||
})}
|
||||
</EuiLink>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiCallOut
|
||||
title={title}
|
||||
color="warning"
|
||||
iconType="help"
|
||||
>
|
||||
<p>{message}</p>
|
||||
</EuiCallOut>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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 { shallow } from 'enzyme';
|
||||
import { Reason } from './reason';
|
||||
|
||||
describe('Logs', () => {
|
||||
it('should render with a no index pattern found reason', () => {
|
||||
const component = shallow(<Reason reason={{ indexPatternExists: false }}/>);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render with a no type found reason', () => {
|
||||
const component = shallow(<Reason reason={{ indexPatternExists: true, typeExists: false }}/>);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render with a no cluster found reason', () => {
|
||||
const component = shallow(<Reason reason={{ indexPatternExists: true, typeExists: true, clusterExists: false }}/>);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render with a no node found reason', () => {
|
||||
const component = shallow(<Reason reason={{ indexPatternExists: true, typeExists: true, clusterExists: true, nodeExists: false }}/>);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render with a time period reason', () => {
|
||||
const reason = {
|
||||
indexPatternExists: true,
|
||||
indexPatternInTimeRangeExists: false,
|
||||
};
|
||||
const component = shallow(<Reason reason={reason}/>);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render with a time period reason for both scenarios', () => {
|
||||
const reason = {
|
||||
indexPatternExists: true,
|
||||
indexPatternInTimeRangeExists: true,
|
||||
clusterExists: true,
|
||||
typeExists: false,
|
||||
typeExistsAtAnyTime: true
|
||||
};
|
||||
const component = shallow(<Reason reason={reason}/>);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render with a no index found reason', () => {
|
||||
const component = shallow(<Reason reason={{
|
||||
indexPatternExists: true,
|
||||
typeExists: true,
|
||||
clusterExists: true,
|
||||
nodeExists: null,
|
||||
indexExists: false
|
||||
}}
|
||||
/>);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -90,12 +90,15 @@ uiRoutes.when('/elasticsearch/indices/:index', {
|
|||
$scope.labels = labels.index;
|
||||
}
|
||||
|
||||
|
||||
this.renderReact(
|
||||
<I18nContext>
|
||||
<Index
|
||||
scope={$scope}
|
||||
kbnUrl={kbnUrl}
|
||||
onBrush={this.onBrush}
|
||||
indexUuid={this.indexName}
|
||||
clusterUuid={$scope.cluster.cluster_uuid}
|
||||
{...data}
|
||||
/>
|
||||
</I18nContext>
|
||||
|
|
|
@ -79,6 +79,8 @@ uiRoutes.when('/elasticsearch/nodes/:node', {
|
|||
<Node
|
||||
scope={$scope}
|
||||
kbnUrl={kbnUrl}
|
||||
nodeId={this.nodeName}
|
||||
clusterUuid={$scope.cluster.cluster_uuid}
|
||||
onBrush={this.onBrush}
|
||||
{...data}
|
||||
/>
|
||||
|
|
|
@ -46,7 +46,7 @@ export class ElasticsearchOverviewController extends MonitoringViewBaseControlle
|
|||
|
||||
initScope($scope) {
|
||||
$scope.$watch(() => this.data, data => {
|
||||
this.renderReact(data);
|
||||
this.renderReact(data, $scope.cluster);
|
||||
});
|
||||
|
||||
// HACK to force table to re-render even if data hasn't changed. This
|
||||
|
@ -56,8 +56,8 @@ export class ElasticsearchOverviewController extends MonitoringViewBaseControlle
|
|||
const { data } = this;
|
||||
const dataWithShardActivityLoading = { ...data, shardActivity: null };
|
||||
// force shard activity to rerender by manipulating and then re-setting its data prop
|
||||
this.renderReact(dataWithShardActivityLoading);
|
||||
this.renderReact(data);
|
||||
this.renderReact(dataWithShardActivityLoading, $scope.cluster);
|
||||
this.renderReact(data, $scope.cluster);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -67,15 +67,17 @@ export class ElasticsearchOverviewController extends MonitoringViewBaseControlle
|
|||
});
|
||||
}
|
||||
|
||||
renderReact(data) {
|
||||
renderReact(data, cluster) {
|
||||
// All data needs to originate in this view, and get passed as a prop to the components, for statelessness
|
||||
const { clusterStatus, metrics, shardActivity } = data;
|
||||
const { clusterStatus, metrics, shardActivity, logs } = data;
|
||||
const shardActivityData = shardActivity && this.filterShardActivityData(shardActivity); // no filter on data = null
|
||||
const component = (
|
||||
<I18nContext>
|
||||
<ElasticsearchOverview
|
||||
clusterStatus={clusterStatus}
|
||||
metrics={metrics}
|
||||
logs={logs}
|
||||
cluster={cluster}
|
||||
shardActivity={shardActivityData}
|
||||
onBrush={this.onBrush}
|
||||
showShardActivityHistory={this.showShardActivityHistory}
|
||||
|
|
|
@ -73,6 +73,7 @@ Array [
|
|||
},
|
||||
"status": "green",
|
||||
},
|
||||
"logs": undefined,
|
||||
},
|
||||
"isCcrEnabled": undefined,
|
||||
"isPrimary": true,
|
||||
|
@ -176,6 +177,7 @@ Array [
|
|||
},
|
||||
"status": "green",
|
||||
},
|
||||
"logs": undefined,
|
||||
},
|
||||
"isCcrEnabled": undefined,
|
||||
"isPrimary": false,
|
||||
|
@ -284,6 +286,7 @@ Array [
|
|||
},
|
||||
"status": "green",
|
||||
},
|
||||
"logs": undefined,
|
||||
},
|
||||
"isCcrEnabled": undefined,
|
||||
"isPrimary": false,
|
||||
|
@ -387,6 +390,7 @@ Array [
|
|||
},
|
||||
"status": "green",
|
||||
},
|
||||
"logs": undefined,
|
||||
},
|
||||
"isCcrEnabled": undefined,
|
||||
"isPrimary": false,
|
||||
|
|
|
@ -22,6 +22,7 @@ import { getApmsForClusters } from '../apm/get_apms_for_clusters';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { checkCcrEnabled } from '../elasticsearch/ccr';
|
||||
import { getStandaloneClusterDefinition, hasStandaloneClusters } from '../standalone_clusters';
|
||||
import { getLogTypes } from '../logs';
|
||||
|
||||
/**
|
||||
* Get all clusters or the cluster associated with {@code clusterUuid} when it is defined.
|
||||
|
@ -33,7 +34,8 @@ export async function getClustersFromRequest(req, indexPatterns, { clusterUuid,
|
|||
lsIndexPattern,
|
||||
beatsIndexPattern,
|
||||
apmIndexPattern,
|
||||
alertsIndex
|
||||
alertsIndex,
|
||||
filebeatIndexPattern
|
||||
} = indexPatterns;
|
||||
|
||||
const isStandaloneCluster = clusterUuid === STANDALONE_CLUSTER_CLUSTER_UUID;
|
||||
|
@ -86,6 +88,8 @@ export async function getClustersFromRequest(req, indexPatterns, { clusterUuid,
|
|||
if (alerts) {
|
||||
cluster.alerts = alerts;
|
||||
}
|
||||
|
||||
cluster.logs = await getLogTypes(req, filebeatIndexPattern, { clusterUuid: cluster.cluster_uuid, start, end });
|
||||
} else if (!isStandaloneCluster) {
|
||||
// get all clusters
|
||||
if (!clusters || clusters.length === 0) {
|
||||
|
|
|
@ -23,6 +23,7 @@ export function getClustersSummary(clusters, kibanaUuid, isCcrEnabled) {
|
|||
alerts,
|
||||
ccs,
|
||||
cluster_settings: clusterSettings,
|
||||
logs,
|
||||
} = cluster;
|
||||
|
||||
const clusterName = get(clusterSettings, 'cluster.metadata.display_name', cluster.cluster_name);
|
||||
|
@ -64,7 +65,8 @@ export function getClustersSummary(clusters, kibanaUuid, isCcrEnabled) {
|
|||
indices,
|
||||
nodes,
|
||||
status
|
||||
}
|
||||
},
|
||||
logs
|
||||
},
|
||||
logstash,
|
||||
kibana: omit(kibana, 'uuids'),
|
||||
|
@ -78,7 +80,7 @@ export function getClustersSummary(clusters, kibanaUuid, isCcrEnabled) {
|
|||
status,
|
||||
kibana && kibana.status || null
|
||||
]),
|
||||
isCcrEnabled
|
||||
isCcrEnabled,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
|
@ -10,6 +10,32 @@ import moment from 'moment';
|
|||
import { standaloneClusterFilter } from './standalone_clusters';
|
||||
import { STANDALONE_CLUSTER_CLUSTER_UUID } from '../../common/constants';
|
||||
|
||||
export function createTimeFilter(options) {
|
||||
const { start, end } = options;
|
||||
if (!start && !end) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const timestampField = get(options, 'metric.timestampField');
|
||||
if (!timestampField) {
|
||||
throw new MissingRequiredError('metric.timestampField');
|
||||
}
|
||||
const timeRangeFilter = {
|
||||
range: {
|
||||
[timestampField]: {
|
||||
format: 'epoch_millis'
|
||||
}
|
||||
}
|
||||
};
|
||||
if (start) {
|
||||
timeRangeFilter.range[timestampField].gte = moment.utc(start).valueOf();
|
||||
}
|
||||
if (end) {
|
||||
timeRangeFilter.range[timestampField].lte = moment.utc(end).valueOf();
|
||||
}
|
||||
return timeRangeFilter;
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates the boilerplace for querying monitoring data, including filling in
|
||||
* document UUIDs, start time and end time, and injecting additional filters.
|
||||
|
@ -25,7 +51,7 @@ import { STANDALONE_CLUSTER_CLUSTER_UUID } from '../../common/constants';
|
|||
*/
|
||||
export function createQuery(options) {
|
||||
options = defaults(options, { filters: [] });
|
||||
const { type, clusterUuid, uuid, start, end, filters } = options;
|
||||
const { type, clusterUuid, uuid, filters } = options;
|
||||
|
||||
const isFromStandaloneCluster = clusterUuid === STANDALONE_CLUSTER_CLUSTER_UUID;
|
||||
|
||||
|
@ -53,22 +79,10 @@ export function createQuery(options) {
|
|||
if (!timestampField) {
|
||||
throw new MissingRequiredError('metric.timestampField');
|
||||
}
|
||||
const timeRangeFilter = {
|
||||
range: {
|
||||
[timestampField]: {
|
||||
format: 'epoch_millis'
|
||||
}
|
||||
}
|
||||
};
|
||||
if (start) {
|
||||
timeRangeFilter.range[timestampField].gte = moment.utc(start).valueOf();
|
||||
}
|
||||
if (end) {
|
||||
timeRangeFilter.range[timestampField].lte = moment.utc(end).valueOf();
|
||||
}
|
||||
const timeRangeFilter = createTimeFilter(options);
|
||||
|
||||
const combinedFilters = [typeFilter, clusterUuidFilter, uuidFilter, ...filters];
|
||||
if (end || start) {
|
||||
if (timeRangeFilter) {
|
||||
combinedFilters.push(timeRangeFilter);
|
||||
}
|
||||
|
||||
|
|
152
x-pack/plugins/monitoring/server/lib/logs/detect_reason.js
Normal file
152
x-pack/plugins/monitoring/server/lib/logs/detect_reason.js
Normal file
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* 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 { createTimeFilter } from '../create_query';
|
||||
import { get } from 'lodash';
|
||||
|
||||
|
||||
async function doesFilebeatIndexExist(req, filebeatIndexPattern, { start, end, clusterUuid, nodeUuid, indexUuid }) {
|
||||
const metric = { timestampField: '@timestamp' };
|
||||
const filter = [
|
||||
createTimeFilter({ start, end, metric })
|
||||
];
|
||||
|
||||
const typeFilter = { term: { 'service.type': 'elasticsearch' } };
|
||||
const clusterFilter = { term: { 'elasticsearch.cluster.uuid': clusterUuid } };
|
||||
const nodeFilter = { term: { 'elasticsearch.node.id': nodeUuid } };
|
||||
const indexFilter = { term: { 'elasticsearch.index.name': indexUuid } };
|
||||
|
||||
const indexPatternExistsQuery = {
|
||||
query: {
|
||||
bool: {
|
||||
filter,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const typeExistsAtAnyTimeQuery = {
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
typeFilter,
|
||||
]
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const typeExistsQuery = {
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
...filter,
|
||||
typeFilter,
|
||||
]
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const clusterExistsQuery = {
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
...filter,
|
||||
typeFilter,
|
||||
clusterFilter
|
||||
]
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const nodeExistsQuery = {
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
...filter,
|
||||
typeFilter,
|
||||
clusterFilter,
|
||||
nodeFilter
|
||||
]
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const indexExistsQuery = {
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
...filter,
|
||||
typeFilter,
|
||||
clusterFilter,
|
||||
indexFilter
|
||||
]
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const defaultParams = {
|
||||
size: 0,
|
||||
terminate_after: 1,
|
||||
};
|
||||
|
||||
const body = [
|
||||
{ index: filebeatIndexPattern },
|
||||
{ ...defaultParams },
|
||||
{ index: filebeatIndexPattern },
|
||||
{ ...defaultParams, ...indexPatternExistsQuery },
|
||||
{ index: filebeatIndexPattern },
|
||||
{ ...defaultParams, ...typeExistsAtAnyTimeQuery },
|
||||
{ index: filebeatIndexPattern },
|
||||
{ ...defaultParams, ...typeExistsQuery },
|
||||
];
|
||||
|
||||
if (clusterUuid) {
|
||||
body.push(...[
|
||||
{ index: filebeatIndexPattern },
|
||||
{ ...defaultParams, ...clusterExistsQuery },
|
||||
]);
|
||||
}
|
||||
|
||||
if (nodeUuid) {
|
||||
body.push(...[
|
||||
{ index: filebeatIndexPattern },
|
||||
{ ...defaultParams, ...nodeExistsQuery },
|
||||
]);
|
||||
}
|
||||
|
||||
if (indexUuid) {
|
||||
body.push(...[
|
||||
{ index: filebeatIndexPattern },
|
||||
{ ...defaultParams, ...indexExistsQuery },
|
||||
]);
|
||||
}
|
||||
|
||||
const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('monitoring');
|
||||
const {
|
||||
responses: [
|
||||
indexPatternExistsResponse,
|
||||
indexPatternExistsInTimeRangeResponse,
|
||||
typeExistsAtAnyTimeResponse,
|
||||
typeExistsResponse,
|
||||
clusterExistsResponse,
|
||||
nodeExistsResponse,
|
||||
indexExistsResponse
|
||||
]
|
||||
} = await callWithRequest(req, 'msearch', { body });
|
||||
|
||||
return {
|
||||
indexPatternExists: get(indexPatternExistsResponse, 'hits.total.value', 0) > 0,
|
||||
indexPatternInTimeRangeExists: get(indexPatternExistsInTimeRangeResponse, 'hits.total.value', 0) > 0,
|
||||
typeExistsAtAnyTime: get(typeExistsAtAnyTimeResponse, 'hits.total.value', 0) > 0,
|
||||
typeExists: get(typeExistsResponse, 'hits.total.value', 0) > 0,
|
||||
clusterExists: clusterUuid ? get(clusterExistsResponse, 'hits.total.value', 0) > 0 : null,
|
||||
nodeExists: nodeUuid ? get(nodeExistsResponse, 'hits.total.value', 0) > 0 : null,
|
||||
indexExists: indexUuid ? get(indexExistsResponse, 'hits.total.value', 0) > 0 : null,
|
||||
};
|
||||
}
|
||||
|
||||
export async function detectReason(req, filebeatIndexPattern, opts) {
|
||||
return await doesFilebeatIndexExist(req, filebeatIndexPattern, opts);
|
||||
}
|
93
x-pack/plugins/monitoring/server/lib/logs/get_log_types.js
Normal file
93
x-pack/plugins/monitoring/server/lib/logs/get_log_types.js
Normal file
|
@ -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;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { get } from 'lodash';
|
||||
import { checkParam } from '../error_missing_required';
|
||||
import { createTimeFilter } from '../create_query';
|
||||
import { detectReason } from './detect_reason';
|
||||
|
||||
async function handleResponse(response, req, filebeatIndexPattern, { start, end }) {
|
||||
const result = {
|
||||
enabled: false,
|
||||
types: []
|
||||
};
|
||||
|
||||
const typeBuckets = get(response, 'aggregations.types.buckets', []);
|
||||
if (typeBuckets.length) {
|
||||
result.enabled = true;
|
||||
result.types = typeBuckets.map(typeBucket => {
|
||||
return {
|
||||
type: typeBucket.key.split('.')[1],
|
||||
levels: typeBucket.levels.buckets.map(levelBucket => {
|
||||
return {
|
||||
level: levelBucket.key.toLowerCase(),
|
||||
count: levelBucket.doc_count
|
||||
};
|
||||
})
|
||||
};
|
||||
});
|
||||
}
|
||||
else {
|
||||
result.reason = await detectReason(req, filebeatIndexPattern, { start, end });
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function getLogTypes(req, filebeatIndexPattern, { clusterUuid, nodeUuid, indexUuid, start, end }) {
|
||||
checkParam(filebeatIndexPattern, 'filebeatIndexPattern in logs/getLogTypes');
|
||||
|
||||
const metric = { timestampField: '@timestamp' };
|
||||
const filter = [
|
||||
{ term: { 'service.type': 'elasticsearch' } },
|
||||
createTimeFilter({ start, end, metric })
|
||||
];
|
||||
if (clusterUuid) {
|
||||
filter.push({ term: { 'elasticsearch.cluster.uuid': clusterUuid } });
|
||||
}
|
||||
if (nodeUuid) {
|
||||
filter.push({ term: { 'elasticsearch.node.id': nodeUuid } });
|
||||
}
|
||||
if (indexUuid) {
|
||||
filter.push({ term: { 'elasticsearch.index.name': indexUuid } });
|
||||
}
|
||||
|
||||
const params = {
|
||||
index: filebeatIndexPattern,
|
||||
size: 0,
|
||||
filterPath: [
|
||||
'aggregations.levels.buckets',
|
||||
'aggregations.types.buckets',
|
||||
],
|
||||
ignoreUnavailable: true,
|
||||
body: {
|
||||
sort: { '@timestamp': { order: 'desc' } },
|
||||
query: {
|
||||
bool: {
|
||||
filter,
|
||||
}
|
||||
},
|
||||
aggs: {
|
||||
types: {
|
||||
terms: {
|
||||
field: 'event.dataset'
|
||||
},
|
||||
aggs: {
|
||||
levels: {
|
||||
terms: {
|
||||
field: 'log.level'
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('monitoring');
|
||||
const response = await callWithRequest(req, 'search', params);
|
||||
return await handleResponse(response, req, filebeatIndexPattern, { start, end });
|
||||
}
|
91
x-pack/plugins/monitoring/server/lib/logs/get_logs.js
Normal file
91
x-pack/plugins/monitoring/server/lib/logs/get_logs.js
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* 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 { get } from 'lodash';
|
||||
import { checkParam } from '../error_missing_required';
|
||||
import { createTimeFilter } from '../create_query';
|
||||
import { detectReason } from './detect_reason';
|
||||
|
||||
async function handleResponse(response, req, filebeatIndexPattern, opts) {
|
||||
const result = {
|
||||
enabled: false,
|
||||
logs: []
|
||||
};
|
||||
|
||||
const hits = get(response, 'hits.hits', []);
|
||||
if (hits.length) {
|
||||
result.enabled = true;
|
||||
result.logs = hits.map(hit => {
|
||||
const source = hit._source;
|
||||
const type = get(source, 'event.dataset').split('.')[1];
|
||||
|
||||
return {
|
||||
timestamp: get(source, '@timestamp'),
|
||||
component: get(source, 'elasticsearch.component'),
|
||||
node: get(source, 'elasticsearch.node.name'),
|
||||
index: get(source, 'elasticsearch.index.name'),
|
||||
level: get(source, 'log.level'),
|
||||
type,
|
||||
message: get(source, 'message'),
|
||||
};
|
||||
});
|
||||
}
|
||||
else {
|
||||
result.reason = await detectReason(req, filebeatIndexPattern, opts);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function getLogs(config, req, filebeatIndexPattern, { clusterUuid, nodeUuid, indexUuid, start, end }) {
|
||||
checkParam(filebeatIndexPattern, 'filebeatIndexPattern in logs/getLogs');
|
||||
|
||||
const metric = { timestampField: '@timestamp' };
|
||||
const filter = [
|
||||
{ term: { 'service.type': 'elasticsearch' } },
|
||||
createTimeFilter({ start, end, metric })
|
||||
];
|
||||
if (clusterUuid) {
|
||||
filter.push({ term: { 'elasticsearch.cluster.uuid': clusterUuid } });
|
||||
}
|
||||
if (nodeUuid) {
|
||||
filter.push({ term: { 'elasticsearch.node.id': nodeUuid } });
|
||||
}
|
||||
if (indexUuid) {
|
||||
filter.push({ term: { 'elasticsearch.index.name': indexUuid } });
|
||||
}
|
||||
|
||||
const params = {
|
||||
index: filebeatIndexPattern,
|
||||
size: Math.min(50, config.get('xpack.monitoring.elasticsearch.logFetchCount')),
|
||||
filterPath: [
|
||||
'hits.hits._source.message',
|
||||
'hits.hits._source.log.level',
|
||||
'hits.hits._source.@timestamp',
|
||||
'hits.hits._source.event.dataset',
|
||||
'hits.hits._source.elasticsearch.component',
|
||||
'hits.hits._source.elasticsearch.index.name',
|
||||
'hits.hits._source.elasticsearch.node.name',
|
||||
],
|
||||
ignoreUnavailable: true,
|
||||
body: {
|
||||
sort: { '@timestamp': { order: 'desc' } },
|
||||
query: {
|
||||
bool: {
|
||||
filter,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('monitoring');
|
||||
const response = await callWithRequest(req, 'search', params);
|
||||
const result = await handleResponse(response, req, filebeatIndexPattern, { clusterUuid, nodeUuid, indexUuid, start, end });
|
||||
return {
|
||||
...result,
|
||||
limit: params.size,
|
||||
};
|
||||
}
|
8
x-pack/plugins/monitoring/server/lib/logs/index.js
Normal file
8
x-pack/plugins/monitoring/server/lib/logs/index.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { getLogs } from './get_logs';
|
||||
export { getLogTypes } from './get_log_types';
|
|
@ -13,7 +13,8 @@ import {
|
|||
INDEX_PATTERN_ELASTICSEARCH,
|
||||
INDEX_PATTERN_LOGSTASH,
|
||||
INDEX_PATTERN_BEATS,
|
||||
INDEX_ALERTS
|
||||
INDEX_ALERTS,
|
||||
INDEX_PATTERN_FILEBEAT
|
||||
} from '../../../../../common/constants';
|
||||
|
||||
export function clusterRoute(server) {
|
||||
|
@ -46,7 +47,16 @@ export function clusterRoute(server) {
|
|||
const beatsIndexPattern = prefixIndexPattern(config, INDEX_PATTERN_BEATS, ccs);
|
||||
const apmIndexPattern = prefixIndexPattern(config, INDEX_PATTERN_BEATS, ccs);
|
||||
const alertsIndex = prefixIndexPattern(config, INDEX_ALERTS, ccs);
|
||||
const indexPatterns = { esIndexPattern, kbnIndexPattern, lsIndexPattern, beatsIndexPattern, apmIndexPattern, alertsIndex };
|
||||
const filebeatIndexPattern = prefixIndexPattern(config, INDEX_PATTERN_FILEBEAT, '*');
|
||||
const indexPatterns = {
|
||||
esIndexPattern,
|
||||
kbnIndexPattern,
|
||||
lsIndexPattern,
|
||||
beatsIndexPattern,
|
||||
apmIndexPattern,
|
||||
alertsIndex,
|
||||
filebeatIndexPattern
|
||||
};
|
||||
const options = {
|
||||
clusterUuid: req.params.clusterUuid,
|
||||
start: req.payload.timeRange.min,
|
||||
|
|
|
@ -14,7 +14,8 @@ import {
|
|||
INDEX_PATTERN_KIBANA,
|
||||
INDEX_PATTERN_LOGSTASH,
|
||||
INDEX_PATTERN_BEATS,
|
||||
INDEX_ALERTS
|
||||
INDEX_ALERTS,
|
||||
INDEX_PATTERN_FILEBEAT
|
||||
} from '../../../../../common/constants';
|
||||
|
||||
export function clustersRoute(server) {
|
||||
|
@ -53,7 +54,16 @@ export function clustersRoute(server) {
|
|||
const beatsIndexPattern = prefixIndexPattern(config, INDEX_PATTERN_BEATS, ccs);
|
||||
const apmIndexPattern = prefixIndexPattern(config, INDEX_PATTERN_BEATS, ccs);
|
||||
const alertsIndex = prefixIndexPattern(config, INDEX_ALERTS, ccs);
|
||||
const indexPatterns = { esIndexPattern, kbnIndexPattern, lsIndexPattern, beatsIndexPattern, apmIndexPattern, alertsIndex };
|
||||
const filebeatIndexPattern = prefixIndexPattern(config, INDEX_PATTERN_FILEBEAT, ccs);
|
||||
const indexPatterns = {
|
||||
esIndexPattern,
|
||||
kbnIndexPattern,
|
||||
lsIndexPattern,
|
||||
beatsIndexPattern,
|
||||
apmIndexPattern,
|
||||
alertsIndex,
|
||||
filebeatIndexPattern
|
||||
};
|
||||
|
||||
clusters = await getClustersFromRequest(req, indexPatterns);
|
||||
} catch (err) {
|
||||
|
|
|
@ -13,7 +13,8 @@ import { getShardAllocation, getShardStats } from '../../../../lib/elasticsearch
|
|||
import { handleError } from '../../../../lib/errors/handle_error';
|
||||
import { prefixIndexPattern } from '../../../../lib/ccs_utils';
|
||||
import { metricSet } from './metric_set_index_detail';
|
||||
import { INDEX_PATTERN_ELASTICSEARCH } from '../../../../../common/constants';
|
||||
import { INDEX_PATTERN_ELASTICSEARCH, INDEX_PATTERN_FILEBEAT } from '../../../../../common/constants';
|
||||
import { getLogs } from '../../../../lib/logs/get_logs';
|
||||
|
||||
const { advanced: metricSetAdvanced, overview: metricSetOverview } = metricSet;
|
||||
|
||||
|
@ -47,6 +48,7 @@ export function esIndexRoute(server) {
|
|||
const start = req.payload.timeRange.min;
|
||||
const end = req.payload.timeRange.max;
|
||||
const esIndexPattern = prefixIndexPattern(config, INDEX_PATTERN_ELASTICSEARCH, ccs);
|
||||
const filebeatIndexPattern = prefixIndexPattern(config, INDEX_PATTERN_FILEBEAT, ccs);
|
||||
const isAdvanced = req.payload.is_advanced;
|
||||
const metricSet = isAdvanced ? metricSetAdvanced : metricSetOverview;
|
||||
|
||||
|
@ -57,6 +59,7 @@ export function esIndexRoute(server) {
|
|||
const indexSummary = await getIndexSummary(req, esIndexPattern, shardStats, { clusterUuid, indexUuid, start, end });
|
||||
const metrics = await getMetrics(req, esIndexPattern, metricSet, [{ term: { 'index_stats.index': indexUuid } }]);
|
||||
|
||||
let logs;
|
||||
let shardAllocation;
|
||||
if (!isAdvanced) {
|
||||
// TODO: Why so many fields needed for a single component (shard legend)?
|
||||
|
@ -69,6 +72,8 @@ export function esIndexRoute(server) {
|
|||
};
|
||||
const shards = await getShardAllocation(req, esIndexPattern, allocationOptions);
|
||||
|
||||
logs = await getLogs(config, req, filebeatIndexPattern, { clusterUuid, indexUuid, start, end });
|
||||
|
||||
shardAllocation = {
|
||||
shards,
|
||||
shardStats: { nodes: shardStats.nodes },
|
||||
|
@ -80,6 +85,7 @@ export function esIndexRoute(server) {
|
|||
return {
|
||||
indexSummary,
|
||||
metrics,
|
||||
logs,
|
||||
...shardAllocation,
|
||||
};
|
||||
|
||||
|
|
|
@ -13,7 +13,8 @@ import { getMetrics } from '../../../../lib/details/get_metrics';
|
|||
import { handleError } from '../../../../lib/errors/handle_error';
|
||||
import { prefixIndexPattern } from '../../../../lib/ccs_utils';
|
||||
import { metricSets } from './metric_set_node_detail';
|
||||
import { INDEX_PATTERN_ELASTICSEARCH } from '../../../../../common/constants';
|
||||
import { INDEX_PATTERN_ELASTICSEARCH, INDEX_PATTERN_FILEBEAT } from '../../../../../common/constants';
|
||||
import { getLogs } from '../../../../lib/logs/get_logs';
|
||||
|
||||
const { advanced: metricSetAdvanced, overview: metricSetOverview } = metricSets;
|
||||
|
||||
|
@ -47,6 +48,7 @@ export function esNodeRoute(server) {
|
|||
const start = req.payload.timeRange.min;
|
||||
const end = req.payload.timeRange.max;
|
||||
const esIndexPattern = prefixIndexPattern(config, INDEX_PATTERN_ELASTICSEARCH, ccs);
|
||||
const filebeatIndexPattern = prefixIndexPattern(config, INDEX_PATTERN_FILEBEAT, '*');
|
||||
const isAdvanced = req.payload.is_advanced;
|
||||
|
||||
let metricSet;
|
||||
|
@ -72,6 +74,8 @@ export function esNodeRoute(server) {
|
|||
const nodeSummary = await getNodeSummary(req, esIndexPattern, clusterState, shardStats, { clusterUuid, nodeUuid, start, end });
|
||||
const metrics = await getMetrics(req, esIndexPattern, metricSet, [{ term: { 'source_node.uuid': nodeUuid } }]);
|
||||
|
||||
|
||||
let logs;
|
||||
let shardAllocation;
|
||||
if (!isAdvanced) {
|
||||
// TODO: Why so many fields needed for a single component (shard legend)?
|
||||
|
@ -90,11 +94,14 @@ export function esNodeRoute(server) {
|
|||
nodes: shardStats.nodes, // for identifying nodes that shard relocates to
|
||||
stateUuid, // for debugging/troubleshooting
|
||||
};
|
||||
|
||||
logs = await getLogs(config, req, filebeatIndexPattern, { clusterUuid, nodeUuid, start, end });
|
||||
}
|
||||
|
||||
return {
|
||||
nodeSummary,
|
||||
metrics,
|
||||
logs,
|
||||
...shardAllocation
|
||||
};
|
||||
} catch (err) {
|
||||
|
|
|
@ -13,7 +13,8 @@ import { getShardStats } from '../../../../lib/elasticsearch/shards';
|
|||
import { handleError } from '../../../../lib/errors/handle_error';
|
||||
import { prefixIndexPattern } from '../../../../lib/ccs_utils';
|
||||
import { metricSet } from './metric_set_overview';
|
||||
import { INDEX_PATTERN_ELASTICSEARCH } from '../../../../../common/constants';
|
||||
import { INDEX_PATTERN_ELASTICSEARCH, INDEX_PATTERN_FILEBEAT } from '../../../../../common/constants';
|
||||
import { getLogs } from '../../../../lib/logs';
|
||||
|
||||
export function esOverviewRoute(server) {
|
||||
server.route({
|
||||
|
@ -38,18 +39,24 @@ export function esOverviewRoute(server) {
|
|||
const ccs = req.payload.ccs;
|
||||
const clusterUuid = req.params.clusterUuid;
|
||||
const esIndexPattern = prefixIndexPattern(config, INDEX_PATTERN_ELASTICSEARCH, ccs);
|
||||
const filebeatIndexPattern = prefixIndexPattern(config, INDEX_PATTERN_FILEBEAT, '*');
|
||||
|
||||
const start = req.payload.timeRange.min;
|
||||
const end = req.payload.timeRange.max;
|
||||
|
||||
try {
|
||||
const [ clusterStats, metrics, shardActivity ] = await Promise.all([
|
||||
const [ clusterStats, metrics, shardActivity, logs ] = await Promise.all([
|
||||
getClusterStats(req, esIndexPattern, clusterUuid),
|
||||
getMetrics(req, esIndexPattern, metricSet),
|
||||
getLastRecovery(req, esIndexPattern),
|
||||
getLogs(config, req, filebeatIndexPattern, { clusterUuid, start, end })
|
||||
]);
|
||||
const shardStats = await getShardStats(req, esIndexPattern, clusterStats);
|
||||
|
||||
return {
|
||||
clusterStatus: getClusterStatus(clusterStats, shardStats),
|
||||
metrics,
|
||||
logs,
|
||||
shardActivity,
|
||||
};
|
||||
} catch (err) {
|
||||
|
|
|
@ -59,6 +59,19 @@
|
|||
}
|
||||
},
|
||||
"status": "green"
|
||||
},
|
||||
"logs": {
|
||||
"enabled": false,
|
||||
"reason": {
|
||||
"clusterExists": null,
|
||||
"indexPatternExists": false,
|
||||
"indexPatternInTimeRangeExists": false,
|
||||
"nodeExists": null,
|
||||
"indexExists": null,
|
||||
"typeExists": false,
|
||||
"typeExistsAtAnyTime": false
|
||||
},
|
||||
"types": []
|
||||
}
|
||||
},
|
||||
"logstash": {
|
||||
|
|
|
@ -9,6 +9,20 @@
|
|||
"totalShards": 10,
|
||||
"status": "green"
|
||||
},
|
||||
"logs": {
|
||||
"enabled": false,
|
||||
"limit": 10,
|
||||
"reason": {
|
||||
"clusterExists": false,
|
||||
"indexPatternExists": false,
|
||||
"indexPatternInTimeRangeExists": false,
|
||||
"typeExistsAtAnyTime": false,
|
||||
"nodeExists": null,
|
||||
"indexExists": false,
|
||||
"typeExists": false
|
||||
},
|
||||
"logs": []
|
||||
},
|
||||
"metrics": {
|
||||
"index_search_request_rate": [
|
||||
{
|
||||
|
|
|
@ -10,6 +10,20 @@
|
|||
"status": "Offline",
|
||||
"isOnline": false
|
||||
},
|
||||
"logs": {
|
||||
"enabled": false,
|
||||
"limit": 10,
|
||||
"reason": {
|
||||
"clusterExists": false,
|
||||
"indexPatternExists": false,
|
||||
"indexPatternInTimeRangeExists": false,
|
||||
"typeExistsAtAnyTime": false,
|
||||
"nodeExists": false,
|
||||
"indexExists": null,
|
||||
"typeExists": false
|
||||
},
|
||||
"logs": []
|
||||
},
|
||||
"metrics": {
|
||||
"node_latency": [{
|
||||
"bucket_size": "10 seconds",
|
||||
|
|
|
@ -5748,5 +5748,19 @@
|
|||
"total_time_in_millis": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"logs": {
|
||||
"enabled": false,
|
||||
"limit": 10,
|
||||
"reason": {
|
||||
"clusterExists": false,
|
||||
"indexPatternExists": false,
|
||||
"indexPatternInTimeRangeExists": false,
|
||||
"typeExistsAtAnyTime": false,
|
||||
"nodeExists": null,
|
||||
"indexExists": null,
|
||||
"typeExists": false
|
||||
},
|
||||
"logs": []
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,20 @@
|
|||
"7.0.0-alpha1"
|
||||
]
|
||||
},
|
||||
"logs": {
|
||||
"enabled": false,
|
||||
"limit": 10,
|
||||
"reason": {
|
||||
"clusterExists": false,
|
||||
"indexPatternExists": false,
|
||||
"indexPatternInTimeRangeExists": false,
|
||||
"typeExistsAtAnyTime": false,
|
||||
"nodeExists": null,
|
||||
"indexExists": null,
|
||||
"typeExists": false
|
||||
},
|
||||
"logs": []
|
||||
},
|
||||
"metrics": {
|
||||
"cluster_index_latency": [
|
||||
{
|
||||
|
|
|
@ -14,6 +14,20 @@
|
|||
"7.0.0-alpha1"
|
||||
]
|
||||
},
|
||||
"logs": {
|
||||
"enabled": false,
|
||||
"limit": 10,
|
||||
"reason": {
|
||||
"clusterExists": false,
|
||||
"indexPatternExists": false,
|
||||
"indexPatternInTimeRangeExists": false,
|
||||
"typeExistsAtAnyTime": false,
|
||||
"nodeExists": null,
|
||||
"indexExists": null,
|
||||
"typeExists": false
|
||||
},
|
||||
"logs": []
|
||||
},
|
||||
"metrics": {
|
||||
"cluster_index_latency": [
|
||||
{
|
||||
|
|
|
@ -15,5 +15,6 @@ export default function ({ loadTestFile }) {
|
|||
loadTestFile(require.resolve('./logstash'));
|
||||
loadTestFile(require.resolve('./common'));
|
||||
loadTestFile(require.resolve('./standalone_cluster'));
|
||||
loadTestFile(require.resolve('./logs'));
|
||||
});
|
||||
}
|
||||
|
|
39
x-pack/test/api_integration/apis/monitoring/logs/cluster.js
Normal file
39
x-pack/test/api_integration/apis/monitoring/logs/cluster.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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 expect from '@kbn/expect';
|
||||
import clusterFixture from './fixtures/cluster';
|
||||
|
||||
export default function ({ getService }) {
|
||||
const supertest = getService('supertest');
|
||||
const esArchiver = getService('esArchiver');
|
||||
|
||||
describe('cluster', () => {
|
||||
const archive = 'monitoring/logs';
|
||||
const timeRange = {
|
||||
min: '2019-03-15T16:19:22.161Z',
|
||||
max: '2019-03-15T17:19:22.161Z'
|
||||
};
|
||||
|
||||
before('load archive', () => {
|
||||
return esArchiver.load(archive);
|
||||
});
|
||||
|
||||
after('unload archive', () => {
|
||||
return esArchiver.unload(archive);
|
||||
});
|
||||
|
||||
it('should get log types at the cluster level', async () => {
|
||||
const { body } = await supertest
|
||||
.post('/api/monitoring/v1/clusters/ZR3ZlJLUTV2V_GlplB83jQ')
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({ timeRange })
|
||||
.expect(200);
|
||||
|
||||
expect(body[0].elasticsearch.logs).to.eql(clusterFixture);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"enabled": true,
|
||||
"types": [{"type":"server","levels":[{"level":"info","count":38},{"level":"warn","count":1}]},{"type":"deprecation","levels":[{"level":"warn","count":3}]}]
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"enabled": true,
|
||||
"logs": [{
|
||||
"timestamp": "2019-03-15T17:07:21.089Z",
|
||||
"component": "o.e.n.Node",
|
||||
"node": "Elastic-MBP.local",
|
||||
"index": ".monitoring-es",
|
||||
"level": "INFO",
|
||||
"type": "server",
|
||||
"message": "started"
|
||||
}],
|
||||
"limit": 10
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
{
|
||||
"enabled": true,
|
||||
"logs": [{
|
||||
"timestamp": "2019-03-15T17:19:07.365Z",
|
||||
"component": "o.e.d.x.m.r.a.RestMonitoringBulkAction",
|
||||
"node": "Elastic-MBP.local",
|
||||
"level": "WARN",
|
||||
"type": "deprecation",
|
||||
"message": "[POST /_xpack/monitoring/_bulk] is deprecated! Use [POST /_monitoring/bulk] instead."
|
||||
}, {
|
||||
"timestamp": "2019-03-15T17:18:57.366Z",
|
||||
"component": "o.e.d.x.m.r.a.RestMonitoringBulkAction",
|
||||
"node": "Elastic-MBP.local",
|
||||
"level": "WARN",
|
||||
"type": "deprecation",
|
||||
"message": "[POST /_xpack/monitoring/_bulk] is deprecated! Use [POST /_monitoring/bulk] instead."
|
||||
}, {
|
||||
"timestamp": "2019-03-15T17:18:47.400Z",
|
||||
"component": "o.e.c.m.MetaDataCreateIndexService",
|
||||
"node": "Elastic-MBP.local",
|
||||
"index": ".monitoring-beats-7-2019.03.15",
|
||||
"level": "INFO",
|
||||
"type": "server",
|
||||
"message": "creating index, cause [auto(bulk api)], templates [.monitoring-beats], shards [1]/[0], mappings [_doc]"
|
||||
}, {
|
||||
"timestamp": "2019-03-15T17:18:47.387Z",
|
||||
"component": "o.e.d.x.m.r.a.RestMonitoringBulkAction",
|
||||
"node": "Elastic-MBP.local",
|
||||
"level": "WARN",
|
||||
"type": "deprecation",
|
||||
"message": "[POST /_xpack/monitoring/_bulk] is deprecated! Use [POST /_monitoring/bulk] instead."
|
||||
}, {
|
||||
"timestamp": "2019-03-15T17:18:42.084Z",
|
||||
"component": "o.e.c.m.MetaDataMappingService",
|
||||
"node": "Elastic-MBP.local",
|
||||
"index": "filebeat-8.0.0-2019.03.15-000001",
|
||||
"level": "INFO",
|
||||
"type": "server",
|
||||
"message": "update_mapping [_doc]"
|
||||
}, {
|
||||
"timestamp": "2019-03-15T17:18:41.811Z",
|
||||
"component": "o.e.c.m.MetaDataMappingService",
|
||||
"node": "Elastic-MBP.local",
|
||||
"index": "filebeat-8.0.0-2019.03.15-000001",
|
||||
"level": "INFO",
|
||||
"type": "server",
|
||||
"message": "update_mapping [_doc]"
|
||||
}, {
|
||||
"timestamp": "2019-03-15T17:18:41.447Z",
|
||||
"component": "o.e.c.m.MetaDataCreateIndexService",
|
||||
"node": "Elastic-MBP.local",
|
||||
"index": "filebeat-8.0.0-2019.03.15-000001",
|
||||
"level": "INFO",
|
||||
"type": "server",
|
||||
"message": "creating index, cause [api], templates [filebeat-8.0.0], shards [1]/[1], mappings [_doc]"
|
||||
}, {
|
||||
"timestamp": "2019-03-15T17:18:41.385Z",
|
||||
"component": "o.e.c.m.MetaDataIndexTemplateService",
|
||||
"node": "Elastic-MBP.local",
|
||||
"level": "INFO",
|
||||
"type": "server",
|
||||
"message": "adding template [filebeat-8.0.0] for index patterns [filebeat-8.0.0-*]"
|
||||
}, {
|
||||
"timestamp": "2019-03-15T17:18:41.185Z",
|
||||
"component": "o.e.x.i.a.TransportPutLifecycleAction",
|
||||
"node": "Elastic-MBP.local",
|
||||
"level": "INFO",
|
||||
"type": "server",
|
||||
"message": "adding index lifecycle policy [filebeat-8.0.0]"
|
||||
}, {
|
||||
"timestamp": "2019-03-15T17:18:36.137Z",
|
||||
"component": "o.e.c.r.a.AllocationService",
|
||||
"node": "Elastic-MBP.local",
|
||||
"level": "INFO",
|
||||
"type": "server",
|
||||
"message": "Cluster health status changed from [YELLOW] to [GREEN] (reason: [shards started [[.monitoring-es-7-2019.03.15][0]] ...])."
|
||||
}],
|
||||
"limit": 10
|
||||
}
|
13
x-pack/test/api_integration/apis/monitoring/logs/index.js
Normal file
13
x-pack/test/api_integration/apis/monitoring/logs/index.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export default function ({ loadTestFile }) {
|
||||
describe('Logs', () => {
|
||||
loadTestFile(require.resolve('./node_detail'));
|
||||
loadTestFile(require.resolve('./index_detail'));
|
||||
loadTestFile(require.resolve('./cluster'));
|
||||
});
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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 expect from '@kbn/expect';
|
||||
import indexDetailFixture from './fixtures/index_detail';
|
||||
|
||||
export default function ({ getService }) {
|
||||
const supertest = getService('supertest');
|
||||
const esArchiver = getService('esArchiver');
|
||||
|
||||
describe('cluster', () => {
|
||||
const archive = 'monitoring/logs';
|
||||
const timeRange = {
|
||||
min: '2019-03-15T16:19:22.161Z',
|
||||
max: '2019-03-15T17:19:22.161Z'
|
||||
};
|
||||
|
||||
before('load archive', () => {
|
||||
return esArchiver.load(archive);
|
||||
});
|
||||
|
||||
after('unload archive', () => {
|
||||
return esArchiver.unload(archive);
|
||||
});
|
||||
|
||||
it('should get logs for the specific index', async () => {
|
||||
const { body } = await supertest
|
||||
.post('/api/monitoring/v1/clusters/ZR3ZlJLUTV2V_GlplB83jQ/elasticsearch/indices/.monitoring-es')
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({ timeRange, is_advanced: false })
|
||||
.expect(200);
|
||||
|
||||
expect(body.logs).to.eql(indexDetailFixture);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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 expect from '@kbn/expect';
|
||||
import nodeDetailFixture from './fixtures/node_detail';
|
||||
|
||||
export default function ({ getService }) {
|
||||
const supertest = getService('supertest');
|
||||
const esArchiver = getService('esArchiver');
|
||||
|
||||
describe('cluster', () => {
|
||||
const archive = 'monitoring/logs';
|
||||
const timeRange = {
|
||||
min: '2019-03-15T16:19:22.161Z',
|
||||
max: '2019-03-15T17:19:22.161Z'
|
||||
};
|
||||
|
||||
before('load archive', () => {
|
||||
return esArchiver.load(archive);
|
||||
});
|
||||
|
||||
after('unload archive', () => {
|
||||
return esArchiver.unload(archive);
|
||||
});
|
||||
|
||||
it('should get logs for the specific node', async () => {
|
||||
const { body } = await supertest
|
||||
.post('/api/monitoring/v1/clusters/ZR3ZlJLUTV2V_GlplB83jQ/elasticsearch/nodes/-pH5RhfsRl6FDeTPwD5vEw')
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({ timeRange, is_advanced: false })
|
||||
.expect(200);
|
||||
|
||||
expect(body.logs).to.eql(nodeDetailFixture);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1 +1,51 @@
|
|||
[{"cluster_uuid":"__standalone_cluster__","license":{},"elasticsearch":{"cluster_stats":{"indices":{},"nodes":{"count":{},"jvm":{}}}},"logstash":{},"kibana":{},"beats":{"totalEvents":348,"bytesSent":319913,"beats":{"total":1,"types":[{"type":"Packetbeat","count":1}]}},"apm":{"totalEvents":0,"memRss":0,"memTotal":0,"apms":{"total":0}},"alerts":{"message":"Cluster Alerts are not displayed because the [production] cluster's license could not be determined."},"isPrimary":false}]
|
||||
[{
|
||||
"cluster_uuid": "__standalone_cluster__",
|
||||
"license": {},
|
||||
"elasticsearch": {
|
||||
"cluster_stats": {
|
||||
"indices": {},
|
||||
"nodes": {
|
||||
"count": {},
|
||||
"jvm": {}
|
||||
}
|
||||
},
|
||||
"logs": {
|
||||
"enabled": false,
|
||||
"reason": {
|
||||
"clusterExists": null,
|
||||
"indexPatternExists": false,
|
||||
"indexPatternInTimeRangeExists": false,
|
||||
"typeExistsAtAnyTime": false,
|
||||
"nodeExists": null,
|
||||
"indexExists": null,
|
||||
"typeExists": false
|
||||
},
|
||||
"types": []
|
||||
}
|
||||
},
|
||||
"logstash": {},
|
||||
"kibana": {},
|
||||
"beats": {
|
||||
"totalEvents": 348,
|
||||
"bytesSent": 319913,
|
||||
"beats": {
|
||||
"total": 1,
|
||||
"types": [{
|
||||
"type": "Packetbeat",
|
||||
"count": 1
|
||||
}]
|
||||
}
|
||||
},
|
||||
"apm": {
|
||||
"totalEvents": 0,
|
||||
"memRss": 0,
|
||||
"memTotal": 0,
|
||||
"apms": {
|
||||
"total": 0
|
||||
}
|
||||
},
|
||||
"alerts": {
|
||||
"message": "Cluster Alerts are not displayed because the [production] cluster's license could not be determined."
|
||||
},
|
||||
"isPrimary": false
|
||||
}]
|
||||
|
|
BIN
x-pack/test/functional/es_archives/monitoring/logs/data.json.gz
Normal file
BIN
x-pack/test/functional/es_archives/monitoring/logs/data.json.gz
Normal file
Binary file not shown.
4441
x-pack/test/functional/es_archives/monitoring/logs/mappings.json
Normal file
4441
x-pack/test/functional/es_archives/monitoring/logs/mappings.json
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue