tale_ndjson/multiplexed/
mod.rs1use std::io::{self, Write};
4use std::path::PathBuf;
5use std::time::Duration;
6
7use bytes::{Buf, BytesMut};
8use miette::{IntoDiagnostic, Result};
9
10mod batch;
11mod file_state;
12pub mod watcher;
13
14use crate::errors::TaleError;
15use crate::logpatterns::*;
16use crate::{config, process_line};
17
18pub fn handle_static(paths: Vec<PathBuf>) -> Result<()> {
20 use file_state::FileStateManager;
21
22 let mut file_manager = FileStateManager::new();
23
24 for path in &paths {
26 file_manager.add_file(path)?;
27 }
28
29 let all_lines = file_manager.read_all_lines()?;
31
32 let mut all_sourced_lines: Vec<SourcedLine<'_>> = Vec::new();
34
35 all_lines.iter().for_each(|(file_path, lines)| {
36 lines.iter().enumerate().for_each(|(line_num, line_content)| {
37 let parsed: Printable<'_> = {
38 match serde_json::from_str::<Printable<'_>>(line_content) {
39 Ok(printable) => printable,
40 Err(_) => {
41 Printable::Text(line_content.to_owned())
44 }
45 }
46 };
47 let sourced = SourcedLine::new(parsed, file_path.clone(), line_num);
48 all_sourced_lines.push(sourced);
49 });
50 });
51 all_sourced_lines.sort_by_key(|line| line.sort_key());
53
54 let mut outlock = io::stdout().lock();
56 let mut buffer = BytesMut::with_capacity(2048);
57
58 for wrapped in all_sourced_lines {
59 wrapped.write(&mut buffer);
60 outlock.write_all(buffer.chunk()).into_diagnostic()?;
61 outlock.write_all(&[0x0a; 1]).into_diagnostic()?; buffer.clear();
63 }
64
65 outlock.flush().into_diagnostic()?;
66 Ok(())
67}
68
69pub async fn handle_tailing(paths: Vec<PathBuf>) -> Result<()> {
71 use batch::{BatchConfig, BatchedLine, batched_with_config};
72 use watcher::{WatchEvent, create_watcher};
73
74 let mut watcher = create_watcher();
76
77 watcher.add_files(paths).await?;
79
80 let batch_config = BatchConfig {
82 batch_window: Duration::from_millis(config::batch_window_ms()),
83 max_batch_size: 1000,
84 _max_buffer_memory: 10 * 1024 * 1024,
85 };
86 let mut batch_processor = batched_with_config(batch_config);
87
88 let (line_sender, mut batch_receiver) = batch_processor.start().await?;
90
91 let mut watch_events = watcher.watch().await?;
93
94 let mut outlock = io::stdout().lock();
96 let mut buffer = BytesMut::with_capacity(2048);
97
98 loop {
100 tokio::select! {
101 watch_event = watch_events.recv() => {
103 match watch_event {
104 Some(WatchEvent::FileModified(path)) => {
105 if let Some(state) = watcher.file_manager_mut().get_state_mut(&path)
107 && let Ok(_changed) = state.refresh()
108 && let Ok(new_lines) = state.read_new_lines() {
109 for (line_num, line) in new_lines.into_iter().enumerate() {
111 let batched_line = BatchedLine::new(
112 line,
113 path.clone(),
114 line_num as u64
115 );
116 match line_sender.send(batched_line) {
117 Ok(v) => v,
118 Err(_) => return Err(TaleError::BatchedLineSender.into())
119 }
120 }
121 }
122 }
123 Some(WatchEvent::Error(err)) => {
124 eprintln!("Watch error: {err}");
125 }
126 Some(_) => {
127 }
129 None => {
130 break;
132 }
133 }
134 }
135
136 batch = batch_receiver.recv() => {
138 match batch {
139 Some(sorted_lines) => {
140 for batched_line in sorted_lines {
142 process_line(&batched_line.content, &mut buffer, &mut outlock)?;
143 }
144 outlock.flush().into_diagnostic()?;
145 }
146 None => {
147 break;
149 }
150 }
151 }
152 }
153 }
154
155 Ok(())
156}