From 1cf8c86acbc1e7a12bf5b2bcd66734bae90705b1 Mon Sep 17 00:00:00 2001 From: Konstantin Pogorelov Date: Sat, 25 Aug 2018 13:06:25 +0200 Subject: [PATCH] Squashed commit of the following: commit bc65dc45cbc3a8301038e13921f7ce4c2c9529a2 Author: Konstantin Pogorelov Date: Sat Aug 25 12:57:15 2018 +0200 #16 fix typo in docs commit 2003e7743ffbbf186679f042052ed33e028ba0c3 Author: Konstantin Pogorelov Date: Sat Aug 25 12:44:24 2018 +0200 #16 fix unit test (cluster_metrics/metrics_cluster), cover the error branch commit 568c87216a5ccd2482df2889f74860d481452f36 Author: Konstantin Pogorelov Date: Sat Aug 25 12:42:53 2018 +0200 #16 remove unnecessary check for existance after new, break on error with 500 commit 98be36244eb55774ff24151c64e2db11333a6416 Author: Konstantin Pogorelov Date: Sat Aug 25 12:40:15 2018 +0200 #16 make the cluster example runnable --- README.md | 36 +++++++++++++++++++++++++++++++ spec/index.spec.js | 53 +++++++++++++++++++++++++++++++++------------- src/index.js | 17 ++++++--------- 3 files changed, 81 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 400d26b..ffe393f 100644 --- a/README.md +++ b/README.md @@ -192,6 +192,42 @@ app.use(/* your middleware */); app.listen(3000); ``` +## using with cluster + +You'll need to use an additional **clusterMetrics()** middleware. + +In the example below the master process will expose an API with a single endpoint `/metrics` +which returns an aggregate of all metrics from all the workers. + +``` javascript +const cluster = require('cluster'); +const promBundle = require('./src/index'); +const numCPUs = Math.max(2, require('os').cpus().length); +const express = require('express'); + +if (cluster.isMaster) { + for (let i = 1; i < numCPUs; i++) { + cluster.fork(); + } + + const metricsApp = express(); + metricsApp.use('/metrics', promBundle.clusterMetrics()); + metricsApp.listen(9999); + + console.log('cluster metrics listening on 9999'); + console.log('call localhost:9999/metrics for aggregated metrics'); +} else { + const app = express(); + app.use(promBundle({ + autoregister: false, // disable /metrics for single workers + includeMethod: true + })); + app.use((req, res) => res.send(`hello from pid ${process.pid}\n`)); + app.listen(3000); + console.log(`worker ${process.pid} listening on 3000`); +} +``` + ## using with kraken.js Here is meddleware config sample, which can be used in a standard **kraken.js** application. diff --git a/spec/index.spec.js b/spec/index.spec.js index 73877f0..e013e19 100644 --- a/spec/index.spec.js +++ b/spec/index.spec.js @@ -8,6 +8,7 @@ const Koa = require('koa'); const c2k = require('koa-connect'); const supertestKoa = require('supertest-koa-agent'); const promClient = require('prom-client'); +const cluster = require('cluster'); describe('index', () => { beforeEach(() => { @@ -259,20 +260,6 @@ describe('index', () => { }); }); - describe('usage of clusterMetrics()', () => { - it('clusterMetrics returns metrics for aggregator', (done) => { - const app = express(); - app.use('/cluster_metrics', bundle.clusterMetrics()); - const agent = supertest(app); - agent - .get('/metrics_cluster') - .expect(200) - .end((err, res) => { - done(err); - }); - }); - }); - it('formatStatusCode can be overridden', done => { const app = express(); const instance = bundle({ @@ -397,4 +384,40 @@ describe('index', () => { }); expect(spy).toHaveBeenCalledWith({timeout: 3000}); }); -}); \ No newline at end of file + + describe('usage of clusterMetrics()', () => { + it('clusterMetrics returns 200 even without a cluster', (done) => { + const app = express(); + + cluster.workers = []; + + app.use('/cluster_metrics', bundle.clusterMetrics()); + const agent = supertest(app); + agent + .get('/cluster_metrics') + .end((err, res) => { + expect(res.status).toBe(200); + done(); + }); + }); + + it('clusterMetrics returns 500 in case of an error', (done) => { + const app = express(); + app.use('/cluster_metrics', bundle.clusterMetrics()); + const agent = supertest(app); + + // create a fake worker, which would not respond in time + cluster.workers = [{send: () => {}}]; + + const errorSpy = spyOn(console, 'error'); // mute console.error + + agent + .get('/cluster_metrics') + .end((err, res) => { + expect(res.status).toBe(500); + expect(errorSpy).toHaveBeenCalled(); + done(); + }); + }, 6000); + }); +}); diff --git a/src/index.js b/src/index.js index bfcdcf0..eb033d5 100644 --- a/src/index.js +++ b/src/index.js @@ -42,17 +42,14 @@ function clusterMetrics() { const aggregatorRegistry = new promClient.AggregatorRegistry(); const metricsMiddleware = function(req, res, next) { - if (aggregatorRegistry) { - aggregatorRegistry.clusterMetrics((err, clusterMetrics) => { - if (err) { - console.error(err); - } - res.set('Content-Type', aggregatorRegistry.contentType); - res.send(clusterMetrics); - }); - } else { - return next(); + aggregatorRegistry.clusterMetrics((err, clusterMetrics) => { + if (err) { + console.error(err); + return res.sendStatus(500); } + res.set('Content-Type', aggregatorRegistry.contentType); + res.send(clusterMetrics); + }); }; return metricsMiddleware;