3 var url = require('url');
\r
4 var http = require('http');
\r
5 var util = require('util');
\r
8 var netdata = require('netdata');
\r
10 var example_chart = {
\r
11 id: 'id', // the unique id of the chart
\r
12 name: 'name', // the name of the chart
\r
13 title: 'title', // the title of the chart
\r
14 units: 'units', // the units of the chart dimensions
\r
15 family: 'family', // the family of the chart
\r
16 category: 'category', // the category of the chart
\r
17 type: netdata.chartTypes.line, // the type of the chart
\r
18 priority: 0, // the priority relative to others in the same family and category
\r
19 update_every: 1, // the expected update frequency of the chart
\r
22 id: 'dim1', // the unique id of the dimension
\r
23 name: 'name', // the name of the dimension
\r
24 algorithm: netdata.chartAlgorithms.absolute, // the id of the netdata algorithm
\r
25 multiplier: 1, // the multiplier
\r
26 divisor: 1, // the divisor
\r
27 hidden: false, // is hidden (boolean)
\r
30 id: 'dim2', // the unique id of the dimension
\r
31 name: 'name', // the name of the dimension
\r
32 algorithm: 'absolute', // the id of the netdata algorithm
\r
33 multiplier: 1, // the multiplier
\r
34 divisor: 1, // the divisor
\r
35 hidden: false, // is hidden (boolean)
\r
37 // add as many dimensions as needed
\r
44 filename: __filename,
\r
50 incremental: 'incremental',
\r
51 absolute: 'absolute',
\r
52 percentage_of_absolute_row: 'percentage-of-absolute-row',
\r
53 percentage_of_incremental_row: 'percentage-of-incremental-row'
\r
62 services: new Array(),
\r
63 modules_configuring: 0,
\r
66 stringify: function(obj) {
\r
67 return util.inspect(obj, {depth: 10});
\r
70 // show debug info, if debug is enabled
\r
71 debug: function(msg) {
\r
72 if(this.options.DEBUG === true) {
\r
73 var now = new Date();
\r
74 console.error(now.toString() + ': ' + netdata.options.filename + ': DEBUG: ' + ((typeof(msg) === 'object')?netdata.stringify(msg):msg).toString());
\r
79 error: function(msg) {
\r
80 var now = new Date();
\r
81 console.error(now.toString() + ': ' + netdata.options.filename + ': ERROR: ' + ((typeof(msg) === 'object')?netdata.stringify(msg):msg).toString());
\r
84 // send data to netdata
\r
85 send: function(msg) {
\r
86 console.log(msg.toString());
\r
89 serviceAdd: function(service) {
\r
90 if(netdata.serviceIsInitialized(service) === false)
\r
91 netdata.serviceInit(service);
\r
93 if(service.added !== true) {
\r
94 service.updates = 0;
\r
95 service.enabled = true;
\r
96 service.added = true;
\r
97 service.running = false;
\r
98 service.started = 0;
\r
100 service._current_chart = null; // the current chart we work on
\r
101 service._queue = '';
\r
102 service.queue = function(txt) {
\r
103 this._queue += txt + '\n';
\r
106 service._send_chart_to_netdata = function(chart) {
\r
107 // internal function to send a chart to netdata
\r
108 this.queue('CHART "' + chart.id + '" "' + chart.name + '" "' + chart.title + '" "' + chart.units + '" "' + chart.family + '" "' + chart.category + '" "' + chart.type + '" ' + chart.priority.toString() + ' ' + chart.update_every.toString());
\r
110 for(var dim in chart.dimensions) {
\r
111 var d = chart.dimensions[dim];
\r
113 this.queue('DIMENSION "' + d.id + '" "' + d.name + '" "' + d.algorithm + '" ' + d.multiplier.toString() + ' ' + d.divisor.toString() + ' ' + ((d.hidden === true)?'hidden':'').toString());
\r
115 d._updated = false;
\r
118 chart._created = true;
\r
119 chart._updated = false;
\r
122 // begin data collection for a chart
\r
123 service.begin = function(chart) {
\r
124 if(this._current_chart !== null && this._current_chart !== chart) {
\r
125 netdata.serviceError(this, 'Called begin() for chart ' + chart.id + ' while chart ' + this._current_chart.id + ' is still open. Closing it.');
\r
129 if(typeof(chart.id) === 'undefined' || netdata.charts[chart.id] != chart) {
\r
130 netdata.serviceError(this, 'Called begin() for chart ' + chart.id + ' that is not mine. Where did you find it? Ignoring it.');
\r
134 if(netdata.options.DEBUG === true) netdata.debug('setting current chart to ' + chart.id);
\r
135 this._current_chart = chart;
\r
136 this._current_chart._began = true;
\r
138 if(this._current_chart._dimensions_count !== 0) {
\r
139 if(this._current_chart._created === false || this._current_chart._updated === true)
\r
140 this._send_chart_to_netdata(this._current_chart);
\r
142 var now = this.ended;
\r
143 this.queue('BEGIN ' + this._current_chart.id + ' ' + ((this._current_chart._last_updated > 0)?((now - this._current_chart._last_updated) * 1000):'').toString());
\r
145 // else netdata.serviceError(this, 'Called begin() for chart ' + chart.id + ' which is empty.');
\r
147 this._current_chart._last_updated = now;
\r
148 this._current_chart._began = true;
\r
149 this._current_chart._counter++;
\r
154 // set a collected value for a chart
\r
155 // we do most things on the first value we attempt to set
\r
156 service.set = function(dimension, value) {
\r
157 if(this._current_chart === null) {
\r
158 netdata.serviceError(this, 'Called set(' + dimension + ', ' + value + ') without an open chart.');
\r
162 if(typeof(this._current_chart.dimensions[dimension]) === 'undefined') {
\r
163 netdata.serviceError(this, 'Called set(' + dimension + ', ' + value + ') but dimension "' + dimension + '" does not exist in chart "' + this._current_chart.id + '".');
\r
167 if(typeof value === 'undefined' || value === null)
\r
170 if(this._current_chart._dimensions_count !== 0)
\r
171 this.queue('SET ' + dimension + ' = ' + value);
\r
176 // end data collection for the current chart - after calling begin()
\r
177 service.end = function() {
\r
178 if(this._current_chart !== null && this._current_chart._began === false) {
\r
179 netdata.serviceError(this, 'Called end() without an open chart.');
\r
183 if(this._current_chart._dimensions_count !== 0) {
\r
185 netdata.send(this._queue);
\r
189 this._current_chart._began = false;
\r
190 if(netdata.options.DEBUG === true) netdata.debug('committed chart ' + this._current_chart.id);
\r
191 this._current_chart = null;
\r
195 // discard the collected values for the current chart - after calling begin()
\r
196 service.flush = function() {
\r
197 if(this._current_chart === null || this._current_chart._began === false) {
\r
198 netdata.serviceError(this, 'Called flush() without an open chart.');
\r
203 this._current_chart._began = false;
\r
204 this._current_chart = null;
\r
208 // create a netdata chart
\r
209 service.chart = function(id, chart) {
\r
210 if(typeof(netdata.charts[id]) === 'undefined') {
\r
211 netdata.charts[id] = {
\r
217 _dimensions_count: 0,
\r
220 title: 'untitled chart',
\r
224 type: netdata.chartTypes.line,
\r
226 update_every: netdata.options.update_every,
\r
231 var c = netdata.charts[id];
\r
233 if(typeof(chart.name) !== 'undefined' && chart.name !== c.name) {
\r
234 if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its name');
\r
235 c.name = chart.name;
\r
239 if(typeof(chart.title) !== 'undefined' && chart.title !== c.title) {
\r
240 if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its title');
\r
241 c.title = chart.title;
\r
245 if(typeof(chart.units) !== 'undefined' && chart.units !== c.units) {
\r
246 if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its units');
\r
247 c.units = chart.units;
\r
251 if(typeof(chart.family) !== 'undefined' && chart.family !== c.family) {
\r
252 if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its family');
\r
253 c.family = chart.family;
\r
257 if(typeof(chart.category) !== 'undefined' && chart.category !== c.category) {
\r
258 if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its category');
\r
259 c.category = chart.category;
\r
263 if(typeof(chart.type) !== 'undefined' && chart.type !== c.type) {
\r
264 if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its type');
\r
265 c.type = chart.type;
\r
269 if(typeof(chart.priority) !== 'undefined' && chart.priority !== c.priority) {
\r
270 if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its priority');
\r
271 c.priority = chart.priority;
\r
275 if(typeof(chart.update_every) !== 'undefined' && chart.update_every !== c.update_every) {
\r
276 if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its update_every');
\r
277 c.update_every = chart.update_every;
\r
281 if(typeof(chart.dimensions) !== 'undefined') {
\r
282 for(var x in chart.dimensions) {
\r
283 if(typeof(c.dimensions[x]) === 'undefined') {
\r
284 c._dimensions_count++;
\r
286 c.dimensions[x] = {
\r
289 id: x, // the unique id of the dimension
\r
290 name: x, // the name of the dimension
\r
291 algorithm: netdata.chartAlgorithms.absolute, // the id of the netdata algorithm
\r
292 multiplier: 1, // the multiplier
\r
293 divisor: 1, // the divisor
\r
294 hidden: false, // is hidden (boolean)
\r
297 if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' created dimension ' + x);
\r
301 var dim = chart.dimensions[x];
\r
302 var d = c.dimensions[x];
\r
304 if(typeof(dim.name) !== 'undefined' && d.name !== dim.name) {
\r
305 if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its name');
\r
310 if(typeof(dim.algorithm) !== 'undefined' && d.algorithm !== dim.algorithm) {
\r
311 if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its algorithm from ' + d.algorithm + ' to ' + dim.algorithm);
\r
312 d.algorithm = dim.algorithm;
\r
316 if(typeof(dim.multiplier) !== 'undefined' && d.multiplier !== dim.multiplier) {
\r
317 if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its multiplier');
\r
318 d.multiplier = dim.multiplier;
\r
322 if(typeof(dim.divisor) !== 'undefined' && d.divisor !== dim.divisor) {
\r
323 if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its divisor');
\r
324 d.divisor = dim.divisor;
\r
328 if(typeof(dim.hidden) !== 'undefined' && d.hidden !== dim.hidden) {
\r
329 if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its hidden status');
\r
330 d.hidden = dim.hidden;
\r
334 if(d._updated) c._updated = true;
\r
338 if(netdata.options.DEBUG === true) netdata.debug(netdata.charts);
\r
339 return netdata.charts[id];
\r
342 this.services.push(service);
\r
343 if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': service added.');
\r
347 serviceRun: function(service) {
\r
348 if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': starting data collection...');
\r
349 service.running = true;
\r
350 service.started = new Date().getTime();
\r
353 service.module.update(service, function() {
\r
354 service.ended = new Date().getTime();
\r
355 service.duration = service.ended - service.started;
\r
356 if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': data collection ended in ' + service.duration.toString() + ' ms.');
\r
357 service.running = false;
\r
361 runAllServices: function() {
\r
362 if(netdata.options.DEBUG === true) netdata.debug('runAllServices()');
\r
364 var now = new Date().getTime();
\r
365 var len = netdata.services.length;
\r
367 var service = netdata.services[len];
\r
369 if(service.enabled === false || service.running === true) continue;
\r
370 if(now - service.ended < service.update_every) continue;
\r
372 netdata.serviceRun(service);
\r
375 setTimeout(netdata.runAllServices, 100);
\r
378 start: function() {
\r
379 if(netdata.options.DEBUG === true) this.debug('started, services:');
\r
381 if(this.services.length === 0) {
\r
382 this.disableNodePlugin();
\r
385 else this.runAllServices();
\r
388 // disable the whole node.js plugin
\r
389 disableNodePlugin: function() {
\r
390 this.send('DISABLE');
\r
394 requestFromParams: function(protocol, hostname, port, path, method) {
\r
396 protocol: protocol,
\r
397 hostname: hostname,
\r
403 'Content-Type': 'application/x-www-form-urlencoded',
\r
404 'Connection': 'keep-alive'
\r
406 agent: new http.Agent({
\r
408 keepAliveMsecs: netdata.options.update_every,
\r
409 maxSockets: 2, // it must be 2 to work
\r
415 requestFromURL: function(a_url) {
\r
416 var u = url.parse(a_url);
\r
417 return netdata.requestFromParams(u.protocol, u.hostname, u.port, u.path, 'GET');
\r
420 processResponse: function(service, data, callback) {
\r
421 if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': processing response...');
\r
423 callback(service, data);
\r
425 service.module.running--;
\r
426 if(service.module.running <= 0) {
\r
427 service.module.running = 0;
\r
429 // check if we run under configure
\r
430 if(service.module.configure_callback !== null) {
\r
431 if(netdata.options.DEBUG === true) this.debug(service.module.name + ': configuration finish callback called from processResponse().');
\r
432 var ccallback = service.module.configure_callback;
\r
433 service.module.configure_callback = null;
\r
439 serviceError: function(service, message) {
\r
440 if(service.error_reported === false) {
\r
441 netdata.error(service.module.name + ': ' + service.name + ': ' + message);
\r
442 service.error_reported = true;
\r
444 else if(netdata.options.DEBUG === true)
\r
445 netdata.debug(service.module.name + ': ' + service.name + ': ' + message);
\r
448 serviceErrorClear: function(service) {
\r
449 service.error_reported = false;
\r
452 serviceInit: function(service) {
\r
453 service.error_reported = false;
\r
454 service.added = false;
\r
455 service.enabled = true;
\r
458 serviceIsInitialized: function(service) {
\r
459 if(typeof service.error_reported === 'undefined')
\r
465 getResponse: function(service, response, callback) {
\r
466 if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': got response...');
\r
470 response.setEncoding('utf8');
\r
472 if(response.statusCode !== 200) {
\r
473 if(end === false) {
\r
474 netdata.serviceError(service, ': got HTTP code ' + response.statusCode + ', failed to get data.');
\r
476 netdata.processResponse(service, null, callback);
\r
480 response.on('data', function(chunk) {
\r
481 if(end === false) data += chunk;
\r
484 response.on('error', function() {
\r
485 if(end === false) {
\r
486 netdata.serviceError(service, ': Read error, failed to get data.');
\r
488 netdata.processResponse(service, null, callback);
\r
492 response.on('end', function() {
\r
493 if(end === false) {
\r
494 netdata.serviceErrorClear(service);
\r
495 if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': read completed.');
\r
497 netdata.processResponse(service, data, callback);
\r
502 serviceExecute: function(service, callback) {
\r
503 if(netdata.serviceIsInitialized(service) === false)
\r
504 netdata.serviceInit(service);
\r
506 service.module.running++;
\r
508 if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': making request: ' + netdata.stringify(service.request));
\r
509 var req = http.request(service.request, function(response) {
\r
510 if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': request done.');
\r
511 netdata.getResponse(service, response, callback);
\r
514 req.on('error', function(e) {
\r
515 netdata.serviceError(service, ': failed to make request: ' + netdata.stringify(service.request) + ', message: ' + e.message);
\r
516 netdata.processResponse(service, null, callback);
\r
519 // write data to request body
\r
520 if(typeof service.postData !== 'undefined' && service.request.method === 'POST') {
\r
521 if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': posting data: ' + service.postData);
\r
522 req.write(service.postData);
\r
528 configure: function(module, config, callback) {
\r
529 if(netdata.options.DEBUG === true) this.debug(module.name + ': configuring (update_every: ' + this.options.update_every + ')...');
\r
531 module.running = 0;
\r
532 module.update_every = this.options.update_every;
\r
534 if(typeof config.update_every !== 'undefined')
\r
535 module.update_every = config.update_every * 1000;
\r
537 module.enable_autodetect = (config.enable_autodetect)?true:false;
\r
539 if(typeof(callback) === 'function')
\r
540 module.configure_callback = callback;
\r
542 module.configure_callback = null;
\r
544 var added = module.configure(config);
\r
546 if(netdata.options.DEBUG === true) this.debug(module.name + ': configured, reporting ' + added + ' eligible services.');
\r
548 if(module.configure_callback !== null && added === 0) {
\r
549 if(netdata.options.DEBUG === true) this.debug(module.name + ': configuration finish callback called from configure().');
\r
550 module.configure_callback = null;
\r
559 if(netdata.options.DEBUG === true) netdata.debug('loaded netdata from: ' + __filename);
\r
560 module.exports = netdata;
\r
563 var test1 = netdata.chart('test1', { name: 'test name', dimensions: { dim1: {}}});
\r
564 netdata.begin(test1);
\r
565 netdata.set('dim1', 1);
\r
567 netdata.begin(test1);
\r
568 netdata.set('dim1', 2);
\r
570 netdata.begin(test1);
\r
571 netdata.set('dim1', 3);
\r
573 netdata.begin(test1);
\r
574 netdata.set('dim1', 4);
\r