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
|
CC ?= gcc
|
||||||
CFLAGS = -fPIC -Wall -Wextra -O2 -c
|
|
||||||
LDFLAGS = -shared
|
|
||||||
BUILD_DIR ?= $(shell pwd)/build
|
BUILD_DIR ?= $(shell pwd)/build
|
||||||
TARGET_DIR ?= $(shell pwd)/target
|
TARGET_DIR ?= $(shell pwd)/target
|
||||||
|
CFLAGS = -Wall -Wextra -O2
|
||||||
|
LDFLAGS = -shared
|
||||||
|
|
||||||
ifdef ALOG_HIJACK_PRINTF
|
ifdef ALOG_HIJACK_PRINTF
|
||||||
CFLAGS += -DALOG_HIJACK_PRINTF
|
CFLAGS += -DALOG_HIJACK_PRINTF
|
||||||
|
|
@ -12,17 +12,20 @@ ifdef ALOG_DEBUG
|
||||||
CFLAGS += -g
|
CFLAGS += -g
|
||||||
endif
|
endif
|
||||||
|
|
||||||
OBJ = $(BUILD_DIR)/alog.o
|
ALOG_SRC = alog.c
|
||||||
LIB = $(TARGET_DIR)/alog.so
|
ALOG_OBJ = $(BUILD_DIR)/alog.o
|
||||||
|
ALOG_LIB = $(TARGET_DIR)/alog.so
|
||||||
|
|
||||||
|
include tests/tests.mk
|
||||||
|
|
||||||
.PHONY: so
|
.PHONY: so
|
||||||
so: $(if $(shell stat $(LIB)), clean) $(LIB)
|
so: $(if $(shell stat $(ALOG_LIB)), clean) $(ALOG_LIB)
|
||||||
|
|
||||||
$(LIB): $(TARGET_DIR) $(OBJ)
|
$(ALOG_LIB): $(TARGET_DIR) $(ALOG_OBJ)
|
||||||
$(CC) $(LDFLAGS) -o $(LIB) $(OBJ)
|
$(CC) $(LDFLAGS) -o $(ALOG_LIB) $(ALOG_OBJ)
|
||||||
|
|
||||||
$(OBJ): $(BUILD_DIR)
|
$(ALOG_OBJ): $(BUILD_DIR)
|
||||||
$(CC) $(CFLAGS) alog.c -o $(OBJ)
|
$(CC) $(CFLAGS) -fPIC -c alog.c -o $(ALOG_OBJ)
|
||||||
|
|
||||||
$(BUILD_DIR):
|
$(BUILD_DIR):
|
||||||
mkdir $(BUILD_DIR)
|
mkdir $(BUILD_DIR)
|
||||||
|
|
@ -32,4 +35,4 @@ $(TARGET_DIR):
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
rm $(LIB) $(OBJ)
|
rm $(ALOG_LIB) $(ALOG_OBJ)
|
||||||
|
|
|
||||||
17
README.md
17
README.md
|
|
@ -1,13 +1,28 @@
|
||||||
# Introduction
|
# Introduction
|
||||||
- *alog* or *a logger* is a lightweight, portable logger written in C.
|
- *alog* or *a logger* is a lightweight, portable logger written in C.
|
||||||
- It is entirely unremarkable
|
- 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
|
# How to build
|
||||||
```bash
|
```bash
|
||||||
$ make so
|
$ make so
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# How to test
|
||||||
|
```
|
||||||
|
|
||||||
# Variables
|
# Variables
|
||||||
The following (shell) variables can be set to influence behavior at runtime:
|
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 <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#define TEST_ALOG_STATE() if (!ALOG_LOGGING_STATE) { _init_alog(); }
|
#define TEST_ALOG_STATE() if (!ALOG_LOGGING_STATE) { _init_alog(); }
|
||||||
|
|
||||||
|
|
@ -37,7 +39,7 @@ void _init_alog() {
|
||||||
_alog_num_err_fds = 1;
|
_alog_num_err_fds = 1;
|
||||||
_alog_err_fds = malloc(sizeof(int) * 1);
|
_alog_err_fds = malloc(sizeof(int) * 1);
|
||||||
// stderr
|
// stderr
|
||||||
_alog_err_fds[1] = 2;
|
_alog_err_fds[0] = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: should I dont close stdin and stdout?
|
// 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_outs = malloc(sizeof(int) * out_fds);
|
||||||
int tmp_iter_idx = 0;
|
int tmp_iter_idx = 0;
|
||||||
for (int i = 0; i < _alog_num_out_fds; i++) {
|
for (int i = 0; i < _alog_num_out_fds; i++) {
|
||||||
|
|
@ -93,6 +101,7 @@ void alog_remove_target(int fd) {
|
||||||
free(_alog_out_fds);
|
free(_alog_out_fds);
|
||||||
_alog_out_fds = tmp_outs;
|
_alog_out_fds = tmp_outs;
|
||||||
_alog_num_out_fds = out_fds;
|
_alog_num_out_fds = out_fds;
|
||||||
|
}
|
||||||
|
|
||||||
int err_fds = 0;
|
int err_fds = 0;
|
||||||
for (int i = 0; i < _alog_num_err_fds; i++) {
|
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);
|
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++) {
|
for (int i = 0; i < _alog_num_err_fds; i++) {
|
||||||
if (_alog_err_fds[i] != fd) {
|
if (_alog_err_fds[i] != fd) {
|
||||||
tmp_errs[tmp_iter_idx] = _alog_err_fds[i];
|
tmp_errs[tmp_iter_idx] = _alog_err_fds[i];
|
||||||
|
|
@ -113,8 +128,11 @@ void alog_remove_target(int fd) {
|
||||||
free(_alog_err_fds);
|
free(_alog_err_fds);
|
||||||
_alog_err_fds = tmp_errs;
|
_alog_err_fds = tmp_errs;
|
||||||
_alog_num_err_fds = err_fds;
|
_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 (
|
void alog (
|
||||||
alog_sev severity,
|
alog_sev severity,
|
||||||
const char * message,
|
const char * message,
|
||||||
|
|
@ -130,9 +148,28 @@ void alog (
|
||||||
va_list fmt_list;
|
va_list fmt_list;
|
||||||
va_start(fmt_list, message);
|
va_start(fmt_list, message);
|
||||||
|
|
||||||
int size = snprintf(NULL, 0, message, fmt_list);
|
char *buffer;
|
||||||
char *buffer = malloc(size + 1);
|
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);
|
sprintf(buffer, message, fmt_list);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < _alog_num_out_fds; i++) {
|
for (int i = 0; i < _alog_num_out_fds; i++) {
|
||||||
if (write(_alog_out_fds[i], buffer, size) < size) {
|
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 */
|
/* adds an fd to out fds or (iff char arg is set to 0x01) err fds */
|
||||||
void alog_add_target(int, char);
|
void alog_add_target(int, char);
|
||||||
|
#define ALOG_OUT 0x00
|
||||||
|
#define ALOG_ERR 0x01
|
||||||
|
|
||||||
/* removes an fd from both out fds and err fds */
|
/* removes an fd from both out fds and err fds */
|
||||||
void alog_remove_target(int);
|
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