1 | //! Utilities for working with [fields] and [field visitors]. |
2 | //! |
3 | //! [fields]: tracing_core::field |
4 | //! [field visitors]: tracing_core::field::Visit |
5 | use core::{fmt, marker::PhantomData}; |
6 | pub use tracing_core::field::Visit; |
7 | use tracing_core::{ |
8 | span::{Attributes, Record}, |
9 | Event, |
10 | }; |
11 | pub mod debug; |
12 | pub mod delimited; |
13 | pub mod display; |
14 | |
15 | /// Creates new [visitors]. |
16 | /// |
17 | /// A type implementing `MakeVisitor` represents a composable factory for types |
18 | /// implementing the [`Visit` trait][visitors]. The `MakeVisitor` trait defines |
19 | /// a single function, `make_visitor`, which takes in a `T`-typed `target` and |
20 | /// returns a type implementing `Visit` configured for that target. A target may |
21 | /// be a string, output stream, or data structure that the visitor will record |
22 | /// data to, configuration variables that determine the visitor's behavior, or |
23 | /// `()` when no input is required to produce a visitor. |
24 | /// |
25 | /// [visitors]: tracing_core::field::Visit |
26 | pub trait MakeVisitor<T> { |
27 | /// The visitor type produced by this `MakeVisitor`. |
28 | type Visitor: Visit; |
29 | |
30 | /// Make a new visitor for the provided `target`. |
31 | fn make_visitor(&self, target: T) -> Self::Visitor; |
32 | } |
33 | |
34 | /// A [visitor] that produces output once it has visited a set of fields. |
35 | /// |
36 | /// [visitor]: tracing_core::field::Visit |
37 | pub trait VisitOutput<Out>: Visit { |
38 | /// Completes the visitor, returning any output. |
39 | /// |
40 | /// This is called once a full set of fields has been visited. |
41 | fn finish(self) -> Out; |
42 | |
43 | /// Visit a set of fields, and return the output of finishing the visitor |
44 | /// once the fields have been visited. |
45 | fn visit<R>(mut self, fields: &R) -> Out |
46 | where |
47 | R: RecordFields, |
48 | Self: Sized, |
49 | { |
50 | fields.record(&mut self); |
51 | self.finish() |
52 | } |
53 | } |
54 | |
55 | /// Extension trait implemented by types which can be recorded by a [visitor]. |
56 | /// |
57 | /// This allows writing code that is generic over `tracing_core`'s |
58 | /// [`span::Attributes`][attr], [`span::Record`][rec], and [`Event`][event] |
59 | /// types. These types all provide inherent `record` methods that allow a |
60 | /// visitor to record their fields, but there is no common trait representing this. |
61 | /// |
62 | /// With `RecordFields`, we can write code like this: |
63 | /// ``` |
64 | /// use tracing_core::field::Visit; |
65 | /// # use tracing_core::field::Field; |
66 | /// use tracing_subscriber::field::RecordFields; |
67 | /// |
68 | /// struct MyVisitor { |
69 | /// // ... |
70 | /// } |
71 | /// # impl MyVisitor { fn new() -> Self { Self{} } } |
72 | /// impl Visit for MyVisitor { |
73 | /// // ... |
74 | /// # fn record_debug(&mut self, _: &Field, _: &dyn std::fmt::Debug) {} |
75 | /// } |
76 | /// |
77 | /// fn record_with_my_visitor<R>(r: R) |
78 | /// where |
79 | /// R: RecordFields, |
80 | /// { |
81 | /// let mut visitor = MyVisitor::new(); |
82 | /// r.record(&mut visitor); |
83 | /// } |
84 | /// ``` |
85 | /// [visitor]: tracing_core::field::Visit |
86 | /// [attr]: tracing_core::span::Attributes |
87 | /// [rec]: tracing_core::span::Record |
88 | /// [event]: tracing_core::event::Event |
89 | pub trait RecordFields: crate::sealed::Sealed<RecordFieldsMarker> { |
90 | /// Record all the fields in `self` with the provided `visitor`. |
91 | fn record(&self, visitor: &mut dyn Visit); |
92 | } |
93 | |
94 | /// Extension trait implemented for all `MakeVisitor` implementations that |
95 | /// produce a visitor implementing `VisitOutput`. |
96 | pub trait MakeOutput<T, Out> |
97 | where |
98 | Self: MakeVisitor<T> + crate::sealed::Sealed<(T, Out)>, |
99 | Self::Visitor: VisitOutput<Out>, |
100 | { |
101 | /// Visits all fields in `fields` with a new visitor constructed from |
102 | /// `target`. |
103 | fn visit_with<F>(&self, target: T, fields: &F) -> Out |
104 | where |
105 | F: RecordFields, |
106 | { |
107 | self.make_visitor(target).visit(fields) |
108 | } |
109 | } |
110 | |
111 | feature! { |
112 | #![feature = "std" ] |
113 | use std::io; |
114 | |
115 | /// Extension trait implemented by visitors to indicate that they write to an |
116 | /// `io::Write` instance, and allow access to that writer. |
117 | pub trait VisitWrite: VisitOutput<Result<(), io::Error>> { |
118 | /// Returns the writer that this visitor writes to. |
119 | fn writer(&mut self) -> &mut dyn io::Write; |
120 | } |
121 | } |
122 | |
123 | /// Extension trait implemented by visitors to indicate that they write to a |
124 | /// `fmt::Write` instance, and allow access to that writer. |
125 | pub trait VisitFmt: VisitOutput<fmt::Result> { |
126 | /// Returns the formatter that this visitor writes to. |
127 | fn writer(&mut self) -> &mut dyn fmt::Write; |
128 | } |
129 | |
130 | /// Extension trait providing `MakeVisitor` combinators. |
131 | pub trait MakeExt<T> |
132 | where |
133 | Self: MakeVisitor<T> + Sized, |
134 | Self: crate::sealed::Sealed<MakeExtMarker<T>>, |
135 | { |
136 | /// Wraps `self` so that any `fmt::Debug` fields are recorded using the |
137 | /// alternate formatter (`{:#?}`). |
138 | fn debug_alt(self) -> debug::Alt<Self> { |
139 | debug::Alt::new(self) |
140 | } |
141 | |
142 | /// Wraps `self` so that any string fields named "message" are recorded |
143 | /// using `fmt::Display`. |
144 | fn display_messages(self) -> display::Messages<Self> { |
145 | display::Messages::new(self) |
146 | } |
147 | |
148 | /// Wraps `self` so that when fields are formatted to a writer, they are |
149 | /// separated by the provided `delimiter`. |
150 | fn delimited<D>(self, delimiter: D) -> delimited::Delimited<D, Self> |
151 | where |
152 | D: AsRef<str> + Clone, |
153 | Self::Visitor: VisitFmt, |
154 | { |
155 | delimited::Delimited::new(delimiter, self) |
156 | } |
157 | } |
158 | |
159 | // === impl RecordFields === |
160 | |
161 | impl<'a> crate::sealed::Sealed<RecordFieldsMarker> for Event<'a> {} |
162 | impl<'a> RecordFields for Event<'a> { |
163 | fn record(&self, visitor: &mut dyn Visit) { |
164 | Event::record(self, visitor) |
165 | } |
166 | } |
167 | |
168 | impl<'a> crate::sealed::Sealed<RecordFieldsMarker> for Attributes<'a> {} |
169 | impl<'a> RecordFields for Attributes<'a> { |
170 | fn record(&self, visitor: &mut dyn Visit) { |
171 | Attributes::record(self, visitor) |
172 | } |
173 | } |
174 | |
175 | impl<'a> crate::sealed::Sealed<RecordFieldsMarker> for Record<'a> {} |
176 | impl<'a> RecordFields for Record<'a> { |
177 | fn record(&self, visitor: &mut dyn Visit) { |
178 | Record::record(self, visitor) |
179 | } |
180 | } |
181 | |
182 | impl<'a, F> crate::sealed::Sealed<RecordFieldsMarker> for &'a F where F: RecordFields {} |
183 | impl<'a, F> RecordFields for &'a F |
184 | where |
185 | F: RecordFields, |
186 | { |
187 | fn record(&self, visitor: &mut dyn Visit) { |
188 | F::record(*self, visitor) |
189 | } |
190 | } |
191 | |
192 | // === blanket impls === |
193 | |
194 | impl<T, V, F> MakeVisitor<T> for F |
195 | where |
196 | F: Fn(T) -> V, |
197 | V: Visit, |
198 | { |
199 | type Visitor = V; |
200 | fn make_visitor(&self, target: T) -> Self::Visitor { |
201 | (self)(target) |
202 | } |
203 | } |
204 | |
205 | impl<T, Out, M> crate::sealed::Sealed<(T, Out)> for M |
206 | where |
207 | M: MakeVisitor<T>, |
208 | M::Visitor: VisitOutput<Out>, |
209 | { |
210 | } |
211 | |
212 | impl<T, Out, M> MakeOutput<T, Out> for M |
213 | where |
214 | M: MakeVisitor<T>, |
215 | M::Visitor: VisitOutput<Out>, |
216 | { |
217 | } |
218 | |
219 | impl<T, M> crate::sealed::Sealed<MakeExtMarker<T>> for M where M: MakeVisitor<T> + Sized {} |
220 | |
221 | impl<T, M> MakeExt<T> for M |
222 | where |
223 | M: MakeVisitor<T> + Sized, |
224 | M: crate::sealed::Sealed<MakeExtMarker<T>>, |
225 | { |
226 | } |
227 | |
228 | #[derive (Debug)] |
229 | #[doc (hidden)] |
230 | pub struct MakeExtMarker<T> { |
231 | _p: PhantomData<T>, |
232 | } |
233 | |
234 | #[derive (Debug)] |
235 | #[doc (hidden)] |
236 | pub struct RecordFieldsMarker { |
237 | _p: (), |
238 | } |
239 | |
240 | #[cfg (all(test, feature = "alloc" ))] |
241 | #[macro_use ] |
242 | pub(in crate::field) mod test_util { |
243 | use super::*; |
244 | pub(in crate::field) use alloc::string::String; |
245 | use tracing_core::{ |
246 | callsite::Callsite, |
247 | field::{Field, Value}, |
248 | metadata::{Kind, Level, Metadata}, |
249 | }; |
250 | |
251 | pub(crate) struct TestAttrs1; |
252 | pub(crate) struct TestAttrs2; |
253 | |
254 | impl TestAttrs1 { |
255 | pub(crate) fn with<T>(f: impl FnOnce(Attributes<'_>) -> T) -> T { |
256 | let fieldset = TEST_META_1.fields(); |
257 | let values = &[ |
258 | ( |
259 | &fieldset.field("question" ).unwrap(), |
260 | Some(&"life, the universe, and everything" as &dyn Value), |
261 | ), |
262 | (&fieldset.field("question.answer" ).unwrap(), None), |
263 | ( |
264 | &fieldset.field("tricky" ).unwrap(), |
265 | Some(&true as &dyn Value), |
266 | ), |
267 | ( |
268 | &fieldset.field("can_you_do_it" ).unwrap(), |
269 | Some(&true as &dyn Value), |
270 | ), |
271 | ]; |
272 | let valueset = fieldset.value_set(values); |
273 | let attrs = tracing_core::span::Attributes::new(&TEST_META_1, &valueset); |
274 | f(attrs) |
275 | } |
276 | } |
277 | |
278 | impl TestAttrs2 { |
279 | pub(crate) fn with<T>(f: impl FnOnce(Attributes<'_>) -> T) -> T { |
280 | let fieldset = TEST_META_1.fields(); |
281 | let none = tracing_core::field::debug(&Option::<&str>::None); |
282 | let values = &[ |
283 | ( |
284 | &fieldset.field("question" ).unwrap(), |
285 | Some(&none as &dyn Value), |
286 | ), |
287 | ( |
288 | &fieldset.field("question.answer" ).unwrap(), |
289 | Some(&42 as &dyn Value), |
290 | ), |
291 | ( |
292 | &fieldset.field("tricky" ).unwrap(), |
293 | Some(&true as &dyn Value), |
294 | ), |
295 | ( |
296 | &fieldset.field("can_you_do_it" ).unwrap(), |
297 | Some(&false as &dyn Value), |
298 | ), |
299 | ]; |
300 | let valueset = fieldset.value_set(values); |
301 | let attrs = tracing_core::span::Attributes::new(&TEST_META_1, &valueset); |
302 | f(attrs) |
303 | } |
304 | } |
305 | |
306 | struct TestCallsite1; |
307 | static TEST_CALLSITE_1: &'static dyn Callsite = &TestCallsite1; |
308 | static TEST_META_1: Metadata<'static> = tracing_core::metadata! { |
309 | name: "field_test1" , |
310 | target: module_path!(), |
311 | level: Level::INFO, |
312 | fields: &["question" , "question.answer" , "tricky" , "can_you_do_it" ], |
313 | callsite: TEST_CALLSITE_1, |
314 | kind: Kind::SPAN, |
315 | }; |
316 | |
317 | impl Callsite for TestCallsite1 { |
318 | fn set_interest(&self, _: tracing_core::subscriber::Interest) { |
319 | unimplemented!() |
320 | } |
321 | |
322 | fn metadata(&self) -> &Metadata<'_> { |
323 | &TEST_META_1 |
324 | } |
325 | } |
326 | |
327 | pub(crate) struct MakeDebug; |
328 | pub(crate) struct DebugVisitor<'a> { |
329 | writer: &'a mut dyn fmt::Write, |
330 | err: fmt::Result, |
331 | } |
332 | |
333 | impl<'a> DebugVisitor<'a> { |
334 | pub(crate) fn new(writer: &'a mut dyn fmt::Write) -> Self { |
335 | Self { |
336 | writer, |
337 | err: Ok(()), |
338 | } |
339 | } |
340 | } |
341 | |
342 | impl<'a> Visit for DebugVisitor<'a> { |
343 | fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { |
344 | write!(self.writer, " {}= {:?}" , field, value).unwrap(); |
345 | } |
346 | } |
347 | |
348 | impl<'a> VisitOutput<fmt::Result> for DebugVisitor<'a> { |
349 | fn finish(self) -> fmt::Result { |
350 | self.err |
351 | } |
352 | } |
353 | |
354 | impl<'a> VisitFmt for DebugVisitor<'a> { |
355 | fn writer(&mut self) -> &mut dyn fmt::Write { |
356 | self.writer |
357 | } |
358 | } |
359 | |
360 | impl<'a> MakeVisitor<&'a mut dyn fmt::Write> for MakeDebug { |
361 | type Visitor = DebugVisitor<'a>; |
362 | fn make_visitor(&self, w: &'a mut dyn fmt::Write) -> DebugVisitor<'a> { |
363 | DebugVisitor::new(w) |
364 | } |
365 | } |
366 | } |
367 | |