Skip to content

Commit 961c6f7

Browse files
committed
feat: Format and print time in which event has occurred
1 parent d398164 commit 961c6f7

File tree

3 files changed

+132
-4
lines changed

3 files changed

+132
-4
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ tracing-subscriber = { version = "0.3", default-features = false, features = ["r
1414
nu-ansi-term = "0.46.0"
1515
atty = "0.2"
1616
tracing-log = { version = "0.1", optional = true }
17+
time = { version = "0.3.20", features = ["formatting", "local-offset"] }
1718

1819
[features]
1920
default = ["tracing-log"]

src/lib.rs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
pub(crate) mod format;
2+
pub mod time;
23

4+
use crate::time::FormatTime;
35
use format::{Buffers, ColorLevel, Config, FmtEvent, SpanMode};
6+
47
use nu_ansi_term::{Color, Style};
58
use std::{
69
fmt::{self, Write as _},
@@ -43,13 +46,15 @@ impl Visit for Data {
4346
}
4447
}
4548
#[derive(Debug)]
46-
pub struct HierarchicalLayer<W = fn() -> io::Stderr>
49+
pub struct HierarchicalLayer<W = fn() -> io::Stderr, FT = ()>
4750
where
4851
W: for<'writer> MakeWriter<'writer> + 'static,
52+
FT: FormatTime,
4953
{
5054
make_writer: W,
5155
bufs: Mutex<Buffers>,
5256
config: Config,
57+
timer: FT,
5358
}
5459

5560
impl Default for HierarchicalLayer {
@@ -70,13 +75,15 @@ impl HierarchicalLayer<fn() -> io::Stderr> {
7075
make_writer: io::stderr,
7176
bufs: Mutex::new(Buffers::new()),
7277
config,
78+
timer: (),
7379
}
7480
}
7581
}
7682

77-
impl<W> HierarchicalLayer<W>
83+
impl<W, FT> HierarchicalLayer<W, FT>
7884
where
7985
W: for<'writer> MakeWriter<'writer> + 'static,
86+
FT: FormatTime,
8087
{
8188
/// Enables terminal colors, boldness and italics.
8289
pub fn with_ansi(self, ansi: bool) -> Self {
@@ -86,14 +93,15 @@ where
8693
}
8794
}
8895

89-
pub fn with_writer<W2>(self, make_writer: W2) -> HierarchicalLayer<W2>
96+
pub fn with_writer<W2>(self, make_writer: W2) -> HierarchicalLayer<W2, FT>
9097
where
9198
W2: for<'writer> MakeWriter<'writer>,
9299
{
93100
HierarchicalLayer {
94101
make_writer,
95102
config: self.config,
96103
bufs: self.bufs,
104+
timer: self.timer,
97105
}
98106
}
99107

@@ -113,6 +121,16 @@ where
113121
}
114122
}
115123

124+
/// Specifies how to measure and format time at which event has occurred.
125+
pub fn with_timer<FT2: FormatTime>(self, timer: FT2) -> HierarchicalLayer<W, FT2> {
126+
HierarchicalLayer {
127+
make_writer: self.make_writer,
128+
config: self.config,
129+
bufs: self.bufs,
130+
timer,
131+
}
132+
}
133+
116134
/// Whether to render the event and span targets. Usually targets are the module path to the
117135
/// event/span macro invocation.
118136
pub fn with_targets(self, targets: bool) -> Self {
@@ -274,10 +292,11 @@ where
274292
}
275293
}
276294

