added sinks types
This commit is contained in:
		
							parent
							
								
									31d1dda7f8
								
							
						
					
					
						commit
						c79004413d
					
				| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <format>
 | 
			
		||||
#include <ios>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
 | 
			
		||||
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<std::ostream> stream,
 | 
			
		||||
              const LogVerbosity &min = LogVerbosity::Info,
 | 
			
		||||
              const LogVerbosity &max = LogVerbosity::Error) -> void;
 | 
			
		||||
 | 
			
		||||
#ifdef JOURNAL_ALL
 | 
			
		||||
#define JOURNAL_TRACE
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										234
									
								
								src/journal.cc
									
									
									
									
									
								
							
							
						
						
									
										234
									
								
								src/journal.cc
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -3,14 +3,13 @@
 | 
			
		|||
#include <chrono>
 | 
			
		||||
#include <compare>
 | 
			
		||||
#include <concepts>
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <filesystem>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <optional>
 | 
			
		||||
#include <ostream>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
#include <type_traits>
 | 
			
		||||
#include <filesystem>
 | 
			
		||||
 | 
			
		||||
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<std::underlying_type_t<decltype(inner)>>(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 {
 | 
			
		||||
  std::shared_ptr<std::ostream> 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<Sink> sinks;
 | 
			
		||||
  std::vector<std::unique_ptr<sinks::BaseSink>> 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::ostream>(&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<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;
 | 
			
		||||
| 
						 | 
				
			
			@ -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<detail::Journal>();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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<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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										14
									
								
								src/main.cc
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								src/main.cc
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -3,11 +3,11 @@
 | 
			
		|||
#include <iostream>
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in a new issue