diff --git a/CHANGELOG.md b/CHANGELOG.md index f2239e06..61a02942 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -81,6 +81,17 @@ ## v8.0.0 +- Unified `JsonFileSink.h` and `JsonConsoleSink.h` into a single header, `JsonSink.h`, with both classes now sharing a + common implementation +- Users can now inherit from `JsonFileSink` or `JsonConsoleSink` and override the `generate_json_message(...)` function + to implement their own custom JSON log formats +- Simplified `ConsoleSink` by applying ANSI colour codes universally across all platforms, including Windows. The + previous Windows-specific implementation has been removed. Note that `quill::ConsoleColours` has been replaced with + `quill::ConsoleSink::Colours`, and `quill::ConsoleColours::ColourMode` has been renamed to + `quill::ConsoleSink::ColourMode`. + +## v7.4.0 + - Added accessors to `Logger` for sinks, user clock source, clock source type, and pattern formatter options that can be used to create another `Logger` with similar configuration - Fixed a build issue when compiling with `-fno-rtti`. This ensures compatibility with projects that disable @@ -97,6 +108,7 @@ ```cpp quill::Frontend::create_or_get_sink( "sink_id_1", quill::ConsoleColours::ColourMode::Automatic); + ``` ## v7.3.0 @@ -218,7 +230,7 @@ in the format message. ([#534](https://github.com/odygrd/quill/pull/534)) - `JSON` sinks now automatically remove any `\n` characters from format messages, ensuring the emission of valid `JSON` messages even when `\n` is present in the format. -- Replaced `static` variables with `static constexpr` in the `ConsoleColours` class. +- Replaced `static` variables with `static constexpr` in the `Colours` class. - Fixed compiler errors in a few rarely used macros. Added a comprehensive test for all macros to prevent similar issues in the future. - Expanded terminal list for color detection in console applications on Linux diff --git a/examples/console_logging.cpp b/examples/console_logging.cpp index 136a608e..c5e26335 100644 --- a/examples/console_logging.cpp +++ b/examples/console_logging.cpp @@ -25,7 +25,7 @@ int main() // Frontend auto console_sink = quill::Frontend::create_or_get_sink( - "sink_id_1", quill::ConsoleColours::ColourMode::Automatic); + "sink_id_1", quill::ConsoleSink::ColourMode::Automatic); quill::Logger* logger = quill::Frontend::create_or_get_logger("root", std::move(console_sink)); diff --git a/examples/custom_console_colours.cpp b/examples/custom_console_colours.cpp index 5de62869..1bb4af8e 100644 --- a/examples/custom_console_colours.cpp +++ b/examples/custom_console_colours.cpp @@ -18,13 +18,12 @@ int main() quill::Backend::start(backend_options); // Frontend - quill::ConsoleColours custom_console_colours; - custom_console_colours.set_default_colours(); - custom_console_colours.set_colour(quill::LogLevel::Info, quill::ConsoleColours::blue); // overwrite the colour for INFO + quill::ConsoleSink::Colours colours; + colours.apply_default_colours(); + colours.assign_colour_to_log_level(quill::LogLevel::Info, quill::ConsoleSink::Colours::blue); // overwrite the colour for INFO // Create the sink - auto console_sink = - quill::Frontend::create_or_get_sink("sink_id_1", custom_console_colours); + auto console_sink = quill::Frontend::create_or_get_sink("sink_id_1", colours); quill::Logger* logger = quill::Frontend::create_or_get_logger("root", std::move(console_sink)); diff --git a/examples/single_logger_multiple_sink_formats/ConsoleSinkWithFormatter.h b/examples/single_logger_multiple_sink_formats/ConsoleSinkWithFormatter.h index 6a097f8c..1f624f0b 100644 --- a/examples/single_logger_multiple_sink_formats/ConsoleSinkWithFormatter.h +++ b/examples/single_logger_multiple_sink_formats/ConsoleSinkWithFormatter.h @@ -13,8 +13,9 @@ class ConsoleSinkWithFormatter : public quill::ConsoleSink { public: ConsoleSinkWithFormatter(quill::PatternFormatterOptions const& pattern_formater_options, - bool enable_colours = true, std::string const& stream = "stdout") - : quill::ConsoleSink(enable_colours, stream), _formatter(pattern_formater_options) + quill::ConsoleSink::ColourMode colour_mode = quill::ConsoleSink::ColourMode::Automatic, + std::string const& stream = "stdout") + : quill::ConsoleSink(colour_mode, stream), _formatter(pattern_formater_options) { } diff --git a/include/quill/sinks/ConsoleSink.h b/include/quill/sinks/ConsoleSink.h index 25896a4f..0d6e9686 100644 --- a/include/quill/sinks/ConsoleSink.h +++ b/include/quill/sinks/ConsoleSink.h @@ -43,16 +43,9 @@ QUILL_BEGIN_NAMESPACE /** Forward Declaration **/ class MacroMetadata; -#if defined(_WIN32) -/** - * Represents console colours - */ -class ConsoleColours +/***/ +class ConsoleSink : public StreamSink { -private: - // define our own to avoid including windows.h in the header.. - using WORD = unsigned short; - public: enum class ColourMode { @@ -61,357 +54,250 @@ class ConsoleColours Never }; - ConsoleColours() - { - // by default _using_colours is false - _colours.fill(white); - } - - ~ConsoleColours() = default; - - /** - * Sets some default colours for terminal - */ - void set_default_colours() noexcept - { - set_colour(LogLevel::TraceL3, white); - set_colour(LogLevel::TraceL2, white); - set_colour(LogLevel::TraceL1, white); - set_colour(LogLevel::Debug, cyan); - set_colour(LogLevel::Info, green); - set_colour(LogLevel::Notice, white | bold); - set_colour(LogLevel::Warning, yellow | bold); - set_colour(LogLevel::Error, red | bold); - set_colour(LogLevel::Critical, on_red | bold | white); // white bold on red background - set_colour(LogLevel::Backtrace, magenta); - } - - /** - * Sets a custom colour per log level - * @param log_level the log level - * @param colour the colour - */ - void set_colour(LogLevel log_level, WORD colour) noexcept - { - auto const log_lvl = static_cast(log_level); - _colours[log_lvl] = colour; - _using_colours = true; - } - /** - * @return true if we are in terminal and have also enabled colours + * Represents console colours */ - QUILL_NODISCARD bool can_use_colours() const noexcept + class Colours { - return _can_use_colours && _using_colours; - } - - /** - * @return true if we have setup colours - */ - QUILL_NODISCARD bool using_colours() const noexcept { return _using_colours; } - - /** - * The colour for the given log level - * @param log_level the message log level - * @return the configured colour for this log level - */ - QUILL_NODISCARD WORD colour_code(LogLevel log_level) const noexcept - { - auto const log_lvl = static_cast(log_level); - return _colours[log_lvl]; - } - - static constexpr WORD bold = FOREGROUND_INTENSITY; - - static constexpr WORD black = 0; - static constexpr WORD red = FOREGROUND_RED; - static constexpr WORD green = FOREGROUND_GREEN; - static constexpr WORD yellow = FOREGROUND_RED | FOREGROUND_GREEN; - static constexpr WORD blue = FOREGROUND_BLUE; - static constexpr WORD magenta = FOREGROUND_RED | FOREGROUND_BLUE; - static constexpr WORD cyan = FOREGROUND_GREEN | FOREGROUND_BLUE; - static constexpr WORD white = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; - - static constexpr WORD on_red = BACKGROUND_RED; - static constexpr WORD on_green = BACKGROUND_GREEN; - static constexpr WORD on_yellow = BACKGROUND_RED | BACKGROUND_GREEN; - static constexpr WORD on_blue = BACKGROUND_BLUE; - static constexpr WORD on_magenta = BACKGROUND_RED | BACKGROUND_BLUE; - static constexpr WORD on_cyan = BACKGROUND_GREEN | BACKGROUND_BLUE; - static constexpr WORD on_white = BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE; - -private: - friend class ConsoleSink; - - /***/ - QUILL_NODISCARD QUILL_ATTRIBUTE_COLD static bool _is_in_terminal(FILE* file) noexcept - { - bool const is_atty = _isatty(_fileno(file)) != 0; + public: + Colours() + { + // by default _using_colours is false + _log_level_colours.fill(white); + } - // ::GetConsoleMode() should return 0 if file is redirected or does not point to the actual console - DWORD console_mode; - bool const is_console = - GetConsoleMode(reinterpret_cast(_get_osfhandle(_fileno(file))), &console_mode) != 0; + ~Colours() = default; - return is_atty && is_console; - } + /** + * Sets some default colours for terminal + */ + void apply_default_colours() noexcept + { + assign_colour_to_log_level(LogLevel::TraceL3, white); + assign_colour_to_log_level(LogLevel::TraceL2, white); + assign_colour_to_log_level(LogLevel::TraceL1, white); + assign_colour_to_log_level(LogLevel::Debug, cyan); + assign_colour_to_log_level(LogLevel::Info, green); + assign_colour_to_log_level(LogLevel::Notice, white_bold); + assign_colour_to_log_level(LogLevel::Warning, yellow_bold); + assign_colour_to_log_level(LogLevel::Error, red_bold); + assign_colour_to_log_level(LogLevel::Critical, bold_on_red); + assign_colour_to_log_level(LogLevel::Backtrace, magenta); + } - void _set_can_use_colours(FILE* file, ColourMode colour_mode) noexcept - { - if (colour_mode == ColourMode::Always) + /** + * Sets a custom colour per log level + * @param log_level the log level + * @param colour the colour + */ + void assign_colour_to_log_level(LogLevel log_level, std::string_view colour) noexcept { - _can_use_colours = true; + auto const log_lvl = static_cast(log_level); + _log_level_colours[log_lvl] = colour; + _colours_enabled = true; } - else if (colour_mode == ColourMode::Automatic) + + /** + * @return true if we are in terminal and have also enabled colours + */ + QUILL_NODISCARD bool colours_enabled() const noexcept { - _can_use_colours = _is_in_terminal(file); + return _colour_output_supported && _colours_enabled; } - else + + /** + * The colour for the given log level + * @param log_level the message log level + * @return the configured colour for this log level + */ + QUILL_NODISCARD std::string_view log_level_colour(LogLevel log_level) const noexcept { - _can_use_colours = false; + auto const log_lvl = static_cast(log_level); + return _log_level_colours[log_lvl]; } - } -private: - std::array _colours = {0}; /**< Colours per log level */ - bool _using_colours{false}; - bool _can_use_colours{false}; -}; + // Formatting codes + static constexpr std::string_view reset{"\033[0m"}; + static constexpr std::string_view bold{"\033[1m"}; + static constexpr std::string_view dark{"\033[2m"}; + static constexpr std::string_view underline{"\033[4m"}; + static constexpr std::string_view blink{"\033[5m"}; + static constexpr std::string_view reverse{"\033[7m"}; + static constexpr std::string_view concealed{"\033[8m"}; + static constexpr std::string_view clear_line{"\033[K"}; + + // Foreground colors + static constexpr std::string_view black{"\033[30m"}; + static constexpr std::string_view red{"\033[31m"}; + static constexpr std::string_view green{"\033[32m"}; + static constexpr std::string_view yellow{"\033[33m"}; + static constexpr std::string_view blue{"\033[34m"}; + static constexpr std::string_view magenta{"\033[35m"}; + static constexpr std::string_view cyan{"\033[36m"}; + static constexpr std::string_view white{"\033[37m"}; + + /// Background colors + static constexpr std::string_view on_black{"\033[40m"}; + static constexpr std::string_view on_red{"\033[41m"}; + static constexpr std::string_view on_green{"\033[42m"}; + static constexpr std::string_view on_yellow{"\033[43m"}; + static constexpr std::string_view on_blue{"\033[44m"}; + static constexpr std::string_view on_magenta{"\033[45m"}; + static constexpr std::string_view on_cyan{"\033[46m"}; + static constexpr std::string_view on_white{"\033[47m"}; + + /// Bold colors + static constexpr std::string_view white_bold{"\033[97m\033[1m"}; + static constexpr std::string_view yellow_bold{"\033[33m\033[1m"}; + static constexpr std::string_view red_bold{"\033[31m\033[1m"}; + static constexpr std::string_view bold_on_red{"\033[1m\033[41m"}; + + private: + friend class ConsoleSink; + + /***/ + QUILL_NODISCARD QUILL_ATTRIBUTE_COLD static bool _supports_colour_output() noexcept + { +#ifdef _WIN32 + // On Windows 10 and later, ANSI colors are supported + return true; #else -/** - * Represents console colours - */ -class ConsoleColours -{ -public: - enum class ColourMode - { - Always, - Automatic, - Never - }; - - ConsoleColours() - { - // by default _using_colours is false - _colours.fill(white); - } - - ~ConsoleColours() = default; - - /** - * Sets some default colours for terminal - */ - void set_default_colours() noexcept - { - set_colour(LogLevel::TraceL3, white); - set_colour(LogLevel::TraceL2, white); - set_colour(LogLevel::TraceL1, white); - set_colour(LogLevel::Debug, cyan); - set_colour(LogLevel::Info, green); - set_colour(LogLevel::Notice, white_bold); - set_colour(LogLevel::Warning, yellow_bold); - set_colour(LogLevel::Error, red_bold); - set_colour(LogLevel::Critical, bold_on_red); - set_colour(LogLevel::Backtrace, magenta); - } - - /** - * Sets a custom colour per log level - * @param log_level the log level - * @param colour the colour - */ - void set_colour(LogLevel log_level, std::string_view colour) noexcept - { - auto const log_lvl = static_cast(log_level); - _colours[log_lvl] = colour; - _using_colours = true; - } + // Get term from env + auto* env_p = std::getenv("TERM"); - /** - * @return true if we are in terminal and have also enabled colours - */ - QUILL_NODISCARD bool can_use_colours() const noexcept - { - return _can_use_colours && _using_colours; - } - - /** - * @return true if we have setup colours - */ - QUILL_NODISCARD bool using_colours() const noexcept { return _using_colours; } + if (env_p == nullptr) + { + return false; + } - /** - * The colour for the given log level - * @param log_level the message log level - * @return the configured colour for this log level - */ - QUILL_NODISCARD std::string_view colour_code(LogLevel log_level) const noexcept - { - auto const log_lvl = static_cast(log_level); - return _colours[log_lvl]; - } + static constexpr const char* terms[] = { + "ansi", "color", "console", "cygwin", "gnome", + "konsole", "kterm", "linux", "msys", "putty", + "rxvt", "screen", "vt100", "xterm", "tmux", + "terminator", "alacritty", "gnome-terminal", "xfce4-terminal", "lxterminal", + "mate-terminal", "uxterm", "eterm", "tilix", "rxvt-unicode", + "kde-konsole"}; - // Formatting codes - static constexpr std::string_view reset{"\033[0m"}; - static constexpr std::string_view bold{"\033[1m"}; - static constexpr std::string_view dark{"\033[2m"}; - static constexpr std::string_view underline{"\033[4m"}; - static constexpr std::string_view blink{"\033[5m"}; - static constexpr std::string_view reverse{"\033[7m"}; - static constexpr std::string_view concealed{"\033[8m"}; - static constexpr std::string_view clear_line{"\033[K"}; - - // Foreground colors - static constexpr std::string_view black{"\033[30m"}; - static constexpr std::string_view red{"\033[31m"}; - static constexpr std::string_view green{"\033[32m"}; - static constexpr std::string_view yellow{"\033[33m"}; - static constexpr std::string_view blue{"\033[34m"}; - static constexpr std::string_view magenta{"\033[35m"}; - static constexpr std::string_view cyan{"\033[36m"}; - static constexpr std::string_view white{"\033[37m"}; - - /// Background colors - static constexpr std::string_view on_black{"\033[40m"}; - static constexpr std::string_view on_red{"\033[41m"}; - static constexpr std::string_view on_green{"\033[42m"}; - static constexpr std::string_view on_yellow{"\033[43m"}; - static constexpr std::string_view on_blue{"\033[44m"}; - static constexpr std::string_view on_magenta{"\033[45m"}; - static constexpr std::string_view on_cyan{"\033[46m"}; - static constexpr std::string_view on_white{"\033[47m"}; - - /// Bold colors - static constexpr std::string_view white_bold{"\033[97m\033[1m"}; - static constexpr std::string_view yellow_bold{"\033[33m\033[1m"}; - static constexpr std::string_view red_bold{"\033[31m\033[1m"}; - static constexpr std::string_view bold_on_red{"\033[1m\033[41m"}; - -private: - friend class ConsoleSink; - - /***/ - QUILL_NODISCARD QUILL_ATTRIBUTE_COLD static bool _is_colour_terminal() noexcept - { - // Get term from env - auto* env_p = std::getenv("TERM"); + // Loop through each term and check if it's found in env_p + for (const char* term : terms) + { + if (std::strstr(env_p, term) != nullptr) + { + // term found + return true; + } + } - if (env_p == nullptr) - { + // none of the terms are found return false; +#endif } - static constexpr const char* terms[] = { - "ansi", "color", "console", "cygwin", "gnome", "konsole", - "kterm", "linux", "msys", "putty", "rxvt", "screen", - "vt100", "xterm", "tmux", "terminator", "alacritty", "gnome-terminal", - "xfce4-terminal", "lxterminal", "mate-terminal", "uxterm", "eterm", "tilix", - "rxvt-unicode", "kde-konsole"}; + /***/ + QUILL_NODISCARD QUILL_ATTRIBUTE_COLD static bool _is_terminal_output(FILE* file) noexcept + { +#ifdef _WIN32 + return _isatty(_fileno(file)) != 0; +#else + return ::isatty(fileno(file)) != 0; +#endif + } - // Loop through each term and check if it's found in env_p - for (const char* term : terms) +#ifdef _WIN32 + /***/ + QUILL_ATTRIBUTE_COLD void _activate_ansi_support(FILE* file) const { - if (std::strstr(env_p, term) != nullptr) + if (!_colour_output_supported) { - // term found - return true; + return; } - } - // none of the terms are found - return false; - } + // Try to enable ANSI support for Windows console + auto const out_handle = reinterpret_cast(_get_osfhandle(_fileno(file))); + if (out_handle == INVALID_HANDLE_VALUE) + { + return; + } - /***/ - QUILL_NODISCARD QUILL_ATTRIBUTE_COLD static bool _is_in_terminal(FILE* file) noexcept - { - return ::isatty(fileno(file)) != 0; - } + DWORD dw_mode = 0; + if (!GetConsoleMode(out_handle, &dw_mode)) + { + return; + } - /***/ - void _set_can_use_colours(FILE* file, ColourMode colour_mode) noexcept - { - if (colour_mode == ColourMode::Always) - { - _can_use_colours = true; - } - else if (colour_mode == ColourMode::Automatic) - { - _can_use_colours = _is_in_terminal(file) && _is_colour_terminal(); + dw_mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + dw_mode |= ENABLE_PROCESSED_OUTPUT; + + SetConsoleMode(out_handle, dw_mode); } - else +#endif + + /***/ + void _configure_colour_support(FILE* file, ColourMode colour_mode) noexcept { - _can_use_colours = false; - } - } + if (colour_mode == ColourMode::Always) + { + _colour_output_supported = true; + } + else if (colour_mode == ColourMode::Automatic) + { + _colour_output_supported = _is_terminal_output(file) && _supports_colour_output(); + } + else + { + _colour_output_supported = false; + } -private: - std::array _colours; /**< Colours per log level */ - bool _using_colours{false}; - bool _can_use_colours{false}; -}; +#ifdef _WIN32 + // Enable ANSI color support on Windows + _activate_ansi_support(file); #endif + } + + private: + std::array _log_level_colours; /**< Colours per log level */ + bool _colours_enabled{false}; + bool _colour_output_supported{false}; + }; -/***/ -class ConsoleSink : public StreamSink -{ -public: /** - * @brief Constructor - * @param console_colours console colours instance - * @param stream stream name can only be "stdout" or "stderr" + * @brief Constructor with custom ConsoleColours + * @param colours console colours instance * @param colour_mode Determines when console colours are enabled. * - Always: Colours are always enabled. * - Automatic: Colours are enabled automatically based on the environment (e.g., terminal support). * - Never: Colours are never enabled. - */ - explicit ConsoleSink(ConsoleColours const& console_colours, std::string const& stream = "stdout", - ConsoleColours::ColourMode colour_mode = ConsoleColours::ColourMode::Automatic) - : StreamSink{stream, nullptr}, _console_colours(console_colours) - { - assert((stream == "stdout") || (stream == "stderr")); - - // In this ctor we take a full copy of console_colours and in our instance we modify it - _console_colours._set_can_use_colours(_file, colour_mode); - } - - /** - * @brief Constructor - * @param enable_colours enable or disable console colours * @param stream stream name can only be "stdout" or "stderr" */ - explicit ConsoleSink(bool enable_colours = true, std::string const& stream = "stdout") - : StreamSink{stream, nullptr} + explicit ConsoleSink(Colours const& colours, ColourMode colour_mode = ColourMode::Automatic, + std::string const& stream = "stdout") + : StreamSink{stream, nullptr}, _colours(colours) { assert((stream == "stdout") || (stream == "stderr")); - if (enable_colours) - { - _console_colours._set_can_use_colours(_file, ConsoleColours::ColourMode::Automatic); - _console_colours.set_default_colours(); - } + // In this ctor we take a full copy of colours and in our instance we modify it + _colours._configure_colour_support(_file, colour_mode); } /** - * @brief Constructor + * @brief Constructor with default ConsoleColours * @param colour_mode Determines when console colours are enabled. * - Always: Colours are always enabled. * - Automatic: Colours are enabled automatically based on the environment (e.g., terminal support). * - Never: Colours are never enabled. * @param stream stream name can only be "stdout" or "stderr" */ - explicit ConsoleSink(ConsoleColours::ColourMode colour_mode, std::string const& stream = "stdout") + explicit ConsoleSink(ColourMode colour_mode = ColourMode::Automatic, + std::string const& stream = "stdout") : StreamSink{stream, nullptr} { assert((stream == "stdout") || (stream == "stderr")); - _console_colours._set_can_use_colours(_file, colour_mode); + _colours._configure_colour_support(_file, colour_mode); - if (colour_mode != ConsoleColours::ColourMode::Never) + if (colour_mode != ColourMode::Never) { - _console_colours.set_default_colours(); + _colours.apply_default_colours(); } } @@ -439,127 +325,32 @@ class ConsoleSink : public StreamSink std::vector> const* named_args, std::string_view log_message, std::string_view log_statement) override { -#if defined(_WIN32) - if (_console_colours.using_colours()) + if (_colours.colours_enabled()) { - WORD const colour_code = _console_colours.colour_code(log_level); - WORD orig_attribs{0}; - - QUILL_TRY - { - // Set foreground colour and store the original attributes - orig_attribs = _set_foreground_colour(colour_code); - } - #if !defined(QUILL_NO_EXCEPTIONS) - QUILL_CATCH(std::exception const& e) - { - // GetConsoleScreenBufferInfo can fail sometimes on windows, in that case still write - // the log without colours - StreamSink::write_log(log_metadata, log_timestamp, thread_id, thread_name, process_id, - logger_name, log_level, log_level_description, log_level_short_code, - named_args, log_message, log_statement); - - if (!_report_write_log_error_once) - { - // Report the error once - _report_write_log_error_once = true; - QUILL_THROW(QuillError{e.what()}); - } - - // do not resume further, we already wrote the log statement - return; - } - #endif - - auto out_handle = reinterpret_cast(_get_osfhandle(_fileno(_file))); - - // Write to console - bool const write_to_console = WriteConsoleA( - out_handle, log_statement.data(), static_cast(log_statement.size()), nullptr, nullptr); + // Write colour code + std::string_view const colour_code = _colours.log_level_colour(log_level); + safe_fwrite(colour_code.data(), sizeof(char), colour_code.size(), _file); - if (QUILL_UNLIKELY(!write_to_console)) - { - auto const error = std::error_code(GetLastError(), std::system_category()); - QUILL_THROW(QuillError{std::string{"WriteConsoleA failed. error: "} + error.message() + - std::string{" errno: "} + std::to_string(error.value())}); - } + // Write record to file + StreamSink::write_log(log_metadata, log_timestamp, thread_id, thread_name, process_id, + logger_name, log_level, log_level_description, log_level_short_code, + named_args, log_message, log_statement); - // reset to orig colors - bool const set_text_attr = SetConsoleTextAttribute(out_handle, orig_attribs); - if (QUILL_UNLIKELY(!set_text_attr)) - { - auto const error = std::error_code(GetLastError(), std::system_category()); - QUILL_THROW(QuillError{std::string{"SetConsoleTextAttribute failed. error: "} + error.message() + - std::string{" errno: "} + std::to_string(error.value())}); - } + // Reset colour code + safe_fwrite(Colours::reset.data(), sizeof(char), Colours::reset.size(), _file); } else { + // Write record to file StreamSink::write_log(log_metadata, log_timestamp, thread_id, thread_name, process_id, logger_name, log_level, log_level_description, log_level_short_code, named_args, log_message, log_statement); } -#else - if (_console_colours.can_use_colours()) - { - // Write colour code - std::string_view const colour_code = _console_colours.colour_code(log_level); - safe_fwrite(colour_code.data(), sizeof(char), colour_code.size(), _file); - } - - // Write record to file - StreamSink::write_log(log_metadata, log_timestamp, thread_id, thread_name, process_id, - logger_name, log_level, log_level_description, log_level_short_code, - named_args, log_message, log_statement); - - if (_console_colours.can_use_colours()) - { - safe_fwrite(ConsoleColours::reset.data(), sizeof(char), ConsoleColours::reset.size(), _file); - } -#endif } -private: -#if defined(_WIN32) - QUILL_NODISCARD ConsoleColours::WORD _set_foreground_colour(ConsoleColours::WORD attributes) - { - CONSOLE_SCREEN_BUFFER_INFO orig_buffer_info; - auto const out_handle = reinterpret_cast(_get_osfhandle(_fileno(_file))); - - bool const screen_buffer_info = GetConsoleScreenBufferInfo(out_handle, &orig_buffer_info); - if (QUILL_UNLIKELY(!screen_buffer_info)) - { - auto const error = std::error_code(GetLastError(), std::system_category()); - QUILL_THROW(QuillError{std::string{"GetConsoleScreenBufferInfo failed. error: "} + - error.message() + std::string{" errno: "} + std::to_string(error.value())}); - } - - WORD back_color = orig_buffer_info.wAttributes; - - // retrieve the current background color - back_color &= - static_cast(~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY)); - - // keep the background color unchanged - bool const console_text_attr = SetConsoleTextAttribute(out_handle, attributes | back_color); - if (QUILL_UNLIKELY(!console_text_attr)) - { - auto const error = std::error_code(GetLastError(), std::system_category()); - QUILL_THROW(QuillError{std::string{"SetConsoleTextAttribute failed. error: "} + error.message() + - std::string{" errno: "} + std::to_string(error.value())}); - } - - return orig_buffer_info.wAttributes; // return orig attribs - } -#endif - protected: // protected in case someone wants to derive from this class and create a custom one, e.g. for json logging to stdout - ConsoleColours _console_colours; - -#if defined(_WIN32) - bool _report_write_log_error_once{false}; -#endif + Colours _colours; }; QUILL_END_NAMESPACE \ No newline at end of file diff --git a/test/integration_tests/ConsoleSinkStderrMultipleFormatsTest.cpp b/test/integration_tests/ConsoleSinkStderrMultipleFormatsTest.cpp index 833cf41a..f37323e3 100644 --- a/test/integration_tests/ConsoleSinkStderrMultipleFormatsTest.cpp +++ b/test/integration_tests/ConsoleSinkStderrMultipleFormatsTest.cpp @@ -27,9 +27,9 @@ TEST_CASE("console_sink_stderr_multiple_formats") quill::testing::CaptureStderr(); // Set writing logging to a file - bool const using_colours = false; std::string const stream = "stderr"; - auto console_sink = Frontend::create_or_get_sink("console_sink", using_colours, stream); + auto console_sink = + Frontend::create_or_get_sink("console_sink", ConsoleSink::ColourMode::Never, stream); Logger* logger_a = Frontend::create_or_get_logger(logger_name_a, console_sink); Logger* logger_b = Frontend::create_or_get_logger( diff --git a/test/integration_tests/ConsoleSinkStdoutMultipleFormatsTest.cpp b/test/integration_tests/ConsoleSinkStdoutMultipleFormatsTest.cpp index 66ae0071..76215789 100644 --- a/test/integration_tests/ConsoleSinkStdoutMultipleFormatsTest.cpp +++ b/test/integration_tests/ConsoleSinkStdoutMultipleFormatsTest.cpp @@ -32,9 +32,9 @@ TEST_CASE("console_sink_stdout_multiple_formats") quill::testing::CaptureStdout(); // Set writing logging to a file - bool const using_colours = false; std::string const stream = "stdout"; - auto console_sink = Frontend::create_or_get_sink("console_sink", using_colours, stream); + auto console_sink = Frontend::create_or_get_sink( + "console_sink", quill::ConsoleSink::ColourMode::Never, stream); Logger* logger_a = Frontend::create_or_get_logger(logger_name_a, console_sink); Logger* logger_b = Frontend::create_or_get_logger( diff --git a/test/integration_tests/MultipleSinksSameLoggerTest.cpp b/test/integration_tests/MultipleSinksSameLoggerTest.cpp index 02289ae3..3318d7fb 100644 --- a/test/integration_tests/MultipleSinksSameLoggerTest.cpp +++ b/test/integration_tests/MultipleSinksSameLoggerTest.cpp @@ -41,9 +41,9 @@ TEST_CASE("multiple_sinks_same_logger") }(), FileEventNotifier{}); - bool const using_colours = false; std::string const stream = "stdout"; - auto console_sink = Frontend::create_or_get_sink("console_sink", using_colours, stream); + auto console_sink = Frontend::create_or_get_sink( + "console_sink", quill::ConsoleSink::ColourMode::Never, stream); std::string const logger_name = "logger"; Logger* logger =