dcontext_tracing/
layer.rs1use std::marker::PhantomData;
2
3use tracing::Subscriber;
4use tracing_core::span;
5use tracing_subscriber::{layer::Context, registry::LookupSpan, Layer};
6
7use crate::field_mapping::{
8 ExtractedFields, FieldExtractor, FieldMapping, TypedFieldSetter,
9};
10use crate::guard_stack;
11use crate::span_info::{SpanInfo, SPAN_INFO_KEY};
12use crate::FromFieldValue;
13
14pub struct DcontextLayer<S> {
45 field_mappings: Vec<FieldMapping>,
46 include_span_info: bool,
47 _subscriber: PhantomData<fn(S)>,
48}
49
50impl<S> DcontextLayer<S> {
51 pub fn new() -> Self {
53 Self {
54 field_mappings: Vec::new(),
55 include_span_info: false,
56 _subscriber: PhantomData,
57 }
58 }
59
60 pub fn builder() -> DcontextLayerBuilder<S> {
62 DcontextLayerBuilder {
63 field_mappings: Vec::new(),
64 include_span_info: false,
65 _subscriber: PhantomData,
66 }
67 }
68}
69
70impl<S> Default for DcontextLayer<S> {
71 fn default() -> Self {
72 Self::new()
73 }
74}
75
76pub struct DcontextLayerBuilder<S> {
78 field_mappings: Vec<FieldMapping>,
79 include_span_info: bool,
80 _subscriber: PhantomData<fn(S)>,
81}
82
83impl<S> DcontextLayerBuilder<S> {
84 pub fn map_field<T: FromFieldValue>(mut self, field_name: &'static str) -> Self {
97 self.field_mappings.push(FieldMapping {
98 field_name,
99 context_key: field_name,
100 setter: Box::new(TypedFieldSetter::<T>::new()),
101 });
102 self
103 }
104
105 pub fn map_field_as<T: FromFieldValue>(
110 mut self,
111 field_name: &'static str,
112 context_key: &'static str,
113 ) -> Self {
114 self.field_mappings.push(FieldMapping {
115 field_name,
116 context_key,
117 setter: Box::new(TypedFieldSetter::<T>::new()),
118 });
119 self
120 }
121
122 pub fn include_span_info(mut self) -> Self {
127 self.include_span_info = true;
128 self
129 }
130
131 pub fn build(self) -> DcontextLayer<S> {
133 DcontextLayer {
134 field_mappings: self.field_mappings,
135 include_span_info: self.include_span_info,
136 _subscriber: PhantomData,
137 }
138 }
139}
140
141impl<S> Layer<S> for DcontextLayer<S>
142where
143 S: Subscriber + for<'a> LookupSpan<'a>,
144{
145 fn on_new_span(
146 &self,
147 attrs: &span::Attributes<'_>,
148 id: &span::Id,
149 ctx: Context<'_, S>,
150 ) {
151 if self.field_mappings.is_empty() {
152 return;
153 }
154
155 let span = match ctx.span(id) {
156 Some(s) => s,
157 None => return,
158 };
159
160 let field_names: Vec<&'static str> =
162 self.field_mappings.iter().map(|m| m.field_name).collect();
163 let mut extractor = FieldExtractor::new(&field_names);
164 attrs.record(&mut extractor);
165
166 if !extractor.extracted.is_empty() {
167 let mut extensions = span.extensions_mut();
168 extensions.insert(extractor.extracted);
169 }
170 }
171
172 fn on_record(
173 &self,
174 id: &span::Id,
175 values: &span::Record<'_>,
176 ctx: Context<'_, S>,
177 ) {
178 if self.field_mappings.is_empty() {
179 return;
180 }
181
182 let span = match ctx.span(id) {
183 Some(s) => s,
184 None => return,
185 };
186
187 let field_names: Vec<&'static str> =
188 self.field_mappings.iter().map(|m| m.field_name).collect();
189 let mut extractor = FieldExtractor::new(&field_names);
190 values.record(&mut extractor);
191
192 if !extractor.extracted.is_empty() {
193 let mut extensions = span.extensions_mut();
194 if let Some(existing) = extensions.get_mut::<ExtractedFields>() {
195 existing.string_values.extend(extractor.extracted.string_values);
197 existing.u64_values.extend(extractor.extracted.u64_values);
198 existing.i64_values.extend(extractor.extracted.i64_values);
199 existing.bool_values.extend(extractor.extracted.bool_values);
200 } else {
201 extensions.insert(extractor.extracted);
202 }
203 }
204 }
205
206 fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
207 dcontext::force_thread_local(|| {
211 let guard = if let Some(span) = ctx.span(id) {
214 let name = span.metadata().name();
215 dcontext::enter_named_scope(name)
216 } else {
217 dcontext::enter_scope()
218 };
219
220 if !self.field_mappings.is_empty() {
222 if let Some(span) = ctx.span(id) {
223 let extensions = span.extensions();
224 if let Some(fields) = extensions.get::<ExtractedFields>() {
225 for mapping in &self.field_mappings {
226 if let Some(v) = fields.string_values.get(mapping.field_name) {
228 mapping.setter.set_from_str(mapping.context_key, v);
229 } else if let Some(&v) = fields.u64_values.get(mapping.field_name) {
230 mapping.setter.set_from_u64(mapping.context_key, v);
231 } else if let Some(&v) = fields.i64_values.get(mapping.field_name) {
232 mapping.setter.set_from_i64(mapping.context_key, v);
233 } else if let Some(&v) = fields.bool_values.get(mapping.field_name) {
234 mapping.setter.set_from_bool(mapping.context_key, v);
235 }
236 }
237 }
238 }
239 }
240
241 if self.include_span_info {
243 if let Some(span) = ctx.span(id) {
244 let metadata = span.metadata();
245 let info = SpanInfo {
246 name: metadata.name().to_string(),
247 target: metadata.target().to_string(),
248 level: metadata.level().to_string(),
249 };
250 dcontext::set_context(SPAN_INFO_KEY, info);
251 }
252 }
253
254 guard_stack::push_guard(id, guard);
256 });
257 }
258
259 fn on_exit(&self, id: &span::Id, _ctx: Context<'_, S>) {
260 dcontext::force_thread_local(|| {
263 guard_stack::pop_guard(id);
264 });
265 }
266
267 fn on_close(&self, id: span::Id, ctx: Context<'_, S>) {
268 dcontext::force_thread_local(|| {
270 guard_stack::pop_guard(&id);
271 });
272
273 if let Some(span) = ctx.span(&id) {
275 let mut extensions = span.extensions_mut();
276 extensions.remove::<ExtractedFields>();
277 }
278 }
279}