kdash-rs / kdash

A simple and fast dashboard for Kubernetes
https://kdash.cli.rs
MIT License
2.06k stars 78 forks source link

Implement a --debug flag to allow users to debug issues easily #424

Closed deepu105 closed 7 months ago

deepu105 commented 7 months ago

Today when users encounter an issue its hard to reproduce as there are minimal debug info provided by the app. The debug flag should add logs to an external file. Default to a file in the current directory, but provide option to specify file path.

codeautopilot[bot] commented 7 months ago

Potential solution

The plan is to implement a --debug flag that enables logging to a file. The user can either use the default file path or specify a custom path. The solution involves updating the command-line interface to accept the new flag and implementing the logging functionality.

How to implement

Step 1: Update src/app/utils.rs with initialize_debug_logging function

// src/app/utils.rs

use simplelog::*;
use std::fs::File;

pub fn initialize_debug_logging(log_file_path: Option<String>) -> Result<(), simplelog::TermLogError> {
    let log_file = log_file_path.unwrap_or_else(|| "debug.log".to_string());
    let log_config = ConfigBuilder::new()
        .set_time_to_local(true)
        .build();

    WriteLogger::init(
        LevelFilter::Debug,
        log_config,
        File::create(log_file)?
    )
}

Step 2: Update src/main.rs to parse --debug flag and initialize logging

// src/main.rs

use clap::Parser;
use app::utils::initialize_debug_logging;

/// kdash CLI
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None, override_usage = "Press `?` while running the app to see keybindings", before_help = BANNER)]
pub struct Cli {
  // ... existing fields ...

  /// Enable debug mode and optionally specify a file path for the debug log
  #[arg(long, value_parser, value_name = "FILE")]
  pub debug: Option<Option<String>>,
}

#[tokio::main]
async fn main() -> Result<()> {
  // ... existing code ...

  // parse CLI arguments
  let cli = Cli::parse();

  // ... existing code ...

  // Initialize debug logging if the --debug flag is present
  if let Some(debug_option) = cli.debug {
    initialize_debug_logging(debug_option)?;
  }

  // ... existing code ...
}

Step 3: Add simplelog to Cargo.toml dependencies

# Cargo.toml

[dependencies]
simplelog = "0.11.0" # Use the latest version compatible with your project

With these changes, the application will support a --debug flag that initializes logging to a file. If no file path is specified, it defaults to debug.log in the current directory. The initialize_debug_logging function in src/app/utils.rs sets up the logging, and the main function in src/main.rs parses the flag and calls this function if needed.

Click here to create a Pull Request with the proposed solution

Files used for this task:

Changes on src/app/utils.rs To implement the functionality for initializing debug logging, we need to add a new function in `src/app/utils.rs` that sets up the log file destination. This function should be able to handle both the default file path and a user-specified file path for the debug logs. Here is a practical proposal for the implementation: 1. Add a new function named `initialize_debug_logging` that takes an `Option` as an argument. This argument represents the optional file path for the debug logs. 2. Inside the function, check if the file path is provided. If not, use a default file name such as `debug.log` in the current directory. 3. Set up the logging to write to the specified file. You can use the `log` crate along with a file-appender crate like `simplelog` or `fern` to write logs to a file. 4. Ensure that the logging level is set to `Debug` or lower (like `Trace`) when the `--debug` flag is used. Here's an example of how the function might look: ```rust use simplelog::*; use std::fs::File; pub fn initialize_debug_logging(log_file_path: Option) -> Result<(), simplelog::TermLogError> { let log_file = log_file_path.unwrap_or_else(|| "debug.log".to_string()); let log_config = ConfigBuilder::new() .set_time_to_local(true) .build(); WriteLogger::init( LevelFilter::Debug, log_config, File::create(log_file)? ) } // You would call this function from the main.rs when parsing the --debug flag ``` Please note that you will need to add the `simplelog` crate to your `Cargo.toml` dependencies to use the above code. Adjust the logging configuration as needed for your application's requirements. Remember to handle any errors that may occur when creating the file or initializing the logger, and return a meaningful error message to the user if necessary.
Changes on src/main.rs To implement the `--debug` flag and the optional file path argument for the debug logs in `src/main.rs`, follow these steps: 1. Add a new field to the `Cli` struct to handle the `--debug` flag and the optional file path. 2. Update the `main` function to check for the `--debug` flag and initialize the logging system accordingly. 3. Implement a logging function or use an existing logging crate to handle the debug output. Here's a practical proposal for the changes: ### Step 1: Update the `Cli` struct Add a new field to the `Cli` struct for the `--debug` flag and an optional file path. ```rust /// kdash CLI #[derive(Parser, Debug)] #[command(author, version, about, long_about = None, override_usage = "Press `?` while running the app to see keybindings", before_help = BANNER)] pub struct Cli { // ... existing fields ... /// Enable debug mode and optionally specify a file path for the debug log #[arg(long, value_parser, value_name = "FILE")] pub debug: Option>, } ``` ### Step 2: Update the `main` function Check for the `--debug` flag in the `main` function and initialize the logging system if it's present. ```rust #[tokio::main] async fn main() -> Result<()> { // ... existing code ... // parse CLI arguments let cli = Cli::parse(); // ... existing code ... // Initialize debug logging if the --debug flag is present if let Some(debug_option) = cli.debug { let log_file_path = debug_option.unwrap_or_else(|| "debug.log".to_string()); init_debug_logging(&log_file_path)?; } // ... existing code ... } // Initialize debug logging to a file fn init_debug_logging(log_file_path: &str) -> Result<()> { // Use a logging crate like `log` and `simplelog` or implement custom logging // Here's an example using `simplelog`: use simplelog::*; let log_level = LevelFilter::Debug; let config = ConfigBuilder::new() .set_time_to_local(true) .build(); WriteLogger::init(log_level, config, File::create(log_file_path)?) .map_err(|e| anyhow!("Failed to initialize logging: {:?}", e)) } ``` ### Step 3: Implement or use a logging function In the example above, we're using the `simplelog` crate to initialize logging to a file. You would need to add `simplelog` to your `Cargo.toml` dependencies and use it throughout your application to log debug information. Make sure to replace the `init_debug_logging` function with the appropriate code if you're using a different logging system. With these changes, running the application with `--debug` will enable debug logging to the specified file or to `debug.log` by default.