Skip to content

Commit 6f23c12

Browse files
renecoutodavidbarskyhawkw
authored
subscriber: add Format::with_file and with_line_number (#1773)
## Motivation Logging line numbers and file names can be useful for debugging. This feature was suggested by #1326 ## Solution As per @hawkw's suggestions, fields were added on `Format`, along with builder methods. Filename and line number information was gathered from the `meta` variable. The `Pretty` formatter already supports printing source locations, but this is configured separately on the `Pretty` formatter rather than on the `Format` type. This branch also changes `Pretty` to honor the `Format`-level configurations and deprecates the `Pretty`-specific method. Fixes #1326 Closes #1804 Co-authored-by: David Barsky <[email protected]> Co-authored-by: Eliza Weisman <[email protected]>
1 parent 67c21fa commit 6f23c12

File tree

7 files changed

+392
-33
lines changed

7 files changed

+392
-33
lines changed

examples/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ This directory contains a collection of examples that demonstrate the use of the
2626
events.
2727
+ `fmt-multiple-writers.rs`: demonstrates how `fmt::Subcriber` can write to multiple
2828
destinations (in this instance, stdout and a file) simultaneously.
29+
+ `fmt-source-locations.rs`: demonstrates displaying source code locations
30+
with `fmt::Subscriber`.
2931
+ `subscriber-filter`: Demonstrates the `tracing-subscriber::filter` module,
3032
which provides a subscriber which adds configurable filtering to a collector
3133
implementation.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//! Demonstrates displaying events' source code locations with the `fmt`
2+
//! subscriber.
3+
#![deny(rust_2018_idioms)]
4+
#[path = "fmt/yak_shave.rs"]
5+
mod yak_shave;
6+
7+
fn main() {
8+
tracing_subscriber::fmt()
9+
// enable everything
10+
.with_max_level(tracing::Level::TRACE)
11+
// display source code file paths
12+
.with_file(true)
13+
// display source code line numbers
14+
.with_line_number(true)
15+
// disable targets
16+
.with_target(false)
17+
// sets this to be the default, global collector for this application.
18+
.init();
19+
20+
let number_of_yaks = 3;
21+
// this creates a new event, outside of any spans.
22+
tracing::info!(number_of_yaks, "preparing to shave yaks");
23+
24+
let number_shaved = yak_shave::shave_all(number_of_yaks);
25+
tracing::info!(
26+
all_yaks_shaved = number_shaved == number_of_yaks,
27+
"yak shaving completed."
28+
);
29+
}

tracing-subscriber/src/fmt/fmt_subscriber.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,30 @@ where
297297
..self
298298
}
299299
}
300+
/// Sets whether or not an event's [source code file path][file] is
301+
/// displayed.
302+
///
303+
/// [file]: tracing_core::Metadata::file
304+
pub fn with_file(self, display_filename: bool) -> Subscriber<C, N, format::Format<L, T>, W> {
305+
Subscriber {
306+
fmt_event: self.fmt_event.with_file(display_filename),
307+
..self
308+
}
309+
}
310+
311+
/// Sets whether or not an event's [source code line number][line] is
312+
/// displayed.
313+
///
314+
/// [line]: tracing_core::Metadata::line
315+
pub fn with_line_number(
316+
self,
317+
display_line_number: bool,
318+
) -> Subscriber<C, N, format::Format<L, T>, W> {
319+
Subscriber {
320+
fmt_event: self.fmt_event.with_line_number(display_line_number),
321+
..self
322+
}
323+
}
300324

301325
/// Sets whether or not an event's level is displayed.
302326
pub fn with_level(self, display_level: bool) -> Subscriber<C, N, format::Format<L, T>, W> {

tracing-subscriber/src/fmt/format/json.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,18 @@ where
240240
serializer.serialize_entry("target", meta.target())?;
241241
}
242242

