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),
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
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
}
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; }
;
#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>
int fd;
off_t limit;
_Atomic off_t pos;
+ void *mapping;
};
struct rfile rf_stderr = {
{
struct rfile *a = (struct rfile *) r;
+ if (a->mapping)
+ munmap(a->mapping, a->limit);
+
close(a->fd);
}
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");
}
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");
}
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;
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);