Adding a general logger and integrating it with daggyr

This commit is contained in:
Ian Roddis
2022-02-04 11:58:45 -04:00
parent 57e93b5045
commit ea3f67f226
8 changed files with 245 additions and 20 deletions

View File

@@ -0,0 +1,71 @@
#pragma once
#include <atomic>
#include <condition_variable>
#include <deque>
#include <functional>
#include <mutex>
#include <string>
#include "Defines.hpp"
namespace daggy {
BETTER_ENUM(LogLevel, int, NONE = 0, ERROR, WARN, INFO, DEBUG);
class GeneralLogger
{
public:
explicit GeneralLogger(std::ostream &os, LogLevel level = LogLevel::WARN);
~GeneralLogger();
void setLevel(LogLevel level);
void log(const std::string &msg, LogLevel level);
// Function will only be called if required
void log(const std::function<std::string()> &fun, LogLevel level);
template <typename T>
void error(const T &msg)
{
log(msg, LogLevel::ERROR);
}
template <typename T>
void warn(const T &msg)
{
log(msg, LogLevel::WARN);
}
template <typename T>
void info(const T &msg)
{
log(msg, LogLevel::INFO);
}
template <typename T>
void debug(const T &msg)
{
log(msg, LogLevel::DEBUG);
}
void shutdown();
private:
void emitMessages();
struct LogMessage
{
LogLevel level;
std::string msg;
TimePoint time;
};
std::atomic<bool> running_;
std::ostream &os_;
LogLevel level_;
std::condition_variable newMessage_;
std::mutex messageGuard_;
std::deque<LogMessage> messages_;
std::thread messageEmiter_;
};
} // namespace daggy

View File

@@ -2,6 +2,7 @@ target_sources(${PROJECT_NAME} PRIVATE
Serialization.cpp
Utilities.cpp
DAGRunner.cpp
GeneralLogger.cpp
)
add_subdirectory(executors)

View File

@@ -0,0 +1,66 @@
#include <daggy/GeneralLogger.hpp>
#include <daggy/Serialization.hpp>
namespace daggy {
GeneralLogger::GeneralLogger(std::ostream &os, LogLevel level)
: running_(true)
, os_(os)
, level_(level)
, messageEmiter_(&GeneralLogger::emitMessages, this)
{
}
GeneralLogger::~GeneralLogger()
{
shutdown();
}
void GeneralLogger::shutdown()
{
if (!running_)
return;
running_ = false;
newMessage_.notify_one();
messageEmiter_.join();
os_.flush();
}
void GeneralLogger::setLevel(LogLevel level)
{
level_ = level;
}
void GeneralLogger::log(const std::string &msg, LogLevel level)
{
if (level > level_) {
return;
}
{
std::lock_guard<std::mutex> lock(messageGuard_);
messages_.emplace_back(
LogMessage{.level = level, .msg = msg, .time = Clock::now()});
}
newMessage_.notify_one();
}
void GeneralLogger::log(const std::function<std::string()> &fun,
LogLevel level)
{
if (level > level_)
return;
log(fun(), level);
}
void GeneralLogger::emitMessages()
{
while (running_ || !messages_.empty()) {
std::unique_lock<std::mutex> lock(messageGuard_);
newMessage_.wait(lock, [&] { return !(messages_.empty() && running_); });
for (const auto &msg : messages_) {
os_ << timePointToString(msg.time) << " [" << msg.level._to_string()
<< "] " << msg.msg << '\n';
}
os_.flush();
messages_.clear();
}
}
} // namespace daggy

View File

@@ -11,6 +11,7 @@ add_executable(${PROJECT_NAME} main.cpp
unit_serialization.cpp
unit_threadpool.cpp
unit_utilities.cpp
unit_generallogger.cpp
# integration tests
int_basic.cpp
# Performance checks

View File

@@ -0,0 +1,31 @@
#include <catch2/catch.hpp>
#include <daggy/GeneralLogger.hpp>
#include <iostream>
#include <sstream>
using namespace daggy;
TEST_CASE("General Logger", "[general_logger]")
{
std::stringstream ss;
GeneralLogger logger(ss);
SECTION("Logger logs a message")
{
std::string testMessage = "Test Message";
logger.setLevel(LogLevel::INFO);
logger.warn(testMessage);
logger.shutdown();
auto captured = ss.str();
REQUIRE(!captured.empty());
REQUIRE(captured.find(testMessage) != std::string::npos);
}
SECTION("Logger does not emit messages of higher levels")
{
logger.setLevel(LogLevel::INFO);
logger.debug("Test Message");
REQUIRE(ss.str().empty());
}
}