mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-22 17:51:53 +00:00
Logging: uses writev instead of in-buffer magic
This commit is contained in:
parent
0e1bfdd9e0
commit
86ba37d8f1
@ -163,10 +163,18 @@ typedef struct buffer {
|
||||
|
||||
#define LOG_BUFFER_SIZE 2560
|
||||
|
||||
enum log_buffer_pos {
|
||||
LBP_TIMESTAMP = 0,
|
||||
LBP_THREAD_ID,
|
||||
LBP_CLASS,
|
||||
LBP_MSG,
|
||||
LBP__MAX,
|
||||
LBPP_TERMINAL,
|
||||
};
|
||||
|
||||
typedef struct log_buffer {
|
||||
struct buffer buf;
|
||||
byte *tm_pos;
|
||||
byte *msg_pos;
|
||||
byte *pos[LBP__MAX+1];
|
||||
int class;
|
||||
char block[LOG_BUFFER_SIZE];
|
||||
} log_buffer;
|
||||
|
@ -66,7 +66,7 @@ log_file:
|
||||
this_log->filename = $2;
|
||||
}
|
||||
| SYSLOG syslog_name { this_log->rf = NULL; new_config->syslog_name = $2; }
|
||||
| STDERR { this_log->rf = &rf_stderr; }
|
||||
| STDERR { this_log->rf = &rf_stderr; this_log->terminal_flag = 1; }
|
||||
;
|
||||
|
||||
log_mask:
|
||||
|
@ -192,36 +192,88 @@ rf_same(struct rfile *a, struct rfile *b)
|
||||
(a->stat.st_ino == b->stat.st_ino);
|
||||
}
|
||||
|
||||
int
|
||||
rf_write(struct rfile *r, const void *buf, size_t _count)
|
||||
void
|
||||
rf_write_crude(struct rfile *r, const char *buf, int sz)
|
||||
{
|
||||
off_t count = _count;
|
||||
if (r->mapping)
|
||||
memcpy(r->mapping, buf, sz);
|
||||
else
|
||||
write(r->fd, buf, sz);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
rf_writev(struct rfile *r, struct iovec *iov, int iov_count)
|
||||
{
|
||||
off_t size = 0;
|
||||
for (int i = 0; i < iov_count; i++)
|
||||
size += iov[i].iov_len;
|
||||
|
||||
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;
|
||||
}
|
||||
off_t target = atomic_fetch_add_explicit(&r->pos, size, memory_order_relaxed) % r->limit;
|
||||
|
||||
/* Write the line */
|
||||
memcpy(r->mapping + target, buf, count);
|
||||
for (int i = 0; i < iov_count; i++)
|
||||
{
|
||||
/* Take care of wrapping; this should really happen only once */
|
||||
off_t rsz;
|
||||
while ((rsz = r->limit - target) < (off_t) iov[i].iov_len)
|
||||
{
|
||||
memcpy(r->mapping + target, iov[i].iov_base, rsz);
|
||||
iov[i].iov_base += rsz;
|
||||
iov[i].iov_len -= rsz;
|
||||
target = 0;
|
||||
}
|
||||
|
||||
memcpy(r->mapping + target, iov[i].iov_base, iov[i].iov_len);
|
||||
target += iov[i].iov_len;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
else if (r->limit && (atomic_fetch_add_explicit(&r->pos, count, memory_order_relaxed) + count > r->limit))
|
||||
else if (r->limit && (atomic_fetch_add_explicit(&r->pos, size, memory_order_relaxed) + size > r->limit))
|
||||
{
|
||||
atomic_fetch_sub_explicit(&r->pos, count, memory_order_relaxed);
|
||||
atomic_fetch_sub_explicit(&r->pos, size, memory_order_relaxed);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
while ((write(r->fd, buf, count) < 0) && (errno == EINTR))
|
||||
;
|
||||
while (size > 0)
|
||||
{
|
||||
/* Try to write */
|
||||
ssize_t e = writev(r->fd, iov, iov_count);
|
||||
if (e < 0)
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
else
|
||||
return 1; /* FIXME: What should we do when we suddenly can't write? */
|
||||
|
||||
/* It is expected that we always write the whole bunch at once */
|
||||
if (e == size)
|
||||
return 1;
|
||||
|
||||
/* Block split should not happen (we write small enough messages)
|
||||
* but if it happens, let's try to write the rest of the log */
|
||||
size -= e;
|
||||
while (e > 0)
|
||||
{
|
||||
if ((ssize_t) iov[0].iov_len > e)
|
||||
{
|
||||
/* Some bytes are remaining in the first chunk */
|
||||
iov[0].iov_len -= e;
|
||||
iov[0].iov_base += e;
|
||||
break;
|
||||
}
|
||||
|
||||
/* First chunk written completely, get rid of it */
|
||||
e -= iov[0].iov_len;
|
||||
iov++;
|
||||
iov_count--;
|
||||
ASSERT_DIE(iov_count > 0);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -48,8 +48,6 @@ static DOMAIN(logging) log_domain;
|
||||
static struct log_channel * _Atomic global_logs;
|
||||
|
||||
/* Logging flags to validly prepare logging messages */
|
||||
#define LOGGING_TO_TERMINAL 0x1
|
||||
#define LOGGING_TO_FILE 0x2
|
||||
|
||||
static _Atomic uint logging_flags;
|
||||
static _Atomic uint logging_mask;
|
||||
@ -92,7 +90,7 @@ struct log_channel {
|
||||
off_t limit; /* Log size limit */
|
||||
_Atomic uint mask; /* Classes to log */
|
||||
uint new_mask; /* Pending new mask */
|
||||
uint terminal:1; /* Is terminal */
|
||||
uint prepare; /* Which message parts to prepare */
|
||||
};
|
||||
|
||||
struct log_thread_syncer {
|
||||
@ -174,6 +172,10 @@ log_rotate(struct log_channel *lc)
|
||||
void
|
||||
log_commit(log_buffer *buf)
|
||||
{
|
||||
/* Store the last pointer */
|
||||
buf->pos[LBP__MAX] = buf->buf.pos;
|
||||
|
||||
/* Append the too-long message if too long */
|
||||
if (buf->buf.pos == buf->buf.end)
|
||||
#define TOO_LONG " ... <too long>"
|
||||
memcpy(buf->buf.end - sizeof TOO_LONG, TOO_LONG, sizeof TOO_LONG);
|
||||
@ -190,18 +192,44 @@ log_commit(log_buffer *buf)
|
||||
continue;
|
||||
|
||||
struct rfile *rf = atomic_load_explicit(&l->rf, memory_order_acquire);
|
||||
if (rf && buf->tm_pos)
|
||||
if (rf)
|
||||
{
|
||||
*buf->buf.pos = '\n';
|
||||
byte *begin = l->terminal ? buf->buf.start : buf->tm_pos;
|
||||
off_t msg_len = buf->buf.pos - begin + 1;
|
||||
/* Construct the iovec */
|
||||
static char terminal_prefix[] = "bird: ",
|
||||
newline[] = "\n";
|
||||
STATIC_ASSERT(sizeof newline == 2);
|
||||
|
||||
struct iovec iov[LBP__MAX+2];
|
||||
uint iov_count = 0;
|
||||
if (BIT32_TEST(&l->prepare, LBPP_TERMINAL))
|
||||
iov[iov_count++] = (struct iovec) {
|
||||
.iov_base = terminal_prefix,
|
||||
.iov_len = sizeof terminal_prefix - 1,
|
||||
};
|
||||
|
||||
for (uint p = 0; p < LBP__MAX; p++)
|
||||
if (BIT32_TEST(&l->prepare, p))
|
||||
{
|
||||
off_t sz = buf->pos[p+1] - buf->pos[p];
|
||||
if (sz > 0)
|
||||
iov[iov_count++] = (struct iovec) {
|
||||
.iov_base = buf->pos[p],
|
||||
.iov_len = sz,
|
||||
};
|
||||
}
|
||||
|
||||
iov[iov_count++] = (struct iovec) {
|
||||
.iov_base = newline,
|
||||
.iov_len = sizeof newline - 1,
|
||||
};
|
||||
|
||||
do {
|
||||
if (rf_write(rf, begin, msg_len))
|
||||
if (rf_writev(rf, iov, iov_count))
|
||||
break;
|
||||
|
||||
log_lock();
|
||||
rf = atomic_load_explicit(&l->rf, memory_order_acquire);
|
||||
if (rf_write(rf, begin, msg_len))
|
||||
if (rf_writev(rf, iov, iov_count))
|
||||
{
|
||||
log_unlock();
|
||||
break;
|
||||
@ -211,18 +239,15 @@ log_commit(log_buffer *buf)
|
||||
log_unlock();
|
||||
|
||||
rf = atomic_load_explicit(&l->rf, memory_order_relaxed);
|
||||
} while (!rf_write(rf, begin, msg_len));
|
||||
} while (!rf_writev(rf, iov, iov_count));
|
||||
}
|
||||
#ifdef HAVE_SYSLOG_H
|
||||
else
|
||||
{
|
||||
*buf->buf.pos = '\0';
|
||||
syslog(syslog_priorities[buf->class], "%s", buf->msg_pos);
|
||||
syslog(syslog_priorities[buf->class], "%s", buf->pos[LBP_MSG]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
buf->msg_pos = buf->tm_pos = NULL;
|
||||
}
|
||||
|
||||
int buffer_vprint(buffer *buf, const char *fmt, va_list args);
|
||||
@ -230,31 +255,35 @@ int buffer_vprint(buffer *buf, const char *fmt, va_list args);
|
||||
void
|
||||
log_prepare(log_buffer *buf, int class)
|
||||
{
|
||||
buf->class = class;
|
||||
|
||||
buf->buf.start = buf->buf.pos = buf->block;
|
||||
buf->buf.end = buf->block + sizeof buf->block;
|
||||
|
||||
int lf = atomic_load_explicit(&logging_flags, memory_order_acquire);
|
||||
if (lf & LOGGING_TO_TERMINAL)
|
||||
buffer_puts(&buf->buf, "bird: ");
|
||||
|
||||
if (lf & LOGGING_TO_FILE)
|
||||
buf->pos[LBP_TIMESTAMP] = buf->buf.pos;
|
||||
if (BIT32_TEST(&lf, LBP_TIMESTAMP))
|
||||
{
|
||||
const char *fmt = config ? config->tf_log.fmt1 : "%F %T.%3f";
|
||||
|
||||
buf->tm_pos = buf->buf.pos;
|
||||
int t = tm_format_real_time(buf->buf.pos, buf->buf.end - buf->buf.pos, fmt, current_real_time());
|
||||
if (t)
|
||||
buf->buf.pos += t;
|
||||
else
|
||||
buffer_puts(&buf->buf, "<time format error>");
|
||||
|
||||
buffer_print(&buf->buf, " [%04x] <%s> ", THIS_THREAD_ID, class_names[class]);
|
||||
*(buf->buf.pos++) = ' ';
|
||||
}
|
||||
else
|
||||
buf->tm_pos = NULL;
|
||||
|
||||
buf->msg_pos = buf->buf.pos;
|
||||
buf->class = class;
|
||||
buf->pos[LBP_THREAD_ID] = buf->buf.pos;
|
||||
if (BIT32_TEST(&lf, LBP_THREAD_ID))
|
||||
buffer_print(&buf->buf, "[%04x] ", THIS_THREAD_ID);
|
||||
|
||||
buf->pos[LBP_CLASS] = buf->buf.pos;
|
||||
if (BIT32_TEST(&lf, LBP_CLASS))
|
||||
buffer_print(&buf->buf, "<%s> ", class_names[class]);
|
||||
|
||||
buf->pos[LBP_MSG] = buf->buf.pos;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -374,7 +403,11 @@ debug(const char *msg, ...)
|
||||
if (s < 0)
|
||||
bug("Extremely long debug output, split it.");
|
||||
|
||||
rf_write(dbg_rf, buf, s);
|
||||
struct iovec i = {
|
||||
.iov_base = buf,
|
||||
.iov_len = s,
|
||||
};
|
||||
rf_writev(dbg_rf, &i, 1);
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
@ -391,7 +424,7 @@ void
|
||||
debug_safe(const char *msg)
|
||||
{
|
||||
if (dbg_rf)
|
||||
rf_write(dbg_rf, msg, strlen(msg));
|
||||
rf_write_crude(dbg_rf, msg, strlen(msg));
|
||||
}
|
||||
|
||||
static list *
|
||||
@ -406,7 +439,7 @@ default_log_list(int initial, const char **syslog_name)
|
||||
{
|
||||
static struct log_config lc_syslog;
|
||||
lc_syslog = (struct log_config){
|
||||
.mask = ~0
|
||||
.mask = ~0,
|
||||
};
|
||||
|
||||
add_tail(&log_list, &lc_syslog.n);
|
||||
@ -453,7 +486,9 @@ log_switch(int initial, list *logs, const char *new_syslog_name)
|
||||
/* Create syslog channel */
|
||||
struct log_channel *lc = mb_alloc(log_pool, sizeof *lc);
|
||||
|
||||
*lc = (struct log_channel) {};
|
||||
*lc = (struct log_channel) {
|
||||
.prepare = BIT32_ALL(LBP_MSG),
|
||||
};
|
||||
ASSERT_DIE(NULL == atomic_exchange_explicit(&global_logs, lc, memory_order_release));
|
||||
#endif
|
||||
|
||||
@ -503,12 +538,7 @@ log_switch(int initial, list *logs, const char *new_syslog_name)
|
||||
total_mask |= l->mask;
|
||||
|
||||
/* Merge flags */
|
||||
flags |= LOGGING_TO_FILE;
|
||||
if (l->terminal_flag)
|
||||
{
|
||||
flags |= LOGGING_TO_TERMINAL;
|
||||
ol->terminal = 1;
|
||||
}
|
||||
flags |= ol->prepare;
|
||||
|
||||
/* The filehandle is no longer needed */
|
||||
if ((l->rf != &rf_stderr ) && (l->rf != dbg_rf))
|
||||
@ -552,18 +582,15 @@ log_switch(int initial, list *logs, const char *new_syslog_name)
|
||||
.rf = l->rf,
|
||||
.limit = l->limit,
|
||||
.new_mask = l->mask,
|
||||
.terminal = l->terminal_flag,
|
||||
.prepare = BIT32_ALL(LBP_TIMESTAMP, LBP_THREAD_ID, LBP_CLASS, LBP_MSG) |
|
||||
(l->terminal_flag ? BIT32_VAL(LBPP_TERMINAL) : 0),
|
||||
};
|
||||
|
||||
/* Mask union */
|
||||
total_mask |= l->mask;
|
||||
|
||||
/* Message preparation flags */
|
||||
flags |= LOGGING_TO_FILE;
|
||||
if (l->terminal_flag)
|
||||
{
|
||||
flags |= LOGGING_TO_TERMINAL;
|
||||
lc->terminal = 1;
|
||||
}
|
||||
flags |= lc->prepare;
|
||||
|
||||
/* Now the file handle ownership is transferred to the log channel */
|
||||
l->rf = NULL;
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "lib/io-loop.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/uio.h>
|
||||
#include <signal.h>
|
||||
|
||||
struct pool;
|
||||
@ -123,7 +124,8 @@ enum rf_mode {
|
||||
struct rfile *rf_open(struct pool *, const char *name, enum rf_mode mode, off_t limit);
|
||||
off_t rf_size(struct rfile *);
|
||||
int rf_same(struct rfile *, struct rfile *);
|
||||
int rf_write(struct rfile *, const void *, size_t);
|
||||
int rf_writev(struct rfile *, struct iovec *, int);
|
||||
void rf_write_crude(struct rfile *, const char *, int);
|
||||
|
||||
extern struct rfile rf_stderr;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user