sc_tracing/logging/
fast_local_time.rs1use chrono::{Datelike, Timelike};
20use std::{cell::RefCell, fmt::Write, time::SystemTime};
21use tracing_subscriber::fmt::{format, time::FormatTime};
22
23#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
25pub struct FastLocalTime {
26 pub with_fractional: bool,
34}
35
36const TIMESTAMP_MAXIMUM_LENGTH: usize = 32;
38
39#[derive(Default)]
40struct InlineString {
41 buffer: [u8; TIMESTAMP_MAXIMUM_LENGTH],
42 length: usize,
43}
44
45impl Write for InlineString {
46 fn write_str(&mut self, s: &str) -> std::fmt::Result {
47 let new_length = self.length + s.len();
48 assert!(
49 new_length <= TIMESTAMP_MAXIMUM_LENGTH,
50 "buffer overflow when formatting the current timestamp"
51 );
52
53 self.buffer[self.length..new_length].copy_from_slice(s.as_bytes());
54 self.length = new_length;
55 Ok(())
56 }
57}
58
59impl InlineString {
60 fn as_str(&self) -> &str {
61 unsafe { std::str::from_utf8_unchecked(&self.buffer[..self.length]) }
64 }
65}
66
67#[derive(Default)]
68struct CachedTimestamp {
69 buffer: InlineString,
70 last_regenerated_at: u64,
71 last_fractional: u32,
72}
73
74thread_local! {
75 static TIMESTAMP: RefCell<CachedTimestamp> = Default::default();
76}
77
78impl FormatTime for FastLocalTime {
79 fn format_time(&self, w: &mut format::Writer<'_>) -> std::fmt::Result {
80 const TIMESTAMP_PARTIAL_LENGTH: usize = "0000-00-00 00:00:00".len();
81
82 let elapsed = SystemTime::now()
83 .duration_since(SystemTime::UNIX_EPOCH)
84 .expect("system time is never before UNIX epoch; qed");
85 let unix_time = elapsed.as_secs();
86
87 TIMESTAMP.with(|cache| {
88 let mut cache = cache.borrow_mut();
89
90 if cache.last_regenerated_at != unix_time {
92 let ts = chrono::Local::now();
93 let fractional = (ts.nanosecond() % 1_000_000_000) / 1_000_000;
94 cache.last_regenerated_at = unix_time;
95 cache.last_fractional = fractional;
96 cache.buffer.length = 0;
97
98 write!(
99 &mut cache.buffer,
100 "{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:03}",
101 ts.year(),
102 ts.month(),
103 ts.day(),
104 ts.hour(),
105 ts.minute(),
106 ts.second(),
107 fractional
108 )?;
109 } else if self.with_fractional {
110 let fractional = elapsed.subsec_millis();
111
112 if cache.last_fractional != fractional {
114 cache.last_fractional = fractional;
115 cache.buffer.length = TIMESTAMP_PARTIAL_LENGTH + 1;
116 write!(&mut cache.buffer, "{:03}", fractional)?;
117 }
118 }
119
120 let mut slice = cache.buffer.as_str();
121 if !self.with_fractional {
122 slice = &slice[..TIMESTAMP_PARTIAL_LENGTH];
123 }
124
125 w.write_str(slice)
126 })
127 }
128}
129
130impl std::fmt::Display for FastLocalTime {
131 fn fmt(&self, mut w: &mut std::fmt::Formatter) -> std::fmt::Result {
132 self.format_time(&mut format::Writer::new(&mut w))
133 }
134}
135
136#[test]
137fn test_format_fast_local_time() {
138 assert_eq!(
139 chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string().len(),
140 FastLocalTime { with_fractional: false }.to_string().len()
141 );
142 assert_eq!(
143 chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f").to_string().len(),
144 FastLocalTime { with_fractional: true }.to_string().len()
145 );
146
147 let now_1 = FastLocalTime { with_fractional: false }.to_string();
150 let expected = chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
151 let now_2 = FastLocalTime { with_fractional: false }.to_string();
152
153 assert!(
154 now_1 == expected || now_2 == expected,
155 "'{}' or '{}' should have been equal to '{}'",
156 now_1,
157 now_2,
158 expected
159 );
160}