Performance improvement

This commit is contained in:
JCW
2025-08-28 20:36:46 +01:00
parent 6e88693caa
commit 257ea88396
32 changed files with 1039 additions and 983 deletions

View File

@@ -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)
@@ -133,7 +131,6 @@ target_link_modules(xrpl PUBLIC
beast
crypto
json
telemetry
protocol
resource
server

View File

@@ -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.server

View File

@@ -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 = [

View File

@@ -167,8 +167,6 @@ private:
beast::severities::Severity thresh_;
File file_;
bool silent_ = false;
static std::unique_ptr<beast::Journal::StructuredLogAttributes>
globalLogAttributes_;
public:
Logs(beast::severities::Severity level);
@@ -191,8 +189,8 @@ public:
beast::Journal
journal(
std::string const& name,
std::unique_ptr<beast::Journal::StructuredLogAttributes> attributes =
{});
std::optional<beast::Journal::JsonLogAttributes> 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<beast::Journal::StructuredLogAttributes>
globalLogAttributes)
{
if (!globalLogAttributes_)
{
globalLogAttributes_ = std::move(globalLogAttributes);
}
else
{
globalLogAttributes_->combine(std::move(globalLogAttributes));
}
}
public:
static LogSeverity
fromSeverity(beast::severities::Severity level);

View File

@@ -21,11 +21,62 @@
#define BEAST_UTILITY_JOURNAL_H_INCLUDED
#include <xrpl/beast/utility/instrumentation.h>
#include <rapidjson/document.h>
#include <memory>
#include <deque>
#include <utility>
#include <source_location>
#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 {
/** A namespace for easy access to logging severity values. */
@@ -64,75 +115,111 @@ to_string(Severity severity);
class Journal
{
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 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<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
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:
//--------------------------------------------------------------------------
static void
enableStructuredJournal(std::unique_ptr<StructuredJournalImpl> 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<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;
};
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<StructuredLogAttributes> attributes,
std::optional<JsonLogAttributes> attributes,
Sink& sink,
Severity level);
template <typename T>
ScopedStream(
std::unique_ptr<StructuredLogAttributes> attributes,
std::optional<JsonLogAttributes> attributes,
Stream const& stream,
T const& t);
ScopedStream(
std::unique_ptr<StructuredLogAttributes> attributes,
std::optional<JsonLogAttributes> attributes,
Stream const& stream,
std::ostream& manip(std::ostream&));
@@ -255,7 +342,7 @@ public:
operator<<(T const& t) const;
private:
std::unique_ptr<StructuredLogAttributes> m_attributes;
std::optional<JsonLogAttributes> 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<StructuredLogAttributes> attributes,
std::optional<JsonLogAttributes> 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<StructuredLogAttributes> m_attributes;
std::optional<JsonLogAttributes> 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<StructuredLogAttributes> attributes)
std::optional<JsonLogAttributes> 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<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. */
Journal(
explicit Journal(
Sink& sink,
std::string const& name = {},
std::unique_ptr<StructuredLogAttributes> attributes = {})
std::optional<JsonLogAttributes> 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,7 +506,6 @@ public:
operator=(Journal&& other) noexcept
{
m_sink = other.m_sink;
if (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<Journal>::value == true, "");
template <typename T>
Journal::ScopedStream::ScopedStream(
std::unique_ptr<StructuredLogAttributes> attributes,
std::optional<JsonLogAttributes> attributes,
Stream const& stream,
T const& t)
: ScopedStream(std::move(attributes), stream.sink(), stream.level())
@@ -570,7 +653,7 @@ template <typename T>
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<wchar_t>;
} // 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

View File

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

View File

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

View File

@@ -19,13 +19,22 @@
#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 <ostream>
#include <string>
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 "";
}
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)
: thresh_(thresh), m_console(console)
{
@@ -153,7 +340,7 @@ Journal::Sink::threshold(Severity thresh)
//------------------------------------------------------------------------------
Journal::ScopedStream::ScopedStream(
std::unique_ptr<StructuredLogAttributes> attributes,
std::optional<JsonLogAttributes> 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<StructuredLogAttributes> attributes,
std::optional<JsonLogAttributes> 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());
m_sink.write(m_level, formatLog("", m_level, m_attributes));
else
m_sink.write(m_level, "");
}
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

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);
}
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
run() override
{
correct_order();
incorrect_order();
thread_specific_storage();
// correct_order();
// incorrect_order();
// thread_specific_storage();
test_yield_and_stop();
}
};

View File

@@ -33,7 +33,6 @@
#include <xrpl/beast/utility/WrappedSink.h>
#include <xrpl/protocol/PublicKey.h>
#include <xrpl/telemetry/JsonLogs.h>
#include <boost/container/flat_map.hpp>
#include <boost/container/flat_set.hpp>
@@ -284,7 +283,7 @@ struct Peer
TrustGraph<Peer*>& 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}

