Merge branch 'pratik/otel-phase8-log-correlation' into pratik/otel-phase9-metric-gap-fill

# Conflicts:
#	OpenTelemetryPlan/08-appendix.md
#	OpenTelemetryPlan/presentation.md
#	docker/telemetry/xrpld-telemetry.cfg
#	docs/telemetry-runbook.md
This commit is contained in:
Pratik Mankawde
2026-07-06 22:06:45 +01:00
42 changed files with 414 additions and 535 deletions

View File

@@ -0,0 +1,64 @@
#pragma once
/** Account-address redaction for telemetry span attributes.
Path-finding RPC handlers would otherwise emit the caller's raw
account addresses as span attributes. To keep plaintext addresses out
of the telemetry backend, they are hashed at the point of emission.
This header exposes a single pure helper that turns an address into a
short, stable, obfuscated token.
Data flow:
handler -> redactAccount(addr) -> span attribute -> OTLP export
The returned token is the first 16 hex characters (lowercase) of the
SHA-512Half digest of the address. It is deterministic (same address
always maps to the same token) so operators can still correlate spans
for a given account across nodes and restarts.
The hash is unsalted, so it is obfuscation, not a secrecy guarantee:
XRP account addresses are a public, enumerable set, so a determined
observer with the telemetry stream could rebuild the address->token
mapping. The goal here is to keep plaintext addresses out of traces
and dashboards, not to defend against a precomputation attack. A salt
is intentionally omitted because it would break cross-node/restart
correlation, which is the reason for hashing rather than dropping.
A second, independent hashing layer runs in the OpenTelemetry
Collector (an `attributes/hash` processor) as defense-in-depth for
any node that emits a raw value.
@note This function is pure and reentrant: it holds no global state,
performs no I/O, and is safe to call concurrently from any thread.
Usage example:
@code
#include <xrpl/telemetry/Redaction.h>
using namespace xrpl::telemetry;
span.setAttribute(
pathfind_span::attr::sourceAccount, redactAccount(src.asString()));
@endcode
Edge case (empty input yields empty output):
@code
assert(redactAccount("") == "");
@endcode
*/
#include <string>
#include <string_view>
namespace xrpl::telemetry {
/** Hash an account address into a short, stable, obfuscated token.
@param addr The account address to redact (e.g. an r-address).
@return The first 16 lowercase hex characters of sha512Half(addr),
or an empty string when @p addr is empty.
*/
[[nodiscard]] std::string
redactAccount(std::string_view addr);
} // namespace xrpl::telemetry

View File

@@ -51,9 +51,11 @@
* @endcode
*
* @code
* // Transactor::operator() uses span() with prefix + suffix:
* auto span = SpanGuard::span(
* TraceCategory::Transactions, seg::tx, tx_apply_span::op::transactor);
* // Transactor::operator() also uses hashSpan on the same txID so it
* // co-traces with preflight and preclaim under one trace_id:
* auto span = SpanGuard::hashSpan(
* TraceCategory::Transactions, tx_apply_span::transactor,
* txID.data(), txID.kBytes);
* span.setAttribute(tx_apply_span::attr::stage, tx_apply_span::val::apply);
* @endcode
*/
@@ -79,6 +81,9 @@ inline constexpr auto transactor = makeStr("transactor");
inline constexpr auto preflight = join(seg::tx, op::preflight);
/// "tx.preclaim" — full name for hashSpan() at the preclaim stage.
inline constexpr auto preclaim = join(seg::tx, op::preclaim);
/// "tx.transactor" — full name for hashSpan() at the apply stage. Shares the
/// txID-derived trace_id so it co-traces with tx.preflight and tx.preclaim.
inline constexpr auto transactor = join(seg::tx, op::transactor);
// ===== Attribute keys ======================================================