Skip to content

Commit c46446b

Browse files
authored
Print the span info on span exit and entry (#16)
1 parent 6a81afb commit c46446b

File tree

3 files changed

+193
-27
lines changed

3 files changed

+193
-27
lines changed

examples/basic.rs

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ fn main() {
88
.with_indent_amount(2)
99
.with_thread_names(true)
1010
.with_thread_ids(true)
11+
.with_verbose_exit(true)
12+
.with_verbose_entry(true)
1113
.with_targets(true);
1214

1315
let subscriber = Registry::default().with(layer);

src/format.rs

+105-8
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,19 @@ use tracing::{
88
Level,
99
};
1010

11-
const LINE_VERT: &str = "│";
11+
pub(crate) const LINE_VERT: &str = "│";
1212
const LINE_HORIZ: &str = "─";
13-
const LINE_BRANCH: &str = "├";
13+
pub(crate) const LINE_BRANCH: &str = "├";
14+
pub(crate) const LINE_CLOSE: &str = "┘";
15+
pub(crate) const LINE_OPEN: &str = "┐";
16+
17+
pub(crate) enum SpanMode {
18+
PreOpen,
19+
Open,
20+
Close,
21+
PostClose,
22+
Event,
23+
}
1424

1525
#[derive(Debug)]
1626
pub struct Config {
@@ -28,6 +38,10 @@ pub struct Config {
2838
pub render_thread_names: bool,
2939
/// Specifies after how many indentation levels we will wrap back around to zero
3040
pub wraparound: usize,
41+
/// Whether to print the current span before activating a new one
42+
pub verbose_entry: bool,
43+
/// Whether to print the current span before exiting it.
44+
pub verbose_exit: bool,
3145
}
3246

3347
impl Config {
@@ -64,6 +78,20 @@ impl Config {
6478
Self { wraparound, ..self }
6579
}
6680

81+
pub fn with_verbose_entry(self, verbose_entry: bool) -> Self {
82+
Self {
83+
verbose_entry,
84+
..self
85+
}
86+
}
87+
88+
pub fn with_verbose_exit(self, verbose_exit: bool) -> Self {
89+
Self {
90+
verbose_exit,
91+
..self
92+
}
93+
}
94+
6795
pub(crate) fn prefix(&self) -> String {
6896
let mut buf = String::new();
6997
if self.render_thread_ids {
@@ -97,6 +125,8 @@ impl Default for Config {
97125
render_thread_ids: false,
98126
render_thread_names: false,
99127
wraparound: usize::max_value(),
128+
verbose_entry: false,
129+
verbose_exit: false,
100130
}
101131
}
102132
}
@@ -125,7 +155,7 @@ impl Buffers {
125155
self.indent_buf.clear();
126156
}
127157

128-
pub fn indent_current(&mut self, indent: usize, config: &Config) {
158+
pub(crate) fn indent_current(&mut self, indent: usize, config: &Config, style: SpanMode) {
129159
self.current_buf.push('\n');
130160
indent_block(
131161
&mut self.current_buf,
@@ -134,6 +164,7 @@ impl Buffers {
134164
config.indent_amount,
135165
config.indent_lines,
136166
&config.prefix(),
167+
style,
137168
);
138169
self.current_buf.clear();
139170
self.flush_indent_buf();
@@ -181,7 +212,15 @@ fn indent_block_with_lines(
181212
indent: usize,
182213
indent_amount: usize,
183214
prefix: &str,
215+
style: SpanMode,
184216
) {
217+
let indent = match style {
218+
SpanMode::PreOpen => indent - 1,
219+
SpanMode::Open => indent - 1,
220+
SpanMode::Close => indent,
221+
SpanMode::PostClose => indent,
222+
SpanMode::Event => indent,
223+
};
185224
let indent_spaces = indent * indent_amount;
186225
if lines.is_empty() {
187226
return;
@@ -208,11 +247,68 @@ fn indent_block_with_lines(
208247

209248
// draw branch
210249
buf.push_str(&s);
211-
buf.push_str(LINE_BRANCH);
212250

213-
// add `indent_amount - 1` horizontal lines before the span/event
214-
for _ in 0..(indent_amount - 1) {
215-
buf.push_str(LINE_HORIZ);
251+
match style {
252+
SpanMode::PreOpen => {
253+
buf.push_str(LINE_BRANCH);
254+
for _ in 1..(indent_amount / 2) {
255+
buf.push_str(LINE_HORIZ);
256+
}
257+
buf.push_str(LINE_OPEN);
258+
}
259+
SpanMode::Open => {
260+
buf.push_str(LINE_VERT);
261+
for _ in 1..(indent_amount / 2) {
262+
buf.push(' ');
263+
}
264+
// We don't have the space for fancy rendering at single space indent.
265+
if indent_amount > 1 {
266+
buf.push('└');
267+
}
268+
for _ in (indent_amount / 2)..(indent_amount - 1) {
269+
buf.push_str(LINE_HORIZ);
270+
}
271+
// We don't have the space for fancy rendering at single space indent.
272+
if indent_amount > 1 {
273+
buf.push_str(LINE_OPEN);
274+
} else {
275+
buf.push_str(LINE_VERT);
276+
}
277+
}
278+
SpanMode::Close => {
279+
buf.push_str(LINE_VERT);
280+
for _ in 1..(indent_amount / 2) {
281+
buf.push(' ');
282+
}
283+
// We don't have the space for fancy rendering at single space indent.
284+
if indent_amount > 1 {
285+
buf.push('┌');
286+
}
287+
for _ in (indent_amount / 2)..(indent_amount - 1) {
288+
buf.push_str(LINE_HORIZ);
289+
}
290+
// We don't have the space for fancy rendering at single space indent.
291+
if indent_amount > 1 {
292+
buf.push_str(LINE_CLOSE);
293+
} else {
294+
buf.push_str(LINE_VERT);
295+
}
296+
}
297+
SpanMode::PostClose => {
298+
buf.push_str(LINE_BRANCH);
299+
for _ in 1..(indent_amount / 2) {
300+
buf.push_str(LINE_HORIZ);
301+
}
302+
buf.push_str(LINE_CLOSE);
303+
}
304+
SpanMode::Event => {
305+
buf.push_str(LINE_BRANCH);
306+
307+
// add `indent_amount - 1` horizontal lines before the span/event
308+
for _ in 0..(indent_amount - 1) {
309+
buf.push_str(LINE_HORIZ);
310+
}
311+
}
216312
}
217313
buf.push_str(&lines[0]);
218314
buf.push('\n');
@@ -242,12 +338,13 @@ fn indent_block(
242338
indent_amount: usize,
243339
indent_lines: bool,
244340
prefix: &str,
341+
style: SpanMode,
245342
) {
246343
let lines: Vec<&str> = block.lines().collect();
247344
let indent_spaces = indent * indent_amount;
248345
buf.reserve(block.len() + (lines.len() * indent_spaces));
249346
if indent_lines {
250-
indent_block_with_lines(&lines, buf, indent, indent_amount, prefix);
347+
indent_block_with_lines(&lines, buf, indent, indent_amount, prefix, style);
251348
} else {
252349
let indent_str = String::from(" ").repeat(indent_spaces);
253350
for line in lines {

src/lib.rs

+86-19
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ pub(crate) mod format;
22

33
use ansi_term::{Color, Style};
44
use chrono::{DateTime, Local};
5-
use format::{Buffers, ColorLevel, Config, FmtEvent};
5+
use format::{Buffers, ColorLevel, Config, FmtEvent, SpanMode};
66
use std::{
77
fmt::{self, Write as _},
88
io,
@@ -149,6 +149,26 @@ where
149149
}
150150
}
151151

152+
/// Whether to print the currently active span's message again before entering a new span.
153+
/// This helps if the entry to the current span was quite a while back (and with scrolling
154+
/// upwards in logs).
155+
pub fn with_verbose_entry(self, verbose_entry: bool) -> Self {
156+
Self {
157+
config: self.config.with_verbose_entry(verbose_entry),
158+
..self
159+
}
160+
}
161+
162+
/// Whether to print the currently active span's message again before dropping it.
163+
/// This helps if the entry to the current span was quite a while back (and with scrolling
164+
/// upwards in logs).
165+
pub fn with_verbose_exit(self, verbose_exit: bool) -> Self {
166+
Self {
167+
config: self.config.with_verbose_exit(verbose_exit),
168+
..self
169+
}
170+
}
171+
152172
fn styled(&self, style: Style, text: impl AsRef<str>) -> String {
153173
if self.config.ansi {
154174
style.paint(text.as_ref()).to_string()
@@ -177,29 +197,30 @@ where
177197
}
178198
Ok(())
179199
}
180-
}
181200

182-
impl<S, W> Layer<S> for HierarchicalLayer<W>
183-
where
184-
S: Subscriber + for<'span> LookupSpan<'span> + fmt::Debug,
185-
W: MakeWriter + 'static,
186-
{
187-
fn new_span(&self, attrs: &Attributes, id: &Id, ctx: Context<S>) {
188-
let data = Data::new(attrs);
189-
let span = ctx.span(id).expect("in new_span but span does not exist");
190-
span.extensions_mut().insert(data);
191-
}
192-
193-
fn on_enter(&self, id: &tracing::Id, ctx: Context<S>) {
194-
let span = ctx.span(&id).expect("in on_enter but span does not exist");
201+
fn write_span_info<S: Subscriber + for<'span> LookupSpan<'span> + fmt::Debug>(
202+
&self,
203+
id: &tracing::Id,
204+
ctx: &Context<S>,
205+
entering: bool,
206+
style: SpanMode,
207+
) {
208+
let span = ctx
209+
.span(&id)
210+
.expect("in on_enter/on_exit but span does not exist");
195211
let ext = span.extensions();
196212
let data = ext.get::<Data>().expect("span does not have data");
197213

198214
let mut guard = self.bufs.lock().unwrap();
199215
let bufs = &mut *guard;
200216
let mut current_buf = &mut bufs.current_buf;
201217

202-
let indent = ctx.scope().count().saturating_sub(1);
218+
let indent = ctx.scope().count();
219+
let indent = if entering {
220+
indent.saturating_sub(1)
221+
} else {
222+
indent
223+
};
203224

204225
if self.config.targets {
205226
let target = span.metadata().target();
@@ -232,10 +253,47 @@ where
232253
)
233254
.unwrap();
234255

235-
bufs.indent_current(indent, &self.config);
256+
bufs.indent_current(indent, &self.config, style);
236257
let writer = self.make_writer.make_writer();
237258
bufs.flush_current_buf(writer)
238259
}
260+
}
261+
262+
impl<S, W> Layer<S> for HierarchicalLayer<W>
263+
where
264+
S: Subscriber + for<'span> LookupSpan<'span> + fmt::Debug,
265+
W: MakeWriter + 'static,
266+
{
267+
fn new_span(&self, attrs: &Attributes, id: &Id, ctx: Context<S>) {
268+
let data = Data::new(attrs);
269+
let span = ctx.span(id).expect("in new_span but span does not exist");
270+
span.extensions_mut().insert(data);
271+
}
272+
273+
fn on_enter(&self, id: &tracing::Id, ctx: Context<S>) {
274+
let mut iter = ctx.scope();
275+
let mut prev = iter.next();
276+
let mut cur = iter.next();
277+
loop {
278+
match (prev, cur) {
279+
(Some(span), Some(cur_elem)) => {
280+
if let Some(next) = iter.next() {
281+
prev = Some(cur_elem);
282+
cur = Some(next);
283+
} else {
284+
self.write_span_info(&span.id(), &ctx, false, SpanMode::PreOpen);
285+
break;
286+
}
287+
}
288+
// Iterator is not sealed, so we need to catch this case.
289+
(None, Some(_)) => break,
290+
// Just the new span on the stack
291+
(Some(_), None) => break,
292+
(None, None) => unreachable!("just entered span must exist"),
293+
}
294+
}
295+
self.write_span_info(id, &ctx, false, SpanMode::Open);
296+
}
239297

240298
fn on_event(&self, event: &Event<'_>, ctx: Context<S>) {
241299
let mut guard = self.bufs.lock().unwrap();
@@ -303,10 +361,19 @@ where
303361
bufs: &mut bufs,
304362
};
305363
event.record(&mut visitor);
306-
visitor.bufs.indent_current(indent, &self.config);
364+
visitor
365+
.bufs
366+
.indent_current(indent, &self.config, SpanMode::Event);
307367
let writer = self.make_writer.make_writer();
308368
bufs.flush_current_buf(writer)
309369
}
310370

311-
fn on_exit(&self, _id: &Id, _ctx: Context<S>) {}
371+
fn on_exit(&self, id: &Id, ctx: Context<S>) {
372+
if self.config.verbose_exit {
373+
self.write_span_info(id, &ctx, false, SpanMode::Close);
374+
if let Some(span) = ctx.scope().last() {
375+
self.write_span_info(&span.id(), &ctx, false, SpanMode::PostClose);
376+
}
377+
}
378+
}
312379
}

0 commit comments

Comments
 (0)