diff options
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/assert.h | 5 | ||||
-rw-r--r-- | src/common/common_paths.h | 3 | ||||
-rw-r--r-- | src/common/file_util.cpp | 111 | ||||
-rw-r--r-- | src/common/file_util.h | 11 | ||||
-rw-r--r-- | src/common/logging/backend.cpp | 157 | ||||
-rw-r--r-- | src/common/logging/backend.h | 87 | ||||
-rw-r--r-- | src/common/logging/filter.cpp | 8 | ||||
-rw-r--r-- | src/common/logging/log.h | 14 | ||||
-rw-r--r-- | src/common/memory_util.cpp | 16 | ||||
-rw-r--r-- | src/common/param_package.cpp | 12 | ||||
-rw-r--r-- | src/common/string_util.cpp | 8 |
11 files changed, 334 insertions, 98 deletions
diff --git a/src/common/assert.h b/src/common/assert.h index 3ee07f6a2..655446f34 100644 --- a/src/common/assert.h +++ b/src/common/assert.h @@ -30,15 +30,14 @@ __declspec(noinline, noreturn) #define ASSERT(_a_) \ do \ if (!(_a_)) { \ - assert_noinline_call([] { NGLOG_CRITICAL(Debug, "Assertion Failed!"); }); \ + assert_noinline_call([] { LOG_CRITICAL(Debug, "Assertion Failed!"); }); \ } \ while (0) #define ASSERT_MSG(_a_, ...) \ do \ if (!(_a_)) { \ - assert_noinline_call( \ - [&] { NGLOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \ + assert_noinline_call([&] { LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \ } \ while (0) diff --git a/src/common/common_paths.h b/src/common/common_paths.h index 0a6132dab..d03fca314 100644 --- a/src/common/common_paths.h +++ b/src/common/common_paths.h @@ -32,12 +32,15 @@ #define SDMC_DIR "sdmc" #define NAND_DIR "nand" #define SYSDATA_DIR "sysdata" +#define LOG_DIR "log" // Filenames // Files in the directory returned by GetUserPath(D_CONFIG_IDX) #define EMU_CONFIG "emu.ini" #define DEBUGGER_CONFIG "debugger.ini" #define LOGGER_CONFIG "logger.ini" +// Files in the directory returned by GetUserPath(D_LOGS_IDX) +#define LOG_FILE "citra_log.txt" // Sys files #define SHARED_FONT "shared_font.bin" diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 2d0b81c6e..b9e1fd1f6 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -118,7 +118,7 @@ bool IsDirectory(const std::string& filename) { #endif if (result < 0) { - NGLOG_DEBUG(Common_Filesystem, "stat failed on {}: {}", filename, GetLastErrorMsg()); + LOG_DEBUG(Common_Filesystem, "stat failed on {}: {}", filename, GetLastErrorMsg()); return false; } @@ -128,29 +128,29 @@ bool IsDirectory(const std::string& filename) { // Deletes a given filename, return true on success // Doesn't supports deleting a directory bool Delete(const std::string& filename) { - NGLOG_TRACE(Common_Filesystem, "file {}", filename); + LOG_TRACE(Common_Filesystem, "file {}", filename); // Return true because we care about the file no // being there, not the actual delete. if (!Exists(filename)) { - NGLOG_DEBUG(Common_Filesystem, "{} does not exist", filename); + LOG_DEBUG(Common_Filesystem, "{} does not exist", filename); return true; } // We can't delete a directory if (IsDirectory(filename)) { - NGLOG_ERROR(Common_Filesystem, "Failed: {} is a directory", filename); + LOG_ERROR(Common_Filesystem, "Failed: {} is a directory", filename); return false; } #ifdef _WIN32 if (!DeleteFileW(Common::UTF8ToUTF16W(filename).c_str())) { - NGLOG_ERROR(Common_Filesystem, "DeleteFile failed on {}: {}", filename, GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "DeleteFile failed on {}: {}", filename, GetLastErrorMsg()); return false; } #else if (unlink(filename.c_str()) == -1) { - NGLOG_ERROR(Common_Filesystem, "unlink failed on {}: {}", filename, GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "unlink failed on {}: {}", filename, GetLastErrorMsg()); return false; } #endif @@ -160,16 +160,16 @@ bool Delete(const std::string& filename) { // Returns true if successful, or path already exists. bool CreateDir(const std::string& path) { - NGLOG_TRACE(Common_Filesystem, "directory {}", path); + LOG_TRACE(Common_Filesystem, "directory {}", path); #ifdef _WIN32 if (::CreateDirectoryW(Common::UTF8ToUTF16W(path).c_str(), nullptr)) return true; DWORD error = GetLastError(); if (error == ERROR_ALREADY_EXISTS) { - NGLOG_DEBUG(Common_Filesystem, "CreateDirectory failed on {}: already exists", path); + LOG_DEBUG(Common_Filesystem, "CreateDirectory failed on {}: already exists", path); return true; } - NGLOG_ERROR(Common_Filesystem, "CreateDirectory failed on {}: {}", path, error); + LOG_ERROR(Common_Filesystem, "CreateDirectory failed on {}: {}", path, error); return false; #else if (mkdir(path.c_str(), 0755) == 0) @@ -178,11 +178,11 @@ bool CreateDir(const std::string& path) { int err = errno; if (err == EEXIST) { - NGLOG_DEBUG(Common_Filesystem, "mkdir failed on {}: already exists", path); + LOG_DEBUG(Common_Filesystem, "mkdir failed on {}: already exists", path); return true; } - NGLOG_ERROR(Common_Filesystem, "mkdir failed on {}: {}", path, strerror(err)); + LOG_ERROR(Common_Filesystem, "mkdir failed on {}: {}", path, strerror(err)); return false; #endif } @@ -190,10 +190,10 @@ bool CreateDir(const std::string& path) { // Creates the full path of fullPath returns true on success bool CreateFullPath(const std::string& fullPath) { int panicCounter = 100; - NGLOG_TRACE(Common_Filesystem, "path {}", fullPath); + LOG_TRACE(Common_Filesystem, "path {}", fullPath); if (FileUtil::Exists(fullPath)) { - NGLOG_DEBUG(Common_Filesystem, "path exists {}", fullPath); + LOG_DEBUG(Common_Filesystem, "path exists {}", fullPath); return true; } @@ -209,14 +209,14 @@ bool CreateFullPath(const std::string& fullPath) { // Include the '/' so the first call is CreateDir("/") rather than CreateDir("") std::string const subPath(fullPath.substr(0, position + 1)); if (!FileUtil::IsDirectory(subPath) && !FileUtil::CreateDir(subPath)) { - NGLOG_ERROR(Common, "CreateFullPath: directory creation failed"); + LOG_ERROR(Common, "CreateFullPath: directory creation failed"); return false; } // A safety check panicCounter--; if (panicCounter <= 0) { - NGLOG_ERROR(Common, "CreateFullPath: directory structure is too deep"); + LOG_ERROR(Common, "CreateFullPath: directory structure is too deep"); return false; } position++; @@ -225,11 +225,11 @@ bool CreateFullPath(const std::string& fullPath) { // Deletes a directory filename, returns true on success bool DeleteDir(const std::string& filename) { - NGLOG_TRACE(Common_Filesystem, "directory {}", filename); + LOG_TRACE(Common_Filesystem, "directory {}", filename); // check if a directory if (!FileUtil::IsDirectory(filename)) { - NGLOG_ERROR(Common_Filesystem, "Not a directory {}", filename); + LOG_ERROR(Common_Filesystem, "Not a directory {}", filename); return false; } @@ -240,14 +240,14 @@ bool DeleteDir(const std::string& filename) { if (rmdir(filename.c_str()) == 0) return true; #endif - NGLOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg()); return false; } // renames file srcFilename to destFilename, returns true on success bool Rename(const std::string& srcFilename, const std::string& destFilename) { - NGLOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename); + LOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename); #ifdef _WIN32 if (_wrename(Common::UTF8ToUTF16W(srcFilename).c_str(), Common::UTF8ToUTF16W(destFilename).c_str()) == 0) @@ -256,21 +256,21 @@ bool Rename(const std::string& srcFilename, const std::string& destFilename) { if (rename(srcFilename.c_str(), destFilename.c_str()) == 0) return true; #endif - NGLOG_ERROR(Common_Filesystem, "failed {} --> {}: {}", srcFilename, destFilename, - GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "failed {} --> {}: {}", srcFilename, destFilename, + GetLastErrorMsg()); return false; } // copies file srcFilename to destFilename, returns true on success bool Copy(const std::string& srcFilename, const std::string& destFilename) { - NGLOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename); + LOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename); #ifdef _WIN32 if (CopyFileW(Common::UTF8ToUTF16W(srcFilename).c_str(), Common::UTF8ToUTF16W(destFilename).c_str(), FALSE)) return true; - NGLOG_ERROR(Common_Filesystem, "failed {} --> {}: {}", srcFilename, destFilename, - GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "failed {} --> {}: {}", srcFilename, destFilename, + GetLastErrorMsg()); return false; #else @@ -282,8 +282,8 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) { // Open input file FILE* input = fopen(srcFilename.c_str(), "rb"); if (!input) { - NGLOG_ERROR(Common_Filesystem, "opening input failed {} --> {}: {}", srcFilename, - destFilename, GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "opening input failed {} --> {}: {}", srcFilename, + destFilename, GetLastErrorMsg()); return false; } @@ -291,8 +291,8 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) { FILE* output = fopen(destFilename.c_str(), "wb"); if (!output) { fclose(input); - NGLOG_ERROR(Common_Filesystem, "opening output failed {} --> {}: {}", srcFilename, - destFilename, GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "opening output failed {} --> {}: {}", srcFilename, + destFilename, GetLastErrorMsg()); return false; } @@ -302,8 +302,8 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) { size_t rnum = fread(buffer, sizeof(char), BSIZE, input); if (rnum != BSIZE) { if (ferror(input) != 0) { - NGLOG_ERROR(Common_Filesystem, "failed reading from source, {} --> {}: {}", - srcFilename, destFilename, GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "failed reading from source, {} --> {}: {}", + srcFilename, destFilename, GetLastErrorMsg()); goto bail; } } @@ -311,8 +311,8 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) { // write output size_t wnum = fwrite(buffer, sizeof(char), rnum, output); if (wnum != rnum) { - NGLOG_ERROR(Common_Filesystem, "failed writing to output, {} --> {}: {}", srcFilename, - destFilename, GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "failed writing to output, {} --> {}: {}", srcFilename, + destFilename, GetLastErrorMsg()); goto bail; } } @@ -332,12 +332,12 @@ bail: // Returns the size of filename (64bit) u64 GetSize(const std::string& filename) { if (!Exists(filename)) { - NGLOG_ERROR(Common_Filesystem, "failed {}: No such file", filename); + LOG_ERROR(Common_Filesystem, "failed {}: No such file", filename); return 0; } if (IsDirectory(filename)) { - NGLOG_ERROR(Common_Filesystem, "failed {}: is a directory", filename); + LOG_ERROR(Common_Filesystem, "failed {}: is a directory", filename); return 0; } @@ -348,11 +348,11 @@ u64 GetSize(const std::string& filename) { if (stat(filename.c_str(), &buf) == 0) #endif { - NGLOG_TRACE(Common_Filesystem, "{}: {}", filename, buf.st_size); + LOG_TRACE(Common_Filesystem, "{}: {}", filename, buf.st_size); return buf.st_size; } - NGLOG_ERROR(Common_Filesystem, "Stat failed {}: {}", filename, GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "Stat failed {}: {}", filename, GetLastErrorMsg()); return 0; } @@ -360,7 +360,7 @@ u64 GetSize(const std::string& filename) { u64 GetSize(const int fd) { struct stat buf; if (fstat(fd, &buf) != 0) { - NGLOG_ERROR(Common_Filesystem, "GetSize: stat failed {}: {}", fd, GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "GetSize: stat failed {}: {}", fd, GetLastErrorMsg()); return 0; } return buf.st_size; @@ -371,14 +371,12 @@ u64 GetSize(FILE* f) { // can't use off_t here because it can be 32-bit u64 pos = ftello(f); if (fseeko(f, 0, SEEK_END) != 0) { - NGLOG_ERROR(Common_Filesystem, "GetSize: seek failed {}: {}", fmt::ptr(f), - GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "GetSize: seek failed {}: {}", fmt::ptr(f), GetLastErrorMsg()); return 0; } u64 size = ftello(f); if ((size != pos) && (fseeko(f, pos, SEEK_SET) != 0)) { - NGLOG_ERROR(Common_Filesystem, "GetSize: seek failed {}: {}", fmt::ptr(f), - GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "GetSize: seek failed {}: {}", fmt::ptr(f), GetLastErrorMsg()); return 0; } return size; @@ -386,10 +384,10 @@ u64 GetSize(FILE* f) { // creates an empty file filename, returns true on success bool CreateEmptyFile(const std::string& filename) { - NGLOG_TRACE(Common_Filesystem, "{}", filename); + LOG_TRACE(Common_Filesystem, "{}", filename); if (!FileUtil::IOFile(filename, "wb")) { - NGLOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg()); return false; } @@ -398,7 +396,7 @@ bool CreateEmptyFile(const std::string& filename) { bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string& directory, DirectoryEntryCallable callback) { - NGLOG_TRACE(Common_Filesystem, "directory {}", directory); + LOG_TRACE(Common_Filesystem, "directory {}", directory); // How many files + directories we found unsigned found_entries = 0; @@ -556,7 +554,7 @@ std::string GetCurrentDir() { char* dir; if (!(dir = getcwd(nullptr, 0))) { #endif - NGLOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg()); + LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg()); return nullptr; } #ifdef _WIN32 @@ -676,7 +674,7 @@ std::string GetSysDirectory() { #endif sysDir += DIR_SEP; - NGLOG_DEBUG(Common_Filesystem, "Setting to {}:", sysDir); + LOG_DEBUG(Common_Filesystem, "Setting to {}:", sysDir); return sysDir; } @@ -692,7 +690,7 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string& new if (!FileUtil::IsDirectory(paths[D_USER_IDX])) { paths[D_USER_IDX] = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP; } else { - NGLOG_INFO(Common_Filesystem, "Using the local user directory"); + LOG_INFO(Common_Filesystem, "Using the local user directory"); } paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP; @@ -715,11 +713,13 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string& new paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP; paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP; + // TODO: Put the logs in a better location for each OS + paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOG_DIR DIR_SEP; } if (!newPath.empty()) { if (!FileUtil::IsDirectory(newPath)) { - NGLOG_ERROR(Common_Filesystem, "Invalid path specified {}", newPath); + LOG_ERROR(Common_Filesystem, "Invalid path specified {}", newPath); return paths[DirIDX]; } else { paths[DirIDX] = newPath; @@ -801,8 +801,8 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam IOFile::IOFile() {} -IOFile::IOFile(const std::string& filename, const char openmode[]) { - Open(filename, openmode); +IOFile::IOFile(const std::string& filename, const char openmode[], int flags) { + Open(filename, openmode, flags); } IOFile::~IOFile() { @@ -823,11 +823,16 @@ void IOFile::Swap(IOFile& other) noexcept { std::swap(m_good, other.m_good); } -bool IOFile::Open(const std::string& filename, const char openmode[]) { +bool IOFile::Open(const std::string& filename, const char openmode[], int flags) { Close(); #ifdef _WIN32 - _wfopen_s(&m_file, Common::UTF8ToUTF16W(filename).c_str(), - Common::UTF8ToUTF16W(openmode).c_str()); + if (flags != 0) { + m_file = _wfsopen(Common::UTF8ToUTF16W(filename).c_str(), + Common::UTF8ToUTF16W(openmode).c_str(), flags); + } else { + _wfopen_s(&m_file, Common::UTF8ToUTF16W(filename).c_str(), + Common::UTF8ToUTF16W(openmode).c_str()); + } #else m_file = fopen(filename.c_str(), openmode); #endif diff --git a/src/common/file_util.h b/src/common/file_util.h index fc6b3ea46..5bc7fbf7c 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h @@ -156,7 +156,10 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam class IOFile : public NonCopyable { public: IOFile(); - IOFile(const std::string& filename, const char openmode[]); + // flags is used for windows specific file open mode flags, which + // allows yuzu to open the logs in shared write mode, so that the file + // isn't considered "locked" while yuzu is open and people can open the log file and view it + IOFile(const std::string& filename, const char openmode[], int flags = 0); ~IOFile(); @@ -165,7 +168,7 @@ public: void Swap(IOFile& other) noexcept; - bool Open(const std::string& filename, const char openmode[]); + bool Open(const std::string& filename, const char openmode[], int flags = 0); bool Close(); template <typename T> @@ -220,6 +223,10 @@ public: return WriteArray(&object, 1); } + size_t WriteString(const std::string& str) { + return WriteArray(str.c_str(), str.length()); + } + bool IsOpen() const { return nullptr != m_file; } diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index c26b20062..242914c6a 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -2,16 +2,145 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <utility> +#include <algorithm> +#include <array> +#include <chrono> +#include <condition_variable> +#include <memory> +#include <thread> +#ifdef _WIN32 +#include <share.h> // For _SH_DENYWR +#else +#define _SH_DENYWR 0 +#endif #include "common/assert.h" +#include "common/common_funcs.h" // snprintf compatibility define #include "common/logging/backend.h" -#include "common/logging/filter.h" #include "common/logging/log.h" #include "common/logging/text_formatter.h" #include "common/string_util.h" +#include "common/threadsafe_queue.h" namespace Log { +/** + * Static state as a singleton. + */ +class Impl { +public: + static Impl& Instance() { + static Impl backend; + return backend; + } + + Impl(Impl const&) = delete; + const Impl& operator=(Impl const&) = delete; + + void PushEntry(Entry e) { + std::lock_guard<std::mutex> lock(message_mutex); + message_queue.Push(std::move(e)); + message_cv.notify_one(); + } + + void AddBackend(std::unique_ptr<Backend> backend) { + std::lock_guard<std::mutex> lock(writing_mutex); + backends.push_back(std::move(backend)); + } + + void RemoveBackend(const std::string& backend_name) { + std::lock_guard<std::mutex> lock(writing_mutex); + auto it = std::remove_if(backends.begin(), backends.end(), [&backend_name](const auto& i) { + return !strcmp(i->GetName(), backend_name.c_str()); + }); + backends.erase(it, backends.end()); + } + + const Filter& GetGlobalFilter() const { + return filter; + } + + void SetGlobalFilter(const Filter& f) { + filter = f; + } + + Backend* GetBackend(const std::string& backend_name) { + auto it = std::find_if(backends.begin(), backends.end(), [&backend_name](const auto& i) { + return !strcmp(i->GetName(), backend_name.c_str()); + }); + if (it == backends.end()) + return nullptr; + return it->get(); + } + +private: + Impl() { + backend_thread = std::thread([&] { + Entry entry; + auto write_logs = [&](Entry& e) { + std::lock_guard<std::mutex> lock(writing_mutex); + for (const auto& backend : backends) { + backend->Write(e); + } + }; + while (true) { + std::unique_lock<std::mutex> lock(message_mutex); + message_cv.wait(lock, [&] { return !running || message_queue.Pop(entry); }); + if (!running) { + break; + } + write_logs(entry); + } + // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a case + // where a system is repeatedly spamming logs even on close. + constexpr int MAX_LOGS_TO_WRITE = 100; + int logs_written = 0; + while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) { + write_logs(entry); + } + }); + } + + ~Impl() { + running = false; + message_cv.notify_one(); + backend_thread.join(); + } + + std::atomic_bool running{true}; + std::mutex message_mutex, writing_mutex; + std::condition_variable message_cv; + std::thread backend_thread; + std::vector<std::unique_ptr<Backend>> backends; + Common::MPSCQueue<Log::Entry> message_queue; + Filter filter; +}; + +void ConsoleBackend::Write(const Entry& entry) { + PrintMessage(entry); +} + +void ColorConsoleBackend::Write(const Entry& entry) { + PrintColoredMessage(entry); +} + +// _SH_DENYWR allows read only access to the file for other programs. +// It is #defined to 0 on other platforms +FileBackend::FileBackend(const std::string& filename) + : file(filename, "w", _SH_DENYWR), bytes_written(0) {} + +void FileBackend::Write(const Entry& entry) { + // prevent logs from going over the maximum size (in case its spamming and the user doesn't + // know) + constexpr size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L; + if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) { + return; + } + bytes_written += file.WriteString(FormatLogMessage(entry) + '\n'); + if (entry.log_level >= Level::Error) { + file.Flush(); + } +} + /// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this. #define ALL_LOG_CLASSES() \ CLS(Log) \ @@ -125,20 +254,32 @@ Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsign return entry; } -static Filter* filter = nullptr; +void SetGlobalFilter(const Filter& filter) { + Impl::Instance().SetGlobalFilter(filter); +} + +void AddBackend(std::unique_ptr<Backend> backend) { + Impl::Instance().AddBackend(std::move(backend)); +} -void SetFilter(Filter* new_filter) { - filter = new_filter; +void RemoveBackend(const std::string& backend_name) { + Impl::Instance().RemoveBackend(backend_name); +} + +Backend* GetBackend(const std::string& backend_name) { + return Impl::Instance().GetBackend(backend_name); } void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, unsigned int line_num, const char* function, const char* format, const fmt::format_args& args) { - if (filter && !filter->CheckMessage(log_class, log_level)) + auto filter = Impl::Instance().GetGlobalFilter(); + if (!filter.CheckMessage(log_class, log_level)) return; + Entry entry = CreateEntry(log_class, log_level, filename, line_num, function, fmt::vformat(format, args)); - PrintColoredMessage(entry); + Impl::Instance().PushEntry(std::move(entry)); } -} // namespace Log +} // namespace Log
\ No newline at end of file diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h index 7e81efb23..57cdf6b2d 100644 --- a/src/common/logging/backend.h +++ b/src/common/logging/backend.h @@ -1,13 +1,15 @@ // Copyright 2014 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. - #pragma once #include <chrono> #include <cstdarg> +#include <memory> #include <string> #include <utility> +#include "common/file_util.h" +#include "common/logging/filter.h" #include "common/logging/log.h" namespace Log { @@ -35,6 +37,80 @@ struct Entry { }; /** + * Interface for logging backends. As loggers can be created and removed at runtime, this can be + * used by a frontend for adding a custom logging backend as needed + */ +class Backend { +public: + virtual ~Backend() = default; + virtual void SetFilter(const Filter& new_filter) { + filter = new_filter; + } + virtual const char* GetName() const = 0; + virtual void Write(const Entry& entry) = 0; + +private: + Filter filter; +}; + +/** + * Backend that writes to stderr without any color commands + */ +class ConsoleBackend : public Backend { +public: + static const char* Name() { + return "console"; + } + const char* GetName() const override { + return Name(); + } + void Write(const Entry& entry) override; +}; + +/** + * Backend that writes to stderr and with color + */ +class ColorConsoleBackend : public Backend { +public: + static const char* Name() { + return "color_console"; + } + + const char* GetName() const override { + return Name(); + } + void Write(const Entry& entry) override; +}; + +/** + * Backend that writes to a file passed into the constructor + */ +class FileBackend : public Backend { +public: + explicit FileBackend(const std::string& filename); + + static const char* Name() { + return "file"; + } + + const char* GetName() const override { + return Name(); + } + + void Write(const Entry& entry) override; + +private: + FileUtil::IOFile file; + size_t bytes_written; +}; + +void AddBackend(std::unique_ptr<Backend> backend); + +void RemoveBackend(const std::string& backend_name); + +Backend* GetBackend(const std::string& backend_name); + +/** * Returns the name of the passed log class as a C-string. Subclasses are separated by periods * instead of underscores as in the enumeration. */ @@ -49,5 +125,10 @@ const char* GetLevelName(Level log_level); Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, const char* function, std::string message); -void SetFilter(Filter* filter); -} // namespace Log +/** + * The global filter will prevent any messages from even being processed if they are filtered. Each + * backend can have a filter, but if the level is lower than the global filter, the backend will + * never get the message + */ +void SetGlobalFilter(const Filter& filter); +} // namespace Log
\ No newline at end of file diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 428723dce..4e783a577 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -65,14 +65,14 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin, const std::string::const_iterator end) { auto level_separator = std::find(begin, end, ':'); if (level_separator == end) { - NGLOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: %s", - std::string(begin, end).c_str()); + LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: {}", + std::string(begin, end)); return false; } const Level level = GetLevelByName(level_separator + 1, end); if (level == Level::Count) { - NGLOG_ERROR(Log, "Unknown log level in filter: %s", std::string(begin, end).c_str()); + LOG_ERROR(Log, "Unknown log level in filter: {}", std::string(begin, end)); return false; } @@ -83,7 +83,7 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin, const Class log_class = GetClassByName(begin, level_separator); if (log_class == Class::Count) { - NGLOG_ERROR(Log, "Unknown log class in filter: %s", std::string(begin, end).c_str()); + LOG_ERROR(Log, "Unknown log class in filter: {}", std::string(begin, end)); return false; } diff --git a/src/common/logging/log.h b/src/common/logging/log.h index c5015531c..e96c90e16 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -109,25 +109,25 @@ void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsig } // namespace Log #ifdef _DEBUG -#define NGLOG_TRACE(log_class, ...) \ +#define LOG_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)) +#define LOG_TRACE(log_class, fmt, ...) (void(0)) #endif -#define NGLOG_DEBUG(log_class, ...) \ +#define LOG_DEBUG(log_class, ...) \ ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Debug, __FILE__, __LINE__, \ __func__, __VA_ARGS__) -#define NGLOG_INFO(log_class, ...) \ +#define LOG_INFO(log_class, ...) \ ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Info, __FILE__, __LINE__, \ __func__, __VA_ARGS__) -#define NGLOG_WARNING(log_class, ...) \ +#define LOG_WARNING(log_class, ...) \ ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Warning, __FILE__, __LINE__, \ __func__, __VA_ARGS__) -#define NGLOG_ERROR(log_class, ...) \ +#define LOG_ERROR(log_class, ...) \ ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Error, __FILE__, __LINE__, \ __func__, __VA_ARGS__) -#define NGLOG_CRITICAL(log_class, ...) \ +#define LOG_CRITICAL(log_class, ...) \ ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Critical, __FILE__, __LINE__, \ __func__, __VA_ARGS__) diff --git a/src/common/memory_util.cpp b/src/common/memory_util.cpp index 4d1ec8fb9..5d89209ed 100644 --- a/src/common/memory_util.cpp +++ b/src/common/memory_util.cpp @@ -55,7 +55,7 @@ void* AllocateExecutableMemory(size_t size, bool low) { if (ptr == MAP_FAILED) { ptr = nullptr; #endif - NGLOG_ERROR(Common_Memory, "Failed to allocate executable memory"); + LOG_ERROR(Common_Memory, "Failed to allocate executable memory"); } #if !defined(_WIN32) && defined(ARCHITECTURE_X64) && !defined(MAP_32BIT) else { @@ -68,7 +68,7 @@ void* AllocateExecutableMemory(size_t size, bool low) { #if EMU_ARCH_BITS == 64 if ((u64)ptr >= 0x80000000 && low == true) - NGLOG_ERROR(Common_Memory, "Executable memory ended up above 2GB!"); + LOG_ERROR(Common_Memory, "Executable memory ended up above 2GB!"); #endif return ptr; @@ -85,7 +85,7 @@ void* AllocateMemoryPages(size_t size) { #endif if (ptr == nullptr) - NGLOG_ERROR(Common_Memory, "Failed to allocate raw memory"); + LOG_ERROR(Common_Memory, "Failed to allocate raw memory"); return ptr; } @@ -99,12 +99,12 @@ void* AllocateAlignedMemory(size_t size, size_t alignment) { ptr = memalign(alignment, size); #else if (posix_memalign(&ptr, alignment, size) != 0) - NGLOG_ERROR(Common_Memory, "Failed to allocate aligned memory"); + LOG_ERROR(Common_Memory, "Failed to allocate aligned memory"); #endif #endif if (ptr == nullptr) - NGLOG_ERROR(Common_Memory, "Failed to allocate aligned memory"); + LOG_ERROR(Common_Memory, "Failed to allocate aligned memory"); return ptr; } @@ -113,7 +113,7 @@ void FreeMemoryPages(void* ptr, size_t size) { if (ptr) { #ifdef _WIN32 if (!VirtualFree(ptr, 0, MEM_RELEASE)) - NGLOG_ERROR(Common_Memory, "FreeMemoryPages failed!\n{}", GetLastErrorMsg()); + LOG_ERROR(Common_Memory, "FreeMemoryPages failed!\n{}", GetLastErrorMsg()); #else munmap(ptr, size); #endif @@ -134,7 +134,7 @@ void WriteProtectMemory(void* ptr, size_t size, bool allowExecute) { #ifdef _WIN32 DWORD oldValue; if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue)) - NGLOG_ERROR(Common_Memory, "WriteProtectMemory failed!\n{}", GetLastErrorMsg()); + LOG_ERROR(Common_Memory, "WriteProtectMemory failed!\n{}", GetLastErrorMsg()); #else mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ); #endif @@ -145,7 +145,7 @@ void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute) { DWORD oldValue; if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, &oldValue)) - NGLOG_ERROR(Common_Memory, "UnWriteProtectMemory failed!\n{}", GetLastErrorMsg()); + LOG_ERROR(Common_Memory, "UnWriteProtectMemory failed!\n{}", GetLastErrorMsg()); #else mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ); diff --git a/src/common/param_package.cpp b/src/common/param_package.cpp index ab0154133..e0df430ab 100644 --- a/src/common/param_package.cpp +++ b/src/common/param_package.cpp @@ -25,7 +25,7 @@ ParamPackage::ParamPackage(const std::string& serialized) { std::vector<std::string> key_value; Common::SplitString(pair, KEY_VALUE_SEPARATOR, key_value); if (key_value.size() != 2) { - NGLOG_ERROR(Common, "invalid key pair {}", pair); + LOG_ERROR(Common, "invalid key pair {}", pair); continue; } @@ -64,7 +64,7 @@ std::string ParamPackage::Serialize() const { std::string ParamPackage::Get(const std::string& key, const std::string& default_value) const { auto pair = data.find(key); if (pair == data.end()) { - NGLOG_DEBUG(Common, "key '{}' not found", key); + LOG_DEBUG(Common, "key '{}' not found", key); return default_value; } @@ -74,14 +74,14 @@ std::string ParamPackage::Get(const std::string& key, const std::string& default int ParamPackage::Get(const std::string& key, int default_value) const { auto pair = data.find(key); if (pair == data.end()) { - NGLOG_DEBUG(Common, "key '{}' not found", key); + LOG_DEBUG(Common, "key '{}' not found", key); return default_value; } try { return std::stoi(pair->second); } catch (const std::logic_error&) { - NGLOG_ERROR(Common, "failed to convert {} to int", pair->second); + LOG_ERROR(Common, "failed to convert {} to int", pair->second); return default_value; } } @@ -89,14 +89,14 @@ int ParamPackage::Get(const std::string& key, int default_value) const { float ParamPackage::Get(const std::string& key, float default_value) const { auto pair = data.find(key); if (pair == data.end()) { - NGLOG_DEBUG(Common, "key {} not found", key); + LOG_DEBUG(Common, "key {} not found", key); return default_value; } try { return std::stof(pair->second); } catch (const std::logic_error&) { - NGLOG_ERROR(Common, "failed to convert {} to float", pair->second); + LOG_ERROR(Common, "failed to convert {} to float", pair->second); return default_value; } } diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 646400db0..ea9d8f77c 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp @@ -281,7 +281,7 @@ static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>& iconv_t const conv_desc = iconv_open("UTF-8", fromcode); if ((iconv_t)(-1) == conv_desc) { - NGLOG_ERROR(Common, "Iconv initialization failure [{}]: {}", fromcode, strerror(errno)); + LOG_ERROR(Common, "Iconv initialization failure [{}]: {}", fromcode, strerror(errno)); iconv_close(conv_desc); return {}; } @@ -310,7 +310,7 @@ static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>& ++src_buffer; } } else { - NGLOG_ERROR(Common, "iconv failure [{}]: {}", fromcode, strerror(errno)); + LOG_ERROR(Common, "iconv failure [{}]: {}", fromcode, strerror(errno)); break; } } @@ -329,7 +329,7 @@ std::u16string UTF8ToUTF16(const std::string& input) { iconv_t const conv_desc = iconv_open("UTF-16LE", "UTF-8"); if ((iconv_t)(-1) == conv_desc) { - NGLOG_ERROR(Common, "Iconv initialization failure [UTF-8]: {}", strerror(errno)); + LOG_ERROR(Common, "Iconv initialization failure [UTF-8]: {}", strerror(errno)); iconv_close(conv_desc); return {}; } @@ -358,7 +358,7 @@ std::u16string UTF8ToUTF16(const std::string& input) { ++src_buffer; } } else { - NGLOG_ERROR(Common, "iconv failure [UTF-8]: {}", strerror(errno)); + LOG_ERROR(Common, "iconv failure [UTF-8]: {}", strerror(errno)); break; } } |