add rudimentary unit tests, and logging timestamps

Signed-off-by: Aidan Hahn <aidan@aidanis.online>
This commit is contained in:
Aidan Hahn 2022-02-28 01:09:33 -08:00
parent 7fafcd344d
commit 1e9b701542
No known key found for this signature in database
GPG key ID: 327711E983899316
6 changed files with 188 additions and 37 deletions

View file

@ -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)

View file

@ -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:

45
alog.c
View file

@ -20,6 +20,8 @@
#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#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,6 +83,12 @@ void alog_remove_target(int fd) {
}
}
if (!out_fds) {
_alog_num_out_fds = 0;
free(_alog_out_fds);
_alog_out_fds = NULL;
} else {
int *tmp_outs = malloc(sizeof(int) * out_fds);
int tmp_iter_idx = 0;
for (int i = 0; i < _alog_num_out_fds; i++) {
@ -93,6 +101,7 @@ void alog_remove_target(int fd) {
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,8 +110,14 @@ void alog_remove_target(int fd) {
}
}
if (!err_fds) {
_alog_num_err_fds = 0;
free(_alog_err_fds);
_alog_err_fds = NULL;
} else {
int *tmp_errs = malloc(sizeof(int) * err_fds);
tmp_iter_idx = 0;
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];
@ -113,8 +128,11 @@ void alog_remove_target(int fd) {
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);
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) {

2
alog.h
View file

@ -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);

82
tests/log_test.c Normal file
View file

@ -0,0 +1,82 @@
#include "../alog.h"
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
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;
}

12
tests/tests.mk Normal file
View file

@ -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