Skip to content

Commit 5081bf0

Browse files
Add a MakeWriter param to HierarchicalLayer (#10)
Authored-by: David Barsky <me@davidbarsky.com> Co-authored-by: Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com>
1 parent 6811411 commit 5081bf0

File tree

5 files changed

+304
-200
lines changed

5 files changed

+304
-200
lines changed

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "tracing-tree"
3-
version = "0.1.2"
3+
version = "0.1.3"
44
authors = ["David Barsky <me@davidbarsky.com>", "Nathan Whitaker"]
55
edition = "2018"
66
license = "MIT OR Apache-2.0"
@@ -10,7 +10,7 @@ description = "a tracing layer that represents prints a heirarchal tree of spans
1010

1111
[dependencies]
1212
tracing = "0.1"
13-
tracing-subscriber = "0.2"
13+
tracing-subscriber = { version = "0.2", default-features = false, features = ["registry", "fmt"] }
1414
quanta = "0.3.1"
1515
termcolor = "1.0.5"
1616
ansi_term = "0.12.1"

examples/basic.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ use tracing_subscriber::{layer::SubscriberExt, registry::Registry};
33
use tracing_tree::HierarchicalLayer;
44

55
fn main() {
6-
let subscriber = Registry::default().with(HierarchicalLayer::new(2).with_indent_lines(true));
6+
let layer = HierarchicalLayer::default()
7+
.with_indent_lines(true)
8+
.with_indent_amount(2);
9+
10+
let subscriber = Registry::default().with(layer);
711
tracing::subscriber::set_global_default(subscriber).unwrap();
812

913
let app_span = span!(Level::TRACE, "hierarchical-example", version = %0.1);

examples/stderr.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use tracing::{debug, info, instrument};
2+
use tracing_subscriber::{layer::SubscriberExt, registry::Registry};
3+
use tracing_tree::HierarchicalLayer;
4+
5+
#[instrument]
6+
fn nth_fibonacci(n: u64) -> u64 {
7+
if n == 0 || n == 1 {
8+
debug!("Base case");
9+
1
10+
} else {
11+
debug!("Recursing");
12+
nth_fibonacci(n - 1) + nth_fibonacci(n - 2)
13+
}
14+
}
15+
16+
#[instrument]
17+
fn fibonacci_seq(to: u64) -> Vec<u64> {
18+
let mut sequence = vec![];
19+
20+
for n in 0..=to {
21+
debug!("Pushing {n} fibonacci", n = n);
22+
sequence.push(nth_fibonacci(n));
23+
}
24+
25+
sequence
26+
}
27+
28+
fn main() {
29+
let layer = HierarchicalLayer::default()
30+
.with_indent_lines(true)
31+
.with_indent_amount(2)
32+
.with_writer(std::io::stderr);
33+
34+
let subscriber = Registry::default().with(layer);
35+
tracing::subscriber::set_global_default(subscriber).unwrap();
36+
37+
let n = 5;
38+
let sequence = fibonacci_seq(n);
39+
info!("The first {} fibonacci numbers are {:?}", n, sequence);
40+
}

src/format.rs

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
use ansi_term::Color;
2+
use std::{
3+
fmt::{self, Write as _},
4+
io,
5+
};
6+
use tracing::{
7+
field::{Field, Visit},
8+
Level,
9+
};
10+
11+
const LINE_VERT: &str = "│";
12+
const LINE_HORIZ: &str = "─";
13+
const LINE_BRANCH: &str = "├";
14+
15+
#[derive(Debug)]
16+
pub struct Config {
17+
pub ansi: bool,
18+
pub indent_lines: bool,
19+
pub indent_amount: usize,
20+
}
21+
22+
impl Config {
23+
pub fn with_ansi(self, ansi: bool) -> Self {
24+
Self { ansi, ..self }
25+
}
26+
27+
pub fn with_indent_lines(self, indent_lines: bool) -> Self {
28+
Self {
29+
indent_lines,
30+
..self
31+
}
32+
}
33+
}
34+
35+
impl Default for Config {
36+
fn default() -> Self {
37+
Self {
38+
ansi: true,
39+
indent_lines: false,
40+
indent_amount: 2,
41+
}
42+
}
43+
}
44+
45+
#[derive(Debug)]
46+
pub struct Buffers {
47+
pub current_buf: String,
48+
pub indent_buf: String,
49+
}
50+
51+
impl Buffers {
52+
pub fn new() -> Self {
53+
Self {
54+
current_buf: String::new(),
55+
indent_buf: String::new(),
56+
}
57+
}
58+
59+
pub fn flush_current_buf(&mut self, mut writer: impl io::Write) {
60+
write!(writer, "{}", &self.current_buf).unwrap();
61+
self.current_buf.clear();
62+
}
63+
64+
pub fn flush_indent_buf(&mut self) {
65+
self.current_buf.push_str(&self.indent_buf);
66+
self.indent_buf.clear();
67+
}
68+
69+
pub fn indent_current(&mut self, indent: usize, config: &Config) {
70+
indent_block(
71+
&mut self.current_buf,
72+
&mut self.indent_buf,
73+
indent,
74+
config.indent_amount,
75+
config.indent_lines,
76+
);
77+
self.current_buf.clear();
78+
}
79+
}
80+
81+
pub struct FmtEvent<'a> {
82+
pub bufs: &'a mut Buffers,
83+
pub comma: bool,
84+
}
85+
86+
impl<'a> Visit for FmtEvent<'a> {
87+
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
88+
let buf = &mut self.bufs.current_buf;
89+
write!(buf, "{comma} ", comma = if self.comma { "," } else { "" },).unwrap();
90+
let name = field.name();
91+
if name == "message" {
92+
write!(buf, "{:?}", value).unwrap();
93+
self.comma = true;
94+
} else {
95+
write!(buf, "{}={:?}", name, value).unwrap();
96+
self.comma = true;
97+
}
98+
}
99+
}
100+
101+
impl<'a> FmtEvent<'a> {
102+
pub fn finish(&mut self, indent: usize, config: &Config) {
103+
self.bufs.current_buf.push('\n');
104+
self.bufs.indent_current(indent, config);
105+
self.bufs.flush_indent_buf();
106+
}
107+
}
108+
109+
pub struct ColorLevel<'a>(pub &'a Level);
110+
111+
impl<'a> fmt::Display for ColorLevel<'a> {
112+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113+
match *self.0 {
114+
Level::TRACE => Color::Purple.bold().paint("TRACE"),
115+
Level::DEBUG => Color::Blue.bold().paint("DEBUG"),
116+
Level::INFO => Color::Green.bold().paint(" INFO"),
117+
Level::WARN => Color::RGB(252, 234, 160).bold().paint(" WARN"), // orange
118+
Level::ERROR => Color::Red.bold().paint("ERROR"),
119+
}
120+
.fmt(f)
121+
}
122+
}
123+
124+
fn indent_block_with_lines(lines: &[&str], buf: &mut String, indent: usize, indent_amount: usize) {
125+
let indent_spaces = indent * indent_amount;
126+
if lines.is_empty() {
127+
return;
128+
} else if indent_spaces == 0 {
129+
for line in lines {
130+
buf.push_str(line);
131+
buf.push('\n');
132+
}
133+
return;
134+
}
135+
let mut s = String::with_capacity(indent_spaces);
136+
137+
// instead of using all spaces to indent, draw a vertical line at every indent level
138+
// up until the last indent
139+
for i in 0..(indent_spaces - indent_amount) {
140+
if i % indent_amount == 0 {
141+
s.push_str(LINE_VERT);
142+
} else {
143+
s.push(' ');
144+
}
145+
}
146+
147+
// draw branch
148+
buf.push_str(&s);
149+
buf.push_str(LINE_BRANCH);
150+
151+
// add `indent_amount - 1` horizontal lines before the span/event
152+
for _ in 0..(indent_amount - 1) {
153+
buf.push_str(LINE_HORIZ);
154+
}
155+
buf.push_str(&lines[0]);
156+
buf.push('\n');
157+
158+
// add the rest of the indentation, since we don't want to draw horizontal lines
159+
// for subsequent lines
160+
for i in 0..indent_amount {
161+
if i % indent_amount == 0 {
162+
s.push_str(LINE_VERT);
163+
} else {
164+
s.push(' ');
165+
}
166+
}
167+
168+
// add all of the actual content, with each line preceded by the indent string
169+
for line in &lines[1..] {
170+
buf.push_str(&s);
171+
buf.push_str(line);
172+
buf.push('\n');
173+
}
174+
}
175+
176+
fn indent_block(
177+
block: &mut String,
178+
buf: &mut String,
179+
indent: usize,
180+
indent_amount: usize,
181+
indent_lines: bool,
182+
) {
183+
let lines: Vec<&str> = block.lines().collect();
184+
let indent_spaces = indent * indent_amount;
185+
buf.reserve(block.len() + (lines.len() * indent_spaces));
186+
if indent_lines {
187+
indent_block_with_lines(&lines, buf, indent, indent_amount);
188+
} else {
189+
let indent_str = String::from(" ").repeat(indent_spaces);
190+
for line in lines {
191+
buf.push_str(&indent_str);
192+
buf.push_str(line);
193+
buf.push('\n');
194+
}
195+
}
196+
}

0 commit comments

Comments
 (0)