From c79004413ddf53851e33c1e75bff7edab6bc9b38 Mon Sep 17 00:00:00 2001 From: janis Date: Mon, 20 Jun 2022 20:31:07 +0100 Subject: [PATCH] added sinks types --- include/journal.hpp | 12 ++- src/journal.cc | 234 +++++++++++++++++++++++++++++++++++++++----- src/main.cc | 14 +-- test.log | 2 + 4 files changed, 230 insertions(+), 32 deletions(-) create mode 100644 test.log diff --git a/include/journal.hpp b/include/journal.hpp index c667ff8..d169a66 100644 --- a/include/journal.hpp +++ b/include/journal.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include namespace journal { @@ -64,7 +65,16 @@ auto log_internal(const LogVerbosity &verbosity, using detail::LogVerbosity; -auto init_stdout(const LogVerbosity& min = LogVerbosity::Info) -> void; +auto init_std_out(const LogVerbosity &min = LogVerbosity::Info) -> void; +auto init_std_out_err(const LogVerbosity &min = LogVerbosity::Info) -> void; +auto init() -> void; +auto add_file(std::string_view file_path, + std::ios_base::openmode = std::ios_base::ate, + const LogVerbosity &min = LogVerbosity::Info, + const LogVerbosity &max = LogVerbosity::Error) -> void; +auto add_sink(std::shared_ptr stream, + const LogVerbosity &min = LogVerbosity::Info, + const LogVerbosity &max = LogVerbosity::Error) -> void; #ifdef JOURNAL_ALL #define JOURNAL_TRACE diff --git a/src/journal.cc b/src/journal.cc index fb3ec10..793206f 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -3,14 +3,13 @@ #include #include #include -#include -#include +#include +#include #include #include #include +#include #include -#include -#include namespace journal { namespace mutex { @@ -69,19 +68,99 @@ public: namespace detail { +enum Color { + Black, + Red, + Green, + Yellow, + Blue, + Magenta, + Cyan, + White, + BrightBlack, + BrightRed, + BrightGreen, + BrightYellow, + BrightBlue, + BrightMagenta, + BrightCyan, + BrightWhite, + None, +}; + +auto color_code(const Color &color) -> std::string { + switch (color) { + case Black: + return "\e[0;30m"; + case Red: + return "\e[0;31m"; + case Green: + return "\e[0;32m"; + case Yellow: + return "\e[0;33m"; + case Blue: + return "\e[0;34m"; + case Magenta: + return "\e[0;35m"; + case Cyan: + return "\e[0;36m"; + case White: + return "\e[0;37m"; + + case BrightBlack: + return "\e[1;30m"; + case BrightRed: + return "\e[1;31m"; + case BrightGreen: + return "\e[1;32m"; + case BrightYellow: + return "\e[1;33m"; + case BrightBlue: + return "\e[1;34m"; + case BrightMagenta: + return "\e[1;35m"; + case BrightCyan: + return "\e[1;36m"; + case BrightWhite: + return "\e[1;37m"; + case None: + return "\e[0;0m"; + break; + } +} + +auto colored(const std::string &str, const Color &color) -> std::string { + return color_code(color) + str + color_code(Color::None); +} + +constexpr auto to_string_colored(const LogVerbosity &verbosity) -> std::string { + switch (verbosity) { + case LogVerbosity::Trace: + return colored("TRACE", White); + case LogVerbosity::Debug: + return colored("DEBUG", Magenta); + case LogVerbosity::Info: + return colored("INFO ", Green); + case LogVerbosity::Warn: + return colored("WARN ", Yellow); + case LogVerbosity::Error: + return colored("ERROR", Red); + } +} + constexpr auto to_string(const LogVerbosity& verbosity) -> std::string { switch (verbosity) { case LogVerbosity::Trace: - return "Trace"; + return "TRACE"; case LogVerbosity::Debug: - return "Debug"; + return "DEBUG"; case LogVerbosity::Info: - return "Info"; + return "INFO "; case LogVerbosity::Warn: - return "Warn"; + return "WARN "; case LogVerbosity::Error: - return "Error"; - } + return "ERROR"; + } } struct Verbosity { @@ -90,12 +169,77 @@ struct Verbosity { Verbosity(journal::detail::LogVerbosity &&value) : inner(value) {} Verbosity(const journal::detail::LogVerbosity &value) : inner(value) {} - auto operator<=>(const Verbosity &rhs) -> std::strong_ordering { + auto operator<=>(const Verbosity &rhs) const -> std::strong_ordering { return static_cast>(inner) <=> static_cast>(rhs.inner); } }; +namespace sinks { +struct BaseSink { + Verbosity min_verbosity; + Verbosity max_verbosity; + + BaseSink() + : min_verbosity(LogVerbosity::Trace), max_verbosity(LogVerbosity::Error) { + } + + BaseSink(const Verbosity &min, const Verbosity &max) + : min_verbosity(min), max_verbosity(max) {} + + auto should_sink_verbosity(const Verbosity &verbosity) const -> bool { + return verbosity >= min_verbosity && verbosity <= max_verbosity; + } + + auto set_max_verbosity(const Verbosity &new_max) -> Verbosity { + return std::exchange(max_verbosity, new_max); + } + + auto set_min_verbosity(const Verbosity &new_min) -> Verbosity { + return std::exchange(min_verbosity, new_min); + } + + virtual auto flush() -> void; + virtual auto operator<<(std::string_view str) -> void; +}; + +struct StdOutSink : public BaseSink { + StdOutSink() : BaseSink(LogVerbosity::Trace, LogVerbosity::Info) {} + + auto write(std::string_view str) -> void { (*this) << str; } + virtual auto flush() -> void { std::cout.flush(); } + virtual auto operator<<(std::string_view str) -> void { std::cout << str; } +}; + +struct StdErrSink : public BaseSink { + StdErrSink() : BaseSink(LogVerbosity::Warn, LogVerbosity::Error) {} + + virtual auto flush() -> void { std::cerr.flush(); } + virtual auto operator<<(std::string_view str) -> void { std::cerr << str; } +}; + +struct OStreamSink : public BaseSink { + std::shared_ptr ostream; + + OStreamSink(const std::shared_ptr &stream, + const Verbosity &min = LogVerbosity::Trace, + const Verbosity &max = LogVerbosity::Error) + : ostream(stream), BaseSink(min, max) {} + + virtual auto flush() -> void { ostream->flush(); } + virtual auto operator<<(std::string_view str) -> void { (*ostream) << str; } +}; + +struct FileSink : public OStreamSink { + FileSink(const std::string &file_path, + std::ios::ios_base::openmode open_mode = std::ios::ios_base::app, + const Verbosity &min = LogVerbosity::Trace, + const Verbosity &max = LogVerbosity::Error) + : OStreamSink(std::make_shared( + std::ofstream(file_path, std::ios::ios_base::out | open_mode))) {} +}; +} // namespace sinks + struct Sink { std::shared_ptr stream; Verbosity min_verbosity; @@ -112,25 +256,39 @@ struct Sink { : stream(std::move(stream)), min_verbosity(min), max_verbosity(journal::detail::Error) {} + ~Sink() { flush(); } + + auto flush() -> void { stream->flush(); } auto operator<<(std::string_view msg) { (*stream) << msg; } }; class Journal { - std::vector sinks; + std::vector> sinks; + std::chrono::system_clock::time_point last_flushed; public: Journal() {} auto log(const Verbosity &verbosity, source_location source, std::string_view msg) { + const auto now = std::chrono::system_clock::now(); + const auto do_flush = ((now - last_flushed).count() <= 5); 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); + if (sink->should_sink_verbosity(verbosity)) { + (*sink) << format(msg, verbosity, source); + } + if (do_flush) { + sink->flush(); } } } +auto format(std::string_view str, const Verbosity &verbosity, + const source_location &source) -> std::string { + return std::format("[{}] [{: <5}] [{}] {}\n", get_current_time_string(), + to_string(verbosity.inner), + get_source_location_string(source), str); +} + 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()); @@ -142,10 +300,19 @@ auto get_current_time_string() -> std::string { 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)); +auto add_file(const std::string &file_name, + std::ios::ios_base::openmode open_mode = std::ios::ios_base::app, + const Verbosity &min = LogVerbosity::Trace, + const Verbosity &max = LogVerbosity::Error) -> void { + add_sink(std::make_unique(file_name, open_mode, min, max)); } + +auto add_sink(std::unique_ptr &&sink) -> void { + sinks.emplace_back(std::move(sink)); +} + +auto add_std_out() { add_sink(std::make_unique()); } +auto add_std_err() { add_sink(std::make_unique()); } }; static mutex::Mutex> global_journal; @@ -158,16 +325,35 @@ auto log_internal(const LogVerbosity &verbosity, detail::source_location source, } } // namespace detail -auto init() { +auto init() -> void { 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(); +auto init_std_out(const LogVerbosity &min) -> void { + init(); + auto &&[lock, ref] = detail::global_journal.mut(); + ref->add_std_out(); +} + +auto init_std_out_err(const LogVerbosity &min) -> void { + init(); + auto &&[lock, ref] = detail::global_journal.mut(); + ref->add_std_out(); + ref->add_std_err(); +} + +auto add_sink(std::shared_ptr stream, const LogVerbosity &min, + const LogVerbosity &max) -> void { + auto &&[lock, ref] = detail::global_journal.mut(); + ref->add_sink(std::make_unique(stream, min, max)); +} + +auto add_file(std::string_view file_path, std::ios_base::openmode open_mode, + const LogVerbosity &min, const LogVerbosity &max) -> void { + auto &&[lock, ref] = detail::global_journal.mut(); + ref->add_file(std::string(file_path), open_mode, min, max); } } // namespace journal diff --git a/src/main.cc b/src/main.cc index 7b32b3c..876a421 100644 --- a/src/main.cc +++ b/src/main.cc @@ -3,11 +3,11 @@ #include int main(int argc, const char* argv[]) { - std::cin.get(); - journal::init_stdout(); - printf("Hello, Alloy!\n"); - journal::info("some info!"); - journal::error("something went wrong!"); - journal::trace("just some very verbose stuff here"); - return 1; + journal::init_std_out_err(); + journal::add_file("test.log"); + journal::info("some info!"); + journal::error("something went wrong!"); + std::cin.get(); + journal::trace("just some very verbose stuff here"); + return 1; } \ No newline at end of file diff --git a/test.log b/test.log new file mode 100644 index 0000000..62e4165 --- /dev/null +++ b/test.log @@ -0,0 +1,2 @@ +[2022-06-20 00:49:18 UTC] [INFO ] [main.cc::8] some info! +[2022-06-20 00:49:18 UTC] [ERROR] [main.cc::9] something went wrong!