#include "Logger.h" #include #include // Platform-dependent console initialization static void InitPlatformConsole(); namespace Feather::Logging { Logger GlobalLogger; ChannelID LOG_GENERAL = REGISTER_LOGGING_CHANNEL("General"); // Since min and max are inclusive we have to add 1 here // This assumes max > min (they better be) static constexpr int NUM_LEVELS = ((int)Level::MAX_LEVEL - (int)Level::MIN_LEVEL) + 1; // Temporary solution while we don't have named enums static const char *s_levelNames[NUM_LEVELS] = {"ERROR", "WARNING", "INFO", "DEBUG", "TRACE"}; // Shifts an enum value to be a positive index in s_levelNames static constexpr int LEVEL_NAME_OFFSET = 0 - (int)Level::MIN_LEVEL; Logger::Logger() { InitPlatformConsole(); } void Logger::LogDirect(ChannelID channel, Level level, const char *message, ...) { char buffer[MAX_LOG_MESSAGE_LENGTH]; int offset = 0; // All color escape sequences follow the following ANSI format: // ESC (\x1B) CSI ([) SGR (m) switch (level) { case Level::WARNING: // 1 BRIGHT, 33 YELLOW offset += snprintf(buffer + offset, MAX_LOG_MESSAGE_LENGTH - offset, "\x1B[1;33m"); break; case Level::ERROR: // 1 BRIGHT, 31 RED offset += snprintf(buffer + offset, MAX_LOG_MESSAGE_LENGTH - offset, "\x1B[1;31m"); break; default: case Level::INFO: break; } const char* levelName = s_levelNames[(int)level + LEVEL_NAME_OFFSET]; if (channel == LOG_GENERAL) { // Print only severity level offset += snprintf(buffer + offset, MAX_LOG_MESSAGE_LENGTH - offset, "[%s] ", levelName); } else { // Print channel name and severity level const char *channelName = m_channels[channel]->GetName(); offset += snprintf(buffer + offset, MAX_LOG_MESSAGE_LENGTH - offset, "[%s] [%s] ", channelName, levelName); } // Write our message va_list args; va_start(args, message); offset += vsnprintf(buffer + offset, MAX_LOG_MESSAGE_LENGTH - offset, message, args); va_end(args); // Ignore any terminal newline if (buffer[offset - 1] == '\n') offset--; // Append ANSI style reset code 0 and newline offset += snprintf(buffer + offset, MAX_LOG_MESSAGE_LENGTH - offset, "\x1b[0m\n"); if (level >= Level::INFO) { // Info and above go to stdout printf(buffer); } else { // Error and warn go to stderr fprintf(stderr, buffer); } } ChannelID Logger::RegisterChannel(const char* name) { Channel* channel = new Channel(name); if (m_channelCount >= MAX_LOGGING_CHANNEL_COUNT) { // complain } m_channels[m_channelCount] = channel; int id = m_channelCount++; return id; } } #ifdef _WIN32 #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 #endif static void InitPlatformConsole() { static bool s_initialized = false; if (s_initialized) return; #ifdef _WIN32 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); if (handle != INVALID_HANDLE_VALUE) { DWORD mode = 0; if (GetConsoleMode(handle, &mode)) { mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; SetConsoleMode(handle, mode); } } #endif s_initialized = true; }