// vim:fileencoding=utf-8:foldmethod=marker #include "log.h" #include "../base/strings/str8/str8.h" #include "../common/aliases/aliases.h" #include "../common/assert/assert.h" #include "../common/misc/misc_utils.h" #include "../os/file/file.h" #include #define MIN_LOG_MSG_LENGTH 32 #define TIME_BUF_CAPACITY 70 wp_intern WpStr8RO L_BRACKET = wpStr8LitRo("["); wp_intern WpStr8RO R_BRACKET_SPACE = wpStr8LitRo("] "); wp_intern WpStr8RO R_BRACKET_NEWLINE = wpStr8LitRo("]\n"); typedef struct { WpFile *outlog; WpFile *errlog; WpLogLevel level; wpMiscUtilsReservePadding(2 * sizeof(WpFile *) + sizeof(WpLogLevel)); } LogConfig; wp_intern LogConfig LOG_CONFIG = { .level = WP_LOG_LEVEL_DEBUG, }; wp_intern WpStr8RO LOG_LEVEL_STRINGS[COUNT_LOG_LEVEL] = { [WP_LOG_LEVEL_FATAL] = wpStr8LitRoInitialiserList("fatal "), [WP_LOG_LEVEL_CRITICAL] = wpStr8LitRoInitialiserList("critical "), [WP_LOG_LEVEL_ERROR] = wpStr8LitRoInitialiserList("error "), [WP_LOG_LEVEL_WARNING] = wpStr8LitRoInitialiserList("warning "), [WP_LOG_LEVEL_INFO] = wpStr8LitRoInitialiserList("info "), [WP_LOG_LEVEL_DEBUG] = wpStr8LitRoInitialiserList("debug "), }; wp_intern void _get_current_time_string(WpStr8 *dst); wp_intern void _write_log_line(WpFile *fp, const WpLogger *logger, WpStr8 msg, WpLogLevel level); void wpLogSetLevel(WpLogLevel level) { LOG_CONFIG.level = level; } void wpLogConfigure(WpFile *outlog, WpFile *errlog, WpLogLevel level) { LOG_CONFIG.outlog = outlog; LOG_CONFIG.errlog = errlog; LOG_CONFIG.level = level; } WpLogger wpLogMakeLogger(WpStr8 name) { return (WpLogger){ .name = name }; } void wpLogDebug(const WpLogger *logger, WpStr8 msg) { wpDebugAssert(logger != NULL, "`logger` should not be NULL"); if (LOG_CONFIG.level < WP_LOG_LEVEL_DEBUG) { return; } WpFile *fp = LOG_CONFIG.outlog != NULL ? LOG_CONFIG.outlog : wpFileStdout(); _write_log_line(fp, logger, msg, WP_LOG_LEVEL_DEBUG); } void wpLogInfo(const WpLogger *logger, WpStr8 msg) { wpDebugAssert(logger != NULL, "`logger` should not be NULL"); if (LOG_CONFIG.level < WP_LOG_LEVEL_INFO) { return; } WpFile *fp = LOG_CONFIG.outlog != NULL ? LOG_CONFIG.outlog : wpFileStdout(); _write_log_line(fp, logger, msg, WP_LOG_LEVEL_INFO); } void wpLogWarning(const WpLogger *logger, WpStr8 msg) { wpDebugAssert(logger != NULL, "`logger` should not be NULL"); if (LOG_CONFIG.level < WP_LOG_LEVEL_WARNING) { return; } WpFile *fp = LOG_CONFIG.outlog != NULL ? LOG_CONFIG.outlog : wpFileStdout(); _write_log_line(fp, logger, msg, WP_LOG_LEVEL_WARNING); } void wpLogError(const WpLogger *logger, WpStr8 msg) { wpDebugAssert(logger != NULL, "`logger` should not be NULL"); if (LOG_CONFIG.level < WP_LOG_LEVEL_ERROR) { return; } WpFile *fp = LOG_CONFIG.errlog != NULL ? LOG_CONFIG.errlog : wpFileStderr(); _write_log_line(fp, logger, msg, WP_LOG_LEVEL_ERROR); } void wpLogCritical(const WpLogger *logger, WpStr8 msg) { wpDebugAssert(logger != NULL, "`logger` should not be NULL"); if (LOG_CONFIG.level < WP_LOG_LEVEL_CRITICAL) { return; } WpFile *fp = LOG_CONFIG.errlog != NULL ? LOG_CONFIG.errlog : wpFileStderr(); _write_log_line(fp, logger, msg, WP_LOG_LEVEL_CRITICAL); } void wpLogFatal(const WpLogger *logger, WpStr8 msg) { wpDebugAssert(logger != NULL, "`logger` should not be NULL"); if (LOG_CONFIG.level < WP_LOG_LEVEL_FATAL) { return; } WpFile *fp = LOG_CONFIG.errlog != NULL ? LOG_CONFIG.errlog : wpFileStderr(); _write_log_line(fp, logger, msg, WP_LOG_LEVEL_FATAL); } wp_intern void _get_current_time_string(WpStr8 *dst) { // TODO (Abdelrahman): Replace with proper date/time utilities char buf[TIME_BUF_CAPACITY]; time_t now = time(NULL); struct tm utc; gmtime_r(&now, &utc); strftime(buf, sizeof(buf), "%FT%TZ ", &utc); wpStr8CopyCstrCapped(dst, buf); } wp_intern void _write_log_line(WpFile *fp, const WpLogger *logger, WpStr8 msg, WpLogLevel level) { WpStr8 padding = wpStr8Buf(MIN_LOG_MSG_LENGTH); u32 padding_size = msg.size < MIN_LOG_MSG_LENGTH ? MIN_LOG_MSG_LENGTH - msg.size + 1 : 0; wpStr8Format(&padding, "%-*s", padding_size, " "); WpStr8 time_str = wpStr8Buf(TIME_BUF_CAPACITY); _get_current_time_string(&time_str); WpStr8RO **strings = wpArray( WpStr8RO *, &time_str, &L_BRACKET, &LOG_LEVEL_STRINGS[level], &R_BRACKET_SPACE, &msg, &padding, &L_BRACKET, &logger->name, &R_BRACKET_NEWLINE ); for (u64 i = 0; i < wpArrayCount(strings); ++i) { wpFileWriteStr8(strings[i], fp); } }