Skip to content

Commit

Permalink
feat(logging): Add configurable logging system with documentation (#126)
Browse files Browse the repository at this point in the history
* [118] (feat on clippy-fixes) Fixed warnings for `cargo clippy -- -D warnings -W clippy::correctness -W clippy::suspicious -W clippy::complexity -W clippy::perf -W clippy::style  -W clippy::pedantic`

* [119] (feat on logger) Added `configuration` folder to the doc.
  • Loading branch information
TomPlanche authored Feb 1, 2025
1 parent 8f049ac commit 06c80be
Show file tree
Hide file tree
Showing 18 changed files with 752 additions and 257 deletions.
270 changes: 268 additions & 2 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ dirs = "5.0.1"
ratatui = "0.28.1"
uci = "0.2.1"
toml = "0.5.8"
log = "0.4.25"
simplelog = "0.12.2"
chrono = "0.4.39"

[features]
chess-tui = []
Expand Down
42 changes: 41 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,46 @@ chess-tui -e /your/bin/path
Here I installed stockfish using homebrew and gave chess-tui the path the the engine binary.
This command will store in your home directory the chess engine path so you don't have to relink it everytime !

### Configuration

Chess-tui uses a TOML configuration file located at `~/.config/chess-tui/config.toml`. Here are the available configuration options:

```toml
# Path to the chess engine binary
engine_path = "/path/to/engine"

# Display mode: "DEFAULT" or "ASCII"
display_mode = "DEFAULT"

# Logging level: "Off", "Error", "Warn", "Info", "Debug", "Trace"
log_level = "Off"
```

#### Configuration Options:

- **engine_path**: Path to your UCI-compatible chess engine binary
- **display_mode**:
- `DEFAULT`: Uses unicode chess pieces
- `ASCII`: Uses ASCII characters for pieces
- **log_level**: Controls the verbosity of logging
- `Off`: No logging (default)
- `Error`: Only errors
- `Warn`: Warnings and errors
- `Info`: General information, warnings and errors
- `Debug`: Debugging information
- `Trace`: Very verbose debugging information

The config file is automatically created when you first run chess-tui. You can manually edit it to customize your experience.

All logs are stored in `~/.config/chess-tui/logs`.

Base config:
```toml
# no engine path
display_mode = "DEFAULT"
log_level = "Off"
```

### Documentation

You can find the documentation of the project [here](https://thomas-mauran.github.io/chess-tui/docs/intro)
Expand All @@ -84,4 +124,4 @@ You can find the roadmap of the project [here](https://github.com/users/thomas-m

### Crates.io

The project is also available on crates.io [here](https://crates.io/crates/chess-tui)
The project is also available on crates.io [here](https://crates.io/crates/chess-tui)
9 changes: 8 additions & 1 deletion src/app.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use dirs::home_dir;
use log::LevelFilter;
use toml::Value;

use crate::{
Expand Down Expand Up @@ -39,6 +40,7 @@ pub struct App {
pub menu_cursor: u8,
/// path of the chess engine
pub chess_engine_path: Option<String>,
pub log_level: LevelFilter,
}

impl Default for App {
Expand All @@ -53,6 +55,7 @@ impl Default for App {
host_ip: None,
menu_cursor: 0,
chess_engine_path: None,
log_level: LevelFilter::Off,
}
}
}
Expand Down Expand Up @@ -216,7 +219,7 @@ impl App {
.game
.bot
.as_ref()
.map_or(false, |bot| bot.is_bot_starting)
.is_some_and(|bot| bot.is_bot_starting)
{
self.game.execute_bot_move();
self.game.player_turn = PieceColor::Black;
Expand Down Expand Up @@ -262,6 +265,10 @@ impl App {
"display_mode".to_string(),
Value::String(self.game.ui.display_mode.to_string()),
);
table.insert(
"log_level".to_string(),
Value::String(self.log_level.to_string().to_string()),
);
}

let mut file = File::create(config_path.clone()).unwrap();
Expand Down
2 changes: 1 addition & 1 deletion src/game_logic/game.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ impl Game {
self.game_state = GameState::Draw;
}

if (self.bot.is_none() || (self.bot.as_ref().map_or(false, |bot| bot.is_bot_starting)))
if (self.bot.is_none() || (self.bot.as_ref().is_some_and(|bot| bot.is_bot_starting)))
&& (self.opponent.is_none())
&& (!self.game_board.is_latest_move_promotion()
|| self.game_board.is_draw(self.player_turn)
Expand Down
4 changes: 2 additions & 2 deletions src/game_logic/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ impl UI {
if !game.game_board.move_history.is_empty() {
last_move = game.game_board.move_history.last();
if game.bot.is_some()
&& !game.bot.as_ref().map_or(false, |bot| bot.is_bot_starting)
&& !game.bot.as_ref().is_some_and(|bot| bot.is_bot_starting)
{
last_move_from = last_move.map(|m| m.from).unwrap();
last_move_to = last_move.map(|m| m.to).unwrap();
Expand Down Expand Up @@ -457,7 +457,7 @@ impl UI {
}
// Draw the cell green if this is the selected cell or if the cell is part of the last move
else if (i == self.selected_coordinates.row && j == self.selected_coordinates.col)
|| (last_move_from == Coord::new(i, j) // If the last move from
|| (last_move_from == Coord::new(i, j) // If the last move from
|| (last_move_to == Coord::new(i, j) // If last move to
&& !is_cell_in_positions(&positions, i, j)))
// and not in the authorized positions (grey instead of green)
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,6 @@ pub mod constants;

// Utils methods for the board
pub mod utils;

// Logging
pub mod logging;
32 changes: 32 additions & 0 deletions src/logging.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use chrono::Local;
use log::LevelFilter;
use simplelog::{CombinedLogger, Config, WriteLogger};
use std::fs;
use std::path::Path;

pub fn setup_logging(
config_dir: &Path,
log_level: &LevelFilter,
) -> Result<(), Box<dyn std::error::Error>> {
match log_level {
LevelFilter::Off => Ok(()), // No logging setup needed
level => {
// Create logs directory
let log_dir = config_dir.join("logs");
fs::create_dir_all(&log_dir)?;

// Create log file with timestamp
let timestamp = Local::now().format("%Y-%m-%d_%H-%M-%S");
let log_file = log_dir.join(format!("chess-tui_{}.log", timestamp));

CombinedLogger::init(vec![WriteLogger::new(
*level,
Config::default(),
fs::File::create(log_file)?,
)])?;

log::info!("Logging initialized at {level} level");
Ok(())
}
}
}
23 changes: 20 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ use chess_tui::event::{Event, EventHandler};
use chess_tui::game_logic::game::GameState;
use chess_tui::game_logic::opponent::wait_for_game_start;
use chess_tui::handler::{handle_key_events, handle_mouse_events};
use chess_tui::logging;
use chess_tui::ui::tui::Tui;
use clap::Parser;
use log::LevelFilter;
use std::fs::{self, File};
use std::io::Write;
use std::panic;
Expand Down Expand Up @@ -59,11 +61,23 @@ fn main() -> AppResult<()> {
_ => DisplayMode::DEFAULT,
};
}
// Add log level handling
if let Some(log_level) = config.get("log_level") {
app.log_level = log_level
.as_str()
.and_then(|s| s.parse().ok())
.unwrap_or(LevelFilter::Off);
}
}
} else {
println!("Error reading the file or the file does not exist");
}

// Setup logging
if let Err(e) = logging::setup_logging(&folder_path, &app.log_level) {
eprintln!("Failed to initialize logging: {}", e);
}

// Initialize the terminal user interface.
let terminal = ratatui::try_init()?;
let events = EventHandler::new(250);
Expand Down Expand Up @@ -91,7 +105,7 @@ fn main() -> AppResult<()> {
Event::Mouse(mouse_event) => handle_mouse_events(mouse_event, &mut app)?,
Event::Resize(_, _) => {}
}
if app.game.bot.is_some() && app.game.bot.as_ref().map_or(false, |bot| bot.bot_will_move) {
if app.game.bot.is_some() && app.game.bot.as_ref().is_some_and(|bot| bot.bot_will_move) {
app.game.execute_bot_move();
app.game.switch_player_turn();
if let Some(bot) = app.game.bot.as_mut() {
Expand All @@ -111,7 +125,7 @@ fn main() -> AppResult<()> {
.game
.opponent
.as_ref()
.map_or(false, |opponent| !opponent.game_started)
.is_some_and(|opponent| !opponent.game_started)
{
let opponent = app.game.opponent.as_mut().unwrap();
wait_for_game_start(opponent.stream.as_ref().unwrap());
Expand All @@ -125,7 +139,7 @@ fn main() -> AppResult<()> {
.game
.opponent
.as_ref()
.map_or(false, |opponent| opponent.opponent_will_move)
.is_some_and(|opponent| opponent.opponent_will_move)
{
tui.draw(&mut app)?;

Expand Down Expand Up @@ -192,6 +206,9 @@ fn config_create(args: &Args, folder_path: &Path, config_path: &Path) -> AppResu
table
.entry("display_mode".to_string())
.or_insert(Value::String("DEFAULT".to_string()));
table
.entry("log_level".to_string())
.or_insert(Value::String(LevelFilter::Off.to_string()));
}

let mut file = File::create(config_path)?;
Expand Down
40 changes: 40 additions & 0 deletions website/docs/Configuration/display.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
id: display
title: Display Mode
sidebar_position: 2
---

import DefaultBoard from '@site/static/img/default-display-mode-board.png';
import AsciiBoard from '@site/static/img/ascii-display-mode-board.png';

# Display Mode

Chess-tui supports two display modes for rendering the chess pieces:

## Default Mode
```toml
display_mode = "DEFAULT"
```
Uses Unicode chess pieces for a richer visual experience.

<div style={{ textAlign: 'center', marginBottom: '20px' }}>
<img src={DefaultBoard} alt="Default display mode" style={{ maxWidth: '500px' }}/>
<p><em>Default mode with Unicode chess pieces</em></p>
</div>

## ASCII Mode
```toml
display_mode = "ASCII"
```
Uses ASCII characters for better compatibility with terminals that don't support Unicode.

<div style={{ textAlign: 'center', marginBottom: '20px' }}>
<img src={AsciiBoard} alt="ASCII display mode" style={{ maxWidth: '500px' }}/>
<p><em>ASCII mode for better compatibility</em></p>
</div>

You can toggle between display modes in-game using the menu option or by editing the configuration file.

:::tip
Use ASCII mode if you experience display issues with the default Unicode pieces in your terminal.
:::
28 changes: 28 additions & 0 deletions website/docs/Configuration/engine.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
id: engine
title: Chess Engine
sidebar_position: 4
---

# Chess Engine Configuration

To play against a computer opponent, you need to configure a UCI-compatible chess engine.

## Configuration

Set the path to your chess engine in the configuration file:

```toml
engine_path = "/path/to/your/engine"
```

## Supported Engines

Any UCI-compatible chess engine should work. Some popular options include:
- Stockfish
- Leela Chess Zero
- Komodo

:::note
The engine path must point to a valid UCI-compatible chess engine executable. If not configured correctly, the bot play option will be disabled.
:::
26 changes: 26 additions & 0 deletions website/docs/Configuration/intro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
id: configuration-intro
title: Configuration
sidebar_position: 1
---

# Configuration

Chess-tui can be configured through the configuration file located at `~/.config/chess-tui/config.toml`. This section covers all available configuration options.

## Configuration File

The configuration file is automatically created when you first run chess-tui. You can modify it manually to customize your experience:

```toml
# ~/.config/chess-tui/config.toml

# Display mode: "DEFAULT" or "ASCII"
display_mode = "DEFAULT"

# Chess engine path (optional)
engine_path = "/path/to/your/engine"

# Logging level: "OFF", "ERROR", "WARN", "INFO", "DEBUG", or "TRACE"
log_level = "OFF"
```
52 changes: 52 additions & 0 deletions website/docs/Configuration/logging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
id: logging
title: Logging
sidebar_position: 3
---

# Logging

Chess-tui includes a configurable logging system that can help with debugging and understanding the application's behavior.

## Configuration

Logging can be configured in the `~/.config/chess-tui/config.toml` file. The log level can be set using the `log_level` option:

```toml
log_level = "INFO" # Default is "OFF"
```

### Available Log Levels

- `OFF` - Logging disabled (default)
- `ERROR` - Only error messages
- `WARN` - Warning and error messages
- `INFO` - Informational messages, warnings, and errors
- `DEBUG` - Detailed debug information plus all above
- `TRACE` - Most detailed logging level

## Log Files

When logging is enabled, log files are stored in:
```
~/.config/chess-tui/logs/
```

Each log file is named with a timestamp:
```
chess-tui_YYYY-MM-DD_HH-MM-SS.log
```

For example: `chess-tui_2024-03-20_15-30-45.log`

## Usage

Logs can be helpful when:
- Debugging multiplayer connection issues
- Understanding game state changes
- Investigating unexpected behavior
- Developing new features

:::tip
For normal gameplay, you can leave logging set to `OFF`. Enable logging only when you need to troubleshoot issues or want to understand the application's behavior in detail.
:::
Loading

0 comments on commit 06c80be

Please sign in to comment.