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