Logging: Remove customizable logging backends

Separate code for customizing logging backends from this branch
This commit is contained in:
Daniel Lim Wee Soong 2018-03-16 11:56:40 +08:00
parent eee388588e
commit 373efd3158
5 changed files with 12 additions and 208 deletions

View file

@ -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;

View file

@ -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();
} }

View file

@ -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();

View file

@ -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

View file

@ -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