2016-06-15 03:32:35 +00:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <cstdio>
|
|
|
|
#include <cstring>
|
|
|
|
|
|
|
|
#include "common.hpp"
|
|
|
|
#include "utils/string.hpp"
|
|
|
|
|
|
|
|
LEMONBUDDY_NS
|
|
|
|
|
|
|
|
enum class loglevel {
|
|
|
|
NONE = 0,
|
|
|
|
ERROR,
|
|
|
|
WARNING,
|
|
|
|
INFO,
|
|
|
|
TRACE,
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert given loglevel name to its enum type counterpart
|
|
|
|
*/
|
|
|
|
auto parse_loglevel_name = [](string name) {
|
|
|
|
if (string_util::compare(name, "error"))
|
|
|
|
return loglevel::ERROR;
|
|
|
|
else if (string_util::compare(name, "warning"))
|
|
|
|
return loglevel::WARNING;
|
|
|
|
else if (string_util::compare(name, "info"))
|
|
|
|
return loglevel::INFO;
|
|
|
|
else if (string_util::compare(name, "trace"))
|
|
|
|
return loglevel::TRACE;
|
|
|
|
else
|
|
|
|
return loglevel::NONE;
|
|
|
|
};
|
|
|
|
|
|
|
|
class logger {
|
|
|
|
public:
|
|
|
|
/**
|
|
|
|
* Construct logger
|
|
|
|
*/
|
|
|
|
explicit logger(loglevel level) : m_level(level) {
|
|
|
|
if (isatty(STDOUT_FILENO)) {
|
|
|
|
// clang-format off
|
|
|
|
m_prefixes[loglevel::TRACE] = "\r\033[0;90m- ";
|
|
|
|
m_prefixes[loglevel::INFO] = "\r\033[1;32m* \033[0m";
|
|
|
|
m_prefixes[loglevel::WARNING] = "\r\033[1;33mwarning: \033[0m";
|
|
|
|
m_prefixes[loglevel::ERROR] = "\r\033[1;31merror: \033[0m";
|
|
|
|
|
|
|
|
m_suffixes[loglevel::TRACE] = "\033[0m";
|
|
|
|
m_suffixes[loglevel::INFO] = "\033[0m";
|
|
|
|
m_suffixes[loglevel::WARNING] = "\033[0m";
|
|
|
|
m_suffixes[loglevel::ERROR] = "\033[0m";
|
|
|
|
// clang-format on
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Construct logger
|
|
|
|
*/
|
|
|
|
explicit logger(string level_name) : logger(parse_loglevel_name(level_name)) {}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set output verbosity
|
|
|
|
*/
|
|
|
|
void verbosity(loglevel level) {
|
|
|
|
#ifndef DEBUG
|
|
|
|
if (level == loglevel::TRACE)
|
|
|
|
throw application_error("not a debug build: trace disabled...");
|
|
|
|
#endif
|
|
|
|
m_level = level;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set output verbosity by loglevel name
|
|
|
|
*/
|
|
|
|
void verbosity(string level) {
|
|
|
|
verbosity(parse_loglevel_name(level));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Output a trace message
|
|
|
|
*/
|
|
|
|
template <typename... Args>
|
|
|
|
void trace(string message, Args... args) const {
|
|
|
|
#ifdef DEBUG
|
|
|
|
output(loglevel::TRACE, message, args...);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Output an info message
|
|
|
|
*/
|
|
|
|
template <typename... Args>
|
|
|
|
void info(string message, Args... args) const {
|
|
|
|
output(loglevel::INFO, message, args...);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Output a warning message
|
|
|
|
*/
|
|
|
|
template <typename... Args>
|
|
|
|
void warn(string message, Args... args) const {
|
|
|
|
output(loglevel::WARNING, message, args...);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Output an error message
|
|
|
|
*/
|
|
|
|
template <typename... Args>
|
|
|
|
void err(string message, Args... args) const {
|
|
|
|
output(loglevel::ERROR, message, args...);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Configure injection module
|
|
|
|
*/
|
|
|
|
template <typename T = const logger&>
|
|
|
|
static di::injector<T> configure(loglevel level = loglevel::NONE) {
|
|
|
|
auto instance = factory::generic_singleton<logger>(level);
|
|
|
|
return di::make_injector(di::bind<>().to(instance));
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
template <typename T>
|
|
|
|
decltype(auto) convert(T&& arg) const {
|
|
|
|
return forward<T>(arg);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert string to const char*
|
|
|
|
*/
|
|
|
|
const char* convert(string arg) const {
|
|
|
|
return arg.c_str();
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Write the log message to the output channel
|
|
|
|
* if the defined verbosity level allows it
|
|
|
|
*/
|
|
|
|
template <typename... Args>
|
|
|
|
void output(loglevel level, string format, Args... values) const {
|
|
|
|
if (level > m_level)
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto prefix = m_prefixes.find(level)->second;
|
|
|
|
auto suffix = m_suffixes.find(level)->second;
|
|
|
|
|
|
|
|
// silence the compiler
|
2016-10-11 10:38:15 +00:00
|
|
|
#if defined(__clang__)
|
2016-06-15 03:32:35 +00:00
|
|
|
#pragma clang diagnostic push
|
|
|
|
#pragma clang diagnostic ignored "-Wformat-security"
|
2016-10-11 10:38:15 +00:00
|
|
|
#endif
|
2016-06-15 03:32:35 +00:00
|
|
|
dprintf(m_fd, (prefix + format + suffix + "\n").c_str(), convert(values)...);
|
2016-10-11 10:38:15 +00:00
|
|
|
#if defined(__clang__)
|
2016-06-15 03:32:35 +00:00
|
|
|
#pragma clang diagnostic pop
|
2016-10-11 10:38:15 +00:00
|
|
|
#endif
|
2016-06-15 03:32:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
/**
|
|
|
|
* Logger verbosity level
|
|
|
|
*/
|
|
|
|
loglevel m_level = loglevel::TRACE;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* File descriptor used when writing the log messages
|
|
|
|
*/
|
2016-10-11 06:34:34 +00:00
|
|
|
int m_fd = STDERR_FILENO;
|
2016-06-15 03:32:35 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Loglevel specific prefixes
|
|
|
|
*/
|
|
|
|
// clang-format off
|
|
|
|
map<loglevel, string> m_prefixes {
|
|
|
|
{loglevel::TRACE, "lemonbuddy|trace "},
|
|
|
|
{loglevel::INFO, "lemonbuddy|info "},
|
|
|
|
{loglevel::WARNING, "lemonbuddy|warn "},
|
|
|
|
{loglevel::ERROR, "lemonbuddy|error "},
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Loglevel specific suffixes
|
|
|
|
*/
|
|
|
|
map<loglevel, string> m_suffixes {
|
|
|
|
{loglevel::TRACE, ""},
|
|
|
|
{loglevel::INFO, ""},
|
|
|
|
{loglevel::WARNING, ""},
|
|
|
|
{loglevel::ERROR, ""},
|
|
|
|
};
|
|
|
|
// clang-format on
|
|
|
|
};
|
|
|
|
|
|
|
|
LEMONBUDDY_NS_END
|