@cryptotaxi247 / netdata-1 / commits / 7a85b2c89

Improve logger (#21928)

* Refactor logger to remove spinlock usage during I/O operations to prevent deadlocks * Refactor logger to use mutexes for write serialization, replacing spinlocks to prevent deadlocks during I/O operations * - Update write loop to break on zero writes instead of treating them the same as successful writes. * Reset flood protection counters and reinitialize spinlocks in post-fork child process. * Make spinlock usage conditional based on `disable_output_mutexes` in logger. * Initialize mutexes using constructor attribute and remove redundant initialization logic. Replace `disable_output_mutexes` with `single_threaded_child` for clarity and accuracy. * Refactor logger to ensure mutexes are initialized lazily, add stdio flush under mutex for write serialization, and remove redundant constructor-based mutex initialization. * Add file descriptor parameter to logger functions for enhanced flexibility in file I/O handling. * Skip logger initialization in single-threaded post-fork child processes to avoid mutex state corruption. * Decouple logger mutex initialization from spinlock logic and centralize it in `nd_log_initialize_mutexes`. * Early init for log mutexes * Improve write loop with chunked writes for large buffers and ensure early initialization of log mutexes * Refactor stderr mutex logic with `nd_logger_stderr_mutex` helper to simplify and consolidate mutex retrieval. * Introduce unbuffered mode for logger streams and simplify mutex/state handling. * Use atomic operations for mutex initialization and access to ensure thread safety.

Stelios Fragkakis committed Mar 12, 2026 at 00:36 UTC 7a85b2c8965818d686348ec151fe70c433ed5b5e
9 files changed +159 -89
src/daemon/main.c
+1
@@ -255,6 +255,7 @@ int netdata_main(int argc, char **argv) {
255 libjudy_malloc_init();
256 string_init();
257 analytics_init();
258 + nd_log_initialize_mutexes();
259
260 netdata_start_time = now_realtime_sec();
261 usec_t started_ut = now_monotonic_usec();
src/libnetdata/log/nd_log-config.c
+4
@@ -192,14 +192,18 @@ void nd_log_set_facility(const char *facility) {
192
193 void nd_log_set_flood_protection(size_t logs, time_t period) {
194 // daemon logs
195 + spinlock_lock(&nd_log.sources[NDLS_DAEMON].limits.spinlock);
196 nd_log.sources[NDLS_DAEMON].limits.logs_per_period = logs;
197 nd_log.sources[NDLS_DAEMON].limits.logs_per_period_backup = logs;
198 nd_log.sources[NDLS_DAEMON].limits.throttle_period = period;
199 + spinlock_unlock(&nd_log.sources[NDLS_DAEMON].limits.spinlock);
200
201 // collectors logs
202 + spinlock_lock(&nd_log.sources[NDLS_COLLECTORS].limits.spinlock);
203 nd_log.sources[NDLS_COLLECTORS].limits.logs_per_period = logs;
204 nd_log.sources[NDLS_COLLECTORS].limits.logs_per_period_backup = logs;
205 nd_log.sources[NDLS_COLLECTORS].limits.throttle_period = period;
206 + spinlock_unlock(&nd_log.sources[NDLS_COLLECTORS].limits.spinlock);
207
208 char buf[100];
209 snprintfz(buf, sizeof(buf), "%" PRIu64, (uint64_t)period);
src/libnetdata/log/nd_log-init.c
+34 -9
@@ -27,7 +27,29 @@ ND_UUID nd_log_get_invocation_id(void) {
27
28 // --------------------------------------------------------------------------------------------------------------------
29
30 +static void nd_log_make_stream_unbuffered(FILE *fp, int fd, const char *filename) {
31 + if(fp && setvbuf(fp, NULL, _IONBF, 0) != 0)
32 + netdata_log_error("Cannot disable buffering on fd %d ('%s')", fd, filename ? filename : "stdio");
33 +}
34 +
35 +// --------------------------------------------------------------------------------------------------------------------
36 +
37 +void nd_log_initialize_mutexes(void) {
38 + FUNCTION_RUN_ONCE();
39 +
40 + for(size_t i = 0 ; i < _NDLS_MAX ; i++)
41 + netdata_mutex_init(&nd_log.sources[i].mutex);
42 +
43 + netdata_mutex_init(&nd_log.std_output.mutex);
44 + netdata_mutex_init(&nd_log.std_error.mutex);
45 + __atomic_store_n(&nd_log.mutexes_initialized, true, __ATOMIC_RELEASE);
46 +}
47 +
48 +// --------------------------------------------------------------------------------------------------------------------
49 +
50 void nd_log_initialize_for_external_plugins(const char *name) {
51 + nd_log_initialize_mutexes();
52 +
53 // if we don't run under Netdata, log to stderr,
54 // otherwise, use the logging method Netdata wants us to use.
55 #if defined(OS_WINDOWS)
@@ -180,6 +202,7 @@ void nd_log_open(struct nd_log_source *e, ND_LOG_SOURCES source) {
202 case NDLM_STDOUT:
203 e->fp = stdout;
204 e->fd = STDOUT_FILENO;
205 + nd_log_make_stream_unbuffered(e->fp, e->fd, "stdout");
206 break;
207
208 case NDLM_DISABLED:
@@ -190,6 +213,7 @@ void nd_log_open(struct nd_log_source *e, ND_LOG_SOURCES source) {
213 e->method = NDLM_STDERR;
214 e->fp = stderr;
215 e->fd = STDERR_FILENO;
216 + nd_log_make_stream_unbuffered(e->fp, e->fd, "stderr");
217 break;
218
219 case NDLM_DEVNULL:
@@ -240,10 +264,8 @@ void nd_log_open(struct nd_log_source *e, ND_LOG_SOURCES source) {
264 e->fd = STDERR_FILENO;
265 }
266 }
243 - else {
244 - if (setvbuf(e->fp, NULL, _IOLBF, 0) != 0)
245 - netdata_log_error("Cannot set line buffering on fd %d ('%s')", e->fd, e->filename);
246 - }
267 +
268 + nd_log_make_stream_unbuffered(e->fp, e->fd, e->filename);
269 }
270 break;
271 }
@@ -263,6 +285,7 @@ void nd_log_stdin_init(int fd, const char *filename) {
285 }
286
287 void nd_log_initialize(void) {
288 + nd_log_initialize_mutexes();
289 nd_log_stdin_init(STDIN_FILENO, "/dev/null");
290
291 for(size_t i = 0 ; i < _NDLS_MAX ; i++)
@@ -290,6 +313,7 @@ int nd_log_systemd_journal_fd(void) {
313 void nd_log_reopen_log_files_for_spawn_server(const char *name) {
314 nd_log.fatal_hook_cb = NULL;
315 nd_log.fatal_final_cb = NULL;
316 + nd_log.single_threaded_child = true;
317
318 gettid_uncached();
319 #if defined(HAVE_LIBBACKTRACE)
@@ -309,8 +333,13 @@ void nd_log_reopen_log_files_for_spawn_server(const char *name) {
333 nd_log.journal_direct.initialized = false;
334 }
335
336 + // Keep flood protection in the post-fork child by resetting the
337 + // per-source throttle spinlocks and counters.
338 for(size_t i = 0; i < _NDLS_MAX ;i++) {
313 - spinlock_init(&nd_log.sources[i].spinlock);
339 + spinlock_init(&nd_log.sources[i].limits.spinlock);
340 + nd_log.sources[i].limits.started_monotonic_ut = 0;
341 + nd_log.sources[i].limits.counter = 0;
342 + nd_log.sources[i].limits.prevented = 0;
343 nd_log.sources[i].method = NDLM_DEFAULT;
344 nd_log.sources[i].fd = -1;
345 nd_log.sources[i].fp = NULL;
@@ -320,10 +349,6 @@ void nd_log_reopen_log_files_for_spawn_server(const char *name) {
349 #endif
350 }
351
323 - // initialize spinlocks
324 - spinlock_init(&nd_log.std_output.spinlock);
325 - spinlock_init(&nd_log.std_error.spinlock);
326 -
352 nd_log.syslog.initialized = false;
353 nd_log.eventlog.initialized = false;
354 nd_log.std_output.initialized = false;
src/libnetdata/log/nd_log-internals.c
-9
@@ -301,16 +301,13 @@ struct nd_log nd_log = {
301 },
302 #endif
303 .std_output = {
304 - .spinlock = SPINLOCK_INITIALIZER,
304 .initialized = false,
305 },
306 .std_error = {
308 - .spinlock = SPINLOCK_INITIALIZER,
307 .initialized = false,
308 },
309 .sources = {
310 [NDLS_UNSET] = {
313 - .spinlock = SPINLOCK_INITIALIZER,
311 .method = NDLM_DISABLED,
312 .format = NDLF_JOURNAL,
313 .filename = NULL,
@@ -320,7 +317,6 @@ struct nd_log nd_log = {
317 .limits = ND_LOG_LIMITS_UNLIMITED,
318 },
319 [NDLS_ACCESS] = {
323 - .spinlock = SPINLOCK_INITIALIZER,
320 .method = NDLM_DEFAULT,
321 .format = NDLF_LOGFMT,
322 .filename = LOG_DIR "/access.log",
@@ -330,7 +326,6 @@ struct nd_log nd_log = {
326 .limits = ND_LOG_LIMITS_UNLIMITED,
327 },
328 [NDLS_ACLK] = {
333 - .spinlock = SPINLOCK_INITIALIZER,
329 .method = NDLM_FILE,
330 .format = NDLF_LOGFMT,
331 .filename = LOG_DIR "/aclk.log",
@@ -340,7 +335,6 @@ struct nd_log nd_log = {
335 .limits = ND_LOG_LIMITS_UNLIMITED,
336 },
337 [NDLS_COLLECTORS] = {
343 - .spinlock = SPINLOCK_INITIALIZER,
338 .method = NDLM_DEFAULT,
339 .format = NDLF_LOGFMT,
340 .filename = LOG_DIR "/collector.log",
@@ -354,7 +348,6 @@ struct nd_log nd_log = {
348 .limits = ND_LOG_LIMITS_DEFAULT,
349 },
350 [NDLS_DEBUG] = {
357 - .spinlock = SPINLOCK_INITIALIZER,
351 .method = NDLM_DISABLED,
352 .format = NDLF_LOGFMT,
353 .filename = LOG_DIR "/debug.log",
@@ -368,7 +361,6 @@ struct nd_log nd_log = {
361 .limits = ND_LOG_LIMITS_UNLIMITED,
362 },
363 [NDLS_DAEMON] = {
371 - .spinlock = SPINLOCK_INITIALIZER,
364 .method = NDLM_DEFAULT,
365 .filename = LOG_DIR "/daemon.log",
366 .format = NDLF_LOGFMT,
@@ -378,7 +370,6 @@ struct nd_log nd_log = {
370 .limits = ND_LOG_LIMITS_DEFAULT,
371 },
372 [NDLS_HEALTH] = {
381 - .spinlock = SPINLOCK_INITIALIZER,
373 .method = NDLM_DEFAULT,
374 .format = NDLF_LOGFMT,
375 .filename = LOG_DIR "/health.log",
src/libnetdata/log/nd_log-internals.h
+7 -4
@@ -99,7 +99,7 @@ int nd_log_facility2id(const char *facility);
99 #include "nd_log_limit.h"
100
101 struct nd_log_source {
102 - SPINLOCK spinlock;
102 + netdata_mutex_t mutex; // protects write() serialization for FILE* output
103 ND_LOG_METHOD method;
104 ND_LOG_FORMAT format;
105 const char *filename;
@@ -125,6 +125,9 @@ struct nd_log {
125 nd_uuid_t invocation_id;
126
127 ND_LOG_SOURCES overwrite_process_source;
128 + bool mutexes_initialized;
129 + // Only set in post-fork nofork spawn-server children, which stay single-threaded in-tree.
130 + bool single_threaded_child;
131 log_event_t fatal_hook_cb;
132 fatal_event_t fatal_final_cb;
133
@@ -155,12 +158,12 @@ struct nd_log {
158 } eventlog;
159
160 struct {
158 - SPINLOCK spinlock;
161 + netdata_mutex_t mutex; // protects write() serialization for stdout
162 bool initialized;
163 } std_output;
164
165 struct {
163 - SPINLOCK spinlock;
166 + netdata_mutex_t mutex; // protects write() serialization for stderr
167 bool initialized;
168 } std_error;
169
@@ -236,7 +239,7 @@ bool nd_logger_journal_libsystemd(struct log_field *fields, size_t fields_max);
239 // --------------------------------------------------------------------------------------------------------------------
240 // output to file
241
239 -bool nd_logger_file(FILE *fp, ND_LOG_FORMAT format, struct log_field *fields, size_t fields_max);
242 +bool nd_logger_file(int fd, FILE *fp, netdata_mutex_t *mutex, ND_LOG_FORMAT format, struct log_field *fields, size_t fields_max);
243
244 // --------------------------------------------------------------------------------------------------------------------
245 // output to windows events log
src/libnetdata/log/nd_log-to-file.c
+40 -4
@@ -25,7 +25,7 @@ void nd_log_chown_log_files(uid_t uid, gid_t gid) {
25 }
26 }
27
28 -bool nd_logger_file(FILE *fp, ND_LOG_FORMAT format, struct log_field *fields, size_t fields_max) {
28 +bool nd_logger_file(int fd, FILE *fp, netdata_mutex_t *mutex, ND_LOG_FORMAT format, struct log_field *fields, size_t fields_max) {
29 BUFFER *wb = buffer_create(1024, NULL);
30
31 if(format == NDLF_JSON)
@@ -33,9 +33,45 @@ bool nd_logger_file(FILE *fp, ND_LOG_FORMAT format, struct log_field *fields, si
33 else
34 nd_logger_logfmt(wb, fields, fields_max);
35
36 - int r = fprintf(fp, "%s\n", buffer_tostring(wb));
37 - fflush(fp);
36 + buffer_strcat(wb, "\n");
37 +
38 + // Serialize writes with a Netdata-owned mutex and use write() on the raw fd.
39 + //
40 + // We avoid libc's stdio locking (flockfile/funlockfile) because spawn-server
41 + // children inherit FILE* state across fork(). In those post-fork children we
42 + // disable logger mutexes entirely, since they are single-threaded at that point.
43 + //
44 + // A netdata_mutex_t sleeps on contention rather than busy-waiting, so blocked
45 + // I/O (full pipe, slow disk) does not burn CPU in other logging threads.
46 + //
47 + // Logger-owned streams are configured unbuffered when opened, so the logger
48 + // can stay on raw fd writes here without taking stdio-internal locks.
49 +
50 + const char *buf = buffer_tostring(wb);
51 + size_t remaining = buffer_strlen(wb);
52 +
53 + if(mutex)
54 + netdata_mutex_lock(mutex);
55 +
56 + while(remaining > 0) {
57 + size_t chunk = remaining;
58 + if(chunk > (size_t)SSIZE_MAX)
59 + chunk = (size_t)SSIZE_MAX;
60 +
61 + ssize_t written = write(fd, buf, chunk);
62 + if(written > 0) {
63 + buf += written;
64 + remaining -= written;
65 + }
66 + else if(written == 0)
67 + break;
68 + else if(errno != EINTR)
69 + break;
70 + }
71 +
72 + if(mutex)
73 + netdata_mutex_unlock(mutex);
74
75 buffer_free(wb);
40 - return r > 0;
76 + return remaining == 0;
77 }
src/libnetdata/log/nd_log.c
+68 -55
@@ -25,12 +25,15 @@ ALWAYS_INLINE void errno_clear(void) {
25 // --------------------------------------------------------------------------------------------------------------------
26 // logger router
27
28 -static ND_LOG_METHOD nd_logger_select_output(ND_LOG_SOURCES source, FILE **fpp, SPINLOCK **spinlock) {
29 - *spinlock = NULL;
28 +static ND_LOG_METHOD nd_logger_select_output(ND_LOG_SOURCES source, FILE **fpp, int *fdp, netdata_mutex_t **mutexp) {
29 + bool mutexes_initialized = __atomic_load_n(&nd_log.mutexes_initialized, __ATOMIC_ACQUIRE);
30 +
31 + *fdp = -1;
32 + *mutexp = NULL;
33
34 if(source >= _NDLS_MAX)
35 source = NDLS_DAEMON;
33 -
36 +
37 ND_LOG_METHOD output = nd_log.sources[source].method;
38
39 switch(output) {
@@ -38,11 +41,11 @@ static ND_LOG_METHOD nd_logger_select_output(ND_LOG_SOURCES source, FILE **fpp,
41 if(unlikely(!nd_log.journal_direct.initialized && !nd_log.journal.initialized)) {
42 output = NDLM_FILE;
43 *fpp = stderr;
41 - *spinlock = &nd_log.std_error.spinlock;
44 + *fdp = STDERR_FILENO;
45 + *mutexp = &nd_log.std_error.mutex;
46 }
47 else {
48 *fpp = NULL;
45 - *spinlock = NULL;
49 }
50 break;
51
@@ -56,11 +59,11 @@ static ND_LOG_METHOD nd_logger_select_output(ND_LOG_SOURCES source, FILE **fpp,
59 if(unlikely(!nd_log.eventlog.initialized)) {
60 output = NDLM_FILE;
61 *fpp = stderr;
59 - *spinlock = &nd_log.std_error.spinlock;
62 + *fdp = STDERR_FILENO;
63 + *mutexp = &nd_log.std_error.mutex;
64 }
65 else {
66 *fpp = NULL;
63 - *spinlock = NULL;
67 }
68 break;
69 #endif
@@ -68,11 +71,11 @@ static ND_LOG_METHOD nd_logger_select_output(ND_LOG_SOURCES source, FILE **fpp,
71 case NDLM_SYSLOG:
72 if(unlikely(!nd_log.syslog.initialized)) {
73 output = NDLM_FILE;
71 - *spinlock = &nd_log.std_error.spinlock;
74 *fpp = stderr;
75 + *fdp = STDERR_FILENO;
76 + *mutexp = &nd_log.std_error.mutex;
77 }
78 else {
75 - *spinlock = NULL;
79 *fpp = NULL;
80 }
81 break;
@@ -80,18 +83,21 @@ static ND_LOG_METHOD nd_logger_select_output(ND_LOG_SOURCES source, FILE **fpp,
83 case NDLM_FILE:
84 if(!nd_log.sources[source].fp) {
85 *fpp = stderr;
83 - *spinlock = &nd_log.std_error.spinlock;
86 + *fdp = STDERR_FILENO;
87 + *mutexp = &nd_log.std_error.mutex;
88 }
89 else {
90 *fpp = nd_log.sources[source].fp;
87 - *spinlock = &nd_log.sources[source].spinlock;
91 + *fdp = nd_log.sources[source].fd;
92 + *mutexp = &nd_log.sources[source].mutex;
93 }
94 break;
95
96 case NDLM_STDOUT:
97 output = NDLM_FILE;
98 *fpp = stdout;
94 - *spinlock = &nd_log.std_output.spinlock;
99 + *fdp = STDOUT_FILENO;
100 + *mutexp = &nd_log.std_output.mutex;
101 break;
102
103 default:
@@ -99,20 +105,41 @@ static ND_LOG_METHOD nd_logger_select_output(ND_LOG_SOURCES source, FILE **fpp,
105 case NDLM_STDERR:
106 output = NDLM_FILE;
107 *fpp = stderr;
102 - *spinlock = &nd_log.std_error.spinlock;
108 + *fdp = STDERR_FILENO;
109 + *mutexp = &nd_log.std_error.mutex;
110 break;
111
112 case NDLM_DISABLED:
113 case NDLM_DEVNULL:
114 output = NDLM_DISABLED;
115 *fpp = NULL;
109 - *spinlock = NULL;
116 break;
117 }
118
119 + if(output == NDLM_FILE && *fpp && *fdp < 0) {
120 + *fpp = stderr;
121 + *fdp = STDERR_FILENO;
122 + *mutexp = &nd_log.std_error.mutex;
123 + }
124 +
125 + // Constructor-time logging can happen before the explicit startup init
126 + // path runs. Until then, bypass write serialization and log unlocked.
127 + if(nd_log.single_threaded_child || !mutexes_initialized)
128 + *mutexp = NULL;
129 +
130 return output;
131 }
132
133 +static inline netdata_mutex_t *nd_logger_stderr_mutex(void) {
134 + if(nd_log.single_threaded_child)
135 + return NULL;
136 +
137 + if(!__atomic_load_n(&nd_log.mutexes_initialized, __ATOMIC_ACQUIRE))
138 + return NULL;
139 +
140 + return &nd_log.std_error.mutex;
141 +}
142 +
143 // --------------------------------------------------------------------------------------------------------------------
144
145 static __thread bool nd_log_fatal_event = false;
@@ -149,30 +176,27 @@ void nd_log_register_fatal_final_cb(fatal_event_t cb) {
176 // --------------------------------------------------------------------------------------------------------------------
177 // high level logger
178
152 -static void nd_logger_log_fields(SPINLOCK *spinlock, FILE *fp, bool limit, ND_LOG_FIELD_PRIORITY priority,
179 +// Write serialization uses a netdata_mutex_t (sleeping mutex) per output
180 +// destination, passed through from nd_logger_select_output(). Post-fork nofork
181 +// spawn-server children stay single-threaded in-tree, so they bypass logger
182 +// locking instead of trying to reuse inherited lock state.
183 +static void nd_logger_log_fields(FILE *fp, int fd, netdata_mutex_t *mutex, bool limit,
184 + ND_LOG_FIELD_PRIORITY priority,
185 ND_LOG_METHOD output, struct nd_log_source *source,
186 struct log_field *fields, size_t fields_max) {
187 nd_log_fatal_hook(fields, fields_max);
188
157 - if(spinlock)
158 - spinlock_lock(spinlock);
159 -
160 - // check the limits
189 + // check the limits (uses its own source->limits.spinlock internally)
190 if(limit && nd_log_limit_reached(source))
162 - goto cleanup;
191 + return;
192
193 if(output == NDLM_JOURNAL) {
194 if(!nd_logger_journal_direct(fields, fields_max) && !nd_logger_journal_libsystemd(fields, fields_max)) {
195 // we can't log to journal, let's log to stderr
167 - if(spinlock)
168 - spinlock_unlock(spinlock);
169 -
196 output = NDLM_FILE;
171 - spinlock = &nd_log.std_error.spinlock;
197 fp = stderr;
173 -
174 - if(spinlock)
175 - spinlock_lock(spinlock);
198 + fd = STDERR_FILENO;
199 + mutex = nd_logger_stderr_mutex();
200 }
201 }
202
@@ -181,15 +205,10 @@ static void nd_logger_log_fields(SPINLOCK *spinlock, FILE *fp, bool limit, ND_LO
205 if(output == NDLM_ETW) {
206 if(!nd_logger_etw(source, fields, fields_max)) {
207 // we can't log to windows events, let's log to stderr
184 - if(spinlock)
185 - spinlock_unlock(spinlock);
186 -
208 output = NDLM_FILE;
188 - spinlock = &nd_log.std_error.spinlock;
209 fp = stderr;
190 -
191 - if(spinlock)
192 - spinlock_lock(spinlock);
210 + fd = STDERR_FILENO;
211 + mutex = nd_logger_stderr_mutex();
212 }
213 }
214 #endif
@@ -197,15 +216,10 @@ static void nd_logger_log_fields(SPINLOCK *spinlock, FILE *fp, bool limit, ND_LO
216 if(output == NDLM_WEL) {
217 if(!nd_logger_wel(source, fields, fields_max)) {
218 // we can't log to windows events, let's log to stderr
200 - if(spinlock)
201 - spinlock_unlock(spinlock);
202 -
219 output = NDLM_FILE;
204 - spinlock = &nd_log.std_error.spinlock;
220 fp = stderr;
206 -
207 - if(spinlock)
208 - spinlock_lock(spinlock);
221 + fd = STDERR_FILENO;
222 + mutex = nd_logger_stderr_mutex();
223 }
224 }
225 #endif
@@ -215,12 +229,7 @@ static void nd_logger_log_fields(SPINLOCK *spinlock, FILE *fp, bool limit, ND_LO
229 nd_logger_syslog(priority, source->format, fields, fields_max);
230
231 if(output == NDLM_FILE)
218 - nd_logger_file(fp, source->format, fields, fields_max);
219 -
220 -
221 -cleanup:
222 - if(spinlock)
223 - spinlock_unlock(spinlock);
232 + nd_logger_file(fd, fp, mutex, source->format, fields, fields_max);
233 }
234
235 static void nd_logger_unset_all_thread_fields(void) {
@@ -258,9 +267,10 @@ static void nd_logger(const char *file, const char *function, const unsigned lon
267 ND_LOG_SOURCES source, ND_LOG_FIELD_PRIORITY priority, bool limit,
268 int saved_errno, size_t saved_winerror __maybe_unused, const char *fmt, va_list ap) {
269
261 - SPINLOCK *spinlock;
270 FILE *fp;
263 - ND_LOG_METHOD output = nd_logger_select_output(source, &fp, &spinlock);
271 + int fd;
272 + netdata_mutex_t *mutex;
273 + ND_LOG_METHOD output = nd_logger_select_output(source, &fp, &fd, &mutex);
274 if(!IS_FINAL_LOG_METHOD(output))
275 return;
276
@@ -293,7 +303,7 @@ static void nd_logger(const char *file, const char *function, const unsigned lon
303
304 if(src != source && src < _NDLS_MAX) {
305 source = src;
296 - output = nd_logger_select_output(source, &fp, &spinlock);
306 + output = nd_logger_select_output(source, &fp, &fd, &mutex);
307 if(output != NDLM_FILE && output != NDLM_JOURNAL && output != NDLM_SYSLOG)
308 return;
309 }
@@ -340,7 +350,7 @@ static void nd_logger(const char *file, const char *function, const unsigned lon
350 thread_log_fields[NDF_MESSAGE].entry = ND_LOG_FIELD_TXT(NDF_MESSAGE, buffer_tostring(wb));
351 }
352
343 - nd_logger_log_fields(spinlock, fp, limit, priority, output, &nd_log.sources[source],
353 + nd_logger_log_fields(fp, fd, mutex, limit, priority, output, &nd_log.sources[source],
354 thread_log_fields, THREAD_FIELDS_MAX);
355
356 if(nd_log.sources[source].pending_msg && spinlock_trylock(&nd_log.sources[source].limits.spinlock)) {
@@ -388,7 +398,7 @@ static void nd_logger(const char *file, const char *function, const unsigned lon
398 spinlock_unlock(&nd_log.sources[source].limits.spinlock);
399
400 if(pending_msg)
391 - nd_logger_log_fields(spinlock, fp, false, priority, output, &nd_log.sources[source],
401 + nd_logger_log_fields(fp, fd, mutex, false, priority, output, &nd_log.sources[source],
402 thread_log_fields, THREAD_FIELDS_MAX);
403
404 freez((void *)pending_msg);
@@ -450,16 +460,19 @@ void netdata_logger_with_limit(ERROR_LIMIT *erl, ND_LOG_SOURCES source, ND_LOG_F
460 if(erl->sleep_ut)
461 sleep_usec(erl->sleep_ut);
462
453 - spinlock_lock(&erl->spinlock);
463 + if(!nd_log.single_threaded_child)
464 + spinlock_lock(&erl->spinlock);
465
466 erl->count++;
467 time_t now = now_boottime_sec();
468 if(now - erl->last_logged < erl->log_every) {
458 - spinlock_unlock(&erl->spinlock);
469 + if(!nd_log.single_threaded_child)
470 + spinlock_unlock(&erl->spinlock);
471 return;
472 }
473
462 - spinlock_unlock(&erl->spinlock);
474 + if(!nd_log.single_threaded_child)
475 + spinlock_unlock(&erl->spinlock);
476
477 va_list args;
478 va_start(args, fmt);
src/libnetdata/log/nd_log.h
+1
@@ -19,6 +19,7 @@ void errno_clear(void);
19 void nd_log_set_user_settings(ND_LOG_SOURCES source, const char *setting);
20 void nd_log_set_facility(const char *facility);
21 void nd_log_set_priority_level(const char *setting);
22 +void nd_log_initialize_mutexes(void);
23 void nd_log_initialize(void);
24 void nd_log_reopen_log_files(bool log);
25 void chown_open_file(int fd, uid_t uid, gid_t gid);
src/libnetdata/log/nd_log_limit.c
+4 -8
@@ -5,26 +5,22 @@
5 void nd_log_limits_reset(void) {
6 usec_t now_ut = now_monotonic_usec();
7
8 - spinlock_lock(&nd_log.std_output.spinlock);
9 - spinlock_lock(&nd_log.std_error.spinlock);
10 -
8 for(size_t i = 0; i < _NDLS_MAX ;i++) {
12 - spinlock_lock(&nd_log.sources[i].spinlock);
9 + spinlock_lock(&nd_log.sources[i].limits.spinlock);
10 nd_log.sources[i].limits.prevented = 0;
11 nd_log.sources[i].limits.counter = 0;
12 nd_log.sources[i].limits.started_monotonic_ut = now_ut;
13 nd_log.sources[i].limits.logs_per_period = nd_log.sources[i].limits.logs_per_period_backup;
17 - spinlock_unlock(&nd_log.sources[i].spinlock);
14 + spinlock_unlock(&nd_log.sources[i].limits.spinlock);
15 }
19 -
20 - spinlock_unlock(&nd_log.std_output.spinlock);
21 - spinlock_unlock(&nd_log.std_error.spinlock);
16 }
17
18 void nd_log_limits_unlimited(void) {
19 nd_log_limits_reset();
20 for(size_t i = 0; i < _NDLS_MAX ;i++) {
21 + spinlock_lock(&nd_log.sources[i].limits.spinlock);
22 nd_log.sources[i].limits.logs_per_period = 0;
23 + spinlock_unlock(&nd_log.sources[i].limits.spinlock);
24 }
25 }
26