]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Logging: fixed size logfiles behaving as mmapped ringbuffers
authorMaria Matejka <mq@ucw.cz>
Thu, 24 Aug 2023 15:00:54 +0000 (17:00 +0200)
committerMaria Matejka <mq@ucw.cz>
Sun, 24 Sep 2023 18:43:04 +0000 (20:43 +0200)
This variant of logging avoids calling write() for every log line,
allowing for waitless logging. This makes heavy logging less heavy
and more useful for race condition debugging.

doc/bird.sgml
sysdep/unix/config.Y
sysdep/unix/io.c
sysdep/unix/unix.h

index 7c8e9895d69fbeccb1c0b0e74d3304255b2e008d..0da616c700c0265c8c70a53c811a4bd3e934bf49 100644 (file)
@@ -471,7 +471,7 @@ ipv6 table
 include "tablename.conf";;
 </code>
 
-       <tag><label id="opt-log">log "<m/filename/" [<m/limit/ "<m/backup/"] | syslog [name <m/name/] | stderr all|{ <m/list of classes/ }</tag>
+       <tag><label id="opt-log">log "<m/filename/" [<m/limit/ "<m/backup/"] | fixed "<m/filename/" <m/size/ | syslog [name <m/name/] | stderr all|{ <m/list of classes/ }</tag>
        Set logging of messages having the given class (either <cf/all/ or <cf>{
        error|trace [, <m/.../] }</cf> etc.) into selected destination - a file
        specified as a filename string (with optional log rotation information),
@@ -488,7 +488,8 @@ include "tablename.conf";;
        Logging directly to file supports basic log rotation -- there is an
        optional log file limit and a backup filename, when log file reaches the
        limit, the current log file is renamed to the backup filename and a new
-       log file is created.
+       log file is created. It's also possible to log to a single file behaving
+       as a ring buffer with a fixed size.
 
        You may specify more than one <cf/log/ line to establish logging to
        multiple destinations. Default: log everything to the system log, or
index a9feec0216686fa916e72891a5a1d238a546b07f..a4aed8474c114889d5f05839f10c9d3683e19958 100644 (file)
@@ -19,7 +19,7 @@ CF_DECLS
 
 CF_KEYWORDS(LOG, SYSLOG, ALL, DEBUG, TRACE, INFO, REMOTE, WARNING, ERROR, AUTH, FATAL, BUG, STDERR, SOFT)
 CF_KEYWORDS(NAME, CONFIRM, UNDO, CHECK, TIMEOUT, DEBUG, LATENCY, LIMIT, WATCHDOG, WARNING, STATUS)
-CF_KEYWORDS(GRACEFUL, RESTART)
+CF_KEYWORDS(GRACEFUL, RESTART, FIXED)
 
 %type <i> log_mask log_mask_list log_cat cfg_timeout
 %type <t> cfg_name
@@ -57,6 +57,14 @@ log_file:
      }
      this_log->filename = $1;
    }
+ | FIXED text expr {
+     if (!parse_and_exit)
+     {
+       this_log->rf = rf_open(new_config->pool, $2, RF_FIXED, this_log->limit = $3);
+       if (!this_log->rf) cf_error("Unable to open log file '%s': %m", $2);
+     }
+     this_log->filename = $2;
+   }
  | SYSLOG syslog_name { this_log->rf = NULL; new_config->syslog_name = $2; }
  | STDERR { this_log->rf = &rf_stderr; }
  ;
index faf759a580979dd82a5fa976264e5726bba28249..83149a964071a0cda1210d2ab0d528d3f1547062 100644 (file)
@@ -16,6 +16,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <time.h>
+#include <sys/mman.h>
 #include <sys/time.h>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -69,6 +70,7 @@ struct rfile {
   int fd;
   off_t limit;
   _Atomic off_t pos;
+  void *mapping;
 };
 
 struct rfile rf_stderr = {
@@ -80,6 +82,9 @@ rf_free(resource *r)
 {
   struct rfile *a = (struct rfile *) r;
 
+  if (a->mapping)
+    munmap(a->mapping, a->limit);
+
   close(a->fd);
 }
 
@@ -112,6 +117,10 @@ rf_open_get_fd(const char *name, enum rf_mode mode)
       flags = O_WRONLY | O_CREAT | O_APPEND;
       break;
 
+    case RF_FIXED:
+      flags = O_RDWR | O_CREAT;
+      break;
+
     default:
       bug("rf_open() must have the mode set");
   }
@@ -145,6 +154,18 @@ rf_open(pool *p, const char *name, enum rf_mode mode, off_t limit)
       r->pos = S_ISREG(r->stat.st_mode) ? r->stat.st_size : 0;
       break;
 
+    case RF_FIXED:
+      if ((ftruncate(fd, limit) < 0)
+         || ((r->mapping = mmap(NULL, limit, PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED))
+      {
+       int erf = errno;
+       r->mapping = NULL;
+       rfree(r);
+       errno = erf;
+       return NULL;
+      }
+      break;
+
     default:
       bug("rf_open() must have the mode set");
   }
@@ -165,15 +186,34 @@ rf_same(struct rfile *a, struct rfile *b)
   rf_stat(b);
 
   return
+    (a->limit == b->limit) &&
     (a->stat.st_mode == b->stat.st_mode) &&
     (a->stat.st_dev == b->stat.st_dev) &&
     (a->stat.st_ino == b->stat.st_ino);
 }
 
 int
-rf_write(struct rfile *r, const void *buf, size_t count)
+rf_write(struct rfile *r, const void *buf, size_t _count)
 {
-  if (r->limit && (atomic_fetch_add_explicit(&r->pos, count, memory_order_relaxed) + (off_t) count > r->limit))
+  off_t count = _count;
+
+  if (r->mapping)
+  {
+    /* Update the pointer */
+    off_t target = atomic_fetch_add_explicit(&r->pos, count, memory_order_relaxed) % r->limit;
+
+    /* Take care of wrapping */
+    if (target + count > r->limit)
+    {
+      memcpy(r->mapping, buf + (r->limit - target), target + count - r->limit);
+      count = r->limit - target;
+    }
+
+    /* Write the line */
+    memcpy(r->mapping + target, buf, count);
+    return 1;
+  }
+  else if (r->limit && (atomic_fetch_add_explicit(&r->pos, count, memory_order_relaxed) + count > r->limit))
   {
     atomic_fetch_sub_explicit(&r->pos, count, memory_order_relaxed);
     return 0;
index f193a8db8f467b6fdaa818096b30063c73f82aa2..7e1ef97c94dc4c25442b91b220d34e76afd1300b 100644 (file)
@@ -116,7 +116,8 @@ void io_log_dump(void);
 int sk_open_unix(struct birdsock *s, struct birdloop *, char *name);
 
 enum rf_mode {
-  RF_APPEND,
+  RF_APPEND = 1,
+  RF_FIXED,
 };
 
 struct rfile *rf_open(struct pool *, const char *name, enum rf_mode mode, off_t limit);