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`] |
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 | pub trait RecordFields: crate::sealed::Sealed<RecordFieldsMarker> { |
89 | /// Record all the fields in `self` with the provided `visitor`. |
90 | fn record(&self, visitor: &mut dyn Visit); |
91 | } |
92 | |
93 | /// Extension trait implemented for all `MakeVisitor` implementations that |
94 | /// produce a visitor implementing `VisitOutput`. |
95 | pub trait MakeOutput<T, Out> |
96 | where |
97 | Self: MakeVisitor<T> + crate::sealed::Sealed<(T, Out)>, |
98 | Self::Visitor: VisitOutput<Out>, |
99 | { |
100 | /// Visits all fields in `fields` with a new visitor constructed from |
101 | /// `target`. |
102 | fn visit_with<F>(&self, target: T, fields: &F) -> Out |
103 | where |
104 | F: RecordFields, |
105 | { |
106 | self.make_visitor(target).visit(fields) |
107 | } |
108 | } |
109 | |
110 | feature! { |
111 | #![feature = "std" ] |
112 | use std::io; |
113 | |
114 | /// Extension trait implemented by visitors to indicate that they write to an |
115 | /// `io::Write` instance, and allow access to that writer. |
116 | pub trait VisitWrite: VisitOutput<Result<(), io::Error>> { |
117 | /// Returns the writer that this visitor writes to. |
118 | fn writer(&mut self) -> &mut dyn io::Write; |
119 | } |
120 | } |
121 | |
122 | /// Extension trait implemented by visitors to indicate that they write to a |
123 | /// `fmt::Write` instance, and allow access to that writer. |
124 | pub trait VisitFmt: VisitOutput<fmt::Result> { |
125 | /// Returns the formatter that this visitor writes to. |
126 | fn writer(&mut self) -> &mut dyn fmt::Write; |
127 | } |
128 | |
129 | /// Extension trait providing `MakeVisitor` combinators. |
130 | pub trait MakeExt<T> |
131 | where |
132 | Self: MakeVisitor<T> + Sized, |
133 | Self: crate::sealed::Sealed<MakeExtMarker<T>>, |
134 | { |
135 | /// Wraps `self` so that any `fmt::Debug` fields are recorded using the |
136 | /// alternate formatter (`{:#?}`). |
137 | fn debug_alt(self) -> debug::Alt<Self> { |
138 | debug::Alt::new(self) |
139 | } |
140 | |
141 | /// Wraps `self` so that any string fields named "message" are recorded |
142 | /// using `fmt::Display`. |
143 | fn display_messages(self) -> display::Messages<Self> { |
144 | display::Messages::new(self) |
145 | } |
146 | |
147 | /// Wraps `self` so that when fields are formatted to a writer, they are |
148 | /// separated by the provided `delimiter`. |
149 | fn delimited<D>(self, delimiter: D) -> delimited::Delimited<D, Self> |
150 | where |
151 | D: AsRef<str> + Clone, |
152 | Self::Visitor: VisitFmt, |
153 | { |
154 | delimited::Delimited::new(delimiter, self) |
155 | } |
156 | } |
157 | |
158 | // === impl RecordFields === |
159 | |
160 | impl<'a> crate::sealed::Sealed<RecordFieldsMarker> for Event<'a> {} |
161 | impl<'a> RecordFields for Event<'a> { |
162 | fn record(&self, visitor: &mut dyn Visit) { |
163 | Event::record(self, visitor) |
164 | } |
165 | } |
166 | |
167 | impl<'a> crate::sealed::Sealed<RecordFieldsMarker> for Attributes<'a> {} |
168 | impl<'a> RecordFields for Attributes<'a> { |
169 | fn record(&self, visitor: &mut dyn Visit) { |
170 | Attributes::record(self, visitor) |
171 | } |
172 | } |
173 | |
174 | impl<'a> crate::sealed::Sealed<RecordFieldsMarker> for Record<'a> {} |
175 | impl<'a> RecordFields for Record<'a> { |
176 | fn record(&self, visitor: &mut dyn Visit) { |
177 | Record::record(self, visitor) |
178 | } |
179 | } |
180 | |
181 | impl<'a, F> crate::sealed::Sealed<RecordFieldsMarker> for &'a F where F: RecordFields {} |
182 | impl<'a, F> RecordFields for &'a F |
183 | where |
184 | F: RecordFields, |
185 | { |
186 | fn record(&self, visitor: &mut dyn Visit) { |
187 | F::record(*self, visitor) |
188 | } |
189 | } |
190 | |
191 | // === blanket impls === |
192 | |
193 | impl<T, V, F> MakeVisitor<T> for F |
194 | where |
195 | F: Fn(T) -> V, |
196 | V: Visit, |
197 | { |
198 | type Visitor = V; |
199 | fn make_visitor(&self, target: T) -> Self::Visitor { |
200 | (self)(target) |
201 | } |
202 | } |
203 | |
204 | impl<T, Out, M> crate::sealed::Sealed<(T, Out)> for M |
205 | where |
206 | M: MakeVisitor<T>, |
207 | M::Visitor: VisitOutput<Out>, |
208 | { |
209 | } |
210 | |
211 | impl<T, Out, M> MakeOutput<T, Out> for M |
212 | where |
213 | M: MakeVisitor<T>, |
214 | M::Visitor: VisitOutput<Out>, |
215 | { |
216 | } |
217 | |
218 | impl<T, M> crate::sealed::Sealed<MakeExtMarker<T>> for M where M: MakeVisitor<T> + Sized {} |
219 | |
220 | impl<T, M> MakeExt<T> for M |
221 | where |
222 | M: MakeVisitor<T> + Sized, |
223 | M: crate::sealed::Sealed<MakeExtMarker<T>>, |
224 | { |
225 | } |
226 | |
227 | #[derive(Debug)] |
228 | #[doc (hidden)] |
229 | pub struct MakeExtMarker<T> { |
230 | _p: PhantomData<T>, |
231 | } |
232 | |
233 | #[derive(Debug)] |
234 | #[doc (hidden)] |
235 | pub struct RecordFieldsMarker { |
236 | _p: (), |
237 | } |
238 | |
239 | #[cfg (all(test, feature = "alloc" ))] |
240 | #[macro_use ] |
241 | pub(in crate::field) mod test_util { |
242 | use super::*; |
243 | pub(in crate::field) use alloc::string::String; |
244 | use tracing_core::{ |
245 | callsite::Callsite, |
246 | field::{Field, Value}, |
247 | metadata::{Kind, Level, Metadata}, |
248 | }; |
249 | |
250 | pub(crate) struct TestAttrs1; |
251 | pub(crate) struct TestAttrs2; |
252 | |
253 | impl TestAttrs1 { |
254 | pub(crate) fn with<T>(f: impl FnOnce(Attributes<'_>) -> T) -> T { |
255 | let fieldset = TEST_META_1.fields(); |
256 | let values = &[ |
257 | ( |
258 | &fieldset.field("question" ).unwrap(), |
259 | Some(&"life, the universe, and everything" as &dyn Value), |
260 | ), |
261 | (&fieldset.field("question.answer" ).unwrap(), None), |
262 | ( |
263 | &fieldset.field("tricky" ).unwrap(), |
264 | Some(&true as &dyn Value), |
265 | ), |
266 | ( |
267 | &fieldset.field("can_you_do_it" ).unwrap(), |
268 | Some(&true as &dyn Value), |
269 | ), |
270 | ]; |
271 | let valueset = fieldset.value_set(values); |
272 | let attrs = tracing_core::span::Attributes::new(&TEST_META_1, &valueset); |
273 | f(attrs) |
274 | } |
275 | } |
276 | |
277 | impl TestAttrs2 { |
278 | pub(crate) fn with<T>(f: impl FnOnce(Attributes<'_>) -> T) -> T { |
279 | let fieldset = TEST_META_1.fields(); |
280 | let none = tracing_core::field::debug(&Option::<&str>::None); |
281 | let values = &[ |
282 | ( |
283 | &fieldset.field("question" ).unwrap(), |
284 | Some(&none as &dyn Value), |
285 | ), |
286 | ( |
287 | &fieldset.field("question.answer" ).unwrap(), |
288 | Some(&42 as &dyn Value), |
289 | ), |
290 | ( |
291 | &fieldset.field("tricky" ).unwrap(), |
292 | Some(&true as &dyn Value), |
293 | ), |
294 | ( |
295 | &fieldset.field("can_you_do_it" ).unwrap(), |
296 | Some(&false as &dyn Value), |
297 | ), |
298 | ]; |
299 | let valueset = fieldset.value_set(values); |
300 | let attrs = tracing_core::span::Attributes::new(&TEST_META_1, &valueset); |
301 | f(attrs) |
302 | } |
303 | } |
304 | |
305 | struct TestCallsite1; |
306 | static TEST_CALLSITE_1: &'static dyn Callsite = &TestCallsite1; |
307 | static TEST_META_1: Metadata<'static> = tracing_core::metadata! { |
308 | name: "field_test1" , |
309 | target: module_path!(), |
310 | level: Level::INFO, |
311 | fields: &["question" , "question.answer" , "tricky" , "can_you_do_it" ], |
312 | callsite: TEST_CALLSITE_1, |
313 | kind: Kind::SPAN, |
314 | }; |
315 | |
316 | impl Callsite for TestCallsite1 { |
317 | fn set_interest(&self, _: tracing_core::subscriber::Interest) { |
318 | unimplemented!() |
319 | } |
320 | |
321 | fn metadata(&self) -> &Metadata<'_> { |
322 | &TEST_META_1 |
323 | } |
324 | } |
325 | |
326 | pub(crate) struct MakeDebug; |
327 | pub(crate) struct DebugVisitor<'a> { |
328 | writer: &'a mut dyn fmt::Write, |
329 | err: fmt::Result, |
330 | } |
331 | |
332 | impl<'a> DebugVisitor<'a> { |
333 | pub(crate) fn new(writer: &'a mut dyn fmt::Write) -> Self { |
334 | Self { |
335 | writer, |
336 | err: Ok(()), |
337 | } |
338 | } |
339 | } |
340 | |
341 | impl<'a> Visit for DebugVisitor<'a> { |
342 | fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { |
343 | write!(self.writer, "{}={:?}" , field, value).unwrap(); |
344 | } |
345 | } |
346 | |
347 | impl<'a> VisitOutput<fmt::Result> for DebugVisitor<'a> { |
348 | fn finish(self) -> fmt::Result { |
349 | self.err |
350 | } |
351 | } |
352 | |
353 | impl<'a> VisitFmt for DebugVisitor<'a> { |
354 | fn writer(&mut self) -> &mut dyn fmt::Write { |
355 | self.writer |
356 | } |
357 | } |
358 | |
359 | impl<'a> MakeVisitor<&'a mut dyn fmt::Write> for MakeDebug { |
360 | type Visitor = DebugVisitor<'a>; |
361 | fn make_visitor(&self, w: &'a mut dyn fmt::Write) -> DebugVisitor<'a> { |
362 | DebugVisitor::new(w) |
363 | } |
364 | } |
365 | } |
366 | |