]> arthur.barton.de Git - netdata.git/blob - node.d/node_modules/netdata.js
added node.js plugin manager, implemented bind (named) 9.10+ JSON plugin, refactored...
[netdata.git] / node.d / node_modules / netdata.js
1 'use strict';\r
2 \r
3 var url = require('url');\r
4 var http = require('http');\r
5 var util = require('util');\r
6 \r
7 /*\r
8 var netdata = require('netdata');\r
9 \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
20         dimensions: {\r
21                 'dim1': {\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
28                 },\r
29                 'dim2': {\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
36                 }\r
37                 // add as many dimensions as needed\r
38         }\r
39 };\r
40 */\r
41 \r
42 var netdata = {\r
43         options: {\r
44                 filename: __filename,\r
45                 DEBUG: false,\r
46                 update_every: 1000,\r
47         },\r
48 \r
49         chartAlgorithms: {\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
54         },\r
55 \r
56         chartTypes: {\r
57                 line: 'line',\r
58                 area: 'area',\r
59                 stacked: 'stacked'\r
60         },\r
61 \r
62         services: new Array(),\r
63         modules_configuring: 0,\r
64         charts: {},\r
65 \r
66         stringify: function(obj) {\r
67                 return util.inspect(obj, {depth: 10});\r
68         },\r
69 \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
75                 }\r
76         },\r
77 \r
78         // log an error\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
82         },\r
83 \r
84         // send data to netdata\r
85         send: function(msg) {\r
86                 console.log(msg.toString());\r
87         },\r
88 \r
89         serviceAdd: function(service) {\r
90                 if(service.added !== true) {\r
91                         service.updates = 0;\r
92                         service.enabled = true;\r
93                         service.added = true;\r
94                         service.running = false;\r
95                         service.started = 0;\r
96                         service.ended = 0;\r
97                         service._current_chart = null; // the current chart we work on\r
98                         service._queue = '';\r
99                         service.queue = function(txt) {\r
100                                 this._queue += txt + '\n';\r
101                         };\r
102 \r
103                         service._send_chart_to_netdata = function(chart) {\r
104                                 // internal function to send a chart to netdata\r
105                                 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
106                                 \r
107                                 for(var dim in chart.dimensions) {\r
108                                         var d = chart.dimensions[dim];\r
109 \r
110                                         this.queue('DIMENSION "' + d.id + '" "' + d.name + '" "' + d.algorithm + '" ' + d.multiplier.toString() + ' ' + d.divisor.toString() + ' ' + ((d.hidden === true)?'hidden':'').toString());\r
111                                         d._created = true;\r
112                                         d._updated = false;\r
113                                 }\r
114 \r
115                                 chart._created = true;\r
116                                 chart._updated = false;\r
117                         };\r
118 \r
119                         // begin data collection for a chart\r
120                         service.begin = function(chart) {\r
121                                 if(this._current_chart !== null && this._current_chart !== chart) {\r
122                                         netdata.error('Called begin() for chart ' + chart.id + ' while chart ' + this._current_chart.id + ' is still open. Closing it.');\r
123                                         this.end();\r
124                                 }\r
125 \r
126                                 if(typeof(chart.id) === 'undefined' || netdata.charts[chart.id] != chart) {\r
127                                         netdata.error('Called begin() for chart ' + chart.id + ' that is not mine. Where did you find it? Ignoring it.');\r
128                                         return false;\r
129                                 }\r
130 \r
131                                 if(netdata.options.DEBUG === true) netdata.debug('setting current chart to ' + chart.id);\r
132                                 this._current_chart = chart;\r
133                                 this._current_chart._began = true;\r
134 \r
135                                 if(this._current_chart._dimensions_count !== 0) {\r
136                                         if(this._current_chart._created === false || this._current_chart._updated === true)\r
137                                                 this._send_chart_to_netdata(this._current_chart);\r
138 \r
139                                         var now = this.ended;\r
140                                         this.queue('BEGIN ' + this._current_chart.id + ' ' + ((this._current_chart._last_updated > 0)?((now - this._current_chart._last_updated) * 1000):'').toString());\r
141                                 }\r
142                                 // else netdata.error('Called begin() for chart ' + chart.id + ' which is empty.');\r
143 \r
144                                 this._current_chart._last_updated = now;\r
145                                 this._current_chart._began = true;\r
146                                 this._current_chart._counter++;\r
147 \r
148                                 return true;\r
149                         };\r
150 \r
151                         // set a collected value for a chart\r
152                         // we do most things on the first value we attempt to set\r
153                         service.set = function(dimension, value) {\r
154                                 if(this._current_chart === null) {\r
155                                         netdata.error('Called set(' + dimension + ', ' + value + ') without an open chart.');\r
156                                         return false;\r
157                                 }\r
158 \r
159                                 if(typeof(this._current_chart.dimensions[dimension]) === 'undefined') {\r
160                                         netdata.error('Called set(' + dimension + ', ' + value + ') but dimension "' + dimension + '" does not exist in chart "' + this._current_chart.id + '".');\r
161                                         return false;\r
162                                 }\r
163 \r
164                                 if(this._current_chart._dimensions_count !== 0)\r
165                                         this.queue('SET ' + dimension + ' = ' + value);\r
166 \r
167                                 return true;\r
168                         };\r
169 \r
170                         // end data collection for the current chart - after calling begin()\r
171                         service.end = function() {\r
172                                 if(this._current_chart !== null && this._current_chart._began === false) {\r
173                                         netdata.error('Called end() without an open chart.');\r
174                                         return false;\r
175                                 }\r
176 \r
177                                 if(this._current_chart._dimensions_count !== 0) {\r
178                                         this.queue('END');\r
179                                         netdata.send(this._queue);\r
180                                 }\r
181 \r
182                                 this._queue = '';\r
183                                 this._current_chart._began = false;\r
184                                 if(netdata.options.DEBUG === true) netdata.debug('committed chart ' + this._current_chart.id);\r
185                                 this._current_chart = null;\r
186                                 return true;\r
187                         };\r
188 \r
189                         // discard the collected values for the current chart - after calling begin()\r
190                         service.flush = function() {\r
191                                 if(this._current_chart === null || this._current_chart._began === false) {\r
192                                         netdata.error('Called flush() without an open chart.');\r
193                                         return false;\r
194                                 }\r
195 \r
196                                 this._queue = '';\r
197                                 this._current_chart._began = false;\r
198                                 this._current_chart = null;\r
199                                 return true;\r
200                         };\r
201 \r
202                         // create a netdata chart\r
203                         service.chart = function(id, chart) {\r
204                                 if(typeof(netdata.charts[id]) === 'undefined') {\r
205                                         netdata.charts[id] = {\r
206                                                 _created: false,\r
207                                                 _updated: false,\r
208                                                 _began: false,\r
209                                                 _counter: 0,\r
210                                                 _last_updated: 0,\r
211                                                 _dimensions_count: 0,\r
212                                                 id: id,\r
213                                                 name: id,\r
214                                                 title: 'untitled chart',\r
215                                                 units: 'a unit',\r
216                                                 family: id,\r
217                                                 category: id,\r
218                                                 type: netdata.chartTypes.line,\r
219                                                 priority: 0,\r
220                                                 update_every: netdata.options.update_every,\r
221                                                 dimensions: {}\r
222                                         };\r
223                                 }\r
224 \r
225                                 var c = netdata.charts[id];\r
226 \r
227                                 if(typeof(chart.name) !== 'undefined' && chart.name !== c.name) {\r
228                                         if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its name');\r
229                                         c.name = chart.name;\r
230                                         c._updated = true;\r
231                                 }\r
232 \r
233                                 if(typeof(chart.title) !== 'undefined' && chart.title !== c.title) {\r
234                                         if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its title');\r
235                                         c.title = chart.title;\r
236                                         c._updated = true;\r
237                                 }\r
238 \r
239                                 if(typeof(chart.units) !== 'undefined' && chart.units !== c.units) {\r
240                                         if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its units');\r
241                                         c.units = chart.units;\r
242                                         c._updated = true;\r
243                                 }\r
244 \r
245                                 if(typeof(chart.family) !== 'undefined' && chart.family !== c.family) {\r
246                                         if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its family');\r
247                                         c.family = chart.family;\r
248                                         c._updated = true;\r
249                                 }\r
250 \r
251                                 if(typeof(chart.category) !== 'undefined' && chart.category !== c.category) {\r
252                                         if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its category');\r
253                                         c.category = chart.category;\r
254                                         c._updated = true;\r
255                                 }\r
256 \r
257                                 if(typeof(chart.type) !== 'undefined' && chart.type !== c.type) {\r
258                                         if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its type');\r
259                                         c.type = chart.type;\r
260                                         c._updated = true;\r
261                                 }\r
262 \r
263                                 if(typeof(chart.priority) !== 'undefined' && chart.priority !== c.priority) {\r
264                                         if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its priority');\r
265                                         c.priority = chart.priority;\r
266                                         c._updated = true;\r
267                                 }\r
268 \r
269                                 if(typeof(chart.update_every) !== 'undefined' && chart.update_every !== c.update_every) {\r
270                                         if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' updated its update_every');\r
271                                         c.update_every = chart.update_every;\r
272                                         c._updated = true;\r
273                                 }\r
274 \r
275                                 if(typeof(chart.dimensions) !== 'undefined') {\r
276                                         for(var x in chart.dimensions) {\r
277                                                 if(typeof(c.dimensions[x]) === 'undefined') {\r
278                                                         c._dimensions_count++;\r
279 \r
280                                                         c.dimensions[x] = {\r
281                                                                 _created: false,\r
282                                                                 _updated: false,\r
283                                                                 id: x,                                  // the unique id of the dimension\r
284                                                                 name: x,                                // the name of the dimension\r
285                                                                 algorithm: netdata.chartAlgorithms.absolute,    // the id of the netdata algorithm\r
286                                                                 multiplier: 1,                  // the multiplier\r
287                                                                 divisor: 1,                             // the divisor\r
288                                                                 hidden: false,                  // is hidden (boolean)\r
289                                                         };\r
290 \r
291                                                         if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ' created dimension ' + x);\r
292                                                         c._updated = true;\r
293                                                 }\r
294 \r
295                                                 var dim = chart.dimensions[x];\r
296                                                 var d = c.dimensions[x];\r
297 \r
298                                                 if(typeof(dim.name) !== 'undefined' && d.name !== dim.name) {\r
299                                                         if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its name');\r
300                                                         d.name = dim.name;\r
301                                                         d._updated = true;\r
302                                                 }\r
303 \r
304                                                 if(typeof(dim.algorithm) !== 'undefined' && d.algorithm !== dim.algorithm) {\r
305                                                         if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its algorithm from ' + d.algorithm + ' to ' + dim.algorithm);\r
306                                                         d.algorithm = dim.algorithm;\r
307                                                         d._updated = true;\r
308                                                 }\r
309 \r
310                                                 if(typeof(dim.multiplier) !== 'undefined' && d.multiplier !== dim.multiplier) {\r
311                                                         if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its multiplier');\r
312                                                         d.multiplier = dim.multiplier;\r
313                                                         d._updated = true;\r
314                                                 }\r
315 \r
316                                                 if(typeof(dim.divisor) !== 'undefined' && d.divisor !== dim.divisor) {\r
317                                                         if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its divisor');\r
318                                                         d.divisor = dim.divisor;\r
319                                                         d._updated = true;\r
320                                                 }\r
321 \r
322                                                 if(typeof(dim.hidden) !== 'undefined' && d.hidden !== dim.hidden) {\r
323                                                         if(netdata.options.DEBUG === true) netdata.debug('chart ' + id + ', dimension ' + x + ' updated its hidden status');\r
324                                                         d.hidden = dim.hidden;\r
325                                                         d._updated = true;\r
326                                                 }\r
327 \r
328                                                 if(d._updated) c._updated = true;\r
329                                         }\r
330                                 }\r
331 \r
332                                 if(netdata.options.DEBUG === true) netdata.debug(netdata.charts);\r
333                                 return netdata.charts[id];\r
334                         };\r
335 \r
336                         this.services.push(service);\r
337                         if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': service added.');\r
338                 }\r
339         },\r
340 \r
341         serviceRun: function(service) {\r
342                 if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': starting data collection...');\r
343                 service.running = true;\r
344                 service.started = new Date().getTime();\r
345                 service.updates++;\r
346 \r
347                 service.module.update(service, function() {\r
348                         service.ended = new Date().getTime();\r
349                         service.duration = service.ended - service.started;\r
350                         if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': data collection ended in ' + service.duration.toString() + ' ms.');\r
351                         service.running = false;\r
352                 });\r
353         },\r
354 \r
355         runAllServices: function() {\r
356                 if(netdata.options.DEBUG === true) netdata.debug('runAllServices()');\r
357 \r
358                 var now = new Date().getTime();\r
359                 var len = netdata.services.length;\r
360                 while(len--) {\r
361                         var service = netdata.services[len];\r
362 \r
363                         if(service.enabled === false || service.running === true) continue;\r
364                         if(now - service.ended < service.update_every) continue;\r
365 \r
366                         netdata.serviceRun(service);\r
367                 }\r
368 \r
369                 setTimeout(netdata.runAllServices, 100);\r
370         },\r
371 \r
372         start: function() {\r
373                 if(netdata.options.DEBUG === true) this.debug('started, services:');\r
374 \r
375                 if(this.services.length === 0) {\r
376                         this.disableNodePlugin();\r
377                         process.exit(1);\r
378                 }\r
379                 else this.runAllServices();\r
380         },\r
381 \r
382         // disable the whole node.js plugin\r
383         disableNodePlugin: function() {\r
384                 this.send('DISABLE');\r
385                 process.exit(1);\r
386         },\r
387 \r
388         requestFromParams: function(protocol, hostname, port, path, method) {\r
389                 return {\r
390                         protocol: protocol,\r
391                         hostname: hostname,\r
392                         port: port,\r
393                         path: path,\r
394                         //family: 4,\r
395                         method: method,\r
396                         headers: {\r
397                                 'Content-Type': 'application/x-www-form-urlencoded',\r
398                                 'Connection': 'keep-alive'\r
399                         },\r
400                         agent: new http.Agent({\r
401                                 keepAlive: true,\r
402                                 keepAliveMsecs: netdata.options.update_every,\r
403                                 maxSockets: 2, // it must be 2 to work\r
404                                 maxFreeSockets: 1\r
405                         })\r
406                 };\r
407         },\r
408 \r
409         requestFromURL: function(a_url) {\r
410                 var u = url.parse(a_url);\r
411                 return netdata.requestFromParams(u.protocol, u.hostname, u.port, u.path, 'GET');\r
412         },\r
413 \r
414         processResponse: function(service, data, callback) {\r
415                 if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': processing response...');\r
416 \r
417                 callback(service, data);\r
418 \r
419                 service.module.running--;\r
420                 if(service.module.running <= 0) {\r
421                         service.module.running = 0;\r
422 \r
423                         // check if we run under configure\r
424                         if(service.module.configure_callback !== null) {\r
425                                 if(netdata.options.DEBUG === true) this.debug(service.module.name + ': configuration finish callback called from processResponse().');\r
426                                 var ccallback = service.module.configure_callback;\r
427                                 service.module.configure_callback = null;\r
428                                 ccallback();\r
429                         }\r
430                 }\r
431         },\r
432 \r
433         getResponse: function(service, response, callback) {\r
434                 if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': got response...');\r
435 \r
436                 var end = false;\r
437                 var data = '';\r
438                 response.setEncoding('utf8');\r
439 \r
440                 if(response.statusCode !== 200) {\r
441                         if(end === false) {\r
442                                 if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': got HTTP code ' + response.statusCode + ', failed to get data.');\r
443                                 end = true;\r
444                                 netdata.processResponse(service, null, callback);\r
445                         }\r
446                 }\r
447 \r
448                 response.on('data', function(chunk) {\r
449                         if(end === false) data += chunk;\r
450                 });\r
451 \r
452                 response.on('error', function() {\r
453                         if(end === false) {\r
454                                 if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': Read error, failed to get data.');\r
455                                 end = true;\r
456                                 netdata.processResponse(service, null, callback);\r
457                         }\r
458                 });\r
459 \r
460                 response.on('end', function() {\r
461                         if(end === false) {\r
462                                 if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': read completed.');\r
463                                 end = true;\r
464                                 netdata.processResponse(service, data, callback);\r
465                         }\r
466                 });\r
467         },\r
468 \r
469         serviceExecute: function(service, callback) {\r
470                 service.module.running++;\r
471 \r
472                 if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': making request: ' + netdata.stringify(service.request));\r
473                 var req = http.request(service.request, function(response) {\r
474                         if(netdata.options.DEBUG === true) netdata.debug(service.module.name + ': ' + service.name + ': request done.');\r
475                         netdata.getResponse(service, response, callback);\r
476                 });\r
477 \r
478                 req.on('error', function(e) {\r
479                         netdata.error(service.module.name + ': ' + service.name + ': failed to make request: ' + netdata.stringify(service.request) + ', message: ' + e.message);\r
480                         netdata.processResponse(service, null, callback);\r
481                 });\r
482 \r
483                 // write data to request body\r
484                 if(typeof service.postData !== 'undefined' && service.request.method === 'POST')\r
485                         req.write(service.postData);\r
486 \r
487                 req.end();\r
488         },\r
489 \r
490         configure: function(module, config, callback) {\r
491                 if(netdata.options.DEBUG === true) this.debug(module.name + ': configuring (update_every: ' + this.options.update_every + ')...');\r
492 \r
493                 module.running = 0;\r
494                 module.update_every = this.options.update_every;\r
495 \r
496                 if(typeof config.update_every !== 'undefined')\r
497                         module.update_every = config.update_every * 1000;\r
498 \r
499                 module.enable_autodetect = (config.enable_autodetect)?true:false;\r
500 \r
501                 if(typeof(callback) === 'function')\r
502                         module.configure_callback = callback;\r
503                 else\r
504                         module.configure_callback = null;\r
505 \r
506                 var added = module.configure(config);\r
507 \r
508                 if(netdata.options.DEBUG === true) this.debug(module.name + ': configured, reporting ' + added + ' eligible services.');\r
509 \r
510                 if(module.configure_callback !== null && added === 0) {\r
511                         if(netdata.options.DEBUG === true) this.debug(module.name + ': configuration finish callback called from configure().');\r
512                         module.configure_callback = null;\r
513                         callback();\r
514                 }\r
515 \r
516                 return added;\r
517         }\r
518 \r
519 };\r
520 \r
521 if(netdata.options.DEBUG === true) netdata.debug('loaded netdata from: ' + __filename);\r
522 module.exports = netdata;\r
523 \r
524 /*\r
525 var test1 = netdata.chart('test1', { name: 'test name', dimensions: { dim1: {}}});\r
526 netdata.begin(test1);\r
527 netdata.set('dim1', 1);\r
528 netdata.end();\r
529 netdata.begin(test1);\r
530 netdata.set('dim1', 2);\r
531 netdata.end();\r
532 netdata.begin(test1);\r
533 netdata.set('dim1', 3);\r
534 netdata.end();\r
535 netdata.begin(test1);\r
536 netdata.set('dim1', 4);\r
537 netdata.end();\r
538 */\r