diff --git a/__performance__/grafana/dashboards/one-app-classic.json b/__performance__/grafana/dashboards/one-app-classic.json index b88c8fe5b..9a49a2757 100644 --- a/__performance__/grafana/dashboards/one-app-classic.json +++ b/__performance__/grafana/dashboards/one-app-classic.json @@ -15,7 +15,7 @@ "editable": true, "gnetId": null, "graphTooltip": 0, - "iteration": 1598299567756, + "iteration": 1610468516396, "links": [], "panels": [ { @@ -250,8 +250,11 @@ "linewidth": 1, "links": [], "nullPointMode": "null", + "options": { + "alertThreshold": true + }, "percentage": false, - "pluginVersion": "7.1.1", + "pluginVersion": "7.3.6", "pointradius": 5, "points": false, "renderer": "flot", @@ -345,8 +348,11 @@ "lines": true, "linewidth": 1, "nullPointMode": "null", + "options": { + "alertThreshold": true + }, "percentage": false, - "pluginVersion": "7.1.1", + "pluginVersion": "7.3.6", "pointradius": 2, "points": false, "renderer": "flot", @@ -437,8 +443,11 @@ "lines": true, "linewidth": 1, "nullPointMode": "null", + "options": { + "alertThreshold": true + }, "percentage": false, - "pluginVersion": "7.1.1", + "pluginVersion": "7.3.6", "pointradius": 2, "points": false, "renderer": "flot", @@ -531,8 +540,11 @@ "linewidth": 1, "links": [], "nullPointMode": "null", + "options": { + "alertThreshold": true + }, "percentage": false, - "pluginVersion": "7.1.1", + "pluginVersion": "7.3.6", "pointradius": 5, "points": false, "renderer": "flot", @@ -640,8 +652,11 @@ "linewidth": 1, "links": [], "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, "percentage": false, - "pluginVersion": "7.1.1", + "pluginVersion": "7.3.6", "pointradius": 5, "points": false, "renderer": "flot", @@ -736,8 +751,11 @@ "linewidth": 1, "links": [], "nullPointMode": "null", + "options": { + "alertThreshold": true + }, "percentage": false, - "pluginVersion": "7.1.1", + "pluginVersion": "7.3.6", "pointradius": 5, "points": false, "renderer": "flot", @@ -796,6 +814,105 @@ "alignLevel": null } }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$datasource", + "description": "Estimated size of the language pack cache on the server", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 21 + }, + "hiddenSeries": false, + "id": 48, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.3.6", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "oneapp_intl_cache_size_total", + "instant": false, + "interval": "", + "legendFormat": "Server Langpack cache: {{podname}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Language Pack Server Cache", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:466", + "format": "decbytes", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:467", + "format": "decbits", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, { "collapsed": false, "datasource": null, @@ -803,7 +920,7 @@ "h": 1, "w": 24, "x": 0, - "y": 21 + "y": 29 }, "id": 4, "panels": [], @@ -829,7 +946,7 @@ "h": 8, "w": 24, "x": 0, - "y": 22 + "y": 30 }, "hiddenSeries": false, "id": 8, @@ -848,8 +965,11 @@ "linewidth": 1, "links": [], "nullPointMode": "null", + "options": { + "alertThreshold": true + }, "percentage": false, - "pluginVersion": "7.1.1", + "pluginVersion": "7.3.6", "pointradius": 5, "points": false, "renderer": "flot", @@ -942,7 +1062,7 @@ "h": 8, "w": 12, "x": 0, - "y": 30 + "y": 38 }, "hiddenSeries": false, "id": 6, @@ -959,8 +1079,11 @@ "linewidth": 1, "links": [], "nullPointMode": "null", + "options": { + "alertThreshold": true + }, "percentage": false, - "pluginVersion": "7.1.1", + "pluginVersion": "7.3.6", "pointradius": 5, "points": false, "renderer": "flot", @@ -1037,7 +1160,7 @@ "h": 8, "w": 12, "x": 12, - "y": 30 + "y": 38 }, "hiddenSeries": false, "id": 2, @@ -1054,8 +1177,11 @@ "linewidth": 1, "links": [], "nullPointMode": "null", + "options": { + "alertThreshold": true + }, "percentage": false, - "pluginVersion": "7.1.1", + "pluginVersion": "7.3.6", "pointradius": 5, "points": false, "renderer": "flot", @@ -1145,7 +1271,7 @@ "h": 1, "w": 24, "x": 0, - "y": 38 + "y": 46 }, "id": 14, "panels": [], @@ -1170,7 +1296,7 @@ "h": 8, "w": 24, "x": 0, - "y": 39 + "y": 47 }, "hiddenSeries": false, "id": 16, @@ -1187,8 +1313,11 @@ "linewidth": 1, "links": [], "nullPointMode": "null", + "options": { + "alertThreshold": true + }, "percentage": false, - "pluginVersion": "7.1.1", + "pluginVersion": "7.3.6", "pointradius": 5, "points": false, "renderer": "flot", @@ -1254,7 +1383,7 @@ "h": 1, "w": 24, "x": 0, - "y": 47 + "y": 55 }, "id": 10, "panels": [], @@ -1279,7 +1408,7 @@ "h": 9, "w": 24, "x": 0, - "y": 48 + "y": 56 }, "hiddenSeries": false, "id": 12, @@ -1296,8 +1425,11 @@ "linewidth": 1, "links": [], "nullPointMode": "null", + "options": { + "alertThreshold": true + }, "percentage": false, - "pluginVersion": "7.1.1", + "pluginVersion": "7.3.6", "pointradius": 5, "points": false, "renderer": "flot", @@ -1384,7 +1516,7 @@ "h": 1, "w": 24, "x": 0, - "y": 57 + "y": 65 }, "id": 42, "panels": [], @@ -1409,7 +1541,7 @@ "h": 9, "w": 12, "x": 0, - "y": 58 + "y": 66 }, "hiddenSeries": false, "id": 40, @@ -1426,8 +1558,11 @@ "linewidth": 1, "links": [], "nullPointMode": "null", + "options": { + "alertThreshold": true + }, "percentage": false, - "pluginVersion": "7.1.1", + "pluginVersion": "7.3.6", "pointradius": 5, "points": false, "renderer": "flot", @@ -1510,7 +1645,7 @@ "h": 9, "w": 12, "x": 12, - "y": 58 + "y": 66 }, "heatmap": {}, "hideZeroBuckets": false, @@ -1569,6 +1704,7 @@ "text": "10m", "value": "10m" }, + "error": null, "hide": 0, "includeAll": false, "label": "Rate Granularity:", @@ -1622,6 +1758,7 @@ "text": "Prometheus", "value": "Prometheus" }, + "error": null, "hide": 2, "includeAll": false, "label": null, @@ -1667,5 +1804,5 @@ "timezone": "browser", "title": "One App Classic", "uid": "100000073", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/__tests__/integration/__snapshots__/one-app.spec.js.snap b/__tests__/integration/__snapshots__/one-app.spec.js.snap index 09a89fddd..4f9d7eb08 100644 --- a/__tests__/integration/__snapshots__/one-app.spec.js.snap +++ b/__tests__/integration/__snapshots__/one-app.spec.js.snap @@ -71,6 +71,7 @@ Array [ "oneapp_holocron_module_map_poll_total", "oneapp_holocron_module_map_poll_wait_seconds", "oneapp_holocron_module_map_updated_total", + "oneapp_intl_cache_size_total", "oneapp_version_info", "process_cpu_seconds_total", "process_cpu_system_seconds_total", diff --git a/__tests__/server/metrics/__snapshots__/intl-cache.spec.js.snap b/__tests__/server/metrics/__snapshots__/intl-cache.spec.js.snap new file mode 100644 index 000000000..b950b937c --- /dev/null +++ b/__tests__/server/metrics/__snapshots__/intl-cache.spec.js.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`intl-cache creates a gauge named size 1`] = ` +Array [ + Object { + "help": "estimated intl server cache size", + "name": "oneapp_intl_cache_size_total", + }, +] +`; + +exports[`intl-cache exports the metric names 1`] = ` +Object { + "cacheSizeCollector": [Function], + "default": Object { + "cacheSize": "oneapp_intl_cache_size_total", + }, +} +`; diff --git a/__tests__/server/metrics/index.spec.js b/__tests__/server/metrics/index.spec.js index bb879259c..0f929c5c3 100644 --- a/__tests__/server/metrics/index.spec.js +++ b/__tests__/server/metrics/index.spec.js @@ -16,11 +16,13 @@ import * as appVersion from '../../../src/server/metrics/app-version'; import holocron from '../../../src/server/metrics/holocron'; +import intlCache from '../../../src/server/metrics/intl-cache'; import * as index from '../../../src/server/metrics'; jest.mock('../../../src/server/metrics/app-version', () => ({ appVersion: 'app-version' })); jest.mock('../../../src/server/metrics/holocron', () => ({ holcron: 'holocron' })); +jest.mock('../../../src/server/metrics/intl-cache', () => ({ intlCache: 'intl-cache' })); describe('index', () => { // counters @@ -53,4 +55,6 @@ describe('index', () => { it('exports appVersion', () => expect(index).toHaveProperty('appVersion', appVersion)); it('exports holocron', () => expect(index).toHaveProperty('holocron', holocron)); + + it('exports intlCache', () => expect(index).toHaveProperty('intlCache', intlCache)); }); diff --git a/__tests__/server/metrics/intl-cache.spec.js b/__tests__/server/metrics/intl-cache.spec.js new file mode 100644 index 000000000..a40ab308a --- /dev/null +++ b/__tests__/server/metrics/intl-cache.spec.js @@ -0,0 +1,56 @@ +/* + * Copyright 2021 American Express Travel Related Services Company, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +jest.mock('@americanexpress/one-app-ducks/lib/intl/server-cache', () => ({ + getEstimatedSize: jest.fn(() => 'mocked'), +})); + +describe('intl-cache', () => { + let createGauge; + let setGauge; + + function load() { + jest.resetModules(); + + // jest.mock('../../../package.json', () => ({ version: '1.2.3' })); + jest.mock('../../../src/server/metrics/counters'); + + jest.mock('../../../src/server/metrics/gauges'); + ({ createGauge, setGauge } = require('../../../src/server/metrics/gauges')); + + return require('../../../src/server/metrics/intl-cache'); + } + + it('creates a gauge named size', () => { + load(); + expect(createGauge).toHaveBeenCalled(); + expect(createGauge.mock.calls[0][0]).toHaveProperty('name'); + expect(createGauge.mock.calls[0][0].name).toMatch('size'); + expect(createGauge.mock.calls[0]).toMatchSnapshot(); + }); + + it('exports the metric names', () => { + expect(load()).toMatchSnapshot(); + }); + + describe('cacheSizeCollector', () => { + it('updates gauge to include estimated size', () => { + const { cacheSizeCollector } = load(); + cacheSizeCollector(); + expect(setGauge).toHaveBeenCalledWith('oneapp_intl_cache_size_total', 'mocked'); + }); + }); +}); diff --git a/__tests__/server/metricsServer.spec.js b/__tests__/server/metricsServer.spec.js index 2bc6ffa04..69e236dbb 100644 --- a/__tests__/server/metricsServer.spec.js +++ b/__tests__/server/metricsServer.spec.js @@ -17,6 +17,9 @@ import request from 'supertest'; describe('metricsServer', () => { + jest.mock('../../src/server/metrics/intl-cache', () => ({ + cacheSizeCollector: 'cacheSizeCollector', + })); jest.spyOn(console, 'log').mockImplementation(() => {}); jest.spyOn(console, 'warn').mockImplementation(() => {}); @@ -43,6 +46,11 @@ describe('metricsServer', () => { load(); expect(client.collectDefaultMetrics).toHaveBeenCalledTimes(1); }); + + it('registers intl cache collector', () => { + load(); + expect(client.register.registerCollector).toHaveBeenCalledWith('cacheSizeCollector'); + }); }); describe('unknown routes', () => { diff --git a/package-lock.json b/package-lock.json index 43ebc03cc..8c936e144 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20016,6 +20016,16 @@ "integrity": "sha512-kRRwkRA4IRmcZcmNF5LTDTZw2s1SK23lT7VfU2GxoRA/WF3Ga5hvja1pehN3HTJte4glTcnh+8vTOmLViO2wqQ==", "requires": { "prom-client": "^12.0.0" + }, + "dependencies": { + "prom-client": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-12.0.0.tgz", + "integrity": "sha512-JbzzHnw0VDwCvoqf8y1WDtq4wSBAbthMB1pcVI/0lzdqHGJI3KBJDXle70XK+c7Iv93Gihqo0a5LlOn+g8+DrQ==", + "requires": { + "tdigest": "^0.1.1" + } + } } }, "optionator": { diff --git a/src/server/metrics/__mocks__/index.js b/src/server/metrics/__mocks__/index.js index f17c9b0d4..ae6fedbda 100644 --- a/src/server/metrics/__mocks__/index.js +++ b/src/server/metrics/__mocks__/index.js @@ -25,6 +25,10 @@ const holocron = { moduleMapPollWait: 'module_map_poll_wait', }; +const intlCache = { + cacheSize: 'cache_size', +}; + const appVersion = { info: 'info' }; export { @@ -39,4 +43,5 @@ export { // metrics holocron, appVersion, + intlCache, }; diff --git a/src/server/metrics/index.js b/src/server/metrics/index.js index 73bd7d2c4..42c0b6723 100644 --- a/src/server/metrics/index.js +++ b/src/server/metrics/index.js @@ -18,6 +18,7 @@ import { incrementCounter } from './counters'; import { incrementGauge, setGauge, resetGauge } from './gauges'; import holocron from './holocron'; +import intlCache from './intl-cache'; import * as appVersion from './app-version'; export { @@ -32,4 +33,5 @@ export { // metrics holocron, appVersion, + intlCache, }; diff --git a/src/server/metrics/intl-cache.js b/src/server/metrics/intl-cache.js new file mode 100644 index 000000000..27e65066a --- /dev/null +++ b/src/server/metrics/intl-cache.js @@ -0,0 +1,34 @@ +/* + * Copyright 2021 American Express Travel Related Services Company, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import { getEstimatedSize } from '@americanexpress/one-app-ducks/lib/intl/server-cache'; +import { createGauge, setGauge } from './gauges'; +import createMetricNamespace from './create-metric-namespace'; + +const intlServerCacheNamespace = createMetricNamespace('intl'); + +createGauge({ + name: intlServerCacheNamespace('cache_size', 'total'), + help: 'estimated intl server cache size', +}); + +const metricNames = intlServerCacheNamespace.getMetricNames(); + +export const cacheSizeCollector = () => { + setGauge(metricNames.cacheSize, getEstimatedSize()); +}; + +export default metricNames; diff --git a/src/server/metricsServer.js b/src/server/metricsServer.js index e6300c53a..8e75dbbd1 100644 --- a/src/server/metricsServer.js +++ b/src/server/metricsServer.js @@ -20,8 +20,10 @@ import { register as metricsRegister, collectDefaultMetrics } from 'prom-client' import logging from './utils/logging/serverMiddleware'; import healthCheck from './middleware/healthCheck'; +import { cacheSizeCollector } from './metrics/intl-cache'; collectDefaultMetrics(); +metricsRegister.registerCollector(cacheSizeCollector); export function createMetricsServer() { const app = express();