mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-02-04 02:51:18 +01:00
[common] remove uneeded allocations in logs
Signed-off-by: lizzie <lizzie@eden-emu.dev>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <climits>
|
||||
#include <cstdlib>
|
||||
#include <regex>
|
||||
#include <thread>
|
||||
|
||||
@@ -41,7 +42,7 @@ namespace {
|
||||
|
||||
/// @brief Trims up to and including the last of ../, ..\, src/, src\ in a string
|
||||
/// do not be fooled this isn't generating new strings on .rodata :)
|
||||
constexpr const char* TrimSourcePath(std::string_view source) {
|
||||
constexpr const char* TrimSourcePath(std::string_view source) noexcept {
|
||||
const auto rfind = [source](const std::string_view match) {
|
||||
return source.rfind(match) == source.npos ? 0 : (source.rfind(match) + match.size());
|
||||
};
|
||||
@@ -51,31 +52,31 @@ constexpr const char* TrimSourcePath(std::string_view source) {
|
||||
|
||||
/// @brief Interface for logging backends.
|
||||
struct Backend {
|
||||
virtual ~Backend() = default;
|
||||
virtual void Write(const Entry& entry) = 0;
|
||||
virtual void EnableForStacktrace() = 0;
|
||||
virtual void Flush() = 0;
|
||||
virtual ~Backend() noexcept = default;
|
||||
virtual void Write(const Entry& entry) noexcept = 0;
|
||||
virtual void EnableForStacktrace() noexcept= 0;
|
||||
virtual void Flush() noexcept = 0;
|
||||
};
|
||||
|
||||
/// @brief Backend that writes to stderr and with color
|
||||
struct ColorConsoleBackend final : public Backend {
|
||||
explicit ColorConsoleBackend() = default;
|
||||
~ColorConsoleBackend() override = default;
|
||||
explicit ColorConsoleBackend() noexcept = default;
|
||||
~ColorConsoleBackend() noexcept override = default;
|
||||
|
||||
void Write(const Entry& entry) override {
|
||||
void Write(const Entry& entry) noexcept override {
|
||||
if (enabled.load(std::memory_order_relaxed))
|
||||
PrintColoredMessage(entry);
|
||||
}
|
||||
|
||||
void Flush() override {
|
||||
void Flush() noexcept override {
|
||||
// stderr shouldn't be buffered
|
||||
}
|
||||
|
||||
void EnableForStacktrace() override {
|
||||
void EnableForStacktrace() noexcept override {
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
void SetEnabled(bool enabled_) {
|
||||
void SetEnabled(bool enabled_) noexcept {
|
||||
enabled = enabled_;
|
||||
}
|
||||
|
||||
@@ -83,9 +84,10 @@ private:
|
||||
std::atomic_bool enabled{false};
|
||||
};
|
||||
|
||||
#ifndef __OPENORBIS__
|
||||
/// @brief Backend that writes to a file passed into the constructor
|
||||
struct FileBackend final : public Backend {
|
||||
explicit FileBackend(const std::filesystem::path& filename) {
|
||||
explicit FileBackend(const std::filesystem::path& filename) noexcept {
|
||||
auto old_filename = filename;
|
||||
old_filename += ".old.txt";
|
||||
|
||||
@@ -97,9 +99,9 @@ struct FileBackend final : public Backend {
|
||||
file = std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write, FS::FileType::TextFile);
|
||||
}
|
||||
|
||||
~FileBackend() override = default;
|
||||
~FileBackend() noexcept override = default;
|
||||
|
||||
void Write(const Entry& entry) override {
|
||||
void Write(const Entry& entry) noexcept override {
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
@@ -147,11 +149,11 @@ struct FileBackend final : public Backend {
|
||||
}
|
||||
}
|
||||
|
||||
void Flush() override {
|
||||
void Flush() noexcept override {
|
||||
file->Flush();
|
||||
}
|
||||
|
||||
void EnableForStacktrace() override {
|
||||
void EnableForStacktrace() noexcept override {
|
||||
enabled = true;
|
||||
bytes_written = 0;
|
||||
}
|
||||
@@ -161,29 +163,30 @@ private:
|
||||
std::size_t bytes_written = 0;
|
||||
bool enabled = true;
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
/// @brief Backend that writes to Visual Studio's output window
|
||||
struct DebuggerBackend final : public Backend {
|
||||
explicit DebuggerBackend() = default;
|
||||
~DebuggerBackend() override = default;
|
||||
void Write(const Entry& entry) override {
|
||||
explicit DebuggerBackend() noexcept = default;
|
||||
~DebuggerBackend() noexcept override = default;
|
||||
void Write(const Entry& entry) noexcept override {
|
||||
::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str());
|
||||
}
|
||||
void Flush() override {}
|
||||
void EnableForStacktrace() override {}
|
||||
void Flush() noexcept override {}
|
||||
void EnableForStacktrace() noexcept override {}
|
||||
};
|
||||
#endif
|
||||
#ifdef ANDROID
|
||||
/// @brief Backend that writes to the Android logcat
|
||||
struct LogcatBackend : public Backend {
|
||||
explicit LogcatBackend() = default;
|
||||
~LogcatBackend() override = default;
|
||||
void Write(const Entry& entry) override {
|
||||
explicit LogcatBackend() noexcept = default;
|
||||
~LogcatBackend() noexcept override = default;
|
||||
void Write(const Entry& entry) noexcept override {
|
||||
PrintMessageToLogcat(entry);
|
||||
}
|
||||
void Flush() override {}
|
||||
void EnableForStacktrace() override {}
|
||||
void Flush() noexcept override {}
|
||||
void EnableForStacktrace() noexcept override {}
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -192,13 +195,13 @@ bool initialization_in_progress_suppress_logging = true;
|
||||
/// @brief Static state as a singleton.
|
||||
class Impl {
|
||||
public:
|
||||
static Impl& Instance() {
|
||||
static Impl& Instance() noexcept {
|
||||
if (!instance)
|
||||
throw std::runtime_error("Using Logging instance before its initialization");
|
||||
std::abort();
|
||||
return *instance;
|
||||
}
|
||||
|
||||
static void Initialize() {
|
||||
static void Initialize() noexcept {
|
||||
if (instance) {
|
||||
LOG_WARNING(Log, "Reinitializing logging backend");
|
||||
return;
|
||||
@@ -212,25 +215,25 @@ public:
|
||||
initialization_in_progress_suppress_logging = false;
|
||||
}
|
||||
|
||||
static void Start() {
|
||||
static void Start() noexcept {
|
||||
instance->StartBackendThread();
|
||||
}
|
||||
|
||||
static void Stop() {
|
||||
static void Stop() noexcept {
|
||||
instance->StopBackendThread();
|
||||
}
|
||||
|
||||
Impl(const Impl&) = delete;
|
||||
Impl& operator=(const Impl&) = delete;
|
||||
Impl(const Impl&) noexcept = delete;
|
||||
Impl& operator=(const Impl&) noexcept = delete;
|
||||
|
||||
Impl(Impl&&) = delete;
|
||||
Impl& operator=(Impl&&) = delete;
|
||||
Impl(Impl&&) noexcept = delete;
|
||||
Impl& operator=(Impl&&) noexcept = delete;
|
||||
|
||||
void SetGlobalFilter(const Filter& f) {
|
||||
void SetGlobalFilter(const Filter& f) noexcept {
|
||||
filter = f;
|
||||
}
|
||||
|
||||
void SetColorConsoleBackendEnabled(bool enabled) {
|
||||
void SetColorConsoleBackendEnabled(bool enabled) noexcept {
|
||||
color_console_backend.SetEnabled(enabled);
|
||||
}
|
||||
|
||||
@@ -238,19 +241,21 @@ public:
|
||||
return filter.CheckMessage(log_class, log_level);
|
||||
}
|
||||
|
||||
void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num,
|
||||
const char* function, std::string&& message) noexcept {
|
||||
message_queue.EmplaceWait(
|
||||
CreateEntry(log_class, log_level, TrimSourcePath(filename), line_num, function, std::move(message)));
|
||||
void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num, const char* function, std::string&& message) noexcept {
|
||||
message_queue.EmplaceWait(CreateEntry(log_class, log_level, TrimSourcePath(filename), line_num, function, std::move(message)));
|
||||
}
|
||||
|
||||
private:
|
||||
Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_)
|
||||
: filter{filter_}, file_backend{file_backend_filename} {}
|
||||
Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_) noexcept :
|
||||
filter{filter_}
|
||||
#ifndef __OPENORBIS__
|
||||
, file_backend{file_backend_filename}
|
||||
#endif
|
||||
{}
|
||||
|
||||
~Impl() = default;
|
||||
~Impl() noexcept = default;
|
||||
|
||||
void StartBackendThread() {
|
||||
void StartBackendThread() noexcept {
|
||||
backend_thread = std::jthread([this](std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("Logger");
|
||||
Entry entry;
|
||||
@@ -271,15 +276,14 @@ private:
|
||||
});
|
||||
}
|
||||
|
||||
void StopBackendThread() {
|
||||
void StopBackendThread() noexcept {
|
||||
backend_thread.request_stop();
|
||||
if (backend_thread.joinable())
|
||||
backend_thread.join();
|
||||
ForEachBackend([](Backend& backend) { backend.Flush(); });
|
||||
}
|
||||
|
||||
Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
|
||||
const char* function, std::string&& message) const {
|
||||
Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, const char* function, std::string&& message) const noexcept {
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::microseconds;
|
||||
using std::chrono::steady_clock;
|
||||
@@ -294,9 +298,11 @@ private:
|
||||
};
|
||||
}
|
||||
|
||||
void ForEachBackend(auto lambda) {
|
||||
void ForEachBackend(auto lambda) noexcept {
|
||||
lambda(static_cast<Backend&>(color_console_backend));
|
||||
#ifndef __OPENORBIS__
|
||||
lambda(static_cast<Backend&>(file_backend));
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
lambda(static_cast<Backend&>(debugger_backend));
|
||||
#endif
|
||||
@@ -305,7 +311,7 @@ private:
|
||||
#endif
|
||||
}
|
||||
|
||||
static void Deleter(Impl* ptr) {
|
||||
static void Deleter(Impl* ptr) noexcept {
|
||||
delete ptr;
|
||||
}
|
||||
|
||||
@@ -313,7 +319,9 @@ private:
|
||||
|
||||
Filter filter;
|
||||
ColorConsoleBackend color_console_backend{};
|
||||
#ifndef __OPENORBIS__
|
||||
FileBackend file_backend;
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
DebuggerBackend debugger_backend{};
|
||||
#endif
|
||||
@@ -351,9 +359,7 @@ void SetColorConsoleBackendEnabled(bool enabled) {
|
||||
Impl::Instance().SetColorConsoleBackendEnabled(enabled);
|
||||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
if (!initialization_in_progress_suppress_logging) {
|
||||
auto& instance = Impl::Instance();
|
||||
if (instance.CanPushEntry(log_class, log_level))
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -9,17 +12,15 @@
|
||||
|
||||
namespace Common::Log {
|
||||
|
||||
/**
|
||||
* A log entry. Log entries are store in a structured format to permit more varied output
|
||||
* formatting on different frontends, as well as facilitating filtering and aggregation.
|
||||
*/
|
||||
/// @brief A log entry. Log entries are store in a structured format to permit more varied output
|
||||
/// formatting on different frontends, as well as facilitating filtering and aggregation.
|
||||
struct Entry {
|
||||
std::chrono::microseconds timestamp;
|
||||
Class log_class{};
|
||||
Level log_level{};
|
||||
const char* filename = nullptr;
|
||||
unsigned int line_num = 0;
|
||||
std::string function;
|
||||
const char* function = nullptr;
|
||||
std::string message;
|
||||
};
|
||||
|
||||
|
||||
@@ -24,24 +24,26 @@
|
||||
namespace Common::Log {
|
||||
|
||||
// Some IDEs prefer <file>:<line> instead, so let's just do that :)
|
||||
std::string FormatLogMessage(const Entry& entry) {
|
||||
std::string FormatLogMessage(const Entry& entry) noexcept {
|
||||
if (!entry.filename) return "";
|
||||
|
||||
auto const time_seconds = uint32_t(entry.timestamp.count() / 1000000);
|
||||
auto const time_fractional = uint32_t(entry.timestamp.count() % 1000000);
|
||||
char const* class_name = GetLogClassName(entry.log_class);
|
||||
char const* level_name = GetLevelName(entry.log_level);
|
||||
auto const class_name = GetLogClassName(entry.log_class);
|
||||
auto const level_name = GetLevelName(entry.log_level);
|
||||
return fmt::format("[{:4d}.{:06d}] {} <{}> {}:{}:{}: {}", time_seconds, time_fractional,
|
||||
class_name, level_name, entry.filename, entry.line_num, entry.function,
|
||||
entry.message);
|
||||
}
|
||||
|
||||
void PrintMessage(const Entry& entry) {
|
||||
/// @brief Formats and prints a log entry to stderr.
|
||||
static void PrintMessage(const Entry& entry) noexcept {
|
||||
#ifdef _WIN32
|
||||
auto const str = FormatLogMessage(entry).append(1, '\n');
|
||||
fwrite(str.c_str(), 1, str.size(), stderr);
|
||||
#else
|
||||
#define ESC "\x1b"
|
||||
auto str = std::string{[&entry]() -> const char* {
|
||||
auto const color_str = [&entry]() -> const char* {
|
||||
switch (entry.log_level) {
|
||||
case Level::Debug: return ESC "[0;36m"; // Cyan
|
||||
case Level::Info: return ESC "[0;37m"; // Bright gray
|
||||
@@ -50,15 +52,19 @@ void PrintMessage(const Entry& entry) {
|
||||
case Level::Critical: return ESC "[1;35m"; // Bright magenta
|
||||
default: return ESC "[1;30m"; // Grey
|
||||
}
|
||||
}()};
|
||||
str.append(FormatLogMessage(entry));
|
||||
str.append(ESC "[0m\n");
|
||||
}();
|
||||
auto const time_seconds = uint32_t(entry.timestamp.count() / 1000000);
|
||||
auto const time_fractional = uint32_t(entry.timestamp.count() % 1000000);
|
||||
auto const class_name = GetLogClassName(entry.log_class);
|
||||
auto const level_name = GetLevelName(entry.log_level);
|
||||
fprintf(stderr, "%s[%4d.%06d] %s <%s> %s:%u:%s: %s" ESC "[0m\n", color_str,
|
||||
time_seconds, time_fractional, class_name, level_name, entry.filename,
|
||||
entry.line_num, entry.function, entry.message.c_str());
|
||||
#undef ESC
|
||||
#endif
|
||||
fwrite(str.c_str(), 1, str.size(), stderr);
|
||||
}
|
||||
|
||||
void PrintColoredMessage(const Entry& entry) {
|
||||
void PrintColoredMessage(const Entry& entry) noexcept {
|
||||
#ifdef _WIN32
|
||||
HANDLE console_handle = GetStdHandle(STD_ERROR_HANDLE);
|
||||
if (console_handle == INVALID_HANDLE_VALUE)
|
||||
@@ -84,7 +90,7 @@ void PrintColoredMessage(const Entry& entry) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void PrintMessageToLogcat(const Entry& entry) {
|
||||
void PrintMessageToLogcat(const Entry& entry) noexcept {
|
||||
#ifdef ANDROID
|
||||
android_LogPriority android_log_priority = [&]() {
|
||||
switch (entry.log_level) {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// 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
|
||||
|
||||
@@ -10,11 +13,9 @@ namespace Common::Log {
|
||||
struct Entry;
|
||||
|
||||
/// Formats a log entry into the provided text buffer.
|
||||
std::string FormatLogMessage(const Entry& entry);
|
||||
/// Formats and prints a log entry to stderr.
|
||||
void PrintMessage(const Entry& entry);
|
||||
std::string FormatLogMessage(const Entry& entry) noexcept;
|
||||
/// Prints the same message as `PrintMessage`, but colored according to the severity level.
|
||||
void PrintColoredMessage(const Entry& entry);
|
||||
void PrintColoredMessage(const Entry& entry) noexcept;
|
||||
/// Formats and prints a log entry to the android logcat.
|
||||
void PrintMessageToLogcat(const Entry& entry);
|
||||
void PrintMessageToLogcat(const Entry& entry) noexcept;
|
||||
} // namespace Common::Log
|
||||
|
||||
Reference in New Issue
Block a user