From 9a751be9a8c6fddec6dcc790b2623302d217bff1 Mon Sep 17 00:00:00 2001 From: Aster Seker Date: Sun, 7 Jun 2026 08:43:42 +0300 Subject: [PATCH 1/3] fix: ensure MultiStream always targets console and file logger CONSOLIX_LOG_STREAM now always writes to console (index 0) and file logger (index 3), plus any extra backends from m_logger_indices. Previously broadcast (-1) skipped single-mode backends (console), and non-empty m_logger_indices skipped console/file. --- include/consolix/components/LoggerComponent/MultiStream.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/consolix/components/LoggerComponent/MultiStream.hpp b/include/consolix/components/LoggerComponent/MultiStream.hpp index ed6d4b2..e20579f 100644 --- a/include/consolix/components/LoggerComponent/MultiStream.hpp +++ b/include/consolix/components/LoggerComponent/MultiStream.hpp @@ -94,9 +94,9 @@ namespace consolix { # endif // if defined(_WIN32) # if CONSOLIX_USE_LOGIT == 1 - if (LOGIT_IS_SINGLE_MODE(CONSOLIX_LOGIT_CONSOLE_INDEX)) { - logit::LogStream(m_level, m_file, m_line, m_function, CONSOLIX_LOGIT_CONSOLE_INDEX) << str; - } + // Always write to the console backend and file logger, + // then fan-out to any additional backends requested via _EX. + logit::LogStream(m_level, m_file, m_line, m_function, CONSOLIX_LOGIT_CONSOLE_INDEX) << str; logit::LogStream(m_level, m_file, m_line, m_function, CONSOLIX_LOGIT_LOGGER_INDEX) << str; for (int logger_index : m_logger_indices) { logit::LogStream(m_level, m_file, m_line, m_function, logger_index) << str; From 78910322dcc46313e33658f02dd9b07d2a8b7ecf Mon Sep 17 00:00:00 2001 From: Aster Seker Date: Sun, 7 Jun 2026 09:12:56 +0300 Subject: [PATCH 2/3] fix: restore Consolix log stream default routing --- CMakeLists.txt | 5 + README.md | 2 +- .../consolix/components/LoggerComponent.hpp | 6 +- .../LoggerComponent/MultiStream.hpp | 10 +- tests/test_log_stream_macros.cpp | 139 +++++++++++++++++- 5 files changed, 153 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 645c415..83f65ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,6 +140,11 @@ if(CONSOLIX_BUILD_TESTS) if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_log_stream_macros.cpp") consolix_add_test(test_log_stream_macros "tests/test_log_stream_macros.cpp") + target_compile_definitions(test_log_stream_macros + PRIVATE + CONSOLIX_FILE_LOGGER_PATH="tmp/agent-work/test-log-stream-macros" + CONSOLIX_UNIQUE_FILE_LOGGER_PATH="tmp/agent-work/test-unique-log-stream-macros" + ) endif() if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_app_component_manager_shutdown_order.cpp") diff --git a/README.md b/README.md index 0561895..586503e 100644 --- a/README.md +++ b/README.md @@ -185,7 +185,7 @@ CONSOLIX_LOG_STREAM_EX(logit::LogLevel::LOG_LVL_ERROR, 5) standard console and file logger, use the `_EX` variant: ```cpp -CONSOLIX_LOG_STREAM_EX(logit::LogLevel::LOG_LVL_ERROR, 0, 2, 5) +CONSOLIX_LOG_STREAM_EX(logit::LogLevel::LOG_LVL_ERROR, 5, 6) << "Hello with extra backends" << std::endl; ``` diff --git a/include/consolix/components/LoggerComponent.hpp b/include/consolix/components/LoggerComponent.hpp index 632da71..d1ed5ba 100644 --- a/include/consolix/components/LoggerComponent.hpp +++ b/include/consolix/components/LoggerComponent.hpp @@ -209,8 +209,10 @@ namespace consolix { if (is_once) return; is_once = true; - // Primary console backend with level-based routing: + // Primary single-mode console backend with level-based routing: // TRACE..WARN -> std::cout, ERROR..FATAL -> std::cerr. + // General LogIt broadcasts skip it; Consolix display streams + // target it explicitly. // The config object's primary stream (std::cout by default) is only // used as a fallback when no route matches; our routes cover all // levels, so every record is dispatched by level. @@ -224,7 +226,7 @@ namespace consolix { logit::ConsoleStreamRoute::to_cerr( logit::LogLevel::LOG_LVL_ERROR, logit::LogLevel::LOG_LVL_FATAL)); - LOGIT_ADD_CONSOLE_CONFIG(console_cfg, console_pattern); + LOGIT_ADD_CONSOLE_CONFIG_SINGLE_MODE(console_cfg, console_pattern); LOGIT_ADD_CONSOLE_SINGLE_MODE("%^%v%$", true); // Logo log stream LOGIT_ADD_CONSOLE(m_debug_pattern, true); // Debug log stream diff --git a/include/consolix/components/LoggerComponent/MultiStream.hpp b/include/consolix/components/LoggerComponent/MultiStream.hpp index e20579f..fdf7fc6 100644 --- a/include/consolix/components/LoggerComponent/MultiStream.hpp +++ b/include/consolix/components/LoggerComponent/MultiStream.hpp @@ -94,11 +94,15 @@ namespace consolix { # endif // if defined(_WIN32) # if CONSOLIX_USE_LOGIT == 1 - // Always write to the console backend and file logger, - // then fan-out to any additional backends requested via _EX. - logit::LogStream(m_level, m_file, m_line, m_function, CONSOLIX_LOGIT_CONSOLE_INDEX) << str; + if (LOGIT_IS_SINGLE_MODE(CONSOLIX_LOGIT_CONSOLE_INDEX)) { + logit::LogStream(m_level, m_file, m_line, m_function, CONSOLIX_LOGIT_CONSOLE_INDEX) << str; + } logit::LogStream(m_level, m_file, m_line, m_function, CONSOLIX_LOGIT_LOGGER_INDEX) << str; for (int logger_index : m_logger_indices) { + if (logger_index == CONSOLIX_LOGIT_CONSOLE_INDEX || + logger_index == CONSOLIX_LOGIT_LOGGER_INDEX) { + continue; + } logit::LogStream(m_level, m_file, m_line, m_function, logger_index) << str; } # else diff --git a/tests/test_log_stream_macros.cpp b/tests/test_log_stream_macros.cpp index 89d519e..b374b5e 100644 --- a/tests/test_log_stream_macros.cpp +++ b/tests/test_log_stream_macros.cpp @@ -1,11 +1,15 @@ // tests/test_log_stream_macros.cpp // -// Verifies that CONSOLIX_LOG_STREAM and CONSOLIX_LOG_STREAM_EX compile -// and execute without throwing. Actual log routing is covered by LogIt -// itself; here we only do a smoke-test. +// Verifies that CONSOLIX_LOG_STREAM and CONSOLIX_LOG_STREAM_EX keep the +// Consolix routing contract: display streams target the single-mode console +// and file logger by default, while plain LogIt broadcasts do not reach the +// console backend. +#include #include +#include #include +#include #include @@ -13,6 +17,19 @@ namespace { #if CONSOLIX_USE_LOGIT == 1 +bool contains_message(const std::string& text, const std::string& marker) { + return text.find(marker) != std::string::npos; +} + +bool contains_message(const std::vector& messages, const std::string& marker) { + return std::find_if( + messages.begin(), + messages.end(), + [&](const std::string& message) { + return contains_message(message, marker); + }) != messages.end(); +} + void test_log_stream() { try { CONSOLIX_LOG_STREAM(logit::LogLevel::LOG_LVL_INFO) @@ -37,6 +54,114 @@ void test_log_stream_ex() { } } +void test_default_console_is_single_mode() { + if (!LOGIT_IS_SINGLE_MODE(CONSOLIX_LOGIT_CONSOLE_INDEX)) { + throw std::runtime_error("primary console logger is not single-mode"); + } +} + +void test_general_logit_stream_does_not_hit_console() { + std::ostringstream captured_cout; + std::ostringstream captured_cerr; + std::streambuf* old_cout = std::cout.rdbuf(captured_cout.rdbuf()); + std::streambuf* old_cerr = std::cerr.rdbuf(captured_cerr.rdbuf()); + + try { + LOGIT_STREAM_INFO() << "[broadcast-not-console]"; + LOGIT_WAIT(); + } catch (...) { + std::cout.rdbuf(old_cout); + std::cerr.rdbuf(old_cerr); + throw; + } + + std::cout.rdbuf(old_cout); + std::cerr.rdbuf(old_cerr); + + if (contains_message(captured_cout.str(), "[broadcast-not-console]") || + contains_message(captured_cerr.str(), "[broadcast-not-console]")) { + throw std::runtime_error("general LogIt broadcast reached the single-mode console"); + } +} + +void test_consolix_log_stream_hits_console_by_level() { + std::ostringstream captured_cout; + std::ostringstream captured_cerr; + std::streambuf* old_cout = std::cout.rdbuf(captured_cout.rdbuf()); + std::streambuf* old_cerr = std::cerr.rdbuf(captured_cerr.rdbuf()); + + try { + CONSOLIX_LOG_STREAM(logit::LogLevel::LOG_LVL_INFO) + << "[consolix-info-console]"; + CONSOLIX_LOG_STREAM(logit::LogLevel::LOG_LVL_ERROR) + << "[consolix-error-console]"; + LOGIT_WAIT(); + } catch (...) { + std::cout.rdbuf(old_cout); + std::cerr.rdbuf(old_cerr); + throw; + } + + std::cout.rdbuf(old_cout); + std::cerr.rdbuf(old_cerr); + + if (!contains_message(captured_cout.str(), "[consolix-info-console]")) { + throw std::runtime_error("CONSOLIX_LOG_STREAM info did not reach stdout"); + } + if (!contains_message(captured_cerr.str(), "[consolix-error-console]")) { + throw std::runtime_error("CONSOLIX_LOG_STREAM error did not reach stderr"); + } +} + +void test_consolix_log_stream_hits_file_logger() { + CONSOLIX_LOG_STREAM(logit::LogLevel::LOG_LVL_INFO) + << "[consolix-file-logger]"; + LOGIT_WAIT(); + + const std::string path = LOGIT_GET_LAST_FILE_PATH(CONSOLIX_LOGIT_LOGGER_INDEX); + if (path.empty()) { + throw std::runtime_error("CONSOLIX_LOG_STREAM did not update the file logger path"); + } + + const logit::LogFileReadResult read_result = + LOGIT_READ_LOG_FILE(CONSOLIX_LOGIT_LOGGER_INDEX, path); + if (!read_result.ok || + !contains_message(read_result.content, "[consolix-file-logger]")) { + throw std::runtime_error("CONSOLIX_LOG_STREAM did not reach the file logger"); + } +} + +void test_consolix_log_stream_ex_keeps_defaults_and_adds_backends() { + LOGIT_ADD_MEMORY_LOGGER_DEFAULT_SINGLE_MODE(); + const int memory_logger_index = static_cast( + logit::Logger::get_instance().logger_count() - 1); + + std::ostringstream captured_cout; + std::streambuf* old_cout = std::cout.rdbuf(captured_cout.rdbuf()); + + try { + CONSOLIX_LOG_STREAM_EX( + logit::LogLevel::LOG_LVL_WARN, + memory_logger_index) + << "[consolix-extra-backend]"; + LOGIT_WAIT(); + } catch (...) { + std::cout.rdbuf(old_cout); + throw; + } + + std::cout.rdbuf(old_cout); + + if (!contains_message(captured_cout.str(), "[consolix-extra-backend]")) { + throw std::runtime_error("CONSOLIX_LOG_STREAM_EX skipped the default console"); + } + + const auto buffered = LOGIT_GET_BUFFERED_STRINGS(memory_logger_index); + if (!contains_message(buffered, "[consolix-extra-backend]")) { + throw std::runtime_error("CONSOLIX_LOG_STREAM_EX skipped the explicit backend"); + } +} + #endif // CONSOLIX_USE_LOGIT == 1 } // namespace @@ -44,8 +169,16 @@ void test_log_stream_ex() { int main() { try { #if CONSOLIX_USE_LOGIT == 1 + consolix::LoggerComponent logger("%v", "%v", "%v", 1); + (void)logger; + test_default_console_is_single_mode(); + test_general_logit_stream_does_not_hit_console(); + test_consolix_log_stream_hits_console_by_level(); + test_consolix_log_stream_hits_file_logger(); + test_consolix_log_stream_ex_keeps_defaults_and_adds_backends(); test_log_stream(); test_log_stream_ex(); + LOGIT_WAIT(); std::cout << "Log stream macro checks passed." << std::endl; #else CONSOLIX_LOG_STREAM(0) << "[fallback]"; From 8451fad896dc7ce006f996f4bc42f9187b6411c9 Mon Sep 17 00:00:00 2001 From: Aster Seker Date: Sun, 7 Jun 2026 09:54:34 +0300 Subject: [PATCH 3/3] fix: separate Consolix broadcast and explicit log routing --- README.md | 30 ++++----- .../consolix/components/LoggerComponent.hpp | 32 ++++++--- .../LoggerComponent/MultiStream.hpp | 36 ++++++++-- tests/test_log_stream_macros.cpp | 67 ++++++++++++++++--- 4 files changed, 127 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 586503e..fb51ae9 100644 --- a/README.md +++ b/README.md @@ -162,31 +162,31 @@ Consolix provides two multi-target log macros that route messages through LogIt backends. The console backend is configured so that `ERROR` and `FATAL` levels go to `std::cerr`, while `TRACE` through `WARN` go to `std::cout`. -| Macro | Console backend | File logger | Extra backends | Color | +| Macro | Console backend | Regular backends | Extra backends | Color | |---|---|---|---|---| -| `CONSOLIX_LOG_STREAM(level)` | yes (level-based routing) | yes | — | via `<< color(...)` | -| `CONSOLIX_LOG_STREAM_EX(level, ...)` | yes (level-based routing) | yes | inline indices | via `<< color(...)` | +| `CONSOLIX_LOG_STREAM(level)` | yes (level-based routing) | broadcast | - | via `<< color(...)` | +| `CONSOLIX_LOG_STREAM_NO_BROADCAST(level)` | yes (level-based routing) | file fallback only | - | via `<< color(...)` | +| `CONSOLIX_LOG_STREAM_EX(level, ...)` | yes (level-based routing) | file fallback only | inline indices | via `<< color(...)` | The `level` argument is mandatory (`logit::LogLevel::LOG_LVL_INFO`, `LOG_LVL_ERROR`, etc.). Color is applied explicitly by the caller through `<< consolix::color(consolix::TextColor::Red)`. **Note on `_EX` variants.** The standard console backend (`CONSOLIX_LOGIT_CONSOLE_INDEX`) -and file logger (`CONSOLIX_LOGIT_LOGGER_INDEX`) are already included by both -`CONSOLIX_LOG_STREAM` and `CONSOLIX_LOG_STREAM_EX`. Only pass *additional* -backend indices to `_EX`: +is already included by both `CONSOLIX_LOG_STREAM` and `CONSOLIX_LOG_STREAM_EX`. +`CONSOLIX_LOG_STREAM` broadcasts to regular non-single backends, including the +standard file logger and user-added regular backends. `CONSOLIX_LOG_STREAM_EX` +does not broadcast; it writes to the file fallback and to the listed backend +indices. -```cpp -CONSOLIX_LOG_STREAM_EX(logit::LogLevel::LOG_LVL_ERROR, 5) - << "Hello with extra backend" << std::endl; -``` - -**Inline override.** To fan out to additional LogIt backends beyond the -standard console and file logger, use the `_EX` variant: +**Inline targets.** To target selected LogIt backends beyond the standard +console and file fallback, use the `_EX` variant: ```cpp -CONSOLIX_LOG_STREAM_EX(logit::LogLevel::LOG_LVL_ERROR, 5, 6) - << "Hello with extra backends" << std::endl; +CONSOLIX_LOG_STREAM_EX( + logit::LogLevel::LOG_LVL_ERROR, + CONSOLIX_LOGIT_UNIQUE_FILE_INDEX) + << "Hello with an extra single-mode backend" << std::endl; ``` See `examples/example_stderr_diagnostics.cpp` for a runnable example. diff --git a/include/consolix/components/LoggerComponent.hpp b/include/consolix/components/LoggerComponent.hpp index d1ed5ba..ba78600 100644 --- a/include/consolix/components/LoggerComponent.hpp +++ b/include/consolix/components/LoggerComponent.hpp @@ -39,7 +39,8 @@ #define CONSOLIX_LOGIT_UNIQUE_FILE_INDEX 4 /// \brief Defines the general log stream for the application. -/// Redirects logs to the main console backend with detailed context. +/// Redirects display output to the main console backend and broadcasts the +/// same record to regular LogIt backends. #define CONSOLIX_STREAM() \ consolix::MultiStream( \ logit::LogLevel::LOG_LVL_TRACE, \ @@ -47,12 +48,22 @@ __LINE__, \ LOGIT_FUNCTION) -/// \brief Defines a multi-target log stream (file logger + console + extra LogIt backends). +/// \brief Defines a multi-target log stream (console + regular LogIt backends). /// \param level LogIt level for the multi-target stream. -/// \details Routes the message to the standard file logger, the main console -/// backend, and any explicit inline backend indices supplied via -/// the `CONSOLIX_LOG_STREAM_EX` variant. +/// \details Routes the message to the main single-mode console backend and +/// broadcasts it to regular non-single LogIt backends, including the +/// standard file logger and user-added regular backends. #define CONSOLIX_LOG_STREAM(level) \ + consolix::MultiStream( \ + (level), \ + logit::make_relative(__FILE__, LOGIT_BASE_PATH), \ + __LINE__, \ + LOGIT_FUNCTION) + +/// \brief Defines a log stream that targets the console and file fallback +/// without broadcasting to user-added regular LogIt backends. +/// \param level LogIt level for the stream. +#define CONSOLIX_LOG_STREAM_NO_BROADCAST(level) \ consolix::MultiStream( \ (level), \ logit::make_relative(__FILE__, LOGIT_BASE_PATH), \ @@ -60,10 +71,10 @@ LOGIT_FUNCTION, \ {}) -/// \brief Defines a multi-target log stream with an explicit inline list of -/// additional LogIt backend indices. +/// \brief Defines a log stream with an explicit inline list of extra LogIt +/// backend indices and no regular broadcast. /// \param level LogIt level for the multi-target stream. -/// \param ... Comma-separated LogIt backend indices (e.g. `0,3`). +/// \param ... Comma-separated extra LogIt backend indices. #define CONSOLIX_LOG_STREAM_EX(level, ...) \ consolix::MultiStream( \ (level), \ @@ -264,6 +275,11 @@ namespace consolix { #define CONSOLIX_LOG_STREAM(level) \ consolix::MultiStream() +/// \brief Fallback no-broadcast log stream. The `level` argument is +/// ignored when LogIt is disabled. +#define CONSOLIX_LOG_STREAM_NO_BROADCAST(level) \ + consolix::MultiStream() + /// \brief Fallback multi-target log stream with an ignored backend list. #define CONSOLIX_LOG_STREAM_EX(level, ...) \ consolix::MultiStream() diff --git a/include/consolix/components/LoggerComponent/MultiStream.hpp b/include/consolix/components/LoggerComponent/MultiStream.hpp index fdf7fc6..b62631e 100644 --- a/include/consolix/components/LoggerComponent/MultiStream.hpp +++ b/include/consolix/components/LoggerComponent/MultiStream.hpp @@ -36,7 +36,8 @@ namespace consolix { : m_level(logit::LogLevel::LOG_LVL_TRACE), m_file(__FILE__), m_line(__LINE__), - m_function(logit::make_relative(__FILE__, LOGIT_BASE_PATH)) { + m_function(logit::make_relative(__FILE__, LOGIT_BASE_PATH)), + m_broadcast_regular(true) { } /// \brief Parameterized constructor for LogIt integration. @@ -49,7 +50,11 @@ namespace consolix { const std::string& file, int line, const std::string& function) - : m_level(level), m_file(file), m_line(line), m_function(function) { + : m_level(level), + m_file(file), + m_line(line), + m_function(function), + m_broadcast_regular(true) { } /// \brief Parameterized constructor for LogIt integration with @@ -59,18 +64,23 @@ namespace consolix { /// \param line Line number in the source file. /// \param function Function name. /// \param logger_indices Extra LogIt backend indices to fan out to - /// in addition to the standard file/console targets. + /// when regular backend broadcast is disabled, or extra + /// single-mode indices when regular backend broadcast is enabled. + /// \param broadcast_regular Whether to broadcast to regular + /// non-single LogIt backends. MultiStream( logit::LogLevel level, const std::string& file, int line, const std::string& function, - std::initializer_list logger_indices) + std::initializer_list logger_indices, + bool broadcast_regular = false) : m_level(level), m_file(file), m_line(line), m_function(function), - m_logger_indices(logger_indices) { + m_logger_indices(logger_indices), + m_broadcast_regular(broadcast_regular) { } # else @@ -97,12 +107,23 @@ namespace consolix { if (LOGIT_IS_SINGLE_MODE(CONSOLIX_LOGIT_CONSOLE_INDEX)) { logit::LogStream(m_level, m_file, m_line, m_function, CONSOLIX_LOGIT_CONSOLE_INDEX) << str; } - logit::LogStream(m_level, m_file, m_line, m_function, CONSOLIX_LOGIT_LOGGER_INDEX) << str; + if (m_broadcast_regular) { + logit::LogStream(m_level, m_file, m_line, m_function, -1) << str; + } else { + logit::LogStream(m_level, m_file, m_line, m_function, CONSOLIX_LOGIT_LOGGER_INDEX) << str; + } for (int logger_index : m_logger_indices) { - if (logger_index == CONSOLIX_LOGIT_CONSOLE_INDEX || + if (logger_index == CONSOLIX_LOGIT_CONSOLE_INDEX) { + continue; + } + if (!m_broadcast_regular && logger_index == CONSOLIX_LOGIT_LOGGER_INDEX) { continue; } + if (m_broadcast_regular && + !LOGIT_IS_SINGLE_MODE(logger_index)) { + continue; + } logit::LogStream(m_level, m_file, m_line, m_function, logger_index) << str; } # else @@ -143,6 +164,7 @@ namespace consolix { int m_line; ///< Line number. std::string m_function; ///< Function name. std::vector m_logger_indices; ///< Extra LogIt backend indices for fan-out. + bool m_broadcast_regular; ///< Broadcast to regular non-single backends. # else bool m_use_utf8; ///< Flag indicating whether UTF-8 encoding should be used. # endif diff --git a/tests/test_log_stream_macros.cpp b/tests/test_log_stream_macros.cpp index b374b5e..2b4544e 100644 --- a/tests/test_log_stream_macros.cpp +++ b/tests/test_log_stream_macros.cpp @@ -2,8 +2,8 @@ // // Verifies that CONSOLIX_LOG_STREAM and CONSOLIX_LOG_STREAM_EX keep the // Consolix routing contract: display streams target the single-mode console -// and file logger by default, while plain LogIt broadcasts do not reach the -// console backend. +// and broadcast to regular LogIt backends by default, while plain LogIt +// broadcasts do not reach the console backend. #include #include @@ -44,7 +44,9 @@ void test_log_stream() { void test_log_stream_ex() { try { - CONSOLIX_LOG_STREAM_EX(logit::LogLevel::LOG_LVL_WARN, 5) + CONSOLIX_LOG_STREAM_EX( + logit::LogLevel::LOG_LVL_WARN, + CONSOLIX_LOGIT_UNIQUE_FILE_INDEX) << "[log-stream-ex] smoke test"; } catch (const std::exception& e) { throw std::runtime_error( @@ -131,18 +133,59 @@ void test_consolix_log_stream_hits_file_logger() { } } -void test_consolix_log_stream_ex_keeps_defaults_and_adds_backends() { - LOGIT_ADD_MEMORY_LOGGER_DEFAULT_SINGLE_MODE(); +void test_consolix_log_stream_hits_user_regular_backend() { + LOGIT_ADD_MEMORY_LOGGER_DEFAULT(); + const int memory_logger_index = static_cast( + logit::Logger::get_instance().logger_count() - 1); + + CONSOLIX_LOG_STREAM(logit::LogLevel::LOG_LVL_INFO) + << "[consolix-user-regular-backend]"; + LOGIT_WAIT(); + + const auto buffered = LOGIT_GET_BUFFERED_STRINGS(memory_logger_index); + if (!contains_message(buffered, "[consolix-user-regular-backend]")) { + throw std::runtime_error("CONSOLIX_LOG_STREAM did not reach a user regular backend"); + } +} + +void test_consolix_log_stream_no_broadcast_skips_user_regular_backend() { + LOGIT_ADD_MEMORY_LOGGER_DEFAULT(); const int memory_logger_index = static_cast( logit::Logger::get_instance().logger_count() - 1); + CONSOLIX_LOG_STREAM_NO_BROADCAST(logit::LogLevel::LOG_LVL_INFO) + << "[consolix-no-broadcast]"; + LOGIT_WAIT(); + + const auto buffered = LOGIT_GET_BUFFERED_STRINGS(memory_logger_index); + if (contains_message(buffered, "[consolix-no-broadcast]")) { + throw std::runtime_error("CONSOLIX_LOG_STREAM_NO_BROADCAST reached a user regular backend"); + } + + const std::string path = LOGIT_GET_LAST_FILE_PATH(CONSOLIX_LOGIT_LOGGER_INDEX); + const logit::LogFileReadResult read_result = + LOGIT_READ_LOG_FILE(CONSOLIX_LOGIT_LOGGER_INDEX, path); + if (!read_result.ok || + !contains_message(read_result.content, "[consolix-no-broadcast]")) { + throw std::runtime_error("CONSOLIX_LOG_STREAM_NO_BROADCAST skipped the file fallback"); + } +} + +void test_consolix_log_stream_ex_keeps_defaults_and_adds_backends() { + LOGIT_ADD_MEMORY_LOGGER_DEFAULT(); + const int skipped_regular_index = static_cast( + logit::Logger::get_instance().logger_count() - 1); + LOGIT_ADD_MEMORY_LOGGER_DEFAULT(); + const int explicit_regular_index = static_cast( + logit::Logger::get_instance().logger_count() - 1); + std::ostringstream captured_cout; std::streambuf* old_cout = std::cout.rdbuf(captured_cout.rdbuf()); try { CONSOLIX_LOG_STREAM_EX( logit::LogLevel::LOG_LVL_WARN, - memory_logger_index) + explicit_regular_index) << "[consolix-extra-backend]"; LOGIT_WAIT(); } catch (...) { @@ -156,8 +199,13 @@ void test_consolix_log_stream_ex_keeps_defaults_and_adds_backends() { throw std::runtime_error("CONSOLIX_LOG_STREAM_EX skipped the default console"); } - const auto buffered = LOGIT_GET_BUFFERED_STRINGS(memory_logger_index); - if (!contains_message(buffered, "[consolix-extra-backend]")) { + const auto skipped_regular = LOGIT_GET_BUFFERED_STRINGS(skipped_regular_index); + if (contains_message(skipped_regular, "[consolix-extra-backend]")) { + throw std::runtime_error("CONSOLIX_LOG_STREAM_EX broadcast to an unlisted regular backend"); + } + + const auto explicit_regular = LOGIT_GET_BUFFERED_STRINGS(explicit_regular_index); + if (!contains_message(explicit_regular, "[consolix-extra-backend]")) { throw std::runtime_error("CONSOLIX_LOG_STREAM_EX skipped the explicit backend"); } } @@ -175,6 +223,8 @@ int main() { test_general_logit_stream_does_not_hit_console(); test_consolix_log_stream_hits_console_by_level(); test_consolix_log_stream_hits_file_logger(); + test_consolix_log_stream_hits_user_regular_backend(); + test_consolix_log_stream_no_broadcast_skips_user_regular_backend(); test_consolix_log_stream_ex_keeps_defaults_and_adds_backends(); test_log_stream(); test_log_stream_ex(); @@ -182,6 +232,7 @@ int main() { std::cout << "Log stream macro checks passed." << std::endl; #else CONSOLIX_LOG_STREAM(0) << "[fallback]"; + CONSOLIX_LOG_STREAM_NO_BROADCAST(0) << "[fallback-no-broadcast]"; CONSOLIX_LOG_STREAM_EX(0, 0) << "[fallback-ex]"; std::cout << "Log stream macro compile-time checks passed." << std::endl; #endif