@cryptotaxi247 / netdata-1 / commits / 8c2443695

daemon status 22 (#19953)

* annotate crash and healthy loops * log successful and failed posts to agent-events * move the already running fatal() outside of netdata_main() * count the number of posts made to the backend * added more info to crash reports init step, to find where the killed hard is happening * add aclk status to daemon status file * also check for localhost * remove the cloud "connecting" state * avoid duplicates

Costa Tsaousis committed Mar 24, 2025 at 19:06 UTC 8c244369582fcd4b78dd9081d14fb33a653a32c5
12 files changed +110 -64
src/aclk/aclk.c
+4
@@ -30,12 +30,16 @@ mqtt_wss_client mqttwss_client;
30 static bool aclk_connected = false;
31 static inline void aclk_set_connected(void) {
32 __atomic_store_n(&aclk_connected, true, __ATOMIC_RELAXED);
33 +
34 + daemon_status_file_update_status(DAEMON_STATUS_NONE);
35 }
36 static inline void aclk_set_disconnected(void) {
37 __atomic_store_n(&aclk_connected, false, __ATOMIC_RELAXED);
38
39 if(mqttwss_client)
40 mqtt_wss_reset_stats(mqttwss_client);
41 +
42 + daemon_status_file_update_status(DAEMON_STATUS_NONE);
43 }
44
45 inline bool aclk_online(void) {
src/claim/cloud-status.c
+16 -30
@@ -2,41 +2,27 @@
2
3 #include "claim.h"
4
5 -const char *cloud_status_to_string(CLOUD_STATUS status) {
6 - switch(status) {
7 - default:
8 - case CLOUD_STATUS_AVAILABLE:
9 - return "available";
10 -
11 - case CLOUD_STATUS_BANNED:
12 - return "banned";
13 -
14 - case CLOUD_STATUS_OFFLINE:
15 - return "offline";
16 -
17 - case CLOUD_STATUS_ONLINE:
18 - return "online";
19 -
20 - case CLOUD_STATUS_CONNECTING:
21 - return "connecting";
22 -
23 - case CLOUD_STATUS_INDIRECT:
24 - return "indirect";
25 - }
26 -}
5 +ENUM_STR_MAP_DEFINE(CLOUD_STATUS) = {
6 + { CLOUD_STATUS_ONLINE, "online"},
7 + { CLOUD_STATUS_INDIRECT, "indirect"},
8 + { CLOUD_STATUS_AVAILABLE, "available"},
9 + { CLOUD_STATUS_BANNED, "banned"},
10 + { CLOUD_STATUS_OFFLINE, "offline"},
11 +
12 + // terminator
13 + { 0, NULL },
14 +};
15 +ENUM_STR_DEFINE_FUNCTIONS(CLOUD_STATUS, CLOUD_STATUS_AVAILABLE, "available");
16
17 CLOUD_STATUS cloud_status(void) {
18 if(unlikely(aclk_disable_runtime))
19 return CLOUD_STATUS_BANNED;
20
32 - if(likely(aclk_online())) {
33 - if (rrdhost_flag_check(localhost, RRDHOST_FLAG_ACLK_STREAM_CONTEXTS))
34 - return CLOUD_STATUS_ONLINE;
35 - else
36 - return CLOUD_STATUS_CONNECTING;
37 - }
21 + if(likely(aclk_online()))
22 + return CLOUD_STATUS_ONLINE;
23
39 - if(localhost->sender &&
24 + if(localhost &&
25 + localhost->sender &&
26 rrdhost_flag_check(localhost, RRDHOST_FLAG_STREAM_SENDER_READY_4_METRICS) &&
27 stream_sender_has_capabilities(localhost, STREAM_CAP_NODE_ID) &&
28 !UUIDiszero(localhost->node_id) &&
@@ -83,7 +69,7 @@ CLOUD_STATUS buffer_json_cloud_status(BUFFER *wb, time_t now_s) {
69 time_t last_change = cloud_last_change();
70 time_t next_connect = cloud_next_connection_attempt();
71 buffer_json_member_add_uint64(wb, "id", id);
86 - buffer_json_member_add_string(wb, "status", cloud_status_to_string(status));
72 + buffer_json_member_add_string(wb, "status", CLOUD_STATUS_2str(status));
73 buffer_json_member_add_time_t(wb, "since", last_change);
74 buffer_json_member_add_time_t(wb, "age", now_s - last_change);
75
src/claim/cloud-status.h
+4 -3
@@ -3,18 +3,19 @@
3 #ifndef NETDATA_CLOUD_STATUS_H
4 #define NETDATA_CLOUD_STATUS_H
5
6 -#include "daemon/common.h"
6 +#include "libnetdata/libnetdata.h"
7 +#include "claim/cloud-status.h"
8
9 typedef enum __attribute__((packed)) {
10 CLOUD_STATUS_AVAILABLE = 1, // cloud and aclk functionality is available, but the agent is not claimed
11 CLOUD_STATUS_BANNED, // the agent has been banned from cloud
12 CLOUD_STATUS_OFFLINE, // the agent tries to connect to cloud, but cannot do it
13 CLOUD_STATUS_INDIRECT, // the agent is connected to cloud via a parent
13 - CLOUD_STATUS_CONNECTING, // the agent is connecting
14 CLOUD_STATUS_ONLINE, // the agent is connected to cloud
15 } CLOUD_STATUS;
16
17 -const char *cloud_status_to_string(CLOUD_STATUS status);
17 +ENUM_STR_DEFINE_FUNCTIONS_EXTERN(CLOUD_STATUS);
18 +
19 CLOUD_STATUS cloud_status(void);
20
21 time_t cloud_last_change(void);
src/daemon/daemon-status-file.c
+66 -8
@@ -174,6 +174,11 @@ static void daemon_status_file_to_json(BUFFER *wb, DAEMON_STATUS_FILE *ds) {
174 buffer_json_member_add_uuid(wb, "claim_id", ds->claim_id.uuid);
175 buffer_json_member_add_uint64(wb, "restarts", ds->restarts);
176
177 + if(ds->v >= 22) {
178 + buffer_json_member_add_uint64(wb, "posts", ds->posts);
179 + buffer_json_member_add_string(wb, "aclk", CLOUD_STATUS_2str(ds->cloud_status));
180 + }
181 +
182 ND_PROFILE_2json(wb, "profile", ds->profile);
183 buffer_json_member_add_string(wb, "status", DAEMON_STATUS_2str(ds->status));
184 EXIT_REASON_2json(wb, "exit_reason", ds->exit_reason);
@@ -337,6 +342,7 @@ static bool daemon_status_file_from_json(json_object *jobj, void *data, BUFFER *
342 bool required_v18 = version >= 18 ? strict : false;
343 bool required_v20 = version >= 20 ? strict : false;
344 bool required_v21 = version >= 21 ? strict : false;
345 + bool required_v22 = version >= 22 ? strict : false;
346
347 // Parse timestamp
348 JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "@timestamp", datetime, error, required_v1);
@@ -378,6 +384,11 @@ static bool daemon_status_file_from_json(json_object *jobj, void *data, BUFFER *
384 if(version >= 4)
385 JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, restarts_key, ds->restarts, error, required_v4);
386
387 + if(version >= 22) {
388 + JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "posts", ds->posts, error, required_v22);
389 + JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, "aclk", CLOUD_STATUS_2id, ds->cloud_status, error, required_v22);
390 + }
391 +
392 if(version >= 14) {
393 JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, db_mode_key, rrd_memory_mode_id, ds->db_mode, error, required_v14);
394 JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, db_tiers_key, ds->db_tiers, error, required_v14);
@@ -569,10 +580,11 @@ static void daemon_status_file_migrate_once(void) {
580 strncpyz(session_status.cloud_instance_type, last_session_status.cloud_instance_type, sizeof(session_status.cloud_instance_type) - 1);
581 strncpyz(session_status.cloud_instance_region, last_session_status.cloud_instance_region, sizeof(session_status.cloud_instance_region) - 1);
582
583 + session_status.posts = last_session_status.posts;
584 session_status.restarts = last_session_status.restarts + 1;
585 session_status.reliability = last_session_status.reliability;
586
575 - if(daemon_status_file_has_last_crashed()) {
587 + if(daemon_status_file_has_last_crashed(&last_session_status)) {
588 if(session_status.reliability > 0) session_status.reliability = 0;
589 session_status.reliability--;
590 }
@@ -627,6 +639,7 @@ static void daemon_status_file_refresh(DAEMON_STATUS status) {
639 session_status.invocation = nd_log_get_invocation_id();
640 session_status.db_mode = default_rrd_memory_mode;
641 session_status.db_tiers = nd_profile.storage_tiers;
642 + session_status.cloud_status = cloud_status();
643
644 session_status.oom_protection = dbengine_out_of_memory_protection;
645 session_status.netdata_max_rss = process_max_rss();
@@ -990,7 +1003,34 @@ struct post_status_file_thread_data {
1003 DAEMON_STATUS_FILE *status;
1004 };
1005
993 -void post_status_file(struct post_status_file_thread_data *d) {
1006 +static const char *agent_health(DAEMON_STATUS_FILE *ds) {
1007 + if(daemon_status_file_has_last_crashed(ds)) {
1008 + // it crashed
1009 +
1010 + if(ds->restarts == 1)
1011 + return "crash-first";
1012 + else if(ds->reliability <= -2)
1013 + return "crash-loop";
1014 + else if(ds->reliability < 0)
1015 + return "crash-repeated";
1016 + else
1017 + return "crash-entered";
1018 + }
1019 +
1020 + // it didn't crash
1021 + if(ds->restarts == 1)
1022 + return "healthy-first";
1023 + else if(ds->reliability >= 2)
1024 + return "healthy-loop";
1025 + else if(ds->reliability > 0)
1026 + return "healthy-repeated";
1027 + else
1028 + return "healthy-recovered";
1029 +}
1030 +
1031 +static void post_status_file(struct post_status_file_thread_data *d) {
1032 + daemon_status_file_startup_step("startup(crash reports json)");
1033 +
1034 CLEAN_BUFFER *wb = buffer_create(0, NULL);
1035 buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_MINIFY);
1036 buffer_json_member_add_string(wb, "exit_cause", d->cause);
@@ -1001,6 +1041,7 @@ void post_status_file(struct post_status_file_thread_data *d) {
1041 buffer_json_member_add_boolean(wb, "host_memory_critical",
1042 OS_SYSTEM_MEMORY_OK(d->status->memory) && d->status->memory.ram_available_bytes <= d->status->oom_protection);
1043 buffer_json_member_add_uint64(wb, "host_memory_free_percent", (uint64_t)round(os_system_memory_available_percent(d->status->memory)));
1044 + buffer_json_member_add_string(wb, "agent_health", agent_health(d->status));
1045 daemon_status_file_to_json(wb, d->status);
1046 buffer_json_finalize(wb);
1047
@@ -1010,6 +1051,8 @@ void post_status_file(struct post_status_file_thread_data *d) {
1051 if(!curl)
1052 return;
1053
1054 + daemon_status_file_startup_step("startup(crash reports curl)");
1055 +
1056 curl_easy_setopt(curl, CURLOPT_URL, "https://agent-events.netdata.cloud/agent-events");
1057 curl_easy_setopt(curl, CURLOPT_POST, 1L);
1058 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_data);
@@ -1020,10 +1063,17 @@ void post_status_file(struct post_status_file_thread_data *d) {
1063
1064 CURLcode rc = curl_easy_perform(curl);
1065 if(rc == CURLE_OK) {
1066 + daemon_status_file_startup_step("startup(crash reports dedup)");
1067 + nd_log(NDLS_DAEMON, NDLP_INFO, "Posted last status to agent-events successfully.");
1068 uint64_t hash = daemon_status_file_hash(d->status, d->msg, d->cause);
1069 dedup_keep_hash(&session_status, hash, false);
1070 + session_status.posts++;
1071 daemon_status_file_save(wb, &session_status, true);
1072 }
1073 + else
1074 + nd_log(NDLS_DAEMON, NDLP_INFO, "Failed to post last status to agent-events.");
1075 +
1076 + daemon_status_file_startup_step("startup(crash reports cleanup)");
1077
1078 curl_easy_cleanup(curl);
1079 curl_slist_free_all(headers);
@@ -1311,18 +1361,24 @@ void daemon_status_file_check_crash(void) {
1361 "Last exit status: %s (%s):\n\n%s",
1362 NETDATA_VERSION, msg, cause, buffer_tostring(wb));
1363
1364 + daemon_status_file_startup_step("startup(crash reports check)");
1365 +
1366 enum crash_report_t r = check_crash_reports_config();
1367 if( // must be first for netdata.conf option to be used
1368 (r == DSF_REPORT_ALL || (this_is_a_crash && r == DSF_REPORT_CRASHES)) &&
1369
1318 - // we have a previous status, or
1319 - // (we managed to save the current one, and (we have more than 2 restarts, or this is not a CI run))
1320 - (!no_previous_status || (daemon_status_file_saved && (last_session_status.restarts > 2 || !is_ci()))) &&
1370 + // we have a previous status, or we managed to save the current one
1371 + (!no_previous_status || daemon_status_file_saved) &&
1372 +
1373 + // we have more than 2 restarts, or this is not a CI run
1374 + (last_session_status.restarts > 2 || !is_ci()) &&
1375
1376 // we have not reported this
1377 !dedup_already_posted(&session_status, daemon_status_file_hash(&last_session_status, msg, cause), false)
1378
1379 ) {
1380 + daemon_status_file_startup_step("startup(crash reports prep)");
1381 +
1382 netdata_conf_ssl();
1383
1384 if(no_previous_status) {
@@ -1545,9 +1601,11 @@ void daemon_status_file_shutdown_step(const char *step) {
1601
1602 // --------------------------------------------------------------------------------------------------------------------
1603
1548 -bool daemon_status_file_has_last_crashed(void) {
1549 - return (last_session_status.status != DAEMON_STATUS_NONE && last_session_status.status != DAEMON_STATUS_EXITED) ||
1550 - !is_exit_reason_normal(last_session_status.exit_reason);
1604 +bool daemon_status_file_has_last_crashed(DAEMON_STATUS_FILE *ds) {
1605 + if(!ds) ds = &last_session_status;
1606 +
1607 + return (ds->status != DAEMON_STATUS_NONE && ds->status != DAEMON_STATUS_EXITED) ||
1608 + !is_exit_reason_normal(ds->exit_reason);
1609 }
1610
1611 bool daemon_status_file_was_incomplete_shutdown(void) {
src/daemon/daemon-status-file.h
+5 -2
@@ -6,8 +6,9 @@
6 #include "libnetdata/libnetdata.h"
7 #include "daemon/config/netdata-conf-profile.h"
8 #include "database/rrd-database-mode.h"
9 +#include "claim/cloud-status.h"
10
10 -#define STATUS_FILE_VERSION 21
11 +#define STATUS_FILE_VERSION 22
12
13 typedef enum {
14 DAEMON_STATUS_NONE,
@@ -37,6 +38,7 @@ typedef struct daemon_status_file {
38 ND_PROFILE profile; // the profile of the agent
39 DAEMON_OS_TYPE os_type;
40 RRD_DB_MODE db_mode;
41 + CLOUD_STATUS cloud_status;
42 uint8_t db_tiers;
43 bool kubernetes;
44 bool sentry_available; // true when sentry support is compiled in
@@ -45,6 +47,7 @@ typedef struct daemon_status_file {
47 time_t uptime; // netdata uptime
48 usec_t timestamp_ut; // the timestamp of the status file
49 size_t restarts; // the number of times this agent has restarted (ever)
50 + size_t posts; // the number of posts to the backend
51 ssize_t reliability; // consecutive restarts: > 0 reliable, < 0 crashing
52
53 ND_UUID boot_id; // the boot id of the system
@@ -116,7 +119,7 @@ bool daemon_status_file_deadly_signal_received(EXIT_REASON reason, SIGNAL_CODE c
119 // check for a crash
120 void daemon_status_file_check_crash(void);
121
119 -bool daemon_status_file_has_last_crashed(void);
122 +bool daemon_status_file_has_last_crashed(DAEMON_STATUS_FILE *ds);
123 bool daemon_status_file_was_incomplete_shutdown(void);
124
125 void daemon_status_file_startup_step(const char *step);
src/daemon/main.c
+3 -10
@@ -922,15 +922,8 @@ int netdata_main(int argc, char **argv) {
922 web_server_threading_selection();
923
924 delta_startup_time("web server sockets");
925 - if(web_server_mode != WEB_SERVER_MODE_NONE) {
926 - errno_clear();
927 - if (!api_listen_sockets_setup()) {
928 - exit_initiated_add(EXIT_REASON_ALREADY_RUNNING);
929 - daemon_status_file_update_status(DAEMON_STATUS_NONE);
930 - fatal("Cannot setup listen port(s). Is Netdata already running?");
931 - exit(1);
932 - }
933 - }
925 + if(web_server_mode != WEB_SERVER_MODE_NONE)
926 + web_server_listen_sockets_setup();
927
928 // ----------------------------------------------------------------------------------------------------------------
929 delta_startup_time("sqlite");
@@ -1105,7 +1098,7 @@ int netdata_main(int argc, char **argv) {
1098
1099 analytics_statistic_t start_statistic = {"START", "-", "-"};
1100 analytics_statistic_send(&start_statistic);
1108 - if (daemon_status_file_has_last_crashed()) {
1101 + if (daemon_status_file_has_last_crashed(NULL)) {
1102 analytics_statistic_t crash_statistic = {"CRASH", "-", "-"};
1103 analytics_statistic_send(&crash_statistic);
1104 }
src/daemon/sentry-native/sentry-native.c
+1 -1
@@ -46,7 +46,7 @@ static void nd_sentry_set_tag_uint64(const char *key, uint64_t value) {
46 nd_sentry_set_tag(key, buf);
47 }
48
49 -static void nd_sentry_set_tag_uuid(const char *key, const ND_UUID uuid) {
49 +static inline void nd_sentry_set_tag_uuid(const char *key, const ND_UUID uuid) {
50 if(UUIDiszero(uuid))
51 return;
52
src/libnetdata/log/nd_log-stacktrace.c
+1 -1
@@ -97,7 +97,7 @@ static void bt_error_handler(void *data, const char *msg, int errnum) {
97 }
98
99 // Full callback for libbacktrace
100 -static int bt_full_handler(void *data, uintptr_t pc,
100 +static int bt_full_handler(void *data, uintptr_t pc __maybe_unused,
101 const char *filename, int lineno,
102 const char *function) {
103 backtrace_data_t *bt_data = (backtrace_data_t *)data;
src/registry/registry.c
+1 -1
@@ -183,7 +183,7 @@ int registry_request_hello_json(RRDHOST *host, struct web_client *w, bool do_not
183 buffer_json_object_close(w->response.data);
184
185 CLOUD_STATUS status = cloud_status();
186 - buffer_json_member_add_string(w->response.data, "cloud_status", cloud_status_to_string(status));
186 + buffer_json_member_add_string(w->response.data, "cloud_status", CLOUD_STATUS_2str(status));
187 buffer_json_member_add_string(w->response.data, "cloud_base_url", registry.cloud_base_url);
188
189 buffer_json_member_add_string(w->response.data, "registry", registry.registry_to_announce);
src/web/api/v2/api_v2_claim.c
-1
@@ -90,7 +90,6 @@ static bool agent_can_be_claimed(void) {
90
91 case CLOUD_STATUS_BANNED:
92 case CLOUD_STATUS_ONLINE:
93 - case CLOUD_STATUS_CONNECTING:
93 return false;
94 }
95
src/web/server/web_server.c
+8 -6
@@ -57,16 +57,18 @@ void debug_sockets() {
57 buffer_free(wb);
58 }
59
60 -bool api_listen_sockets_setup(void) {
61 - int socks = listen_sockets_setup(&api_sockets);
60 +void web_server_listen_sockets_setup(void) {
61 + errno_clear();
62
63 - if(!socks)
64 - return false;
63 + int socks = listen_sockets_setup(&api_sockets);
64 + if(!socks) {
65 + exit_initiated_add(EXIT_REASON_ALREADY_RUNNING);
66 + daemon_status_file_update_status(DAEMON_STATUS_NONE);
67 + fatal("Cannot setup listen port(s). Is Netdata already running?");
68 + }
69
70 if(unlikely(debug_flags & D_WEB_CLIENT))
71 debug_sockets();
68 -
69 - return true;
72 }
73
74
src/web/server/web_server.h
+1 -1
@@ -38,7 +38,7 @@ extern WEB_SERVER_MODE web_server_mode;
38 WEB_SERVER_MODE web_server_mode_id(const char *mode);
39 const char *web_server_mode_name(WEB_SERVER_MODE id);
40
41 -bool api_listen_sockets_setup(void);
41 +void web_server_listen_sockets_setup(void);
42
43 #define DEFAULT_TIMEOUT_TO_RECEIVE_FIRST_WEB_REQUEST 60
44 #define DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS 60