159 lines
5.2 KiB
C++
159 lines
5.2 KiB
C++
#include <modulus/log.hpp>
|
|
|
|
#include <cassert>
|
|
#include <cstdint>
|
|
#include <iostream>
|
|
#include <mutex>
|
|
#include <utility>
|
|
#include <modulus/types.hpp>
|
|
|
|
|
|
namespace {
|
|
|
|
auto is_valid_log_level(modulus::log_level level) noexcept -> std::bool_t {
|
|
return static_cast<std::int32_t>(level) >= static_cast<std::int32_t>(modulus::log_level::TRACE) && static_cast<std::int32_t>(level) <= static_cast<std::int32_t>(modulus::log_level::OFF);
|
|
}
|
|
|
|
auto should_log(modulus::log_level record_level, modulus::log_level configured_level) noexcept -> std::bool_t {
|
|
assert(is_valid_log_level(record_level));
|
|
assert(is_valid_log_level(configured_level));
|
|
return configured_level != modulus::log_level::OFF && static_cast<std::int32_t>(record_level) >= static_cast<std::int32_t>(configured_level);
|
|
}
|
|
|
|
auto format_log_record(std::ostream &stream, const modulus::log_record &record) -> void {
|
|
assert(is_valid_log_level(record.level));
|
|
assert(record.location.file_name() != nullptr);
|
|
stream << '[' << modulus::to_string(record.level) << "] " << record.message << " (" << record.location.file_name() << ':' << record.location.line() << ")\n";
|
|
}
|
|
|
|
auto stderr_sink(const modulus::log_record &record) -> void {
|
|
assert(is_valid_log_level(record.level));
|
|
assert(record.location.file_name() != nullptr);
|
|
static auto mutex = std::mutex{};
|
|
auto lock = std::lock_guard{mutex};
|
|
format_log_record(std::cerr, record);
|
|
}
|
|
|
|
}
|
|
|
|
auto modulus::to_string(modulus::log_level level) noexcept -> std::string_view {
|
|
assert(is_valid_log_level(level));
|
|
switch (level) {
|
|
case modulus::log_level::TRACE:
|
|
return "trace";
|
|
case modulus::log_level::DEBUG:
|
|
return "debug";
|
|
case modulus::log_level::INFO:
|
|
return "info";
|
|
case modulus::log_level::WARN:
|
|
return "warn";
|
|
case modulus::log_level::ERROR:
|
|
return "error";
|
|
case modulus::log_level::CRITICAL:
|
|
return "critical";
|
|
case modulus::log_level::OFF:
|
|
return "off";
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
modulus::logger::logger()
|
|
: sink_(stderr_sink) {}
|
|
|
|
auto modulus::logger::set_level(modulus::log_level level) -> void {
|
|
assert(is_valid_log_level(level));
|
|
auto lock = std::lock_guard{mutex_};
|
|
level_ = level;
|
|
}
|
|
|
|
auto modulus::logger::level() const -> modulus::log_level {
|
|
auto lock = std::lock_guard{mutex_};
|
|
assert(is_valid_log_level(level_));
|
|
return level_;
|
|
}
|
|
|
|
auto modulus::logger::enabled(modulus::log_level level) const -> std::bool_t {
|
|
assert(is_valid_log_level(level));
|
|
auto lock = std::lock_guard{mutex_};
|
|
return should_log(level, level_) && static_cast<std::bool_t>(sink_);
|
|
}
|
|
|
|
auto modulus::logger::set_sink(std::function<void(const modulus::log_record &)> sink) -> void {
|
|
auto lock = std::lock_guard{mutex_};
|
|
sink_ = std::move(sink);
|
|
}
|
|
|
|
auto modulus::logger::write(modulus::log_record record) const -> void {
|
|
assert(is_valid_log_level(record.level));
|
|
assert(record.location.file_name() != nullptr);
|
|
assert(record.location.function_name() != nullptr);
|
|
auto sink = std::function<void(const modulus::log_record &)>{};
|
|
{
|
|
auto lock = std::lock_guard{mutex_};
|
|
if (!should_log(record.level, level_) || !sink_) {
|
|
return;
|
|
}
|
|
sink = sink_;
|
|
}
|
|
assert(static_cast<std::bool_t>(sink));
|
|
auto sink_lock = std::lock_guard(sink_mutex_);
|
|
sink(record);
|
|
}
|
|
|
|
auto modulus::logger::log(modulus::log_level level, std::string message, modulus::source_location location) const -> void {
|
|
assert(is_valid_log_level(level));
|
|
assert(location.file_name() != nullptr);
|
|
assert(location.function_name() != nullptr);
|
|
write(modulus::log_record{
|
|
.level = level,
|
|
.message = std::move(message),
|
|
.location = location,
|
|
});
|
|
}
|
|
|
|
auto modulus::default_logger() -> modulus::logger & {
|
|
static auto instance = modulus::logger{};
|
|
return instance;
|
|
}
|
|
|
|
auto modulus::set_default_log_level(modulus::log_level level) -> void {
|
|
assert(is_valid_log_level(level));
|
|
modulus::default_logger().set_level(level);
|
|
}
|
|
|
|
auto modulus::is_log_enabled(modulus::log_level level) -> std::bool_t {
|
|
assert(is_valid_log_level(level));
|
|
return modulus::default_logger().enabled(level);
|
|
}
|
|
|
|
auto modulus::log(modulus::log_level level, std::string message, modulus::source_location location) -> void {
|
|
assert(is_valid_log_level(level));
|
|
assert(location.file_name() != nullptr);
|
|
assert(location.function_name() != nullptr);
|
|
modulus::default_logger().log(level, std::move(message), location);
|
|
}
|
|
|
|
auto modulus::log_trace(std::string message, modulus::source_location location) -> void {
|
|
modulus::log(modulus::log_level::TRACE, std::move(message), location);
|
|
}
|
|
|
|
auto modulus::log_debug(std::string message, modulus::source_location location) -> void {
|
|
modulus::log(modulus::log_level::DEBUG, std::move(message), location);
|
|
}
|
|
|
|
auto modulus::log_info(std::string message, modulus::source_location location) -> void {
|
|
modulus::log(modulus::log_level::INFO, std::move(message), location);
|
|
}
|
|
|
|
auto modulus::log_warn(std::string message, modulus::source_location location) -> void {
|
|
modulus::log(modulus::log_level::WARN, std::move(message), location);
|
|
}
|
|
|
|
auto modulus::log_error(std::string message, modulus::source_location location) -> void {
|
|
modulus::log(modulus::log_level::ERROR, std::move(message), location);
|
|
}
|
|
|
|
auto modulus::log_critical(std::string message, modulus::source_location location) -> void {
|
|
modulus::log(modulus::log_level::CRITICAL, std::move(message), location);
|
|
}
|