/rust/registry/src/index.crates.io-1949cf8c6b5b557f/tracing-log-0.1.4/src/trace_logger.rs
Line | Count | Source |
1 | | //! A `tracing` [`Subscriber`] that uses the [`log`] crate as a backend for |
2 | | //! formatting `tracing` spans and events. |
3 | | //! |
4 | | //! When a [`TraceLogger`] is set as the current subscriber, it will record |
5 | | //! traces by emitting [`log::Record`]s that can be collected by a logger. |
6 | | //! |
7 | | //! **Note**: This API has been deprecated since version 0.1.1. In order to emit |
8 | | //! `tracing` events as `log` records, the ["log" and "log-always" feature |
9 | | //! flags][flags] on the `tracing` crate should be used instead. |
10 | | //! |
11 | | //! [`log`]: log |
12 | | //! [`Subscriber`]: https://docs.rs/tracing/0.1.7/tracing/subscriber/trait.Subscriber.html |
13 | | //! [`log::Record`]:log::Record |
14 | | //! [flags]: https://docs.rs/tracing/latest/tracing/#crate-feature-flags |
15 | | #![deprecated( |
16 | | since = "0.1.1", |
17 | | note = "use the `tracing` crate's \"log\" feature flag instead" |
18 | | )] |
19 | | use crate::AsLog; |
20 | | use std::{ |
21 | | cell::RefCell, |
22 | | collections::HashMap, |
23 | | fmt::{self, Write}, |
24 | | sync::{ |
25 | | atomic::{AtomicUsize, Ordering}, |
26 | | Mutex, |
27 | | }, |
28 | | }; |
29 | | use tracing_core::{ |
30 | | field, |
31 | | span::{self, Id}, |
32 | | Event, Metadata, Subscriber, |
33 | | }; |
34 | | |
35 | | /// A `tracing` [`Subscriber`] implementation that logs all recorded |
36 | | /// trace events. |
37 | | /// |
38 | | /// **Note**: This API has been deprecated since version 0.1.1. In order to emit |
39 | | /// `tracing` events as `log` records, the ["log" and "log-always" feature |
40 | | /// flags][flags] on the `tracing` crate should be used instead. |
41 | | /// |
42 | | /// [`Subscriber`]: https://docs.rs/tracing/0.1.7/tracing/subscriber/trait.Subscriber.html |
43 | | /// [flags]: https://docs.rs/tracing/latest/tracing/#crate-feature-flags |
44 | | pub struct TraceLogger { |
45 | | settings: Builder, |
46 | | spans: Mutex<HashMap<Id, SpanLineBuilder>>, |
47 | | next_id: AtomicUsize, |
48 | | } |
49 | | |
50 | | thread_local! { |
51 | | static CURRENT: RefCell<Vec<Id>> = RefCell::new(Vec::new()); |
52 | | } |
53 | | /// Configures and constructs a [`TraceLogger`]. |
54 | | /// |
55 | | #[derive(Debug)] |
56 | | pub struct Builder { |
57 | | log_span_closes: bool, |
58 | | log_enters: bool, |
59 | | log_exits: bool, |
60 | | log_ids: bool, |
61 | | parent_fields: bool, |
62 | | log_parent: bool, |
63 | | } |
64 | | |
65 | | // ===== impl TraceLogger ===== |
66 | | |
67 | | impl TraceLogger { |
68 | | /// Returns a new `TraceLogger` with the default configuration. |
69 | 0 | pub fn new() -> Self { |
70 | 0 | Self::builder().finish() |
71 | 0 | } |
72 | | |
73 | | /// Returns a `Builder` for configuring a `TraceLogger`. |
74 | 0 | pub fn builder() -> Builder { |
75 | 0 | Default::default() |
76 | 0 | } |
77 | | |
78 | 0 | fn from_builder(settings: Builder) -> Self { |
79 | 0 | Self { |
80 | 0 | settings, |
81 | 0 | ..Default::default() |
82 | 0 | } |
83 | 0 | } |
84 | | |
85 | 0 | fn next_id(&self) -> Id { |
86 | 0 | Id::from_u64(self.next_id.fetch_add(1, Ordering::SeqCst) as u64) |
87 | 0 | } |
88 | | } |
89 | | |
90 | | // ===== impl Builder ===== |
91 | | |
92 | | impl Builder { |
93 | | /// Configures whether or not the [`TraceLogger`] being constructed will log |
94 | | /// when a span closes. |
95 | | /// |
96 | 0 | pub fn with_span_closes(self, log_span_closes: bool) -> Self { |
97 | 0 | Self { |
98 | 0 | log_span_closes, |
99 | 0 | ..self |
100 | 0 | } |
101 | 0 | } |
102 | | |
103 | | /// Configures whether or not the [`TraceLogger`] being constructed will |
104 | | /// include the fields of parent spans when formatting events. |
105 | | /// |
106 | 0 | pub fn with_parent_fields(self, parent_fields: bool) -> Self { |
107 | 0 | Self { |
108 | 0 | parent_fields, |
109 | 0 | ..self |
110 | 0 | } |
111 | 0 | } |
112 | | |
113 | | /// Configures whether or not the [`TraceLogger`] being constructed will log |
114 | | /// when a span is entered. |
115 | | /// |
116 | | /// If this is set to false, fields from the current span will still be |
117 | | /// recorded as context, but the actual entry will not create a log record. |
118 | | /// |
119 | 0 | pub fn with_span_entry(self, log_enters: bool) -> Self { |
120 | 0 | Self { log_enters, ..self } |
121 | 0 | } |
122 | | |
123 | | /// Configures whether or not the [`TraceLogger`] being constructed will log |
124 | | /// when a span is exited. |
125 | | /// |
126 | 0 | pub fn with_span_exits(self, log_exits: bool) -> Self { |
127 | 0 | Self { log_exits, ..self } |
128 | 0 | } |
129 | | |
130 | | /// Configures whether or not the [`TraceLogger`] being constructed will |
131 | | /// include span IDs when formatting log output. |
132 | | /// |
133 | 0 | pub fn with_ids(self, log_ids: bool) -> Self { |
134 | 0 | Self { log_ids, ..self } |
135 | 0 | } |
136 | | |
137 | | /// Configures whether or not the [`TraceLogger`] being constructed will |
138 | | /// include the names of parent spans as context when formatting events. |
139 | | /// |
140 | 0 | pub fn with_parent_names(self, log_parent: bool) -> Self { |
141 | 0 | Self { log_parent, ..self } |
142 | 0 | } |
143 | | |
144 | | /// Complete the builder, returning a configured [`TraceLogger`]. |
145 | | /// |
146 | 0 | pub fn finish(self) -> TraceLogger { |
147 | 0 | TraceLogger::from_builder(self) |
148 | 0 | } |
149 | | } |
150 | | |
151 | | impl Default for Builder { |
152 | 0 | fn default() -> Self { |
153 | 0 | Builder { |
154 | 0 | log_span_closes: false, |
155 | 0 | parent_fields: true, |
156 | 0 | log_exits: false, |
157 | 0 | log_ids: false, |
158 | 0 | log_parent: true, |
159 | 0 | log_enters: false, |
160 | 0 | } |
161 | 0 | } |
162 | | } |
163 | | |
164 | | impl Default for TraceLogger { |
165 | 0 | fn default() -> Self { |
166 | 0 | TraceLogger { |
167 | 0 | settings: Default::default(), |
168 | 0 | spans: Default::default(), |
169 | 0 | next_id: AtomicUsize::new(1), |
170 | 0 | } |
171 | 0 | } |
172 | | } |
173 | | |
174 | | #[derive(Debug)] |
175 | | struct SpanLineBuilder { |
176 | | parent: Option<Id>, |
177 | | ref_count: usize, |
178 | | fields: String, |
179 | | file: Option<String>, |
180 | | line: Option<u32>, |
181 | | module_path: Option<String>, |
182 | | target: String, |
183 | | level: log::Level, |
184 | | name: &'static str, |
185 | | } |
186 | | |
187 | | impl SpanLineBuilder { |
188 | 0 | fn new(parent: Option<Id>, meta: &Metadata<'_>, fields: String) -> Self { |
189 | 0 | Self { |
190 | 0 | parent, |
191 | 0 | ref_count: 1, |
192 | 0 | fields, |
193 | 0 | file: meta.file().map(String::from), |
194 | 0 | line: meta.line(), |
195 | 0 | module_path: meta.module_path().map(String::from), |
196 | 0 | target: String::from(meta.target()), |
197 | 0 | level: meta.level().as_log(), |
198 | 0 | name: meta.name(), |
199 | 0 | } |
200 | 0 | } |
201 | | |
202 | 0 | fn log_meta(&self) -> log::Metadata<'_> { |
203 | 0 | log::MetadataBuilder::new() |
204 | 0 | .level(self.level) |
205 | 0 | .target(self.target.as_ref()) |
206 | 0 | .build() |
207 | 0 | } |
208 | | |
209 | 0 | fn finish(self) { |
210 | 0 | let log_meta = self.log_meta(); |
211 | 0 | let logger = log::logger(); |
212 | 0 | if logger.enabled(&log_meta) { |
213 | 0 | logger.log( |
214 | 0 | &log::Record::builder() |
215 | 0 | .metadata(log_meta) |
216 | 0 | .target(self.target.as_ref()) |
217 | 0 | .module_path(self.module_path.as_ref().map(String::as_ref)) |
218 | 0 | .file(self.file.as_ref().map(String::as_ref)) |
219 | 0 | .line(self.line) |
220 | 0 | .args(format_args!("close {}; {}", self.name, self.fields)) |
221 | 0 | .build(), |
222 | 0 | ); |
223 | 0 | } |
224 | 0 | } |
225 | | } |
226 | | |
227 | | impl field::Visit for SpanLineBuilder { |
228 | 0 | fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) { |
229 | 0 | write!(self.fields, " {}={:?};", field.name(), value) |
230 | 0 | .expect("write to string should never fail") |
231 | 0 | } |
232 | | } |
233 | | |
234 | | impl Subscriber for TraceLogger { |
235 | 0 | fn enabled(&self, metadata: &Metadata<'_>) -> bool { |
236 | 0 | log::logger().enabled(&metadata.as_log()) |
237 | 0 | } |
238 | | |
239 | 0 | fn new_span(&self, attrs: &span::Attributes<'_>) -> Id { |
240 | 0 | let id = self.next_id(); |
241 | 0 | let mut spans = self.spans.lock().unwrap(); |
242 | 0 | let mut fields = String::new(); |
243 | 0 | let parent = self.current_id(); |
244 | 0 | if self.settings.parent_fields { |
245 | 0 | let mut next_parent = parent.as_ref(); |
246 | 0 | while let Some(parent) = next_parent.and_then(|p| spans.get(p)) { |
247 | 0 | write!(&mut fields, "{}", parent.fields).expect("write to string cannot fail"); |
248 | 0 | next_parent = parent.parent.as_ref(); |
249 | 0 | } |
250 | 0 | } |
251 | 0 | let mut span = SpanLineBuilder::new(parent, attrs.metadata(), fields); |
252 | 0 | attrs.record(&mut span); |
253 | 0 | spans.insert(id.clone(), span); |
254 | 0 | id |
255 | 0 | } |
256 | | |
257 | 0 | fn record(&self, span: &Id, values: &span::Record<'_>) { |
258 | 0 | let mut spans = self.spans.lock().unwrap(); |
259 | 0 | if let Some(span) = spans.get_mut(span) { |
260 | 0 | values.record(span); |
261 | 0 | } |
262 | 0 | } |
263 | | |
264 | 0 | fn record_follows_from(&self, span: &Id, follows: &Id) { |
265 | | // TODO: this should eventually track the relationship? |
266 | 0 | log::logger().log( |
267 | 0 | &log::Record::builder() |
268 | 0 | .level(log::Level::Trace) |
269 | 0 | .args(format_args!("span {:?} follows_from={:?};", span, follows)) |
270 | 0 | .build(), |
271 | | ); |
272 | 0 | } |
273 | | |
274 | 0 | fn enter(&self, id: &Id) { |
275 | 0 | let _ = CURRENT.try_with(|current| { |
276 | 0 | let mut current = current.borrow_mut(); |
277 | 0 | if current.contains(id) { |
278 | | // Ignore duplicate enters. |
279 | 0 | return; |
280 | 0 | } |
281 | 0 | current.push(id.clone()); |
282 | 0 | }); |
283 | 0 | let spans = self.spans.lock().unwrap(); |
284 | 0 | if self.settings.log_enters { |
285 | 0 | if let Some(span) = spans.get(id) { |
286 | 0 | let log_meta = span.log_meta(); |
287 | 0 | let logger = log::logger(); |
288 | 0 | if logger.enabled(&log_meta) { |
289 | 0 | let current_id = self.current_id(); |
290 | 0 | let current_fields = current_id |
291 | 0 | .as_ref() |
292 | 0 | .and_then(|id| spans.get(id)) |
293 | 0 | .map(|span| span.fields.as_ref()) |
294 | 0 | .unwrap_or(""); |
295 | 0 | if self.settings.log_ids { |
296 | 0 | logger.log( |
297 | 0 | &log::Record::builder() |
298 | 0 | .metadata(log_meta) |
299 | 0 | .target(span.target.as_ref()) |
300 | 0 | .module_path(span.module_path.as_ref().map(String::as_ref)) |
301 | 0 | .file(span.file.as_ref().map(String::as_ref)) |
302 | 0 | .line(span.line) |
303 | 0 | .args(format_args!( |
304 | 0 | "enter {}; in={:?}; {}", |
305 | 0 | span.name, current_id, current_fields |
306 | 0 | )) |
307 | 0 | .build(), |
308 | 0 | ); |
309 | 0 | } else { |
310 | 0 | logger.log( |
311 | 0 | &log::Record::builder() |
312 | 0 | .metadata(log_meta) |
313 | 0 | .target(span.target.as_ref()) |
314 | 0 | .module_path(span.module_path.as_ref().map(String::as_ref)) |
315 | 0 | .file(span.file.as_ref().map(String::as_ref)) |
316 | 0 | .line(span.line) |
317 | 0 | .args(format_args!("enter {}; {}", span.name, current_fields)) |
318 | 0 | .build(), |
319 | 0 | ); |
320 | 0 | } |
321 | 0 | } |
322 | 0 | } |
323 | 0 | } |
324 | 0 | } |
325 | | |
326 | 0 | fn exit(&self, id: &Id) { |
327 | 0 | let _ = CURRENT.try_with(|current| { |
328 | 0 | let mut current = current.borrow_mut(); |
329 | 0 | if current.last() == Some(id) { |
330 | 0 | current.pop() |
331 | | } else { |
332 | 0 | None |
333 | | } |
334 | 0 | }); |
335 | 0 | if self.settings.log_exits { |
336 | 0 | let spans = self.spans.lock().unwrap(); |
337 | 0 | if let Some(span) = spans.get(id) { |
338 | 0 | let log_meta = span.log_meta(); |
339 | 0 | let logger = log::logger(); |
340 | 0 | if logger.enabled(&log_meta) { |
341 | 0 | logger.log( |
342 | 0 | &log::Record::builder() |
343 | 0 | .metadata(log_meta) |
344 | 0 | .target(span.target.as_ref()) |
345 | 0 | .module_path(span.module_path.as_ref().map(String::as_ref)) |
346 | 0 | .file(span.file.as_ref().map(String::as_ref)) |
347 | 0 | .line(span.line) |
348 | 0 | .args(format_args!("exit {}", span.name)) |
349 | 0 | .build(), |
350 | 0 | ); |
351 | 0 | } |
352 | 0 | } |
353 | 0 | } |
354 | 0 | } |
355 | | |
356 | 0 | fn event(&self, event: &Event<'_>) { |
357 | 0 | let meta = event.metadata(); |
358 | 0 | let log_meta = meta.as_log(); |
359 | 0 | let logger = log::logger(); |
360 | 0 | if logger.enabled(&log_meta) { |
361 | 0 | let spans = self.spans.lock().unwrap(); |
362 | 0 | let current = self.current_id().and_then(|id| spans.get(&id)); |
363 | 0 | let (current_fields, parent) = current |
364 | 0 | .map(|span| { |
365 | 0 | let fields = span.fields.as_ref(); |
366 | 0 | let parent = if self.settings.log_parent { |
367 | 0 | Some(span.name) |
368 | | } else { |
369 | 0 | None |
370 | | }; |
371 | 0 | (fields, parent) |
372 | 0 | }) |
373 | 0 | .unwrap_or(("", None)); |
374 | 0 | logger.log( |
375 | 0 | &log::Record::builder() |
376 | 0 | .metadata(log_meta) |
377 | 0 | .target(meta.target()) |
378 | 0 | .module_path(meta.module_path().as_ref().cloned()) |
379 | 0 | .file(meta.file().as_ref().cloned()) |
380 | 0 | .line(meta.line()) |
381 | 0 | .args(format_args!( |
382 | 0 | "{}{}{}{}", |
383 | 0 | parent.unwrap_or(""), |
384 | 0 | if parent.is_some() { ": " } else { "" }, |
385 | 0 | LogEvent(event), |
386 | | current_fields, |
387 | | )) |
388 | 0 | .build(), |
389 | | ); |
390 | 0 | } |
391 | 0 | } |
392 | | |
393 | 0 | fn clone_span(&self, id: &Id) -> Id { |
394 | 0 | let mut spans = self.spans.lock().unwrap(); |
395 | 0 | if let Some(span) = spans.get_mut(id) { |
396 | 0 | span.ref_count += 1; |
397 | 0 | } |
398 | 0 | id.clone() |
399 | 0 | } |
400 | | |
401 | 0 | fn try_close(&self, id: Id) -> bool { |
402 | 0 | let mut spans = self.spans.lock().unwrap(); |
403 | 0 | if spans.contains_key(&id) { |
404 | 0 | if spans.get(&id).unwrap().ref_count == 1 { |
405 | 0 | let span = spans.remove(&id).unwrap(); |
406 | 0 | if self.settings.log_span_closes { |
407 | 0 | span.finish(); |
408 | 0 | } |
409 | 0 | return true; |
410 | 0 | } else { |
411 | 0 | spans.get_mut(&id).unwrap().ref_count -= 1; |
412 | 0 | } |
413 | 0 | } |
414 | 0 | false |
415 | 0 | } |
416 | | } |
417 | | |
418 | | impl TraceLogger { |
419 | | #[inline] |
420 | 0 | fn current_id(&self) -> Option<Id> { |
421 | 0 | CURRENT |
422 | 0 | .try_with(|current| current.borrow().last().map(|span| self.clone_span(span))) |
423 | 0 | .ok()? |
424 | 0 | } |
425 | | } |
426 | | |
427 | | struct LogEvent<'a>(&'a Event<'a>); |
428 | | |
429 | | impl<'a> fmt::Display for LogEvent<'a> { |
430 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
431 | 0 | let mut has_logged = false; |
432 | 0 | let mut format_fields = |field: &field::Field, value: &dyn fmt::Debug| { |
433 | 0 | let name = field.name(); |
434 | 0 | let leading = if has_logged { " " } else { "" }; |
435 | | // TODO: handle fmt error? |
436 | 0 | let _ = if name == "message" { |
437 | 0 | write!(f, "{}{:?};", leading, value) |
438 | | } else { |
439 | 0 | write!(f, "{}{}={:?};", leading, name, value) |
440 | | }; |
441 | 0 | has_logged = true; |
442 | 0 | }; |
443 | | |
444 | 0 | self.0.record(&mut format_fields); |
445 | 0 | Ok(()) |
446 | 0 | } |
447 | | } |
448 | | |
449 | | impl fmt::Debug for TraceLogger { |
450 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
451 | 0 | f.debug_struct("TraceLogger") |
452 | 0 | .field("settings", &self.settings) |
453 | 0 | .field("spans", &self.spans) |
454 | 0 | .field("current", &self.current_id()) |
455 | 0 | .field("next_id", &self.next_id) |
456 | 0 | .finish() |
457 | 0 | } |
458 | | } |