I wrote a simple logger class for my project. Some key points:
- The interface is heavily inspired by Java's logging libraries (you can tell from the getters/setters that I'm a Java programmer). In particular I'm using printf style functions instead of std::ostream style, which I don't like very much.
- verify is a macro similar to assert, but instead of aborting it throws std::logic_error and cannot be disabled with NDEBUG.
- The heavy lifting is done by Handler, which I didn't include here because it's quite a lot of code and it's not finished.
Without further ado, here's log.hpp
#pragma once
#include "verify.hpp"
#include <cassert>
#include <ostream>
#include <utility>
namespace bran {
enum class log_severity : unsigned {
INHERIT,
TRACE,
DEBUG,
INFO,
WARN,
ERROR,
FATAL
};
std::ostream& operator<<(std::ostream& out, log_severity severity);
template<class Handler>
class basic_logger;
template<class Handler>
void swap(basic_logger<Handler>& a, basic_logger<Handler>& b) noexcept;
template<class Handler>
class basic_logger {
public:
explicit basic_logger(const std::string& name, basic_logger<Handler>* parent, Handler* handler);
explicit basic_logger(const std::string& name, basic_logger<Handler>& parent);
explicit basic_logger(const std::string& name, Handler& handler);
basic_logger(basic_logger&& l) noexcept;
basic_logger(const basic_logger&) = delete;
~basic_logger() = default;
basic_logger& operator=(basic_logger l) noexcept;
[[nodiscard]] inline std::string get_name() const noexcept;
void set_threshold(log_severity threshold);
[[nodiscard]] inline log_severity get_threshold() const noexcept;
void set_handler(Handler* handler);
[[nodiscard]] inline Handler* get_handler() const noexcept;
void set_parent(basic_logger<Handler>* parent);
[[nodiscard]] inline basic_logger<Handler>* get_parent() const noexcept;
[[nodiscard]] inline bool is_trace() const noexcept;
[[nodiscard]] inline bool is_debug() const noexcept;
[[nodiscard]] inline bool is_info() const noexcept;
[[nodiscard]] inline bool is_warn() const noexcept;
[[nodiscard]] inline bool is_error() const noexcept;
template<class... T>
inline void trace(const std::string& message, T... args);
template<class... T>
inline void debug(const std::string& message, T... args);
template<class... T>
inline void info(const std::string& message, T... args);
template<class... T>
inline void warn(const std::string& message, T... args);
template<class... T>
inline void error(const std::string& message, T... args);
template<class... T>
inline void fatal(const std::string& message, T... args);
private:
std::string name;
log_severity threshold;
basic_logger<Handler>* parent;
Handler* handler;
template<class... T>
inline void log(log_severity severity, const std::string& message, T... args);
public:
friend void swap<Handler>(basic_logger<Handler>& a, basic_logger<Handler>& b) noexcept;
};
}
template<class Handler>
bran::basic_logger<Handler>::basic_logger(const std::string& name, basic_logger<Handler>* parent, Handler* handler)
: name{name}, parent{parent}, handler{handler} {
verify(!name.empty() && (parent != nullptr || handler != nullptr));
if (parent != nullptr) {
threshold = log_severity::INHERIT;
} else {
threshold = log_severity::TRACE;
}
}
template<class Handler>
bran::basic_logger<Handler>::basic_logger(const std::string& name, basic_logger<Handler>& parent)
: basic_logger{name, &parent, nullptr} {
}
template<class Handler>
bran::basic_logger<Handler>::basic_logger(const std::string& name, Handler& handler)
: basic_logger{name, nullptr, &handler} {
}
template<class Handler>
bran::basic_logger<Handler>::basic_logger(basic_logger&& l) noexcept {
swap(*this, l);
}
template<class Handler>
bran::basic_logger<Handler>& bran::basic_logger<Handler>::operator=(basic_logger l) noexcept {
swap(*this, l);
return *this;
}
template<class Handler>
std::string bran::basic_logger<Handler>::get_name() const noexcept {
return name;
}
template<class Handler>
void bran::basic_logger<Handler>::set_threshold(log_severity threshold) {
verify(threshold != log_severity::INHERIT || parent != nullptr);
this->threshold = threshold;
}
template<class Handler>
bran::log_severity bran::basic_logger<Handler>::get_threshold() const noexcept {
assert(threshold != log_severity::INHERIT || parent != nullptr);
return threshold == log_severity::INHERIT ? parent->get_threshold() : threshold;
}
template<class Handler>
void bran::basic_logger<Handler>::set_handler(Handler* handler) {
verify(handler != nullptr || parent != nullptr);
this->handler = handler;
}
template<class Handler>
Handler* bran::basic_logger<Handler>::get_handler() const noexcept {
assert(handler != nullptr || parent != nullptr);
return handler == nullptr ? parent->get_handler() : handler;
}
template<class Handler>
void bran::basic_logger<Handler>::set_parent(basic_logger<Handler>* parent) {
verify(parent != nullptr || (handler != nullptr && threshold != log_severity::INHERIT));
this->parent = parent;
}
template<class Handler>
bran::basic_logger<Handler>* bran::basic_logger<Handler>::get_parent() const noexcept {
return parent;
}
template<class Handler>
bool bran::basic_logger<Handler>::is_trace() const noexcept {
return get_threshold() <= log_severity::TRACE;
}
template<class Handler>
bool bran::basic_logger<Handler>::is_debug() const noexcept {
return get_threshold() <= log_severity::DEBUG;
}
template<class Handler>
bool bran::basic_logger<Handler>::is_info() const noexcept {
return get_threshold() <= log_severity::INFO;
}
template<class Handler>
bool bran::basic_logger<Handler>::is_warn() const noexcept {
return get_threshold() <= log_severity::WARN;
}
template<class Handler>
bool bran::basic_logger<Handler>::is_error() const noexcept {
return get_threshold() <= log_severity::ERROR;
}
template<class Handler>
template<class... T>
void bran::basic_logger<Handler>::trace(const std::string& message, T... args) {
log(log_severity::TRACE, message, args...);
}
template<class Handler>
template<class... T>
void bran::basic_logger<Handler>::debug(const std::string& message, T... args) {
log(log_severity::DEBUG, message, args...);
}
template<class Handler>
template<class... T>
void bran::basic_logger<Handler>::info(const std::string& message, T... args) {
log(log_severity::INFO, message, args...);
}
template<class Handler>
template<class... T>
void bran::basic_logger<Handler>::warn(const std::string& message, T... args) {
log(log_severity::WARN, message, args...);
}
template<class Handler>
template<class... T>
void bran::basic_logger<Handler>::error(const std::string& message, T... args) {
log(log_severity::ERROR, message, args...);
}
template<class Handler>
template<class... T>
void bran::basic_logger<Handler>::fatal(const std::string& message, T... args) {
log(log_severity::FATAL, message, args...);
}
template<class Handler>
template<class... T>
void bran::basic_logger<Handler>::log(log_severity severity, const std::string& message, T... args) {
if (get_threshold() > severity) {
return;
}
get_handler()->log(*this, severity, message, args...);
}
template<class Handler>
void bran::swap(basic_logger<Handler>& a, basic_logger<Handler>& b) noexcept {
using std::swap;
swap(a.name, b.name);
swap(a.threshold, b.threshold);
swap(a.parent, b.parent);
swap(a.handler, b.handler);
}
And the puny log.cpp
#include "log.hpp"
std::ostream& bran::operator<<(std::ostream& out, bran::log_severity severity) {
static const std::string SEVERITY_NAMES[] = {"INHERIT", "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"};
static const size_t SEVERITY_COUNT = sizeof(SEVERITY_NAMES) / sizeof(SEVERITY_NAMES[0]);
auto index = static_cast<size_t>(severity);
assert(index < SEVERITY_COUNT);
return out << SEVERITY_NAMES[index];
}
Handler
, but the posted code never invokesHandler
for any purpose. If you could supply those missing pieces, it would help a great deal. \$\endgroup\$