|
| 1 | +use crate::event::log::write::EventLogWriter; |
| 2 | +use crate::event::MonitoringEvent; |
| 3 | +use std::future::Future; |
| 4 | +use std::time::Duration; |
| 5 | +use tokio::sync::mpsc; |
| 6 | + |
| 7 | +pub type EventStreamSender = mpsc::UnboundedSender<MonitoringEvent>; |
| 8 | +pub type EventStreamReceiver = mpsc::UnboundedReceiver<MonitoringEvent>; |
| 9 | + |
| 10 | +fn create_event_stream_queue() -> (EventStreamSender, EventStreamReceiver) { |
| 11 | + mpsc::unbounded_channel() |
| 12 | +} |
| 13 | + |
| 14 | +/// Start event streaming into a log file. |
| 15 | +/// Streaming is running on another thread to reduce overhead and interference. |
| 16 | +/// |
| 17 | +/// Returns a future that resolves once the event streaming thread finishes. |
| 18 | +/// The thread will finish if there is some I/O error or if the `receiver` is closed. |
| 19 | +pub fn start_event_streaming( |
| 20 | + writer: EventLogWriter, |
| 21 | +) -> (EventStreamSender, impl Future<Output = ()>) { |
| 22 | + let (tx, rx) = create_event_stream_queue(); |
| 23 | + |
| 24 | + let handle = std::thread::spawn(move || { |
| 25 | + let process = streaming_process(writer, rx); |
| 26 | + |
| 27 | + let runtime = tokio::runtime::Builder::new_current_thread() |
| 28 | + .enable_all() |
| 29 | + .build() |
| 30 | + .unwrap(); |
| 31 | + |
| 32 | + if let Err(error) = runtime.block_on(process) { |
| 33 | + log::error!("Event streaming has ended with an error: {error:?}"); |
| 34 | + } else { |
| 35 | + log::debug!("Event streaming has finished successfully"); |
| 36 | + } |
| 37 | + }); |
| 38 | + let end_fut = async move { |
| 39 | + handle.join().expect("Event streaming thread has crashed"); |
| 40 | + }; |
| 41 | + (tx, end_fut) |
| 42 | +} |
| 43 | + |
| 44 | +const FLUSH_PERIOD: Duration = Duration::from_secs(30); |
| 45 | + |
| 46 | +async fn streaming_process( |
| 47 | + mut writer: EventLogWriter, |
| 48 | + mut receiver: EventStreamReceiver, |
| 49 | +) -> anyhow::Result<()> { |
| 50 | + let mut flush_fut = tokio::time::interval(FLUSH_PERIOD); |
| 51 | + |
| 52 | + loop { |
| 53 | + tokio::select! { |
| 54 | + _ = flush_fut.tick() => { |
| 55 | + writer.flush().await?; |
| 56 | + } |
| 57 | + res = receiver.recv() => { |
| 58 | + match res { |
| 59 | + Some(event) => { |
| 60 | + log::trace!("Event: {event:?}"); |
| 61 | + writer.store(event).await?; |
| 62 | + } |
| 63 | + None => break |
| 64 | + } |
| 65 | + } |
| 66 | + } |
| 67 | + } |
| 68 | + writer.finish().await?; |
| 69 | + Ok(()) |
| 70 | +} |
0 commit comments