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 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
|
||||||
|
|
Reference in New Issue