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

Conflict resolutions:

- docker/telemetry/xrpld-telemetry.cfg: relocation conflict. phase-9 had
  already moved [insight] to the end of the file with server=otel, so the
  incoming block was dropped rather than inserted. Keeping both would have
  produced two [insight] sections, which merge last-wins into a single
  effective section, silently reviving the bug this branch just fixed.
  phase-9's per-branch service_instance_id=xrpld-devnet is preserved.

- OpenTelemetryPlan/06-implementation-phases.md: kept both corrections.
  phase-9's "Tempo" is right (no Jaeger anywhere in the stack) and
  phase-8's "active, sampled span" is right: Log.cpp:328 injects only
  when spanCtx.IsValid() && spanCtx.IsSampled().

- OpenTelemetryPlan/09-data-collection-reference.md and
  docs/telemetry-runbook.md: kept phase-9's structured-metadata LogQL.
  The collector's filelog regex_parser already extracts partition,
  severity, trace_id and span_id, so phase-8's inline regexp forms are
  redundant, and a line filter matches the literal text in a message body.
This commit is contained in:
Pratik Mankawde
2026-08-15 17:52:42 +01:00
7 changed files with 90 additions and 26 deletions

View File

@@ -302,9 +302,9 @@ Logs::format(
}
#ifdef XRPL_ENABLE_TELEMETRY
// Inject OTel trace context when an active span exists on this thread.
// Checks the thread-local context value directly to avoid the heap
// allocation that GetSpan() performs on the no-span path.
// Inject OTel trace context when an active, sampled span exists on this
// thread. Checks the thread-local context value directly to avoid the
// heap allocation that GetSpan() performs on the no-span path.
{
auto context = opentelemetry::context::RuntimeContext::GetCurrent();
auto spanValue = context.GetValue(opentelemetry::trace::kSpanKey);
@@ -314,7 +314,18 @@ Logs::format(
auto span = opentelemetry::nostd::get<
opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span>>(spanValue);
auto spanCtx = span->GetContext();
if (spanCtx.IsValid())
// Require the sampled flag as well as a valid context. A dropped
// span still carries its parent's ids, so a valid context does
// not imply the span reaches the backend. An unsampled remote
// parent arrives either because an upstream node propagated
// sampled=0, or because a peer omitted trace_flags entirely and
// it defaults to 0 (TraceContextPropagator, TxTracing,
// ConsensusReceiveTracing). Either way the ParentBasedSampler
// drops the local span, while the tracer still returns a no-op
// span with a valid context.
// Logging those ids would advertise a trace that was never
// exported, leaving the log-to-trace link resolving to nothing.
if (spanCtx.IsValid() && spanCtx.IsSampled())
{
// Hex widths of a W3C trace context: 16-byte trace_id and
// 8-byte span_id render to 32 and 16 lowercase hex chars.