dcontext_tracing/
layer.rs1use std::cell::Cell;
2use std::marker::PhantomData;
3
4use tracing::Subscriber;
5use tracing_core::span;
6use tracing_subscriber::{layer::Context, registry::LookupSpan, Layer};
7
8use crate::field_mapping::{ExtractedFields, FieldExtractor};
9use crate::guard_stack;
10use crate::span_info::{SpanInfo, SPAN_INFO_KEY};
11use crate::tracing_field::get_tracing_fields;
12
13thread_local! {
15 static SELF_RECORDING: Cell<bool> = const { Cell::new(false) };
16}
17
18pub struct DcontextLayer<S> {
56 include_span_info: bool,
57 _subscriber: PhantomData<fn(S)>,
58}
59
60impl<S> DcontextLayer<S> {
61 pub fn new() -> Self {
66 Self {
67 include_span_info: false,
68 _subscriber: PhantomData,
69 }
70 }
71
72 pub fn builder() -> DcontextLayerBuilder<S> {
74 DcontextLayerBuilder {
75 include_span_info: false,
76 _subscriber: PhantomData,
77 }
78 }
79}
80
81impl<S> Default for DcontextLayer<S> {
82 fn default() -> Self {
83 Self::new()
84 }
85}
86
87pub struct DcontextLayerBuilder<S> {
89 include_span_info: bool,
90 _subscriber: PhantomData<fn(S)>,
91}
92
93impl<S> DcontextLayerBuilder<S> {
94 pub fn include_span_info(mut self) -> Self {
99 self.include_span_info = true;
100 self
101 }
102
103 pub fn build(self) -> DcontextLayer<S> {
105 DcontextLayer {
106 include_span_info: self.include_span_info,
107 _subscriber: PhantomData,
108 }
109 }
110}
111
112impl<S> Layer<S> for DcontextLayer<S>
113where
114 S: Subscriber + for<'a> LookupSpan<'a>,
115{
116 fn on_new_span(
117 &self,
118 attrs: &span::Attributes<'_>,
119 id: &span::Id,
120 ctx: Context<'_, S>,
121 ) {
122 let metadata_fields = get_tracing_fields();
123
124 let extract_names: Vec<&'static str> = metadata_fields
126 .iter()
127 .filter(|e| e.extract.is_some())
128 .map(|e| e.span_field)
129 .collect();
130
131 let record_fields: Vec<&'static str> = metadata_fields
132 .iter()
133 .filter(|e| e.span_fmt_fn.is_some())
134 .map(|e| e.record_field)
135 .collect();
136
137 if extract_names.is_empty() && record_fields.is_empty() {
138 return;
139 }
140
141 let span = match ctx.span(id) {
142 Some(s) => s,
143 None => return,
144 };
145
146 let all_target: Vec<&'static str> = extract_names
148 .iter()
149 .chain(record_fields.iter())
150 .copied()
151 .collect();
152 let mut extractor = FieldExtractor::new(&all_target);
153 attrs.record(&mut extractor);
154
155 for &rf in &record_fields {
161 if extractor.extracted.string_values.contains_key(rf)
162 || extractor.extracted.u64_values.contains_key(rf)
163 || extractor.extracted.i64_values.contains_key(rf)
164 || extractor.extracted.bool_values.contains_key(rf)
165 {
166 extractor.extracted.mark_user_set(rf);
167 }
168 }
169
170 if !extractor.extracted.is_empty() || !extractor.extracted.user_set_fields.is_empty() {
173 let mut extensions = span.extensions_mut();
174 extensions.insert(extractor.extracted);
175 }
176 }
177
178 fn on_record(
179 &self,
180 id: &span::Id,
181 values: &span::Record<'_>,
182 ctx: Context<'_, S>,
183 ) {
184 if SELF_RECORDING.with(|f| f.get()) {
186 return;
187 }
188
189 let metadata_fields = get_tracing_fields();
190 let extract_names: Vec<&'static str> = metadata_fields
191 .iter()
192 .filter(|e| e.extract.is_some())
193 .map(|e| e.span_field)
194 .collect();
195
196 let record_fields: Vec<&'static str> = metadata_fields
197 .iter()
198 .filter(|e| e.span_fmt_fn.is_some())
199 .map(|e| e.record_field)
200 .collect();
201
202 if extract_names.is_empty() && record_fields.is_empty() {
203 return;
204 }
205
206 let span = match ctx.span(id) {
207 Some(s) => s,
208 None => return,
209 };
210
211 let all_target: Vec<&'static str> = extract_names
212 .iter()
213 .chain(record_fields.iter())
214 .copied()
215 .collect();
216 let mut extractor = FieldExtractor::new(&all_target);
217 values.record(&mut extractor);
218
219 if !extractor.extracted.is_empty() {
220 let mut extensions = span.extensions_mut();
221
222 for &rf in &record_fields {
224 if extractor.extracted.string_values.contains_key(rf)
225 || extractor.extracted.u64_values.contains_key(rf)
226 || extractor.extracted.i64_values.contains_key(rf)
227 || extractor.extracted.bool_values.contains_key(rf)
228 {
229 extractor.extracted.mark_user_set(rf);
230 }
231 }
232
233 if let Some(existing) = extensions.get_mut::<ExtractedFields>() {
234 existing.string_values.extend(extractor.extracted.string_values);
235 existing.u64_values.extend(extractor.extracted.u64_values);
236 existing.i64_values.extend(extractor.extracted.i64_values);
237 existing.bool_values.extend(extractor.extracted.bool_values);
238 existing.user_set_fields.extend(extractor.extracted.user_set_fields);
239 } else {
240 extensions.insert(extractor.extracted);
241 }
242 }
243 }
244
245 fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
246 dcontext::force_thread_local(|| {
247 let guard = if let Some(span) = ctx.span(id) {
249 let name = span.metadata().name();
250 dcontext::enter_named_scope(name)
251 } else {
252 dcontext::enter_scope()
253 };
254
255 let metadata_fields = get_tracing_fields();
257 if metadata_fields.iter().any(|e| e.extract.is_some()) {
258 if let Some(span) = ctx.span(id) {
259 let extensions = span.extensions();
260 if let Some(fields) = extensions.get::<ExtractedFields>() {
261 for entry in metadata_fields {
262 if let Some(ref extract) = entry.extract {
263 if let Some(v) = fields.string_values.get(entry.span_field) {
264 if let Some(ref f) = extract.from_str {
265 f(v, entry.context_key);
266 }
267 } else if let Some(&v) = fields.u64_values.get(entry.span_field) {
268 if let Some(ref f) = extract.from_u64 {
269 f(v, entry.context_key);
270 }
271 } else if let Some(&v) = fields.i64_values.get(entry.span_field) {
272 if let Some(ref f) = extract.from_i64 {
273 f(v, entry.context_key);
274 }
275 } else if let Some(&v) = fields.bool_values.get(entry.span_field) {
276 if let Some(ref f) = extract.from_bool {
277 f(v, entry.context_key);
278 }
279 }
280 }
281 }
282 }
283 }
284 }
285
286 if self.include_span_info {
288 if let Some(span) = ctx.span(id) {
289 let metadata = span.metadata();
290 let info = SpanInfo {
291 name: metadata.name().to_string(),
292 target: metadata.target().to_string(),
293 level: metadata.level().to_string(),
294 };
295 dcontext::set_context(SPAN_INFO_KEY, info);
296 }
297 }
298
299 if metadata_fields.iter().any(|e| e.span_fmt_fn.is_some()) {
304 let user_set: std::collections::HashSet<&str> = ctx
306 .span(id)
307 .and_then(|span| {
308 let extensions = span.extensions();
309 extensions
310 .get::<ExtractedFields>()
311 .map(|ef| ef.user_set_fields.iter().copied().collect())
312 })
313 .unwrap_or_default();
314
315 let to_record: Vec<(&'static str, String)> = metadata_fields
316 .iter()
317 .filter_map(|entry| {
318 let fmt_fn = entry.span_fmt_fn.as_ref()?;
319 if user_set.contains(entry.record_field) {
321 return None;
322 }
323 let formatted = dcontext::with_context_value(
324 entry.context_key,
325 |any_val| fmt_fn(any_val),
326 )
327 .flatten()?;
328 Some((entry.record_field, formatted))
329 })
330 .collect();
331
332 if !to_record.is_empty() {
333 let current = tracing::Span::current();
334 SELF_RECORDING.with(|f| f.set(true));
336 for (field_name, value) in &to_record {
337 current.record(*field_name, value.as_str());
338 }
339 SELF_RECORDING.with(|f| f.set(false));
340 }
341 }
342
343 guard_stack::push_guard(id, guard);
344 });
345 }
346
347 fn on_exit(&self, id: &span::Id, _ctx: Context<'_, S>) {
348 dcontext::force_thread_local(|| {
349 guard_stack::pop_guard(id);
350 });
351 }
352
353 fn on_close(&self, id: span::Id, ctx: Context<'_, S>) {
354 dcontext::force_thread_local(|| {
355 guard_stack::pop_guard(&id);
356 });
357
358 if let Some(span) = ctx.span(&id) {
359 let mut extensions = span.extensions_mut();
360 extensions.remove::<ExtractedFields>();
361 }
362 }
363}