add rudimentary unit tests, and logging timestamps
Signed-off-by: Aidan Hahn <aidan@aidanis.online>
This commit is contained in:
parent
7fafcd344d
commit
1e9b701542
6 changed files with 188 additions and 37 deletions
23
Makefile
23
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)
|
||||
|
|
|
|||
17
README.md
17
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:
|
||||
|
||||
|
|
|
|||
45
alog.c
45
alog.c
|
|
@ -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
2
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);
|
||||
|
|
|
|||
82
tests/log_test.c
Normal file
82
tests/log_test.c
Normal 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
12
tests/tests.mk
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue