Skip to content

Instantly share code, notes, and snippets.

@Fogapod
Last active February 10, 2019 11:47
Show Gist options
  • Save Fogapod/a7d9d53eb54eddcc5a0fdfdc53950b62 to your computer and use it in GitHub Desktop.
Save Fogapod/a7d9d53eb54eddcc5a0fdfdc53950b62 to your computer and use it in GitHub Desktop.
My c++ logger v0.1.0
#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
#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
#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