diff options
Diffstat (limited to 'src/common/logging')
| -rw-r--r-- | src/common/logging/backend.cpp | 352 | ||||
| -rw-r--r-- | src/common/logging/backend.h | 113 | ||||
| -rw-r--r-- | src/common/logging/filter.cpp | 1 | ||||
| -rw-r--r-- | src/common/logging/types.h | 1 | 
4 files changed, 222 insertions, 245 deletions
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 61dddab3f..e40d117d6 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -2,13 +2,10 @@  // Licensed under GPLv2 or any later version  // Refer to the license.txt file included. -#include <algorithm>  #include <atomic>  #include <chrono>  #include <climits> -#include <condition_variable> -#include <memory> -#include <mutex> +#include <exception>  #include <thread>  #include <vector> @@ -16,104 +13,230 @@  #include <windows.h> // For OutputDebugStringW  #endif -#include "common/assert.h"  #include "common/fs/file.h"  #include "common/fs/fs.h" +#include "common/fs/fs_paths.h" +#include "common/fs/path_util.h"  #include "common/literals.h" +#include "common/thread.h"  #include "common/logging/backend.h"  #include "common/logging/log.h"  #include "common/logging/text_formatter.h"  #include "common/settings.h" +#ifdef _WIN32  #include "common/string_util.h" +#endif  #include "common/threadsafe_queue.h"  namespace Common::Log { +namespace { +  /** - * Static state as a singleton. + * Interface for logging backends.   */ -class Impl { +class Backend {  public: -    static Impl& Instance() { -        static Impl backend; -        return backend; +    virtual ~Backend() = default; + +    virtual void Write(const Entry& entry) = 0; + +    virtual void EnableForStacktrace() = 0; + +    virtual void Flush() = 0; +}; + +/** + * Backend that writes to stderr and with color + */ +class ColorConsoleBackend final : public Backend { +public: +    explicit ColorConsoleBackend() = default; + +    ~ColorConsoleBackend() override = default; + +    void Write(const Entry& entry) override { +        if (enabled.load(std::memory_order_relaxed)) { +            PrintColoredMessage(entry); +        }      } -    Impl(const Impl&) = delete; -    Impl& operator=(const Impl&) = delete; +    void Flush() override { +        // stderr shouldn't be buffered +    } -    Impl(Impl&&) = delete; -    Impl& operator=(Impl&&) = delete; +    void EnableForStacktrace() override { +        enabled = true; +    } -    void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num, -                   const char* function, std::string message) { -        message_queue.Push( -            CreateEntry(log_class, log_level, filename, line_num, function, std::move(message))); +    void SetEnabled(bool enabled_) { +        enabled = enabled_; +    } + +private: +    std::atomic_bool enabled{false}; +}; + +/** + * Backend that writes to a file passed into the constructor + */ +class FileBackend final : public Backend { +public: +    explicit FileBackend(const std::filesystem::path& filename) { +        auto old_filename = filename; +        old_filename += ".old.txt"; + +        // Existence checks are done within the functions themselves. +        // We don't particularly care if these succeed or not. +        static_cast<void>(FS::RemoveFile(old_filename)); +        static_cast<void>(FS::RenameFile(filename, old_filename)); + +        file = std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write, +                                            FS::FileType::TextFile); +    } + +    ~FileBackend() override = default; + +    void Write(const Entry& entry) override { +        if (!enabled) { +            return; +        } + +        bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n')); + +        using namespace Common::Literals; +        // Prevent logs from exceeding a set maximum size in the event that log entries are spammed. +        const auto write_limit = Settings::values.extended_logging ? 1_GiB : 100_MiB; +        const bool write_limit_exceeded = bytes_written > write_limit; +        if (entry.log_level >= Level::Error || write_limit_exceeded) { +            if (write_limit_exceeded) { +                // Stop writing after the write limit is exceeded. +                // Don't close the file so we can print a stacktrace if necessary +                enabled = false; +            } +            file->Flush(); +        } +    } + +    void Flush() override { +        file->Flush(); +    } + +    void EnableForStacktrace() override { +        enabled = true; +        bytes_written = 0;      } -    void AddBackend(std::unique_ptr<Backend> backend) { -        std::lock_guard lock{writing_mutex}; -        backends.push_back(std::move(backend)); +private: +    std::unique_ptr<FS::IOFile> file; +    bool enabled = true; +    std::size_t bytes_written = 0; +}; + +/** + * Backend that writes to Visual Studio's output window + */ +class DebuggerBackend final : public Backend { +public: +    explicit DebuggerBackend() = default; + +    ~DebuggerBackend() override = default; + +    void Write(const Entry& entry) override { +#ifdef _WIN32 +        ::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str()); +#endif      } -    void RemoveBackend(std::string_view backend_name) { -        std::lock_guard lock{writing_mutex}; +    void Flush() override {} + +    void EnableForStacktrace() override {} +}; + +bool initialization_in_progress_suppress_logging = true; -        std::erase_if(backends, [&backend_name](const auto& backend) { -            return backend_name == backend->GetName(); -        }); +/** + * Static state as a singleton. + */ +class Impl { +public: +    static Impl& Instance() { +        if (!instance) { +            throw std::runtime_error("Using Logging instance before its initialization"); +        } +        return *instance;      } -    const Filter& GetGlobalFilter() const { -        return filter; +    static void Initialize() { +        if (instance) { +            LOG_WARNING(Log, "Reinitializing logging backend"); +            return; +        } +        using namespace Common::FS; +        const auto& log_dir = GetYuzuPath(YuzuPath::LogDir); +        void(CreateDir(log_dir)); +        Filter filter; +        filter.ParseFilterString(Settings::values.log_filter.GetValue()); +        instance = std::unique_ptr<Impl, decltype(&Deleter)>(new Impl(log_dir / LOG_FILE, filter), +                                                             Deleter); +        initialization_in_progress_suppress_logging = false;      } +    Impl(const Impl&) = delete; +    Impl& operator=(const Impl&) = delete; + +    Impl(Impl&&) = delete; +    Impl& operator=(Impl&&) = delete; +      void SetGlobalFilter(const Filter& f) {          filter = f;      } -    Backend* GetBackend(std::string_view backend_name) { -        const auto it = -            std::find_if(backends.begin(), backends.end(), -                         [&backend_name](const auto& i) { return backend_name == i->GetName(); }); -        if (it == backends.end()) -            return nullptr; -        return it->get(); +    void SetColorConsoleBackendEnabled(bool enabled) { +        color_console_backend.SetEnabled(enabled); +    } + +    void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num, +                   const char* function, std::string message) { +        if (!filter.CheckMessage(log_class, log_level)) +            return; +        const Entry& entry = +            CreateEntry(log_class, log_level, filename, line_num, function, std::move(message)); +        message_queue.Push(entry);      }  private: -    Impl() { -        backend_thread = std::thread([&] { -            Entry entry; -            auto write_logs = [&](Entry& e) { -                std::lock_guard lock{writing_mutex}; -                for (const auto& backend : backends) { -                    backend->Write(e); -                } -            }; -            while (true) { -                entry = message_queue.PopWait(); -                if (entry.final_entry) { -                    break; -                } -                write_logs(entry); -            } +    Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_) +        : filter{filter_}, file_backend{file_backend_filename}, backend_thread{std::thread([this] { +              Common::SetCurrentThreadName("yuzu:Log"); +              Entry entry; +              const auto write_logs = [this, &entry]() { +                  ForEachBackend([&entry](Backend& backend) { backend.Write(entry); }); +              }; +              while (true) { +                  entry = message_queue.PopWait(); +                  if (entry.final_entry) { +                      break; +                  } +                  write_logs(); +              } +              // 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. +              int max_logs_to_write = filter.IsDebug() ? INT_MAX : 100; +              while (max_logs_to_write-- && message_queue.Pop(entry)) { +                  write_logs(); +              } +          })} {} -            // 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. -            const int MAX_LOGS_TO_WRITE = filter.IsDebug() ? INT_MAX : 100; -            int logs_written = 0; -            while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) { -                write_logs(entry); -            } -        }); +    ~Impl() { +        StopBackendThread();      } -    ~Impl() { -        Entry entry; -        entry.final_entry = true; -        message_queue.Push(entry); +    void StopBackendThread() { +        Entry stop_entry{}; +        stop_entry.final_entry = true; +        message_queue.Push(stop_entry);          backend_thread.join();      } @@ -135,100 +258,51 @@ private:          };      } -    std::mutex writing_mutex; -    std::thread backend_thread; -    std::vector<std::unique_ptr<Backend>> backends; -    MPSCQueue<Entry> message_queue; -    Filter filter; -    std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; -}; - -ConsoleBackend::~ConsoleBackend() = default; - -void ConsoleBackend::Write(const Entry& entry) { -    PrintMessage(entry); -} - -ColorConsoleBackend::~ColorConsoleBackend() = default; - -void ColorConsoleBackend::Write(const Entry& entry) { -    PrintColoredMessage(entry); -} - -FileBackend::FileBackend(const std::filesystem::path& filename) { -    auto old_filename = filename; -    old_filename += ".old.txt"; - -    // Existence checks are done within the functions themselves. -    // We don't particularly care if these succeed or not. -    FS::RemoveFile(old_filename); -    void(FS::RenameFile(filename, old_filename)); - -    file = -        std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write, FS::FileType::TextFile); -} - -FileBackend::~FileBackend() = default; +    void ForEachBackend(auto lambda) { +        lambda(static_cast<Backend&>(debugger_backend)); +        lambda(static_cast<Backend&>(color_console_backend)); +        lambda(static_cast<Backend&>(file_backend)); +    } -void FileBackend::Write(const Entry& entry) { -    if (!file->IsOpen()) { -        return; +    static void Deleter(Impl* ptr) { +        delete ptr;      } -    using namespace Common::Literals; -    // Prevent logs from exceeding a set maximum size in the event that log entries are spammed. -    constexpr std::size_t MAX_BYTES_WRITTEN = 100_MiB; -    constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1_GiB; +    static inline std::unique_ptr<Impl, decltype(&Deleter)> instance{nullptr, Deleter}; -    const bool write_limit_exceeded = -        bytes_written > MAX_BYTES_WRITTEN_EXTENDED || -        (bytes_written > MAX_BYTES_WRITTEN && !Settings::values.extended_logging); +    Filter filter; +    DebuggerBackend debugger_backend{}; +    ColorConsoleBackend color_console_backend{}; +    FileBackend file_backend; -    // Close the file after the write limit is exceeded. -    if (write_limit_exceeded) { -        file->Close(); -        return; -    } +    std::thread backend_thread; +    MPSCQueue<Entry> message_queue{}; +    std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; +}; +} // namespace -    bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n')); -    if (entry.log_level >= Level::Error) { -        file->Flush(); -    } +void Initialize() { +    Impl::Initialize();  } -DebuggerBackend::~DebuggerBackend() = default; - -void DebuggerBackend::Write(const Entry& entry) { -#ifdef _WIN32 -    ::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str()); -#endif +void DisableLoggingInTests() { +    initialization_in_progress_suppress_logging = true;  }  void SetGlobalFilter(const Filter& filter) {      Impl::Instance().SetGlobalFilter(filter);  } -void AddBackend(std::unique_ptr<Backend> backend) { -    Impl::Instance().AddBackend(std::move(backend)); -} - -void RemoveBackend(std::string_view backend_name) { -    Impl::Instance().RemoveBackend(backend_name); -} - -Backend* GetBackend(std::string_view backend_name) { -    return Impl::Instance().GetBackend(backend_name); +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, const char* format,                         const fmt::format_args& args) { -    auto& instance = Impl::Instance(); -    const auto& filter = instance.GetGlobalFilter(); -    if (!filter.CheckMessage(log_class, log_level)) -        return; - -    instance.PushEntry(log_class, log_level, filename, line_num, function, -                       fmt::vformat(format, args)); +    if (!initialization_in_progress_suppress_logging) { +        Impl::Instance().PushEntry(log_class, log_level, filename, line_num, function, +                                   fmt::vformat(format, args)); +    }  }  } // namespace Common::Log diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h index 4b9a910c1..cb7839ee9 100644 --- a/src/common/logging/backend.h +++ b/src/common/logging/backend.h @@ -5,120 +5,21 @@  #pragma once  #include <filesystem> -#include <memory> -#include <string> -#include <string_view>  #include "common/logging/filter.h" -#include "common/logging/log.h" - -namespace Common::FS { -class IOFile; -}  namespace Common::Log {  class Filter; -/** - * 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: -    ~ConsoleBackend() override; - -    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: -    ~ColorConsoleBackend() override; - -    static const char* Name() { -        return "color_console"; -    } - -    const char* GetName() const override { -        return Name(); -    } -    void Write(const Entry& entry) override; -}; +/// Initializes the logging system. This should be the first thing called in main. +void Initialize(); -/** - * Backend that writes to a file passed into the constructor - */ -class FileBackend : public Backend { -public: -    explicit FileBackend(const std::filesystem::path& filename); -    ~FileBackend() override; - -    static const char* Name() { -        return "file"; -    } - -    const char* GetName() const override { -        return Name(); -    } - -    void Write(const Entry& entry) override; - -private: -    std::unique_ptr<FS::IOFile> file; -    std::size_t bytes_written = 0; -}; - -/** - * Backend that writes to Visual Studio's output window - */ -class DebuggerBackend : public Backend { -public: -    ~DebuggerBackend() override; - -    static const char* Name() { -        return "debugger"; -    } -    const char* GetName() const override { -        return Name(); -    } -    void Write(const Entry& entry) override; -}; - -void AddBackend(std::unique_ptr<Backend> backend); - -void RemoveBackend(std::string_view backend_name); - -Backend* GetBackend(std::string_view backend_name); +void DisableLoggingInTests();  /** - * 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 + * The global filter will prevent any messages from even being processed if they are filtered.   */  void SetGlobalFilter(const Filter& filter); -} // namespace Common::Log
\ No newline at end of file + +void SetColorConsoleBackendEnabled(bool enabled); +} // namespace Common::Log diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index f055f0e11..42744c994 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -111,6 +111,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {      SUB(Service, NCM)                                                                              \      SUB(Service, NFC)                                                                              \      SUB(Service, NFP)                                                                              \ +    SUB(Service, NGCT)                                                                             \      SUB(Service, NIFM)                                                                             \      SUB(Service, NIM)                                                                              \      SUB(Service, NPNS)                                                                             \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index 7ad0334fc..ddf9d27ca 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -81,6 +81,7 @@ enum class Class : u8 {      Service_NCM,       ///< The NCM service      Service_NFC,       ///< The NFC (Near-field communication) service      Service_NFP,       ///< The NFP service +    Service_NGCT,      ///< The NGCT (No Good Content for Terra) service      Service_NIFM,      ///< The NIFM (Network interface) service      Service_NIM,       ///< The NIM service      Service_NPNS,      ///< The NPNS service  | 
