Avoid post initialization errors repeateadly (#19709)
do not post initialization errors repeateadly; detect almost full disks; increase status file version
Costa Tsaousis committed
Feb 25, 2025 at 21:03 UTC
84a207f559e1656ccda87941acbd87a416c77fd5
5 files changed
+77
-25
src/daemon/daemon-shutdown.c
+2
-2
@@ -173,7 +173,7 @@ void netdata_cleanup_and_exit(EXIT_REASON reason, const char *action, const char
173
exit(ret);
174
}
175
run = true;
176
- daemon_status_file_save(DAEMON_STATUS_EXITING);
176
+ daemon_status_file_update_status(DAEMON_STATUS_EXITING);
177
178
nd_log_limits_unlimited();
179
netdata_log_exit_reason();
@@ -322,7 +322,7 @@ void netdata_cleanup_and_exit(EXIT_REASON reason, const char *action, const char
322
watcher_thread_stop();
323
curl_global_cleanup();
324
325
- daemon_status_file_save(DAEMON_STATUS_EXITED);
325
+ daemon_status_file_update_status(DAEMON_STATUS_EXITED);
326
327
#ifdef OS_WINDOWS
328
return;
src/daemon/daemon-status-file.c
+65
-19
@@ -37,13 +37,14 @@ ENUM_STR_DEFINE_FUNCTIONS(DAEMON_OS_TYPE, DAEMON_OS_TYPE_UNKNOWN, "unknown");
37
38
static DAEMON_STATUS_FILE last_session_status = { 0 };
39
static DAEMON_STATUS_FILE session_status = { 0 };
40
+static SPINLOCK dsf_spinlock = SPINLOCK_INITIALIZER;
41
42
// --------------------------------------------------------------------------------------------------------------------
43
// json generation
44
45
static void daemon_status_file_to_json(BUFFER *wb, DAEMON_STATUS_FILE *ds) {
46
buffer_json_member_add_datetime_rfc3339(wb, "@timestamp", ds->timestamp_ut, true); // ECS
46
- buffer_json_member_add_uint64(wb, "version", 1); // custom
47
+ buffer_json_member_add_uint64(wb, "version", 2); // custom
48
49
buffer_json_member_add_object(wb, "agent"); // ECS
50
{
@@ -116,7 +117,7 @@ static void daemon_status_file_to_json(BUFFER *wb, DAEMON_STATUS_FILE *ds) {
117
}
118
buffer_json_object_close(wb);
119
119
- buffer_json_member_add_object(wb, "fatal");
120
+ buffer_json_member_add_object(wb, "fatal"); // custom
121
{
122
buffer_json_member_add_uint64(wb, "line", ds->fatal.line);
123
buffer_json_member_add_string_or_empty(wb, "filename", ds->fatal.filename);
@@ -125,6 +126,14 @@ static void daemon_status_file_to_json(BUFFER *wb, DAEMON_STATUS_FILE *ds) {
126
buffer_json_member_add_string_or_empty(wb, "stack_trace", ds->fatal.stack_trace);
127
}
128
buffer_json_object_close(wb);
129
+
130
+ buffer_json_member_add_object(wb, "dedup"); // custom
131
+ {
132
+ buffer_json_member_add_time_t(wb, "timestamp", ds->dedup.timestamp); // custom
133
+ buffer_json_member_add_string(wb, "status", DAEMON_STATUS_2str(ds->dedup.status)); // custom
134
+ EXIT_REASON_2json(wb, "exit_reason", ds->dedup.exit_reason); // custom
135
+ }
136
+ buffer_json_object_close(wb);
137
}
138
139
// --------------------------------------------------------------------------------------------------------------------
@@ -141,6 +150,7 @@ static bool daemon_status_file_from_json(json_object *jobj, void *data, BUFFER *
150
JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "version", version, error, true);
151
152
bool required = false; // allow missing fields and values
153
+ bool required_v2 = false; // allow missing fields and values for version 2
154
155
// Parse timestamp
156
JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "@timestamp", datetime, error, required);
@@ -215,13 +225,22 @@ static bool daemon_status_file_from_json(json_object *jobj, void *data, BUFFER *
225
JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "line", ds->fatal.line, error, required);
226
});
227
228
+ // Parse the last posted object
229
+ JSONC_PARSE_SUBOBJECT(jobj, path, "dedup", error, required_v2, {
230
+ JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "timestamp", ds->dedup.timestamp, error, required_v2);
231
+ JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, "status", DAEMON_STATUS_2id, ds->dedup.status, error, required_v2);
232
+ JSONC_PARSE_ARRAY_OF_TXT2BITMAP_OR_ERROR_AND_RETURN(jobj, path, "exit_reason", EXIT_REASON_2id_one, ds->dedup.exit_reason, error, required_v2);
233
+ });
234
+
235
return true;
236
}
237
238
// --------------------------------------------------------------------------------------------------------------------
239
// get the current status
240
224
-static DAEMON_STATUS_FILE daemon_status_file_get(DAEMON_STATUS status) {
241
+static void daemon_status_file_refresh(DAEMON_STATUS status) {
242
+ spinlock_lock(&dsf_spinlock);
243
+
244
usec_t now_ut = now_realtime_usec();
245
246
#if defined(OS_LINUX)
@@ -295,6 +314,11 @@ static DAEMON_STATUS_FILE daemon_status_file_get(DAEMON_STATUS status) {
314
session_status.os_id = strdupz(last_session_status.os_id);
315
if(!session_status.os_id_like && last_session_status.os_id_like)
316
session_status.os_id_like = strdupz(last_session_status.os_id_like);
317
+ if(!session_status.dedup.timestamp) {
318
+ session_status.dedup.timestamp = last_session_status.dedup.timestamp;
319
+ session_status.dedup.status = last_session_status.dedup.status;
320
+ session_status.dedup.exit_reason = last_session_status.dedup.exit_reason;
321
+ }
322
323
get_daemon_status_fields_from_system_info(&session_status);
324
@@ -307,7 +331,7 @@ static DAEMON_STATUS_FILE daemon_status_file_get(DAEMON_STATUS status) {
331
session_status.memory = os_system_memory(true);
332
session_status.var_cache = os_disk_space(netdata_configured_cache_dir);
333
310
- return session_status;
334
+ spinlock_unlock(&dsf_spinlock);
335
}
336
337
// --------------------------------------------------------------------------------------------------------------------
@@ -439,17 +463,13 @@ static bool save_status_file(const char *directory, const char *content, size_t
463
return true;
464
}
465
442
-void daemon_status_file_save(DAEMON_STATUS status) {
443
- static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
444
- spinlock_lock(&spinlock);
445
-
446
- // Get current status
447
- DAEMON_STATUS_FILE ds = daemon_status_file_get(status);
466
+static void daemon_status_file_save(DAEMON_STATUS_FILE *ds) {
467
+ spinlock_lock(&dsf_spinlock);
468
469
// Prepare JSON content
470
CLEAN_BUFFER *wb = buffer_create(0, NULL);
471
buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_DEFAULT);
452
- daemon_status_file_to_json(wb, &ds);
472
+ daemon_status_file_to_json(wb, ds);
473
buffer_json_finalize(wb);
474
475
const char *content = buffer_tostring(wb);
@@ -476,7 +496,12 @@ void daemon_status_file_save(DAEMON_STATUS status) {
496
if (!saved)
497
nd_log(NDLS_DAEMON, NDLP_ERR, "Failed to save status file in any location");
498
479
- spinlock_unlock(&spinlock);
499
+ spinlock_unlock(&dsf_spinlock);
500
+}
501
+
502
+void daemon_status_file_update_status(DAEMON_STATUS status) {
503
+ daemon_status_file_refresh(status);
504
+ daemon_status_file_save(&session_status);
505
}
506
507
// --------------------------------------------------------------------------------------------------------------------
@@ -512,7 +537,14 @@ void post_status_file(struct post_status_file_thread_data *d) {
537
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
538
539
CURLcode rc = curl_easy_perform(curl);
515
- (void)rc;
540
+ if(rc == CURLE_OK) {
541
+ spinlock_lock(&dsf_spinlock);
542
+ session_status.dedup.timestamp = now_realtime_sec();
543
+ session_status.dedup.status = d->status.status;
544
+ session_status.dedup.exit_reason = d->status.exit_reason;
545
+ spinlock_unlock(&dsf_spinlock);
546
+ daemon_status_file_save(&session_status);
547
+ }
548
549
curl_easy_cleanup(curl);
550
curl_slist_free_all(headers);
@@ -532,7 +564,7 @@ void *post_status_file_thread(void *ptr) {
564
565
void daemon_status_file_check_crash(void) {
566
last_session_status = daemon_status_file_load();
535
- daemon_status_file_save(DAEMON_STATUS_INITIALIZING);
567
+ daemon_status_file_update_status(DAEMON_STATUS_INITIALIZING);
568
ND_LOG_FIELD_PRIORITY pri = NDLP_NOTICE;
569
570
bool new_version = strcmp(last_session_status.version, session_status.version) != 0;
@@ -592,12 +624,24 @@ void daemon_status_file_check_crash(void) {
624
cause = "disk full";
625
msg = "Netdata couldn't start because the disk is full";
626
}
627
+ else if (OS_SYSTEM_DISK_SPACE_OK(last_session_status.var_cache) &&
628
+ last_session_status.var_cache.free_bytes < 1 * 1024 * 1024) {
629
+ cause = "disk almost full";
630
+ msg = "Netdata couldn't start while the disk is almost full";
631
+ }
632
else {
633
cause = "crashed on start";
634
msg = "Netdata was last killed/crashed while starting";
635
}
636
pri = NDLP_ERR;
637
post_crash_report = true;
638
+
639
+ if(session_status.dedup.status == DAEMON_STATUS_INITIALIZING &&
640
+ now_realtime_sec() - last_session_status.dedup.timestamp < 86400) {
641
+ // we have already posted this crash
642
+ disable_crash_report = true;
643
+ }
644
+
645
break;
646
647
case DAEMON_STATUS_EXITING:
@@ -678,19 +722,18 @@ void daemon_status_file_startup_step(const char *step) {
722
freez((char *)session_status.fatal.function);
723
session_status.fatal.function = step ? strdupz(step) : NULL;
724
if(step != NULL)
681
- daemon_status_file_save(DAEMON_STATUS_NONE);
725
+ daemon_status_file_update_status(DAEMON_STATUS_NONE);
726
}
727
728
// --------------------------------------------------------------------------------------------------------------------
729
// ng_log() hook for receiving fatal message information
730
731
void daemon_status_file_register_fatal(const char *filename, const char *function, const char *message, const char *stack_trace, long line) {
688
- static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
689
- spinlock_lock(&spinlock);
732
+ spinlock_lock(&dsf_spinlock);
733
734
// do not check the function, because it may have a startup step in it
735
if(session_status.fatal.filename || session_status.fatal.message || session_status.fatal.stack_trace) {
693
- spinlock_unlock(&spinlock);
736
+ spinlock_unlock(&dsf_spinlock);
737
freez((void *)filename);
738
freez((void *)function);
739
freez((void *)message);
@@ -705,5 +748,8 @@ void daemon_status_file_register_fatal(const char *filename, const char *functio
748
session_status.fatal.stack_trace = stack_trace;
749
session_status.fatal.line = line;
750
708
- spinlock_unlock(&spinlock);
751
+ spinlock_unlock(&dsf_spinlock);
752
+
753
+ exit_initiated |= EXIT_REASON_FATAL;
754
+ daemon_status_file_save(&session_status);
755
}
src/daemon/daemon-status-file.h
+7
-1
@@ -66,13 +66,19 @@ typedef struct daemon_status_file {
66
const char *stack_trace;
67
const char *message;
68
} fatal;
69
+
70
+ struct {
71
+ time_t timestamp;
72
+ DAEMON_STATUS status;
73
+ EXIT_REASON exit_reason;
74
+ } dedup;
75
} DAEMON_STATUS_FILE;
76
77
// loads the last status saved
78
DAEMON_STATUS_FILE daemon_status_file_load(void);
79
80
// saves the current status
75
-void daemon_status_file_save(DAEMON_STATUS status);
81
+void daemon_status_file_update_status(DAEMON_STATUS status);
82
83
// check for a crash
84
void daemon_status_file_check_crash(void);
src/daemon/main.c
+1
-1
@@ -1058,7 +1058,7 @@ int netdata_main(int argc, char **argv) {
1058
webrtc_initialize();
1059
1060
daemon_status_file_startup_step(NULL);
1061
- daemon_status_file_save(DAEMON_STATUS_RUNNING);
1061
+ daemon_status_file_update_status(DAEMON_STATUS_RUNNING);
1062
return 10;
1063
}
1064
src/daemon/signals.c
+2
-2
@@ -113,7 +113,7 @@ void nd_process_signals(void) {
113
// is delivered that either terminates the process or causes the invocation
114
// of a signal-catching function.
115
if(pause() == -1 && errno == EINTR) {
116
- daemon_status_file_save(DAEMON_STATUS_NONE);
116
+ daemon_status_file_update_status(DAEMON_STATUS_NONE);
117
errno_clear();
118
119
// loop once, but keep looping while signals are coming in,
@@ -157,7 +157,7 @@ void nd_process_signals(void) {
157
case NETDATA_SIGNAL_FATAL:
158
nd_log_limits_unlimited();
159
exit_initiated_set(signals_waiting[i].reason);
160
- daemon_status_file_save(DAEMON_STATUS_NONE);
160
+ daemon_status_file_update_status(DAEMON_STATUS_NONE);
161
fatal("SIGNAL: Received %s. netdata now exits.", name);
162
break;
163