mirror of
https://git.h3cjp.net/H3cJP/yuzu.git
synced 2024-11-14 12:42:52 +00:00
Logging: Create logging macros based on fmtlib
Add a new set of logging macros based on fmtlib Similar but not exactly the same as https://github.com/citra-emu/citra/pull/3533 Citra currently uses a different version of fmt, which does not support FMT_VARIADIC so make_args is used instead. On the other hand, yuzu uses fmt 4.1.0 which doesn't have make_args yet so FMT_VARIADIC is used.
This commit is contained in:
parent
f1beb69899
commit
3b558eebee
|
@ -90,7 +90,7 @@ endif()
|
||||||
|
|
||||||
create_target_directory_groups(common)
|
create_target_directory_groups(common)
|
||||||
|
|
||||||
target_link_libraries(common PUBLIC Boost::boost microprofile)
|
target_link_libraries(common PUBLIC Boost::boost fmt microprofile)
|
||||||
if (ARCHITECTURE_x86_64)
|
if (ARCHITECTURE_x86_64)
|
||||||
target_link_libraries(common PRIVATE xbyak)
|
target_link_libraries(common PRIVATE xbyak)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "common/logging/filter.h"
|
#include "common/logging/filter.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/logging/text_formatter.h"
|
#include "common/logging/text_formatter.h"
|
||||||
|
#include "common/string_util.h"
|
||||||
|
|
||||||
namespace Log {
|
namespace Log {
|
||||||
|
|
||||||
|
@ -102,25 +103,20 @@ const char* GetLevelName(Level log_level) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
|
Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
|
||||||
const char* function, const char* format, va_list args) {
|
const char* function, std::string message) {
|
||||||
using std::chrono::duration_cast;
|
using std::chrono::duration_cast;
|
||||||
using std::chrono::steady_clock;
|
using std::chrono::steady_clock;
|
||||||
|
|
||||||
static steady_clock::time_point time_origin = steady_clock::now();
|
static steady_clock::time_point time_origin = steady_clock::now();
|
||||||
|
|
||||||
std::array<char, 4 * 1024> formatting_buffer;
|
|
||||||
|
|
||||||
Entry entry;
|
Entry entry;
|
||||||
entry.timestamp = duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin);
|
entry.timestamp = duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin);
|
||||||
entry.log_class = log_class;
|
entry.log_class = log_class;
|
||||||
entry.log_level = log_level;
|
entry.log_level = log_level;
|
||||||
|
entry.filename = Common::TrimSourcePath(filename);
|
||||||
snprintf(formatting_buffer.data(), formatting_buffer.size(), "%s:%s:%u", filename, function,
|
entry.line_num = line_nr;
|
||||||
line_nr);
|
entry.function = function;
|
||||||
entry.location = std::string(formatting_buffer.data());
|
entry.message = std::move(message);
|
||||||
|
|
||||||
vsnprintf(formatting_buffer.data(), formatting_buffer.size(), format, args);
|
|
||||||
entry.message = std::string(formatting_buffer.data());
|
|
||||||
|
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
@ -131,15 +127,27 @@ void SetFilter(Filter* new_filter) {
|
||||||
filter = new_filter;
|
filter = new_filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
|
void LogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num,
|
||||||
const char* function, const char* format, ...) {
|
const char* function, const char* format, ...) {
|
||||||
if (filter != nullptr && !filter->CheckMessage(log_class, log_level))
|
if (filter && !filter->CheckMessage(log_class, log_level))
|
||||||
return;
|
return;
|
||||||
|
std::array<char, 4 * 1024> formatting_buffer;
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, format);
|
va_start(args, format);
|
||||||
Entry entry = CreateEntry(log_class, log_level, filename, line_nr, function, format, args);
|
vsnprintf(formatting_buffer.data(), formatting_buffer.size(), format, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
Entry entry = CreateEntry(log_class, log_level, filename, line_num, function,
|
||||||
|
std::string(formatting_buffer.data()));
|
||||||
|
|
||||||
|
PrintColoredMessage(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num,
|
||||||
|
const char* function, const char* format, const fmt::ArgList& args) {
|
||||||
|
if (filter && !filter->CheckMessage(log_class, log_level))
|
||||||
|
return;
|
||||||
|
Entry entry =
|
||||||
|
CreateEntry(log_class, log_level, filename, line_num, function, fmt::format(format, args));
|
||||||
|
|
||||||
PrintColoredMessage(entry);
|
PrintColoredMessage(entry);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,13 +22,16 @@ struct Entry {
|
||||||
std::chrono::microseconds timestamp;
|
std::chrono::microseconds timestamp;
|
||||||
Class log_class;
|
Class log_class;
|
||||||
Level log_level;
|
Level log_level;
|
||||||
std::string location;
|
std::string filename;
|
||||||
|
unsigned int line_num;
|
||||||
|
std::string function;
|
||||||
std::string message;
|
std::string message;
|
||||||
|
|
||||||
Entry() = default;
|
Entry() = default;
|
||||||
Entry(Entry&& o) = default;
|
Entry(Entry&& o) = default;
|
||||||
|
|
||||||
Entry& operator=(Entry&& o) = default;
|
Entry& operator=(Entry&& o) = default;
|
||||||
|
Entry& operator=(const Entry& o) = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -44,7 +47,7 @@ const char* GetLevelName(Level log_level);
|
||||||
|
|
||||||
/// Creates a log entry by formatting the given source location, and message.
|
/// Creates a log entry by formatting the given source location, and message.
|
||||||
Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
|
Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
|
||||||
const char* function, const char* format, va_list args);
|
const char* function, std::string message);
|
||||||
|
|
||||||
void SetFilter(Filter* filter);
|
void SetFilter(Filter* filter);
|
||||||
} // namespace Log
|
} // namespace Log
|
||||||
|
|
|
@ -65,14 +65,14 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin,
|
||||||
const std::string::const_iterator end) {
|
const std::string::const_iterator end) {
|
||||||
auto level_separator = std::find(begin, end, ':');
|
auto level_separator = std::find(begin, end, ':');
|
||||||
if (level_separator == end) {
|
if (level_separator == end) {
|
||||||
LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: %s",
|
NGLOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: %s",
|
||||||
std::string(begin, end).c_str());
|
std::string(begin, end).c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Level level = GetLevelByName(level_separator + 1, end);
|
const Level level = GetLevelByName(level_separator + 1, end);
|
||||||
if (level == Level::Count) {
|
if (level == Level::Count) {
|
||||||
LOG_ERROR(Log, "Unknown log level in filter: %s", std::string(begin, end).c_str());
|
NGLOG_ERROR(Log, "Unknown log level in filter: %s", std::string(begin, end).c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin,
|
||||||
|
|
||||||
const Class log_class = GetClassByName(begin, level_separator);
|
const Class log_class = GetClassByName(begin, level_separator);
|
||||||
if (log_class == Class::Count) {
|
if (log_class == Class::Count) {
|
||||||
LOG_ERROR(Log, "Unknown log class in filter: %s", std::string(begin, end).c_str());
|
NGLOG_ERROR(Log, "Unknown log class in filter: %s", std::string(begin, end).c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ namespace Log {
|
||||||
class Filter {
|
class Filter {
|
||||||
public:
|
public:
|
||||||
/// Initializes the filter with all classes having `default_level` as the minimum level.
|
/// Initializes the filter with all classes having `default_level` as the minimum level.
|
||||||
Filter(Level default_level);
|
Filter(Level default_level = Level::Info);
|
||||||
|
|
||||||
/// Resets the filter so that all classes have `level` as the minimum displayed level.
|
/// Resets the filter so that all classes have `level` as the minimum displayed level.
|
||||||
void ResetAll(Level level);
|
void ResetAll(Level level);
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <fmt/format.h>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
namespace Log {
|
namespace Log {
|
||||||
|
@ -87,7 +89,7 @@ enum class Class : ClassType {
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Logs a message to the global logger.
|
/// Logs a message to the global logger.
|
||||||
void LogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
|
void LogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num,
|
||||||
const char* function,
|
const char* function,
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
_Printf_format_string_
|
_Printf_format_string_
|
||||||
|
@ -99,6 +101,10 @@ void LogMessage(Class log_class, Level log_level, const char* filename, unsigned
|
||||||
#endif
|
#endif
|
||||||
;
|
;
|
||||||
|
|
||||||
|
void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num,
|
||||||
|
const char* function, const char* format, const fmt::ArgList& args);
|
||||||
|
FMT_VARIADIC(void, FmtLogMessage, Class, Level, const char*, unsigned int, const char*, const char*)
|
||||||
|
|
||||||
} // namespace Log
|
} // namespace Log
|
||||||
|
|
||||||
#define LOG_GENERIC(log_class, log_level, ...) \
|
#define LOG_GENERIC(log_class, log_level, ...) \
|
||||||
|
@ -121,3 +127,28 @@ void LogMessage(Class log_class, Level log_level, const char* filename, unsigned
|
||||||
LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Error, __VA_ARGS__)
|
LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Error, __VA_ARGS__)
|
||||||
#define LOG_CRITICAL(log_class, ...) \
|
#define LOG_CRITICAL(log_class, ...) \
|
||||||
LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Critical, __VA_ARGS__)
|
LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Critical, __VA_ARGS__)
|
||||||
|
|
||||||
|
// Define the fmt lib macros
|
||||||
|
#ifdef _DEBUG
|
||||||
|
#define NGLOG_TRACE(log_class, ...) \
|
||||||
|
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Trace, __FILE__, __LINE__, \
|
||||||
|
__func__, __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define NGLOG_TRACE(log_class, fmt, ...) (void(0))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NGLOG_DEBUG(log_class, ...) \
|
||||||
|
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Debug, __FILE__, __LINE__, \
|
||||||
|
__func__, __VA_ARGS__)
|
||||||
|
#define NGLOG_INFO(log_class, ...) \
|
||||||
|
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Info, __FILE__, __LINE__, \
|
||||||
|
__func__, __VA_ARGS__)
|
||||||
|
#define NGLOG_WARNING(log_class, ...) \
|
||||||
|
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Warning, __FILE__, __LINE__, \
|
||||||
|
__func__, __VA_ARGS__)
|
||||||
|
#define NGLOG_ERROR(log_class, ...) \
|
||||||
|
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Error, __FILE__, __LINE__, \
|
||||||
|
__func__, __VA_ARGS__)
|
||||||
|
#define NGLOG_CRITICAL(log_class, ...) \
|
||||||
|
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Critical, __FILE__, __LINE__, \
|
||||||
|
__func__, __VA_ARGS__)
|
||||||
|
|
|
@ -18,50 +18,29 @@
|
||||||
|
|
||||||
namespace Log {
|
namespace Log {
|
||||||
|
|
||||||
// TODO(bunnei): This should be moved to a generic path manipulation library
|
std::string FormatLogMessage(const Entry& entry) {
|
||||||
const char* TrimSourcePath(const char* path, const char* root) {
|
|
||||||
const char* p = path;
|
|
||||||
|
|
||||||
while (*p != '\0') {
|
|
||||||
const char* next_slash = p;
|
|
||||||
while (*next_slash != '\0' && *next_slash != '/' && *next_slash != '\\') {
|
|
||||||
++next_slash;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_src = Common::ComparePartialString(p, next_slash, root);
|
|
||||||
p = next_slash;
|
|
||||||
|
|
||||||
if (*p != '\0') {
|
|
||||||
++p;
|
|
||||||
}
|
|
||||||
if (is_src) {
|
|
||||||
path = p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len) {
|
|
||||||
unsigned int time_seconds = static_cast<unsigned int>(entry.timestamp.count() / 1000000);
|
unsigned int time_seconds = static_cast<unsigned int>(entry.timestamp.count() / 1000000);
|
||||||
unsigned int time_fractional = static_cast<unsigned int>(entry.timestamp.count() % 1000000);
|
unsigned int time_fractional = static_cast<unsigned int>(entry.timestamp.count() % 1000000);
|
||||||
|
|
||||||
const char* class_name = GetLogClassName(entry.log_class);
|
const char* class_name = GetLogClassName(entry.log_class);
|
||||||
const char* level_name = GetLevelName(entry.log_level);
|
const char* level_name = GetLevelName(entry.log_level);
|
||||||
|
|
||||||
snprintf(out_text, text_len, "[%4u.%06u] %s <%s> %s: %s", time_seconds, time_fractional,
|
return fmt::format("[{:4d}.{:06d}] {} <{}> {}:{}:{}: {}", time_seconds, time_fractional,
|
||||||
class_name, level_name, TrimSourcePath(entry.location.c_str()), entry.message.c_str());
|
class_name, level_name, entry.filename, entry.function, entry.line_num,
|
||||||
|
entry.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintMessage(const Entry& entry) {
|
void PrintMessage(const Entry& entry) {
|
||||||
std::array<char, 4 * 1024> format_buffer;
|
auto str = FormatLogMessage(entry) + '\n';
|
||||||
FormatLogMessage(entry, format_buffer.data(), format_buffer.size());
|
fputs(str.c_str(), stderr);
|
||||||
fputs(format_buffer.data(), stderr);
|
|
||||||
fputc('\n', stderr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintColoredMessage(const Entry& entry) {
|
void PrintColoredMessage(const Entry& entry) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
static HANDLE console_handle = GetStdHandle(STD_ERROR_HANDLE);
|
HANDLE console_handle = GetStdHandle(STD_ERROR_HANDLE);
|
||||||
|
if (console_handle == INVALID_HANDLE_VALUE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
CONSOLE_SCREEN_BUFFER_INFO original_info = {0};
|
CONSOLE_SCREEN_BUFFER_INFO original_info = {0};
|
||||||
GetConsoleScreenBufferInfo(console_handle, &original_info);
|
GetConsoleScreenBufferInfo(console_handle, &original_info);
|
||||||
|
|
|
@ -10,20 +10,8 @@ namespace Log {
|
||||||
|
|
||||||
struct Entry;
|
struct Entry;
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's
|
|
||||||
* intended to be used to strip a system-specific build directory from the `__FILE__` macro,
|
|
||||||
* leaving only the path relative to the sources root.
|
|
||||||
*
|
|
||||||
* @param path The input file path as a null-terminated string
|
|
||||||
* @param root The name of the root source directory as a null-terminated string. Path up to and
|
|
||||||
* including the last occurrence of this name will be stripped
|
|
||||||
* @return A pointer to the same string passed as `path`, but starting at the trimmed portion
|
|
||||||
*/
|
|
||||||
const char* TrimSourcePath(const char* path, const char* root = "src");
|
|
||||||
|
|
||||||
/// Formats a log entry into the provided text buffer.
|
/// Formats a log entry into the provided text buffer.
|
||||||
void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len);
|
std::string FormatLogMessage(const Entry& entry);
|
||||||
/// Formats and prints a log entry to stderr.
|
/// Formats and prints a log entry to stderr.
|
||||||
void PrintMessage(const Entry& entry);
|
void PrintMessage(const Entry& entry);
|
||||||
/// Prints the same message as `PrintMessage`, but colored acoording to the severity level.
|
/// Prints the same message as `PrintMessage`, but colored acoording to the severity level.
|
||||||
|
|
|
@ -462,4 +462,27 @@ std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, size_t max_l
|
||||||
|
|
||||||
return std::string(buffer, len);
|
return std::string(buffer, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* TrimSourcePath(const char* path, const char* root) {
|
||||||
|
const char* p = path;
|
||||||
|
|
||||||
|
while (*p != '\0') {
|
||||||
|
const char* next_slash = p;
|
||||||
|
while (*next_slash != '\0' && *next_slash != '/' && *next_slash != '\\') {
|
||||||
|
++next_slash;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_src = Common::ComparePartialString(p, next_slash, root);
|
||||||
|
p = next_slash;
|
||||||
|
|
||||||
|
if (*p != '\0') {
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
if (is_src) {
|
||||||
|
path = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|
|
@ -134,4 +134,17 @@ bool ComparePartialString(InIt begin, InIt end, const char* other) {
|
||||||
* NUL-terminated then the string ends at max_len characters.
|
* NUL-terminated then the string ends at max_len characters.
|
||||||
*/
|
*/
|
||||||
std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, size_t max_len);
|
std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, size_t max_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's
|
||||||
|
* intended to be used to strip a system-specific build directory from the `__FILE__` macro,
|
||||||
|
* leaving only the path relative to the sources root.
|
||||||
|
*
|
||||||
|
* @param path The input file path as a null-terminated string
|
||||||
|
* @param root The name of the root source directory as a null-terminated string. Path up to and
|
||||||
|
* including the last occurrence of this name will be stripped
|
||||||
|
* @return A pointer to the same string passed as `path`, but starting at the trimmed portion
|
||||||
|
*/
|
||||||
|
const char* TrimSourcePath(const char* path, const char* root = "src");
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|
Loading…
Reference in a new issue