Make it possible to open more than one vlog client socket at a time.
[sliver-openvswitch.git] / lib / vlog.c
index 6b3d871..a136e56 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "vlog.h"
 #include <assert.h>
 #include <errno.h>
 #include <syslog.h>
 #include <time.h>
 #include "dynamic-string.h"
+#include "sat-math.h"
+#include "timeval.h"
 #include "util.h"
 
 #define THIS_MODULE VLM_vlog
 
 /* Name for each logging level. */
 static const char *level_names[VLL_N_LEVELS] = {
-    [VLL_EMER] = "EMER",
-    [VLL_ERR] = "ERR",
-    [VLL_WARN] = "WARN",
-    [VLL_DBG] = "DBG",
+#define VLOG_LEVEL(NAME) #NAME,
+    VLOG_LEVELS
+#undef VLOG_LEVEL
 };
 
 /* Name for each logging facility. */
 static const char *facility_names[VLF_N_FACILITIES] = { 
-    [VLF_CONSOLE] = "console",
-    [VLF_SYSLOG] = "syslog",
+#define VLOG_FACILITY(NAME) #NAME,
+    VLOG_FACILITIES
+#undef VLOG_FACILITY
 };
 
 /* Name for each logging module */
 static const char *module_names[VLM_N_MODULES] = { 
 #define VLOG_MODULE(NAME) #NAME,
-    VLOG_MODULES
-#undef VLOG_MODULES
+#include "vlog-modules.def"
 };
 
 static int levels[VLM_N_MODULES][VLF_N_FACILITIES];
@@ -238,7 +240,7 @@ vlog_set_verbosity(const char *arg)
     if (arg) {
         char *msg = vlog_set_levels_from_string(arg);
         if (msg) {
-            fatal(0, "processing \"%s\": %s", arg, msg);
+            ofp_fatal(0, "processing \"%s\": %s", arg, msg);
         }
     } else {
         vlog_set_levels(VLM_ANY_MODULE, VLF_ANY_FACILITY, VLL_DBG);
@@ -254,7 +256,7 @@ vlog_init(void)
     openlog(program_name, LOG_NDELAY, LOG_DAEMON);
     vlog_set_levels(VLM_ANY_MODULE, VLF_ANY_FACILITY, VLL_WARN);
 
-    now = time(0);
+    now = time_now();
     if (now < 0) {
         struct tm tm;
         char s[128];
@@ -307,7 +309,8 @@ vlog_is_enabled(enum vlog_module module, enum vlog_level level)
  *
  * Guaranteed to preserve errno. */
 void
-vlog(enum vlog_module module, enum vlog_level level, const char *message, ...)
+vlog_valist(enum vlog_module module, enum vlog_level level,
+            const char *message, va_list args)
 {
     bool log_console = levels[module][VLF_CONSOLE] >= level;
     bool log_syslog = levels[module][VLF_SYSLOG] >= level;
@@ -318,40 +321,98 @@ vlog(enum vlog_module module, enum vlog_level level, const char *message, ...)
         const char *level_name = vlog_get_level_name(level);
         time_t now;
         struct tm tm;
-        va_list args;
-        char s[1024];
-        size_t len, time_len;
+        size_t time_len;
+        struct ds s;
 
-        now = time(0);
+        now = time_now();
         localtime_r(&now, &tm);
 
-        len = time_len = strftime(s, sizeof s, "%b %d %H:%M:%S|", &tm);
-        len += sprintf(s + len, "%05d|%s|%s:",
-                       ++msg_num, module_name, level_name);
-        va_start(args, message);
-        len += vsnprintf(s + len, sizeof s - len, message, args);
-        va_end(args);
-        if (len >= sizeof s) {
-            len = sizeof s;
-        }
-        if (s[len - 1] == '\n') {
-            s[len - 1] = '\0';
-        }
+        /* Compose log message. */
+        ds_init(&s);
+        ds_reserve(&s, 1024);
+        ds_put_strftime(&s, "%b %d %H:%M:%S|", &tm);
+        time_len = s.length;
+        ds_put_format(&s, "%05d|%s|%s:", ++msg_num, module_name, level_name);
+        ds_put_format_valist(&s, message, args);
+        ds_chomp(&s, '\n');
 
         if (log_console) {
-            fprintf(stderr, "%s\n", s);
+            fprintf(stderr, "%s\n", s.string);
         }
 
         if (log_syslog) {
-            static const int syslog_levels[VLL_N_LEVELS] = {
-                [VLL_EMER] = LOG_EMERG,
-                [VLL_ERR] = LOG_ERR,
-                [VLL_WARN] = LOG_WARNING,
-                [VLL_DBG] = LOG_DEBUG,
-            };
-
-            syslog(syslog_levels[level], "%s", s + time_len);
+            int syslog_level = LOG_ALERT;
+            char *save_ptr = NULL;
+            char *line;
+
+            switch (level) {
+            case VLL_EMER: syslog_level = LOG_ALERT; break;
+            case VLL_ERR: syslog_level = LOG_ERR; break;
+            case VLL_WARN: syslog_level = LOG_WARNING; break;
+            case VLL_DBG: syslog_level = LOG_DEBUG; break;
+            case VLL_N_LEVELS: NOT_REACHED();
+            }
+            for (line = strtok_r(&s.string[time_len], "\n", &save_ptr); line;
+                 line = strtok_r(NULL, "\n", &save_ptr)) {
+                syslog(syslog_level, "%s", line);
+            }
         }
+
+        ds_destroy(&s);
         errno = save_errno;
     }
 }
+
+void
+vlog(enum vlog_module module, enum vlog_level level, const char *message, ...)
+{
+    va_list args;
+
+    va_start(args, message);
+    vlog_valist(module, level, message, args);
+    va_end(args);
+}
+
+void
+vlog_rate_limit(enum vlog_module module, enum vlog_level level,
+                struct vlog_rate_limit *rl, const char *message, ...)
+{
+    va_list args;
+
+    if (!vlog_is_enabled(module, level)) {
+        return;
+    }
+
+    if (rl->tokens < VLOG_MSG_TOKENS) {
+        time_t now = time_now();
+        if (rl->last_fill > now) {
+            /* Last filled in the future?  Time must have gone backward, or
+             * 'rl' has not been used before. */
+            rl->tokens = rl->burst;
+        } else if (rl->last_fill < now) {
+            unsigned int add = sat_mul(rl->rate, now - rl->last_fill);
+            unsigned int tokens = sat_add(rl->tokens, add);
+            rl->tokens = MIN(tokens, rl->burst);
+            rl->last_fill = now;
+        }
+        if (rl->tokens < VLOG_MSG_TOKENS) {
+            if (!rl->n_dropped) {
+                rl->first_dropped = now;
+            }
+            rl->n_dropped++;
+            return;
+        }
+    }
+    rl->tokens -= VLOG_MSG_TOKENS;
+
+    va_start(args, message);
+    vlog_valist(module, level, message, args);
+    va_end(args);
+
+    if (rl->n_dropped) {
+        vlog(module, level,
+             "Dropped %u messages in last %u seconds due to excessive rate",
+             rl->n_dropped, (unsigned int) (time_now() - rl->first_dropped));
+        rl->n_dropped = 0;
+    }
+}