dcontext_tracing/
async_layer.rs1use std::collections::HashSet;
29use std::marker::PhantomData;
30
31use tracing::Subscriber;
32use tracing_core::span;
33use tracing_subscriber::{layer::Context, registry::LookupSpan, Layer};
34
35use crate::field_mapping::ExtractedFields;
36use crate::layer_common;
37use crate::span_info::{SpanInfo, SPAN_INFO_KEY};
38use crate::tracing_field::get_tracing_fields;
39
40struct AsyncScopeState {
43 depth: usize,
46}
47
48pub struct AsyncDcontextLayer<S> {
68 include_span_info: bool,
69 _subscriber: PhantomData<fn(S)>,
70}
71
72impl<S> AsyncDcontextLayer<S> {
73 pub fn new() -> Self {
78 Self {
79 include_span_info: false,
80 _subscriber: PhantomData,
81 }
82 }
83
84 pub fn builder() -> AsyncDcontextLayerBuilder<S> {
86 AsyncDcontextLayerBuilder {
87 include_span_info: false,
88 _subscriber: PhantomData,
89 }
90 }
91}
92
93impl<S> Default for AsyncDcontextLayer<S> {
94 fn default() -> Self {
95 Self::new()
96 }
97}
98
99pub struct AsyncDcontextLayerBuilder<S> {
101 include_span_info: bool,
102 _subscriber: PhantomData<fn(S)>,
103}
104
105impl<S> AsyncDcontextLayerBuilder<S> {
106 pub fn include_span_info(mut self) -> Self {
111 self.include_span_info = true;
112 self
113 }
114
115 pub fn build(self) -> AsyncDcontextLayer<S> {
117 AsyncDcontextLayer {
118 include_span_info: self.include_span_info,
119 _subscriber: PhantomData,
120 }
121 }
122}
123
124use tracing_subscriber::registry::SpanRef;
127
128fn apply_field_extraction<S>(span: &SpanRef<'_, S>)
130where
131 S: for<'a> LookupSpan<'a>,
132{
133 let metadata_fields = get_tracing_fields();
134 if !metadata_fields.iter().any(|e| e.extract.is_some()) {
135 return;
136 }
137
138 let extensions = span.extensions();
139 let fields = match extensions.get::<ExtractedFields>() {
140 Some(f) => f,
141 None => return,
142 };
143
144 for entry in metadata_fields {
145 if let Some(ref extract) = entry.extract {
146 let value = if let Some(v) = fields.string_values.get(entry.span_field) {
147 extract.from_str.as_ref().and_then(|f| f(v))
148 } else if let Some(&v) = fields.u64_values.get(entry.span_field) {
149 extract.from_u64.as_ref().and_then(|f| f(v))
150 } else if let Some(&v) = fields.i64_values.get(entry.span_field) {
151 extract.from_i64.as_ref().and_then(|f| f(v))
152 } else if let Some(&v) = fields.bool_values.get(entry.span_field) {
153 extract.from_bool.as_ref().and_then(|f| f(v))
154 } else {
155 None
156 };
157
158 if let Some(val) = value {
159 dcontext::async_ctx::set_raw_value(entry.context_key, val);
160 }
161 }
162 }
163}
164
165fn set_span_info<S>(span: &SpanRef<'_, S>)
167where
168 S: for<'a> LookupSpan<'a>,
169{
170 let metadata = span.metadata();
171 let info = SpanInfo {
172 name: metadata.name().to_string(),
173 target: metadata.target().to_string(),
174 level: metadata.level().to_string(),
175 };
176 dcontext::async_ctx::set_context(SPAN_INFO_KEY, info);
177}
178
179fn record_context_to_span<S>(span: &SpanRef<'_, S>)
181where
182 S: for<'a> LookupSpan<'a>,
183{
184 let metadata_fields = get_tracing_fields();
185 if !metadata_fields.iter().any(|e| e.span_fmt_fn.is_some()) {
186 return;
187 }
188
189 let user_set: HashSet<&str> = {
190 let extensions = span.extensions();
191 extensions
192 .get::<ExtractedFields>()
193 .map(|ef| ef.user_set_fields.iter().copied().collect())
194 .unwrap_or_default()
195 };
196
197 let to_record: Vec<(&'static str, String)> = metadata_fields
198 .iter()
199 .filter_map(|entry| {
200 let fmt_fn = entry.span_fmt_fn.as_ref()?;
201 if user_set.contains(entry.record_field) {
202 return None;
203 }
204 let formatted = dcontext::async_ctx::with_context_value(entry.context_key, |any_val| {
205 fmt_fn(any_val)
206 })
207 .flatten()?;
208 Some((entry.record_field, formatted))
209 })
210 .collect();
211
212 if !to_record.is_empty() {
213 let current = tracing::Span::current();
214 layer_common::SELF_RECORDING.with(|f| f.set(true));
215 for (field_name, value) in &to_record {
216 current.record(*field_name, value.as_str());
217 }
218 layer_common::SELF_RECORDING.with(|f| f.set(false));
219 }
220}
221
222impl<S> Layer<S> for AsyncDcontextLayer<S>
225where
226 S: Subscriber + for<'a> LookupSpan<'a>,
227{
228 fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) {
229 if let Some(span) = ctx.span(id) {
230 layer_common::extract_span_fields(attrs, &span);
231 }
232 }
233
234 fn on_record(&self, id: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) {
235 if let Some(span) = ctx.span(id) {
236 layer_common::merge_recorded_fields(values, &span);
237 }
238 }
239
240 fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
241 let span = match ctx.span(id) {
242 Some(s) => s,
243 None => return,
244 };
245
246 {
248 let extensions = span.extensions();
249 if extensions.get::<AsyncScopeState>().is_some() {
250 return;
251 }
252 }
253
254 let name = span.metadata().name();
256 let guard = match dcontext::async_ctx::try_push_scope(name) {
257 Some(guard) => guard,
258 None => return,
259 };
260 let depth = guard.expected_depth();
261 std::mem::forget(guard);
262
263 apply_field_extraction(&span);
265
266 if self.include_span_info {
268 set_span_info(&span);
269 }
270
271 record_context_to_span(&span);
273
274 let mut extensions = span.extensions_mut();
276 extensions.insert(AsyncScopeState { depth });
277 }
278
279 fn on_exit(&self, _id: &span::Id, _ctx: Context<'_, S>) {
280 }
282
283 fn on_close(&self, id: span::Id, ctx: Context<'_, S>) {
284 let span = match ctx.span(&id) {
285 Some(s) => s,
286 None => return,
287 };
288
289 let state = {
290 let mut extensions = span.extensions_mut();
291 extensions.remove::<AsyncScopeState>()
292 };
293
294 if let Some(state) = state {
295 let current = dcontext::async_ctx::current_depth();
296 if current == Some(state.depth) {
297 dcontext::async_ctx::pop_scope(state.depth);
298 }
299 }
300
301 {
302 let mut extensions = span.extensions_mut();
303 extensions.remove::<ExtractedFields>();
304 }
305 }
306}