#include "journal.hpp" #include #include #include #include #include #include #include #include #include #include #include namespace journal { namespace mutex { class Lock { std::mutex &mutex; bool moved = false; public: Lock(Lock &rhs) = delete; Lock(std::mutex &mutex) : mutex(mutex) { mutex.lock(); }; Lock(Lock &&rhs) : mutex(rhs.mutex) { rhs.moved = true; }; auto operator=(Lock &rhs) = delete; ~Lock() { if (!moved) { mutex.unlock(); } } }; template class Mutex { std::mutex mutex; T inner; public: template Mutex(U &&value) requires(std::assignable_from) : inner(std::move(value)) {} Mutex() requires(std::default_initializable) : inner(T{}) {} auto ref() -> std::pair { return {std::move(scoped_lock()), inner}; } auto mut() -> std::pair { return {std::move(scoped_lock()), inner}; } auto operator->() -> T& { return inner; } auto scoped_lock() -> Lock { return Lock(mutex); } auto unique_lock() -> std::unique_lock { return std::unique_lock(mutex); } auto lock() -> void { mutex.lock(); } auto try_lock() -> bool { return mutex.try_lock(); } auto unlock() -> void { mutex.unlock(); } }; } // namespace mutex namespace detail { constexpr auto to_string(const LogVerbosity& verbosity) -> std::string { switch (verbosity) { case LogVerbosity::Trace: return "Trace"; case LogVerbosity::Debug: return "Debug"; case LogVerbosity::Info: return "Info"; case LogVerbosity::Warn: return "Warn"; case LogVerbosity::Error: return "Error"; } } struct Verbosity { journal::detail::LogVerbosity inner; Verbosity(journal::detail::LogVerbosity &&value) : inner(value) {} Verbosity(const journal::detail::LogVerbosity &value) : inner(value) {} auto operator<=>(const Verbosity &rhs) -> std::strong_ordering { return static_cast>(inner) <=> static_cast>(rhs.inner); } }; struct Sink { std::shared_ptr stream; Verbosity min_verbosity; Verbosity max_verbosity; Sink(std::shared_ptr &&stream) : stream(std::move(stream)), min_verbosity(journal::detail::LogVerbosity::Trace), max_verbosity(journal::detail::LogVerbosity::Error) {} Sink(std::shared_ptr&& stream, const Verbosity &min, const Verbosity &max) : stream(std::move(stream)),min_verbosity(min), max_verbosity(max) {} Sink(std::shared_ptr &&stream, const Verbosity &min) : stream(std::move(stream)), min_verbosity(min), max_verbosity(journal::detail::Error) {} auto operator<<(std::string_view msg) { (*stream) << msg; } }; class Journal { std::vector sinks; public: Journal() {} auto log(const Verbosity &verbosity, source_location source, std::string_view msg) { for (auto &&sink : sinks) { if (verbosity >= sink.min_verbosity && verbosity <= sink.max_verbosity) { sink << std::format("[{}] [{: <5}] [{}] {}\n", get_current_time_string(), to_string(verbosity.inner), get_source_location_string(source), msg); } } } auto get_source_location_string(const source_location& source) -> std::string { const auto file = std::filesystem::path(source.file_name()).filename().string(); return std::format("{}::{}", file, source.line()); } auto get_current_time_string() -> std::string { const auto now = std::chrono::utc_clock::now(); return std::format("{:%Y-%m-%d %H:%M:%OS} UTC", now); } auto add_std_out(const Verbosity &min = journal::detail::LogVerbosity::Info) { auto out = std::shared_ptr(&std::cout, [](auto &&_) {}); sinks.emplace_back(std::move(out)); } }; static mutex::Mutex> global_journal; auto log_internal(const LogVerbosity &verbosity, detail::source_location source, const std::string &msg) -> void { auto &&[lock, ref] = global_journal.ref(); ref->log(verbosity, source, msg); } } // namespace detail auto init() { auto &&[lock, ref] = detail::global_journal.mut(); if (!ref.get()) { ref = std::make_unique(); } } auto init_stdout(const LogVerbosity &min) -> void { init(); auto &&[lock, ref] = detail::global_journal.mut(); ref->add_std_out(); } } // namespace journal