diff --git a/isototest/Cargo.toml b/isototest/Cargo.toml index b2e4b7d..d81075e 100644 --- a/isototest/Cargo.toml +++ b/isototest/Cargo.toml @@ -9,8 +9,14 @@ license = "GPL-2.0" [dependencies] image = "0.25.2" +log = "0.4.22" tokio = "1.38.1" vnc-rs = "0.5.1" +env_logger = { version= "0.10", optional=true } [dev-dependencies] mockito = "1.4.0" + +[features] +# Feature to enable default logging configuration +default-logging = ["env_logger"] diff --git a/isototest/src/action/keyboard.rs b/isototest/src/action/keyboard.rs index 75085bd..b5aa00a 100644 --- a/isototest/src/action/keyboard.rs +++ b/isototest/src/action/keyboard.rs @@ -11,8 +11,10 @@ extern crate proc_macro; use std::{thread::sleep, time::Duration}; +use log::info; use vnc::{client::VncClient, ClientKeyEvent, VncError, X11Event}; +use crate::logging::LOG_TARGET; use crate::types::{KeyCode, KeyEventType}; /// Sleep. @@ -52,6 +54,7 @@ pub async fn write_to_console( framerate: Option, ) -> Result<(), VncError> { // Translate each character to a keycode + info!(target: LOG_TARGET, "Sending text '{}' with intervall of {}FPS....", text, framerate.unwrap_or(30 as f64)); let mut keycode: u32; for ch in text.chars() { @@ -72,6 +75,7 @@ pub async fn write_to_console( press_button(client, modifier, KeyEventType::Release, framerate).await?; } } + info!(target: LOG_TARGET, "Text '{}' sent.", text); Ok(()) } diff --git a/isototest/src/action/view.rs b/isototest/src/action/view.rs index 05060ea..01aab50 100644 --- a/isototest/src/action/view.rs +++ b/isototest/src/action/view.rs @@ -10,6 +10,10 @@ use image::ImageBuffer; use image::DynamicImage::ImageRgba8; use vnc::{Rect, VncClient, VncError, VncEvent, X11Event}; +use log::{error, info, warn}; + +use crate::logging::LOG_TARGET; + /// Receive a screenshot of the remote machine. /// /// # Parameters @@ -36,6 +40,7 @@ pub async fn read_screen( resolution: Option<(u32, u32)>, timeout: Duration, ) -> Result<(u32, u32), VncError> { + info!(target: LOG_TARGET, "Requesting screenshot..."); // Request screen update. client.input(X11Event::Refresh).await?; @@ -47,21 +52,23 @@ pub async fn read_screen( // **This will cause issues, if you try to use this functionality a second time.** match resolution { Some((x, y)) => { + info!(target: LOG_TARGET, "Resolution provided; proceeding..."); width = Some(x); height = Some(y); } None => match client.recv_event().await? { VncEvent::SetResolution(screen) => { - println!("Screen resolution: {}x{}", screen.width, screen.height); + info!(target: LOG_TARGET, "Resolution received. Screen resolution: {}x{}", screen.width, screen.height); width = Some(screen.width as u32); height = Some(screen.height as u32); client.input(X11Event::Refresh).await?; } _ => { + error!(target: LOG_TARGET, "Failed to retrieve screen resolution. Aborting..."); return Err(VncError::General( "[error] No resolution found!".to_string(), - )) + )); } }, } @@ -88,8 +95,8 @@ pub async fn read_screen( return Err(VncError::General(e)); } x => { - println!( - "[warning] Function 'read_screen' got unexpected event '{:?}'.", + warn!(target: LOG_TARGET, + "Function 'read_screen' got unexpected event '{:?}'.", x ); break; @@ -97,6 +104,7 @@ pub async fn read_screen( }, None => { if idle_timer.elapsed() >= timeout { + warn!(target: LOG_TARGET, "Timeout while waiting for VNC Event."); break; } } @@ -135,6 +143,6 @@ pub async fn read_screen( .save_with_format(path, ImageFormat::Png) .unwrap(); - println!("Screenshot saved to {}", file_path); + info!(target: LOG_TARGET, "Screenshot saved to '{}'", file_path); Ok((width.unwrap(), height.unwrap())) } diff --git a/isototest/src/connection.rs b/isototest/src/connection.rs index 566b022..0e67d3b 100644 --- a/isototest/src/connection.rs +++ b/isototest/src/connection.rs @@ -2,9 +2,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later //! This module handles the VncClient and its connection to the VncServer. +use log::{debug, error, info}; use tokio::{self, net::TcpStream}; use vnc::{PixelFormat, VncClient, VncConnector, VncError}; +use crate::logging::LOG_TARGET; + /// Create a new VNC client. /// /// During the connection process the connection to the VNC server is @@ -29,19 +32,23 @@ pub async fn create_vnc_client( target_ip: String, mut psw: Option, ) -> Result { + info!(target: LOG_TARGET, "Creating VNC client for target IP: '{}'", target_ip); + if psw.is_none() { + debug!("No password provided; using empty password."); psw = Some(String::new()); } let tcp: TcpStream = match TcpStream::connect(target_ip).await { Ok(tcp) => tcp, Err(e) => { + error!(target: LOG_TARGET, "Failed to connect: {}", e); let err = VncError::IoError(e); return Err(err); } }; - let vnc: VncClient = VncConnector::new(tcp) + let vnc: VncClient = match VncConnector::new(tcp) .set_auth_method(async move { Ok(psw.unwrap()) }) .add_encoding(vnc::VncEncoding::Tight) .add_encoding(vnc::VncEncoding::Zrle) @@ -54,10 +61,19 @@ pub async fn create_vnc_client( // NOTE: If the color encoding is changed in the following line, you must also change it in // view.rs to avoid the saved screenshots from having swapped colors. .set_pixel_format(PixelFormat::rgba()) - .build()? - .try_start() - .await? - .finish()?; + .build() + { + Ok(vnc) => vnc, + Err(e) => { + error!(target: LOG_TARGET, "Failed to build VNC client: {}", e); + return Err(e); + } + } + .try_start() + .await? + .finish()?; + + info!("VNC Client successfully built and started."); Ok(vnc) } @@ -74,10 +90,17 @@ pub async fn create_vnc_client( /// * `Err(VncError)` - Escalates the `VncError` upwards, if the `.close()` function of `vnc-rs` /// returns an error. pub async fn kill_client(client: VncClient) -> Result<(), VncError> { + info!(target: LOG_TARGET, "Closing connection..."); match client.close().await { - Ok(_) => {} - Err(e) => return Err(e), + Ok(_) => { + info!(target: LOG_TARGET, "Connection closed."); + } + Err(e) => { + error!(target: LOG_TARGET, "Unable to close connection: {}", e); + return Err(e); + } }; drop(client); + info!(target: LOG_TARGET, "Client dropped."); Ok(()) } diff --git a/isototest/src/lib.rs b/isototest/src/lib.rs index 8e4e83c..e13e040 100644 --- a/isototest/src/lib.rs +++ b/isototest/src/lib.rs @@ -65,7 +65,18 @@ //! Ok(()) //! } //! ``` +//! +//! ## Optional Features +//! +//! * `default-logging` - Provides you with a sensible logger configuration using the `env_logger` +//! crate. pub mod action; pub mod connection; +pub mod logging; pub(crate) mod types; + +#[cfg(feature = "default-logging")] +pub fn init_logging() { + logging::initialize_default_logging(); +} diff --git a/isototest/src/logging.rs b/isototest/src/logging.rs new file mode 100644 index 0000000..8b161e4 --- /dev/null +++ b/isototest/src/logging.rs @@ -0,0 +1,26 @@ +//! This module provides a sensible default configuration of a logging system. + +#[cfg(feature = "default-logging")] +use env_logger::Builder; +#[cfg(feature = "default-logging")] +use std::io::Write; + +pub const LOG_TARGET: &str = "[isototest]"; + +#[cfg(feature = "default-logging")] +/// Initialize default logging configuration. +pub fn initialize_default_logging() { + Builder::new() + .filter_level(log::LevelFilter::Info) + .format(|bug, record| { + writeln!( + buf, + "{} [{}] {}: {}", + buf.timestamp, + record.level(), + record.target(), + record.args() + ) + }) + .init(); +}