View File

@@ -2,15 +2,14 @@ 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)
target_link_libraries(xrpl.test.basics PRIVATE xrpl.imports.test)
xrpl_add_test(crypto)
target_link_libraries(xrpl.test.crypto PRIVATE xrpl.imports.test)
xrpl_add_test(telemetry)
target_link_libraries(xrpl.test.telemetry PRIVATE xrpl.imports.test)

View File

@@ -18,9 +18,8 @@
//==============================================================================
#include <xrpl/basics/Log.h>
#include <xrpl/json/json_reader.h>
#include <xrpl/telemetry/JsonLogs.h>
#include <rapidjson/document.h>
#include <doctest/doctest.h>
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<log::JsonStructuredJournal>();
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<log::JsonStructuredJournal>();
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<log::JsonStructuredJournal>();
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<log::JsonStructuredJournal>();
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<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,
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())

View File

@@ -69,7 +69,6 @@
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/STParsedJSON.h>
#include <xrpl/resource/Fees.h>
#include <xrpl/telemetry/JsonLogs.h>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/asio/steady_timer.hpp>
@@ -836,8 +835,8 @@ public:
beast::Journal
journal(
std::string const& name,
std::unique_ptr<beast::Journal::StructuredLogAttributes> attributes =
{}) override;
std::optional<beast::Journal::JsonLogAttributes> attributes =
std::nullopt) override;
//--------------------------------------------------------------------------
@@ -1222,9 +1221,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)
{
@@ -2174,7 +2173,7 @@ ApplicationImp::serverOkay(std::string& reason)
beast::Journal
ApplicationImp::journal(
std::string const& name,
std::unique_ptr<beast::Journal::StructuredLogAttributes> attributes)
std::optional<beast::Journal::JsonLogAttributes> attributes)
{
return logs_->journal(name, std::move(attributes));
}

View File

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

View File

@@ -27,7 +27,6 @@
#include <xrpl/basics/Log.h>
#include <xrpl/beast/core/CurrentThreadName.h>
#include <xrpl/protocol/BuildInfo.h>
#include <xrpl/telemetry/JsonLogs.h>
#ifdef ENABLE_TESTS
#include <test/unit_test/multi_runner.h>
@@ -791,10 +790,10 @@ run(int argc, char** argv)
if (config->LOG_STYLE == LogStyle::Json)
{
auto structuredJournal = std::make_unique<log::JsonStructuredJournal>();
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<Logs>(thresh);

View File

@@ -1193,7 +1193,7 @@ NetworkOPsImp::submitTransaction(std::shared_ptr<STTx const> const& iTrans)
beast::Journal journal{
m_journal,
log::attributes(
{{"TransactionID", to_string(iTrans->getTransactionID())}})};
log::attr("TransactionID", to_string(iTrans->getTransactionID())))};
if (isNeedNetworkLedger())
{
// Nothing we can do if we've never been in sync
@@ -1260,7 +1260,7 @@ NetworkOPsImp::preProcessTransaction(std::shared_ptr<Transaction>& transaction)
{
beast::Journal journal{
m_journal,
log::attributes({{"TransactionID", to_string(transaction->getID())}})};
log::attributes(log::attr("TransactionID", to_string(transaction->getID())))};
auto const newFlags = app_.getHashRouter().getFlags(transaction->getID());
if ((newFlags & HashRouterFlags::BAD) != HashRouterFlags::UNDEFINED)

View File

@@ -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_))
))
{
}

View File

@@ -26,7 +26,6 @@
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/protocol/Permissions.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/telemetry/JsonLogs.h>
namespace ripple {
@@ -54,10 +53,10 @@ public:
, flags(flags_)
, parentBatchId(parentBatchId_)
, j(j_,
log::attributes({
{"TransactionID", to_string(tx.getTransactionID())},
{"AccountID", to_string(tx.getAccountID(sfAccount))},
}))
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");
@@ -106,10 +105,10 @@ public:
, tx(tx_)
, parentBatchId(parentBatchId_)
, j(j_,
log::attributes({
{"TransactionID", {to_string(tx.getTransactionID())}},
{"AccountID", to_string(tx.getAccountID(sfAccount))},
}))
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),

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.
@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/utility/Journal.h>
#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

View File

@@ -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;

View File

@@ -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<std::mutex> 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");

View File

@@ -39,7 +39,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_(io_service)

View File

@@ -165,7 +165,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))

View File

@@ -80,15 +80,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_)
@@ -1303,10 +1303,10 @@ PeerImp::handleTransaction(
uint256 txID = stx->getTransactionID();
beast::Journal protocolJournal{
p_journal_,
log::attributes({
{"TransactionID", to_string(txID)},
{"RawTransaction", strHex(m->rawtransaction())},
})};
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

View File

@@ -660,15 +660,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_)