dcontext_tracing/
sync_layer.rs1use std::collections::HashSet;
22use std::marker::PhantomData;
23
24use tracing::Subscriber;
25use tracing_core::span;
26use tracing_subscriber::{layer::Context, registry::LookupSpan, Layer};
27
28use crate::field_mapping::ExtractedFields;
29use crate::guard_stack;
30use crate::layer_common;
31use crate::span_info::{SpanInfo, SPAN_INFO_KEY};
32use crate::tracing_field::get_tracing_fields;
33
34struct SyncSkipMarker {
38 skip: bool,
40}
41
42pub struct SyncDcontextLayer<S> {
59 include_span_info: bool,
60 async_aware: bool,
62 _subscriber: PhantomData<fn(S)>,
63}
64
65impl<S> SyncDcontextLayer<S> {
66 pub fn new() -> Self {
68 Self {
69 include_span_info: false,
70 async_aware: false,
71 _subscriber: PhantomData,
72 }
73 }
74
75 pub fn builder() -> SyncDcontextLayerBuilder<S> {
77 SyncDcontextLayerBuilder {
78 include_span_info: false,
79 async_aware: false,
80 _subscriber: PhantomData,
81 }
82 }
83
84 fn should_skip_for_span<S2: Subscriber + for<'a> LookupSpan<'a>>(
86 &self,
87 id: &span::Id,
88 ctx: &Context<'_, S2>,
89 ) -> bool {
90 if !self.async_aware {
91 return false;
92 }
93
94 let span = match ctx.span(id) {
95 Some(s) => s,
96 None => return false,
97 };
98
99 let extensions = span.extensions();
100 if let Some(marker) = extensions.get::<SyncSkipMarker>() {
101 return marker.skip;
102 }
103 drop(extensions);
104
105 let skip = dcontext::async_ctx::current_depth().is_some();
106 let mut extensions = span.extensions_mut();
107 extensions.insert(SyncSkipMarker { skip });
108 skip
109 }
110}
111
112impl<S> Default for SyncDcontextLayer<S> {
113 fn default() -> Self {
114 Self::new()
115 }
116}
117
118pub struct SyncDcontextLayerBuilder<S> {
120 include_span_info: bool,
121 async_aware: bool,
122 _subscriber: PhantomData<fn(S)>,
123}
124
125impl<S> SyncDcontextLayerBuilder<S> {
126 pub fn include_span_info(mut self) -> Self {
128 self.include_span_info = true;
129 self
130 }
131
132 pub fn async_aware(mut self) -> Self {
134 self.async_aware = true;
135 self
136 }
137
138 pub fn build(self) -> SyncDcontextLayer<S> {
140 SyncDcontextLayer {
141 include_span_info: self.include_span_info,
142 async_aware: self.async_aware,
143 _subscriber: PhantomData,
144 }
145 }
146}
147
148use tracing_subscriber::registry::SpanRef;
151
152fn apply_field_extraction<S>(span: &SpanRef<'_, S>)
154where
155 S: for<'a> LookupSpan<'a>,
156{
157 let metadata_fields = get_tracing_fields();
158 if !metadata_fields.iter().any(|e| e.extract.is_some()) {
159 return;
160 }
161
162 let extensions = span.extensions();
163 let fields = match extensions.get::<ExtractedFields>() {
164 Some(f) => f,
165 None => return,
166 };
167
168 for entry in metadata_fields {
169 if let Some(ref extract) = entry.extract {
170 let value = if let Some(v) = fields.string_values.get(entry.span_field) {
171 extract.from_str.as_ref().and_then(|f| f(v))
172 } else if let Some(&v) = fields.u64_values.get(entry.span_field) {
173 extract.from_u64.as_ref().and_then(|f| f(v))
174 } else if let Some(&v) = fields.i64_values.get(entry.span_field) {
175 extract.from_i64.as_ref().and_then(|f| f(v))
176 } else if let Some(&v) = fields.bool_values.get(entry.span_field) {
177 extract.from_bool.as_ref().and_then(|f| f(v))
178 } else {
179 None
180 };
181
182 if let Some(val) = value {
183 dcontext::sync_ctx::set_raw_value(entry.context_key, val);
184 }
185 }
186 }
187}
188
189fn set_span_info<S>(span: &SpanRef<'_, S>)
191where
192 S: for<'a> LookupSpan<'a>,
193{
194 let metadata = span.metadata();
195 let info = SpanInfo {
196 name: metadata.name().to_string(),
197 target: metadata.target().to_string(),
198 level: metadata.level().to_string(),
199 };
200 dcontext::sync_ctx::set_context(SPAN_INFO_KEY, info);
201}
202
203fn record_context_to_span<S>(span: &SpanRef<'_, S>)
205where
206 S: for<'a> LookupSpan<'a>,
207{
208 let metadata_fields = get_tracing_fields();
209 if !metadata_fields.iter().any(|e| e.span_fmt_fn.is_some()) {
210 return;
211 }
212
213 let user_set: HashSet<&str> = {
214 let extensions = span.extensions();
215 extensions
216 .get::<ExtractedFields>()
217 .map(|ef| ef.user_set_fields.iter().copied().collect())
218 .unwrap_or_default()
219 };
220
221 let to_record: Vec<(&'static str, String)> = metadata_fields
222 .iter()
223 .filter_map(|entry| {
224 let fmt_fn = entry.span_fmt_fn.as_ref()?;
225 if user_set.contains(entry.record_field) {
226 return None;
227 }
228 let formatted = dcontext::sync_ctx::with_context_value(entry.context_key, |any_val| {
229 fmt_fn(any_val)
230 })
231 .flatten()?;
232 Some((entry.record_field, formatted))
233 })
234 .collect();
235
236 if !to_record.is_empty() {
237 let current = tracing::Span::current();
238 layer_common::SELF_RECORDING.with(|f| f.set(true));
239 for (field_name, value) in &to_record {
240 current.record(*field_name, value.as_str());
241 }
242 layer_common::SELF_RECORDING.with(|f| f.set(false));
243 }
244}
245
246impl<S> Layer<S> for SyncDcontextLayer<S>
249where
250 S: Subscriber + for<'a> LookupSpan<'a>,
251{
252 fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) {
253 if let Some(span) = ctx.span(id) {
254 layer_common::extract_span_fields(attrs, &span);
255 }
256 }
257
258 fn on_record(&self, id: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) {
259 if let Some(span) = ctx.span(id) {
260 layer_common::merge_recorded_fields(values, &span);
261 }
262 }
263
264 fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
265 if self.should_skip_for_span(id, &ctx) {
266 return;
267 }
268
269 let guard = if let Some(span) = ctx.span(id) {
271 let name = span.metadata().name();
272 dcontext::sync_ctx::push_scope(name)
273 } else {
274 dcontext::sync_ctx::push_scope("")
275 };
276
277 if let Some(span) = ctx.span(id) {
279 apply_field_extraction(&span);
280 }
281
282 if self.include_span_info {
284 if let Some(span) = ctx.span(id) {
285 set_span_info(&span);
286 }
287 }
288
289 if let Some(span) = ctx.span(id) {
291 record_context_to_span(&span);
292 }
293
294 guard_stack::push_guard(id, guard);
295 }
296
297 fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) {
298 if let Some(span) = ctx.span(id) {
299 let extensions = span.extensions();
300 if let Some(marker) = extensions.get::<SyncSkipMarker>() {
301 if marker.skip {
302 return;
303 }
304 }
305 }
306
307 guard_stack::pop_guard(id);
308 }
309
310 fn on_close(&self, id: span::Id, ctx: Context<'_, S>) {
311 if let Some(span) = ctx.span(&id) {
312 let extensions = span.extensions();
313 if let Some(marker) = extensions.get::<SyncSkipMarker>() {
314 if marker.skip {
315 return;
316 }
317 }
318 }
319
320 guard_stack::pop_guard(&id);
321
322 if let Some(span) = ctx.span(&id) {
323 let mut extensions = span.extensions_mut();
324 extensions.remove::<ExtractedFields>();
325 extensions.remove::<SyncSkipMarker>();
326 }
327 }
328}