mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-27 17:10:46 +00:00
405 lines
8.8 KiB
C++
405 lines
8.8 KiB
C++
#include <xrpl/basics/Log.h>
|
|
|
|
#include <xrpl/basics/chrono.h>
|
|
#include <xrpl/beast/utility/Journal.h>
|
|
#include <xrpl/beast/utility/instrumentation.h>
|
|
|
|
#include <boost/algorithm/string/predicate.hpp>
|
|
#include <boost/filesystem/path.hpp>
|
|
|
|
#include <chrono>
|
|
#include <cstring>
|
|
#include <fstream>
|
|
#include <functional>
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace xrpl {
|
|
|
|
Logs::Sink::Sink(std::string partition, beast::Severity thresh, Logs& logs)
|
|
: beast::Journal::Sink(thresh, false), logs_(logs), partition_(std::move(partition))
|
|
{
|
|
}
|
|
|
|
void
|
|
Logs::Sink::write(beast::Severity level, std::string const& text)
|
|
{
|
|
if (level < threshold())
|
|
return;
|
|
|
|
logs_.write(level, partition_, text, console());
|
|
}
|
|
|
|
void
|
|
Logs::Sink::writeAlways(beast::Severity level, std::string const& text)
|
|
{
|
|
logs_.write(level, partition_, text, console());
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
Logs::File::File() : stream_(nullptr)
|
|
{
|
|
}
|
|
|
|
bool
|
|
Logs::File::isOpen() const noexcept
|
|
{
|
|
return stream_ != nullptr;
|
|
}
|
|
|
|
bool
|
|
Logs::File::open(boost::filesystem::path const& path)
|
|
{
|
|
close();
|
|
|
|
bool wasOpened = false;
|
|
|
|
// VFALCO TODO Make this work with Unicode file paths
|
|
std::unique_ptr<std::ofstream> stream =
|
|
std::make_unique<std::ofstream>(path.c_str(), std::fstream::app);
|
|
|
|
if (stream->good())
|
|
{
|
|
path_ = path;
|
|
|
|
stream_ = std::move(stream);
|
|
|
|
wasOpened = true;
|
|
}
|
|
|
|
return wasOpened;
|
|
}
|
|
|
|
bool
|
|
Logs::File::closeAndReopen()
|
|
{
|
|
close();
|
|
|
|
return open(path_);
|
|
}
|
|
|
|
void
|
|
Logs::File::close()
|
|
{
|
|
stream_ = nullptr;
|
|
}
|
|
|
|
void
|
|
Logs::File::write(char const* text)
|
|
{
|
|
if (stream_ != nullptr)
|
|
(*stream_) << text;
|
|
}
|
|
|
|
void
|
|
Logs::File::writeln(char const* text)
|
|
{
|
|
if (stream_ != nullptr)
|
|
{
|
|
(*stream_) << text;
|
|
(*stream_) << std::endl;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
Logs::Logs(beast::Severity thresh) : thresh_(thresh) // default severity
|
|
{
|
|
}
|
|
|
|
bool
|
|
Logs::open(boost::filesystem::path const& pathToLogFile)
|
|
{
|
|
return file_.open(pathToLogFile);
|
|
}
|
|
|
|
beast::Journal::Sink&
|
|
Logs::get(std::string const& name)
|
|
{
|
|
std::scoped_lock const lock(mutex_);
|
|
auto const result = sinks_.emplace(name, makeSink(name, thresh_));
|
|
return *result.first->second;
|
|
}
|
|
|
|
beast::Journal::Sink&
|
|
Logs::operator[](std::string const& name)
|
|
{
|
|
return get(name);
|
|
}
|
|
|
|
beast::Journal
|
|
Logs::journal(std::string const& name)
|
|
{
|
|
return beast::Journal(get(name));
|
|
}
|
|
|
|
beast::Severity
|
|
Logs::threshold() const
|
|
{
|
|
return thresh_;
|
|
}
|
|
|
|
void
|
|
Logs::threshold(beast::Severity thresh)
|
|
{
|
|
std::scoped_lock const lock(mutex_);
|
|
thresh_ = thresh;
|
|
for (auto& sink : sinks_)
|
|
sink.second->threshold(thresh);
|
|
}
|
|
|
|
std::vector<std::pair<std::string, std::string>>
|
|
Logs::partitionSeverities() const
|
|
{
|
|
std::vector<std::pair<std::string, std::string>> list;
|
|
std::scoped_lock const lock(mutex_);
|
|
list.reserve(sinks_.size());
|
|
for (auto const& [name, sink] : sinks_)
|
|
list.emplace_back(name, toString(sink->threshold()));
|
|
return list;
|
|
}
|
|
|
|
void
|
|
Logs::write(
|
|
beast::Severity level,
|
|
std::string const& partition,
|
|
std::string const& text,
|
|
bool console)
|
|
{
|
|
std::string s;
|
|
format(s, text, level, partition);
|
|
std::scoped_lock const lock(mutex_);
|
|
file_.writeln(s);
|
|
if (!silent_)
|
|
std::cerr << s << '\n';
|
|
// VFALCO TODO Fix console output
|
|
// if (console)
|
|
// out_.write_console(s);
|
|
}
|
|
|
|
std::string
|
|
Logs::rotate()
|
|
{
|
|
std::scoped_lock const lock(mutex_);
|
|
bool const wasOpened = file_.closeAndReopen();
|
|
if (wasOpened)
|
|
return "The log file was closed and reopened.";
|
|
return "The log file could not be closed and reopened.";
|
|
}
|
|
|
|
std::unique_ptr<beast::Journal::Sink>
|
|
Logs::makeSink(std::string const& name, beast::Severity threshold)
|
|
{
|
|
return std::make_unique<Sink>(name, threshold, *this);
|
|
}
|
|
|
|
std::string
|
|
Logs::toString(beast::Severity s)
|
|
{
|
|
switch (s)
|
|
{
|
|
case beast::Severity::Trace:
|
|
return "Trace";
|
|
case beast::Severity::Debug:
|
|
return "Debug";
|
|
case beast::Severity::Info:
|
|
return "Info";
|
|
case beast::Severity::Warning:
|
|
return "Warning";
|
|
case beast::Severity::Error:
|
|
return "Error";
|
|
case beast::Severity::Fatal:
|
|
return "Fatal";
|
|
// LCOV_EXCL_START
|
|
default:
|
|
UNREACHABLE("xrpl::Logs::toString : invalid severity");
|
|
return "Unknown";
|
|
// LCOV_EXCL_STOP
|
|
}
|
|
}
|
|
|
|
std::optional<beast::Severity>
|
|
Logs::fromString(std::string const& s)
|
|
{
|
|
if (boost::iequals(s, "trace"))
|
|
return beast::Severity::Trace;
|
|
|
|
if (boost::iequals(s, "debug"))
|
|
return beast::Severity::Debug;
|
|
|
|
if (boost::iequals(s, "info") || boost::iequals(s, "information"))
|
|
return beast::Severity::Info;
|
|
|
|
if (boost::iequals(s, "warn") || boost::iequals(s, "warning") || boost::iequals(s, "warnings"))
|
|
return beast::Severity::Warning;
|
|
|
|
if (boost::iequals(s, "error") || boost::iequals(s, "errors"))
|
|
return beast::Severity::Error;
|
|
|
|
if (boost::iequals(s, "fatal") || boost::iequals(s, "fatals"))
|
|
return beast::Severity::Fatal;
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
void
|
|
Logs::format(
|
|
std::string& output,
|
|
std::string const& message,
|
|
beast::Severity severity,
|
|
std::string const& partition)
|
|
{
|
|
output.reserve(message.size() + partition.size() + 100);
|
|
|
|
output = xrpl::to_string(std::chrono::system_clock::now());
|
|
|
|
output += " ";
|
|
if (!partition.empty())
|
|
output += partition + ":";
|
|
|
|
using beast::Severity;
|
|
switch (severity)
|
|
{
|
|
case Severity::Trace:
|
|
output += "TRC ";
|
|
break;
|
|
case Severity::Debug:
|
|
output += "DBG ";
|
|
break;
|
|
case Severity::Info:
|
|
output += "NFO ";
|
|
break;
|
|
case Severity::Warning:
|
|
output += "WRN ";
|
|
break;
|
|
case Severity::Error:
|
|
output += "ERR ";
|
|
break;
|
|
// LCOV_EXCL_START
|
|
default:
|
|
UNREACHABLE("xrpl::Logs::format : invalid severity");
|
|
[[fallthrough]];
|
|
// LCOV_EXCL_STOP
|
|
case Severity::Fatal:
|
|
output += "FTL ";
|
|
break;
|
|
}
|
|
|
|
output += message;
|
|
|
|
// Limit the maximum length of the output
|
|
if (output.size() > kMaximumMessageCharacters)
|
|
{
|
|
output.resize(kMaximumMessageCharacters - 3);
|
|
output += "...";
|
|
}
|
|
|
|
// Attempt to prevent sensitive information from appearing in log files by
|
|
// redacting it with asterisks.
|
|
auto scrubber = [&output](char const* token) {
|
|
auto first = output.find(token);
|
|
|
|
// If we have found the specified token, then attempt to isolate the
|
|
// sensitive data (it's enclosed by double quotes) and mask it off:
|
|
if (first != std::string::npos)
|
|
{
|
|
first = output.find('\"', first + std::strlen(token));
|
|
|
|
if (first != std::string::npos)
|
|
{
|
|
auto last = output.find('\"', ++first);
|
|
|
|
if (last == std::string::npos)
|
|
last = output.size();
|
|
|
|
output.replace(first, last - first, last - first, '*');
|
|
}
|
|
}
|
|
};
|
|
|
|
scrubber("\"seed\"");
|
|
scrubber("\"seed_hex\"");
|
|
scrubber("\"secret\"");
|
|
scrubber("\"master_key\"");
|
|
scrubber("\"master_seed\"");
|
|
scrubber("\"master_seed_hex\"");
|
|
scrubber("\"passphrase\"");
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
class DebugSink
|
|
{
|
|
private:
|
|
std::reference_wrapper<beast::Journal::Sink> sink_;
|
|
std::unique_ptr<beast::Journal::Sink> holder_;
|
|
std::mutex mtx_;
|
|
|
|
public:
|
|
DebugSink() : sink_(beast::Journal::getNullSink())
|
|
{
|
|
}
|
|
|
|
DebugSink(DebugSink const&) = delete;
|
|
DebugSink&
|
|
operator=(DebugSink const&) = delete;
|
|
|
|
DebugSink(DebugSink&&) = delete;
|
|
DebugSink&
|
|
operator=(DebugSink&&) = delete;
|
|
|
|
std::unique_ptr<beast::Journal::Sink>
|
|
set(std::unique_ptr<beast::Journal::Sink> sink)
|
|
{
|
|
std::scoped_lock const _(mtx_);
|
|
|
|
using std::swap;
|
|
swap(holder_, sink);
|
|
|
|
if (holder_)
|
|
{
|
|
sink_ = *holder_;
|
|
}
|
|
else
|
|
{
|
|
sink_ = beast::Journal::getNullSink();
|
|
}
|
|
|
|
return sink;
|
|
}
|
|
|
|
beast::Journal::Sink&
|
|
get()
|
|
{
|
|
std::scoped_lock const _(mtx_);
|
|
return sink_.get();
|
|
}
|
|
};
|
|
|
|
static DebugSink&
|
|
debugSink()
|
|
{
|
|
static DebugSink kInst;
|
|
return kInst;
|
|
}
|
|
|
|
std::unique_ptr<beast::Journal::Sink>
|
|
setDebugLogSink(std::unique_ptr<beast::Journal::Sink> sink)
|
|
{
|
|
return debugSink().set(std::move(sink));
|
|
}
|
|
|
|
beast::Journal
|
|
debugLog()
|
|
{
|
|
return beast::Journal(debugSink().get());
|
|
}
|
|
|
|
} // namespace xrpl
|