mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Support structured logs
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
This commit is contained in:
@@ -167,6 +167,8 @@ private:
|
||||
beast::severities::Severity thresh_;
|
||||
File file_;
|
||||
bool silent_ = false;
|
||||
static std::unique_ptr<beast::Journal::StructuredLogAttributes>
|
||||
globalLogAttributes_;
|
||||
|
||||
public:
|
||||
Logs(beast::severities::Severity level);
|
||||
@@ -187,7 +189,10 @@ public:
|
||||
operator[](std::string const& name);
|
||||
|
||||
beast::Journal
|
||||
journal(std::string const& name);
|
||||
journal(
|
||||
std::string const& name,
|
||||
std::unique_ptr<beast::Journal::StructuredLogAttributes> attributes =
|
||||
{});
|
||||
|
||||
beast::severities::Severity
|
||||
threshold() const;
|
||||
@@ -224,6 +229,20 @@ 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);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
|
||||
#include <source_location>
|
||||
#include <sstream>
|
||||
|
||||
namespace beast {
|
||||
@@ -42,6 +43,9 @@ enum Severity {
|
||||
kDisabled,
|
||||
kNone = kDisabled
|
||||
};
|
||||
|
||||
std::string
|
||||
to_string(Severity severity);
|
||||
} // namespace severities
|
||||
|
||||
/** A generic endpoint for log messages.
|
||||
@@ -61,16 +65,68 @@ class Journal
|
||||
public:
|
||||
class Sink;
|
||||
|
||||
class StructuredJournalImpl;
|
||||
|
||||
class StructuredLogAttributes;
|
||||
|
||||
private:
|
||||
// Severity level / threshold of a Journal message.
|
||||
using Severity = severities::Severity;
|
||||
|
||||
std::unique_ptr<StructuredLogAttributes> m_attributes;
|
||||
|
||||
static StructuredJournalImpl* m_structuredJournalImpl;
|
||||
|
||||
// Invariant: m_sink always points to a valid Sink
|
||||
Sink* m_sink;
|
||||
Sink* m_sink = nullptr;
|
||||
|
||||
public:
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
static void
|
||||
enableStructuredJournal(StructuredJournalImpl* impl)
|
||||
{
|
||||
m_structuredJournalImpl = impl;
|
||||
}
|
||||
|
||||
static bool
|
||||
isStructuredJournalEnabled()
|
||||
{
|
||||
return m_structuredJournalImpl;
|
||||
}
|
||||
|
||||
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. */
|
||||
class Sink
|
||||
{
|
||||
@@ -150,16 +206,28 @@ public:
|
||||
{
|
||||
public:
|
||||
ScopedStream(ScopedStream const& other)
|
||||
: ScopedStream(other.m_sink, other.m_level)
|
||||
: ScopedStream(
|
||||
other.m_attributes ? other.m_attributes->clone() : nullptr,
|
||||
other.m_sink,
|
||||
other.m_level)
|
||||
{
|
||||
}
|
||||
|
||||
ScopedStream(Sink& sink, Severity level);
|
||||
ScopedStream(
|
||||
std::unique_ptr<StructuredLogAttributes> attributes,
|
||||
Sink& sink,
|
||||
Severity level);
|
||||
|
||||
template <typename T>
|
||||
ScopedStream(Stream const& stream, T const& t);
|
||||
ScopedStream(
|
||||
std::unique_ptr<StructuredLogAttributes> attributes,
|
||||
Stream const& stream,
|
||||
T const& t);
|
||||
|
||||
ScopedStream(Stream const& stream, std::ostream& manip(std::ostream&));
|
||||
ScopedStream(
|
||||
std::unique_ptr<StructuredLogAttributes> attributes,
|
||||
Stream const& stream,
|
||||
std::ostream& manip(std::ostream&));
|
||||
|
||||
ScopedStream&
|
||||
operator=(ScopedStream const&) = delete;
|
||||
@@ -180,6 +248,7 @@ public:
|
||||
operator<<(T const& t) const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<StructuredLogAttributes> m_attributes;
|
||||
Sink& m_sink;
|
||||
Severity const m_level;
|
||||
std::ostringstream mutable m_ostream;
|
||||
@@ -214,7 +283,11 @@ public:
|
||||
|
||||
Constructor is inlined so checking active() very inexpensive.
|
||||
*/
|
||||
Stream(Sink& sink, Severity level) : m_sink(sink), m_level(level)
|
||||
Stream(
|
||||
std::unique_ptr<StructuredLogAttributes> attributes,
|
||||
Sink& sink,
|
||||
Severity level)
|
||||
: m_attributes(std::move(attributes)), m_sink(sink), m_level(level)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
m_level < severities::kDisabled,
|
||||
@@ -222,7 +295,11 @@ public:
|
||||
}
|
||||
|
||||
/** Construct or copy another Stream. */
|
||||
Stream(Stream const& other) : Stream(other.m_sink, other.m_level)
|
||||
Stream(Stream const& other)
|
||||
: Stream(
|
||||
other.m_attributes ? other.m_attributes->clone() : nullptr,
|
||||
other.m_sink,
|
||||
other.m_level)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -269,6 +346,7 @@ public:
|
||||
/** @} */
|
||||
|
||||
private:
|
||||
std::unique_ptr<StructuredLogAttributes> m_attributes;
|
||||
Sink& m_sink;
|
||||
Severity m_level;
|
||||
};
|
||||
@@ -287,11 +365,92 @@ public:
|
||||
/** Journal has no default constructor. */
|
||||
Journal() = delete;
|
||||
|
||||
/** Create a journal that writes to the specified sink. */
|
||||
explicit Journal(Sink& sink) : m_sink(&sink)
|
||||
[[deprecated]]
|
||||
Journal(Journal const& other)
|
||||
: Journal(other, nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
Journal(
|
||||
Journal const& other,
|
||||
std::unique_ptr<StructuredLogAttributes> attributes)
|
||||
: m_sink(other.m_sink)
|
||||
{
|
||||
if (attributes)
|
||||
{
|
||||
m_attributes = std::move(attributes);
|
||||
}
|
||||
if (other.m_attributes)
|
||||
{
|
||||
if (m_attributes)
|
||||
{
|
||||
m_attributes->combine(other.m_attributes);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_attributes = other.m_attributes->clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
Sink& sink,
|
||||
std::string const& name = {},
|
||||
std::unique_ptr<StructuredLogAttributes> attributes = {})
|
||||
: m_sink(&sink)
|
||||
{
|
||||
if (attributes)
|
||||
{
|
||||
m_attributes = std::move(attributes);
|
||||
m_attributes->setModuleName(name);
|
||||
}
|
||||
}
|
||||
|
||||
Journal&
|
||||
operator=(Journal const& other)
|
||||
{
|
||||
m_sink = other.m_sink;
|
||||
if (other.m_attributes)
|
||||
{
|
||||
m_attributes = other.m_attributes->clone();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Journal&
|
||||
operator=(Journal&& other) noexcept
|
||||
{
|
||||
m_sink = other.m_sink;
|
||||
if (other.m_attributes)
|
||||
{
|
||||
m_attributes = std::move(other.m_attributes);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Returns the Sink associated with this Journal. */
|
||||
Sink&
|
||||
sink() const
|
||||
@@ -303,7 +462,8 @@ public:
|
||||
Stream
|
||||
stream(Severity level) const
|
||||
{
|
||||
return Stream(*m_sink, level);
|
||||
return Stream(
|
||||
m_attributes ? m_attributes->clone() : nullptr, *m_sink, level);
|
||||
}
|
||||
|
||||
/** Returns `true` if any message would be logged at this severity level.
|
||||
@@ -319,39 +479,81 @@ public:
|
||||
/** Severity stream access functions. */
|
||||
/** @{ */
|
||||
Stream
|
||||
trace() const
|
||||
trace(std::source_location location = std::source_location::current()) const
|
||||
{
|
||||
return {*m_sink, severities::kTrace};
|
||||
if (m_structuredJournalImpl)
|
||||
{
|
||||
m_structuredJournalImpl->initMessageContext(location);
|
||||
}
|
||||
return {
|
||||
m_attributes ? m_attributes->clone() : nullptr,
|
||||
*m_sink,
|
||||
severities::kTrace};
|
||||
}
|
||||
|
||||
Stream
|
||||
debug() const
|
||||
debug(std::source_location location = std::source_location::current()) const
|
||||
{
|
||||
return {*m_sink, severities::kDebug};
|
||||
if (m_structuredJournalImpl)
|
||||
{
|
||||
m_structuredJournalImpl->initMessageContext(location);
|
||||
}
|
||||
return {
|
||||
m_attributes ? m_attributes->clone() : nullptr,
|
||||
*m_sink,
|
||||
severities::kDebug};
|
||||
}
|
||||
|
||||
Stream
|
||||
info() const
|
||||
info(std::source_location location = std::source_location::current()) const
|
||||
{
|
||||
return {*m_sink, severities::kInfo};
|
||||
if (m_structuredJournalImpl)
|
||||
{
|
||||
m_structuredJournalImpl->initMessageContext(location);
|
||||
}
|
||||
return {
|
||||
m_attributes ? m_attributes->clone() : nullptr,
|
||||
*m_sink,
|
||||
severities::kInfo};
|
||||
}
|
||||
|
||||
Stream
|
||||
warn() const
|
||||
warn(std::source_location location = std::source_location::current()) const
|
||||
{
|
||||
return {*m_sink, severities::kWarning};
|
||||
if (m_structuredJournalImpl)
|
||||
{
|
||||
m_structuredJournalImpl->initMessageContext(location);
|
||||
}
|
||||
return {
|
||||
m_attributes ? m_attributes->clone() : nullptr,
|
||||
*m_sink,
|
||||
severities::kWarning};
|
||||
}
|
||||
|
||||
Stream
|
||||
error() const
|
||||
error(std::source_location location = std::source_location::current()) const
|
||||
{
|
||||
return {*m_sink, severities::kError};
|
||||
if (m_structuredJournalImpl)
|
||||
{
|
||||
m_structuredJournalImpl->initMessageContext(location);
|
||||
}
|
||||
return {
|
||||
m_attributes ? m_attributes->clone() : nullptr,
|
||||
*m_sink,
|
||||
severities::kError};
|
||||
}
|
||||
|
||||
Stream
|
||||
fatal() const
|
||||
fatal(std::source_location location = std::source_location::current()) const
|
||||
{
|
||||
return {*m_sink, severities::kFatal};
|
||||
if (m_structuredJournalImpl)
|
||||
{
|
||||
m_structuredJournalImpl->initMessageContext(location);
|
||||
}
|
||||
return {
|
||||
m_attributes ? m_attributes->clone() : nullptr,
|
||||
*m_sink,
|
||||
severities::kFatal};
|
||||
}
|
||||
/** @} */
|
||||
};
|
||||
@@ -368,8 +570,11 @@ static_assert(std::is_nothrow_destructible<Journal>::value == true, "");
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <typename T>
|
||||
Journal::ScopedStream::ScopedStream(Journal::Stream const& stream, T const& t)
|
||||
: ScopedStream(stream.sink(), stream.level())
|
||||
Journal::ScopedStream::ScopedStream(
|
||||
std::unique_ptr<StructuredLogAttributes> attributes,
|
||||
Stream const& stream,
|
||||
T const& t)
|
||||
: ScopedStream(std::move(attributes), stream.sink(), stream.level())
|
||||
{
|
||||
m_ostream << t;
|
||||
}
|
||||
@@ -388,7 +593,7 @@ template <typename T>
|
||||
Journal::ScopedStream
|
||||
Journal::Stream::operator<<(T const& t) const
|
||||
{
|
||||
return ScopedStream(*this, t);
|
||||
return {m_attributes ? m_attributes->clone() : nullptr, *this, t};
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
223
include/xrpl/logging/JsonLogs.h
Normal file
223
include/xrpl/logging/JsonLogs.h
Normal file
@@ -0,0 +1,223 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
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/basics/Log.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/json/Writer.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <source_location>
|
||||
#include <utility>
|
||||
|
||||
namespace ripple {
|
||||
namespace 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;
|
||||
|
||||
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 log
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <xrpl/resource/Fees.h>
|
||||
#include <xrpl/resource/Gossip.h>
|
||||
#include <xrpl/resource/detail/Import.h>
|
||||
#include <xrpl/telemetry/JsonLogs.h>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
@@ -132,7 +133,8 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
JLOG(m_journal.debug()) << "New inbound endpoint " << *entry;
|
||||
JLOG(m_journal.debug())
|
||||
<< "New inbound endpoint " << log::param("Entry", *entry);
|
||||
|
||||
return Consumer(*this, *entry);
|
||||
}
|
||||
@@ -160,7 +162,8 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
JLOG(m_journal.debug()) << "New outbound endpoint " << *entry;
|
||||
JLOG(m_journal.debug())
|
||||
<< "New outbound endpoint " << log::param("Entry", *entry);
|
||||
|
||||
return Consumer(*this, *entry);
|
||||
}
|
||||
@@ -193,7 +196,8 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
JLOG(m_journal.debug()) << "New unlimited endpoint " << *entry;
|
||||
JLOG(m_journal.debug())
|
||||
<< "New unlimited endpoint " << log::param("Entry", *entry);
|
||||
|
||||
return Consumer(*this, *entry);
|
||||
}
|
||||
@@ -350,7 +354,8 @@ public:
|
||||
{
|
||||
if (iter->whenExpires <= elapsed)
|
||||
{
|
||||
JLOG(m_journal.debug()) << "Expired " << *iter;
|
||||
JLOG(m_journal.debug())
|
||||
<< "Expired " << log::param("Entry", *iter);
|
||||
auto table_iter = table_.find(*iter->key);
|
||||
++iter;
|
||||
erase(table_iter);
|
||||
@@ -422,7 +427,9 @@ public:
|
||||
std::lock_guard _(lock_);
|
||||
if (--entry.refcount == 0)
|
||||
{
|
||||
JLOG(m_journal.debug()) << "Inactive " << entry;
|
||||
JLOG(m_journal.debug())
|
||||
<< "Inactive " << log::param("Entry", entry);
|
||||
;
|
||||
|
||||
switch (entry.key->kind)
|
||||
{
|
||||
@@ -474,7 +481,8 @@ public:
|
||||
clock_type::time_point const now(m_clock.now());
|
||||
int const balance(entry.add(fee.cost(), now));
|
||||
JLOG(getStream(fee.cost(), m_journal))
|
||||
<< "Charging " << entry << " for " << fee << context;
|
||||
<< "Charging " << log::param("Entry", entry) << " for "
|
||||
<< log::param("Fee", fee) << context;
|
||||
return disposition(balance);
|
||||
}
|
||||
|
||||
@@ -496,7 +504,9 @@ public:
|
||||
}
|
||||
if (notify)
|
||||
{
|
||||
JLOG(m_journal.info()) << "Load warning: " << entry;
|
||||
JLOG(m_journal.info())
|
||||
<< "Load warning: " << log::param("Entry", entry);
|
||||
;
|
||||
++m_stats.warn;
|
||||
}
|
||||
return notify;
|
||||
@@ -515,8 +525,10 @@ public:
|
||||
if (balance >= dropThreshold)
|
||||
{
|
||||
JLOG(m_journal.warn())
|
||||
<< "Consumer entry " << entry << " dropped with balance "
|
||||
<< balance << " at or above drop threshold " << dropThreshold;
|
||||
<< "Consumer entry " << log::param("Entry", entry)
|
||||
<< " dropped with balance " << log::param("Entry", balance)
|
||||
<< " at or above drop threshold "
|
||||
<< log::param("Entry", dropThreshold);
|
||||
|
||||
// Adding feeDrop at this point keeps the dropped connection
|
||||
// from re-connecting for at least a little while after it is
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#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>
|
||||
|
||||
@@ -47,7 +48,6 @@ protected:
|
||||
Port const& port_;
|
||||
Handler& handler_;
|
||||
endpoint_type remote_address_;
|
||||
beast::WrappedSink sink_;
|
||||
beast::Journal const j_;
|
||||
|
||||
boost::asio::executor_work_guard<boost::asio::executor> work_;
|
||||
@@ -84,15 +84,15 @@ BasePeer<Handler, Impl>::BasePeer(
|
||||
: port_(port)
|
||||
, handler_(handler)
|
||||
, remote_address_(remote_address)
|
||||
, sink_(
|
||||
journal.sink(),
|
||||
[] {
|
||||
static std::atomic<unsigned> id{0};
|
||||
return "##" + std::to_string(++id) + " ";
|
||||
}())
|
||||
, j_(sink_)
|
||||
, work_(executor)
|
||||
, strand_(executor)
|
||||
, j_(journal,
|
||||
log::attributes(
|
||||
{{"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))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
221
include/xrpl/telemetry/JsonLogs.h
Normal file
221
include/xrpl/telemetry/JsonLogs.h
Normal file
@@ -0,0 +1,221 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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 <iostream>
|
||||
#include <source_location>
|
||||
#include <utility>
|
||||
|
||||
namespace ripple {
|
||||
namespace 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;
|
||||
|
||||
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 log
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user