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

@@ -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,8 +506,7 @@ public:
operator=(Journal&& other) noexcept
{
m_sink = other.m_sink;
if (other.m_attributes)
m_attributes = std::move(other.m_attributes);
m_attributes = std::move(other.m_attributes);
return *this;
}
@@ -452,7 +522,7 @@ public:
stream(Severity level) const
{
return Stream(
m_attributes ? m_attributes->clone() : nullptr, *m_sink, level);
m_attributes, *m_sink, level);
}
/** Returns `true` if any message would be logged at this severity level.
@@ -470,10 +540,10 @@ public:
Stream
trace(std::source_location location = std::source_location::current()) const
{
if (m_structuredJournalImpl)
m_structuredJournalImpl->initMessageContext(location);
if (m_jsonLogsEnabled)
initMessageContext(location);
return {
m_attributes ? m_attributes->clone() : nullptr,
m_attributes,
*m_sink,
severities::kTrace};
}
@@ -481,10 +551,10 @@ public:
Stream
debug(std::source_location location = std::source_location::current()) const
{
if (m_structuredJournalImpl)
m_structuredJournalImpl->initMessageContext(location);
if (m_jsonLogsEnabled)
initMessageContext(location);
return {
m_attributes ? m_attributes->clone() : nullptr,
m_attributes,
*m_sink,
severities::kDebug};
}
@@ -492,10 +562,10 @@ public:
Stream
info(std::source_location location = std::source_location::current()) const
{
if (m_structuredJournalImpl)
m_structuredJournalImpl->initMessageContext(location);
if (m_jsonLogsEnabled)
initMessageContext(location);
return {
m_attributes ? m_attributes->clone() : nullptr,
m_attributes,
*m_sink,
severities::kInfo};
}
@@ -503,10 +573,12 @@ public:
Stream
warn(std::source_location location = std::source_location::current()) const
{
if (m_structuredJournalImpl)
m_structuredJournalImpl->initMessageContext(location);
const char* a = "a";
rapidjson::Value v{a, 1};
if (m_jsonLogsEnabled)
initMessageContext(location);
return {
m_attributes ? m_attributes->clone() : nullptr,
m_attributes,
*m_sink,
severities::kWarning};
}
@@ -514,10 +586,10 @@ public:
Stream
error(std::source_location location = std::source_location::current()) const
{
if (m_structuredJournalImpl)
m_structuredJournalImpl->initMessageContext(location);
if (m_jsonLogsEnabled)
initMessageContext(location);
return {
m_attributes ? m_attributes->clone() : nullptr,
m_attributes,
*m_sink,
severities::kError};
}
@@ -525,14 +597,25 @@ public:
Stream
fatal(std::source_location location = std::source_location::current()) const
{
if (m_structuredJournalImpl)
m_structuredJournalImpl->initMessageContext(location);
if (m_jsonLogsEnabled)
initMessageContext(location);
return {
m_attributes ? m_attributes->clone() : nullptr,
m_attributes,
*m_sink,
severities::kFatal};
}
/** @} */
static void
addGlobalAttributes(JsonLogAttributes globalLogAttributes)
{
std::lock_guard lock(globalLogAttributesMutex_);
if (!globalLogAttributes_)
{
globalLogAttributes_ = JsonLogAttributes{};
}
globalLogAttributes_ = JsonLogAttributes::combine(globalLogAttributes_->contextValues(), globalLogAttributes.contextValues());
}
};
#ifndef __INTELLISENSE__
@@ -548,7 +631,7 @@ static_assert(std::is_nothrow_destructible<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