summaryrefslogtreecommitdiffstats
path: root/libnetdata/log
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--libnetdata/log/Makefile.am8
-rw-r--r--libnetdata/log/README.md5
-rw-r--r--libnetdata/log/log.c1084
-rw-r--r--libnetdata/log/log.h141
4 files changed, 1238 insertions, 0 deletions
diff --git a/libnetdata/log/Makefile.am b/libnetdata/log/Makefile.am
new file mode 100644
index 0000000..161784b
--- /dev/null
+++ b/libnetdata/log/Makefile.am
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+AUTOMAKE_OPTIONS = subdir-objects
+MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
+
+dist_noinst_DATA = \
+ README.md \
+ $(NULL)
diff --git a/libnetdata/log/README.md b/libnetdata/log/README.md
new file mode 100644
index 0000000..a767dd4
--- /dev/null
+++ b/libnetdata/log/README.md
@@ -0,0 +1,5 @@
+<!--
+custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/log/README.md
+-->
+
+
diff --git a/libnetdata/log/log.c b/libnetdata/log/log.c
new file mode 100644
index 0000000..fb3b2d0
--- /dev/null
+++ b/libnetdata/log/log.c
@@ -0,0 +1,1084 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include <daemon/main.h>
+#include "../libnetdata.h"
+
+#ifdef HAVE_BACKTRACE
+#include <execinfo.h>
+#endif
+
+int web_server_is_multithreaded = 1;
+
+const char *program_name = "";
+uint64_t debug_flags = 0;
+
+int access_log_syslog = 1;
+int error_log_syslog = 1;
+int output_log_syslog = 1; // debug log
+int health_log_syslog = 1;
+
+int stdaccess_fd = -1;
+FILE *stdaccess = NULL;
+
+int stdhealth_fd = -1;
+FILE *stdhealth = NULL;
+
+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;
+
+#ifdef ENABLE_ACLK
+const char *aclklog_filename = NULL;
+int aclklog_fd = -1;
+FILE *aclklog = NULL;
+int aclklog_syslog = 1;
+int aclklog_enabled = 0;
+#endif
+
+// ----------------------------------------------------------------------------
+// Log facility(https://tools.ietf.org/html/rfc5424)
+//
+// The facilities accepted in the Netdata are in according with the following
+// header files for their respective operating system:
+// 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);
+ }
+
+ 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;
+ }
+#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;
+#endif
+ }
+ 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;
+ }
+#ifdef __APPLE__
+ else if ( hash == hash_launchd )
+ {
+ return LOG_LAUNCHD;
+ }
+#endif
+
+ return LOG_DAEMON;
+}
+
+//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
+ }
+
+ return defvalue;
+}
+*/
+
+// ----------------------------------------------------------------------------
+
+void syslog_init() {
+ static int i = 0;
+
+ if(!i) {
+ openlog(program_name, LOG_PID,log_facility_id(facility_log));
+ i = 1;
+ }
+}
+
+void log_date(char *buffer, size_t len, time_t now) {
+ if(unlikely(!buffer || !len))
+ return;
+
+ time_t t = now;
+ struct tm *tmp, tmbuf;
+
+ tmp = localtime_r(&t, &tmbuf);
+
+ if (tmp == NULL) {
+ buffer[0] = '\0';
+ return;
+ }
+
+ if (unlikely(strftime(buffer, len, "%Y-%m-%d %H:%M:%S", tmp) == 0))
+ buffer[0] = '\0';
+
+ 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;
+
+ if(!filename || !*filename || !strcmp(filename, "none") || !strcmp(filename, "/dev/null")) {
+ filename = "/dev/null";
+ devnull = 1;
+ }
+
+ if(!strcmp(filename, "syslog")) {
+ filename = "/dev/null";
+ devnull = 1;
+
+ 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;
+ }
+
+ filename = "stderr";
+ }
+
+ if(!strcmp(filename, "stdout"))
+ f = STDOUT_FILENO;
+
+ else if(!strcmp(filename, "stderr"))
+ f = STDERR_FILENO;
+
+ else {
+ f = open(filename, O_WRONLY | O_APPEND | O_CREAT, 0664);
+ if(f == -1) {
+ error("Cannot open file '%s'. Leaving %d to its default.", filename, fd);
+ if(fd_ptr) *fd_ptr = fd;
+ return fp;
+ }
+ }
+
+ // if there is a level-2 file pointer
+ // flush it before switching the level-1 fds
+ if(fp)
+ fflush(fp);
+
+ if(devnull && is_stdaccess) {
+ fd = -1;
+ fp = NULL;
+ }
+
+ if(fd != f && fd != -1) {
+ // it automatically closes
+ int t = dup2(f, fd);
+ if (t == -1) {
+ 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;
+ }
+ // 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)
+ error("Cannot fdopen() fd %d ('%s')", fd, filename);
+ else {
+ if (setvbuf(fp, NULL, _IOLBF, 0) != 0)
+ error("Cannot set line buffering on fd %d ('%s')", fd, filename);
+ }
+ }
+
+ if(fd_ptr) *fd_ptr = fd;
+ return fp;
+}
+
+void reopen_all_log_files() {
+ if(stdout_filename)
+ open_log_file(STDOUT_FILENO, stdout, stdout_filename, &output_log_syslog, 0, NULL);
+
+ if(stderr_filename)
+ open_log_file(STDERR_FILENO, stderr, stderr_filename, &error_log_syslog, 0, NULL);
+
+#ifdef ENABLE_ACLK
+ if (aclklog_enabled)
+ aclklog = open_log_file(aclklog_fd, aclklog, aclklog_filename, NULL, 0, &aclklog_fd);
+#endif
+
+ if(stdaccess_filename)
+ stdaccess = open_log_file(stdaccess_fd, stdaccess, stdaccess_filename, &access_log_syslog, 1, &stdaccess_fd);
+
+ if(stdhealth_filename)
+ stdhealth = open_log_file(stdhealth_fd, stdhealth, stdhealth_filename, &health_log_syslog, 1, &stdhealth_fd);
+}
+
+void open_all_log_files() {
+ // disable stdin
+ open_log_file(STDIN_FILENO, stdin, "/dev/null", NULL, 0, NULL);
+
+ open_log_file(STDOUT_FILENO, stdout, stdout_filename, &output_log_syslog, 0, NULL);
+ open_log_file(STDERR_FILENO, stderr, stderr_filename, &error_log_syslog, 0, NULL);
+
+#ifdef ENABLE_ACLK
+ if(aclklog_enabled)
+ aclklog = open_log_file(aclklog_fd, aclklog, aclklog_filename, NULL, 0, &aclklog_fd);
+#endif
+
+ stdaccess = open_log_file(stdaccess_fd, stdaccess, stdaccess_filename, &access_log_syslog, 1, &stdaccess_fd);
+
+ stdhealth = open_log_file(stdhealth_fd, stdhealth, stdhealth_filename, &health_log_syslog, 1, &stdhealth_fd);
+}
+
+// ----------------------------------------------------------------------------
+// error log throttling
+
+time_t error_log_throttle_period = 1200;
+unsigned long error_log_errors_per_period = 200;
+unsigned long error_log_errors_per_period_backup = 0;
+
+int error_log_limit(int reset) {
+ static time_t start = 0;
+ static unsigned long counter = 0, prevented = 0;
+
+ // fprintf(stderr, "FLOOD: counter=%lu, allowed=%lu, backup=%lu, period=%llu\n", counter, error_log_errors_per_period, error_log_errors_per_period_backup, (unsigned long long)error_log_throttle_period);
+
+ // do not throttle if the period is 0
+ if(error_log_throttle_period == 0)
+ return 0;
+
+ // prevent all logs if the errors per period is 0
+ if(error_log_errors_per_period == 0)
+#ifdef NETDATA_INTERNAL_CHECKS
+ return 0;
+#else
+ return 1;
+#endif
+
+ time_t now = now_monotonic_sec();
+ if(!start) start = now;
+
+ if(reset) {
+ if(prevented) {
+ char date[LOG_DATE_LENGTH];
+ log_date(date, LOG_DATE_LENGTH, now_realtime_sec());
+ fprintf(
+ stderr,
+ "%s: %s LOG FLOOD PROTECTION reset for process '%s' "
+ "(prevented %lu logs in the last %"PRId64" seconds).\n",
+ date,
+ program_name,
+ program_name,
+ prevented,
+ (int64_t)(now - start));
+ }
+
+ start = now;
+ counter = 0;
+ prevented = 0;
+ }
+
+ // detect if we log too much
+ counter++;
+
+ if(now - start > error_log_throttle_period) {
+ if(prevented) {
+ char date[LOG_DATE_LENGTH];
+ log_date(date, LOG_DATE_LENGTH, now_realtime_sec());
+ fprintf(
+ stderr,
+ "%s: %s LOG FLOOD PROTECTION resuming logging from process '%s' "
+ "(prevented %lu logs in the last %"PRId64" seconds).\n",
+ date,
+ program_name,
+ program_name,
+ prevented,
+ (int64_t)error_log_throttle_period);
+ }
+
+ // restart the period accounting
+ start = now;
+ counter = 1;
+ prevented = 0;
+
+ // log this error
+ return 0;
+ }
+
+ if(counter > error_log_errors_per_period) {
+ if(!prevented) {
+ char date[LOG_DATE_LENGTH];
+ log_date(date, LOG_DATE_LENGTH, now_realtime_sec());
+ fprintf(
+ stderr,
+ "%s: %s LOG FLOOD PROTECTION too many logs (%lu logs in %"PRId64" seconds, threshold is set to %lu logs "
+ "in %"PRId64" seconds). Preventing more logs from process '%s' for %"PRId64" seconds.\n",
+ date,
+ program_name,
+ counter,
+ (int64_t)(now - start),
+ error_log_errors_per_period,
+ (int64_t)error_log_throttle_period,
+ program_name,
+ (int64_t)(start + error_log_throttle_period - now));
+ }
+
+ prevented++;
+
+ // prevent logging this error
+#ifdef NETDATA_INTERNAL_CHECKS
+ return 0;
+#else
+ return 1;
+#endif
+ }
+
+ return 0;
+}
+
+void error_log_limit_reset(void) {
+ log_lock();
+
+ error_log_errors_per_period = error_log_errors_per_period_backup;
+ error_log_limit(1);
+
+ log_unlock();
+}
+
+void error_log_limit_unlimited(void) {
+ log_lock();
+
+ error_log_errors_per_period = error_log_errors_per_period_backup;
+ error_log_limit(1);
+
+ error_log_errors_per_period = ((error_log_errors_per_period_backup * 10) < 10000) ? 10000 : (error_log_errors_per_period_backup * 10);
+
+ log_unlock();
+}
+
+// ----------------------------------------------------------------------------
+// debug log
+
+void debug_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) {
+ va_list args;
+
+ char date[LOG_DATE_LENGTH];
+ log_date(date, LOG_DATE_LENGTH, now_realtime_sec());
+
+ va_start( args, fmt );
+ printf("%s: %s DEBUG : %s : (%04lu@%-20.20s:%-15.15s): ", date, program_name, netdata_thread_tag(), line, file, function);
+ vprintf(fmt, args);
+ va_end( args );
+ putchar('\n');
+
+ if(output_log_syslog) {
+ va_start( args, fmt );
+ vsyslog(LOG_ERR, fmt, args );
+ va_end( args );
+ }
+
+ fflush(stdout);
+}
+
+// ----------------------------------------------------------------------------
+// info log
+
+void info_int( const char *file __maybe_unused, const char *function __maybe_unused, const unsigned long line __maybe_unused, const char *fmt, ... )
+{
+ va_list args;
+
+ log_lock();
+
+ // prevent logging too much
+ if (error_log_limit(0)) {
+ log_unlock();
+ return;
+ }
+
+ if(error_log_syslog) {
+ va_start( args, fmt );
+ vsyslog(LOG_INFO, fmt, args );
+ va_end( args );
+ }
+
+ char date[LOG_DATE_LENGTH];
+ log_date(date, LOG_DATE_LENGTH, now_realtime_sec());
+
+ va_start( args, fmt );
+#ifdef NETDATA_INTERNAL_CHECKS
+ fprintf(stderr, "%s: %s INFO : %s : (%04lu@%-20.20s:%-15.15s): ", date, program_name, netdata_thread_tag(), line, file, function);
+#else
+ fprintf(stderr, "%s: %s INFO : %s : ", date, program_name, netdata_thread_tag());
+#endif
+ vfprintf( stderr, fmt, args );
+ va_end( args );
+
+ fputc('\n', stderr);
+
+ log_unlock();
+}
+
+// ----------------------------------------------------------------------------
+// error log
+
+#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
+
+void error_limit_int(ERROR_LIMIT *erl, const char *prefix, const char *file __maybe_unused, const char *function __maybe_unused, const unsigned long line __maybe_unused, const char *fmt, ... ) {
+ if(erl->sleep_ut)
+ sleep_usec(erl->sleep_ut);
+
+ // save a copy of errno - just in case this function generates a new error
+ int __errno = errno;
+
+ va_list args;
+
+ log_lock();
+
+ erl->count++;
+ time_t now = now_boottime_sec();
+ if(now - erl->last_logged < erl->log_every) {
+ log_unlock();
+ return;
+ }
+
+ // prevent logging too much
+ if (error_log_limit(0)) {
+ log_unlock();
+ return;
+ }
+
+ if(error_log_syslog) {
+ va_start( args, fmt );
+ vsyslog(LOG_ERR, fmt, args );
+ va_end( args );
+ }
+
+ char date[LOG_DATE_LENGTH];
+ log_date(date, LOG_DATE_LENGTH, now_realtime_sec());
+
+ va_start( args, fmt );
+#ifdef NETDATA_INTERNAL_CHECKS
+ fprintf(stderr, "%s: %s %-5.5s : %s : (%04lu@%-20.20s:%-15.15s): ", date, program_name, prefix, netdata_thread_tag(), line, file, function);
+#else
+ fprintf(stderr, "%s: %s %-5.5s : %s : ", date, program_name, prefix, netdata_thread_tag());
+#endif
+ vfprintf( stderr, fmt, args );
+ va_end( args );
+
+ if(erl->count > 1)
+ fprintf(stderr, " (similar messages repeated %zu times in the last %llu secs)", erl->count, (unsigned long long)(erl->last_logged ? now - erl->last_logged : 0));
+
+ if(erl->sleep_ut)
+ fprintf(stderr, " (sleeping for %llu microseconds every time this happens)", erl->sleep_ut);
+
+ if(__errno) {
+ char buf[1024];
+ fprintf(stderr, " (errno %d, %s)\n", __errno, strerror_result(strerror_r(__errno, buf, 1023), buf));
+ errno = 0;
+ }
+ else
+ fputc('\n', stderr);
+
+ erl->last_logged = now;
+ erl->count = 0;
+
+ log_unlock();
+}
+
+void error_int(const char *prefix, const char *file __maybe_unused, const char *function __maybe_unused, const unsigned long line __maybe_unused, const char *fmt, ... ) {
+ // save a copy of errno - just in case this function generates a new error
+ int __errno = errno;
+
+ va_list args;
+
+ log_lock();
+
+ // prevent logging too much
+ if (error_log_limit(0)) {
+ log_unlock();
+ return;
+ }
+
+ if(error_log_syslog) {
+ va_start( args, fmt );
+ vsyslog(LOG_ERR, fmt, args );
+ va_end( args );
+ }
+
+ char date[LOG_DATE_LENGTH];
+ log_date(date, LOG_DATE_LENGTH, now_realtime_sec());
+
+ va_start( args, fmt );
+#ifdef NETDATA_INTERNAL_CHECKS
+ fprintf(stderr, "%s: %s %-5.5s : %s : (%04lu@%-20.20s:%-15.15s): ", date, program_name, prefix, netdata_thread_tag(), line, file, function);
+#else
+ fprintf(stderr, "%s: %s %-5.5s : %s : ", date, program_name, prefix, netdata_thread_tag());
+#endif
+ vfprintf( stderr, fmt, args );
+ va_end( args );
+
+ if(__errno) {
+ char buf[1024];
+ fprintf(stderr, " (errno %d, %s)\n", __errno, strerror_result(strerror_r(__errno, buf, 1023), buf));
+ errno = 0;
+ }
+ else
+ fputc('\n', stderr);
+
+ log_unlock();
+}
+
+#ifdef NETDATA_INTERNAL_CHECKS
+static void crash_netdata(void) {
+ // make Netdata core dump
+ abort();
+}
+#endif
+
+#ifdef HAVE_BACKTRACE
+#define BT_BUF_SIZE 100
+static void print_call_stack(void) {
+ int nptrs;
+ void *buffer[BT_BUF_SIZE];
+
+ nptrs = backtrace(buffer, BT_BUF_SIZE);
+ if(nptrs)
+ backtrace_symbols_fd(buffer, nptrs, fileno(stderr));
+}
+#endif
+
+void fatal_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) {
+ // save a copy of errno - just in case this function generates a new error
+ int __errno = errno;
+ va_list args;
+ const char *thread_tag;
+ char os_threadname[NETDATA_THREAD_NAME_MAX + 1];
+
+ if(error_log_syslog) {
+ va_start( args, fmt );
+ vsyslog(LOG_CRIT, fmt, args );
+ va_end( args );
+ }
+
+ thread_tag = netdata_thread_tag();
+ if (!netdata_thread_tag_exists()) {
+ os_thread_get_current_name_np(os_threadname);
+ if ('\0' != os_threadname[0]) { /* If it is not an empty string replace "MAIN" thread_tag */
+ thread_tag = os_threadname;
+ }
+ }
+
+ char date[LOG_DATE_LENGTH];
+ log_date(date, LOG_DATE_LENGTH, now_realtime_sec());
+
+ log_lock();
+
+ va_start( args, fmt );
+#ifdef NETDATA_INTERNAL_CHECKS
+ fprintf(stderr, "%s: %s FATAL : %s : (%04lu@%-20.20s:%-15.15s): ", date, program_name, thread_tag, line, file, function);
+#else
+ fprintf(stderr, "%s: %s FATAL : %s : ", date, program_name, thread_tag);
+#endif
+ vfprintf( stderr, fmt, args );
+ va_end( args );
+
+ perror(" # ");
+ fputc('\n', stderr);
+
+ log_unlock();
+
+ char action_data[70+1];
+ snprintfz(action_data, 70, "%04lu@%-10.10s:%-15.15s/%d", line, file, function, __errno);
+ char action_result[60+1];
+
+ snprintfz(action_result, 60, "%s:%s", program_name, strncmp(thread_tag, "STREAM_RECEIVER", strlen("STREAM_RECEIVER")) ? thread_tag : "[x]");
+ send_statistics("FATAL", action_result, action_data);
+
+#ifdef HAVE_BACKTRACE
+ print_call_stack();
+#endif
+
+#ifdef NETDATA_INTERNAL_CHECKS
+ crash_netdata();
+#endif
+
+ netdata_cleanup_and_exit(1);
+}
+
+// ----------------------------------------------------------------------------
+// access log
+
+void log_access( const char *fmt, ... ) {
+ va_list args;
+
+ if(access_log_syslog) {
+ va_start( args, fmt );
+ vsyslog(LOG_INFO, fmt, args );
+ va_end( args );
+ }
+
+ if(stdaccess) {
+ static netdata_mutex_t access_mutex = NETDATA_MUTEX_INITIALIZER;
+
+ if(web_server_is_multithreaded)
+ netdata_mutex_lock(&access_mutex);
+
+ char date[LOG_DATE_LENGTH];
+ log_date(date, LOG_DATE_LENGTH, now_realtime_sec());
+ fprintf(stdaccess, "%s: ", date);
+
+ va_start( args, fmt );
+ vfprintf( stdaccess, fmt, args );
+ va_end( args );
+ fputc('\n', stdaccess);
+
+ if(web_server_is_multithreaded)
+ netdata_mutex_unlock(&access_mutex);
+ }
+}
+
+// ----------------------------------------------------------------------------
+// health log
+
+void log_health( const char *fmt, ... ) {
+ va_list args;
+
+ if(health_log_syslog) {
+ va_start( args, fmt );
+ vsyslog(LOG_INFO, fmt, args );
+ va_end( args );
+ }
+
+ if(stdhealth) {
+ static netdata_mutex_t health_mutex = NETDATA_MUTEX_INITIALIZER;
+
+ if(web_server_is_multithreaded)
+ netdata_mutex_lock(&health_mutex);
+
+ char date[LOG_DATE_LENGTH];
+ log_date(date, LOG_DATE_LENGTH, now_realtime_sec());
+ fprintf(stdhealth, "%s: ", date);
+
+ va_start( args, fmt );
+ vfprintf( stdhealth, fmt, args );
+ va_end( args );
+ fputc('\n', stdhealth);
+
+ if(web_server_is_multithreaded)
+ netdata_mutex_unlock(&health_mutex);
+ }
+}
+
+#ifdef ENABLE_ACLK
+void log_aclk_message_bin( const char *data, const size_t data_len, int tx, const char *mqtt_topic, const char *message_name) {
+ if (aclklog) {
+ static netdata_mutex_t aclklog_mutex = NETDATA_MUTEX_INITIALIZER;
+ netdata_mutex_lock(&aclklog_mutex);
+
+ char date[LOG_DATE_LENGTH];
+ log_date(date, LOG_DATE_LENGTH, now_realtime_sec());
+ fprintf(aclklog, "%s: %s Msg:\"%s\", MQTT-topic:\"%s\": ", date, tx ? "OUTGOING" : "INCOMING", message_name, mqtt_topic);
+
+ fwrite(data, data_len, 1, aclklog);
+
+ fputc('\n', aclklog);
+
+ netdata_mutex_unlock(&aclklog_mutex);
+ }
+}
+#endif
diff --git a/libnetdata/log/log.h b/libnetdata/log/log.h
new file mode 100644
index 0000000..11dab4c
--- /dev/null
+++ b/libnetdata/log/log.h
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef NETDATA_LOG_H
+#define NETDATA_LOG_H 1
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+#include "../libnetdata.h"
+
+#define D_WEB_BUFFER 0x0000000000000001
+#define D_WEB_CLIENT 0x0000000000000002
+#define D_LISTENER 0x0000000000000004
+#define D_WEB_DATA 0x0000000000000008
+#define D_OPTIONS 0x0000000000000010
+#define D_PROCNETDEV_LOOP 0x0000000000000020
+#define D_RRD_STATS 0x0000000000000040
+#define D_WEB_CLIENT_ACCESS 0x0000000000000080
+#define D_TC_LOOP 0x0000000000000100
+#define D_DEFLATE 0x0000000000000200
+#define D_CONFIG 0x0000000000000400
+#define D_PLUGINSD 0x0000000000000800
+#define D_CHILDS 0x0000000000001000
+#define D_EXIT 0x0000000000002000
+#define D_CHECKS 0x0000000000004000
+#define D_NFACCT_LOOP 0x0000000000008000
+#define D_PROCFILE 0x0000000000010000
+#define D_RRD_CALLS 0x0000000000020000
+#define D_DICTIONARY 0x0000000000040000
+#define D_MEMORY 0x0000000000080000
+#define D_CGROUP 0x0000000000100000
+#define D_REGISTRY 0x0000000000200000
+#define D_VARIABLES 0x0000000000400000
+#define D_HEALTH 0x0000000000800000
+#define D_CONNECT_TO 0x0000000001000000
+#define D_RRDHOST 0x0000000002000000
+#define D_LOCKS 0x0000000004000000
+#define D_EXPORTING 0x0000000008000000
+#define D_STATSD 0x0000000010000000
+#define D_POLLFD 0x0000000020000000
+#define D_STREAM 0x0000000040000000
+#define D_ANALYTICS 0x0000000080000000
+#define D_RRDENGINE 0x0000000100000000
+#define D_ACLK 0x0000000200000000
+#define D_METADATALOG 0x0000000400000000
+#define D_ACLK_SYNC 0x0000000800000000
+#define D_META_SYNC 0x0000001000000000
+#define D_REPLICATION 0x0000002000000000
+#define D_SYSTEM 0x8000000000000000
+
+extern int web_server_is_multithreaded;
+
+extern uint64_t debug_flags;
+
+extern const char *program_name;
+
+extern int stdaccess_fd;
+extern FILE *stdaccess;
+
+extern int stdhealth_fd;
+extern FILE *stdhealth;
+
+extern const char *stdaccess_filename;
+extern const char *stderr_filename;
+extern const char *stdout_filename;
+extern const char *stdhealth_filename;
+extern const char *facility_log;
+
+#ifdef ENABLE_ACLK
+extern const char *aclklog_filename;
+extern int aclklog_fd;
+extern FILE *aclklog;
+extern int aclklog_enabled;
+#endif
+
+extern int access_log_syslog;
+extern int error_log_syslog;
+extern int output_log_syslog;
+extern int health_log_syslog;
+
+extern time_t error_log_throttle_period;
+extern unsigned long error_log_errors_per_period, error_log_errors_per_period_backup;
+int error_log_limit(int reset);
+
+void open_all_log_files();
+void reopen_all_log_files();
+
+#define LOG_DATE_LENGTH 26
+void log_date(char *buffer, size_t len, time_t now);
+
+static inline void debug_dummy(void) {}
+
+void error_log_limit_reset(void);
+void error_log_limit_unlimited(void);
+
+typedef struct error_with_limit {
+ time_t log_every;
+ size_t count;
+ time_t last_logged;
+ usec_t sleep_ut;
+} ERROR_LIMIT;
+
+#define error_limit_static_global_var(var, log_every_secs, sleep_usecs) static ERROR_LIMIT var = { .last_logged = 0, .count = 0, .log_every = (log_every_secs), .sleep_ut = (sleep_usecs) }
+#define error_limit_static_thread_var(var, log_every_secs, sleep_usecs) static __thread ERROR_LIMIT var = { .last_logged = 0, .count = 0, .log_every = (log_every_secs), .sleep_ut = (sleep_usecs) }
+
+#ifdef NETDATA_INTERNAL_CHECKS
+#define debug(type, args...) do { if(unlikely(debug_flags & type)) debug_int(__FILE__, __FUNCTION__, __LINE__, ##args); } while(0)
+#define internal_error(condition, args...) do { if(unlikely(condition)) error_int("IERR", __FILE__, __FUNCTION__, __LINE__, ##args); } while(0)
+#define internal_fatal(condition, args...) do { if(unlikely(condition)) fatal_int(__FILE__, __FUNCTION__, __LINE__, ##args); } while(0)
+#else
+#define debug(type, args...) debug_dummy()
+#define internal_error(args...) debug_dummy()
+#define internal_fatal(args...) debug_dummy()
+#endif
+
+#define info(args...) info_int(__FILE__, __FUNCTION__, __LINE__, ##args)
+#define infoerr(args...) error_int("INFO", __FILE__, __FUNCTION__, __LINE__, ##args)
+#define error(args...) error_int("ERROR", __FILE__, __FUNCTION__, __LINE__, ##args)
+#define error_limit(erl, args...) error_limit_int(erl, "ERROR", __FILE__, __FUNCTION__, __LINE__, ##args)
+#define fatal(args...) fatal_int(__FILE__, __FUNCTION__, __LINE__, ##args)
+#define fatal_assert(expr) ((expr) ? (void)(0) : fatal_int(__FILE__, __FUNCTION__, __LINE__, "Assertion `%s' failed", #expr))
+
+void send_statistics(const char *action, const char *action_result, const char *action_data);
+void debug_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) PRINTFLIKE(4, 5);
+void info_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) PRINTFLIKE(4, 5);
+void error_int( const char *prefix, const char *file, const char *function, const unsigned long line, const char *fmt, ... ) PRINTFLIKE(5, 6);
+void error_limit_int(ERROR_LIMIT *erl, const char *prefix, const char *file __maybe_unused, const char *function __maybe_unused, unsigned long line __maybe_unused, const char *fmt, ... ) PRINTFLIKE(6, 7);;
+void fatal_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... ) NORETURN PRINTFLIKE(4, 5);
+void log_access( const char *fmt, ... ) PRINTFLIKE(1, 2);
+void log_health( const char *fmt, ... ) PRINTFLIKE(1, 2);
+
+#ifdef ENABLE_ACLK
+void log_aclk_message_bin( const char *data, const size_t data_len, int tx, const char *mqtt_topic, const char *message_name);
+#endif
+
+# ifdef __cplusplus
+}
+# endif
+
+#endif /* NETDATA_LOG_H */