mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	Logging: Remove customizable logging backends
Separate code for customizing logging backends from this branch
This commit is contained in:
		
							parent
							
								
									eee388588e
								
							
						
					
					
						commit
						373efd3158
					
				
					 5 changed files with 12 additions and 208 deletions
				
			
		|  | @ -239,12 +239,7 @@ int main(int argc, char** argv) { | ||||||
| 
 | 
 | ||||||
|     Log::Filter log_filter; |     Log::Filter log_filter; | ||||||
|     log_filter.ParseFilterString(Settings::values.log_filter); |     log_filter.ParseFilterString(Settings::values.log_filter); | ||||||
|     Log::SetGlobalFilter(log_filter); |     Log::SetFilter(&log_filter); | ||||||
| 
 |  | ||||||
|     Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); |  | ||||||
|     FileUtil::CreateFullPath(FileUtil::GetUserPath(D_LOGS_IDX)); |  | ||||||
|     Log::AddBackend( |  | ||||||
|         std::make_unique<Log::FileBackend>(FileUtil::GetUserPath(D_LOGS_IDX) + LOG_FILE)); |  | ||||||
| 
 | 
 | ||||||
|     // Apply the command line arguments
 |     // Apply the command line arguments
 | ||||||
|     Settings::values.gdbstub_port = gdb_port; |     Settings::values.gdbstub_port = gdb_port; | ||||||
|  |  | ||||||
|  | @ -44,7 +44,7 @@ void ConfigureDebug::applyConfiguration() { | ||||||
|     Debugger::ToggleConsole(); |     Debugger::ToggleConsole(); | ||||||
|     Log::Filter filter; |     Log::Filter filter; | ||||||
|     filter.ParseFilterString(Settings::values.log_filter); |     filter.ParseFilterString(Settings::values.log_filter); | ||||||
|     Log::SetGlobalFilter(filter); |     Log::SetFilter(&filter); | ||||||
|     Settings::Apply(); |     Settings::Apply(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1298,8 +1298,6 @@ void GMainWindow::SyncMenuUISettings() { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| int main(int argc, char* argv[]) { | int main(int argc, char* argv[]) { | ||||||
|     Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); |  | ||||||
| 
 |  | ||||||
|     MicroProfileOnThreadCreate("Frontend"); |     MicroProfileOnThreadCreate("Frontend"); | ||||||
|     SCOPE_EXIT({ MicroProfileShutdown(); }); |     SCOPE_EXIT({ MicroProfileShutdown(); }); | ||||||
| 
 | 
 | ||||||
|  | @ -1318,10 +1316,7 @@ int main(int argc, char* argv[]) { | ||||||
|     // After settings have been loaded by GMainWindow, apply the filter
 |     // After settings have been loaded by GMainWindow, apply the filter
 | ||||||
|     Log::Filter log_filter; |     Log::Filter log_filter; | ||||||
|     log_filter.ParseFilterString(Settings::values.log_filter); |     log_filter.ParseFilterString(Settings::values.log_filter); | ||||||
|     Log::SetGlobalFilter(log_filter); |     Log::SetFilter(&log_filter); | ||||||
|     FileUtil::CreateFullPath(FileUtil::GetUserPath(D_LOGS_IDX)); |  | ||||||
|     Log::AddBackend( |  | ||||||
|         std::make_unique<Log::FileBackend>(FileUtil::GetUserPath(D_LOGS_IDX) + LOG_FILE)); |  | ||||||
| 
 | 
 | ||||||
|     main_window.show(); |     main_window.show(); | ||||||
|     return app.exec(); |     return app.exec(); | ||||||
|  |  | ||||||
|  | @ -4,118 +4,17 @@ | ||||||
| 
 | 
 | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
| #include <array> | #include <array> | ||||||
| #include <chrono> |  | ||||||
| #include <cstdio> | #include <cstdio> | ||||||
| #include <future> |  | ||||||
| #include <memory> |  | ||||||
| #include <thread> |  | ||||||
| #ifdef _WIN32 |  | ||||||
| #include <share.h> // For _SH_DENYWR
 |  | ||||||
| #else |  | ||||||
| #define _SH_DENYWR 0 |  | ||||||
| #endif |  | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| #include "common/common_funcs.h" // snprintf compatibility define
 | #include "common/common_funcs.h" // snprintf compatibility define
 | ||||||
| #include "common/logging/backend.h" | #include "common/logging/backend.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" | #include "common/string_util.h" | ||||||
| #include "common/threadsafe_queue.h" |  | ||||||
| 
 | 
 | ||||||
| namespace Log { | 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) { |  | ||||||
|         message_queue.Push(std::move(e)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void AddBackend(std::unique_ptr<Backend> backend) { |  | ||||||
|         backends.push_back(std::move(backend)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void RemoveBackend(const std::string& backend_name) { |  | ||||||
|         std::remove_if(backends.begin(), backends.end(), |  | ||||||
|                        [&backend_name](const auto& i) { return i->GetName() == backend_name; }); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     const Filter& GetGlobalFilter() const { |  | ||||||
|         return filter; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void SetGlobalFilter(const Filter& f) { |  | ||||||
|         filter = f; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     boost::optional<Backend*> GetBackend(const std::string& backend_name) { |  | ||||||
|         auto it = std::find_if(backends.begin(), backends.end(), [&backend_name](const auto& i) { |  | ||||||
|             return i->GetName() == backend_name; |  | ||||||
|         }); |  | ||||||
|         if (it == backends.end()) |  | ||||||
|             return boost::none; |  | ||||||
|         return it->get(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     Impl() { |  | ||||||
|         backend_thread = std::async(std::launch::async, [&] { |  | ||||||
|             using namespace std::chrono_literals; |  | ||||||
|             Entry entry; |  | ||||||
|             while (running) { |  | ||||||
|                 if (!message_queue.PopWait(entry)) { |  | ||||||
|                     continue; |  | ||||||
|                 } |  | ||||||
|                 for (const auto& backend : backends) { |  | ||||||
|                     backend->Write(entry); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|     ~Impl() { |  | ||||||
|         running = false; |  | ||||||
|         backend_thread.get(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     std::atomic_bool running{true}; |  | ||||||
|     std::future<void> 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) {} |  | ||||||
| 
 |  | ||||||
| void FileBackend::Write(const Entry& entry) { |  | ||||||
|     if (!file.IsOpen()) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|     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.
 | /// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
 | ||||||
| #define ALL_LOG_CLASSES()                                                                          \ | #define ALL_LOG_CLASSES()                                                                          \ | ||||||
|     CLS(Log)                                                                                       \ |     CLS(Log)                                                                                       \ | ||||||
|  | @ -233,26 +132,15 @@ Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsign | ||||||
|     return entry; |     return entry; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void SetGlobalFilter(const Filter& filter) { | static Filter* filter = nullptr; | ||||||
|     Impl::Instance().SetGlobalFilter(filter); |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| void AddBackend(std::unique_ptr<Backend> backend) { | void SetFilter(Filter* new_filter) { | ||||||
|     Impl::Instance().AddBackend(std::move(backend)); |     filter = new_filter; | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void RemoveBackend(const std::string& backend_name) { |  | ||||||
|     Impl::Instance().RemoveBackend(backend_name); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| boost::optional<Backend*> GetBackend(const std::string& backend_name) { |  | ||||||
|     return Impl::Instance().GetBackend(backend_name); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void LogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num, | 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, ...) { | ||||||
|     auto filter = Impl::Instance().GetGlobalFilter(); |     if (!filter->CheckMessage(log_class, log_level)) | ||||||
|     if (!filter.CheckMessage(log_class, log_level)) |  | ||||||
|         return; |         return; | ||||||
|     std::array<char, 4 * 1024> formatting_buffer; |     std::array<char, 4 * 1024> formatting_buffer; | ||||||
|     va_list args; |     va_list args; | ||||||
|  | @ -262,14 +150,13 @@ void LogMessage(Class log_class, Level log_level, const char* filename, unsigned | ||||||
|     Entry entry = CreateEntry(log_class, log_level, filename, line_num, function, |     Entry entry = CreateEntry(log_class, log_level, filename, line_num, function, | ||||||
|                               std::string(formatting_buffer.data())); |                               std::string(formatting_buffer.data())); | ||||||
| 
 | 
 | ||||||
|     Impl::Instance().PushEntry(std::move(entry)); |     PrintColoredMessage(entry); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void LogEntry(Entry& entry) { | void LogEntry(Entry& entry) { | ||||||
|     auto filter = Impl::Instance().GetGlobalFilter(); |     if (!filter->CheckMessage(entry.log_class, entry.log_level)) | ||||||
|     if (!filter.CheckMessage(entry.log_class, entry.log_level)) |  | ||||||
|         return; |         return; | ||||||
| 
 | 
 | ||||||
|     Impl::Instance().PushEntry(std::move(entry)); |     PrintColoredMessage(entry); | ||||||
| } | } | ||||||
| } // namespace Log
 | } // namespace Log
 | ||||||
|  |  | ||||||
|  | @ -6,81 +6,14 @@ | ||||||
| 
 | 
 | ||||||
| #include <chrono> | #include <chrono> | ||||||
| #include <cstdarg> | #include <cstdarg> | ||||||
| #include <memory> |  | ||||||
| #include <string> | #include <string> | ||||||
| #include <utility> | #include <utility> | ||||||
| #include <boost/optional.hpp> |  | ||||||
| #include "common/file_util.h" |  | ||||||
| #include "common/logging/filter.h" |  | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| 
 | 
 | ||||||
| namespace Log { | namespace Log { | ||||||
| 
 | 
 | ||||||
| class Filter; | 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: |  | ||||||
|     const char* GetName() const override { |  | ||||||
|         return "console"; |  | ||||||
|     } |  | ||||||
|     void Write(const Entry& entry) override; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * Backend that writes to stderr and with color |  | ||||||
|  */ |  | ||||||
| class ColorConsoleBackend : public Backend { |  | ||||||
| public: |  | ||||||
|     const char* GetName() const override { |  | ||||||
|         return "color_console"; |  | ||||||
|     } |  | ||||||
|     void Write(const Entry& entry) override; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * Backend that writes to a file passed into the constructor. If a log level is error or higher, it |  | ||||||
|  * will flush immediately after writing |  | ||||||
|  */ |  | ||||||
| class FileBackend : public Backend { |  | ||||||
| public: |  | ||||||
|     explicit FileBackend(const std::string& filename); |  | ||||||
| 
 |  | ||||||
|     const char* GetName() const override { |  | ||||||
|         return "file"; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void Write(const Entry& entry) override; |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     FileUtil::IOFile file; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| void AddBackend(std::unique_ptr<Backend> backend); |  | ||||||
| 
 |  | ||||||
| void RemoveBackend(const std::string& backend_name); |  | ||||||
| 
 |  | ||||||
| boost::optional<Backend*> GetBackend(const std::string& backend_name); |  | ||||||
| 
 |  | ||||||
| /**
 | /**
 | ||||||
|  * Returns the name of the passed log class as a C-string. Subclasses are separated by periods |  * Returns the name of the passed log class as a C-string. Subclasses are separated by periods | ||||||
|  * instead of underscores as in the enumeration. |  * instead of underscores as in the enumeration. | ||||||
|  | @ -92,11 +25,5 @@ const char* GetLogClassName(Class log_class); | ||||||
|  */ |  */ | ||||||
| const char* GetLevelName(Level log_level); | const char* GetLevelName(Level log_level); | ||||||
| 
 | 
 | ||||||
| /**
 | void SetFilter(Filter* filter); | ||||||
|  * 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
 | } // namespace Log
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue