Last active
July 10, 2019 02:28
-
-
Save mik30s/6768d90307e67c95d9c3404cf6da0f68 to your computer and use it in GitHub Desktop.
A simple and hacky logger using fmtlib in C++17
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <variant> | |
// use fmtlib as the backbone for printing and formating messages. | |
#include <fmt/fmt.h> | |
// Colors from @gon1332 | |
// https://github.com/gon1332/fort320 | |
/* FOREGROUND */ | |
#define RST "\x1B[0m" | |
#define KRED "\x1B[31m" | |
#define KGRN "\x1B[32m" | |
#define KYEL "\x1B[33m" | |
#define KBLU "\x1B[34m" | |
#define KMAG "\x1B[35m" | |
#define KCYN "\x1B[36m" | |
#define KWHT "\x1B[37m" | |
///\brief A simple wrapper class for fmt::format for logging | |
class LogManager final | |
{ | |
#define DECLARE_STATE(STATE_NAME) \ | |
struct STATE_NAME { \ | |
fmt::MemoryWriter format; \ | |
STATE_NAME(){} \ | |
template<typename ...Args> \ | |
explicit STATE_NAME (const std::string& name= "", Args&&... args) { \ | |
format.write(name);\ | |
format.write(std::forward<Args>(args)...); \ | |
} \ | |
}; \ | |
DECLARE_STATE(Info) | |
DECLARE_STATE(Warn) | |
DECLARE_STATE(Fatal) | |
DECLARE_STATE(Error) | |
DECLARE_STATE(Success) | |
using LogState = std::variant<Success, Error, Warn, Info, Fatal>; | |
bool immediate; | |
unsigned long fatalCount; | |
std::vector<LogState> logs; | |
///\brief Handles printing of logs to streams | |
struct LogPrinter { | |
#define LOG_PRINT_STUB(color, text) { \ | |
fmt::print("{} {} {}\n", color, text, RST); \ | |
} \ | |
// Overloads for operator `()` for later use with visitor pattern. | |
void operator() (const Info& state) {LOG_PRINT_STUB(KBLU, state.format.c_str());} | |
void operator() (const Warn& state) {LOG_PRINT_STUB(KYEL, state.format.c_str());} | |
void operator() (const Error& state) {LOG_PRINT_STUB(KRED, state.format.c_str());} | |
void operator() (const Fatal& state) {LOG_PRINT_STUB(KRED, state.format.c_str());} | |
void operator() (const Success& state) {LOG_PRINT_STUB(KGRN, state.format.c_str());} | |
#undef LOG_PRINT_STUB | |
}; | |
///\brief Default constructor for LogManager | |
explicit LogManager():name("") {fatalCount = 0;} | |
///\brief Constructs a logger | |
///\param name The name of a logger | |
///\param imm Tells the logger not to store logs | |
LogManager(const std::string& _name, bool imm = false) | |
: immediate(imm) | |
{ | |
errorCount = 0 ; | |
name = "[" + _name + "] "; | |
} | |
#define DECLARE_NO_THROW_STATE_HANDLER(STATE, HANDLER) \ | |
template<typename ...Args> \ | |
void HANDLER(Args&&... args) { \ | |
if (immediate) { \ | |
LogPrinter{}(STATE(name, std::forward<Args>(args)...)); \ | |
return; \ | |
} \ | |
logs.emplace_back(STATE(name, std::forward<Args>(args)...)); \ | |
} \ | |
// State handler for throwing states | |
#define DECLARE_THROW_STATE_HANDLER(STATE, HANDLER) \ | |
template<typename ...Args> \ | |
void HANDLER(Args&&... args) { \ | |
if (immediate) { \ | |
LogPrinter{}(STATE(name, std::forward<Args>(args)...)); \ | |
throw std::runtime_error(name + " failed to complete."); \ | |
} \ | |
logs.emplace_back(STATE(name, std::forward<Args>(args)...)); \ | |
} \ | |
// state handler definitions | |
DECLARE_THROW_STATE_HANDLER(Fatal, fatal) | |
DECLARE_NO_THROW_STATE_HANDLER(Info, info) | |
DECLARE_NO_THROW_STATE_HANDLER(Warn, warn) | |
DECLARE_NO_THROW_STATE_HANDLER(Error, error) | |
///\brief Finalizes logging after deferring. | |
/// Should be called at the end of each section. | |
/// Preferably at the end of Stage::start() method | |
/// \param msg A message to log at the end if there are errors. | |
void finalize(const char* msg = "") | |
{ | |
// for each log we call the appropriate LogPrinter overload. | |
for (LogState& state : logs) { | |
// if its an error in the logs the increas error count | |
if (std::holds_alternative<Fatal>(state) { | |
errorCount++; | |
} | |
// use printer to print log to console. | |
std::visit(LogPrinter{}, state); | |
} | |
// when done check your error count and | |
// throw an exception if there are any errors | |
if (errorCount > 0) { | |
throw std::runtime_error(msg); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment