@cryptotaxi247 / netdata-1 / commits / e9ccc75a4

Provide more agent analytics to posthog (#11020)

* Move statistics related functions to analytics.c * error message change, space added after if * start an analytics thread * use heartbeat instead of sleep * add late enviroment (after rrdinit) pick of some attributes * change loop * re-enable info messages * remove possible new line * log and report hits on allmetrics pages. detect if exporting engines are enabled/in use, and report them * use lowercase for analytics variables * add collectors * add buildinfo * more attributes from late environment * add new attributes to v1/info * re-gather meta data before exit. update allmetrics counters to be available in v1/info * log hits to dashboard * add mirrored hosts * added notification methods * fix spaces, proper JSON naming * add alerts, charts and metrics count * more attributes * keep the thread up, and report a meta event every 2 hours * small formating changes. Disable analytics_log_prometheus when for unit testing. Add the new attributes to the anonymous-statistics.sh.in script * applied clang-format * dont gather data again on exit * safe buffer length in snprintfz * add rrdset lock * remove show_archived * remove setenv * calculate lengths during sets

Emmanuel Vasilakis committed Apr 27, 2021 at 10:11 UTC e9ccc75a451f986224c5f294a74001929b4bbe97
13 files changed +1220 -197
CMakeLists.txt
+2
@@ -828,6 +828,8 @@ set(DAEMON_FILES
828 daemon/daemon.h
829 daemon/global_statistics.c
830 daemon/global_statistics.h
831 + daemon/analytics.c
832 + daemon/analytics.h
833 daemon/main.c
834 daemon/main.h
835 daemon/signals.c
Makefile.am
+2
@@ -668,6 +668,8 @@ DAEMON_FILES = \
668 daemon/daemon.h \
669 daemon/global_statistics.c \
670 daemon/global_statistics.h \
671 + daemon/analytics.c \
672 + daemon/analytics.h \
673 daemon/main.c \
674 daemon/main.h \
675 daemon/signals.c \
daemon/analytics.c new
+856
@@ -0,0 +1,856 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "common.h"
4 +
5 +struct analytics_data analytics_data;
6 +extern void analytics_exporting_connectors (BUFFER *b);
7 +extern void analytics_build_info (BUFFER *b);
8 +extern int aclk_connected;
9 +
10 +struct collector {
11 + char *plugin;
12 + char *module;
13 +};
14 +
15 +struct array_printer {
16 + int c;
17 + BUFFER *both;
18 +};
19 +
20 +/*
21 + * Debug logging
22 + */
23 +void analytics_log_data(void)
24 +{
25 + debug(D_ANALYTICS, "NETDATA_CONFIG_STREAM_ENABLED : [%s]", analytics_data.netdata_config_stream_enabled);
26 + debug(D_ANALYTICS, "NETDATA_CONFIG_MEMORY_MODE : [%s]", analytics_data.netdata_config_memory_mode);
27 + debug(D_ANALYTICS, "NETDATA_CONFIG_EXPORTING_ENABLED : [%s]", analytics_data.netdata_config_exporting_enabled);
28 + debug(D_ANALYTICS, "NETDATA_EXPORTING_CONNECTORS : [%s]", analytics_data.netdata_exporting_connectors);
29 + debug(D_ANALYTICS, "NETDATA_ALLMETRICS_PROMETHEUS_USED : [%s]", analytics_data.netdata_allmetrics_prometheus_used);
30 + debug(D_ANALYTICS, "NETDATA_ALLMETRICS_SHELL_USED : [%s]", analytics_data.netdata_allmetrics_shell_used);
31 + debug(D_ANALYTICS, "NETDATA_ALLMETRICS_JSON_USED : [%s]", analytics_data.netdata_allmetrics_json_used);
32 + debug(D_ANALYTICS, "NETDATA_DASHBOARD_USED : [%s]", analytics_data.netdata_dashboard_used);
33 + debug(D_ANALYTICS, "NETDATA_COLLECTORS : [%s]", analytics_data.netdata_collectors);
34 + debug(D_ANALYTICS, "NETDATA_COLLECTORS_COUNT : [%s]", analytics_data.netdata_collectors_count);
35 + debug(D_ANALYTICS, "NETDATA_BUILDINFO : [%s]", analytics_data.netdata_buildinfo);
36 + debug(D_ANALYTICS, "NETDATA_CONFIG_PAGE_CACHE_SIZE : [%s]", analytics_data.netdata_config_page_cache_size);
37 + debug(D_ANALYTICS, "NETDATA_CONFIG_MULTIDB_DISK_QUOTA : [%s]", analytics_data.netdata_config_multidb_disk_quota);
38 + debug(D_ANALYTICS, "NETDATA_CONFIG_HTTPS_ENABLED : [%s]", analytics_data.netdata_config_https_enabled);
39 + debug(D_ANALYTICS, "NETDATA_CONFIG_WEB_ENABLED : [%s]", analytics_data.netdata_config_web_enabled);
40 + debug(D_ANALYTICS, "NETDATA_CONFIG_RELEASE_CHANNEL : [%s]", analytics_data.netdata_config_release_channel);
41 + debug(D_ANALYTICS, "NETDATA_MIRRORED_HOST_COUNT : [%s]", analytics_data.netdata_mirrored_host_count);
42 + debug(D_ANALYTICS, "NETDATA_MIRRORED_HOSTS_REACHABLE : [%s]", analytics_data.netdata_mirrored_hosts_reachable);
43 + debug(D_ANALYTICS, "NETDATA_MIRRORED_HOSTS_UNREACHABLE : [%s]", analytics_data.netdata_mirrored_hosts_unreachable);
44 + debug(D_ANALYTICS, "NETDATA_NOTIFICATION_METHODS : [%s]", analytics_data.netdata_notification_methods);
45 + debug(D_ANALYTICS, "NETDATA_ALARMS_NORMAL : [%s]", analytics_data.netdata_alarms_normal);
46 + debug(D_ANALYTICS, "NETDATA_ALARMS_WARNING : [%s]", analytics_data.netdata_alarms_warning);
47 + debug(D_ANALYTICS, "NETDATA_ALARMS_CRITICAL : [%s]", analytics_data.netdata_alarms_critical);
48 + debug(D_ANALYTICS, "NETDATA_CHARTS_COUNT : [%s]", analytics_data.netdata_charts_count);
49 + debug(D_ANALYTICS, "NETDATA_METRICS_COUNT : [%s]", analytics_data.netdata_metrics_count);
50 + debug(D_ANALYTICS, "NETDATA_CONFIG_IS_PARENT : [%s]", analytics_data.netdata_config_is_parent);
51 + debug(D_ANALYTICS, "NETDATA_CONFIG_HOSTS_AVAILABLE : [%s]", analytics_data.netdata_config_hosts_available);
52 + debug(D_ANALYTICS, "NETDATA_HOST_CLOUD_AVAILABLE : [%s]", analytics_data.netdata_host_cloud_available);
53 + debug(D_ANALYTICS, "NETDATA_HOST_ACLK_AVAILABLE : [%s]", analytics_data.netdata_host_aclk_available);
54 + debug(D_ANALYTICS, "NETDATA_HOST_ACLK_IMPLEMENTATION : [%s]", analytics_data.netdata_host_aclk_implementation);
55 + debug(D_ANALYTICS, "NETDATA_HOST_AGENT_CLAIMED : [%s]", analytics_data.netdata_host_agent_claimed);
56 +}
57 +
58 +/*
59 + * Free data
60 + */
61 +void analytics_free_data(void)
62 +{
63 + freez(analytics_data.netdata_config_stream_enabled);
64 + freez(analytics_data.netdata_config_memory_mode);
65 + freez(analytics_data.netdata_config_exporting_enabled);
66 + freez(analytics_data.netdata_exporting_connectors);
67 + freez(analytics_data.netdata_allmetrics_prometheus_used);
68 + freez(analytics_data.netdata_allmetrics_shell_used);
69 + freez(analytics_data.netdata_allmetrics_json_used);
70 + freez(analytics_data.netdata_dashboard_used);
71 + freez(analytics_data.netdata_collectors);
72 + freez(analytics_data.netdata_collectors_count);
73 + freez(analytics_data.netdata_buildinfo);
74 + freez(analytics_data.netdata_config_page_cache_size);
75 + freez(analytics_data.netdata_config_multidb_disk_quota);
76 + freez(analytics_data.netdata_config_https_enabled);
77 + freez(analytics_data.netdata_config_web_enabled);
78 + freez(analytics_data.netdata_config_release_channel);
79 + freez(analytics_data.netdata_mirrored_host_count);
80 + freez(analytics_data.netdata_mirrored_hosts_reachable);
81 + freez(analytics_data.netdata_mirrored_hosts_unreachable);
82 + freez(analytics_data.netdata_notification_methods);
83 + freez(analytics_data.netdata_alarms_normal);
84 + freez(analytics_data.netdata_alarms_warning);
85 + freez(analytics_data.netdata_alarms_critical);
86 + freez(analytics_data.netdata_charts_count);
87 + freez(analytics_data.netdata_metrics_count);
88 + freez(analytics_data.netdata_config_is_parent);
89 + freez(analytics_data.netdata_config_hosts_available);
90 + freez(analytics_data.netdata_host_cloud_available);
91 + freez(analytics_data.netdata_host_aclk_available);
92 + freez(analytics_data.netdata_host_aclk_implementation);
93 + freez(analytics_data.netdata_host_agent_claimed);
94 +}
95 +
96 +/*
97 + * Set a numeric/boolean data with a value
98 + */
99 +void analytics_set_data(char **name, char *value)
100 +{
101 + if (*name) {
102 + analytics_data.data_length -= strlen(*name);
103 + freez(*name);
104 + }
105 + *name = strdupz(value);
106 + analytics_data.data_length += strlen(*name);
107 +}
108 +
109 +/*
110 + * Set a string data with a value
111 + */
112 +void analytics_set_data_str(char **name, char *value)
113 +{
114 + size_t value_string_len;
115 + if (*name) {
116 + analytics_data.data_length -= strlen(*name);
117 + freez(*name);
118 + }
119 + value_string_len = strlen(value) + 4;
120 + *name = mallocz(sizeof(char) * value_string_len);
121 + snprintfz(*name, value_string_len - 1, "\"%s\"", value);
122 + analytics_data.data_length += strlen(*name);
123 +}
124 +
125 +/*
126 + * Get data, used by web api v1
127 + */
128 +void analytics_get_data(char *name, BUFFER *wb)
129 +{
130 + buffer_strcat(wb, name);
131 +}
132 +
133 +/*
134 + * Log hits on the allmetrics page, with prometheus parameter
135 + */
136 +void analytics_log_prometheus(void)
137 +{
138 + if (likely(analytics_data.prometheus_hits < ANALYTICS_MAX_PROMETHEUS_HITS)) {
139 + analytics_data.prometheus_hits++;
140 + char b[7];
141 + snprintfz(b, 6, "%d", analytics_data.prometheus_hits);
142 + analytics_set_data(&analytics_data.netdata_allmetrics_prometheus_used, b);
143 + }
144 +}
145 +
146 +/*
147 + * Log hits on the allmetrics page, with shell parameter (or default)
148 + */
149 +void analytics_log_shell(void)
150 +{
151 + if (likely(analytics_data.shell_hits < ANALYTICS_MAX_SHELL_HITS)) {
152 + analytics_data.shell_hits++;
153 + char b[7];
154 + snprintfz(b, 6, "%d", analytics_data.shell_hits);
155 + analytics_set_data(&analytics_data.netdata_allmetrics_shell_used, b);
156 + }
157 +}
158 +
159 +/*
160 + * Log hits on the allmetrics page, with json parameter
161 + */
162 +void analytics_log_json(void)
163 +{
164 + if (likely(analytics_data.json_hits < ANALYTICS_MAX_JSON_HITS)) {
165 + analytics_data.json_hits++;
166 + char b[7];
167 + snprintfz(b, 6, "%d", analytics_data.json_hits);
168 + analytics_set_data(&analytics_data.netdata_allmetrics_json_used, b);
169 + }
170 +}
171 +
172 +/*
173 + * Log hits on the dashboard, (when calling HELLO).
174 + */
175 +void analytics_log_dashboard(void)
176 +{
177 + if (likely(analytics_data.dashboard_hits < ANALYTICS_MAX_DASHBOARD_HITS)) {
178 + analytics_data.dashboard_hits++;
179 + char b[7];
180 + snprintfz(b, 6, "%d", analytics_data.dashboard_hits);
181 + analytics_set_data(&analytics_data.netdata_dashboard_used, b);
182 + }
183 +}
184 +
185 +void analytics_mirrored_hosts(void)
186 +{
187 + RRDHOST *host;
188 + int count = 0;
189 + int reachable = 0;
190 + int unreachable = 0;
191 + char b[11];
192 +
193 + rrd_rdlock();
194 + rrdhost_foreach_read(host)
195 + {
196 + if (rrdhost_flag_check(host, RRDHOST_FLAG_ARCHIVED))
197 + continue;
198 +
199 + netdata_mutex_lock(&host->receiver_lock);
200 + ((host->receiver || host == localhost) ? reachable++ : unreachable++);
201 + netdata_mutex_unlock(&host->receiver_lock);
202 +
203 + count++;
204 + }
205 + rrd_unlock();
206 +
207 + snprintfz(b, 10, "%d", count);
208 + analytics_set_data(&analytics_data.netdata_mirrored_host_count, b);
209 + snprintfz(b, 10, "%d", reachable);
210 + analytics_set_data(&analytics_data.netdata_mirrored_hosts_reachable, b);
211 + snprintfz(b, 10, "%d", unreachable);
212 + analytics_set_data(&analytics_data.netdata_mirrored_hosts_unreachable, b);
213 +}
214 +
215 +void analytics_exporters(void)
216 +{
217 + //when no exporters are available, an empty string will be sent
218 + //decide if something else is more suitable (but propably not null)
219 + BUFFER *bi = buffer_create(1000);
220 + analytics_exporting_connectors(bi);
221 + analytics_set_data_str(&analytics_data.netdata_exporting_connectors, (char *)buffer_tostring(bi));
222 + buffer_free(bi);
223 +}
224 +
225 +int collector_counter_callb(void *entry, void *data)
226 +{
227 + struct array_printer *ap = (struct array_printer *)data;
228 + struct collector *col = (struct collector *)entry;
229 +
230 + BUFFER *bt = ap->both;
231 +
232 + if (likely(ap->c)) {
233 + buffer_strcat(bt, ",");
234 + }
235 +
236 + buffer_strcat(bt, "{");
237 + buffer_strcat(bt, " \"plugin\": \"");
238 + buffer_strcat(bt, col->plugin);
239 + buffer_strcat(bt, "\", \"module\":\"");
240 + buffer_strcat(bt, col->module);
241 + buffer_strcat(bt, "\" }");
242 +
243 + (ap->c)++;
244 +
245 + return 0;
246 +}
247 +
248 +/*
249 + * Create a JSON array of available collectors, same as in api/v1/info
250 + */
251 +void analytics_collectors(void)
252 +{
253 + RRDSET *st;
254 + DICTIONARY *dict = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED);
255 + char name[500];
256 + BUFFER *bt = buffer_create(1000);
257 +
258 + rrdset_foreach_read(st, localhost)
259 + {
260 + if (rrdset_is_available_for_viewers(st)) {
261 + struct collector col = { .plugin = st->plugin_name ? st->plugin_name : "",
262 + .module = st->module_name ? st->module_name : "" };
263 + snprintfz(name, 499, "%s:%s", col.plugin, col.module);
264 + dictionary_set(dict, name, &col, sizeof(struct collector));
265 + }
266 + }
267 +
268 + struct array_printer ap;
269 + ap.c = 0;
270 + ap.both = bt;
271 +
272 + dictionary_get_all(dict, collector_counter_callb, &ap);
273 + dictionary_destroy(dict);
274 +
275 + analytics_set_data(&analytics_data.netdata_collectors, (char *)buffer_tostring(ap.both));
276 +
277 + {
278 + char b[7];
279 + snprintfz(b, 6, "%d", ap.c);
280 + analytics_set_data(&analytics_data.netdata_collectors_count, b);
281 + }
282 +
283 + buffer_free(bt);
284 +}
285 +
286 +/*
287 + * Run alarm-notify.sh script using the dump_methods parameter
288 + * SEND_CUSTOM is always available
289 + */
290 +void analytics_alarms_notifications(void)
291 +{
292 + char *script;
293 + script = mallocz(
294 + sizeof(char) * (strlen(netdata_configured_primary_plugins_dir) + strlen("alarm-notify.sh dump_methods") + 2));
295 + sprintf(script, "%s/%s", netdata_configured_primary_plugins_dir, "alarm-notify.sh");
296 + if (unlikely(access(script, R_OK) != 0)) {
297 + info("Alarm notify script %s not found.", script);
298 + freez(script);
299 + return;
300 + }
301 +
302 + strcat(script, " dump_methods");
303 +
304 + pid_t command_pid;
305 +
306 + info("Executing %s", script);
307 +
308 + BUFFER *b = buffer_create(1000);
309 + int cnt = 0;
310 + FILE *fp = mypopen(script, &command_pid);
311 + if (fp) {
312 + char line[200 + 1];
313 +
314 + while (fgets(line, 200, fp) != NULL) {
315 + char *end = line;
316 + while (*end && *end != '\n')
317 + end++;
318 + *end = '\0';
319 +
320 + if (likely(cnt))
321 + buffer_strcat(b, "|");
322 +
323 + buffer_strcat(b, line);
324 +
325 + cnt++;
326 + }
327 + mypclose(fp, command_pid);
328 + }
329 + freez(script);
330 +
331 + analytics_set_data_str(&analytics_data.netdata_notification_methods, (char *)buffer_tostring(b));
332 +
333 + buffer_free(b);
334 +}
335 +
336 +void analytics_charts(void)
337 +{
338 + RRDSET *st;
339 + int c = 0;
340 + rrdset_foreach_read(st, localhost)
341 + {
342 + if (rrdset_is_available_for_viewers(st)) {
343 + c++;
344 + }
345 + }
346 + {
347 + char b[7];
348 + snprintfz(b, 6, "%d", c);
349 + analytics_set_data(&analytics_data.netdata_charts_count, b);
350 + }
351 +}
352 +
353 +void analytics_metrics(void)
354 +{
355 + RRDSET *st;
356 + long int dimensions = 0;
357 + RRDDIM *rd;
358 + rrdset_foreach_read(st, localhost)
359 + {
360 + rrdset_rdlock(st);
361 + rrddim_foreach_read(rd, st)
362 + {
363 + if (rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN) || rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE))
364 + continue;
365 + dimensions++;
366 + }
367 + rrdset_unlock(st);
368 + }
369 + {
370 + char b[7];
371 + snprintfz(b, 6, "%ld", dimensions);
372 + analytics_set_data(&analytics_data.netdata_metrics_count, b);
373 + }
374 +}
375 +
376 +void analytics_alarms(void)
377 +{
378 + int alarm_warn = 0, alarm_crit = 0, alarm_normal = 0;
379 + char b[10];
380 + RRDCALC *rc;
381 + for (rc = localhost->alarms; rc; rc = rc->next) {
382 + if (unlikely(!rc->rrdset || !rc->rrdset->last_collected_time.tv_sec))
383 + continue;
384 +
385 + switch (rc->status) {
386 + case RRDCALC_STATUS_WARNING:
387 + alarm_warn++;
388 + break;
389 + case RRDCALC_STATUS_CRITICAL:
390 + alarm_crit++;
391 + break;
392 + default:
393 + alarm_normal++;
394 + }
395 + }
396 +
397 + snprintfz(b, 9, "%d", alarm_normal);
398 + analytics_set_data(&analytics_data.netdata_alarms_normal, b);
399 + snprintfz(b, 9, "%d", alarm_warn);
400 + analytics_set_data(&analytics_data.netdata_alarms_warning, b);
401 + snprintfz(b, 9, "%d", alarm_crit);
402 + analytics_set_data(&analytics_data.netdata_alarms_critical, b);
403 +}
404 +
405 +/*
406 + * Misc attributes to get (run from meta)
407 + */
408 +void analytics_misc(void)
409 +{
410 +#ifdef ENABLE_ACLK
411 + analytics_set_data(&analytics_data.netdata_host_cloud_available, "true");
412 +#ifdef ACLK_NG
413 + analytics_set_data_str(&analytics_data.netdata_host_aclk_implementation, "Next Generation");
414 +#else
415 + analytics_set_data_str(&analytics_data.netdata_host_aclk_implementation, "legacy");
416 +#endif
417 +#else
418 + analytics_set_data(&analytics_data.netdata_host_cloud_available, "false");
419 +#endif
420 +
421 +#ifdef ENABLE_ACLK
422 + if (aclk_connected)
423 + analytics_set_data(&analytics_data.netdata_host_aclk_available, "true");
424 + else
425 +#endif
426 + analytics_set_data(&analytics_data.netdata_host_aclk_available, "false");
427 +}
428 +
429 +/*
430 + * Get the meta data, called from the thread once after the original delay
431 + * These are values that won't change between agent restarts, and therefore
432 + * don't try to read them on each META event send
433 + */
434 +void analytics_gather_immutable_meta_data(void)
435 +{
436 + analytics_misc();
437 + analytics_exporters();
438 +}
439 +
440 +/*
441 + * Get the meta data, called from the thread on every heartbeat, and right before the EXIT event
442 + * These are values that can change between agent restarts, and therefore
443 + * try to read them on each META event send
444 + */
445 +void analytics_gather_mutable_meta_data(void)
446 +{
447 + rrdhost_rdlock(localhost);
448 +
449 + analytics_collectors();
450 + analytics_alarms();
451 + analytics_charts();
452 + analytics_metrics();
453 +
454 + rrdhost_unlock(localhost);
455 +
456 + analytics_mirrored_hosts();
457 + analytics_alarms_notifications();
458 +
459 + analytics_set_data(
460 + &analytics_data.netdata_config_is_parent, (localhost->next || configured_as_parent()) ? "true" : "false");
461 +
462 + if (is_agent_claimed())
463 + analytics_set_data(&analytics_data.netdata_host_agent_claimed, "true");
464 + else {
465 + analytics_set_data(&analytics_data.netdata_host_agent_claimed, "false");
466 + }
467 +
468 + {
469 + char b[7];
470 + snprintfz(b, 6, "%d", analytics_data.prometheus_hits);
471 + analytics_set_data(&analytics_data.netdata_allmetrics_prometheus_used, b);
472 +
473 + snprintfz(b, 6, "%d", analytics_data.shell_hits);
474 + analytics_set_data(&analytics_data.netdata_allmetrics_shell_used, b);
475 +
476 + snprintfz(b, 6, "%d", analytics_data.json_hits);
477 + analytics_set_data(&analytics_data.netdata_allmetrics_json_used, b);
478 +
479 + snprintfz(b, 6, "%d", analytics_data.dashboard_hits);
480 + analytics_set_data(&analytics_data.netdata_dashboard_used, b);
481 +
482 + snprintfz(b, 6, "%zu", rrd_hosts_available);
483 + analytics_set_data(&analytics_data.netdata_config_hosts_available, b);
484 + }
485 +}
486 +
487 +void analytics_main_cleanup(void *ptr)
488 +{
489 + struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
490 + static_thread->enabled = NETDATA_MAIN_THREAD_EXITING;
491 +
492 + debug(D_ANALYTICS, "Cleaning up...");
493 +
494 + static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
495 +}
496 +
497 +/*
498 + * The analytics thread. Sleep for ANALYTICS_INIT_SLEEP_SEC,
499 + * gather the data, and then go to a loop where every ANALYTICS_HEARTBEAT
500 + * it will send a new META event after gathering data that could be changed
501 + * while the agent is running
502 + */
503 +void *analytics_main(void *ptr)
504 +{
505 + netdata_thread_cleanup_push(analytics_main_cleanup, ptr);
506 + unsigned int sec = 0;
507 + heartbeat_t hb;
508 + heartbeat_init(&hb);
509 + usec_t step_ut = USEC_PER_SEC;
510 +
511 + debug(D_ANALYTICS, "Analytics thread starts");
512 +
513 + //first delay after agent start
514 + while (!netdata_exit && likely(sec <= ANALYTICS_INIT_SLEEP_SEC)) {
515 + heartbeat_next(&hb, step_ut);
516 + sec++;
517 + }
518 +
519 + if (unlikely(netdata_exit))
520 + goto cleanup;
521 +
522 + analytics_gather_immutable_meta_data();
523 + analytics_gather_mutable_meta_data();
524 + send_statistics("META", "-", "-");
525 + analytics_log_data();
526 +
527 + sec = 0;
528 + while (1) {
529 + heartbeat_next(&hb, step_ut * 2);
530 + sec += 2;
531 +
532 + if (unlikely(netdata_exit))
533 + break;
534 +
535 + if (likely(sec < ANALYTICS_HEARTBEAT))
536 + continue;
537 +
538 + analytics_gather_mutable_meta_data();
539 + send_statistics("META", "-", "-");
540 + analytics_log_data();
541 + sec = 0;
542 + }
543 +
544 +cleanup:
545 + netdata_thread_cleanup_pop(1);
546 + return NULL;
547 +}
548 +
549 +static const char *verify_required_directory(const char *dir)
550 +{
551 + if (chdir(dir) == -1)
552 + fatal("Cannot change directory to '%s'", dir);
553 +
554 + DIR *d = opendir(dir);
555 + if (!d)
556 + fatal("Cannot examine the contents of directory '%s'", dir);
557 + closedir(d);
558 +
559 + return dir;
560 +}
561 +
562 +/*
563 + * This is called after the rrdinit
564 + * These values will be sent on the START event
565 + */
566 +void set_late_global_environment()
567 +{
568 + analytics_set_data(&analytics_data.netdata_config_stream_enabled, default_rrdpush_enabled ? "true" : "false");
569 + analytics_set_data_str(&analytics_data.netdata_config_memory_mode, (char *)rrd_memory_mode_name(default_rrd_memory_mode));
570 + analytics_set_data(&analytics_data.netdata_config_exporting_enabled, appconfig_get_boolean(&exporting_config, CONFIG_SECTION_EXPORTING, "enabled", 1) ? "true" : "false");
571 +
572 +#ifdef ENABLE_DBENGINE
573 + {
574 + char b[16];
575 + snprintfz(b, 15, "%d", default_rrdeng_page_cache_mb);
576 + analytics_set_data(&analytics_data.netdata_config_page_cache_size, b);
577 +
578 + snprintfz(b, 15, "%d", default_multidb_disk_quota_mb);
579 + analytics_set_data(&analytics_data.netdata_config_multidb_disk_quota, b);
580 + }
581 +#endif
582 +
583 +#ifdef ENABLE_HTTPS
584 + analytics_set_data(&analytics_data.netdata_config_https_enabled, "true");
585 +#else
586 + analytics_set_data(&analytics_data.netdata_config_https_enabled, "false");
587 +#endif
588 +
589 + if (web_server_mode == WEB_SERVER_MODE_NONE)
590 + analytics_set_data(&analytics_data.netdata_config_web_enabled, "false");
591 + else
592 + analytics_set_data(&analytics_data.netdata_config_web_enabled, "true");
593 +
594 + analytics_set_data_str(&analytics_data.netdata_config_release_channel, (char *)get_release_channel());
595 +
596 + {
597 + BUFFER *bi = buffer_create(1000);
598 + analytics_build_info(bi);
599 + analytics_set_data_str(&analytics_data.netdata_buildinfo, (char *)buffer_tostring(bi));
600 + buffer_free(bi);
601 + }
602 +}
603 +
604 +static void get_system_timezone(void)
605 +{
606 + // avoid flood calls to stat(/etc/localtime)
607 + // http://stackoverflow.com/questions/4554271/how-to-avoid-excessive-stat-etc-localtime-calls-in-strftime-on-linux
608 + const char *tz = getenv("TZ");
609 + if (!tz || !*tz)
610 + setenv("TZ", config_get(CONFIG_SECTION_GLOBAL, "TZ environment variable", ":/etc/localtime"), 0);
611 +
612 + char buffer[FILENAME_MAX + 1] = "";
613 + const char *timezone = NULL;
614 + ssize_t ret;
615 +
616 + // use the TZ variable
617 + if (tz && *tz && *tz != ':') {
618 + timezone = tz;
619 + info("TIMEZONE: using TZ variable '%s'", timezone);
620 + }
621 +
622 + // use the contents of /etc/timezone
623 + if (!timezone && !read_file("/etc/timezone", buffer, FILENAME_MAX)) {
624 + timezone = buffer;
625 + info("TIMEZONE: using the contents of /etc/timezone: '%s'", timezone);
626 + }
627 +
628 + // read the link /etc/localtime
629 + if (!timezone) {
630 + ret = readlink("/etc/localtime", buffer, FILENAME_MAX);
631 +
632 + if (ret > 0) {
633 + buffer[ret] = '\0';
634 +
635 + char *cmp = "/usr/share/zoneinfo/";
636 + size_t cmp_len = strlen(cmp);
637 +
638 + char *s = strstr(buffer, cmp);
639 + if (s && s[cmp_len]) {
640 + timezone = &s[cmp_len];
641 + info("TIMEZONE: using the link of /etc/localtime: '%s'", timezone);
642 + }
643 + } else
644 + buffer[0] = '\0';
645 + }
646 +
647 + // find the timezone from strftime()
648 + if (!timezone) {
649 + time_t t;
650 + struct tm *tmp, tmbuf;
651 +
652 + t = now_realtime_sec();
653 + tmp = localtime_r(&t, &tmbuf);
654 +
655 + if (tmp != NULL) {
656 + if (strftime(buffer, FILENAME_MAX, "%Z", tmp) == 0)
657 + buffer[0] = '\0';
658 + else {
659 + buffer[FILENAME_MAX] = '\0';
660 + timezone = buffer;
661 + info("TIMEZONE: using strftime(): '%s'", timezone);
662 + }
663 + }
664 + }
665 +
666 + if (timezone && *timezone) {
667 + // make sure it does not have illegal characters
668 + // info("TIMEZONE: fixing '%s'", timezone);
669 +
670 + size_t len = strlen(timezone);
671 + char tmp[len + 1];
672 + char *d = tmp;
673 + *d = '\0';
674 +
675 + while (*timezone) {
676 + if (isalnum(*timezone) || *timezone == '_' || *timezone == '/')
677 + *d++ = *timezone++;
678 + else
679 + timezone++;
680 + }
681 + *d = '\0';
682 + strncpyz(buffer, tmp, len);
683 + timezone = buffer;
684 + info("TIMEZONE: fixed as '%s'", timezone);
685 + }
686 +
687 + if (!timezone || !*timezone)
688 + timezone = "unknown";
689 +
690 + netdata_configured_timezone = config_get(CONFIG_SECTION_GLOBAL, "timezone", timezone);
691 +}
692 +
693 +void set_global_environment()
694 +{
695 + {
696 + char b[16];
697 + snprintfz(b, 15, "%d", default_rrd_update_every);
698 + setenv("NETDATA_UPDATE_EVERY", b, 1);
699 + }
700 +
701 + setenv("NETDATA_VERSION", program_version, 1);
702 + setenv("NETDATA_HOSTNAME", netdata_configured_hostname, 1);
703 + setenv("NETDATA_CONFIG_DIR", verify_required_directory(netdata_configured_user_config_dir), 1);
704 + setenv("NETDATA_USER_CONFIG_DIR", verify_required_directory(netdata_configured_user_config_dir), 1);
705 + setenv("NETDATA_STOCK_CONFIG_DIR", verify_required_directory(netdata_configured_stock_config_dir), 1);
706 + setenv("NETDATA_PLUGINS_DIR", verify_required_directory(netdata_configured_primary_plugins_dir), 1);
707 + setenv("NETDATA_WEB_DIR", verify_required_directory(netdata_configured_web_dir), 1);
708 + setenv("NETDATA_CACHE_DIR", verify_required_directory(netdata_configured_cache_dir), 1);
709 + setenv("NETDATA_LIB_DIR", verify_required_directory(netdata_configured_varlib_dir), 1);
710 + setenv("NETDATA_LOCK_DIR", netdata_configured_lock_dir, 1);
711 + setenv("NETDATA_LOG_DIR", verify_required_directory(netdata_configured_log_dir), 1);
712 + setenv("HOME", verify_required_directory(netdata_configured_home_dir), 1);
713 + setenv("NETDATA_HOST_PREFIX", netdata_configured_host_prefix, 1);
714 +
715 + analytics_data.data_length = 0;
716 + analytics_set_data(&analytics_data.netdata_config_stream_enabled, "null");
717 + analytics_set_data(&analytics_data.netdata_config_memory_mode, "null");
718 + analytics_set_data(&analytics_data.netdata_config_exporting_enabled, "null");
719 + analytics_set_data(&analytics_data.netdata_exporting_connectors, "null");
720 + analytics_set_data(&analytics_data.netdata_allmetrics_prometheus_used, "null");
721 + analytics_set_data(&analytics_data.netdata_allmetrics_shell_used, "null");
722 + analytics_set_data(&analytics_data.netdata_allmetrics_json_used, "null");
723 + analytics_set_data(&analytics_data.netdata_dashboard_used, "null");
724 + analytics_set_data(&analytics_data.netdata_collectors, "null");
725 + analytics_set_data(&analytics_data.netdata_collectors_count, "null");
726 + analytics_set_data(&analytics_data.netdata_buildinfo, "null");
727 + analytics_set_data(&analytics_data.netdata_config_page_cache_size, "null");
728 + analytics_set_data(&analytics_data.netdata_config_multidb_disk_quota, "null");
729 + analytics_set_data(&analytics_data.netdata_config_https_enabled, "null");
730 + analytics_set_data(&analytics_data.netdata_config_web_enabled, "null");
731 + analytics_set_data(&analytics_data.netdata_config_release_channel, "null");
732 + analytics_set_data(&analytics_data.netdata_mirrored_host_count, "null");
733 + analytics_set_data(&analytics_data.netdata_mirrored_hosts_reachable, "null");
734 + analytics_set_data(&analytics_data.netdata_mirrored_hosts_unreachable, "null");
735 + analytics_set_data(&analytics_data.netdata_notification_methods, "null");
736 + analytics_set_data(&analytics_data.netdata_alarms_normal, "null");
737 + analytics_set_data(&analytics_data.netdata_alarms_warning, "null");
738 + analytics_set_data(&analytics_data.netdata_alarms_critical, "null");
739 + analytics_set_data(&analytics_data.netdata_charts_count, "null");
740 + analytics_set_data(&analytics_data.netdata_metrics_count, "null");
741 + analytics_set_data(&analytics_data.netdata_config_is_parent, "null");
742 + analytics_set_data(&analytics_data.netdata_config_hosts_available, "null");
743 + analytics_set_data(&analytics_data.netdata_host_cloud_available, "null");
744 + analytics_set_data(&analytics_data.netdata_host_aclk_implementation, "null");
745 + analytics_set_data(&analytics_data.netdata_host_aclk_available, "null");
746 + analytics_set_data(&analytics_data.netdata_host_agent_claimed, "null");
747 +
748 + analytics_data.prometheus_hits = 0;
749 + analytics_data.shell_hits = 0;
750 + analytics_data.json_hits = 0;
751 + analytics_data.dashboard_hits = 0;
752 +
753 + char *default_port = appconfig_get(&netdata_config, CONFIG_SECTION_WEB, "default port", NULL);
754 + int clean = 0;
755 + if (!default_port) {
756 + default_port = strdupz("19999");
757 + clean = 1;
758 + }
759 +
760 + setenv("NETDATA_LISTEN_PORT", default_port, 1);
761 + if (clean)
762 + freez(default_port);
763 +
764 + get_system_timezone();
765 +
766 + // set the path we need
767 + char path[1024 + 1], *p = getenv("PATH");
768 + if (!p)
769 + p = "/bin:/usr/bin";
770 + snprintfz(path, 1024, "%s:%s", p, "/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin");
771 + setenv("PATH", config_get(CONFIG_SECTION_PLUGINS, "PATH environment variable", path), 1);
772 +
773 + // python options
774 + p = getenv("PYTHONPATH");
775 + if (!p)
776 + p = "";
777 + setenv("PYTHONPATH", config_get(CONFIG_SECTION_PLUGINS, "PYTHONPATH environment variable", p), 1);
778 +
779 + // disable buffering for python plugins
780 + setenv("PYTHONUNBUFFERED", "1", 1);
781 +
782 + // switch to standard locale for plugins
783 + setenv("LC_ALL", "C", 1);
784 +}
785 +
786 +void send_statistics(const char *action, const char *action_result, const char *action_data)
787 +{
788 + static char *as_script;
789 +
790 + if (netdata_anonymous_statistics_enabled == -1) {
791 + char *optout_file = mallocz(
792 + sizeof(char) *
793 + (strlen(netdata_configured_user_config_dir) + strlen(".opt-out-from-anonymous-statistics") + 2));
794 + sprintf(optout_file, "%s/%s", netdata_configured_user_config_dir, ".opt-out-from-anonymous-statistics");
795 + if (likely(access(optout_file, R_OK) != 0)) {
796 + as_script = mallocz(
797 + sizeof(char) *
798 + (strlen(netdata_configured_primary_plugins_dir) + strlen("anonymous-statistics.sh") + 2));
799 + sprintf(as_script, "%s/%s", netdata_configured_primary_plugins_dir, "anonymous-statistics.sh");
800 + if (unlikely(access(as_script, R_OK) != 0)) {
801 + netdata_anonymous_statistics_enabled = 0;
802 + info("Anonymous statistics script %s not found.", as_script);
803 + freez(as_script);
804 + } else {
805 + netdata_anonymous_statistics_enabled = 1;
806 + }
807 + } else {
808 + netdata_anonymous_statistics_enabled = 0;
809 + as_script = NULL;
810 + }
811 + freez(optout_file);
812 + }
813 + if (!netdata_anonymous_statistics_enabled)
814 + return;
815 + if (!action)
816 + return;
817 + if (!action_result)
818 + action_result = "";
819 + if (!action_data)
820 + action_data = "";
821 + char *command_to_run = mallocz(
822 + sizeof(char) * (strlen(action) + strlen(action_result) + strlen(action_data) + strlen(as_script) +
823 + analytics_data.data_length + (ANALYTICS_NO_OF_ITEMS * 3) + 15));
824 + pid_t command_pid;
825 +
826 + sprintf(
827 + command_to_run,
828 + "%s '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' ",
829 + as_script, action, action_result, action_data, analytics_data.netdata_config_stream_enabled,
830 + analytics_data.netdata_config_memory_mode, analytics_data.netdata_config_exporting_enabled,
831 + analytics_data.netdata_exporting_connectors, analytics_data.netdata_allmetrics_prometheus_used,
832 + analytics_data.netdata_allmetrics_shell_used, analytics_data.netdata_allmetrics_json_used,
833 + analytics_data.netdata_dashboard_used, analytics_data.netdata_collectors,
834 + analytics_data.netdata_collectors_count, analytics_data.netdata_buildinfo,
835 + analytics_data.netdata_config_page_cache_size, analytics_data.netdata_config_multidb_disk_quota,
836 + analytics_data.netdata_config_https_enabled, analytics_data.netdata_config_web_enabled,
837 + analytics_data.netdata_config_release_channel, analytics_data.netdata_mirrored_host_count,
838 + analytics_data.netdata_mirrored_hosts_reachable, analytics_data.netdata_mirrored_hosts_unreachable,
839 + analytics_data.netdata_notification_methods, analytics_data.netdata_alarms_normal,
840 + analytics_data.netdata_alarms_warning, analytics_data.netdata_alarms_critical,
841 + analytics_data.netdata_charts_count, analytics_data.netdata_metrics_count,
842 + analytics_data.netdata_config_is_parent, analytics_data.netdata_config_hosts_available,
843 + analytics_data.netdata_host_cloud_available, analytics_data.netdata_host_aclk_available,
844 + analytics_data.netdata_host_aclk_implementation, analytics_data.netdata_host_agent_claimed);
845 +
846 + info("%s", command_to_run);
847 +
848 + FILE *fp = mypopen(command_to_run, &command_pid);
849 + if (fp) {
850 + char buffer[100 + 1];
851 + while (fgets(buffer, 100, fp) != NULL)
852 + ;
853 + mypclose(fp, command_pid);
854 + }
855 + freez(command_to_run);
856 +}
daemon/analytics.h new
+89
@@ -0,0 +1,89 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_ANALYTICS_H
4 +#define NETDATA_ANALYTICS_H 1
5 +
6 +#include "../daemon/common.h"
7 +
8 +/* Max number of seconds before the first META analytics is sent */
9 +#define ANALYTICS_INIT_SLEEP_SEC 120
10 +
11 +/* Send a META event every X seconds */
12 +#define ANALYTICS_HEARTBEAT 7200
13 +
14 +/* Maximum number of hits to log */
15 +#define ANALYTICS_MAX_PROMETHEUS_HITS 255
16 +#define ANALYTICS_MAX_SHELL_HITS 255
17 +#define ANALYTICS_MAX_JSON_HITS 255
18 +#define ANALYTICS_MAX_DASHBOARD_HITS 255
19 +
20 +#define NETDATA_PLUGIN_HOOK_ANALYTICS \
21 + { \
22 + .name = "ANALYTICS", \
23 + .config_section = NULL, \
24 + .config_name = NULL, \
25 + .enabled = 0, \
26 + .thread = NULL, \
27 + .init_routine = NULL, \
28 + .start_routine = analytics_main \
29 + },
30 +
31 +/* Needed to calculate the space needed for parameters */
32 +#define ANALYTICS_NO_OF_ITEMS 31
33 +
34 +struct analytics_data {
35 + char *netdata_config_stream_enabled;
36 + char *netdata_config_memory_mode;
37 + char *netdata_exporting_connectors;
38 + char *netdata_config_exporting_enabled;
39 + char *netdata_allmetrics_prometheus_used;
40 + char *netdata_allmetrics_shell_used;
41 + char *netdata_allmetrics_json_used;
42 + char *netdata_dashboard_used;
43 + char *netdata_collectors;
44 + char *netdata_collectors_count;
45 + char *netdata_buildinfo;
46 + char *netdata_config_page_cache_size;
47 + char *netdata_config_multidb_disk_quota;
48 + char *netdata_config_https_enabled;
49 + char *netdata_config_web_enabled;
50 + char *netdata_config_release_channel;
51 + char *netdata_mirrored_host_count;
52 + char *netdata_mirrored_hosts_reachable;
53 + char *netdata_mirrored_hosts_unreachable;
54 + char *netdata_notification_methods;
55 + char *netdata_alarms_normal;
56 + char *netdata_alarms_warning;
57 + char *netdata_alarms_critical;
58 + char *netdata_charts_count;
59 + char *netdata_metrics_count;
60 + char *netdata_config_is_parent;
61 + char *netdata_config_hosts_available;
62 + char *netdata_host_cloud_available;
63 + char *netdata_host_aclk_available;
64 + char *netdata_host_aclk_implementation;
65 + char *netdata_host_agent_claimed;
66 +
67 + size_t data_length;
68 +
69 + uint8_t prometheus_hits;
70 + uint8_t shell_hits;
71 + uint8_t json_hits;
72 + uint8_t dashboard_hits;
73 +};
74 +
75 +extern void *analytics_main(void *ptr);
76 +extern void analytics_get_data(char *name, BUFFER *wb);
77 +extern void set_late_global_environment(void);
78 +extern void analytics_free_data(void);
79 +extern void set_global_environment(void);
80 +extern void send_statistics(const char *action, const char *action_result, const char *action_data);
81 +extern void analytics_log_shell(void);
82 +extern void analytics_log_json(void);
83 +extern void analytics_log_prometheus(void);
84 +extern void analytics_log_dashboard(void);
85 +extern void analytics_gather_mutable_meta_data(void);
86 +
87 +extern struct analytics_data analytics_data;
88 +
89 +#endif //NETDATA_ANALYTICS_H
daemon/anonymous-statistics.sh.in
+67 -1
@@ -26,6 +26,39 @@ fi
26 NETDATA_VERSION=$(echo "${NETDATA_VERSION}" | sed 's/-.*//g' | tr -d 'v')
27
28 # -------------------------------------------------------------------------------------------------
29 +# Get the extra variables
30 +
31 +NETDATA_CONFIG_STREAM_ENABLED="${4}"
32 +NETDATA_CONFIG_MEMORY_MODE="${5}"
33 +NETDATA_CONFIG_EXPORTING_ENABLED="${6}"
34 +NETDATA_EXPORTING_CONNECTORS="${7}"
35 +NETDATA_ALLMETRICS_PROMETHEUS_USED="${8}"
36 +NETDATA_ALLMETRICS_SHELL_USED="${9}"
37 +NETDATA_ALLMETRICS_JSON_USED="${10}"
38 +NETDATA_DASHBOARD_USED="${11}"
39 +NETDATA_COLLECTORS="${12}"
40 +NETDATA_COLLECTORS_COUNT="${13}"
41 +NETDATA_BUILDINFO="${14}"
42 +NETDATA_CONFIG_PAGE_CACHE_SIZE="${15}"
43 +NETDATA_CONFIG_MULTIDB_DISK_QUOTA="${16}"
44 +NETDATA_CONFIG_HTTPS_ENABLED="${17}"
45 +NETDATA_CONFIG_WEB_ENABLED="${18}"
46 +NETDATA_CONFIG_RELEASE_CHANNEL="${19}"
47 +NETDATA_MIRRORED_HOST_COUNT="${20}"
48 +NETDATA_MIRRORED_HOSTS_REACHABLE="${21}"
49 +NETDATA_MIRRORED_HOSTS_UNREACHABLE="${22}"
50 +NETDATA_NOTIFICATION_METHODS="${23}"
51 +NETDATA_ALARMS_NORMAL="${24}"
52 +NETDATA_ALARMS_WARNING="${25}"
53 +NETDATA_ALARMS_CRITICAL="${26}"
54 +NETDATA_CHARTS_COUNT="${27}"
55 +NETDATA_METRICS_COUNT="${28}"
56 +NETDATA_CONFIG_IS_PARENT="${29}"
57 +NETDATA_CONFIG_HOSTS_AVAILABLE="${30}"
58 +NETDATA_HOST_CLOUD_AVAILABLE="${31}"
59 +NETDATA_HOST_ACLK_AVAILABLE="${32}"
60 +NETDATA_HOST_ACLK_IMPLEMENTATION="${33}"
61 +NETDATA_HOST_AGENT_CLAIMED="${34}"
62
63 # define body of request to be sent
64 REQ_BODY="$(cat << EOF
@@ -44,6 +77,8 @@ REQ_BODY="$(cat << EOF
77 "action_data": "${ACTION_DATA}",
78 "netdata_machine_guid": "${NETDATA_REGISTRY_UNIQUE_ID}",
79 "netdata_version": "${NETDATA_VERSION}",
80 + "netdata_buildinfo": ${NETDATA_BUILDINFO},
81 + "netdata_release_channel": ${NETDATA_CONFIG_RELEASE_CHANNEL},
82 "host_os_name": "${NETDATA_HOST_OS_NAME}",
83 "host_os_id": "${NETDATA_HOST_OS_ID}",
84 "host_os_id_like": "${NETDATA_HOST_OS_ID_LIKE}",
@@ -72,7 +107,38 @@ REQ_BODY="$(cat << EOF
107 "system_disk_detection": "${NETDATA_SYSTEM_DISK_DETECTION}",
108 "system_ram_detection": "${NETDATA_SYSTEM_RAM_DETECTION}",
109 "system_total_disk_size": "${NETDATA_SYSTEM_TOTAL_DISK_SIZE}",
75 - "system_total_ram": "${NETDATA_SYSTEM_TOTAL_RAM}"
110 + "system_total_ram": "${NETDATA_SYSTEM_TOTAL_RAM}",
111 + "config_stream_enabled": ${NETDATA_CONFIG_STREAM_ENABLED},
112 + "config_memory_mode": ${NETDATA_CONFIG_MEMORY_MODE},
113 + "config_page_cache_size": ${NETDATA_CONFIG_PAGE_CACHE_SIZE},
114 + "config_multidb_disk_quota": ${NETDATA_CONFIG_MULTIDB_DISK_QUOTA},
115 + "config_https_enabled": ${NETDATA_CONFIG_HTTPS_ENABLED},
116 + "config_web_enabled": ${NETDATA_CONFIG_WEB_ENABLED},
117 + "config_exporting_enabled": ${NETDATA_CONFIG_EXPORTING_ENABLED},
118 + "config_is_parent": ${NETDATA_CONFIG_IS_PARENT},
119 + "config_hosts_available": ${NETDATA_CONFIG_HOSTS_AVAILABLE},
120 + "alarms_normal": ${NETDATA_ALARMS_NORMAL},
121 + "alarms_warning": ${NETDATA_ALARMS_WARNING},
122 + "alarms_critical": ${NETDATA_ALARMS_CRITICAL},
123 + "host_charts_count": ${NETDATA_CHARTS_COUNT},
124 + "host_metrics_count": ${NETDATA_METRICS_COUNT},
125 + "host_collectors":[
126 + ${NETDATA_COLLECTORS}
127 + ],
128 + "host_collectors_count": ${NETDATA_COLLECTORS_COUNT},
129 + "host_notification_methods": ${NETDATA_NOTIFICATION_METHODS},
130 + "host_allmetrics_prometheus_used": ${NETDATA_ALLMETRICS_PROMETHEUS_USED},
131 + "host_allmetrics_shell_used": ${NETDATA_ALLMETRICS_SHELL_USED},
132 + "host_allmetrics_json_used": ${NETDATA_ALLMETRICS_JSON_USED},
133 + "host_dashboard_used": ${NETDATA_DASHBOARD_USED},
134 + "host_cloud_available": ${NETDATA_HOST_CLOUD_AVAILABLE},
135 + "host_agent_claimed": ${NETDATA_HOST_AGENT_CLAIMED},
136 + "host_aclk_available": ${NETDATA_HOST_ACLK_AVAILABLE},
137 + "host_aclk_implementation": ${NETDATA_HOST_ACLK_IMPLEMENTATION},
138 + "mirrored_host_count": ${NETDATA_MIRRORED_HOST_COUNT},
139 + "mirrored_hosts_reachable": ${NETDATA_MIRRORED_HOSTS_REACHABLE},
140 + "mirrored_hosts_unreachable": ${NETDATA_MIRRORED_HOSTS_UNREACHABLE},
141 + "exporting_connectors": ${NETDATA_EXPORTING_CONNECTORS}
142 }
143 }
144 EOF
daemon/buildinfo.c
+47
@@ -2,6 +2,7 @@
2
3 #include <stdio.h>
4 #include "./config.h"
5 +#include "common.h"
6
7 // Optional features
8
@@ -312,3 +313,49 @@ void print_build_info_json(void) {
313 printf(" }\n");
314 printf("}\n");
315 };
316 +
317 +//return a list of enabled features for use in analytics
318 +//find a way to have proper |
319 +void analytics_build_info(BUFFER *b) {
320 + if(FEAT_DBENGINE) buffer_strcat (b, "dbengine");
321 + if(FEAT_NATIVE_HTTPS) buffer_strcat (b, "|Native HTTPS");
322 + if(FEAT_CLOUD) buffer_strcat (b, "|Netdata Cloud");
323 + if(FEAT_TLS_HOST_VERIFY) buffer_strcat (b, "|TLS Host Verification");
324 +
325 + if(FEAT_JEMALLOC) buffer_strcat (b, "|jemalloc");
326 + if(FEAT_JSONC) buffer_strcat (b, "|JSON-C");
327 + if(FEAT_LIBCAP) buffer_strcat (b, "|libcap");
328 + if(FEAT_CRYPTO) buffer_strcat (b, "|libcrypto");
329 + if(FEAT_LIBM) buffer_strcat (b, "|libm");
330 +
331 +#ifndef ACLK_NG
332 +#if defined(ENABLE_ACLK)
333 + {
334 + char buf[20];
335 + snprintfz(buf, 19, "|LWS v%d.%d.%d", LWS_LIBRARY_VERSION_MAJOR, LWS_LIBRARY_VERSION_MINOR, LWS_LIBRARY_VERSION_PATCH);
336 + if(FEAT_LWS) buffer_strcat(b, buf);
337 + }
338 +#else
339 + if(FEAT_LWS) buffer_strcat(b, "|LWS");
340 +#endif
341 + if(FEAT_MOSQUITTO) buffer_strcat(b, "|mosquitto");
342 +#endif
343 + if(FEAT_TCMALLOC) buffer_strcat(b, "|tcalloc");
344 + if(FEAT_ZLIB) buffer_strcat(b, "|zlib");
345 +
346 + if(FEAT_APPS_PLUGIN) buffer_strcat(b, "|apps");
347 + if(FEAT_CGROUP_NET) buffer_strcat(b, "|cgroup Network Tracking");
348 + if(FEAT_CUPS) buffer_strcat(b, "|CUPS");
349 + if(FEAT_EBPF) buffer_strcat(b, "|EBPF");
350 + if(FEAT_IPMI) buffer_strcat(b, "|IPMI");
351 + if(FEAT_NFACCT) buffer_strcat(b, "|NFACCT");
352 + if(FEAT_PERF) buffer_strcat(b, "|perf");
353 + if(FEAT_SLABINFO) buffer_strcat(b, "|slabinfo");
354 + if(FEAT_XEN) buffer_strcat(b, "|Xen");
355 + if(FEAT_XEN_VBD_ERROR) buffer_strcat(b, "|Xen VBD Error Tracking");
356 +
357 + if(FEAT_KINESIS) buffer_strcat(b, "|AWS Kinesis");
358 + if(FEAT_PUBSUB) buffer_strcat(b, "|GCP PubSub");
359 + if(FEAT_MONGO) buffer_strcat(b, "|MongoDB");
360 + if(FEAT_REMOTE_WRITE) buffer_strcat(b, "|Prometheus Remote Write");
361 +}
daemon/common.h
+1
@@ -82,6 +82,7 @@
82 #include "main.h"
83 #include "signals.h"
84 #include "commands.h"
85 +#include "analytics.h"
86
87 // global netdata daemon variables
88 extern char *netdata_configured_hostname;
daemon/main.c
+17 -194
@@ -28,6 +28,7 @@ void netdata_cleanup_and_exit(int ret) {
28 info("EXIT: netdata prepares to exit with code %d...", ret);
29
30 send_statistics("EXIT", ret?"ERROR":"OK","-");
31 + analytics_free_data();
32
33 char agent_crash_file[FILENAME_MAX + 1];
34 snprintfz(agent_crash_file, FILENAME_MAX, "%s/.agent_crash", netdata_configured_varlib_dir);
@@ -99,6 +100,7 @@ struct netdata_static_thread static_threads[] = {
100
101 NETDATA_PLUGIN_HOOK_PLUGINSD
102 NETDATA_PLUGIN_HOOK_HEALTH
103 + NETDATA_PLUGIN_HOOK_ANALYTICS
104
105 {NULL, NULL, NULL, 0, NULL, NULL, NULL}
106 };
@@ -397,18 +399,6 @@ void remove_option(int opt_index, int *argc, char **argv) {
399 } while(argv[i][0] != '-' && opt_index >= *argc);
400 }
401
400 -static const char *verify_required_directory(const char *dir) {
401 - if(chdir(dir) == -1)
402 - fatal("Cannot cd to directory '%s'", dir);
403 -
404 - DIR *d = opendir(dir);
405 - if(!d)
406 - fatal("Cannot examine the contents of directory '%s'", dir);
407 - closedir(d);
408 -
409 - return dir;
410 -}
411 -
402 #ifdef ENABLE_HTTPS
403 static void security_init(){
404 char filename[FILENAME_MAX + 1];
@@ -612,147 +602,6 @@ static void get_netdata_configured_variables() {
602
603 }
604
615 -static void get_system_timezone(void) {
616 - // avoid flood calls to stat(/etc/localtime)
617 - // http://stackoverflow.com/questions/4554271/how-to-avoid-excessive-stat-etc-localtime-calls-in-strftime-on-linux
618 - const char *tz = getenv("TZ");
619 - if(!tz || !*tz)
620 - setenv("TZ", config_get(CONFIG_SECTION_GLOBAL, "TZ environment variable", ":/etc/localtime"), 0);
621 -
622 - char buffer[FILENAME_MAX + 1] = "";
623 - const char *timezone = NULL;
624 - ssize_t ret;
625 -
626 - // use the TZ variable
627 - if(tz && *tz && *tz != ':') {
628 - timezone = tz;
629 - // info("TIMEZONE: using TZ variable '%s'", timezone);
630 - }
631 -
632 - // use the contents of /etc/timezone
633 - if(!timezone && !read_file("/etc/timezone", buffer, FILENAME_MAX)) {
634 - timezone = buffer;
635 - // info("TIMEZONE: using the contents of /etc/timezone: '%s'", timezone);
636 - }
637 -
638 - // read the link /etc/localtime
639 - if(!timezone) {
640 - ret = readlink("/etc/localtime", buffer, FILENAME_MAX);
641 -
642 - if(ret > 0) {
643 - buffer[ret] = '\0';
644 -
645 - char *cmp = "/usr/share/zoneinfo/";
646 - size_t cmp_len = strlen(cmp);
647 -
648 - char *s = strstr(buffer, cmp);
649 - if (s && s[cmp_len]) {
650 - timezone = &s[cmp_len];
651 - // info("TIMEZONE: using the link of /etc/localtime: '%s'", timezone);
652 - }
653 - }
654 - else
655 - buffer[0] = '\0';
656 - }
657 -
658 - // find the timezone from strftime()
659 - if(!timezone) {
660 - time_t t;
661 - struct tm *tmp, tmbuf;
662 -
663 - t = now_realtime_sec();
664 - tmp = localtime_r(&t, &tmbuf);
665 -
666 - if (tmp != NULL) {
667 - if(strftime(buffer, FILENAME_MAX, "%Z", tmp) == 0)
668 - buffer[0] = '\0';
669 - else {
670 - buffer[FILENAME_MAX] = '\0';
671 - timezone = buffer;
672 - // info("TIMEZONE: using strftime(): '%s'", timezone);
673 - }
674 - }
675 - }
676 -
677 - if(timezone && *timezone) {
678 - // make sure it does not have illegal characters
679 - // info("TIMEZONE: fixing '%s'", timezone);
680 -
681 - size_t len = strlen(timezone);
682 - char tmp[len + 1];
683 - char *d = tmp;
684 - *d = '\0';
685 -
686 - while(*timezone) {
687 - if(isalnum(*timezone) || *timezone == '_' || *timezone == '/')
688 - *d++ = *timezone++;
689 - else
690 - timezone++;
691 - }
692 - *d = '\0';
693 - strncpyz(buffer, tmp, len);
694 - timezone = buffer;
695 - // info("TIMEZONE: fixed as '%s'", timezone);
696 - }
697 -
698 - if(!timezone || !*timezone)
699 - timezone = "unknown";
700 -
701 - netdata_configured_timezone = config_get(CONFIG_SECTION_GLOBAL, "timezone", timezone);
702 -}
703 -
704 -void set_global_environment() {
705 - {
706 - char b[16];
707 - snprintfz(b, 15, "%d", default_rrd_update_every);
708 - setenv("NETDATA_UPDATE_EVERY", b, 1);
709 - }
710 -
711 - setenv("NETDATA_VERSION" , program_version, 1);
712 - setenv("NETDATA_HOSTNAME" , netdata_configured_hostname, 1);
713 - setenv("NETDATA_CONFIG_DIR" , verify_required_directory(netdata_configured_user_config_dir), 1);
714 - setenv("NETDATA_USER_CONFIG_DIR" , verify_required_directory(netdata_configured_user_config_dir), 1);
715 - setenv("NETDATA_STOCK_CONFIG_DIR" , verify_required_directory(netdata_configured_stock_config_dir), 1);
716 - setenv("NETDATA_PLUGINS_DIR" , verify_required_directory(netdata_configured_primary_plugins_dir), 1);
717 - setenv("NETDATA_WEB_DIR" , verify_required_directory(netdata_configured_web_dir), 1);
718 - setenv("NETDATA_CACHE_DIR" , verify_required_directory(netdata_configured_cache_dir), 1);
719 - setenv("NETDATA_LIB_DIR" , verify_required_directory(netdata_configured_varlib_dir), 1);
720 - setenv("NETDATA_LOCK_DIR" , netdata_configured_lock_dir, 1);
721 - setenv("NETDATA_LOG_DIR" , verify_required_directory(netdata_configured_log_dir), 1);
722 - setenv("HOME" , verify_required_directory(netdata_configured_home_dir), 1);
723 - setenv("NETDATA_HOST_PREFIX" , netdata_configured_host_prefix, 1);
724 -
725 - char *default_port = appconfig_get(&netdata_config, CONFIG_SECTION_WEB, "default port", NULL);
726 - int clean = 0;
727 - if (!default_port) {
728 - default_port = strdupz("19999");
729 - clean = 1;
730 - }
731 -
732 - setenv("NETDATA_LISTEN_PORT" , default_port, 1);
733 - if(clean)
734 - freez(default_port);
735 -
736 - get_system_timezone();
737 -
738 - // set the path we need
739 - char path[1024 + 1], *p = getenv("PATH");
740 - if(!p) p = "/bin:/usr/bin";
741 - snprintfz(path, 1024, "%s:%s", p, "/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin");
742 - setenv("PATH", config_get(CONFIG_SECTION_PLUGINS, "PATH environment variable", path), 1);
743 -
744 - // python options
745 - p = getenv("PYTHONPATH");
746 - if(!p) p = "";
747 - setenv("PYTHONPATH", config_get(CONFIG_SECTION_PLUGINS, "PYTHONPATH environment variable", p), 1);
748 -
749 - // disable buffering for python plugins
750 - setenv("PYTHONUNBUFFERED", "1", 1);
751 -
752 - // switch to standard locale for plugins
753 - setenv("LC_ALL", "C", 1);
754 -}
755 -
605 static int load_netdata_conf(char *filename, char overwrite_used) {
606 errno = 0;
607
@@ -835,47 +684,6 @@ int get_system_info(struct rrdhost_system_info *system_info) {
684 return 0;
685 }
686
838 -void send_statistics( const char *action, const char *action_result, const char *action_data) {
839 - static char *as_script;
840 -
841 - if (netdata_anonymous_statistics_enabled == -1) {
842 - char *optout_file = mallocz(sizeof(char) * (strlen(netdata_configured_user_config_dir) +strlen(".opt-out-from-anonymous-statistics") + 2));
843 - sprintf(optout_file, "%s/%s", netdata_configured_user_config_dir, ".opt-out-from-anonymous-statistics");
844 - if (likely(access(optout_file, R_OK) != 0)) {
845 - as_script = mallocz(sizeof(char) * (strlen(netdata_configured_primary_plugins_dir) + strlen("anonymous-statistics.sh") + 2));
846 - sprintf(as_script, "%s/%s", netdata_configured_primary_plugins_dir, "anonymous-statistics.sh");
847 - if (unlikely(access(as_script, R_OK) != 0)) {
848 - netdata_anonymous_statistics_enabled=0;
849 - info("Anonymous statistics script %s not found.",as_script);
850 - freez(as_script);
851 - } else {
852 - netdata_anonymous_statistics_enabled=1;
853 - }
854 - } else {
855 - netdata_anonymous_statistics_enabled = 0;
856 - as_script = NULL;
857 - }
858 - freez(optout_file);
859 - }
860 - if(!netdata_anonymous_statistics_enabled) return;
861 - if (!action) return;
862 - if (!action_result) action_result="";
863 - if (!action_data) action_data="";
864 - char *command_to_run=mallocz(sizeof(char) * (strlen(action) + strlen(action_result) + strlen(action_data) + strlen(as_script) + 10));
865 - pid_t command_pid;
866 -
867 - sprintf(command_to_run,"%s '%s' '%s' '%s'", as_script, action, action_result, action_data);
868 - info("%s", command_to_run);
869 -
870 - FILE *fp = mypopen(command_to_run, &command_pid);
871 - if(fp) {
872 - char buffer[100 + 1];
873 - while (fgets(buffer, 100, fp) != NULL);
874 - mypclose(fp, command_pid);
875 - }
876 - freez(command_to_run);
877 -}
878 -
687 void set_silencers_filename() {
688 char filename[FILENAME_MAX + 1];
689 snprintfz(filename, FILENAME_MAX, "%s/health.silencers.json", netdata_configured_varlib_dir);
@@ -1511,10 +1319,25 @@ int main(int argc, char **argv) {
1319 info("netdata initialization completed. Enjoy real-time performance monitoring!");
1320 netdata_ready = 1;
1321
1322 + set_late_global_environment();
1323 +
1324 send_statistics("START", "-", "-");
1325 if (crash_detected)
1326 send_statistics("CRASH", "-", "-");
1327
1328 + //check if ANALYTICS needs to start
1329 + if (netdata_anonymous_statistics_enabled == 1) {
1330 + for (i = 0; static_threads[i].name != NULL; i++) {
1331 + if (!strncmp(static_threads[i].name, "ANALYTICS", 9)) {
1332 + struct netdata_static_thread *st = &static_threads[i];
1333 + st->thread = mallocz(sizeof(netdata_thread_t));
1334 + st->enabled = 1;
1335 + debug(D_SYSTEM, "Starting thread %s.", st->name);
1336 + netdata_thread_create(st->thread, st->name, NETDATA_THREAD_OPTION_DEFAULT, st->start_routine, st);
1337 + }
1338 + }
1339 + }
1340 +
1341 // ------------------------------------------------------------------------
1342 // Report ACLK build failure
1343 #ifndef ENABLE_ACLK
exporting/exporting_engine.c
+58
@@ -4,6 +4,64 @@
4
5 static struct engine *engine = NULL;
6
7 +void analytics_exporting_connectors(BUFFER *b)
8 +{
9 + if (!engine)
10 + return;
11 +
12 + uint8_t count = 0;
13 +
14 + for (struct instance *instance = engine->instance_root; instance; instance = instance->next) {
15 + if (count)
16 + buffer_strcat(b, "|");
17 +
18 + switch (instance->config.type) {
19 + case EXPORTING_CONNECTOR_TYPE_GRAPHITE:
20 + buffer_strcat(b, "Graphite");
21 + break;
22 + case EXPORTING_CONNECTOR_TYPE_GRAPHITE_HTTP:
23 + buffer_strcat(b, "GraphiteHTTP");
24 + break;
25 + case EXPORTING_CONNECTOR_TYPE_JSON:
26 + buffer_strcat(b, "JSON");
27 + break;
28 + case EXPORTING_CONNECTOR_TYPE_JSON_HTTP:
29 + buffer_strcat(b, "JSONHTTP");
30 + break;
31 + case EXPORTING_CONNECTOR_TYPE_OPENTSDB:
32 + buffer_strcat(b, "OpenTSDB");
33 + break;
34 + case EXPORTING_CONNECTOR_TYPE_OPENTSDB_HTTP:
35 + buffer_strcat(b, "OpenTSDBHTTP");
36 + break;
37 + case EXPORTING_CONNECTOR_TYPE_PROMETHEUS_REMOTE_WRITE:
38 +#if ENABLE_PROMETHEUS_REMOTE_WRITE
39 + buffer_strcat(b, "PrometheusRemoteWrite");
40 +#endif
41 + break;
42 + case EXPORTING_CONNECTOR_TYPE_KINESIS:
43 +#if HAVE_KINESIS
44 + buffer_strcat(b, "Kinesis");
45 +#endif
46 + break;
47 + case EXPORTING_CONNECTOR_TYPE_PUBSUB:
48 +#if ENABLE_EXPORTING_PUBSUB
49 + buffer_strcat(b, "Pubsub");
50 +#endif
51 + break;
52 + case EXPORTING_CONNECTOR_TYPE_MONGODB:
53 +#if HAVE_MONGOC
54 + buffer_strcat(b, "MongoDB");
55 +#endif
56 + break;
57 + default:
58 + buffer_strcat(b, "Unknown");
59 + }
60 +
61 + count++;
62 + }
63 +}
64 +
65 /**
66 * Exporting Clean Engine
67 *
exporting/prometheus/prometheus.c
+3
@@ -794,6 +794,9 @@ static inline time_t prometheus_preparation(
794 time_t now,
795 PROMETHEUS_OUTPUT_OPTIONS output_options)
796 {
797 +#ifndef UNIT_TESTING
798 + analytics_log_prometheus();
799 +#endif
800 if (!server || !*server)
801 server = "default";
802
libnetdata/log/log.h
+1
@@ -40,6 +40,7 @@ extern "C" {
40 #define D_STATSD 0x0000000010000000
41 #define D_POLLFD 0x0000000020000000
42 #define D_STREAM 0x0000000040000000
43 +#define D_ANALYTICS 0x0000000080000000
44 #define D_RRDENGINE 0x0000000100000000
45 #define D_ACLK 0x0000000200000000
46 #define D_METADATALOG 0x0000000400000000
web/api/exporters/shell/allmetrics_shell.c
+2
@@ -23,6 +23,7 @@ static inline size_t shell_name_copy(char *d, const char *s, size_t usable) {
23 #define SHELL_ELEMENT_MAX 100
24
25 void rrd_stats_api_v1_charts_allmetrics_shell(RRDHOST *host, BUFFER *wb) {
26 + analytics_log_shell();
27 rrdhost_rdlock(host);
28
29 // for each chart
@@ -92,6 +93,7 @@ void rrd_stats_api_v1_charts_allmetrics_shell(RRDHOST *host, BUFFER *wb) {
93 // ----------------------------------------------------------------------------
94
95 void rrd_stats_api_v1_charts_allmetrics_json(RRDHOST *host, BUFFER *wb) {
96 + analytics_log_json();
97 rrdhost_rdlock(host);
98
99 buffer_strcat(wb, "{");
web/api/web_api_v1.c
+75 -2
@@ -750,6 +750,7 @@ inline int web_client_api_request_v1_registry(RRDHOST *host, struct web_client *
750
751 if(unlikely(action == 'H')) {
752 // HELLO request, dashboard ACL
753 + analytics_log_dashboard();
754 if(unlikely(!web_client_can_access_dashboard(w)))
755 return web_client_permission_denied(w);
756 }
@@ -995,10 +996,82 @@ inline int web_client_api_request_v1_info_fill_buffer(RRDHOST *host, BUFFER *wb)
996 }
997 #ifdef ENABLE_ACLK
998 if (aclk_connected)
998 - buffer_strcat(wb, "\t\"aclk-available\": true\n");
999 + buffer_strcat(wb, "\t\"aclk-available\": true,\n");
1000 else
1001 #endif
1001 - buffer_strcat(wb, "\t\"aclk-available\": false\n"); // Intentionally valid with/without #ifdef above
1002 + buffer_strcat(wb, "\t\"aclk-available\": false,\n"); // Intentionally valid with/without #ifdef above
1003 +
1004 + buffer_strcat(wb, "\t\"memory-mode\": ");
1005 + analytics_get_data(analytics_data.netdata_config_memory_mode, wb);
1006 + buffer_strcat(wb, ",\n");
1007 +
1008 + buffer_strcat(wb, "\t\"multidb-disk-quota\": ");
1009 + analytics_get_data(analytics_data.netdata_config_multidb_disk_quota, wb);
1010 + buffer_strcat(wb, ",\n");
1011 +
1012 + buffer_strcat(wb, "\t\"page-cache-size\": ");
1013 + analytics_get_data(analytics_data.netdata_config_page_cache_size, wb);
1014 + buffer_strcat(wb, ",\n");
1015 +
1016 + buffer_strcat(wb, "\t\"stream-enabled\": ");
1017 + analytics_get_data(analytics_data.netdata_config_stream_enabled, wb);
1018 + buffer_strcat(wb, ",\n");
1019 +
1020 + buffer_strcat(wb, "\t\"hosts-available\": ");
1021 + analytics_get_data(analytics_data.netdata_config_hosts_available, wb);
1022 + buffer_strcat(wb, ",\n");
1023 +
1024 + buffer_strcat(wb, "\t\"https-enabled\": ");
1025 + analytics_get_data(analytics_data.netdata_config_https_enabled, wb);
1026 + buffer_strcat(wb, ",\n");
1027 +
1028 + buffer_strcat(wb, "\t\"buildinfo\": ");
1029 + analytics_get_data(analytics_data.netdata_buildinfo, wb);
1030 + buffer_strcat(wb, ",\n");
1031 +
1032 + buffer_strcat(wb, "\t\"release-channel\": ");
1033 + analytics_get_data(analytics_data.netdata_config_release_channel, wb);
1034 + buffer_strcat(wb, ",\n");
1035 +
1036 + buffer_strcat(wb, "\t\"web-enabled\": ");
1037 + analytics_get_data(analytics_data.netdata_config_web_enabled, wb);
1038 + buffer_strcat(wb, ",\n");
1039 +
1040 + buffer_strcat(wb, "\t\"notification-methods\": ");
1041 + analytics_get_data(analytics_data.netdata_notification_methods, wb);
1042 + buffer_strcat(wb, ",\n");
1043 +
1044 + buffer_strcat(wb, "\t\"exporting-enabled\": ");
1045 + analytics_get_data(analytics_data.netdata_config_exporting_enabled, wb);
1046 + buffer_strcat(wb, ",\n");
1047 +
1048 + buffer_strcat(wb, "\t\"exporting-connectors\": ");
1049 + analytics_get_data(analytics_data.netdata_exporting_connectors, wb);
1050 + buffer_strcat(wb, ",\n");
1051 +
1052 + buffer_strcat(wb, "\t\"allmetrics-prometheus-used\": ");
1053 + analytics_get_data(analytics_data.netdata_allmetrics_prometheus_used, wb);
1054 + buffer_strcat(wb, ",\n");
1055 +
1056 + buffer_strcat(wb, "\t\"allmetrics-shell-used\": ");
1057 + analytics_get_data(analytics_data.netdata_allmetrics_shell_used, wb);
1058 + buffer_strcat(wb, ",\n");
1059 +
1060 + buffer_strcat(wb, "\t\"allmetrics-json-used\": ");
1061 + analytics_get_data(analytics_data.netdata_allmetrics_json_used, wb);
1062 + buffer_strcat(wb, ",\n");
1063 +
1064 + buffer_strcat(wb, "\t\"dashboard-used\": ");
1065 + analytics_get_data(analytics_data.netdata_dashboard_used, wb);
1066 + buffer_strcat(wb, ",\n");
1067 +
1068 + buffer_strcat(wb, "\t\"charts-count\": ");
1069 + analytics_get_data(analytics_data.netdata_charts_count, wb);
1070 + buffer_strcat(wb, ",\n");
1071 +
1072 + buffer_strcat(wb, "\t\"metrics-count\": ");
1073 + analytics_get_data(analytics_data.netdata_metrics_count, wb);
1074 + buffer_strcat(wb, "\n");
1075
1076 buffer_strcat(wb, "}");
1077 return 0;