cli: Add `rad node logs` sub-command

Prints the last `n` lines and follows the log.
This commit is contained in:
Alexis Sellier 2023-06-29 14:58:11 +02:00
parent e33661738c
commit ba9c5997b7
No known key found for this signature in database
2 changed files with 69 additions and 10 deletions

View File

@ -24,24 +24,31 @@ pub const HELP: Help = Help {
usage: r#"
Usage
rad node events [<option>...]
rad node status [<option>...]
rad node start [--daemon|-d] [<option>...] [-- <node-option>...]
rad node start [--daemon | -d] [<option>...] [-- <node-option>...]
rad node stop [<option>...]
rad node logs [-n <lines>]
rad node connect <nid> <addr> [<option>...]
rad node routing [--rid <rid>] [--nid <nid>] [--json] [<option>...]
rad node tracking [--repos|--nodes] [<option>...]
rad node tracking [--repos | --nodes] [<option>...]
rad node events [<option>...]
For `<node-option>` see `radicle-node --help`.
Options
Routing options
--rid <rid> Show the routing table entries for the given RID
--nid <nid> Show the routing table entries for the given NID
--json Output the routing table as json
Tracking options
--help Print help
--repos Show the tracked repositories table
--nodes Show the tracked nodes table
--rid Show the routing table entries for the given RID
--nid Show the routing table entries for the given NID
--json Output the routing table as json
General options
--help Print help
"#,
};
@ -64,6 +71,9 @@ pub enum Operation {
daemon: bool,
options: Vec<OsString>,
},
Logs {
lines: usize,
},
Status,
Stop,
Tracking {
@ -83,6 +93,7 @@ pub enum OperationName {
Connect,
Events,
Routing,
Logs,
Start,
#[default]
Status,
@ -103,6 +114,7 @@ impl Args for Options {
let mut rid: Option<Id> = None;
let mut json: bool = false;
let mut addr: Option<Address> = None;
let mut lines: usize = 10;
while let Some(arg) = parser.next()? {
match arg {
@ -112,6 +124,7 @@ impl Args for Options {
Value(val) if op.is_none() => match val.to_string_lossy().as_ref() {
"connect" => op = Some(OperationName::Connect),
"events" => op = Some(OperationName::Events),
"logs" => op = Some(OperationName::Logs),
"routing" => op = Some(OperationName::Routing),
"start" => op = Some(OperationName::Start),
"status" => op = Some(OperationName::Status),
@ -151,6 +164,9 @@ impl Args for Options {
Long("daemon") | Short('d') if matches!(op, Some(OperationName::Start)) => {
daemon = true;
}
Short('n') if matches!(op, Some(OperationName::Logs)) => {
lines = parser.value()?.parse()?;
}
Value(val) if matches!(op, Some(OperationName::Start)) => {
options.push(val);
}
@ -165,6 +181,7 @@ impl Args for Options {
},
OperationName::Events => Operation::Events,
OperationName::Routing => Operation::Routing { rid, nid, json },
OperationName::Logs => Operation::Logs { lines },
OperationName::Start => Operation::Start { daemon, options },
OperationName::Status => Operation::Status,
OperationName::Stop => Operation::Stop,
@ -193,6 +210,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
radicle::node::routing::Table::reader(profile.home.node().join(ROUTING_DB_FILE))?;
routing::run(&store, rid, nid, json)?;
}
Operation::Logs { lines } => control::logs(lines)?,
Operation::Start { daemon, options } => control::start(daemon, options)?,
Operation::Status => {
let node = Node::new(profile.socket());

View File

@ -1,6 +1,8 @@
use std::ffi::OsString;
use std::fs::OpenOptions;
use std::process;
use std::fs::{File, OpenOptions};
use std::io::{BufRead, BufReader, Read, Seek, SeekFrom};
use std::time::Duration;
use std::{process, thread};
use radicle::node::{Address, Handle as _, NodeId};
use radicle::Node;
@ -41,6 +43,45 @@ pub fn stop(node: Node) -> anyhow::Result<()> {
Ok(())
}
pub fn logs(lines: usize) -> anyhow::Result<()> {
let home = radicle::profile::home()?;
let logs = home.node().join("node.log");
let mut file = BufReader::new(File::open(logs)?);
file.seek(SeekFrom::End(-1))?;
let mut tail = Vec::new();
let mut nlines = 0;
for i in (0..=file.stream_position()?).rev() {
let mut buf = [0; 1];
file.seek(SeekFrom::Start(i))?;
file.read_exact(&mut buf)?;
if buf[0] == b'\n' {
nlines += 1;
}
if nlines > lines {
break;
}
tail.push(buf[0]);
}
tail.reverse();
print!("{}", String::from_utf8_lossy(&tail));
file.seek(SeekFrom::End(0))?;
loop {
let mut line = String::new();
let len = file.read_line(&mut line)?;
if len == 0 {
thread::sleep(Duration::from_millis(250));
} else {
print!("{line}");
}
}
}
pub fn connect(node: &mut Node, nid: NodeId, addr: Address) -> anyhow::Result<()> {
let spinner = term::spinner(format!(
"Connecting to {}@{addr}...",