cli: Add `rad node logs` sub-command
Prints the last `n` lines and follows the log.
This commit is contained in:
parent
e33661738c
commit
ba9c5997b7
|
|
@ -24,24 +24,31 @@ pub const HELP: Help = Help {
|
||||||
usage: r#"
|
usage: r#"
|
||||||
Usage
|
Usage
|
||||||
|
|
||||||
rad node events [<option>...]
|
|
||||||
rad node status [<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 stop [<option>...]
|
||||||
|
rad node logs [-n <lines>]
|
||||||
rad node connect <nid> <addr> [<option>...]
|
rad node connect <nid> <addr> [<option>...]
|
||||||
rad node routing [--rid <rid>] [--nid <nid>] [--json] [<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`.
|
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
|
--repos Show the tracked repositories table
|
||||||
--nodes Show the tracked nodes 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
|
General options
|
||||||
--json Output the routing table as json
|
|
||||||
|
--help Print help
|
||||||
"#,
|
"#,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -64,6 +71,9 @@ pub enum Operation {
|
||||||
daemon: bool,
|
daemon: bool,
|
||||||
options: Vec<OsString>,
|
options: Vec<OsString>,
|
||||||
},
|
},
|
||||||
|
Logs {
|
||||||
|
lines: usize,
|
||||||
|
},
|
||||||
Status,
|
Status,
|
||||||
Stop,
|
Stop,
|
||||||
Tracking {
|
Tracking {
|
||||||
|
|
@ -83,6 +93,7 @@ pub enum OperationName {
|
||||||
Connect,
|
Connect,
|
||||||
Events,
|
Events,
|
||||||
Routing,
|
Routing,
|
||||||
|
Logs,
|
||||||
Start,
|
Start,
|
||||||
#[default]
|
#[default]
|
||||||
Status,
|
Status,
|
||||||
|
|
@ -103,6 +114,7 @@ impl Args for Options {
|
||||||
let mut rid: Option<Id> = None;
|
let mut rid: Option<Id> = None;
|
||||||
let mut json: bool = false;
|
let mut json: bool = false;
|
||||||
let mut addr: Option<Address> = None;
|
let mut addr: Option<Address> = None;
|
||||||
|
let mut lines: usize = 10;
|
||||||
|
|
||||||
while let Some(arg) = parser.next()? {
|
while let Some(arg) = parser.next()? {
|
||||||
match arg {
|
match arg {
|
||||||
|
|
@ -112,6 +124,7 @@ impl Args for Options {
|
||||||
Value(val) if op.is_none() => match val.to_string_lossy().as_ref() {
|
Value(val) if op.is_none() => match val.to_string_lossy().as_ref() {
|
||||||
"connect" => op = Some(OperationName::Connect),
|
"connect" => op = Some(OperationName::Connect),
|
||||||
"events" => op = Some(OperationName::Events),
|
"events" => op = Some(OperationName::Events),
|
||||||
|
"logs" => op = Some(OperationName::Logs),
|
||||||
"routing" => op = Some(OperationName::Routing),
|
"routing" => op = Some(OperationName::Routing),
|
||||||
"start" => op = Some(OperationName::Start),
|
"start" => op = Some(OperationName::Start),
|
||||||
"status" => op = Some(OperationName::Status),
|
"status" => op = Some(OperationName::Status),
|
||||||
|
|
@ -151,6 +164,9 @@ impl Args for Options {
|
||||||
Long("daemon") | Short('d') if matches!(op, Some(OperationName::Start)) => {
|
Long("daemon") | Short('d') if matches!(op, Some(OperationName::Start)) => {
|
||||||
daemon = true;
|
daemon = true;
|
||||||
}
|
}
|
||||||
|
Short('n') if matches!(op, Some(OperationName::Logs)) => {
|
||||||
|
lines = parser.value()?.parse()?;
|
||||||
|
}
|
||||||
Value(val) if matches!(op, Some(OperationName::Start)) => {
|
Value(val) if matches!(op, Some(OperationName::Start)) => {
|
||||||
options.push(val);
|
options.push(val);
|
||||||
}
|
}
|
||||||
|
|
@ -165,6 +181,7 @@ impl Args for Options {
|
||||||
},
|
},
|
||||||
OperationName::Events => Operation::Events,
|
OperationName::Events => Operation::Events,
|
||||||
OperationName::Routing => Operation::Routing { rid, nid, json },
|
OperationName::Routing => Operation::Routing { rid, nid, json },
|
||||||
|
OperationName::Logs => Operation::Logs { lines },
|
||||||
OperationName::Start => Operation::Start { daemon, options },
|
OperationName::Start => Operation::Start { daemon, options },
|
||||||
OperationName::Status => Operation::Status,
|
OperationName::Status => Operation::Status,
|
||||||
OperationName::Stop => Operation::Stop,
|
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))?;
|
radicle::node::routing::Table::reader(profile.home.node().join(ROUTING_DB_FILE))?;
|
||||||
routing::run(&store, rid, nid, json)?;
|
routing::run(&store, rid, nid, json)?;
|
||||||
}
|
}
|
||||||
|
Operation::Logs { lines } => control::logs(lines)?,
|
||||||
Operation::Start { daemon, options } => control::start(daemon, options)?,
|
Operation::Start { daemon, options } => control::start(daemon, options)?,
|
||||||
Operation::Status => {
|
Operation::Status => {
|
||||||
let node = Node::new(profile.socket());
|
let node = Node::new(profile.socket());
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::fs::OpenOptions;
|
use std::fs::{File, OpenOptions};
|
||||||
use std::process;
|
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::{Address, Handle as _, NodeId};
|
||||||
use radicle::Node;
|
use radicle::Node;
|
||||||
|
|
@ -41,6 +43,45 @@ pub fn stop(node: Node) -> anyhow::Result<()> {
|
||||||
Ok(())
|
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<()> {
|
pub fn connect(node: &mut Node, nid: NodeId, addr: Address) -> anyhow::Result<()> {
|
||||||
let spinner = term::spinner(format!(
|
let spinner = term::spinner(format!(
|
||||||
"Connecting to {}@{addr}...",
|
"Connecting to {}@{addr}...",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue