carton_runner_interface/
slowlog.rs1use std::{
18 fmt::Display,
19 sync::{Arc, Mutex},
20 time::{Duration, Instant},
21};
22
23use tokio::sync::oneshot;
24
25pub struct Progress<T> {
26 progress: Option<T>,
27 total: Option<T>,
28}
29
30impl<T> Default for Progress<T> {
31 fn default() -> Self {
32 Self {
33 progress: Default::default(),
34 total: Default::default(),
35 }
36 }
37}
38
39pub struct SlowLog<T> {
40 done: Option<oneshot::Sender<()>>,
41
42 progress: Arc<Mutex<Progress<T>>>,
44}
45
46impl<T> SlowLog<T> {
47 pub fn done(&mut self) {
48 self.done.take().map(|d| d.send(()).unwrap());
49 }
50
51 pub fn set_total(&self, total: Option<T>) {
52 self.progress.lock().unwrap().total = total;
53 }
54
55 pub fn set_progress(&self, progress: Option<T>) {
56 self.progress.lock().unwrap().progress = progress;
57 }
58}
59
60pub struct WithoutProgress;
61impl Display for WithoutProgress {
62 fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 Ok(())
64 }
65}
66
67impl SlowLog<WithoutProgress> {
68 pub fn without_progress(self) -> Self {
71 self
72 }
73}
74
75impl<T> Drop for SlowLog<T> {
76 fn drop(&mut self) {
77 self.done();
78 }
79}
80
81pub async fn slowlog<S, T>(msg: S, interval_seconds: u64) -> SlowLog<T>
82where
83 S: Into<String>,
84 T: Send + 'static + Display,
85{
86 let msg = msg.into();
87
88 let progress = Arc::new(Mutex::new(Progress::default()));
90
91 let progress2 = progress.clone();
92 let (tx, mut rx) = oneshot::channel::<()>();
93 tokio::spawn(async move {
94 let start = Instant::now();
95 loop {
96 match tokio::time::timeout(Duration::from_secs(interval_seconds), &mut rx).await {
97 Ok(_) => break,
98 Err(_) => {
99 let p = {
101 let guard = progress2.lock().unwrap();
102 match (&guard.progress, &guard.total) {
103 (None, None) => "".to_string(),
104 (None, Some(total)) => format!(" ({total})"),
105 (Some(progress), None) => format!(" ({progress} / unknown)"),
106 (Some(progress), Some(total)) => format!(" ({progress} / {total})"),
107 }
108 };
109
110 let duration = start.elapsed().as_secs();
112 log::info!(target: "slowlog", "Task running for {duration} seconds: {msg}{p}")
113 }
114 }
115 }
116 });
117
118 SlowLog {
119 done: Some(tx),
120 progress,
121 }
122}