From ac85fed8d4b3ba1681c1c6d69b01f882a2343931 Mon Sep 17 00:00:00 2001 From: Baber Nawaz Date: Tue, 4 Nov 2025 15:18:57 +0000 Subject: [PATCH] feat: Use buffer pool for logging Eliminates dynamic allocations SDB-10043 --- toolbox/sys/Log.hpp | 6 +++++- toolbox/sys/Log.ut.cpp | 3 +++ toolbox/sys/Logger.cpp | 32 ++++++++++++++++++++++++++++++-- toolbox/sys/Logger.hpp | 13 ++++++++++--- 4 files changed, 48 insertions(+), 6 deletions(-) diff --git a/toolbox/sys/Log.hpp b/toolbox/sys/Log.hpp index d502c75de..1577a5503 100644 --- a/toolbox/sys/Log.hpp +++ b/toolbox/sys/Log.hpp @@ -46,7 +46,11 @@ class Log { , level_{level} , os_{log_stream()} { - os_.set_storage(os_.make_storage()); + LogMsgPtr storage; + if (!log_buf_pool().pop(storage)) [[unlikely]] { + storage = LogStream::make_storage(); + } + os_.set_storage(std::move(storage)); } ~Log() { diff --git a/toolbox/sys/Log.ut.cpp b/toolbox/sys/Log.ut.cpp index 9c9e660a1..8dce3f399 100644 --- a/toolbox/sys/Log.ut.cpp +++ b/toolbox/sys/Log.ut.cpp @@ -44,6 +44,9 @@ struct TestLogger final : Logger { void do_write_log(WallTime /*ts*/, LogLevel level, int /*tid*/, LogMsgPtr&& msg, size_t size) noexcept override { + const auto finally = make_finally([&]() noexcept { + log_buf_pool().bounded_push(std::move(msg)); + }); last_level = level; last_msg.assign(static_cast(msg.get()), size); } diff --git a/toolbox/sys/Logger.cpp b/toolbox/sys/Logger.cpp index 4f51d744a..686fdada0 100644 --- a/toolbox/sys/Logger.cpp +++ b/toolbox/sys/Logger.cpp @@ -16,6 +16,8 @@ #include "Logger.hpp" +#include + #include #include @@ -61,10 +63,23 @@ inline pid_t gettid() } #endif +struct LogBufPoolWrapper { + static constexpr std::size_t InitialPoolSize = 8; + LogBufPoolWrapper() + { + for ([[maybe_unused]] std::size_t i = 0; i < InitialPoolSize; i++) { + pool.bounded_push(util::make_storage()); + } + } + LogBufPool pool; +}; +static LogBufPoolWrapper log_buf_pool_{}; + class NullLogger final : public Logger { - void do_write_log(WallTime /*ts*/, LogLevel /*level*/, int /*tid*/, LogMsgPtr&& /*msg*/, + void do_write_log(WallTime /*ts*/, LogLevel /*level*/, int /*tid*/, LogMsgPtr&& msg, size_t /*size*/) noexcept override { + log_buf_pool().bounded_push(std::move(msg)); } } null_logger_; @@ -72,6 +87,10 @@ class StdLogger final : public Logger { void do_write_log(WallTime ts, LogLevel level, int tid, LogMsgPtr&& msg, size_t size) noexcept override { + const auto finally = make_finally([&]() noexcept { + log_buf_pool().bounded_push(std::move(msg)); + }); + const auto t{WallClock::to_time_t(ts)}; tm tm; localtime_r(&t, &tm); @@ -107,6 +126,10 @@ class SysLogger final : public Logger { void do_write_log(WallTime /*ts*/, LogLevel level, int /*tid*/, LogMsgPtr&& msg, size_t size) noexcept override { + const auto finally = make_finally([&]() noexcept { + log_buf_pool().bounded_push(std::move(msg)); + }); + int prio; switch (level) { case LogLevel::None: @@ -152,6 +175,11 @@ inline Logger& acquire_logger() noexcept } // namespace +LogBufPool& log_buf_pool() noexcept +{ + return log_buf_pool_.pool; +} + Logger& null_logger() noexcept { return null_logger_; @@ -244,7 +272,7 @@ void AsyncLogger::do_write_log(WallTime ts, LogLevel level, int tid, LogMsgPtr&& // and prevents crashes caused by an out-of-memory situation during rare log-burst spikes. } // Failed to push the task, restore ownership of msg_ptr. - LogMsgPtr{msg_ptr}; + log_buf_pool().bounded_push(LogMsgPtr{msg_ptr}); } } // namespace sys diff --git a/toolbox/sys/Logger.hpp b/toolbox/sys/Logger.hpp index 305ae5bd2..9b315f18e 100644 --- a/toolbox/sys/Logger.hpp +++ b/toolbox/sys/Logger.hpp @@ -17,14 +17,16 @@ #ifndef TOOLBOX_SYS_LOGGER_HPP #define TOOLBOX_SYS_LOGGER_HPP -#include -#include - #include #include #include #include +#include +#include + +#include + namespace toolbox { inline namespace sys { @@ -50,6 +52,11 @@ enum class LogLevel : int { }; using LogMsgPtr = StoragePtr; +inline constexpr std::size_t LogBufPoolCapacity = 64; +using LogBufPool = boost::lockfree::stack>; + +/// A pool of log buffers, eliminating the need for dynamic memory allocations when logging +TOOLBOX_API LogBufPool& log_buf_pool() noexcept; /// Null logger. This logger does nothing and is effectively /dev/null. TOOLBOX_API Logger& null_logger() noexcept;