From 1e9b7015420d8e2e85611b2c4387faeb444d14bf Mon Sep 17 00:00:00 2001 From: Aidan Hahn Date: Mon, 28 Feb 2022 01:09:33 -0800 Subject: [PATCH] add rudimentary unit tests, and logging timestamps Signed-off-by: Aidan Hahn --- Makefile | 23 +++++++------ README.md | 17 ++++++++- alog.c | 89 ++++++++++++++++++++++++++++++++++-------------- alog.h | 2 ++ tests/log_test.c | 82 ++++++++++++++++++++++++++++++++++++++++++++ tests/tests.mk | 12 +++++++ 6 files changed, 188 insertions(+), 37 deletions(-) create mode 100644 tests/log_test.c create mode 100644 tests/tests.mk diff --git a/Makefile b/Makefile index d9517ff..59e630d 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ CC ?= gcc -CFLAGS = -fPIC -Wall -Wextra -O2 -c -LDFLAGS = -shared BUILD_DIR ?= $(shell pwd)/build TARGET_DIR ?= $(shell pwd)/target +CFLAGS = -Wall -Wextra -O2 +LDFLAGS = -shared ifdef ALOG_HIJACK_PRINTF CFLAGS += -DALOG_HIJACK_PRINTF @@ -12,17 +12,20 @@ ifdef ALOG_DEBUG CFLAGS += -g endif -OBJ = $(BUILD_DIR)/alog.o -LIB = $(TARGET_DIR)/alog.so +ALOG_SRC = alog.c +ALOG_OBJ = $(BUILD_DIR)/alog.o +ALOG_LIB = $(TARGET_DIR)/alog.so + +include tests/tests.mk .PHONY: so -so: $(if $(shell stat $(LIB)), clean) $(LIB) +so: $(if $(shell stat $(ALOG_LIB)), clean) $(ALOG_LIB) -$(LIB): $(TARGET_DIR) $(OBJ) - $(CC) $(LDFLAGS) -o $(LIB) $(OBJ) +$(ALOG_LIB): $(TARGET_DIR) $(ALOG_OBJ) + $(CC) $(LDFLAGS) -o $(ALOG_LIB) $(ALOG_OBJ) -$(OBJ): $(BUILD_DIR) - $(CC) $(CFLAGS) alog.c -o $(OBJ) +$(ALOG_OBJ): $(BUILD_DIR) + $(CC) $(CFLAGS) -fPIC -c alog.c -o $(ALOG_OBJ) $(BUILD_DIR): mkdir $(BUILD_DIR) @@ -32,4 +35,4 @@ $(TARGET_DIR): .PHONY: clean clean: - rm $(LIB) $(OBJ) + rm $(ALOG_LIB) $(ALOG_OBJ) diff --git a/README.md b/README.md index 66f62de..76b8a43 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,28 @@ # Introduction - *alog* or *a logger* is a lightweight, portable logger written in C. - It is entirely unremarkable -- Usage of *alog* is extremely simple. [See the API](alog.h) +- Usage of *alog* is extremely simple. + +### What kind of things can I log to? +Anything that is a file descriptor and can be written to with write() and fsync(). This includes the following: +- pipes +- files +- stdout +- stderr +- sockets (untested) + +### How do I use this library? +- [See the API](alog.h) +- [See the tests](tests/log_test.c) # How to build ```bash $ make so ``` +# How to test +``` + # Variables The following (shell) variables can be set to influence behavior at runtime: diff --git a/alog.c b/alog.c index 3e4b83a..9abf360 100644 --- a/alog.c +++ b/alog.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #define TEST_ALOG_STATE() if (!ALOG_LOGGING_STATE) { _init_alog(); } @@ -37,7 +39,7 @@ void _init_alog() { _alog_num_err_fds = 1; _alog_err_fds = malloc(sizeof(int) * 1); // stderr - _alog_err_fds[1] = 2; + _alog_err_fds[0] = 2; } // TODO: should I dont close stdin and stdout? @@ -81,18 +83,25 @@ void alog_remove_target(int fd) { } } - int *tmp_outs = malloc(sizeof(int) * out_fds); - int tmp_iter_idx = 0; - for (int i = 0; i < _alog_num_out_fds; i++) { - if (_alog_out_fds[i] != fd) { - tmp_outs[tmp_iter_idx] = _alog_out_fds[i]; - tmp_iter_idx++; - } - } + if (!out_fds) { + _alog_num_out_fds = 0; + free(_alog_out_fds); + _alog_out_fds = NULL; - free(_alog_out_fds); - _alog_out_fds = tmp_outs; - _alog_num_out_fds = out_fds; + } else { + int *tmp_outs = malloc(sizeof(int) * out_fds); + int tmp_iter_idx = 0; + for (int i = 0; i < _alog_num_out_fds; i++) { + if (_alog_out_fds[i] != fd) { + tmp_outs[tmp_iter_idx] = _alog_out_fds[i]; + tmp_iter_idx++; + } + } + + free(_alog_out_fds); + _alog_out_fds = tmp_outs; + _alog_num_out_fds = out_fds; + } int err_fds = 0; for (int i = 0; i < _alog_num_err_fds; i++) { @@ -101,20 +110,29 @@ void alog_remove_target(int fd) { } } - int *tmp_errs = malloc(sizeof(int) * err_fds); - tmp_iter_idx = 0; - for (int i = 0; i < _alog_num_err_fds; i++) { - if (_alog_err_fds[i] != fd) { - tmp_errs[tmp_iter_idx] = _alog_err_fds[i]; - tmp_iter_idx++; - } - } + if (!err_fds) { + _alog_num_err_fds = 0; + free(_alog_err_fds); + _alog_err_fds = NULL; - free(_alog_err_fds); - _alog_err_fds = tmp_errs; - _alog_num_err_fds = err_fds; + } else { + int *tmp_errs = malloc(sizeof(int) * err_fds); + int tmp_iter_idx = 0; + for (int i = 0; i < _alog_num_err_fds; i++) { + if (_alog_err_fds[i] != fd) { + tmp_errs[tmp_iter_idx] = _alog_err_fds[i]; + tmp_iter_idx++; + } + } + + free(_alog_err_fds); + _alog_err_fds = tmp_errs; + _alog_num_err_fds = err_fds; + } } +// TODO: use preprocessor directives to gate off the posix timestamp stuff +// unless the right variable is passed in at compile time void alog ( alog_sev severity, const char * message, @@ -130,9 +148,28 @@ void alog ( va_list fmt_list; va_start(fmt_list, message); - int size = snprintf(NULL, 0, message, fmt_list); - char *buffer = malloc(size + 1); - sprintf(buffer, message, fmt_list); + char *buffer; + int size; + if (severity != PRINT) { + // GET TIMESTAMP + time_t t = time(NULL); + struct tm * p = localtime(&t); + int timestamp_size = strftime(NULL, 0, "[%c]", p); + char *timestamp = malloc(sizeof(char) * (timestamp_size+1)); + strftime(timestamp, timestamp_size, "[%c]", p); + char *msg_and_timestamp = malloc(sizeof(char) * (timestamp_size + strlen(message) + 3)); + sprintf(msg_and_timestamp, "%s %s\n", timestamp, message); + free(timestamp); + size = snprintf(NULL, 0, msg_and_timestamp, fmt_list); + buffer = malloc(size + 1); + sprintf(buffer, msg_and_timestamp, fmt_list); + free(msg_and_timestamp); + // if severity is PRINT we avoid timestamp + } else { + size = snprintf(NULL, 0, message, fmt_list); + buffer = malloc(size + 1); + sprintf(buffer, message, fmt_list); + } for (int i = 0; i < _alog_num_out_fds; i++) { if (write(_alog_out_fds[i], buffer, size) < size) { diff --git a/alog.h b/alog.h index 086cc28..b7e8cfb 100644 --- a/alog.h +++ b/alog.h @@ -71,6 +71,8 @@ void deinit_alog(); /* adds an fd to out fds or (iff char arg is set to 0x01) err fds */ void alog_add_target(int, char); +#define ALOG_OUT 0x00 +#define ALOG_ERR 0x01 /* removes an fd from both out fds and err fds */ void alog_remove_target(int); diff --git a/tests/log_test.c b/tests/log_test.c new file mode 100644 index 0000000..d73a223 --- /dev/null +++ b/tests/log_test.c @@ -0,0 +1,82 @@ +#include "../alog.h" +#include +#include +#include +#include +#include + +int out_fd[2]; +int err_fd[2]; + +const char *out_message = "standard message"; +const char *err_message = "error message"; + +int main() { + // make RPC pipe + pipe(out_fd); + pipe(err_fd); + + // remove stdout and stderr + _init_alog(); + alog_remove_target(1); + alog_remove_target(2); + + pid_t childpid; + if ((childpid = fork()) == -1) { + perror("fork fail"); + return 1; + } + + // child process + if (childpid == 0) { + close(out_fd[0]); + close(err_fd[0]); + + // add alog targets in this pipe + alog_add_target(out_fd[1], ALOG_OUT); + alog_add_target(err_fd[1], ALOG_ERR); + + // write to the things + alog(ERROR, err_message); + alog(PRINT, out_message); + + exit(0); + // parent process + } else { + // we can do checks from here + char out_read_buffer[128] = {0}; + char err_read_buffer[128] = {0}; + + usleep(10); + + read(err_fd[0], err_read_buffer, 128); + // cant check read len because of timestamp + if (!strstr(err_read_buffer, err_message)) { + printf("err log should contain '%s'.\ninstead reads '%s'\n", + err_message, err_read_buffer); + return 1; + } + + int num_out = read(out_fd[0], out_read_buffer, 128); + if (num_out != (int) (strlen(out_message) + strlen(err_read_buffer))) { + printf("Read %i bytes from out log when %lu were written!\n", + num_out, strlen(out_message) + strlen(err_read_buffer) + 1); + return 1; + } + if (strncmp(out_read_buffer, err_read_buffer, strlen(err_read_buffer))) { + printf("out log first message should read '%s'.\ninstead reads '%s'\n", + err_read_buffer, out_read_buffer); + } + if (strcmp(&out_read_buffer[strlen(err_read_buffer)], out_message)) { + printf("out log second message should read '%s'.\ninstead reads '%s'\n", + out_message, &out_read_buffer[strlen(err_read_buffer)]); + return 1; + } + + + printf("out and err log printing are successfull.\n"); + } + + deinit_alog(); + return 0; +} diff --git a/tests/tests.mk b/tests/tests.mk new file mode 100644 index 0000000..9cc1397 --- /dev/null +++ b/tests/tests.mk @@ -0,0 +1,12 @@ +ALOG_TEST_SRCS = $(shell find tests -iname "*.c" -exec basename {} \;) +ALOG_TESTS = $(ALOG_TEST_SRCS:.c=) + +$(ALOG_TESTS): $(ALOG_LIB) + $(CC) $(CFLAGS) -g -o $(BUILD_DIR)/$@.o -c tests/$@.c + $(CC) -o $(TARGET_DIR)/$@ $(BUILD_DIR)/$@.o $(ALOG_LIB) + chmod +x $(TARGET_DIR)/$@ + +run-tests: $(ALOG_TESTS) + for test in $^ ; do \ + $(TARGET_DIR)/$$test ; \ + done