Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(logging): Add configurable logging system with documentation #126

Merged
merged 5 commits into from
Feb 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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