deduplicate all crash reports (#19712)
* deduplicate all crash reports * fatal messages also log errno; verify required directory logs also the env variable
Costa Tsaousis committed
Feb 26, 2025 at 11:00 UTC
00c4ad86489786e41273d3c32dccc3f764df2c6c
7 files changed
+176
-125
src/daemon/daemon-status-file.c
+105
-66
@@ -8,6 +8,8 @@
8
#include <openssl/pem.h>
9
#include <openssl/err.h>
10
11
+#define STATUS_FILE_VERSION 3
12
+
13
#define STATUS_FILENAME "status-netdata.json"
14
#define STATUS_FILENAME_TMP "status-netdata.json.tmp"
15
@@ -42,9 +44,33 @@ static SPINLOCK dsf_spinlock = SPINLOCK_INITIALIZER;
44
// --------------------------------------------------------------------------------------------------------------------
45
// json generation
46
47
+static XXH64_hash_t daemon_status_file_hash(DAEMON_STATUS_FILE *ds, const char *msg, const char *cause) {
48
+ CLEAN_BUFFER *wb = buffer_create(0, NULL);
49
+ buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_MINIFY);
50
+ buffer_json_member_add_uint64(wb, "version", STATUS_FILE_VERSION);
51
+ buffer_json_member_add_uuid(wb, "host_id", ds->host_id.uuid);
52
+ buffer_json_member_add_uuid(wb, "node_id", ds->node_id.uuid);
53
+ buffer_json_member_add_uuid(wb, "claim_id", ds->claim_id.uuid);
54
+ buffer_json_member_add_string(wb, "agent_version", ds->version);
55
+ buffer_json_member_add_uint64(wb, "fatal_line", ds->fatal.line);
56
+ buffer_json_member_add_string_or_empty(wb, "fatal_filename", ds->fatal.filename);
57
+ buffer_json_member_add_string_or_empty(wb, "fatal_errno", ds->fatal.errno_str);
58
+ buffer_json_member_add_string_or_empty(wb, "fatal_function", ds->fatal.function);
59
+ buffer_json_member_add_string_or_empty(wb, "fatal_message", ds->fatal.message);
60
+ buffer_json_member_add_string_or_empty(wb, "fatal_stack_trace", ds->fatal.stack_trace);
61
+ buffer_json_member_add_string(wb, "message", msg);
62
+ buffer_json_member_add_string(wb, "cause", cause);
63
+ buffer_json_member_add_string(wb, "status", DAEMON_STATUS_2str(ds->status));
64
+ EXIT_REASON_2json(wb, "exit_reason", ds->exit_reason);
65
+ ND_PROFILE_2json(wb, "profile", ds->profile);
66
+ buffer_json_finalize(wb);
67
+ XXH64_hash_t hash = XXH3_64bits((const void *)buffer_tostring(wb), buffer_strlen(wb));
68
+ return hash;
69
+}
70
+
71
static void daemon_status_file_to_json(BUFFER *wb, DAEMON_STATUS_FILE *ds) {
72
buffer_json_member_add_datetime_rfc3339(wb, "@timestamp", ds->timestamp_ut, true); // ECS
47
- buffer_json_member_add_uint64(wb, "version", 2); // custom
73
+ buffer_json_member_add_uint64(wb, "version", STATUS_FILE_VERSION); // custom
74
75
buffer_json_member_add_object(wb, "agent"); // ECS
76
{
@@ -123,15 +149,16 @@ static void daemon_status_file_to_json(BUFFER *wb, DAEMON_STATUS_FILE *ds) {
149
buffer_json_member_add_string_or_empty(wb, "filename", ds->fatal.filename);
150
buffer_json_member_add_string_or_empty(wb, "function", ds->fatal.function);
151
buffer_json_member_add_string_or_empty(wb, "message", ds->fatal.message);
152
+ buffer_json_member_add_string_or_empty(wb, "errno", ds->fatal.errno_str);
153
buffer_json_member_add_string_or_empty(wb, "stack_trace", ds->fatal.stack_trace);
154
}
155
buffer_json_object_close(wb);
156
157
buffer_json_member_add_object(wb, "dedup"); // custom
158
{
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
159
+ buffer_json_member_add_datetime_rfc3339(wb, "@timestamp", ds->dedup.timestamp_ut, true); // custom
160
+ buffer_json_member_add_uint64(wb, "hash", ds->dedup.hash); // custom
161
+ buffer_json_member_add_uint64(wb, "restarts", ds->dedup.restarts); // custom
162
}
163
buffer_json_object_close(wb);
164
}
@@ -149,52 +176,53 @@ static bool daemon_status_file_from_json(json_object *jobj, void *data, BUFFER *
176
uint64_t version = 0;
177
JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "version", version, error, true);
178
152
- bool required = false; // allow missing fields and values
153
- bool required_v2 = false; // allow missing fields and values for version 2
179
+ bool strict = false; // allow missing fields and values
180
+ bool required_v1 = version >= 1 ? strict : false;
181
+ bool required_v3 = version >= 3 ? strict : false;
182
183
// Parse timestamp
156
- JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "@timestamp", datetime, error, required);
184
+ JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "@timestamp", datetime, error, required_v1);
185
if(datetime[0])
186
ds->timestamp_ut = rfc3339_parse_ut(datetime, NULL);
187
188
// Parse agent object
161
- JSONC_PARSE_SUBOBJECT(jobj, path, "agent", error, required, {
162
- JSONC_PARSE_TXT2UUID_OR_ERROR_AND_RETURN(jobj, path, "id", ds->host_id.uuid, error, required);
163
- JSONC_PARSE_TXT2UUID_OR_ERROR_AND_RETURN(jobj, path, "ephemeral_id", ds->invocation.uuid, error, required);
164
- JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "version", ds->version, error, required);
165
- JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "uptime", ds->uptime, error, required);
166
- JSONC_PARSE_ARRAY_OF_TXT2BITMAP_OR_ERROR_AND_RETURN(jobj, path, "ND_profile", ND_PROFILE_2id_one, ds->profile, error, required);
167
- JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, "ND_status", DAEMON_STATUS_2id, ds->status, error, required);
168
- JSONC_PARSE_ARRAY_OF_TXT2BITMAP_OR_ERROR_AND_RETURN(jobj, path, "ND_exit_reason", EXIT_REASON_2id_one, ds->exit_reason, error, required);
169
- JSONC_PARSE_TXT2UUID_OR_ERROR_AND_RETURN(jobj, path, "ND_node_id", ds->node_id.uuid, error, required);
170
- JSONC_PARSE_TXT2UUID_OR_ERROR_AND_RETURN(jobj, path, "ND_claim_id", ds->claim_id.uuid, error, required);
171
-
172
- JSONC_PARSE_SUBOBJECT(jobj, path, "ND_timings", error, required, {
173
- JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "init", ds->timings.init, error, required);
174
- JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "exit", ds->timings.exit, error, required);
189
+ JSONC_PARSE_SUBOBJECT(jobj, path, "agent", error, required_v1, {
190
+ JSONC_PARSE_TXT2UUID_OR_ERROR_AND_RETURN(jobj, path, "id", ds->host_id.uuid, error, required_v1);
191
+ JSONC_PARSE_TXT2UUID_OR_ERROR_AND_RETURN(jobj, path, "ephemeral_id", ds->invocation.uuid, error, required_v1);
192
+ JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "version", ds->version, error, required_v1);
193
+ JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "uptime", ds->uptime, error, required_v1);
194
+ JSONC_PARSE_ARRAY_OF_TXT2BITMAP_OR_ERROR_AND_RETURN(jobj, path, "ND_profile", ND_PROFILE_2id_one, ds->profile, error, required_v1);
195
+ JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, "ND_status", DAEMON_STATUS_2id, ds->status, error, required_v1);
196
+ JSONC_PARSE_ARRAY_OF_TXT2BITMAP_OR_ERROR_AND_RETURN(jobj, path, "ND_exit_reason", EXIT_REASON_2id_one, ds->exit_reason, error, required_v1);
197
+ JSONC_PARSE_TXT2UUID_OR_ERROR_AND_RETURN(jobj, path, "ND_node_id", ds->node_id.uuid, error, required_v1);
198
+ JSONC_PARSE_TXT2UUID_OR_ERROR_AND_RETURN(jobj, path, "ND_claim_id", ds->claim_id.uuid, error, required_v1);
199
+
200
+ JSONC_PARSE_SUBOBJECT(jobj, path, "ND_timings", error, required_v1, {
201
+ JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "init", ds->timings.init, error, required_v1);
202
+ JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "exit", ds->timings.exit, error, required_v1);
203
});
204
});
205
206
// Parse host object
179
- JSONC_PARSE_SUBOBJECT(jobj, path, "host", error, required, {
180
- JSONC_PARSE_TXT2STRDUPZ_OR_ERROR_AND_RETURN(jobj, path, "architecture", ds->architecture, error, required);
181
- JSONC_PARSE_TXT2STRDUPZ_OR_ERROR_AND_RETURN(jobj, path, "virtualization", ds->virtualization, error, required);
182
- JSONC_PARSE_TXT2STRDUPZ_OR_ERROR_AND_RETURN(jobj, path, "container", ds->container, error, required);
183
- JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "uptime", ds->boottime, error, required);
184
-
185
- JSONC_PARSE_SUBOBJECT(jobj, path, "boot", error, required, {
186
- JSONC_PARSE_TXT2UUID_OR_ERROR_AND_RETURN(jobj, path, "id", ds->boot_id.uuid, error, required);
207
+ JSONC_PARSE_SUBOBJECT(jobj, path, "host", error, required_v1, {
208
+ JSONC_PARSE_TXT2STRDUPZ_OR_ERROR_AND_RETURN(jobj, path, "architecture", ds->architecture, error, required_v1);
209
+ JSONC_PARSE_TXT2STRDUPZ_OR_ERROR_AND_RETURN(jobj, path, "virtualization", ds->virtualization, error, required_v1);
210
+ JSONC_PARSE_TXT2STRDUPZ_OR_ERROR_AND_RETURN(jobj, path, "container", ds->container, error, required_v1);
211
+ JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "uptime", ds->boottime, error, required_v1);
212
+
213
+ JSONC_PARSE_SUBOBJECT(jobj, path, "boot", error, required_v1, {
214
+ JSONC_PARSE_TXT2UUID_OR_ERROR_AND_RETURN(jobj, path, "id", ds->boot_id.uuid, error, required_v1);
215
});
216
189
- JSONC_PARSE_SUBOBJECT(jobj, path, "memory", error, required, {
190
- JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "total", ds->memory.ram_total_bytes, error, required);
191
- JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "free", ds->memory.ram_available_bytes, error, required);
217
+ JSONC_PARSE_SUBOBJECT(jobj, path, "memory", error, required_v1, {
218
+ JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "total", ds->memory.ram_total_bytes, error, false);
219
+ JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "free", ds->memory.ram_available_bytes, error, false);
220
if(!OS_SYSTEM_MEMORY_OK(ds->memory))
221
ds->memory = OS_SYSTEM_MEMORY_EMPTY;
222
});
223
196
- JSONC_PARSE_SUBOBJECT(jobj, path, "disk", error, required, {
197
- JSONC_PARSE_SUBOBJECT(jobj, path, "db", error, required, {
224
+ JSONC_PARSE_SUBOBJECT(jobj, path, "disk", error, required_v1, {
225
+ JSONC_PARSE_SUBOBJECT(jobj, path, "db", error, required_v1, {
226
JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "total", ds->var_cache.total_bytes, error, false);
227
JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "free", ds->var_cache.free_bytes, error, false);
228
JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "inodes_total", ds->var_cache.total_inodes, error, false);
@@ -207,29 +235,34 @@ static bool daemon_status_file_from_json(json_object *jobj, void *data, BUFFER *
235
});
236
237
// Parse os object
210
- JSONC_PARSE_SUBOBJECT(jobj, path, "os", error, required, {
211
- JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, "type", DAEMON_OS_TYPE_2id, ds->os_type, error, required);
212
- JSONC_PARSE_TXT2STRDUPZ_OR_ERROR_AND_RETURN(jobj, path, "kernel", ds->kernel_version, error, required);
213
- JSONC_PARSE_TXT2STRDUPZ_OR_ERROR_AND_RETURN(jobj, path, "name", ds->os_name, error, required);
214
- JSONC_PARSE_TXT2STRDUPZ_OR_ERROR_AND_RETURN(jobj, path, "version", ds->os_version, error, required);
215
- JSONC_PARSE_TXT2STRDUPZ_OR_ERROR_AND_RETURN(jobj, path, "family", ds->os_id, error, required);
216
- JSONC_PARSE_TXT2STRDUPZ_OR_ERROR_AND_RETURN(jobj, path, "platform", ds->os_id_like, error, required);
238
+ JSONC_PARSE_SUBOBJECT(jobj, path, "os", error, required_v1, {
239
+ JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, "type", DAEMON_OS_TYPE_2id, ds->os_type, error, required_v1);
240
+ JSONC_PARSE_TXT2STRDUPZ_OR_ERROR_AND_RETURN(jobj, path, "kernel", ds->kernel_version, error, required_v1);
241
+ JSONC_PARSE_TXT2STRDUPZ_OR_ERROR_AND_RETURN(jobj, path, "name", ds->os_name, error, required_v1);
242
+ JSONC_PARSE_TXT2STRDUPZ_OR_ERROR_AND_RETURN(jobj, path, "version", ds->os_version, error, required_v1);
243
+ JSONC_PARSE_TXT2STRDUPZ_OR_ERROR_AND_RETURN(jobj, path, "family", ds->os_id, error, required_v1);
244
+ JSONC_PARSE_TXT2STRDUPZ_OR_ERROR_AND_RETURN(jobj, path, "platform", ds->os_id_like, error, required_v1);
245
});
246
247
// Parse fatal object
220
- JSONC_PARSE_SUBOBJECT(jobj, path, "fatal", error, required, {
221
- JSONC_PARSE_TXT2STRDUPZ_OR_ERROR_AND_RETURN(jobj, path, "filename", ds->fatal.filename, error, required);
222
- JSONC_PARSE_TXT2STRDUPZ_OR_ERROR_AND_RETURN(jobj, path, "function", ds->fatal.function, error, required);
223
- JSONC_PARSE_TXT2STRDUPZ_OR_ERROR_AND_RETURN(jobj, path, "message", ds->fatal.message, error, required);
224
- JSONC_PARSE_TXT2STRDUPZ_OR_ERROR_AND_RETURN(jobj, path, "stack_trace", ds->fatal.stack_trace, error, required);
225
- JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "line", ds->fatal.line, error, required);
248
+ JSONC_PARSE_SUBOBJECT(jobj, path, "fatal", error, required_v1, {
249
+ JSONC_PARSE_TXT2STRDUPZ_OR_ERROR_AND_RETURN(jobj, path, "filename", ds->fatal.filename, error, required_v1);
250
+ JSONC_PARSE_TXT2STRDUPZ_OR_ERROR_AND_RETURN(jobj, path, "function", ds->fatal.function, error, required_v1);
251
+ JSONC_PARSE_TXT2STRDUPZ_OR_ERROR_AND_RETURN(jobj, path, "message", ds->fatal.message, error, required_v1);
252
+ JSONC_PARSE_TXT2STRDUPZ_OR_ERROR_AND_RETURN(jobj, path, "errno", ds->fatal.errno_str, error, required_v3);
253
+ JSONC_PARSE_TXT2STRDUPZ_OR_ERROR_AND_RETURN(jobj, path, "stack_trace", ds->fatal.stack_trace, error, required_v1);
254
+ JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "line", ds->fatal.line, error, required_v1);
255
});
256
257
// 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);
258
+ JSONC_PARSE_SUBOBJECT(jobj, path, "dedup", error, required_v3, {
259
+ datetime[0] = '\0';
260
+ JSONC_PARSE_TXT2CHAR_OR_ERROR_AND_RETURN(jobj, path, "@timestamp", datetime, error, required_v1);
261
+ if(datetime[0])
262
+ ds->dedup.timestamp_ut = rfc3339_parse_ut(datetime, NULL);
263
+
264
+ JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "hash", ds->dedup.hash, error, required_v3);
265
+ JSONC_PARSE_UINT64_OR_ERROR_AND_RETURN(jobj, path, "restarts", ds->dedup.restarts, error, required_v3);
266
});
267
268
return true;
@@ -314,10 +347,11 @@ static void daemon_status_file_refresh(DAEMON_STATUS status) {
347
session_status.os_id = strdupz(last_session_status.os_id);
348
if(!session_status.os_id_like && last_session_status.os_id_like)
349
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;
350
+ if(!session_status.dedup.restarts)
351
+ session_status.dedup.restarts = last_session_status.dedup.restarts + 1;
352
+ if(!session_status.dedup.timestamp_ut || !session_status.dedup.hash) {
353
+ session_status.dedup.timestamp_ut = last_session_status.dedup.timestamp_ut;
354
+ session_status.dedup.hash = last_session_status.dedup.hash;
355
}
356
357
get_daemon_status_fields_from_system_info(&session_status);
@@ -538,10 +572,10 @@ void post_status_file(struct post_status_file_thread_data *d) {
572
573
CURLcode rc = curl_easy_perform(curl);
574
if(rc == CURLE_OK) {
575
+ XXH64_hash_t hash = daemon_status_file_hash(&d->status, d->msg, d->cause);
576
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;
577
+ session_status.dedup.timestamp_ut = now_realtime_usec();
578
+ session_status.dedup.hash = hash;
579
spinlock_unlock(&dsf_spinlock);
580
daemon_status_file_save(&session_status);
581
}
@@ -636,12 +670,6 @@ void daemon_status_file_check_crash(void) {
670
pri = NDLP_ERR;
671
post_crash_report = true;
672
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
-
673
break;
674
675
case DAEMON_STATUS_EXITING:
@@ -698,6 +726,15 @@ void daemon_status_file_check_crash(void) {
726
"Last exit status: %s (%s):\n\n%s",
727
NETDATA_VERSION, msg, cause, buffer_tostring(wb));
728
729
+ if(last_session_status.dedup.timestamp_ut && last_session_status.dedup.hash) {
730
+ XXH64_hash_t hash = daemon_status_file_hash(&last_session_status, msg, cause);
731
+ if(hash == last_session_status.dedup.hash &&
732
+ now_realtime_usec() - last_session_status.dedup.timestamp_ut < 86400 * USEC_PER_SEC) {
733
+ // we have already posted this crash
734
+ disable_crash_report = true;
735
+ }
736
+ }
737
+
738
if(!disable_crash_report && (analytics_check_enabled() || post_crash_report)) {
739
netdata_conf_ssl();
740
@@ -728,15 +765,16 @@ void daemon_status_file_startup_step(const char *step) {
765
// --------------------------------------------------------------------------------------------------------------------
766
// ng_log() hook for receiving fatal message information
767
731
-void daemon_status_file_register_fatal(const char *filename, const char *function, const char *message, const char *stack_trace, long line) {
768
+void daemon_status_file_register_fatal(const char *filename, const char *function, const char *message, const char *errno_str, const char *stack_trace, long line) {
769
spinlock_lock(&dsf_spinlock);
770
771
// 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) {
772
+ if(session_status.fatal.filename || session_status.fatal.message || session_status.fatal.errno_str || session_status.fatal.stack_trace) {
773
spinlock_unlock(&dsf_spinlock);
774
freez((void *)filename);
775
freez((void *)function);
776
freez((void *)message);
777
+ freez((void *)errno_str);
778
freez((void *)stack_trace);
779
return;
780
}
@@ -745,6 +783,7 @@ void daemon_status_file_register_fatal(const char *filename, const char *functio
783
freez((char *)session_status.fatal.function); // it may have a startup step
784
session_status.fatal.function = function;
785
session_status.fatal.message = message;
786
+ session_status.fatal.errno_str = errno_str;
787
session_status.fatal.stack_trace = stack_trace;
788
session_status.fatal.line = line;
789
src/daemon/daemon-status-file.h
+5
-4
@@ -63,14 +63,15 @@ typedef struct daemon_status_file {
63
long line;
64
const char *filename;
65
const char *function;
66
+ const char *errno_str;
67
const char *stack_trace;
68
const char *message;
69
} fatal;
70
71
struct {
71
- time_t timestamp;
72
- DAEMON_STATUS status;
73
- EXIT_REASON exit_reason;
72
+ XXH64_hash_t hash;
73
+ usec_t timestamp_ut;
74
+ size_t restarts;
75
} dedup;
76
} DAEMON_STATUS_FILE;
77
@@ -87,6 +88,6 @@ bool daemon_status_file_has_last_crashed(void);
88
bool daemon_status_file_was_incomplete_shutdown(void);
89
void daemon_status_file_startup_step(const char *step);
90
90
-void daemon_status_file_register_fatal(const char *filename, const char *function, const char *message, const char *stack_trace, long line);
91
+void daemon_status_file_register_fatal(const char *filename, const char *function, const char *message, const char *errno_str, const char *stack_trace, long line);
92
93
#endif //NETDATA_DAEMON_STATUS_FILE_H
src/daemon/environment.c
+13
-7
@@ -6,7 +6,7 @@ void verify_required_directory(const char *env, const char *dir, bool create_it,
6
errno_clear();
7
8
if (!dir || *dir != '/')
9
- fatal("Invalid directory path (must be an absolute path): '%s'\n", dir);
9
+ fatal("Invalid directory path (must be an absolute path): '%s' (%s)", dir, env?env:"");
10
11
if (chdir(dir) == 0) {
12
if(env)
@@ -33,10 +33,12 @@ void verify_required_directory(const char *env, const char *dir, bool create_it,
33
34
errno_clear();
35
if (stat(path, &st) == -1)
36
- fatal("Required directory: '%s' - Missing or inaccessible component: '%s' (error: %s)\n", dir, path, strerror(errno));
36
+ fatal("Required directory: '%s' (%s) - Missing or inaccessible component: '%s'",
37
+ dir, env?env:"", path);
38
39
if (!S_ISDIR(st.st_mode))
39
- fatal("Required directory: '%s' - Component '%s' exists but is not a directory.\n", dir, path);
40
+ fatal("Required directory: '%s' (%s) - Component '%s' exists but is not a directory.",
41
+ dir, env?env:"", path);
42
43
*p = '/';
44
}
@@ -44,15 +46,19 @@ void verify_required_directory(const char *env, const char *dir, bool create_it,
46
}
47
48
if (stat(dir, &st) == -1)
47
- fatal("Required directory: '%s' - Missing or inaccessible: '%s' (error: %s)\n", dir, dir, strerror(errno));
49
+ fatal("Required directory: '%s' (%s) - Missing or inaccessible: '%s'",
50
+ dir, env?env:"", dir);
51
52
if (!S_ISDIR(st.st_mode))
50
- fatal("Required directory: '%s' - '%s' exists but is not a directory.\n", dir, dir);
53
+ fatal("Required directory: '%s' (%s) - '%s' exists but is not a directory.",
54
+ dir, env?env:"", dir);
55
56
if (access(dir, R_OK | X_OK) == -1)
53
- fatal("Required directory: '%s' - Insufficient permissions for: '%s' (error: %s)\n", dir, dir, strerror(errno));
57
+ fatal("Required directory: '%s' (%s) - Insufficient permissions for: '%s'",
58
+ dir, env?env:"", dir);
59
55
- fatal("Required directory: '%s' - Failed (error: %s)\n", dir, strerror(errno));
60
+ fatal("Required directory: '%s' (%s) - Failed",
61
+ dir, env?env:"");
62
}
63
64
void set_environment_for_plugins_and_scripts(void) {
src/libnetdata/log/nd_log-field-formatters.c
+49
-45
@@ -126,7 +126,7 @@ uint64_t log_field_to_uint64(struct log_field *lf) {
126
return 0;
127
}
128
129
-char *log_field_strdupz(struct log_field *lf) {
129
+const char *log_field_strdupz(struct log_field *lf) {
130
131
// --- FIELD_PARSER_VERSIONS ---
132
//
@@ -146,50 +146,54 @@ char *log_field_strdupz(struct log_field *lf) {
146
const char *s = NULL;
147
char buf[DOUBLE_MAX_LENGTH];
148
149
- switch(lf->entry.type) {
150
- default:
151
- case NDFT_UNSET:
152
- return NULL;
153
-
154
- case NDFT_UUID:
155
- uuid_unparse_lower_compact(*lf->entry.uuid, buf);
156
- s = buf;
157
- break;
158
-
159
- case NDFT_TXT:
160
- s = lf->entry.txt;
161
- break;
162
-
163
- case NDFT_STR:
164
- s = string2str(lf->entry.str);
165
- break;
166
-
167
- case NDFT_BFR:
168
- s = buffer_tostring(lf->entry.bfr);
169
- break;
170
-
171
- case NDFT_CALLBACK:
172
- tmp = buffer_create(0, NULL);
173
-
174
- if(lf->entry.cb.formatter(tmp, lf->entry.cb.formatter_data))
175
- s = buffer_tostring(tmp);
176
- else
177
- s = NULL;
178
- break;
179
-
180
- case NDFT_U64:
181
- print_uint64(buf, lf->entry.u64);
182
- s = buf;
183
- break;
184
-
185
- case NDFT_I64:
186
- print_int64(buf, lf->entry.i64);
187
- s = buf;
188
- break;
189
-
190
- case NDFT_DBL:
191
- print_netdata_double(buf, lf->entry.dbl);
192
- break;
149
+ if(lf->logfmt_annotator)
150
+ s = lf->logfmt_annotator(lf);
151
+ else {
152
+ switch (lf->entry.type) {
153
+ default:
154
+ case NDFT_UNSET:
155
+ return NULL;
156
+
157
+ case NDFT_UUID:
158
+ uuid_unparse_lower_compact(*lf->entry.uuid, buf);
159
+ s = buf;
160
+ break;
161
+
162
+ case NDFT_TXT:
163
+ s = lf->entry.txt;
164
+ break;
165
+
166
+ case NDFT_STR:
167
+ s = string2str(lf->entry.str);
168
+ break;
169
+
170
+ case NDFT_BFR:
171
+ s = buffer_tostring(lf->entry.bfr);
172
+ break;
173
+
174
+ case NDFT_CALLBACK:
175
+ tmp = buffer_create(0, NULL);
176
+
177
+ if (lf->entry.cb.formatter(tmp, lf->entry.cb.formatter_data))
178
+ s = buffer_tostring(tmp);
179
+ else
180
+ s = NULL;
181
+ break;
182
+
183
+ case NDFT_U64:
184
+ print_uint64(buf, lf->entry.u64);
185
+ s = buf;
186
+ break;
187
+
188
+ case NDFT_I64:
189
+ print_int64(buf, lf->entry.i64);
190
+ s = buf;
191
+ break;
192
+
193
+ case NDFT_DBL:
194
+ print_netdata_double(buf, lf->entry.dbl);
195
+ break;
196
+ }
197
}
198
199
if(s && *s)
src/libnetdata/log/nd_log-internals.h
+1
-1
@@ -212,7 +212,7 @@ const char *winerror_annotator(struct log_field *lf);
212
213
uint64_t log_field_to_uint64(struct log_field *lf);
214
int64_t log_field_to_int64(struct log_field *lf);
215
-char *log_field_strdupz(struct log_field *lf);
215
+const char *log_field_strdupz(struct log_field *lf);
216
217
// --------------------------------------------------------------------------------------------------------------------
218
// common text formatters
src/libnetdata/log/nd_log.c
+2
-1
@@ -125,9 +125,10 @@ static void nd_log_event(struct log_field *fields, size_t fields_max __maybe_unu
125
const char *message = log_field_strdupz(&fields[NDF_MESSAGE]);
126
const char *function = log_field_strdupz(&fields[NDF_FUNC]);
127
const char *stack_trace = log_field_strdupz(&fields[NDF_STACK_TRACE]);
128
+ const char *errno_str = log_field_strdupz(&fields[NDF_ERRNO]);
129
long line = log_field_to_int64(&fields[NDF_LINE]);
130
130
- nd_log.log_event_cb(filename, function, message, stack_trace, line);
131
+ nd_log.log_event_cb(filename, function, message, errno_str, stack_trace, line);
132
}
133
134
void nd_log_register_event_cb(log_event_t cb) {
src/libnetdata/log/nd_log.h
+1
-1
@@ -33,7 +33,7 @@ const char *nd_log_id2priority(ND_LOG_FIELD_PRIORITY priority);
33
const char *nd_log_method_for_external_plugins(const char *s);
34
ND_UUID nd_log_get_invocation_id(void);
35
36
-typedef void (*log_event_t)(const char *filename, const char *function, const char *message, const char *stack_trace, long line);
36
+typedef void (*log_event_t)(const char *filename, const char *function, const char *message, const char *errno_str, const char *stack_trace, long line);
37
void nd_log_register_event_cb(log_event_t cb);
38
39
int nd_log_health_fd(void);