From 9bc4d9158bc23bc0ddfe4a80d5aaee42b04bb27e Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Thu, 16 Feb 2017 10:30:36 -0600 Subject: [PATCH] Updates to status API, re-align status page (#10180) * [status] Only provide latest metrics * [status] Snake case requests object * [status] Add build snapshot flag, last updated time * [status] Re-add requests per second * [status] Add uptime * [status] collection_time -> collection_interval, add memory object * [status] Add api tests * [status] Add breaking changes docs * [status] Remove metrics config * [status] nest load under cpu, shorten memory and average * [status] collection_time -> collection_interval * [status api] Unnest heap, rename load to load average --- docs/migration/migrate_6_0.asciidoc | 12 +++- .../status_page/public/lib/format_number.js | 3 + .../status_page/public/lib/to_title_case.js | 10 --- .../status_page/public/status_page.html | 4 +- .../status_page/public/status_page.js | 34 +++++++++- .../status_page/public/status_page.less | 1 + .../public/status_page_metric.html | 4 +- .../status_page/public/status_page_metric.js | 66 +++---------------- src/server/status/__tests__/metrics.js | 56 ++++++++++++++++ src/server/status/index.js | 19 ++++-- src/server/status/metrics.js | 57 +++++++++------- src/ui/public/ingest/ingest.js | 2 +- .../__tests__/case_conversion.js | 0 .../common/lib => utils}/case_conversion.js | 0 14 files changed, 165 insertions(+), 103 deletions(-) delete mode 100644 src/core_plugins/status_page/public/lib/to_title_case.js create mode 100644 src/server/status/__tests__/metrics.js rename src/{core_plugins/kibana/common/lib => utils}/__tests__/case_conversion.js (100%) rename src/{core_plugins/kibana/common/lib => utils}/case_conversion.js (100%) diff --git a/docs/migration/migrate_6_0.asciidoc b/docs/migration/migrate_6_0.asciidoc index 27830c6f806f..b0122e66eb1e 100644 --- a/docs/migration/migrate_6_0.asciidoc +++ b/docs/migration/migrate_6_0.asciidoc @@ -10,4 +10,14 @@ your application to Kibana 6.0. Kibana 6.0 will only support Painless and Lucene expression based scripts. -*Impact:* You will need to migrate your groovy, python, javascript, etc. scripted fields to Painless or Lucene expressions. \ No newline at end of file +*Impact:* You will need to migrate your groovy, python, javascript, etc. scripted fields to Painless or Lucene expressions. + +[float] +=== Changed response format of status API +*Details:* In an effort to align with our style guidelines and provide a digestible response, +the status API has changed: + +* Properties are now snake cased and several have been renamed +* Metrics now provide the latest available data instead of samples over time + +*Impact:* You will need to update anything using the status API and expecting the previous response format. \ No newline at end of file diff --git a/src/core_plugins/status_page/public/lib/format_number.js b/src/core_plugins/status_page/public/lib/format_number.js index a5e0a7f72bfd..d31a44889993 100644 --- a/src/core_plugins/status_page/public/lib/format_number.js +++ b/src/core_plugins/status_page/public/lib/format_number.js @@ -14,6 +14,9 @@ module.exports = function formatNumber(num, which) { case 'ms': postfix = ' ms'; break; + case 'integer': + format = '0'; + break; } return numeral(num).format(format) + postfix; }; diff --git a/src/core_plugins/status_page/public/lib/to_title_case.js b/src/core_plugins/status_page/public/lib/to_title_case.js deleted file mode 100644 index 94e230cc3fef..000000000000 --- a/src/core_plugins/status_page/public/lib/to_title_case.js +++ /dev/null @@ -1,10 +0,0 @@ -import _ from 'lodash'; - -// Turns thisIsASentence to -// This Is A Sentence -module.exports = function toTitleCase(name) { - return name - .split(/(?=[A-Z])/) - .map(function (word) { return word[0].toUpperCase() + _.rest(word).join(''); }) - .join(' '); -}; diff --git a/src/core_plugins/status_page/public/status_page.html b/src/core_plugins/status_page/public/status_page.html index c0ae2fa404f0..455ec252dc18 100644 --- a/src/core_plugins/status_page/public/status_page.html +++ b/src/core_plugins/status_page/public/status_page.html @@ -10,8 +10,8 @@
-
- +
+
diff --git a/src/core_plugins/status_page/public/status_page.js b/src/core_plugins/status_page/public/status_page.js index 05c0ad1b1b22..c350aa360e3d 100644 --- a/src/core_plugins/status_page/public/status_page.js +++ b/src/core_plugins/status_page/public/status_page.js @@ -27,9 +27,39 @@ const chrome = require('ui/chrome') } const data = resp.data; - ui.metrics = data.metrics; - ui.name = data.name; + const metrics = data.metrics; + if (metrics) { + ui.metrics = [{ + name: 'Heap Total', + value: _.get(metrics, 'process.mem.heap_max_in_bytes'), + type: 'byte' + }, { + name: 'Heap Used', + value: _.get(metrics, 'process.mem.heap_used_in_bytes'), + type: 'byte' + }, { + name: 'Load', + value: [ + _.get(metrics, 'os.cpu.load_average.1m'), + _.get(metrics, 'os.cpu.load_average.5m'), + _.get(metrics, 'os.cpu.load_average.15m') + ], + type: 'float' + }, { + name: 'Response Time Avg', + value: _.get(metrics, 'response_times.avg_in_millis'), + type: 'ms' + }, { + name: 'Response Time Max', + value: _.get(metrics, 'response_times.max_in_millis'), + type: 'ms' + }, { + name: 'Requests Per Second', + value: _.get(metrics, 'requests.total') * 1000 / _.get(metrics, 'collection_interval_in_millis') + }]; + } + ui.name = data.name; ui.statuses = data.status.statuses; const overall = data.status.overall; diff --git a/src/core_plugins/status_page/public/status_page.less b/src/core_plugins/status_page/public/status_page.less index d82736de7278..7b391d547715 100644 --- a/src/core_plugins/status_page/public/status_page.less +++ b/src/core_plugins/status_page/public/status_page.less @@ -36,6 +36,7 @@ border: 0; .content { + display: block; text-align: right; padding: 15px; padding-right: 20px; diff --git a/src/core_plugins/status_page/public/status_page_metric.html b/src/core_plugins/status_page/public/status_page_metric.html index 9bda93533fc1..7d5a99d90a0d 100644 --- a/src/core_plugins/status_page/public/status_page_metric.html +++ b/src/core_plugins/status_page/public/status_page_metric.html @@ -1,6 +1,6 @@
-

{{metric.extendedTitle}}

-

{{ metric.averages.join(', ') }}

+

{{ metric.name }}

+

{{ metric.value | statusMetric: metric.type}}

diff --git a/src/core_plugins/status_page/public/status_page_metric.js b/src/core_plugins/status_page/public/status_page_metric.js index 1893e8eda2d4..412d93854672 100644 --- a/src/core_plugins/status_page/public/status_page_metric.js +++ b/src/core_plugins/status_page/public/status_page_metric.js @@ -2,74 +2,28 @@ import _ from 'lodash'; import moment from 'moment'; import numeral from 'numeral'; -import toTitleCase from './lib/to_title_case'; import formatNumber from './lib/format_number'; import readStatData from './lib/read_stat_data'; import uiModules from 'ui/modules'; import statusPageMetricTemplate from 'plugins/status_page/status_page_metric.html'; -function calcAvg(metricList, metricNumberType) { - return metricList.map(function (data) { - const uglySum = data.values.reduce(function (sumSoFar, vector) { - return sumSoFar + vector.y; - }, 0); - return formatNumber(uglySum / data.values.length, metricNumberType); - }); -} - uiModules .get('kibana', []) +.filter('statusMetric', function () { + return function (input, type) { + const metrics = [].concat(input); + return metrics.map(function (metric) { + return formatNumber(metric, type); + }).join(', '); + }; +}) .directive('statusPageMetric', function () { return { restrict: 'E', template: statusPageMetricTemplate, scope: { - name: '@', - data: '=' + metric: '=', }, - controllerAs: 'metric', - controller: function ($scope) { - const self = this; - - self.name = $scope.name; - self.title = toTitleCase(self.name); - self.extendedTitle = self.title; - self.numberType = 'precise'; - self.seriesNames = []; - - switch (self.name) { - case 'heapTotal': - case 'heapUsed': - self.numberType = 'byte'; - break; - - case 'responseTimeAvg': - case 'responseTimeMax': - self.numberType = 'ms'; - break; - - case 'load': - self.seriesNames = ['1min', '5min', '15min']; - break; - } - - $scope.$watch('data', function (data) { - self.rawData = data; - self.chartData = readStatData(self.rawData, self.seriesNames); - self.averages = calcAvg(self.chartData, self.numberType); - - let unit = ''; - self.averages = self.averages.map(function (average) { - const parts = average.split(' '); - const value = parts.shift(); - unit = parts.join(' '); - return value; - }); - self.extendedTitle = self.title; - if (unit) { - self.extendedTitle = `${self.extendedTitle} (${unit})`; - } - }); - } + controllerAs: 'metric' }; }); diff --git a/src/server/status/__tests__/metrics.js b/src/server/status/__tests__/metrics.js new file mode 100644 index 000000000000..67d5167e3cfb --- /dev/null +++ b/src/server/status/__tests__/metrics.js @@ -0,0 +1,56 @@ +import _ from 'lodash'; +import expect from 'expect.js'; + +import { getMetrics } from '../metrics'; + +describe('Metrics', function () { + const mockOps = { + 'requests': { '5603': { 'total': 22, 'disconnects': 0, 'statusCodes': { '200': 22 } } }, + 'responseTimes': { '5603': { 'avg': 1.8636363636363635, 'max': 4 } }, + 'sockets': { + 'http': { 'total': 0 }, + 'https': { 'total': 0 } + }, + 'osload': [2.20751953125, 2.02294921875, 1.89794921875], + 'osmem': { 'total': 17179869184, 'free': 102318080 }, + 'osup': 1008991, + 'psup': 7.168, + 'psmem': { 'rss': 193716224, 'heapTotal': 168194048, 'heapUsed': 130553400 }, + 'concurrents': { '5603': 0 }, + 'psdelay': 1.6091690063476562, + 'host': '123' + }; + const config = { + ops: { + interval: 5000 + }, + server: { + port: 5603 + } + }; + + let metrics; + beforeEach(() => { + metrics = getMetrics({ + event: _.cloneDeep(mockOps), + config: { + get: path => _.get(config, path) + } + }); + }); + + it('should snake case the request object', () => { + expect(metrics.requests.status_codes).not.to.be(undefined); + expect(metrics.requests.statusCodes).to.be(undefined); + }); + + it('should provide defined metrics', () => { + (function checkMetrics(currentMetric) { + _.forOwn(currentMetric, value => { + if (typeof value === 'object') return checkMetrics(value); + expect(currentMetric).not.to.be(undefined); + }); + + }(metrics)); + }); +}); diff --git a/src/server/status/index.js b/src/server/status/index.js index 90dea44ce537..b074c0ba6594 100644 --- a/src/server/status/index.js +++ b/src/server/status/index.js @@ -7,24 +7,29 @@ export default function (kbnServer, server, config) { kbnServer.status = new ServerStatus(kbnServer.server); if (server.plugins['even-better']) { - kbnServer.mixin(require('./metrics')); + kbnServer.mixin(require('./metrics').collectMetrics); } const wrapAuth = wrapAuthConfig(config.get('status.allowAnonymous')); - + const matchSnapshot = /-SNAPSHOT$/; server.route(wrapAuth({ method: 'GET', path: '/api/status', handler: function (request, reply) { - return reply({ + const status = { name: config.get('server.name'), - version: config.get('pkg.version'), - buildNum: config.get('pkg.buildNum'), - buildSha: config.get('pkg.buildSha'), uuid: config.get('server.uuid'), + version: { + number: config.get('pkg.version').replace(matchSnapshot, ''), + build_hash: config.get('pkg.buildSha'), + build_number: config.get('pkg.buildNum'), + build_snapshot: matchSnapshot.test(config.get('pkg.version')) + }, status: kbnServer.status.toJSON(), metrics: kbnServer.metrics - }); + }; + + return reply(status); } })); diff --git a/src/server/status/metrics.js b/src/server/status/metrics.js index d00a8eb540d0..a2505b727b00 100644 --- a/src/server/status/metrics.js +++ b/src/server/status/metrics.js @@ -1,27 +1,40 @@ import _ from 'lodash'; import Samples from './samples'; -module.exports = function (kbnServer, server, config) { - let lastReport = Date.now(); - - kbnServer.metrics = new Samples(12); +import { keysToSnakeCaseShallow } from '../../utils/case_conversion'; +export function collectMetrics(kbnServer, server, config) { server.plugins['even-better'].monitor.on('ops', function (event) { - const now = Date.now(); - const secSinceLast = (now - lastReport) / 1000; - lastReport = now; - - const port = config.get('server.port'); - const requests = _.get(event, ['requests', port, 'total'], 0); - const requestsPerSecond = requests / secSinceLast; - - kbnServer.metrics.add({ - heapTotal: _.get(event, 'psmem.heapTotal'), - heapUsed: _.get(event, 'psmem.heapUsed'), - load: event.osload, - responseTimeAvg: _.get(event, ['responseTimes', port, 'avg']), - responseTimeMax: _.get(event, ['responseTimes', port, 'max']), - requestsPerSecond: requestsPerSecond - }); - + kbnServer.metrics = getMetrics({ event, config }); }); -}; +} + +export function getMetrics({ event, config }) { + const port = config.get('server.port'); + const timestamp = new Date().toISOString(); + return { + last_updated: timestamp, + collection_interval_in_millis: config.get('ops.interval'), + uptime_in_millis: process.uptime() * 1000, + process: { + mem: { + heap_max_in_bytes: _.get(event, 'psmem.heapTotal'), + heap_used_in_bytes: _.get(event, 'psmem.heapUsed') + } + }, + os: { + cpu: { + load_average: { + '1m': _.get(event, 'osload.0'), + '5m': _.get(event, 'osload.1'), + '15m': _.get(event, 'osload.1') + } + } + }, + response_times: { + avg_in_millis: _.get(event, ['responseTimes', port, 'avg']), + max_in_millis: _.get(event, ['responseTimes', port, 'max']) + }, + requests: keysToSnakeCaseShallow(_.get(event, ['requests', port])), + concurrent_connections: _.get(event, ['concurrents', port]) + }; +} diff --git a/src/ui/public/ingest/ingest.js b/src/ui/public/ingest/ingest.js index 6b523d41c6c7..9fe0c43b9d09 100644 --- a/src/ui/public/ingest/ingest.js +++ b/src/ui/public/ingest/ingest.js @@ -1,5 +1,5 @@ import RefreshKibanaIndexProvider from 'plugins/kibana/management/sections/indices/_refresh_kibana_index'; -import { keysToCamelCaseShallow, keysToSnakeCaseShallow } from '../../../core_plugins/kibana/common/lib/case_conversion'; +import { keysToCamelCaseShallow, keysToSnakeCaseShallow } from '../../../utils/case_conversion'; import _ from 'lodash'; import angular from 'angular'; import chrome from 'ui/chrome'; diff --git a/src/core_plugins/kibana/common/lib/__tests__/case_conversion.js b/src/utils/__tests__/case_conversion.js similarity index 100% rename from src/core_plugins/kibana/common/lib/__tests__/case_conversion.js rename to src/utils/__tests__/case_conversion.js diff --git a/src/core_plugins/kibana/common/lib/case_conversion.js b/src/utils/case_conversion.js similarity index 100% rename from src/core_plugins/kibana/common/lib/case_conversion.js rename to src/utils/case_conversion.js