@cryptotaxi247 / netdata-1 / commits / a1ce482f3

Provide more agent analytics to posthog (#10887)

* 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

Emmanuel Vasilakis committed Apr 21, 2021 at 18:24 UTC a1ce482f3e336dbabe1b12b92f6339af6a2bbbf8
13 files changed +1206 -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
+880
@@ -0,0 +1,880 @@
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 + * Set the env variables
22 + */
23 +void analytics_setenv_data(void)
24 +{
25 + setenv("NETDATA_CONFIG_STREAM_ENABLED", analytics_data.netdata_config_stream_enabled, 1);
26 + setenv("NETDATA_CONFIG_MEMORY_MODE", analytics_data.netdata_config_memory_mode, 1);
27 + setenv("NETDATA_CONFIG_EXPORTING_ENABLED", analytics_data.netdata_config_exporting_enabled, 1);
28 + setenv("NETDATA_EXPORTING_CONNECTORS", analytics_data.netdata_exporting_connectors, 1);
29 + setenv("NETDATA_ALLMETRICS_PROMETHEUS_USED", analytics_data.netdata_allmetrics_prometheus_used, 1);
30 + setenv("NETDATA_ALLMETRICS_SHELL_USED", analytics_data.netdata_allmetrics_shell_used, 1);
31 + setenv("NETDATA_ALLMETRICS_JSON_USED", analytics_data.netdata_allmetrics_json_used, 1);
32 + setenv("NETDATA_DASHBOARD_USED", analytics_data.netdata_dashboard_used, 1);
33 + setenv("NETDATA_COLLECTORS", analytics_data.netdata_collectors, 1);
34 + setenv("NETDATA_COLLECTORS_COUNT", analytics_data.netdata_collectors_count, 1);
35 + setenv("NETDATA_BUILDINFO", analytics_data.netdata_buildinfo, 1);
36 + setenv("NETDATA_CONFIG_PAGE_CACHE_SIZE", analytics_data.netdata_config_page_cache_size, 1);
37 + setenv("NETDATA_CONFIG_MULTIDB_DISK_QUOTA", analytics_data.netdata_config_multidb_disk_quota, 1);
38 + setenv("NETDATA_CONFIG_HTTPS_ENABLED", analytics_data.netdata_config_https_enabled, 1);
39 + setenv("NETDATA_CONFIG_WEB_ENABLED", analytics_data.netdata_config_web_enabled, 1);
40 + setenv("NETDATA_CONFIG_RELEASE_CHANNEL", analytics_data.netdata_config_release_channel, 1);
41 + setenv("NETDATA_MIRRORED_HOST_COUNT", analytics_data.netdata_mirrored_host_count, 1);
42 + setenv("NETDATA_MIRRORED_HOSTS_REACHABLE", analytics_data.netdata_mirrored_hosts_reachable, 1);
43 + setenv("NETDATA_MIRRORED_HOSTS_UNREACHABLE", analytics_data.netdata_mirrored_hosts_unreachable, 1);
44 + setenv("NETDATA_NOTIFICATION_METHODS", analytics_data.netdata_notification_methods, 1);
45 + setenv("NETDATA_ALARMS_NORMAL", analytics_data.netdata_alarms_normal, 1);
46 + setenv("NETDATA_ALARMS_WARNING", analytics_data.netdata_alarms_warning, 1);
47 + setenv("NETDATA_ALARMS_CRITICAL", analytics_data.netdata_alarms_critical, 1);
48 + setenv("NETDATA_CHARTS_COUNT", analytics_data.netdata_charts_count, 1);
49 + setenv("NETDATA_METRICS_COUNT", analytics_data.netdata_metrics_count, 1);
50 + setenv("NETDATA_CONFIG_IS_PARENT", analytics_data.netdata_config_is_parent, 1);
51 + setenv("NETDATA_CONFIG_HOSTS_AVAILABLE", analytics_data.netdata_config_hosts_available, 1);
52 + setenv("NETDATA_HOST_CLOUD_AVAILABLE", analytics_data.netdata_host_cloud_available, 1);
53 + setenv("NETDATA_HOST_ACLK_AVAILABLE", analytics_data.netdata_host_aclk_available, 1);
54 + setenv("NETDATA_HOST_ACLK_IMPLEMENTATION", analytics_data.netdata_host_aclk_implementation, 1);
55 + setenv("NETDATA_HOST_AGENT_CLAIMED", analytics_data.netdata_host_agent_claimed, 1);
56 +}
57 +
58 +/*
59 + * Debug logging
60 + */
61 +void analytics_log_data(void)
62 +{
63 + debug(D_ANALYTICS, "NETDATA_CONFIG_STREAM_ENABLED : [%s]", analytics_data.netdata_config_stream_enabled);
64 + debug(D_ANALYTICS, "NETDATA_CONFIG_MEMORY_MODE : [%s]", analytics_data.netdata_config_memory_mode);
65 + debug(D_ANALYTICS, "NETDATA_CONFIG_EXPORTING_ENABLED : [%s]", analytics_data.netdata_config_exporting_enabled);
66 + debug(D_ANALYTICS, "NETDATA_EXPORTING_CONNECTORS : [%s]", analytics_data.netdata_exporting_connectors);
67 + debug(D_ANALYTICS, "NETDATA_ALLMETRICS_PROMETHEUS_USED : [%s]", analytics_data.netdata_allmetrics_prometheus_used);
68 + debug(D_ANALYTICS, "NETDATA_ALLMETRICS_SHELL_USED : [%s]", analytics_data.netdata_allmetrics_shell_used);
69 + debug(D_ANALYTICS, "NETDATA_ALLMETRICS_JSON_USED : [%s]", analytics_data.netdata_allmetrics_json_used);
70 + debug(D_ANALYTICS, "NETDATA_DASHBOARD_USED : [%s]", analytics_data.netdata_dashboard_used);
71 + debug(D_ANALYTICS, "NETDATA_COLLECTORS : [%s]", analytics_data.netdata_collectors);
72 + debug(D_ANALYTICS, "NETDATA_COLLECTORS_COUNT : [%s]", analytics_data.netdata_collectors_count);
73 + debug(D_ANALYTICS, "NETDATA_BUILDINFO : [%s]", analytics_data.netdata_buildinfo);
74 + debug(D_ANALYTICS, "NETDATA_CONFIG_PAGE_CACHE_SIZE : [%s]", analytics_data.netdata_config_page_cache_size);
75 + debug(D_ANALYTICS, "NETDATA_CONFIG_MULTIDB_DISK_QUOTA : [%s]", analytics_data.netdata_config_multidb_disk_quota);
76 + debug(D_ANALYTICS, "NETDATA_CONFIG_HTTPS_ENABLED : [%s]", analytics_data.netdata_config_https_enabled);
77 + debug(D_ANALYTICS, "NETDATA_CONFIG_WEB_ENABLED : [%s]", analytics_data.netdata_config_web_enabled);
78 + debug(D_ANALYTICS, "NETDATA_CONFIG_RELEASE_CHANNEL : [%s]", analytics_data.netdata_config_release_channel);
79 + debug(D_ANALYTICS, "NETDATA_MIRRORED_HOST_COUNT : [%s]", analytics_data.netdata_mirrored_host_count);
80 + debug(D_ANALYTICS, "NETDATA_MIRRORED_HOSTS_REACHABLE : [%s]", analytics_data.netdata_mirrored_hosts_reachable);
81 + debug(D_ANALYTICS, "NETDATA_MIRRORED_HOSTS_UNREACHABLE : [%s]", analytics_data.netdata_mirrored_hosts_unreachable);
82 + debug(D_ANALYTICS, "NETDATA_NOTIFICATION_METHODS : [%s]", analytics_data.netdata_notification_methods);
83 + debug(D_ANALYTICS, "NETDATA_ALARMS_NORMAL : [%s]", analytics_data.netdata_alarms_normal);
84 + debug(D_ANALYTICS, "NETDATA_ALARMS_WARNING : [%s]", analytics_data.netdata_alarms_warning);
85 + debug(D_ANALYTICS, "NETDATA_ALARMS_CRITICAL : [%s]", analytics_data.netdata_alarms_critical);
86 + debug(D_ANALYTICS, "NETDATA_CHARTS_COUNT : [%s]", analytics_data.netdata_charts_count);
87 + debug(D_ANALYTICS, "NETDATA_METRICS_COUNT : [%s]", analytics_data.netdata_metrics_count);
88 + debug(D_ANALYTICS, "NETDATA_CONFIG_IS_PARENT : [%s]", analytics_data.netdata_config_is_parent);
89 + debug(D_ANALYTICS, "NETDATA_CONFIG_HOSTS_AVAILABLE : [%s]", analytics_data.netdata_config_hosts_available);
90 + debug(D_ANALYTICS, "NETDATA_HOST_CLOUD_AVAILABLE : [%s]", analytics_data.netdata_host_cloud_available);
91 + debug(D_ANALYTICS, "NETDATA_HOST_ACLK_AVAILABLE : [%s]", analytics_data.netdata_host_aclk_available);
92 + debug(D_ANALYTICS, "NETDATA_HOST_ACLK_IMPLEMENTATION : [%s]", analytics_data.netdata_host_aclk_implementation);
93 + debug(D_ANALYTICS, "NETDATA_HOST_AGENT_CLAIMED : [%s]", analytics_data.netdata_host_agent_claimed);
94 +}
95 +
96 +/*
97 + * Free data
98 + */
99 +void analytics_free_data(void)
100 +{
101 + freez(analytics_data.netdata_config_stream_enabled);
102 + freez(analytics_data.netdata_config_memory_mode);
103 + freez(analytics_data.netdata_config_exporting_enabled);
104 + freez(analytics_data.netdata_exporting_connectors);
105 + freez(analytics_data.netdata_allmetrics_prometheus_used);
106 + freez(analytics_data.netdata_allmetrics_shell_used);
107 + freez(analytics_data.netdata_allmetrics_json_used);
108 + freez(analytics_data.netdata_dashboard_used);
109 + freez(analytics_data.netdata_collectors);
110 + freez(analytics_data.netdata_collectors_count);
111 + freez(analytics_data.netdata_buildinfo);
112 + freez(analytics_data.netdata_config_page_cache_size);
113 + freez(analytics_data.netdata_config_multidb_disk_quota);
114 + freez(analytics_data.netdata_config_https_enabled);
115 + freez(analytics_data.netdata_config_web_enabled);
116 + freez(analytics_data.netdata_config_release_channel);
117 + freez(analytics_data.netdata_mirrored_host_count);
118 + freez(analytics_data.netdata_mirrored_hosts_reachable);
119 + freez(analytics_data.netdata_mirrored_hosts_unreachable);
120 + freez(analytics_data.netdata_notification_methods);
121 + freez(analytics_data.netdata_alarms_normal);
122 + freez(analytics_data.netdata_alarms_warning);
123 + freez(analytics_data.netdata_alarms_critical);
124 + freez(analytics_data.netdata_charts_count);
125 + freez(analytics_data.netdata_metrics_count);
126 + freez(analytics_data.netdata_config_is_parent);
127 + freez(analytics_data.netdata_config_hosts_available);
128 + freez(analytics_data.netdata_host_cloud_available);
129 + freez(analytics_data.netdata_host_aclk_available);
130 + freez(analytics_data.netdata_host_aclk_implementation);
131 + freez(analytics_data.netdata_host_agent_claimed);
132 +}
133 +
134 +/*
135 + * Set a numeric/boolean data with a value
136 + */
137 +void analytics_set_data(char **name, char *value)
138 +{
139 + if (*name)
140 + freez(*name);
141 + *name = strdupz(value);
142 +}
143 +
144 +/*
145 + * Set a string data with a value
146 + */
147 +void analytics_set_data_str(char **name, char *value)
148 +{
149 + size_t value_string_len;
150 + if (*name)
151 + freez(*name);
152 + value_string_len = strlen(value) + 4;
153 + *name = mallocz(sizeof(char) * value_string_len);
154 + snprintfz(*name, value_string_len - 1, "\"%s\"", value);
155 +}
156 +
157 +/*
158 + * Get data, used by web api v1
159 + */
160 +void analytics_get_data(char *name, BUFFER *wb)
161 +{
162 + buffer_strcat(wb, name);
163 +}
164 +
165 +/*
166 + * Log hits on the allmetrics page, with prometheus parameter
167 + */
168 +void analytics_log_prometheus(void)
169 +{
170 + if (likely(analytics_data.prometheus_hits < ANALYTICS_MAX_PROMETHEUS_HITS)) {
171 + analytics_data.prometheus_hits++;
172 + char b[7];
173 + snprintfz(b, 6, "%d", analytics_data.prometheus_hits);
174 + analytics_set_data(&analytics_data.netdata_allmetrics_prometheus_used, b);
175 + }
176 +}
177 +
178 +/*
179 + * Log hits on the allmetrics page, with shell parameter (or default)
180 + */
181 +void analytics_log_shell(void)
182 +{
183 + if (likely(analytics_data.shell_hits < ANALYTICS_MAX_SHELL_HITS)) {
184 + analytics_data.shell_hits++;
185 + char b[7];
186 + snprintfz(b, 6, "%d", analytics_data.shell_hits);
187 + analytics_set_data(&analytics_data.netdata_allmetrics_shell_used, b);
188 + }
189 +}
190 +
191 +/*
192 + * Log hits on the allmetrics page, with json parameter
193 + */
194 +void analytics_log_json(void)
195 +{
196 + if (likely(analytics_data.json_hits < ANALYTICS_MAX_JSON_HITS)) {
197 + analytics_data.json_hits++;
198 + char b[7];
199 + snprintfz(b, 6, "%d", analytics_data.json_hits);
200 + analytics_set_data(&analytics_data.netdata_allmetrics_json_used, b);
201 + }
202 +}
203 +
204 +/*
205 + * Log hits on the dashboard, (when calling HELLO).
206 + */
207 +void analytics_log_dashboard(void)
208 +{
209 + if (likely(analytics_data.dashboard_hits < ANALYTICS_MAX_DASHBOARD_HITS)) {
210 + analytics_data.dashboard_hits++;
211 + char b[7];
212 + snprintfz(b, 6, "%d", analytics_data.dashboard_hits);
213 + analytics_set_data(&analytics_data.netdata_dashboard_used, b);
214 + }
215 +}
216 +
217 +void analytics_mirrored_hosts(void)
218 +{
219 + RRDHOST *host;
220 + int count = 0;
221 + int reachable = 0;
222 + int unreachable = 0;
223 + char b[11];
224 +
225 + rrd_rdlock();
226 + rrdhost_foreach_read(host)
227 + {
228 + if (rrdhost_flag_check(host, RRDHOST_FLAG_ARCHIVED))
229 + continue;
230 +
231 + netdata_mutex_lock(&host->receiver_lock);
232 + ((host->receiver || host == localhost) ? reachable++ : unreachable++);
233 + netdata_mutex_unlock(&host->receiver_lock);
234 +
235 + count++;
236 + }
237 + rrd_unlock();
238 +
239 + snprintfz(b, 10, "%d", count);
240 + analytics_set_data(&analytics_data.netdata_mirrored_host_count, b);
241 + snprintfz(b, 10, "%d", reachable);
242 + analytics_set_data(&analytics_data.netdata_mirrored_hosts_reachable, b);
243 + snprintfz(b, 10, "%d", unreachable);
244 + analytics_set_data(&analytics_data.netdata_mirrored_hosts_unreachable, b);
245 +}
246 +
247 +void analytics_exporters(void)
248 +{
249 + //when no exporters are available, an empty string will be sent
250 + //decide if something else is more suitable (but propably not null)
251 + BUFFER *bi = buffer_create(1000);
252 + analytics_exporting_connectors(bi);
253 + analytics_set_data_str(&analytics_data.netdata_exporting_connectors, (char *)buffer_tostring(bi));
254 + buffer_free(bi);
255 +}
256 +
257 +int collector_counter_callb(void *entry, void *data)
258 +{
259 + struct array_printer *ap = (struct array_printer *)data;
260 + struct collector *col = (struct collector *)entry;
261 +
262 + BUFFER *bt = ap->both;
263 +
264 + if (likely(ap->c)) {
265 + buffer_strcat(bt, ",");
266 + }
267 +
268 + buffer_strcat(bt, "{");
269 + buffer_strcat(bt, " \"plugin\": \"");
270 + buffer_strcat(bt, col->plugin);
271 + buffer_strcat(bt, "\", \"module\":\"");
272 + buffer_strcat(bt, col->module);
273 + buffer_strcat(bt, "\" }");
274 +
275 + (ap->c)++;
276 +
277 + return 0;
278 +}
279 +
280 +/*
281 + * Create a JSON array of available collectors, same as in api/v1/info
282 + */
283 +void analytics_collectors(void)
284 +{
285 + RRDSET *st;
286 + DICTIONARY *dict = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED);
287 + char name[500];
288 + BUFFER *bt = buffer_create(1000);
289 +
290 + rrdset_foreach_read(st, localhost)
291 + {
292 + if (rrdset_is_available_for_viewers(st)) {
293 + struct collector col = { .plugin = st->plugin_name ? st->plugin_name : "",
294 + .module = st->module_name ? st->module_name : "" };
295 + snprintfz(name, 499, "%s:%s", col.plugin, col.module);
296 + dictionary_set(dict, name, &col, sizeof(struct collector));
297 + }
298 + }
299 +
300 + struct array_printer ap;
301 + ap.c = 0;
302 + ap.both = bt;
303 +
304 + dictionary_get_all(dict, collector_counter_callb, &ap);
305 + dictionary_destroy(dict);
306 +
307 + analytics_set_data(&analytics_data.netdata_collectors, (char *)buffer_tostring(ap.both));
308 +
309 + {
310 + char b[7];
311 + snprintfz(b, 6, "%d", ap.c);
312 + analytics_set_data(&analytics_data.netdata_collectors_count, b);
313 + }
314 +
315 + buffer_free(bt);
316 +}
317 +
318 +/*
319 + * Run alarm-notify.sh script using the dump_methods parameter
320 + * SEND_CUSTOM is always available
321 + */
322 +void analytics_alarms_notifications(void)
323 +{
324 + char *script;
325 + script = mallocz(
326 + sizeof(char) * (strlen(netdata_configured_primary_plugins_dir) + strlen("alarm-notify.sh dump_methods") + 2));
327 + sprintf(script, "%s/%s", netdata_configured_primary_plugins_dir, "alarm-notify.sh");
328 + if (unlikely(access(script, R_OK) != 0)) {
329 + info("Alarm notify script %s not found.", script);
330 + freez(script);
331 + return;
332 + }
333 +
334 + strcat(script, " dump_methods");
335 +
336 + pid_t command_pid;
337 +
338 + info("Executing %s", script);
339 +
340 + BUFFER *b = buffer_create(1000);
341 + int cnt = 0;
342 + FILE *fp = mypopen(script, &command_pid);
343 + if (fp) {
344 + char line[200 + 1];
345 +
346 + while (fgets(line, 200, fp) != NULL) {
347 + char *end = line;
348 + while (*end && *end != '\n')
349 + end++;
350 + *end = '\0';
351 +
352 + if (likely(cnt))
353 + buffer_strcat(b, "|");
354 +
355 + buffer_strcat(b, line);
356 +
357 + cnt++;
358 + }
359 + mypclose(fp, command_pid);
360 + }
361 + freez(script);
362 +
363 + analytics_set_data_str(&analytics_data.netdata_notification_methods, (char *)buffer_tostring(b));
364 +
365 + buffer_free(b);
366 +}
367 +
368 +void analytics_charts(void)
369 +{
370 + RRDSET *st;
371 + int c = 0;
372 + rrdset_foreach_read(st, localhost)
373 + {
374 + if (rrdset_is_available_for_viewers(st)) {
375 + c++;
376 + }
377 + }
378 + {
379 + char b[7];
380 + snprintfz(b, 6, "%d", c);
381 + analytics_set_data(&analytics_data.netdata_charts_count, b);
382 + }
383 +}
384 +
385 +void analytics_metrics(void)
386 +{
387 + RRDSET *st;
388 + long int dimensions = 0;
389 + RRDDIM *rd;
390 + rrdset_foreach_read(st, localhost)
391 + {
392 + rrdset_rdlock(st);
393 + rrddim_foreach_read(rd, st)
394 + {
395 + if (rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN) || rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE))
396 + continue;
397 + dimensions++;
398 + }
399 + rrdset_unlock(st);
400 + }
401 + {
402 + char b[7];
403 + snprintfz(b, 6, "%ld", dimensions);
404 + analytics_set_data(&analytics_data.netdata_metrics_count, b);
405 + }
406 +}
407 +
408 +void analytics_alarms(void)
409 +{
410 + int alarm_warn = 0, alarm_crit = 0, alarm_normal = 0;
411 + char b[10];
412 + RRDCALC *rc;
413 + for (rc = localhost->alarms; rc; rc = rc->next) {
414 + if (unlikely(!rc->rrdset || !rc->rrdset->last_collected_time.tv_sec))
415 + continue;
416 +
417 + switch (rc->status) {
418 + case RRDCALC_STATUS_WARNING:
419 + alarm_warn++;
420 + break;
421 + case RRDCALC_STATUS_CRITICAL:
422 + alarm_crit++;
423 + break;
424 + default:
425 + alarm_normal++;
426 + }
427 + }
428 +
429 + snprintfz(b, 9, "%d", alarm_normal);
430 + analytics_set_data(&analytics_data.netdata_alarms_normal, b);
431 + snprintfz(b, 9, "%d", alarm_warn);
432 + analytics_set_data(&analytics_data.netdata_alarms_warning, b);
433 + snprintfz(b, 9, "%d", alarm_crit);
434 + analytics_set_data(&analytics_data.netdata_alarms_critical, b);
435 +}
436 +
437 +/*
438 + * Misc attributes to get (run from meta)
439 + */
440 +void analytics_misc(void)
441 +{
442 +#ifdef ENABLE_ACLK
443 + analytics_set_data(&analytics_data.netdata_host_cloud_available, "true");
444 +#ifdef ACLK_NG
445 + analytics_set_data_str(&analytics_data.netdata_host_aclk_implementation, "Next Generation");
446 +#else
447 + analytics_set_data_str(&analytics_data.netdata_host_aclk_implementation, "legacy");
448 +#endif
449 +#else
450 + analytics_set_data(&analytics_data.netdata_host_cloud_available, "false");
451 +#endif
452 +
453 +#ifdef ENABLE_ACLK
454 + if (aclk_connected)
455 + analytics_set_data(&analytics_data.netdata_host_aclk_available, "true");
456 + else
457 +#endif
458 + analytics_set_data(&analytics_data.netdata_host_aclk_available, "false");
459 +}
460 +
461 +/*
462 + * Get the meta data, called from the thread once after the original delay
463 + * These are values that won't change between agent restarts, and therefore
464 + * don't try to read them on each META event send
465 + */
466 +void analytics_gather_immutable_meta_data(void)
467 +{
468 + analytics_misc();
469 + analytics_exporters();
470 +
471 + analytics_setenv_data();
472 +}
473 +
474 +/*
475 + * Get the meta data, called from the thread on every heartbeat, and right before the EXIT event
476 + * These are values that can change between agent restarts, and therefore
477 + * try to read them on each META event send
478 + */
479 +void analytics_gather_mutable_meta_data(void)
480 +{
481 + rrdhost_rdlock(localhost);
482 +
483 + analytics_collectors();
484 + analytics_alarms();
485 + analytics_charts();
486 + analytics_metrics();
487 +
488 + rrdhost_unlock(localhost);
489 +
490 + analytics_mirrored_hosts();
491 + analytics_alarms_notifications();
492 +
493 + analytics_set_data(
494 + &analytics_data.netdata_config_is_parent, (localhost->next || configured_as_parent()) ? "true" : "false");
495 +
496 + if (is_agent_claimed())
497 + analytics_set_data(&analytics_data.netdata_host_agent_claimed, "true");
498 + else {
499 + analytics_set_data(&analytics_data.netdata_host_agent_claimed, "false");
500 + }
501 +
502 + {
503 + char b[7];
504 + snprintfz(b, 6, "%d", analytics_data.prometheus_hits);
505 + analytics_set_data(&analytics_data.netdata_allmetrics_prometheus_used, b);
506 +
507 + snprintfz(b, 6, "%d", analytics_data.shell_hits);
508 + analytics_set_data(&analytics_data.netdata_allmetrics_shell_used, b);
509 +
510 + snprintfz(b, 6, "%d", analytics_data.json_hits);
511 + analytics_set_data(&analytics_data.netdata_allmetrics_json_used, b);
512 +
513 + snprintfz(b, 6, "%d", analytics_data.dashboard_hits);
514 + analytics_set_data(&analytics_data.netdata_dashboard_used, b);
515 +
516 + snprintfz(b, 6, "%zu", rrd_hosts_available);
517 + analytics_set_data(&analytics_data.netdata_config_hosts_available, b);
518 + }
519 +
520 + analytics_setenv_data();
521 +}
522 +
523 +void analytics_main_cleanup(void *ptr)
524 +{
525 + struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
526 + static_thread->enabled = NETDATA_MAIN_THREAD_EXITING;
527 +
528 + debug(D_ANALYTICS, "Cleaning up...");
529 +
530 + static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
531 +}
532 +
533 +/*
534 + * The analytics thread. Sleep for ANALYTICS_INIT_SLEEP_SEC,
535 + * gather the data, and then go to a loop where every ANALYTICS_HEARTBEAT
536 + * it will send a new META event after gathering data that could be changed
537 + * while the agent is running
538 + */
539 +void *analytics_main(void *ptr)
540 +{
541 + netdata_thread_cleanup_push(analytics_main_cleanup, ptr);
542 + unsigned int sec = 0;
543 + heartbeat_t hb;
544 + heartbeat_init(&hb);
545 + usec_t step_ut = USEC_PER_SEC;
546 +
547 + debug(D_ANALYTICS, "Analytics thread starts");
548 +
549 + //first delay after agent start
550 + while (!netdata_exit && likely(sec <= ANALYTICS_INIT_SLEEP_SEC)) {
551 + heartbeat_next(&hb, step_ut);
552 + sec++;
553 + }
554 +
555 + if (unlikely(netdata_exit))
556 + goto cleanup;
557 +
558 + analytics_gather_immutable_meta_data();
559 + analytics_gather_mutable_meta_data();
560 + send_statistics("META", "-", "-");
561 + analytics_log_data();
562 +
563 + sec = 0;
564 + while (1) {
565 + heartbeat_next(&hb, step_ut * 2);
566 + sec += 2;
567 +
568 + if (unlikely(netdata_exit))
569 + break;
570 +
571 + if (likely(sec < ANALYTICS_HEARTBEAT))
572 + continue;
573 +
574 + analytics_gather_mutable_meta_data();
575 + send_statistics("META", "-", "-");
576 + analytics_log_data();
577 + sec = 0;
578 + }
579 +
580 +cleanup:
581 + netdata_thread_cleanup_pop(1);
582 + return NULL;
583 +}
584 +
585 +static const char *verify_required_directory(const char *dir)
586 +{
587 + if (chdir(dir) == -1)
588 + fatal("Cannot change directory to '%s'", dir);
589 +
590 + DIR *d = opendir(dir);
591 + if (!d)
592 + fatal("Cannot examine the contents of directory '%s'", dir);
593 + closedir(d);
594 +
595 + return dir;
596 +}
597 +
598 +/*
599 + * This is called after the rrdinit
600 + * These values will be sent on the START event
601 + */
602 +void set_late_global_environment()
603 +{
604 + analytics_set_data(&analytics_data.netdata_config_stream_enabled, default_rrdpush_enabled ? "true" : "false");
605 + analytics_set_data_str(&analytics_data.netdata_config_memory_mode, (char *)rrd_memory_mode_name(default_rrd_memory_mode));
606 + analytics_set_data(&analytics_data.netdata_config_exporting_enabled, appconfig_get_boolean(&exporting_config, CONFIG_SECTION_EXPORTING, "enabled", 1) ? "true" : "false");
607 +
608 +#ifdef ENABLE_DBENGINE
609 + {
610 + char b[16];
611 + snprintfz(b, 15, "%d", default_rrdeng_page_cache_mb);
612 + analytics_set_data(&analytics_data.netdata_config_page_cache_size, b);
613 +
614 + snprintfz(b, 15, "%d", default_multidb_disk_quota_mb);
615 + analytics_set_data(&analytics_data.netdata_config_multidb_disk_quota, b);
616 + }
617 +#endif
618 +
619 +#ifdef ENABLE_HTTPS
620 + analytics_set_data(&analytics_data.netdata_config_https_enabled, "true");
621 +#else
622 + analytics_set_data(&analytics_data.netdata_config_https_enabled, "false");
623 +#endif
624 +
625 + if (web_server_mode == WEB_SERVER_MODE_NONE)
626 + analytics_set_data(&analytics_data.netdata_config_web_enabled, "false");
627 + else
628 + analytics_set_data(&analytics_data.netdata_config_web_enabled, "true");
629 +
630 + analytics_set_data_str(&analytics_data.netdata_config_release_channel, (char *)get_release_channel());
631 +
632 + {
633 + BUFFER *bi = buffer_create(1000);
634 + analytics_build_info(bi);
635 + analytics_set_data_str(&analytics_data.netdata_buildinfo, (char *)buffer_tostring(bi));
636 + buffer_free(bi);
637 + }
638 +
639 + /* set what we have, to send the START event */
640 + analytics_setenv_data();
641 +}
642 +
643 +static void get_system_timezone(void)
644 +{
645 + // avoid flood calls to stat(/etc/localtime)
646 + // http://stackoverflow.com/questions/4554271/how-to-avoid-excessive-stat-etc-localtime-calls-in-strftime-on-linux
647 + const char *tz = getenv("TZ");
648 + if (!tz || !*tz)
649 + setenv("TZ", config_get(CONFIG_SECTION_GLOBAL, "TZ environment variable", ":/etc/localtime"), 0);
650 +
651 + char buffer[FILENAME_MAX + 1] = "";
652 + const char *timezone = NULL;
653 + ssize_t ret;
654 +
655 + // use the TZ variable
656 + if (tz && *tz && *tz != ':') {
657 + timezone = tz;
658 + info("TIMEZONE: using TZ variable '%s'", timezone);
659 + }
660 +
661 + // use the contents of /etc/timezone
662 + if (!timezone && !read_file("/etc/timezone", buffer, FILENAME_MAX)) {
663 + char *c = buffer;
664 + while (*c) {
665 + if (unlikely(*c == '\n'))
666 + *c = '\0';
667 + c++;
668 + }
669 + timezone = buffer;
670 + info("TIMEZONE: using the contents of /etc/timezone: '%s'", timezone);
671 + }
672 +
673 + // read the link /etc/localtime
674 + if (!timezone) {
675 + ret = readlink("/etc/localtime", buffer, FILENAME_MAX);
676 +
677 + if (ret > 0) {
678 + buffer[ret] = '\0';
679 +
680 + char *cmp = "/usr/share/zoneinfo/";
681 + size_t cmp_len = strlen(cmp);
682 +
683 + char *s = strstr(buffer, cmp);
684 + if (s && s[cmp_len]) {
685 + timezone = &s[cmp_len];
686 + info("TIMEZONE: using the link of /etc/localtime: '%s'", timezone);
687 + }
688 + } else
689 + buffer[0] = '\0';
690 + }
691 +
692 + // find the timezone from strftime()
693 + if (!timezone) {
694 + time_t t;
695 + struct tm *tmp, tmbuf;
696 +
697 + t = now_realtime_sec();
698 + tmp = localtime_r(&t, &tmbuf);
699 +
700 + if (tmp != NULL) {
701 + if (strftime(buffer, FILENAME_MAX, "%Z", tmp) == 0)
702 + buffer[0] = '\0';
703 + else {
704 + buffer[FILENAME_MAX] = '\0';
705 + timezone = buffer;
706 + info("TIMEZONE: using strftime(): '%s'", timezone);
707 + }
708 + }
709 + }
710 +
711 + if (timezone && *timezone) {
712 + // make sure it does not have illegal characters
713 + // info("TIMEZONE: fixing '%s'", timezone);
714 +
715 + size_t len = strlen(timezone);
716 + char tmp[len + 1];
717 + char *d = tmp;
718 + *d = '\0';
719 +
720 + while (*timezone) {
721 + if (isalnum(*timezone) || *timezone == '_' || *timezone == '/')
722 + *d++ = *timezone++;
723 + else
724 + timezone++;
725 + }
726 + *d = '\0';
727 + strncpyz(buffer, tmp, len);
728 + timezone = buffer;
729 + // info("TIMEZONE: fixed as '%s'", timezone);
730 + }
731 +
732 + if (!timezone || !*timezone)
733 + timezone = "unknown";
734 +
735 + netdata_configured_timezone = config_get(CONFIG_SECTION_GLOBAL, "timezone", timezone);
736 +}
737 +
738 +void set_global_environment()
739 +{
740 + {
741 + char b[16];
742 + snprintfz(b, 15, "%d", default_rrd_update_every);
743 + setenv("NETDATA_UPDATE_EVERY", b, 1);
744 + }
745 +
746 + setenv("NETDATA_VERSION", program_version, 1);
747 + setenv("NETDATA_HOSTNAME", netdata_configured_hostname, 1);
748 + setenv("NETDATA_CONFIG_DIR", verify_required_directory(netdata_configured_user_config_dir), 1);
749 + setenv("NETDATA_USER_CONFIG_DIR", verify_required_directory(netdata_configured_user_config_dir), 1);
750 + setenv("NETDATA_STOCK_CONFIG_DIR", verify_required_directory(netdata_configured_stock_config_dir), 1);
751 + setenv("NETDATA_PLUGINS_DIR", verify_required_directory(netdata_configured_primary_plugins_dir), 1);
752 + setenv("NETDATA_WEB_DIR", verify_required_directory(netdata_configured_web_dir), 1);
753 + setenv("NETDATA_CACHE_DIR", verify_required_directory(netdata_configured_cache_dir), 1);
754 + setenv("NETDATA_LIB_DIR", verify_required_directory(netdata_configured_varlib_dir), 1);
755 + setenv("NETDATA_LOCK_DIR", netdata_configured_lock_dir, 1);
756 + setenv("NETDATA_LOG_DIR", verify_required_directory(netdata_configured_log_dir), 1);
757 + setenv("HOME", verify_required_directory(netdata_configured_home_dir), 1);
758 + setenv("NETDATA_HOST_PREFIX", netdata_configured_host_prefix, 1);
759 +
760 + analytics_set_data(&analytics_data.netdata_config_stream_enabled, "null");
761 + analytics_set_data(&analytics_data.netdata_config_memory_mode, "null");
762 + analytics_set_data(&analytics_data.netdata_config_exporting_enabled, "null");
763 + analytics_set_data(&analytics_data.netdata_exporting_connectors, "null");
764 + analytics_set_data(&analytics_data.netdata_allmetrics_prometheus_used, "null");
765 + analytics_set_data(&analytics_data.netdata_allmetrics_shell_used, "null");
766 + analytics_set_data(&analytics_data.netdata_allmetrics_json_used, "null");
767 + analytics_set_data(&analytics_data.netdata_dashboard_used, "null");
768 + analytics_set_data(&analytics_data.netdata_collectors, "null");
769 + analytics_set_data(&analytics_data.netdata_collectors_count, "null");
770 + analytics_set_data(&analytics_data.netdata_buildinfo, "null");
771 + analytics_set_data(&analytics_data.netdata_config_page_cache_size, "null");
772 + analytics_set_data(&analytics_data.netdata_config_multidb_disk_quota, "null");
773 + analytics_set_data(&analytics_data.netdata_config_https_enabled, "null");
774 + analytics_set_data(&analytics_data.netdata_config_web_enabled, "null");
775 + analytics_set_data(&analytics_data.netdata_config_release_channel, "null");
776 + analytics_set_data(&analytics_data.netdata_mirrored_host_count, "null");
777 + analytics_set_data(&analytics_data.netdata_mirrored_hosts_reachable, "null");
778 + analytics_set_data(&analytics_data.netdata_mirrored_hosts_unreachable, "null");
779 + analytics_set_data(&analytics_data.netdata_notification_methods, "null");
780 + analytics_set_data(&analytics_data.netdata_alarms_normal, "null");
781 + analytics_set_data(&analytics_data.netdata_alarms_warning, "null");
782 + analytics_set_data(&analytics_data.netdata_alarms_critical, "null");
783 + analytics_set_data(&analytics_data.netdata_charts_count, "null");
784 + analytics_set_data(&analytics_data.netdata_metrics_count, "null");
785 + analytics_set_data(&analytics_data.netdata_config_is_parent, "null");
786 + analytics_set_data(&analytics_data.netdata_config_hosts_available, "null");
787 + analytics_set_data(&analytics_data.netdata_host_cloud_available, "null");
788 + analytics_set_data(&analytics_data.netdata_host_aclk_implementation, "null");
789 + analytics_set_data(&analytics_data.netdata_host_aclk_available, "null");
790 + analytics_set_data(&analytics_data.netdata_host_agent_claimed, "null");
791 +
792 + analytics_data.prometheus_hits = 0;
793 + analytics_data.shell_hits = 0;
794 + analytics_data.json_hits = 0;
795 + analytics_data.dashboard_hits = 0;
796 +
797 + char *default_port = appconfig_get(&netdata_config, CONFIG_SECTION_WEB, "default port", NULL);
798 + int clean = 0;
799 + if (!default_port) {
800 + default_port = strdupz("19999");
801 + clean = 1;
802 + }
803 +
804 + setenv("NETDATA_LISTEN_PORT", default_port, 1);
805 + if (clean)
806 + freez(default_port);
807 +
808 + get_system_timezone();
809 +
810 + // set the path we need
811 + char path[1024 + 1], *p = getenv("PATH");
812 + if (!p)
813 + p = "/bin:/usr/bin";
814 + snprintfz(path, 1024, "%s:%s", p, "/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin");
815 + setenv("PATH", config_get(CONFIG_SECTION_PLUGINS, "PATH environment variable", path), 1);
816 +
817 + // python options
818 + p = getenv("PYTHONPATH");
819 + if (!p)
820 + p = "";
821 + setenv("PYTHONPATH", config_get(CONFIG_SECTION_PLUGINS, "PYTHONPATH environment variable", p), 1);
822 +
823 + // disable buffering for python plugins
824 + setenv("PYTHONUNBUFFERED", "1", 1);
825 +
826 + // switch to standard locale for plugins
827 + setenv("LC_ALL", "C", 1);
828 +}
829 +
830 +void send_statistics(const char *action, const char *action_result, const char *action_data)
831 +{
832 + static char *as_script;
833 +
834 + if (netdata_anonymous_statistics_enabled == -1) {
835 + char *optout_file = mallocz(
836 + sizeof(char) *
837 + (strlen(netdata_configured_user_config_dir) + strlen(".opt-out-from-anonymous-statistics") + 2));
838 + sprintf(optout_file, "%s/%s", netdata_configured_user_config_dir, ".opt-out-from-anonymous-statistics");
839 + if (likely(access(optout_file, R_OK) != 0)) {
840 + as_script = mallocz(
841 + sizeof(char) *
842 + (strlen(netdata_configured_primary_plugins_dir) + strlen("anonymous-statistics.sh") + 2));
843 + sprintf(as_script, "%s/%s", netdata_configured_primary_plugins_dir, "anonymous-statistics.sh");
844 + if (unlikely(access(as_script, R_OK) != 0)) {
845 + netdata_anonymous_statistics_enabled = 0;
846 + info("Anonymous statistics script %s not found.", as_script);
847 + freez(as_script);
848 + } else {
849 + netdata_anonymous_statistics_enabled = 1;
850 + }
851 + } else {
852 + netdata_anonymous_statistics_enabled = 0;
853 + as_script = NULL;
854 + }
855 + freez(optout_file);
856 + }
857 + if (!netdata_anonymous_statistics_enabled)
858 + return;
859 + if (!action)
860 + return;
861 + if (!action_result)
862 + action_result = "";
863 + if (!action_data)
864 + action_data = "";
865 + char *command_to_run =
866 + mallocz(sizeof(char) * (strlen(action) + strlen(action_result) + strlen(action_data) + strlen(as_script) + 10));
867 + pid_t command_pid;
868 +
869 + sprintf(command_to_run, "%s '%s' '%s' '%s'", as_script, action, action_result, action_data);
870 + info("%s", command_to_run);
871 +
872 + FILE *fp = mypopen(command_to_run, &command_pid);
873 + if (fp) {
874 + char buffer[100 + 1];
875 + while (fgets(buffer, 100, fp) != NULL)
876 + ;
877 + mypclose(fp, command_pid);
878 + }
879 + freez(command_to_run);
880 +}
daemon/analytics.h new
+84
@@ -0,0 +1,84 @@
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 +struct analytics_data {
32 + char *netdata_config_stream_enabled;
33 + char *netdata_config_memory_mode;
34 + char *netdata_exporting_connectors;
35 + char *netdata_config_exporting_enabled;
36 + char *netdata_allmetrics_prometheus_used;
37 + char *netdata_allmetrics_shell_used;
38 + char *netdata_allmetrics_json_used;
39 + char *netdata_dashboard_used;
40 + char *netdata_collectors;
41 + char *netdata_collectors_count;
42 + char *netdata_buildinfo;
43 + char *netdata_config_page_cache_size;
44 + char *netdata_config_multidb_disk_quota;
45 + char *netdata_config_https_enabled;
46 + char *netdata_config_web_enabled;
47 + char *netdata_config_release_channel;
48 + char *netdata_mirrored_host_count;
49 + char *netdata_mirrored_hosts_reachable;
50 + char *netdata_mirrored_hosts_unreachable;
51 + char *netdata_notification_methods;
52 + char *netdata_alarms_normal;
53 + char *netdata_alarms_warning;
54 + char *netdata_alarms_critical;
55 + char *netdata_charts_count;
56 + char *netdata_metrics_count;
57 + char *netdata_config_is_parent;
58 + char *netdata_config_hosts_available;
59 + char *netdata_host_cloud_available;
60 + char *netdata_host_aclk_available;
61 + char *netdata_host_aclk_implementation;
62 + char *netdata_host_agent_claimed;
63 +
64 + uint8_t prometheus_hits;
65 + uint8_t shell_hits;
66 + uint8_t json_hits;
67 + uint8_t dashboard_hits;
68 +};
69 +
70 +extern void *analytics_main(void *ptr);
71 +extern void analytics_get_data(char *name, BUFFER *wb);
72 +extern void set_late_global_environment(void);
73 +extern void analytics_free_data(void);
74 +extern void set_global_environment(void);
75 +extern void send_statistics(const char *action, const char *action_result, const char *action_data);
76 +extern void analytics_log_shell(void);
77 +extern void analytics_log_json(void);
78 +extern void analytics_log_prometheus(void);
79 +extern void analytics_log_dashboard(void);
80 +extern void analytics_gather_mutable_meta_data(void);
81 +
82 +extern struct analytics_data analytics_data;
83 +
84 +#endif //NETDATA_ANALYTICS_H
daemon/anonymous-statistics.sh.in
+34 -1
@@ -44,6 +44,8 @@ REQ_BODY="$(cat << EOF
44 "action_data": "${ACTION_DATA}",
45 "netdata_machine_guid": "${NETDATA_REGISTRY_UNIQUE_ID}",
46 "netdata_version": "${NETDATA_VERSION}",
47 + "netdata_buildinfo": ${NETDATA_BUILDINFO},
48 + "netdata_release_channel": ${NETDATA_CONFIG_RELEASE_CHANNEL},
49 "host_os_name": "${NETDATA_HOST_OS_NAME}",
50 "host_os_id": "${NETDATA_HOST_OS_ID}",
51 "host_os_id_like": "${NETDATA_HOST_OS_ID_LIKE}",
@@ -72,7 +74,38 @@ REQ_BODY="$(cat << EOF
74 "system_disk_detection": "${NETDATA_SYSTEM_DISK_DETECTION}",
75 "system_ram_detection": "${NETDATA_SYSTEM_RAM_DETECTION}",
76 "system_total_disk_size": "${NETDATA_SYSTEM_TOTAL_DISK_SIZE}",
75 - "system_total_ram": "${NETDATA_SYSTEM_TOTAL_RAM}"
77 + "system_total_ram": "${NETDATA_SYSTEM_TOTAL_RAM}",
78 + "config_stream_enabled": ${NETDATA_CONFIG_STREAM_ENABLED},
79 + "config_memory_mode": ${NETDATA_CONFIG_MEMORY_MODE},
80 + "config_page_cache_size": ${NETDATA_CONFIG_PAGE_CACHE_SIZE},
81 + "config_multidb_disk_quota": ${NETDATA_CONFIG_MULTIDB_DISK_QUOTA},
82 + "config_https_enabled": ${NETDATA_CONFIG_HTTPS_ENABLED},
83 + "config_web_enabled": ${NETDATA_CONFIG_WEB_ENABLED},
84 + "config_exporting_enabled": ${NETDATA_CONFIG_EXPORTING_ENABLED},
85 + "config_is_parent": ${NETDATA_CONFIG_IS_PARENT},
86 + "config_hosts_available": ${NETDATA_CONFIG_HOSTS_AVAILABLE},
87 + "alarms_normal": ${NETDATA_ALARMS_NORMAL},
88 + "alarms_warning": ${NETDATA_ALARMS_WARNING},
89 + "alarms_critical": ${NETDATA_ALARMS_CRITICAL},
90 + "host_charts_count": ${NETDATA_CHARTS_COUNT},
91 + "host_metrics_count": ${NETDATA_METRICS_COUNT},
92 + "host_collectors":[
93 + ${NETDATA_COLLECTORS}
94 + ],
95 + "host_collectors_count": ${NETDATA_COLLECTORS_COUNT},
96 + "host_notification_methods": ${NETDATA_NOTIFICATION_METHODS},
97 + "host_allmetrics_prometheus_used": ${NETDATA_ALLMETRICS_PROMETHEUS_USED},
98 + "host_allmetrics_shell_used": ${NETDATA_ALLMETRICS_SHELL_USED},
99 + "host_allmetrics_json_used": ${NETDATA_ALLMETRICS_JSON_USED},
100 + "host_dashboard_used": ${NETDATA_DASHBOARD_USED},
101 + "host_cloud_available": ${NETDATA_HOST_CLOUD_AVAILABLE},
102 + "host_agent_claimed": ${NETDATA_HOST_AGENT_CLAIMED},
103 + "host_aclk_available": ${NETDATA_HOST_ACLK_AVAILABLE},
104 + "host_aclk_implementation": ${NETDATA_HOST_ACLK_IMPLEMENTATION},
105 + "mirrored_host_count": ${NETDATA_MIRRORED_HOST_COUNT},
106 + "mirrored_hosts_reachable": ${NETDATA_MIRRORED_HOSTS_REACHABLE},
107 + "mirrored_hosts_unreachable": ${NETDATA_MIRRORED_HOSTS_UNREACHABLE},
108 + "exporting_connectors": ${NETDATA_EXPORTING_CONNECTORS}
109 }
110 }
111 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;