diff --git a/x-pack/plugins/monitoring/public/components/apm/instance/index.js b/x-pack/plugins/monitoring/public/components/apm/instance/index.js new file mode 100644 index 0000000000000..50462a66fc134 --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/apm/instance/index.js @@ -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 { ApmServerInstance } from './instance'; diff --git a/x-pack/plugins/monitoring/public/components/apm/instance/instance.js b/x-pack/plugins/monitoring/public/components/apm/instance/instance.js new file mode 100644 index 0000000000000..e03dc375e9b86 --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/apm/instance/instance.js @@ -0,0 +1,50 @@ +/* + * 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 { MonitoringTimeseriesContainer } from '../../chart'; +import { EuiFlexGrid, EuiFlexItem, EuiSpacer, EuiPage, EuiPageBody } from '@elastic/eui'; +import { Status } from './status'; + +export function ApmServerInstance({ summary, metrics, ...props }) { + const metricsToShow = [ + metrics.apm_cpu, + metrics.apm_os_load, + + metrics.apm_memory, + metrics.apm_requests, + + metrics.apm_incoming_requests_size, + metrics.apm_transformations, + + metrics.apm_output_events_rate_success, + metrics.apm_output_events_rate_failure, + + metrics.apm_responses_valid, + metrics.apm_responses_errors, + ]; + + return ( +
+ + + + + {metricsToShow.map((metric, index) => ( + + + + + ))} + + + +
+ ); +} diff --git a/x-pack/plugins/monitoring/public/components/apm/instance/status.js b/x-pack/plugins/monitoring/public/components/apm/instance/status.js new file mode 100644 index 0000000000000..f68bf6a72d2f8 --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/apm/instance/status.js @@ -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, { Fragment } from 'react'; +import moment from 'moment'; +import { SummaryStatus } from '../../summary_status'; +import { ApmStatusIcon } from '../status_icon'; +import { formatMetric } from '../../../lib/format_number'; +import { formatTimestampToDuration } from '../../../../common'; + +export function Status({ stats }) { + const { + name, + output, + version, + uptime, + timeOfLastEvent, + } = stats; + + const metrics = [ + { + label: 'Name', + value: name, + dataTestSubj: 'name' + }, + { + label: 'Output', + value: output, + dataTestSubj: 'output' + }, + { + label: 'Version', + value: version, + dataTestSubj: 'version' + }, + { + label: 'Uptime', + value: formatMetric(uptime, 'time_since'), + dataTestSubj: 'uptime' + }, + { + label: 'Last Event', + value: formatTimestampToDuration(+moment(timeOfLastEvent), 'since') + ' ago', + dataTestSubj: 'timeOfLastEvent', + } + ]; + + const IconComponent = ({ status }) => ( + + Status: + + ); + + return ( + + ); +} diff --git a/x-pack/plugins/monitoring/public/components/apm/instances/index.js b/x-pack/plugins/monitoring/public/components/apm/instances/index.js new file mode 100644 index 0000000000000..b6d0d6fa853ba --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/apm/instances/index.js @@ -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 { ApmServerInstances } from './instances'; diff --git a/x-pack/plugins/monitoring/public/components/apm/instances/instances.js b/x-pack/plugins/monitoring/public/components/apm/instances/instances.js new file mode 100644 index 0000000000000..f53502a655247 --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/apm/instances/instances.js @@ -0,0 +1,101 @@ +/* + * 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 { MonitoringTable } from '../../table'; +import { + KuiTableRowCell, + KuiTableRow +} from '@kbn/ui-framework/components'; +import { EuiLink } from '@elastic/eui'; +import { Status } from './status'; +import { SORT_ASCENDING, SORT_DESCENDING, TABLE_ACTION_UPDATE_FILTER } from '../../../../common/constants'; +import { formatMetric } from '../../../lib/format_number'; + + +const filterFields = [ 'name', 'type', 'version', 'output' ]; +const columns = [ + { title: 'Name', sortKey: 'name', sortOrder: SORT_ASCENDING }, + { title: 'Output Enabled', sortKey: 'output' }, + { title: 'Total Events Rate', sortKey: 'total_events_rate', secondarySortOrder: SORT_DESCENDING }, + { title: 'Bytes Sent Rate', sortKey: 'bytes_sent_rate' }, + { title: 'Output Errors', sortKey: 'errors' }, + { title: 'Allocated Memory', sortKey: 'memory' }, + { title: 'Version', sortKey: 'version' }, +]; +const instanceRowFactory = (goToInstance) => { + return function KibanaRow(props) { + const applyFiltering = filterText => () => { + props.dispatchTableAction(TABLE_ACTION_UPDATE_FILTER, filterText); + }; + + return ( + + +
+ goToInstance(props.uuid)} + data-test-subj={`apmLink-${props.name}`} + > + {props.name} + +
+
+ + {props.output} + + + {formatMetric(props.total_events_rate, '', '/s')} + + + {formatMetric(props.bytes_sent_rate, 'byte', '/s')} + + + {formatMetric(props.errors, '0')} + + + {formatMetric(props.memory, 'byte')} + + + + {props.version} + + +
+ ); + }; +}; + +export function ApmServerInstances({ apms, goToInstance }) { + const { + pageIndex, + filterText, + sortKey, + sortOrder, + onNewState, + } = apms; + + return ( +
+ + +
+ ); +} diff --git a/x-pack/plugins/monitoring/public/components/apm/instances/status.js b/x-pack/plugins/monitoring/public/components/apm/instances/status.js new file mode 100644 index 0000000000000..ac5f9770fa88d --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/apm/instances/status.js @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { Fragment } from 'react'; +import moment from 'moment'; +import { SummaryStatus } from '../../summary_status'; +import { ApmStatusIcon } from '../status_icon'; +import { formatMetric } from '../../../lib/format_number'; +import { formatTimestampToDuration } from '../../../../common'; + +export function Status({ stats }) { + const { + apms: { + total + }, + bytesSent, + totalEvents, + timeOfLastEvent, + } = stats; + + const metrics = [ + { + label: 'Servers', + value: total, + dataTestSubj: 'total' + }, + { + label: 'Bytes Sent', + value: formatMetric(bytesSent, 'bytes'), + dataTestSubj: 'bytesSent' + }, + { + label: 'Total Events', + value: formatMetric(totalEvents, '0.[0]a'), + dataTestSubj: 'totalEvents' + }, + { + label: 'Last Event', + value: formatTimestampToDuration(+moment(timeOfLastEvent), 'since') + ' ago', + dataTestSubj: 'timeOfLastEvent', + } + ]; + + const IconComponent = ({ status }) => ( + + Status: + + ); + + return ( + + ); +} diff --git a/x-pack/plugins/monitoring/public/components/apm/overview/index.js b/x-pack/plugins/monitoring/public/components/apm/overview/index.js new file mode 100644 index 0000000000000..212e4771805fe --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/apm/overview/index.js @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { MonitoringTimeseriesContainer } from '../../chart'; +import { + EuiSpacer, + EuiPage, + EuiFlexGroup, + EuiFlexItem, + EuiPageBody, + EuiPanel +} from '@elastic/eui'; +import { Status } from '../instances/status'; + +export function ApmOverview({ + stats, + metrics, + ...props +}) { + const seriesToShow = [ + metrics.apm_cpu, + metrics.apm_os_load, + + metrics.apm_memory, + metrics.apm_requests, + + metrics.apm_incoming_requests_size, + metrics.apm_transformations, + + metrics.apm_output_events_rate_success, + metrics.apm_output_events_rate_failure, + + metrics.apm_responses_valid, + metrics.apm_responses_errors, + ]; + + const charts = seriesToShow.map((data, index) => ( + + + + + + )); + + return ( + + + + + + {charts} + + + + ); +} diff --git a/x-pack/plugins/monitoring/public/components/apm/status_icon.js b/x-pack/plugins/monitoring/public/components/apm/status_icon.js new file mode 100644 index 0000000000000..49fe0faed1ad1 --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/apm/status_icon.js @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { StatusIcon } from 'plugins/monitoring/components/status_icon'; + +export function ApmStatusIcon({ status, availability = true }) { + const type = (() => { + if (!availability) { + return StatusIcon.TYPES.GRAY; + } + + const statusKey = status.toUpperCase(); + return StatusIcon.TYPES[statusKey] || StatusIcon.TYPES.YELLOW; + })(); + + return ( + + ); +} diff --git a/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js b/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js new file mode 100644 index 0000000000000..64d9c0e45c056 --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js @@ -0,0 +1,79 @@ +/* + * 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 { get } from 'lodash'; +import { formatMetric } from 'plugins/monitoring/lib/format_number'; +import { ClusterItemContainer } from './helpers'; + +import { + EuiFlexGrid, + EuiFlexItem, + EuiLink, + EuiTitle, + EuiPanel, + EuiDescriptionList, + EuiDescriptionListTitle, + EuiDescriptionListDescription, + EuiHorizontalRule, +} from '@elastic/eui'; + +export function ApmPanel(props) { + if (!get(props, 'apms.total', 0) > 0) { + return null; + } + + const goToApm = () => props.changeUrl('apm'); + const goToInstances = () => props.changeUrl('apm/instances'); + + return ( + + + + + +

+ + Overview + +

+
+ + + Total Events + + {formatMetric(props.totalEvents, '0.[0]a')} + + Bytes Sent + + {formatMetric(props.bytesSent, 'byte')} + + +
+
+ + + +

+ + APM Servers: {props.apms.total} + +

+
+
+
+
+
+ ); +} diff --git a/x-pack/plugins/monitoring/public/components/cluster/overview/helpers.js b/x-pack/plugins/monitoring/public/components/cluster/overview/helpers.js index c0e3a6a6b28e4..28e3a857cf29d 100644 --- a/x-pack/plugins/monitoring/public/components/cluster/overview/helpers.js +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/helpers.js @@ -53,6 +53,7 @@ export function ClusterItemContainer(props) { kibana: 'logoKibana', logstash: 'logoLogstash', beats: 'logoBeats', + apm: 'logoApm' }; const icon = iconMap[props.url]; diff --git a/x-pack/plugins/monitoring/public/components/cluster/overview/index.js b/x-pack/plugins/monitoring/public/components/cluster/overview/index.js index ba166d73e449b..67549912e5315 100644 --- a/x-pack/plugins/monitoring/public/components/cluster/overview/index.js +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/index.js @@ -12,6 +12,7 @@ import { AlertsPanel } from './alerts_panel'; import { BeatsPanel } from './beats_panel'; import { EuiPage, EuiPageBody } from '@elastic/eui'; +import { ApmPanel } from './apm_panel'; export function Overview(props) { return ( @@ -33,6 +34,8 @@ export function Overview(props) { + + ); diff --git a/x-pack/plugins/monitoring/public/directives/all.js b/x-pack/plugins/monitoring/public/directives/all.js index 98b3a10237ba2..08993329e8363 100644 --- a/x-pack/plugins/monitoring/public/directives/all.js +++ b/x-pack/plugins/monitoring/public/directives/all.js @@ -27,3 +27,6 @@ import './kibana/summary'; import './beats/overview'; import './beats/listing'; import './beats/beat'; +import './apm/instance'; +import './apm/instances'; +import './apm/overview'; diff --git a/x-pack/plugins/monitoring/public/directives/apm/instance/index.js b/x-pack/plugins/monitoring/public/directives/apm/instance/index.js new file mode 100644 index 0000000000000..23bdee5e79590 --- /dev/null +++ b/x-pack/plugins/monitoring/public/directives/apm/instance/index.js @@ -0,0 +1,43 @@ +/* + * 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 moment from 'moment'; +import { render } from 'react-dom'; +import { uiModules } from 'ui/modules'; +import { ApmServerInstance } from 'plugins/monitoring/components/apm/instance'; +import { timefilter } from 'ui/timefilter'; + +const uiModule = uiModules.get('monitoring/directives', []); +uiModule.directive('monitoringApmServerInstance', () => { + return { + restrict: 'E', + scope: { + data: '=', + }, + link(scope, $el) { + + function onBrush({ xaxis }) { + timefilter.setTime({ + from: moment(xaxis.from), + to: moment(xaxis.to), + mode: 'absolute', + }); + } + + scope.$watch('data', (data = {}) => { + render(( + + ), $el[0]); + }); + + } + }; +}); diff --git a/x-pack/plugins/monitoring/public/directives/apm/instances/index.js b/x-pack/plugins/monitoring/public/directives/apm/instances/index.js new file mode 100644 index 0000000000000..28d3c6a781976 --- /dev/null +++ b/x-pack/plugins/monitoring/public/directives/apm/instances/index.js @@ -0,0 +1,38 @@ +/* + * 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 { render } from 'react-dom'; +import { uiModules } from 'ui/modules'; +import { ApmServerInstances } from '../../../components/apm/instances'; + +const uiModule = uiModules.get('monitoring/directives', []); +uiModule.directive('monitoringApmInstances', kbnUrl => { + return { + restrict: 'E', + scope: { + apms: '=', + }, + link(scope, $el) { + const goToInstance = uuid => { + scope.$evalAsync(() => { + kbnUrl.changePath(`/apm/instances/${uuid}`); + }); + }; + + scope.$watch('apms.data.apms', () => { + const apmsTable = ( + + ); + render(apmsTable, $el[0]); + }); + + } + }; +}); diff --git a/x-pack/plugins/monitoring/public/directives/apm/overview/index.js b/x-pack/plugins/monitoring/public/directives/apm/overview/index.js new file mode 100644 index 0000000000000..606c624c52018 --- /dev/null +++ b/x-pack/plugins/monitoring/public/directives/apm/overview/index.js @@ -0,0 +1,42 @@ +/* + * 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 moment from 'moment'; +import { render } from 'react-dom'; +import { uiModules } from 'ui/modules'; +import { ApmOverview } from 'plugins/monitoring/components/apm/overview'; +import { timefilter } from 'ui/timefilter'; + +const uiModule = uiModules.get('monitoring/directives', []); +uiModule.directive('monitoringApmOverview', () => { + return { + restrict: 'E', + scope: { + data: '=', + }, + link(scope, $el) { + + function onBrush({ xaxis }) { + timefilter.setTime({ + from: moment(xaxis.from), + to: moment(xaxis.to), + mode: 'absolute' + }); + } + + scope.$watch('data', (data = {}) => { + render(( + + ), $el[0]); + }); + + } + }; +}); diff --git a/x-pack/plugins/monitoring/public/directives/main/index.html b/x-pack/plugins/monitoring/public/directives/main/index.html index 2804898b85283..c48775107dd4c 100644 --- a/x-pack/plugins/monitoring/public/directives/main/index.html +++ b/x-pack/plugins/monitoring/public/directives/main/index.html @@ -60,6 +60,12 @@ {{ monitoringMain.instance }} + +