//----------------------------------------------------------------------------- // ___ __ _ _ // / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __ // / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ / // / ___/ (_| | | \__ \ __/ /__| | | | | < // \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ . // //----------------------------------------------------------------------------- // Author: Kurt Sassenrath // Module: Logging // // Logging infrastructure. // // Copyright (c) 2023 Kurt Sassenrath. // // License TBD. //----------------------------------------------------------------------------- #ifndef logging_982a89e400976f59 #define logging_982a89e400976f59 #include "logging/formatters.h" #include "logging/theme.h" #include "logging/traits.h" #include #include #include #include #include namespace parselink { namespace logging { // Any log levels higher (lower severity) than static_threshold cannot not be // enabled in the library, but the compiler should be able to optimize away // some/most of the calls. constexpr inline auto static_threshold = level::trace; constexpr inline auto default_threshold = level::debug; // Structure for holding a message. Note: message is a view over some buffer, // not it's own string. It will either be a static buffer supplied by the // endpoint implementation, or it will be dynamically created by the logger // before calling endpoint::write() struct message { level lvl; std::chrono::system_clock::time_point time; std::string_view name; std::string_view message; }; struct endpoint { virtual ~endpoint() = default; virtual std::span buffer() { return {}; } virtual void write(message const& msg) = 0; virtual bool colored() const noexcept = 0; level threshold{default_threshold}; }; class logger { public: // This constructor utilizes the default logging endpoint. logger(std::string_view name); // This constructor allows for an arbitrary number of logging endpoints // to be passed in. logger(std::string_view name, std::vector> eps); template explicit logger(std::string_view name, Endpoints&&... eps) : logger(name, {std::forward(eps)...}) {} template requires(Level <= static_threshold) void log(fmt::format_string format, Args&&... args) const { do_write(Level, format.get(), std::forward(args)...); } template requires(Level > static_threshold) [[gnu::flatten]] void log(fmt::format_string, Args&&...) const {} #define LOG_API(lvl) \ template \ [[gnu::always_inline]] void lvl( \ fmt::format_string&& format, Args&&... args) const { \ log(std::forward(format), \ std::forward(args)...); \ } LOG_API(critical); LOG_API(error); LOG_API(warning); LOG_API(info); LOG_API(verbose); LOG_API(debug); LOG_API(trace); #undef LOG_API void set_threshold(level new_threshold) noexcept; private: template void write_endpoint(std::shared_ptr const& endpoint, message msg, fmt::string_view format, Args&&... args) const { auto buff = endpoint->buffer(); if (buff.empty()) { // Allocate memory. auto buff = fmt::memory_buffer(); fmt::vformat_to(std::back_inserter(buff), format, fmt::make_format_args(args...)); msg.message = std::string_view{buff.data(), buff.size()}; endpoint->write(msg); } else { // Fill the static buffer. fmt::vformat_to( buff.begin(), format, fmt::make_format_args(args...)); msg.message = std::string_view{buff.data(), buff.size()}; endpoint->write(msg); } } template void do_write(level level, fmt::string_view format, Args&&... args) const { message msg{level, std::chrono::system_clock::now(), name_}; for (auto const& ep : endpoints_) { if (level > ep->threshold) continue; if (ep->colored()) { write_endpoint(ep, msg, format, themed_arg{std::forward(args)}...); } else { write_endpoint(ep, msg, format, format_arg{std::forward(args)}...); } } } std::string name_; std::vector> endpoints_; }; } // namespace logging } // namespace parselink #endif // logging_982a89e400976f59