Skip to content

Commit 7231a33

Browse files
zaharidichevhawkw
andauthored
feat(console): add retain-for cmd line arg (console-rs#119)
This PR adds a `retain-for` duration argument to the console. Additionally, I have modified the proto to not include the total_time for tasks,resources and async ops and instead contain `created_at` and `dropped_at` fields. A separate `console-util` crate has been added that contains the logic for parsing a duration string. Close console-rs#108 Signed-off-by: Zahari Dichev <zaharidichev@gmail.com> Co-authored-by: Eliza Weisman <eliza@buoyant.io>
1 parent 81cd611 commit 7231a33

File tree

12 files changed

+122
-262
lines changed

12 files changed

+122
-262
lines changed

console-subscriber/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ serde_json = "1"
2626
# The parking_lot dependency is renamed, because we want our `parking_lot`
2727
# feature to also enable `tracing-subscriber`'s parking_lot feature flag.
2828
parking_lot_crate = { package = "parking_lot", version = "0.11", optional = true }
29+
humantime = "2.1.0"
2930

3031
[dev-dependencies]
3132

console-subscriber/src/aggregator/id_data.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::{shrink::ShrinkMap, Closable, Id, Ids, ToProto};
1+
use super::{shrink::ShrinkMap, DroppedAt, Id, Ids, ToProto};
22
use std::collections::{HashMap, HashSet};
33
use std::ops::{Deref, DerefMut};
44
use std::time::{Duration, SystemTime};
@@ -72,7 +72,7 @@ impl<T> IdData<T> {
7272
}
7373
}
7474