243+
if self.display_filename {
244+
if let Some(filename) = meta.file() {
245+
serializer.serialize_entry("filename", filename)?;
246+
}
247+
}
248+
249+
if self.display_line_number {
250+
if let Some(line_number) = meta.line() {
251+
serializer.serialize_entry("line_number", &line_number)?;
252+
}
253+
}
254+
243255
if self.format.display_current_span {
244256
if let Some(ref span) = current_span {
245257
serializer
@@ -483,6 +495,7 @@ mod test {
483495
use tracing::{self, collect::with_default};
484496

485497
use std::fmt;
498+
use std::path::Path;
486499

487500
struct MockTime;
488501
impl FormatTime for MockTime {
@@ -510,6 +523,50 @@ mod test {
510523
});
511524
}
512525

526+
#[test]
527+
fn json_filename() {
528+
let current_path = Path::new("tracing-subscriber")
529+
.join("src")
530+
.join("fmt")
531+
.join("format")
532+
.join("json.rs")
533+
.to_str()
534+
.expect("path must be valid unicode")
535+
// escape windows backslashes
536+
.replace('\\', "\\\\");
537+
let expected =
538+
&format!("{}{}{}",
539+
"{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":42,\"name\":\"json_span\",\"number\":3},\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3}],\"target\":\"tracing_subscriber::fmt::format::json::test\",\"filename\":\"",
540+
current_path,
541+
"\",\"fields\":{\"message\":\"some json test\"}}\n");
542+
let collector = collector()
543+
.flatten_event(false)
544+
.with_current_span(true)
545+
.with_file(true)
546+
.with_span_list(true);
547+
test_json(expected, collector, || {
548+
let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3);
549+
let _guard = span.enter();
550+
tracing::info!("some json test");
551+
});
552+
}
553+
554+
#[test]
555+
fn json_line_number() {
556+
let expected =
557+
"{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":42,\"name\":\"json_span\",\"number\":3},\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3}],\"target\":\"tracing_subscriber::fmt::format::json::test\",\"line_number\":42,\"fields\":{\"message\":\"some json test\"}}\n";
558+
let collector = collector()
559+
.flatten_event(false)
560+
.with_current_span(true)
561+
.with_line_number(true)
562+
.with_span_list(true);
563+
test_json_with_line_number(expected, collector, || {
564+
let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3);
565+
let _guard = span.enter();
566+
tracing::info!("some json test");
567+
});
568+
}
569+
513570
#[test]
514571
fn json_flattened_event() {
515572
let expected =
@@ -740,4 +797,34 @@ mod test {
740797
serde_json::from_str(actual).unwrap()
741798
);
742799
}
800+
801+
fn test_json_with_line_number<T>(
802+
expected: &str,
803+
builder: crate::fmt::CollectorBuilder<JsonFields, Format<Json>>,
804+
producer: impl FnOnce() -> T,
805+
) {
806+
let make_writer = MockMakeWriter::default();
807+
let collector = builder
808+
.with_writer(make_writer.clone())
809+
.with_timer(MockTime)
810+
.finish();
811+
812+
with_default(collector, producer);
813+
814+
let buf = make_writer.buf();
815+
let actual = std::str::from_utf8(&buf[..]).unwrap();
816+
let mut expected =
817+
serde_json::from_str::<std::collections::HashMap<&str, serde_json::Value>>(expected)
818+
.unwrap();
819+
let expect_line_number = expected.remove("line_number").is_some();
820+
let mut actual: std::collections::HashMap<&str, serde_json::Value> =
821+
serde_json::from_str(actual).unwrap();
822+
let line_number = actual.remove("line_number");
823+
if expect_line_number {
824+
assert_eq!(line_number.map(|x| x.is_number()), Some(true));
825+
} else {
826+
assert!(line_number.is_none());
827+
}
828+
assert_eq!(actual, expected);
829+
}
743830
}

0 commit comments

Comments
 (0)