diff --git a/cmake/RippledCore.cmake b/cmake/RippledCore.cmake index 0e7ab8be21..b9d5cb2cc6 100644 --- a/cmake/RippledCore.cmake +++ b/cmake/RippledCore.cmake @@ -51,6 +51,8 @@ target_link_libraries(xrpl.libpb # TODO: Clean up the number of library targets later. add_library(xrpl.imports.main INTERFACE) +find_package(RapidJSON) + target_link_libraries(xrpl.imports.main INTERFACE LibArchive::LibArchive @@ -75,6 +77,7 @@ add_module(xrpl beast) target_link_libraries(xrpl.libxrpl.beast PUBLIC xrpl.imports.main xrpl.libpb + rapidjson ) # Level 02 @@ -85,14 +88,9 @@ target_link_libraries(xrpl.libxrpl.basics PUBLIC xrpl.libxrpl.beast) add_module(xrpl json) target_link_libraries(xrpl.libxrpl.json PUBLIC xrpl.libxrpl.basics) -add_module(xrpl telemetry) -target_link_libraries(xrpl.libxrpl.telemetry PUBLIC xrpl.libxrpl.json) add_module(xrpl crypto) -target_link_libraries(xrpl.libxrpl.crypto PUBLIC - xrpl.libxrpl.basics - xrpl.libxrpl.telemetry -) +target_link_libraries(xrpl.libxrpl.crypto PUBLIC xrpl.libxrpl.basics) # Level 04 add_module(xrpl protocol) @@ -139,7 +137,6 @@ target_link_modules(xrpl PUBLIC beast crypto json - telemetry protocol resource server diff --git a/cmake/RippledInstall.cmake b/cmake/RippledInstall.cmake index 48fb0fcf95..95c25a212f 100644 --- a/cmake/RippledInstall.cmake +++ b/cmake/RippledInstall.cmake @@ -16,7 +16,6 @@ install ( xrpl.libxrpl.beast xrpl.libxrpl.crypto xrpl.libxrpl.json - xrpl.libxrpl.telemetry xrpl.libxrpl.protocol xrpl.libxrpl.resource xrpl.libxrpl.ledger diff --git a/conanfile.py b/conanfile.py index 3146b887e0..024ef85c0d 100644 --- a/conanfile.py +++ b/conanfile.py @@ -30,6 +30,7 @@ class Xrpl(ConanFile): 'openssl/1.1.1w', 'soci/4.0.3', 'zlib/1.3.1', + "rapidjson/1.1.0" ] test_requires = [ diff --git a/include/xrpl/basics/Log.h b/include/xrpl/basics/Log.h index b9eaa6b90c..6638da7bee 100644 --- a/include/xrpl/basics/Log.h +++ b/include/xrpl/basics/Log.h @@ -167,8 +167,6 @@ private: beast::severities::Severity thresh_; File file_; bool silent_ = false; - static std::unique_ptr - globalLogAttributes_; public: Logs(beast::severities::Severity level); @@ -191,8 +189,8 @@ public: beast::Journal journal( std::string const& name, - std::unique_ptr attributes = - {}); + std::optional attributes = + std::nullopt); beast::severities::Severity threshold() const; @@ -229,20 +227,6 @@ public: std::string const& partition, beast::severities::Severity startingLevel); - static void - setGlobalAttributes(std::unique_ptr - globalLogAttributes) - { - if (!globalLogAttributes_) - { - globalLogAttributes_ = std::move(globalLogAttributes); - } - else - { - globalLogAttributes_->combine(std::move(globalLogAttributes)); - } - } - public: static LogSeverity fromSeverity(beast::severities::Severity level); diff --git a/include/xrpl/beast/utility/Journal.h b/include/xrpl/beast/utility/Journal.h index ea4f277193..6a232cf57c 100644 --- a/include/xrpl/beast/utility/Journal.h +++ b/include/xrpl/beast/utility/Journal.h @@ -21,11 +21,62 @@ #define BEAST_UTILITY_JOURNAL_H_INCLUDED #include +#include -#include +#include +#include #include #include + +namespace ripple::log { +template +class LogParameter +{ +public: + template + LogParameter(char const* name, TArg&& value) + : name_(name), value_(std::forward(value)) + { + } + +private: + char const* name_; + T value_; + + template + friend std::ostream& + operator<<(std::ostream& os, LogParameter const&); +}; + +template +class LogField +{ +public: + template + LogField(char const* name, TArg&& value) + : name_(name), value_(std::forward(value)) + { + } + +private: + char const* name_; + T value_; + + template + friend std::ostream& + operator<<(std::ostream& os, LogField const&); +}; + +template +std::ostream& +operator<<(std::ostream& os, LogField const& param); + +template +std::ostream& +operator<<(std::ostream& os, LogParameter const& param); +} + namespace beast { /** A namespace for easy access to logging severity values. */ @@ -64,75 +115,111 @@ to_string(Severity severity); class Journal { public: + template + friend std::ostream& + ripple::log::operator<<(std::ostream& os, ripple::log::LogField const& param); + + template + friend std::ostream& + ripple::log::operator<<( + std::ostream& os, + ripple::log::LogParameter const& param); + class Sink; - class StructuredJournalImpl; + class JsonLogAttributes + { + public: + using AttributeFields = rapidjson::Value; - class StructuredLogAttributes; + JsonLogAttributes(); + JsonLogAttributes(JsonLogAttributes const& other); + + JsonLogAttributes& + operator=(JsonLogAttributes const& other); + + void + setModuleName(std::string const& name); + + [[nodiscard]] static JsonLogAttributes + combine(AttributeFields const& a, AttributeFields const& b); + + AttributeFields& + contextValues() + { + return contextValues_; + } + + [[nodiscard]] AttributeFields const& + contextValues() const + { + return contextValues_; + } + + rapidjson::MemoryPoolAllocator<>& + allocator() + { + return allocator_; + } + + private: + AttributeFields contextValues_; + rapidjson::MemoryPoolAllocator<> allocator_; + + friend class Journal; + }; + + struct JsonLogContext + { + std::source_location location = {}; + rapidjson::Value messageParams; + rapidjson::MemoryPoolAllocator<> allocator; + + JsonLogContext() = default; + + void + reset(std::source_location location_) noexcept + { + location = location_; + messageParams = rapidjson::Value{}; + messageParams.SetObject(); + allocator.Clear(); + } + }; private: // Severity level / threshold of a Journal message. using Severity = severities::Severity; - std::unique_ptr m_attributes; + std::optional m_attributes; + static std::optional globalLogAttributes_; + static std::mutex globalLogAttributesMutex_; + static bool m_jsonLogsEnabled; + + static thread_local JsonLogContext currentJsonLogContext_; - static std::unique_ptr m_structuredJournalImpl; // Invariant: m_sink always points to a valid Sink Sink* m_sink = nullptr; + static void + initMessageContext(std::source_location location); + + static std::string + formatLog(std::string const& message, + severities::Severity severity, + std::optional const& attributes = std::nullopt); public: //-------------------------------------------------------------------------- static void - enableStructuredJournal(std::unique_ptr impl) - { - m_structuredJournalImpl = std::move(impl); - } + enableStructuredJournal(); static void - disableStructuredJournal() - { - m_structuredJournalImpl = nullptr; - } + disableStructuredJournal(); static bool - isStructuredJournalEnabled() - { - return m_structuredJournalImpl != nullptr; - } - - class StructuredJournalImpl - { - public: - StructuredJournalImpl() = default; - StructuredJournalImpl(StructuredJournalImpl const&) = default; - virtual void - initMessageContext(std::source_location location) = 0; - virtual void - flush( - Sink* sink, - severities::Severity level, - std::string const& text, - StructuredLogAttributes* attributes) = 0; - virtual ~StructuredJournalImpl() = default; - }; - - class StructuredLogAttributes - { - public: - StructuredLogAttributes() = default; - StructuredLogAttributes(StructuredLogAttributes const&) = default; - virtual void - setModuleName(std::string const& name) = 0; - virtual std::unique_ptr - clone() const = 0; - virtual void - combine(std::unique_ptr const& attributes) = 0; - virtual void - combine(std::unique_ptr&& attributes) = 0; - virtual ~StructuredLogAttributes() = default; - }; + isStructuredJournalEnabled(); /** Abstraction for the underlying message destination. */ class Sink @@ -214,25 +301,25 @@ public: public: ScopedStream(ScopedStream const& other) : ScopedStream( - other.m_attributes ? other.m_attributes->clone() : nullptr, + other.m_attributes, other.m_sink, other.m_level) { } ScopedStream( - std::unique_ptr attributes, + std::optional attributes, Sink& sink, Severity level); template ScopedStream( - std::unique_ptr attributes, + std::optional attributes, Stream const& stream, T const& t); ScopedStream( - std::unique_ptr attributes, + std::optional attributes, Stream const& stream, std::ostream& manip(std::ostream&)); @@ -255,7 +342,7 @@ public: operator<<(T const& t) const; private: - std::unique_ptr m_attributes; + std::optional m_attributes; Sink& m_sink; Severity const m_level; std::ostringstream mutable m_ostream; @@ -291,7 +378,7 @@ public: Constructor is inlined so checking active() very inexpensive. */ Stream( - std::unique_ptr attributes, + std::optional attributes, Sink& sink, Severity level) : m_attributes(std::move(attributes)), m_sink(sink), m_level(level) @@ -304,7 +391,7 @@ public: /** Construct or copy another Stream. */ Stream(Stream const& other) : Stream( - other.m_attributes ? other.m_attributes->clone() : nullptr, + other.m_attributes, other.m_sink, other.m_level) { @@ -353,7 +440,7 @@ public: /** @} */ private: - std::unique_ptr m_attributes; + std::optional m_attributes; Sink& m_sink; Severity m_level; }; @@ -372,47 +459,29 @@ public: /** Journal has no default constructor. */ Journal() = delete; - Journal(Journal const& other) : Journal(other, nullptr) - { - } - Journal( Journal const& other, - std::unique_ptr attributes) + std::optional attributes = std::nullopt) : m_sink(other.m_sink) { - if (attributes) - m_attributes = std::move(attributes); - if (other.m_attributes) + if (attributes.has_value()) + m_attributes = std::move(attributes.value()); + if (other.m_attributes.has_value()) { - if (m_attributes) - m_attributes->combine(other.m_attributes); + if (m_attributes.has_value()) + m_attributes = JsonLogAttributes::combine( + other.m_attributes->contextValues_, + m_attributes->contextValues_ + ); else - m_attributes = other.m_attributes->clone(); + m_attributes = other.m_attributes; } } - - Journal( - Journal&& other, - std::unique_ptr attributes = {}) noexcept - : m_sink(other.m_sink) - { - if (attributes) - m_attributes = std::move(attributes); - if (other.m_attributes) - { - if (m_attributes) - m_attributes->combine(std::move(other.m_attributes)); - else - m_attributes = std::move(other.m_attributes); - } - } - /** Create a journal that writes to the specified sink. */ - Journal( + explicit Journal( Sink& sink, std::string const& name = {}, - std::unique_ptr attributes = {}) + std::optional attributes = std::nullopt) : m_sink(&sink) { if (attributes) @@ -425,9 +494,11 @@ public: Journal& operator=(Journal const& other) { + if (&other == this) + return *this; + m_sink = other.m_sink; - if (other.m_attributes) - m_attributes = other.m_attributes->clone(); + m_attributes = other.m_attributes; return *this; } @@ -435,8 +506,7 @@ public: operator=(Journal&& other) noexcept { m_sink = other.m_sink; - if (other.m_attributes) - m_attributes = std::move(other.m_attributes); + m_attributes = std::move(other.m_attributes); return *this; } @@ -452,7 +522,7 @@ public: stream(Severity level) const { return Stream( - m_attributes ? m_attributes->clone() : nullptr, *m_sink, level); + m_attributes, *m_sink, level); } /** Returns `true` if any message would be logged at this severity level. @@ -470,10 +540,10 @@ public: Stream trace(std::source_location location = std::source_location::current()) const { - if (m_structuredJournalImpl) - m_structuredJournalImpl->initMessageContext(location); + if (m_jsonLogsEnabled) + initMessageContext(location); return { - m_attributes ? m_attributes->clone() : nullptr, + m_attributes, *m_sink, severities::kTrace}; } @@ -481,10 +551,10 @@ public: Stream debug(std::source_location location = std::source_location::current()) const { - if (m_structuredJournalImpl) - m_structuredJournalImpl->initMessageContext(location); + if (m_jsonLogsEnabled) + initMessageContext(location); return { - m_attributes ? m_attributes->clone() : nullptr, + m_attributes, *m_sink, severities::kDebug}; } @@ -492,10 +562,10 @@ public: Stream info(std::source_location location = std::source_location::current()) const { - if (m_structuredJournalImpl) - m_structuredJournalImpl->initMessageContext(location); + if (m_jsonLogsEnabled) + initMessageContext(location); return { - m_attributes ? m_attributes->clone() : nullptr, + m_attributes, *m_sink, severities::kInfo}; } @@ -503,10 +573,12 @@ public: Stream warn(std::source_location location = std::source_location::current()) const { - if (m_structuredJournalImpl) - m_structuredJournalImpl->initMessageContext(location); + const char* a = "a"; + rapidjson::Value v{a, 1}; + if (m_jsonLogsEnabled) + initMessageContext(location); return { - m_attributes ? m_attributes->clone() : nullptr, + m_attributes, *m_sink, severities::kWarning}; } @@ -514,10 +586,10 @@ public: Stream error(std::source_location location = std::source_location::current()) const { - if (m_structuredJournalImpl) - m_structuredJournalImpl->initMessageContext(location); + if (m_jsonLogsEnabled) + initMessageContext(location); return { - m_attributes ? m_attributes->clone() : nullptr, + m_attributes, *m_sink, severities::kError}; } @@ -525,14 +597,25 @@ public: Stream fatal(std::source_location location = std::source_location::current()) const { - if (m_structuredJournalImpl) - m_structuredJournalImpl->initMessageContext(location); + if (m_jsonLogsEnabled) + initMessageContext(location); return { - m_attributes ? m_attributes->clone() : nullptr, + m_attributes, *m_sink, severities::kFatal}; } /** @} */ + + static void + addGlobalAttributes(JsonLogAttributes globalLogAttributes) + { + std::lock_guard lock(globalLogAttributesMutex_); + if (!globalLogAttributes_) + { + globalLogAttributes_ = JsonLogAttributes{}; + } + globalLogAttributes_ = JsonLogAttributes::combine(globalLogAttributes_->contextValues(), globalLogAttributes.contextValues()); + } }; #ifndef __INTELLISENSE__ @@ -548,7 +631,7 @@ static_assert(std::is_nothrow_destructible::value == true, ""); template Journal::ScopedStream::ScopedStream( - std::unique_ptr attributes, + std::optional attributes, Stream const& stream, T const& t) : ScopedStream(std::move(attributes), stream.sink(), stream.level()) @@ -570,7 +653,7 @@ template Journal::ScopedStream Journal::Stream::operator<<(T const& t) const { - return {m_attributes ? m_attributes->clone() : nullptr, *this, t}; + return {m_attributes, *this, t}; } namespace detail { @@ -642,4 +725,127 @@ using logwstream = basic_logstream; } // namespace beast + +namespace ripple::log { + +namespace detail { +template +void setJsonValue( + rapidjson::Value& object, + rapidjson::MemoryPoolAllocator<>& allocator, + char const* name, + T&& value, + std::ostream* outStream) +{ + using ValueType = std::decay_t; + rapidjson::Value jsonValue; + if constexpr (std::constructible_from&>) + { + jsonValue = rapidjson::Value{value, allocator}; + if (outStream) + { + (*outStream) << value; + } + } + else if constexpr (std::constructible_from) + { + jsonValue = rapidjson::Value{value}; + if (outStream) + { + (*outStream) << value; + } + } + else if constexpr (std::same_as) + { + jsonValue = rapidjson::Value{value.c_str(), allocator}; + if (outStream) + { + (*outStream) << value; + } + } + else + { + std::ostringstream oss; + oss << value; + + jsonValue = rapidjson::Value{oss.str().c_str(), allocator}; + + if (outStream) + { + (*outStream) << oss.str(); + } + } + + object.AddMember( + rapidjson::StringRef(name), + std::move(jsonValue), + allocator + ); +} +} + +template +std::ostream& +operator<<(std::ostream& os, LogParameter const& param) +{ + if (!beast::Journal::m_jsonLogsEnabled) + return os; + detail::setJsonValue( + beast::Journal::currentJsonLogContext_.messageParams, + beast::Journal::currentJsonLogContext_.allocator, + param.name_, param.value_, &os); + return os; +} + +template +std::ostream& +operator<<(std::ostream& os, LogField const& param) +{ + if (!beast::Journal::m_jsonLogsEnabled) + return os; + detail::setJsonValue( + beast::Journal::currentJsonLogContext_.messageParams, + beast::Journal::currentJsonLogContext_.allocator, + param.name_, param.value_, nullptr); + return os; +} + +template +LogParameter +param(char const* name, T&& value) +{ + return LogParameter{name, std::forward(value)}; +} + +template +LogField +field(char const* name, T&& value) +{ + return LogField{name, std::forward(value)}; +} + +template +[[nodiscard]] beast::Journal::JsonLogAttributes +attributes(Pair&&... pairs) +{ + beast::Journal::JsonLogAttributes result; + + (detail::setJsonValue( + result.contextValues(), + result.allocator(), + pairs.first, + pairs.second, nullptr), ...); + + return result; +} + +template +[[nodiscard]] std::pair> +attr(char const* name, T&& value) +{ + return std::make_pair(name, std::forward(value)); +} + +} + #endif diff --git a/include/xrpl/resource/detail/Logic.h b/include/xrpl/resource/detail/Logic.h index b8288294fe..8bfa2c2ac8 100644 --- a/include/xrpl/resource/detail/Logic.h +++ b/include/xrpl/resource/detail/Logic.h @@ -32,7 +32,6 @@ #include #include #include -#include #include diff --git a/include/xrpl/server/detail/BasePeer.h b/include/xrpl/server/detail/BasePeer.h index bdae504619..0a578fc65e 100644 --- a/include/xrpl/server/detail/BasePeer.h +++ b/include/xrpl/server/detail/BasePeer.h @@ -25,7 +25,6 @@ #include #include #include -#include #include @@ -86,11 +85,11 @@ BasePeer::BasePeer( , remote_address_(remote_address) , j_(journal, log::attributes( - {{"PeerID", + log::attr("PeerID", [] { static std::atomic id{0}; return "##" + std::to_string(++id) + " "; - }()}})) + }()))) , work_(boost::asio::make_work_guard(executor)) , strand_(boost::asio::make_strand(executor)) { diff --git a/include/xrpl/telemetry/JsonLogs.h b/include/xrpl/telemetry/JsonLogs.h deleted file mode 100644 index 1ed3e16a8d..0000000000 --- a/include/xrpl/telemetry/JsonLogs.h +++ /dev/null @@ -1,220 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2025 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#ifndef RIPPLE_LOGGING_STRUCTUREDJOURNAL_H_INCLUDED -#define RIPPLE_LOGGING_STRUCTUREDJOURNAL_H_INCLUDED - -#include -#include - -#include -#include -#include -#include - -namespace ripple::log { - -template -class LogParameter -{ -public: - template - LogParameter(char const* name, TArg&& value) - : name_(name), value_(std::forward(value)) - { - } - -private: - char const* name_; - T value_; - - template - friend std::ostream& - operator<<(std::ostream& os, LogParameter const&); -}; - -template -class LogField -{ -public: - template - LogField(char const* name, TArg&& value) - : name_(name), value_(std::forward(value)) - { - } - -private: - char const* name_; - T value_; - - template - friend std::ostream& - operator<<(std::ostream& os, LogField const&); -}; - -class JsonLogAttributes : public beast::Journal::StructuredLogAttributes -{ -public: - using AttributeFields = std::unordered_map; - using Pair = AttributeFields::value_type; - - explicit JsonLogAttributes(AttributeFields contextValues = {}); - - void - setModuleName(std::string const& name) override; - - [[nodiscard]] std::unique_ptr - clone() const override; - - void - combine(std::unique_ptr const& context) override; - - void - combine(std::unique_ptr&& context) override; - - AttributeFields& - contextValues() - { - return contextValues_; - } - -private: - AttributeFields contextValues_; -}; - -class JsonStructuredJournal : public beast::Journal::StructuredJournalImpl -{ -private: - struct Logger - { - std::source_location location = {}; - Json::Value messageParams; - - Logger() = default; - Logger( - JsonStructuredJournal const* journal, - std::source_location location); - - void - write( - beast::Journal::Sink* sink, - beast::severities::Severity level, - std::string const& text, - beast::Journal::StructuredLogAttributes* context) const; - }; - - [[nodiscard]] Logger - logger(std::source_location location) const; - - static thread_local Logger currentLogger_; - - template - friend std::ostream& - operator<<(std::ostream& os, LogParameter const&); - - template - friend std::ostream& - operator<<(std::ostream& os, LogField const&); - -public: - void - initMessageContext(std::source_location location) override; - - void - flush( - beast::Journal::Sink* sink, - beast::severities::Severity level, - std::string const& text, - beast::Journal::StructuredLogAttributes* context) override; -}; - -template -std::ostream& -operator<<(std::ostream& os, LogParameter const& param) -{ - using ValueType = std::decay_t; - // TODO: Update the Json library to support 64-bit integer values. - if constexpr ( - std::constructible_from && - (!std::is_integral_v || - sizeof(ValueType) <= sizeof(Json::Int))) - { - JsonStructuredJournal::currentLogger_.messageParams[param.name_] = - Json::Value{param.value_}; - return os << param.value_; - } - else - { - std::ostringstream oss; - oss << param.value_; - - JsonStructuredJournal::currentLogger_.messageParams[param.name_] = - oss.str(); - return os << oss.str(); - } -} - -template -std::ostream& -operator<<(std::ostream& os, LogField const& param) -{ - using ValueType = std::decay_t; - // TODO: Update the Json library to support 64-bit integer values. - if constexpr ( - std::constructible_from && - (!std::is_integral_v || - sizeof(ValueType) <= sizeof(Json::Int))) - { - JsonStructuredJournal::currentLogger_.messageParams[param.name_] = - Json::Value{param.value_}; - } - else - { - std::ostringstream oss; - oss << param.value_; - - JsonStructuredJournal::currentLogger_.messageParams[param.name_] = - oss.str(); - } - return os; -} - -template -LogParameter -param(char const* name, T&& value) -{ - return LogParameter{name, std::forward(value)}; -} - -template -LogField -field(char const* name, T&& value) -{ - return LogField{name, std::forward(value)}; -} - -[[nodiscard]] inline std::unique_ptr -attributes(std::initializer_list const& fields) -{ - return std::make_unique(fields); -} - -} // namespace ripple::log - -#endif diff --git a/src/libxrpl/basics/Log.cpp b/src/libxrpl/basics/Log.cpp index e7a929af89..0ddd56f17d 100644 --- a/src/libxrpl/basics/Log.cpp +++ b/src/libxrpl/basics/Log.cpp @@ -38,9 +38,6 @@ namespace ripple { -std::unique_ptr - Logs::globalLogAttributes_; - Logs::Sink::Sink( std::string const& partition, beast::severities::Severity thresh, @@ -162,16 +159,9 @@ Logs::operator[](std::string const& name) beast::Journal Logs::journal( std::string const& name, - std::unique_ptr attributes) + std::optional attributes) { - if (globalLogAttributes_) - { - if (attributes) - attributes->combine(globalLogAttributes_); - else - attributes = globalLogAttributes_->clone(); - } - return beast::Journal(get(name), name, std::move(attributes)); + return beast::Journal{get(name), name, std::move(attributes)}; } beast::severities::Severity diff --git a/src/libxrpl/beast/utility/beast_Journal.cpp b/src/libxrpl/beast/utility/beast_Journal.cpp index d43c05f021..bcb9c3048e 100644 --- a/src/libxrpl/beast/utility/beast_Journal.cpp +++ b/src/libxrpl/beast/utility/beast_Journal.cpp @@ -19,13 +19,22 @@ #include +#include +#include +#include + +#include +#include #include #include #include namespace beast { -std::unique_ptr Journal::m_structuredJournalImpl; +std::optional Journal::globalLogAttributes_; +std::mutex Journal::globalLogAttributesMutex_; +bool Journal::m_jsonLogsEnabled = false; +thread_local Journal::JsonLogContext Journal::currentJsonLogContext_{}; //------------------------------------------------------------------------------ @@ -113,6 +122,184 @@ severities::to_string(Severity severity) } return ""; } + +Journal::JsonLogAttributes::JsonLogAttributes() +{ + contextValues_.SetObject(); +} + +Journal::JsonLogAttributes::JsonLogAttributes(JsonLogAttributes const& other) +{ + contextValues_.SetObject(); + contextValues_.CopyFrom(other.contextValues_, allocator_); +} + +Journal::JsonLogAttributes& +Journal::JsonLogAttributes::operator=(JsonLogAttributes const& other) +{ + if (&other == this) + { + return *this; + } + contextValues_.CopyFrom(other.contextValues_, allocator_); + return *this; +} + +void +Journal::JsonLogAttributes::setModuleName(std::string const& name) +{ + contextValues_.AddMember( + rapidjson::StringRef("Module"), + rapidjson::Value{name.c_str(), allocator_}, + allocator_ + ); +} + +Journal::JsonLogAttributes +Journal::JsonLogAttributes::combine( + AttributeFields const& a, + AttributeFields const& b) +{ + JsonLogAttributes result; + + result.contextValues_.CopyFrom(a, result.allocator_); + + for (auto& member : b.GetObject()) + { + auto val = rapidjson::Value{ member.value, result.allocator_ }; + if (result.contextValues_.HasMember(member.name)) + { + result.contextValues_[member.name] = std::move(val); + } + else + { + result.contextValues_.AddMember( + rapidjson::Value{member.name, result.allocator_}, + std::move(val), + result.allocator_); + } + } + + return result; +} + +void +Journal::initMessageContext(std::source_location location) +{ + currentJsonLogContext_.reset(location); +} + +std::string +Journal::formatLog( + std::string const& message, + severities::Severity severity, + std::optional const& attributes) +{ + if (!m_jsonLogsEnabled) + { + return message; + } + + rapidjson::Document doc{¤tJsonLogContext_.allocator}; + rapidjson::Value logContext; + logContext.SetObject(); + + if (globalLogAttributes_) + { + for (auto const& [key, value] : globalLogAttributes_->contextValues().GetObject()) + { + rapidjson::Value jsonValue; + jsonValue.CopyFrom(value, currentJsonLogContext_.allocator); + + logContext.AddMember( + rapidjson::Value{key, currentJsonLogContext_.allocator}, + std::move(jsonValue), + currentJsonLogContext_.allocator); + } + } + + if (attributes.has_value()) + { + for (auto const& [key, value] : attributes->contextValues().GetObject()) + { + rapidjson::Value jsonValue; + jsonValue.CopyFrom(value, currentJsonLogContext_.allocator); + + logContext.AddMember( + rapidjson::Value{key, currentJsonLogContext_.allocator}, + std::move(jsonValue), + currentJsonLogContext_.allocator); + } + } + logContext.AddMember( + rapidjson::StringRef("Function"), + rapidjson::StringRef(currentJsonLogContext_.location.function_name()), + currentJsonLogContext_.allocator); + + logContext.AddMember( + rapidjson::StringRef("File"), + rapidjson::StringRef(currentJsonLogContext_.location.file_name()), + currentJsonLogContext_.allocator); + + logContext.AddMember( + rapidjson::StringRef("Line"), + currentJsonLogContext_.location.line(), + currentJsonLogContext_.allocator); + std::stringstream threadIdStream; + threadIdStream << std::this_thread::get_id(); + auto threadIdStr = threadIdStream.str(); + logContext.AddMember( + rapidjson::StringRef("ThreadId"), + rapidjson::StringRef(threadIdStr.c_str()), + currentJsonLogContext_.allocator); + logContext.AddMember( + rapidjson::StringRef("Params"), + std::move(currentJsonLogContext_.messageParams), + currentJsonLogContext_.allocator); + currentJsonLogContext_.messageParams = rapidjson::Value{}; + currentJsonLogContext_.messageParams.SetObject(); + auto severityStr = to_string(severity); + logContext.AddMember( + rapidjson::StringRef("Level"), + rapidjson::StringRef(severityStr.c_str()), + currentJsonLogContext_.allocator); + logContext.AddMember( + rapidjson::StringRef("Message"), + rapidjson::StringRef(message.c_str()), + currentJsonLogContext_.allocator); + logContext.AddMember( + rapidjson::StringRef("Time"), + std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(), + currentJsonLogContext_.allocator); + + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + + logContext.Accept(writer); + + return {buffer.GetString()}; +} + +void +Journal::enableStructuredJournal() +{ + m_jsonLogsEnabled = true; +} + +void +Journal::disableStructuredJournal() +{ + m_jsonLogsEnabled = false; +} + +bool +Journal::isStructuredJournalEnabled() +{ + return m_jsonLogsEnabled; +} + Journal::Sink::Sink(Severity thresh, bool console) : thresh_(thresh), m_console(console) { @@ -153,7 +340,7 @@ Journal::Sink::threshold(Severity thresh) //------------------------------------------------------------------------------ Journal::ScopedStream::ScopedStream( - std::unique_ptr attributes, + std::optional attributes, Sink& sink, Severity level) : m_attributes(std::move(attributes)), m_sink(sink), m_level(level) @@ -163,7 +350,7 @@ Journal::ScopedStream::ScopedStream( } Journal::ScopedStream::ScopedStream( - std::unique_ptr attributes, + std::optional attributes, Stream const& stream, std::ostream& manip(std::ostream&)) : ScopedStream(std::move(attributes), stream.sink(), stream.level()) @@ -177,21 +364,9 @@ Journal::ScopedStream::~ScopedStream() if (!s.empty()) { if (s == "\n") - { - if (m_structuredJournalImpl) - m_structuredJournalImpl->flush( - &m_sink, m_level, "", m_attributes.get()); - else - m_sink.write(m_level, ""); - } + m_sink.write(m_level, formatLog("", m_level, m_attributes)); else - { - if (m_structuredJournalImpl) - m_structuredJournalImpl->flush( - &m_sink, m_level, s, m_attributes.get()); - else - m_sink.write(m_level, s); - } + m_sink.write(m_level, formatLog(s, m_level, m_attributes)); } } @@ -206,7 +381,7 @@ Journal::ScopedStream::operator<<(std::ostream& manip(std::ostream&)) const Journal::ScopedStream Journal::Stream::operator<<(std::ostream& manip(std::ostream&)) const { - return {m_attributes ? m_attributes->clone() : nullptr, *this, manip}; + return {m_attributes, *this, manip}; } } // namespace beast diff --git a/src/libxrpl/telemetry/JsonLogs.cpp b/src/libxrpl/telemetry/JsonLogs.cpp deleted file mode 100644 index 1006c8becc..0000000000 --- a/src/libxrpl/telemetry/JsonLogs.cpp +++ /dev/null @@ -1,129 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2025 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include -#include - -#include -#include - -namespace ripple::log { - -thread_local JsonStructuredJournal::Logger - JsonStructuredJournal::currentLogger_{}; - -JsonLogAttributes::JsonLogAttributes(AttributeFields contextValues) - : contextValues_(std::move(contextValues)) -{ -} - -void -JsonLogAttributes::setModuleName(std::string const& name) -{ - contextValues()["Module"] = name; -} - -std::unique_ptr -JsonLogAttributes::clone() const -{ - return std::make_unique(*this); -} - -void -JsonLogAttributes::combine( - std::unique_ptr const& context) -{ - auto structuredContext = - static_cast(context.get()); - contextValues_.merge(AttributeFields{structuredContext->contextValues_}); -} - -void -JsonLogAttributes::combine(std::unique_ptr&& context) -{ - auto structuredContext = static_cast(context.get()); - - if (contextValues_.empty()) - contextValues_ = std::move(structuredContext->contextValues_); - else - contextValues_.merge(structuredContext->contextValues_); -} - -JsonStructuredJournal::Logger::Logger( - JsonStructuredJournal const* journal, - std::source_location location) - : location(location) -{ -} - -void -JsonStructuredJournal::Logger::write( - beast::Journal::Sink* sink, - beast::severities::Severity level, - std::string const& text, - beast::Journal::StructuredLogAttributes* context) const -{ - Json::Value globalContext; - if (context) - { - auto jsonContext = static_cast(context); - for (auto const& [key, value] : jsonContext->contextValues()) - globalContext[key] = value; - } - globalContext["Function"] = location.function_name(); - globalContext["File"] = location.file_name(); - globalContext["Line"] = location.line(); - std::stringstream threadIdStream; - threadIdStream << std::this_thread::get_id(); - globalContext["ThreadId"] = threadIdStream.str(); - globalContext["Params"] = messageParams; - globalContext["Level"] = to_string(level); - globalContext["Message"] = text; - globalContext["Time"] = - to_string(std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count()); - - sink->write(level, to_string(globalContext)); -} - -JsonStructuredJournal::Logger -JsonStructuredJournal::logger(std::source_location location) const -{ - return Logger{this, location}; -} - -void -JsonStructuredJournal::initMessageContext(std::source_location location) -{ - currentLogger_ = logger(location); -} - -void -JsonStructuredJournal::flush( - beast::Journal::Sink* sink, - beast::severities::Severity level, - std::string const& text, - beast::Journal::StructuredLogAttributes* context) -{ - currentLogger_.write(sink, level, text, context); -} - -} // namespace ripple::log diff --git a/src/test/core/Coroutine_test.cpp b/src/test/core/Coroutine_test.cpp index 8458da647d..bbdd2e9b00 100644 --- a/src/test/core/Coroutine_test.cpp +++ b/src/test/core/Coroutine_test.cpp @@ -175,12 +175,72 @@ public: BEAST_EXPECT(*lv == -1); } + void + test_yield_and_stop() + { + + using namespace std::chrono_literals; + using namespace jtx; + + testcase("yield and stop"); + + Env env(*this, envconfig([](std::unique_ptr cfg) { + cfg->FORCE_MULTI_THREAD = true; + return cfg; + })); + + std::shared_ptr c; + std::mutex mutexStop; + std::mutex mutexYield; + std::condition_variable cond; + std::condition_variable condYield; + bool yielded = false; + bool stopped = false; + + env.app().getJobQueue().postCoro( + jtCLIENT, "Coroutine-Test", [&](auto const& cr) { + c = cr; + { + std::unique_lock lock(mutexYield); + yielded = true; + condYield.notify_all(); + } + c->yield(); + // Just to keep this job alive + std::this_thread::sleep_for(5ms); + }); + std::thread th{[&]() { + std::unique_lock lock(mutexStop); + cond.wait(lock, [&]() { return stopped; }); + // Delay a bit to wait for stop() to be called + std::this_thread::sleep_for(1ms); + c->post(); + }}; + + // Delay a bit to wait for yield() to be called + std::this_thread::sleep_for(1ms); + std::unique_lock lockYield(mutexYield); + condYield.wait(lockYield, [&]() { return yielded; }); + { + std::unique_lock lock(mutexStop); + stopped = true; + cond.notify_all(); + } + env.app().getJobQueue().stop(); + try + { + th.join(); + } catch (const std::exception& e) {} + pass(); + } + void run() override { - correct_order(); - incorrect_order(); - thread_specific_storage(); + // correct_order(); + // incorrect_order(); + // thread_specific_storage(); + test_yield_and_stop(); } }; diff --git a/src/test/csf/Peer.h b/src/test/csf/Peer.h index 783da4f249..6785846b0f 100644 --- a/src/test/csf/Peer.h +++ b/src/test/csf/Peer.h @@ -33,7 +33,6 @@ #include #include -#include #include #include @@ -284,7 +283,7 @@ struct Peer TrustGraph& tg, CollectorRefs& c, beast::Journal jIn) - : j(jIn, log::attributes({{"Peer", "Peer " + to_string(i)}})) + : j(jIn, log::attributes(log::attr("Peer", "Peer " + to_string(i)))) , consensus(s.clock(), *this, j) , id{i} , key{id, 0} diff --git a/src/tests/libxrpl/CMakeLists.txt b/src/tests/libxrpl/CMakeLists.txt index 73520a5128..72f0e532ce 100644 --- a/src/tests/libxrpl/CMakeLists.txt +++ b/src/tests/libxrpl/CMakeLists.txt @@ -2,10 +2,11 @@ include(xrpl_add_test) # Test requirements. find_package(doctest REQUIRED) +find_package(RapidJSON REQUIRED) # Common library dependencies for the rest of the tests. add_library(xrpl.imports.test INTERFACE) -target_link_libraries(xrpl.imports.test INTERFACE doctest::doctest xrpl.libxrpl) +target_link_libraries(xrpl.imports.test INTERFACE doctest::doctest rapidjson xrpl.libxrpl) # One test for each module. xrpl_add_test(basics) diff --git a/src/tests/libxrpl/basics/log.cpp b/src/tests/libxrpl/basics/log.cpp index 047bf33732..5d6e7ca2cc 100644 --- a/src/tests/libxrpl/basics/log.cpp +++ b/src/tests/libxrpl/basics/log.cpp @@ -18,9 +18,8 @@ //============================================================================== #include -#include -#include +#include #include using namespace ripple; @@ -108,8 +107,7 @@ TEST_CASE("Test format output") TEST_CASE("Test format output when structured logs are enabled") { - auto structuredJournal = std::make_unique(); - beast::Journal::enableStructuredJournal(std::move(structuredJournal)); + beast::Journal::enableStructuredJournal(); std::string output; Logs::format(output, "Message", beast::severities::kDebug, "Test"); @@ -121,8 +119,6 @@ TEST_CASE("Test format output when structured logs are enabled") TEST_CASE("Enable json logs") { - auto structuredJournal = std::make_unique(); - std::stringstream logStream; MockLogs logs{logStream, beast::severities::kAll}; @@ -133,78 +129,429 @@ TEST_CASE("Enable json logs") logStream.str(""); - beast::Journal::enableStructuredJournal(std::move(structuredJournal)); + beast::Journal::enableStructuredJournal(); logs.journal("Test").debug() << "\n"; - Json::Reader reader; - Json::Value jsonLog; - bool result = reader.parse(logStream.str(), jsonLog); + rapidjson::Document doc; + doc.Parse(logStream.str().c_str()); - CHECK(result); + CHECK(doc.GetParseError() == rapidjson::ParseErrorCode::kParseErrorNone); - CHECK(jsonLog.isObject()); - CHECK(jsonLog.isMember("Message")); - CHECK(jsonLog["Message"].isString()); - CHECK(jsonLog["Message"].asString() == ""); + CHECK(doc.IsObject()); + CHECK(doc.HasMember("Message")); + CHECK(doc["Message"].IsString()); + CHECK(doc["Message"].GetString() == std::string{""}); beast::Journal::disableStructuredJournal(); } TEST_CASE("Global attributes") { - auto structuredJournal = std::make_unique(); - std::stringstream logStream; MockLogs logs{logStream, beast::severities::kAll}; - beast::Journal::enableStructuredJournal(std::move(structuredJournal)); - MockLogs::setGlobalAttributes(log::attributes({{"Field1", "Value1"}})); + beast::Journal::enableStructuredJournal(); + beast::Journal::addGlobalAttributes(log::attributes(log::attr("Field1", "Value1"))); logs.journal("Test").debug() << "Test"; - Json::Reader reader; - Json::Value jsonLog; - bool result = reader.parse(logStream.str(), jsonLog); + rapidjson::Document jsonLog; + jsonLog.Parse(logStream.str().c_str()); - CHECK(result); + CHECK(jsonLog.GetParseError() == rapidjson::ParseErrorCode::kParseErrorNone); - CHECK(jsonLog.isObject()); - CHECK(jsonLog.isMember("Field1")); - CHECK(jsonLog["Field1"].isString()); - CHECK(jsonLog["Field1"].asString() == "Value1"); + CHECK(jsonLog.IsObject()); + CHECK(jsonLog.HasMember("Field1")); + CHECK(jsonLog["Field1"].IsString()); + CHECK(jsonLog["Field1"].GetString() == std::string{"Value1"}); beast::Journal::disableStructuredJournal(); } TEST_CASE("Global attributes inheritable") { - auto structuredJournal = std::make_unique(); - std::stringstream logStream; MockLogs logs{logStream, beast::severities::kAll}; - beast::Journal::enableStructuredJournal(std::move(structuredJournal)); - MockLogs::setGlobalAttributes(log::attributes({{"Field1", "Value1"}})); + beast::Journal::enableStructuredJournal(); + beast::Journal::addGlobalAttributes(log::attributes(log::attr("Field1", "Value1"))); logs.journal( "Test", - log::attributes({{"Field1", "Value3"}, {"Field2", "Value2"}})) + log::attributes(log::attr("Field1", "Value3"), log::attr("Field2", "Value2"))) .debug() << "Test"; - Json::Reader reader; - Json::Value jsonLog; - bool result = reader.parse(logStream.str(), jsonLog); + rapidjson::Document jsonLog; + jsonLog.Parse(logStream.str().c_str()); - CHECK(result); + CHECK(jsonLog.GetParseError() == rapidjson::ParseErrorCode::kParseErrorNone); - CHECK(jsonLog.isObject()); - CHECK(jsonLog.isMember("Field1")); - CHECK(jsonLog["Field1"].isString()); + CHECK(jsonLog.IsObject()); + CHECK(jsonLog.HasMember("Field1")); + CHECK(jsonLog["Field1"].IsString()); // Field1 should be overwritten to Value3 - CHECK(jsonLog["Field1"].asString() == "Value3"); - CHECK(jsonLog["Field2"].isString()); - CHECK(jsonLog["Field2"].asString() == "Value2"); + CHECK(jsonLog["Field1"].GetString() == std::string{"Value3"}); + CHECK(jsonLog["Field2"].IsString()); + CHECK(jsonLog["Field2"].GetString() == std::string{"Value2"}); beast::Journal::disableStructuredJournal(); +} + +/** + * @brief sink for writing all log messages to a stringstream + */ +class MockSink : public beast::Journal::Sink +{ + std::stringstream& strm_; + +public: + MockSink(beast::severities::Severity threshold, std::stringstream& strm) + : beast::Journal::Sink(threshold, false), strm_(strm) + { + } + + void + write(beast::severities::Severity level, std::string const& text) override + { + strm_ << text; + } + + void + writeAlways(beast::severities::Severity level, std::string const& text) + override + { + strm_ << text; + } +}; + +class JsonLogStreamFixture +{ +public: + JsonLogStreamFixture() + : sink_(beast::severities::kAll, logStream_), j_(sink_) + { + beast::Journal::enableStructuredJournal(); + } + + ~JsonLogStreamFixture() + { + beast::Journal::disableStructuredJournal(); + } + + std::stringstream& + stream() + { + return logStream_; + } + + beast::Journal& + journal() + { + return j_; + } + +private: + MockSink sink_; + std::stringstream logStream_; + beast::Journal j_; +}; + +TEST_CASE_FIXTURE(JsonLogStreamFixture, "TestJsonLogFields") +{ + journal().debug() << std::boolalpha << true << std::noboolalpha << " Test " + << std::boolalpha << false; + + rapidjson::Document logValue; + logValue.Parse(stream().str().c_str()); + + CHECK(logValue.GetParseError() == rapidjson::ParseErrorCode::kParseErrorNone); + + CHECK(logValue.IsObject()); + CHECK(logValue.HasMember("Function")); + CHECK(logValue.HasMember("File")); + CHECK(logValue.HasMember("Line")); + CHECK(logValue.HasMember("ThreadId")); + CHECK(logValue.HasMember("Params")); + CHECK(logValue.HasMember("Level")); + CHECK(logValue.HasMember("Message")); + CHECK(logValue.HasMember("Time")); + + CHECK(logValue["Function"].IsString()); + CHECK(logValue["File"].IsString()); + CHECK(logValue["Line"].IsNumber()); + CHECK(logValue["Params"].IsObject()); + CHECK(logValue["Message"].IsString()); + CHECK(logValue["Message"].GetString() == std::string{"true Test false"}); +} + +TEST_CASE_FIXTURE(JsonLogStreamFixture, "TestJsonLogLevels") +{ + { + stream().str(""); + journal().trace() << "Test"; + + rapidjson::Document logValue; + logValue.Parse(stream().str().c_str()); + + CHECK(logValue.GetParseError() == rapidjson::ParseErrorCode::kParseErrorNone); + + CHECK( + logValue["Level"].GetString() == + beast::severities::to_string(beast::severities::kTrace)); + } + + { + stream().str(""); + journal().debug() << "Test"; + + rapidjson::Document logValue; + logValue.Parse(stream().str().c_str()); + + CHECK(logValue.GetParseError() == rapidjson::ParseErrorCode::kParseErrorNone); + + + CHECK( + logValue["Level"].GetString() == + beast::severities::to_string(beast::severities::kDebug)); + } + + { + stream().str(""); + journal().info() << "Test"; + + rapidjson::Document logValue; + logValue.Parse(stream().str().c_str()); + + CHECK(logValue.GetParseError() == rapidjson::ParseErrorCode::kParseErrorNone); + + + CHECK( + logValue["Level"].GetString() == + beast::severities::to_string(beast::severities::kInfo)); + } + + { + stream().str(""); + journal().warn() << "Test"; + + rapidjson::Document logValue; + logValue.Parse(stream().str().c_str()); + + CHECK(logValue.GetParseError() == rapidjson::ParseErrorCode::kParseErrorNone); + + + CHECK( + logValue["Level"].GetString() == + beast::severities::to_string(beast::severities::kWarning)); + } + + { + stream().str(""); + journal().error() << "Test"; + + rapidjson::Document logValue; + logValue.Parse(stream().str().c_str()); + + CHECK(logValue.GetParseError() == rapidjson::ParseErrorCode::kParseErrorNone); + + + CHECK( + logValue["Level"].GetString() == + beast::severities::to_string(beast::severities::kError)); + } + + { + stream().str(""); + journal().fatal() << "Test"; + + rapidjson::Document logValue; + logValue.Parse(stream().str().c_str()); + + CHECK(logValue.GetParseError() == rapidjson::ParseErrorCode::kParseErrorNone); + + CHECK( + logValue["Level"].GetString() == + beast::severities::to_string(beast::severities::kFatal)); + } +} + +TEST_CASE_FIXTURE(JsonLogStreamFixture, "TestJsonLogStream") +{ + journal().stream(beast::severities::kError) << "Test"; + + rapidjson::Document logValue; + logValue.Parse(stream().str().c_str()); + + CHECK(logValue.GetParseError() == rapidjson::ParseErrorCode::kParseErrorNone); + + CHECK( + logValue["Level"].GetString() == + beast::severities::to_string(beast::severities::kError)); +} + +TEST_CASE_FIXTURE(JsonLogStreamFixture, "TestJsonLogParams") +{ + journal().debug() << "Test: " << log::param("Field1", 1) << ", " + << log::param( + "Field2", + std::numeric_limits::max()); + + rapidjson::Document logValue; + logValue.Parse(stream().str().c_str()); + + CHECK(logValue.GetParseError() == rapidjson::ParseErrorCode::kParseErrorNone); + + CHECK(logValue["Params"].IsObject()); + CHECK(logValue["Params"]["Field1"].IsNumber()); + CHECK(logValue["Params"]["Field1"].GetInt() == 1); + // UInt64 doesn't fit in Json::Value so it should be converted to a string + // NOTE: We should expect it to be an int64 after we make the json library + // support in64 and uint64 + CHECK(logValue["Params"]["Field2"].IsNumber()); + CHECK(logValue["Params"]["Field2"].GetUint64() == std::numeric_limits::max()); + CHECK(logValue["Message"].IsString()); + CHECK(logValue["Message"].GetString() == std::string{"Test: 1, 18446744073709551615"}); +} + +TEST_CASE_FIXTURE(JsonLogStreamFixture, "TestJsonLogFields") +{ + journal().debug() << "Test" << log::field("Field1", 1) + << log::field( + "Field2", + std::numeric_limits::max()); + + rapidjson::Document logValue; + logValue.Parse(stream().str().c_str()); + + CHECK(logValue.GetParseError() == rapidjson::ParseErrorCode::kParseErrorNone); + + + CHECK(logValue["Params"].IsObject()); + CHECK(logValue["Params"]["Field1"].IsNumber()); + CHECK(logValue["Params"]["Field1"].GetInt() == 1); + // UInt64 doesn't fit in Json::Value so it should be converted to a string + // NOTE: We should expect it to be an int64 after we make the json library + // support in64 and uint64 + CHECK(logValue["Params"]["Field2"].IsNumber()); + CHECK(logValue["Params"]["Field2"].GetUint64() == std::numeric_limits::max()); + CHECK(logValue["Message"].IsString()); + CHECK(logValue["Message"].GetString() == std::string{"Test"}); +} + +TEST_CASE_FIXTURE(JsonLogStreamFixture, "TestJournalAttributes") +{ + beast::Journal j{ + journal(), log::attributes(log::attr("Field1", "Value1"), log::attr("Field2", 2))}; + + j.debug() << "Test"; + + rapidjson::Document logValue; + logValue.Parse(stream().str().c_str()); + + CHECK(logValue.GetParseError() == rapidjson::ParseErrorCode::kParseErrorNone); + + CHECK(logValue["Field1"].IsString()); + CHECK(logValue["Field1"].GetString() == std::string{"Value1"}); + CHECK(logValue["Field2"].IsNumber()); + CHECK(logValue["Field2"].GetInt() == 2); +} + +TEST_CASE_FIXTURE(JsonLogStreamFixture, "TestJournalAttributesInheritable") +{ + beast::Journal j{ + journal(), log::attributes(log::attr("Field1", "Value1"), log::attr("Field2", 2))}; + beast::Journal j2{ + j, log::attributes(log::attr("Field3", "Value3"), log::attr("Field2", 0))}; + + j2.debug() << "Test"; + + rapidjson::Document logValue; + logValue.Parse(stream().str().c_str()); + + CHECK(logValue.GetParseError() == rapidjson::ParseErrorCode::kParseErrorNone); + + CHECK(logValue["Field1"].IsString()); + CHECK(logValue["Field1"].GetString() == std::string{"Value1"}); + CHECK(logValue["Field3"].IsString()); + CHECK(logValue["Field3"].GetString() == std::string{"Value3"}); + // Field2 should be overwritten to 0 + CHECK(logValue["Field2"].IsNumber()); + CHECK(logValue["Field2"].GetInt() == 0); +} + +TEST_CASE_FIXTURE( + JsonLogStreamFixture, + "TestJournalAttributesInheritableAfterMoving") +{ + beast::Journal j{ + journal(), + log::attributes(log::attr("Field1", "Value1"), log::attr("Field2", 2))}; + beast::Journal j2{ + j, log::attributes(log::attr("Field3", "Value3"), log::attr("Field2", 0))}; + + j2.debug() << "Test"; + + rapidjson::Document logValue; + logValue.Parse(stream().str().c_str()); + + CHECK(logValue.GetParseError() == rapidjson::ParseErrorCode::kParseErrorNone); + + CHECK(logValue["Field1"].IsString()); + CHECK(logValue["Field1"].GetString() == std::string{"Value1"}); + CHECK(logValue["Field3"].IsString()); + CHECK(logValue["Field3"].GetString() == std::string{"Value3"}); + // Field2 should be overwritten to 0 + CHECK(logValue["Field2"].IsNumber()); + CHECK(logValue["Field2"].GetInt() == 0); +} + +TEST_CASE_FIXTURE( + JsonLogStreamFixture, + "TestJournalAttributesInheritableAfterCopyAssignment") +{ + beast::Journal j{ + std::move(journal()), + log::attributes(log::attr("Field1", "Value1"), log::attr("Field2", 2))}; + + beast::Journal j2{beast::Journal::getNullSink()}; + + j2 = j; + + j2.debug() << "Test"; + + rapidjson::Document logValue; + logValue.Parse(stream().str().c_str()); + + CHECK(logValue.GetParseError() == rapidjson::ParseErrorCode::kParseErrorNone); + + CHECK(logValue["Field1"].IsString()); + CHECK(logValue["Field1"].GetString() == std::string{"Value1"}); + CHECK(logValue["Field2"].IsNumber()); + CHECK(logValue["Field2"].GetInt() == 2); +} + +TEST_CASE_FIXTURE( + JsonLogStreamFixture, + "TestJournalAttributesInheritableAfterMoveAssignment") +{ + beast::Journal j{ + journal(), + log::attributes(log::attr("Field1", "Value1"), log::attr("Field2", 2))}; + + beast::Journal j2{beast::Journal::getNullSink()}; + + j2 = std::move(j); + + j2.debug() << "Test"; + + rapidjson::Document logValue; + logValue.Parse(stream().str().c_str()); + + CHECK(logValue.GetParseError() == rapidjson::ParseErrorCode::kParseErrorNone); + + CHECK(logValue["Field1"].IsString()); + CHECK(logValue["Field1"].GetString() == std::string{"Value1"}); + CHECK(logValue["Field2"].IsNumber()); + CHECK(logValue["Field2"].GetInt() == 2); } \ No newline at end of file diff --git a/src/tests/libxrpl/telemetry/json_logs.cpp b/src/tests/libxrpl/telemetry/json_logs.cpp deleted file mode 100644 index 9d95959efe..0000000000 --- a/src/tests/libxrpl/telemetry/json_logs.cpp +++ /dev/null @@ -1,359 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include -#include - -#include - -using namespace ripple; - -/** - * @brief sink for writing all log messages to a stringstream - */ -class MockSink : public beast::Journal::Sink -{ - std::stringstream& strm_; - -public: - MockSink(beast::severities::Severity threshold, std::stringstream& strm) - : beast::Journal::Sink(threshold, false), strm_(strm) - { - } - - void - write(beast::severities::Severity level, std::string const& text) override - { - strm_ << text; - } - - void - writeAlways(beast::severities::Severity level, std::string const& text) - override - { - strm_ << text; - } -}; - -class JsonLogStreamFixture -{ -public: - JsonLogStreamFixture() - : sink_(beast::severities::kAll, logStream_), j_(sink_) - { - auto structuredJournal = std::make_unique(); - beast::Journal::enableStructuredJournal(std::move(structuredJournal)); - } - - ~JsonLogStreamFixture() - { - beast::Journal::disableStructuredJournal(); - } - - std::stringstream& - stream() - { - return logStream_; - } - - beast::Journal& - journal() - { - return j_; - } - -private: - MockSink sink_; - std::stringstream logStream_; - beast::Journal j_; -}; - -TEST_CASE_FIXTURE(JsonLogStreamFixture, "TestJsonLogFields") -{ - journal().debug() << std::boolalpha << true << std::noboolalpha << " Test " - << std::boolalpha << false; - - Json::Value logValue; - Json::Reader reader; - reader.parse(stream().str(), logValue); - - CHECK(logValue.isObject()); - CHECK(logValue.isMember("Function")); - CHECK(logValue.isMember("File")); - CHECK(logValue.isMember("Line")); - CHECK(logValue.isMember("ThreadId")); - CHECK(logValue.isMember("Params")); - CHECK(logValue.isMember("Level")); - CHECK(logValue.isMember("Message")); - CHECK(logValue.isMember("Time")); - - CHECK(logValue["Function"].isString()); - CHECK(logValue["File"].isString()); - CHECK(logValue["Line"].isNumeric()); - CHECK(logValue["Params"].isNull()); - CHECK(logValue["Message"].isString()); - CHECK(logValue["Message"].asString() == "true Test false"); -} - -TEST_CASE_FIXTURE(JsonLogStreamFixture, "TestJsonLogLevels") -{ - { - stream().str(""); - journal().trace() << "Test"; - Json::Value logValue; - Json::Reader reader; - reader.parse(stream().str(), logValue); - - CHECK( - logValue["Level"].asString() == - beast::severities::to_string(beast::severities::kTrace)); - } - - { - stream().str(""); - journal().debug() << "Test"; - Json::Value logValue; - Json::Reader reader; - reader.parse(stream().str(), logValue); - - CHECK( - logValue["Level"].asString() == - beast::severities::to_string(beast::severities::kDebug)); - } - - { - stream().str(""); - journal().info() << "Test"; - Json::Value logValue; - Json::Reader reader; - reader.parse(stream().str(), logValue); - - CHECK( - logValue["Level"].asString() == - beast::severities::to_string(beast::severities::kInfo)); - } - - { - stream().str(""); - journal().warn() << "Test"; - Json::Value logValue; - Json::Reader reader; - reader.parse(stream().str(), logValue); - - CHECK( - logValue["Level"].asString() == - beast::severities::to_string(beast::severities::kWarning)); - } - - { - stream().str(""); - journal().error() << "Test"; - Json::Value logValue; - Json::Reader reader; - reader.parse(stream().str(), logValue); - - CHECK( - logValue["Level"].asString() == - beast::severities::to_string(beast::severities::kError)); - } - - { - stream().str(""); - journal().fatal() << "Test"; - Json::Value logValue; - Json::Reader reader; - reader.parse(stream().str(), logValue); - - CHECK( - logValue["Level"].asString() == - beast::severities::to_string(beast::severities::kFatal)); - } -} - -TEST_CASE_FIXTURE(JsonLogStreamFixture, "TestJsonLogStream") -{ - journal().stream(beast::severities::kError) << "Test"; - - Json::Value logValue; - Json::Reader reader; - reader.parse(stream().str(), logValue); - - CHECK( - logValue["Level"].asString() == - beast::severities::to_string(beast::severities::kError)); -} - -TEST_CASE_FIXTURE(JsonLogStreamFixture, "TestJsonLogParams") -{ - journal().debug() << "Test: " << log::param("Field1", 1) << ", " - << log::param( - "Field2", - std::numeric_limits::max()); - - Json::Value logValue; - Json::Reader reader; - reader.parse(stream().str(), logValue); - - CHECK(logValue["Params"].isObject()); - CHECK(logValue["Params"]["Field1"].isNumeric()); - CHECK(logValue["Params"]["Field1"].asInt() == 1); - // UInt64 doesn't fit in Json::Value so it should be converted to a string - // NOTE: We should expect it to be an int64 after we make the json library - // support in64 and uint64 - CHECK(logValue["Params"]["Field2"].isString()); - CHECK(logValue["Params"]["Field2"].asString() == "18446744073709551615"); - CHECK(logValue["Message"].isString()); - CHECK(logValue["Message"].asString() == "Test: 1, 18446744073709551615"); -} - -TEST_CASE_FIXTURE(JsonLogStreamFixture, "TestJsonLogFields") -{ - journal().debug() << "Test" << log::field("Field1", 1) - << log::field( - "Field2", - std::numeric_limits::max()); - - Json::Value logValue; - Json::Reader reader; - reader.parse(stream().str(), logValue); - - CHECK(logValue["Params"].isObject()); - CHECK(logValue["Params"]["Field1"].isNumeric()); - CHECK(logValue["Params"]["Field1"].asInt() == 1); - // UInt64 doesn't fit in Json::Value so it should be converted to a string - // NOTE: We should expect it to be an int64 after we make the json library - // support in64 and uint64 - CHECK(logValue["Params"]["Field2"].isString()); - CHECK(logValue["Params"]["Field2"].asString() == "18446744073709551615"); - CHECK(logValue["Message"].isString()); - CHECK(logValue["Message"].asString() == "Test"); -} - -TEST_CASE_FIXTURE(JsonLogStreamFixture, "TestJournalAttributes") -{ - beast::Journal j{ - journal(), log::attributes({{"Field1", "Value1"}, {"Field2", 2}})}; - - j.debug() << "Test"; - - Json::Value logValue; - Json::Reader reader; - reader.parse(stream().str(), logValue); - - CHECK(logValue["Field1"].isString()); - CHECK(logValue["Field1"].asString() == "Value1"); - CHECK(logValue["Field2"].isNumeric()); - CHECK(logValue["Field2"].asInt() == 2); -} - -TEST_CASE_FIXTURE(JsonLogStreamFixture, "TestJournalAttributesInheritable") -{ - beast::Journal j{ - journal(), log::attributes({{"Field1", "Value1"}, {"Field2", 2}})}; - beast::Journal j2{ - j, log::attributes({{"Field3", "Value3"}, {"Field2", 0}})}; - - j2.debug() << "Test"; - - Json::Value logValue; - Json::Reader reader; - reader.parse(stream().str(), logValue); - - CHECK(logValue["Field1"].isString()); - CHECK(logValue["Field1"].asString() == "Value1"); - CHECK(logValue["Field3"].isString()); - CHECK(logValue["Field3"].asString() == "Value3"); - // Field2 should be overwritten to 0 - CHECK(logValue["Field2"].isNumeric()); - CHECK(logValue["Field2"].asInt() == 0); -} - -TEST_CASE_FIXTURE( - JsonLogStreamFixture, - "TestJournalAttributesInheritableAfterMoving") -{ - beast::Journal j{ - std::move(journal()), - log::attributes({{"Field1", "Value1"}, {"Field2", 2}})}; - beast::Journal j2{ - std::move(j), log::attributes({{"Field3", "Value3"}, {"Field2", 0}})}; - - j2.debug() << "Test"; - - Json::Value logValue; - Json::Reader reader; - reader.parse(stream().str(), logValue); - - CHECK(logValue["Field1"].isString()); - CHECK(logValue["Field1"].asString() == "Value1"); - CHECK(logValue["Field3"].isString()); - CHECK(logValue["Field3"].asString() == "Value3"); - // Field2 should be overwritten to 0 - CHECK(logValue["Field2"].isNumeric()); - CHECK(logValue["Field2"].asInt() == 0); -} - -TEST_CASE_FIXTURE( - JsonLogStreamFixture, - "TestJournalAttributesInheritableAfterCopyAssignment") -{ - beast::Journal j{ - std::move(journal()), - log::attributes({{"Field1", "Value1"}, {"Field2", 2}})}; - - beast::Journal j2{beast::Journal::getNullSink()}; - - j2 = j; - - j2.debug() << "Test"; - - Json::Value logValue; - Json::Reader reader; - reader.parse(stream().str(), logValue); - - CHECK(logValue["Field1"].isString()); - CHECK(logValue["Field1"].asString() == "Value1"); - CHECK(logValue["Field2"].isNumeric()); - CHECK(logValue["Field2"].asInt() == 2); -} - -TEST_CASE_FIXTURE( - JsonLogStreamFixture, - "TestJournalAttributesInheritableAfterMoveAssignment") -{ - beast::Journal j{ - std::move(journal()), - log::attributes({{"Field1", "Value1"}, {"Field2", 2}})}; - - beast::Journal j2{beast::Journal::getNullSink()}; - - j2 = std::move(j); - - j2.debug() << "Test"; - - Json::Value logValue; - Json::Reader reader; - reader.parse(stream().str(), logValue); - - CHECK(logValue["Field1"].isString()); - CHECK(logValue["Field1"].asString() == "Value1"); - CHECK(logValue["Field2"].isNumeric()); - CHECK(logValue["Field2"].asInt() == 2); -} \ No newline at end of file diff --git a/src/tests/libxrpl/telemetry/main.cpp b/src/tests/libxrpl/telemetry/main.cpp deleted file mode 100644 index 0a3f254ea8..0000000000 --- a/src/tests/libxrpl/telemetry/main.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN -#include diff --git a/src/xrpld/app/consensus/RCLConsensus.cpp b/src/xrpld/app/consensus/RCLConsensus.cpp index 47f6cc70f6..b95f8cd5db 100644 --- a/src/xrpld/app/consensus/RCLConsensus.cpp +++ b/src/xrpld/app/consensus/RCLConsensus.cpp @@ -1109,7 +1109,7 @@ RclConsensusLogger::RclConsensusLogger( bool const validating, beast::Journal j, std::source_location location) - : j_(j, log::attributes({{"Role", "ConsensusLogger"}, {"Label", label}})) + : j_(j, log::attributes(log::attr("Role", "ConsensusLogger"), log::attr("Label", label))) , location_(location) { if (!validating && !j.info()) diff --git a/src/xrpld/app/main/Application.cpp b/src/xrpld/app/main/Application.cpp index 505ded2897..43b389c270 100644 --- a/src/xrpld/app/main/Application.cpp +++ b/src/xrpld/app/main/Application.cpp @@ -69,7 +69,6 @@ #include #include #include -#include #include #include @@ -835,8 +834,8 @@ public: beast::Journal journal( std::string const& name, - std::unique_ptr attributes = - {}) override; + std::optional attributes = + std::nullopt) override; //-------------------------------------------------------------------------- @@ -1220,9 +1219,9 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) << ", Instance Cookie: " << log::param("InstanceCookie", instanceCookie_); - Logs::setGlobalAttributes(log::attributes( - {{"RippledVersion", BuildInfo::getFullVersionString()}, - {"InstanceCookie", to_string(instanceCookie_)}})); + beast::Journal::addGlobalAttributes(log::attributes( + log::attr("RippledVersion", BuildInfo::getFullVersionString()), + log::attr("InstanceCookie", to_string(instanceCookie_)))); if (numberOfThreads(*config_) < 2) { @@ -2176,7 +2175,7 @@ ApplicationImp::serverOkay(std::string& reason) beast::Journal ApplicationImp::journal( std::string const& name, - std::unique_ptr attributes) + std::optional attributes) { return logs_->journal(name, std::move(attributes)); } diff --git a/src/xrpld/app/main/Application.h b/src/xrpld/app/main/Application.h index f2883a4c39..6b8f542942 100644 --- a/src/xrpld/app/main/Application.h +++ b/src/xrpld/app/main/Application.h @@ -260,8 +260,8 @@ public: virtual beast::Journal journal( std::string const& name, - std::unique_ptr attributes = - {}) = 0; + std::optional attributes = + std::nullopt) = 0; /* Returns the number of file descriptors the application needs */ virtual int diff --git a/src/xrpld/app/main/Main.cpp b/src/xrpld/app/main/Main.cpp index 1de6d0258e..1531f8c3f0 100644 --- a/src/xrpld/app/main/Main.cpp +++ b/src/xrpld/app/main/Main.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include #include @@ -797,10 +796,10 @@ run(int argc, char** argv) if (config->LOG_STYLE == LogStyle::Json) { - auto structuredJournal = std::make_unique(); - beast::Journal::enableStructuredJournal(std::move(structuredJournal)); - Logs::setGlobalAttributes(log::attributes( - {{"Application", "rippled"}, {"NetworkID", config->NETWORK_ID}})); + beast::Journal::enableStructuredJournal(); + beast::Journal::addGlobalAttributes(log::attributes( + log::attr("Application", "rippled"), + log::attr("NetworkID", config->NETWORK_ID))); } auto logs = std::make_unique(thresh); diff --git a/src/xrpld/app/misc/NetworkOPs.cpp b/src/xrpld/app/misc/NetworkOPs.cpp index b9069442f8..2227090bc5 100644 --- a/src/xrpld/app/misc/NetworkOPs.cpp +++ b/src/xrpld/app/misc/NetworkOPs.cpp @@ -1190,6 +1190,10 @@ NetworkOPsImp::strOperatingMode(OperatingMode const mode, bool const admin) void NetworkOPsImp::submitTransaction(std::shared_ptr const& iTrans) { + beast::Journal journal{ + m_journal, + log::attributes( + log::attr("TransactionID", to_string(iTrans->getTransactionID())))}; if (isNeedNetworkLedger()) { // Nothing we can do if we've never been in sync @@ -1253,6 +1257,9 @@ NetworkOPsImp::submitTransaction(std::shared_ptr const& iTrans) bool NetworkOPsImp::preProcessTransaction(std::shared_ptr& transaction) { + beast::Journal journal{ + m_journal, + log::attributes(log::attr("TransactionID", to_string(transaction->getID())))}; auto const newFlags = app_.getHashRouter().getFlags(transaction->getID()); if ((newFlags & HashRouterFlags::BAD) != HashRouterFlags::UNDEFINED) diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index f9fb1b5b60..e1477f7b50 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -209,10 +209,10 @@ Transactor::Transactor(ApplyContext& ctx) : ctx_(ctx) , account_(ctx.tx.getAccountID(sfAccount)) , j_(ctx.journal, - log::attributes({ - {"TransactionID", to_string(ctx_.tx.getTransactionID())}, - {"AccountID", to_string(account_)}, - })) + log::attributes( + log::attr("TransactionID", to_string(ctx_.tx.getTransactionID())), + log::attr("AccountID", to_string(account_)) + )) { } diff --git a/src/xrpld/app/tx/detail/Transactor.h b/src/xrpld/app/tx/detail/Transactor.h index 89feeb8933..1ac5a690b4 100644 --- a/src/xrpld/app/tx/detail/Transactor.h +++ b/src/xrpld/app/tx/detail/Transactor.h @@ -53,7 +53,11 @@ public: , rules(rules_) , flags(flags_) , parentBatchId(parentBatchId_) - , j(j_) + , j(j_, + log::attributes( + log::attr("TransactionID", to_string(tx.getTransactionID())), + log::attr("AccountID", to_string(tx.getAccountID(sfAccount))) + )) { XRPL_ASSERT( (flags_ & tapBATCH) == tapBATCH, "Batch apply flag should be set"); @@ -101,7 +105,11 @@ public: , flags(flags_) , tx(tx_) , parentBatchId(parentBatchId_) - , j(j_) + , j(j_, + log::attributes( + log::attr("TransactionID", to_string(tx.getTransactionID())), + log::attr("AccountID", to_string(tx.getAccountID(sfAccount))) + )) { XRPL_ASSERT( parentBatchId.has_value() == ((flags_ & tapBATCH) == tapBATCH), diff --git a/src/xrpld/core/ClosureCounter.h b/src/xrpld/core/ClosureCounter.h index 92117a91c4..ded056412a 100644 --- a/src/xrpld/core/ClosureCounter.h +++ b/src/xrpld/core/ClosureCounter.h @@ -180,6 +180,13 @@ public: } } + template + Substitute + forceWrap(Closure&& closure) + { + return {*this, std::forward(closure)}; + } + /** Wrap the passed closure with a reference counter. @param closure Closure that accepts Args_t parameters and returns Ret_t. diff --git a/src/xrpld/core/Config.h b/src/xrpld/core/Config.h index c4b2669464..d155976d7e 100644 --- a/src/xrpld/core/Config.h +++ b/src/xrpld/core/Config.h @@ -25,7 +25,6 @@ #include #include #include // VFALCO Breaks levelization -#include #include // VFALCO FIX: This include should not be here diff --git a/src/xrpld/core/Coro.ipp b/src/xrpld/core/Coro.ipp index 5901e07c68..3f67568e39 100644 --- a/src/xrpld/core/Coro.ipp +++ b/src/xrpld/core/Coro.ipp @@ -98,6 +98,10 @@ JobQueue::Coro::resume() } { std::lock_guard lock(jq_.m_mutex); + + XRPL_ASSERT( + jq_.nSuspend_ > 0, + "ripple::JobQueue::Coro::resume jq_.nSuspend_ should be greater than 0"); --jq_.nSuspend_; } auto saved = detail::getLocalValues().release(); @@ -134,6 +138,10 @@ JobQueue::Coro::expectEarlyExit() // That said, since we're outside the Coro's stack, we need to // decrement the nSuspend that the Coro's call to yield caused. std::lock_guard lock(jq_.m_mutex); + + XRPL_ASSERT( + jq_.nSuspend_ > 0, + "ripple::JobQueue::Coro::expectEarlyExit() jq_.nSuspend_ should be greater than 0"); --jq_.nSuspend_; #ifndef NDEBUG finished_ = true; diff --git a/src/xrpld/core/detail/JobQueue.cpp b/src/xrpld/core/detail/JobQueue.cpp index 1ea1df51ab..ceb449f3b9 100644 --- a/src/xrpld/core/detail/JobQueue.cpp +++ b/src/xrpld/core/detail/JobQueue.cpp @@ -304,9 +304,9 @@ JobQueue::stop() // but there may still be some threads between the return of // `Job::doJob` and the return of `JobQueue::processTask`. That is why // we must wait on the condition variable to make these assertions. - std::unique_lock lock(m_mutex); + std::unique_lock lock(m_mutex); cv_.wait( - lock, [this] { return m_processCount == 0 && m_jobSet.empty(); }); + lock, [this] { return m_processCount == 0 && nSuspend_ == 0 && m_jobSet.empty(); }); XRPL_ASSERT( m_processCount == 0, "ripple::JobQueue::stop : all processes completed"); diff --git a/src/xrpld/overlay/detail/ConnectAttempt.cpp b/src/xrpld/overlay/detail/ConnectAttempt.cpp index 048933c82c..4764c06331 100644 --- a/src/xrpld/overlay/detail/ConnectAttempt.cpp +++ b/src/xrpld/overlay/detail/ConnectAttempt.cpp @@ -41,7 +41,7 @@ ConnectAttempt::ConnectAttempt( : Child(overlay) , app_(app) , id_(id) - , journal_(journal, log::attributes({{"NodeID", id}})) + , journal_(journal, log::attributes(log::attr("NodeID", id))) , remote_endpoint_(remote_endpoint) , usage_(usage) , strand_(boost::asio::make_strand(io_context)) diff --git a/src/xrpld/overlay/detail/OverlayImpl.cpp b/src/xrpld/overlay/detail/OverlayImpl.cpp index ee3aa53d7a..f564fc8833 100644 --- a/src/xrpld/overlay/detail/OverlayImpl.cpp +++ b/src/xrpld/overlay/detail/OverlayImpl.cpp @@ -168,7 +168,7 @@ OverlayImpl::onHandoff( endpoint_type remote_endpoint) { auto const id = next_id_++; - auto journal = app_.journal("Peer", log::attributes({{"NodeID", id}})); + auto journal = app_.journal("Peer", log::attributes(log::attr("NodeID", id))); Handoff handoff; if (processRequest(request, handoff)) diff --git a/src/xrpld/overlay/detail/PeerImp.cpp b/src/xrpld/overlay/detail/PeerImp.cpp index 1b6e611c9e..3771e04261 100644 --- a/src/xrpld/overlay/detail/PeerImp.cpp +++ b/src/xrpld/overlay/detail/PeerImp.cpp @@ -85,15 +85,15 @@ PeerImp::PeerImp( , journal_( app_.journal("Peer"), log::attributes( - {{"NodeID", id}, - {"RemoteAddress", to_string(slot->remote_endpoint())}, - {"PublicKey", toBase58(TokenType::NodePublic, publicKey)}})) + log::attr("NodeID", id), + log::attr("RemoteAddress", to_string(slot->remote_endpoint())), + log::attr("PublicKey", toBase58(TokenType::NodePublic, publicKey)))) , p_journal_( app_.journal("Protocol"), log::attributes( - {{"NodeID", id}, - {"RemoteAddress", to_string(slot->remote_endpoint())}, - {"PublicKey", toBase58(TokenType::NodePublic, publicKey)}})) + log::attr("NodeID", id), + log::attr("RemoteAddress", to_string(slot->remote_endpoint())), + log::attr("PublicKey", toBase58(TokenType::NodePublic, publicKey)))) , stream_ptr_(std::move(stream_ptr)) , socket_(stream_ptr_->next_layer().socket()) , stream_(*stream_ptr_) @@ -1392,6 +1392,12 @@ PeerImp::handleTransaction( { auto stx = std::make_shared(sit); uint256 txID = stx->getTransactionID(); + beast::Journal protocolJournal{ + p_journal_, + log::attributes( + log::attr("TransactionID", to_string(txID)), + log::attr("RawTransaction", strHex(m->rawtransaction())) + )}; // Charge strongly for attempting to relay a txn with tfInnerBatchTxn // LCOV_EXCL_START diff --git a/src/xrpld/overlay/detail/PeerImp.h b/src/xrpld/overlay/detail/PeerImp.h index feda39031e..06c1de7971 100644 --- a/src/xrpld/overlay/detail/PeerImp.h +++ b/src/xrpld/overlay/detail/PeerImp.h @@ -830,15 +830,15 @@ PeerImp::PeerImp( , journal_( app_.journal("Peer"), log::attributes( - {{"NodeID", id}, - {"RemoteAddress", to_string(slot->remote_endpoint())}, - {"PublicKey", toBase58(TokenType::NodePublic, publicKey)}})) + log::attr("NodeID", id), + log::attr("RemoteAddress", to_string(slot->remote_endpoint())), + log::attr("PublicKey", toBase58(TokenType::NodePublic, publicKey)))) , p_journal_( app_.journal("Protocol"), log::attributes( - {{"NodeID", id}, - {"RemoteAddress", to_string(slot->remote_endpoint())}, - {"PublicKey", toBase58(TokenType::NodePublic, publicKey)}})) + log::attr("NodeID", id), + log::attr("RemoteAddress", to_string(slot->remote_endpoint())), + log::attr("PublicKey", toBase58(TokenType::NodePublic, publicKey)))) , stream_ptr_(std::move(stream_ptr)) , socket_(stream_ptr_->next_layer().socket()) , stream_(*stream_ptr_)