1
// SPDX-License-Identifier: GPL-3.0-or-later
2
3
-#include <daemon/main.h>
3
+#define SD_JOURNAL_SUPPRESS_LOCATION
4
+
5
#include "../libnetdata.h"
6
+#include <daemon/main.h>
7
+
8
+#ifdef __FreeBSD__
9
+#include <sys/endian.h>
10
+#endif
11
+
12
+#ifdef __APPLE__
13
+#include <machine/endian.h>
14
+#endif
15
16
#ifdef HAVE_BACKTRACE
17
#include <execinfo.h>
18
#endif
19
10
-int web_server_is_multithreaded = 1;
20
+#ifdef HAVE_SYSTEMD
21
+#include <systemd/sd-journal.h>
22
+#endif
23
+
24
+#include <syslog.h>
25
26
const char *program_name = "";
27
+
28
uint64_t debug_flags = 0;
29
15
-int access_log_syslog = 0;
16
-int error_log_syslog = 0;
17
-int collector_log_syslog = 0;
18
-int output_log_syslog = 0; // debug log
19
-int health_log_syslog = 0;
30
+#ifdef ENABLE_ACLK
31
+int aclklog_enabled = 0;
32
+#endif
33
+
34
+// ----------------------------------------------------------------------------
35
+
36
+struct nd_log_source;
37
+static bool nd_log_limit_reached(struct nd_log_source *source);
38
21
-int stdaccess_fd = -1;
22
-FILE *stdaccess = NULL;
39
+// ----------------------------------------------------------------------------
40
+// logging method
41
+
42
+typedef enum __attribute__((__packed__)) {
43
+ NDLO_DISABLED = 0,
44
+ NDLO_DEVNULL,
45
+ NDLO_DEFAULT,
46
+ NDLO_JOURNAL,
47
+ NDLO_SYSLOG,
48
+ NDLO_STDOUT,
49
+ NDLO_STDERR,
50
+ NDLO_FILE,
51
+} ND_LOG_OUTPUT;
52
+
53
+
54
+static struct {
55
+ ND_LOG_OUTPUT output;
56
+ const char *name;
57
+} nd_log_outputs[] = {
58
+ { .output = NDLO_DISABLED, .name = "none" },
59
+ { .output = NDLO_DEVNULL, .name = "/dev/null" },
60
+ { .output = NDLO_DEFAULT, .name = "default" },
61
+ { .output = NDLO_JOURNAL, .name = "journal" },
62
+ { .output = NDLO_SYSLOG, .name = "syslog" },
63
+ { .output = NDLO_STDOUT, .name = "stdout" },
64
+ { .output = NDLO_STDERR, .name = "stderr" },
65
+ { .output = NDLO_FILE, .name = "file" },
66
+};
67
+
68
+static ND_LOG_OUTPUT nd_log_output2id(const char *output) {
69
+ if(!output || !*output)
70
+ return NDLO_DEFAULT;
71
+
72
+ size_t entries = sizeof(nd_log_outputs) / sizeof(nd_log_outputs[0]);
73
+ for(size_t i = 0; i < entries ;i++) {
74
+ if(strcmp(nd_log_outputs[i].name, output) == 0)
75
+ return nd_log_outputs[i].output;
76
+ }
77
24
-int stdhealth_fd = -1;
25
-FILE *stdhealth = NULL;
78
+ return NDLO_FILE;
79
+}
80
27
-int stdcollector_fd = -1;
28
-FILE *stderror = NULL;
81
+static const char *nd_log_id2output(ND_LOG_OUTPUT output) {
82
+ size_t entries = sizeof(nd_log_outputs) / sizeof(nd_log_outputs[0]);
83
+ for(size_t i = 0; i < entries ;i++) {
84
+ if(output == nd_log_outputs[i].output)
85
+ return nd_log_outputs[i].name;
86
+ }
87
30
-const char *stdaccess_filename = NULL;
31
-const char *stderr_filename = NULL;
32
-const char *stdout_filename = NULL;
33
-const char *facility_log = NULL;
34
-const char *stdhealth_filename = NULL;
35
-const char *stdcollector_filename = NULL;
88
+ return "unknown";
89
+}
90
37
-netdata_log_level_t global_log_severity_level = NETDATA_LOG_LEVEL_INFO;
91
+// ----------------------------------------------------------------------------
92
+// workaround strerror_r()
93
39
-#ifdef ENABLE_ACLK
40
-const char *aclklog_filename = NULL;
41
-int aclklog_fd = -1;
42
-FILE *aclklog = NULL;
43
-int aclklog_syslog = 1;
44
-int aclklog_enabled = 0;
94
+#if defined(STRERROR_R_CHAR_P)
95
+// GLIBC version of strerror_r
96
+static const char *strerror_result(const char *a, const char *b) { (void)b; return a; }
97
+#elif defined(HAVE_STRERROR_R)
98
+// POSIX version of strerror_r
99
+static const char *strerror_result(int a, const char *b) { (void)a; return b; }
100
+#elif defined(HAVE_C__GENERIC)
101
+
102
+// what a trick!
103
+// http://stackoverflow.com/questions/479207/function-overloading-in-c
104
+static const char *strerror_result_int(int a, const char *b) { (void)a; return b; }
105
+static const char *strerror_result_string(const char *a, const char *b) { (void)b; return a; }
106
+
107
+#define strerror_result(a, b) _Generic((a), \
108
+ int: strerror_result_int, \
109
+ char *: strerror_result_string \
110
+ )(a, b)
111
+
112
+#else
113
+#error "cannot detect the format of function strerror_r()"
114
#endif
115
116
+static const char *errno2str(int errnum, char *buf, size_t size) {
117
+ return strerror_result(strerror_r(errnum, buf, size), buf);
118
+}
119
+
120
// ----------------------------------------------------------------------------
48
-// Log facility(https://tools.ietf.org/html/rfc5424)
121
+// facilities
122
//
50
-// The facilities accepted in the Netdata are in according with the following
51
-// header files for their respective operating system:
52
-// sys/syslog.h (Linux )
123
+// sys/syslog.h (Linux)
124
// sys/sys/syslog.h (FreeBSD)
125
// bsd/sys/syslog.h (darwin-xnu)
126
56
-#define LOG_AUTH_KEY "auth"
57
-#define LOG_AUTHPRIV_KEY "authpriv"
58
-#ifdef __FreeBSD__
59
-# define LOG_CONSOLE_KEY "console"
60
-#endif
61
-#define LOG_CRON_KEY "cron"
62
-#define LOG_DAEMON_KEY "daemon"
63
-#define LOG_FTP_KEY "ftp"
64
-#ifdef __APPLE__
65
-# define LOG_INSTALL_KEY "install"
66
-#endif
67
-#define LOG_KERN_KEY "kern"
68
-#define LOG_LPR_KEY "lpr"
69
-#define LOG_MAIL_KEY "mail"
70
-//#define LOG_INTERNAL_MARK_KEY "mark"
71
-#ifdef __APPLE__
72
-# define LOG_NETINFO_KEY "netinfo"
73
-# define LOG_RAS_KEY "ras"
74
-# define LOG_REMOTEAUTH_KEY "remoteauth"
75
-#endif
76
-#define LOG_NEWS_KEY "news"
77
-#ifdef __FreeBSD__
78
-# define LOG_NTP_KEY "ntp"
79
-#endif
80
-#define LOG_SECURITY_KEY "security"
81
-#define LOG_SYSLOG_KEY "syslog"
82
-#define LOG_USER_KEY "user"
83
-#define LOG_UUCP_KEY "uucp"
84
-#ifdef __APPLE__
85
-# define LOG_LAUNCHD_KEY "launchd"
86
-#endif
87
-#define LOG_LOCAL0_KEY "local0"
88
-#define LOG_LOCAL1_KEY "local1"
89
-#define LOG_LOCAL2_KEY "local2"
90
-#define LOG_LOCAL3_KEY "local3"
91
-#define LOG_LOCAL4_KEY "local4"
92
-#define LOG_LOCAL5_KEY "local5"
93
-#define LOG_LOCAL6_KEY "local6"
94
-#define LOG_LOCAL7_KEY "local7"
95
-
96
-static int log_facility_id(const char *facility_name)
97
-{
98
- static int
99
- hash_auth = 0,
100
- hash_authpriv = 0,
101
-#ifdef __FreeBSD__
102
- hash_console = 0,
103
-#endif
104
- hash_cron = 0,
105
- hash_daemon = 0,
106
- hash_ftp = 0,
107
-#ifdef __APPLE__
108
- hash_install = 0,
109
-#endif
110
- hash_kern = 0,
111
- hash_lpr = 0,
112
- hash_mail = 0,
113
-// hash_mark = 0,
114
-#ifdef __APPLE__
115
- hash_netinfo = 0,
116
- hash_ras = 0,
117
- hash_remoteauth = 0,
118
-#endif
119
- hash_news = 0,
120
-#ifdef __FreeBSD__
121
- hash_ntp = 0,
122
-#endif
123
- hash_security = 0,
124
- hash_syslog = 0,
125
- hash_user = 0,
126
- hash_uucp = 0,
127
-#ifdef __APPLE__
128
- hash_launchd = 0,
129
-#endif
130
- hash_local0 = 0,
131
- hash_local1 = 0,
132
- hash_local2 = 0,
133
- hash_local3 = 0,
134
- hash_local4 = 0,
135
- hash_local5 = 0,
136
- hash_local6 = 0,
137
- hash_local7 = 0;
138
-
139
- if(unlikely(!hash_auth))
140
- {
141
- hash_auth = simple_hash(LOG_AUTH_KEY);
142
- hash_authpriv = simple_hash(LOG_AUTHPRIV_KEY);
143
-#ifdef __FreeBSD__
144
- hash_console = simple_hash(LOG_CONSOLE_KEY);
145
-#endif
146
- hash_cron = simple_hash(LOG_CRON_KEY);
147
- hash_daemon = simple_hash(LOG_DAEMON_KEY);
148
- hash_ftp = simple_hash(LOG_FTP_KEY);
149
-#ifdef __APPLE__
150
- hash_install = simple_hash(LOG_INSTALL_KEY);
151
-#endif
152
- hash_kern = simple_hash(LOG_KERN_KEY);
153
- hash_lpr = simple_hash(LOG_LPR_KEY);
154
- hash_mail = simple_hash(LOG_MAIL_KEY);
155
-// hash_mark = simple_uhash();
156
-#ifdef __APPLE__
157
- hash_netinfo = simple_hash(LOG_NETINFO_KEY);
158
- hash_ras = simple_hash(LOG_RAS_KEY);
159
- hash_remoteauth = simple_hash(LOG_REMOTEAUTH_KEY);
160
-#endif
161
- hash_news = simple_hash(LOG_NEWS_KEY);
127
+static struct {
128
+ int facility;
129
+ const char *name;
130
+} nd_log_facilities[] = {
131
+ { LOG_AUTH, "auth" },
132
+ { LOG_AUTHPRIV, "authpriv" },
133
+ { LOG_CRON, "cron" },
134
+ { LOG_DAEMON, "daemon" },
135
+ { LOG_FTP, "ftp" },
136
+ { LOG_KERN, "kern" },
137
+ { LOG_LPR, "lpr" },
138
+ { LOG_MAIL, "mail" },
139
+ { LOG_NEWS, "news" },
140
+ { LOG_SYSLOG, "syslog" },
141
+ { LOG_USER, "user" },
142
+ { LOG_UUCP, "uucp" },
143
+ { LOG_LOCAL0, "local0" },
144
+ { LOG_LOCAL1, "local1" },
145
+ { LOG_LOCAL2, "local2" },
146
+ { LOG_LOCAL3, "local3" },
147
+ { LOG_LOCAL4, "local4" },
148
+ { LOG_LOCAL5, "local5" },
149
+ { LOG_LOCAL6, "local6" },
150
+ { LOG_LOCAL7, "local7" },
151
+
152
#ifdef __FreeBSD__
163
- hash_ntp = simple_hash(LOG_NTP_KEY);
153
+ { LOG_CONSOLE, "console" },
154
+ { LOG_NTP, "ntp" },
155
+
156
+ // FreeBSD does not consider 'security' as deprecated.
157
+ { LOG_SECURITY, "security" },
158
+#else
159
+ // For all other O/S 'security' is mapped to 'auth'.
160
+ { LOG_AUTH, "security" },
161
#endif
165
- hash_security = simple_hash(LOG_SECURITY_KEY);
166
- hash_syslog = simple_hash(LOG_SYSLOG_KEY);
167
- hash_user = simple_hash(LOG_USER_KEY);
168
- hash_uucp = simple_hash(LOG_UUCP_KEY);
162
+
163
#ifdef __APPLE__
170
- hash_launchd = simple_hash(LOG_LAUNCHD_KEY);
164
+ { LOG_INSTALL, "install" },
165
+ { LOG_NETINFO, "netinfo" },
166
+ { LOG_RAS, "ras" },
167
+ { LOG_REMOTEAUTH, "remoteauth" },
168
+ { LOG_LAUNCHD, "launchd" },
169
+
170
#endif
172
- hash_local0 = simple_hash(LOG_LOCAL0_KEY);
173
- hash_local1 = simple_hash(LOG_LOCAL1_KEY);
174
- hash_local2 = simple_hash(LOG_LOCAL2_KEY);
175
- hash_local3 = simple_hash(LOG_LOCAL3_KEY);
176
- hash_local4 = simple_hash(LOG_LOCAL4_KEY);
177
- hash_local5 = simple_hash(LOG_LOCAL5_KEY);
178
- hash_local6 = simple_hash(LOG_LOCAL6_KEY);
179
- hash_local7 = simple_hash(LOG_LOCAL7_KEY);
171
+};
172
+
173
+static int nd_log_facility2id(const char *facility) {
174
+ size_t entries = sizeof(nd_log_facilities) / sizeof(nd_log_facilities[0]);
175
+ for(size_t i = 0; i < entries ;i++) {
176
+ if(strcmp(nd_log_facilities[i].name, facility) == 0)
177
+ return nd_log_facilities[i].facility;
178
}
179
182
- int hash = simple_hash(facility_name);
183
- if ( hash == hash_auth )
184
- {
185
- return LOG_AUTH;
180
+ return LOG_DAEMON;
181
+}
182
+
183
+static const char *nd_log_id2facility(int facility) {
184
+ size_t entries = sizeof(nd_log_facilities) / sizeof(nd_log_facilities[0]);
185
+ for(size_t i = 0; i < entries ;i++) {
186
+ if(nd_log_facilities[i].facility == facility)
187
+ return nd_log_facilities[i].name;
188
}
187
- else if ( hash == hash_authpriv )
188
- {
189
- return LOG_AUTHPRIV;
189
+
190
+ return "daemon";
191
+}
192
+
193
+// ----------------------------------------------------------------------------
194
+// priorities
195
+
196
+static struct {
197
+ ND_LOG_FIELD_PRIORITY priority;
198
+ const char *name;
199
+} nd_log_priorities[] = {
200
+ { .priority = NDLP_EMERG, .name = "emergency" },
201
+ { .priority = NDLP_EMERG, .name = "emerg" },
202
+ { .priority = NDLP_ALERT, .name = "alert" },
203
+ { .priority = NDLP_CRIT, .name = "critical" },
204
+ { .priority = NDLP_CRIT, .name = "crit" },
205
+ { .priority = NDLP_ERR, .name = "error" },
206
+ { .priority = NDLP_ERR, .name = "err" },
207
+ { .priority = NDLP_WARNING, .name = "warning" },
208
+ { .priority = NDLP_WARNING, .name = "warn" },
209
+ { .priority = NDLP_NOTICE, .name = "notice" },
210
+ { .priority = NDLP_INFO, .name = NDLP_INFO_STR },
211
+ { .priority = NDLP_DEBUG, .name = "debug" },
212
+};
213
+
214
+int nd_log_priority2id(const char *priority) {
215
+ size_t entries = sizeof(nd_log_priorities) / sizeof(nd_log_priorities[0]);
216
+ for(size_t i = 0; i < entries ;i++) {
217
+ if(strcmp(nd_log_priorities[i].name, priority) == 0)
218
+ return nd_log_priorities[i].priority;
219
}
191
-#ifdef __FreeBSD__
192
- else if ( hash == hash_console )
193
- {
194
- return LOG_CONSOLE;
220
+
221
+ return NDLP_INFO;
222
+}
223
+
224
+static const char *nd_log_id2priority(ND_LOG_FIELD_PRIORITY priority) {
225
+ size_t entries = sizeof(nd_log_priorities) / sizeof(nd_log_priorities[0]);
226
+ for(size_t i = 0; i < entries ;i++) {
227
+ if(priority == nd_log_priorities[i].priority)
228
+ return nd_log_priorities[i].name;
229
}
196
-#endif
197
- else if ( hash == hash_cron )
198
- {
199
- return LOG_CRON;
230
+
231
+ return NDLP_INFO_STR;
232
+}
233
+
234
+// ----------------------------------------------------------------------------
235
+// log sources
236
+
237
+const char *log_sources_str[] = {
238
+ [NDLS_UNSET] = "UNSET",
239
+ [NDLS_ACCESS] = "access",
240
+ [NDLS_ACLK] = "aclk",
241
+ [NDLS_COLLECTORS] = "collector",
242
+ [NDLS_DAEMON] = "daemon",
243
+ [NDLS_HEALTH] = "health",
244
+ [NDLS_DEBUG] = "debug",
245
+};
246
+
247
+static const char *nd_log_source2str(ND_LOG_SOURCES source) {
248
+ size_t entries = sizeof(log_sources_str) / sizeof(log_sources_str[0]);
249
+ if(source < entries)
250
+ return log_sources_str[source];
251
+
252
+ return "UNKNOWN";
253
+}
254
+
255
+// ----------------------------------------------------------------------------
256
+// log output formats
257
+
258
+typedef enum __attribute__((__packed__)) {
259
+ NDLOF_JOURNAL,
260
+ NDLOF_LOGFMT,
261
+ NDLOF_JSON,
262
+} ND_LOG_OUTPUT_FORMAT;
263
+
264
+static struct {
265
+ ND_LOG_OUTPUT_FORMAT format;
266
+ const char *name;
267
+} nd_log_formats[] = {
268
+ { .format = NDLOF_JOURNAL, .name = "journal" },
269
+ { .format = NDLOF_LOGFMT, .name = "logfmt" },
270
+ { .format = NDLOF_JSON, .name = "json" },
271
+};
272
+
273
+static ND_LOG_OUTPUT_FORMAT nd_log_format2id(const char *format) {
274
+ if(!format || !*format)
275
+ return NDLOF_LOGFMT;
276
+
277
+ size_t entries = sizeof(nd_log_formats) / sizeof(nd_log_formats[0]);
278
+ for(size_t i = 0; i < entries ;i++) {
279
+ if(strcmp(nd_log_formats[i].name, format) == 0)
280
+ return nd_log_formats[i].format;
281
}
201
- else if ( hash == hash_daemon )
202
- {
203
- return LOG_DAEMON;
282
+
283
+ return NDLOF_LOGFMT;
284
+}
285
+
286
+static const char *nd_log_id2format(ND_LOG_OUTPUT_FORMAT format) {
287
+ size_t entries = sizeof(nd_log_formats) / sizeof(nd_log_formats[0]);
288
+ for(size_t i = 0; i < entries ;i++) {
289
+ if(format == nd_log_formats[i].format)
290
+ return nd_log_formats[i].name;
291
}
205
- else if ( hash == hash_ftp )
206
- {
207
- return LOG_FTP;
292
+
293
+ return "logfmt";
294
+}
295
+
296
+// ----------------------------------------------------------------------------
297
+// format dates
298
+
299
+void log_date(char *buffer, size_t len, time_t now) {
300
+ if(unlikely(!buffer || !len))
301
+ return;
302
+
303
+ time_t t = now;
304
+ struct tm *tmp, tmbuf;
305
+
306
+ tmp = localtime_r(&t, &tmbuf);
307
+
308
+ if (unlikely(!tmp)) {
309
+ buffer[0] = '\0';
310
+ return;
311
}
209
-#ifdef __APPLE__
210
- else if ( hash == hash_install )
211
- {
212
- return LOG_INSTALL;
312
+
313
+ if (unlikely(strftime(buffer, len, "%Y-%m-%d %H:%M:%S", tmp) == 0))
314
+ buffer[0] = '\0';
315
+
316
+ buffer[len - 1] = '\0';
317
+}
318
+
319
+// ----------------------------------------------------------------------------
320
+
321
+struct nd_log_limit {
322
+ usec_t started_monotonic_ut;
323
+ uint32_t counter;
324
+ uint32_t prevented;
325
+
326
+ uint32_t throttle_period;
327
+ uint32_t logs_per_period;
328
+ uint32_t logs_per_period_backup;
329
+};
330
+
331
+#define ND_LOG_LIMITS_DEFAULT (struct nd_log_limit){ .logs_per_period = ND_LOG_DEFAULT_THROTTLE_LOGS, .logs_per_period_backup = ND_LOG_DEFAULT_THROTTLE_LOGS, .throttle_period = ND_LOG_DEFAULT_THROTTLE_PERIOD, }
332
+#define ND_LOG_LIMITS_UNLIMITED (struct nd_log_limit){ .logs_per_period = 0, .logs_per_period_backup = 0, .throttle_period = 0, }
333
+
334
+struct nd_log_source {
335
+ SPINLOCK spinlock;
336
+ ND_LOG_OUTPUT method;
337
+ ND_LOG_OUTPUT_FORMAT format;
338
+ const char *filename;
339
+ int fd;
340
+ FILE *fp;
341
+
342
+ ND_LOG_FIELD_PRIORITY min_priority;
343
+ const char *pending_msg;
344
+ struct nd_log_limit limits;
345
+};
346
+
347
+static __thread ND_LOG_SOURCES overwrite_thread_source = 0;
348
+
349
+void nd_log_set_thread_source(ND_LOG_SOURCES source) {
350
+ overwrite_thread_source = source;
351
+}
352
+
353
+static struct {
354
+ uuid_t invocation_id;
355
+
356
+ ND_LOG_SOURCES overwrite_process_source;
357
+
358
+ struct nd_log_source sources[_NDLS_MAX];
359
+
360
+ struct {
361
+ bool initialized;
362
+ } journal;
363
+
364
+ struct {
365
+ bool initialized;
366
+ int fd;
367
+ } journal_direct;
368
+
369
+ struct {
370
+ bool initialized;
371
+ int facility;
372
+ } syslog;
373
+
374
+ struct {
375
+ SPINLOCK spinlock;
376
+ bool initialized;
377
+ } std_output;
378
+
379
+ struct {
380
+ SPINLOCK spinlock;
381
+ bool initialized;
382
+ } std_error;
383
+
384
+} nd_log = {
385
+ .overwrite_process_source = 0,
386
+ .journal = {
387
+ .initialized = false,
388
+ },
389
+ .journal_direct = {
390
+ .initialized = false,
391
+ .fd = -1,
392
+ },
393
+ .syslog = {
394
+ .initialized = false,
395
+ .facility = LOG_DAEMON,
396
+ },
397
+ .std_output = {
398
+ .spinlock = NETDATA_SPINLOCK_INITIALIZER,
399
+ .initialized = false,
400
+ },
401
+ .std_error = {
402
+ .spinlock = NETDATA_SPINLOCK_INITIALIZER,
403
+ .initialized = false,
404
+ },
405
+ .sources = {
406
+ [NDLS_UNSET] = {
407
+ .spinlock = NETDATA_SPINLOCK_INITIALIZER,
408
+ .method = NDLO_DISABLED,
409
+ .format = NDLOF_JOURNAL,
410
+ .filename = NULL,
411
+ .fd = -1,
412
+ .fp = NULL,
413
+ .min_priority = NDLP_EMERG,
414
+ .limits = ND_LOG_LIMITS_UNLIMITED,
415
+ },
416
+ [NDLS_ACCESS] = {
417
+ .spinlock = NETDATA_SPINLOCK_INITIALIZER,
418
+ .method = NDLO_DEFAULT,
419
+ .format = NDLOF_LOGFMT,
420
+ .filename = LOG_DIR "/access.log",
421
+ .fd = -1,
422
+ .fp = NULL,
423
+ .min_priority = NDLP_DEBUG,
424
+ .limits = ND_LOG_LIMITS_UNLIMITED,
425
+ },
426
+ [NDLS_ACLK] = {
427
+ .spinlock = NETDATA_SPINLOCK_INITIALIZER,
428
+ .method = NDLO_FILE,
429
+ .format = NDLOF_LOGFMT,
430
+ .filename = LOG_DIR "/aclk.log",
431
+ .fd = -1,
432
+ .fp = NULL,
433
+ .min_priority = NDLP_DEBUG,
434
+ .limits = ND_LOG_LIMITS_UNLIMITED,
435
+ },
436
+ [NDLS_COLLECTORS] = {
437
+ .spinlock = NETDATA_SPINLOCK_INITIALIZER,
438
+ .method = NDLO_DEFAULT,
439
+ .format = NDLOF_LOGFMT,
440
+ .filename = LOG_DIR "/collectors.log",
441
+ .fd = STDERR_FILENO,
442
+ .fp = NULL,
443
+ .min_priority = NDLP_INFO,
444
+ .limits = ND_LOG_LIMITS_DEFAULT,
445
+ },
446
+ [NDLS_DEBUG] = {
447
+ .spinlock = NETDATA_SPINLOCK_INITIALIZER,
448
+ .method = NDLO_DISABLED,
449
+ .format = NDLOF_LOGFMT,
450
+ .filename = LOG_DIR "/debug.log",
451
+ .fd = STDOUT_FILENO,
452
+ .fp = NULL,
453
+ .min_priority = NDLP_DEBUG,
454
+ .limits = ND_LOG_LIMITS_UNLIMITED,
455
+ },
456
+ [NDLS_DAEMON] = {
457
+ .spinlock = NETDATA_SPINLOCK_INITIALIZER,
458
+ .method = NDLO_DEFAULT,
459
+ .filename = LOG_DIR "/daemon.log",
460
+ .format = NDLOF_LOGFMT,
461
+ .fd = -1,
462
+ .fp = NULL,
463
+ .min_priority = NDLP_INFO,
464
+ .limits = ND_LOG_LIMITS_DEFAULT,
465
+ },
466
+ [NDLS_HEALTH] = {
467
+ .spinlock = NETDATA_SPINLOCK_INITIALIZER,
468
+ .method = NDLO_DEFAULT,
469
+ .format = NDLOF_LOGFMT,
470
+ .filename = LOG_DIR "/health.log",
471
+ .fd = -1,
472
+ .fp = NULL,
473
+ .min_priority = NDLP_DEBUG,
474
+ .limits = ND_LOG_LIMITS_UNLIMITED,
475
+ },
476
+ },
477
+};
478
+
479
+__attribute__((constructor)) void initialize_invocation_id(void) {
480
+ // check for a NETDATA_INVOCATION_ID
481
+ if(uuid_parse_flexi(getenv("NETDATA_INVOCATION_ID"), nd_log.invocation_id) != 0) {
482
+ // not found, check for systemd set INVOCATION_ID
483
+ if(uuid_parse_flexi(getenv("INVOCATION_ID"), nd_log.invocation_id) != 0) {
484
+ // not found, generate a new one
485
+ uuid_generate_random(nd_log.invocation_id);
486
+ }
487
}
214
-#endif
215
- else if ( hash == hash_kern )
216
- {
217
- return LOG_KERN;
488
+
489
+ char uuid[UUID_COMPACT_STR_LEN];
490
+ uuid_unparse_lower_compact(nd_log.invocation_id, uuid);
491
+ setenv("NETDATA_INVOCATION_ID", uuid, 1);
492
+}
493
+
494
+void nd_log_set_user_settings(ND_LOG_SOURCES source, const char *setting) {
495
+ char buf[FILENAME_MAX + 100];
496
+ if(setting && *setting)
497
+ strncpyz(buf, setting, sizeof(buf) - 1);
498
+ else
499
+ buf[0] = '\0';
500
+
501
+ struct nd_log_source *ls = &nd_log.sources[source];
502
+ char *output = strrchr(buf, '@');
503
+
504
+ if(!output)
505
+ // all of it is the output
506
+ output = buf;
507
+ else {
508
+ // we found an '@', the next char is the output
509
+ *output = '\0';
510
+ output++;
511
+
512
+ // parse the other params
513
+ char *remaining = buf;
514
+ while(remaining) {
515
+ char *value = strsep_skip_consecutive_separators(&remaining, ",");
516
+ if (!value || !*value) continue;
517
+
518
+ char *name = strsep_skip_consecutive_separators(&value, "=");
519
+ if (!name || !*name) continue;
520
+
521
+ if(strcmp(name, "logfmt") == 0)
522
+ ls->format = NDLOF_LOGFMT;
523
+ else if(strcmp(name, "json") == 0)
524
+ ls->format = NDLOF_JSON;
525
+ else if(strcmp(name, "journal") == 0)
526
+ ls->format = NDLOF_JOURNAL;
527
+ else if(strcmp(name, "level") == 0 && value && *value)
528
+ ls->min_priority = nd_log_priority2id(value);
529
+ else if(strcmp(name, "protection") == 0 && value && *value) {
530
+ if(strcmp(value, "off") == 0 || strcmp(value, "none") == 0) {
531
+ ls->limits = ND_LOG_LIMITS_UNLIMITED;
532
+ ls->limits.counter = 0;
533
+ ls->limits.prevented = 0;
534
+ }
535
+ else {
536
+ ls->limits = ND_LOG_LIMITS_DEFAULT;
537
+
538
+ char *slash = strchr(value, '/');
539
+ if(slash) {
540
+ *slash = '\0';
541
+ slash++;
542
+ ls->limits.logs_per_period = ls->limits.logs_per_period_backup = str2u(value);
543
+ ls->limits.throttle_period = str2u(slash);
544
+ }
545
+ else {
546
+ ls->limits.logs_per_period = ls->limits.logs_per_period_backup = str2u(value);
547
+ ls->limits.throttle_period = ND_LOG_DEFAULT_THROTTLE_PERIOD;
548
+ }
549
+ }
550
+ }
551
+ else
552
+ nd_log(NDLS_DAEMON, NDLP_ERR, "Error while parsing configuration of log source '%s'. "
553
+ "In config '%s', '%s' is not understood.",
554
+ nd_log_source2str(source), setting, name);
555
+ }
556
}
219
- else if ( hash == hash_lpr )
220
- {
221
- return LOG_LPR;
557
+
558
+ if(!output || !*output || strcmp(output, "none") == 0 || strcmp(output, "off") == 0) {
559
+ ls->method = NDLO_DISABLED;
560
+ ls->filename = "/dev/null";
561
}
223
- else if ( hash == hash_mail )
224
- {
225
- return LOG_MAIL;
562
+ else if(strcmp(output, "journal") == 0) {
563
+ ls->method = NDLO_JOURNAL;
564
+ ls->filename = NULL;
565
}
227
- /*
228
- else if ( hash == hash_mark )
229
- {
230
- //this is internal for all OS
231
- return INTERNAL_MARK;
566
+ else if(strcmp(output, "syslog") == 0) {
567
+ ls->method = NDLO_SYSLOG;
568
+ ls->filename = NULL;
569
}
233
- */
234
-#ifdef __APPLE__
235
- else if ( hash == hash_netinfo )
236
- {
237
- return LOG_NETINFO;
570
+ else if(strcmp(output, "/dev/null") == 0) {
571
+ ls->method = NDLO_DEVNULL;
572
+ ls->filename = "/dev/null";
573
}
239
- else if ( hash == hash_ras )
240
- {
241
- return LOG_RAS;
574
+ else if(strcmp(output, "system") == 0) {
575
+ if(ls->fd == STDERR_FILENO) {
576
+ ls->method = NDLO_STDERR;
577
+ ls->filename = NULL;
578
+ ls->fd = STDERR_FILENO;
579
+ }
580
+ else {
581
+ ls->method = NDLO_STDOUT;
582
+ ls->filename = NULL;
583
+ ls->fd = STDOUT_FILENO;
584
+ }
585
}
243
- else if ( hash == hash_remoteauth )
244
- {
245
- return LOG_REMOTEAUTH;
586
+ else if(strcmp(output, "stderr") == 0) {
587
+ ls->method = NDLO_STDERR;
588
+ ls->filename = NULL;
589
+ ls->fd = STDERR_FILENO;
590
}
247
-#endif
248
- else if ( hash == hash_news )
249
- {
250
- return LOG_NEWS;
591
+ else if(strcmp(output, "stdout") == 0) {
592
+ ls->method = NDLO_STDOUT;
593
+ ls->filename = NULL;
594
+ ls->fd = STDOUT_FILENO;
595
}
252
-#ifdef __FreeBSD__
253
- else if ( hash == hash_ntp )
254
- {
255
- return LOG_NTP;
596
+ else {
597
+ ls->method = NDLO_FILE;
598
+ ls->filename = strdupz(output);
599
}
600
+
601
+#if defined(NETDATA_INTERNAL_CHECKS) || defined(NETDATA_DEV_MODE)
602
+ ls->min_priority = NDLP_DEBUG;
603
#endif
258
- else if ( hash == hash_security )
259
- {
260
- //FreeBSD is the unique that does not consider
261
- //this facility deprecated. We are keeping
262
- //it for other OS while they are kept in their headers.
263
-#ifdef __FreeBSD__
264
- return LOG_SECURITY;
604
+
605
+ if(source == NDLS_COLLECTORS) {
606
+ // set the method for the collector processes we will spawn
607
+
608
+ ND_LOG_OUTPUT method;
609
+ ND_LOG_OUTPUT_FORMAT format = ls->format;
610
+ ND_LOG_FIELD_PRIORITY priority = ls->min_priority;
611
+
612
+ if(ls->method == NDLO_SYSLOG || ls->method == NDLO_JOURNAL)
613
+ method = ls->method;
614
+ else
615
+ method = NDLO_STDERR;
616
+
617
+ setenv("NETDATA_LOG_METHOD", nd_log_id2output(method), 1);
618
+ setenv("NETDATA_LOG_FORMAT", nd_log_id2format(format), 1);
619
+ setenv("NETDATA_LOG_SEVERITY_LEVEL", nd_log_id2priority(priority), 1);
620
+ setenv("NETDATA_LOG_PRIORITY_LEVEL", nd_log_id2priority(priority), 1);
621
+ }
622
+}
623
+
624
+void nd_log_set_priority_level(const char *setting) {
625
+ if(!setting || !*setting)
626
+ setting = "info";
627
+
628
+ ND_LOG_FIELD_PRIORITY priority = nd_log_priority2id(setting);
629
+
630
+#if defined(NETDATA_INTERNAL_CHECKS) || defined(NETDATA_DEV_MODE)
631
+ priority = NDLP_DEBUG;
632
+#endif
633
+
634
+ nd_log.sources[NDLS_DAEMON].min_priority = priority;
635
+ nd_log.sources[NDLS_COLLECTORS].min_priority = priority;
636
+
637
+ // backwards compatibility
638
+ setenv("NETDATA_LOG_SEVERITY_LEVEL", nd_log_id2priority(priority), 1);
639
+
640
+ // the right one
641
+ setenv("NETDATA_LOG_PRIORITY_LEVEL", nd_log_id2priority(priority), 1);
642
+}
643
+
644
+void nd_log_set_facility(const char *facility) {
645
+ if(!facility || !*facility)
646
+ facility = "daemon";
647
+
648
+ nd_log.syslog.facility = nd_log_facility2id(facility);
649
+ setenv("NETDATA_SYSLOG_FACILITY", nd_log_id2facility(nd_log.syslog.facility), 1);
650
+}
651
+
652
+void nd_log_set_flood_protection(size_t logs, time_t period) {
653
+ nd_log.sources[NDLS_DAEMON].limits.logs_per_period =
654
+ nd_log.sources[NDLS_DAEMON].limits.logs_per_period_backup;
655
+ nd_log.sources[NDLS_COLLECTORS].limits.logs_per_period =
656
+ nd_log.sources[NDLS_COLLECTORS].limits.logs_per_period_backup = logs;
657
+
658
+ nd_log.sources[NDLS_DAEMON].limits.throttle_period =
659
+ nd_log.sources[NDLS_COLLECTORS].limits.throttle_period = period;
660
+
661
+ char buf[100];
662
+ snprintfz(buf, sizeof(buf), "%" PRIu64, (uint64_t )period);
663
+ setenv("NETDATA_ERRORS_THROTTLE_PERIOD", buf, 1);
664
+ snprintfz(buf, sizeof(buf), "%" PRIu64, (uint64_t )logs);
665
+ setenv("NETDATA_ERRORS_PER_PERIOD", buf, 1);
666
+}
667
+
668
+static bool nd_log_journal_systemd_init(void) {
669
+#ifdef HAVE_SYSTEMD
670
+ nd_log.journal.initialized = true;
671
#else
266
- return LOG_AUTH;
672
+ nd_log.journal.initialized = false;
673
#endif
674
+
675
+ return nd_log.journal.initialized;
676
+}
677
+
678
+static bool nd_log_journal_direct_init(const char *path) {
679
+ if(nd_log.journal_direct.initialized)
680
+ return true;
681
+
682
+ char filename[FILENAME_MAX + 1];
683
+ if(!is_path_unix_socket(path)) {
684
+ journal_construct_path(filename, sizeof(filename), netdata_configured_host_prefix, "netdata");
685
+ if (!is_path_unix_socket(filename)) {
686
+ journal_construct_path(filename, sizeof(filename), netdata_configured_host_prefix, NULL);
687
+ if (!is_path_unix_socket(filename)) {
688
+ journal_construct_path(filename, sizeof(filename), NULL, "netdata");
689
+ if (!is_path_unix_socket(filename)) {
690
+ journal_construct_path(filename, sizeof(filename), NULL, NULL);
691
+ if (!is_path_unix_socket(filename))
692
+ return false;
693
+ }
694
+ }
695
+ }
696
}
269
- else if ( hash == hash_syslog )
270
- {
271
- return LOG_SYSLOG;
272
- }
273
- else if ( hash == hash_user )
274
- {
275
- return LOG_USER;
276
- }
277
- else if ( hash == hash_uucp )
278
- {
279
- return LOG_UUCP;
280
- }
281
- else if ( hash == hash_local0 )
282
- {
283
- return LOG_LOCAL0;
284
- }
285
- else if ( hash == hash_local1 )
286
- {
287
- return LOG_LOCAL1;
288
- }
289
- else if ( hash == hash_local2 )
290
- {
291
- return LOG_LOCAL2;
697
+ else
698
+ snprintfz(filename, sizeof(filename), "%s", path);
699
+
700
+ int fd = journal_direct_fd(filename);
701
+ if(fd < 0)
702
+ return false;
703
+
704
+ nd_log.journal_direct.fd = fd;
705
+ nd_log.journal_direct.initialized = true;
706
+
707
+ if(nd_log.sources[NDLS_COLLECTORS].method == NDLO_JOURNAL)
708
+ setenv("NETDATA_SYSTEMD_JOURNAL_PATH", filename, 1);
709
+
710
+ return true;
711
+}
712
+
713
+static void nd_log_syslog_init() {
714
+ if(nd_log.syslog.initialized)
715
+ return;
716
+
717
+ openlog(program_name, LOG_PID, nd_log.syslog.facility);
718
+ nd_log.syslog.initialized = true;
719
+}
720
+
721
+void nd_log_initialize_for_external_plugins(const char *name) {
722
+ // if we don't run under Netdata, log to stderr,
723
+ // otherwise, use the logging method Netdata wants us to use.
724
+ setenv("NETDATA_LOG_METHOD", "stderr", 0);
725
+ setenv("NETDATA_LOG_FORMAT", "logfmt", 0);
726
+
727
+ nd_log.overwrite_process_source = NDLS_COLLECTORS;
728
+ program_name = name;
729
+
730
+ for(size_t i = 0; i < _NDLS_MAX ;i++) {
731
+ nd_log.sources[i].method = STDERR_FILENO;
732
+ nd_log.sources[i].fd = -1;
733
+ nd_log.sources[i].fp = NULL;
734
}
293
- else if ( hash == hash_local3 )
294
- {
295
- return LOG_LOCAL3;
735
+
736
+ nd_log_set_priority_level(getenv("NETDATA_LOG_PRIORITY_LEVEL"));
737
+ nd_log_set_facility(getenv("NETDATA_SYSLOG_FACILITY"));
738
+
739
+ time_t period = 1200;
740
+ size_t logs = 200;
741
+ const char *s = getenv("NETDATA_ERRORS_THROTTLE_PERIOD");
742
+ if(s && *s >= '0' && *s <= '9') {
743
+ period = str2l(s);
744
+ if(period < 0) period = 0;
745
}
297
- else if ( hash == hash_local4 )
298
- {
299
- return LOG_LOCAL4;
746
+
747
+ s = getenv("NETDATA_ERRORS_PER_PERIOD");
748
+ if(s && *s >= '0' && *s <= '9')
749
+ logs = str2u(s);
750
+
751
+ nd_log_set_flood_protection(logs, period);
752
+
753
+ if(!netdata_configured_host_prefix) {
754
+ s = getenv("NETDATA_HOST_PREFIX");
755
+ if(s && *s)
756
+ netdata_configured_host_prefix = (char *)s;
757
}
301
- else if ( hash == hash_local5 )
302
- {
303
- return LOG_LOCAL5;
758
+
759
+ ND_LOG_OUTPUT method = nd_log_output2id(getenv("NETDATA_LOG_METHOD"));
760
+ ND_LOG_OUTPUT_FORMAT format = nd_log_format2id(getenv("NETDATA_LOG_FORMAT"));
761
+
762
+ if(method != NDLO_JOURNAL && method != NDLO_SYSLOG && method != NDLO_STDERR) {
763
+ if(is_stderr_connected_to_journal()) {
764
+ nd_log(NDLS_COLLECTORS, NDLP_WARNING, "NETDATA_LOG_METHOD is not set. Using journal.");
765
+ method = NDLO_JOURNAL;
766
+ }
767
+ else {
768
+ nd_log(NDLS_COLLECTORS, NDLP_WARNING, "NETDATA_LOG_METHOD is not set. Using stderr.");
769
+ method = NDLO_STDERR;
770
+ }
771
}
305
- else if ( hash == hash_local6 )
306
- {
307
- return LOG_LOCAL6;
772
+
773
+ switch(method) {
774
+ case NDLO_JOURNAL:
775
+ if(!nd_log_journal_direct_init(getenv("NETDATA_SYSTEMD_JOURNAL_PATH")) ||
776
+ !nd_log_journal_direct_init(NULL) || !nd_log_journal_systemd_init()) {
777
+ nd_log(NDLS_COLLECTORS, NDLP_WARNING, "Failed to initialize journal. Using stderr.");
778
+ method = NDLO_STDERR;
779
+ }
780
+ break;
781
+
782
+ case NDLO_SYSLOG:
783
+ nd_log_syslog_init();
784
+ break;
785
+
786
+ default:
787
+ method = NDLO_STDERR;
788
+ break;
789
}
309
- else if ( hash == hash_local7 )
310
- {
311
- return LOG_LOCAL7;
790
+
791
+ for(size_t i = 0; i < _NDLS_MAX ;i++) {
792
+ nd_log.sources[i].method = method;
793
+ nd_log.sources[i].format = format;
794
+ nd_log.sources[i].fd = -1;
795
+ nd_log.sources[i].fp = NULL;
796
}
313
-#ifdef __APPLE__
314
- else if ( hash == hash_launchd )
315
- {
316
- return LOG_LAUNCHD;
797
+
798
+// nd_log(NDLS_COLLECTORS, NDLP_NOTICE, "FINAL_LOG_METHOD: %s", nd_log_id2method(method));
799
+}
800
+
801
+static bool nd_log_replace_existing_fd(struct nd_log_source *e, int new_fd) {
802
+ if(new_fd == -1 || e->fd == -1 ||
803
+ (e->fd == STDOUT_FILENO && nd_log.std_output.initialized) ||
804
+ (e->fd == STDERR_FILENO && nd_log.std_error.initialized))
805
+ return false;
806
+
807
+ if(new_fd != e->fd) {
808
+ int t = dup2(new_fd, e->fd);
809
+
810
+ bool ret = true;
811
+ if (t == -1) {
812
+ netdata_log_error("Cannot dup2() new fd %d to old fd %d for '%s'", new_fd, e->fd, e->filename);
813
+ ret = false;
814
+ }
815
+ else
816
+ close(new_fd);
817
+
818
+ if(e->fd == STDOUT_FILENO)
819
+ nd_log.std_output.initialized = true;
820
+ else if(e->fd == STDERR_FILENO)
821
+ nd_log.std_error.initialized = true;
822
+
823
+ return ret;
824
}
318
-#endif
825
320
- return LOG_DAEMON;
826
+ return false;
827
}
828
323
-//we do not need to use this now, but I already created this function to be
324
-//used case necessary.
325
-/*
326
-char *log_facility_name(int code)
327
-{
328
- char *defvalue = { "daemon" };
329
- switch(code)
330
- {
331
- case LOG_AUTH:
332
- {
333
- return "auth";
334
- }
335
- case LOG_AUTHPRIV:
336
- {
337
- return "authpriv";
338
- }
339
-#ifdef __FreeBSD__
340
- case LOG_CONSOLE:
341
- {
342
- return "console";
343
- }
344
-#endif
345
- case LOG_CRON:
346
- {
347
- return "cron";
348
- }
349
- case LOG_DAEMON:
350
- {
351
- return defvalue;
352
- }
353
- case LOG_FTP:
354
- {
355
- return "ftp";
356
- }
357
-#ifdef __APPLE__
358
- case LOG_INSTALL:
359
- {
360
- return "install";
361
- }
362
-#endif
363
- case LOG_KERN:
364
- {
365
- return "kern";
366
- }
367
- case LOG_LPR:
368
- {
369
- return "lpr";
370
- }
371
- case LOG_MAIL:
372
- {
373
- return "mail";
374
- }
375
-#ifdef __APPLE__
376
- case LOG_NETINFO:
377
- {
378
- return "netinfo" ;
379
- }
380
- case LOG_RAS:
381
- {
382
- return "ras";
383
- }
384
- case LOG_REMOTEAUTH:
385
- {
386
- return "remoteauth";
387
- }
388
-#endif
389
- case LOG_NEWS:
390
- {
391
- return "news";
392
- }
393
-#ifdef __FreeBSD__
394
- case LOG_NTP:
395
- {
396
- return "ntp" ;
397
- }
398
- case LOG_SECURITY:
399
- {
400
- return "security";
401
- }
402
-#endif
403
- case LOG_SYSLOG:
404
- {
405
- return "syslog";
406
- }
407
- case LOG_USER:
408
- {
409
- return "user";
410
- }
411
- case LOG_UUCP:
412
- {
413
- return "uucp";
414
- }
415
- case LOG_LOCAL0:
416
- {
417
- return "local0";
418
- }
419
- case LOG_LOCAL1:
420
- {
421
- return "local1";
422
- }
423
- case LOG_LOCAL2:
424
- {
425
- return "local2";
426
- }
427
- case LOG_LOCAL3:
428
- {
429
- return "local3";
430
- }
431
- case LOG_LOCAL4:
432
- {
433
- return "local4" ;
434
- }
435
- case LOG_LOCAL5:
436
- {
437
- return "local5";
829
+static void nd_log_open(struct nd_log_source *e, ND_LOG_SOURCES source) {
830
+ if(e->method == NDLO_DEFAULT)
831
+ nd_log_set_user_settings(source, e->filename);
832
+
833
+ if((e->method == NDLO_FILE && !e->filename) ||
834
+ (e->method == NDLO_DEVNULL && e->fd == -1))
835
+ e->method = NDLO_DISABLED;
836
+
837
+ if(e->fp)
838
+ fflush(e->fp);
839
+
840
+ switch(e->method) {
841
+ case NDLO_SYSLOG:
842
+ nd_log_syslog_init();
843
+ break;
844
+
845
+ case NDLO_JOURNAL:
846
+ nd_log_journal_direct_init(NULL);
847
+ nd_log_journal_systemd_init();
848
+ break;
849
+
850
+ case NDLO_STDOUT:
851
+ e->fp = stdout;
852
+ e->fd = STDOUT_FILENO;
853
+ break;
854
+
855
+ default:
856
+ case NDLO_DEFAULT:
857
+ case NDLO_STDERR:
858
+ e->method = NDLO_STDERR;
859
+ e->fp = stderr;
860
+ e->fd = STDERR_FILENO;
861
+ break;
862
+
863
+ case NDLO_DEVNULL:
864
+ case NDLO_FILE: {
865
+ int fd = open(e->filename, O_WRONLY | O_APPEND | O_CREAT, 0664);
866
+ if(fd == -1) {
867
+ if(e->fd != STDOUT_FILENO && e->fd != STDERR_FILENO) {
868
+ e->fd = STDERR_FILENO;
869
+ e->method = NDLO_STDERR;
870
+ netdata_log_error("Cannot open log file '%s'. Falling back to stderr.", e->filename);
871
+ }
872
+ else
873
+ netdata_log_error("Cannot open log file '%s'. Leaving fd %d as-is.", e->filename, e->fd);
874
}
439
- case LOG_LOCAL6:
440
- {
441
- return "local6";
875
+ else {
876
+ if (!nd_log_replace_existing_fd(e, fd)) {
877
+ if(e->fd == STDOUT_FILENO || e->fd == STDERR_FILENO) {
878
+ if(e->fd == STDOUT_FILENO)
879
+ e->method = NDLO_STDOUT;
880
+ else if(e->fd == STDERR_FILENO)
881
+ e->method = NDLO_STDERR;
882
+
883
+ // we have dup2() fd, so we can close the one we opened
884
+ if(fd != STDOUT_FILENO && fd != STDERR_FILENO)
885
+ close(fd);
886
+ }
887
+ else
888
+ e->fd = fd;
889
+ }
890
}
443
- case LOG_LOCAL7:
444
- {
445
- return "local7" ;
891
+
892
+ // at this point we have e->fd set properly
893
+
894
+ if(e->fd == STDOUT_FILENO)
895
+ e->fp = stdout;
896
+ else if(e->fd == STDERR_FILENO)
897
+ e->fp = stderr;
898
+
899
+ if(!e->fp) {
900
+ e->fp = fdopen(e->fd, "a");
901
+ if (!e->fp) {
902
+ netdata_log_error("Cannot fdopen() fd %d ('%s')", e->fd, e->filename);
903
+
904
+ if(e->fd != STDOUT_FILENO && e->fd != STDERR_FILENO)
905
+ close(e->fd);
906
+
907
+ e->fp = stderr;
908
+ e->fd = STDERR_FILENO;
909
+ }
910
}
447
-#ifdef __APPLE__
448
- case LOG_LAUNCHD:
449
- {
450
- return "launchd";
911
+ else {
912
+ if (setvbuf(e->fp, NULL, _IOLBF, 0) != 0)
913
+ netdata_log_error("Cannot set line buffering on fd %d ('%s')", e->fd, e->filename);
914
}
452
-#endif
915
+ }
916
+ break;
917
}
918
+}
919
+
920
+static void nd_log_stdin_init(int fd, const char *filename) {
921
+ int f = open(filename, O_WRONLY | O_APPEND | O_CREAT, 0664);
922
+ if(f == -1)
923
+ return;
924
+
925
+ if(f != fd) {
926
+ dup2(f, fd);
927
+ close(f);
928
+ }
929
+}
930
+
931
+void nd_log_initialize(void) {
932
+ nd_log_stdin_init(STDIN_FILENO, "/dev/null");
933
455
- return defvalue;
934
+ for(size_t i = 0 ; i < _NDLS_MAX ; i++)
935
+ nd_log_open(&nd_log.sources[i], i);
936
}
457
-*/
937
459
-// ----------------------------------------------------------------------------
938
+void nd_log_reopen_log_files(void) {
939
+ netdata_log_info("Reopening all log files.");
940
461
-void syslog_init() {
462
- static int i = 0;
941
+ nd_log.std_output.initialized = false;
942
+ nd_log.std_error.initialized = false;
943
+ nd_log_initialize();
944
464
- if(!i) {
465
- openlog(program_name, LOG_PID,log_facility_id(facility_log));
466
- i = 1;
467
- }
945
+ netdata_log_info("Log files re-opened.");
946
}
947
470
-void log_date(char *buffer, size_t len, time_t now) {
471
- if(unlikely(!buffer || !len))
472
- return;
473
-
474
- time_t t = now;
475
- struct tm *tmp, tmbuf;
948
+void chown_open_file(int fd, uid_t uid, gid_t gid) {
949
+ if(fd == -1) return;
950
477
- tmp = localtime_r(&t, &tmbuf);
951
+ struct stat buf;
952
479
- if (tmp == NULL) {
480
- buffer[0] = '\0';
953
+ if(fstat(fd, &buf) == -1) {
954
+ netdata_log_error("Cannot fstat() fd %d", fd);
955
return;
956
}
957
484
- if (unlikely(strftime(buffer, len, "%Y-%m-%d %H:%M:%S", tmp) == 0))
485
- buffer[0] = '\0';
486
-
487
- buffer[len - 1] = '\0';
958
+ if((buf.st_uid != uid || buf.st_gid != gid) && S_ISREG(buf.st_mode)) {
959
+ if(fchown(fd, uid, gid) == -1)
960
+ netdata_log_error("Cannot fchown() fd %d.", fd);
961
+ }
962
}
963
490
-static netdata_mutex_t log_mutex = NETDATA_MUTEX_INITIALIZER;
491
-static inline void log_lock() {
492
- netdata_mutex_lock(&log_mutex);
493
-}
494
-static inline void log_unlock() {
495
- netdata_mutex_unlock(&log_mutex);
964
+void nd_log_chown_log_files(uid_t uid, gid_t gid) {
965
+ for(size_t i = 0 ; i < _NDLS_MAX ; i++) {
966
+ if(nd_log.sources[i].fd != -1 && nd_log.sources[i].fd != STDIN_FILENO)
967
+ chown_open_file(nd_log.sources[i].fd, uid, gid);
968
+ }
969
}
970
498
-static FILE *open_log_file(int fd, FILE *fp, const char *filename, int *enabled_syslog, int is_stdaccess, int *fd_ptr) {
499
- int f, devnull = 0;
971
+// ----------------------------------------------------------------------------
972
+// annotators
973
+struct log_field;
974
+static void errno_annotator(BUFFER *wb, const char *key, struct log_field *lf);
975
+static void priority_annotator(BUFFER *wb, const char *key, struct log_field *lf);
976
+static void timestamp_usec_annotator(BUFFER *wb, const char *key, struct log_field *lf);
977
+
978
+// ----------------------------------------------------------------------------
979
501
- if(!filename || !*filename || !strcmp(filename, "none") || !strcmp(filename, "/dev/null")) {
502
- filename = "/dev/null";
503
- devnull = 1;
980
+typedef void (*annotator_t)(BUFFER *wb, const char *key, struct log_field *lf);
981
+
982
+struct log_field {
983
+ const char *journal;
984
+ const char *logfmt;
985
+ annotator_t logfmt_annotator;
986
+ struct log_stack_entry entry;
987
+};
988
+
989
+#define THREAD_LOG_STACK_MAX 50
990
+
991
+static __thread struct log_stack_entry *thread_log_stack_base[THREAD_LOG_STACK_MAX];
992
+static __thread size_t thread_log_stack_next = 0;
993
+
994
+static __thread struct log_field thread_log_fields[_NDF_MAX] = {
995
+ // THE ORDER DEFINES THE ORDER FIELDS WILL APPEAR IN logfmt
996
+
997
+ [NDF_STOP] = { // processing will not stop on this - so it is ok to be first
998
+ .journal = NULL,
999
+ .logfmt = NULL,
1000
+ .logfmt_annotator = NULL,
1001
+ },
1002
+ [NDF_TIMESTAMP_REALTIME_USEC] = {
1003
+ .journal = NULL,
1004
+ .logfmt = "time",
1005
+ .logfmt_annotator = timestamp_usec_annotator,
1006
+ },
1007
+ [NDF_SYSLOG_IDENTIFIER] = {
1008
+ .journal = "SYSLOG_IDENTIFIER", // standard journald field
1009
+ .logfmt = "comm",
1010
+ },
1011
+ [NDF_LOG_SOURCE] = {
1012
+ .journal = "ND_LOG_SOURCE",
1013
+ .logfmt = "source",
1014
+ },
1015
+ [NDF_PRIORITY] = {
1016
+ .journal = "PRIORITY", // standard journald field
1017
+ .logfmt = "level",
1018
+ .logfmt_annotator = priority_annotator,
1019
+ },
1020
+ [NDF_ERRNO] = {
1021
+ .journal = "ERRNO", // standard journald field
1022
+ .logfmt = "errno",
1023
+ .logfmt_annotator = errno_annotator,
1024
+ },
1025
+ [NDF_INVOCATION_ID] = {
1026
+ .journal = "INVOCATION_ID", // standard journald field
1027
+ .logfmt = NULL,
1028
+ },
1029
+ [NDF_LINE] = {
1030
+ .journal = "CODE_LINE", // standard journald field
1031
+ .logfmt = NULL,
1032
+ },
1033
+ [NDF_FILE] = {
1034
+ .journal = "CODE_FILE", // standard journald field
1035
+ .logfmt = NULL,
1036
+ },
1037
+ [NDF_FUNC] = {
1038
+ .journal = "CODE_FUNC", // standard journald field
1039
+ .logfmt = NULL,
1040
+ },
1041
+ [NDF_TID] = {
1042
+ .journal = "TID", // standard journald field
1043
+ .logfmt = "tid",
1044
+ },
1045
+ [NDF_THREAD_TAG] = {
1046
+ .journal = "THREAD_TAG",
1047
+ .logfmt = "thread",
1048
+ },
1049
+ [NDF_MESSAGE_ID] = {
1050
+ .journal = "MESSAGE_ID",
1051
+ .logfmt = "msg_id",
1052
+ },
1053
+ [NDF_MODULE] = {
1054
+ .journal = "ND_MODULE",
1055
+ .logfmt = "module",
1056
+ },
1057
+ [NDF_NIDL_NODE] = {
1058
+ .journal = "ND_NIDL_NODE",
1059
+ .logfmt = "node",
1060
+ },
1061
+ [NDF_NIDL_INSTANCE] = {
1062
+ .journal = "ND_NIDL_INSTANCE",
1063
+ .logfmt = "instance",
1064
+ },
1065
+ [NDF_NIDL_CONTEXT] = {
1066
+ .journal = "ND_NIDL_CONTEXT",
1067
+ .logfmt = "context",
1068
+ },
1069
+ [NDF_NIDL_DIMENSION] = {
1070
+ .journal = "ND_NIDL_DIMENSION",
1071
+ .logfmt = "dimension",
1072
+ },
1073
+ [NDF_SRC_TRANSPORT] = {
1074
+ .journal = "ND_SRC_TRANSPORT",
1075
+ .logfmt = "src_transport",
1076
+ },
1077
+ [NDF_SRC_IP] = {
1078
+ .journal = "ND_SRC_IP",
1079
+ .logfmt = "src_ip",
1080
+ },
1081
+ [NDF_SRC_PORT] = {
1082
+ .journal = "ND_SRC_PORT",
1083
+ .logfmt = "src_port",
1084
+ },
1085
+ [NDF_SRC_CAPABILITIES] = {
1086
+ .journal = "ND_SRC_CAPABILITIES",
1087
+ .logfmt = "src_capabilities",
1088
+ },
1089
+ [NDF_DST_TRANSPORT] = {
1090
+ .journal = "ND_DST_TRANSPORT",
1091
+ .logfmt = "dst_transport",
1092
+ },
1093
+ [NDF_DST_IP] = {
1094
+ .journal = "ND_DST_IP",
1095
+ .logfmt = "dst_ip",
1096
+ },
1097
+ [NDF_DST_PORT] = {
1098
+ .journal = "ND_DST_PORT",
1099
+ .logfmt = "dst_port",
1100
+ },
1101
+ [NDF_DST_CAPABILITIES] = {
1102
+ .journal = "ND_DST_CAPABILITIES",
1103
+ .logfmt = "dst_capabilities",
1104
+ },
1105
+ [NDF_REQUEST_METHOD] = {
1106
+ .journal = "ND_REQUEST_METHOD",
1107
+ .logfmt = "req_method",
1108
+ },
1109
+ [NDF_RESPONSE_CODE] = {
1110
+ .journal = "ND_RESPONSE_CODE",
1111
+ .logfmt = "code",
1112
+ },
1113
+ [NDF_CONNECTION_ID] = {
1114
+ .journal = "ND_CONNECTION_ID",
1115
+ .logfmt = "conn",
1116
+ },
1117
+ [NDF_TRANSACTION_ID] = {
1118
+ .journal = "ND_TRANSACTION_ID",
1119
+ .logfmt = "transaction",
1120
+ },
1121
+ [NDF_RESPONSE_SENT_BYTES] = {
1122
+ .journal = "ND_RESPONSE_SENT_BYTES",
1123
+ .logfmt = "sent_bytes",
1124
+ },
1125
+ [NDF_RESPONSE_SIZE_BYTES] = {
1126
+ .journal = "ND_RESPONSE_SIZE_BYTES",
1127
+ .logfmt = "size_bytes",
1128
+ },
1129
+ [NDF_RESPONSE_PREPARATION_TIME_USEC] = {
1130
+ .journal = "ND_RESPONSE_PREP_TIME_USEC",
1131
+ .logfmt = "prep_ut",
1132
+ },
1133
+ [NDF_RESPONSE_SENT_TIME_USEC] = {
1134
+ .journal = "ND_RESPONSE_SENT_TIME_USEC",
1135
+ .logfmt = "sent_ut",
1136
+ },
1137
+ [NDF_RESPONSE_TOTAL_TIME_USEC] = {
1138
+ .journal = "ND_RESPONSE_TOTAL_TIME_USEC",
1139
+ .logfmt = "total_ut",
1140
+ },
1141
+ [NDF_ALERT_ID] = {
1142
+ .journal = "ND_ALERT_ID",
1143
+ .logfmt = "alert_id",
1144
+ },
1145
+ [NDF_ALERT_UNIQUE_ID] = {
1146
+ .journal = "ND_ALERT_UNIQUE_ID",
1147
+ .logfmt = "alert_unique_id",
1148
+ },
1149
+ [NDF_ALERT_TRANSITION_ID] = {
1150
+ .journal = "ND_ALERT_TRANSITION_ID",
1151
+ .logfmt = "alert_transition_id",
1152
+ },
1153
+ [NDF_ALERT_EVENT_ID] = {
1154
+ .journal = "ND_ALERT_EVENT_ID",
1155
+ .logfmt = "alert_event_id",
1156
+ },
1157
+ [NDF_ALERT_CONFIG_HASH] = {
1158
+ .journal = "ND_ALERT_CONFIG",
1159
+ .logfmt = "alert_config",
1160
+ },
1161
+ [NDF_ALERT_NAME] = {
1162
+ .journal = "ND_ALERT_NAME",
1163
+ .logfmt = "alert",
1164
+ },
1165
+ [NDF_ALERT_CLASS] = {
1166
+ .journal = "ND_ALERT_CLASS",
1167
+ .logfmt = "alert_class",
1168
+ },
1169
+ [NDF_ALERT_COMPONENT] = {
1170
+ .journal = "ND_ALERT_COMPONENT",
1171
+ .logfmt = "alert_component",
1172
+ },
1173
+ [NDF_ALERT_TYPE] = {
1174
+ .journal = "ND_ALERT_TYPE",
1175
+ .logfmt = "alert_type",
1176
+ },
1177
+ [NDF_ALERT_EXEC] = {
1178
+ .journal = "ND_ALERT_EXEC",
1179
+ .logfmt = "alert_exec",
1180
+ },
1181
+ [NDF_ALERT_RECIPIENT] = {
1182
+ .journal = "ND_ALERT_RECIPIENT",
1183
+ .logfmt = "alert_recipient",
1184
+ },
1185
+ [NDF_ALERT_VALUE] = {
1186
+ .journal = "ND_ALERT_VALUE",
1187
+ .logfmt = "alert_value",
1188
+ },
1189
+ [NDF_ALERT_VALUE_OLD] = {
1190
+ .journal = "ND_ALERT_VALUE_OLD",
1191
+ .logfmt = "alert_value_old",
1192
+ },
1193
+ [NDF_ALERT_STATUS] = {
1194
+ .journal = "ND_ALERT_STATUS",
1195
+ .logfmt = "alert_status",
1196
+ },
1197
+ [NDF_ALERT_STATUS_OLD] = {
1198
+ .journal = "ND_ALERT_STATUS_OLD",
1199
+ .logfmt = "alert_value_old",
1200
+ },
1201
+ [NDF_ALERT_UNITS] = {
1202
+ .journal = "ND_ALERT_UNITS",
1203
+ .logfmt = "alert_units",
1204
+ },
1205
+ [NDF_ALERT_SUMMARY] = {
1206
+ .journal = "ND_ALERT_SUMMARY",
1207
+ .logfmt = "alert_summary",
1208
+ },
1209
+ [NDF_ALERT_INFO] = {
1210
+ .journal = "ND_ALERT_INFO",
1211
+ .logfmt = "alert_info",
1212
+ },
1213
+ [NDF_ALERT_DURATION] = {
1214
+ .journal = "ND_ALERT_DURATION",
1215
+ .logfmt = "alert_duration",
1216
+ },
1217
+ [NDF_ALERT_NOTIFICATION_REALTIME_USEC] = {
1218
+ .journal = "ND_ALERT_NOTIFICATION_TIMESTAMP_USEC",
1219
+ .logfmt = "alert_notification_timestamp",
1220
+ .logfmt_annotator = timestamp_usec_annotator,
1221
+ },
1222
+
1223
+ // put new items here
1224
+ // leave the request URL and the message last
1225
+
1226
+ [NDF_REQUEST] = {
1227
+ .journal = "ND_REQUEST",
1228
+ .logfmt = "request",
1229
+ },
1230
+ [NDF_MESSAGE] = {
1231
+ .journal = "MESSAGE",
1232
+ .logfmt = "msg",
1233
+ },
1234
+};
1235
+
1236
+#define THREAD_FIELDS_MAX (sizeof(thread_log_fields) / sizeof(thread_log_fields[0]))
1237
+
1238
+ND_LOG_FIELD_ID nd_log_field_id_by_name(const char *field, size_t len) {
1239
+ for(size_t i = 0; i < THREAD_FIELDS_MAX ;i++) {
1240
+ if(thread_log_fields[i].journal && strlen(thread_log_fields[i].journal) == len && strncmp(field, thread_log_fields[i].journal, len) == 0)
1241
+ return i;
1242
}
1243
506
- if(!strcmp(filename, "syslog")) {
507
- filename = "/dev/null";
508
- devnull = 1;
1244
+ return NDF_STOP;
1245
+}
1246
510
- syslog_init();
511
- if(enabled_syslog) *enabled_syslog = 1;
512
- }
513
- else if(enabled_syslog) *enabled_syslog = 0;
514
-
515
- // don't do anything if the user is willing
516
- // to have the standard one
517
- if(!strcmp(filename, "system")) {
518
- if(fd != -1 && !is_stdaccess) {
519
- if(fd_ptr) *fd_ptr = fd;
520
- return fp;
521
- }
1247
+void log_stack_pop(void *ptr) {
1248
+ if(!ptr) return;
1249
+
1250
+ struct log_stack_entry *lgs = *(struct log_stack_entry (*)[])ptr;
1251
523
- filename = "stderr";
1252
+ if(unlikely(!thread_log_stack_next || lgs != thread_log_stack_base[thread_log_stack_next - 1])) {
1253
+ fatal("You cannot pop in the middle of the stack, or an item not in the stack");
1254
+ return;
1255
}
1256
526
- if(!strcmp(filename, "stdout"))
527
- f = STDOUT_FILENO;
1257
+ thread_log_stack_next--;
1258
+}
1259
529
- else if(!strcmp(filename, "stderr"))
530
- f = STDERR_FILENO;
1260
+void log_stack_push(struct log_stack_entry *lgs) {
1261
+ if(!lgs || thread_log_stack_next >= THREAD_LOG_STACK_MAX) return;
1262
+ thread_log_stack_base[thread_log_stack_next++] = lgs;
1263
+}
1264
532
- else {
533
- f = open(filename, O_WRONLY | O_APPEND | O_CREAT, 0664);
534
- if(f == -1) {
535
- netdata_log_error("Cannot open file '%s'. Leaving %d to its default.", filename, fd);
536
- if(fd_ptr) *fd_ptr = fd;
537
- return fp;
1265
+// ----------------------------------------------------------------------------
1266
+// json formatter
1267
+
1268
+static void nd_logger_json(BUFFER *wb, struct log_field *fields, size_t fields_max) {
1269
+
1270
+ // --- FIELD_PARSER_VERSIONS ---
1271
+ //
1272
+ // IMPORTANT:
1273
+ // THERE ARE 6 VERSIONS OF THIS CODE
1274
+ //
1275
+ // 1. journal (direct socket API),
1276
+ // 2. journal (libsystemd API),
1277
+ // 3. logfmt,
1278
+ // 4. json,
1279
+ // 5. convert to uint64
1280
+ // 6. convert to int64
1281
+ //
1282
+ // UPDATE ALL OF THEM FOR NEW FEATURES OR FIXES
1283
+
1284
+ buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_MINIFY);
1285
+ CLEAN_BUFFER *tmp = NULL;
1286
+
1287
+ for (size_t i = 0; i < fields_max; i++) {
1288
+ if (!fields[i].entry.set || !fields[i].logfmt)
1289
+ continue;
1290
+
1291
+ const char *key = fields[i].logfmt;
1292
+
1293
+ const char *s = NULL;
1294
+ switch(fields[i].entry.type) {
1295
+ case NDFT_TXT:
1296
+ s = fields[i].entry.txt;
1297
+ break;
1298
+ case NDFT_STR:
1299
+ s = string2str(fields[i].entry.str);
1300
+ break;
1301
+ case NDFT_BFR:
1302
+ s = buffer_tostring(fields[i].entry.bfr);
1303
+ break;
1304
+ case NDFT_U64:
1305
+ buffer_json_member_add_uint64(wb, key, fields[i].entry.u64);
1306
+ break;
1307
+ case NDFT_I64:
1308
+ buffer_json_member_add_int64(wb, key, fields[i].entry.i64);
1309
+ break;
1310
+ case NDFT_DBL:
1311
+ buffer_json_member_add_double(wb, key, fields[i].entry.dbl);
1312
+ break;
1313
+ case NDFT_UUID:{
1314
+ char u[UUID_COMPACT_STR_LEN];
1315
+ uuid_unparse_lower_compact(*fields[i].entry.uuid, u);
1316
+ buffer_json_member_add_string(wb, key, u);
1317
+ }
1318
+ break;
1319
+ case NDFT_CALLBACK: {
1320
+ if(!tmp)
1321
+ tmp = buffer_create(1024, NULL);
1322
+ else
1323
+ buffer_flush(tmp);
1324
+ if(fields[i].entry.cb.formatter(tmp, fields[i].entry.cb.formatter_data))
1325
+ s = buffer_tostring(tmp);
1326
+ else
1327
+ s = NULL;
1328
+ }
1329
+ break;
1330
+ default:
1331
+ s = "UNHANDLED";
1332
+ break;
1333
}
1334
+
1335
+ if(s && *s)
1336
+ buffer_json_member_add_string(wb, key, s);
1337
}
1338
541
- // if there is a level-2 file pointer
542
- // flush it before switching the level-1 fds
543
- if(fp)
544
- fflush(fp);
1339
+ buffer_json_finalize(wb);
1340
+}
1341
546
- if(devnull && is_stdaccess) {
547
- fd = -1;
548
- fp = NULL;
1342
+// ----------------------------------------------------------------------------
1343
+// logfmt formatter
1344
+
1345
+
1346
+static int64_t log_field_to_int64(struct log_field *lf) {
1347
+
1348
+ // --- FIELD_PARSER_VERSIONS ---
1349
+ //
1350
+ // IMPORTANT:
1351
+ // THERE ARE 6 VERSIONS OF THIS CODE
1352
+ //
1353
+ // 1. journal (direct socket API),
1354
+ // 2. journal (libsystemd API),
1355
+ // 3. logfmt,
1356
+ // 4. json,
1357
+ // 5. convert to uint64
1358
+ // 6. convert to int64
1359
+ //
1360
+ // UPDATE ALL OF THEM FOR NEW FEATURES OR FIXES
1361
+
1362
+ CLEAN_BUFFER *tmp = NULL;
1363
+ const char *s = NULL;
1364
+
1365
+ switch(lf->entry.type) {
1366
+ case NDFT_UUID:
1367
+ case NDFT_UNSET:
1368
+ return 0;
1369
+
1370
+ case NDFT_TXT:
1371
+ s = lf->entry.txt;
1372
+ break;
1373
+
1374
+ case NDFT_STR:
1375
+ s = string2str(lf->entry.str);
1376
+ break;
1377
+
1378
+ case NDFT_BFR:
1379
+ s = buffer_tostring(lf->entry.bfr);
1380
+ break;
1381
+
1382
+ case NDFT_CALLBACK:
1383
+ if(!tmp)
1384
+ tmp = buffer_create(0, NULL);
1385
+ else
1386
+ buffer_flush(tmp);
1387
+
1388
+ if(lf->entry.cb.formatter(tmp, lf->entry.cb.formatter_data))
1389
+ s = buffer_tostring(tmp);
1390
+ else
1391
+ s = NULL;
1392
+ break;
1393
+
1394
+ case NDFT_U64:
1395
+ return lf->entry.u64;
1396
+
1397
+ case NDFT_I64:
1398
+ return lf->entry.i64;
1399
+
1400
+ case NDFT_DBL:
1401
+ return lf->entry.dbl;
1402
}
1403
551
- if(fd != f && fd != -1) {
552
- // it automatically closes
553
- int t = dup2(f, fd);
554
- if (t == -1) {
555
- netdata_log_error("Cannot dup2() new fd %d to old fd %d for '%s'", f, fd, filename);
556
- close(f);
557
- if(fd_ptr) *fd_ptr = fd;
558
- return fp;
559
- }
560
- // netdata_log_info("dup2() new fd %d to old fd %d for '%s'", f, fd, filename);
561
- close(f);
562
- }
563
- else fd = f;
1404
+ if(s && *s)
1405
+ return str2ll(s, NULL);
1406
565
- if(!fp) {
566
- fp = fdopen(fd, "a");
567
- if (!fp)
568
- netdata_log_error("Cannot fdopen() fd %d ('%s')", fd, filename);
569
- else {
570
- if (setvbuf(fp, NULL, _IOLBF, 0) != 0)
571
- netdata_log_error("Cannot set line buffering on fd %d ('%s')", fd, filename);
572
- }
1407
+ return 0;
1408
+}
1409
+
1410
+static uint64_t log_field_to_uint64(struct log_field *lf) {
1411
+
1412
+ // --- FIELD_PARSER_VERSIONS ---
1413
+ //
1414
+ // IMPORTANT:
1415
+ // THERE ARE 6 VERSIONS OF THIS CODE
1416
+ //
1417
+ // 1. journal (direct socket API),
1418
+ // 2. journal (libsystemd API),
1419
+ // 3. logfmt,
1420
+ // 4. json,
1421
+ // 5. convert to uint64
1422
+ // 6. convert to int64
1423
+ //
1424
+ // UPDATE ALL OF THEM FOR NEW FEATURES OR FIXES
1425
+
1426
+ CLEAN_BUFFER *tmp = NULL;
1427
+ const char *s = NULL;
1428
+
1429
+ switch(lf->entry.type) {
1430
+ case NDFT_UUID:
1431
+ case NDFT_UNSET:
1432
+ return 0;
1433
+
1434
+ case NDFT_TXT:
1435
+ s = lf->entry.txt;
1436
+ break;
1437
+
1438
+ case NDFT_STR:
1439
+ s = string2str(lf->entry.str);
1440
+ break;
1441
+
1442
+ case NDFT_BFR:
1443
+ s = buffer_tostring(lf->entry.bfr);
1444
+ break;
1445
+
1446
+ case NDFT_CALLBACK:
1447
+ if(!tmp)
1448
+ tmp = buffer_create(0, NULL);
1449
+ else
1450
+ buffer_flush(tmp);
1451
+
1452
+ if(lf->entry.cb.formatter(tmp, lf->entry.cb.formatter_data))
1453
+ s = buffer_tostring(tmp);
1454
+ else
1455
+ s = NULL;
1456
+ break;
1457
+
1458
+ case NDFT_U64:
1459
+ return lf->entry.u64;
1460
+
1461
+ case NDFT_I64:
1462
+ return lf->entry.i64;
1463
+
1464
+ case NDFT_DBL:
1465
+ return lf->entry.dbl;
1466
}
1467
575
- if(fd_ptr) *fd_ptr = fd;
576
- return fp;
577
-}
1468
+ if(s && *s)
1469
+ return str2uint64_t(s, NULL);
1470
579
-void reopen_all_log_files() {
580
- if(stdout_filename)
581
- open_log_file(STDOUT_FILENO, stdout, stdout_filename, &output_log_syslog, 0, NULL);
1471
+ return 0;
1472
+}
1473
583
- if(stdcollector_filename)
584
- open_log_file(STDERR_FILENO, stderr, stdcollector_filename, &collector_log_syslog, 0, NULL);
1474
+static void timestamp_usec_annotator(BUFFER *wb, const char *key, struct log_field *lf) {
1475
+ usec_t ut = log_field_to_uint64(lf);
1476
586
- if(stderr_filename) {
587
- // Netdata starts using stderr and if it has success to open file it redirects
588
- FILE *fp = open_log_file(stdcollector_fd, stderror, stderr_filename,
589
- &error_log_syslog, 1, &stdcollector_fd);
590
- if (fp)
591
- stderror = fp;
592
- }
1477
+ if(!ut)
1478
+ return;
1479
594
-#ifdef ENABLE_ACLK
595
- if (aclklog_enabled)
596
- aclklog = open_log_file(aclklog_fd, aclklog, aclklog_filename, NULL, 0, &aclklog_fd);
597
-#endif
1480
+ char datetime[ISO8601_MAX_LENGTH];
1481
+ iso8601_datetime_ut(datetime, sizeof(datetime), ut, ISO8601_LOCAL_TIMEZONE | ISO8601_MILLISECONDS);
1482
599
- if(stdaccess_filename)
600
- stdaccess = open_log_file(stdaccess_fd, stdaccess, stdaccess_filename, &access_log_syslog, 1, &stdaccess_fd);
1483
+ if(buffer_strlen(wb))
1484
+ buffer_fast_strcat(wb, " ", 1);
1485
602
- if(stdhealth_filename)
603
- stdhealth = open_log_file(stdhealth_fd, stdhealth, stdhealth_filename, &health_log_syslog, 1, &stdhealth_fd);
1486
+ buffer_strcat(wb, key);
1487
+ buffer_fast_strcat(wb, "=", 1);
1488
+ buffer_json_strcat(wb, datetime);
1489
}
1490
606
-void open_all_log_files() {
607
- // disable stdin
608
- open_log_file(STDIN_FILENO, stdin, "/dev/null", NULL, 0, NULL);
1491
+static void errno_annotator(BUFFER *wb, const char *key, struct log_field *lf) {
1492
+ int64_t errnum = log_field_to_int64(lf);
1493
610
- open_log_file(STDOUT_FILENO, stdout, stdout_filename, &output_log_syslog, 0, NULL);
611
- open_log_file(STDERR_FILENO, stderr, stdcollector_filename, &collector_log_syslog, 0, NULL);
1494
+ if(errnum == 0)
1495
+ return;
1496
613
- // Netdata starts using stderr and if it has success to open file it redirects
614
- FILE *fp = open_log_file(stdcollector_fd, NULL, stderr_filename, &error_log_syslog, 1, &stdcollector_fd);
615
- if (fp)
616
- stderror = fp;
1497
+ char buf[1024];
1498
+ const char *s = errno2str(errnum, buf, sizeof(buf));
1499
618
-#ifdef ENABLE_ACLK
619
- if(aclklog_enabled)
620
- aclklog = open_log_file(aclklog_fd, aclklog, aclklog_filename, NULL, 0, &aclklog_fd);
621
-#endif
1500
+ if(buffer_strlen(wb))
1501
+ buffer_fast_strcat(wb, " ", 1);
1502
+
1503
+ buffer_strcat(wb, key);
1504
+ buffer_fast_strcat(wb, "=\"", 2);
1505
+ buffer_print_int64(wb, errnum);
1506
+ buffer_fast_strcat(wb, ", ", 2);
1507
+ buffer_json_strcat(wb, s);
1508
+ buffer_fast_strcat(wb, "\"", 1);
1509
+}
1510
+
1511
+static void priority_annotator(BUFFER *wb, const char *key, struct log_field *lf) {
1512
+ uint64_t pri = log_field_to_uint64(lf);
1513
623
- stdaccess = open_log_file(stdaccess_fd, stdaccess, stdaccess_filename, &access_log_syslog, 1, &stdaccess_fd);
1514
+ if(buffer_strlen(wb))
1515
+ buffer_fast_strcat(wb, " ", 1);
1516
625
- stdhealth = open_log_file(stdhealth_fd, stdhealth, stdhealth_filename, &health_log_syslog, 1, &stdhealth_fd);
1517
+ buffer_strcat(wb, key);
1518
+ buffer_fast_strcat(wb, "=", 1);
1519
+ buffer_strcat(wb, nd_log_id2priority(pri));
1520
}
1521
628
-// ----------------------------------------------------------------------------
629
-// error log throttling
1522
+static bool needs_quotes_for_logfmt(const char *s) {
1523
+ static bool safe_for_logfmt[256] = {
1524
+ [' '] = true, ['!'] = true, ['"'] = false, ['#'] = true, ['$'] = true, ['%'] = true, ['&'] = true,
1525
+ ['\''] = true, ['('] = true, [')'] = true, ['*'] = true, ['+'] = true, [','] = true, ['-'] = true,
1526
+ ['.'] = true, ['/'] = true, ['0'] = true, ['1'] = true, ['2'] = true, ['3'] = true, ['4'] = true,
1527
+ ['5'] = true, ['6'] = true, ['7'] = true, ['8'] = true, ['9'] = true, [':'] = true, [';'] = true,
1528
+ ['<'] = true, ['='] = true, ['>'] = true, ['?'] = true, ['@'] = true, ['A'] = true, ['B'] = true,
1529
+ ['C'] = true, ['D'] = true, ['E'] = true, ['F'] = true, ['G'] = true, ['H'] = true, ['I'] = true,
1530
+ ['J'] = true, ['K'] = true, ['L'] = true, ['M'] = true, ['N'] = true, ['O'] = true, ['P'] = true,
1531
+ ['Q'] = true, ['R'] = true, ['S'] = true, ['T'] = true, ['U'] = true, ['V'] = true, ['W'] = true,
1532
+ ['X'] = true, ['Y'] = true, ['Z'] = true, ['['] = true, ['\\'] = false, [']'] = true, ['^'] = true,
1533
+ ['_'] = true, ['`'] = true, ['a'] = true, ['b'] = true, ['c'] = true, ['d'] = true, ['e'] = true,
1534
+ ['f'] = true, ['g'] = true, ['h'] = true, ['i'] = true, ['j'] = true, ['k'] = true, ['l'] = true,
1535
+ ['m'] = true, ['n'] = true, ['o'] = true, ['p'] = true, ['q'] = true, ['r'] = true, ['s'] = true,
1536
+ ['t'] = true, ['u'] = true, ['v'] = true, ['w'] = true, ['x'] = true, ['y'] = true, ['z'] = true,
1537
+ ['{'] = true, ['|'] = true, ['}'] = true, ['~'] = true, [0x7f] = true,
1538
+ };
1539
+
1540
+ if(!*s)
1541
+ return true;
1542
+
1543
+ while(*s) {
1544
+ if(*s == '=' || isspace(*s) || !safe_for_logfmt[(uint8_t)*s])
1545
+ return true;
1546
+
1547
+ s++;
1548
+ }
1549
631
-time_t error_log_throttle_period = 1200;
632
-unsigned long error_log_errors_per_period = 200;
633
-unsigned long error_log_errors_per_period_backup = 0;
1550
+ return false;
1551
+}
1552
635
-int error_log_limit(int reset) {
636
- static time_t start = 0;
637
- static unsigned long counter = 0, prevented = 0;
1553
+static void string_to_logfmt(BUFFER *wb, const char *s) {
1554
+ bool spaces = needs_quotes_for_logfmt(s);
1555
639
- FILE *fp = stderror ? stderror : stderr;
1556
+ if(spaces)
1557
+ buffer_fast_strcat(wb, "\"", 1);
1558
641
- // fprintf(fp, "FLOOD: counter=%lu, allowed=%lu, backup=%lu, period=%llu\n", counter, error_log_errors_per_period, error_log_errors_per_period_backup, (unsigned long long)error_log_throttle_period);
1559
+ buffer_json_strcat(wb, s);
1560
643
- // do not throttle if the period is 0
644
- if(error_log_throttle_period == 0)
645
- return 0;
1561
+ if(spaces)
1562
+ buffer_fast_strcat(wb, "\"", 1);
1563
+}
1564
647
- // prevent all logs if the errors per period is 0
648
- if(error_log_errors_per_period == 0)
649
-#ifdef NETDATA_INTERNAL_CHECKS
650
- return 0;
651
-#else
652
- return 1;
653
-#endif
1565
+static void nd_logger_logfmt(BUFFER *wb, struct log_field *fields, size_t fields_max) {
1566
655
- time_t now = now_monotonic_sec();
656
- if(!start) start = now;
657
-
658
- if(reset) {
659
- if(prevented) {
660
- char date[LOG_DATE_LENGTH];
661
- log_date(date, LOG_DATE_LENGTH, now_realtime_sec());
662
- fprintf(
663
- fp,
664
- "%s: %s LOG FLOOD PROTECTION reset for process '%s' "
665
- "(prevented %lu logs in the last %"PRId64" seconds).\n",
666
- date,
667
- program_name,
668
- program_name,
669
- prevented,
670
- (int64_t)(now - start));
671
- }
1567
+ // --- FIELD_PARSER_VERSIONS ---
1568
+ //
1569
+ // IMPORTANT:
1570
+ // THERE ARE 6 VERSIONS OF THIS CODE
1571
+ //
1572
+ // 1. journal (direct socket API),
1573
+ // 2. journal (libsystemd API),
1574
+ // 3. logfmt,
1575
+ // 4. json,
1576
+ // 5. convert to uint64
1577
+ // 6. convert to int64
1578
+ //
1579
+ // UPDATE ALL OF THEM FOR NEW FEATURES OR FIXES
1580
673
- start = now;
674
- counter = 0;
675
- prevented = 0;
676
- }
1581
+ CLEAN_BUFFER *tmp = NULL;
1582
+
1583
+ for (size_t i = 0; i < fields_max; i++) {
1584
+ if (!fields[i].entry.set || !fields[i].logfmt)
1585
+ continue;
1586
678
- // detect if we log too much
679
- counter++;
680
-
681
- if(now - start > error_log_throttle_period) {
682
- if(prevented) {
683
- char date[LOG_DATE_LENGTH];
684
- log_date(date, LOG_DATE_LENGTH, now_realtime_sec());
685
- fprintf(
686
- fp,
687
- "%s: %s LOG FLOOD PROTECTION resuming logging from process '%s' "
688
- "(prevented %lu logs in the last %"PRId64" seconds).\n",
689
- date,
690
- program_name,
691
- program_name,
692
- prevented,
693
- (int64_t)error_log_throttle_period);
1587
+ const char *key = fields[i].logfmt;
1588
+
1589
+ if(fields[i].logfmt_annotator)
1590
+ fields[i].logfmt_annotator(wb, key, &fields[i]);
1591
+ else {
1592
+ if(buffer_strlen(wb))
1593
+ buffer_fast_strcat(wb, " ", 1);
1594
+
1595
+ switch(fields[i].entry.type) {
1596
+ case NDFT_TXT:
1597
+ if(*fields[i].entry.txt) {
1598
+ buffer_strcat(wb, key);
1599
+ buffer_fast_strcat(wb, "=", 1);
1600
+ string_to_logfmt(wb, fields[i].entry.txt);
1601
+ }
1602
+ break;
1603
+ case NDFT_STR:
1604
+ buffer_strcat(wb, key);
1605
+ buffer_fast_strcat(wb, "=", 1);
1606
+ string_to_logfmt(wb, string2str(fields[i].entry.str));
1607
+ break;
1608
+ case NDFT_BFR:
1609
+ if(buffer_strlen(fields[i].entry.bfr)) {
1610
+ buffer_strcat(wb, key);
1611
+ buffer_fast_strcat(wb, "=", 1);
1612
+ string_to_logfmt(wb, buffer_tostring(fields[i].entry.bfr));
1613
+ }
1614
+ break;
1615
+ case NDFT_U64:
1616
+ buffer_strcat(wb, key);
1617
+ buffer_fast_strcat(wb, "=", 1);
1618
+ buffer_print_uint64(wb, fields[i].entry.u64);
1619
+ break;
1620
+ case NDFT_I64:
1621
+ buffer_strcat(wb, key);
1622
+ buffer_fast_strcat(wb, "=", 1);
1623
+ buffer_print_int64(wb, fields[i].entry.i64);
1624
+ break;
1625
+ case NDFT_DBL:
1626
+ buffer_strcat(wb, key);
1627
+ buffer_fast_strcat(wb, "=", 1);
1628
+ buffer_print_netdata_double(wb, fields[i].entry.dbl);
1629
+ break;
1630
+ case NDFT_UUID: {
1631
+ char u[UUID_COMPACT_STR_LEN];
1632
+ uuid_unparse_lower_compact(*fields[i].entry.uuid, u);
1633
+ buffer_strcat(wb, key);
1634
+ buffer_fast_strcat(wb, "=", 1);
1635
+ buffer_fast_strcat(wb, u, sizeof(u) - 1);
1636
+ }
1637
+ break;
1638
+ case NDFT_CALLBACK: {
1639
+ if(!tmp)
1640
+ tmp = buffer_create(1024, NULL);
1641
+ else
1642
+ buffer_flush(tmp);
1643
+ if(fields[i].entry.cb.formatter(tmp, fields[i].entry.cb.formatter_data)) {
1644
+ buffer_strcat(wb, key);
1645
+ buffer_fast_strcat(wb, "=", 1);
1646
+ string_to_logfmt(wb, buffer_tostring(tmp));
1647
+ }
1648
+ }
1649
+ break;
1650
+ default:
1651
+ buffer_strcat(wb, "UNHANDLED");
1652
+ break;
1653
+ }
1654
}
1655
+ }
1656
+}
1657
696
- // restart the period accounting
697
- start = now;
698
- counter = 1;
699
- prevented = 0;
1658
+// ----------------------------------------------------------------------------
1659
+// journal logger
1660
701
- // log this error
702
- return 0;
1661
+bool nd_log_journal_socket_available(void) {
1662
+ if(netdata_configured_host_prefix && *netdata_configured_host_prefix) {
1663
+ char filename[FILENAME_MAX + 1];
1664
+
1665
+ snprintfz(filename, sizeof(filename), "%s%s",
1666
+ netdata_configured_host_prefix, "/run/systemd/journal/socket");
1667
+
1668
+ if(is_path_unix_socket(filename))
1669
+ return true;
1670
}
1671
705
- if(counter > error_log_errors_per_period) {
706
- if(!prevented) {
707
- char date[LOG_DATE_LENGTH];
708
- log_date(date, LOG_DATE_LENGTH, now_realtime_sec());
709
- fprintf(
710
- fp,
711
- "%s: %s LOG FLOOD PROTECTION too many logs (%lu logs in %"PRId64" seconds, threshold is set to %lu logs "
712
- "in %"PRId64" seconds). Preventing more logs from process '%s' for %"PRId64" seconds.\n",
713
- date,
714
- program_name,
715
- counter,
716
- (int64_t)(now - start),
717
- error_log_errors_per_period,
718
- (int64_t)error_log_throttle_period,
719
- program_name,
720
- (int64_t)(start + error_log_throttle_period - now));
1672
+ return is_path_unix_socket("/run/systemd/journal/socket");
1673
+}
1674
+
1675
+static bool nd_logger_journal_libsystemd(struct log_field *fields, size_t fields_max) {
1676
+#ifdef HAVE_SYSTEMD
1677
+
1678
+ // --- FIELD_PARSER_VERSIONS ---
1679
+ //
1680
+ // IMPORTANT:
1681
+ // THERE ARE 6 VERSIONS OF THIS CODE
1682
+ //
1683
+ // 1. journal (direct socket API),
1684
+ // 2. journal (libsystemd API),
1685
+ // 3. logfmt,
1686
+ // 4. json,
1687
+ // 5. convert to uint64
1688
+ // 6. convert to int64
1689
+ //
1690
+ // UPDATE ALL OF THEM FOR NEW FEATURES OR FIXES
1691
+
1692
+ struct iovec iov[fields_max];
1693
+ int iov_count = 0;
1694
+
1695
+ memset(iov, 0, sizeof(iov));
1696
+
1697
+ CLEAN_BUFFER *tmp = NULL;
1698
+
1699
+ for (size_t i = 0; i < fields_max; i++) {
1700
+ if (!fields[i].entry.set || !fields[i].journal)
1701
+ continue;
1702
+
1703
+ const char *key = fields[i].journal;
1704
+ char *value = NULL;
1705
+ switch (fields[i].entry.type) {
1706
+ case NDFT_TXT:
1707
+ if(*fields[i].entry.txt)
1708
+ asprintf(&value, "%s=%s", key, fields[i].entry.txt);
1709
+ break;
1710
+ case NDFT_STR:
1711
+ asprintf(&value, "%s=%s", key, string2str(fields[i].entry.str));
1712
+ break;
1713
+ case NDFT_BFR:
1714
+ if(buffer_strlen(fields[i].entry.bfr))
1715
+ asprintf(&value, "%s=%s", key, buffer_tostring(fields[i].entry.bfr));
1716
+ break;
1717
+ case NDFT_U64:
1718
+ asprintf(&value, "%s=%" PRIu64, key, fields[i].entry.u64);
1719
+ break;
1720
+ case NDFT_I64:
1721
+ asprintf(&value, "%s=%" PRId64, key, fields[i].entry.i64);
1722
+ break;
1723
+ case NDFT_DBL:
1724
+ asprintf(&value, "%s=%f", key, fields[i].entry.dbl);
1725
+ break;
1726
+ case NDFT_UUID: {
1727
+ char u[UUID_COMPACT_STR_LEN];
1728
+ uuid_unparse_lower_compact(*fields[i].entry.uuid, u);
1729
+ asprintf(&value, "%s=%s", key, u);
1730
+ }
1731
+ break;
1732
+ case NDFT_CALLBACK: {
1733
+ if(!tmp)
1734
+ tmp = buffer_create(1024, NULL);
1735
+ else
1736
+ buffer_flush(tmp);
1737
+ if(fields[i].entry.cb.formatter(tmp, fields[i].entry.cb.formatter_data))
1738
+ asprintf(&value, "%s=%s", key, buffer_tostring(tmp));
1739
+ }
1740
+ break;
1741
+ default:
1742
+ asprintf(&value, "%s=%s", key, "UNHANDLED");
1743
+ break;
1744
+ }
1745
+
1746
+ if (value) {
1747
+ iov[iov_count].iov_base = value;
1748
+ iov[iov_count].iov_len = strlen(value);
1749
+ iov_count++;
1750
}
1751
+ }
1752
723
- prevented++;
1753
+ int r = sd_journal_sendv(iov, iov_count);
1754
725
- // prevent logging this error
726
-#ifdef NETDATA_INTERNAL_CHECKS
727
- return 0;
728
-#else
729
- return 1;
730
-#endif
1755
+ // Clean up allocated memory
1756
+ for (int i = 0; i < iov_count; i++) {
1757
+ if (iov[i].iov_base != NULL) {
1758
+ free(iov[i].iov_base);
1759
+ }
1760
}
1761
733
- return 0;
1762
+ return r == 0;
1763
+#else
1764
+ return false;
1765
+#endif
1766
}
1767
736
-void error_log_limit_reset(void) {
737
- log_lock();
1768
+static bool nd_logger_journal_direct(struct log_field *fields, size_t fields_max) {
1769
+ if(!nd_log.journal_direct.initialized)
1770
+ return false;
1771
+
1772
+ // --- FIELD_PARSER_VERSIONS ---
1773
+ //
1774
+ // IMPORTANT:
1775
+ // THERE ARE 6 VERSIONS OF THIS CODE
1776
+ //
1777
+ // 1. journal (direct socket API),
1778
+ // 2. journal (libsystemd API),
1779
+ // 3. logfmt,
1780
+ // 4. json,
1781
+ // 5. convert to uint64
1782
+ // 6. convert to int64
1783
+ //
1784
+ // UPDATE ALL OF THEM FOR NEW FEATURES OR FIXES
1785
+
1786
+ CLEAN_BUFFER *wb = buffer_create(4096, NULL);
1787
+ CLEAN_BUFFER *tmp = NULL;
1788
+
1789
+ for (size_t i = 0; i < fields_max; i++) {
1790
+ if (!fields[i].entry.set || !fields[i].journal)
1791
+ continue;
1792
+
1793
+ const char *key = fields[i].journal;
1794
+
1795
+ const char *s = NULL;
1796
+ switch(fields[i].entry.type) {
1797
+ case NDFT_TXT:
1798
+ s = fields[i].entry.txt;
1799
+ break;
1800
+ case NDFT_STR:
1801
+ s = string2str(fields[i].entry.str);
1802
+ break;
1803
+ case NDFT_BFR:
1804
+ s = buffer_tostring(fields[i].entry.bfr);
1805
+ break;
1806
+ case NDFT_U64:
1807
+ buffer_strcat(wb, key);
1808
+ buffer_putc(wb, '=');
1809
+ buffer_print_uint64(wb, fields[i].entry.u64);
1810
+ buffer_putc(wb, '\n');
1811
+ break;
1812
+ case NDFT_I64:
1813
+ buffer_strcat(wb, key);
1814
+ buffer_putc(wb, '=');
1815
+ buffer_print_int64(wb, fields[i].entry.i64);
1816
+ buffer_putc(wb, '\n');
1817
+ break;
1818
+ case NDFT_DBL:
1819
+ buffer_strcat(wb, key);
1820
+ buffer_putc(wb, '=');
1821
+ buffer_print_netdata_double(wb, fields[i].entry.dbl);
1822
+ buffer_putc(wb, '\n');
1823
+ break;
1824
+ case NDFT_UUID:{
1825
+ char u[UUID_COMPACT_STR_LEN];
1826
+ uuid_unparse_lower_compact(*fields[i].entry.uuid, u);
1827
+ buffer_strcat(wb, key);
1828
+ buffer_putc(wb, '=');
1829
+ buffer_fast_strcat(wb, u, sizeof(u) - 1);
1830
+ buffer_putc(wb, '\n');
1831
+ }
1832
+ break;
1833
+ case NDFT_CALLBACK: {
1834
+ if(!tmp)
1835
+ tmp = buffer_create(1024, NULL);
1836
+ else
1837
+ buffer_flush(tmp);
1838
+ if(fields[i].entry.cb.formatter(tmp, fields[i].entry.cb.formatter_data))
1839
+ s = buffer_tostring(tmp);
1840
+ else
1841
+ s = NULL;
1842
+ }
1843
+ break;
1844
+ default:
1845
+ s = "UNHANDLED";
1846
+ break;
1847
+ }
1848
739
- error_log_errors_per_period = error_log_errors_per_period_backup;
740
- error_log_limit(1);
1849
+ if(s && *s) {
1850
+ buffer_strcat(wb, key);
1851
+ if(!strchr(s, '\n')) {
1852
+ buffer_putc(wb, '=');
1853
+ buffer_strcat(wb, s);
1854
+ buffer_putc(wb, '\n');
1855
+ }
1856
+ else {
1857
+ buffer_putc(wb, '\n');
1858
+ size_t size = strlen(s);
1859
+ uint64_t le_size = htole64(size);
1860
+ buffer_memcat(wb, &le_size, sizeof(le_size));
1861
+ buffer_memcat(wb, s, size);
1862
+ buffer_putc(wb, '\n');
1863
+ }
1864
+ }
1865
+ }
1866
742
- log_unlock();
1867
+ return journal_direct_send(nd_log.journal_direct.fd, buffer_tostring(wb), buffer_strlen(wb));
1868
}
1869
745
-void error_log_limit_unlimited(void) {
746
- log_lock();
1870
+// ----------------------------------------------------------------------------
1871
+// syslog logger - uses logfmt
1872
748
- error_log_errors_per_period = error_log_errors_per_period_backup;
749
- error_log_limit(1);
1873
+static bool nd_logger_syslog(int priority, ND_LOG_OUTPUT_FORMAT format, struct log_field *fields, size_t fields_max) {
1874
+ CLEAN_BUFFER *wb = buffer_create(1024, NULL);
1875
751
- error_log_errors_per_period = ((error_log_errors_per_period_backup * 10) < 10000) ? 10000 : (error_log_errors_per_period_backup * 10);
1876
+ nd_logger_logfmt(wb, fields, fields_max);
1877
+ syslog(priority, "%s", buffer_tostring(wb));
1878
753
- log_unlock();
1879
+ return true;
1880
}
1881
1882
// ----------------------------------------------------------------------------
757
-// debug log
758
-
759
-void debug_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) {
760
- va_list args;
1883
+// file logger - uses logfmt
1884
762
- char date[LOG_DATE_LENGTH];
763
- log_date(date, LOG_DATE_LENGTH, now_realtime_sec());
1885
+static bool nd_logger_file(FILE *fp, ND_LOG_OUTPUT_FORMAT format, struct log_field *fields, size_t fields_max) {
1886
+ BUFFER *wb = buffer_create(1024, NULL);
1887
765
- va_start( args, fmt );
766
- printf("%s: %s DEBUG : %s : (%04lu@%-20.20s:%-15.15s): ", date, program_name, netdata_thread_tag(), line, file, function);
767
- vprintf(fmt, args);
768
- va_end( args );
769
- putchar('\n');
1888
+ if(format == NDLOF_JSON)
1889
+ nd_logger_json(wb, fields, fields_max);
1890
+ else
1891
+ nd_logger_logfmt(wb, fields, fields_max);
1892
771
- if(output_log_syslog) {
772
- va_start( args, fmt );
773
- vsyslog(LOG_ERR, fmt, args );
774
- va_end( args );
775
- }
1893
+ int r = fprintf(fp, "%s\n", buffer_tostring(wb));
1894
+ fflush(fp);
1895
777
- fflush(stdout);
1896
+ buffer_free(wb);
1897
+ return r > 0;
1898
}
1899
1900
// ----------------------------------------------------------------------------
781
-// info log
1901
+// logger router
1902
+
1903
+static ND_LOG_OUTPUT nd_logger_select_output(ND_LOG_SOURCES source, FILE **fpp, SPINLOCK **spinlock) {
1904
+ *spinlock = NULL;
1905
+ ND_LOG_OUTPUT output = nd_log.sources[source].method;
1906
+
1907
+ switch(output) {
1908
+ case NDLO_JOURNAL:
1909
+ if(unlikely(!nd_log.journal_direct.initialized && !nd_log.journal.initialized)) {
1910
+ output = NDLO_FILE;
1911
+ *fpp = stderr;
1912
+ *spinlock = &nd_log.std_error.spinlock;
1913
+ }
1914
+ else {
1915
+ *fpp = NULL;
1916
+ *spinlock = NULL;
1917
+ }
1918
+ break;
1919
783
-void info_int( int is_collector, const char *file __maybe_unused, const char *function __maybe_unused, const unsigned long line __maybe_unused, const char *fmt, ... )
784
-{
785
-#if !defined(NETDATA_INTERNAL_CHECKS) && !defined(NETDATA_DEV_MODE)
786
- if (NETDATA_LOG_LEVEL_INFO > global_log_severity_level)
787
- return;
788
-#endif
1920
+ case NDLO_SYSLOG:
1921
+ if(unlikely(!nd_log.syslog.initialized)) {
1922
+ output = NDLO_FILE;
1923
+ *spinlock = &nd_log.std_error.spinlock;
1924
+ *fpp = stderr;
1925
+ }
1926
+ else {
1927
+ *spinlock = NULL;
1928
+ *fpp = NULL;
1929
+ }
1930
+ break;
1931
790
- va_list args;
791
- FILE *fp = (is_collector || !stderror) ? stderr : stderror;
1932
+ case NDLO_FILE:
1933
+ if(!nd_log.sources[source].fp) {
1934
+ *fpp = stderr;
1935
+ *spinlock = &nd_log.std_error.spinlock;
1936
+ }
1937
+ else {
1938
+ *fpp = nd_log.sources[source].fp;
1939
+ *spinlock = &nd_log.sources[source].spinlock;
1940
+ }
1941
+ break;
1942
793
- log_lock();
1943
+ case NDLO_STDOUT:
1944
+ output = NDLO_FILE;
1945
+ *fpp = stdout;
1946
+ *spinlock = &nd_log.std_output.spinlock;
1947
+ break;
1948
795
- // prevent logging too much
796
- if (error_log_limit(0)) {
797
- log_unlock();
798
- return;
1949
+ default:
1950
+ case NDLO_DEFAULT:
1951
+ case NDLO_STDERR:
1952
+ output = NDLO_FILE;
1953
+ *fpp = stderr;
1954
+ *spinlock = &nd_log.std_error.spinlock;
1955
+ break;
1956
+
1957
+ case NDLO_DISABLED:
1958
+ case NDLO_DEVNULL:
1959
+ output = NDLO_DISABLED;
1960
+ *fpp = NULL;
1961
+ *spinlock = NULL;
1962
+ break;
1963
}
1964
801
- if(collector_log_syslog) {
802
- va_start( args, fmt );
803
- vsyslog(LOG_INFO, fmt, args );
804
- va_end( args );
1965
+ return output;
1966
+}
1967
+
1968
+// ----------------------------------------------------------------------------
1969
+// high level logger
1970
+
1971
+static void nd_logger_log_fields(SPINLOCK *spinlock, FILE *fp, bool limit, ND_LOG_FIELD_PRIORITY priority,
1972
+ ND_LOG_OUTPUT output, struct nd_log_source *source,
1973
+ struct log_field *fields, size_t fields_max) {
1974
+ if(spinlock)
1975
+ spinlock_lock(spinlock);
1976
+
1977
+ // check the limits
1978
+ if(limit && nd_log_limit_reached(source))
1979
+ goto cleanup;
1980
+
1981
+ if(output == NDLO_JOURNAL) {
1982
+ if(!nd_logger_journal_direct(fields, fields_max) && !nd_logger_journal_libsystemd(fields, fields_max)) {
1983
+ // we can't log to journal, let's log to stderr
1984
+ if(spinlock)
1985
+ spinlock_unlock(spinlock);
1986
+
1987
+ output = NDLO_FILE;
1988
+ spinlock = &nd_log.std_error.spinlock;
1989
+ fp = stderr;
1990
+
1991
+ if(spinlock)
1992
+ spinlock_lock(spinlock);
1993
+ }
1994
}
1995
807
- char date[LOG_DATE_LENGTH];
808
- log_date(date, LOG_DATE_LENGTH, now_realtime_sec());
1996
+ if(output == NDLO_SYSLOG)
1997
+ nd_logger_syslog(priority, source->format, fields, fields_max);
1998
810
- va_start( args, fmt );
811
-#ifdef NETDATA_INTERNAL_CHECKS
812
- fprintf(fp, "%s: %s INFO : %s : (%04lu@%-20.20s:%-15.15s): ",
813
- date, program_name, netdata_thread_tag(), line, file, function);
814
-#else
815
- fprintf(fp, "%s: %s INFO : %s : ", date, program_name, netdata_thread_tag());
816
-#endif
817
- vfprintf(fp, fmt, args );
818
- va_end( args );
1999
+ if(output == NDLO_FILE)
2000
+ nd_logger_file(fp, source->format, fields, fields_max);
2001
820
- fputc('\n', fp);
2002
822
- log_unlock();
2003
+cleanup:
2004
+ if(spinlock)
2005
+ spinlock_unlock(spinlock);
2006
}
2007
825
-// ----------------------------------------------------------------------------
826
-// error log
2008
+static void nd_logger_unset_all_thread_fields(void) {
2009
+ size_t fields_max = THREAD_FIELDS_MAX;
2010
+ for(size_t i = 0; i < fields_max ; i++)
2011
+ thread_log_fields[i].entry.set = false;
2012
+}
2013
828
-#if defined(STRERROR_R_CHAR_P)
829
-// GLIBC version of strerror_r
830
-static const char *strerror_result(const char *a, const char *b) { (void)b; return a; }
831
-#elif defined(HAVE_STRERROR_R)
832
-// POSIX version of strerror_r
833
-static const char *strerror_result(int a, const char *b) { (void)a; return b; }
834
-#elif defined(HAVE_C__GENERIC)
2014
+static void nd_logger_merge_log_stack_to_thread_fields(void) {
2015
+ for(size_t c = 0; c < thread_log_stack_next ;c++) {
2016
+ struct log_stack_entry *lgs = thread_log_stack_base[c];
2017
836
-// what a trick!
837
-// http://stackoverflow.com/questions/479207/function-overloading-in-c
838
-static const char *strerror_result_int(int a, const char *b) { (void)a; return b; }
839
-static const char *strerror_result_string(const char *a, const char *b) { (void)b; return a; }
2018
+ for(size_t i = 0; lgs[i].id != NDF_STOP ; i++) {
2019
+ if(lgs[i].id >= _NDF_MAX || !lgs[i].set)
2020
+ continue;
2021
841
-#define strerror_result(a, b) _Generic((a), \
842
- int: strerror_result_int, \
843
- char *: strerror_result_string \
844
- )(a, b)
2022
+ struct log_stack_entry *e = &lgs[i];
2023
+ ND_LOG_STACK_FIELD_TYPE type = lgs[i].type;
2024
846
-#else
847
-#error "cannot detect the format of function strerror_r()"
848
-#endif
2025
+ // do not add empty / unset fields
2026
+ if((type == NDFT_TXT && (!e->txt || !*e->txt)) ||
2027
+ (type == NDFT_BFR && (!e->bfr || !buffer_strlen(e->bfr))) ||
2028
+ (type == NDFT_STR && !e->str) ||
2029
+ (type == NDFT_UUID && !e->uuid) ||
2030
+ (type == NDFT_CALLBACK && !e->cb.formatter) ||
2031
+ type == NDFT_UNSET)
2032
+ continue;
2033
850
-void error_limit_int(ERROR_LIMIT *erl, const char *prefix, const char *file __maybe_unused, const char *function __maybe_unused, const unsigned long line __maybe_unused, const char *fmt, ... ) {
851
- FILE *fp = stderror ? stderror : stderr;
2034
+ thread_log_fields[lgs[i].id].entry = *e;
2035
+ }
2036
+ }
2037
+}
2038
853
- if(erl->sleep_ut)
854
- sleep_usec(erl->sleep_ut);
2039
+static void nd_logger(const char *file, const char *function, const unsigned long line,
2040
+ ND_LOG_SOURCES source, ND_LOG_FIELD_PRIORITY priority, bool limit, int saved_errno,
2041
+ const char *fmt, va_list ap) {
2042
856
- // save a copy of errno - just in case this function generates a new error
857
- int __errno = errno;
2043
+ SPINLOCK *spinlock;
2044
+ FILE *fp;
2045
+ ND_LOG_OUTPUT output = nd_logger_select_output(source, &fp, &spinlock);
2046
+ if(output != NDLO_FILE && output != NDLO_JOURNAL && output != NDLO_SYSLOG)
2047
+ return;
2048
859
- va_list args;
2049
+ // mark all fields as unset
2050
+ nd_logger_unset_all_thread_fields();
2051
861
- log_lock();
2052
+ // flatten the log stack into the fields
2053
+ nd_logger_merge_log_stack_to_thread_fields();
2054
863
- erl->count++;
864
- time_t now = now_boottime_sec();
865
- if(now - erl->last_logged < erl->log_every) {
866
- log_unlock();
867
- return;
868
- }
2055
+ // set the common fields that are automatically set by the logging subsystem
2056
870
- // prevent logging too much
871
- if (error_log_limit(0)) {
872
- log_unlock();
873
- return;
2057
+ if(likely(!thread_log_fields[NDF_INVOCATION_ID].entry.set))
2058
+ thread_log_fields[NDF_INVOCATION_ID].entry = ND_LOG_FIELD_UUID(NDF_INVOCATION_ID, &nd_log.invocation_id);
2059
+
2060
+ if(likely(!thread_log_fields[NDF_LOG_SOURCE].entry.set))
2061
+ thread_log_fields[NDF_LOG_SOURCE].entry = ND_LOG_FIELD_TXT(NDF_LOG_SOURCE, nd_log_source2str(source));
2062
+
2063
+ if(likely(!thread_log_fields[NDF_SYSLOG_IDENTIFIER].entry.set))
2064
+ thread_log_fields[NDF_SYSLOG_IDENTIFIER].entry = ND_LOG_FIELD_TXT(NDF_SYSLOG_IDENTIFIER, program_name);
2065
+
2066
+ if(likely(!thread_log_fields[NDF_LINE].entry.set)) {
2067
+ thread_log_fields[NDF_LINE].entry = ND_LOG_FIELD_U64(NDF_LINE, line);
2068
+ thread_log_fields[NDF_FILE].entry = ND_LOG_FIELD_TXT(NDF_FILE, file);
2069
+ thread_log_fields[NDF_FUNC].entry = ND_LOG_FIELD_TXT(NDF_FUNC, function);
2070
}
2071
876
- if(collector_log_syslog) {
877
- va_start( args, fmt );
878
- vsyslog(LOG_ERR, fmt, args );
879
- va_end( args );
2072
+ if(likely(!thread_log_fields[NDF_PRIORITY].entry.set)) {
2073
+ thread_log_fields[NDF_PRIORITY].entry = ND_LOG_FIELD_U64(NDF_PRIORITY, priority);
2074
}
2075
882
- char date[LOG_DATE_LENGTH];
883
- log_date(date, LOG_DATE_LENGTH, now_realtime_sec());
2076
+ if(likely(!thread_log_fields[NDF_TID].entry.set))
2077
+ thread_log_fields[NDF_TID].entry = ND_LOG_FIELD_U64(NDF_TID, gettid());
2078
+
2079
+ if(likely(!thread_log_fields[NDF_THREAD_TAG].entry.set)) {
2080
+ char os_threadname[NETDATA_THREAD_NAME_MAX + 1];
2081
+ const char *thread_tag = netdata_thread_tag();
2082
+ if(!netdata_thread_tag_exists()) {
2083
+ if (!netdata_thread_tag_exists()) {
2084
+ os_thread_get_current_name_np(os_threadname);
2085
+ if ('\0' != os_threadname[0])
2086
+ /* If it is not an empty string replace "MAIN" thread_tag */
2087
+ thread_tag = os_threadname;
2088
+ }
2089
+ }
2090
+ thread_log_fields[NDF_THREAD_TAG].entry = ND_LOG_FIELD_TXT(NDF_THREAD_TAG, thread_tag);
2091
885
- va_start( args, fmt );
886
-#ifdef NETDATA_INTERNAL_CHECKS
887
- fprintf(fp, "%s: %s %-5.5s : %s : (%04lu@%-20.20s:%-15.15s): ",
888
- date, program_name, prefix, netdata_thread_tag(), line, file, function);
889
-#else
890
- fprintf(fp, "%s: %s %-5.5s : %s : ", date, program_name, prefix, netdata_thread_tag());
891
-#endif
892
- vfprintf(fp, fmt, args );
893
- va_end( args );
2092
+ // TODO: fix the ND_MODULE in logging by setting proper module name in threads
2093
+// if(!thread_log_fields[NDF_MODULE].entry.set)
2094
+// thread_log_fields[NDF_MODULE].entry = ND_LOG_FIELD_CB(NDF_MODULE, thread_tag_to_module, (void *)thread_tag);
2095
+ }
2096
895
- if(erl->count > 1)
896
- fprintf(fp, " (similar messages repeated %zu times in the last %llu secs)",
897
- erl->count, (unsigned long long)(erl->last_logged ? now - erl->last_logged : 0));
2097
+ if(likely(!thread_log_fields[NDF_TIMESTAMP_REALTIME_USEC].entry.set))
2098
+ thread_log_fields[NDF_TIMESTAMP_REALTIME_USEC].entry = ND_LOG_FIELD_U64(NDF_TIMESTAMP_REALTIME_USEC, now_realtime_usec());
2099
899
- if(erl->sleep_ut)
900
- fprintf(fp, " (sleeping for %"PRIu64" microseconds every time this happens)", erl->sleep_ut);
2100
+ if(saved_errno != 0 && !thread_log_fields[NDF_ERRNO].entry.set)
2101
+ thread_log_fields[NDF_ERRNO].entry = ND_LOG_FIELD_I64(NDF_ERRNO, saved_errno);
2102
902
- if(__errno) {
903
- char buf[1024];
904
- fprintf(fp,
905
- " (errno %d, %s)\n", __errno, strerror_result(strerror_r(__errno, buf, 1023), buf));
906
- errno = 0;
2103
+ CLEAN_BUFFER *wb = NULL;
2104
+ if(fmt && !thread_log_fields[NDF_MESSAGE].entry.set) {
2105
+ wb = buffer_create(1024, NULL);
2106
+ buffer_vsprintf(wb, fmt, ap);
2107
+ thread_log_fields[NDF_MESSAGE].entry = ND_LOG_FIELD_TXT(NDF_MESSAGE, buffer_tostring(wb));
2108
}
908
- else
909
- fputc('\n', fp);
910
-
911
- erl->last_logged = now;
912
- erl->count = 0;
2109
914
- log_unlock();
915
-}
2110
+ nd_logger_log_fields(spinlock, fp, limit, priority, output, &nd_log.sources[source],
2111
+ thread_log_fields, THREAD_FIELDS_MAX);
2112
917
-void error_int(int is_collector, const char *prefix, const char *file __maybe_unused, const char *function __maybe_unused, const unsigned long line __maybe_unused, const char *fmt, ... ) {
918
-#if !defined(NETDATA_INTERNAL_CHECKS) && !defined(NETDATA_DEV_MODE)
919
- if (NETDATA_LOG_LEVEL_ERROR > global_log_severity_level)
920
- return;
921
-#endif
2113
+ if(nd_log.sources[source].pending_msg) {
2114
+ // log a pending message
2115
923
- // save a copy of errno - just in case this function generates a new error
924
- int __errno = errno;
925
- FILE *fp = (is_collector || !stderror) ? stderr : stderror;
2116
+ nd_logger_unset_all_thread_fields();
2117
927
- va_list args;
2118
+ thread_log_fields[NDF_TIMESTAMP_REALTIME_USEC].entry = (struct log_stack_entry){
2119
+ .set = true,
2120
+ .type = NDFT_U64,
2121
+ .u64 = now_realtime_usec(),
2122
+ };
2123
929
- log_lock();
2124
+ thread_log_fields[NDF_LOG_SOURCE].entry = (struct log_stack_entry){
2125
+ .set = true,
2126
+ .type = NDFT_TXT,
2127
+ .txt = nd_log_source2str(source),
2128
+ };
2129
931
- // prevent logging too much
932
- if (error_log_limit(0)) {
933
- log_unlock();
934
- return;
935
- }
2130
+ thread_log_fields[NDF_SYSLOG_IDENTIFIER].entry = (struct log_stack_entry){
2131
+ .set = true,
2132
+ .type = NDFT_TXT,
2133
+ .txt = program_name,
2134
+ };
2135
937
- if(collector_log_syslog) {
938
- va_start( args, fmt );
939
- vsyslog(LOG_ERR, fmt, args );
940
- va_end( args );
941
- }
2136
+ thread_log_fields[NDF_MESSAGE].entry = (struct log_stack_entry){
2137
+ .set = true,
2138
+ .type = NDFT_TXT,
2139
+ .txt = nd_log.sources[source].pending_msg,
2140
+ };
2141
943
- char date[LOG_DATE_LENGTH];
944
- log_date(date, LOG_DATE_LENGTH, now_realtime_sec());
2142
+ nd_logger_log_fields(spinlock, fp, false, priority, output,
2143
+ &nd_log.sources[source],
2144
+ thread_log_fields, THREAD_FIELDS_MAX);
2145
946
- va_start( args, fmt );
947
-#ifdef NETDATA_INTERNAL_CHECKS
948
- fprintf(fp, "%s: %s %-5.5s : %s : (%04lu@%-20.20s:%-15.15s): ",
949
- date, program_name, prefix, netdata_thread_tag(), line, file, function);
950
-#else
951
- fprintf(fp, "%s: %s %-5.5s : %s : ", date, program_name, prefix, netdata_thread_tag());
952
-#endif
953
- vfprintf(fp, fmt, args );
954
- va_end( args );
955
-
956
- if(__errno) {
957
- char buf[1024];
958
- fprintf(fp,
959
- " (errno %d, %s)\n", __errno, strerror_result(strerror_r(__errno, buf, 1023), buf));
960
- errno = 0;
2146
+ freez((void *)nd_log.sources[source].pending_msg);
2147
+ nd_log.sources[source].pending_msg = NULL;
2148
}
962
- else
963
- fputc('\n', fp);
2149
965
- log_unlock();
2150
+ errno = 0;
2151
}
2152
968
-#ifdef NETDATA_INTERNAL_CHECKS
969
-static void crash_netdata(void) {
970
- // make Netdata core dump
971
- abort();
972
-}
973
-#endif
2153
+static ND_LOG_SOURCES nd_log_validate_source(ND_LOG_SOURCES source) {
2154
+ if(source >= _NDLS_MAX)
2155
+ source = NDLS_DAEMON;
2156
975
-#ifdef HAVE_BACKTRACE
976
-#define BT_BUF_SIZE 100
977
-static void print_call_stack(void) {
978
- FILE *fp = (!stderror) ? stderr : stderror;
2157
+ if(overwrite_thread_source)
2158
+ source = overwrite_thread_source;
2159
980
- int nptrs;
981
- void *buffer[BT_BUF_SIZE];
2160
+ if(nd_log.overwrite_process_source)
2161
+ source = nd_log.overwrite_process_source;
2162
983
- nptrs = backtrace(buffer, BT_BUF_SIZE);
984
- if(nptrs)
985
- backtrace_symbols_fd(buffer, nptrs, fileno(fp));
2163
+ return source;
2164
}
987
-#endif
2165
989
-void fatal_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) {
990
- FILE *fp = stderror ? stderror : stderr;
2166
+// ----------------------------------------------------------------------------
2167
+// public API for loggers
2168
+
2169
+void netdata_logger(ND_LOG_SOURCES source, ND_LOG_FIELD_PRIORITY priority, const char *file, const char *function, unsigned long line, const char *fmt, ... ) {
2170
+ int saved_errno = errno;
2171
+ source = nd_log_validate_source(source);
2172
+
2173
+ if((source == NDLS_DAEMON || source == NDLS_COLLECTORS) && priority > nd_log.sources[source].min_priority)
2174
+ return;
2175
992
- // save a copy of errno - just in case this function generates a new error
993
- int __errno = errno;
2176
va_list args;
995
- const char *thread_tag;
996
- char os_threadname[NETDATA_THREAD_NAME_MAX + 1];
2177
+ va_start(args, fmt);
2178
+ nd_logger(file, function, line, source, priority,
2179
+ source == NDLS_DAEMON || source == NDLS_COLLECTORS,
2180
+ saved_errno, fmt, args);
2181
+ va_end(args);
2182
+}
2183
998
- if(collector_log_syslog) {
999
- va_start( args, fmt );
1000
- vsyslog(LOG_CRIT, fmt, args );
1001
- va_end( args );
1002
- }
2184
+void netdata_logger_with_limit(ERROR_LIMIT *erl, ND_LOG_SOURCES source, ND_LOG_FIELD_PRIORITY priority, const char *file __maybe_unused, const char *function __maybe_unused, const unsigned long line __maybe_unused, const char *fmt, ... ) {
2185
+ int saved_errno = errno;
2186
+ source = nd_log_validate_source(source);
2187
1004
- thread_tag = netdata_thread_tag();
1005
- if (!netdata_thread_tag_exists()) {
1006
- os_thread_get_current_name_np(os_threadname);
1007
- if ('\0' != os_threadname[0]) { /* If it is not an empty string replace "MAIN" thread_tag */
1008
- thread_tag = os_threadname;
1009
- }
2188
+ if(erl->sleep_ut)
2189
+ sleep_usec(erl->sleep_ut);
2190
+
2191
+ spinlock_lock(&erl->spinlock);
2192
+
2193
+ erl->count++;
2194
+ time_t now = now_boottime_sec();
2195
+ if(now - erl->last_logged < erl->log_every) {
2196
+ spinlock_unlock(&erl->spinlock);
2197
+ return;
2198
}
2199
1012
- char date[LOG_DATE_LENGTH];
1013
- log_date(date, LOG_DATE_LENGTH, now_realtime_sec());
2200
+ spinlock_unlock(&erl->spinlock);
2201
1015
- log_lock();
2202
+ va_list args;
2203
+ va_start(args, fmt);
2204
+ nd_logger(file, function, line, source, priority,
2205
+ source == NDLS_DAEMON || source == NDLS_COLLECTORS,
2206
+ saved_errno, fmt, args);
2207
+ va_end(args);
2208
+}
2209
1017
- va_start( args, fmt );
1018
-#ifdef NETDATA_INTERNAL_CHECKS
1019
- fprintf(fp,
1020
- "%s: %s FATAL : %s : (%04lu@%-20.20s:%-15.15s): ", date, program_name, thread_tag, line, file, function);
1021
-#else
1022
- fprintf(fp, "%s: %s FATAL : %s : ", date, program_name, thread_tag);
1023
-#endif
1024
- vfprintf(fp, fmt, args );
1025
- va_end( args );
2210
+void netdata_logger_fatal( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) {
2211
+ int saved_errno = errno;
2212
+ ND_LOG_SOURCES source = NDLS_DAEMON;
2213
+ source = nd_log_validate_source(source);
2214
1027
- perror(" # ");
1028
- fputc('\n', fp);
2215
+ va_list args;
2216
+ va_start(args, fmt);
2217
+ nd_logger(file, function, line, source, NDLP_ALERT, true, saved_errno, fmt, args);
2218
+ va_end(args);
2219
1030
- log_unlock();
2220
+ char date[LOG_DATE_LENGTH];
2221
+ log_date(date, LOG_DATE_LENGTH, now_realtime_sec());
2222
2223
char action_data[70+1];
1033
- snprintfz(action_data, 70, "%04lu@%-10.10s:%-15.15s/%d", line, file, function, __errno);
2224
+ snprintfz(action_data, 70, "%04lu@%-10.10s:%-15.15s/%d", line, file, function, saved_errno);
2225
char action_result[60+1];
2226
2227
+ const char *thread_tag = thread_log_fields[NDF_THREAD_TAG].entry.txt;
2228
+ if(!thread_tag)
2229
+ thread_tag = "UNKNOWN";
2230
+
2231
const char *tag_to_send = thread_tag;
2232
2233
// anonymize thread names
2240
send_statistics("FATAL", action_result, action_data);
2241
2242
#ifdef HAVE_BACKTRACE
1048
- print_call_stack();
2243
+ int fd = nd_log.sources[NDLS_DAEMON].fd;
2244
+ if(fd == -1)
2245
+ fd = STDERR_FILENO;
2246
+
2247
+ int nptrs;
2248
+ void *buffer[10000];
2249
+
2250
+ nptrs = backtrace(buffer, sizeof(buffer));
2251
+ if(nptrs)
2252
+ backtrace_symbols_fd(buffer, nptrs, fd);
2253
#endif
2254
2255
#ifdef NETDATA_INTERNAL_CHECKS
1052
- crash_netdata();
2256
+ abort();
2257
#endif
2258
2259
netdata_cleanup_and_exit(1);
2260
}
2261
2262
// ----------------------------------------------------------------------------
1059
-// access log
2263
+// log limits
2264
1061
-void netdata_log_access( const char *fmt, ... ) {
1062
- va_list args;
1063
-
1064
- if(access_log_syslog) {
1065
- va_start( args, fmt );
1066
- vsyslog(LOG_INFO, fmt, args );
1067
- va_end( args );
1068
- }
1069
-
1070
- if(stdaccess) {
1071
- static netdata_mutex_t access_mutex = NETDATA_MUTEX_INITIALIZER;
2265
+void nd_log_limits_reset(void) {
2266
+ usec_t now_ut = now_monotonic_usec();
2267
1073
- if(web_server_is_multithreaded)
1074
- netdata_mutex_lock(&access_mutex);
2268
+ spinlock_lock(&nd_log.std_output.spinlock);
2269
+ spinlock_lock(&nd_log.std_error.spinlock);
2270
1076
- char date[LOG_DATE_LENGTH];
1077
- log_date(date, LOG_DATE_LENGTH, now_realtime_sec());
1078
- fprintf(stdaccess, "%s: ", date);
1079
-
1080
- va_start( args, fmt );
1081
- vfprintf( stdaccess, fmt, args );
1082
- va_end( args );
1083
- fputc('\n', stdaccess);
1084
-
1085
- if(web_server_is_multithreaded)
1086
- netdata_mutex_unlock(&access_mutex);
2271
+ for(size_t i = 0; i < _NDLS_MAX ;i++) {
2272
+ spinlock_lock(&nd_log.sources[i].spinlock);
2273
+ nd_log.sources[i].limits.prevented = 0;
2274
+ nd_log.sources[i].limits.counter = 0;
2275
+ nd_log.sources[i].limits.started_monotonic_ut = now_ut;
2276
+ nd_log.sources[i].limits.logs_per_period = nd_log.sources[i].limits.logs_per_period_backup;
2277
+ spinlock_unlock(&nd_log.sources[i].spinlock);
2278
}
1088
-}
1089
-
1090
-// ----------------------------------------------------------------------------
1091
-// health log
2279
1093
-void netdata_log_health( const char *fmt, ... ) {
1094
- va_list args;
2280
+ spinlock_unlock(&nd_log.std_output.spinlock);
2281
+ spinlock_unlock(&nd_log.std_error.spinlock);
2282
+}
2283
1096
- if(health_log_syslog) {
1097
- va_start( args, fmt );
1098
- vsyslog(LOG_INFO, fmt, args );
1099
- va_end( args );
2284
+void nd_log_limits_unlimited(void) {
2285
+ nd_log_limits_reset();
2286
+ for(size_t i = 0; i < _NDLS_MAX ;i++) {
2287
+ nd_log.sources[i].limits.logs_per_period = 0;
2288
}
2289
+}
2290
1102
- if(stdhealth) {
1103
- static netdata_mutex_t health_mutex = NETDATA_MUTEX_INITIALIZER;
1104
-
1105
- if(web_server_is_multithreaded)
1106
- netdata_mutex_lock(&health_mutex);
2291
+static bool nd_log_limit_reached(struct nd_log_source *source) {
2292
+ if(source->limits.throttle_period == 0 || source->limits.logs_per_period == 0)
2293
+ return false;
2294
1108
- char date[LOG_DATE_LENGTH];
1109
- log_date(date, LOG_DATE_LENGTH, now_realtime_sec());
1110
- fprintf(stdhealth, "%s: ", date);
2295
+ usec_t now_ut = now_monotonic_usec();
2296
+ if(!source->limits.started_monotonic_ut)
2297
+ source->limits.started_monotonic_ut = now_ut;
2298
1112
- va_start( args, fmt );
1113
- vfprintf( stdhealth, fmt, args );
1114
- va_end( args );
1115
- fputc('\n', stdhealth);
2299
+ source->limits.counter++;
2300
1117
- if(web_server_is_multithreaded)
1118
- netdata_mutex_unlock(&health_mutex);
1119
- }
1120
-}
2301
+ if(now_ut - source->limits.started_monotonic_ut > (usec_t)source->limits.throttle_period) {
2302
+ if(source->limits.prevented) {
2303
+ BUFFER *wb = buffer_create(1024, NULL);
2304
+ buffer_sprintf(wb,
2305
+ "LOG FLOOD PROTECTION: resuming logging "
2306
+ "(prevented %"PRIu32" logs in the last %"PRIu32" seconds).",
2307
+ source->limits.prevented,
2308
+ source->limits.throttle_period);
2309
1122
-#ifdef ENABLE_ACLK
1123
-void log_aclk_message_bin( const char *data, const size_t data_len, int tx, const char *mqtt_topic, const char *message_name) {
1124
- if (aclklog) {
1125
- static netdata_mutex_t aclklog_mutex = NETDATA_MUTEX_INITIALIZER;
1126
- netdata_mutex_lock(&aclklog_mutex);
2310
+ if(source->pending_msg)
2311
+ freez((void *)source->pending_msg);
2312
1128
- char date[LOG_DATE_LENGTH];
1129
- log_date(date, LOG_DATE_LENGTH, now_realtime_sec());
1130
- fprintf(aclklog, "%s: %s Msg:\"%s\", MQTT-topic:\"%s\": ", date, tx ? "OUTGOING" : "INCOMING", message_name, mqtt_topic);
2313
+ source->pending_msg = strdupz(buffer_tostring(wb));
2314
1132
- fwrite(data, data_len, 1, aclklog);
2315
+ buffer_free(wb);
2316
+ }
2317
1134
- fputc('\n', aclklog);
2318
+ // restart the period accounting
2319
+ source->limits.started_monotonic_ut = now_ut;
2320
+ source->limits.counter = 1;
2321
+ source->limits.prevented = 0;
2322
1136
- netdata_mutex_unlock(&aclklog_mutex);
2323
+ // log this error
2324
+ return false;
2325
}
1138
-}
1139
-#endif
1140
-
1141
-void log_set_global_severity_level(netdata_log_level_t value)
1142
-{
1143
- global_log_severity_level = value;
1144
-}
2326
1146
-netdata_log_level_t log_severity_string_to_severity_level(char *level)
1147
-{
1148
- if (!strcmp(level, NETDATA_LOG_LEVEL_INFO_STR))
1149
- return NETDATA_LOG_LEVEL_INFO;
1150
- if (!strcmp(level, NETDATA_LOG_LEVEL_ERROR_STR) || !strcmp(level, NETDATA_LOG_LEVEL_ERROR_SHORT_STR))
1151
- return NETDATA_LOG_LEVEL_ERROR;
2327
+ if(source->limits.counter > source->limits.logs_per_period) {
2328
+ if(!source->limits.prevented) {
2329
+ BUFFER *wb = buffer_create(1024, NULL);
2330
+ buffer_sprintf(wb,
2331
+ "LOG FLOOD PROTECTION: too many logs (%"PRIu32" logs in %"PRId64" seconds, threshold is set to %"PRIu32" logs "
2332
+ "in %"PRIu32" seconds). Preventing more logs from process '%s' for %"PRId64" seconds.",
2333
+ source->limits.counter,
2334
+ (int64_t)((now_ut - source->limits.started_monotonic_ut) / USEC_PER_SEC),
2335
+ source->limits.logs_per_period,
2336
+ source->limits.throttle_period,
2337
+ program_name,
2338
+ (int64_t)((source->limits.started_monotonic_ut + (source->limits.throttle_period * USEC_PER_SEC) - now_ut)) / USEC_PER_SEC);
2339
+
2340
+ if(source->pending_msg)
2341
+ freez((void *)source->pending_msg);
2342
+
2343
+ source->pending_msg = strdupz(buffer_tostring(wb));
2344
+
2345
+ buffer_free(wb);
2346
+ }
2347
1153
- return NETDATA_LOG_LEVEL_INFO;
1154
-}
2348
+ source->limits.prevented++;
2349
1156
-char *log_severity_level_to_severity_string(netdata_log_level_t level)
1157
-{
1158
- switch (level) {
1159
- case NETDATA_LOG_LEVEL_ERROR:
1160
- return NETDATA_LOG_LEVEL_ERROR_STR;
1161
- case NETDATA_LOG_LEVEL_INFO:
1162
- default:
1163
- return NETDATA_LOG_LEVEL_INFO_STR;
2350
+ // prevent logging this error
2351
+#ifdef NETDATA_INTERNAL_CHECKS
2352
+ return false;
2353
+#else
2354
+ return true;
2355
+#endif
2356
}
1165
-}
2357
1167
-void log_set_global_severity_for_external_plugins() {
1168
- char *s = getenv("NETDATA_LOG_SEVERITY_LEVEL");
1169
- if (!s)
1170
- return;
1171
- netdata_log_level_t level = log_severity_string_to_severity_level(s);
1172
- log_set_global_severity_level(level);
2358
+ return false;
2359
}