174 lines
4.5 KiB
C++
174 lines
4.5 KiB
C++
#include "journal.hpp"
|
|
|
|
#include <chrono>
|
|
#include <compare>
|
|
#include <concepts>
|
|
#include <cstdio>
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <optional>
|
|
#include <string_view>
|
|
#include <type_traits>
|
|
#include <filesystem>
|
|
|
|
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 <typename T> class Mutex {
|
|
std::mutex mutex;
|
|
T inner;
|
|
|
|
public:
|
|
template <typename U>
|
|
Mutex(U &&value) requires(std::assignable_from<U, T>)
|
|
: inner(std::move(value)) {}
|
|
Mutex() requires(std::default_initializable<T>) : inner(T{}) {}
|
|
|
|
auto ref() -> std::pair<Lock, const T &> {
|
|
return {std::move(scoped_lock()), inner};
|
|
}
|
|
auto mut() -> std::pair<Lock, T &> {
|
|
return {std::move(scoped_lock()), inner};
|
|
}
|
|
|
|
auto operator->() -> T& {
|
|
return inner;
|
|
}
|
|
|
|
auto scoped_lock() -> Lock { return Lock(mutex); }
|
|
|
|
auto unique_lock() -> std::unique_lock<std::mutex> {
|
|
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<std::underlying_type_t<decltype(inner)>>(inner) <=>
|
|
static_cast<std::underlying_type_t<decltype(rhs.inner)>>(rhs.inner);
|
|
}
|
|
};
|
|
|
|
struct Sink {
|
|
std::shared_ptr<std::ostream> stream;
|
|
Verbosity min_verbosity;
|
|
Verbosity max_verbosity;
|
|
|
|
Sink(std::shared_ptr<std::ostream> &&stream)
|
|
: stream(std::move(stream)),
|
|
min_verbosity(journal::detail::LogVerbosity::Trace),
|
|
max_verbosity(journal::detail::LogVerbosity::Error) {}
|
|
Sink(std::shared_ptr<std::ostream>&& stream, const Verbosity &min,
|
|
const Verbosity &max)
|
|
: stream(std::move(stream)),min_verbosity(min), max_verbosity(max) {}
|
|
Sink(std::shared_ptr<std::ostream> &&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<Sink> 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::ostream>(&std::cout, [](auto &&_) {});
|
|
sinks.emplace_back(std::move(out));
|
|
}
|
|
};
|
|
|
|
static mutex::Mutex<std::unique_ptr<Journal>> 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<detail::Journal>();
|
|
}
|
|
}
|
|
|
|
auto init_stdout(const LogVerbosity &min) -> void {
|
|
init();
|
|
auto &&[lock, ref] = detail::global_journal.mut();
|
|
ref->add_std_out();
|
|
}
|
|
} // namespace journal
|