diff --git a/Makefile b/Makefile index c7d07a2..35fb163 100644 --- a/Makefile +++ b/Makefile @@ -7,15 +7,16 @@ BIN_NAME = cfloor all: $(BIN_NAME) -test: obj/test.o obj/networking.o obj/linked.o +test: obj/test.o obj/networking.o obj/linked.o obj/logging.o $(LD) $(LDFLAGS) -o $@ $^ valgrind: CFLAGS += -static -g valgrind: clean test valgrind --leak-check=yes ./test -obj/test.o: src/networking.h -obj/networking.o: src/networking.h src/headers.h +obj/test.o: src/networking.h src/linked.h src/logging.h +obj/networking.o: src/networking.h src/headers.h src/linked.h obj/linked.o: src/linked.h +obj/loggin.o: src/logging.h obj/%.o: src/%.c obj $(CC) $(CFLAGS) -c -o $@ $< diff --git a/src/logging.c b/src/logging.c new file mode 100644 index 0000000..865baf0 --- /dev/null +++ b/src/logging.c @@ -0,0 +1,269 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "logging.h" + +#ifdef BACKTRACE +#include +#endif + +struct { + FILE* file; + enum loglevel loglevel; + bool color; +} logger[MAX_LOGGER]; +int loggerCount = 0; + +void (*_criticalHandler)() = NULL; + +void setLogging(FILE* file, enum loglevel loglevel, bool color) { + if (loggerCount == MAX_LOGGER - 1) { + return; + } + + bool found = false; + for (int i = 0; i < loggerCount; i++) { + if ((logger[i].file == file)) { + found = true; + logger[i].loglevel = loglevel; + logger[i].color = color; + break; + } + } + + if (!found) { + logger[loggerCount].file = file; + logger[loggerCount].loglevel = loglevel; + logger[loggerCount].color = color; + loggerCount++; + } +} + +void setCriticalHandler(void (*handler)()) { + _criticalHandler = handler; +} + +void callCritical() { + if (_criticalHandler != NULL) + _criticalHandler(); +} + +#define BACKTRACE_BUFFER_SIZE 16 +void printBacktrace() { + #ifdef BACKTRACE + + void* buffer[BACKTRACE_BUFFER_SIZE]; + char** strings; + int entries = backtrace(buffer, BACKTRACE_BUFFER_SIZE); + strings = backtrace_symbols(buffer, entries); + + if (strings == NULL) { + fprintf(stderr, "Error while backtracing: %s\n", strerror(errno)); + return; + } + + int tmp = (entries < BACKTRACE_BUFFER_SIZE) ? entries : entries - 1; + + // from 1 to ignore printBacktrace + for (int i = 1; i < tmp; i++) { + fprintf(stderr, " at %s\n", strings[i]); + } + if (tmp < entries) { + fprintf(stderr, " ...\n"); + } + + #else + + fprintf(stderr, "Error: Not compiled with backtrace support.\n"); + + #endif +} + +char* getTimestamp() { + #define MSEC_LENGTH (3) + #define TIME_LENGTH (4 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + MSEC_LENGTH) + char* result = malloc(TIME_LENGTH + 1); + if (result == NULL) { + fprintf(stderr, "\nDEVASTATING: Couldn't malloc for log time.\n"); + callCritical(); + exit(EXIT_DEVASTATING); + } + + + int millisec; + struct tm* tm_info; + struct timeval tv; + + gettimeofday(&tv, NULL); + + millisec = (int) (tv.tv_usec / 1000.0); + if (millisec >= 1000) { + millisec -=1000; + tv.tv_sec++; + } + + tm_info = localtime(&tv.tv_sec); + + strftime(result, 26, "%Y-%m-%dT%H:%M:%S", tm_info); + + char* msecBuffer = malloc(1 + MSEC_LENGTH + 1); + + if (msecBuffer == NULL) { + fprintf(stderr, "\nDEVASTATING: Couldn't malloc for log time.\n"); + callCritical(); + exit(EXIT_DEVASTATING); + } + + snprintf(msecBuffer, 1 + MSEC_LENGTH + 1, ".%03d", millisec); + + strncat(result, msecBuffer, TIME_LENGTH + 1); + + free(msecBuffer); + + return result; +} + +char* getLoglevelString(enum loglevel loglevel, bool color) { + #define DEBUG_STRING "[DEBUG]" + #define VERBOSE_STRING "[VERBOSE]" + #define INFO_STRING "[INFO]" + #define WARN_STRING "[WARNING]" + #define ERROR_STRING "[ERROR]" + #define CRITICAL_STRING "[CRITICAL]" + #define UNKNOWN_STRING "[UNKNOWN]" + + #define DEBUG_COLOR "\033[37m" + #define VERBOSE_COLOR "\033[35m" + #define INFO_COLOR "\033[36m" + #define WARN_COLOR "\033[33m" + #define ERROR_COLOR "\033[31m" + #define CRITICAL_COLOR "\033[41m\033[30m" + #define UNKNOWN_COLOR "\033[41m\033[30m" + + #define COLOR_END "\033[0m" + + switch(loglevel) { + case DEBUG: + if (color) + return DEBUG_COLOR DEBUG_STRING COLOR_END; + else + return DEBUG_STRING; + break; + case VERBOSE: + if (color) + return VERBOSE_COLOR VERBOSE_STRING COLOR_END; + else + return VERBOSE_STRING; + break; + case INFO: + if (color) + return INFO_COLOR INFO_STRING COLOR_END; + else + return INFO_STRING; + break; + case WARN: + if (color) + return WARN_COLOR WARN_STRING COLOR_END; + else + return WARN_STRING; + break; + case ERROR: + if (color) + return ERROR_COLOR ERROR_STRING COLOR_END; + else + return ERROR_STRING; + break; + case CRITICAL: + if (color) + return CRITICAL_COLOR CRITICAL_STRING COLOR_END; + else + return CRITICAL_STRING; + break; + default: + if (color) + return UNKNOWN_COLOR UNKNOWN_STRING COLOR_END; + else + return UNKNOWN_STRING; + break; + } + + assert(false); + return NULL; +} + +void vlogging(enum loglevel loglevel, const char* format, va_list argptr) { + char* timestamp = getTimestamp(); + + for(int i = 0; i < loggerCount; i++) { + if (loglevel < logger[i].loglevel) + continue; + + char* loglevelString = getLoglevelString(loglevel, logger[i].color); + fprintf(logger[i].file, "%s %s ", timestamp, loglevelString); + + vfprintf(logger[i].file, format, argptr); + + fprintf(logger[i].file, "\n"); + } + + free(timestamp); + + if (loglevel == CRITICAL) + callCritical(); +} + +void logging(enum loglevel loglevel, const char* format, ...) { + va_list argptr; + va_start(argptr, format); + vlogging(loglevel, format, argptr); + va_end(argptr); +} + +void debug(const char* format, ...) { + va_list argptr; + va_start(argptr, format); + vlogging(DEBUG, format, argptr); + va_end(argptr); +} + +void verbose(const char* format, ...) { + va_list argptr; + va_start(argptr, format); + vlogging(VERBOSE, format, argptr); + va_end(argptr); +} + +void info(const char* format, ...) { + va_list argptr; + va_start(argptr, format); + vlogging(INFO, format, argptr); + va_end(argptr); +} + +void warn(const char* format, ...) { + va_list argptr; + va_start(argptr, format); + vlogging(WARN, format, argptr); + va_end(argptr); +} + +void error(const char* format, ...) { + va_list argptr; + va_start(argptr, format); + vlogging(ERROR, format, argptr); + va_end(argptr); +} + +void critical(const char* format, ...) { + va_list argptr; + va_start(argptr, format); + vlogging(CRITICAL, format, argptr); + va_end(argptr); +} diff --git a/src/logging.h b/src/logging.h new file mode 100644 index 0000000..0ad7ad8 --- /dev/null +++ b/src/logging.h @@ -0,0 +1,29 @@ +#ifndef LOGGING_H +#define LOGGING_H + +#include + +enum loglevel { + DEBUG, VERBOSE, INFO, WARN, ERROR, CRITICAL +}; + +#define EXIT_DEVASTATING (255) + +#define DEFAULT_LOGLEVEL (WARN) + +#define MAX_LOGGER (10) + +void setLogging(FILE* file, enum loglevel loglevel, bool color); +void setCriticalHandler(void (*handler)()); + +void printBacktrace(); + +void logging(enum loglevel loglevel, const char* format, ...); +void debug(const char* format, ...); +void verbose(const char* format, ...); +void info(const char* format, ...); +void warn(const char* format, ...); +void error(const char* format, ...); +void critical(const char* format, ...); + +#endif diff --git a/src/test.c b/src/test.c index 6f4db2a..c9fe21b 100644 --- a/src/test.c +++ b/src/test.c @@ -5,6 +5,7 @@ #include "networking.h" #include "linked.h" +#include "logging.h" bool global = true; @@ -84,6 +85,22 @@ void testLinkedList() { linked_destroy(&list); } +void criticalHandler() { + printf("This is the critical handler.\n"); + printBacktrace(); +} +void testLogging() { + setLogging(stderr, DEFAULT_LOGLEVEL, true); + + info("This info should not be displayed."); + warn("This warning should be displayed."); + error("This error should be displayed."); + + setCriticalHandler(&criticalHandler); + + critical("This critical should be displayed."); +} + handler_t handlerGetter(struct metaData metaData, const char* host) { return NULL; } @@ -109,6 +126,14 @@ int main(int argc, char** argv) { printf("linked lists: %s\n\n", global ? "OK" : "FAILED"); global = true; + printf("logging\n"); + printf("============\n\n"); + testLogging(); + if (!global) + overall = false; + printf("logging: %s\n\n", global ? "OK" : "FAILED"); + global = true; + printf("\nOverall: %s\n", overall ? "OK" : "FAILED"); }