module routemon:log$impl; import :log; namespace routemon::log { 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; } sink::sink(enum level lvl) : lvl_{lvl} {} auto sink::level() const -> enum level { return lvl_; } auto sink::set_level(enum level lvl) -> void { lvl_ = lvl; } auto sink::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 << std::endl; // we want the flush! } auto make_sink(level lvl) -> std::shared_ptr { return std::shared_ptr{new sink{lvl}}; } auto logger::log_at(log::level lvl, 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; } logger::logger(std::shared_ptr const& sink) : sink_{sink} { if (!sink) { throw std::invalid_argument{"logger sink may not be null"}; } } auto logger::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; } auto logger::with(std::string const& k, std::string&& v) const -> logger { auto l = *this; l.attrs_[k] = std::move(v); return l; } auto logger::with(std::string const& k, std::string_view v) const -> logger { return with(k, std::string{v}); } } // namespace routemon::log