module; // Seems like ADL for std::quoted is broken with // import std; // Might be because the _Quoted_string object is defined in // std::__detail, which is not exported by the module. #include export module routemon:log; import std; namespace routemon::log { export enum class level : std::uint8_t { debug, info, warn, error, }; namespace { auto operator<<(std::ostream& os, level lvl) -> std::ostream& { switch (lvl) { case level::debug: os << "dbg"; break; case level::info: os << "inf"; break; case level::warn: os << "wrn"; break; case level::error: os << "err"; break; } return os; } } // namespace export class sink { std::atomic lvl_; std::ostream& os_ = std::cout; struct tmp_message { level lvl; std::string_view component; std::string_view txt; std::map const& attrs; }; auto write(tmp_message msg) -> void { auto sos = std::osyncstream{os_}; sos << "[" << msg.lvl; if (!msg.component.empty()) sos << " " << msg.component; sos << "] " << msg.txt; for (auto const& [k, v] : msg.attrs) { sos << " " << k << "=" << std::quoted(v); } sos << '\n'; } explicit sink(level lvl) : lvl_{lvl} {} friend auto make_sink(level lvl) -> std::shared_ptr; friend class logger; public: [[nodiscard]] auto level() const -> enum level { return lvl_; } auto set_level(enum level lvl) -> void { lvl_ = lvl; } }; export auto make_sink(level lvl) -> std::shared_ptr { return std::shared_ptr{new sink{lvl}}; } export class logger { std::shared_ptr sink_; std::string component_; std::map attrs_; template auto log_at(std::string_view fmt, std::format_args args) -> logger& { if (sink_->level() <= lvl) sink_->write( sink::tmp_message{ .lvl = lvl, .component = component_, .txt = std::vformat(fmt, args), .attrs = attrs_, }); return *this; } public: explicit logger(std::shared_ptr const& sink) : sink_{sink} { if (!sink) { throw std::invalid_argument{"logger sink may not be null"}; } } [[nodiscard]] auto sub(std::string_view component) const -> logger { auto l = *this; if (l.component_.empty()) { l.component_ = component; } else { l.component_ += "."; l.component_ += component; } return l; } [[nodiscard]] auto with(std::string const& k, std::string&& v) const -> logger { auto l = *this; l.attrs_[k] = std::move(v); return l; } [[nodiscard]] auto with(std::string const& k, std::string_view v) const -> logger { return with(k, std::string{v}); } template auto debug(std::format_string fmt, Args&&... args) -> logger& { return log_at(fmt.get(), std::make_format_args(args...)); } template auto info(std::format_string fmt, Args&&... args) -> logger& { return log_at(fmt.get(), std::make_format_args(args...)); } template auto warn(std::format_string fmt, Args&&... args) -> logger& { return log_at(fmt.get(), std::make_format_args(args...)); } template auto error(std::format_string fmt, Args&&... args) -> logger& { return log_at(fmt.get(), std::make_format_args(args...)); } }; } // namespace routemon::log