277-
impl<S, W> Layer<S> for HierarchicalLayer<W>
295+
impl<S, W, FT> Layer<S> for HierarchicalLayer<W, FT>
278296
where
279297
S: Subscriber + for<'span> LookupSpan<'span>,
280298
W: for<'writer> MakeWriter<'writer> + 'static,
299+
FT: FormatTime + 'static, // TODO(TmLev): Maybe it doesn't have to be static?
281300
{
282301
fn on_new_span(&self, attrs: &Attributes, id: &Id, ctx: Context<S>) {
283302
let span = ctx.span(id).expect("in new_span but span does not exist");
@@ -306,6 +325,13 @@ where
306325
let bufs = &mut *guard;
307326
let mut event_buf = &mut bufs.current_buf;
308327

328+
// Time.
329+
330+
// TODO(TmLev): Error handling?
331+
self.timer
332+
.format_time(&mut event_buf)
333+
.expect("Unable to write time to buffer");
334+
309335
// printing the indentation
310336
let indent = ctx
311337
.event_scope(event)

src/time.rs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/// A type that can measure and format the current time.
2+
///
3+
/// This trait is used by [HierarchicalLayer] to include a timestamp with each
4+
/// [Event] when it is logged.
5+
///
6+
/// Notable default implementations of this trait are [OffsetDateTime] and `()`.
7+
/// The former prints the current time as reported by [time's OffsetDateTime]
8+
/// (note that it may panic! make sure to check out the docs for the [OffsetDateTime]),
9+
/// and the latter does not print the current time at all.
10+
///
11+
/// Inspired by the [FormatTime] trait from [tracing-subscriber].
12+
///
13+
/// [HierarchicalLayer]: crate::HierarchicalLayer
14+
/// [Event]: tracing_core::Event
15+
/// [time's OffsetDateTime]: time::OffsetDateTime
16+
/// [FormatTime]: tracing_subscriber::fmt::time::FormatTime
17+
/// [tracing-subscriber]: tracing_subscriber
18+
// NB:
19+
// We can't use `tracing_subscriber::fmt::format::Writer`
20+
// since it doesn't have a public constructor.
21+
pub trait FormatTime {
22+
fn format_time(&self, w: &mut impl std::fmt::Write) -> std::fmt::Result;
23+
}
24+
25+
////////////////////////////////////////////////////////////////////////////////////////////////////
26+
27+
/// Default do-nothing time formatter.
28+
impl FormatTime for () {
29+
fn format_time(&self, _w: &mut impl std::fmt::Write) -> std::fmt::Result {
30+
Ok(())
31+
}
32+
}
33+
34+
////////////////////////////////////////////////////////////////////////////////////////////////////
35+
36+
/// Retrieve and print the current wall-clock time.
37+
///
38+
/// # Panics
39+
///
40+
/// Panics if [time crate] cannot determine the local UTC offset.
41+
///
42+
/// [time crate]: time
43+
// NB:
44+
// Can't use `tracing_subscriber::fmt::time::SystemTime` since it uses
45+
// private `datetime` module to format the actual time.
46+
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
47+
pub struct OffsetDateTime;
48+
49+
impl FormatTime for OffsetDateTime {
50+
fn format_time(&self, w: &mut impl std::fmt::Write) -> std::fmt::Result {
51+
let time = time::OffsetDateTime::now_local().expect("time offset cannot be determined");
52+
write!(w, "{}", time)
53+
}
54+
}
55+
56+
////////////////////////////////////////////////////////////////////////////////////////////////////
57+
58+
/// Retrieve and print the relative elapsed wall-clock time since an epoch.
59+
///
60+
/// The `Default` implementation for `Uptime` makes the epoch the current time.
61+
// NB: Copy-pasted from `tracing-subscriber::fmt::time::Uptime`.
62+
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
63+
pub struct Uptime {
64+
epoch: std::time::Instant,
65+
}
66+
67+
impl Default for Uptime {
68+
fn default() -> Self {
69+
Uptime {
70+
epoch: std::time::Instant::now(),
71+
}
72+
}
73+
}
74+
75+
impl From<std::time::Instant> for Uptime {
76+
fn from(epoch: std::time::Instant) -> Self {
77+
Uptime { epoch }
78+
}
79+
}
80+
81+
impl FormatTime for Uptime {
82+
fn format_time(&self, w: &mut impl std::fmt::Write) -> std::fmt::Result {
83+
let e = self.epoch.elapsed();
84+
write!(w, "{:4}.{:09}s", e.as_secs(), e.subsec_nanos())
85+
}
86+
}
87+
88+
////////////////////////////////////////////////////////////////////////////////////////////////////
89+
90+
impl<'a, F> FormatTime for &'a F
91+
where
92+
F: FormatTime,
93+
{
94+
fn format_time(&self, w: &mut impl std::fmt::Write) -> std::fmt::Result {
95+
(*self).format_time(w)
96+
}
97+
}
98+
99+
// NB:
100+
// Can't impl for `fn(&mut impl std::fmt::Write)` since impl trait is not allowed
101+
// outside of function and inherent method return types for now.

0 commit comments

Comments
 (0)