UNPKG

strong-supervisor

Version:

supervisor and monitor for node.js applications

149 lines (127 loc) 3.66 kB
// Copyright IBM Corp. 2014,2016. All Rights Reserved. // Node module: strong-supervisor // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 'use strict'; var Statsd = require('strong-statsd'); var agent = require('./agent'); var app = require('./app'); var assert = require('assert'); var cluster = require('cluster'); var master = require('strong-cluster-control'); var debug = require('./debug')('metrics'); var expandString = require('./expander').expand; try { var syslog = require('strong-fork-syslog'); } catch (er) { debug('syslog optional dep not present'); } function sendMetrics(parentCtl, callback) { callback = callback || function() {}; // Scenarios: // no STRONGLOOP_METRICS, return // // called from master: // needs to .start() with expanded scope // if invalid, warn, and clear env... so children don't warn // needs to wait for start // needs to reset STRONGLOOP_METRICS *before* starting cluster // statsd://:PORT/SCOPE-TEMPLATE // // // called from worker: // needs to .start() with expanded scope (will always be a statsd: URL) // // called from single-instance: // needs to .start() with expanded scope (can reset env, its ok) var self = this; var endpoints = parse(process.env.STRONGLOOP_METRICS); function parse(url) { if (url == null) return []; try { return JSON.parse(url); } catch (er) { return [url]; } } if (cluster.isMaster) { endpoints.push('internal:'); } if (!endpoints.length) { process.nextTick(callback); return false; } var server = Statsd({ scope: '%a.%h.%w', expandScope: expandScope, flushInterval: process.env.STRONGLOOP_FLUSH_INTERVAL, syslog: syslog, }); debug('metrics endpoints: %j', endpoints); endpoints.forEach(function(endpoint) { try { server.backend(endpoint); if (cluster.isMaster) { self.logger.info('supervisor reporting metrics to `%s`', endpoint); } } catch (er) { console.error('Invalid metrics endpoint `%s`: %s', endpoint, er.message); process.exit(1); } }); // Internal metrics support, forwarded over node ipc from cluster master // to parent. if (parentCtl) { server.on('metrics', forwardMetrics); } server.start(function(er) { assert.ifError(er); process.env.STRONGLOOP_METRICS = server.url; agent().use(function(name, value) { server.send(name, value); }); return callback(server); }); if (this.setupChildLogger && server.child) { var statsdWorker = { process: { stdout: server.child.stdout, stderr: server.child.stderr, pid: process.pid, }, id: 'statsd', }; this.setupChildLogger(statsdWorker); } function expandScope(scope) { return expandString(scope, { id: (cluster.worker && cluster.worker.id) | 0, pid: process.pid, hostname: app.host(), appName: app.name(), }); } function forwardMetrics(metrics) { debug('received: %j', metrics); parentCtl.notify({cmd: 'metrics', metrics: injectIdentifiers(metrics)}); } return true; } function injectIdentifiers(metrics) { var wid; var m; for (wid in metrics.processes) { m = metrics.processes[wid]; m.wid = wid; if (cluster.workers[wid]) { m.pid = cluster.workers[wid].process.pid; m.pst = cluster.workers[wid].startTime; } else { m.pid = process.pid; m.pst = master.startTime; } } return metrics; } module.exports = sendMetrics;