mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-24 16:30:44 +00:00
b610b03d29
Signed-off-by: lizzie <lizzie@eden-emu.dev> - [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions). - [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md) - [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability. ------------------- the idea is simple, `std::string{}` adds pressure to the memory allocator so whats the best next thing we can do? well of course use our stack its plenty, but we shouldn't be greedy either, BUFSIZ should be a fair amount of space for any would-be messages anyways the main idea behind this PR is to remove std::string{} allocations done with libfmt, that way we have 0-allocs per logging entry this should 100% remove pressure uneeded from the memory allocator we should only allocate things that are important, we can use our trusty fast stack for any string manip we need to do not await/global lock or do evil things with the memory allocator obviously stack is thread local already sooo... this is even better than having to deal with malloc()/free() in any capacity whatsoever and no clang can't heap ellide this (how would you even?) Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4273 Reviewed-by: Maufeat <sahyno1996@gmail.com> Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
144 lines
6.2 KiB
C++
144 lines
6.2 KiB
C++
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#pragma once
|
|
|
|
#include <chrono>
|
|
#include <algorithm>
|
|
#include <type_traits>
|
|
#include <fmt/ranges.h>
|
|
#include "common/swap.h"
|
|
|
|
// adapted from https://github.com/fmtlib/fmt/issues/2704
|
|
// a generic formatter for enum classes
|
|
#if FMT_VERSION >= 80100
|
|
template <typename T>
|
|
struct fmt::formatter<T, std::enable_if_t<std::is_enum_v<T>, char>>
|
|
: formatter<std::underlying_type_t<T>> {
|
|
template <typename FormatContext>
|
|
auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) {
|
|
return fmt::formatter<std::underlying_type_t<T>>::format(
|
|
static_cast<std::underlying_type_t<T>>(value), ctx);
|
|
}
|
|
};
|
|
#endif
|
|
|
|
template <typename T, typename U>
|
|
struct fmt::formatter<SwapStructT<T, U>> {
|
|
constexpr auto parse(format_parse_context& ctx) {
|
|
return ctx.begin();
|
|
}
|
|
template <typename FormatContext>
|
|
auto format(const SwapStructT<T, U>& reg, FormatContext& ctx) const {
|
|
return fmt::format_to(ctx.out(), "{}", T(reg));
|
|
}
|
|
};
|
|
|
|
#ifdef _DEBUG
|
|
#define LOG_TRACE(log_class, ...) \
|
|
Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Trace, \
|
|
__FILE__, __LINE__, __func__, __VA_ARGS__)
|
|
#else
|
|
#define LOG_TRACE(log_class, fmt, ...) (void(0))
|
|
#endif
|
|
|
|
#define LOG_DEBUG(log_class, ...) \
|
|
::Common::Log::FmtLogMessage(::Common::Log::Class::log_class, ::Common::Log::Level::Debug, \
|
|
__FILE__, __LINE__, __func__, __VA_ARGS__)
|
|
#define LOG_INFO(log_class, ...) \
|
|
::Common::Log::FmtLogMessage(::Common::Log::Class::log_class, ::Common::Log::Level::Info, \
|
|
__FILE__, __LINE__, __func__, __VA_ARGS__)
|
|
#define LOG_WARNING(log_class, ...) \
|
|
::Common::Log::FmtLogMessage(::Common::Log::Class::log_class, ::Common::Log::Level::Warning, \
|
|
__FILE__, __LINE__, __func__, __VA_ARGS__)
|
|
#define LOG_ERROR(log_class, ...) \
|
|
::Common::Log::FmtLogMessage(::Common::Log::Class::log_class, ::Common::Log::Level::Error, \
|
|
__FILE__, __LINE__, __func__, __VA_ARGS__)
|
|
#define LOG_CRITICAL(log_class, ...) \
|
|
::Common::Log::FmtLogMessage(::Common::Log::Class::log_class, ::Common::Log::Level::Critical, \
|
|
__FILE__, __LINE__, __func__, __VA_ARGS__)
|
|
|
|
namespace Common::Log {
|
|
|
|
/// Specifies the severity or level of detail of the log message.
|
|
enum class Level : u8 {
|
|
Trace, ///< Extremely detailed and repetitive debugging information that is likely to pollute logs.
|
|
Debug, ///< Less detailed debugging information.
|
|
Info, ///< Status information from important points during execution.
|
|
Warning, ///< Minor or potential problems found during execution of a task.
|
|
Error, ///< Major problems found during execution of a task that prevent it from being completed.
|
|
Critical, ///< Major problems during execution that threaten the stability of the entire application.
|
|
Count ///< Total number of logging levels
|
|
};
|
|
|
|
/// Specifies the sub-system that generated the log message.
|
|
enum class Class : u8 {
|
|
#define SUB(A, B) A##_##B,
|
|
#define CLS(A) A,
|
|
#include "log_classes.inc"
|
|
#undef SUB
|
|
#undef CLS
|
|
Count,
|
|
};
|
|
|
|
/// Logs a message to the global logger, using fmt
|
|
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, unsigned int line_num, const char* function, fmt::string_view format, const fmt::format_args& args);
|
|
|
|
template <typename... Args>
|
|
void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num, const char* function, fmt::format_string<Args...> format, const Args&... args) {
|
|
FmtLogMessageImpl(log_class, log_level, filename, line_num, function, format.get(), fmt::make_format_args(args...));
|
|
}
|
|
|
|
/// Implements a log message filter which allows different log classes to have different minimum
|
|
/// severity levels. The filter can be changed at runtime and can be parsed from a string to allow
|
|
/// editing via the interface or loading from a configuration file.
|
|
struct Filter {
|
|
/// Initializes the filter with all classes having `default_level` as the minimum level.
|
|
explicit Filter(Level level = Level::Info) {
|
|
class_levels.fill(level);
|
|
}
|
|
/// Sets the minimum level of `log_class` (and not of its subclasses) to `level`.
|
|
void SetClassLevel(Class log_class, Level level) {
|
|
class_levels[std::size_t(log_class)] = level;
|
|
}
|
|
/// Parses a filter string and applies it to this filter.
|
|
/// A filter string consists of a space-separated list of filter rules, each of the format
|
|
/// `<class>:<level>`. `<class>` is a log class name, with subclasses separated using periods.
|
|
/// `*` is allowed as a class name and will reset all filters to the specified level. `<level>`
|
|
/// a severity level name which will be set as the minimum logging level of the matched classes.
|
|
/// Rules are applied left to right, with each rule overriding previous ones in the sequence.
|
|
/// A few examples of filter rules:
|
|
/// - `*:Info` -- Resets the level of all classes to Info.
|
|
/// - `Service:Info` -- Sets the level of Service to Info.
|
|
/// - `Service.FS:Trace` -- Sets the level of the Service.FS class to Trace.
|
|
void ParseFilterString(std::string_view filter_view);
|
|
/// Matches class/level combination against the filter, returning true if it passed.
|
|
[[nodiscard]] bool CheckMessage(Class log_class, Level level) const {
|
|
return u8(level) >= u8(class_levels[std::size_t(log_class)]);
|
|
}
|
|
/// Returns true if any logging classes are set to debug
|
|
[[nodiscard]] bool IsDebug() const {
|
|
return std::any_of(class_levels.begin(), class_levels.end(), [](const Level& l) {
|
|
return u8(l) <= u8(Level::Debug);
|
|
});
|
|
}
|
|
std::array<Level, std::size_t(Class::Count)> class_levels;
|
|
};
|
|
|
|
/// Initializes the logging system. This should be the first thing called in main.
|
|
void Initialize();
|
|
|
|
void Start();
|
|
|
|
/// Explicitly stops the logger thread and flushes the buffers
|
|
void Stop();
|
|
|
|
/// The global filter will prevent any messages from even being processed if they are filtered.
|
|
void SetGlobalFilter(const Filter& filter);
|
|
void SetColorConsoleBackendEnabled(bool enabled);
|
|
|
|
} // namespace Common::Log
|