pespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/libosmocore/+/41925?usp=email )
Change subject: Implement log file target using osmo_io ......................................................................
Implement log file target using osmo_io
TODO: re-add talloc_pool.
Related: OS#6918 Change-Id: Ieb6420246454ef59442b1fd7b1d14e2c00fa69a5 --- M include/osmocom/core/logging_internal.h M src/core/logging_file.c M src/vty/logging_vty.c M tests/logging/logging_test.c M tests/logging/logging_test.err 5 files changed, 112 insertions(+), 145 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/25/41925/1
diff --git a/include/osmocom/core/logging_internal.h b/include/osmocom/core/logging_internal.h index cadd4df..187a7df 100644 --- a/include/osmocom/core/logging_internal.h +++ b/include/osmocom/core/logging_internal.h @@ -9,6 +9,7 @@ #include <osmocom/core/utils.h> #include <osmocom/core/linuxlist.h> #include <osmocom/core/logging.h> +#include <osmocom/core/osmo_io.h>
/* maximum length of the log string of a single log event (typically line) */ #define MAX_LOG_SIZE 4096 @@ -52,8 +53,8 @@ /* direct, blocking output via stdio */ FILE *out; const char *fname; - /* indirect output via write_queue and osmo_select_main() */ - struct osmo_wqueue *wqueue; + /* indirect output via iofd and osmo_select_main() */ + struct osmo_io_fd *iofd; } tgt_file;
struct { diff --git a/src/core/logging_file.c b/src/core/logging_file.c index 1a5bdb2..d859dab 100644 --- a/src/core/logging_file.c +++ b/src/core/logging_file.c @@ -37,44 +37,52 @@ #include <osmocom/core/talloc.h> #include <osmocom/core/utils.h> #include <osmocom/core/select.h> -#include <osmocom/core/write_queue.h> +#include <osmocom/core/osmo_io.h> #include <osmocom/core/logging_internal.h>
/* maximum number of log statements we queue in file/stderr target write queue */ #define LOG_WQUEUE_LEN 156
- /*! close and re-open a log file (for log file rotation) * \param[in] target log target to re-open * \returns 0 in case of success; negative otherwise */ int log_target_file_reopen(struct log_target *target) { - struct osmo_wqueue *wq; int rc;
- OSMO_ASSERT(target->type == LOG_TGT_TYPE_FILE || target->type == LOG_TGT_TYPE_STDERR); - OSMO_ASSERT(target->tgt_file.out || target->tgt_file.wqueue); + OSMO_ASSERT(target->type == LOG_TGT_TYPE_FILE || + target->type == LOG_TGT_TYPE_STDERR); + OSMO_ASSERT(target->tgt_file.out || target->tgt_file.iofd);
- if (target->tgt_file.out) { - fclose(target->tgt_file.out); - target->tgt_file.out = fopen(target->tgt_file.fname, "a"); - if (!target->tgt_file.out) - return -errno; - } else { - wq = target->tgt_file.wqueue; - if (wq->bfd.fd >= 0) { - osmo_fd_unregister(&wq->bfd); - close(wq->bfd.fd); - wq->bfd.fd = -1; + if (target->type == LOG_TGT_TYPE_STDERR) + return -ENOTSUP; + + switch (target->type) { + case LOG_TGT_TYPE_STDERR: + return -ENOTSUP; + case LOG_TGT_TYPE_FILE: + if (target->tgt_file.out) { /* stream mode */ + fclose(target->tgt_file.out); + target->tgt_file.out = fopen(target->tgt_file.fname, "a"); + if (!target->tgt_file.out) + return -errno; + return 0; }
+ osmo_iofd_close(target->tgt_file.iofd); rc = open(target->tgt_file.fname, O_WRONLY|O_APPEND|O_CREAT|O_NONBLOCK, 0660); if (rc < 0) return -errno; - wq->bfd.fd = rc; - rc = osmo_fd_register(&wq->bfd); - if (rc < 0) - return rc; + + rc = osmo_iofd_register(target->tgt_file.iofd, rc); + if (rc < 0) { + osmo_iofd_free(target->tgt_file.iofd); + target->tgt_file.iofd = NULL; + return -EIO; + } + break; + default: + OSMO_ASSERT(0); }
return 0; @@ -87,7 +95,6 @@ * object. */ void log_target_file_destroy(struct log_target *target) { - struct osmo_wqueue *wq;
OSMO_ASSERT(target->type == LOG_TGT_TYPE_FILE || target->type == LOG_TGT_TYPE_STDERR); @@ -97,38 +104,20 @@ fclose(target->tgt_file.out); target->tgt_file.out = NULL; } - wq = target->tgt_file.wqueue; - if (wq) { - if (wq->bfd.fd >= 0) { - osmo_fd_unregister(&wq->bfd); - if (target->type == LOG_TGT_TYPE_FILE) - close(wq->bfd.fd); - wq->bfd.fd = -1; - } - osmo_wqueue_clear(wq); - talloc_free(wq); - target->tgt_file.wqueue = NULL; + + if (target->tgt_file.iofd) { + osmo_iofd_free(target->tgt_file.iofd); + target->tgt_file.iofd = NULL; } + talloc_free((void *)target->tgt_file.fname); target->tgt_file.fname = NULL; }
-/* write-queue tells us we should write another msgb (log line) to the output fd */ -static int _file_wq_write_cb(struct osmo_fd *ofd, struct msgb *msg) -{ - int rc; - - rc = write(ofd->fd, msgb_data(msg), msgb_length(msg)); - if (rc < 0) - return rc; - if (rc != msgb_length(msg)) { - /* pull the number of bytes we have already written */ - msgb_pull(msg, rc); - /* ask write_queue to re-insert the msgb at the head of the queue */ - return -EAGAIN; - } - return 0; -} +static struct osmo_io_ops log_target_file_io_ops = { + .read_cb = NULL, + .write_cb = NULL, +};
/* output via buffered, blocking stdio streams */ static void _file_output_stream(struct log_target *target, unsigned int level, @@ -146,8 +135,8 @@ struct msgb *msg; int rc;
- OSMO_ASSERT(target->tgt_file.wqueue); - msg = msgb_alloc_c(target->tgt_file.wqueue, MAX_LOG_SIZE, "log_file_msg"); + OSMO_ASSERT(target->tgt_file.iofd); + msg = msgb_alloc_c(target->tgt_file.iofd, MAX_LOG_SIZE, "log_file_msg"); if (!msg) return;
@@ -157,18 +146,8 @@ rc = log_output_buf((char *)msgb_data(msg), msgb_tailroom(msg), target, subsys, level, file, line, cont, format, ap); msgb_put(msg, rc);
- /* attempt a synchronous, non-blocking write, if the write queue is empty */ - if (target->tgt_file.wqueue->current_length == 0) { - rc = _file_wq_write_cb(&target->tgt_file.wqueue->bfd, msg); - if (rc == 0) { - /* the write was complete, we can exit early */ - msgb_free(msg); - return; - } - } - /* if we reach here, either we already had elements in the write_queue, or the synchronous write - * failed: enqueue the message to the write_queue (backlog) */ - if (osmo_wqueue_enqueue_quiet(target->tgt_file.wqueue, msg) < 0) { + rc = osmo_iofd_write_msgb(target->tgt_file.iofd, msg); + if (rc < 0) { msgb_free(msg); /* TODO: increment some counter so we can see that messages were dropped */ } @@ -181,7 +160,7 @@ */ int log_target_file_switch_to_stream(struct log_target *target) { - struct osmo_wqueue *wq; + unsigned int prev_queue_len;
if (!target) return -ENODEV; @@ -191,37 +170,28 @@ return 1; }
- wq = target->tgt_file.wqueue; - OSMO_ASSERT(wq); - /* re-open output as stream */ if (target->type == LOG_TGT_TYPE_STDERR) target->tgt_file.out = stderr; else target->tgt_file.out = fopen(target->tgt_file.fname, "a"); + if (!target->tgt_file.out) return -EIO;
- /* synchronously write anything left in the queue */ - while (!llist_empty(&wq->msg_queue)) { - struct msgb *msg = msgb_dequeue(&wq->msg_queue); - fwrite(msgb_data(msg), msgb_length(msg), 1, target->tgt_file.out); - msgb_free(msg); - } + prev_queue_len = osmo_iofd_txqueue_len(target->tgt_file.iofd);
- /* now that everything succeeded, we can finally close the old output fd */ - if (target->type == LOG_TGT_TYPE_FILE) { - osmo_fd_unregister(&wq->bfd); - close(wq->bfd.fd); - wq->bfd.fd = -1; - } - - /* release the queue itself */ - talloc_free(wq); - target->tgt_file.wqueue = NULL; + /* now that everything succeeded, we can finally close the old iofd */ + osmo_iofd_free(target->tgt_file.iofd); + target->tgt_file.iofd = NULL; target->output = _file_output_stream; target->raw_output = NULL;
+ + if (prev_queue_len > 0) + LOGP(DLGLOBAL, LOGL_NOTICE, + "Dropped %u messages switching log target file to stream\n", prev_queue_len); + return 0; }
@@ -232,8 +202,7 @@ */ int log_target_file_switch_to_wqueue(struct log_target *target) { - struct osmo_wqueue *wq; - int rc; + int rc, fd;
if (!target) return -ENODEV; @@ -243,35 +212,33 @@ return 1; }
- /* we create a ~640kB sized talloc pool within the write-queue to ensure individual - * log lines (stored as msgbs) will not put result in malloc() calls, and also to - * reduce the OOM probability within logging, as the pool is already allocated */ - wq = talloc_pooled_object(target, struct osmo_wqueue, LOG_WQUEUE_LEN, - LOG_WQUEUE_LEN*(sizeof(struct msgb)+MAX_LOG_SIZE)); - if (!wq) - return -ENOMEM; - osmo_wqueue_init(wq, LOG_WQUEUE_LEN); - fflush(target->tgt_file.out); - if (target->type == LOG_TGT_TYPE_FILE) { - rc = open(target->tgt_file.fname, O_WRONLY|O_APPEND|O_CREAT|O_NONBLOCK, 0660); - if (rc < 0) { - talloc_free(wq); - return -errno; - } - } else { - rc = STDERR_FILENO; - } - wq->bfd.fd = rc; - wq->bfd.when = OSMO_FD_WRITE; - wq->write_cb = _file_wq_write_cb; + if (target->type == LOG_TGT_TYPE_FILE) + fd = open(target->tgt_file.fname, O_WRONLY|O_APPEND|O_CREAT|O_NONBLOCK, 0660); + else /* LOG_TGT_TYPE_STDERR: dup file so we can close it later with osmo_iofd_free() */ + fd = dup(STDERR_FILENO);
- rc = osmo_fd_register(&wq->bfd); - if (rc < 0) { - talloc_free(wq); + if (fd < 0) + return -errno; + + target->tgt_file.iofd = osmo_iofd_setup(target, fd, target->tgt_file.fname, + OSMO_IO_FD_MODE_READ_WRITE, + &log_target_file_io_ops, target); + if (!target->tgt_file.iofd) { + if (target->type == LOG_TGT_TYPE_FILE) + close(fd); return -EIO; } - target->tgt_file.wqueue = wq; + + osmo_iofd_set_txqueue_max_length(target->tgt_file.iofd, LOG_WQUEUE_LEN); + + rc = osmo_iofd_register(target->tgt_file.iofd, -1); + if (rc < 0) { + osmo_iofd_free(target->tgt_file.iofd); + target->tgt_file.iofd = NULL; + return -EIO; + } + target->raw_output = _file_raw_output; target->output = NULL;
@@ -290,45 +257,40 @@ struct log_target *log_target_create_file(const char *fname) { struct log_target *target; - struct osmo_wqueue *wq; - int rc; + int rc, fd;
target = log_target_create(); if (!target) return NULL;
target->type = LOG_TGT_TYPE_FILE; - /* we create a ~640kB sized talloc pool within the write-queue to ensure individual - * log lines (stored as msgbs) will not put result in malloc() calls, and also to - * reduce the OOM probability within logging, as the pool is already allocated */ - wq = talloc_pooled_object(target, struct osmo_wqueue, LOG_WQUEUE_LEN, - LOG_WQUEUE_LEN*(sizeof(struct msgb)+MAX_LOG_SIZE)); - if (!wq) { - log_target_destroy(target); - return NULL; - } - osmo_wqueue_init(wq, LOG_WQUEUE_LEN); - wq->bfd.fd = open(fname, O_WRONLY|O_APPEND|O_CREAT|O_NONBLOCK, 0660); - if (wq->bfd.fd < 0) { - talloc_free(wq); - log_target_destroy(target); - return NULL; - } - wq->bfd.when = OSMO_FD_WRITE; - wq->write_cb = _file_wq_write_cb; - - rc = osmo_fd_register(&wq->bfd); - if (rc < 0) { - talloc_free(wq); - log_target_destroy(target); - return NULL; - } - - target->tgt_file.wqueue = wq; - target->raw_output = _file_raw_output; target->tgt_file.fname = talloc_strdup(target, fname); + OSMO_ASSERT(target->tgt_file.fname); + target->raw_output = _file_raw_output; + + fd = open(fname, O_WRONLY|O_APPEND|O_CREAT|O_NONBLOCK, 0660); + if (fd < 0) + goto free_ret; + + target->tgt_file.iofd = osmo_iofd_setup(target, fd, target->tgt_file.fname, + OSMO_IO_FD_MODE_READ_WRITE, + &log_target_file_io_ops, target); + if (!target->tgt_file.iofd) { + close(fd); + goto free_ret; + } + + osmo_iofd_set_txqueue_max_length(target->tgt_file.iofd, LOG_WQUEUE_LEN); + + rc = osmo_iofd_register(target->tgt_file.iofd, -1); + if (rc < 0) + goto free_ret;
return target; + +free_ret: + log_target_destroy(target); + return NULL; } #endif
diff --git a/src/vty/logging_vty.c b/src/vty/logging_vty.c index 6fd7abc..8e10582 100644 --- a/src/vty/logging_vty.c +++ b/src/vty/logging_vty.c @@ -1043,7 +1043,7 @@ return 1; break; case LOG_TGT_TYPE_STDERR: - if (tgt->tgt_file.wqueue) + if (tgt->tgt_file.iofd) vty_out(vty, "log stderr%s", VTY_NEWLINE); else vty_out(vty, "log stderr blocking-io%s", VTY_NEWLINE); @@ -1057,7 +1057,7 @@ #endif break; case LOG_TGT_TYPE_FILE: - if (tgt->tgt_file.wqueue) + if (tgt->tgt_file.iofd) vty_out(vty, "log file %s%s", tgt->tgt_file.fname, VTY_NEWLINE); else vty_out(vty, "log file %s blocking-io%s", tgt->tgt_file.fname, VTY_NEWLINE); diff --git a/tests/logging/logging_test.c b/tests/logging/logging_test.c index 3548b1d..27ad550 100644 --- a/tests/logging/logging_test.c +++ b/tests/logging/logging_test.c @@ -16,6 +16,7 @@ */
#include <osmocom/core/logging.h> +#include <osmocom/core/select.h> #include <osmocom/core/utils.h>
#include <stdlib.h> @@ -138,5 +139,8 @@ log_set_print_filename_pos(stderr_target, LOG_FILENAME_POS_LINE_END); DEBUGP(DLGLOBAL, "A message with source info printed last\n");
+ for (int i = 0; i < 20; i++) + osmo_select_main(1); + return 0; } diff --git a/tests/logging/logging_test.err b/tests/logging/logging_test.err index 01ab878..fb5181c 100644 --- a/tests/logging/logging_test.err +++ b/tests/logging/logging_test.err @@ -7,5 +7,5 @@ DLGLOBAL You should see this on DLGLOBAL (d) DLGLOBAL You should see this on DLGLOBAL (e) DLGLOBAL You should see this (DLGLOBAL on DEBUG) -DLGLOBAL logging_test.c:137 A message with source info printed first -DLGLOBAL A message with source info printed last (logging_test.c:139) +DLGLOBAL logging_test.c:138 A message with source info printed first +DLGLOBAL A message with source info printed last (logging_test.c:140)