Last active
February 10, 2019 11:47
-
-
Save Fogapod/a7d9d53eb54eddcc5a0fdfdc53950b62 to your computer and use it in GitHub Desktop.
My c++ logger v0.1.0
This file contains 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 "logger.h" | |
namespace log { | |
bool LoggingTarget::WriteMessage(const std::string &) | |
{ | |
return true; | |
} | |
bool LoggingTarget::SupportsColors() const | |
{ | |
return false; | |
} | |
bool stdoutLogger::WriteMessage(const std::string &body) | |
{ | |
std::cout << body << std::endl; | |
return std::cout.good(); | |
} | |
bool stdoutLogger::SupportsColors() const | |
{ | |
return true; | |
} | |
FileLogger::FileLogger(const char *filename) | |
{ | |
file_.open(filename, std::ios_base::app); | |
} | |
FileLogger::~FileLogger() | |
{ | |
file_.close(); | |
} | |
bool FileLogger::WriteMessage(const std::string &body) | |
{ | |
if (!file_.is_open()) { | |
return false; | |
} | |
file_ << body << std::endl; | |
return file_.good(); | |
} | |
void Logger::Reset() { | |
if (!targets_.empty()) { | |
CA_LOG_DEBUG("Deleting", targets_.size(), "logging targets"); | |
} | |
targets_ = std::list<std::shared_ptr<LoggingTarget>>(); | |
} | |
void Logger::AddTarget(const std::shared_ptr<LoggingTarget> &t) | |
{ | |
targets_.push_back(t); | |
} | |
// TODO: colors? | |
bool Logger::WriteMessage(const char *prefix, const std::string &body, const Level) | |
{ | |
// TODO: less hardcoded values? | |
size_t len = | |
8 // time format | |
+ 3 // additional chars: "[ ]" | |
+ 3 // prefix | |
+ body.size(); // body | |
std::time_t t = std::time(nullptr); | |
std::tm *time = std::localtime(&t); | |
char *message = new char[len]; | |
snprintf(message, len, "[%02i:%02i:%02i %s]%s", | |
time->tm_hour, time->tm_min, time->tm_sec, prefix, body.c_str() | |
); | |
bool full_success = true; | |
for (auto &target : targets_) { | |
// TODO: wrap with color if supported by target | |
if (!target->WriteMessage(message)) { | |
full_success = false; | |
} | |
} | |
delete[] message; | |
return full_success; | |
} | |
std::list<std::shared_ptr<LoggingTarget>> Logger::targets_; | |
} // namespace logging |
This file contains 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
#define CA_LOG_LEVEL_SILENT 0 | |
#define CA_LOG_LEVEL_FATAL 1 | |
#define CA_LOG_LEVEL_ERROR 2 | |
#define CA_LOG_LEVEL_INFO 3 | |
#define CA_LOG_LEVEL_DEBUG 4 | |
#define CA_LOG_LEVEL_TRACE 5 | |
#define LOGGING_LEVEL CA_LOG_LEVEL_TRACE | |
#ifndef LOGGER_H | |
#define LOGGER_H | |
#include <iostream> | |
#include <sstream> | |
#include <fstream> | |
#include <list> | |
#include <ctime> | |
#include <memory> | |
#include <typeinfo> | |
namespace log { | |
namespace streamfallback { | |
// default to_string for types without << operator | |
template<typename T> | |
inline std::string to_string(const T &) | |
{ | |
return "{" + std::string(typeid(T).name()) + "}"; | |
} | |
// overrides for types without << operator | |
inline std::string to_string(const std::nullptr_t &) | |
{ | |
return "{nullPtr}"; | |
} | |
// a fallback << operator | |
template <typename CharT, typename Traits, typename T> | |
typename std::enable_if<std::is_same<CharT, char>::value, | |
std::basic_ostream<CharT, Traits> &>::type | |
inline operator<<(std::basic_ostream<CharT, Traits> &os, const T &x) | |
{ | |
return os << to_string(x); | |
} | |
} // namespace streamfallback | |
enum Level { | |
FATAL=CA_LOG_LEVEL_FATAL, | |
ERROR=CA_LOG_LEVEL_ERROR, | |
INFO =CA_LOG_LEVEL_INFO , | |
DEBUG=CA_LOG_LEVEL_DEBUG, | |
TRACE=CA_LOG_LEVEL_TRACE | |
}; | |
// TODO: LoggableBuffer class | |
class LoggingTarget { | |
public: | |
virtual ~LoggingTarget() { } | |
virtual bool WriteMessage(const std::string &); | |
virtual bool SupportsColors() const; | |
}; | |
class stdoutLogger : public LoggingTarget { | |
public: | |
bool WriteMessage(const std::string &body) override; | |
bool SupportsColors() const override; | |
}; | |
class FileLogger : public LoggingTarget { | |
public: | |
FileLogger(const char *filename = "log.log"); | |
~FileLogger() override; | |
bool WriteMessage(const std::string &body) override; | |
private: | |
std::ofstream file_; | |
}; | |
// TODO: BufferLogger class | |
class Logger | |
{ | |
public: | |
static void Reset(); | |
static void AddTarget(const std::shared_ptr<LoggingTarget> &t); | |
template<typename... Args> | |
static bool LogArgs(const Level level, const Args &...args) | |
{ | |
const char *prefix; | |
// requirement: 3 characters | |
switch (level) { | |
case Level::TRACE: | |
prefix = "TRC"; | |
break; | |
case Level::DEBUG: | |
prefix = "DEB"; | |
break; | |
case Level::INFO: | |
prefix = "INF"; | |
break; | |
case Level::ERROR: | |
prefix = "ERR"; | |
break; | |
case Level::FATAL: | |
prefix = "FAT"; | |
break; | |
} | |
std::stringstream output; | |
output << std::boolalpha; | |
JoinArgs(&output, args...); | |
return WriteMessage(prefix, output.str(), level); | |
} | |
private: | |
template<typename First, typename... Rest> | |
static void JoinArgs(std::stringstream *output, const First &first, const Rest &...rest) | |
{ | |
using streamfallback::operator<<; | |
*output << first << " "; | |
JoinArgs(output, rest...); | |
} | |
// no arguments left for JoinArgs | |
static void JoinArgs(std::stringstream *) { } | |
static bool WriteMessage(const char *prefix, const std::string &body, const Level level); | |
private: | |
static std::list<std::shared_ptr<LoggingTarget>> targets_; | |
}; | |
} // namespace log | |
#ifdef LOGGING_LEVEL | |
#if LOGGING_LEVEL >= CA_LOG_LEVEL_FATAL | |
#define CA_LOG_FATAL(...) log::Logger::LogArgs(log::Level::FATAL, __VA_ARGS__) | |
#else | |
#define CA_LOG_FATAL(...) | |
#endif | |
#if LOGGING_LEVEL >= CA_LOG_LEVEL_ERROR | |
#define CA_LOG_ERROR(...) log::Logger::LogArgs(log::Level::ERROR, __VA_ARGS__) | |
#else | |
#define CA_LOG_ERROR(...) | |
#endif | |
#if LOGGING_LEVEL >= CA_LOG_LEVEL_INFO | |
#define CA_LOG_INFO(...) log::Logger::LogArgs(log::Level::INFO, __VA_ARGS__) | |
#else | |
#define CA_LOG_INFO(...) | |
#endif | |
#if LOGGING_LEVEL >= CA_LOG_LEVEL_DEBUG | |
#define CA_LOG_DEBUG(...) log::Logger::LogArgs(log::Level::DEBUG, __VA_ARGS__) | |
#else | |
#define CA_LOG_DEBUG(...) | |
#endif | |
#if LOGGING_LEVEL >= CA_LOG_LEVEL_TRACE | |
#define CA_LOG_TRACE(...) log::Logger::LogArgs(log::Level::TRACE, __VA_ARGS__) | |
#else | |
#define CA_LOG_TRACE(...) | |
#endif | |
#else | |
#define CA_LOG_FATAL(...) | |
#define CA_LOG_ERROR(...) | |
#define CA_LOG_INFO(...) | |
#define CA_LOG_DEBUG(...) | |
#define CA_LOG_TRACE(...) | |
#endif | |
#define CA_LOG CA_LOG_INFO | |
#endif // LOGGER_H |
This file contains 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 "logger.h" | |
int main() { | |
log::Logger::AddTarget(std::make_shared<log::stdoutLogger>()); | |
log::Logger::AddTarget(std::make_shared<log::FileLogger>()); | |
CA_LOG(1, .1f, .2, "str", 'c', true); // basic types | |
class A {}; | |
A a; | |
CA_LOG(nullptr, a); // no << operator | |
CA_LOG("тест"); // locale support. why it works? | |
CA_LOG_TRACE(1, "trace"); | |
CA_LOG_DEBUG(2, "DEBUG"); | |
CA_LOG_INFO (3, "INFO"); | |
CA_LOG_ERROR(4, "ERROR"); | |
CA_LOG_FATAL(5, "FATAL"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment