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) {
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
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
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;
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:
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;
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
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
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
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) {
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
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
}
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)) {
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);
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);