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