From 4feaa7b27942ac021c1cdb528fab5be238a3ef2d Mon Sep 17 00:00:00 2001 From: JCW Date: Fri, 12 Sep 2025 14:28:26 +0100 Subject: [PATCH] WIP Signed-off-by: JCW --- include/xrpl/basics/Log.h | 24 ++- include/xrpl/beast/utility/Journal.h | 197 +++++++++++++++++++- include/xrpl/beast/utility/WrappedSink.h | 8 +- src/libxrpl/basics/Log.cpp | 90 +++++++-- src/libxrpl/beast/utility/beast_Journal.cpp | 22 ++- src/test/beast/beast_Journal_test.cpp | 4 +- src/test/csf/Sim.h | 4 +- src/test/jtx/CaptureLogs.h | 4 +- src/test/jtx/CheckMessageLogs.h | 6 +- src/test/server/Server_test.cpp | 4 +- src/test/unit_test/SuiteJournal.h | 17 +- src/tests/libxrpl/basics/log.cpp | 8 +- src/xrpld/app/consensus/RCLConsensus.cpp | 9 +- 13 files changed, 330 insertions(+), 67 deletions(-) diff --git a/include/xrpl/basics/Log.h b/include/xrpl/basics/Log.h index 7c9e566ed4..f24c89c3d6 100644 --- a/include/xrpl/basics/Log.h +++ b/include/xrpl/basics/Log.h @@ -27,12 +27,15 @@ #include #include +#include #include +#include #include #include #include #include #include +#include #include namespace ripple { @@ -71,10 +74,10 @@ private: operator=(Sink const&) = delete; void - write(beast::severities::Severity level, std::string_view text) override; + write(beast::severities::Severity level, std::string_view text, beast::Journal::MessagePoolNode owner = nullptr) override; void - writeAlways(beast::severities::Severity level, std::string_view text) + writeAlways(beast::severities::Severity level, std::string_view text, beast::Journal::MessagePoolNode owner = nullptr) override; }; @@ -132,12 +135,12 @@ private: Does nothing if there is no associated system file. */ void - write(std::string_view str); + write(std::string&& str); /** @} */ private: - std::unique_ptr m_stream; + std::optional m_stream; boost::filesystem::path m_path; }; @@ -153,11 +156,18 @@ private: // Batching members mutable std::mutex batchMutex_; -public: + beast::lockfree::queue messages_; static constexpr size_t BATCH_BUFFER_SIZE = 64 * 1024; // 64KB buffer std::array batchBuffer_{}; std::span writeBuffer_; // Points to available write space std::span readBuffer_; // Points to data ready to flush + + // Log thread members + std::thread logThread_; + std::atomic stopLogThread_; + std::mutex logMutex_; + std::condition_variable logCondition_; + private: std::chrono::steady_clock::time_point lastFlush_ = std::chrono::steady_clock::now(); @@ -208,6 +218,7 @@ public: beast::severities::Severity level, std::string const& partition, std::string_view text, + beast::Journal::MessagePoolNode owner, bool console); std::string @@ -261,6 +272,9 @@ private: void flushBatchUnsafe(); + + void + logThreadWorker(); }; // Wraps a Journal::Stream to skip evaluation of diff --git a/include/xrpl/beast/utility/Journal.h b/include/xrpl/beast/utility/Journal.h index 42a4ed5e8b..1c30644de4 100644 --- a/include/xrpl/beast/utility/Journal.h +++ b/include/xrpl/beast/utility/Journal.h @@ -22,6 +22,7 @@ #include +#include #include #include #include @@ -34,6 +35,164 @@ #include #include +namespace beast { + +class StringBufferPool { +public: + // ----- Empty index marker ----- + static constexpr std::uint32_t kEmptyIdx = std::numeric_limits::max(); + + // ----- Single-word CAS target: {tag | idx} with pack/unpack ----- + struct Head { + std::uint32_t tag; + std::uint32_t idx; // kEmptyIdx means empty + + static std::uint64_t pack(Head h) noexcept { + return (std::uint64_t(h.tag) << 32) | h.idx; + } + static Head unpack(std::uint64_t v) noexcept { + return Head{ std::uint32_t(v >> 32), std::uint32_t(v) }; + } + }; + + // ----- Internal node ----- + struct Node { + std::uint32_t next_idx{kEmptyIdx}; + std::uint32_t self_idx{kEmptyIdx}; + std::string buf{}; + }; + static_assert(std::is_standard_layout_v, "Node must be standard layout"); + + // ----- User-facing move-only RAII handle ----- + class Handle { + public: + Handle() = default; + Handle(Handle&& other) noexcept + : owner_(other.owner_), node_(other.node_) { + other.owner_ = nullptr; other.node_ = nullptr; + } + Handle& operator=(Handle&& other) noexcept { + if (this != &other) { + // Return current if still held + if (owner_ && node_) owner_->give_back(std::move(*this)); + owner_ = other.owner_; + node_ = other.node_; + other.owner_ = nullptr; + other.node_ = nullptr; + } + return *this; + } + + Handle(const Handle&) = delete; + Handle& operator=(const Handle&) = delete; + + ~Handle() noexcept { + if (owner_ && node_) owner_->give_back(std::move(*this)); + } + + bool valid() const noexcept { return node_ != nullptr; } + std::string& string() noexcept { return node_->buf; } + const std::string& string() const noexcept { return node_->buf; } + + private: + friend class StringBufferPool; + Handle(StringBufferPool* owner, Node* n) : owner_(owner), node_(n) {} + + StringBufferPool* owner_ = nullptr; + Node* node_ = nullptr; + }; + + explicit StringBufferPool(std::uint32_t grow_by = 20) + : grow_by_(grow_by), head_(Head::pack({0, kEmptyIdx})) {} + + // Rent a buffer; grows on demand. Returns move-only RAII handle. + Handle rent() { + for (;;) { + std::uint64_t old64 = head_.load(std::memory_order_acquire); + Head old = Head::unpack(old64); + if (old.idx == kEmptyIdx) { grow_(); continue; } // rare slow path + + Node& n = nodes_[old.idx]; + std::uint32_t next = n.next_idx; + + Head neu{ std::uint32_t(old.tag + 1), next }; + if (head_.compare_exchange_weak(old64, Head::pack(neu), + std::memory_order_acq_rel, + std::memory_order_acquire)) { + return {this, &n}; + } + } + } + +private: + // Only the pool/handle can call this + void give_back(Handle&& h) noexcept { + Node* node = h.node_; + if (!node) return; // already invalid + const std::uint32_t idx = node->self_idx; + + node->buf.clear(); + + for (;;) { + std::uint64_t old64 = head_.load(std::memory_order_acquire); + Head old = Head::unpack(old64); + + node->next_idx = old.idx; + + Head neu{ std::uint32_t(old.tag + 1), idx }; + if (head_.compare_exchange_weak(old64, Head::pack(neu), + std::memory_order_acq_rel, + std::memory_order_acquire)) { + // Invalidate handle (prevents double return) + h.owner_ = nullptr; + h.node_ = nullptr; + return; + } + } + } + + void grow_() { + if (Head::unpack(head_.load(std::memory_order_acquire)).idx != kEmptyIdx) return; + std::scoped_lock lk(grow_mu_); + if (Head::unpack(head_.load(std::memory_order_acquire)).idx != kEmptyIdx) return; + + const std::uint32_t base = static_cast(nodes_.size()); + nodes_.resize(base + grow_by_); // indices [base .. base+grow_by_-1] + + // Init nodes and local chain + for (std::uint32_t i = 0; i < grow_by_; ++i) { + std::uint32_t idx = base + i; + Node& n = nodes_[idx]; + n.self_idx = idx; + n.next_idx = (i + 1 < grow_by_) ? (idx + 1) : kEmptyIdx; + } + + // Splice chain onto global head: [base .. base+grow_by_-1] + const std::uint32_t chain_head = base; + const std::uint32_t chain_tail = base + grow_by_ - 1; + + for (;;) { + std::uint64_t old64 = head_.load(std::memory_order_acquire); + Head old = Head::unpack(old64); + + nodes_[chain_tail].next_idx = old.idx; // tail -> old head + Head neu{ std::uint32_t(old.tag + 1), chain_head }; + + if (head_.compare_exchange_weak(old64, Head::pack(neu), + std::memory_order_acq_rel, + std::memory_order_acquire)) { + break; + } + } + } + + const std::uint32_t grow_by_; + std::atomic head_; // single 64-bit CAS (Head packed) + std::mutex grow_mu_; // only during growth + std::deque nodes_; // stable storage for nodes/strings +}; +} // namespace beast + namespace ripple::log { template class LogParameter @@ -181,10 +340,10 @@ public: buffer_.append(str); } - [[nodiscard]] std::string_view + void finish() { - return std::string_view{buffer_.c_str(), buffer_.size() - 1}; + buffer_.pop_back(); } private: @@ -323,18 +482,25 @@ public: class Sink; + using MessagePoolNode = lockfree::queue::Node*; + class JsonLogContext { - std::string buffer_; + MessagePoolNode messageBuffer_; detail::SimpleJsonWriter messageParamsWriter_; bool hasMessageParams_ = false; public: - JsonLogContext() : messageParamsWriter_(buffer_) + explicit JsonLogContext() + : messageBuffer_(rentFromPool()) + , messageParamsWriter_(messageBuffer_->data) { - buffer_.reserve(1024 * 5); + messageBuffer_->data.reserve(1024 * 5); } + MessagePoolNode + messageBuffer() { return messageBuffer_; } + void startMessageParams() { @@ -379,6 +545,7 @@ private: static std::shared_mutex globalLogAttributesMutex_; static bool jsonLogsEnabled_; + static lockfree::queue messagePool_; static thread_local JsonLogContext currentJsonLogContext_; // Invariant: m_sink always points to a valid Sink @@ -389,12 +556,26 @@ private: std::source_location location, severities::Severity severity) const; - static std::string_view + static MessagePoolNode formatLog(std::string const& message); public: //-------------------------------------------------------------------------- + static MessagePoolNode + rentFromPool() + { + auto node = messagePool_.pop(); + if (!node) + { + node = new lockfree::queue::Node(); + } + return node; + } + + static void + returnMessageNode(MessagePoolNode node) { messagePool_.push(node); } + static void enableStructuredJournal(); @@ -444,7 +625,7 @@ public: level is below the current threshold(). */ virtual void - write(Severity level, std::string_view text) = 0; + write(Severity level, std::string_view text, MessagePoolNode owner = nullptr) = 0; /** Bypass filter and write text to the sink at the specified severity. * Always write the message, but maintain the same formatting as if @@ -454,7 +635,7 @@ public: * @param text Text to write to sink. */ virtual void - writeAlways(Severity level, std::string_view text) = 0; + writeAlways(Severity level, std::string_view text, MessagePoolNode owner = nullptr) = 0; private: Severity thresh_; diff --git a/include/xrpl/beast/utility/WrappedSink.h b/include/xrpl/beast/utility/WrappedSink.h index e1343dc9e6..6024e6b797 100644 --- a/include/xrpl/beast/utility/WrappedSink.h +++ b/include/xrpl/beast/utility/WrappedSink.h @@ -88,17 +88,17 @@ public: } void - write(beast::severities::Severity level, std::string_view text) override + write(beast::severities::Severity level, std::string_view text, beast::Journal::MessagePoolNode owner = nullptr) override { using beast::Journal; - sink_.write(level, prefix_ + std::string{text}); + sink_.write(level, prefix_ + std::string(text), owner); } void - writeAlways(severities::Severity level, std::string_view text) override + writeAlways(severities::Severity level, std::string_view text, beast::Journal::MessagePoolNode owner = nullptr) override { using beast::Journal; - sink_.writeAlways(level, prefix_ + std::string{text}); + sink_.writeAlways(level, prefix_ + std::string(text), owner); } }; diff --git a/src/libxrpl/basics/Log.cpp b/src/libxrpl/basics/Log.cpp index 7b548fc05a..07fdffe682 100644 --- a/src/libxrpl/basics/Log.cpp +++ b/src/libxrpl/basics/Log.cpp @@ -43,6 +43,7 @@ constexpr auto FLUSH_INTERVAL = std::chrono::milliseconds(10); // Max delay before flush } + Logs::Sink::Sink( std::string const& partition, beast::severities::Severity thresh, @@ -52,18 +53,18 @@ Logs::Sink::Sink( } void -Logs::Sink::write(beast::severities::Severity level, std::string_view text) +Logs::Sink::write(beast::severities::Severity level, std::string_view text, beast::Journal::MessagePoolNode owner) { if (level < threshold()) return; - logs_.write(level, partition_, text, console()); + logs_.write(level, partition_, text, owner, console()); } void -Logs::Sink::writeAlways(beast::severities::Severity level, std::string_view text) +Logs::Sink::writeAlways(beast::severities::Severity level, std::string_view text, beast::Journal::MessagePoolNode owner) { - logs_.write(level, partition_, text, console()); + logs_.write(level, partition_, text, owner, console()); } //------------------------------------------------------------------------------ @@ -75,7 +76,7 @@ Logs::File::File() : m_stream(nullptr) bool Logs::File::isOpen() const noexcept { - return m_stream != nullptr; + return m_stream.has_value(); } bool @@ -86,10 +87,9 @@ Logs::File::open(boost::filesystem::path const& path) bool wasOpened = false; // VFALCO TODO Make this work with Unicode file paths - std::unique_ptr stream( - new std::ofstream(path.c_str(), std::fstream::app)); + std::ofstream stream(path.c_str(), std::fstream::app); - if (stream->good()) + if (stream.good()) { m_path = path; @@ -115,13 +115,13 @@ Logs::File::closeAndReopen() void Logs::File::close() { - m_stream = nullptr; + m_stream.reset(); } void -Logs::File::write(std::string_view text) +Logs::File::write(std::string&& text) { - if (m_stream != nullptr) + if (m_stream.has_value()) m_stream->write(text.data(), text.size()); } @@ -132,11 +132,23 @@ Logs::Logs(beast::severities::Severity thresh) , writeBuffer_( batchBuffer_) // Initially, entire buffer is available for writing , readBuffer_(batchBuffer_.data(), 0) // No data ready to flush initially + , stopLogThread_(false) { + logThread_ = std::thread(&Logs::logThreadWorker, this); } Logs::~Logs() { + // Signal log thread to stop and wait for it to finish + { + std::lock_guard lock(logMutex_); + stopLogThread_ = true; + } + logCondition_.notify_all(); + + if (logThread_.joinable()) + logThread_.join(); + flushBatch(); // Ensure all logs are written on shutdown } @@ -191,6 +203,7 @@ Logs::write( beast::severities::Severity level, std::string const& partition, std::string_view text, + beast::Journal::MessagePoolNode owner, bool console) { std::string s; @@ -201,8 +214,18 @@ Logs::write( result = s; } + // if (!silent_) + // std::cerr << s << '\n'; + + // Get a node from the pool or create a new one + if (!owner) return; + messages_.push(owner); + + // Signal log thread that new messages are available + logCondition_.notify_one(); + // Add to batch buffer for file output - { + if (0) { // std::lock_guard lock(batchMutex_); // Console output still immediate for responsiveness @@ -258,13 +281,54 @@ Logs::flushBatchUnsafe() return; // Write the read buffer contents to file in one system call - file_.write(std::string_view{readBuffer_.data(), readBuffer_.size()}); + // file_.write(std::string_view{readBuffer_.data(), readBuffer_.size()}); // Reset spans: entire buffer available for writing, nothing to read writeBuffer_ = std::span(batchBuffer_); readBuffer_ = std::span(batchBuffer_.data(), 0); } +void +Logs::logThreadWorker() +{ + beast::lockfree::queue::Node* node; + + while (!stopLogThread_) + { + std::unique_lock lock(logMutex_); + + // Wait for messages or stop signal + logCondition_.wait(lock, [this] { + return stopLogThread_ || !messages_.empty(); + }); + + // Process all available messages + while ((node = messages_.pop())) + { + // Write to file + file_.write(std::move(node->data)); + + // Also write to console if not silent + if (!silent_) + std::cerr << node->data << '\n'; + + // Return node to pool for reuse + beast::Journal::returnMessageNode(node); + } + } + + // Process any remaining messages on shutdown + while ((node = messages_.pop())) + { + file_.write(std::move(node->data)); + if (!silent_) + std::cerr << node->data << '\n'; + + // Return node to pool for reuse + beast::Journal::returnMessageNode(node); + } +} + std::string Logs::rotate() { diff --git a/src/libxrpl/beast/utility/beast_Journal.cpp b/src/libxrpl/beast/utility/beast_Journal.cpp index 3421fd7a6d..b7a7c7ef18 100644 --- a/src/libxrpl/beast/utility/beast_Journal.cpp +++ b/src/libxrpl/beast/utility/beast_Journal.cpp @@ -113,6 +113,7 @@ fastTimestampToString(std::int64_t milliseconds_since_epoch) std::string Journal::globalLogAttributes_; std::shared_mutex Journal::globalLogAttributesMutex_; bool Journal::jsonLogsEnabled_ = false; +lockfree::queue Journal::messagePool_{}; thread_local Journal::JsonLogContext Journal::currentJsonLogContext_{}; //------------------------------------------------------------------------------ @@ -156,12 +157,12 @@ public: } void - write(severities::Severity, std::string_view) override + write(severities::Severity, std::string_view, Journal::MessagePoolNode = nullptr) override { } void - writeAlways(severities::Severity, std::string_view) override + writeAlways(severities::Severity, std::string_view, Journal::MessagePoolNode = nullptr) override { } }; @@ -222,7 +223,7 @@ Journal::JsonLogContext::reset( }; thread_local ThreadIdStringInitializer const threadId; - buffer_.clear(); + messageBuffer_->data.clear(); if (!jsonLogsEnabled_) { @@ -291,13 +292,13 @@ Journal::initMessageContext( currentJsonLogContext_.reset(location, severity, name_, attributes_); } -std::string_view +Journal::MessagePoolNode Journal::formatLog(std::string const& message) { if (!jsonLogsEnabled_) { currentJsonLogContext_.writer().buffer() += message; - return currentJsonLogContext_.writer().buffer(); + return currentJsonLogContext_.messageBuffer(); } auto& writer = currentJsonLogContext_.writer(); @@ -309,7 +310,9 @@ Journal::formatLog(std::string const& message) writer.endObject(); - return writer.finish(); + writer.finish(); + + return currentJsonLogContext_.messageBuffer(); } void @@ -391,9 +394,10 @@ Journal::ScopedStream::~ScopedStream() if (!s.empty()) { if (s == "\n") - m_sink.write(m_level, formatLog("")); - else - m_sink.write(m_level, formatLog(s)); + s = ""; + + auto messageHandle = formatLog(s); + m_sink.write(m_level, messageHandle->data, messageHandle); } } diff --git a/src/test/beast/beast_Journal_test.cpp b/src/test/beast/beast_Journal_test.cpp index b1379a3456..e50d47b2e8 100644 --- a/src/test/beast/beast_Journal_test.cpp +++ b/src/test/beast/beast_Journal_test.cpp @@ -48,14 +48,14 @@ public: } void - write(severities::Severity level, std::string_view) override + write(severities::Severity level, std::string_view, beast::Journal::MessagePoolNode = nullptr) override { if (level >= threshold()) ++m_count; } void - writeAlways(severities::Severity level, std::string_view) override + writeAlways(severities::Severity level, std::string_view, beast::Journal::MessagePoolNode = nullptr) override { ++m_count; } diff --git a/src/test/csf/Sim.h b/src/test/csf/Sim.h index 313f8abd8f..7a606b40bf 100644 --- a/src/test/csf/Sim.h +++ b/src/test/csf/Sim.h @@ -49,7 +49,7 @@ public: } void - write(beast::severities::Severity level, std::string_view text) override + write(beast::severities::Severity level, std::string_view text, beast::Journal::MessagePoolNode owner = nullptr) override { if (level < threshold()) return; @@ -59,7 +59,7 @@ public: } void - writeAlways(beast::severities::Severity level, std::string_view text) override + writeAlways(beast::severities::Severity level, std::string_view text, beast::Journal::MessagePoolNode owner = nullptr) override { std::cout << clock_.now().time_since_epoch().count() << " " << text << std::endl; diff --git a/src/test/jtx/CaptureLogs.h b/src/test/jtx/CaptureLogs.h index 4fffae130e..2abcc6bd25 100644 --- a/src/test/jtx/CaptureLogs.h +++ b/src/test/jtx/CaptureLogs.h @@ -57,14 +57,14 @@ class CaptureLogs : public Logs } void - write(beast::severities::Severity level, std::string_view text) override + write(beast::severities::Severity level, std::string_view text, beast::Journal::MessagePoolNode owner = nullptr) override { std::lock_guard lock(strmMutex_); strm_ << text; } void - writeAlways(beast::severities::Severity level, std::string_view text) + writeAlways(beast::severities::Severity level, std::string_view text, beast::Journal::MessagePoolNode owner = nullptr) override { std::lock_guard lock(strmMutex_); diff --git a/src/test/jtx/CheckMessageLogs.h b/src/test/jtx/CheckMessageLogs.h index 6b950a82ee..18ae98b196 100644 --- a/src/test/jtx/CheckMessageLogs.h +++ b/src/test/jtx/CheckMessageLogs.h @@ -45,17 +45,17 @@ class CheckMessageLogs : public Logs } void - write(beast::severities::Severity level, std::string_view text) override + write(beast::severities::Severity level, std::string_view text, beast::Journal::MessagePoolNode owner = nullptr) override { if (text.find(owner_.msg_) != std::string::npos) *owner_.pFound_ = true; } void - writeAlways(beast::severities::Severity level, std::string_view text) + writeAlways(beast::severities::Severity level, std::string_view text, beast::Journal::MessagePoolNode owner = nullptr) override { - write(level, std::move(text)); + write(level, text, owner); } }; diff --git a/src/test/server/Server_test.cpp b/src/test/server/Server_test.cpp index 1c5aa67992..3ebed8b699 100644 --- a/src/test/server/Server_test.cpp +++ b/src/test/server/Server_test.cpp @@ -92,7 +92,7 @@ public: } void - write(beast::severities::Severity level, std::string_view text) override + write(beast::severities::Severity level, std::string_view text, beast::Journal::MessagePoolNode owner = nullptr) override { if (level < threshold()) return; @@ -101,7 +101,7 @@ public: } void - writeAlways(beast::severities::Severity level, std::string_view text) + writeAlways(beast::severities::Severity level, std::string_view text, beast::Journal::MessagePoolNode owner = nullptr) override { suite_.log << text << std::endl; diff --git a/src/test/unit_test/SuiteJournal.h b/src/test/unit_test/SuiteJournal.h index e58e435e6f..fc0f85d69a 100644 --- a/src/test/unit_test/SuiteJournal.h +++ b/src/test/unit_test/SuiteJournal.h @@ -49,24 +49,25 @@ public: } void - write(beast::severities::Severity level, std::string_view text) override; + write(beast::severities::Severity level, std::string_view text, beast::Journal::MessagePoolNode owner = nullptr) override; void - writeAlways(beast::severities::Severity level, std::string_view text) override; + writeAlways(beast::severities::Severity level, std::string_view text, beast::Journal::MessagePoolNode owner = nullptr) override; }; inline void -SuiteJournalSink::write(beast::severities::Severity level, std::string_view text) +SuiteJournalSink::write(beast::severities::Severity level, std::string_view text, beast::Journal::MessagePoolNode owner) { // Only write the string if the level at least equals the threshold. if (level >= threshold()) - writeAlways(level, std::move(text)); + writeAlways(level, text, owner); } inline void SuiteJournalSink::writeAlways( beast::severities::Severity level, - std::string_view text) + std::string_view text, + beast::Journal::MessagePoolNode owner) { using namespace beast::severities; @@ -134,15 +135,15 @@ public: } void - write(beast::severities::Severity level, std::string_view text) override + write(beast::severities::Severity level, std::string_view text, beast::Journal::MessagePoolNode owner = nullptr) override { if (level < threshold()) return; - writeAlways(level, std::move(text)); + writeAlways(level, text, owner); } inline void - writeAlways(beast::severities::Severity level, std::string_view text) override + writeAlways(beast::severities::Severity level, std::string_view text, beast::Journal::MessagePoolNode = nullptr) override { strm_ << text << std::endl; } diff --git a/src/tests/libxrpl/basics/log.cpp b/src/tests/libxrpl/basics/log.cpp index 9d12202ea6..10c2bb4c89 100644 --- a/src/tests/libxrpl/basics/log.cpp +++ b/src/tests/libxrpl/basics/log.cpp @@ -53,13 +53,13 @@ private: operator=(Sink const&) = delete; void - write(beast::severities::Severity level, std::string_view text) override + write(beast::severities::Severity level, std::string_view text, beast::Journal::MessagePoolNode owner = nullptr) override { logs_.write(level, partition_, text, false); } void - writeAlways(beast::severities::Severity level, std::string_view text) + writeAlways(beast::severities::Severity level, std::string_view text, beast::Journal::MessagePoolNode owner = nullptr) override { logs_.write(level, partition_, text, false); @@ -358,13 +358,13 @@ public: } void - write(beast::severities::Severity level, std::string_view text) override + write(beast::severities::Severity level, std::string_view text, beast::Journal::MessagePoolNode owner = nullptr) override { strm_ << text; } void - writeAlways(beast::severities::Severity level, std::string_view text) override + writeAlways(beast::severities::Severity level, std::string_view text, beast::Journal::MessagePoolNode owner = nullptr) override { strm_ << text; } diff --git a/src/xrpld/app/consensus/RCLConsensus.cpp b/src/xrpld/app/consensus/RCLConsensus.cpp index 4e13edc649..8dfd48c761 100644 --- a/src/xrpld/app/consensus/RCLConsensus.cpp +++ b/src/xrpld/app/consensus/RCLConsensus.cpp @@ -1134,17 +1134,16 @@ RclConsensusLogger::~RclConsensusLogger() if (beast::Journal::isStructuredJournalEnabled()) { - thread_local std::string buffer; - buffer.reserve(1024); - buffer.clear(); - beast::detail::SimpleJsonWriter writer{buffer}; + auto node = beast::Journal::rentFromPool(); + beast::detail::SimpleJsonWriter writer{node->data}; writer.startObject(); writer.writeKey("Msg"); writer.writeString(outSs.str()); writer.writeKey("Tm"); writer.writeString(to_string(std::chrono::system_clock::now())); writer.endObject(); - j_.sink().writeAlways(beast::severities::kInfo, writer.finish()); + writer.finish(); + j_.sink().writeAlways(beast::severities::kInfo, writer.buffer(), node); } else {