@cryptotaxi247 / netdata-1 / commits / 00c4ad864

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);