added sinks types

This commit is contained in:
janis 2022-06-20 20:31:07 +01:00
parent 31d1dda7f8
commit c79004413d
4 changed files with 230 additions and 32 deletions

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <format> #include <format>
#include <ios>
#include <string_view> #include <string_view>
namespace journal { namespace journal {
@ -64,7 +65,16 @@ auto log_internal(const LogVerbosity &verbosity,
using detail::LogVerbosity; 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<std::ostream> stream,
const LogVerbosity &min = LogVerbosity::Info,
const LogVerbosity &max = LogVerbosity::Error) -> void;
#ifdef JOURNAL_ALL #ifdef JOURNAL_ALL
#define JOURNAL_TRACE #define JOURNAL_TRACE

View file

@ -3,14 +3,13 @@
#include <chrono> #include <chrono>
#include <compare> #include <compare>
#include <concepts> #include <concepts>
#include <cstdio> #include <filesystem>
#include <iostream> #include <fstream>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <optional> #include <optional>
#include <ostream>
#include <string_view> #include <string_view>
#include <type_traits>
#include <filesystem>
namespace journal { namespace journal {
namespace mutex { namespace mutex {
@ -69,19 +68,99 @@ public:
namespace detail { 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 { constexpr auto to_string(const LogVerbosity& verbosity) -> std::string {
switch (verbosity) { switch (verbosity) {
case LogVerbosity::Trace: case LogVerbosity::Trace:
return "Trace"; return "TRACE";
case LogVerbosity::Debug: case LogVerbosity::Debug:
return "Debug"; return "DEBUG";
case LogVerbosity::Info: case LogVerbosity::Info:
return "Info"; return "INFO ";
case LogVerbosity::Warn: case LogVerbosity::Warn:
return "Warn"; return "WARN ";
case LogVerbosity::Error: case LogVerbosity::Error:
return "Error"; return "ERROR";
} }
} }
struct Verbosity { struct Verbosity {
@ -90,12 +169,77 @@ struct Verbosity {
Verbosity(journal::detail::LogVerbosity &&value) : inner(value) {} Verbosity(journal::detail::LogVerbosity &&value) : inner(value) {}
Verbosity(const 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<std::underlying_type_t<decltype(inner)>>(inner) <=> return static_cast<std::underlying_type_t<decltype(inner)>>(inner) <=>
static_cast<std::underlying_type_t<decltype(rhs.inner)>>(rhs.inner); static_cast<std::underlying_type_t<decltype(rhs.inner)>>(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<std::ostream> ostream;
OStreamSink(const std::shared_ptr<std::ostream> &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::ostream>(
std::ofstream(file_path, std::ios::ios_base::out | open_mode))) {}
};
} // namespace sinks
struct Sink { struct Sink {
std::shared_ptr<std::ostream> stream; std::shared_ptr<std::ostream> stream;
Verbosity min_verbosity; Verbosity min_verbosity;
@ -112,25 +256,39 @@ struct Sink {
: stream(std::move(stream)), min_verbosity(min), : stream(std::move(stream)), min_verbosity(min),
max_verbosity(journal::detail::Error) {} max_verbosity(journal::detail::Error) {}
~Sink() { flush(); }
auto flush() -> void { stream->flush(); }
auto operator<<(std::string_view msg) { (*stream) << msg; } auto operator<<(std::string_view msg) { (*stream) << msg; }
}; };
class Journal { class Journal {
std::vector<Sink> sinks; std::vector<std::unique_ptr<sinks::BaseSink>> sinks;
std::chrono::system_clock::time_point last_flushed;
public: public:
Journal() {} Journal() {}
auto log(const Verbosity &verbosity, source_location source, auto log(const Verbosity &verbosity, source_location source,
std::string_view msg) { 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) { for (auto &&sink : sinks) {
if (verbosity >= sink.min_verbosity && verbosity <= sink.max_verbosity) { if (sink->should_sink_verbosity(verbosity)) {
sink << std::format("[{}] [{: <5}] [{}] {}\n", (*sink) << format(msg, verbosity, source);
get_current_time_string(), to_string(verbosity.inner), }
get_source_location_string(source), msg); 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 { auto get_source_location_string(const source_location& source) -> std::string {
const auto file = std::filesystem::path(source.file_name()).filename().string(); const auto file = std::filesystem::path(source.file_name()).filename().string();
return std::format("{}::{}", file, source.line()); 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); return std::format("{:%Y-%m-%d %H:%M:%OS} UTC", now);
} }
auto add_std_out(const Verbosity &min = journal::detail::LogVerbosity::Info) { auto add_file(const std::string &file_name,
auto out = std::shared_ptr<std::ostream>(&std::cout, [](auto &&_) {}); std::ios::ios_base::openmode open_mode = std::ios::ios_base::app,
sinks.emplace_back(std::move(out)); const Verbosity &min = LogVerbosity::Trace,
const Verbosity &max = LogVerbosity::Error) -> void {
add_sink(std::make_unique<sinks::FileSink>(file_name, open_mode, min, max));
} }
auto add_sink(std::unique_ptr<sinks::BaseSink> &&sink) -> void {
sinks.emplace_back(std::move(sink));
}
auto add_std_out() { add_sink(std::make_unique<sinks::StdOutSink>()); }
auto add_std_err() { add_sink(std::make_unique<sinks::StdErrSink>()); }
}; };
static mutex::Mutex<std::unique_ptr<Journal>> global_journal; static mutex::Mutex<std::unique_ptr<Journal>> global_journal;
@ -158,16 +325,35 @@ auto log_internal(const LogVerbosity &verbosity, detail::source_location source,
} }
} // namespace detail } // namespace detail
auto init() { auto init() -> void {
auto &&[lock, ref] = detail::global_journal.mut(); auto &&[lock, ref] = detail::global_journal.mut();
if (!ref.get()) { if (!ref.get()) {
ref = std::make_unique<detail::Journal>(); ref = std::make_unique<detail::Journal>();
} }
} }
auto init_stdout(const LogVerbosity &min) -> void { auto init_std_out(const LogVerbosity &min) -> void {
init(); init();
auto &&[lock, ref] = detail::global_journal.mut(); auto &&[lock, ref] = detail::global_journal.mut();
ref->add_std_out(); 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<std::ostream> stream, const LogVerbosity &min,
const LogVerbosity &max) -> void {
auto &&[lock, ref] = detail::global_journal.mut();
ref->add_sink(std::make_unique<detail::sinks::OStreamSink>(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 } // namespace journal

View file

@ -3,11 +3,11 @@
#include <iostream> #include <iostream>
int main(int argc, const char* argv[]) { int main(int argc, const char* argv[]) {
std::cin.get(); journal::init_std_out_err();
journal::init_stdout(); journal::add_file("test.log");
printf("Hello, Alloy!\n"); journal::info("some info!");
journal::info("some info!"); journal::error("something went wrong!");
journal::error("something went wrong!"); std::cin.get();
journal::trace("just some very verbose stuff here"); journal::trace("just some very verbose stuff here");
return 1; return 1;
} }

2
test.log Normal file
View file

@ -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!