waitui/library/log/src/log.c
Rick Barenthin 219812f20e
All checks were successful
continuous-integration/drone/push Build is passing
feat(hashtable): add a hashtable implementation
This is a generic hashtable implementation.

Reviewed-on: #12
PR #12
2022-07-27 19:10:09 +02:00

225 lines
6.6 KiB
C

/**
* @file log.c
* @author rick
* @date 28.08.20
* @brief File for the Log implementation
*/
#include "waitui/log.h"
// -----------------------------------------------------------------------------
// Local defines
// -----------------------------------------------------------------------------
/**
* @brief The maximum number of possible callbacks to register
*/
#define MAX_CALLBACKS 32
// -----------------------------------------------------------------------------
// Local types
// -----------------------------------------------------------------------------
/**
* @brief Internal type to store the log callback function with all its info
*/
typedef struct waitui_log_callback {
waitui_log_logging_fn logFn;
waitui_log_level level;
void *userData;
} waitui_log_callback;
/**
* @brief Internal type to store the log with all its info
*/
typedef struct waitui_log {
waitui_log_lock_fn lockFn;
waitui_log_level level;
bool quiet;
waitui_log_callback callbacks[MAX_CALLBACKS];
void *userData;
} log;
// -----------------------------------------------------------------------------
// Local variables
// -----------------------------------------------------------------------------
/**
* @brief The internal state of the log lib
*/
static struct waitui_log L;
/**
* @brief String representations of the log levels
*/
static const char *waitui_log_level_strings[] = {
"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL",
};
#ifdef LOG_USE_COLOR
/**
* @brief Color representations of the log levels
*/
static const char *waitui_log_level_colors[] = {
"\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m",
};
#endif
// -----------------------------------------------------------------------------
// Local functions
// -----------------------------------------------------------------------------
/**
* @brief Return the string representations of the log level
* @param level The level to return as a string.
* @return The string representations of the log level or empty when out of bounds.
*/
static inline const char *waitui_log_levelAsString(waitui_log_level level) {
if (level < WAITUI_LOG_TRACE || level >= WAITUI_LOG_MAX) { return ""; }
return waitui_log_level_strings[level];
}
/**
* @brief Return the color representations of the log level
* @param level The level to return as a color.
* @return The color representations of the log level or empty when out of bounds.
*/
static inline const char *waitui_log_levelAsColor(waitui_log_level level) {
if (level < WAITUI_LOG_TRACE || level >= WAITUI_LOG_MAX) { return ""; }
return waitui_log_level_colors[level];
}
/**
* @brief Calls the locking callback function to get the lock
*/
static inline void waitui_log_lock(void) {
if (L.lockFn) { L.lockFn(true, L.userData); }
}
/**
* @brief Calls the locking callback function to release the lock
*/
static inline void waitui_log_unlock(void) {
if (L.lockFn) { L.lockFn(false, L.userData); }
}
/**
* @brief Callback that write the log event to the console
* @param[in] event log event to write to the console
*/
static void waitui_log_console_callback(waitui_log_event *event) {
char buffer[16];
buffer[strftime(buffer, sizeof(buffer), "%H:%M:%S", event->time)] = '\0';
#ifdef LOG_USE_COLOR
fprintf(event->userData, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", buffer,
waitui_log_levelAsColor(event->level),
waitui_log_levelAsString(event->level), event->file, event->line);
#else
fprintf(event->userData, "%s %-5s %s:%d: ", buffer,
waitui_log_levelAsString(event->level), event->file, event->line);
#endif
vfprintf(event->userData, event->format, event->ap);
fprintf(event->userData, "\n");
fflush(event->userData);
}
/**
* @brief Callback that write the log event to a file
* @param[in] event log event to write to a file
*/
static void waitui_log_file_callback(waitui_log_event *event) {
char buffer[64];
buffer[strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", event->time)] =
'\0';
fprintf(event->userData, "%s %-5s %s:%d: ", buffer,
waitui_log_levelAsString(event->level), event->file, event->line);
vfprintf(event->userData, event->format, event->ap);
fprintf(event->userData, "\n");
fflush(event->userData);
}
/**
* @brief Initializes the log event with time and userdata
* @param[in,out] event log event to write to initialize
* @param[in] user_data user data to set for the log event
*/
static inline void waitui_log_init_event(waitui_log_event *event,
void *userData) {
if (!event->time) {
time_t t = time(NULL);
event->time = localtime(&t);
}
event->userData = userData;
}
// -----------------------------------------------------------------------------
// Public functions
// -----------------------------------------------------------------------------
void waitui_log_set_lock(waitui_log_lock_fn lockFn, void *userData) {
L.lockFn = lockFn;
L.userData = userData;
}
void waitui_log_setLevel(waitui_log_level level) { L.level = level; }
void waitui_log_setQuiet(bool enable) { L.quiet = enable; }
int waitui_log_addCallback(waitui_log_logging_fn logFn, void *userData,
waitui_log_level level) {
for (int i = 0; i < MAX_CALLBACKS; ++i) {
if (!L.callbacks[i].logFn) {
L.callbacks[i] = (waitui_log_callback){.logFn = logFn,
.userData = userData,
.level = level};
return 1;
}
}
return 0;
}
int waitui_log_addFile(FILE *file, waitui_log_level level) {
return waitui_log_addCallback(waitui_log_file_callback, file, level);
}
void waitui_log_writeLog(waitui_log_level level, const char *file, int line,
const char *format, ...) {
waitui_log_event event = {
.format = format,
.file = file,
.line = line,
.level = level,
};
waitui_log_lock();
if (!L.quiet && level >= L.level) {
waitui_log_init_event(&event, stderr);
va_start(event.ap, format);
waitui_log_console_callback(&event);
va_end(event.ap);
}
for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].logFn; ++i) {
waitui_log_callback *cb = &L.callbacks[i];
if (level >= cb->level) {
waitui_log_init_event(&event, cb->userData);
va_start(event.ap, format);
cb->logFn(&event);
va_end(event.ap);
}
}
waitui_log_unlock();
}