//------------------------------------------------------------------------------ /* This file is part of rippled: https://github.com/ripple/rippled Copyright (c) 2025 Ripple Labs Inc. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ //============================================================================== #ifndef RIPPLE_LOGGING_STRUCTUREDJOURNAL_H_INCLUDED #define RIPPLE_LOGGING_STRUCTUREDJOURNAL_H_INCLUDED #include #include #include #include namespace ripple::log { template class LogParameter { public: template LogParameter(char const* name, TArg&& value) : name_(name), value_(std::forward(value)) { } private: char const* name_; T value_; template friend std::ostream& operator<<(std::ostream& os, LogParameter const&); }; template class LogField { public: template LogField(char const* name, TArg&& value) : name_(name), value_(std::forward(value)) { } private: char const* name_; T value_; template friend std::ostream& operator<<(std::ostream& os, LogField const&); }; class JsonLogAttributes : public beast::Journal::StructuredLogAttributes { public: using AttributeFields = std::unordered_map; using Pair = AttributeFields::value_type; explicit JsonLogAttributes(AttributeFields contextValues = {}); void setModuleName(std::string const& name) override; std::unique_ptr clone() const override; void combine(std::unique_ptr const& context) override; void combine(std::unique_ptr&& context) override; AttributeFields& contextValues() { return contextValues_; } private: AttributeFields contextValues_; }; class JsonStructuredJournal : public beast::Journal::StructuredJournalImpl { private: struct Logger { std::source_location location = {}; Json::Value messageParams; Logger() = default; Logger( JsonStructuredJournal const* journal, std::source_location location); void write( beast::Journal::Sink* sink, beast::severities::Severity level, std::string const& text, beast::Journal::StructuredLogAttributes* context) const; }; [[nodiscard]] Logger logger(std::source_location location) const; static thread_local Logger currentLogger_; template friend std::ostream& operator<<(std::ostream& os, LogParameter const&); template friend std::ostream& operator<<(std::ostream& os, LogField const&); public: void initMessageContext(std::source_location location) override; void flush( beast::Journal::Sink* sink, beast::severities::Severity level, std::string const& text, beast::Journal::StructuredLogAttributes* context) override; }; template std::ostream& operator<<(std::ostream& os, LogParameter const& param) { using ValueType = std::decay_t; // TODO: Update the Json library to support 64-bit integer values. if constexpr ( std::constructible_from && (!std::is_integral_v || sizeof(ValueType) <= sizeof(Json::Int))) { JsonStructuredJournal::currentLogger_.messageParams[param.name_] = Json::Value{param.value_}; return os << param.value_; } else { std::ostringstream oss; oss << param.value_; JsonStructuredJournal::currentLogger_.messageParams[param.name_] = oss.str(); return os << oss.str(); } } template std::ostream& operator<<(std::ostream& os, LogField const& param) { using ValueType = std::decay_t; // TODO: Update the Json library to support 64-bit integer values. if constexpr ( std::constructible_from && (!std::is_integral_v || sizeof(ValueType) <= sizeof(Json::Int))) { JsonStructuredJournal::currentLogger_.messageParams[param.name_] = Json::Value{param.value_}; } else { std::ostringstream oss; oss << param.value_; JsonStructuredJournal::currentLogger_.messageParams[param.name_] = oss.str(); } return os; } template LogParameter param(char const* name, T&& value) { return LogParameter{name, std::forward(value)}; } template LogField field(char const* name, T&& value) { return LogField{name, std::forward(value)}; } [[nodiscard]] inline std::unique_ptr attributes(std::initializer_list const& fields) { return std::make_unique(fields); } } // namespace ripple::log #endif