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::{ExtractedFields, FieldExtractor};
8use crate::guard_stack;
9use crate::span_info::{SpanInfo, SPAN_INFO_KEY};
10use crate::tracing_field::get_tracing_fields;
11
12pub struct DcontextLayer<S> {
50 include_span_info: bool,
51 _subscriber: PhantomData<fn(S)>,
52}
53
54impl<S> DcontextLayer<S> {
55 pub fn new() -> Self {
60 Self {
61 include_span_info: false,
62 _subscriber: PhantomData,
63 }
64 }
65
66 pub fn builder() -> DcontextLayerBuilder<S> {
68 DcontextLayerBuilder {
69 include_span_info: false,
70 _subscriber: PhantomData,
71 }
72 }
73}
74
75impl<S> Default for DcontextLayer<S> {
76 fn default() -> Self {
77 Self::new()
78 }
79}
80
81pub struct DcontextLayerBuilder<S> {
83 include_span_info: bool,
84 _subscriber: PhantomData<fn(S)>,
85}
86
87impl<S> DcontextLayerBuilder<S> {
88 pub fn include_span_info(mut self) -> Self {
93 self.include_span_info = true;
94 self
95 }
96
97 pub fn build(self) -> DcontextLayer<S> {
99 DcontextLayer {
100 include_span_info: self.include_span_info,
101 _subscriber: PhantomData,
102 }
103 }
104}
105
106impl<S> Layer<S> for DcontextLayer<S>
107where
108 S: Subscriber + for<'a> LookupSpan<'a>,
109{
110 fn on_new_span(
111 &self,
112 attrs: &span::Attributes<'_>,
113 id: &span::Id,
114 ctx: Context<'_, S>,
115 ) {
116 let metadata_fields = get_tracing_fields();
117 let extract_names: Vec<&'static str> = metadata_fields
118 .iter()
119 .filter(|e| e.extract.is_some())
120 .map(|e| e.span_field)
121 .collect();
122
123 if extract_names.is_empty() {
124 return;
125 }
126
127 let span = match ctx.span(id) {
128 Some(s) => s,
129 None => return,
130 };
131
132 let mut extractor = FieldExtractor::new(&extract_names);
133 attrs.record(&mut extractor);
134
135 if !extractor.extracted.is_empty() {
136 let mut extensions = span.extensions_mut();
137 extensions.insert(extractor.extracted);
138 }
139 }
140
141 fn on_record(
142 &self,
143 id: &span::Id,
144 values: &span::Record<'_>,
145 ctx: Context<'_, S>,
146 ) {
147 let metadata_fields = get_tracing_fields();
148 let extract_names: Vec<&'static str> = metadata_fields
149 .iter()
150 .filter(|e| e.extract.is_some())
151 .map(|e| e.span_field)
152 .collect();
153
154 if extract_names.is_empty() {
155 return;
156 }
157
158 let span = match ctx.span(id) {
159 Some(s) => s,
160 None => return,
161 };
162
163 let mut extractor = FieldExtractor::new(&extract_names);
164 values.record(&mut extractor);
165
166 if !extractor.extracted.is_empty() {
167 let mut extensions = span.extensions_mut();
168 if let Some(existing) = extensions.get_mut::<ExtractedFields>() {
169 existing.string_values.extend(extractor.extracted.string_values);
170 existing.u64_values.extend(extractor.extracted.u64_values);
171 existing.i64_values.extend(extractor.extracted.i64_values);
172 existing.bool_values.extend(extractor.extracted.bool_values);
173 } else {
174 extensions.insert(extractor.extracted);
175 }
176 }
177 }
178
179 fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
180 dcontext::force_thread_local(|| {
181 let guard = if let Some(span) = ctx.span(id) {
183 let name = span.metadata().name();
184 dcontext::enter_named_scope(name)
185 } else {
186 dcontext::enter_scope()
187 };
188
189 let metadata_fields = get_tracing_fields();
191 if metadata_fields.iter().any(|e| e.extract.is_some()) {
192 if let Some(span) = ctx.span(id) {
193 let extensions = span.extensions();
194 if let Some(fields) = extensions.get::<ExtractedFields>() {
195 for entry in metadata_fields {
196 if let Some(ref extract) = entry.extract {
197 if let Some(v) = fields.string_values.get(entry.span_field) {
198 if let Some(ref f) = extract.from_str {
199 f(v, entry.context_key);
200 }
201 } else if let Some(&v) = fields.u64_values.get(entry.span_field) {
202 if let Some(ref f) = extract.from_u64 {
203 f(v, entry.context_key);
204 }
205 } else if let Some(&v) = fields.i64_values.get(entry.span_field) {
206 if let Some(ref f) = extract.from_i64 {
207 f(v, entry.context_key);
208 }
209 } else if let Some(&v) = fields.bool_values.get(entry.span_field) {
210 if let Some(ref f) = extract.from_bool {
211 f(v, entry.context_key);
212 }
213 }
214 }
215 }
216 }
217 }
218 }
219
220 if self.include_span_info {
222 if let Some(span) = ctx.span(id) {
223 let metadata = span.metadata();
224 let info = SpanInfo {
225 name: metadata.name().to_string(),
226 target: metadata.target().to_string(),
227 level: metadata.level().to_string(),
228 };
229 dcontext::set_context(SPAN_INFO_KEY, info);
230 }
231 }
232
233 guard_stack::push_guard(id, guard);
234 });
235 }
236
237 fn on_exit(&self, id: &span::Id, _ctx: Context<'_, S>) {
238 dcontext::force_thread_local(|| {
239 guard_stack::pop_guard(id);
240 });
241 }
242
243 fn on_close(&self, id: span::Id, ctx: Context<'_, S>) {
244 dcontext::force_thread_local(|| {
245 guard_stack::pop_guard(&id);
246 });
247
248 if let Some(span) = ctx.span(&id) {
249 let mut extensions = span.extensions_mut();
250 extensions.remove::<ExtractedFields>();
251 }
252 }
253}