Skip to content

Instantly share code, notes, and snippets.

@mik30s
Last active July 10, 2019 02:28
Show Gist options
  • Save mik30s/6768d90307e67c95d9c3404cf6da0f68 to your computer and use it in GitHub Desktop.
Save mik30s/6768d90307e67c95d9c3404cf6da0f68 to your computer and use it in GitHub Desktop.
A simple and hacky logger using fmtlib in C++17
#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