merge: bring the review fixes forward from otel-phase8-log-correlation

This commit is contained in:
Pratik Mankawde
2026-09-08 15:51:07 +01:00
16 changed files with 701 additions and 120 deletions

View File

@@ -317,7 +317,9 @@ namespace event {
*/
inline constexpr auto disputeResolve = join(makeStr("dispute"), makeStr("resolve"));
/**
* "tx.included"
* "tx.included" — one per transaction of the agreed consensus set, recorded
* before the ledger is built. A transaction that then fails to apply still
* has an event, so this is a superset of the accepted ledger's contents.
*/
inline constexpr auto txIncluded = join(makeStr("tx"), makeStr("included"));

View File

@@ -103,6 +103,7 @@
#ifdef XRPL_ENABLE_TELEMETRY
#include <opentelemetry/context/context.h>
#include <opentelemetry/exporters/otlp/otlp_http_exporter_options.h>
#include <opentelemetry/metrics/meter.h>
#include <opentelemetry/nostd/shared_ptr.h>
#include <opentelemetry/trace/span.h>
@@ -150,6 +151,69 @@ inline constexpr auto kDefaultMetricExportInterval = std::chrono::milliseconds{1
*/
inline constexpr auto kDefaultMetricExportTimeout = std::chrono::milliseconds{500};
/**
* How a consensus round span picks its trace id.
*
* consensus_trace_strategy (xrpld.cfg)
* |
* v
* makeTelemetrySetup() ──> Setup::consensusTraceStrategy
* |
* v
* RCLConsensus::Adaptor::startRoundTracing()
* |
* +-- Deterministic ──> SpanGuard::hashSpan(prev ledger hash)
* +-- Random ──> SpanGuard::span() / linkedSpan()
*
* Deterministic is the strategy in use. Every validator of a round hashes the
* same previous ledger id, so all of them land in one trace.
*
* Random is experimental and not used. Each node would invent its own trace
* id, so one round would arrive as one trace per node, joinable only by the
* `consensus_ledger_id` attribute.
*
* @code
* // Branch on the strategy rather than on a string.
* if (telemetry.getConsensusTraceStrategy() == ConsensusTraceStrategy::Random)
* span = SpanGuard::span(TraceCategory::Consensus, seg::consensus, op::round);
* else
* span = SpanGuard::hashSpan(TraceCategory::Consensus, name, id.data(), id.kBytes);
*
* // Edge case: the value also goes on a span attribute, so it needs its
* // config spelling back.
* span.setAttribute(attr::traceStrategy, strategyName(ConsensusTraceStrategy::Random));
* @endcode
*
* @note Adding an enumerator means adding a spelling to strategyName() below
* and to the parser in TelemetryConfig.cpp. Both switch without a default, so
* the compiler catches a missed one.
*/
enum class ConsensusTraceStrategy : std::uint8_t { Deterministic, Random };
/**
* Config spelling of a consensus trace strategy.
*
* This is the same text `consensus_trace_strategy` accepts, and it is what
* goes on the `trace_strategy` span attribute, so the two cannot drift.
*
* @param strategy Strategy to name.
* @return "deterministic" or "random", pointing at a string literal.
*/
[[nodiscard]] constexpr char const*
strategyName(ConsensusTraceStrategy strategy)
{
switch (strategy)
{
case ConsensusTraceStrategy::Deterministic:
return "deterministic";
case ConsensusTraceStrategy::Random:
return "random";
}
// The switch covers every enumerator. This return only satisfies the
// compiler, which cannot rule out a value outside the enumeration.
return "deterministic";
}
class Telemetry
{
/**
@@ -338,12 +402,12 @@ public:
bool traceLedger = true;
/**
* Strategy for cross-node consensus trace correlation.
* "deterministic" — derive trace_id from ledger hash so all
* validators in the same round share the same trace_id.
* "attribute" — random trace_id, correlate via ledger_id attribute.
* How a consensus round span picks its trace id.
*
* Read from `consensus_trace_strategy`. Deterministic is the strategy
* in use; Random is experimental. See ConsensusTraceStrategy.
*/
std::string consensusTraceStrategy = "deterministic";
ConsensusTraceStrategy consensusTraceStrategy = ConsensusTraceStrategy::Deterministic;
};
virtual ~Telemetry() = default;
@@ -433,9 +497,9 @@ public:
shouldTraceLedger() const = 0;
/**
* @return The configured consensus trace correlation strategy.
* @return How a consensus round span picks its trace id.
*/
[[nodiscard]] virtual std::string const&
[[nodiscard]] virtual ConsensusTraceStrategy
getConsensusTraceStrategy() const = 0;
#ifdef XRPL_ENABLE_TELEMETRY
@@ -527,10 +591,12 @@ makeTelemetry(Telemetry::Setup const& setup, beast::Journal journal);
* @return A populated Setup struct with defaults for missing values.
* @throws std::runtime_error If `enabled` is set and the mutual TLS (mTLS)
* settings contradict each other: only one of `tls_client_cert`/`tls_client_key`
* is given, or a client certificate is given while `use_tls` is 0. Also if
* is given, a client certificate is given while `use_tls` is 0, or a client
* certificate is given while `traces_endpoint` is not an `https://` URL — which
* includes leaving `traces_endpoint` at its plain-HTTP default. Also if
* `enabled` and `use_tls` are both set and a non-empty `tls_ca_cert`,
* `tls_client_cert` or `tls_client_key` cannot be read; an empty path is skipped,
* so an empty `tls_ca_cert` still means "use the system CA store". All three
* so an empty `tls_ca_cert` still means "use the system CA store". All four
* checks are skipped when `enabled` is 0. Also if `metric_export_interval_ms`
* or `metric_export_timeout_ms` is unreadable, is not positive, or the timeout
* is not below the interval. Those three run whether telemetry is on or off.
@@ -558,4 +624,46 @@ makeTelemetrySetup(
[[nodiscard]] std::string
networkTypeFromId(std::uint32_t networkId);
#ifdef XRPL_ENABLE_TELEMETRY
/**
* Build the OTLP/HTTP trace exporter options that Setup asks for.
*
* Telemetry::Setup ──> makeTraceExporterOptions() ──> OtlpHttpExporterOptions
* |
* v
* OtlpHttpExporterFactory::Create
*
* Named and declared here rather than left inline in start() so the mapping
* from config to exporter options can be asserted directly. A swapped
* certificate and key, or a CA path written to the wrong field, is invisible
* from outside a running exporter.
*
* TLS fields are set only when `use_tls` is on. Whether the transport is
* actually encrypted is decided by the scheme of the URL, not by this function;
* makeTelemetrySetup() is what rejects a client certificate on a plain-HTTP
* endpoint.
*
* @code
* // Primary use: one-way TLS with a custom CA bundle.
* Telemetry::Setup setup;
* setup.useTls = true;
* setup.tlsCertPath = "/etc/ssl/ca.pem";
* auto const opts = makeTraceExporterOptions(setup); // ssl_ca_cert_path set
*
* // Edge case: use_tls off leaves every ssl_ field empty, even when paths
* // are configured.
* setup.useTls = false;
* auto const plain = makeTraceExporterOptions(setup); // ssl_ca_cert_path empty
* @endcode
*
* @param setup Parsed [telemetry] configuration.
* @return Options carrying `traces_endpoint` as the URL, plus the TLS paths
* when `use_tls` is on. Every other field keeps its SDK default.
* @note Pure: reads `setup` and touches no global state, so it is safe to call
* from any thread.
*/
[[nodiscard]] opentelemetry::exporter::otlp::OtlpHttpExporterOptions
makeTraceExporterOptions(Telemetry::Setup const& setup);
#endif
} // namespace xrpl::telemetry