Refactoring: split logging code into individual files
This commit is contained in:
parent
24e8da4474
commit
4c0d706857
|
@ -347,8 +347,18 @@ target_link_libraries(rhubarb-lib
|
|||
|
||||
# ... rhubarb-logging
|
||||
add_library(rhubarb-logging
|
||||
src/logging/Entry.cpp
|
||||
src/logging/Entry.h
|
||||
src/logging/Formatter.h
|
||||
src/logging/formatters.cpp
|
||||
src/logging/formatters.h
|
||||
src/logging/Level.cpp
|
||||
src/logging/Level.h
|
||||
src/logging/logging.cpp
|
||||
src/logging/logging.h
|
||||
src/logging/Sink.h
|
||||
src/logging/sinks.cpp
|
||||
src/logging/sinks.h
|
||||
)
|
||||
target_include_directories(rhubarb-logging PUBLIC "src/logging")
|
||||
target_link_libraries(rhubarb-logging
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
#include "Entry.h"
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
using std::lock_guard;
|
||||
using std::unordered_map;
|
||||
using std::string;
|
||||
|
||||
namespace logging {
|
||||
|
||||
// Returns an int representing the current thread.
|
||||
// This used to be a simple thread_local variable, but Xcode doesn't support that yet
|
||||
int getThreadCounter() {
|
||||
using thread_id = std::thread::id;
|
||||
|
||||
static std::mutex counterMutex;
|
||||
lock_guard<std::mutex> lock(counterMutex);
|
||||
|
||||
static unordered_map<thread_id, int> threadCounters;
|
||||
static int lastThreadId = 0;
|
||||
thread_id threadId = std::this_thread::get_id();
|
||||
if (threadCounters.find(threadId) == threadCounters.end()) {
|
||||
threadCounters.insert({threadId, ++lastThreadId});
|
||||
}
|
||||
return threadCounters.find(threadId)->second;
|
||||
}
|
||||
|
||||
Entry::Entry(Level level, const string& message) :
|
||||
level(level),
|
||||
message(message)
|
||||
{
|
||||
time(×tamp);
|
||||
this->threadCounter = getThreadCounter();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include "Level.h"
|
||||
|
||||
namespace logging {
|
||||
|
||||
struct Entry {
|
||||
Entry(Level level, const std::string& message);
|
||||
|
||||
time_t timestamp;
|
||||
int threadCounter;
|
||||
Level level;
|
||||
std::string message;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "Entry.h"
|
||||
|
||||
namespace logging {
|
||||
|
||||
class Formatter {
|
||||
public:
|
||||
virtual ~Formatter() = default;
|
||||
virtual std::string format(const Entry& entry) = 0;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
#include "Level.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace logging {
|
||||
|
||||
LevelConverter& LevelConverter::get() {
|
||||
static LevelConverter converter;
|
||||
return converter;
|
||||
}
|
||||
|
||||
string LevelConverter::getTypeName() {
|
||||
return "Level";
|
||||
}
|
||||
|
||||
EnumConverter<Level>::member_data LevelConverter::getMemberData() {
|
||||
return member_data{
|
||||
{Level::Trace, "Trace"},
|
||||
{Level::Debug, "Debug"},
|
||||
{Level::Info, "Info"},
|
||||
{Level::Warn, "Warn"},
|
||||
{Level::Error, "Error"},
|
||||
{Level::Fatal, "Fatal"}
|
||||
};
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, Level value) {
|
||||
return LevelConverter::get().write(stream, value);
|
||||
}
|
||||
|
||||
std::istream& operator >> (std::istream& stream, Level& value) {
|
||||
return LevelConverter::get().read(stream, value);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
#include "EnumConverter.h"
|
||||
|
||||
namespace logging {
|
||||
|
||||
enum class Level {
|
||||
Trace,
|
||||
Debug,
|
||||
Info,
|
||||
Warn,
|
||||
Error,
|
||||
Fatal,
|
||||
EndSentinel
|
||||
};
|
||||
|
||||
class LevelConverter : public EnumConverter<Level> {
|
||||
public:
|
||||
static LevelConverter& get();
|
||||
protected:
|
||||
std::string getTypeName() override;
|
||||
member_data getMemberData() override;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, Level value);
|
||||
|
||||
std::istream& operator >> (std::istream& stream, Level& value);
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "Entry.h"
|
||||
|
||||
namespace logging {
|
||||
|
||||
class Sink {
|
||||
public:
|
||||
virtual ~Sink() = default;
|
||||
virtual void receive(const Entry& entry) = 0;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
#include "formatters.h"
|
||||
#include <format.h>
|
||||
#include "Entry.h"
|
||||
#include "tools.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace logging {
|
||||
|
||||
string SimpleConsoleFormatter::format(const Entry& entry) {
|
||||
return fmt::format("[{0}] {1}", entry.level, entry.message);
|
||||
}
|
||||
|
||||
string SimpleFileFormatter::format(const Entry& entry) {
|
||||
return fmt::format("[{0}] {1} {2}", formatTime(entry.timestamp, "%F %H:%M:%S"), entry.threadCounter, consoleFormatter.format(entry));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include "Formatter.h"
|
||||
|
||||
namespace logging {
|
||||
|
||||
class SimpleConsoleFormatter : public Formatter {
|
||||
public:
|
||||
std::string format(const Entry& entry) override;
|
||||
};
|
||||
|
||||
class SimpleFileFormatter : public Formatter {
|
||||
public:
|
||||
std::string format(const Entry& entry) override;
|
||||
private:
|
||||
SimpleConsoleFormatter consoleFormatter;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,130 +1,13 @@
|
|||
#include "logging.h"
|
||||
#include <tools.h>
|
||||
#include <iostream>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
#include "Entry.h"
|
||||
|
||||
using namespace logging;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using std::shared_ptr;
|
||||
using std::lock_guard;
|
||||
using std::unordered_map;
|
||||
|
||||
LevelConverter& LevelConverter::get() {
|
||||
static LevelConverter converter;
|
||||
return converter;
|
||||
}
|
||||
|
||||
string LevelConverter::getTypeName() {
|
||||
return "Level";
|
||||
}
|
||||
|
||||
EnumConverter<Level>::member_data LevelConverter::getMemberData() {
|
||||
return member_data {
|
||||
{ Level::Trace, "Trace" },
|
||||
{ Level::Debug, "Debug" },
|
||||
{ Level::Info, "Info" },
|
||||
{ Level::Warn, "Warn" },
|
||||
{ Level::Error, "Error" },
|
||||
{ Level::Fatal, "Fatal" }
|
||||
};
|
||||
}
|
||||
|
||||
std::ostream& logging::operator<<(std::ostream& stream, Level value) {
|
||||
return LevelConverter::get().write(stream, value);
|
||||
}
|
||||
|
||||
std::istream& logging::operator>>(std::istream& stream, Level& value) {
|
||||
return LevelConverter::get().read(stream, value);
|
||||
}
|
||||
|
||||
// Returns an int representing the current thread.
|
||||
// This used to be a simple thread_local variable, but Xcode doesn't support that yet
|
||||
int getThreadCounter() {
|
||||
using thread_id = std::thread::id;
|
||||
|
||||
static std::mutex counterMutex;
|
||||
lock_guard<std::mutex> lock(counterMutex);
|
||||
|
||||
static unordered_map<thread_id, int> threadCounters;
|
||||
static int lastThreadId = 0;
|
||||
thread_id threadId = std::this_thread::get_id();
|
||||
if (threadCounters.find(threadId) == threadCounters.end()) {
|
||||
threadCounters.insert({threadId, ++lastThreadId});
|
||||
}
|
||||
return threadCounters.find(threadId)->second;
|
||||
}
|
||||
|
||||
Entry::Entry(Level level, const string& message) :
|
||||
level(level),
|
||||
message(message)
|
||||
{
|
||||
time(×tamp);
|
||||
this->threadCounter = getThreadCounter();
|
||||
}
|
||||
|
||||
string SimpleConsoleFormatter::format(const Entry& entry) {
|
||||
return fmt::format("[{0}] {1}", entry.level, entry.message);
|
||||
}
|
||||
|
||||
string SimpleFileFormatter::format(const Entry& entry) {
|
||||
return fmt::format("[{0}] {1} {2}", formatTime(entry.timestamp, "%F %H:%M:%S"), entry.threadCounter, consoleFormatter.format(entry));
|
||||
}
|
||||
|
||||
LevelFilter::LevelFilter(shared_ptr<Sink> innerSink, Level minLevel) :
|
||||
innerSink(innerSink),
|
||||
minLevel(minLevel)
|
||||
{}
|
||||
|
||||
void LevelFilter::receive(const Entry& entry) {
|
||||
if (entry.level >= minLevel) {
|
||||
innerSink->receive(entry);
|
||||
}
|
||||
}
|
||||
|
||||
StreamSink::StreamSink(shared_ptr<std::ostream> stream, shared_ptr<Formatter> formatter) :
|
||||
stream(stream),
|
||||
formatter(formatter)
|
||||
{}
|
||||
|
||||
void StreamSink::receive(const Entry& entry) {
|
||||
string line = formatter->format(entry);
|
||||
*stream << line << std::endl;
|
||||
}
|
||||
|
||||
StdErrSink::StdErrSink(shared_ptr<Formatter> formatter) :
|
||||
StreamSink(std::shared_ptr<std::ostream>(&std::cerr, [](void*) {}), formatter)
|
||||
{}
|
||||
|
||||
PausableSink::PausableSink(shared_ptr<Sink> innerSink) :
|
||||
innerSink(innerSink)
|
||||
{}
|
||||
|
||||
void PausableSink::receive(const Entry& entry) {
|
||||
lock_guard<std::mutex> lock(mutex);
|
||||
if (isPaused) {
|
||||
buffer.push_back(entry);
|
||||
} else {
|
||||
innerSink->receive(entry);
|
||||
}
|
||||
}
|
||||
|
||||
void PausableSink::pause() {
|
||||
lock_guard<std::mutex> lock(mutex);
|
||||
isPaused = true;
|
||||
|
||||
}
|
||||
|
||||
void PausableSink::resume() {
|
||||
lock_guard<std::mutex> lock(mutex);
|
||||
isPaused = false;
|
||||
for (const Entry& entry : buffer) {
|
||||
innerSink->receive(entry);
|
||||
}
|
||||
buffer.clear();
|
||||
}
|
||||
|
||||
std::mutex& getLogMutex() {
|
||||
static std::mutex mutex;
|
||||
|
|
|
@ -1,103 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include "tools.h"
|
||||
#include "EnumConverter.h"
|
||||
#include "Sink.h"
|
||||
#include "Level.h"
|
||||
|
||||
namespace logging {
|
||||
|
||||
enum class Level {
|
||||
Trace,
|
||||
Debug,
|
||||
Info,
|
||||
Warn,
|
||||
Error,
|
||||
Fatal,
|
||||
EndSentinel
|
||||
};
|
||||
|
||||
class LevelConverter : public EnumConverter<Level> {
|
||||
public:
|
||||
static LevelConverter& get();
|
||||
protected:
|
||||
std::string getTypeName() override;
|
||||
member_data getMemberData() override;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, Level value);
|
||||
|
||||
std::istream& operator>>(std::istream& stream, Level& value);
|
||||
|
||||
struct Entry {
|
||||
Entry(Level level, const std::string& message);
|
||||
|
||||
time_t timestamp;
|
||||
int threadCounter;
|
||||
Level level;
|
||||
std::string message;
|
||||
};
|
||||
|
||||
class Formatter {
|
||||
public:
|
||||
virtual ~Formatter() = default;
|
||||
virtual std::string format(const Entry& entry) = 0;
|
||||
};
|
||||
|
||||
class SimpleConsoleFormatter : public Formatter {
|
||||
public:
|
||||
std::string format(const Entry& entry) override;
|
||||
};
|
||||
|
||||
class SimpleFileFormatter : public Formatter {
|
||||
public:
|
||||
std::string format(const Entry& entry) override;
|
||||
private:
|
||||
SimpleConsoleFormatter consoleFormatter;
|
||||
};
|
||||
|
||||
class Sink {
|
||||
public:
|
||||
virtual ~Sink() = default;
|
||||
virtual void receive(const Entry& entry) = 0;
|
||||
};
|
||||
|
||||
class LevelFilter : public Sink {
|
||||
public:
|
||||
LevelFilter(std::shared_ptr<Sink> innerSink, Level minLevel);
|
||||
void receive(const Entry& entry) override;
|
||||
private:
|
||||
std::shared_ptr<Sink> innerSink;
|
||||
Level minLevel;
|
||||
};
|
||||
|
||||
class StreamSink : public Sink {
|
||||
public:
|
||||
StreamSink(std::shared_ptr<std::ostream> stream, std::shared_ptr<Formatter> formatter);
|
||||
void receive(const Entry& entry) override;
|
||||
private:
|
||||
std::shared_ptr<std::ostream> stream;
|
||||
std::shared_ptr<Formatter> formatter;
|
||||
};
|
||||
|
||||
class StdErrSink : public StreamSink {
|
||||
public:
|
||||
explicit StdErrSink(std::shared_ptr<Formatter> formatter);
|
||||
};
|
||||
|
||||
class PausableSink : public Sink {
|
||||
public:
|
||||
explicit PausableSink(std::shared_ptr<Sink> innerSink);
|
||||
void receive(const Entry& entry) override;
|
||||
void pause();
|
||||
void resume();
|
||||
private:
|
||||
std::shared_ptr<Sink> innerSink;
|
||||
std::vector<Entry> buffer;
|
||||
std::mutex mutex;
|
||||
bool isPaused = false;
|
||||
};
|
||||
|
||||
void addSink(std::shared_ptr<Sink> sink);
|
||||
|
||||
void log(Level level, const std::string& message);
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
#include "sinks.h"
|
||||
#include <iostream>
|
||||
#include "Entry.h"
|
||||
|
||||
using std::string;
|
||||
using std::lock_guard;
|
||||
using std::shared_ptr;
|
||||
|
||||
namespace logging {
|
||||
|
||||
LevelFilter::LevelFilter(shared_ptr<Sink> innerSink, Level minLevel) :
|
||||
innerSink(innerSink),
|
||||
minLevel(minLevel)
|
||||
{}
|
||||
|
||||
void LevelFilter::receive(const Entry& entry) {
|
||||
if (entry.level >= minLevel) {
|
||||
innerSink->receive(entry);
|
||||
}
|
||||
}
|
||||
|
||||
StreamSink::StreamSink(shared_ptr<std::ostream> stream, shared_ptr<Formatter> formatter) :
|
||||
stream(stream),
|
||||
formatter(formatter)
|
||||
{}
|
||||
|
||||
void StreamSink::receive(const Entry& entry) {
|
||||
string line = formatter->format(entry);
|
||||
*stream << line << std::endl;
|
||||
}
|
||||
|
||||
StdErrSink::StdErrSink(shared_ptr<Formatter> formatter) :
|
||||
StreamSink(std::shared_ptr<std::ostream>(&std::cerr, [](void*) {}), formatter)
|
||||
{}
|
||||
|
||||
PausableSink::PausableSink(shared_ptr<Sink> innerSink) :
|
||||
innerSink(innerSink)
|
||||
{}
|
||||
|
||||
void PausableSink::receive(const Entry& entry) {
|
||||
lock_guard<std::mutex> lock(mutex);
|
||||
if (isPaused) {
|
||||
buffer.push_back(entry);
|
||||
} else {
|
||||
innerSink->receive(entry);
|
||||
}
|
||||
}
|
||||
|
||||
void PausableSink::pause() {
|
||||
lock_guard<std::mutex> lock(mutex);
|
||||
isPaused = true;
|
||||
|
||||
}
|
||||
|
||||
void PausableSink::resume() {
|
||||
lock_guard<std::mutex> lock(mutex);
|
||||
isPaused = false;
|
||||
for (const Entry& entry : buffer) {
|
||||
innerSink->receive(entry);
|
||||
}
|
||||
buffer.clear();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
#pragma once
|
||||
|
||||
#include "Sink.h"
|
||||
#include <memory>
|
||||
#include "Formatter.h"
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
|
||||
namespace logging {
|
||||
enum class Level;
|
||||
|
||||
class LevelFilter : public Sink {
|
||||
public:
|
||||
LevelFilter(std::shared_ptr<Sink> innerSink, Level minLevel);
|
||||
void receive(const Entry& entry) override;
|
||||
private:
|
||||
std::shared_ptr<Sink> innerSink;
|
||||
Level minLevel;
|
||||
};
|
||||
|
||||
class StreamSink : public Sink {
|
||||
public:
|
||||
StreamSink(std::shared_ptr<std::ostream> stream, std::shared_ptr<Formatter> formatter);
|
||||
void receive(const Entry& entry) override;
|
||||
private:
|
||||
std::shared_ptr<std::ostream> stream;
|
||||
std::shared_ptr<Formatter> formatter;
|
||||
};
|
||||
|
||||
class StdErrSink : public StreamSink {
|
||||
public:
|
||||
explicit StdErrSink(std::shared_ptr<Formatter> formatter);
|
||||
};
|
||||
|
||||
class PausableSink : public Sink {
|
||||
public:
|
||||
explicit PausableSink(std::shared_ptr<Sink> innerSink);
|
||||
void receive(const Entry& entry) override;
|
||||
void pause();
|
||||
void resume();
|
||||
private:
|
||||
std::shared_ptr<Sink> innerSink;
|
||||
std::vector<Entry> buffer;
|
||||
std::mutex mutex;
|
||||
bool isPaused = false;
|
||||
};
|
||||
|
||||
}
|
|
@ -5,6 +5,8 @@
|
|||
#include "NiceCmdLineOutput.h"
|
||||
#include "ProgressBar.h"
|
||||
#include "logging.h"
|
||||
#include "sinks.h"
|
||||
#include "formatters.h"
|
||||
#include <gsl_util.h>
|
||||
#include "Exporter.h"
|
||||
#include "ContinuousTimeline.h"
|
||||
|
|
Loading…
Reference in New Issue