#include #include #include #include "core/basalt_logger.h" #include "memory.h" static struct { logger_stream_t streams[BASALT_LOGGER_MAX_STREAMS]; u8 global_mask; u8 num_streams; u8 initialized; u8 _padding[5];// Unused - Signifies how many bytes aren't being used } logger_state; void initialize_logger() { if (logger_state.initialized != 0x00) return; memset(&logger_state, 0, sizeof(logger_state)); #if defined(DEBUG) || defined(_DEBUG) logger_state.global_mask = LOG_MASK_ALL; #else logger_state.global_mask = LOG_MASK_DEFAULT; #endif logger_state.num_streams = 2; logger_add_stream(stdout, LOG_MASK_INFO | LOG_MASK_DEBUGGING, LOG_STREAM_FLAG_SUPPORTS_ANSI_BIT | LOG_STREAM_FLAG_INCLUDE_DATETIME_BIT); logger_add_stream(stderr, LOG_MASK_ERRORS, LOG_STREAM_FLAG_SUPPORTS_ANSI_BIT | LOG_STREAM_FLAG_INCLUDE_DATETIME_BIT); logger_state.initialized = 0xff; } void terminate_logger() { if (logger_state.initialized != 0xff) return; for (u8 i = 0; i < BASALT_LOGGER_MAX_STREAMS; ++i) logger_remove_stream(i); memset(&logger_state, 0, sizeof(logger_state)); logger_state.initialized = 0x00; } inline FILE* logger_stream_get_file(logger_stream_t* state) { return (FILE*)((state->stream & 0xffffffffffff) | ((u64)stdout & 0xffff0000000000)); } u8 logger_add_stream(FILE* output, LOG_LEVEL mask, LOG_STREAM_FLAGS flags) { if (logger_state.num_streams == BASALT_LOGGER_MAX_STREAMS) return -1; if ((((u64)output) & 0xffffffffffff) != 0) __builtin_trap(); for (u8 i = 0; i < BASALT_LOGGER_MAX_STREAMS; ++i) { if ((logger_state.streams[i].flags & LOG_STREAM_FLAG_INTERNAL_ALLOCATED_BIT) == 0) { logger_state.streams[i].stream = (u64)output; logger_state.streams[i].flags = flags; logger_state.streams[i].mask = mask; logger_state.num_streams++; return i; } } return -1; } void logger_remove_stream(u8 index) { if (index >= sizeof(logger_state.streams) / sizeof(logger_state.streams[0])) return; logger_stream_t* stream = logger_state.streams+index; FILE* f = logger_stream_get_file(stream); if (f != stdout && f != stderr) fclose(f); stream->flags = 0; stream->mask = 0; stream->stream = (u64)nullptr; logger_state.num_streams--; } u8 logger_find_stream(FILE* target) { for (size_t i = 0; i < BASALT_LOGGER_MAX_STREAMS; ++i) { if (logger_stream_get_file(logger_state.streams + i) == target && (logger_state.streams[i].flags & LOG_STREAM_FLAG_INTERNAL_ALLOCATED_BIT) != 0) return i; } return (u8)(-1); } void basalt_log(const LOG_LEVEL level, const char* msg, ...) { const char log_prefixes[8][6] = { "FATAL", "ERROR", "WARN ", "INFO ", "OK ", "WEIRD", "DEBUG", "TRACE" }; const char* log_ansi_prefixes[8] = { "\033[38;5;0m\033[48;5;9m", "\033[38;5;9m", "\033[38;5;11m", "\033[38;5;15m", "\033[38;5;10m", "\033[38;5;13m", "\033[38;5;12m", "\033[38;5;6m" }; if (level == 0) return; const u8 level = (u8)__builtin_ctz(level); if (level > 8) { debug_break(); return; } if ((level & logger_state.global_mask) == 0) return; u8 valid_streams = 0; for (u8 i = 0; i < BASALT_LOGGER_MAX_STREAMS; ++i) { logger_stream_t* stream = logger_state.streams + i; if ((stream->flags & LOG_STREAM_FLAG_INTERNAL_ALLOCATED_BIT) == 0) continue; valid_streams++; if ((stream->mask & level) == 0) continue; FILE* out = logger_stream_get_file(stream); if (stream->flags & LOG_STREAM_FLAG_SUPPORTS_ANSI_BIT) fprintf(out, log_ansi_prefixes[level]); if ((stream->flags & LOG_STREAM_FLAG_DISABLE_PREFIXES) == 0) { if (stream->flags & LOG_STREAM_FLAG_INCLUDE_DATETIME_BIT) { char time_buffer[32]; const time_t timer = time(NULL); struct tm tm_info; errno_t err = localtime_s(&tm_info, &timer); if (!err) { int n_chars = (int)strftime(time_buffer, sizeof(time_buffer), "%d-%m-%y %R:%S", &tm_info); fprintf(out, "[%*s %*s]: ", sizeof(log_prefixes[0]), log_prefixes[level], n_chars, time_buffer); } else fprintf(out, "[%*s]: ", sizeof(log_prefixes[0]), log_prefixes[level]); } else fprintf(out, "[%*s]: ", sizeof(log_prefixes[0]), log_prefixes[level]); } __builtin_va_list argstart; va_start(argstart, msg); vfprintf(out, msg, argstart); va_end(argstart); if ((stream->flags & LOG_STREAM_FLAG_SUPPORTS_ANSI_BIT) != 0) fprintf(out, "\033[m"); } } void basalt_write(const LOG_LEVEL level, const char* msg, ...) { const char log_prefixes[8][6] = { "FATAL", "ERROR", "WARN ", "INFO ", "OK ", "WEIRD", "DEBUG", "TRACE" }; const char* log_ansi_prefixes[8] = { "\033[38;5;0m\033[48;5;9m", "\033[38;5;9m", "\033[38;5;11m", "\033[38;5;15m", "\033[38;5;10m", "\033[38;5;13m", "\033[38;5;12m", "\033[38;5;6m" }; if (level == 0) return; const u8 level = (u8)__builtin_ctz(level); if (level > 8) { debug_break(); return; } if ((level & logger_state.global_mask) == 0) return; u8 valid_streams = 0; for (u8 i = 0; i < BASALT_LOGGER_MAX_STREAMS; ++i) { logger_stream_t* stream = logger_state.streams + i; if ((stream->flags & LOG_STREAM_FLAG_INTERNAL_ALLOCATED_BIT) == 0) continue; valid_streams++; if ((stream->mask & level) == 0) continue; FILE* out = logger_stream_get_file(stream); if (stream->flags & LOG_STREAM_FLAG_SUPPORTS_ANSI_BIT) fprintf(out, log_ansi_prefixes[level]); __builtin_va_list argstart; va_start(argstart, msg); vfprintf(out, msg, argstart); va_end(argstart); if ((stream->flags & LOG_STREAM_FLAG_SUPPORTS_ANSI_BIT) != 0) fprintf(out, "\033[m"); } }