@@ -38,8 +38,6 @@ impl Factor for ObserveFactor {
3838 & self ,
3939 ctx : spin_factors:: PrepareContext < T , Self > ,
4040 ) -> anyhow:: Result < Self :: InstanceBuilder > {
41- // TODO(Lann): Is it fine that we're using BoxedTracer and BoxedSpan? Are there perf tradeoffs?
42- // TODO(Lann): Would it make sense to put the tracer on the app state and just hold a reference to it in the instance state?
4341 let tracer = global:: tracer ( ctx. app_component ( ) . app . id ( ) . to_string ( ) ) ;
4442 Ok ( InstanceState {
4543 state : Arc :: new ( RwLock :: new ( State {
@@ -73,11 +71,10 @@ pub(crate) struct State {
7371 /// A resource table that holds the guest spans.
7472 pub ( crate ) guest_spans : table:: Table < GuestSpan > ,
7573
76- /// A LIFO stack of guest spans that are currently active.
74+ /// A stack of resource ids for all the active guest spans. The topmost span is the active span .
7775 ///
78- /// Only a reference ID to the guest span is held here. The actual guest span must be looked up
79- /// in the `guest_spans` table using the reference ID.
80- /// TODO(Caleb): Fix comment
76+ /// When a guest span is ended it is removed from this stack (regardless of whether is the
77+ /// active span) and all other spans are shifted back to retain relative order.
8178 pub ( crate ) active_spans : IndexSet < u32 > ,
8279
8380 /// Id of the last span emitted from within the host before entering the guest.
@@ -93,13 +90,17 @@ pub struct GuestSpan {
9390 pub inner : opentelemetry:: global:: BoxedSpan ,
9491}
9592
96- /// TODO(Caleb): comment
93+ /// Manages access to the ObserveFactor state for the purpose of maintaining proper span
94+ /// parent/child relationships when WASI Observe spans are being created.
9795pub struct ObserveContext {
9896 pub ( crate ) state : Option < Arc < RwLock < State > > > ,
9997}
10098
10199impl ObserveContext {
102- /// TODO(Caleb): Comment
100+ /// Creates an [`ObserveContext`] from a [`PrepareContext`].
101+ ///
102+ /// If [`RuntimeFactors`] does not contain an [`ObserveFactor`], then calling
103+ /// [`ObserveContext::reparent_tracing_span`] will be a no-op.
103104 pub fn from_prepare_context < T : RuntimeFactors , F : Factor > (
104105 prepare_context : & mut PrepareContext < T , F > ,
105106 ) -> anyhow:: Result < Self > {
@@ -111,21 +112,45 @@ impl ObserveContext {
111112 Ok ( Self { state } )
112113 }
113114
114- /// TODO(Caleb): comment
115- /// Make sure to mention this should only be called from an instrumented function in a factor.
116- /// Make sure this is called before any awaits
115+ /// Reparents the current [tracing] span to be a child of the last active guest span.
116+ ///
117+ /// The observe factor enables guests to emit spans that should be part of the same trace as the
118+ /// host is producing for a request. Below is an example trace. A request is made to an app, a
119+ /// guest span is created and then the host is re-entered to fetch a key value.
120+ ///
121+ /// ```text
122+ /// | GET /... _________________________________|
123+ /// | execute_wasm_component foo ___________|
124+ /// | my_guest_span ___________________|
125+ /// | spin_key_value.get |
126+ /// ```
127+ ///
128+ /// Setting the guest spans parent as the host is trivially done. However, the more difficult
129+ /// task is having the host factor spans be children of the guest span.
130+ /// [`ObserveContext::reparent_tracing_span`] handles this by reparenting the current span to be
131+ /// a child of the last active guest span (which is tracked internally in the observe factor).
132+ ///
133+ /// Note that if the observe factor is not in your [`RuntimeFactors`] than this is effectively a
134+ /// no-op.
135+ ///
136+ /// This MUST only be called from a factor host implementation function that is instrumented.
137+ ///
138+ /// This MUST be called at the very start of the function before any awaits.
117139 pub fn reparent_tracing_span ( & self ) {
118- // TODO(Caleb): Refactor to be similar to start
140+ // If state is None then we want to return early b/c the factor doesn't depend on the
141+ // Observe factor and therefore there is nothing to do
119142 let state = if let Some ( state) = self . state . as_ref ( ) {
120143 state. read ( ) . unwrap ( )
121144 } else {
122145 return ;
123146 } ;
124147
148+ // If there are no active guest spans then there is nothing to do
125149 if state. active_spans . is_empty ( ) {
126150 return ;
127151 }
128152
153+ // Ensure that we are not reparenting the original host span
129154 if let Some ( original_host_span_id) = state. original_host_span_id {
130155 if tracing:: Span :: current ( )
131156 . context ( )
@@ -134,19 +159,20 @@ impl ObserveContext {
134159 . span_id ( )
135160 . eq ( & original_host_span_id)
136161 {
137- panic ! ( "TODO(Caleb): This should not happen" )
162+ // TODO(Caleb): Figure out why this occurs under load
163+ panic ! ( "The original host span was incorrectly reparented. Likely `reparent_tracing_span` was called in an incorrect location." )
138164 }
139165 }
140166
141- let parent_context = Context :: new ( ) . with_remote_span_context (
142- state
143- . guest_spans
144- . get ( * state. active_spans . last ( ) . unwrap ( ) )
145- . unwrap ( )
146- . inner
147- . span_context ( )
148- . clone ( ) ,
149- ) ;
167+ // Now reparent the current span to the last active guest span
168+ let span_context = state
169+ . guest_spans
170+ . get ( * state. active_spans . last ( ) . unwrap ( ) )
171+ . unwrap ( )
172+ . inner
173+ . span_context ( )
174+ . clone ( ) ;
175+ let parent_context = Context :: new ( ) . with_remote_span_context ( span_context ) ;
150176 tracing:: Span :: current ( ) . set_parent ( parent_context) ;
151177 }
152178}
0 commit comments