Merge branch 'pratik/otel-phase3-tx-tracing' into pratik/otel-phase4-consensus-tracing

This commit is contained in:
Pratik Mankawde
2026-07-21 21:55:25 +01:00
3 changed files with 69 additions and 26 deletions

View File

@@ -33,7 +33,8 @@
* | + freshRoot(cat, prefix, name) [static] |
* | + childSpan(name) : SpanGuard |
* | + linkedSpan(name) : SpanGuard |
* | + captureContext() : SpanContext |
* | + spanContext() : SpanContext |
* | + threadLocalContext() [static] |
* | + setAttribute(key, value) |
* | + setOk() / setError(desc) |
* | + addEvent(name) |
@@ -89,10 +90,10 @@
* #include <xrpld/rpc/detail/RpcSpanNames.h>
* using namespace xrpl::telemetry;
*
* // Thread A: create span and capture context
* // Thread A: create span and capture its own context as parent
* auto span = SpanGuard::span(
* TraceCategory::Rpc, rpc_span::prefix::rpc, rpc_span::op::process);
* auto ctx = span.captureContext();
* auto ctx = span.spanContext();
*
* // Thread B: create child with captured context
* auto child = SpanGuard::childSpan(rpc_span::op::process, ctx);
@@ -155,8 +156,9 @@
* span (no Scope), so it never binds to a thread-local context stack
* and may be moved to and destroyed on any thread. To make a span the
* ambient parent for child spans on a thread, wrap it in a
* ScopedSpanGuard. Use captureContext() to propagate the trace
* context across threads explicitly.
* ScopedSpanGuard. Use spanContext() to propagate this span's own
* context across threads explicitly, or threadLocalContext() to
* snapshot the thread's currently-active context.
*
* @note Move semantics: Move construction and move assignment both
* transfer ownership of the pimpl pointer. There is no Scope to
@@ -220,8 +222,10 @@ using EventAttribute = std::pair<std::string_view, std::string_view>;
* Opaque wrapper for an OTel context snapshot.
*
* Used to propagate trace context across threads. Created by
* SpanGuard::captureContext(), consumed by SpanGuard::childSpan()
* or SpanGuard::linkedSpan() with an explicit parent/link context.
* SpanGuard::spanContext() (the guard's own span) or
* SpanGuard::threadLocalContext() (the thread's current context),
* consumed by SpanGuard::childSpan() or SpanGuard::linkedSpan()
* with an explicit parent/link context.
*/
class SpanContext
{
@@ -342,7 +346,7 @@ public:
/**
* Create a child span parented to an explicit captured context.
* @param name Span name for the child.
* @param parentCtx Context captured via captureContext().
* @param parentCtx Context captured via spanContext().
* @return A new guard, or null if parentCtx is invalid.
*/
[[nodiscard]] static SpanGuard
@@ -418,16 +422,34 @@ public:
// --- Context capture -----------------------------------------------
/**
* Snapshot the current thread's OTel context for cross-thread use.
* @return An opaque SpanContext, or an invalid one if null guard.
* Return a SpanContext referring to THIS guard's own span, for use
* as an explicit parent in childSpan(name, ctx) across threads.
* Independent of the thread's active span (a SpanGuard is unscoped).
* Returns an invalid context if this guard is null.
* @return A SpanContext holding this guard's own span.
* @note This is NOT the thread's ambient context — use
* threadLocalContext() for that.
*/
[[nodiscard]] SpanContext
captureContext() const;
spanContext() const;
/**
* Snapshot the calling thread's CURRENTLY-ACTIVE OTel context (the
* ambient span on this thread's context stack), for cross-thread
* propagation. This is the OpenTelemetry "capture current context"
* operation: capture -> pass to another thread -> childSpan(name,
* ctx). Unlike spanContext() (which refers to a specific guard's own
* span), this reflects whatever span is active on THIS thread right
* now, or an invalid context if none is active.
* @return A SpanContext holding the thread's current context.
*/
[[nodiscard]] static SpanContext
threadLocalContext();
/**
* Extract raw trace context bytes from this span for propagation.
*
* Unlike captureContext() which captures the thread-local runtime
* Unlike threadLocalContext() which captures the thread-local runtime
* context, this method reads the span's own SpanContext directly.
* Safe to call from any thread that holds a reference to this guard.
*
@@ -568,7 +590,7 @@ public:
* | + linkedSpan(name) : ScopedSpanGuard |
* | + operator SpanGuard() && (handoff) |
* | + setAttribute / setOk / setError / ... |
* | + captureContext() / discard() |
* | + spanContext() / discard() |
* | + operator bool() |
* +--------------------------------------------+
* | hides (pimpl)
@@ -674,7 +696,7 @@ public:
* Create a scoped child span parented to an explicit captured
* context and activate it on this thread.
* @param name Span name for the child.
* @param parentCtx Context captured via captureContext().
* @param parentCtx Context captured via spanContext().
* @return A new scoped guard, or a null one if parentCtx is invalid.
*/
[[nodiscard]] static ScopedSpanGuard
@@ -712,11 +734,16 @@ public:
// --- Context capture -----------------------------------------------
/**
* Snapshot the current thread's OTel context for cross-thread use.
* @return An opaque SpanContext, or an invalid one if null guard.
* Return a SpanContext referring to the owned guard's own span, for
* use as an explicit parent in childSpan(name, ctx) across threads.
* Forwards to the owned SpanGuard's spanContext(). Returns an invalid
* context if this guard is null.
* @return A SpanContext holding the owned guard's own span.
* @note This is NOT the thread's ambient context — use
* SpanGuard::threadLocalContext() for that.
*/
[[nodiscard]] SpanContext
captureContext() const;
spanContext() const;
// --- Forwarding methods (delegate to the owned SpanGuard) ----------
@@ -868,7 +895,7 @@ public:
}
[[nodiscard]] SpanContext
captureContext() const
spanContext() const
{
return {};
}
@@ -884,6 +911,12 @@ public:
}
// NOLINTEND(readability-convert-member-functions-to-static)
[[nodiscard]] static SpanContext
threadLocalContext()
{
return {};
}
void
setAttribute(std::string_view, std::string_view)
{
@@ -986,7 +1019,7 @@ public:
}
[[nodiscard]] SpanContext
captureContext() const
spanContext() const
{
return {};
}

View File

@@ -74,8 +74,8 @@
*
* 4. Cross-thread context propagation:
* @code
* // Thread A: capture the active context while span is in scope
* auto ctx = parentGuard.captureContext();
* // Thread A: take a handle to the parent span's own context
* auto ctx = parentGuard.spanContext();
*
* // Thread B: create child span with explicit parent
* auto child = SpanGuard::childSpan(rpc_span::op::process, ctx);

View File

@@ -399,20 +399,30 @@ SpanGuard::hashSpan(
// ===== Context capture =====================================================
SpanContext
SpanGuard::captureContext() const
SpanGuard::spanContext() const
{
if (!impl_ || !impl_->span)
return {};
// Capture THIS guard's own span, not the thread's ambient span. A
// Refer to THIS guard's own span, not the thread's ambient span. A
// SpanGuard is unscoped (never pushed onto the thread-local stack), so
// RuntimeContext::GetCurrent() would return an unrelated ambient span.
// Building the context from impl_->span makes captureContext() correct
// Building the context from impl_->span makes spanContext() correct
// for every caller regardless of scoping, and lets childSpan(name, ctx)
// parent explicitly to this span across threads.
auto ctx = opentelemetry::context::Context{}.SetValue(otel_trace::kSpanKey, impl_->span);
return SpanContext(std::make_shared<SpanContext::Impl>(std::move(ctx)));
}
SpanContext
SpanGuard::threadLocalContext()
{
// Snapshot whatever span is currently active on THIS thread's context
// stack (the ambient context), independent of any guard. This is the
// OTel "capture current context" operation used for propagation.
auto ctx = opentelemetry::context::RuntimeContext::GetCurrent();
return SpanContext(std::make_shared<SpanContext::Impl>(std::move(ctx)));
}
TraceBytes
SpanGuard::getTraceBytes() const
{
@@ -679,9 +689,9 @@ operator SpanGuard() &&
// ===== ScopedSpanGuard context capture =====================================
SpanContext
ScopedSpanGuard::captureContext() const
ScopedSpanGuard::spanContext() const
{
return impl_->guard.captureContext();
return impl_->guard.spanContext();
}
// ===== ScopedSpanGuard forwarding methods ==================================