Last active
August 29, 2015 13:55
-
-
Save Fiona-J-W/8744670 to your computer and use it in GitHub Desktop.
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
#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 |
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
#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