From e51adb498fbdf0b1a308ad0bd45b2fe1bae21d7e Mon Sep 17 00:00:00 2001 From: Aster Seker Date: Sun, 14 Sep 2025 03:22:50 +0300 Subject: [PATCH] feat(executor): add queue policy macros --- README-RU.md | 80 +++++++++--------- README.md | 82 ++++++++++--------- docs/groups.dox | 7 ++ examples/example_logit_basic.cpp | 2 + include/logit_cpp/logit/LogMacros.hpp | 22 +++++ .../logit_cpp/logit/detail/TaskExecutor.hpp | 36 +++++++- 6 files changed, 148 insertions(+), 81 deletions(-) diff --git a/README-RU.md b/README-RU.md index a654fe4..3c65c8f 100644 --- a/README-RU.md +++ b/README-RU.md @@ -88,51 +88,53 @@ LOGIT_ADD_LOGGER(CustomLogger, (), logit::SimpleLogFormatter, ("%v")); ## Использование -Ниже приведен простой пример использования LogIt++ в вашем приложении: +Ниже приведен простой пример использования LogIt++ в вашем приложении. Размер очереди и поведение при переполнении настраиваются с помощью `LOGIT_SET_MAX_QUEUE` и `LOGIT_SET_QUEUE_POLICY` (используйте `LOGIT_QUEUE_DROP` или `LOGIT_QUEUE_BLOCK`): ```cpp #define LOGIT_SHORT_NAME #include int main() { - // Инициализация логгера с выводом в консоль по умолчанию - LOGIT_ADD_CONSOLE_DEFAULT(); - - float a = 123.456f; - int b = 789; - int c = 899; - const char* someStr = "Hello, World!"; - - // Базовое логирование с использованием макросов - LOG_INFO("Starting the application"); - LOG_DEBUG("Variable values", a, b); - LOG_WARN("This is a warning message"); - - // Логирование с форматированием - LOG_PRINTF_INFO("Formatted log: value of a = %.2f", a); - LOG_FORMAT_WARN("%.4d", b, c); - - // Логирование ошибок и фатальных ошибок - LOG_ERROR("An error occurred", b); - LOG_FATAL("Fatal error. Terminating application."); - - // Условное логирование - LOG_ERROR_IF(b < 0, "Value of b is negative"); - LOG_WARN_IF(a > 100, "Value of a exceeds 100"); - - // Потоковое логирование с использованием коротких и длинных макросов - LOG_S_INFO() << "Logging a float: " << a << ", and an int: " << b; - LOG_S_ERROR() << "Error occurred in the system"; - LOGIT_STREAM_WARN() << "Warning: potential issue detected with value: " << someStr; - - // Использование LOGIT_TRACE для трассировки выполнения функций - LOGIT_TRACE0(); // Trace without arguments - LOG_PRINT_TRACE("Entering main function with variable a =", a); - - // Ожидание завершения всех асинхронных операций логирования - LOGIT_WAIT(); - - return 0; + // Инициализация логгера с выводом в консоль по умолчанию + LOGIT_ADD_CONSOLE_DEFAULT(); + LOGIT_SET_MAX_QUEUE(64); + LOGIT_SET_QUEUE_POLICY(LOGIT_QUEUE_DROP); + + float a = 123.456f; + int b = 789; + int c = 899; + const char* someStr = "Hello, World!"; + + // Базовое логирование с использованием макросов + LOG_INFO("Starting the application"); + LOG_DEBUG("Variable values", a, b); + LOG_WARN("This is a warning message"); + + // Логирование с форматированием + LOG_PRINTF_INFO("Formatted log: value of a = %.2f", a); + LOG_FORMAT_WARN("%.4d", b, c); + + // Логирование ошибок и фатальных ошибок + LOG_ERROR("An error occurred", b); + LOG_FATAL("Fatal error. Terminating application."); + + // Условное логирование + LOG_ERROR_IF(b < 0, "Value of b is negative"); + LOG_WARN_IF(a > 100, "Value of a exceeds 100"); + + // Потоковое логирование с использованием коротких и длинных макросов + LOG_S_INFO() << "Logging a float: " << a << ", and an int: " << b; + LOG_S_ERROR() << "Error occurred in the system"; + LOGIT_STREAM_WARN() << "Warning: potential issue detected with value: " << someStr; + + // Использование LOGIT_TRACE для трассировки выполнения функций + LOGIT_TRACE0(); // Trace without arguments + LOG_PRINT_TRACE("Entering main function with variable a =", a); + + // Ожидание завершения всех асинхронных операций логирования + LOGIT_WAIT(); + + return 0; } ``` diff --git a/README.md b/README.md index 20077b4..a1f8e03 100644 --- a/README.md +++ b/README.md @@ -93,51 +93,53 @@ LOGIT_ADD_LOGGER(CustomLogger, (), logit::SimpleLogFormatter, ("%v")); ## Usage -Here’s a simple example demonstrating how to use LogIt++ in your application: +Here’s a simple example demonstrating how to use LogIt++ in your application. The task queue size and overflow behavior are configurable via `LOGIT_SET_MAX_QUEUE` and `LOGIT_SET_QUEUE_POLICY` (use `LOGIT_QUEUE_DROP` or `LOGIT_QUEUE_BLOCK`): ```cpp #define LOGIT_SHORT_NAME #include int main() { - // Initialize the logger with default console output - LOGIT_ADD_CONSOLE_DEFAULT(); - - float a = 123.456f; - int b = 789; - int c = 899; - const char* someStr = "Hello, World!"; - - // Basic logging using macros - LOG_INFO("Starting the application"); - LOG_DEBUG("Variable values", a, b); - LOG_WARN("This is a warning message"); - - // Formatted logging - LOG_PRINTF_INFO("Formatted log: value of a = %.2f", a); - LOG_FORMAT_WARN("%.4d", b, c); - - // Error and fatal logs - LOG_ERROR("An error occurred", b); - LOG_FATAL("Fatal error. Terminating application."); - - // Conditional logging - LOG_ERROR_IF(b < 0, "Value of b is negative"); - LOG_WARN_IF(a > 100, "Value of a exceeds 100"); - - // Stream-based logging with short and long names - LOG_S_INFO() << "Logging a float: " << a << ", and an int: " << b; - LOG_S_ERROR() << "Error occurred in the system"; - LOGIT_STREAM_WARN() << "Warning: potential issue detected with value: " << someStr; - - // Using LOGIT_TRACE for tracing function execution - LOGIT_TRACE0(); // Trace without arguments - LOG_PRINT_TRACE("Entering main function with variable a =", a); - - // Wait for all asynchronous logs to be processed - LOGIT_WAIT(); - - return 0; + // Initialize the logger with default console output + LOGIT_ADD_CONSOLE_DEFAULT(); + LOGIT_SET_MAX_QUEUE(64); + LOGIT_SET_QUEUE_POLICY(LOGIT_QUEUE_DROP); + + float a = 123.456f; + int b = 789; + int c = 899; + const char* someStr = "Hello, World!"; + + // Basic logging using macros + LOG_INFO("Starting the application"); + LOG_DEBUG("Variable values", a, b); + LOG_WARN("This is a warning message"); + + // Formatted logging + LOG_PRINTF_INFO("Formatted log: value of a = %.2f", a); + LOG_FORMAT_WARN("%.4d", b, c); + + // Error and fatal logs + LOG_ERROR("An error occurred", b); + LOG_FATAL("Fatal error. Terminating application."); + + // Conditional logging + LOG_ERROR_IF(b < 0, "Value of b is negative"); + LOG_WARN_IF(a > 100, "Value of a exceeds 100"); + + // Stream-based logging with short and long names + LOG_S_INFO() << "Logging a float: " << a << ", and an int: " << b; + LOG_S_ERROR() << "Error occurred in the system"; + LOGIT_STREAM_WARN() << "Warning: potential issue detected with value: " << someStr; + + // Using LOGIT_TRACE for tracing function execution + LOGIT_TRACE0(); // Trace without arguments + LOG_PRINT_TRACE("Entering main function with variable a =", a); + + // Wait for all asynchronous logs to be processed + LOGIT_WAIT(); + + return 0; } ``` @@ -495,4 +497,4 @@ Detailed documentation for LogIt++, including API reference and usage examples, --- ## License -This library is licensed under the MIT License. See the [LICENSE](https://github.com/NewYaroslav/log-it-cpp/blob/main/LICENSE) file in the repository for more details. \ No newline at end of file +This library is licensed under the MIT License. See the [LICENSE](https://github.com/NewYaroslav/log-it-cpp/blob/main/LICENSE) file in the repository for more details. diff --git a/docs/groups.dox b/docs/groups.dox index f51054b..92db18f 100644 --- a/docs/groups.dox +++ b/docs/groups.dox @@ -33,6 +33,13 @@ if (LOGIT_IS_LOGGER_ENABLED(1)) { LOGIT_INFO("Logger 1 is enabled"); } \endcode + +\par Task Executor Configuration +Use `LOGIT_QUEUE_DROP` or `LOGIT_QUEUE_BLOCK` with `LOGIT_SET_QUEUE_POLICY`. +\code +LOGIT_SET_MAX_QUEUE(64); // Limit queue size +LOGIT_SET_QUEUE_POLICY(LOGIT_QUEUE_DROP); // Drop tasks when full +\endcode */ /*! diff --git a/examples/example_logit_basic.cpp b/examples/example_logit_basic.cpp index a479757..4d16818 100644 --- a/examples/example_logit_basic.cpp +++ b/examples/example_logit_basic.cpp @@ -74,6 +74,8 @@ int main() { LOGIT_ADD_CONSOLE_DEFAULT(); LOGIT_ADD_FILE_LOGGER_DEFAULT(); LOGIT_ADD_UNIQUE_FILE_LOGGER_DEFAULT_SINGLE_MODE(); + LOGIT_SET_MAX_QUEUE(64); + LOGIT_SET_QUEUE_POLICY(LOGIT_QUEUE_DROP); logit::test_log_depth_3(); diff --git a/include/logit_cpp/logit/LogMacros.hpp b/include/logit_cpp/logit/LogMacros.hpp index 7a66809..9d5bd38 100644 --- a/include/logit_cpp/logit/LogMacros.hpp +++ b/include/logit_cpp/logit/LogMacros.hpp @@ -959,6 +959,28 @@ /// \} +/// \name Task Executor Configuration +/// Macros for configuring the TaskExecutor. +/// \{ + +/// \brief Sets the maximum number of queued tasks. +/// \param size Maximum queue size (0 for unlimited). +#define LOGIT_SET_MAX_QUEUE(size) \ + logit::detail::TaskExecutor::get_instance().set_max_queue_size(size) + +/// \brief Queue policy for dropping tasks when the queue is full. +#define LOGIT_QUEUE_DROP logit::detail::QueuePolicy::Drop + +/// \brief Queue policy for blocking when the queue is full. +#define LOGIT_QUEUE_BLOCK logit::detail::QueuePolicy::Block + +/// \brief Sets the behavior when the queue is full. +/// \param mode LOGIT_QUEUE_DROP or LOGIT_QUEUE_BLOCK. +#define LOGIT_SET_QUEUE_POLICY(mode) \ + logit::detail::TaskExecutor::get_instance().set_queue_policy(mode) + +/// \} + /// \brief Macro for waiting for all asynchronous loggers to finish processing. #define LOGIT_WAIT() logit::Logger::get_instance().wait() diff --git a/include/logit_cpp/logit/detail/TaskExecutor.hpp b/include/logit_cpp/logit/detail/TaskExecutor.hpp index 678288d..b5d6e3f 100644 --- a/include/logit_cpp/logit/detail/TaskExecutor.hpp +++ b/include/logit_cpp/logit/detail/TaskExecutor.hpp @@ -15,6 +15,9 @@ namespace logit { namespace detail { + /// \brief Queue overflow handling policy. + enum class QueuePolicy { Drop, Block }; + #if defined(__EMSCRIPTEN__) /// \class TaskExecutor @@ -33,6 +36,9 @@ namespace logit { namespace detail { void wait() {} void shutdown() {} + void set_max_queue_size(std::size_t) {} + void set_queue_policy(QueuePolicy) {} + private: TaskExecutor() = default; ~TaskExecutor() = default; @@ -63,6 +69,15 @@ namespace logit { namespace detail { void add_task(std::function task) { std::unique_lock lock(m_queue_mutex); if (m_stop_flag) return; + if (m_max_queue_size > 0 && m_tasks_queue.size() >= m_max_queue_size) { + if (m_overflow_policy == QueuePolicy::Drop) { + return; + } + m_queue_condition.wait(lock, [this]() { + return m_tasks_queue.size() < m_max_queue_size || m_stop_flag; + }); + if (m_stop_flag) return; + } m_tasks_queue.push(std::move(task)); lock.unlock(); m_queue_condition.notify_one(); @@ -86,18 +101,34 @@ namespace logit { namespace detail { std::unique_lock lock(m_queue_mutex); m_stop_flag = true; lock.unlock(); - m_queue_condition.notify_one(); + m_queue_condition.notify_all(); if (m_worker_thread.joinable()) { m_worker_thread.join(); } } + /// \brief Sets the maximum size of the task queue. + /// \param size Maximum number of tasks in the queue (0 for unlimited). + void set_max_queue_size(std::size_t size) { + std::lock_guard lock(m_queue_mutex); + m_max_queue_size = size; + } + + /// \brief Sets the behavior when the queue is full. + /// \param policy QueuePolicy::Drop to discard tasks or QueuePolicy::Block to wait. + void set_queue_policy(QueuePolicy policy) { + std::lock_guard lock(m_queue_mutex); + m_overflow_policy = policy; + } + private: std::queue> m_tasks_queue; ///< Queue holding tasks to be executed. mutable std::mutex m_queue_mutex; ///< Mutex to protect access to the task queue. std::condition_variable m_queue_condition; ///< Condition variable to signal task availability. std::thread m_worker_thread; ///< Worker thread for executing tasks. bool m_stop_flag; ///< Flag indicating if the worker thread should stop. + std::size_t m_max_queue_size; ///< Maximum number of tasks in the queue (0 for unlimited). + QueuePolicy m_overflow_policy; ///< Policy for handling queue overflow. /// \brief The worker thread function that processes tasks from the queue. void worker_function() { @@ -113,12 +144,13 @@ namespace logit { namespace detail { task = std::move(m_tasks_queue.front()); m_tasks_queue.pop(); lock.unlock(); + m_queue_condition.notify_one(); task(); } } /// \brief Private constructor to enforce the singleton pattern. - TaskExecutor() : m_stop_flag(false) { + TaskExecutor() : m_stop_flag(false), m_max_queue_size(0), m_overflow_policy(QueuePolicy::Block) { m_worker_thread = std::thread(&TaskExecutor::worker_function, this); }