@@ -38,8 +38,6 @@ impl Factor for ObserveFactor {
38
38
& self ,
39
39
ctx : spin_factors:: PrepareContext < T , Self > ,
40
40
) -> 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?
43
41
let tracer = global:: tracer ( ctx. app_component ( ) . app . id ( ) . to_string ( ) ) ;
44
42
Ok ( InstanceState {
45
43
state : Arc :: new ( RwLock :: new ( State {
@@ -73,11 +71,10 @@ pub(crate) struct State {
73
71
/// A resource table that holds the guest spans.
74
72
pub ( crate ) guest_spans : table:: Table < GuestSpan > ,
75
73
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 .
77
75
///
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.
81
78
pub ( crate ) active_spans : IndexSet < u32 > ,
82
79
83
80
/// Id of the last span emitted from within the host before entering the guest.
@@ -93,13 +90,17 @@ pub struct GuestSpan {
93
90
pub inner : opentelemetry:: global:: BoxedSpan ,
94
91
}
95
92
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.
97
95
pub struct ObserveContext {
98
96
pub ( crate ) state : Option < Arc < RwLock < State > > > ,
99
97
}
100
98
101
99
impl 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.
103
104
pub fn from_prepare_context < T : RuntimeFactors , F : Factor > (
104
105
prepare_context : & mut PrepareContext < T , F > ,
105
106
) -> anyhow:: Result < Self > {
@@ -111,21 +112,45 @@ impl ObserveContext {
111
112
Ok ( Self { state } )
112
113
}
113
114
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.
117
139
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
119
142
let state = if let Some ( state) = self . state . as_ref ( ) {
120
143
state. read ( ) . unwrap ( )
121
144
} else {
122
145
return ;
123
146
} ;
124
147
148
+ // If there are no active guest spans then there is nothing to do
125
149
if state. active_spans . is_empty ( ) {
126
150
return ;
127
151
}
128
152
153
+ // Ensure that we are not reparenting the original host span
129
154
if let Some ( original_host_span_id) = state. original_host_span_id {
130
155
if tracing:: Span :: current ( )
131
156
. context ( )
@@ -134,19 +159,20 @@ impl ObserveContext {
134
159
. span_id ( )
135
160
. eq ( & original_host_span_id)
136
161
{
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." )
138
164
}
139
165
}
140
166
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 ) ;
150
176
tracing:: Span :: current ( ) . set_parent ( parent_context) ;
151
177
}
152
178
}
0 commit comments