Skip to content

Commit

Permalink
feat: option to display all logs
Browse files Browse the repository at this point in the history
  • Loading branch information
robertpsoane committed Jul 11, 2024
1 parent c4f79f6 commit da9bb8e
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 45 deletions.
52 changes: 40 additions & 12 deletions src/docker/logs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,42 @@ use futures::{Stream, StreamExt};

use super::container::DockerContainer;

#[derive(Debug, Clone)]
pub struct StreamOptions {
pub tail: String,
pub all: bool,
}

impl Default for StreamOptions {
fn default() -> Self {
Self {
tail: "50".into(),
all: false,
}
}

Check warning on line 18 in src/docker/logs.rs

View check run for this annotation

Codecov / codecov/patch

src/docker/logs.rs#L13-L18

Added lines #L13 - L18 were not covered by tests
}

impl From<StreamOptions> for LogsOptions<String> {
fn from(val: StreamOptions) -> Self {
let mut opts = LogsOptions::<String> {
follow: true,
stdout: true,
stderr: true,
tail: val.tail,
..Default::default()
};

if val.all {
opts.tail = "all".into()
}

Check warning on line 33 in src/docker/logs.rs

View check run for this annotation

Codecov / codecov/patch

src/docker/logs.rs#L22-L33

Added lines #L22 - L33 were not covered by tests

opts
}

Check warning on line 36 in src/docker/logs.rs

View check run for this annotation

Codecov / codecov/patch

src/docker/logs.rs#L35-L36

Added lines #L35 - L36 were not covered by tests
}

#[derive(Debug, Clone)]
pub struct DockerLogs {
container: DockerContainer,
pub container: DockerContainer,
}

