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