Skip to content

Instantly share code, notes, and snippets.

@Fiona-J-W
Last active August 29, 2015 13:55
Show Gist options
  • Save Fiona-J-W/8744670 to your computer and use it in GitHub Desktop.
Save Fiona-J-W/8744670 to your computer and use it in GitHub Desktop.
#include <atomic>
#include <chrono>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <ios>
#include "Log.h"
namespace Aux { namespace Log {
void setLogLevel(std::string logLevel) {
if (logLevel == "TRACE") {
Settings::setLogLevel(LogLevel::trace);
} else if (logLevel == "DEBUG") {
Settings::setLogLevel(LogLevel::debug);
} else if (logLevel == "INFO") {
Settings::setLogLevel(LogLevel::info);
} else if (logLevel == "WARN") {
Settings::setLogLevel(LogLevel::warn);
} else if (logLevel == "ERROR") {
Settings::setLogLevel(LogLevel::error);
} else if (logLevel == "FATAL") {
Settings::setLogLevel(LogLevel::fatal);
} else {
throw std::invalid_argument("unknown loglevel");
}
}
std::string getLogLevel() {
LogLevel current = Settings::getLogLevel();
if (current == LogLevel::trace) {
return "TRACE";
} else if (current == LogLevel::debug) {
return "DEBUG";
} else if (current == LogLevel::info) {
return "INFO";
} else if (current == LogLevel::warn) {
return "WARN";
} else if (current == LogLevel::error) {
return "ERROR";
} else if (current == LogLevel::fatal) {
return "FATAL";
} else {
throw std::runtime_error("impossible loglevel. This should never happen!");
}
}
namespace Settings {
namespace {
bool printTime = false;
bool printLocation = false;
LogLevel loglevel = LogLevel::info;
std::ofstream logfile;
std::atomic_bool logfileIsOpen{false};
std::mutex logfileMutex;
}
LogLevel getLogLevel() {return loglevel;}
void setLogLevel(LogLevel p) {loglevel = p;}
void setPrintTime(bool b) {printTime = b;}
bool getPrintTime() {return printTime;}
void setPrintLocation(bool b) {printLocation = b;}
bool getPrintLocation() {return printLocation;}
void setLogfile(const std::string& filename) {
std::lock_guard<std::mutex> guard{logfileMutex};
if(logfile.is_open()) {
logfile.close();
}
if(filename.empty()) {
logfile.open(filename, std::ios_base::out | std::ios_base::app);
logfileIsOpen = logfile.is_open();
} else {
logfileIsOpen = false;
}
}
} // namespace Settings
void printLogLevel(std::ostream& stream, LogLevel p) {
switch(p) {
case LogLevel::fatal:
stream << "[FATAL]"; break;
case LogLevel::error:
stream << "[ERROR]"; break;
case LogLevel::warn:
stream << "[WARN ]"; break;
case LogLevel::info:
stream << "[INFO ]"; break;
case LogLevel::debug:
stream << "[DEBUG]"; break;
case LogLevel::trace:
stream << "[TRACE]"; break;
}
}
void printTime(std::ostream& stream,
const std::chrono::time_point<std::chrono::system_clock>& timePoint) {
stream << "[" << timePoint.time_since_epoch().count() << "]";
}
void printLocation(std::ostream& stream, const Location& loc) {
stream << "[“" << loc.file << "”, " << loc.line << ": " << loc.function << "]";
}
std::tuple<std::string, std::string> getTerminalFormat(LogLevel p) {
switch (p) {
case LogLevel::fatal:
return std::make_tuple("\033[1;31m", "\033[0m");
case LogLevel::error:
return std::make_tuple("\033[31m", "\033[0m");
case LogLevel::warn:
return std::make_tuple("\033[33m", "\033[0m");
case LogLevel::info:
case LogLevel::debug:
case LogLevel::trace:
return std::make_tuple("", "");
}
}
static void logToTerminal(const Location& loc, LogLevel p,
const std::chrono::time_point<std::chrono::system_clock>& timePoint,
const std::string msg) {
std::stringstream stream;
if(Settings::getPrintTime()) {
printTime(stream, timePoint);
}
std::string termFormatOpen, termFormatClose;
std::tie(termFormatOpen, termFormatClose) = getTerminalFormat(p);
stream << termFormatOpen;
printLogLevel(stream, p);
stream <<termFormatClose;
if(Settings::getPrintLocation()) {
printLocation(stream, loc);
}
stream << ": ";
stream << termFormatOpen;
stream << msg;
stream << termFormatClose;
stream.put('\n');
if (p < LogLevel::warn) {
static std::mutex cout_mutex;
{
std::lock_guard<std::mutex> guard{cout_mutex};
std::cout << stream.str();
}
} else {
static std::mutex cerr_mutex;
{
std::lock_guard<std::mutex> guard{cerr_mutex};
std::cerr << stream.str();
}
}
}
static void logToFile(const Location& loc, LogLevel p,
const std::chrono::time_point<std::chrono::system_clock>& timePoint,
const std::string& msg) {
if(!Settings::logfileIsOpen) {
return;
}
std::stringstream stream;
printTime(stream, timePoint);
stream << ' ';
printLogLevel(stream, p);
if(Settings::getPrintLocation()) {
stream << ' ';
printLocation(stream, loc);
}
stream << ": " << msg << '\n';
{
std::lock_guard<std::mutex> guard{Settings::logfileMutex};
if(!Settings::logfileIsOpen) {
return;
}
Settings::logfile << stream.str() << std::flush;
}
}
namespace Impl {
void log(const Location& loc, LogLevel p, const std::string msg) {
auto time =std::chrono::system_clock::now();
logToTerminal(loc, p, time, msg);
logToFile(loc, p, time, msg);
}
} // namespace impl
}} // namespace Aux::Log
#ifndef LOG_H_
#define LOG_H_
#include <iostream>
#include <mutex>
#include "StringBuilder.h"
#define LOG_NOTHING 7
#define LOG_FATAL 6
#define LOG_ERROR 5
#define LOG_WARN 4
#define LOG_INFO 3
#define LOG_DEBUG 2
#define LOG_TRACE 1
#define LOG_ALL 0
#ifndef LOG_LEVEL
#define LOG_LEVEL LOG_ALL
#endif
#ifdef NOLOGGING
#define LOG_LEVEL LOG_NOTHING
#endif
#if LOG_LEVEL <= LOG_FATAL
#define FATAL(...) ::Aux::Log::log({__FILE__, __PRETTY_FUNCTION__, __LINE__},\
::Aux::Log::LogLevel::fatal, __VA_ARGS__)
#define FATALF(...) ::Aux::Log::logF({__FILE__, __PRETTY_FUNCTION__, __LINE__},\
::Aux::Log::LogLevel::fatal, __VA_ARGS__)
#else
#define FATAL(...) do{}while(false)
#define FATALF(...) do{}while(false)
#endif
#if LOG_LEVEL <= LOG_ERROR
#define ERROR(...) ::Aux::Log::log({__FILE__, __PRETTY_FUNCTION__, __LINE__},\
::Aux::Log::LogLevel::error, __VA_ARGS__)
#define ERRORF(...) ::Aux::Log::logF({__FILE__, __PRETTY_FUNCTION__, __LINE__},\
::Aux::Log::LogLevel::error, __VA_ARGS__)
#else
#define ERROR(...) do{}while(false)
#define ERRORF(...) do{}while(false)
#endif
#if LOG_LEVEL <= LOG_WARN
#define WARN(...) ::Aux::Log::log({__FILE__, __PRETTY_FUNCTION__, __LINE__},\
::Aux::Log::LogLevel::warn, __VA_ARGS__)
#define WARNF(...) ::Aux::Log::logF({__FILE__, __PRETTY_FUNCTION__, __LINE__},\
::Aux::Log::LogLevel::warn, __VA_ARGS__)
#else
#define WARN(...) do{}while(false)
#define WARNF(...) do{}while(false)
#endif
#if LOG_LEVEL <= LOG_INFO
#define INFO(...) ::Aux::Log::log({__FILE__, __PRETTY_FUNCTION__, __LINE__},\
::Aux::Log::LogLevel::info, __VA_ARGS__)
#define INFOF(...) ::Aux::Log::logF({__FILE__, __PRETTY_FUNCTION__, __LINE__},\
::Aux::Log::LogLevel::info, __VA_ARGS__)
#else
#define INFO(...) do{}while(false)
#define INFOF(...) do{}while(false)
#endif
#if LOG_LEVEL <= LOG_DEBUG
#define DEBUG(...) ::Aux::Log::log({__FILE__, __PRETTY_FUNCTION__, __LINE__},\
::Aux::Log::LogLevel::debug, __VA_ARGS__)
#define DEBUGF(...) ::Aux::Log::logF({__FILE__, __PRETTY_FUNCTION__, __LINE__},\
::Aux::Log::LogLevel::debug, __VA_ARGS__)
#else
#define DEBUG(...) do{}while(false)
#define DEBUGF(...) do{}while(false)
#endif
#if LOG_LEVEL <= LOG_TRACE
#define TRACE(...) ::Aux::Log::log({__FILE__, __PRETTY_FUNCTION__, __LINE__},\
::Aux::Log::LogLevel::trace, __VA_ARGS__)
#define TRACEF(...) ::Aux::Log::logF({__FILE__, __PRETTY_FUNCTION__, __LINE__},\
::Aux::Log::LogLevel::trace, __VA_ARGS__)
#define TRACEPOINT ::Aux::Log::log({__FILE__, __PRETTY_FUNCTION__, __LINE__},\
::Aux::Log::LogLevel::trace, "tracepoint")
#else
#define TRACE(...) do{}while(false)
#define TRACEF(...) do{}while(false)
#define TRACEPOINT do{}while(false)
#endif
namespace Aux { namespace Log {
struct Location {
const char* file;
const char* function;
const int line;
};
enum class LogLevel {
trace,
debug,
info,
warn,
error,
fatal
};
/**
* Accept loglevel as string and set.
* @param logLevel as string
*/
void setLogLevel(std::string logLevel);
/**
* @return current loglevel as string
*/
std::string getLogLevel();
namespace Settings {
LogLevel getLogLevel();
void setLogLevel(LogLevel p = LogLevel::info);
void setPrintTime(bool b);
bool getPrintTime();
void setPrintLocation(bool b);
bool getPrintLocation();
void setLogfile(const std::string& filename);
}
namespace Impl {
void log(const Location& loc, LogLevel p, const std::string msg);
} //namespace impl
template<typename...T>
void log(const Location& loc, LogLevel p, const T&...args) {
if(p >= Settings::getLogLevel()) {
std::stringstream stream;
printToStream(stream, args...);
Impl::log(loc, p, stream.str());
}
}
template<typename...T>
void logF(const Location& loc, LogLevel p, const std::string& format, const T&...args) {
if(p >= Settings::getLogLevel()) {
std::stringstream stream;
printToStreamF(stream, format, args...);
Impl::log(loc, p, stream.str());
}
}
}} // namespace Aux::Log
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment