#pragma once #include #include #include #include #include #include #include #include #include namespace ztu { namespace logger::settings { /** * @brief Placeholder symbol used in format strings to signal value position. */ constexpr char format_symbol = '%'; /** * @brief symbol for escaping the format symbol. */ constexpr char format_escape_symbol = '\\'; /** * @brief Fall back to dynamic global config (instead of the static one) if none is provided. */ constexpr auto use_dynamic_global_config = false; /** * @brief Throw a compile time error if there are errors in a given format string.. */ constexpr auto compile_time_exceptions = true; /** * @brief Throw a run time error if there are errors in a given format string.. */ inline auto run_time_exceptions = false; } // namespace logger::settings namespace logger_internal { template concept printeable = requires(std::ostream& os, const T& value) { { os << value } -> std::same_as; }; namespace format_errors { using type = std::uint8_t; static constexpr type none{ 0b0 }, too_many_values = error_t{ 0b1 }, not_enough_values{ 0b10 }; } // namespace format_errors template struct basic_format_string { using string_view = std::basic_string_view; using size_type = string_view::size_type; string_view view; std::array seg_lens; format_errors::type errors{ format_errors::none }; template consteval basic_format_string(const CharT (&s)[N]) : view(s) { compile(logger::settings::compile_time_exceptions); } template requires(std::is_convertible_v) basic_format_string(const S& s) : view(s) { compile(logger::settings::run_time_exceptions); } constexpr void compile(const bool throw_exceptions) { auto begin = view.data(); const auto end = begin + view.length(); for (auto& seg_len : seg_lens) { const auto it = find_format_symbol(begin, end); seg_len = it - begin; if (it == end) { if (throw_exceptions) { throw std::invalid_argument("More values than placeholders."); } errors = format_errors::too_many_values; begin = end; } else { begin = it + 1; } } if (find_format_symbol(begin, end) != end) { if (throw_exceptions) { throw std::invalid_argument("Fewer values than placeholders."); } errors = format_errors::not_enough_values; } } static constexpr const CharT* find_format_symbol(const CharT* begin, const CharT* const end) { auto escaped = false; while (begin != end) { if (*begin == logger::settings::format_escape_symbol) { escaped = true; } else { if (*begin == logger::settings::format_symbol and not escaped) { break; } escaped = false; } ++begin; } return begin; } }; template using format_string = basic_format_string...>; template requires(N > 0) struct basic_string_literal { std::array m_value{}; consteval basic_string_literal(const CharT (&str)[N]) { std::copy_n(std::begin(str), N, m_value.begin()); m_value.back() = '\0'; } [[nodiscard]] constexpr std::basic_string_view view() const { return { m_value.data(), N - 1 }; } }; template using string_literal = basic_string_literal; using flags_int_t = std::uint16_t; } // namespace logger_internal /** * @brief A lightweight logging library providing color-coded output and flexible configuration. * * @details This namespace serves as a wrapper around std::ostream, offering a * simple yet powerful logging interface with support for ANSI color-coded log * levels. It provides a global logging context as well as customizable logger * contexts, allowing users to configure output streams, log levels and more. */ namespace logger { /** * @brief Enumeration representing the log levels. * * The log levels are ordered from least verbose to most verbose, as follows: * - @c mute: Indicates that no log messages should be emitted. * - @c generic: A catch-all levels for generic log messages via 'println'. * - @c error: Used for critical errors that require immediate attention. * - @c warn: Used for potential issues that may need attention. * - @c info: Used for significant events and information about the process. * - @c log: General log messages providing information about the application's operation. * - @c debug: Detailed debug information helpful for troubleshooting. */ enum class level : std::uint8_t { mute = 0, generic = 1, error = 2, warn = 3, info = 4, log = 5, debug = 6 }; /** * @brief Enumeration of feature flag for the logger class. * * The `flag` enum is a set of feature flag used in the logger class to control the behavior of log message output. * Each flag corresponds to a specific feature or information component that can be included in log messages, * providing a flexible and customizable way to tailor the levels of detail included in log outputs * based on the specific needs of the application. * * @c none: Disables all flag. Log messages will only include the message without any additional information. * @c all: Enables all available flag. Log messages will include all following features: * @c colors: Enables color-coding for log messages, providing visual cues for different log levels. * @c locking: Enables the use of a spin lock mechanism for thread-safe log message output. * @c identifier -----------------------------------------------------------------------------------+ * @c thread_id -----------------------------------------------------------------------+ | * @c function_name ------------------------------------------------------------+ | | * @c line_number ------------------------------------------------------+ | | | * @c filename -----------------------------------------------+ | | | | * @c level_name ----------------------------------------+ | | | | | * @c timestamp -------------------------------+ | | | | | | * @c time ----------------+ | | | | | | | * @c date ----+ | | | | | | | | * | | | | | | | | | * example message: [2024-1-8][19:44:9.535][1704739449535][log][main.cpp:61][main][140654793804672][tester] Hello World */ enum class flag : logger_internal::flags_int_t { none = 0, all = std::numeric_limits::max(), colors = (1 << 0), locking = (1 << 1), date = (1 << 2), time = (1 << 3), timestamp = (1 << 4), level_name = (1 << 5), filename = (1 << 6), line_number = (1 << 7), function_name = (1 << 8), thread_id = (1 << 9) }; } // namespace logger } // namespace ztu [[nodiscard]] constexpr ztu::logger::flag operator|(const ztu::logger::flag& a, const ztu::logger::flag& b) { return static_cast( static_cast(a) | static_cast(b) ); } [[nodiscard]] constexpr ztu::logger::flag operator&(const ztu::logger::flag& a, const ztu::logger::flag& b) { return static_cast( static_cast(a) & static_cast(b) ); } [[nodiscard]] constexpr ztu::logger::flag operator^(const ztu::logger::flag& a, const ztu::logger::flag& b) { return static_cast( static_cast(a) ^ static_cast(b) ); } [[nodiscard]] constexpr ztu::logger::flag operator~(const ztu::logger::flag& a) { return static_cast(~static_cast(a)); } constexpr ztu::logger::flag& operator|=(ztu::logger::flag& a, const ztu::logger::flag& b) { return a = a | b; } constexpr ztu::logger::flag& operator&=(ztu::logger::flag& a, const ztu::logger::flag& b) { return a = a & b; } constexpr ztu::logger::flag& operator^=(ztu::logger::flag& a, const ztu::logger::flag& b) { return a = a ^ b; } inline std::ostream& operator<<(std::ostream& out, ztu::logger::level level) { switch (level) { using enum ztu::logger::level; case mute: return out << "mute"; case error: return out << "error"; case warn: return out << "warn"; case info: return out << "info"; case log: return out << "log"; case debug: return out << "debug"; case generic: return out << "generic"; default: return out << "unknown(" << std::to_string(static_cast(level)) << ")"; } } namespace ztu { namespace logger { /** * @brief Context structure for configuring the behavior of the logger class. * * The `context` struct provides a means to configure the logger's behavior by * specifying the output streams, log message formatting flag, log levels, and * an optional spin lock for thread safety. * * @c out: Pointer to the output stream for standard log messages. * @c err: Pointer to the output stream for error log messages. * @c lock: Atomic flag used for thread-safe log message output when the `locking` flag is enabled. */ struct context { std::ostream* out; std::ostream* err; std::atomic_flag lock = ATOMIC_FLAG_INIT; inline context(std::ostream&, std::ostream&); }; /** * @brief Dynamic config structure for configuring the behavior of the logger class. * * The `context` struct provides a means to configure the logger's behavior by sspecifying the output streams, * log message formatting flag, log levels, and an optional spin lock for thread safety. * * @param flag Flags representing the formatting options for log messages. See the flag enum for details. * @param threshold Log levels setting, controlling the verbosity of logged messages. See the levels enum for details. * @param identifier Atomic flag used for thread-safe log message output when the `locking` flag is enabled. */ struct dynamic_config { flag flags; level threshold; std::string identifier{}; }; /** * @brief Static config structure for configuring the behavior of the logger class. * * The `context` struct provides a means to configure the logger's behavior by sspecifying the output streams, * log message formatting flag, log levels, and an optional spin lock for thread safety. * * @tparam Flags Flags representing the formatting options for log messages. See the flag enum for details. * @tparam Threshold Log levels setting, controlling the verbosity of logged messages. See the levels enum for details. * @tparam Identifier Atomic flag used for thread-safe log message output when the `locking` flag is enabled. */ template struct static_config { static constexpr auto flags = Flags; static constexpr auto threshold = Threshold; static constexpr auto identifier = Identifier.view(); }; /** * @brief Returns the global fallback context used in all logging functions. * * @return A reference to the global context. */ inline context& global_context(); /** * @brief Returns the global dynamic fallback config used in all logging functions. * * @return A reference to the global dynamic config. */ inline dynamic_config& global_dynamic_config(); /** * @brief The global static fallback config used in all logging functions. */ inline constexpr auto global_static_config = static_config< (flag::colors | flag::locking /*| flag::time*/ | flag::level_name | flag::filename | flag::line_number), level::debug, "">{}; namespace functions { /** * @brief Formats and logs an error message using the @c global_handle. * * The format string may contain placeholders (@c fmt_symbol) * replaced by corresponding values provided in the arguments (@c args). * * @tparam Args Variadic template for the types of arguments passed to the format string. * @param ctx The logging context used for writing out the formatted message. * @param cfg The config for the message formatting and filtering. * @param fmt The format string for the error message, supporting placeholders. * @param args The values to be formatted and included in the error message. */ template struct error { explicit error( logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); explicit error( context& ctx, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); explicit error( const dynamic_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); template explicit error( const static_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); explicit error( context& ctx, const dynamic_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); template explicit error( context& ctx, const static_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); }; template error(logger_internal::format_string, Args&&...) -> error; template error(context& ctx, logger_internal::format_string, Args&&...) -> error; template error(const dynamic_config& cfg, logger_internal::format_string, Args&&...) -> error; template error(context& ctx, const dynamic_config& cfg, logger_internal::format_string, Args&&...) -> error; template error(const static_config& cfg, logger_internal::format_string, Args&&...) -> error; template error( context& ctx, const static_config& cfg, logger_internal::format_string, Args&&... ) -> error; /** * @brief Formats and logs a warn message using the @c global_handle. * * The format string may contain placeholders (@c fmt_symbol) * replaced by corresponding values provided in the arguments (@c args). * * @tparam Args Variadic template for the types of arguments passed to the format string. * @param ctx The logging context used for writing out the formatted message. * @param cfg The config for the message formatting and filtering. * @param fmt The format string for the warn message, supporting placeholders. * @param args The values to be formatted and included in the warn message. */ template struct warn { explicit warn( logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); explicit warn( context& ctx, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); explicit warn( const dynamic_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); template explicit warn( const static_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); explicit warn( context& ctx, const dynamic_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); template explicit warn( context& ctx, const static_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); }; template warn(logger_internal::format_string, Args&&...) -> warn; template warn(context& ctx, logger_internal::format_string, Args&&...) -> warn; template warn(const dynamic_config& cfg, logger_internal::format_string, Args&&...) -> warn; template warn(context& ctx, const dynamic_config& cfg, logger_internal::format_string, Args&&...) -> warn; template warn(const static_config& cfg, logger_internal::format_string, Args&&...) -> warn; template warn( context& ctx, const static_config& cfg, logger_internal::format_string, Args&&... ) -> warn; /** * @brief Formats and logs an info message using the @c global_handle. * * The format string may contain placeholders (@c fmt_symbol) * replaced by corresponding values provided in the arguments (@c args). * * @tparam Args Variadic template for the types of arguments passed to the format string. * @param ctx The logging context used for writing out the formatted message. * @param cfg The config for the message formatting and filtering. * @param fmt The format string for the info message, supporting placeholders. * @param args The values to be formatted and included in the info message. */ template struct info { explicit info( logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); explicit info( context& ctx, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); explicit info( const dynamic_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); template explicit info( const static_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); explicit info( context& ctx, const dynamic_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); template explicit info( context& ctx, const static_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); }; template info(logger_internal::format_string, Args&&...) -> info; template info(context& ctx, logger_internal::format_string, Args&&...) -> info; template info(const dynamic_config& cfg, logger_internal::format_string, Args&&...) -> info; template info(context& ctx, const dynamic_config& cfg, logger_internal::format_string, Args&&...) -> info; template info(const static_config& cfg, logger_internal::format_string, Args&&...) -> info; template info( context& ctx, const static_config& cfg, logger_internal::format_string, Args&&... ) -> info; /** * @brief Formats and logs a log message using the @c global_handle. * * The format string may contain placeholders (@c fmt_symbol) * replaced by corresponding values provided in the arguments (@c args). * * @tparam Args Variadic template for the types of arguments passed to the format string. * @param ctx The logging context used for writing out the formatted message. * @param cfg The config for the message formatting and filtering. * @param fmt The format string for the log message, supporting placeholders. * @param args The values to be formatted and included in the log message. */ template struct log { explicit log( logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); explicit log( context& ctx, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); explicit log( const dynamic_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); template explicit log( const static_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); explicit log( context& ctx, const dynamic_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); template explicit log( context& ctx, const static_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); }; template log(logger_internal::format_string, Args&&...) -> log; template log(context& ctx, logger_internal::format_string, Args&&...) -> log; template log(const dynamic_config& cfg, logger_internal::format_string, Args&&...) -> log; template log(context& ctx, const dynamic_config& cfg, logger_internal::format_string, Args&&...) -> log; template log(const static_config& cfg, logger_internal::format_string, Args&&...) -> log; template log(context& ctx, const static_config& cfg, logger_internal::format_string, Args&&...) -> log; /** * @brief Formats and logs a debug message using the @c global_handle. * * The format string may contain placeholders (@c fmt_symbol) * replaced by corresponding values provided in the arguments (@c args). * * @tparam Args Variadic template for the types of arguments passed to the format string. * @param ctx The logging context used for writing out the formatted message. * @param cfg The config for the message formatting and filtering. * @param fmt The format string for the debug message, supporting placeholders. * @param args The values to be formatted and included in the debug message. */ template struct debug { explicit debug( logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); explicit debug( context& ctx, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); explicit debug( const dynamic_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); template explicit debug( const static_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); explicit debug( context& ctx, const dynamic_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); template explicit debug( context& ctx, const static_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); }; template debug(logger_internal::format_string, Args&&...) -> debug; template debug(context& ctx, logger_internal::format_string, Args&&...) -> debug; template debug(const dynamic_config& cfg, logger_internal::format_string, Args&&...) -> debug; template debug(context& ctx, const dynamic_config& cfg, logger_internal::format_string, Args&&...) -> debug; template debug(const static_config& cfg, logger_internal::format_string, Args&&...) -> debug; template debug( context& ctx, const static_config& cfg, logger_internal::format_string, Args&&... ) -> debug; /** * @brief Formats and logs a generic message using the @c global_handle. * * The format string may contain placeholders (@c fmt_symbol) * replaced by corresponding values provided in the arguments (@c args). * * @tparam Args Variadic template for the types of arguments passed to the format string. * @param ctx The logging context used for writing out the formatted message. * @param cfg The config for the message formatting and filtering. * @param fmt The format string for the generic message, supporting placeholders. * @param args The values to be formatted and included in the generic message. */ template struct println { explicit println( logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); explicit println( context& ctx, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); explicit println( const dynamic_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); template explicit println( const static_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); explicit println( context& ctx, const dynamic_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); template explicit println( context& ctx, const static_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location = std::source_location::current() ); }; template println(logger_internal::format_string, Args&&...) -> println; template println(context& ctx, logger_internal::format_string, Args&&...) -> println; template println(const dynamic_config& cfg, logger_internal::format_string, Args&&...) -> println; template println(context& ctx, const dynamic_config& cfg, logger_internal::format_string, Args&&...) -> println; template println(const static_config& cfg, logger_internal::format_string, Args&&...) -> println; template println( context& ctx, const static_config& cfg, logger_internal::format_string, Args&&... ) -> println; } // namespace functions } // namespace logger namespace logger_internal { enum class color : std::uint8_t { reset = 0, black = 30, red = 31, green = 32, yellow = 33, blue = 34, magenta = 35, cyan = 36, white = 37 }; constexpr std::size_t num_digits(std::uint32_t n, const std::uint32_t base = 10) { if (n < base) { return 1; } auto digits = std::size_t{}; while (n > 0) { n /= base; ++digits; } return digits; } template constexpr auto int_to_str() { auto str = std::array{}; auto index = str.size() - 1; auto num = N; do { str[index] = static_cast('0' + num % B); num /= B; } while (index--); return str; } template constexpr auto create_ansi_color() { constexpr auto color_index = static_cast(color); constexpr auto color_index_str = int_to_str(); constexpr auto prefix = std::string_view("\x1B["); constexpr auto postfix = [&]() -> std::string_view { if constexpr (bright) { return ";1m"; } else { return "m"; } }(); constexpr auto color_str_length = (prefix.size() + color_index_str.size() + postfix.size()); std::array color_str{}; auto it = color_str.begin(); const auto append = [&it](auto begin, const auto end) { while (begin != end) { *it++ = *begin++; } }; append(prefix.begin(), prefix.end()); append(color_index_str.begin(), color_index_str.end()); append(postfix.begin(), postfix.end()); return color_str; } inline constexpr auto reset_color_array = create_ansi_color(); inline constexpr auto reset_color = std::string_view(reset_color_array.data(), reset_color_array.size()); inline constexpr auto internal_color_array = create_ansi_color(); inline constexpr auto internal_color = std::string_view(internal_color_array.data(), internal_color_array.size()); inline constexpr std::size_t level_count = 6; inline constexpr std::array level_names{ "generic", "error", "warning", "info", "log", "debug" }; template constexpr auto create_color_tuple() { return std::make_tuple(create_ansi_color()...); } template constexpr auto build_string_lookup(const std::tuple& string_tuple, std::index_sequence) { return std::array{ std::string_view(std::get(string_tuple).data(), std::get(string_tuple).size())... }; } inline constexpr auto level_dark_color_tuple = create_color_tuple< false, color::white, color::red, color::yellow, color::green, color::blue, color::magenta>(); inline constexpr auto level_dark_color_lookup = build_string_lookup( level_dark_color_tuple, std::make_index_sequence{} ); inline constexpr auto level_bright_color_tuple = create_color_tuple< true, color::white, color::red, color::yellow, color::green, color::blue, color::magenta>(); inline constexpr auto level_bright_color_lookup = build_string_lookup( level_bright_color_tuple, std::make_index_sequence{} ); inline void print_escaped_string( std::ostream& out, const std::string_view& reset_clr, const std::string_view& internal_clr, const char* begin, const char* const end ) { auto escaped = false; auto it = begin; while (it != end) { if (*it == logger::settings::format_escape_symbol) { escaped = true; } else { if (*it == logger::settings::format_symbol) { out.write(begin, it - begin - escaped); begin = it + 1 - escaped; if (not escaped) { out << internal_clr << logger::settings::format_symbol << reset_clr; } } escaped = false; } ++it; } out.write(begin, it - begin); } template void print_format_impl( std::ostream& out, const std::string_view&, std::string_view& reset_clr, const std::string_view& internal_clr, std::size_t pos, bool&, const Fmt& fmt ) { print_escaped_string(out, reset_clr, internal_clr, &fmt.view[pos], fmt.view.data() + fmt.view.length()); } template void print_format_impl( std::ostream& out, const std::string_view& value_clr, std::string_view& reset_clr, const std::string_view& internal_clr, std::size_t pos, bool& excess_value, Fmt& fmt, Arg&& arg, Rest&&... rest ) { const auto arg_index = fmt.seg_lens.size() - sizeof...(Rest) - 1; const auto len = fmt.seg_lens[arg_index]; const auto str = &fmt.view[pos]; print_escaped_string(out, reset_clr, internal_clr, str, str + len); pos += len; if (pos == fmt.view.length()) { if (excess_value) { out << ','; } else { out << internal_clr << " <["; excess_value = true; } reset_clr = internal_clr; } else { ++pos; } out << value_clr << arg << reset_clr; print_format_impl(out, value_clr, reset_clr, internal_clr, pos, excess_value, fmt, std::forward(rest)...); } template void print_format( std::ostream& out, const std::string_view& value_clr, std::string_view& reset_clr, const std::string_view& internal_clr, Fmt& fmt, Args&&... args ) { const auto reset = reset_clr; auto excess_value = false; print_format_impl(out, value_clr, reset_clr, internal_clr, 0ul, excess_value, fmt, std::forward(args)...); if (fmt.errors & format_errors::too_many_values) { out << "] Excess value(s)>"; } else if (fmt.errors & format_errors::not_enough_values) { out << internal_clr << " "; } out << reset; } template void println_impl( logger::context& ctx, const logger::dynamic_config& cfg, const std::source_location& location, logger::level threshold, format_string fmt, Args&&... args ) { using namespace logger; const auto current_level_index = static_cast(cfg.threshold); auto level_index = static_cast(threshold); if (current_level_index < level_index) { return; } if ((cfg.flags & flag::locking) != flag::none) { while (ctx.lock.test_and_set(std::memory_order_relaxed)) { } } level_index = std::min(level_index, static_cast(level_count)) - 1; std::string_view bright_clr, dark_clr, reset_clr, internal_clr; if ((cfg.flags & flag::colors) != flag::none) { bright_clr = level_bright_color_lookup[level_index]; dark_clr = level_dark_color_lookup[level_index]; reset_clr = reset_color; internal_clr = internal_color; } auto& stream = (threshold == level::warn or threshold == level::error) ? *ctx.err : *ctx.out; const auto print_prefix = [&](const auto&... values) { stream << '[' << bright_clr; ((stream << values), ...) << reset_clr << ']'; }; static constexpr auto needs_clock = flag::date | flag::time | flag::timestamp; if ((cfg.flags & needs_clock) != flag::none) { namespace chr = std::chrono; using clock = chr::system_clock; const auto now = clock::now(); static constexpr auto needs_time = flag::date | flag::time; if ((cfg.flags & needs_time) != flag::none) { const auto time = clock::to_time_t(now); const auto local_time = std::localtime(&time); if ((cfg.flags & flag::date) != flag::none) { print_prefix(1'900 + local_time->tm_year, '-', 1 + local_time->tm_mon, '-', local_time->tm_mday); } if ((cfg.flags & flag::time) != flag::none) { const auto truncated = std::chrono::system_clock::from_time_t(time); const auto millis = chr::duration_cast(now - truncated).count(); print_prefix(local_time->tm_hour, ':', local_time->tm_min, ':', local_time->tm_sec, '.', millis); } } if ((cfg.flags & flag::timestamp) != flag::none) { const auto timestamp = chr::duration_cast(now.time_since_epoch()); print_prefix(timestamp.count()); } } if ((cfg.flags & flag::level_name) != flag::none) { print_prefix(level_names[level_index]); } if ((cfg.flags & flag::filename) != flag::none) { if ((cfg.flags & flag::line_number) != flag::none) { print_prefix(' ', location.file_name(), ':', std::dec, location.line()); } else { print_prefix(location.file_name()); } } else if ((cfg.flags & flag::line_number) != flag::none) { print_prefix(std::dec, location.line()); } if ((cfg.flags & flag::function_name) != flag::none) { print_prefix(location.function_name()); } if ((cfg.flags & flag::thread_id) != flag::none) { print_prefix(std::this_thread::get_id()); } if (not cfg.identifier.empty()) { print_prefix(cfg.identifier); } static constexpr auto prefix_flags = ~(flag::colors | flag::locking); if ((cfg.flags & prefix_flags) != flag::none or not cfg.identifier.empty()) { stream << ' '; } logger_internal::print_format(stream, dark_clr, reset_clr, internal_clr, fmt, std::forward(args)...); stream << '\n'; ctx.lock.clear(); } template void println_impl( logger::context& ctx, const logger::static_config&, const std::source_location& location, logger::level threshold, format_string fmt, Args&&... args ) { using namespace logger; constexpr auto current_level_index = static_cast(Threshold); auto level_index = static_cast(threshold); if (current_level_index < level_index) { return; } if constexpr ((Flags & flag::locking) != flag::none) { while (ctx.lock.test_and_set(std::memory_order_relaxed)) { } } level_index = std::min(level_index, static_cast(level_count)) - 1; std::string_view bright_clr, dark_clr, reset_clr, internal_clr; if constexpr ((Flags & flag::colors) != flag::none) { bright_clr = level_bright_color_lookup[level_index]; dark_clr = level_dark_color_lookup[level_index]; reset_clr = reset_color; internal_clr = internal_color; } auto& stream = (threshold == level::warn or threshold == level::error) ? *ctx.err : *ctx.out; const auto print_prefix = [&](const auto&... values) { stream << '[' << bright_clr; ((stream << values), ...) << reset_clr << ']'; }; static constexpr auto needs_clock = flag::date | flag::time | flag::timestamp; if constexpr ((Flags & needs_clock) != flag::none) { namespace chr = std::chrono; using clock = chr::system_clock; const auto now = clock::now(); static constexpr auto needs_time = flag::date | flag::time; if constexpr ((Flags & needs_time) != flag::none) { const auto time = clock::to_time_t(now); const auto local_time = std::localtime(&time); if constexpr ((Flags & flag::date) != flag::none) { print_prefix(1'900 + local_time->tm_year, '-', 1 + local_time->tm_mon, '-', local_time->tm_mday); } if constexpr ((Flags & flag::time) != flag::none) { const auto truncated = std::chrono::system_clock::from_time_t(time); const auto millis = chr::duration_cast(now - truncated).count(); print_prefix(local_time->tm_hour, ':', local_time->tm_min, ':', local_time->tm_sec, '.', millis); } } if ((Flags & flag::timestamp) != flag::none) { const auto timestamp = chr::duration_cast(now.time_since_epoch()); print_prefix(timestamp.count()); } } if constexpr ((Flags & flag::level_name) != flag::none) { print_prefix(level_names[level_index]); } if constexpr ((Flags & flag::filename) != flag::none) { if constexpr ((Flags & flag::line_number) != flag::none) { print_prefix(' ', location.file_name(), ':', std::dec, location.line()); } else { print_prefix(location.file_name()); } } else if constexpr ((Flags & flag::line_number) != flag::none) { print_prefix(std::dec, location.line()); } if constexpr ((Flags & flag::function_name) != flag::none) { print_prefix(location.function_name()); } if constexpr ((Flags & flag::thread_id) != flag::none) { print_prefix(std::this_thread::get_id()); } constexpr auto identifier = Identifier.view(); if constexpr (not identifier.empty()) { print_prefix(identifier); } static constexpr auto prefix_flags = ~(flag::colors | flag::locking); if constexpr ((Flags & prefix_flags) != flag::none or not identifier.empty()) { stream << ' '; } logger_internal::print_format(stream, dark_clr, reset_clr, internal_clr, fmt, std::forward(args)...); stream << '\n'; ctx.lock.clear(); } } // namespace logger_internal namespace logger { context::context(std::ostream& new_out, std::ostream& new_err) : out{ &new_out }, err{ &new_err } { } context& global_context() { static auto ctx = context(std::cout, std::cerr); return ctx; } dynamic_config& global_dynamic_config() { static auto cfg = dynamic_config{ (flag::colors | flag::locking | flag::time | flag::level_name | flag::filename | flag::line_number), level::debug, "" }; return cfg; } namespace functions { template error::error( logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { if constexpr (settings::use_dynamic_global_config) { println_impl( global_context(), global_dynamic_config(), location, level::error, fmt, std::forward(args)... ); } else { println_impl(global_context(), global_static_config, location, level::error, fmt, std::forward(args)...); } } template error::error( context& ctx, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { if constexpr (settings::use_dynamic_global_config) { println_impl(ctx, global_dynamic_config(), location, level::error, fmt, std::forward(args)...); } else { println_impl(ctx, global_static_config, location, level::error, fmt, std::forward(args)...); } } template error::error( const dynamic_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { println_impl(global_context(), cfg, location, level::error, fmt, std::forward(args)...); } template template error::error( const static_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { println_impl(global_context(), cfg, location, level::error, fmt, std::forward(args)...); } template error::error( context& ctx, const dynamic_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { println_impl(ctx, cfg, location, level::error, fmt, std::forward(args)...); } template template error::error( context& ctx, const static_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { println_impl(ctx, cfg, location, level::error, fmt, std::forward(args)...); } template warn::warn(logger_internal::format_string fmt, Args&&... args, const std::source_location& location) { if constexpr (settings::use_dynamic_global_config) { println_impl( global_context(), global_dynamic_config(), location, level::warn, fmt, std::forward(args)... ); } else { println_impl(global_context(), global_static_config, location, level::warn, fmt, std::forward(args)...); } } template warn::warn( context& ctx, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { if constexpr (settings::use_dynamic_global_config) { println_impl(ctx, global_dynamic_config(), location, level::warn, fmt, std::forward(args)...); } else { println_impl(ctx, global_static_config, location, level::warn, fmt, std::forward(args)...); } } template warn::warn( const dynamic_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { println_impl(global_context(), cfg, location, level::warn, fmt, std::forward(args)...); } template template warn::warn( const static_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { println_impl(global_context(), cfg, location, level::warn, fmt, std::forward(args)...); } template warn::warn( context& ctx, const dynamic_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { println_impl(ctx, cfg, location, level::warn, fmt, std::forward(args)...); } template template warn::warn( context& ctx, const static_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { println_impl(ctx, cfg, location, level::warn, fmt, std::forward(args)...); } template info::info(logger_internal::format_string fmt, Args&&... args, const std::source_location& location) { if constexpr (settings::use_dynamic_global_config) { println_impl( global_context(), global_dynamic_config(), location, level::info, fmt, std::forward(args)... ); } else { println_impl(global_context(), global_static_config, location, level::info, fmt, std::forward(args)...); } } template info::info( context& ctx, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { if constexpr (settings::use_dynamic_global_config) { println_impl(ctx, global_dynamic_config(), location, level::info, fmt, std::forward(args)...); } else { println_impl(ctx, global_static_config, location, level::info, fmt, std::forward(args)...); } } template info::info( const dynamic_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { println_impl(global_context(), cfg, location, level::info, fmt, std::forward(args)...); } template template info::info( const static_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { println_impl(global_context(), cfg, location, level::info, fmt, std::forward(args)...); } template info::info( context& ctx, const dynamic_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { println_impl(ctx, cfg, location, level::info, fmt, std::forward(args)...); } template template info::info( context& ctx, const static_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { println_impl(ctx, cfg, location, level::info, fmt, std::forward(args)...); } template log::log(logger_internal::format_string fmt, Args&&... args, const std::source_location& location) { if constexpr (settings::use_dynamic_global_config) { println_impl(global_context(), global_dynamic_config(), location, level::log, fmt, std::forward(args)...); } else { println_impl(global_context(), global_static_config, location, level::log, fmt, std::forward(args)...); } } template log::log( context& ctx, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { if constexpr (settings::use_dynamic_global_config) { println_impl(ctx, global_dynamic_config(), location, level::log, fmt, std::forward(args)...); } else { println_impl(ctx, global_static_config, location, level::log, fmt, std::forward(args)...); } } template log::log( const dynamic_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { println_impl(global_context(), cfg, location, level::log, fmt, std::forward(args)...); } template template log::log( const static_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { println_impl(global_context(), cfg, location, level::log, fmt, std::forward(args)...); } template log::log( context& ctx, const dynamic_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { println_impl(ctx, cfg, location, level::log, fmt, std::forward(args)...); } template template log::log( context& ctx, const static_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { println_impl(ctx, cfg, location, level::log, fmt, std::forward(args)...); } template debug::debug( logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { if constexpr (settings::use_dynamic_global_config) { println_impl( global_context(), global_dynamic_config(), location, level::debug, fmt, std::forward(args)... ); } else { println_impl(global_context(), global_static_config, location, level::debug, fmt, std::forward(args)...); } } template debug::debug( context& ctx, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { if constexpr (settings::use_dynamic_global_config) { println_impl(ctx, global_dynamic_config(), location, level::debug, fmt, std::forward(args)...); } else { println_impl(ctx, global_static_config, location, level::debug, fmt, std::forward(args)...); } } template debug::debug( const dynamic_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { println_impl(global_context(), cfg, location, level::debug, fmt, std::forward(args)...); } template template debug::debug( const static_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { println_impl(global_context(), cfg, location, level::debug, fmt, std::forward(args)...); } template debug::debug( context& ctx, const dynamic_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { println_impl(ctx, cfg, location, level::debug, fmt, std::forward(args)...); } template template debug::debug( context& ctx, const static_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { println_impl(ctx, cfg, location, level::debug, fmt, std::forward(args)...); } template println::println( logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { if constexpr (settings::use_dynamic_global_config) { println_impl( global_context(), global_dynamic_config(), location, level::generic, fmt, std::forward(args)... ); } else { println_impl( global_context(), global_static_config, location, level::generic, fmt, std::forward(args)... ); } } template println::println( context& ctx, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { if constexpr (settings::use_dynamic_global_config) { println_impl(ctx, global_dynamic_config(), location, level::generic, fmt, std::forward(args)...); } else { println_impl(ctx, global_static_config, location, level::generic, fmt, std::forward(args)...); } } template println::println( const dynamic_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { println_impl(global_context(), cfg, location, level::generic, fmt, std::forward(args)...); } template template println::println( const static_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { println_impl(global_context(), cfg, location, level::generic, fmt, std::forward(args)...); } template println::println( context& ctx, const dynamic_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { println_impl(ctx, cfg, location, level::generic, fmt, std::forward(args)...); } template template println::println( context& ctx, const static_config& cfg, logger_internal::format_string fmt, Args&&... args, const std::source_location& location ) { println_impl(ctx, cfg, location, level::generic, fmt, std::forward(args)...); } } // namespace functions using namespace functions; } // namespace logger } // namespace ztu