diff options
Diffstat (limited to 'node.d/node_modules/netdata.js')
-rw-r--r-- | node.d/node_modules/netdata.js | 1235 |
1 files changed, 636 insertions, 599 deletions
diff --git a/node.d/node_modules/netdata.js b/node.d/node_modules/netdata.js index 6183993b5..11202061e 100644 --- a/node.d/node_modules/netdata.js +++ b/node.d/node_modules/netdata.js @@ -1,5 +1,10 @@ 'use strict'; +// netdata +// real-time performance and health monitoring, done right! +// (C) 2016 Costa Tsaousis <costa@tsaousis.gr> +// GPL v3+ + var url = require('url'); var http = require('http'); var util = require('util'); @@ -8,609 +13,641 @@ var util = require('util'); var netdata = require('netdata'); var example_chart = { - id: 'id', // the unique id of the chart - name: 'name', // the name of the chart - title: 'title', // the title of the chart - units: 'units', // the units of the chart dimensions - family: 'family', // the family of the chart - context: 'context', // the context of the chart - type: netdata.chartTypes.line, // the type of the chart - priority: 0, // the priority relative to others in the same family - update_every: 1, // the expected update frequency of the chart - dimensions: { - 'dim1': { - id: 'dim1', // the unique id of the dimension - name: 'name', // the name of the dimension - algorithm: netdata.chartAlgorithms.absolute, // the id of the netdata algorithm - multiplier: 1, // the multiplier - divisor: 1, // the divisor - hidden: false, // is hidden (boolean) - }, - 'dim2': { - id: 'dim2', // the unique id of the dimension - name: 'name', // the name of the dimension - algorithm: 'absolute', // the id of the netdata algorithm - multiplier: 1, // the multiplier - divisor: 1, // the divisor - hidden: false, // is hidden (boolean) - } - // add as many dimensions as needed - } + id: 'id', // the unique id of the chart + name: 'name', // the name of the chart + title: 'title', // the title of the chart + units: 'units', // the units of the chart dimensions + family: 'family', // the family of the chart + context: 'context', // the context of the chart + type: netdata.chartTypes.line, // the type of the chart + priority: 0, // the priority relative to others in the same family + update_every: 1, // the expected update frequency of the chart + dimensions: { + 'dim1': { + id: 'dim1', // the unique id of the dimension + name: 'name', // the name of the dimension + algorithm: netdata.chartAlgorithms.absolute, // the id of the netdata algorithm + multiplier: 1, // the multiplier + divisor: 1, // the divisor + hidden: false, // is hidden (boolean) + }, + 'dim2': { + id: 'dim2', // the unique id of the dimension + name: 'name', // the name of the dimension + algorithm: 'absolute', // the id of the netdata algorithm + multiplier: 1, // the multiplier + divisor: 1, // the divisor + hidden: false, // is hidden (boolean) + } + // add as many dimensions as needed + } }; */ var netdata = { - options: { - filename: __filename, - DEBUG: false, - update_every: 1, - }, - - chartAlgorithms: { - incremental: 'incremental', - absolute: 'absolute', - percentage_of_absolute_row: 'percentage-of-absolute-row', - percentage_of_incremental_row: 'percentage-of-incremental-row' - }, - - chartTypes: { - line: 'line', - area: 'area', - stacked: 'stacked' - }, - - services: new Array(), - modules_configuring: 0, - charts: {}, - - - processors: { - http: { - name: 'http', - - process: function(service, callback) { - if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': making ' + this.name + ' request: ' + netdata.stringify(service.request)); - - var req = http.request(service.request, function(response) { - if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': got server response...'); - - var end = false; - var data = ''; - response.setEncoding('utf8'); - - if(response.statusCode !== 200) { - if(end === false) { - service.error('Got HTTP code ' + response.statusCode + ', failed to get data.'); - end = true; - callback(null); - } - } - - response.on('data', function(chunk) { - if(end === false) data += chunk; - }); - - response.on('error', function() { - if(end === false) { - service.error(': Read error, failed to get data.'); - end = true; - callback(null); - } - }); - - response.on('end', function() { - if(end === false) { - if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': read completed.'); - end = true; - callback(data); - } - }); - }); - - req.on('error', function(e) { - service.error('Failed to make request: ' + netdata.stringify(service.request) + ', message: ' + e.message); - callback(null); - }); - - // write data to request body - if(typeof service.postData !== 'undefined' && service.request.method === 'POST') { - if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': posting data: ' + service.postData); - req.write(service.postData); - } - - req.end(); - } - } - }, - - stringify: function(obj) { - return util.inspect(obj, {depth: 10}); - }, - - // show debug info, if debug is enabled - debug: function(msg) { - if(this.options.DEBUG === true) { - var now = new Date(); - console.error(now.toString() + ': ' + netdata.options.filename + ': DEBUG: ' + ((typeof(msg) === 'object')?netdata.stringify(msg):msg).toString()); - } - }, - - // log an error - error: function(msg) { - var now = new Date(); - console.error(now.toString() + ': ' + netdata.options.filename + ': ERROR: ' + ((typeof(msg) === 'object')?netdata.stringify(msg):msg).toString()); - }, - - // send data to netdata - send: function(msg) { - console.log(msg.toString()); - }, - - service: function(service) { - if(typeof service === 'undefined') - service = {}; - - var now = new Date().getTime(); - - service._current_chart = null; // the current chart we work on - service._queue = ''; // data to be sent to netdata - - service.error_reported = false; // error log flood control - - service.added = false; // added to netdata.services - service.enabled = true; - service.updates = 0; - service.running = false; - service.started = 0; - service.ended = 0; - - if(typeof service.module === 'undefined') { - service.module = { name: 'not-defined-module' }; - service.error('Attempted to create service without a module.'); - service.enabled = false; - } - - if(typeof service.name === 'undefined') { - service.name = 'unnamed@' + service.module.name + '/' + now; - } - - if(typeof service.processor === 'undefined') - service.processor = netdata.processors.http; - - if(typeof service.update_every === 'undefined') - service.update_every = service.module.update_every; - - if(typeof service.update_every === 'undefined') - service.update_every = netdata.options.update_every; - - if(service.update_every < netdata.options.update_every) - service.update_every = netdata.options.update_every; - - // align the runs - service.next_run = now - (now % (service.update_every * 1000)); - - service.commit = function() { - if(this.added !== true) { - this.added = true; - - var now = new Date().getTime(); - while( this.next_run < now ) - this.next_run += (this.update_every * 1000); - - netdata.services.push(this); - if(netdata.options.DEBUG === true) netdata.debug(this.module.name + ': ' + this.name + ': service committed.'); - } - }; - - service.execute = function(callback) { - if(service.enabled === false) { - callback(null); - return; - } - - this.module.active++; - this.running = true; - this.started = new Date().getTime(); - this.updates++; - - if(netdata.options.DEBUG === true) - netdata.debug(this.module.name + ': ' + this.name + ': making ' + this.processor.name + ' request: ' + netdata.stringify(this)); - - this.processor.process(this, function(response) { - service.ended = new Date().getTime(); - service.duration = service.ended - service.started; - - if(typeof response === 'undefined') - response = null; - - if(response !== null) - service.errorClear(); - - if(netdata.options.DEBUG === true) - netdata.debug(service.module.name + ': ' + service.name + ': processing ' + service.processor.name + ' response (received in ' + (service.ended - service.started).toString() + ' ms)'); - - callback(service, response); - - service.running = false; - service.module.active--; - if(service.module.active < 0) { - service.module.active = 0; - if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': active module counter below zero.'); - } - - if(service.module.active === 0) { - // check if we run under configure - if(service.module.configure_callback !== null) { - if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': configuration finish callback called from processResponse().'); - var ccallback = service.module.configure_callback; - service.module.configure_callback = null; - ccallback(); - } - } - }); - }; - - service.update = function() { - if(netdata.options.DEBUG === true) netdata.debug(this.module.name + ': ' + this.name + ': starting data collection...'); - - this.module.update(this, function() { - if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': data collection ended in ' + service.duration.toString() + ' ms.'); - }); - }; - - service.error = function(message) { - if(this.error_reported === false) { - netdata.error(this.module.name + ': ' + this.name + ': ' + message); - this.error_reported = true; - } - else if(netdata.options.DEBUG === true) - netdata.debug(this.module.name + ': ' + this.name + ': ' + message); - }; - - service.errorClear = function() { - this.error_reported = false; - }; - - service.queue = function(txt) { - this._queue += txt + '\n'; - }; - - service._send_chart_to_netdata = function(chart) { - // internal function to send a chart to netdata - this.queue('CHART "' + chart.id + '" "' + chart.name + '" "' + chart.title + '" "' + chart.units + '" "' + chart.family + '" "' + chart.context + '" "' + chart.type + '" ' + chart.priority.toString() + ' ' + chart.update_every.toString()); - - for(var dim in chart.dimensions) { - var d = chart.dimensions[dim]; - - this.queue('DIMENSION "' + d.id + '" "' + d.name + '" "' + d.algorithm + '" ' + d.multiplier.toString() + ' ' + d.divisor.toString() + ' ' + ((d.hidden === true)?'hidden':'').toString()); - d._created = true; - d._updated = false; - } - - chart._created = true; - chart._updated = false; - }; - - // begin data collection for a chart - service.begin = function(chart) { - if(this._current_chart !== null && this._current_chart !== chart) { - this.error('Called begin() for chart ' + chart.id + ' while chart ' + this._current_chart.id + ' is still open. Closing it.'); - this.end(); - } - - if(typeof(chart.id) === 'undefined' || netdata.charts[chart.id] != chart) { - this.error('Called begin() for chart ' + chart.id + ' that is not mine. Where did you find it? Ignoring it.'); - return false; - } - - if(netdata.options.DEBUG === true) netdata.debug('setting current chart to ' + chart.id); - this._current_chart = chart; - this._current_chart._began = true; - - if(this._current_chart._dimensions_count !== 0) { - if(this._current_chart._created === false || this._current_chart._updated === true) - this._send_chart_to_netdata(this._current_chart); - - var now = this.ended; - this.queue('BEGIN ' + this._current_chart.id + ' ' + ((this._current_chart._last_updated > 0)?((now - this._current_chart._last_updated) * 1000):'').toString()); - } - // else this.error('Called begin() for chart ' + chart.id + ' which is empty.'); - - this._current_chart._last_updated = now; - this._current_chart._began = true; - this._current_chart._counter++; - - return true; - }; - - // set a collected value for a chart - // we do most things on the first value we attempt to set - service.set = function(dimension, value) { - if(this._current_chart === null) { - this.error('Called set(' + dimension + ', ' + value + ') without an open chart.'); - return false; - } - - if(typeof(this._current_chart.dimensions[dimension]) === 'undefined') { - this.error('Called set(' + dimension + ', ' + value + ') but dimension "' + dimension + '" does not exist in chart "' + this._current_chart.id + '".'); - return false; - } - - if(typeof value === 'undefined' || value === null) - return false; - - if(this._current_chart._dimensions_count !== 0) { - if (value instanceof Buffer) - this.queue('SET ' + dimension + ' = 0x' + value.toString('hex')); - else - this.queue('SET ' + dimension + ' = ' + value.toString()); - } - - return true; - }; - - // end data collection for the current chart - after calling begin() - service.end = function() { - if(this._current_chart !== null && this._current_chart._began === false) { - this.error('Called end() without an open chart.'); - return false; - } - - if(this._current_chart._dimensions_count !== 0) { - this.queue('END'); - netdata.send(this._queue); - } - - this._queue = ''; - this._current_chart._began = false; - if(netdata.options.DEBUG === true) netdata.debug('sent chart ' + this._current_chart.id); - this._current_chart = null; - return true; - }; - - // discard the collected values for the current chart - after calling begin() - service.flush = function() { - if(this._current_chart === null || this._current_chart._began === false) { - this.error('Called flush() without an open chart.'); - return false; - } - - this._queue = ''; - this._current_chart._began = false; - this._current_chart = null; - return true; - }; - - // create a netdata chart - service.chart = function(id, chart) { - if(typeof(netdata.charts[id]) === 'undefined') { - netdata.charts[id] = { - _created: false, - _updated: true, - _began: false, - _counter: 0, - _last_updated: 0, - _dimensions_count: 0, - id: id, - name: id, - title: 'untitled chart', - units: 'a unit', - family: '', - context: '', - type: netdata.chartTypes.line, - priority: 50000, - update_every: netdata.options.update_every, - dimensions: {} - }; - } - - var c = netdata.charts[id]; - - if(typeof(chart.name) !== 'undefined' && chart.name !== c.name) { - if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its name'); - c.name = chart.name; - c._updated = true; - } - - if(typeof(chart.title) !== 'undefined' && chart.title !== c.title) { - if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its title'); - c.title = chart.title; - c._updated = true; - } - - if(typeof(chart.units) !== 'undefined' && chart.units !== c.units) { - if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its units'); - c.units = chart.units; - c._updated = true; - } - - if(typeof(chart.family) !== 'undefined' && chart.family !== c.family) { - if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its family'); - c.family = chart.family; - c._updated = true; - } - - if(typeof(chart.context) !== 'undefined' && chart.context !== c.context) { - if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its context'); - c.context = chart.context; - c._updated = true; - } - - if(typeof(chart.type) !== 'undefined' && chart.type !== c.type) { - if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its type'); - c.type = chart.type; - c._updated = true; - } - - if(typeof(chart.priority) !== 'undefined' && chart.priority !== c.priority) { - if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its priority'); - c.priority = chart.priority; - c._updated = true; - } - - if(typeof(chart.update_every) !== 'undefined' && chart.update_every !== c.update_every) { - if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its update_every from ' + c.update_every + ' to ' + chart.update_every); - c.update_every = chart.update_every; - c._updated = true; - } - - if(typeof(chart.dimensions) !== 'undefined') { - for(var x in chart.dimensions) { - if(typeof(c.dimensions[x]) === 'undefined') { - c._dimensions_count++; - - c.dimensions[x] = { - _created: false, - _updated: false, - id: x, // the unique id of the dimension - name: x, // the name of the dimension - algorithm: netdata.chartAlgorithms.absolute, // the id of the netdata algorithm - multiplier: 1, // the multiplier - divisor: 1, // the divisor - hidden: false, // is hidden (boolean) - }; - - if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' created dimension ' + x); - c._updated = true; - } - - var dim = chart.dimensions[x]; - var d = c.dimensions[x]; - - if(typeof(dim.name) !== 'undefined' && d.name !== dim.name) { - if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its name'); - d.name = dim.name; - d._updated = true; - } - - if(typeof(dim.algorithm) !== 'undefined' && d.algorithm !== dim.algorithm) { - if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its algorithm from ' + d.algorithm + ' to ' + dim.algorithm); - d.algorithm = dim.algorithm; - d._updated = true; - } - - if(typeof(dim.multiplier) !== 'undefined' && d.multiplier !== dim.multiplier) { - if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its multiplier'); - d.multiplier = dim.multiplier; - d._updated = true; - } - - if(typeof(dim.divisor) !== 'undefined' && d.divisor !== dim.divisor) { - if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its divisor'); - d.divisor = dim.divisor; - d._updated = true; - } - - if(typeof(dim.hidden) !== 'undefined' && d.hidden !== dim.hidden) { - if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its hidden status'); - d.hidden = dim.hidden; - d._updated = true; - } - - if(d._updated) c._updated = true; - } - } - - //if(netdata.options.DEBUG === true) netdata.debug(netdata.charts); - return netdata.charts[id]; - }; - - return service; - }, - - runAllServices: function() { - if(netdata.options.DEBUG === true) netdata.debug('runAllServices()'); - - var now = new Date().getTime(); - var len = netdata.services.length; - while(len--) { - var service = netdata.services[len]; - - if(service.enabled === false || service.running === true) continue; - if(now <= service.next_run) continue; - - service.update(); - - now = new Date().getTime(); - while(service.next_run < now) - service.next_run += (service.update_every * 1000); - } - - // 1/10th of update_every in pause - setTimeout(netdata.runAllServices, netdata.options.update_every * 100); - }, - - start: function() { - if(netdata.options.DEBUG === true) this.debug('started, services: ' + netdata.stringify(this.services)); - - if(this.services.length === 0) { - this.disableNodePlugin(); - process.exit(1); - } - else this.runAllServices(); - }, - - // disable the whole node.js plugin - disableNodePlugin: function() { - this.send('DISABLE'); - process.exit(1); - }, - - requestFromParams: function(protocol, hostname, port, path, method) { - return { - protocol: protocol, - hostname: hostname, - port: port, - path: path, - //family: 4, - method: method, - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Connection': 'keep-alive' - }, - agent: new http.Agent({ - keepAlive: true, - keepAliveMsecs: netdata.options.update_every * 1000, - maxSockets: 2, // it must be 2 to work - maxFreeSockets: 1 - }) - }; - }, - - requestFromURL: function(a_url) { - var u = url.parse(a_url); - return netdata.requestFromParams(u.protocol, u.hostname, u.port, u.path, 'GET'); - }, - - configure: function(module, config, callback) { - if(netdata.options.DEBUG === true) this.debug(module.name + ': configuring (update_every: ' + this.options.update_every + ')...'); - - module.active = 0; - module.update_every = this.options.update_every; - - if(typeof config.update_every !== 'undefined') - module.update_every = config.update_every; - - module.enable_autodetect = (config.enable_autodetect)?true:false; - - if(typeof(callback) === 'function') - module.configure_callback = callback; - else - module.configure_callback = null; - - var added = module.configure(config); - - if(netdata.options.DEBUG === true) this.debug(module.name + ': configured, reporting ' + added + ' eligible services.'); - - if(module.configure_callback !== null && added === 0) { - if(netdata.options.DEBUG === true) this.debug(module.name + ': configuration finish callback called from configure().'); - module.configure_callback = null; - callback(); - } - - return added; - } + options: { + filename: __filename, + DEBUG: false, + update_every: 1 + }, + + chartAlgorithms: { + incremental: 'incremental', + absolute: 'absolute', + percentage_of_absolute_row: 'percentage-of-absolute-row', + percentage_of_incremental_row: 'percentage-of-incremental-row' + }, + + chartTypes: { + line: 'line', + area: 'area', + stacked: 'stacked' + }, + + services: new Array(), + modules_configuring: 0, + charts: {}, + + + processors: { + http: { + name: 'http', + + process: function(service, callback) { + var __DEBUG = netdata.options.DEBUG; + + if(__DEBUG === true) + netdata.debug(service.module.name + ': ' + service.name + ': making ' + this.name + ' request: ' + netdata.stringify(service.request)); + + var req = http.request(service.request, function(response) { + if(__DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': got server response...'); + + var end = false; + var data = ''; + response.setEncoding('utf8'); + + if(response.statusCode !== 200) { + if(end === false) { + service.error('Got HTTP code ' + response.statusCode + ', failed to get data.'); + end = true; + return callback(null); + } + } + + response.on('data', function(chunk) { + if(end === false) data += chunk; + }); + + response.on('error', function() { + if(end === false) { + service.error(': Read error, failed to get data.'); + end = true; + return callback(null); + } + }); + + response.on('end', function() { + if(end === false) { + if(__DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': read completed.'); + end = true; + return callback(data); + } + }); + }); + + req.on('error', function(e) { + if(__DEBUG === true) netdata.debug('Failed to make request: ' + netdata.stringify(service.request) + ', message: ' + e.message); + service.error('Failed to make request, message: ' + e.message); + return callback(null); + }); + + // write data to request body + if(typeof service.postData !== 'undefined' && service.request.method === 'POST') { + if(__DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': posting data: ' + service.postData); + req.write(service.postData); + } + + req.end(); + } + } + }, + + stringify: function(obj) { + return util.inspect(obj, {depth: 10}); + }, + + zeropad2: function(s) { + return ("00" + s).slice(-2); + }, + + logdate: function(d) { + if(typeof d === 'undefined') d = new Date(); + return d.getFullYear().toString() + '-' + this.zeropad2(d.getMonth() + 1) + '-' + this.zeropad2(d.getDate()) + + ' ' + this.zeropad2(d.getHours()) + ':' + this.zeropad2(d.getMinutes()) + ':' + this.zeropad2(d.getSeconds()); + }, + + // show debug info, if debug is enabled + debug: function(msg) { + if(this.options.DEBUG === true) { + console.error(this.logdate() + ': ' + netdata.options.filename + ': DEBUG: ' + ((typeof(msg) === 'object')?netdata.stringify(msg):msg).toString()); + } + }, + + // log an error + error: function(msg) { + console.error(this.logdate() + ': ' + netdata.options.filename + ': ERROR: ' + ((typeof(msg) === 'object')?netdata.stringify(msg):msg).toString()); + }, + + // send data to netdata + send: function(msg) { + console.log(msg.toString()); + }, + + service: function(service) { + if(typeof service === 'undefined') + service = {}; + + var now = Date.now(); + + service._current_chart = null; // the current chart we work on + service._queue = ''; // data to be sent to netdata + + service.error_reported = false; // error log flood control + + service.added = false; // added to netdata.services + service.enabled = true; + service.updates = 0; + service.running = false; + service.started = 0; + service.ended = 0; + + if(typeof service.module === 'undefined') { + service.module = { name: 'not-defined-module' }; + service.error('Attempted to create service without a module.'); + service.enabled = false; + } + + if(typeof service.name === 'undefined') { + service.name = 'unnamed@' + service.module.name + '/' + now; + } + + if(typeof service.processor === 'undefined') + service.processor = netdata.processors.http; + + if(typeof service.update_every === 'undefined') + service.update_every = service.module.update_every; + + if(typeof service.update_every === 'undefined') + service.update_every = netdata.options.update_every; + + if(service.update_every < netdata.options.update_every) + service.update_every = netdata.options.update_every; + + // align the runs + service.next_run = now - (now % (service.update_every * 1000)) + (service.update_every * 1000); + + service.commit = function() { + if(this.added !== true) { + this.added = true; + + var now = Date.now(); + this.next_run = now - (now % (service.update_every * 1000)) + (service.update_every * 1000); + + netdata.services.push(this); + if(netdata.options.DEBUG === true) netdata.debug(this.module.name + ': ' + this.name + ': service committed.'); + } + }; + + service.execute = function(responseProcessor) { + var __DEBUG = netdata.options.DEBUG; + + if(service.enabled === false) + return responseProcessor(null); + + this.module.active++; + this.running = true; + this.started = Date.now(); + this.updates++; + + if(__DEBUG === true) + netdata.debug(this.module.name + ': ' + this.name + ': making ' + this.processor.name + ' request: ' + netdata.stringify(this)); + + this.processor.process(this, function(response) { + service.ended = Date.now(); + service.duration = service.ended - service.started; + + if(typeof response === 'undefined') + response = null; + + if(response !== null) + service.errorClear(); + + if(__DEBUG === true) + netdata.debug(service.module.name + ': ' + service.name + ': processing ' + service.processor.name + ' response (received in ' + (service.ended - service.started).toString() + ' ms)'); + + responseProcessor(service, response); + + service.running = false; + service.module.active--; + if(service.module.active < 0) { + service.module.active = 0; + if(__DEBUG === true) + netdata.debug(service.module.name + ': active module counter below zero.'); + } + + if(service.module.active === 0) { + // check if we run under configure + if(service.module.configure_callback !== null) { + if(__DEBUG === true) + netdata.debug(service.module.name + ': configuration finish callback called from processResponse().'); + + var configure_callback = service.module.configure_callback; + service.module.configure_callback = null; + configure_callback(); + } + } + }); + }; + + service.update = function() { + if(netdata.options.DEBUG === true) + netdata.debug(this.module.name + ': ' + this.name + ': starting data collection...'); + + this.module.update(this, function() { + if(netdata.options.DEBUG === true) + netdata.debug(service.module.name + ': ' + service.name + ': data collection ended in ' + service.duration.toString() + ' ms.'); + }); + }; + + service.error = function(message) { + if(this.error_reported === false) { + netdata.error(this.module.name + ': ' + this.name + ': ' + message); + this.error_reported = true; + } + else if(netdata.options.DEBUG === true) + netdata.debug(this.module.name + ': ' + this.name + ': ' + message); + }; + + service.errorClear = function() { + this.error_reported = false; + }; + + service.queue = function(txt) { + this._queue += txt + '\n'; + }; + + service._send_chart_to_netdata = function(chart) { + // internal function to send a chart to netdata + this.queue('CHART "' + chart.id + '" "' + chart.name + '" "' + chart.title + '" "' + chart.units + '" "' + chart.family + '" "' + chart.context + '" "' + chart.type + '" ' + chart.priority.toString() + ' ' + chart.update_every.toString()); + + if(typeof(chart.dimensions) !== 'undefined') { + var dims = Object.keys(chart.dimensions); + var len = dims.length; + while(len--) { + var d = chart.dimensions[dims[len]]; + + this.queue('DIMENSION "' + d.id + '" "' + d.name + '" "' + d.algorithm + '" ' + d.multiplier.toString() + ' ' + d.divisor.toString() + ' ' + ((d.hidden === true) ? 'hidden' : '').toString()); + d._created = true; + d._updated = false; + } + } + + chart._created = true; + chart._updated = false; + }; + + // begin data collection for a chart + service.begin = function(chart) { + if(this._current_chart !== null && this._current_chart !== chart) { + this.error('Called begin() for chart ' + chart.id + ' while chart ' + this._current_chart.id + ' is still open. Closing it.'); + this.end(); + } + + if(typeof(chart.id) === 'undefined' || netdata.charts[chart.id] !== chart) { + this.error('Called begin() for chart ' + chart.id + ' that is not mine. Where did you find it? Ignoring it.'); + return false; + } + + if(netdata.options.DEBUG === true) netdata.debug('setting current chart to ' + chart.id); + this._current_chart = chart; + this._current_chart._began = true; + + if(this._current_chart._dimensions_count !== 0) { + if(this._current_chart._created === false || this._current_chart._updated === true) + this._send_chart_to_netdata(this._current_chart); + + var now = this.ended; + this.queue('BEGIN ' + this._current_chart.id + ' ' + ((this._current_chart._last_updated > 0)?((now - this._current_chart._last_updated) * 1000):'').toString()); + } + // else this.error('Called begin() for chart ' + chart.id + ' which is empty.'); + + this._current_chart._last_updated = now; + this._current_chart._began = true; + this._current_chart._counter++; + + return true; + }; + + // set a collected value for a chart + // we do most things on the first value we attempt to set + service.set = function(dimension, value) { + if(this._current_chart === null) { + this.error('Called set(' + dimension + ', ' + value + ') without an open chart.'); + return false; + } + + if(typeof(this._current_chart.dimensions[dimension]) === 'undefined') { + this.error('Called set(' + dimension + ', ' + value + ') but dimension "' + dimension + '" does not exist in chart "' + this._current_chart.id + '".'); + return false; + } + + if(typeof value === 'undefined' || value === null) + return false; + + if(this._current_chart._dimensions_count !== 0) { + if (value instanceof Buffer) + this.queue('SET ' + dimension + ' = 0x' + value.toString('hex')); + else + this.queue('SET ' + dimension + ' = ' + value.toString()); + } + + return true; + }; + + // end data collection for the current chart - after calling begin() + service.end = function() { + if(this._current_chart !== null && this._current_chart._began === false) { + this.error('Called end() without an open chart.'); + return false; + } + + if(this._current_chart._dimensions_count !== 0) { + this.queue('END'); + netdata.send(this._queue); + } + + this._queue = ''; + this._current_chart._began = false; + if(netdata.options.DEBUG === true) netdata.debug('sent chart ' + this._current_chart.id); + this._current_chart = null; + return true; + }; + + // discard the collected values for the current chart - after calling begin() + service.flush = function() { + if(this._current_chart === null || this._current_chart._began === false) { + this.error('Called flush() without an open chart.'); + return false; + } + + this._queue = ''; + this._current_chart._began = false; + this._current_chart = null; + return true; + }; + + // create a netdata chart + service.chart = function(id, chart) { + var __DEBUG = netdata.options.DEBUG; + + if(typeof(netdata.charts[id]) === 'undefined') { + netdata.charts[id] = { + _created: false, + _updated: true, + _began: false, + _counter: 0, + _last_updated: 0, + _dimensions_count: 0, + id: id, + name: id, + title: 'untitled chart', + units: 'a unit', + family: '', + context: '', + type: netdata.chartTypes.line, + priority: 50000, + update_every: netdata.options.update_every, + dimensions: {} + }; + } + + var c = netdata.charts[id]; + + if(typeof(chart.name) !== 'undefined' && chart.name !== c.name) { + if(__DEBUG === true) netdata.debug('chart ' + id + ' updated its name'); + c.name = chart.name; + c._updated = true; + } + + if(typeof(chart.title) !== 'undefined' && chart.title !== c.title) { + if(__DEBUG === true) netdata.debug('chart ' + id + ' updated its title'); + c.title = chart.title; + c._updated = true; + } + + if(typeof(chart.units) !== 'undefined' && chart.units !== c.units) { + if(__DEBUG === true) netdata.debug('chart ' + id + ' updated its units'); + c.units = chart.units; + c._updated = true; + } + + if(typeof(chart.family) !== 'undefined' && chart.family !== c.family) { + if(__DEBUG === true) netdata.debug('chart ' + id + ' updated its family'); + c.family = chart.family; + c._updated = true; + } + + if(typeof(chart.context) !== 'undefined' && chart.context !== c.context) { + if(__DEBUG === true) netdata.debug('chart ' + id + ' updated its context'); + c.context = chart.context; + c._updated = true; + } + + if(typeof(chart.type) !== 'undefined' && chart.type !== c.type) { + if(__DEBUG === true) netdata.debug('chart ' + id + ' updated its type'); + c.type = chart.type; + c._updated = true; + } + + if(typeof(chart.priority) !== 'undefined' && chart.priority !== c.priority) { + if(__DEBUG === true) netdata.debug('chart ' + id + ' updated its priority'); + c.priority = chart.priority; + c._updated = true; + } + + if(typeof(chart.update_every) !== 'undefined' && chart.update_every !== c.update_every) { + if(__DEBUG === true) netdata.debug('chart ' + id + ' updated its update_every from ' + c.update_every + ' to ' + chart.update_every); + c.update_every = chart.update_every; + c._updated = true; + } + + if(typeof(chart.dimensions) !== 'undefined') { + var dims = Object.keys(chart.dimensions); + var len = dims.length; + while(len--) { + var x = dims[len]; + + if(typeof(c.dimensions[x]) === 'undefined') { + c._dimensions_count++; + + c.dimensions[x] = { + _created: false, + _updated: false, + id: x, // the unique id of the dimension + name: x, // the name of the dimension + algorithm: netdata.chartAlgorithms.absolute, // the id of the netdata algorithm + multiplier: 1, // the multiplier + divisor: 1, // the divisor + hidden: false // is hidden (boolean) + }; + + if(__DEBUG === true) netdata.debug('chart ' + id + ' created dimension ' + x); + c._updated = true; + } + + var dim = chart.dimensions[x]; + var d = c.dimensions[x]; + + if(typeof(dim.name) !== 'undefined' && d.name !== dim.name) { + if(__DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its name'); + d.name = dim.name; + d._updated = true; + } + + if(typeof(dim.algorithm) !== 'undefined' && d.algorithm !== dim.algorithm) { + if(__DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its algorithm from ' + d.algorithm + ' to ' + dim.algorithm); + d.algorithm = dim.algorithm; + d._updated = true; + } + + if(typeof(dim.multiplier) !== 'undefined' && d.multiplier !== dim.multiplier) { + if(__DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its multiplier'); + d.multiplier = dim.multiplier; + d._updated = true; + } + + if(typeof(dim.divisor) !== 'undefined' && d.divisor !== dim.divisor) { + if(__DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its divisor'); + d.divisor = dim.divisor; + d._updated = true; + } + + if(typeof(dim.hidden) !== 'undefined' && d.hidden !== dim.hidden) { + if(__DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its hidden status'); + d.hidden = dim.hidden; + d._updated = true; + } + + if(d._updated) c._updated = true; + } + } + + //if(netdata.options.DEBUG === true) netdata.debug(netdata.charts); + return netdata.charts[id]; + }; + + return service; + }, + + runAllServices: function() { + if(netdata.options.DEBUG === true) netdata.debug('runAllServices()'); + + var now = Date.now(); + var len = netdata.services.length; + while(len--) { + var service = netdata.services[len]; + + if(service.enabled === false || service.running === true) continue; + if(now <= service.next_run) continue; + + service.update(); + + now = Date.now(); + service.next_run = now - (now % (service.update_every * 1000)) + (service.update_every * 1000); + } + + // 1/10th of update_every in pause + setTimeout(netdata.runAllServices, netdata.options.update_every * 100); + }, + + start: function() { + if(netdata.options.DEBUG === true) this.debug('started, services: ' + netdata.stringify(this.services)); + + if(this.services.length === 0) { + this.disableNodePlugin(); + + // eslint suggested way to exit + var exit = process.exit; + exit(1); + } + else this.runAllServices(); + }, + + // disable the whole node.js plugin + disableNodePlugin: function() { + this.send('DISABLE'); + + // eslint suggested way to exit + var exit = process.exit; + exit(1); + }, + + requestFromParams: function(protocol, hostname, port, path, method) { + return { + protocol: protocol, + hostname: hostname, + port: port, + path: path, + //family: 4, + method: method, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Connection': 'keep-alive' + }, + agent: new http.Agent({ + keepAlive: true, + keepAliveMsecs: netdata.options.update_every * 1000, + maxSockets: 2, // it must be 2 to work + maxFreeSockets: 1 + }) + }; + }, + + requestFromURL: function(a_url) { + var u = url.parse(a_url); + return netdata.requestFromParams(u.protocol, u.hostname, u.port, u.path, 'GET'); + }, + + configure: function(module, config, callback) { + if(netdata.options.DEBUG === true) this.debug(module.name + ': configuring (update_every: ' + this.options.update_every + ')...'); + + module.active = 0; + module.update_every = this.options.update_every; + + if(typeof config.update_every !== 'undefined') + module.update_every = config.update_every; + + module.enable_autodetect = (config.enable_autodetect)?true:false; + + if(typeof(callback) === 'function') + module.configure_callback = callback; + else + module.configure_callback = null; + + var added = module.configure(config); + + if(netdata.options.DEBUG === true) this.debug(module.name + ': configured, reporting ' + added + ' eligible services.'); + + if(module.configure_callback !== null && added === 0) { + if(netdata.options.DEBUG === true) this.debug(module.name + ': configuration finish callback called from configure().'); + var configure_callback = module.configure_callback; + module.configure_callback = null; + configure_callback(); + } + + return added; + } }; -if(netdata.options.DEBUG === true) netdata.debug('loaded netdata from: ' + __filename); +if(netdata.options.DEBUG === true) netdata.debug('loaded netdata from:', __filename); module.exports = netdata; |