diff --git a/CMakeLists.txt b/CMakeLists.txt index 74446e5..7ecf3bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,6 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten") endif() option(LOGIT_FORCE_ASYNC_OFF "Force disable async logging" OFF) option(LOGIT_USE_MPSC_RING "Enable lock-free TaskExecutor queue" ON) -option(LOGIT_ENABLE_DROP_OLDEST_SLOWPATH "Enable TaskExecutor DropOldest slow-path" ON) if(NOT DEFINED CMAKE_CXX_STANDARD) if(LOGIT_WITH_OTLP OR LOGIT_WITH_PROMETHEUS_SERVER OR LOGIT_WITH_MDBX) @@ -63,10 +62,6 @@ if(LOGIT_USE_MPSC_RING) target_compile_definitions(log-it-cpp INTERFACE LOGIT_USE_MPSC_RING=1) endif() -if(LOGIT_ENABLE_DROP_OLDEST_SLOWPATH) - target_compile_definitions(log-it-cpp INTERFACE LOGIT_ENABLE_DROP_OLDEST_SLOWPATH=1) -endif() - if(LOGIT_EMSCRIPTEN) set(LOGIT_WITH_SYSLOG OFF CACHE BOOL "" FORCE) set(LOGIT_WITH_WIN_EVENT_LOG OFF CACHE BOOL "" FORCE) diff --git a/include/logit_cpp/AGENTS.md b/include/logit_cpp/AGENTS.md index f7d4d8b..747628a 100644 --- a/include/logit_cpp/AGENTS.md +++ b/include/logit_cpp/AGENTS.md @@ -23,8 +23,12 @@ For subsystem-specific work, also read the nearest guide: normally consumed through `` or the relevant module umbrella. - Preserve the existing public names, overloads, macro expansion contracts, and feature guards. Add new API only with a focused test and documentation. -- Keep headers self-contained: include every standard type used directly and - do not depend on include order or transitive headers. +- Keep the supported public umbrellas (`logit.hpp`, `utils.hpp`, + `formatter.hpp`, `loggers.hpp`) self-contained. +- Leaf headers follow the aggregate-first/NHR contract and may rely on + prerequisites prepared by their nearest umbrella. Do not add standalone + include contracts to leaf headers unless explicitly required by the public + API and covered by a focused test. - A `noexcept` declaration is a contract. Do not perform allocation, invoke a user callback, or execute code that may throw in a `noexcept` function. Signal/crash handlers are the explicit exception and must stay async-signal diff --git a/include/logit_cpp/logit/detail/QueuePolicy.hpp b/include/logit_cpp/logit/detail/QueuePolicy.hpp index a1ecf50..e5ab7ac 100644 --- a/include/logit_cpp/logit/detail/QueuePolicy.hpp +++ b/include/logit_cpp/logit/detail/QueuePolicy.hpp @@ -7,7 +7,8 @@ namespace logit { namespace detail { /// \brief Queue overflow handling policy used by TaskExecutor and SingleThreadExecutor. enum class QueuePolicy { DropNewest, ///< Reject the incoming task when the queue is full. - DropOldest, ///< Drop the oldest queued task. + DropOldest, ///< Drop-oldest policy; MPSC mode rejects the incoming task + ///< to preserve the ordering of already accepted work. Block ///< Producers wait until capacity is available. }; diff --git a/include/logit_cpp/logit/log_macros.hpp b/include/logit_cpp/logit/log_macros.hpp index e2cfe0a..39635b6 100644 --- a/include/logit_cpp/logit/log_macros.hpp +++ b/include/logit_cpp/logit/log_macros.hpp @@ -2928,16 +2928,16 @@ static_assert(LOGIT_LEVEL_FATAL == static_cast(logit::LogLevel::LOG_LVL_FAT logit::detail::TaskExecutor::get_instance().set_max_queue_size(size) /// \brief Queue policy for dropping the newest task when the queue is full. -#define LOGIT_QUEUE_DROP_NEWEST logit::detail::QueuePolicy::DropNewest +#define LOGIT_QUEUE_DROP_NEWEST logit::QueuePolicy::DropNewest /// \brief Queue policy for dropping the oldest task when the queue is full. -#define LOGIT_QUEUE_DROP_OLDEST logit::detail::QueuePolicy::DropOldest +#define LOGIT_QUEUE_DROP_OLDEST logit::QueuePolicy::DropOldest /// \brief Backward-compatible alias for dropping the newest task. #define LOGIT_QUEUE_DROP LOGIT_QUEUE_DROP_NEWEST /// \brief Queue policy for blocking when the queue is full. -#define LOGIT_QUEUE_BLOCK logit::detail::QueuePolicy::Block +#define LOGIT_QUEUE_BLOCK logit::QueuePolicy::Block /// \brief Sets the behavior when the queue is full. /// \param mode LOGIT_QUEUE_DROP_NEWEST, LOGIT_QUEUE_DROP_OLDEST or LOGIT_QUEUE_BLOCK. diff --git a/include/logit_cpp/logit/loggers.hpp b/include/logit_cpp/logit/loggers.hpp index 2c6bd86..f4740bd 100644 --- a/include/logit_cpp/logit/loggers.hpp +++ b/include/logit_cpp/logit/loggers.hpp @@ -13,6 +13,14 @@ #include "utils.hpp" #include "detail/TaskExecutor.hpp" #include "detail/SingleThreadExecutor.hpp" + +namespace logit { + + /// \brief Public queue overflow handling policy. + using QueuePolicy = detail::QueuePolicy; + +} // namespace logit + #ifndef __EMSCRIPTEN__ #include "detail/CompressionWorker.hpp" #endif diff --git a/include/logit_cpp/logit/loggers/ConsoleLogger.hpp b/include/logit_cpp/logit/loggers/ConsoleLogger.hpp index 34c39f8..68666bb 100644 --- a/include/logit_cpp/logit/loggers/ConsoleLogger.hpp +++ b/include/logit_cpp/logit/loggers/ConsoleLogger.hpp @@ -83,7 +83,7 @@ namespace logit { #endif bool use_dedicated_executor = false; ///< Use a dedicated executor instead of the global TaskExecutor; native builds create one worker thread per logger. std::size_t queue_capacity = 0; ///< Maximum queue size for the dedicated executor (0 = unlimited). - detail::QueuePolicy queue_policy = detail::QueuePolicy::Block; ///< Overflow policy for the dedicated executor. + QueuePolicy queue_policy = QueuePolicy::Block; ///< Overflow policy for the dedicated executor. /// \brief Optional level-based stream routing. /// \details When non-empty, the first matching route (inclusive range /// `[min_level, max_level]`) wins. Falls back to the primary stream when @@ -140,7 +140,7 @@ namespace logit { bool async, bool use_dedicated_executor, std::size_t queue_capacity = 0, - detail::QueuePolicy queue_policy = detail::QueuePolicy::Block) + QueuePolicy queue_policy = QueuePolicy::Block) : ConsoleLogger(make_config( async, use_dedicated_executor, @@ -153,7 +153,7 @@ namespace logit { bool async, bool use_dedicated_executor, std::size_t queue_capacity = 0, - detail::QueuePolicy queue_policy = detail::QueuePolicy::Block) + QueuePolicy queue_policy = QueuePolicy::Block) : ConsoleLogger(stream, make_config( async, use_dedicated_executor, @@ -429,7 +429,7 @@ namespace logit { bool async, bool use_dedicated_executor, std::size_t queue_capacity, - detail::QueuePolicy queue_policy) { + QueuePolicy queue_policy) { Config config; config.async = async; config.use_dedicated_executor = use_dedicated_executor; diff --git a/include/logit_cpp/logit/loggers/EventLogLogger.hpp b/include/logit_cpp/logit/loggers/EventLogLogger.hpp index 5d0f649..9e53692 100644 --- a/include/logit_cpp/logit/loggers/EventLogLogger.hpp +++ b/include/logit_cpp/logit/loggers/EventLogLogger.hpp @@ -33,7 +33,7 @@ namespace logit { bool async; ///< Use TaskExecutor when true. bool use_dedicated_executor = false; ///< Use a dedicated executor instead of the global TaskExecutor; native builds create one worker thread per logger. std::size_t queue_capacity = 0; ///< Maximum queue size for the dedicated executor (0 = unlimited). - detail::QueuePolicy queue_policy = detail::QueuePolicy::Block; ///< Overflow policy for the dedicated executor. + QueuePolicy queue_policy = QueuePolicy::Block; ///< Overflow policy for the dedicated executor. /// \brief Initialize configuration. /// \param s Source name. /// \param a Run asynchronously. @@ -66,7 +66,7 @@ namespace logit { bool async, bool use_dedicated_executor, std::size_t queue_capacity, - detail::QueuePolicy queue_policy) + QueuePolicy queue_policy) : EventLogLogger(make_config( source, async, @@ -164,7 +164,7 @@ namespace logit { bool async, bool use_dedicated_executor, std::size_t queue_capacity, - detail::QueuePolicy queue_policy) { + QueuePolicy queue_policy) { Config config(source, async); config.use_dedicated_executor = use_dedicated_executor; config.queue_capacity = queue_capacity; @@ -184,7 +184,7 @@ namespace logit { bool async; ///< Unused flag. bool use_dedicated_executor = false; ///< Unused flag. std::size_t queue_capacity = 0; ///< Unused. - detail::QueuePolicy queue_policy = detail::QueuePolicy::Block; ///< Unused. + QueuePolicy queue_policy = QueuePolicy::Block; ///< Unused. Config(const wchar_t* s = L"", bool a = false) : source(s), async(a) {} }; @@ -202,7 +202,7 @@ namespace logit { /// \brief Construct with parameters and ignored dedicated executor options. EventLogLogger(const wchar_t* source, bool async, bool use_dedicated_executor, - std::size_t queue_capacity, detail::QueuePolicy queue_policy) { + std::size_t queue_capacity, QueuePolicy queue_policy) { (void)source; (void)async; (void)use_dedicated_executor; (void)queue_capacity; (void)queue_policy; } diff --git a/include/logit_cpp/logit/loggers/FileLogger.hpp b/include/logit_cpp/logit/loggers/FileLogger.hpp index 9a3cfe6..1b4f7bf 100644 --- a/include/logit_cpp/logit/loggers/FileLogger.hpp +++ b/include/logit_cpp/logit/loggers/FileLogger.hpp @@ -43,7 +43,7 @@ namespace logit { uint32_t seq_width = 3; bool use_dedicated_executor = false; std::size_t queue_capacity = 0; - detail::QueuePolicy queue_policy = detail::QueuePolicy::Block; + QueuePolicy queue_policy = QueuePolicy::Block; }; FileLogger() { warn(); } @@ -51,9 +51,9 @@ namespace logit { FileLogger(const std::string&, const bool& = true, const int& = 30, const uint64_t& = 0, const uint32_t& = 0) { warn(); } FileLogger(const std::string&, const bool&, const int&, bool, std::size_t, - detail::QueuePolicy) { warn(); } + QueuePolicy) { warn(); } FileLogger(const std::string&, const bool&, const int&, uint64_t, uint32_t, - bool, std::size_t, detail::QueuePolicy) { warn(); } + bool, std::size_t, QueuePolicy) { warn(); } void log(const LogRecord&, const std::string&) override { warn(); } std::string get_string_param(const LoggerParam&) const override { return {}; } @@ -106,7 +106,7 @@ namespace logit { uint32_t seq_width = 3; ///< Width of sequence index. bool use_dedicated_executor = false; ///< Use a dedicated executor instead of the global TaskExecutor; native builds create one worker thread per logger. std::size_t queue_capacity = 0; ///< Maximum queue size for the dedicated executor (0 = unlimited). - detail::QueuePolicy queue_policy = detail::QueuePolicy::Block; ///< Overflow policy for the dedicated executor. + QueuePolicy queue_policy = QueuePolicy::Block; ///< Overflow policy for the dedicated executor. }; /// \brief Default constructor that uses default configuration. @@ -146,7 +146,7 @@ namespace logit { const int& auto_delete_days, bool use_dedicated_executor, std::size_t queue_capacity, - detail::QueuePolicy queue_policy) + QueuePolicy queue_policy) : FileLogger(make_config( directory, async, @@ -181,7 +181,7 @@ namespace logit { uint32_t max_rotated_files, bool use_dedicated_executor, std::size_t queue_capacity, - detail::QueuePolicy queue_policy) + QueuePolicy queue_policy) : FileLogger(make_config( directory, async, @@ -255,7 +255,7 @@ namespace logit { /// changes async mode or executor ownership. If no dedicated executor exists, /// only the stored config fields are updated for future reference. bool set_queue_config(std::size_t queue_capacity, - detail::QueuePolicy queue_policy) { + QueuePolicy queue_policy) { std::lock_guard lifecycle_lock(m_lifecycle_mutex); if (m_shutdown.load(std::memory_order_acquire)) { @@ -563,7 +563,7 @@ namespace logit { uint32_t max_rotated_files, bool use_dedicated_executor, std::size_t queue_capacity, - detail::QueuePolicy queue_policy) { + QueuePolicy queue_policy) { Config config; config.directory = directory; config.async = async; diff --git a/include/logit_cpp/logit/loggers/SyslogLogger.hpp b/include/logit_cpp/logit/loggers/SyslogLogger.hpp index 924cfa0..7fb0595 100644 --- a/include/logit_cpp/logit/loggers/SyslogLogger.hpp +++ b/include/logit_cpp/logit/loggers/SyslogLogger.hpp @@ -34,7 +34,7 @@ namespace logit { bool async; ///< Use TaskExecutor when true. bool use_dedicated_executor = false; ///< Use a dedicated executor instead of the global TaskExecutor; native builds create one worker thread per logger. std::size_t queue_capacity = 0; ///< Maximum queue size for the dedicated executor (0 = unlimited). - detail::QueuePolicy queue_policy = detail::QueuePolicy::Block; ///< Overflow policy for the dedicated executor. + QueuePolicy queue_policy = QueuePolicy::Block; ///< Overflow policy for the dedicated executor. /// \brief Initialize configuration. /// \param i Identifier string. /// \param f Facility code. @@ -71,7 +71,7 @@ namespace logit { bool async, bool use_dedicated_executor, std::size_t queue_capacity, - detail::QueuePolicy queue_policy) + QueuePolicy queue_policy) : SyslogLogger(make_config( ident, facility, @@ -163,7 +163,7 @@ namespace logit { bool async, bool use_dedicated_executor, std::size_t queue_capacity, - detail::QueuePolicy queue_policy) { + QueuePolicy queue_policy) { Config config(ident, facility, async); config.use_dedicated_executor = use_dedicated_executor; config.queue_capacity = queue_capacity; @@ -184,7 +184,7 @@ namespace logit { bool async; ///< Unused flag. bool use_dedicated_executor = false; ///< Unused flag. std::size_t queue_capacity = 0; ///< Unused. - detail::QueuePolicy queue_policy = detail::QueuePolicy::Block; ///< Unused. + QueuePolicy queue_policy = QueuePolicy::Block; ///< Unused. Config(const char* i="", int f=0, bool a=false) : ident(i), facility(f), async(a) {} }; @@ -203,7 +203,7 @@ namespace logit { /// \brief Construct with parameters and ignored dedicated executor options. SyslogLogger(const char* ident, int facility, bool async, bool use_dedicated_executor, - std::size_t queue_capacity, detail::QueuePolicy queue_policy) { + std::size_t queue_capacity, QueuePolicy queue_policy) { (void)ident; (void)facility; (void)async; (void)use_dedicated_executor; (void)queue_capacity; (void)queue_policy; } diff --git a/include/logit_cpp/logit/loggers/UniqueFileLogger.hpp b/include/logit_cpp/logit/loggers/UniqueFileLogger.hpp index 8249fd2..7a1d20a 100644 --- a/include/logit_cpp/logit/loggers/UniqueFileLogger.hpp +++ b/include/logit_cpp/logit/loggers/UniqueFileLogger.hpp @@ -34,14 +34,14 @@ namespace logit { size_t hash_length = 8; bool use_dedicated_executor = false; std::size_t queue_capacity = 0; - detail::QueuePolicy queue_policy = detail::QueuePolicy::Block; + QueuePolicy queue_policy = QueuePolicy::Block; }; UniqueFileLogger() { warn(); } UniqueFileLogger(const Config&) { warn(); } UniqueFileLogger(const std::string&, bool = true, int = 30, size_t = 8) { warn(); } UniqueFileLogger(const std::string&, bool, int, size_t, bool, std::size_t, - detail::QueuePolicy) { warn(); } + QueuePolicy) { warn(); } void log(const LogRecord&, const std::string&) override { warn(); } std::string get_string_param(const LoggerParam&) const override { return {}; } @@ -88,7 +88,7 @@ namespace logit { size_t hash_length = 8; ///< Length of the hash used in filenames. bool use_dedicated_executor = false; ///< Use a dedicated executor instead of the global TaskExecutor; native builds create one worker thread per logger. std::size_t queue_capacity = 0; ///< Maximum queue size for the dedicated executor (0 = unlimited). - detail::QueuePolicy queue_policy = detail::QueuePolicy::Block; ///< Overflow policy for the dedicated executor. + QueuePolicy queue_policy = QueuePolicy::Block; ///< Overflow policy for the dedicated executor. }; /// \brief Default constructor that uses default configuration. @@ -132,7 +132,7 @@ namespace logit { size_t hash_length, bool use_dedicated_executor, std::size_t queue_capacity, - detail::QueuePolicy queue_policy) + QueuePolicy queue_policy) : UniqueFileLogger(make_config( directory, async, @@ -199,7 +199,7 @@ namespace logit { /// changes async mode or executor ownership. If no dedicated executor exists, /// only the stored config fields are updated for future reference. bool set_queue_config(std::size_t queue_capacity, - detail::QueuePolicy queue_policy) { + QueuePolicy queue_policy) { std::lock_guard lifecycle_lock(m_lifecycle_mutex); if (m_shutdown.load(std::memory_order_acquire)) { @@ -478,7 +478,7 @@ namespace logit { size_t hash_length, bool use_dedicated_executor, std::size_t queue_capacity, - detail::QueuePolicy queue_policy) { + QueuePolicy queue_policy) { Config config; config.directory = directory; config.async = async; diff --git a/include/logit_cpp/logit/loggers/WindowsDebugLogger.hpp b/include/logit_cpp/logit/loggers/WindowsDebugLogger.hpp index 37f2419..5c91685 100644 --- a/include/logit_cpp/logit/loggers/WindowsDebugLogger.hpp +++ b/include/logit_cpp/logit/loggers/WindowsDebugLogger.hpp @@ -46,12 +46,12 @@ namespace logit { : async(async_value) , use_dedicated_executor(false) , queue_capacity(0) - , queue_policy(detail::QueuePolicy::Block) {} + , queue_policy(QueuePolicy::Block) {} bool async; ///< Flag indicating whether logging should be asynchronous. bool use_dedicated_executor; ///< Use a dedicated executor instead of the global TaskExecutor; native builds create one worker thread per logger. std::size_t queue_capacity; ///< Maximum queue size for the dedicated executor (0 = unlimited). - detail::QueuePolicy queue_policy; ///< Overflow policy for the dedicated executor. + QueuePolicy queue_policy; ///< Overflow policy for the dedicated executor. }; /// \brief Default constructor that uses default configuration. @@ -72,7 +72,7 @@ namespace logit { bool async, bool use_dedicated_executor, std::size_t queue_capacity, - detail::QueuePolicy queue_policy) + QueuePolicy queue_policy) : WindowsDebugLogger(make_config( async, use_dedicated_executor, @@ -126,7 +126,7 @@ namespace logit { /// changes async mode or executor ownership. If no dedicated executor exists, /// only the stored config fields are updated for future reference. bool set_queue_config(std::size_t queue_capacity, - detail::QueuePolicy queue_policy) { + QueuePolicy queue_policy) { std::lock_guard lifecycle_lock(m_lifecycle_mutex); if (m_shutdown.load(std::memory_order_acquire)) { @@ -256,7 +256,7 @@ namespace logit { bool async, bool use_dedicated_executor, std::size_t queue_capacity, - detail::QueuePolicy queue_policy) { + QueuePolicy queue_policy) { Config config(async); config.use_dedicated_executor = use_dedicated_executor; config.queue_capacity = queue_capacity; diff --git a/tests/include_loggers_nhr_test.cpp b/tests/include_loggers_nhr_test.cpp index 4002c8d..2f45b97 100644 --- a/tests/include_loggers_nhr_test.cpp +++ b/tests/include_loggers_nhr_test.cpp @@ -2,8 +2,9 @@ #include int main() { + const logit::QueuePolicy policy = logit::QueuePolicy::Block; logit::ConsoleLogger logger(false); logger.set_log_level(logit::LogLevel::LOG_LVL_WARN); logger.wait(); - return 0; + return policy == logit::QueuePolicy::Block ? 0 : 1; }