diff options
Diffstat (limited to 'libnetdata/log/log.c')
-rw-r--r-- | libnetdata/log/log.c | 3006 |
1 files changed, 2096 insertions, 910 deletions
diff --git a/libnetdata/log/log.c b/libnetdata/log/log.c index 02bb776c5b..b771cf96ea 100644 --- a/libnetdata/log/log.c +++ b/libnetdata/log/log.c @@ -1,472 +1,301 @@ // SPDX-License-Identifier: GPL-3.0-or-later -#include <daemon/main.h> +#define SD_JOURNAL_SUPPRESS_LOCATION + #include "../libnetdata.h" +#include <daemon/main.h> + +#ifdef __FreeBSD__ +#include <sys/endian.h> +#endif + +#ifdef __APPLE__ +#include <machine/endian.h> +#endif #ifdef HAVE_BACKTRACE #include <execinfo.h> #endif -int web_server_is_multithreaded = 1; +#ifdef HAVE_SYSTEMD +#include <systemd/sd-journal.h> +#endif + +#include <syslog.h> const char *program_name = ""; + uint64_t debug_flags = 0; -int access_log_syslog = 0; -int error_log_syslog = 0; -int collector_log_syslog = 0; -int output_log_syslog = 0; // debug log -int health_log_syslog = 0; +#ifdef ENABLE_ACLK +int aclklog_enabled = 0; +#endif + +// ---------------------------------------------------------------------------- + +struct nd_log_source; +static bool nd_log_limit_reached(struct nd_log_source *source); -int stdaccess_fd = -1; -FILE *stdaccess = NULL; +// ---------------------------------------------------------------------------- +// logging method + +typedef enum __attribute__((__packed__)) { + NDLO_DISABLED = 0, + NDLO_DEVNULL, + NDLO_DEFAULT, + NDLO_JOURNAL, + NDLO_SYSLOG, + NDLO_STDOUT, + NDLO_STDERR, + NDLO_FILE, +} ND_LOG_OUTPUT; + + +static struct { + ND_LOG_OUTPUT output; + const char *name; +} nd_log_outputs[] = { + { .output = NDLO_DISABLED, .name = "none" }, + { .output = NDLO_DEVNULL, .name = "/dev/null" }, + { .output = NDLO_DEFAULT, .name = "default" }, + { .output = NDLO_JOURNAL, .name = "journal" }, + { .output = NDLO_SYSLOG, .name = "syslog" }, + { .output = NDLO_STDOUT, .name = "stdout" }, + { .output = NDLO_STDERR, .name = "stderr" }, + { .output = NDLO_FILE, .name = "file" }, +}; + +static ND_LOG_OUTPUT nd_log_output2id(const char *output) { + if(!output || !*output) + return NDLO_DEFAULT; + + size_t entries = sizeof(nd_log_outputs) / sizeof(nd_log_outputs[0]); + for(size_t i = 0; i < entries ;i++) { + if(strcmp(nd_log_outputs[i].name, output) == 0) + return nd_log_outputs[i].output; + } -int stdhealth_fd = -1; -FILE *stdhealth = NULL; + return NDLO_FILE; +} -int stdcollector_fd = -1; -FILE *stderror = NULL; +static const char *nd_log_id2output(ND_LOG_OUTPUT output) { + size_t entries = sizeof(nd_log_outputs) / sizeof(nd_log_outputs[0]); + for(size_t i = 0; i < entries ;i++) { + if(output == nd_log_outputs[i].output) + return nd_log_outputs[i].name; + } -const char *stdaccess_filename = NULL; -const char *stderr_filename = NULL; -const char *stdout_filename = NULL; -const char *facility_log = NULL; -const char *stdhealth_filename = NULL; -const char *stdcollector_filename = NULL; + return "unknown"; +} -netdata_log_level_t global_log_severity_level = NETDATA_LOG_LEVEL_INFO; +// ---------------------------------------------------------------------------- +// workaround strerror_r() -#ifdef ENABLE_ACLK -const char *aclklog_filename = NULL; -int aclklog_fd = -1; -FILE *aclklog = NULL; -int aclklog_syslog = 1; -int aclklog_enabled = 0; +#if defined(STRERROR_R_CHAR_P) +// GLIBC version of strerror_r +static const char *strerror_result(const char *a, const char *b) { (void)b; return a; } +#elif defined(HAVE_STRERROR_R) +// POSIX version of strerror_r +static const char *strerror_result(int a, const char *b) { (void)a; return b; } +#elif defined(HAVE_C__GENERIC) + +// what a trick! +// http://stackoverflow.com/questions/479207/function-overloading-in-c +static const char *strerror_result_int(int a, const char *b) { (void)a; return b; } +static const char *strerror_result_string(const char *a, const char *b) { (void)b; return a; } + +#define strerror_result(a, b) _Generic((a), \ + int: strerror_result_int, \ + char *: strerror_result_string \ + )(a, b) + +#else +#error "cannot detect the format of function strerror_r()" #endif +static const char *errno2str(int errnum, char *buf, size_t size) { + return strerror_result(strerror_r(errnum, buf, size), buf); +} + // ---------------------------------------------------------------------------- -// Log facility(https://tools.ietf.org/html/rfc5424) +// facilities // -// The facilities accepted in the Netdata are in according with the following -// header files for their respective operating system: -// sys/syslog.h (Linux ) +// sys/syslog.h (Linux) // sys/sys/syslog.h (FreeBSD) // bsd/sys/syslog.h (darwin-xnu) -#define LOG_AUTH_KEY "auth" -#define LOG_AUTHPRIV_KEY "authpriv" -#ifdef __FreeBSD__ -# define LOG_CONSOLE_KEY "console" -#endif -#define LOG_CRON_KEY "cron" -#define LOG_DAEMON_KEY "daemon" -#define LOG_FTP_KEY "ftp" -#ifdef __APPLE__ -# define LOG_INSTALL_KEY "install" -#endif -#define LOG_KERN_KEY "kern" -#define LOG_LPR_KEY "lpr" -#define LOG_MAIL_KEY "mail" -//#define LOG_INTERNAL_MARK_KEY "mark" -#ifdef __APPLE__ -# define LOG_NETINFO_KEY "netinfo" -# define LOG_RAS_KEY "ras" -# define LOG_REMOTEAUTH_KEY "remoteauth" -#endif -#define LOG_NEWS_KEY "news" -#ifdef __FreeBSD__ -# define LOG_NTP_KEY "ntp" -#endif -#define LOG_SECURITY_KEY "security" -#define LOG_SYSLOG_KEY "syslog" -#define LOG_USER_KEY "user" -#define LOG_UUCP_KEY "uucp" -#ifdef __APPLE__ -# define LOG_LAUNCHD_KEY "launchd" -#endif -#define LOG_LOCAL0_KEY "local0" -#define LOG_LOCAL1_KEY "local1" -#define LOG_LOCAL2_KEY "local2" -#define LOG_LOCAL3_KEY "local3" -#define LOG_LOCAL4_KEY "local4" -#define LOG_LOCAL5_KEY "local5" -#define LOG_LOCAL6_KEY "local6" -#define LOG_LOCAL7_KEY "local7" - -static int log_facility_id(const char *facility_name) -{ - static int - hash_auth = 0, - hash_authpriv = 0, -#ifdef __FreeBSD__ - hash_console = 0, -#endif - hash_cron = 0, - hash_daemon = 0, - hash_ftp = 0, -#ifdef __APPLE__ - hash_install = 0, -#endif - hash_kern = 0, - hash_lpr = 0, - hash_mail = 0, -// hash_mark = 0, -#ifdef __APPLE__ - hash_netinfo = 0, - hash_ras = 0, - hash_remoteauth = 0, -#endif - hash_news = 0, -#ifdef __FreeBSD__ - hash_ntp = 0, -#endif - hash_security = 0, - hash_syslog = 0, - hash_user = 0, - hash_uucp = 0, -#ifdef __APPLE__ - hash_launchd = 0, -#endif - hash_local0 = 0, - hash_local1 = 0, - hash_local2 = 0, - hash_local3 = 0, - hash_local4 = 0, - hash_local5 = 0, - hash_local6 = 0, - hash_local7 = 0; - - if(unlikely(!hash_auth)) - { - hash_auth = simple_hash(LOG_AUTH_KEY); - hash_authpriv = simple_hash(LOG_AUTHPRIV_KEY); -#ifdef __FreeBSD__ - hash_console = simple_hash(LOG_CONSOLE_KEY); -#endif - hash_cron = simple_hash(LOG_CRON_KEY); - hash_daemon = simple_hash(LOG_DAEMON_KEY); - hash_ftp = simple_hash(LOG_FTP_KEY); -#ifdef __APPLE__ - hash_install = simple_hash(LOG_INSTALL_KEY); -#endif - hash_kern = simple_hash(LOG_KERN_KEY); - hash_lpr = simple_hash(LOG_LPR_KEY); - hash_mail = simple_hash(LOG_MAIL_KEY); -// hash_mark = simple_uhash(); -#ifdef __APPLE__ - hash_netinfo = simple_hash(LOG_NETINFO_KEY); - hash_ras = simple_hash(LOG_RAS_KEY); - hash_remoteauth = simple_hash(LOG_REMOTEAUTH_KEY); -#endif - hash_news = simple_hash(LOG_NEWS_KEY); -#ifdef __FreeBSD__ - hash_ntp = simple_hash(LOG_NTP_KEY); -#endif - hash_security = simple_hash(LOG_SECURITY_KEY); - hash_syslog = simple_hash(LOG_SYSLOG_KEY); - hash_user = simple_hash(LOG_USER_KEY); - hash_uucp = simple_hash(LOG_UUCP_KEY); -#ifdef __APPLE__ - hash_launchd = simple_hash(LOG_LAUNCHD_KEY); -#endif - hash_local0 = simple_hash(LOG_LOCAL0_KEY); - hash_local1 = simple_hash(LOG_LOCAL1_KEY); - hash_local2 = simple_hash(LOG_LOCAL2_KEY); - hash_local3 = simple_hash(LOG_LOCAL3_KEY); - hash_local4 = simple_hash(LOG_LOCAL4_KEY); - hash_local5 = simple_hash(LOG_LOCAL5_KEY); - hash_local6 = simple_hash(LOG_LOCAL6_KEY); - hash_local7 = simple_hash(LOG_LOCAL7_KEY); - } +static struct { + int facility; + const char *name; +} nd_log_facilities[] = { + { LOG_AUTH, "auth" }, + { LOG_AUTHPRIV, "authpriv" }, + { LOG_CRON, "cron" }, + { LOG_DAEMON, "daemon" }, + { LOG_FTP, "ftp" }, + { LOG_KERN, "kern" }, + { LOG_LPR, "lpr" }, + { LOG_MAIL, "mail" }, + { LOG_NEWS, "news" }, + { LOG_SYSLOG, "syslog" }, + { LOG_USER, "user" }, + { LOG_UUCP, "uucp" }, + { LOG_LOCAL0, "local0" }, + { LOG_LOCAL1, "local1" }, + { LOG_LOCAL2, "local2" }, + { LOG_LOCAL3, "local3" }, + { LOG_LOCAL4, "local4" }, + { LOG_LOCAL5, "local5" }, + { LOG_LOCAL6, "local6" }, + { LOG_LOCAL7, "local7" }, - int hash = simple_hash(facility_name); - if ( hash == hash_auth ) - { - return LOG_AUTH; - } - else if ( hash == hash_authpriv ) - { - return LOG_AUTHPRIV; - } #ifdef __FreeBSD__ - else if ( hash == hash_console ) - { - return LOG_CONSOLE; - } -#endif - else if ( hash == hash_cron ) - { - return LOG_CRON; - } - else if ( hash == hash_daemon ) - { - return LOG_DAEMON; - } - else if ( hash == hash_ftp ) - { - return LOG_FTP; - } -#ifdef __APPLE__ - else if ( hash == hash_install ) - { - return LOG_INSTALL; - } + { LOG_CONSOLE, "console" }, + { LOG_NTP, "ntp" }, + + // FreeBSD does not consider 'security' as deprecated. + { LOG_SECURITY, "security" }, +#else + // For all other O/S 'security' is mapped to 'auth'. + { LOG_AUTH, "security" }, #endif - else if ( hash == hash_kern ) - { - return LOG_KERN; - } - else if ( hash == hash_lpr ) - { - return LOG_LPR; - } - else if ( hash == hash_mail ) - { - return LOG_MAIL; - } - /* - else if ( hash == hash_mark ) - { - //this is internal for all OS - return INTERNAL_MARK; - } - */ + #ifdef __APPLE__ - else if ( hash == hash_netinfo ) - { - return LOG_NETINFO; - } - else if ( hash == hash_ras ) - { - return LOG_RAS; - } - else if ( hash == hash_remoteauth ) - { - return LOG_REMOTEAUTH; - } -#endif - else if ( hash == hash_news ) - { - return LOG_NEWS; - } -#ifdef __FreeBSD__ - else if ( hash == hash_ntp ) - { - return LOG_NTP; - } -#endif - else if ( hash == hash_security ) - { - //FreeBSD is the unique that does not consider - //this facility deprecated. We are keeping - //it for other OS while they are kept in their headers. -#ifdef __FreeBSD__ - return LOG_SECURITY; -#else - return LOG_AUTH; + { LOG_INSTALL, "install" }, + { LOG_NETINFO, "netinfo" }, + { LOG_RAS, "ras" }, + { LOG_REMOTEAUTH, "remoteauth" }, + { LOG_LAUNCHD, "launchd" }, + #endif +}; + +static int nd_log_facility2id(const char *facility) { + size_t entries = sizeof(nd_log_facilities) / sizeof(nd_log_facilities[0]); + for(size_t i = 0; i < entries ;i++) { + if(strcmp(nd_log_facilities[i].name, facility) == 0) + return nd_log_facilities[i].facility; } - else if ( hash == hash_syslog ) - { - return LOG_SYSLOG; - } - else if ( hash == hash_user ) - { - return LOG_USER; - } - else if ( hash == hash_uucp ) - { - return LOG_UUCP; - } - else if ( hash == hash_local0 ) - { - return LOG_LOCAL0; - } - else if ( hash == hash_local1 ) - { - return LOG_LOCAL1; - } - else if ( hash == hash_local2 ) - { - return LOG_LOCAL2; - } - else if ( hash == hash_local3 ) - { - return LOG_LOCAL3; - } - else if ( hash == hash_local4 ) - { - return LOG_LOCAL4; - } - else if ( hash == hash_local5 ) - { - return LOG_LOCAL5; - } - else if ( hash == hash_local6 ) - { - return LOG_LOCAL6; - } - else if ( hash == hash_local7 ) - { - return LOG_LOCAL7; + + return LOG_DAEMON; +} + +static const char *nd_log_id2facility(int facility) { + size_t entries = sizeof(nd_log_facilities) / sizeof(nd_log_facilities[0]); + for(size_t i = 0; i < entries ;i++) { + if(nd_log_facilities[i].facility == facility) + return nd_log_facilities[i].name; } -#ifdef __APPLE__ - else if ( hash == hash_launchd ) - { - return LOG_LAUNCHD; + + return "daemon"; +} + +// ---------------------------------------------------------------------------- +// priorities + +static struct { + ND_LOG_FIELD_PRIORITY priority; + const char *name; +} nd_log_priorities[] = { + { .priority = NDLP_EMERG, .name = "emergency" }, + { .priority = NDLP_EMERG, .name = "emerg" }, + { .priority = NDLP_ALERT, .name = "alert" }, + { .priority = NDLP_CRIT, .name = "critical" }, + { .priority = NDLP_CRIT, .name = "crit" }, + { .priority = NDLP_ERR, .name = "error" }, + { .priority = NDLP_ERR, .name = "err" }, + { .priority = NDLP_WARNING, .name = "warning" }, + { .priority = NDLP_WARNING, .name = "warn" }, + { .priority = NDLP_NOTICE, .name = "notice" }, + { .priority = NDLP_INFO, .name = NDLP_INFO_STR }, + { .priority = NDLP_DEBUG, .name = "debug" }, +}; + +int nd_log_priority2id(const char *priority) { + size_t entries = sizeof(nd_log_priorities) / sizeof(nd_log_priorities[0]); + for(size_t i = 0; i < entries ;i++) { + if(strcmp(nd_log_priorities[i].name, priority) == 0) + return nd_log_priorities[i].priority; } -#endif - return LOG_DAEMON; + return NDLP_INFO; } -//we do not need to use this now, but I already created this function to be -//used case necessary. -/* -char *log_facility_name(int code) -{ - char *defvalue = { "daemon" }; - switch(code) - { - case LOG_AUTH: - { - return "auth"; - } - case LOG_AUTHPRIV: - { - return "authpriv"; - } -#ifdef __FreeBSD__ - case LOG_CONSOLE: - { - return "console"; - } -#endif - case LOG_CRON: - { - return "cron"; - } - case LOG_DAEMON: - { - return defvalue; - } - case LOG_FTP: - { - return "ftp"; - } -#ifdef __APPLE__ - case LOG_INSTALL: - { - return "install"; - } -#endif - case LOG_KERN: - { - return "kern"; - } - case LOG_LPR: - { - return "lpr"; - } - case LOG_MAIL: - { - return "mail"; - } -#ifdef __APPLE__ - case LOG_NETINFO: - { - return "netinfo" ; - } - case LOG_RAS: - { - return "ras"; - } - case LOG_REMOTEAUTH: - { - return "remoteauth"; - } -#endif - case LOG_NEWS: - { - return "news"; - } -#ifdef __FreeBSD__ - case LOG_NTP: - { - return "ntp" ; - } - case LOG_SECURITY: - { - return "security"; - } -#endif - case LOG_SYSLOG: - { - return "syslog"; - } - case LOG_USER: - { - return "user"; - } - case LOG_UUCP: - { - return "uucp"; - } - case LOG_LOCAL0: - { - return "local0"; - } - case LOG_LOCAL1: - { - return "local1"; - } - case LOG_LOCAL2: - { - return "local2"; - } - case LOG_LOCAL3: - { - return "local3"; - } - case LOG_LOCAL4: - { - return "local4" ; - } - case LOG_LOCAL5: - { - return "local5"; - } - case LOG_LOCAL6: - { - return "local6"; - } - case LOG_LOCAL7: - { - return "local7" ; - } -#ifdef __APPLE__ - case LOG_LAUNCHD: - { - return "launchd"; - } -#endif +static const char *nd_log_id2priority(ND_LOG_FIELD_PRIORITY priority) { + size_t entries = sizeof(nd_log_priorities) / sizeof(nd_log_priorities[0]); + for(size_t i = 0; i < entries ;i++) { + if(priority == nd_log_priorities[i].priority) + return nd_log_priorities[i].name; } - return defvalue; + return NDLP_INFO_STR; +} + +// ---------------------------------------------------------------------------- +// log sources + +const char *log_sources_str[] = { + [NDLS_UNSET] = "UNSET", + [NDLS_ACCESS] = "access", + [NDLS_ACLK] = "aclk", + [NDLS_COLLECTORS] = "collector", + [NDLS_DAEMON] = "daemon", + [NDLS_HEALTH] = "health", + [NDLS_DEBUG] = "debug", +}; + +static const char *nd_log_source2str(ND_LOG_SOURCES source) { + size_t entries = sizeof(log_sources_str) / sizeof(log_sources_str[0]); + if(source < entries) + return log_sources_str[source]; + + return "UNKNOWN"; } -*/ // ---------------------------------------------------------------------------- +// log output formats + +typedef enum __attribute__((__packed__)) { + NDLOF_JOURNAL, + NDLOF_LOGFMT, + NDLOF_JSON, +} ND_LOG_OUTPUT_FORMAT; + +static struct { + ND_LOG_OUTPUT_FORMAT format; + const char *name; +} nd_log_formats[] = { + { .format = NDLOF_JOURNAL, .name = "journal" }, + { .format = NDLOF_LOGFMT, .name = "logfmt" }, + { .format = NDLOF_JSON, .name = "json" }, +}; + +static ND_LOG_OUTPUT_FORMAT nd_log_format2id(const char *format) { + if(!format || !*format) + return NDLOF_LOGFMT; + + size_t entries = sizeof(nd_log_formats) / sizeof(nd_log_formats[0]); + for(size_t i = 0; i < entries ;i++) { + if(strcmp(nd_log_formats[i].name, format) == 0) + return nd_log_formats[i].format; + } -void syslog_init() { - static int i = 0; + return NDLOF_LOGFMT; +} - if(!i) { - openlog(program_name, LOG_PID,log_facility_id(facility_log)); - i = 1; +static const char *nd_log_id2format(ND_LOG_OUTPUT_FORMAT format) { + size_t entries = sizeof(nd_log_formats) / sizeof(nd_log_formats[0]); + for(size_t i = 0; i < entries ;i++) { + if(format == nd_log_formats[i].format) + return nd_log_formats[i].name; } + + return "logfmt"; } +// ---------------------------------------------------------------------------- +// format dates + void log_date(char *buffer, size_t len, time_t now) { if(unlikely(!buffer || !len)) return; @@ -476,7 +305,7 @@ void log_date(char *buffer, size_t len, time_t now) { tmp = localtime_r(&t, &tmbuf); - if (tmp == NULL) { + if (unlikely(!tmp)) { buffer[0] = '\0'; return; } @@ -487,552 +316,1918 @@ void log_date(char *buffer, size_t len, time_t now) { buffer[len - 1] = '\0'; } -static netdata_mutex_t log_mutex = NETDATA_MUTEX_INITIALIZER; -static inline void log_lock() { - netdata_mutex_lock(&log_mutex); -} -static inline void log_unlock() { - netdata_mutex_unlock(&log_mutex); -} +// ---------------------------------------------------------------------------- -static FILE *open_log_file(int fd, FILE *fp, const char *filename, int *enabled_syslog, int is_stdaccess, int *fd_ptr) { - int f, devnull = 0; +struct nd_log_limit { + usec_t started_monotonic_ut; + uint32_t counter; + uint32_t prevented; - if(!filename || !*filename || !strcmp(filename, "none") || !strcmp(filename, "/dev/null")) { - filename = "/dev/null"; - devnull = 1; - } + uint32_t throttle_period; + uint32_t logs_per_period; + uint32_t logs_per_period_backup; +}; - if(!strcmp(filename, "syslog")) { - filename = "/dev/null"; - devnull = 1; +#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, } +#define ND_LOG_LIMITS_UNLIMITED (struct nd_log_limit){ .logs_per_period = 0, .logs_per_period_backup = 0, .throttle_period = 0, } - syslog_init(); - if(enabled_syslog) *enabled_syslog = 1; - } - else if(enabled_syslog) *enabled_syslog = 0; - - // don't do anything if the user is willing - // to have the standard one - if(!strcmp(filename, "system")) { - if(fd != -1 && !is_stdaccess) { - if(fd_ptr) *fd_ptr = fd; - return fp; - } +struct nd_log_source { + SPINLOCK spinlock; + ND_LOG_OUTPUT method; + ND_LOG_OUTPUT_FORMAT format; + const char *filename; + int fd; + FILE *fp; - filename = "stderr"; - } + ND_LOG_FIELD_PRIORITY min_priority; + const char *pending_msg; + struct nd_log_limit limits; +}; - if(!strcmp(filename, "stdout")) - f = STDOUT_FILENO; +static __thread ND_LOG_SOURCES overwrite_thread_source = 0; - else if(!strcmp(filename, "stderr")) - f = STDERR_FILENO; +void nd_log_set_thread_source(ND_LOG_SOURCES source) { + overwrite_thread_source = source; +} - else { - f = open(filename, O_WRONLY | O_APPEND | O_CREAT, 0664); - if(f == -1) { - netdata_log_error("Cannot open file '%s'. Leaving %d to its default.", filename, fd); - if(fd_ptr) *fd_ptr = fd; - return fp; +static struct { + uuid_t invocation_id; + + ND_LOG_SOURCES overwrite_process_source; + + struct nd_log_source sources[_NDLS_MAX]; + + struct { + bool initialized; + } journal; + + struct { + bool initialized; + int fd; + } journal_direct; + + struct { + bool initialized; + int facility; + } syslog; + + struct { + SPINLOCK spinlock; + bool initialized; + } std_output; + + struct { + SPINLOCK spinlock; + bool initialized; + } std_error; + +} nd_log = { + .overwrite_process_source = 0, + .journal = { + .initialized = false, + }, + .journal_direct = { + .initialized = false, + .fd = -1, + }, + .syslog = { + .initialized = false, + .facility = LOG_DAEMON, + }, + .std_output = { + .spinlock = NETDATA_SPINLOCK_INITIALIZER, + .initialized = false, + }, + .std_error = { + .spinlock = NETDATA_SPINLOCK_INITIALIZER, + .initialized = false, + }, + .sources = { + [NDLS_UNSET] = { + .spinlock = NETDATA_SPINLOCK_INITIALIZER, + .method = NDLO_DISABLED, + .format = NDLOF_JOURNAL, + .filename = NULL, + .fd = -1, + .fp = NULL, + .min_priority = NDLP_EMERG, + .limits = ND_LOG_LIMITS_UNLIMITED, + }, + [NDLS_ACCESS] = { + .spinlock = NETDATA_SPINLOCK_INITIALIZER, + .method = NDLO_DEFAULT, + .format = NDLOF_LOGFMT, + .filename = LOG_DIR "/access.log", + .fd = -1, + .fp = NULL, + .min_priority = NDLP_DEBUG, + .limits = ND_LOG_LIMITS_UNLIMITED, + }, + [NDLS_ACLK] = { + .spinlock = NETDATA_SPINLOCK_INITIALIZER, + .method = NDLO_FILE, + .format = NDLOF_LOGFMT, + .filename = LOG_DIR "/aclk.log", + .fd = -1, + .fp = NULL, + .min_priority = NDLP_DEBUG, + .limits = ND_LOG_LIMITS_UNLIMITED, + }, + [NDLS_COLLECTORS] = { + .spinlock = NETDATA_SPINLOCK_INITIALIZER, + .method = NDLO_DEFAULT, + .format = NDLOF_LOGFMT, + .filename = LOG_DIR "/collectors.log", + .fd = STDERR_FILENO, + .fp = NULL, + .min_priority = NDLP_INFO, + .limits = ND_LOG_LIMITS_DEFAULT, + }, + [NDLS_DEBUG] = { + .spinlock = NETDATA_SPINLOCK_INITIALIZER, + .method = NDLO_DISABLED, + .format = NDLOF_LOGFMT, + .filename = LOG_DIR "/debug.log", + .fd = STDOUT_FILENO, + .fp = NULL, + .min_priority = NDLP_DEBUG, + .limits = ND_LOG_LIMITS_UNLIMITED, + }, + [NDLS_DAEMON] = { + .spinlock = NETDATA_SPINLOCK_INITIALIZER, + .method = NDLO_DEFAULT, + .filename = LOG_DIR "/daemon.log", + .format = NDLOF_LOGFMT, + .fd = -1, + .fp = NULL, + .min_priority = NDLP_INFO, + .limits = ND_LOG_LIMITS_DEFAULT, + }, + [NDLS_HEALTH] = { + .spinlock = NETDATA_SPINLOCK_INITIALIZER, + .method = NDLO_DEFAULT, + .format = NDLOF_LOGFMT, + .filename = LOG_DIR "/health.log", + .fd = -1, + .fp = NULL, + .min_priority = NDLP_DEBUG, + .limits = ND_LOG_LIMITS_UNLIMITED, + }, + }, +}; + +__attribute__((constructor)) void initialize_invocation_id(void) { + // check for a NETDATA_INVOCATION_ID + if(uuid_parse_flexi(getenv("NETDATA_INVOCATION_ID"), nd_log.invocation_id) != 0) { + // not found, check for systemd set INVOCATION_ID + if(uuid_parse_flexi(getenv("INVOCATION_ID"), nd_log.invocation_id) != 0) { + // not found, generate a new one + uuid_generate_random(nd_log.invocation_id); } } - // if there is a level-2 file pointer - // flush it before switching the level-1 fds - if(fp) - fflush(fp); + char uuid[UUID_COMPACT_STR_LEN]; + uuid_unparse_lower_compact(nd_log.invocation_id, uuid); + setenv("NETDATA_INVOCATION_ID", uuid, 1); +} - if(devnull && is_stdaccess) { - fd = -1; - fp = NULL; - } +void nd_log_set_user_settings(ND_LOG_SOURCES source, const char *setting) { + char buf[FILENAME_MAX + 100]; + if(setting && *setting) + strncpyz(buf, setting, sizeof(buf) - 1); + else + buf[0] = '\0'; - if(fd != f && fd != -1) { - // it automatically closes - int t = dup2(f, fd); - if (t == -1) { - netdata_log_error("Cannot dup2() new fd %d to old fd %d for '%s'", f, fd, filename); - close(f); - if(fd_ptr) *fd_ptr = fd; - return fp; + struct nd_log_source *ls = &nd_log.sources[source]; + char *output = strrchr(buf, '@'); + + if(!output) + // all of it is the output + output = buf; + else { + // we found an '@', the next char is the output + *output = '\0'; + output++; + + // parse the other params + char *remaining = buf; + while(remaining) { + char *value = strsep_skip_consecutive_separators(&remaining, ","); + if (!value || !*value) continue; + + char *name = strsep_skip_consecutive_separators(&value, "="); + if (!name || !*name) continue; + + if(strcmp(name, "logfmt") == 0) + ls->format = NDLOF_LOGFMT; + else if(strcmp(name, "json") == 0) + ls->format = NDLOF_JSON; + else if(strcmp(name, "journal") == 0) + ls->format = NDLOF_JOURNAL; + else if(strcmp(name, "level") == 0 && value && *value) + ls->min_priority = nd_log_priority2id(value); + else if(strcmp(name, "protection") == 0 && value && *value) { + if(strcmp(value, "off") == 0 || strcmp(value, "none") == 0) { + ls->limits = ND_LOG_LIMITS_UNLIMITED; + ls->limits.counter = 0; + ls->limits.prevented = 0; + } + else { + ls->limits = ND_LOG_LIMITS_DEFAULT; + + char *slash = strchr(value, '/'); + if(slash) { + *slash = '\0'; + slash++; + ls->limits.logs_per_period = ls->limits.logs_per_period_backup = str2u(value); + ls->limits.throttle_period = str2u(slash); + } + else { + ls->limits.logs_per_period = ls->limits.logs_per_period_backup = str2u(value); + ls->limits.throttle_period = ND_LOG_DEFAULT_THROTTLE_PERIOD; + } + } + } + else + nd_log(NDLS_DAEMON, NDLP_ERR, "Error while parsing configuration of log source '%s'. " + "In config '%s', '%s' is not understood.", + nd_log_source2str(source), setting, name); } - // netdata_log_info("dup2() new fd %d to old fd %d for '%s'", f, fd, filename); - close(f); } - else fd = f; - if(!fp) { - fp = fdopen(fd, "a"); - if (!fp) - netdata_log_error("Cannot fdopen() fd %d ('%s')", fd, filename); + if(!output || !*output || strcmp(output, "none") == 0 || strcmp(output, "off") == 0) { + ls->method = NDLO_DISABLED; + ls->filename = "/dev/null"; + } + else if(strcmp(output, "journal") == 0) { + ls->method = NDLO_JOURNAL; + ls->filename = NULL; + } + else if(strcmp(output, "syslog") == 0) { + ls->method = NDLO_SYSLOG; + ls->filename = NULL; + } + else if(strcmp(output, "/dev/null") == 0) { + ls->method = NDLO_DEVNULL; + ls->filename = "/dev/null"; + } + else if(strcmp(output, "system") == 0) { + if(ls->fd == STDERR_FILENO) { + ls->method = NDLO_STDERR; + ls->filename = NULL; + ls->fd = STDERR_FILENO; + } else { - if (setvbuf(fp, NULL, _IOLBF, 0) != 0) - netdata_log_error("Cannot set line buffering on fd %d ('%s')", fd, filename); + ls->method = NDLO_STDOUT; + ls->filename = NULL; + ls->fd = STDOUT_FILENO; } } + else if(strcmp(output, "stderr") == 0) { + ls->method = NDLO_STDERR; + ls->filename = NULL; + ls->fd = STDERR_FILENO; + } + else if(strcmp(output, "stdout") == 0) { + ls->method = NDLO_STDOUT; + ls->filename = NULL; + ls->fd = STDOUT_FILENO; + } + else { + ls->method = NDLO_FILE; + ls->filename = strdupz(output); + } - if(fd_ptr) *fd_ptr = fd; - return fp; -} +#if defined(NETDATA_INTERNAL_CHECKS) || defined(NETDATA_DEV_MODE) + ls->min_priority = NDLP_DEBUG; +#endif |