Files
rogue/source/util/logger.hpp
2026-04-16 23:27:01 -03:00

119 lines
3.0 KiB
C++

#ifndef LOGGER_HPP
#define LOGGER_HPP
#include <chrono>
#include <ctime>
#include <format>
#include <functional>
#include <string>
#include <string_view>
#include <vector>
namespace rog {
enum class log_level {
debug = 0,
info = 1,
warn = 2,
error = 3,
fatal = 4,
};
class logger {
private:
using SinkFunction = std::function<void(std::string_view)>;
std::vector<SinkFunction> sinks;
log_level current_level = log_level::info; // default level
[[nodiscard]] bool verify_level(log_level l) const noexcept {
return static_cast<int>(current_level) <= static_cast<int>(l);
}
static std::string get_timestamp() {
using namespace std::chrono;
const auto now = system_clock::now();
const auto time_t_now = system_clock::to_time_t(now);
const auto ms = duration_cast<milliseconds>(now.time_since_epoch()) % 1000;
std::tm local_tm{};
#if defined(_MSC_VER)
localtime_s(&local_tm, &time_t_now);
#else
localtime_r(&time_t_now, &local_tm); // safe on macOS / Linux
#endif
auto buf = std::array<char, 64>();
std::strftime(buf.data(), 64, "%Y-%m-%d %H:%M:%S", &local_tm);
auto buf_str = std::string(buf.data());
return std::format("{}.{:03}", buf_str, ms.count());
}
void dispatch(log_level level, std::string_view level_str,
std::string_view message) {
if (!verify_level(level)) {
return;
}
std::string timestamp = get_timestamp();
std::string formatted =
std::format("{:>5} [{}] -- {}", level_str, timestamp, message);
for (const auto &sink : sinks) {
sink(formatted);
}
}
public:
logger() = default;
template <typename... Sinks>
explicit logger(log_level level, Sinks &&...sinks) : current_level(level) {
(this->sinks.push_back(std::forward<Sinks>(sinks)), ...);
}
void set_level(log_level level) noexcept { current_level = level; }
template <typename... Args>
void debug(std::format_string<Args...> fmt, Args &&...args) {
dispatch(log_level::debug, "DEBUG",
std::format(fmt, std::forward<Args>(args)...));
}
template <typename... Args>
void info(std::format_string<Args...> fmt, Args &&...args) {
dispatch(log_level::info, "INFO",
std::format(fmt, std::forward<Args>(args)...));
}
template <typename... Args>
void warn(std::format_string<Args...> fmt, Args &&...args) {
dispatch(log_level::warn, "WARN",
std::format(fmt, std::forward<Args>(args)...));
}
template <typename... Args>
void error(std::format_string<Args...> fmt, Args &&...args) {
dispatch(log_level::error, "ERROR",
std::format(fmt, std::forward<Args>(args)...));
}
template <typename... Args>
void fatal(std::format_string<Args...> fmt, Args &&...args) {
dispatch(log_level::fatal, "FATAL",
std::format(fmt, std::forward<Args>(args)...));
}
// Add more sinks at runtime if desired
void add_sink(SinkFunction sink) { sinks.push_back(std::move(sink)); }
};
} // namespace rog
#endif