Skip to content

Commit

Permalink
feat(metrics): Collect healthcheck results
Browse files Browse the repository at this point in the history
  • Loading branch information
bripkens committed May 2, 2017
1 parent 518074f commit ff748fc
Show file tree
Hide file tree
Showing 10 changed files with 189 additions and 6 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## Unreleased
- Collect healthcheck results.

## 1.23.1
- Support CPU profiling in Node.js >=7.0.0

Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
"v8-profiler": "5.7.0"
},
"devDependencies": {
"admin": "^1.4.0",
"admin-plugin-healthcheck": "^1.1.0",
"bluebird": "3.4.6",
"body-parser": "1.15.2",
"chai": "3.3.0",
Expand All @@ -62,6 +64,7 @@
"lodash": "4.15.0",
"mocha": "2.3.3",
"mongodb": "2.2.9",
"morgan": "^1.8.1",
"proxyquire": "1.7.3",
"request": "2.74.0",
"request-promise": "4.1.1",
Expand Down
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ module.exports = exports = function start(config) {
require('./agent/opts').init(config);
require('./actions/profiling/cpu').init(config);
require('./tracing').init(config);
require('./states/agentready').init(config);

var logger = log.getLogger('index');

Expand Down
58 changes: 58 additions & 0 deletions src/metrics/healthchecks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
'use strict';

var requireHook = require('../util/requireHook');
var logger = require('../logger').getLogger('metrics/healthchecks');

var timeBetweenHealthcheckCalls;
var healthy = 1;
var unhealthy = 0;

var adminPluginHealthcheck;
var timeoutHandle;

exports.payloadPrefix = 'healthchecks';
exports.currentPayload = {};

requireHook.on('admin-plugin-healthcheck', function onAdminPluginHealthcheckLoaded(_adminPluginHealthcheck) {
adminPluginHealthcheck = _adminPluginHealthcheck;
});

exports.activate = function(config) {
timeBetweenHealthcheckCalls = config.timeBetweenHealthcheckCalls || 3000;

if (adminPluginHealthcheck != null) {
gatherHealthcheckResults();
}
};

function gatherHealthcheckResults() {
adminPluginHealthcheck.getHealthCheckResult()
.then(function onHealthcheckResults(adminHealthcheckResults) {
var results = {};
var previousResults = exports.currentPayload;

for (var key in adminHealthcheckResults) {
if (adminHealthcheckResults.hasOwnProperty(key)) {
var result = adminHealthcheckResults[key];
var checkHealthy = result.healthy ? healthy : unhealthy;
var changed = previousResults[key] == null || previousResults[key].healthy !== checkHealthy;
results[key] = {
healthy: checkHealthy,
since: changed ? new Date().getTime() : previousResults[key].since
};
}
}

exports.currentPayload = results;
timeoutHandle = setTimeout(gatherHealthcheckResults, timeBetweenHealthcheckCalls);
})
.catch(function onHealthcheckResultFailure(err) {
exports.currentPayload = {};
logger.warn('Unexpected error while getting healthcheck results', err);
timeoutHandle = setTimeout(gatherHealthcheckResults, timeBetweenHealthcheckCalls);
});
}

exports.deactivate = function() {
clearTimeout(timeoutHandle);
};
8 changes: 6 additions & 2 deletions src/states/agentready.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ var modules = fs.readdirSync(metricsBaseDir)
return require(path.join(metricsBaseDir, moduleName));
});

var config;
var resendFullDataEveryXTransmissions = 300; /* about every 5 minutes */

var transmissionsSinceLastFullDataEmit = 0;
var previousTransmittedValue = undefined;


