From 06193bde5d1373a1352692b0d3d5ce467e014bc9 Mon Sep 17 00:00:00 2001 From: JCW Date: Wed, 29 Apr 2026 22:39:13 +0100 Subject: [PATCH] Performance improvements --- include/xrpl/basics/Logger.h | 28 +++++++-------- src/libxrpl/basics/Logger.cpp | 67 ++++++++++++++++++++++------------- 2 files changed, 56 insertions(+), 39 deletions(-) diff --git a/include/xrpl/basics/Logger.h b/include/xrpl/basics/Logger.h index 3a5598aef8..f639bb286b 100644 --- a/include/xrpl/basics/Logger.h +++ b/include/xrpl/basics/Logger.h @@ -102,10 +102,10 @@ class Logger */ class Pump final { - std::shared_ptr logger_; + spdlog::logger* logger_; Severity const severity_; std::source_location const sourceLocation_; - std::string stream_; + fmt::memory_buffer stream_; bool const enabled_; bool const jsonMode_; std::string parameters_; // accumulated JSON parameter fragments @@ -113,11 +113,7 @@ class Logger public: ~Pump(); - Pump( - std::shared_ptr logger, - Severity sev, - std::source_location const& loc, - bool jsonMode); + Pump(spdlog::logger* logger, Severity sev, std::source_location const& loc, bool jsonMode); Pump(Pump&&) = delete; Pump(Pump const&) = delete; @@ -142,12 +138,15 @@ class Logger operator<<(T&& data) { if (enabled_) - stream_ += to_string(data); + { + auto const s = to_string(data); + stream_.append(s.data(), s.data() + s.size()); + } return *this; } /** - * @brief Appends any fmt-formattable data into the output string. + * @brief Appends any fmt-formattable data into the output buffer. * * Fallback for types that do not have an @c xrpl::to_string overload * but can be formatted by @c fmt::format (e.g. arithmetic types, @@ -163,14 +162,14 @@ class Logger operator<<(T&& data) { if (enabled_) - fmt::format_to(std::back_inserter(stream_), "{}", std::forward(data)); + fmt::format_to(fmt::appender(stream_), "{}", std::forward(data)); return *this; } /** * @brief Captures a structured log parameter. * - * The parameter value is always appended to the output string. + * The parameter value is always appended to the output buffer. * In JSON mode, the parameter is also accumulated into the * parameters string for the "values" object emitted in the * destructor. @@ -186,14 +185,15 @@ class Logger if (!enabled_) return *this; - // Append the raw string representation to the output stream + // Append the raw string representation to the output buffer if constexpr (detail::HasToString) { - stream_ += to_string(p.value()); + auto const s = to_string(p.value()); + stream_.append(s.data(), s.data() + s.size()); } else { - fmt::format_to(std::back_inserter(stream_), "{}", p.value()); + fmt::format_to(fmt::appender(stream_), "{}", p.value()); } if (jsonMode_) diff --git a/src/libxrpl/basics/Logger.cpp b/src/libxrpl/basics/Logger.cpp index 6f002f34b6..048c1b61b5 100644 --- a/src/libxrpl/basics/Logger.cpp +++ b/src/libxrpl/basics/Logger.cpp @@ -174,13 +174,14 @@ createPatternFormatter(std::string const& pattern) * @param message The message to potentially truncate (modified in-place) */ static void -truncateMessage(std::string& message) +truncateMessage(fmt::memory_buffer& message) { static constexpr std::size_t kMAX_MESSAGE_CHARS = 12 * 1024; if (message.size() > kMAX_MESSAGE_CHARS) { message.resize(kMAX_MESSAGE_CHARS - 3); - message += "..."; + static constexpr char kELLIPSIS[] = "..."; + message.append(kELLIPSIS, kELLIPSIS + 3); } } @@ -196,25 +197,33 @@ truncateMessage(std::string& message) * @param output The log message to scrub (modified in-place) */ static void -scrubSecrets(std::string& output) +scrubSecrets(fmt::memory_buffer& output) { - auto scrubber = [&output](char const* token) { - auto first = output.find(token); + // Fast path: if there's no double-quote anywhere in the message, + // none of the JSON-like tokens can possibly match. + std::string_view const view{output.data(), output.size()}; + if (view.find('"') == std::string_view::npos) + return; + + // We need string operations (find/replace) so convert temporarily. + // This is only reached for messages that contain at least one '"'. + std::string tmp{view}; + + auto scrubber = [&tmp](char const* token) { + auto first = tmp.find(token); - // If we have found the specified token, then attempt to isolate the - // sensitive data (it's enclosed by double quotes) and mask it off: if (first != std::string::npos) { - first = output.find('\"', first + std::strlen(token)); + first = tmp.find('\"', first + std::strlen(token)); if (first != std::string::npos) { - auto last = output.find('\"', ++first); + auto last = tmp.find('\"', ++first); if (last == std::string::npos) - last = output.size(); + last = tmp.size(); - output.replace(first, last - first, last - first, '*'); + tmp.replace(first, last - first, last - first, '*'); } } }; @@ -226,6 +235,10 @@ scrubSecrets(std::string& output) scrubber("\"master_seed\""); scrubber("\"master_seed_hex\""); scrubber("\"passphrase\""); + + // Copy the scrubbed result back into the buffer + output.clear(); + output.append(tmp.data(), tmp.data() + tmp.size()); } /** @@ -538,11 +551,11 @@ Logger::~Logger() } Logger::Pump::Pump( - std::shared_ptr logger, + spdlog::logger* logger, Severity sev, std::source_location const& loc, bool jsonMode) - : logger_(std::move(logger)) + : logger_(logger) , severity_(sev) , sourceLocation_(loc) , enabled_(logger_ != nullptr && logger_->should_log(toSpdlogLevel(sev))) @@ -551,7 +564,7 @@ Logger::Pump::Pump( if (enabled_ && jsonMode_) { // Open the quoted message value - stream_ += "\""; + stream_.push_back('"'); } } @@ -565,14 +578,15 @@ Logger::Pump::~Pump() if (jsonMode_) { // Close the quoted message value - stream_ += "\""; + stream_.push_back('"'); // Append the parameters object if any were captured if (!parameters_.empty()) { - stream_ += ", \"values\": {"; - stream_ += parameters_; - stream_ += "}"; + static constexpr char kVALUES_OPEN[] = ", \"values\": {"; + stream_.append(kVALUES_OPEN, kVALUES_OPEN + sizeof(kVALUES_OPEN) - 1); + stream_.append(parameters_.data(), parameters_.data() + parameters_.size()); + stream_.push_back('}'); } } @@ -580,39 +594,42 @@ Logger::Pump::~Pump() truncateMessage(stream_); scrubSecrets(stream_); - logger_->log(sourceLocation, toSpdlogLevel(severity_), stream_); + logger_->log( + sourceLocation, + toSpdlogLevel(severity_), + std::string_view{stream_.data(), stream_.size()}); } } Logger::Pump Logger::trace(std::source_location const& loc) const { - return {logger_, Severity::TRC, loc, LogServiceState::jsonMode_}; + return {logger_.get(), Severity::TRC, loc, LogServiceState::jsonMode_}; } Logger::Pump Logger::debug(std::source_location const& loc) const { - return {logger_, Severity::DBG, loc, LogServiceState::jsonMode_}; + return {logger_.get(), Severity::DBG, loc, LogServiceState::jsonMode_}; } Logger::Pump Logger::info(std::source_location const& loc) const { - return {logger_, Severity::NFO, loc, LogServiceState::jsonMode_}; + return {logger_.get(), Severity::NFO, loc, LogServiceState::jsonMode_}; } Logger::Pump Logger::warn(std::source_location const& loc) const { - return {logger_, Severity::WRN, loc, LogServiceState::jsonMode_}; + return {logger_.get(), Severity::WRN, loc, LogServiceState::jsonMode_}; } Logger::Pump Logger::error(std::source_location const& loc) const { - return {logger_, Severity::ERR, loc, LogServiceState::jsonMode_}; + return {logger_.get(), Severity::ERR, loc, LogServiceState::jsonMode_}; } Logger::Pump Logger::fatal(std::source_location const& loc) const { - return {logger_, Severity::FTL, loc, LogServiceState::jsonMode_}; + return {logger_.get(), Severity::FTL, loc, LogServiceState::jsonMode_}; } Logger::Logger(std::shared_ptr logger) : logger_(std::move(logger))