export module logger; import preliminary; export namespace lt::log { /** Severity of a log message. */ enum class Level : u8 { /** Lowest and most vebose log level, for tracing execution paths and events */ trace = 0, /** Vebose log level, for enabling temporarily to debug */ debug = 1, /** General information */ info = 2, /** Things we should to be aware of and edge cases */ warn = 3, /** Defects, bugs and undesired behaviour */ error = 4, /** Unrecoverable errors */ critical = 5, /** * Logs from the testing-framework. * Highest so we still get them while turning off all logs from the code under test. * * @note: log::test does NOT include source_location */ test = 6, /** No logging */ off = 7, }; auto min_severity = Level::trace; auto set_min_severity(Level severity) { min_severity = severity; } template struct [[maybe_unused]] print { [[maybe_unused]] print( Level level, const std::source_location &location, std::format_string format, Args &&...arguments ) noexcept { if (std::to_underlying(level) < std::to_underlying(min_severity)) { return; } constexpr auto to_string = [](Level level) { // clang-format off switch (level) { using enum ::lt::log::Level; case trace : return "\033[1;37m| trc |\033[0m"; case debug : return "\033[1;36m| dbg |\033[0m"; case info : return "\033[1;32m| inf |\033[0m"; case warn : return "\033[1;33m| wrn |\033[0m"; case error : return "\033[1;31m| err |\033[0m"; case critical: return "\033[1;41m| crt |\033[0m"; case off: return "off"; } // clang-format on std::unreachable(); }; const auto path = std::filesystem::path { location.file_name() }; std::println( "{} {} ==> {}", to_string(level), std::format("{}:{}", path.filename().string(), location.line()), std::format(format, std::forward(arguments)...) ); } [[maybe_unused]] print( Level level, std::format_string format, Args &&...arguments ) noexcept { constexpr auto to_string = [](Level level) { // clang-format off switch (level) { using enum ::lt::log::Level; case trace : return "\033[1;37m| trc |\033[0m"; case debug : return "\033[1;36m| dbg |\033[0m"; case info : return "\033[1;32m| inf |\033[0m"; case warn : return "\033[1;33m| wrn |\033[0m"; case error : return "\033[1;31m| err |\033[0m"; case critical: return "\033[1;41m| crt |\033[0m"; case test : return "\033[1;33m| test |\033[0m"; case off : return ""; } // clang-format on std::unreachable(); }; std::println( "{} {}", to_string(level), std::format(format, std::forward(arguments)...) ); } }; template print(Level, const std::source_location &, std::format_string, Args &&...) noexcept -> print; template struct [[maybe_unused]] trace { [[maybe_unused]] trace( std::format_string format, Args &&...arguments, const std::source_location &location = std::source_location::current() ) noexcept { print(Level::trace, location, format, std::forward(arguments)...); } }; template trace(std::format_string, Args &&...) noexcept -> trace; template struct [[maybe_unused]] debug { [[maybe_unused]] debug( std::format_string format, Args &&...arguments, const std::source_location &location = std::source_location::current() ) noexcept { print(Level::debug, location, format, std::forward(arguments)...); } }; template debug(std::format_string, Args &&...) noexcept -> debug; template struct [[maybe_unused]] info { [[maybe_unused]] info( std::format_string format, Args &&...arguments, const std::source_location &location = std::source_location::current() ) noexcept { print(Level::info, location, format, std::forward(arguments)...); } }; template info(std::format_string, Args &&...) noexcept -> info; template struct [[maybe_unused]] warn { [[maybe_unused]] warn( std::format_string format, Args &&...arguments, const std::source_location &location = std::source_location::current() ) noexcept { print(Level::warn, location, format, std::forward(arguments)...); } }; template warn(std::format_string, Args &&...) noexcept -> warn; template struct [[maybe_unused]] error { [[maybe_unused]] error( std::format_string format, Args &&...arguments, const std::source_location &location = std::source_location::current() ) noexcept { print(Level::error, location, format, std::forward(arguments)...); } }; template error(std::format_string, Args &&...) noexcept -> error; template struct [[maybe_unused]] critical { [[maybe_unused]] critical( std::format_string format, Args &&...arguments, const std::source_location &location = std::source_location::current() ) noexcept { print(Level::critical, location, format, std::forward(arguments)...); } }; template critical(std::format_string, Args &&...) noexcept -> critical; template void test(std::format_string format, Args &&...arguments) noexcept { print(Level::test, format, std::forward(arguments)...); } } // namespace lt::log