Log: Add option coloured variants

This commit is contained in:
Stenzek 2024-11-02 14:21:56 +10:00
parent 596cca9be2
commit 3f41dcc97d
No known key found for this signature in database
5 changed files with 266 additions and 134 deletions

View File

@ -44,36 +44,24 @@ static void UnregisterCallback(CallbackFunctionType callbackFunction, void* pUse
const std::unique_lock<std::mutex>& lock); const std::unique_lock<std::mutex>& lock);
static bool FilterTest(Channel channel, Level level); static bool FilterTest(Channel channel, Level level);
static void ExecuteCallbacks(Channel channel, const char* functionName, Level level, std::string_view message, static void ExecuteCallbacks(MessageCategory cat, const char* functionName, std::string_view message);
const std::unique_lock<std::mutex>& lock); static void FormatLogMessageForDisplay(fmt::memory_buffer& buffer, MessageCategory cat, const char* functionName,
static void FormatLogMessageForDisplay(fmt::memory_buffer& buffer, const char* channelName, const char* functionName, std::string_view message, bool timestamp, bool ansi_color_code);
Level level, std::string_view message, bool timestamp, bool ansi_color_code); static void ConsoleOutputLogCallback(void* pUserParam, MessageCategory cat, const char* functionName,
static void ConsoleOutputLogCallback(void* pUserParam, const char* channelName, const char* functionName, Level level,
std::string_view message); std::string_view message);
static void DebugOutputLogCallback(void* pUserParam, const char* channelName, const char* functionName, Level level, static void DebugOutputLogCallback(void* pUserParam, MessageCategory cat, const char* functionName,
std::string_view message); std::string_view message);
static void FileOutputLogCallback(void* pUserParam, const char* channelName, const char* functionName, Level level, static void FileOutputLogCallback(void* pUserParam, MessageCategory cat, const char* functionName,
std::string_view message); std::string_view message);
template<typename T> template<typename T>
static void FormatLogMessageAndPrint(const char* channelName, const char* functionName, Level level, static void FormatLogMessageAndPrint(MessageCategory cat, const char* functionName, std::string_view message,
std::string_view message, bool timestamp, bool ansi_color_code, const T& callback); bool timestamp, bool ansi_color_code, const T& callback);
#ifdef _WIN32 #ifdef _WIN32
template<typename T> template<typename T>
static void FormatLogMessageAndPrintW(const char* channelName, const char* functionName, Level level, static void FormatLogMessageAndPrintW(MessageCategory cat, const char* functionName, std::string_view message,
std::string_view message, bool timestamp, bool ansi_color_code, bool timestamp, bool ansi_color_code, const T& callback);
const T& callback);
#endif #endif
ALWAYS_INLINE static Channel UnpackLogChannel(PackedChannelAndLevel cat)
{
return static_cast<Channel>(cat >> 3);
}
ALWAYS_INLINE static Level UnpackLogLevel(PackedChannelAndLevel cat)
{
return static_cast<Level>(cat & 0x7);
}
static constexpr const std::array<char, static_cast<size_t>(Level::MaxCount)> s_log_level_characters = { static constexpr const std::array<char, static_cast<size_t>(Level::MaxCount)> s_log_level_characters = {
{'X', 'E', 'W', 'I', 'V', 'D', 'B', 'T'}}; {'X', 'E', 'W', 'I', 'V', 'D', 'B', 'T'}};
@ -191,31 +179,59 @@ bool Log::IsDebugOutputEnabled()
return s_state.debug_output_enabled; return s_state.debug_output_enabled;
} }
void Log::ExecuteCallbacks(Channel channel, const char* functionName, Level level, std::string_view message, Log::Color Log::GetColorForLevel(Level level)
const std::unique_lock<std::mutex>& lock)
{ {
for (RegisteredCallback& callback : s_state.callbacks) static constexpr const std::array s_level_colours = {
callback.Function(callback.Parameter, s_log_channel_names[static_cast<size_t>(channel)], functionName, level, Color::Default, // None
message); Color::StrongRed, // Error
} Color::StrongYellow, // Warning
Color::StrongWhite, // Info
ALWAYS_INLINE_RELEASE void Log::FormatLogMessageForDisplay(fmt::memory_buffer& buffer, const char* channelName, Color::StrongGreen, // Verbose
const char* functionName, Level level, Color::White, // Dev
std::string_view message, bool timestamp, Color::Green, // Debug
bool ansi_color_code) Color::Blue, // Trace
{
static constexpr const std::array s_ansi_color_codes = {
"\033[0m"sv, // None
"\033[1;31m"sv, // Error
"\033[1;33m"sv, // Warning
"\033[1;37m"sv, // Info
"\033[1;32m"sv, // Verbose
"\033[0;37m"sv, // Dev
"\033[0;32m"sv, // Debug
"\033[0;34m"sv, // Trace
}; };
std::string_view color_start = ansi_color_code ? s_ansi_color_codes[static_cast<size_t>(level)] : ""sv; return s_level_colours[static_cast<size_t>(level)];
}
void Log::ExecuteCallbacks(MessageCategory cat, const char* functionName, std::string_view message)
{
for (RegisteredCallback& callback : s_state.callbacks)
callback.Function(callback.Parameter, cat, functionName, message);
}
ALWAYS_INLINE_RELEASE void Log::FormatLogMessageForDisplay(fmt::memory_buffer& buffer, MessageCategory cat,
const char* functionName, std::string_view message,
bool timestamp, bool ansi_color_code)
{
static constexpr const std::array s_ansi_color_codes = {
"\033[0m"sv, // default
"\033[30m\033[1m"sv, // black
"\033[32m"sv, // green
"\033[31m"sv, // red
"\033[34m"sv, // blue
"\033[35m"sv, // magenta
"\033[38;5;217m"sv, // orange
"\033[36m"sv, // cyan
"\033[33m"sv, // yellow
"\033[37m"sv, // white
"\033[30m\033[1m"sv, // strong black
"\033[31m\033[1m"sv, // strong red
"\033[32m\033[1m"sv, // strong green
"\033[34m\033[1m"sv, // strong blue
"\033[35m\033[1m"sv, // strong magenta
"\033[38;5;202m"sv, // strong orange
"\033[36m\033[1m"sv, // strong cyan
"\033[33m\033[1m"sv, // strong yellow
"\033[37m\033[1m"sv, // strong white
};
const Level level = UnpackLevel(cat);
const Color color = (UnpackColor(cat) == Color::Default) ? GetColorForLevel(level) : UnpackColor(cat);
const char* channel_name = GetChannelName(UnpackChannel(cat));
std::string_view color_start = ansi_color_code ? s_ansi_color_codes[static_cast<size_t>(color)] : ""sv;
std::string_view color_end = ansi_color_code ? s_ansi_color_codes[0] : ""sv; std::string_view color_end = ansi_color_code ? s_ansi_color_codes[0] : ""sv;
auto appender = std::back_inserter(buffer); auto appender = std::back_inserter(buffer);
@ -243,7 +259,7 @@ ALWAYS_INLINE_RELEASE void Log::FormatLogMessageForDisplay(fmt::memory_buffer& b
else else
{ {
fmt::format_to(appender, "[{:10.4f}] {}{}/{}: {}{}{}", message_time, color_start, fmt::format_to(appender, "[{:10.4f}] {}{}/{}: {}{}{}", message_time, color_start,
s_log_level_characters[static_cast<size_t>(level)], channelName, sub_message, color_end, s_log_level_characters[static_cast<size_t>(level)], channel_name, sub_message, color_end,
end_message); end_message);
} }
@ -263,30 +279,30 @@ ALWAYS_INLINE_RELEASE void Log::FormatLogMessageForDisplay(fmt::memory_buffer& b
else else
{ {
fmt::format_to(appender, "{}{}/{}: {}{}\n", color_start, s_log_level_characters[static_cast<size_t>(level)], fmt::format_to(appender, "{}{}/{}: {}{}\n", color_start, s_log_level_characters[static_cast<size_t>(level)],
channelName, message, color_end); channel_name, message, color_end);
} }
} }
} }
template<typename T> template<typename T>
ALWAYS_INLINE_RELEASE void Log::FormatLogMessageAndPrint(const char* channelName, const char* functionName, Level level, ALWAYS_INLINE_RELEASE void Log::FormatLogMessageAndPrint(MessageCategory cat, const char* functionName,
std::string_view message, bool timestamp, bool ansi_color_code, std::string_view message, bool timestamp, bool ansi_color_code,
const T& callback) const T& callback)
{ {
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
FormatLogMessageForDisplay(buffer, channelName, functionName, level, message, timestamp, ansi_color_code); FormatLogMessageForDisplay(buffer, cat, functionName, message, timestamp, ansi_color_code);
callback(std::string_view(buffer.data(), buffer.size())); callback(std::string_view(buffer.data(), buffer.size()));
} }
#ifdef _WIN32 #ifdef _WIN32
template<typename T> template<typename T>
ALWAYS_INLINE_RELEASE void Log::FormatLogMessageAndPrintW(const char* channelName, const char* functionName, ALWAYS_INLINE_RELEASE void Log::FormatLogMessageAndPrintW(MessageCategory cat, const char* functionName,
Level level, std::string_view message, bool timestamp, std::string_view message, bool timestamp,
bool ansi_color_code, const T& callback) bool ansi_color_code, const T& callback)
{ {
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
FormatLogMessageForDisplay(buffer, channelName, functionName, level, message, timestamp, ansi_color_code); FormatLogMessageForDisplay(buffer, cat, functionName, message, timestamp, ansi_color_code);
// Convert to UTF-16 first so unicode characters display correctly. NT is going to do it // Convert to UTF-16 first so unicode characters display correctly. NT is going to do it
// anyway... // anyway...
@ -326,7 +342,7 @@ static bool EnableVirtualTerminalProcessing(HANDLE hConsole)
#endif #endif
void Log::ConsoleOutputLogCallback(void* pUserParam, const char* channelName, const char* functionName, Level level, void Log::ConsoleOutputLogCallback(void* pUserParam, MessageCategory cat, const char* functionName,
std::string_view message) std::string_view message)
{ {
if (!s_state.console_output_enabled) if (!s_state.console_output_enabled)
@ -334,29 +350,28 @@ void Log::ConsoleOutputLogCallback(void* pUserParam, const char* channelName, co
#if defined(_WIN32) #if defined(_WIN32)
FormatLogMessageAndPrintW( FormatLogMessageAndPrintW(
channelName, functionName, level, message, s_state.console_output_timestamps, true, cat, functionName, message, s_state.console_output_timestamps, true, [cat](const std::wstring_view& message) {
[level](const std::wstring_view& message) { HANDLE hOutput = (UnpackLevel(cat) <= Level::Warning) ? s_state.hConsoleStdErr : s_state.hConsoleStdOut;
HANDLE hOutput = (level <= Level::Warning) ? s_state.hConsoleStdErr : s_state.hConsoleStdOut;
DWORD chars_written; DWORD chars_written;
WriteConsoleW(hOutput, message.data(), static_cast<DWORD>(message.length()), &chars_written, nullptr); WriteConsoleW(hOutput, message.data(), static_cast<DWORD>(message.length()), &chars_written, nullptr);
}); });
#elif !defined(__ANDROID__) #elif !defined(__ANDROID__)
FormatLogMessageAndPrint(channelName, functionName, level, message, s_state.console_output_timestamps, true, FormatLogMessageAndPrint(
[level](std::string_view message) { cat, functionName, message, s_state.console_output_timestamps, true, [cat](std::string_view message) {
const int outputFd = (level <= Log::Level::Warning) ? STDERR_FILENO : STDOUT_FILENO; const int outputFd = (UnpackLevel(cat) <= Log::Level::Warning) ? STDERR_FILENO : STDOUT_FILENO;
write(outputFd, message.data(), message.length()); write(outputFd, message.data(), message.length());
}); });
#endif #endif
} }
void Log::DebugOutputLogCallback(void* pUserParam, const char* channelName, const char* functionName, Level level, void Log::DebugOutputLogCallback(void* pUserParam, MessageCategory cat, const char* functionName,
std::string_view message) std::string_view message)
{ {
if (!s_state.debug_output_enabled) if (!s_state.debug_output_enabled)
return; return;
#if defined(_WIN32) #if defined(_WIN32)
FormatLogMessageAndPrintW(channelName, functionName, level, message, false, false, FormatLogMessageAndPrintW(cat, functionName, message, false, false,
[](const std::wstring_view& message) { OutputDebugStringW(message.data()); }); [](const std::wstring_view& message) { OutputDebugStringW(message.data()); });
#elif defined(__ANDROID__) #elif defined(__ANDROID__)
if (message.empty()) if (message.empty())
@ -373,8 +388,8 @@ void Log::DebugOutputLogCallback(void* pUserParam, const char* channelName, cons
ANDROID_LOG_DEBUG, // Trace ANDROID_LOG_DEBUG, // Trace
}; };
__android_log_print(logPriority[static_cast<size_t>(level)], channelName, "%.*s", static_cast<int>(message.length()), __android_log_print(logPriority[static_cast<size_t>(UnpackLevel(cat))], GetChannelName(UnpackChannel(cat)), "%.*s",
message.data()); static_cast<int>(message.length()), message.data());
#endif #endif
} }
@ -471,13 +486,13 @@ void Log::SetDebugOutputParams(bool enabled)
UnregisterCallback(DebugOutputLogCallback, nullptr, lock); UnregisterCallback(DebugOutputLogCallback, nullptr, lock);
} }
void Log::FileOutputLogCallback(void* pUserParam, const char* channelName, const char* functionName, Level level, void Log::FileOutputLogCallback(void* pUserParam, MessageCategory cat, const char* functionName,
std::string_view message) std::string_view message)
{ {
if (!s_state.file_output_enabled) if (!s_state.file_output_enabled)
return; return;
FormatLogMessageAndPrint(channelName, functionName, level, message, true, false, [](std::string_view message) { FormatLogMessageAndPrint(cat, functionName, message, true, false, [](std::string_view message) {
std::fwrite(message.data(), 1, message.size(), s_state.file_handle.get()); std::fwrite(message.data(), 1, message.size(), s_state.file_handle.get());
std::fflush(s_state.file_handle.get()); std::fflush(s_state.file_handle.get());
}); });
@ -494,8 +509,8 @@ void Log::SetFileOutputParams(bool enabled, const char* filename, bool timestamp
s_state.file_handle = FileSystem::OpenManagedCFile(filename, "wb"); s_state.file_handle = FileSystem::OpenManagedCFile(filename, "wb");
if (!s_state.file_handle) [[unlikely]] if (!s_state.file_handle) [[unlikely]]
{ {
ExecuteCallbacks(Log::Channel::Log, __FUNCTION__, Level::Error, ExecuteCallbacks(PackCategory(Channel::Log, Level::Error, Color::Default), nullptr,
TinyString::from_format("Failed to open log file '{}'", filename), lock); TinyString::from_format("Failed to open log file '{}'", filename));
return; return;
} }
@ -535,57 +550,54 @@ void Log::SetLogChannelEnabled(Channel channel, bool enabled)
s_state.log_channels_enabled[static_cast<size_t>(channel)] = enabled; s_state.log_channels_enabled[static_cast<size_t>(channel)] = enabled;
} }
const char* Log::GetChannelName(Channel channel)
{
return s_log_channel_names[static_cast<size_t>(channel)];
}
ALWAYS_INLINE_RELEASE bool Log::FilterTest(Channel channel, Level level) ALWAYS_INLINE_RELEASE bool Log::FilterTest(Channel channel, Level level)
{ {
return (level <= s_state.log_level && s_state.log_channels_enabled[static_cast<size_t>(channel)]); return (level <= s_state.log_level && s_state.log_channels_enabled[static_cast<size_t>(channel)]);
} }
void Log::Write(PackedChannelAndLevel cat, std::string_view message) void Log::Write(MessageCategory cat, std::string_view message)
{ {
const Channel channel = UnpackLogChannel(cat); if (!FilterTest(UnpackChannel(cat), UnpackLevel(cat)))
const Level level = UnpackLogLevel(cat);
if (!FilterTest(channel, level))
return; return;
std::unique_lock lock(s_state.callbacks_mutex); std::unique_lock lock(s_state.callbacks_mutex);
ExecuteCallbacks(channel, nullptr, level, message, lock); ExecuteCallbacks(cat, nullptr, message);
} }
void Log::Write(PackedChannelAndLevel cat, const char* functionName, std::string_view message) void Log::Write(MessageCategory cat, const char* functionName, std::string_view message)
{ {
const Channel channel = UnpackLogChannel(cat); if (!FilterTest(UnpackChannel(cat), UnpackLevel(cat)))
const Level level = UnpackLogLevel(cat);
if (!FilterTest(channel, level))
return; return;
std::unique_lock lock(s_state.callbacks_mutex); std::unique_lock lock(s_state.callbacks_mutex);
ExecuteCallbacks(channel, functionName, level, message, lock); ExecuteCallbacks(cat, functionName, message);
} }
void Log::WriteFmtArgs(PackedChannelAndLevel cat, fmt::string_view fmt, fmt::format_args args) void Log::WriteFmtArgs(MessageCategory cat, fmt::string_view fmt, fmt::format_args args)
{ {
const Channel channel = UnpackLogChannel(cat); if (!FilterTest(UnpackChannel(cat), UnpackLevel(cat)))
const Level level = UnpackLogLevel(cat);
if (!FilterTest(channel, level))
return; return;
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
fmt::vformat_to(std::back_inserter(buffer), fmt, args); fmt::vformat_to(std::back_inserter(buffer), fmt, args);
std::unique_lock lock(s_state.callbacks_mutex); std::unique_lock lock(s_state.callbacks_mutex);
ExecuteCallbacks(channel, nullptr, level, std::string_view(buffer.data(), buffer.size()), lock); ExecuteCallbacks(cat, nullptr, std::string_view(buffer.data(), buffer.size()));
} }
void Log::WriteFmtArgs(PackedChannelAndLevel cat, const char* functionName, fmt::string_view fmt, fmt::format_args args) void Log::WriteFmtArgs(MessageCategory cat, const char* functionName, fmt::string_view fmt, fmt::format_args args)
{ {
const Channel channel = UnpackLogChannel(cat); if (!FilterTest(UnpackChannel(cat), UnpackLevel(cat)))
const Level level = UnpackLogLevel(cat);
if (!FilterTest(channel, level))
return; return;
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
fmt::vformat_to(std::back_inserter(buffer), fmt, args); fmt::vformat_to(std::back_inserter(buffer), fmt, args);
std::unique_lock lock(s_state.callbacks_mutex); std::unique_lock lock(s_state.callbacks_mutex);
ExecuteCallbacks(channel, functionName, level, std::string_view(buffer.data(), buffer.size()), lock); ExecuteCallbacks(cat, functionName, std::string_view(buffer.data(), buffer.size()));
} }

View File

@ -29,6 +29,31 @@ enum class Level : u32
MaxCount MaxCount
}; };
enum class Color : u32
{
Default,
Black,
Green,
Red,
Blue,
Magenta,
Orange,
Cyan,
Yellow,
White,
StrongBlack,
StrongRed,
StrongGreen,
StrongBlue,
StrongMagenta,
StrongOrange,
StrongCyan,
StrongYellow,
StrongWhite,
MaxCount
};
enum class Channel : u32 enum class Channel : u32
{ {
#define LOG_CHANNEL_ENUM(X) X, #define LOG_CHANNEL_ENUM(X) X,
@ -38,8 +63,28 @@ enum class Channel : u32
MaxCount MaxCount
}; };
// Packs a level and channel into one 16-bit number.
using MessageCategory = u32;
[[maybe_unused]] ALWAYS_INLINE static constexpr u32 PackCategory(Channel channel, Level level, Color colour)
{
return ((static_cast<MessageCategory>(colour) << 10) | (static_cast<MessageCategory>(channel) << 3) |
static_cast<MessageCategory>(level));
}
[[maybe_unused]] ALWAYS_INLINE static constexpr Color UnpackColor(MessageCategory cat)
{
return static_cast<Color>((cat >> 10) & 0x1f);
}
[[maybe_unused]] ALWAYS_INLINE static constexpr Channel UnpackChannel(MessageCategory cat)
{
return static_cast<Channel>((cat >> 3) & 0x7f);
}
[[maybe_unused]] ALWAYS_INLINE static constexpr Level UnpackLevel(MessageCategory cat)
{
return static_cast<Level>(cat & 0x7);
}
// log message callback type // log message callback type
using CallbackFunctionType = void (*)(void* pUserParam, const char* channelName, const char* functionName, Level level, using CallbackFunctionType = void (*)(void* pUserParam, MessageCategory category, const char* functionName,
std::string_view message); std::string_view message);
// registers a log callback // registers a log callback
@ -79,41 +124,65 @@ void SetLogLevel(Level level);
// Sets global filter, any messages from these channels won't be sent to any of the logging sinks. // Sets global filter, any messages from these channels won't be sent to any of the logging sinks.
void SetLogChannelEnabled(Channel channel, bool enabled); void SetLogChannelEnabled(Channel channel, bool enabled);
// Packs a level and channel into one 16-bit number. // Returns the name of the specified log channel.
using PackedChannelAndLevel = u32; const char* GetChannelName(Channel channel);
[[maybe_unused]] ALWAYS_INLINE static u32 PackChannelAndLevel(Channel channel, Level level)
{ // Returns the default colour for a log level.
return ((static_cast<PackedChannelAndLevel>(channel) << 3) | static_cast<PackedChannelAndLevel>(level)); Color GetColorForLevel(Level level);
}
// writes a message to the log // writes a message to the log
void Write(PackedChannelAndLevel cat, std::string_view message); void Write(MessageCategory cat, std::string_view message);
void Write(PackedChannelAndLevel cat, const char* functionName, std::string_view message); void Write(MessageCategory cat, const char* functionName, std::string_view message);
void WriteFmtArgs(PackedChannelAndLevel cat, fmt::string_view fmt, fmt::format_args args); void WriteFmtArgs(MessageCategory cat, fmt::string_view fmt, fmt::format_args args);
void WriteFmtArgs(PackedChannelAndLevel cat, const char* functionName, fmt::string_view fmt, fmt::format_args args); void WriteFmtArgs(MessageCategory cat, const char* functionName, fmt::string_view fmt, fmt::format_args args);
ALWAYS_INLINE static void FastWrite(Channel channel, Level level, std::string_view message) ALWAYS_INLINE static void FastWrite(Channel channel, Level level, std::string_view message)
{ {
if (level <= GetLogLevel()) [[unlikely]] if (level <= GetLogLevel()) [[unlikely]]
Write(PackChannelAndLevel(channel, level), message); Write(PackCategory(channel, level, Color::Default), message);
} }
ALWAYS_INLINE static void FastWrite(Channel channel, const char* functionName, Level level, std::string_view message) ALWAYS_INLINE static void FastWrite(Channel channel, const char* functionName, Level level, std::string_view message)
{ {
if (level <= GetLogLevel()) [[unlikely]] if (level <= GetLogLevel()) [[unlikely]]
Write(PackChannelAndLevel(channel, level), functionName, message); Write(PackCategory(channel, level, Color::Default), functionName, message);
} }
template<typename... T> template<typename... T>
ALWAYS_INLINE static void FastWrite(Channel channel, Level level, fmt::format_string<T...> fmt, T&&... args) ALWAYS_INLINE static void FastWrite(Channel channel, Level level, fmt::format_string<T...> fmt, T&&... args)
{ {
if (level <= GetLogLevel()) [[unlikely]] if (level <= GetLogLevel()) [[unlikely]]
WriteFmtArgs(PackChannelAndLevel(channel, level), fmt, fmt::make_format_args(args...)); WriteFmtArgs(PackCategory(channel, level, Color::Default), fmt, fmt::make_format_args(args...));
} }
template<typename... T> template<typename... T>
ALWAYS_INLINE static void FastWrite(Channel channel, const char* functionName, Level level, ALWAYS_INLINE static void FastWrite(Channel channel, const char* functionName, Level level,
fmt::format_string<T...> fmt, T&&... args) fmt::format_string<T...> fmt, T&&... args)
{ {
if (level <= GetLogLevel()) [[unlikely]] if (level <= GetLogLevel()) [[unlikely]]
WriteFmtArgs(PackChannelAndLevel(channel, level), functionName, fmt, fmt::make_format_args(args...)); WriteFmtArgs(PackCategory(channel, level, Color::Default), functionName, fmt, fmt::make_format_args(args...));
}
ALWAYS_INLINE static void FastWrite(Channel channel, Level level, Color colour, std::string_view message)
{
if (level <= GetLogLevel()) [[unlikely]]
Write(PackCategory(channel, level, colour), message);
}
ALWAYS_INLINE static void FastWrite(Channel channel, const char* functionName, Level level, Color colour,
std::string_view message)
{
if (level <= GetLogLevel()) [[unlikely]]
Write(PackCategory(channel, level, colour), functionName, message);
}
template<typename... T>
ALWAYS_INLINE static void FastWrite(Channel channel, Level level, Color colour, fmt::format_string<T...> fmt,
T&&... args)
{
if (level <= GetLogLevel()) [[unlikely]]
WriteFmtArgs(PackCategory(channel, level, colour), fmt, fmt::make_format_args(args...));
}
template<typename... T>
ALWAYS_INLINE static void FastWrite(Channel channel, const char* functionName, Level level, Color colour,
fmt::format_string<T...> fmt, T&&... args)
{
if (level <= GetLogLevel()) [[unlikely]]
WriteFmtArgs(PackCategory(channel, level, colour), functionName, fmt, fmt::make_format_args(args...));
} }
} // namespace Log } // namespace Log
@ -139,3 +208,26 @@ ALWAYS_INLINE static void FastWrite(Channel channel, const char* functionName, L
{ \ { \
} while (0) } while (0)
#endif #endif
// clang-format off
#define ERROR_COLOR_LOG(colour, ...) Log::FastWrite(___LogChannel___, __func__, Log::Level::Error, Log::Color::colour, __VA_ARGS__)
#define WARNING_COLOR_LOG(colour, ...) Log::FastWrite(___LogChannel___, __func__, Log::Level::Warning, Log::Color::colour, __VA_ARGS__)
#define INFO_COLOR_LOG(colour, ...) Log::FastWrite(___LogChannel___, Log::Level::Info, Log::Color::colour, __VA_ARGS__)
#define VERBOSE_COLOR_LOG(colour, ...) Log::FastWrite(___LogChannel___, Log::Level::Verbose, Log::Color::colour, __VA_ARGS__)
#define DEV_COLOR_LOG(colour, ...) Log::FastWrite(___LogChannel___, Log::Level::Dev, Log::Color::colour, __VA_ARGS__)
#ifdef _DEBUG
#define DEBUG_COLOR_LOG(colour, ...) Log::FastWrite(___LogChannel___, Log::Level::Debug, Log::Color::colour, __VA_ARGS__)
#define TRACE_COLOR_LOG(colour, ...) Log::FastWrite(___LogChannel___, Log::Level::Trace, Log::Color::colour,__VA_ARGS__)
#else
#define DEBUG_COLOR_LOG(colour, ...) \
do \
{ \
} while (0)
#define TRACE_COLOR_LOG(colour, ...) \
do \
{ \
} while (0)
#endif
// clang-format on

View File

@ -77,7 +77,7 @@ public:
{ {
if (!stop_on_error) if (!stop_on_error)
{ {
Log::WriteFmtArgs(Log::PackChannelAndLevel(Log::Channel::Cheats, Log::Level::Warning), fmt, Log::WriteFmtArgs(Log::PackCategory(Log::Channel::Cheats, Log::Level::Warning, Log::Color::StrongOrange), fmt,
fmt::make_format_args(args...)); fmt::make_format_args(args...));
return true; return true;
} }

View File

@ -254,7 +254,7 @@ void LogWindow::onSaveTriggered()
tr("Log was written to %1.\n").arg(path)); tr("Log was written to %1.\n").arg(path));
} }
void LogWindow::logCallback(void* pUserParam, const char* channelName, const char* functionName, Log::Level level, void LogWindow::logCallback(void* pUserParam, Log::MessageCategory cat, const char* functionName,
std::string_view message) std::string_view message)
{ {
LogWindow* this_ptr = static_cast<LogWindow*>(pUserParam); LogWindow* this_ptr = static_cast<LogWindow*>(pUserParam);
@ -267,16 +267,17 @@ void LogWindow::logCallback(void* pUserParam, const char* channelName, const cha
qmessage.append(QUtf8StringView(message.data(), message.length())); qmessage.append(QUtf8StringView(message.data(), message.length()));
qmessage.append(QChar('\n')); qmessage.append(QChar('\n'));
const QLatin1StringView qchannel((level <= Log::Level::Warning) ? functionName : channelName); const QLatin1StringView qchannel(
(Log::UnpackLevel(cat) <= Log::Level::Warning) ? functionName : Log::GetChannelName(Log::UnpackChannel(cat)));
if (QThread::isMainThread()) if (QThread::isMainThread())
{ {
this_ptr->appendMessage(qchannel, static_cast<u32>(level), qmessage); this_ptr->appendMessage(qchannel, static_cast<u32>(cat), qmessage);
} }
else else
{ {
QMetaObject::invokeMethod(this_ptr, "appendMessage", Qt::QueuedConnection, QMetaObject::invokeMethod(this_ptr, "appendMessage", Qt::QueuedConnection,
Q_ARG(const QLatin1StringView&, qchannel), Q_ARG(quint32, static_cast<u32>(level)), Q_ARG(const QLatin1StringView&, qchannel), Q_ARG(quint32, static_cast<u32>(cat)),
Q_ARG(const QString&, qmessage)); Q_ARG(const QString&, qmessage));
} }
} }
@ -304,7 +305,7 @@ void LogWindow::changeEvent(QEvent* event)
QMainWindow::changeEvent(event); QMainWindow::changeEvent(event);
} }
void LogWindow::appendMessage(const QLatin1StringView& channel, quint32 level, const QString& message) void LogWindow::appendMessage(const QLatin1StringView& channel, quint32 cat, const QString& message)
{ {
QTextCursor temp_cursor = m_text->textCursor(); QTextCursor temp_cursor = m_text->textCursor();
QScrollBar* scrollbar = m_text->verticalScrollBar(); QScrollBar* scrollbar = m_text->verticalScrollBar();
@ -316,28 +317,50 @@ void LogWindow::appendMessage(const QLatin1StringView& channel, quint32 level, c
{ {
static constexpr const QChar level_characters[static_cast<size_t>(Log::Level::MaxCount)] = {'X', 'E', 'W', 'I', static constexpr const QChar level_characters[static_cast<size_t>(Log::Level::MaxCount)] = {'X', 'E', 'W', 'I',
'V', 'D', 'B', 'T'}; 'V', 'D', 'B', 'T'};
static constexpr const QColor level_colors[2][static_cast<size_t>(Log::Level::MaxCount)] = { static constexpr const QColor message_colors[2][static_cast<size_t>(Log::Color::MaxCount)] = {
{
// Light theme // Light theme
QColor(0, 0, 0), // NONE
QColor(0x80, 0x00, 0x00), // ERROR, Red Intensity
QColor(0xb4, 0xb4, 0x00), // WARNING, Yellow Intensity
QColor(0x0d, 0x0d, 0x0d), // INFO, White Intensity
QColor(0x00, 0x80, 0x00), // VERBOSE, Green Intensity
QColor(0x70, 0x70, 0x70), // DEV, White
QColor(0xec, 0x5e, 0xf1), // DEBUG, Green
QColor(0xe9, 0x39, 0xf3), // TRACE, Blue
},
{ {
QColor(0x00, 0x00, 0x00), // Default
QColor(0x00, 0x00, 0x00), // Black
QColor(0x70, 0x00, 0x00), // Red
QColor(0xec, 0x5e, 0xf1), // Green
QColor(0xe9, 0x39, 0xf3), // Blue
QColor(0xA0, 0x00, 0xA0), // Magenta
QColor(0xA0, 0x78, 0x00), // Orange
QColor(0x80, 0xB4, 0xB4), // Cyan
QColor(0xB4, 0xB4, 0x80), // Yellow
QColor(0x70, 0x70, 0x70), // White
QColor(0x00, 0x00, 0x00), // StrongBlack
QColor(0x80, 0x00, 0x00), // StrongRed
QColor(0x00, 0x80, 0x00), // StrongGreen
QColor(0x00, 0x00, 0x80), // StrongBlue
QColor(0xA0, 0x00, 0xA0), // StrongMagenta
QColor(0xA0, 0x78, 0x00), // StrongOrange
QColor(0x80, 0xB4, 0xB4), // StrongCyan
QColor(0xb4, 0xb4, 0x00), // StrongYellow
QColor(0x0D, 0x0d, 0x0D) // StrongWhite
},
// Dark theme // Dark theme
QColor(255, 255, 255), // NONE {
QColor(0xE7, 0x48, 0x56), // ERROR, Red Intensity QColor(0xD0, 0xD0, 0xD0), // Default
QColor(0xF9, 0xF1, 0xA5), // WARNING, Yellow Intensity QColor(0xFF, 0xFF, 0xFF), // Black
QColor(0xF2, 0xF2, 0xF2), // INFO, White Intensity QColor(0xB4, 0x00, 0x00), // Red
QColor(0x16, 0xC6, 0x0C), // VERBOSE, Green Intensity QColor(0x13, 0xA1, 0x0E), // Green
QColor(0xCC, 0xCC, 0xCC), // DEV, White QColor(0x00, 0x37, 0xDA), // Blue
QColor(0x13, 0xA1, 0x0E), // DEBUG, Green QColor(0xA0, 0x00, 0xA0), // Magenta
QColor(0x00, 0x37, 0xDA), // TRACE, Blue QColor(0xA0, 0x78, 0x00), // Orange
QColor(0x80, 0xB4, 0xB4), // Cyan
QColor(0xB4, 0xB4, 0x80), // Yellow
QColor(0xCC, 0xCC, 0xCC), // White
QColor(0xFF, 0xFF, 0xFF), // StrongBlack
QColor(0xE7, 0x48, 0x56), // StrongRed
QColor(0x16, 0xC6, 0x0C), // StrongGreen
QColor(0x20, 0x20, 0xCC), // StrongBlue
QColor(0xA0, 0x00, 0xA0), // StrongMagenta
QColor(0xB4, 0x96, 0x00), // StrongOrange
QColor(0x80, 0xB4, 0xB4), // StrongCyan
QColor(0xF9, 0xF1, 0xA5), // StrongYellow
QColor(0xFF, 0xFF, 0xFF), // StrongWhite
}, },
}; };
static constexpr const QColor timestamp_color[2] = {QColor(0x60, 0x60, 0x60), QColor(0xcc, 0xcc, 0xcc)}; static constexpr const QColor timestamp_color[2] = {QColor(0x60, 0x60, 0x60), QColor(0xcc, 0xcc, 0xcc)};
@ -355,15 +378,20 @@ void LogWindow::appendMessage(const QLatin1StringView& channel, quint32 level, c
temp_cursor.insertText(qtimestamp); temp_cursor.insertText(qtimestamp);
} }
const QString qchannel = (level <= static_cast<u32>(Log::Level::Warning)) ? const Log::Level level = Log::UnpackLevel(static_cast<Log::MessageCategory>(cat));
QStringLiteral("%1(%2): ").arg(level_characters[level]).arg(channel) : const Log::Color color = (Log::UnpackColor(static_cast<Log::MessageCategory>(cat)) == Log::Color::Default) ?
QStringLiteral("%1/%2: ").arg(level_characters[level]).arg(channel); Log::GetColorForLevel(level) :
Log::UnpackColor(static_cast<Log::MessageCategory>(cat));
const QString qchannel =
(level <= Log::Level::Warning) ?
QStringLiteral("%1(%2): ").arg(level_characters[static_cast<size_t>(level)]).arg(channel) :
QStringLiteral("%1/%2: ").arg(level_characters[static_cast<size_t>(level)]).arg(channel);
format.setForeground(QBrush(channel_color[dark])); format.setForeground(QBrush(channel_color[dark]));
temp_cursor.setCharFormat(format); temp_cursor.setCharFormat(format);
temp_cursor.insertText(qchannel); temp_cursor.insertText(qchannel);
// message has \n already // message has \n already
format.setForeground(QBrush(level_colors[dark][level])); format.setForeground(QBrush(message_colors[dark][static_cast<size_t>(color)]));
temp_cursor.setCharFormat(format); temp_cursor.setCharFormat(format);
temp_cursor.insertText(message); temp_cursor.insertText(message);
} }

View File

@ -32,7 +32,7 @@ private:
void updateLogLevelUi(); void updateLogLevelUi();
void setLogLevel(Log::Level level); void setLogLevel(Log::Level level);
static void logCallback(void* pUserParam, const char* channelName, const char* functionName, Log::Level level, static void logCallback(void* pUserParam, Log::MessageCategory cat, const char* functionName,
std::string_view message); std::string_view message);
protected: protected:
@ -42,7 +42,7 @@ protected:
private Q_SLOTS: private Q_SLOTS:
void onClearTriggered(); void onClearTriggered();
void onSaveTriggered(); void onSaveTriggered();
void appendMessage(const QLatin1StringView& channel, quint32 level, const QString& message); void appendMessage(const QLatin1StringView& channel, quint32 cat, const QString& message);
private: private:
static constexpr int DEFAULT_WIDTH = 750; static constexpr int DEFAULT_WIDTH = 750;