impl DockerLogs {
Expand All @@ -17,18 +50,13 @@ impl DockerLogs {
Self::new(container)
}

pub fn get_log_stream(&self, docker: &bollard::Docker, tail: u8) -> impl Stream<Item = String> {
pub fn get_log_stream(
&self,
docker: &bollard::Docker,
stream_options: StreamOptions,
) -> impl Stream<Item = String> {

Check warning on line 57 in src/docker/logs.rs

View check run for this annotation

Codecov / codecov/patch

src/docker/logs.rs#L53-L57

Added lines #L53 - L57 were not covered by tests
let logstream = docker
.logs(
&self.container.id,
Some(LogsOptions::<String> {
follow: true,
stdout: true,
stderr: true,
tail: tail.to_string(),
..Default::default()
}),
)
.logs(&self.container.id, Some(stream_options.into()))

Check warning on line 59 in src/docker/logs.rs

View check run for this annotation

Codecov / codecov/patch

src/docker/logs.rs#L59

Added line #L59 was not covered by tests
.filter_map(|res| async move {
Some(match res {
Ok(r) => format!("{r}"),
Expand Down
110 changes: 77 additions & 33 deletions src/pages/logs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ use tokio::sync::mpsc::Sender;

use crate::config::Config;
use crate::context::AppContext;
use crate::docker::logs::StreamOptions;
use crate::{
components::help::{PageHelp, PageHelpBuilder},
docker::{container::DockerContainer, logs::DockerLogs},
docker::logs::DockerLogs,
events::{message::MessageResponse, Key, Message, Transition},
traits::{Close, Component, Page},
};
Expand All @@ -22,6 +23,7 @@ const NAME: &str = "Logs";

const ESC_KEY: Key = Key::Esc;
const J_KEY: Key = Key::Char('j');
const A_KEY: Key = Key::Char('a');
const UP_KEY: Key = Key::Up;
const K_KEY: Key = Key::Char('k');
const DOWN_KEY: Key = Key::Down;
Expand All @@ -34,14 +36,14 @@ pub struct Logs {
config: Box<Config>,
docker: bollard::Docker,
tx: Sender<Message<Key, Transition>>,
container: Option<DockerContainer>,
logs: Option<DockerLogs>,
page_help: Arc<Mutex<PageHelp>>,
log_messages: Arc<Mutex<Vec<String>>>,
log_streamer_handle: Option<JoinHandle<()>>,
list_state: ListState,
auto_scroll: bool,
next: Option<Transition>,
stream_options: StreamOptions,
}

impl Logs {
Expand All @@ -50,12 +52,11 @@ impl Logs {
tx: Sender<Message<Key, Transition>>,
config: Box<Config>,
) -> Self {
let page_help = Self::build_page_help(config.clone()).build();
let page_help = Self::build_page_help(NAME, config.clone()).build();

Check warning on line 55 in src/pages/logs.rs

View check run for this annotation

Codecov / codecov/patch

src/pages/logs.rs#L55

Added line #L55 was not covered by tests

Self {
config,
docker,
container: None,
logs: None,
tx,
page_help: Arc::new(Mutex::new(page_help)),
Expand All @@ -64,23 +65,34 @@ impl Logs {
list_state: ListState::default(),
auto_scroll: true,
next: None,
stream_options: StreamOptions::default(),

Check warning on line 68 in src/pages/logs.rs

View check run for this annotation

Codecov / codecov/patch

src/pages/logs.rs#L68

Added line #L68 was not covered by tests
}
}

fn build_page_help(config: Box<Config>) -> PageHelpBuilder {
PageHelpBuilder::new(NAME.into(), config)
fn build_page_help(name: &str, config: Box<Config>) -> PageHelpBuilder {
PageHelpBuilder::new(format!("{} ({})", NAME, name), config)

Check warning on line 73 in src/pages/logs.rs

View check run for this annotation

Codecov / codecov/patch

src/pages/logs.rs#L72-L73

Added lines #L72 - L73 were not covered by tests
.add_input(format!("{ESC_KEY}"), "back".into())
.add_input(format!("{G_KEY}"), "top".into())
.add_input(format!("{SHIFT_G_KEY}"), "bottom".into())
.add_input(format!("{A_KEY}"), "<all>".into())

Check warning on line 77 in src/pages/logs.rs

View check run for this annotation

Codecov / codecov/patch

src/pages/logs.rs#L77

Added line #L77 was not covered by tests
}

fn activate_auto_scroll(&mut self) {
if self.auto_scroll {
return;
}
self.auto_scroll = true;

self.page_help = Arc::new(Mutex::new(
Self::build_page_help(self.config.clone()).build(),
Self::build_page_help(
if let Some(l) = &self.logs {
&l.container.names

Check warning on line 89 in src/pages/logs.rs

View check run for this annotation

Codecov / codecov/patch

src/pages/logs.rs#L88-L89

Added lines #L88 - L89 were not covered by tests
} else {
""

Check warning on line 91 in src/pages/logs.rs

View check run for this annotation

Codecov / codecov/patch

src/pages/logs.rs#L91

Added line #L91 was not covered by tests
},
self.config.clone(),
)
.build(),

Check warning on line 95 in src/pages/logs.rs

View check run for this annotation

Codecov / codecov/patch

src/pages/logs.rs#L93-L95

Added lines #L93 - L95 were not covered by tests
));
}

Expand All @@ -90,11 +102,49 @@ impl Logs {
}
self.auto_scroll = false;
self.page_help = Arc::new(Mutex::new(
Self::build_page_help(self.config.clone())
.add_input(format!("{SPACE_BAR}"), "auto-scroll".into())
.build(),
Self::build_page_help(
if let Some(l) = &self.logs {
&l.container.names

Check warning on line 107 in src/pages/logs.rs

View check run for this annotation

Codecov / codecov/patch

src/pages/logs.rs#L106-L107

Added lines #L106 - L107 were not covered by tests
} else {
""

Check warning on line 109 in src/pages/logs.rs

View check run for this annotation

Codecov / codecov/patch

src/pages/logs.rs#L109

Added line #L109 was not covered by tests
},
self.config.clone(),
)
.add_input(format!("{SPACE_BAR}"), "auto-scroll".into())
.build(),

Check warning on line 114 in src/pages/logs.rs

View check run for this annotation

Codecov / codecov/patch

src/pages/logs.rs#L111-L114

Added lines #L111 - L114 were not covered by tests
));
}

fn abort(&mut self) {
if let Some(handle) = &self.log_streamer_handle {
handle.abort()
}
self.log_streamer_handle = None;
self.log_messages = Arc::new(Mutex::new(vec![String::new()]));
self.logs = None;
}

Check warning on line 125 in src/pages/logs.rs

View check run for this annotation

Codecov / codecov/patch

src/pages/logs.rs#L118-L125

Added lines #L118 - L125 were not covered by tests

async fn start_log_stream(&mut self) -> Result<()> {
self.auto_scroll = true;
if let Some(logs) = &self.logs {
let mut logs_stream = logs.get_log_stream(&self.docker, self.stream_options.clone());
let tx = self.tx.clone();
let log_messages = self.log_messages.clone();

self.log_streamer_handle = Some(tokio::spawn(async move {
while let Some(v) = logs_stream.next().await {
{
log_messages.lock().unwrap().push(v);
}
let _ = tx.send(Message::Tick).await;

Check warning on line 139 in src/pages/logs.rs

View check run for this annotation

Codecov / codecov/patch

src/pages/logs.rs#L127-L139

Added lines #L127 - L139 were not covered by tests
}
}));
} else {
bail!("unable to stream logs without logs to stream");

Check warning on line 143 in src/pages/logs.rs

View check run for this annotation

Codecov / codecov/patch

src/pages/logs.rs#L141-L143

Added lines #L141 - L143 were not covered by tests
}

Ok(())
}

Check warning on line 147 in src/pages/logs.rs

View check run for this annotation

Codecov / codecov/patch

src/pages/logs.rs#L146-L147

Added lines #L146 - L147 were not covered by tests
}

#[async_trait::async_trait]
Expand All @@ -104,11 +154,13 @@ impl Page for Logs {
Key::Esc => {
let transition = if let Some(t) = self.next.clone() {
t
} else {
} else if let Some(logs) = &self.logs {

Check warning on line 157 in src/pages/logs.rs

View check run for this annotation

Codecov / codecov/patch

src/pages/logs.rs#L157

Added line #L157 was not covered by tests
Transition::ToContainerPage(AppContext {
docker_container: self.container.clone(),
docker_container: Some(logs.container.clone()),

Check warning on line 159 in src/pages/logs.rs

View check run for this annotation

Codecov / codecov/patch

src/pages/logs.rs#L159

Added line #L159 was not covered by tests
..Default::default()
})
} else {
Transition::ToContainerPage(AppContext::default())

Check warning on line 163 in src/pages/logs.rs

View check run for this annotation

Codecov / codecov/patch

src/pages/logs.rs#L162-L163

Added lines #L162 - L163 were not covered by tests
};

self.tx.send(Message::Transition(transition)).await?;
Expand Down Expand Up @@ -136,6 +188,16 @@ impl Page for Logs {
self.activate_auto_scroll();
MessageResponse::Consumed
}
A_KEY => {
self.stream_options.all = true;
let logs = self.logs.clone();
self.abort();
if let Some(l) = logs {
self.logs = Some(DockerLogs::from(l.container.clone()));
}
self.start_log_stream().await?;
MessageResponse::Consumed
}

Check warning on line 200 in src/pages/logs.rs

View check run for this annotation

Codecov / codecov/patch

src/pages/logs.rs#L191-L200

Added lines #L191 - L200 were not covered by tests
_ => MessageResponse::NotConsumed,
};

Expand All @@ -148,31 +210,16 @@ impl Page for Logs {
async fn initialise(&mut self, cx: AppContext) -> Result<()> {
if let Some(container) = cx.clone().docker_container {
self.logs = Some(DockerLogs::from(container.clone()));
self.container = Some(container);
} else {
bail!("no docker container")
}
self.auto_scroll = true;
if let Some(logs) = &self.logs {
let mut logs_stream = logs.get_log_stream(&self.docker, 50);
let tx = self.tx.clone();
let log_messages = self.log_messages.clone();
self.log_streamer_handle = Some(tokio::spawn(async move {
while let Some(v) = logs_stream.next().await {
{
log_messages.lock().unwrap().push(v);
}
let _ = tx.send(Message::Tick).await;
}
}));
} else {
bail!("unable to stream logs without logs to stream");
}

if let Some(t) = cx.next() {
self.next = Some(t)
}

self.start_log_stream().await?;

Check warning on line 222 in src/pages/logs.rs

View check run for this annotation

Codecov / codecov/patch

src/pages/logs.rs#L221-L222

Added lines #L221 - L222 were not covered by tests
Ok(())
}

Expand All @@ -184,10 +231,7 @@ impl Page for Logs {
#[async_trait::async_trait]
impl Close for Logs {
async fn close(&mut self) -> Result<()> {
if let Some(handle) = &self.log_streamer_handle {
handle.abort()
}
self.log_streamer_handle = None;
self.abort();

Check warning on line 234 in src/pages/logs.rs

View check run for this annotation

Codecov / codecov/patch

src/pages/logs.rs#L234

Added line #L234 was not covered by tests
self.logs = None;
self.log_messages = Arc::new(Mutex::new(vec![]));
Ok(())
Expand Down

0 comments on commit da9bb8e

Please sign in to comment.