Refactoring: split logging code into individual files

This commit is contained in:
Daniel Wolf 2017-09-10 22:17:16 +02:00
parent 24e8da4474
commit 4c0d706857
14 changed files with 310 additions and 213 deletions

View File

@ -347,8 +347,18 @@ target_link_libraries(rhubarb-lib
# ... rhubarb-logging # ... rhubarb-logging
add_library(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.cpp
src/logging/logging.h 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_include_directories(rhubarb-logging PUBLIC "src/logging")
target_link_libraries(rhubarb-logging target_link_libraries(rhubarb-logging

38
src/logging/Entry.cpp Normal file
View File

@ -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(&timestamp);
this->threadCounter = getThreadCounter();
}
}

16
src/logging/Entry.h Normal file
View File

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

14
src/logging/Formatter.h Normal file
View File

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

35
src/logging/Level.cpp Normal file
View File

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

29
src/logging/Level.h Normal file
View File

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

13
src/logging/Sink.h Normal file
View File

@ -0,0 +1,13 @@
#pragma once
#include "Entry.h"
namespace logging {
class Sink {
public:
virtual ~Sink() = default;
virtual void receive(const Entry& entry) = 0;
};
}

View File

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

19
src/logging/formatters.h Normal file
View File

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

View File

@ -1,130 +1,13 @@
#include "logging.h" #include "logging.h"
#include <tools.h> #include <tools.h>
#include <iostream> #include <mutex>
#include <atomic> #include "Entry.h"
#include <thread>
#include <unordered_map>
using namespace logging; using namespace logging;
using std::string; using std::string;
using std::vector; using std::vector;
using std::shared_ptr; using std::shared_ptr;
using std::lock_guard; 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(&timestamp);
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() { std::mutex& getLogMutex() {
static std::mutex mutex; static std::mutex mutex;

View File

@ -1,103 +1,11 @@
#pragma once #pragma once
#include <vector>
#include <mutex>
#include "tools.h"
#include "EnumConverter.h" #include "EnumConverter.h"
#include "Sink.h"
#include "Level.h"
namespace logging { 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 addSink(std::shared_ptr<Sink> sink);
void log(Level level, const std::string& message); void log(Level level, const std::string& message);

64
src/logging/sinks.cpp Normal file
View File

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

48
src/logging/sinks.h Normal file
View File

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

View File

@ -5,6 +5,8 @@
#include "NiceCmdLineOutput.h" #include "NiceCmdLineOutput.h"
#include "ProgressBar.h" #include "ProgressBar.h"
#include "logging.h" #include "logging.h"
#include "sinks.h"
#include "formatters.h"
#include <gsl_util.h> #include <gsl_util.h>
#include "Exporter.h" #include "Exporter.h"
#include "ContinuousTimeline.h" #include "ContinuousTimeline.h"