Performance improvement

This commit is contained in:
JCW
2025-08-28 20:36:46 +01:00
parent 0fe8f3f62d
commit cdf1109558
32 changed files with 1048 additions and 968 deletions

View File

@@ -51,6 +51,8 @@ target_link_libraries(xrpl.libpb
# TODO: Clean up the number of library targets later. # TODO: Clean up the number of library targets later.
add_library(xrpl.imports.main INTERFACE) add_library(xrpl.imports.main INTERFACE)
find_package(RapidJSON)
target_link_libraries(xrpl.imports.main target_link_libraries(xrpl.imports.main
INTERFACE INTERFACE
LibArchive::LibArchive LibArchive::LibArchive
@@ -75,6 +77,7 @@ add_module(xrpl beast)
target_link_libraries(xrpl.libxrpl.beast PUBLIC target_link_libraries(xrpl.libxrpl.beast PUBLIC
xrpl.imports.main xrpl.imports.main
xrpl.libpb xrpl.libpb
rapidjson
) )
# Level 02 # Level 02
@@ -85,14 +88,9 @@ target_link_libraries(xrpl.libxrpl.basics PUBLIC xrpl.libxrpl.beast)
add_module(xrpl json) add_module(xrpl json)
target_link_libraries(xrpl.libxrpl.json PUBLIC xrpl.libxrpl.basics) 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) add_module(xrpl crypto)
target_link_libraries(xrpl.libxrpl.crypto PUBLIC target_link_libraries(xrpl.libxrpl.crypto PUBLIC xrpl.libxrpl.basics)
xrpl.libxrpl.basics
xrpl.libxrpl.telemetry
)
# Level 04 # Level 04
add_module(xrpl protocol) add_module(xrpl protocol)
@@ -139,7 +137,6 @@ target_link_modules(xrpl PUBLIC
beast beast
crypto crypto
json json
telemetry
protocol protocol
resource resource
server server

View File

@@ -16,7 +16,6 @@ install (
xrpl.libxrpl.beast xrpl.libxrpl.beast
xrpl.libxrpl.crypto xrpl.libxrpl.crypto
xrpl.libxrpl.json xrpl.libxrpl.json
xrpl.libxrpl.telemetry
xrpl.libxrpl.protocol xrpl.libxrpl.protocol
xrpl.libxrpl.resource xrpl.libxrpl.resource
xrpl.libxrpl.ledger xrpl.libxrpl.ledger

View File

@@ -30,6 +30,7 @@ class Xrpl(ConanFile):
'openssl/1.1.1w', 'openssl/1.1.1w',
'soci/4.0.3', 'soci/4.0.3',
'zlib/1.3.1', 'zlib/1.3.1',
"rapidjson/1.1.0"
] ]
test_requires = [ test_requires = [

View File

@@ -167,8 +167,6 @@ private:
beast::severities::Severity thresh_; beast::severities::Severity thresh_;
File file_; File file_;
bool silent_ = false; bool silent_ = false;
static std::unique_ptr<beast::Journal::StructuredLogAttributes>
globalLogAttributes_;
public: public:
Logs(beast::severities::Severity level); Logs(beast::severities::Severity level);
@@ -191,8 +189,8 @@ public:
beast::Journal beast::Journal
journal( journal(
std::string const& name, std::string const& name,
std::unique_ptr<beast::Journal::StructuredLogAttributes> attributes = std::optional<beast::Journal::JsonLogAttributes> attributes =
{}); std::nullopt);
beast::severities::Severity beast::severities::Severity
threshold() const; threshold() const;
@@ -229,20 +227,6 @@ public:
std::string const& partition, std::string const& partition,
beast::severities::Severity startingLevel); beast::severities::Severity startingLevel);
static void
setGlobalAttributes(std::unique_ptr<beast::Journal::StructuredLogAttributes>
globalLogAttributes)
{
if (!globalLogAttributes_)
{
globalLogAttributes_ = std::move(globalLogAttributes);
}
else
{
globalLogAttributes_->combine(std::move(globalLogAttributes));
}
}
public: public:
static LogSeverity static LogSeverity
fromSeverity(beast::severities::Severity level); fromSeverity(beast::severities::Severity level);

View File

@@ -21,11 +21,62 @@
#define BEAST_UTILITY_JOURNAL_H_INCLUDED #define BEAST_UTILITY_JOURNAL_H_INCLUDED
#include <xrpl/beast/utility/instrumentation.h> #include <xrpl/beast/utility/instrumentation.h>
#include <rapidjson/document.h>
#include <memory> #include <deque>
#include <utility>
#include <source_location> #include <source_location>
#include <sstream> #include <sstream>
namespace ripple::log {
template <typename T>
class LogParameter
{
public:
template <typename TArg>
LogParameter(char const* name, TArg&& value)
: name_(name), value_(std::forward<TArg>(value))
{
}
private:
char const* name_;
T value_;
template <typename U>
friend std::ostream&
operator<<(std::ostream& os, LogParameter<U> const&);
};
template <typename T>
class LogField
{
public:
template <typename TArg>
LogField(char const* name, TArg&& value)
: name_(name), value_(std::forward<TArg>(value))
{
}
private:
char const* name_;
T value_;
template <typename U>
friend std::ostream&
operator<<(std::ostream& os, LogField<U> const&);
};
template <typename T>
std::ostream&
operator<<(std::ostream& os, LogField<T> const& param);
template <typename T>
std::ostream&
operator<<(std::ostream& os, LogParameter<T> const& param);
}
namespace beast { namespace beast {
/** A namespace for easy access to logging severity values. */ /** A namespace for easy access to logging severity values. */
@@ -64,75 +115,111 @@ to_string(Severity severity);
class Journal class Journal
{ {
public: public:
template <typename T>
friend std::ostream&
ripple::log::operator<<(std::ostream& os, ripple::log::LogField<T> const& param);
template <typename T>
friend std::ostream&
ripple::log::operator<<(
std::ostream& os,
ripple::log::LogParameter<T> const& param);
class Sink; 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: private:
// Severity level / threshold of a Journal message. // Severity level / threshold of a Journal message.
using Severity = severities::Severity; using Severity = severities::Severity;
std::unique_ptr<StructuredLogAttributes> m_attributes; std::optional<JsonLogAttributes> m_attributes;
static std::optional<JsonLogAttributes> globalLogAttributes_;
static std::mutex globalLogAttributesMutex_;
static bool m_jsonLogsEnabled;
static thread_local JsonLogContext currentJsonLogContext_;
static std::unique_ptr<StructuredJournalImpl> m_structuredJournalImpl;
// Invariant: m_sink always points to a valid Sink // Invariant: m_sink always points to a valid Sink
Sink* m_sink = nullptr; Sink* m_sink = nullptr;
static void
initMessageContext(std::source_location location);
static std::string
formatLog(std::string const& message,
severities::Severity severity,
std::optional<JsonLogAttributes> const& attributes = std::nullopt);
public: public:
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
static void static void
enableStructuredJournal(std::unique_ptr<StructuredJournalImpl> impl) enableStructuredJournal();
{
m_structuredJournalImpl = std::move(impl);
}
static void static void
disableStructuredJournal() disableStructuredJournal();
{
m_structuredJournalImpl = nullptr;
}
static bool static bool
isStructuredJournalEnabled() 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<StructuredLogAttributes>
clone() const = 0;
virtual void
combine(std::unique_ptr<StructuredLogAttributes> const& attributes) = 0;
virtual void
combine(std::unique_ptr<StructuredLogAttributes>&& attributes) = 0;
virtual ~StructuredLogAttributes() = default;
};
/** Abstraction for the underlying message destination. */ /** Abstraction for the underlying message destination. */
class Sink class Sink
@@ -214,25 +301,25 @@ public:
public: public:
ScopedStream(ScopedStream const& other) ScopedStream(ScopedStream const& other)
: ScopedStream( : ScopedStream(
other.m_attributes ? other.m_attributes->clone() : nullptr, other.m_attributes,
other.m_sink, other.m_sink,
other.m_level) other.m_level)
{ {
} }
ScopedStream( ScopedStream(
std::unique_ptr<StructuredLogAttributes> attributes, std::optional<JsonLogAttributes> attributes,
Sink& sink, Sink& sink,
Severity level); Severity level);
template <typename T> template <typename T>
ScopedStream( ScopedStream(
std::unique_ptr<StructuredLogAttributes> attributes, std::optional<JsonLogAttributes> attributes,
Stream const& stream, Stream const& stream,
T const& t); T const& t);
ScopedStream( ScopedStream(
std::unique_ptr<StructuredLogAttributes> attributes, std::optional<JsonLogAttributes> attributes,
Stream const& stream, Stream const& stream,
std::ostream& manip(std::ostream&)); std::ostream& manip(std::ostream&));
@@ -255,7 +342,7 @@ public:
operator<<(T const& t) const; operator<<(T const& t) const;
private: private:
std::unique_ptr<StructuredLogAttributes> m_attributes; std::optional<JsonLogAttributes> m_attributes;
Sink& m_sink; Sink& m_sink;
Severity const m_level; Severity const m_level;
std::ostringstream mutable m_ostream; std::ostringstream mutable m_ostream;
@@ -291,7 +378,7 @@ public:
Constructor is inlined so checking active() very inexpensive. Constructor is inlined so checking active() very inexpensive.
*/ */
Stream( Stream(
std::unique_ptr<StructuredLogAttributes> attributes, std::optional<JsonLogAttributes> attributes,
Sink& sink, Sink& sink,
Severity level) Severity level)
: m_attributes(std::move(attributes)), m_sink(sink), m_level(level) : m_attributes(std::move(attributes)), m_sink(sink), m_level(level)
@@ -304,7 +391,7 @@ public:
/** Construct or copy another Stream. */ /** Construct or copy another Stream. */
Stream(Stream const& other) Stream(Stream const& other)
: Stream( : Stream(
other.m_attributes ? other.m_attributes->clone() : nullptr, other.m_attributes,
other.m_sink, other.m_sink,
other.m_level) other.m_level)
{ {
@@ -353,7 +440,7 @@ public:
/** @} */ /** @} */
private: private:
std::unique_ptr<StructuredLogAttributes> m_attributes; std::optional<JsonLogAttributes> m_attributes;
Sink& m_sink; Sink& m_sink;
Severity m_level; Severity m_level;
}; };
@@ -372,47 +459,29 @@ public:
/** Journal has no default constructor. */ /** Journal has no default constructor. */
Journal() = delete; Journal() = delete;
Journal(Journal const& other) : Journal(other, nullptr)
{
}
Journal( Journal(
Journal const& other, Journal const& other,
std::unique_ptr<StructuredLogAttributes> attributes) std::optional<JsonLogAttributes> attributes = std::nullopt)
: m_sink(other.m_sink) : m_sink(other.m_sink)
{ {
if (attributes) if (attributes.has_value())
m_attributes = std::move(attributes); m_attributes = std::move(attributes.value());
if (other.m_attributes) if (other.m_attributes.has_value())
{ {
if (m_attributes) if (m_attributes.has_value())
m_attributes->combine(other.m_attributes); m_attributes = JsonLogAttributes::combine(
other.m_attributes->contextValues_,
m_attributes->contextValues_
);
else else
m_attributes = other.m_attributes->clone(); m_attributes = other.m_attributes;
} }
} }
Journal(
Journal&& other,
std::unique_ptr<StructuredLogAttributes> 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. */ /** Create a journal that writes to the specified sink. */
Journal( explicit Journal(
Sink& sink, Sink& sink,
std::string const& name = {}, std::string const& name = {},
std::unique_ptr<StructuredLogAttributes> attributes = {}) std::optional<JsonLogAttributes> attributes = std::nullopt)
: m_sink(&sink) : m_sink(&sink)
{ {
if (attributes) if (attributes)
@@ -425,9 +494,11 @@ public:
Journal& Journal&
operator=(Journal const& other) operator=(Journal const& other)
{ {
if (&other == this)
return *this;
m_sink = other.m_sink; m_sink = other.m_sink;
if (other.m_attributes) m_attributes = other.m_attributes;
m_attributes = other.m_attributes->clone();
return *this; return *this;
} }
@@ -435,7 +506,6 @@ public:
operator=(Journal&& other) noexcept operator=(Journal&& other) noexcept
{ {
m_sink = other.m_sink; 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; return *this;
} }
@@ -452,7 +522,7 @@ public:
stream(Severity level) const stream(Severity level) const
{ {
return Stream( 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. /** Returns `true` if any message would be logged at this severity level.
@@ -470,10 +540,10 @@ public:
Stream Stream
trace(std::source_location location = std::source_location::current()) const trace(std::source_location location = std::source_location::current()) const
{ {
if (m_structuredJournalImpl) if (m_jsonLogsEnabled)
m_structuredJournalImpl->initMessageContext(location); initMessageContext(location);
return { return {
m_attributes ? m_attributes->clone() : nullptr, m_attributes,
*m_sink, *m_sink,
severities::kTrace}; severities::kTrace};
} }
@@ -481,10 +551,10 @@ public:
Stream Stream
debug(std::source_location location = std::source_location::current()) const debug(std::source_location location = std::source_location::current()) const
{ {
if (m_structuredJournalImpl) if (m_jsonLogsEnabled)
m_structuredJournalImpl->initMessageContext(location); initMessageContext(location);
return { return {
m_attributes ? m_attributes->clone() : nullptr, m_attributes,
*m_sink, *m_sink,
severities::kDebug}; severities::kDebug};
} }
@@ -492,10 +562,10 @@ public:
Stream Stream
info(std::source_location location = std::source_location::current()) const info(std::source_location location = std::source_location::current()) const
{ {
if (m_structuredJournalImpl) if (m_jsonLogsEnabled)
m_structuredJournalImpl->initMessageContext(location); initMessageContext(location);
return { return {
m_attributes ? m_attributes->clone() : nullptr, m_attributes,
*m_sink, *m_sink,
severities::kInfo}; severities::kInfo};
} }
@@ -503,10 +573,12 @@ public:
Stream Stream
warn(std::source_location location = std::source_location::current()) const warn(std::source_location location = std::source_location::current()) const
{ {
if (m_structuredJournalImpl) const char* a = "a";
m_structuredJournalImpl->initMessageContext(location); rapidjson::Value v{a, 1};
if (m_jsonLogsEnabled)
initMessageContext(location);
return { return {
m_attributes ? m_attributes->clone() : nullptr, m_attributes,
*m_sink, *m_sink,
severities::kWarning}; severities::kWarning};
} }
@@ -514,10 +586,10 @@ public:
Stream Stream
error(std::source_location location = std::source_location::current()) const error(std::source_location location = std::source_location::current()) const
{ {
if (m_structuredJournalImpl) if (m_jsonLogsEnabled)
m_structuredJournalImpl->initMessageContext(location); initMessageContext(location);
return { return {
m_attributes ? m_attributes->clone() : nullptr, m_attributes,
*m_sink, *m_sink,
severities::kError}; severities::kError};
} }
@@ -525,14 +597,25 @@ public:
Stream Stream
fatal(std::source_location location = std::source_location::current()) const fatal(std::source_location location = std::source_location::current()) const
{ {
if (m_structuredJournalImpl) if (m_jsonLogsEnabled)
m_structuredJournalImpl->initMessageContext(location); initMessageContext(location);
return { return {
m_attributes ? m_attributes->clone() : nullptr, m_attributes,
*m_sink, *m_sink,
severities::kFatal}; 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__ #ifndef __INTELLISENSE__
@@ -548,7 +631,7 @@ static_assert(std::is_nothrow_destructible<Journal>::value == true, "");
template <typename T> template <typename T>
Journal::ScopedStream::ScopedStream( Journal::ScopedStream::ScopedStream(
std::unique_ptr<StructuredLogAttributes> attributes, std::optional<JsonLogAttributes> attributes,
Stream const& stream, Stream const& stream,
T const& t) T const& t)
: ScopedStream(std::move(attributes), stream.sink(), stream.level()) : ScopedStream(std::move(attributes), stream.sink(), stream.level())
@@ -570,7 +653,7 @@ template <typename T>
Journal::ScopedStream Journal::ScopedStream
Journal::Stream::operator<<(T const& t) const Journal::Stream::operator<<(T const& t) const
{ {
return {m_attributes ? m_attributes->clone() : nullptr, *this, t}; return {m_attributes, *this, t};
} }
namespace detail { namespace detail {
@@ -642,4 +725,127 @@ using logwstream = basic_logstream<wchar_t>;
} // namespace beast } // namespace beast
namespace ripple::log {
namespace detail {
template <typename T>
void setJsonValue(
rapidjson::Value& object,
rapidjson::MemoryPoolAllocator<>& allocator,
char const* name,
T&& value,
std::ostream* outStream)
{
using ValueType = std::decay_t<T>;
rapidjson::Value jsonValue;
if constexpr (std::constructible_from<rapidjson::Value, ValueType, rapidjson::MemoryPoolAllocator<>&>)
{
jsonValue = rapidjson::Value{value, allocator};
if (outStream)
{
(*outStream) << value;
}
}
else if constexpr (std::constructible_from<rapidjson::Value, ValueType>)
{
jsonValue = rapidjson::Value{value};
if (outStream)
{
(*outStream) << value;
}
}
else if constexpr (std::same_as<ValueType, std::string>)
{
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 <typename T>
std::ostream&
operator<<(std::ostream& os, LogParameter<T> 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 <typename T>
std::ostream&
operator<<(std::ostream& os, LogField<T> 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 <typename T>
LogParameter<T>
param(char const* name, T&& value)
{
return LogParameter<T>{name, std::forward<T>(value)};
}
template <typename T>
LogField<T>
field(char const* name, T&& value)
{
return LogField<T>{name, std::forward<T>(value)};
}
template<typename... Pair>
[[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 <typename T>
[[nodiscard]] std::pair<char const*, std::decay_t<T>>
attr(char const* name, T&& value)
{
return std::make_pair(name, std::forward<T>(value));
}
}
#endif #endif

View File

@@ -32,7 +32,6 @@
#include <xrpl/resource/Fees.h> #include <xrpl/resource/Fees.h>
#include <xrpl/resource/Gossip.h> #include <xrpl/resource/Gossip.h>
#include <xrpl/resource/detail/Import.h> #include <xrpl/resource/detail/Import.h>
#include <xrpl/telemetry/JsonLogs.h>
#include <mutex> #include <mutex>

View File

@@ -25,7 +25,6 @@
#include <xrpl/server/Port.h> #include <xrpl/server/Port.h>
#include <xrpl/server/detail/LowestLayer.h> #include <xrpl/server/detail/LowestLayer.h>
#include <xrpl/server/detail/io_list.h> #include <xrpl/server/detail/io_list.h>
#include <xrpl/telemetry/JsonLogs.h>
#include <boost/asio.hpp> #include <boost/asio.hpp>
@@ -86,11 +85,11 @@ BasePeer<Handler, Impl>::BasePeer(
, remote_address_(remote_address) , remote_address_(remote_address)
, j_(journal, , j_(journal,
log::attributes( log::attributes(
{{"PeerID", log::attr("PeerID",
[] { [] {
static std::atomic<unsigned> id{0}; static std::atomic<unsigned> id{0};
return "##" + std::to_string(++id) + " "; return "##" + std::to_string(++id) + " ";
}()}})) }())))
, work_(boost::asio::make_work_guard(executor)) , work_(boost::asio::make_work_guard(executor))
, strand_(boost::asio::make_strand(executor)) , strand_(boost::asio::make_strand(executor))
{ {

View File

@@ -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 <xrpl/beast/utility/Journal.h>
#include <xrpl/json/json_value.h>
#include <memory>
#include <source_location>
#include <unordered_map>
#include <utility>
namespace ripple::log {
template <typename T>
class LogParameter
{
public:
template <typename TArg>
LogParameter(char const* name, TArg&& value)
: name_(name), value_(std::forward<TArg>(value))
{
}
private:
char const* name_;
T value_;
template <typename U>
friend std::ostream&
operator<<(std::ostream& os, LogParameter<U> const&);
};
template <typename T>
class LogField
{
public:
template <typename TArg>
LogField(char const* name, TArg&& value)
: name_(name), value_(std::forward<TArg>(value))
{
}
private:
char const* name_;
T value_;
template <typename U>
friend std::ostream&
operator<<(std::ostream& os, LogField<U> const&);
};
class JsonLogAttributes : public beast::Journal::StructuredLogAttributes
{
public:
using AttributeFields = std::unordered_map<std::string, Json::Value>;
using Pair = AttributeFields::value_type;
explicit JsonLogAttributes(AttributeFields contextValues = {});
void
setModuleName(std::string const& name) override;
[[nodiscard]] std::unique_ptr<StructuredLogAttributes>
clone() const override;
void
combine(std::unique_ptr<StructuredLogAttributes> const& context) override;
void
combine(std::unique_ptr<StructuredLogAttributes>&& 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 <typename T>
friend std::ostream&
operator<<(std::ostream& os, LogParameter<T> const&);
template <typename T>
friend std::ostream&
operator<<(std::ostream& os, LogField<T> 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 <typename T>
std::ostream&
operator<<(std::ostream& os, LogParameter<T> const& param)
{
using ValueType = std::decay_t<T>;
// TODO: Update the Json library to support 64-bit integer values.
if constexpr (
std::constructible_from<Json::Value, ValueType> &&
(!std::is_integral_v<ValueType> ||
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 <typename T>
std::ostream&
operator<<(std::ostream& os, LogField<T> const& param)
{
using ValueType = std::decay_t<T>;
// TODO: Update the Json library to support 64-bit integer values.
if constexpr (
std::constructible_from<Json::Value, ValueType> &&
(!std::is_integral_v<ValueType> ||
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 <typename T>
LogParameter<T>
param(char const* name, T&& value)
{
return LogParameter<T>{name, std::forward<T>(value)};
}
template <typename T>
LogField<T>
field(char const* name, T&& value)
{
return LogField<T>{name, std::forward<T>(value)};
}
[[nodiscard]] inline std::unique_ptr<JsonLogAttributes>
attributes(std::initializer_list<JsonLogAttributes::Pair> const& fields)
{
return std::make_unique<JsonLogAttributes>(fields);
}
} // namespace ripple::log
#endif

View File

@@ -38,9 +38,6 @@
namespace ripple { namespace ripple {
std::unique_ptr<beast::Journal::StructuredLogAttributes>
Logs::globalLogAttributes_;
Logs::Sink::Sink( Logs::Sink::Sink(
std::string const& partition, std::string const& partition,
beast::severities::Severity thresh, beast::severities::Severity thresh,
@@ -162,16 +159,9 @@ Logs::operator[](std::string const& name)
beast::Journal beast::Journal
Logs::journal( Logs::journal(
std::string const& name, std::string const& name,
std::unique_ptr<beast::Journal::StructuredLogAttributes> attributes) std::optional<beast::Journal::JsonLogAttributes> attributes)
{ {
if (globalLogAttributes_) return beast::Journal{get(name), name, std::move(attributes)};
{
if (attributes)
attributes->combine(globalLogAttributes_);
else
attributes = globalLogAttributes_->clone();
}
return beast::Journal(get(name), name, std::move(attributes));
} }
beast::severities::Severity beast::severities::Severity

View File

@@ -19,13 +19,22 @@
#include <xrpl/beast/utility/Journal.h> #include <xrpl/beast/utility/Journal.h>
#include <rapidjson/document.h>
#include <rapidjson/writer.h>
#include <rapidjson/stringbuffer.h>
#include <thread>
#include <ranges>
#include <ios> #include <ios>
#include <ostream> #include <ostream>
#include <string> #include <string>
namespace beast { namespace beast {
std::unique_ptr<Journal::StructuredJournalImpl> Journal::m_structuredJournalImpl; std::optional<Journal::JsonLogAttributes> 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 ""; 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<JsonLogAttributes> const& attributes)
{
if (!m_jsonLogsEnabled)
{
return message;
}
rapidjson::Document doc{&currentJsonLogContext_.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::milliseconds>(
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) Journal::Sink::Sink(Severity thresh, bool console)
: thresh_(thresh), m_console(console) : thresh_(thresh), m_console(console)
{ {
@@ -153,7 +340,7 @@ Journal::Sink::threshold(Severity thresh)
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
Journal::ScopedStream::ScopedStream( Journal::ScopedStream::ScopedStream(
std::unique_ptr<StructuredLogAttributes> attributes, std::optional<JsonLogAttributes> attributes,
Sink& sink, Sink& sink,
Severity level) Severity level)
: m_attributes(std::move(attributes)), m_sink(sink), m_level(level) : m_attributes(std::move(attributes)), m_sink(sink), m_level(level)
@@ -163,7 +350,7 @@ Journal::ScopedStream::ScopedStream(
} }
Journal::ScopedStream::ScopedStream( Journal::ScopedStream::ScopedStream(
std::unique_ptr<StructuredLogAttributes> attributes, std::optional<JsonLogAttributes> attributes,
Stream const& stream, Stream const& stream,
std::ostream& manip(std::ostream&)) std::ostream& manip(std::ostream&))
: ScopedStream(std::move(attributes), stream.sink(), stream.level()) : ScopedStream(std::move(attributes), stream.sink(), stream.level())
@@ -177,21 +364,9 @@ Journal::ScopedStream::~ScopedStream()
if (!s.empty()) if (!s.empty())
{ {
if (s == "\n") if (s == "\n")
{ m_sink.write(m_level, formatLog("", m_level, m_attributes));
if (m_structuredJournalImpl)
m_structuredJournalImpl->flush(
&m_sink, m_level, "", m_attributes.get());
else else
m_sink.write(m_level, ""); m_sink.write(m_level, formatLog(s, 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);
}
} }
} }
@@ -206,7 +381,7 @@ Journal::ScopedStream::operator<<(std::ostream& manip(std::ostream&)) const
Journal::ScopedStream Journal::ScopedStream
Journal::Stream::operator<<(std::ostream& manip(std::ostream&)) const 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 } // namespace beast

View File

@@ -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 <xrpl/basics/ToString.h>
#include <xrpl/json/to_string.h>
#include <xrpl/telemetry/JsonLogs.h>
#include <chrono>
#include <thread>
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<beast::Journal::StructuredLogAttributes>
JsonLogAttributes::clone() const
{
return std::make_unique<JsonLogAttributes>(*this);
}
void
JsonLogAttributes::combine(
std::unique_ptr<StructuredLogAttributes> const& context)
{
auto structuredContext =
static_cast<JsonLogAttributes const*>(context.get());
contextValues_.merge(AttributeFields{structuredContext->contextValues_});
}
void
JsonLogAttributes::combine(std::unique_ptr<StructuredLogAttributes>&& context)
{
auto structuredContext = static_cast<JsonLogAttributes*>(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<JsonLogAttributes*>(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::milliseconds>(
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

View File

@@ -175,12 +175,72 @@ public:
BEAST_EXPECT(*lv == -1); 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<Config> cfg) {
cfg->FORCE_MULTI_THREAD = true;
return cfg;
}));
std::shared_ptr<JobQueue::Coro> 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 void
run() override run() override
{ {
correct_order(); // correct_order();
incorrect_order(); // incorrect_order();
thread_specific_storage(); // thread_specific_storage();
test_yield_and_stop();
} }
}; };

View File

@@ -33,7 +33,6 @@
#include <xrpl/beast/utility/WrappedSink.h> #include <xrpl/beast/utility/WrappedSink.h>
#include <xrpl/protocol/PublicKey.h> #include <xrpl/protocol/PublicKey.h>
#include <xrpl/telemetry/JsonLogs.h>
#include <boost/container/flat_map.hpp> #include <boost/container/flat_map.hpp>
#include <boost/container/flat_set.hpp> #include <boost/container/flat_set.hpp>
@@ -284,7 +283,7 @@ struct Peer
TrustGraph<Peer*>& tg, TrustGraph<Peer*>& tg,
CollectorRefs& c, CollectorRefs& c,
beast::Journal jIn) 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) , consensus(s.clock(), *this, j)
, id{i} , id{i}
, key{id, 0} , key{id, 0}

View File

@@ -2,10 +2,11 @@ include(xrpl_add_test)
# Test requirements. # Test requirements.
find_package(doctest REQUIRED) find_package(doctest REQUIRED)
find_package(RapidJSON REQUIRED)
# Common library dependencies for the rest of the tests. # Common library dependencies for the rest of the tests.
add_library(xrpl.imports.test INTERFACE) 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. # One test for each module.
xrpl_add_test(basics) xrpl_add_test(basics)

View File

@@ -18,9 +18,8 @@
//============================================================================== //==============================================================================
#include <xrpl/basics/Log.h> #include <xrpl/basics/Log.h>
#include <xrpl/json/json_reader.h>
#include <xrpl/telemetry/JsonLogs.h>
#include <rapidjson/document.h>
#include <doctest/doctest.h> #include <doctest/doctest.h>
using namespace ripple; using namespace ripple;
@@ -108,8 +107,7 @@ TEST_CASE("Test format output")
TEST_CASE("Test format output when structured logs are enabled") TEST_CASE("Test format output when structured logs are enabled")
{ {
auto structuredJournal = std::make_unique<log::JsonStructuredJournal>(); beast::Journal::enableStructuredJournal();
beast::Journal::enableStructuredJournal(std::move(structuredJournal));
std::string output; std::string output;
Logs::format(output, "Message", beast::severities::kDebug, "Test"); 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") TEST_CASE("Enable json logs")
{ {
auto structuredJournal = std::make_unique<log::JsonStructuredJournal>();
std::stringstream logStream; std::stringstream logStream;
MockLogs logs{logStream, beast::severities::kAll}; MockLogs logs{logStream, beast::severities::kAll};
@@ -133,78 +129,429 @@ TEST_CASE("Enable json logs")
logStream.str(""); logStream.str("");
beast::Journal::enableStructuredJournal(std::move(structuredJournal)); beast::Journal::enableStructuredJournal();
logs.journal("Test").debug() << "\n"; logs.journal("Test").debug() << "\n";
Json::Reader reader; rapidjson::Document doc;
Json::Value jsonLog; doc.Parse(logStream.str().c_str());
bool result = reader.parse(logStream.str(), jsonLog);
CHECK(result); CHECK(doc.GetParseError() == rapidjson::ParseErrorCode::kParseErrorNone);
CHECK(jsonLog.isObject()); CHECK(doc.IsObject());
CHECK(jsonLog.isMember("Message")); CHECK(doc.HasMember("Message"));
CHECK(jsonLog["Message"].isString()); CHECK(doc["Message"].IsString());
CHECK(jsonLog["Message"].asString() == ""); CHECK(doc["Message"].GetString() == std::string{""});
beast::Journal::disableStructuredJournal(); beast::Journal::disableStructuredJournal();
} }
TEST_CASE("Global attributes") TEST_CASE("Global attributes")
{ {
auto structuredJournal = std::make_unique<log::JsonStructuredJournal>();
std::stringstream logStream; std::stringstream logStream;
MockLogs logs{logStream, beast::severities::kAll}; MockLogs logs{logStream, beast::severities::kAll};
beast::Journal::enableStructuredJournal(std::move(structuredJournal)); beast::Journal::enableStructuredJournal();
MockLogs::setGlobalAttributes(log::attributes({{"Field1", "Value1"}})); beast::Journal::addGlobalAttributes(log::attributes(log::attr("Field1", "Value1")));
logs.journal("Test").debug() << "Test"; logs.journal("Test").debug() << "Test";
Json::Reader reader; rapidjson::Document jsonLog;
Json::Value jsonLog; jsonLog.Parse(logStream.str().c_str());
bool result = reader.parse(logStream.str(), jsonLog);
CHECK(result); CHECK(jsonLog.GetParseError() == rapidjson::ParseErrorCode::kParseErrorNone);
CHECK(jsonLog.isObject()); CHECK(jsonLog.IsObject());
CHECK(jsonLog.isMember("Field1")); CHECK(jsonLog.HasMember("Field1"));
CHECK(jsonLog["Field1"].isString()); CHECK(jsonLog["Field1"].IsString());
CHECK(jsonLog["Field1"].asString() == "Value1"); CHECK(jsonLog["Field1"].GetString() == std::string{"Value1"});
beast::Journal::disableStructuredJournal(); beast::Journal::disableStructuredJournal();
} }
TEST_CASE("Global attributes inheritable") TEST_CASE("Global attributes inheritable")
{ {
auto structuredJournal = std::make_unique<log::JsonStructuredJournal>();
std::stringstream logStream; std::stringstream logStream;
MockLogs logs{logStream, beast::severities::kAll}; MockLogs logs{logStream, beast::severities::kAll};
beast::Journal::enableStructuredJournal(std::move(structuredJournal)); beast::Journal::enableStructuredJournal();
MockLogs::setGlobalAttributes(log::attributes({{"Field1", "Value1"}})); beast::Journal::addGlobalAttributes(log::attributes(log::attr("Field1", "Value1")));
logs.journal( logs.journal(
"Test", "Test",
log::attributes({{"Field1", "Value3"}, {"Field2", "Value2"}})) log::attributes(log::attr("Field1", "Value3"), log::attr("Field2", "Value2")))
.debug() .debug()
<< "Test"; << "Test";
Json::Reader reader; rapidjson::Document jsonLog;
Json::Value jsonLog; jsonLog.Parse(logStream.str().c_str());
bool result = reader.parse(logStream.str(), jsonLog);
CHECK(result); CHECK(jsonLog.GetParseError() == rapidjson::ParseErrorCode::kParseErrorNone);
CHECK(jsonLog.isObject()); CHECK(jsonLog.IsObject());
CHECK(jsonLog.isMember("Field1")); CHECK(jsonLog.HasMember("Field1"));
CHECK(jsonLog["Field1"].isString()); CHECK(jsonLog["Field1"].IsString());
// Field1 should be overwritten to Value3 // Field1 should be overwritten to Value3
CHECK(jsonLog["Field1"].asString() == "Value3"); CHECK(jsonLog["Field1"].GetString() == std::string{"Value3"});
CHECK(jsonLog["Field2"].isString()); CHECK(jsonLog["Field2"].IsString());
CHECK(jsonLog["Field2"].asString() == "Value2"); CHECK(jsonLog["Field2"].GetString() == std::string{"Value2"});
beast::Journal::disableStructuredJournal(); 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<std::uint64_t>::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<std::uint64_t>::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<std::uint64_t>::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<std::uint64_t>::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);
}

View File

@@ -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 <xrpl/basics/Log.h>
#include <xrpl/json/json_reader.h>
#include <xrpl/telemetry/JsonLogs.h>
#include <doctest/doctest.h>
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<log::JsonStructuredJournal>();
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<std::uint64_t>::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<std::uint64_t>::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);
}

View File

@@ -1,2 +0,0 @@
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include <doctest/doctest.h>

View File

@@ -1109,7 +1109,7 @@ RclConsensusLogger::RclConsensusLogger(
bool const validating, bool const validating,
beast::Journal j, beast::Journal j,
std::source_location location) 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) , location_(location)
{ {
if (!validating && !j.info()) if (!validating && !j.info())

View File

@@ -69,7 +69,6 @@
#include <xrpl/protocol/Protocol.h> #include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/STParsedJSON.h> #include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/resource/Fees.h> #include <xrpl/resource/Fees.h>
#include <xrpl/telemetry/JsonLogs.h>
#include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/predicate.hpp>
#include <boost/asio/steady_timer.hpp> #include <boost/asio/steady_timer.hpp>
@@ -835,8 +834,8 @@ public:
beast::Journal beast::Journal
journal( journal(
std::string const& name, std::string const& name,
std::unique_ptr<beast::Journal::StructuredLogAttributes> attributes = std::optional<beast::Journal::JsonLogAttributes> attributes =
{}) override; std::nullopt) override;
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
@@ -1220,9 +1219,9 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline)
<< ", Instance Cookie: " << ", Instance Cookie: "
<< log::param("InstanceCookie", instanceCookie_); << log::param("InstanceCookie", instanceCookie_);
Logs::setGlobalAttributes(log::attributes( beast::Journal::addGlobalAttributes(log::attributes(
{{"RippledVersion", BuildInfo::getFullVersionString()}, log::attr("RippledVersion", BuildInfo::getFullVersionString()),
{"InstanceCookie", to_string(instanceCookie_)}})); log::attr("InstanceCookie", to_string(instanceCookie_))));
if (numberOfThreads(*config_) < 2) if (numberOfThreads(*config_) < 2)
{ {
@@ -2176,7 +2175,7 @@ ApplicationImp::serverOkay(std::string& reason)
beast::Journal beast::Journal
ApplicationImp::journal( ApplicationImp::journal(
std::string const& name, std::string const& name,
std::unique_ptr<beast::Journal::StructuredLogAttributes> attributes) std::optional<beast::Journal::JsonLogAttributes> attributes)
{ {
return logs_->journal(name, std::move(attributes)); return logs_->journal(name, std::move(attributes));
} }

View File

@@ -260,8 +260,8 @@ public:
virtual beast::Journal virtual beast::Journal
journal( journal(
std::string const& name, std::string const& name,
std::unique_ptr<beast::Journal::StructuredLogAttributes> attributes = std::optional<beast::Journal::JsonLogAttributes> attributes =
{}) = 0; std::nullopt) = 0;
/* Returns the number of file descriptors the application needs */ /* Returns the number of file descriptors the application needs */
virtual int virtual int

View File

@@ -27,7 +27,6 @@
#include <xrpl/basics/Log.h> #include <xrpl/basics/Log.h>
#include <xrpl/beast/core/CurrentThreadName.h> #include <xrpl/beast/core/CurrentThreadName.h>
#include <xrpl/protocol/BuildInfo.h> #include <xrpl/protocol/BuildInfo.h>
#include <xrpl/telemetry/JsonLogs.h>
#include <boost/asio/io_context.hpp> #include <boost/asio/io_context.hpp>
#include <boost/process/v1/args.hpp> #include <boost/process/v1/args.hpp>
@@ -797,10 +796,10 @@ run(int argc, char** argv)
if (config->LOG_STYLE == LogStyle::Json) if (config->LOG_STYLE == LogStyle::Json)
{ {
auto structuredJournal = std::make_unique<log::JsonStructuredJournal>(); beast::Journal::enableStructuredJournal();
beast::Journal::enableStructuredJournal(std::move(structuredJournal)); beast::Journal::addGlobalAttributes(log::attributes(
Logs::setGlobalAttributes(log::attributes( log::attr("Application", "rippled"),
{{"Application", "rippled"}, {"NetworkID", config->NETWORK_ID}})); log::attr("NetworkID", config->NETWORK_ID)));
} }
auto logs = std::make_unique<Logs>(thresh); auto logs = std::make_unique<Logs>(thresh);

View File

@@ -1190,6 +1190,10 @@ NetworkOPsImp::strOperatingMode(OperatingMode const mode, bool const admin)
void void
NetworkOPsImp::submitTransaction(std::shared_ptr<STTx const> const& iTrans) NetworkOPsImp::submitTransaction(std::shared_ptr<STTx const> const& iTrans)
{ {
beast::Journal journal{
m_journal,
log::attributes(
log::attr("TransactionID", to_string(iTrans->getTransactionID())))};
if (isNeedNetworkLedger()) if (isNeedNetworkLedger())
{ {
// Nothing we can do if we've never been in sync // Nothing we can do if we've never been in sync
@@ -1253,6 +1257,9 @@ NetworkOPsImp::submitTransaction(std::shared_ptr<STTx const> const& iTrans)
bool bool
NetworkOPsImp::preProcessTransaction(std::shared_ptr<Transaction>& transaction) NetworkOPsImp::preProcessTransaction(std::shared_ptr<Transaction>& transaction)
{ {
beast::Journal journal{
m_journal,
log::attributes(log::attr("TransactionID", to_string(transaction->getID())))};
auto const newFlags = app_.getHashRouter().getFlags(transaction->getID()); auto const newFlags = app_.getHashRouter().getFlags(transaction->getID());
if ((newFlags & HashRouterFlags::BAD) != HashRouterFlags::UNDEFINED) if ((newFlags & HashRouterFlags::BAD) != HashRouterFlags::UNDEFINED)

View File

@@ -209,10 +209,10 @@ Transactor::Transactor(ApplyContext& ctx)
: ctx_(ctx) : ctx_(ctx)
, account_(ctx.tx.getAccountID(sfAccount)) , account_(ctx.tx.getAccountID(sfAccount))
, j_(ctx.journal, , j_(ctx.journal,
log::attributes({ log::attributes(
{"TransactionID", to_string(ctx_.tx.getTransactionID())}, log::attr("TransactionID", to_string(ctx_.tx.getTransactionID())),
{"AccountID", to_string(account_)}, log::attr("AccountID", to_string(account_))
})) ))
{ {
} }

View File

@@ -53,7 +53,11 @@ public:
, rules(rules_) , rules(rules_)
, flags(flags_) , flags(flags_)
, parentBatchId(parentBatchId_) , 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( XRPL_ASSERT(
(flags_ & tapBATCH) == tapBATCH, "Batch apply flag should be set"); (flags_ & tapBATCH) == tapBATCH, "Batch apply flag should be set");
@@ -101,7 +105,11 @@ public:
, flags(flags_) , flags(flags_)
, tx(tx_) , tx(tx_)
, parentBatchId(parentBatchId_) , 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( XRPL_ASSERT(
parentBatchId.has_value() == ((flags_ & tapBATCH) == tapBATCH), parentBatchId.has_value() == ((flags_ & tapBATCH) == tapBATCH),

View File

@@ -180,6 +180,13 @@ public:
} }
} }
template <class Closure>
Substitute<Closure>
forceWrap(Closure&& closure)
{
return {*this, std::forward<Closure>(closure)};
}
/** Wrap the passed closure with a reference counter. /** Wrap the passed closure with a reference counter.
@param closure Closure that accepts Args_t parameters and returns Ret_t. @param closure Closure that accepts Args_t parameters and returns Ret_t.

View File

@@ -25,7 +25,6 @@
#include <xrpl/beast/net/IPEndpoint.h> #include <xrpl/beast/net/IPEndpoint.h>
#include <xrpl/beast/utility/Journal.h> #include <xrpl/beast/utility/Journal.h>
#include <xrpl/protocol/SystemParameters.h> // VFALCO Breaks levelization #include <xrpl/protocol/SystemParameters.h> // VFALCO Breaks levelization
#include <xrpl/telemetry/JsonLogs.h>
#include <boost/filesystem.hpp> // VFALCO FIX: This include should not be here #include <boost/filesystem.hpp> // VFALCO FIX: This include should not be here

View File

@@ -98,6 +98,10 @@ JobQueue::Coro::resume()
} }
{ {
std::lock_guard lock(jq_.m_mutex); 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_; --jq_.nSuspend_;
} }
auto saved = detail::getLocalValues().release(); 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 // That said, since we're outside the Coro's stack, we need to
// decrement the nSuspend that the Coro's call to yield caused. // decrement the nSuspend that the Coro's call to yield caused.
std::lock_guard lock(jq_.m_mutex); 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_; --jq_.nSuspend_;
#ifndef NDEBUG #ifndef NDEBUG
finished_ = true; finished_ = true;

View File

@@ -304,9 +304,9 @@ JobQueue::stop()
// but there may still be some threads between the return of // but there may still be some threads between the return of
// `Job::doJob` and the return of `JobQueue::processTask`. That is why // `Job::doJob` and the return of `JobQueue::processTask`. That is why
// we must wait on the condition variable to make these assertions. // we must wait on the condition variable to make these assertions.
std::unique_lock<std::mutex> lock(m_mutex); std::unique_lock lock(m_mutex);
cv_.wait( cv_.wait(
lock, [this] { return m_processCount == 0 && m_jobSet.empty(); }); lock, [this] { return m_processCount == 0 && nSuspend_ == 0 && m_jobSet.empty(); });
XRPL_ASSERT( XRPL_ASSERT(
m_processCount == 0, m_processCount == 0,
"ripple::JobQueue::stop : all processes completed"); "ripple::JobQueue::stop : all processes completed");

View File

@@ -41,7 +41,7 @@ ConnectAttempt::ConnectAttempt(
: Child(overlay) : Child(overlay)
, app_(app) , app_(app)
, id_(id) , id_(id)
, journal_(journal, log::attributes({{"NodeID", id}})) , journal_(journal, log::attributes(log::attr("NodeID", id)))
, remote_endpoint_(remote_endpoint) , remote_endpoint_(remote_endpoint)
, usage_(usage) , usage_(usage)
, strand_(boost::asio::make_strand(io_context)) , strand_(boost::asio::make_strand(io_context))

View File

@@ -168,7 +168,7 @@ OverlayImpl::onHandoff(
endpoint_type remote_endpoint) endpoint_type remote_endpoint)
{ {
auto const id = next_id_++; 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; Handoff handoff;
if (processRequest(request, handoff)) if (processRequest(request, handoff))

View File

@@ -85,15 +85,15 @@ PeerImp::PeerImp(
, journal_( , journal_(
app_.journal("Peer"), app_.journal("Peer"),
log::attributes( log::attributes(
{{"NodeID", id}, log::attr("NodeID", id),
{"RemoteAddress", to_string(slot->remote_endpoint())}, log::attr("RemoteAddress", to_string(slot->remote_endpoint())),
{"PublicKey", toBase58(TokenType::NodePublic, publicKey)}})) log::attr("PublicKey", toBase58(TokenType::NodePublic, publicKey))))
, p_journal_( , p_journal_(
app_.journal("Protocol"), app_.journal("Protocol"),
log::attributes( log::attributes(
{{"NodeID", id}, log::attr("NodeID", id),
{"RemoteAddress", to_string(slot->remote_endpoint())}, log::attr("RemoteAddress", to_string(slot->remote_endpoint())),
{"PublicKey", toBase58(TokenType::NodePublic, publicKey)}})) log::attr("PublicKey", toBase58(TokenType::NodePublic, publicKey))))
, stream_ptr_(std::move(stream_ptr)) , stream_ptr_(std::move(stream_ptr))
, socket_(stream_ptr_->next_layer().socket()) , socket_(stream_ptr_->next_layer().socket())
, stream_(*stream_ptr_) , stream_(*stream_ptr_)
@@ -1392,6 +1392,12 @@ PeerImp::handleTransaction(
{ {
auto stx = std::make_shared<STTx const>(sit); auto stx = std::make_shared<STTx const>(sit);
uint256 txID = stx->getTransactionID(); 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 // Charge strongly for attempting to relay a txn with tfInnerBatchTxn
// LCOV_EXCL_START // LCOV_EXCL_START

View File

@@ -830,15 +830,15 @@ PeerImp::PeerImp(
, journal_( , journal_(
app_.journal("Peer"), app_.journal("Peer"),
log::attributes( log::attributes(
{{"NodeID", id}, log::attr("NodeID", id),
{"RemoteAddress", to_string(slot->remote_endpoint())}, log::attr("RemoteAddress", to_string(slot->remote_endpoint())),
{"PublicKey", toBase58(TokenType::NodePublic, publicKey)}})) log::attr("PublicKey", toBase58(TokenType::NodePublic, publicKey))))
, p_journal_( , p_journal_(
app_.journal("Protocol"), app_.journal("Protocol"),
log::attributes( log::attributes(
{{"NodeID", id}, log::attr("NodeID", id),
{"RemoteAddress", to_string(slot->remote_endpoint())}, log::attr("RemoteAddress", to_string(slot->remote_endpoint())),
{"PublicKey", toBase58(TokenType::NodePublic, publicKey)}})) log::attr("PublicKey", toBase58(TokenType::NodePublic, publicKey))))
, stream_ptr_(std::move(stream_ptr)) , stream_ptr_(std::move(stream_ptr))
, socket_(stream_ptr_->next_layer().socket()) , socket_(stream_ptr_->next_layer().socket())
, stream_(*stream_ptr_) , stream_(*stream_ptr_)