Logging: Remove customizable logging backends
Separate code for customizing logging backends from this branch
This commit is contained in:
parent
eee388588e
commit
373efd3158
|
@ -239,12 +239,7 @@ int main(int argc, char** argv) {
|
|||
|
||||
Log::Filter log_filter;
|
||||
log_filter.ParseFilterString(Settings::values.log_filter);
|
||||
Log::SetGlobalFilter(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));
|
||||
Log::SetFilter(&log_filter);
|
||||
|
||||
// Apply the command line arguments
|
||||
Settings::values.gdbstub_port = gdb_port;
|
||||
|
|
|
@ -44,7 +44,7 @@ void ConfigureDebug::applyConfiguration() {
|
|||
Debugger::ToggleConsole();
|
||||
Log::Filter filter;
|
||||
filter.ParseFilterString(Settings::values.log_filter);
|
||||
Log::SetGlobalFilter(filter);
|
||||
Log::SetFilter(&filter);
|
||||
Settings::Apply();
|
||||
}
|
||||
|
||||
|
|
|
@ -1298,8 +1298,6 @@ void GMainWindow::SyncMenuUISettings() {
|
|||
#endif
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
|
||||
|
||||
MicroProfileOnThreadCreate("Frontend");
|
||||
SCOPE_EXIT({ MicroProfileShutdown(); });
|
||||
|
||||
|
@ -1318,10 +1316,7 @@ int main(int argc, char* argv[]) {
|
|||
// After settings have been loaded by GMainWindow, apply the filter
|
||||
Log::Filter log_filter;
|
||||
log_filter.ParseFilterString(Settings::values.log_filter);
|
||||
Log::SetGlobalFilter(log_filter);
|
||||
FileUtil::CreateFullPath(FileUtil::GetUserPath(D_LOGS_IDX));
|
||||
Log::AddBackend(
|
||||
std::make_unique<Log::FileBackend>(FileUtil::GetUserPath(D_LOGS_IDX) + LOG_FILE));
|
||||
Log::SetFilter(&log_filter);
|
||||
|
||||
main_window.show();
|
||||
return app.exec();
|
||||
|
|
|
@ -4,118 +4,17 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#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/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) {
|
||||
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.
|
||||
#define ALL_LOG_CLASSES() \
|
||||
CLS(Log) \
|
||||
|
@ -233,26 +132,15 @@ Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsign
|
|||
return entry;
|
||||
}
|
||||
|
||||
void SetGlobalFilter(const Filter& filter) {
|
||||
Impl::Instance().SetGlobalFilter(filter);
|
||||
}
|
||||
static Filter* filter = nullptr;
|
||||
|
||||
void AddBackend(std::unique_ptr<Backend> backend) {
|
||||
Impl::Instance().AddBackend(std::move(backend));
|
||||
}
|
||||
|
||||
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 SetFilter(Filter* new_filter) {
|
||||
filter = new_filter;
|
||||
}
|
||||
|
||||
void LogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num,
|
||||
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;
|
||||
std::array<char, 4 * 1024> formatting_buffer;
|
||||
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,
|
||||
std::string(formatting_buffer.data()));
|
||||
|
||||
Impl::Instance().PushEntry(std::move(entry));
|
||||
PrintColoredMessage(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;
|
||||
|
||||
Impl::Instance().PushEntry(std::move(entry));
|
||||
PrintColoredMessage(entry);
|
||||
}
|
||||
} // namespace Log
|
||||
|
|
|
@ -6,81 +6,14 @@
|
|||
|
||||
#include <chrono>
|
||||
#include <cstdarg>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <boost/optional.hpp>
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/filter.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace 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:
|
||||
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
|
||||
* instead of underscores as in the enumeration.
|
||||
|
@ -92,11 +25,5 @@ const char* GetLogClassName(Class log_class);
|
|||
*/
|
||||
const char* GetLevelName(Level log_level);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
void SetFilter(Filter* filter);
|
||||
} // namespace Log
|
||||
|
|
Reference in New Issue