#pragma once #include #define FMT_HEADER_ONLY #include #include #include #include #include //#define FEATHER_Log::COLUMNS namespace Feather::Log { enum class Level { // Serious problems Error = -2, // Potential problems of note Warning = -1, // General messages for end-users Info = 0, // More advanced information for problem-solving Debug = 1, // Fine grained spew Trace = 2, }; constexpr std::string_view GetLevelStringView(Level level) { switch (level) { case Level::Error: return "Error"; case Level::Warning: return "Warning"; case Level::Info: return "Info"; case Level::Debug: return "Debug"; case Level::Trace: return "Trace"; default: return "Unknown"; } } constexpr size_t MaxLevelLength = 7; constexpr size_t MaxChannelLength = 15; inline std::ostream& operator << (std::ostream& os, Level level) { os << GetLevelStringView(level); return os; } constexpr fmt::text_style GetLevelTextStyle(Level level) { switch (level) { default: case Level::Info: return fmt::fg(fmt::color::white); case Level::Error: return fmt::fg(fmt::color::crimson); case Level::Warning: return fmt::fg(fmt::color::yellow); case Level::Debug: return fmt::fg(fmt::color::rebecca_purple); case Level::Trace: return fmt::fg(fmt::color::aquamarine); } } using ChannelID = size_t; class Channel { public: Channel(std::string_view name) : m_name(name) {} inline std::string_view GetName() { return m_name; } private: std::string_view m_name; }; namespace Channels { extern ChannelID General; } class Logger { public: template void LogRaw(const fmt::text_style& style, const S& fmt, Args... args) { fmt::print(style, fmt, args...); } template void LogRaw(const S& fmt, Args... args) { constexpr fmt::text_style white = fmt::fg(fmt::color::white); LogRaw(white, fmt, args...); } template void Log(ChannelID channel, Level level, const S& fmt, Args... args) { std::string_view levelString = GetLevelStringView(level); std::string_view channelString = m_channels[channel].GetName(); #ifdef FEATHER_Log::COLUMNS std::array levelSpaces = {}; std::fill_n(levelSpaces.data(), MaxLevelLength - levelString.size(), ' '); std::array channelSpaces = {}; std::fill_n(channelSpaces.data(), MaxChannelLength - channelString.size(), ' '); LogRaw(GetLevelTextStyle(level), levelString); LogRaw("{}|", levelSpaces.data()); LogRaw(channelString); LogRaw("{}|", channelSpaces.data()); LogRaw(fmt, args...); LogRaw("\n"); #else if (channel != Channels::General) LogRaw("[{}] ", channelString); LogRaw("["); LogRaw(GetLevelTextStyle(level), levelString); LogRaw("] "); LogRaw(fmt, args...); LogRaw("\n"); #endif } ChannelID RegisterChannel(const char* name); static Logger& Instance(); private: std::vector m_channels; }; template void Msg(ChannelID channel, Level level, const S& fmt, Args... args) { Logger::Instance().Log(channel, level, fmt, args...); } template void Info(const S& fmt, Args... args) { Msg(Channels::General, Level::Info, fmt, args...); } template void Warn(const S& fmt, Args... args) { Msg(Channels::General, Level::Warning, fmt, args...); } template void Error(const S& fmt, Args... args) { Msg(Channels::General, Level::Error, fmt, args...); } template void Debug(const S& fmt, Args... args) { Msg(Channels::General, Level::Debug, fmt, args...); } template void Trace(const S& fmt, Args... args) { Msg(Channels::General, Level::Trace, fmt, args...); } }