feat(m1): journald watcher — streams journalctl JSON to event channel
This commit is contained in:
parent
d1bea47495
commit
e48536dfbe
1 changed files with 68 additions and 3 deletions
|
|
@ -1,6 +1,71 @@
|
|||
use super::SystemEvent;
|
||||
use super::{EventSource, SystemEvent, now_unix};
|
||||
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||
use tokio::process::Command;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
pub async fn watch(_tx: mpsc::Sender<SystemEvent>) {
|
||||
// implemented in Task 6
|
||||
pub async fn watch(tx: mpsc::Sender<SystemEvent>) {
|
||||
let mut child = match Command::new("journalctl")
|
||||
.args(["--follow", "--output=json", "--lines=0"])
|
||||
.stdout(std::process::Stdio::piped())
|
||||
.spawn()
|
||||
{
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
log::error!("journald watcher: failed to spawn journalctl: {e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let stdout = match child.stdout.take() {
|
||||
Some(s) => s,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let mut lines = BufReader::new(stdout).lines();
|
||||
while let Ok(Some(line)) = lines.next_line().await {
|
||||
if let Some(msg) = extract_message(&line) {
|
||||
let _ = tx
|
||||
.send(SystemEvent {
|
||||
source: EventSource::Journald,
|
||||
raw_line: msg,
|
||||
timestamp: now_unix(),
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_message(line: &str) -> Option<String> {
|
||||
let json: serde_json::Value = serde_json::from_str(line).ok()?;
|
||||
let msg = json.get("MESSAGE")?.as_str()?;
|
||||
if msg.is_empty() {
|
||||
return None;
|
||||
}
|
||||
Some(msg.to_string())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn extract_message_from_journald_json() {
|
||||
let line = r#"{"MESSAGE":"AUR build failed for foo","PRIORITY":"3","_COMM":"makepkg"}"#;
|
||||
assert_eq!(
|
||||
extract_message(line),
|
||||
Some("AUR build failed for foo".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_message_missing_returns_none() {
|
||||
let line = r#"{"PRIORITY":"6","_COMM":"systemd"}"#;
|
||||
assert_eq!(extract_message(line), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_message_empty_skipped() {
|
||||
let line = r#"{"MESSAGE":"","PRIORITY":"6"}"#;
|
||||
assert_eq!(extract_message(line), None);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue