2020-07-29 02:43:59 +01:00
|
|
|
#pragma once
|
|
|
|
|
2020-08-07 06:23:12 +01:00
|
|
|
#include <fmt/core.h>
|
|
|
|
#include <fmt/color.h>
|
|
|
|
#include <ostream>
|
|
|
|
#include <vector>
|
|
|
|
#include <array>
|
2020-07-29 05:32:35 +01:00
|
|
|
|
2020-08-08 04:01:58 +01:00
|
|
|
//#define FEATHER_LOG_COLUMNS
|
2020-07-29 05:32:35 +01:00
|
|
|
|
2020-08-23 02:44:28 +01:00
|
|
|
#ifdef _DEBUG
|
|
|
|
|
|
|
|
// Prints an error if 'expr' is not false. Message is optional.
|
|
|
|
#define Assert(expr,/* fmt, */...) \
|
|
|
|
do { \
|
|
|
|
if (!(expr)) { \
|
|
|
|
Feather::Log::Logger::Instance().LogAssert(Feather::Log::Channels::General, __FILE__, __LINE__, #expr, ##__VA_ARGS__); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
// Asserts an expression once only.
|
|
|
|
#define AssertOnce(expr,/* fmt, */...) \
|
|
|
|
do { \
|
|
|
|
static bool asserted = false; \
|
|
|
|
if (!(expr) && !asserted) { \
|
|
|
|
asserted = true; \
|
|
|
|
Feather::Log::Logger::Instance().LogAssert(Feather::Log::Channels::General, __FILE__, __LINE__, #expr, ##__VA_ARGS__); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
#else // !defined(_DEBUG)
|
|
|
|
|
|
|
|
#define Assert(expr,...) ((void)0)
|
|
|
|
#define AssertOnce(expr,...) ((void)0)
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2020-08-07 06:23:12 +01:00
|
|
|
namespace Feather::Log
|
|
|
|
{
|
2020-07-29 02:43:59 +01:00
|
|
|
enum class Level
|
|
|
|
{
|
2020-08-23 02:44:28 +01:00
|
|
|
// Failed assertions
|
|
|
|
Assert = -3,
|
|
|
|
|
2020-07-29 02:43:59 +01:00
|
|
|
// Serious problems
|
2020-08-01 04:53:21 +01:00
|
|
|
Error = -2,
|
2020-07-29 02:43:59 +01:00
|
|
|
|
|
|
|
// Potential problems of note
|
2020-08-01 04:53:21 +01:00
|
|
|
Warning = -1,
|
2020-07-29 02:43:59 +01:00
|
|
|
|
|
|
|
// General messages for end-users
|
2020-08-01 04:53:21 +01:00
|
|
|
Info = 0,
|
2020-07-29 02:43:59 +01:00
|
|
|
|
|
|
|
// More advanced information for problem-solving
|
2020-08-01 04:53:21 +01:00
|
|
|
Debug = 1,
|
2020-07-29 02:43:59 +01:00
|
|
|
|
|
|
|
// Fine grained spew
|
2020-08-01 04:53:21 +01:00
|
|
|
Trace = 2,
|
2020-07-29 02:43:59 +01:00
|
|
|
};
|
|
|
|
|
2020-08-07 06:23:12 +01:00
|
|
|
constexpr std::string_view GetLevelStringView(Level level)
|
|
|
|
{
|
|
|
|
switch (level)
|
|
|
|
{
|
2020-08-23 02:44:28 +01:00
|
|
|
case Level::Assert: return "Assert";
|
2020-08-07 06:23:12 +01:00
|
|
|
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;
|
|
|
|
}
|
2020-07-29 05:32:35 +01:00
|
|
|
|
2020-08-07 06:23:12 +01:00
|
|
|
constexpr fmt::text_style GetLevelTextStyle(Level level)
|
|
|
|
{
|
|
|
|
switch (level)
|
|
|
|
{
|
|
|
|
default:
|
2020-08-23 02:44:28 +01:00
|
|
|
case Level::Assert: return fmt::fg(fmt::color::orange_red);
|
2020-08-07 06:23:12 +01:00
|
|
|
case Level::Error: return fmt::fg(fmt::color::crimson);
|
|
|
|
case Level::Warning: return fmt::fg(fmt::color::yellow);
|
2020-08-23 02:44:28 +01:00
|
|
|
case Level::Info: return fmt::fg(fmt::color::white);
|
2020-08-07 06:23:12 +01:00
|
|
|
case Level::Debug: return fmt::fg(fmt::color::rebecca_purple);
|
|
|
|
case Level::Trace: return fmt::fg(fmt::color::aquamarine);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
using ChannelID = size_t;
|
2020-07-29 05:32:35 +01:00
|
|
|
|
2020-07-29 02:43:59 +01:00
|
|
|
class Channel
|
2020-07-29 05:32:35 +01:00
|
|
|
{
|
|
|
|
public:
|
2020-08-07 06:23:12 +01:00
|
|
|
Channel(std::string_view name) : m_name(name) {}
|
2020-07-29 02:43:59 +01:00
|
|
|
|
2020-08-07 06:23:12 +01:00
|
|
|
inline std::string_view GetName() { return m_name; }
|
2020-07-29 02:43:59 +01:00
|
|
|
|
2020-08-07 06:23:12 +01:00
|
|
|
private:
|
|
|
|
std::string_view m_name;
|
|
|
|
};
|
2020-07-29 05:32:35 +01:00
|
|
|
|
2020-08-07 06:23:12 +01:00
|
|
|
namespace Channels
|
|
|
|
{
|
|
|
|
extern ChannelID General;
|
|
|
|
}
|
2020-07-29 02:43:59 +01:00
|
|
|
|
|
|
|
class Logger
|
|
|
|
{
|
|
|
|
public:
|
2020-08-07 06:23:12 +01:00
|
|
|
template <class S, typename... Args>
|
|
|
|
void LogRaw(const fmt::text_style& style, const S& fmt, Args... args)
|
|
|
|
{
|
|
|
|
fmt::print(style, fmt, args...);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class S, typename... Args>
|
|
|
|
void LogRaw(const S& fmt, Args... args)
|
|
|
|
{
|
|
|
|
constexpr fmt::text_style white = fmt::fg(fmt::color::white);
|
|
|
|
LogRaw(white, fmt, args...);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class S, typename... Args>
|
|
|
|
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();
|
2020-08-08 04:01:58 +01:00
|
|
|
#ifdef FEATHER_LOG_COLUMNS
|
2020-08-07 06:23:12 +01:00
|
|
|
std::array<char, MaxLevelLength + 1> levelSpaces = {};
|
|
|
|
std::fill_n(levelSpaces.data(), MaxLevelLength - levelString.size(), ' ');
|
|
|
|
|
|
|
|
std::array<char, MaxChannelLength> 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
|
2020-08-23 02:52:06 +01:00
|
|
|
if (level < Level::Info)
|
|
|
|
{
|
|
|
|
fmt::text_style style = GetLevelTextStyle(level);
|
|
|
|
if (channel != Channels::General)
|
|
|
|
LogRaw(style, "[{}] ", channelString);
|
|
|
|
|
|
|
|
LogRaw(style, "[");
|
|
|
|
LogRaw(style, levelString);
|
|
|
|
LogRaw(style, "] ");
|
|
|
|
LogRaw(style, fmt, args...);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (channel != Channels::General)
|
|
|
|
LogRaw("[{}] ", channelString);
|
|
|
|
|
|
|
|
LogRaw("[");
|
|
|
|
LogRaw(GetLevelTextStyle(level), levelString);
|
|
|
|
LogRaw("] ");
|
|
|
|
LogRaw(fmt, args...);
|
|
|
|
}
|
2020-08-07 06:23:12 +01:00
|
|
|
LogRaw("\n");
|
|
|
|
#endif
|
2020-08-23 02:44:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Prints message for a failed assertion. Use the Assert or AssertOnce macros instead of calling this directly.
|
|
|
|
template <class S, typename... Args>
|
2020-08-23 02:48:59 +01:00
|
|
|
inline void LogAssert(ChannelID channel, const char* file, uint64_t lineNum, const char* expr, const S& fmt, Args... args)
|
2020-08-23 02:44:28 +01:00
|
|
|
{
|
|
|
|
std::string msg = fmt::format("{} ({}): {}", file, lineNum, fmt);
|
|
|
|
Log(channel, Level::Assert, msg, args...);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Prints message for a failed assertion. Use the Assert or AssertOnce macros instead of calling this directly.
|
2020-08-23 02:48:59 +01:00
|
|
|
inline void LogAssert(ChannelID channel, const char* file, uint64_t lineNum, const char* expr)
|
2020-08-23 02:44:28 +01:00
|
|
|
{
|
|
|
|
// When no message is provided we print the asserted expression instead
|
|
|
|
Log(channel, Level::Assert, "{} ({}): Assertion failed: {}", file, lineNum, expr);
|
2020-08-07 06:23:12 +01:00
|
|
|
}
|
2020-07-29 05:32:35 +01:00
|
|
|
|
|
|
|
ChannelID RegisterChannel(const char* name);
|
|
|
|
|
2020-08-07 06:23:12 +01:00
|
|
|
static Logger& Instance();
|
|
|
|
|
2020-07-29 05:32:35 +01:00
|
|
|
private:
|
2020-08-07 06:23:12 +01:00
|
|
|
std::vector<Channel> m_channels;
|
2020-07-29 02:43:59 +01:00
|
|
|
};
|
|
|
|
|
2020-08-07 06:55:17 +01:00
|
|
|
// Logs a message, specifying a channel and log level
|
2020-08-07 06:23:12 +01:00
|
|
|
template <class S, typename... Args>
|
|
|
|
void Msg(ChannelID channel, Level level, const S& fmt, Args... args)
|
|
|
|
{
|
|
|
|
Logger::Instance().Log(channel, level, fmt, args...);
|
|
|
|
}
|
2020-07-29 02:43:59 +01:00
|
|
|
|
2020-08-07 06:55:17 +01:00
|
|
|
// Logs a general message for end-users
|
2020-08-07 06:23:12 +01:00
|
|
|
template <class S, typename... Args>
|
|
|
|
void Info(const S& fmt, Args... args) { Msg(Channels::General, Level::Info, fmt, args...); }
|
2020-07-29 05:37:22 +01:00
|
|
|
|
2020-08-07 06:55:17 +01:00
|
|
|
// Logs a potential problem of note
|
2020-08-07 06:23:12 +01:00
|
|
|
template <class S, typename... Args>
|
|
|
|
void Warn(const S& fmt, Args... args) { Msg(Channels::General, Level::Warning, fmt, args...); }
|
2020-07-29 05:37:22 +01:00
|
|
|
|
2020-08-07 06:55:17 +01:00
|
|
|
// Logs a serious problem
|
2020-08-07 06:23:12 +01:00
|
|
|
template <class S, typename... Args>
|
|
|
|
void Error(const S& fmt, Args... args) { Msg(Channels::General, Level::Error, fmt, args...); }
|
2020-07-29 05:37:22 +01:00
|
|
|
|
2020-08-07 06:55:17 +01:00
|
|
|
// Logs debug information for developers
|
2020-08-07 06:23:12 +01:00
|
|
|
template <class S, typename... Args>
|
|
|
|
void Debug(const S& fmt, Args... args) { Msg(Channels::General, Level::Debug, fmt, args...); }
|
2020-07-29 05:37:22 +01:00
|
|
|
|
2020-08-07 06:55:17 +01:00
|
|
|
// Logs fine grained debug information
|
2020-08-07 06:23:12 +01:00
|
|
|
template <class S, typename... Args>
|
|
|
|
void Trace(const S& fmt, Args... args) { Msg(Channels::General, Level::Trace, fmt, args...); }
|
|
|
|
}
|