75-
pub(crate) fn drop_closed<R: Closable>(
75+
pub(crate) fn drop_closed<R: DroppedAt>(
7676
&mut self,
7777
stats: &mut IdData<R>,
7878
now: SystemTime,
@@ -92,16 +92,16 @@ impl<T> IdData<T> {
9292

9393
let mut dropped_ids = HashSet::new();
9494
stats.data.retain_and_shrink(|id, (stats, dirty)| {
95-
if let Some(closed) = stats.closed_at() {
96-
let closed_for = now.duration_since(closed).unwrap_or_default();
95+
if let Some(dropped_at) = stats.dropped_at() {
96+
let dropped_for = now.duration_since(dropped_at).unwrap_or_default();
9797
let should_drop =
9898
// if there are any clients watching, retain all dirty tasks regardless of age
9999
(*dirty && has_watchers)
100-
|| closed_for > retention;
100+
|| dropped_for > retention;
101101
tracing::trace!(
102102
stats.id = ?id,
103-
stats.closed_at = ?closed,
104-
stats.closed_for = ?closed_for,
103+
stats.dropped_at = ?dropped_at,
104+
stats.dropped_for = ?dropped_for,
105105
stats.dirty = *dirty,
106106
should_drop,
107107
);

console-subscriber/src/aggregator/mod.rs

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,12 @@ pub(crate) struct Flush {
105105
pub(crate) triggered: AtomicBool,
106106
}
107107

108-
// An entity that at some point in time can be closed.
109-
// This generally refers to spans that have been closed
110-
// indicating that a task, async op or a resource is not
111-
// in use anymore
112-
pub(crate) trait Closable {
113-
fn closed_at(&self) -> Option<SystemTime>;
108+
// An entity (e.g Task, Resource) that at some point in
109+
// time can be dropped. This generally refers to spans that
110+
// have been closed indicating that a task, async op or a
111+
// resource is not in use anymore
112+
pub(crate) trait DroppedAt {
113+
fn dropped_at(&self) -> Option<SystemTime>;
114114
}
115115

116116
pub(crate) trait ToProto {
@@ -164,7 +164,7 @@ struct FieldKey {
164164
#[derive(Default)]
165165
struct ResourceStats {
166166
created_at: Option<SystemTime>,
167-
closed_at: Option<SystemTime>,
167+
dropped_at: Option<SystemTime>,
168168
attributes: HashMap<FieldKey, Attribute>,
169169
}
170170

@@ -178,7 +178,7 @@ struct Task {
178178
struct TaskStats {
179179
// task stats
180180
created_at: Option<SystemTime>,
181-
closed_at: Option<SystemTime>,
181+
dropped_at: Option<SystemTime>,
182182

183183
// waker stats
184184
wakes: u64,
@@ -200,27 +200,27 @@ struct AsyncOp {
200200
#[derive(Default)]
201201
struct AsyncOpStats {
202202
created_at: Option<SystemTime>,
203-
closed_at: Option<SystemTime>,
203+
dropped_at: Option<SystemTime>,
204204
resource_id: Option<Id>,
205205
task_id: Option<Id>,
206206
poll_stats: PollStats,
207207
}
208208

209-
impl Closable for ResourceStats {
210-
fn closed_at(&self) -> Option<SystemTime> {
211-
self.closed_at
209+
impl DroppedAt for ResourceStats {
210+
fn dropped_at(&self) -> Option<SystemTime> {
211+
self.dropped_at
212212
}
213213
}
214214

215-
impl Closable for TaskStats {
216-
fn closed_at(&self) -> Option<SystemTime> {
217-
self.closed_at
215+
impl DroppedAt for TaskStats {
216+
fn dropped_at(&self) -> Option<SystemTime> {
217+
self.dropped_at
218218
}
219219
}
220220

221-
impl Closable for AsyncOpStats {
222-
fn closed_at(&self) -> Option<SystemTime> {
223-
self.closed_at
221+
impl DroppedAt for AsyncOpStats {
222+
fn dropped_at(&self) -> Option<SystemTime> {
223+
self.dropped_at
224224
}
225225
}
226226

@@ -270,7 +270,7 @@ impl Default for TaskStats {
270270
fn default() -> Self {
271271
TaskStats {
272272
created_at: None,
273-
closed_at: None,
273+
dropped_at: None,
274274
wakes: 0,
275275
waker_clones: 0,
276276
waker_drops: 0,
@@ -643,15 +643,15 @@ impl Aggregator {
643643
Event::Close { id, at } => {
644644
let id = self.ids.id_for(id);
645645
if let Some(mut task_stats) = self.task_stats.update(&id) {
646-
task_stats.closed_at = Some(at);
646+
task_stats.dropped_at = Some(at);
647647
}
648648

649649
if let Some(mut resource_stats) = self.resource_stats.update(&id) {
650-
resource_stats.closed_at = Some(at);
650+
resource_stats.dropped_at = Some(at);
651651
}
652652

653653
if let Some(mut async_op_stats) = self.async_op_stats.update(&id) {
654-
async_op_stats.closed_at = Some(at);
654+
async_op_stats.dropped_at = Some(at);
655655
}
656656
}
657657

@@ -871,7 +871,7 @@ impl ToProto for TaskStats {
871871
proto::tasks::Stats {
872872
poll_stats: Some(self.poll_stats.to_proto()),
873873
created_at: self.created_at.map(Into::into),
874-
total_time: total_time(self.created_at, self.closed_at).map(Into::into),
874+
dropped_at: self.dropped_at.map(Into::into),
875875
wakes: self.wakes,
876876
waker_clones: self.waker_clones,
877877
self_wakes: self.self_wakes,
@@ -901,7 +901,7 @@ impl ToProto for ResourceStats {
901901
let attributes = self.attributes.values().cloned().collect();
902902
proto::resources::Stats {
903903
created_at: self.created_at.map(Into::into),
904-
total_time: total_time(self.created_at, self.closed_at).map(Into::into),
904+
dropped_at: self.dropped_at.map(Into::into),
905905
attributes,
906906
}
907907
}
@@ -926,8 +926,7 @@ impl ToProto for AsyncOpStats {
926926
proto::async_ops::Stats {
927927
poll_stats: Some(self.poll_stats.to_proto()),
928928
created_at: self.created_at.map(Into::into),
929-
total_time: total_time(self.created_at, self.closed_at).map(Into::into),
930-
929+
dropped_at: self.dropped_at.map(Into::into),
931930
resource_id: self.resource_id.map(Into::into),
932931
task_id: self.task_id.map(Into::into),
933932
}
@@ -986,12 +985,6 @@ fn serialize_histogram(histogram: &Histogram<u64>) -> Result<Vec<u8>, V2Serializ
986985
Ok(buf)
987986
}
988987

989-
fn total_time(created_at: Option<SystemTime>, closed_at: Option<SystemTime>) -> Option<Duration> {
990-
let end = closed_at?;
991-
let start = created_at?;
992-
end.duration_since(start).ok()
993-
}
994-
995988
fn update_attribute(attribute: &mut Attribute, update: AttributeUpdate) {
996989
use proto::field::Value::*;
997990
let attribute_val = attribute.field.as_mut().and_then(|a| a.value.as_mut());

console-subscriber/src/builder.rs

Lines changed: 2 additions & 178 deletions
Original file line numberDiff line numberDiff line change
@@ -153,187 +153,11 @@ impl Builder {
153153

154154
fn duration_from_env(var_name: &str) -> Option<Duration> {
155155
let var = std::env::var(var_name).ok()?;
156-
match parse_duration(&var) {
157-
Ok(dur) => Some(dur),
156+
match var.parse::<humantime::Duration>() {
157+
Ok(dur) => Some(dur.into()),
158158
Err(e) => panic!(
159159
"failed to parse a duration from `{}={:?}`: {}",
160160
var_name, var, e
161161
),
162162
}
163163
}
164-
165-
fn parse_duration(s: &str) -> Result<Duration, Box<dyn std::error::Error>> {
166-
let s = s.trim();
167-
if let Some(s) = s
168-
.strip_suffix('h')
169-
.or_else(|| s.strip_suffix("hour"))
170-
.or_else(|| s.strip_suffix("hours"))
171-
{
172-
let s = s.trim();
173-
return Ok(s
174-
.parse::<u64>()
175-
.map(|hours| Duration::from_secs(hours * 60 * 60))
176-
.or_else(|_| {
177-
s.parse::<f64>()
178-
.map(|hours| Duration::from_secs_f64(hours * 60.0 * 60.0))
179-
})?);
180-
}
181-
182-
if let Some(s) = s
183-
.strip_suffix('m')
184-
.or_else(|| s.strip_suffix("min"))
185-
.or_else(|| s.strip_suffix("mins"))
186-
.or_else(|| s.strip_suffix("minute"))
187-
.or_else(|| s.strip_suffix("minutes"))
188-
{
189-
let s = s.trim();
190-
return Ok(s
191-
.parse::<u64>()
192-
.map(|mins| Duration::from_secs(mins * 60))
193-
.or_else(|_| {
194-
s.parse::<f64>()
195-
.map(|mins| Duration::from_secs_f64(mins * 60.0))
196-
})?);
197-
}
198-
199-
if let Some(s) = s.strip_suffix("ms") {
200-
return Ok(Duration::from_millis(s.trim().parse()?));
201-
}
202-
203-
if let Some(s) = s.strip_suffix("us") {
204-
return Ok(Duration::from_micros(s.trim().parse()?));
205-
}
206-
207-
// Order matters here -- we have to try `ns` for nanoseconds after we try
208-
// minutes, because `mins` ends in `ns`.
209-
if let Some(s) = s.strip_suffix("ns") {
210-
return Ok(Duration::from_nanos(s.trim().parse()?));
211-
}
212-
213-
if let Some(s) = s
214-
.strip_suffix("sec")
215-
.or_else(|| s.strip_suffix("secs"))
216-
.or_else(|| s.strip_suffix("seconds"))
217-
// Order matters here -- we have to try `s` for seconds _last_, because
218-
// every other plural and subsecond unit also ends in `s`...
219-
.or_else(|| s.strip_suffix('s'))
220-
{
221-
let s = s.trim();
222-
return Ok(s
223-
.parse::<u64>()
224-
.map(Duration::from_secs)
225-
.or_else(|_| s.parse::<f64>().map(Duration::from_secs_f64))?);
226-
}
227-
228-
Err("expected an integer followed by one of {`ns`, `us`, `ms`, `s`, `sec`, `m`, `min`, `h`, `hours`}".into())
229-
}
230-
231-
#[cfg(test)]
232-
mod tests {
233-
use super::*;
234-
235-
fn test_parse_durations(expected: Duration, inputs: &[&str]) {
236-
for input in inputs {
237-
println!("trying: parse_duration({:?}) -> {:?}", input, expected);
238-
match parse_duration(input) {
239-
Err(e) => panic!(
240-
"parse_duration({:?}) -> {} (expected {:?})",
241-
input, e, expected
242-
),
243-
Ok(dur) => assert_eq!(
244-
dur, expected,
245-
"parse_duration({:?}) -> {:?} (expected {:?})",
246-
input, dur, expected
247-
),
248-
}
249-
}
250-
}
251-
252-
#[test]
253-
fn parse_hours() {
254-
test_parse_durations(
255-
Duration::from_secs(3 * 60 * 60),
256-
&["3h", "3 h", " 3 h", "3 hours", "3hours"],
257-
)
258-
}
259-
260-
#[test]
261-
fn parse_mins() {
262-
test_parse_durations(
263-
Duration::from_secs(10 * 60),
264-
&[
265-
"10m",
266-
"10 m",
267-
"10 m",
268-
"10 minutes",
269-
"10minutes",
270-
" 10 minutes",
271-
"10 min",
272-
" 10 min",
273-
"10min",
274-
],
275-
)
276-
}
277-
278-
#[test]
279-
fn parse_secs() {
280-
test_parse_durations(
281-
Duration::from_secs(10),
282-
&[
283-
"10s",
284-
"10 s",
285-
"10 s",
286-
"10 seconds",
287-
"10seconds",
288-
" 10 seconds",
289-
"10 sec",
290-
" 10 sec",
291-
"10sec",
292-
],
293-
)
294-
}
295-
296-
#[test]
297-
fn parse_fractional_hours() {
298-
test_parse_durations(
299-
Duration::from_millis(1500 * 60 * 60),
300-
&["1.5h", "1.5 h", " 1.5 h", "1.5 hours", "1.5hours"],
301-
)
302-
}
303-
304-
#[test]
305-
fn parse_fractional_mins() {
306-
test_parse_durations(
307-
Duration::from_millis(1500 * 60),
308-
&[
309-
"1.5m",
310-
"1.5 m",
311-
"1.5 m",
312-
"1.5 minutes",
313-
"1.5 minutes",
314-
" 1.5 minutes",
315-
"1.5 min",
316-
" 1.5 min",
317-
"1.5min",
318-
],
319-
)
320-
}
321-
322-
#[test]
323-
fn parse_fractional_secs() {
324-
test_parse_durations(
325-
Duration::from_millis(1500),
326-
&[
327-
"1.5s",
328-
"1.5 s",
329-
"1.5 s",
330-
"1.5 seconds",
331-
"1.5 seconds",
332-
" 1.5 seconds",
333-
"1.5 sec",
334-
" 1.5 sec",
335-
"1.5sec",
336-
],
337-
)
338-
}
339-
}

console/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@ color-eyre = { version = "0.5", features = ["issue-url"] }
2525
hdrhistogram = { version = "7.3.0", default-features = false, features = ["serialization"] }
2626
h2 = "0.3"
2727
regex = "1.5"
28-
once_cell = "1.8"
28+
once_cell = "1.8"
29+
humantime = "2.1.0"

0 commit comments

Comments
 (0)