module.exports = {
module.exports = exports = {
enter: function(ctx) {
transmissionsSinceLastFullDataEmit = 0;

Expand Down Expand Up @@ -72,10 +73,13 @@ module.exports = {
}
};

exports.init = function init(_config) {
config = _config;
};

function enableAllSensors() {
modules.forEach(function(mod) {
mod.activate();
mod.activate(config);
});
}

Expand Down
13 changes: 10 additions & 3 deletions test/apps/agentStub.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
/* eslint-disable */

var express = require('express');
var bodyParser = require('body-parser');
var express = require('express');
var morgan = require('morgan');
var app = express();

var logPrefix = 'Agent Stub (' + process.pid + '):\t';
var discoveries = {};
var requests = {};
var retrievedData = {
Expand All @@ -12,6 +14,10 @@ var retrievedData = {
responses: []
};

if (process.env.WITH_STDOUT) {
app.use(morgan(logPrefix + ':method :url :status'));
}

app.use(bodyParser.json());


Expand Down Expand Up @@ -103,7 +109,8 @@ app.delete('/retrievedData', function(req, res) {
log('Clearing retrieved data');
retrievedData = {
runtime: [],
traces: []
traces: [],
responses: []
};
res.sendStatus(200);
});
Expand Down Expand Up @@ -135,6 +142,6 @@ app.listen(process.env.AGENT_PORT, function() {

function log() {
var args = Array.prototype.slice.call(arguments);
args[0] = 'Agent Stub (' + process.pid + '):\t' + args[0];
args[0] = logPrefix + args[0];
console.log.apply(console, args);
}
34 changes: 34 additions & 0 deletions test/apps/express.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,53 @@ require('../../')({
agentPort: process.env.AGENT_PORT,
level: 'info',
tracing: {
timeBetweenHealthcheckCalls: 1000,
enabled: process.env.TRACING_ENABLED === 'true',
forceTransmissionStartingAt: 1,
stackTraceLength: process.env.STACK_TRACE_LENGTH != null ? parseInt(process.env.STACK_TRACE_LENGTH, 10) : 10
}
});

var express = require('express');
var semver = require('semver');
var app = express();

var healthcheckFunction = function() {
return 'OK!';
};

if (semver.satisfies(process.versions.node, '>=6.0.0')) {
require('admin').configure({
plugins: [
require('admin-plugin-healthcheck')({
checks: {
configurable: function() {
healthcheckFunction();
}
}
})
]
});
}

app.get('/return-instana-trace-id', function(req, res) {
res.send(req.get('x-instana-t'));
});

app.post('/admin/set-to-unhealthy', function(req, res) {
healthcheckFunction = function() {
throw new Error('Explicit healthcheck failure');
};
res.send('OK');
});

app.post('/admin/set-to-healthy', function(req, res) {
healthcheckFunction = function() {
return 'OK';
};
res.send('OK');
});

app.use(function(req, res) {
log(req.method, req.url);
var delay = parseInt(req.query.delay || 0, 10);
Expand Down
14 changes: 14 additions & 0 deletions test/apps/expressControls.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,17 @@ exports.sendRequest = function(opts) {
throw reason;
});
};

exports.setHealthy = function() {
return request({
method: 'POST',
url: 'http://127.0.0.1:' + appPort + '/admin/set-to-healthy'
});
};

exports.setUnhealthy = function() {
return request({
method: 'POST',
url: 'http://127.0.0.1:' + appPort + '/admin/set-to-unhealthy'
});
};
8 changes: 7 additions & 1 deletion test/apps/expressMongodb.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,17 @@ require('../../')({
var MongoClient = require('mongodb').MongoClient;
var bodyParser = require('body-parser');
var express = require('express');
var morgan = require('morgan');
var assert = require('assert');

var app = express();
var db;
var collection;
var logPrefix = 'Express / MongoDB App (' + process.pid + '):\t';

if (process.env.WITH_STDOUT) {
app.use(morgan(logPrefix + ':method :url :status'));
}

app.use(bodyParser.json());

Expand Down Expand Up @@ -77,6 +83,6 @@ app.listen(process.env.APP_PORT, function() {

function log() {
var args = Array.prototype.slice.call(arguments);
args[0] = 'Express / MongoDB App (' + process.pid + '):\t' + args[0];
args[0] = logPrefix + args[0];
console.log.apply(console, args);
}
53 changes: 53 additions & 0 deletions test/metrics/healthchecks_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
'use strict';

var expect = require('chai').expect;
var semver = require('semver');

var agentStubControls = require('../apps/agentStubControls');
var expressControls = require('../apps/expressControls');
var config = require('../config');
var utils = require('../utils');

describe('metrics/healthchecks', function() {
// admin uses JavaScript language features which aren't available in all
// Node.js versions
if (!semver.satisfies(process.versions.node, '>=6.0.0')) {
return;
}
this.timeout(config.getTestTimeout());

var start = new Date().getTime();

agentStubControls.registerTestHooks();
expressControls.registerTestHooks({
enableTracing: false
});

beforeEach(function() {
return agentStubControls.waitUntilAppIsCompletelyInitialized(expressControls.getPid());
});

it('must report health status', function() {
var healthyTimestamp;
return utils.retry(function() {
return agentStubControls.getLastMetricValue(expressControls.getPid(), ['healthchecks'])
.then(function(healthchecks) {
expect(healthchecks.configurable.healthy).to.equal(1);
expect(healthchecks.configurable.since).to.be.gte(start);
healthyTimestamp = healthchecks.configurable.since;
});
})
.then(function() {
return expressControls.setUnhealthy();
})
.then(function() {
return utils.retry(function() {
return agentStubControls.getLastMetricValue(expressControls.getPid(), ['healthchecks'])
.then(function(healthchecks) {
expect(healthchecks.configurable.healthy).to.equal(0);
expect(healthchecks.configurable.since).to.be.gt(healthyTimestamp);
});
});
});
});
});

0 comments on commit ff748fc

Please # to comment.