diff --git a/README.md b/README.md index 52db3602..d587a592 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,8 @@ cargo install --path . torrust-tracker-deployer ``` +The application includes comprehensive logging with configurable format, output mode, and directory. See **[๐Ÿ“– Logging Guide](docs/user-guide/logging.md)** for details on logging configuration options. + #### ๐Ÿ”ง Development Tasks This project includes convenient scripts for common development tasks: diff --git a/docs/contributing/logging-guide.md b/docs/contributing/logging-guide.md index e4c69374..1de8cfa8 100644 --- a/docs/contributing/logging-guide.md +++ b/docs/contributing/logging-guide.md @@ -2,6 +2,63 @@ This guide explains the structured logging implementation in the Torrust Tracker Deployer project, which uses hierarchical structured logging. +## Main Application Logging + +The main CLI application (`src/main.rs` โ†’ `src/app.rs`) initializes logging at startup with user-configurable options. This provides a consistent logging infrastructure for all operations. + +### Application Logging Setup + +The main application uses `LoggingBuilder` with CLI arguments for configuration: + +```rust +// src/app.rs +use torrust_tracker_deployer_lib::logging::{LogFormat, LogOutput, LoggingBuilder}; + +pub fn run() { + let cli = Cli::parse(); + + // Initialize logging FIRST before any other logic + LoggingBuilder::new(&cli.log_dir) + .with_format(cli.log_format) + .with_output(cli.log_output) + .init(); + + // Log startup with context + info!( + app = "torrust-tracker-deployer", + version = env!("CARGO_PKG_VERSION"), + log_dir = %cli.log_dir.display(), + log_format = ?cli.log_format, + log_output = ?cli.log_output, + "Application started" + ); + + // ... application logic ... + + info!("Application finished"); +} +``` + +### User-Facing Configuration + +Users can configure logging via CLI arguments: + +```bash +# Default (production): file-only, compact format +torrust-tracker-deployer + +# Development: stderr output, pretty format +torrust-tracker-deployer --log-format pretty --log-output file-and-stderr + +# Custom log directory +torrust-tracker-deployer --log-dir /var/log/deployer + +# JSON format for log aggregation +torrust-tracker-deployer --log-format json +``` + +See [User Guide: Logging](../user-guide/logging.md) for complete user documentation. + ## JSON Output Format When using `logging::init_json()` or `LogFormat::Json`, logs are output in JSON format suitable for log aggregation: diff --git a/docs/issues/5-remove-ansi-codes-from-file-logging.md b/docs/issues/5-remove-ansi-codes-from-file-logging.md new file mode 100644 index 00000000..5354b955 --- /dev/null +++ b/docs/issues/5-remove-ansi-codes-from-file-logging.md @@ -0,0 +1,375 @@ +# Remove ANSI Color Codes from File Logging + +**Issue**: [#5](https://github.com/torrust/torrust-tracker-deployer/issues/5) (follow-up to #3) +**Parent Epic**: [#2](https://github.com/torrust/torrust-tracker-deployer/issues/2) - Scaffolding for main app +**Related**: + +- Issue [#3](https://github.com/torrust/torrust-tracker-deployer/issues/3) - Setup logging for production CLI +- [User Output vs Logging Separation](../research/UX/user-output-vs-logging-separation.md) +- [Development Principles](../development-principles.md) + +## Overview + +Remove ANSI color codes from log files when using `compact` and `pretty` formats, or implement independent format control for file vs stderr outputs. Currently, both `compact` and `pretty` formats write ANSI escape sequences to log files, making them harder to parse with standard text tools (grep, awk, editors). + +This is a follow-up refinement to Issue #3, discovered during PR review. The JSON format correctly omits ANSI codes, but the text-based formats include them regardless of output destination. + +## Problem Statement + +### Current Behavior + +When using `--log-format compact` or `--log-format pretty` with file logging: + +```bash +# Logs contain ANSI escape codes in files +$ torrust-tracker-deployer --log-format compact --log-dir /tmp/logs +$ hexdump -C /tmp/logs/log.txt | head -5 +00000000 1b 5b 32 6d 32 30 32 35 2d 31 30 2d 31 35 54 31 |.[2m2025-10-15T1| +00000010 35 3a 30 39 3a 33 31 2e 34 39 39 36 35 32 5a 1b |5:09:31.499652Z.| +00000020 5b 30 6d 20 1b 5b 33 32 6d 20 49 4e 46 4f 1b 5b |[0m .[32m INFO.[| +00000030 30 6d 20 1b 5b 32 6d 74 6f 72 72 75 73 74 5f 74 |0m .[2mtorrust_t| +``` + +ANSI codes present: + +- `\x1b[2m` - dim text +- `\x1b[32m` - green color +- `\x1b[0m` - reset formatting + +### Why This Is Problematic + +1. **Parsing difficulty**: Tools like `grep`, `awk`, `sed` see escape sequences as part of the text +2. **File size**: ANSI codes add ~15-20% overhead to log file size +3. **Readability**: Opening in plain text editors shows escape codes instead of text +4. **Processing**: Log aggregation tools may not handle ANSI codes correctly +5. **Searching**: Searching for patterns becomes harder with embedded codes + +### What Works Correctly + +โœ… **JSON format**: No ANSI codes, clean machine-readable output + +```json +{ + "timestamp": "2025-10-15T15:09:48.417627Z", + "level": "INFO", + "fields": { "message": "Application started" } +} +``` + +## Goals + +- [ ] Remove ANSI codes from log files when using `compact` format +- [ ] Remove ANSI codes from log files when using `pretty` format +- [ ] Keep ANSI codes for stderr output (development mode) +- [ ] Maintain JSON format as-is (already correct) +- [ ] Update documentation to reflect behavior +- [ ] Verify backwards compatibility with existing log files + +## Specifications + +### Option A: Independent Format Control (Recommended) + +Allow users to specify different formats for file and stderr, giving them full control over what format they want in each destination: + +```rust +#[derive(Parser)] +pub struct Cli { + /// Format for file logging (default: compact without ANSI) + #[arg(long, value_enum, default_value = "compact")] + pub log_file_format: LogFormat, + + /// Format for stderr logging (default: pretty with ANSI) + #[arg(long, value_enum, default_value = "pretty")] + pub log_stderr_format: LogFormat, + + // ... other args +} +``` + +Example usage: + +```bash +# JSON to file, pretty to stderr (common for development) +torrust-tracker-deployer \ + --log-file-format json \ + --log-stderr-format pretty \ + --log-output file-and-stderr + +# Compact to file, compact to stderr (production with real-time monitoring) +torrust-tracker-deployer \ + --log-file-format compact \ + --log-stderr-format compact \ + --log-output file-and-stderr + +# JSON to file only (production default) +torrust-tracker-deployer --log-file-format json --log-output file-only +``` + +**Benefits:** + +- Users choose exactly what format they want for each destination +- No assumptions about user preferences (some may want compact in files, others JSON) +- ANSI codes automatically disabled for file formats (compact/pretty write without ANSI) +- ANSI codes automatically enabled for stderr formats (compact/pretty write with ANSI) +- Backwards compatible: can deprecate old `--log-format` in favor of new flags + +### Option B: Auto-detect and Force No-ANSI for Files (Simpler but Less Flexible) + +Automatically disable ANSI codes when writing to files, regardless of format chosen: + +```rust +// Pseudocode - actual implementation in src/logging.rs +match output_mode { + LogOutput::FileOnly => { + // File layer: no ANSI codes (forced) + let file_layer = fmt::layer() + .with_ansi(false) // Always disable ANSI for files + .with_format(self.format) + .with_writer(file); + + subscriber.with(file_layer) + }, + LogOutput::FileAndStderr => { + // File layer: no ANSI codes (forced) + let file_layer = fmt::layer() + .with_ansi(false) + .with_format(self.format) + .with_writer(file); + + // Stderr layer: with ANSI codes (forced) + let stderr_layer = fmt::layer() + .with_ansi(true) + .with_format(self.format) + .with_writer(stderr); + + subscriber.with(file_layer).with(stderr_layer) + } +} +``` + +**Drawbacks:** + +- Forces same format for both file and stderr +- Removes user choice (what if they want pretty in files with ANSI?) +- Less flexible for advanced use cases + +### Recommendation + +**Implement Option A** (independent format control): + +- Gives users full control over logging behavior +- Most flexible solution +- Better aligns with principle of not making assumptions +- Can support diverse production scenarios (JSON for aggregation, compact for grep, pretty for debugging) + +## Implementation Plan + +### Phase 1: Update CLI Arguments (1-2 hours) + +- [ ] Task 1.1: Add `--log-file-format` argument to `src/app.rs` +- [ ] Task 1.2: Add `--log-stderr-format` argument to `src/app.rs` +- [ ] Task 1.3: Deprecate old `--log-format` argument (or keep for backwards compat) +- [ ] Task 1.4: Update `LoggingBuilder` to accept separate file and stderr formats +- [ ] Task 1.5: Update help text to explain independent format control + +### Phase 2: Modify Logging Layer (1-2 hours) + +- [ ] Task 2.1: Update `src/logging.rs` to support two separate formats +- [ ] Task 2.2: Add `.with_ansi(false)` for file writers (all formats) +- [ ] Task 2.3: Add `.with_ansi(true)` for stderr writers (all formats) +- [ ] Task 2.4: Handle `FileOnly` mode with single format +- [ ] Task 2.5: Handle `FileAndStderr` mode with dual formats +- [ ] Task 2.6: Test all format combinations (compact, pretty, json ร— file-only, file-and-stderr) + +### Phase 3: Update Tests (45 minutes) + +- [ ] Task 3.1: Update `tests/logging_integration.rs` to verify no ANSI in files +- [ ] Task 3.2: Add test to verify ANSI codes present in stderr output +- [ ] Task 3.3: Test different format combinations (json file + pretty stderr, etc.) +- [ ] Task 3.4: Verify backwards compatibility if keeping old `--log-format` +- [ ] Task 3.5: Review E2E test binaries (`src/bin/e2e_*.rs`) - verify if they need updates or if defaults are acceptable + +### Phase 4: Documentation Updates (45 minutes) + +- [ ] Task 4.1: Update `docs/user-guide/logging.md` to explain independent format control +- [ ] Task 4.2: Add examples showing different format combinations +- [ ] Task 4.3: Add note about ANSI codes (files clean, stderr colored) +- [ ] Task 4.4: Update `README.md` with new argument examples +- [ ] Task 4.5: Update `docs/contributing/logging-guide.md` for contributors +- [ ] Task 4.6: Document migration path from old `--log-format` (if deprecated) + +### Phase 5: Verification (30 minutes) + +- [ ] Task 5.1: Manual testing with all format combinations +- [ ] Task 5.2: Verify file sizes reduced (no ANSI overhead in files) +- [ ] Task 5.3: Test grep/awk/sed work cleanly on log files +- [ ] Task 5.4: Verify stderr colors display correctly in terminal +- [ ] Task 5.5: Test backwards compatibility (if keeping old args) +- [ ] Task 5.6: Run full test suite (`./scripts/pre-commit.sh`) + +## Technical Details + +### Current Implementation Location + +The logging configuration is in `src/logging.rs`, specifically in the `LoggingBuilder` implementation: + +```rust +// Current code (approximate location) +pub fn init(self) { + // ... setup code ... + + let file_layer = fmt::layer() + .with_format(self.format) + .with_writer(file); + + // Need to add: .with_ansi(false) here for files +} +``` + +### Key Files to Modify + +1. **`src/app.rs`**: Add new CLI arguments (`--log-file-format`, `--log-stderr-format`) +2. **`src/logging.rs`**: Main implementation (add `.with_ansi()` calls, support dual formats) +3. **`src/bin/e2e_*.rs`**: E2E test binaries (verify logging setup, may need updates) +4. **`tests/logging_integration.rs`**: Add/update tests for ANSI detection +5. **`docs/user-guide/logging.md`**: Document clean file output behavior +6. **`docs/contributing/logging-guide.md`**: Update contributor guide + +### Backward Compatibility + +**Breaking changes** (requires major version bump or deprecation period): + +- Old `--log-format` argument replaced with `--log-file-format` and `--log-stderr-format` +- **Migration path**: Keep `--log-format` for backwards compatibility, applies to both outputs if new args not specified + +**Recommended approach** (backwards compatible): + +```rust +// If new args not provided, fall back to old --log-format for both +let file_format = cli.log_file_format.unwrap_or(cli.log_format); +let stderr_format = cli.log_stderr_format.unwrap_or(cli.log_format); +``` + +**No data breaking changes**: + +- Existing log files with ANSI codes remain valid +- New log files will be cleaner and easier to process +- JSON format unchanged +- E2E test binaries continue to work + +## Acceptance Criteria + +- [ ] Compact format files contain no ANSI escape codes +- [ ] Pretty format files contain no ANSI escape codes +- [ ] JSON format files remain unchanged (no ANSI codes) +- [ ] Stderr output shows colors when using `--log-output file-and-stderr` +- [ ] File-only mode produces clean, grep-friendly logs +- [ ] Log file sizes reduced (less ANSI overhead) +- [ ] Documentation updated to explain ANSI behavior +- [ ] Integration tests verify ANSI presence/absence +- [ ] All existing tests pass (`./scripts/pre-commit.sh`) +- [ ] Manual testing confirms grep/awk work cleanly on log files + +## Example Outputs + +### Before (Current - with ANSI codes in files) + +```bash +$ torrust-tracker-deployer --log-format compact +$ cat data/logs/log.txt +^[[2m2025-10-15T15:09:31.499652Z^[[0m ^[[32m INFO^[[0m ^[[2mtorrust_tracker_deployer::app^[[0m... +# Hard to grep, includes escape sequences +``` + +### After (Fixed - no ANSI codes in files) + +```bash +$ torrust-tracker-deployer --log-format compact +$ cat data/logs/log.txt +2025-10-15T15:09:31.499652Z INFO torrust_tracker_deployer::app: Application started... +# Clean, grep-friendly output + +$ grep "Application started" data/logs/log.txt +2025-10-15T15:09:31.499652Z INFO torrust_tracker_deployer::app: Application started... +# Works perfectly! +``` + +### Stderr Still Has Colors (Development Mode) + +```bash +$ torrust-tracker-deployer --log-format pretty --log-output file-and-stderr + 2025-10-15T15:09:31.499652Z INFO torrust_tracker_deployer::app: Application started... +# Terminal shows colored output for real-time monitoring +# But file remains clean for post-mortem analysis +``` + +## Related Documentation + +- [User Output vs Logging Separation](../research/UX/user-output-vs-logging-separation.md) - Design principles +- [Development Principles](../development-principles.md) - Observability and user-friendliness +- [Logging User Guide](../user-guide/logging.md) - End-user documentation +- [Logging Contributor Guide](../contributing/logging-guide.md) - Developer documentation +- Issue #3 - Parent issue that introduced logging CLI +- [tracing-subscriber documentation](https://docs.rs/tracing-subscriber/) - Upstream library reference + +## Notes + +### Why This Wasn't Caught Earlier + +- The implementation in Issue #3 correctly followed standard Rust logging patterns +- ANSI codes in files is common behavior for many Rust logging libraries +- The focus was on getting logging infrastructure working (achieved successfully) +- This is a refinement, not a bug - JSON format works correctly for production + +### Production Workaround (Until Fixed) + +Users who need clean log files immediately can: + +```bash +# Option 1: Use JSON format (already clean) +torrust-tracker-deployer --log-format json + +# Option 2: Strip ANSI codes with sed (post-processing) +sed 's/\x1b\[[0-9;]*m//g' data/logs/log.txt > data/logs/log-clean.txt +``` + +### E2E Test Binaries Consideration + +The E2E test binaries (`src/bin/e2e_provision_tests.rs`, `src/bin/e2e_config_tests.rs`, etc.) currently initialize logging directly: + +```rust +// Example from e2e_provision_tests.rs +LoggingBuilder::new(std::path::Path::new("./data/logs")) + .with_format(LogFormat::Compact) + .with_output(LogOutput::FileAndStderr) + .init(); +``` + +**Action needed**: Review whether E2E tests should: + +- **Option 1**: Keep current behavior (compact format for both file and stderr) - likely acceptable since they need file+stderr for CI visibility +- **Option 2**: Update to use new dual format API once available +- **Option 3**: Use JSON for files, pretty for stderr (better for debugging) + +**Recommendation**: Start with Option 1 (no changes needed) unless testing reveals issues with ANSI codes in E2E log files affecting CI/CD tooling. + +### Future Enhancements (Out of Scope) + +- Automatic log rotation based on size +- Compression of old log files +- Integration with log aggregation services (CloudWatch, DataDog, etc.) +- Separate verbosity control for file vs stderr +- Custom format templates + +## Estimated Effort + +**Total**: 5-6 hours + +- Phase 1: 1-2 hours (CLI arguments update) +- Phase 2: 1-2 hours (logging layer modifications) +- Phase 3: 45 minutes (tests) +- Phase 4: 45 minutes (documentation) +- Phase 5: 30 minutes (verification) +- Buffer: 1 hour + +This is a moderate refactoring that adds new functionality while maintaining backwards compatibility. diff --git a/docs/user-guide/logging.md b/docs/user-guide/logging.md new file mode 100644 index 00000000..20e437e2 --- /dev/null +++ b/docs/user-guide/logging.md @@ -0,0 +1,411 @@ +# Logging Guide + +This guide explains how to configure and use logging in the Torrust Tracker Deployer. + +## Overview + +The application provides comprehensive structured logging for observability and troubleshooting. All operations are logged to persistent log files, allowing you to review what happened even after the application has finished running. + +### Key Principles + +- **Always persistent**: Logs are always written to files for post-mortem analysis +- **Configurable output**: Choose between file-only (production) or file+stderr (development) +- **Multiple formats**: Pretty, JSON, or compact formatting to suit your needs +- **Environment-based filtering**: Use `RUST_LOG` to control log verbosity + +## Quick Start + +### Default Behavior (Production) + +By default, the application uses production-safe settings: + +```bash +torrust-tracker-deployer +``` + +This configuration: + +- Writes logs to `./data/logs/log.txt` +- Uses **compact** format (space-efficient, readable) +- **File-only** output (no stderr pollution) +- **Info** level logging (controlled by `RUST_LOG`) + +### Development Mode + +For development and troubleshooting, enable stderr output: + +```bash +torrust-tracker-deployer --log-output file-and-stderr +``` + +This shows log events in real-time on your terminal while still writing to the log file. + +### Pretty Format for Debugging + +For maximum readability during development: + +```bash +torrust-tracker-deployer --log-format pretty --log-output file-and-stderr +``` + +## Configuration Options + +### Log Format (`--log-format`) + +Controls how log entries are formatted: + +#### Compact (Default) + +Space-efficient single-line format, ideal for production: + +```bash +torrust-tracker-deployer --log-format compact +``` + +Example output: + +```text +2025-10-15T12:39:22.793955Z INFO torrust_tracker_deployer::app: Application started app="torrust-tracker-deployer" version="0.1.0" +``` + +#### Pretty + +Multi-line format with visual structure, ideal for development: + +```bash +torrust-tracker-deployer --log-format pretty --log-output file-and-stderr +``` + +Example output: + +```text + 2025-10-15T12:40:36.097921Z INFO torrust_tracker_deployer::app: Application started, app: "torrust-tracker-deployer", version: "0.1.0" + at src/app.rs:69 +``` + +#### JSON + +Machine-readable format for log aggregation systems: + +```bash +torrust-tracker-deployer --log-format json +``` + +Example output: + +```json +{"timestamp":"2025-10-15T12:42:34.178335Z","level":"INFO","fields":{"message":"Application started","app":"torrust-tracker-deployer","version":"0.1.0"},"target":"torrust_tracker_deployer::app"} +``` + +### Log Output (`--log-output`) + +Controls where logs are written: + +#### File Only (Default) + +Production mode - logs written only to file: + +```bash +torrust-tracker-deployer --log-output file-only +``` + +- โœ… Clean terminal output (no log noise) +- โœ… All logs captured in persistent file +- โœ… Suitable for production deployments + +#### File and Stderr + +Development mode - logs written to both file and stderr: + +```bash +torrust-tracker-deployer --log-output file-and-stderr +``` + +- โœ… Real-time log visibility on terminal +- โœ… All logs still captured in persistent file +- โœ… Suitable for development and troubleshooting + +### Log Directory (`--log-dir`) + +Specifies where log files should be written: + +```bash +# Use custom directory +torrust-tracker-deployer --log-dir /var/log/deployer + +# Use relative path +torrust-tracker-deployer --log-dir ./custom-logs + +# Use deeply nested directory (created automatically) +torrust-tracker-deployer --log-dir /tmp/app/logs/production +``` + +The log file is always named `log.txt` inside the specified directory. Parent directories are created automatically if they don't exist. + +## Log Levels + +Control log verbosity using the `RUST_LOG` environment variable: + +### Info Level (Default) + +Standard operational logging: + +```bash +torrust-tracker-deployer +# or explicitly +RUST_LOG=info torrust-tracker-deployer +``` + +Shows: + +- Application startup/shutdown +- Major operations and milestones +- Errors and warnings + +### Debug Level + +Detailed diagnostic information: + +```bash +RUST_LOG=debug torrust-tracker-deployer --log-output file-and-stderr +``` + +Shows everything from Info level, plus: + +- Internal operation details +- State transitions +- Configuration values + +### Trace Level + +Maximum verbosity for deep debugging: + +```bash +RUST_LOG=trace torrust-tracker-deployer --log-output file-and-stderr +``` + +Shows everything from Debug level, plus: + +- Function entry/exit +- Low-level details +- All internal operations + +โš ๏ธ **Warning**: Trace level generates significant log volume. Use only for specific debugging scenarios. + +### Module-Specific Filtering + +Filter logs by module or crate: + +```bash +# Only logs from torrust_tracker_deployer +RUST_LOG=torrust_tracker_deployer=debug torrust-tracker-deployer + +# Multiple modules with different levels +RUST_LOG=torrust_tracker_deployer=debug,ansible=trace torrust-tracker-deployer + +# Exclude specific modules +RUST_LOG=debug,tokio=warn torrust-tracker-deployer +``` + +## Common Scenarios + +### Scenario 1: Production Deployment + +Production-safe defaults with minimal configuration: + +```bash +torrust-tracker-deployer +``` + +- Logs to `./data/logs/log.txt` +- Compact format +- File-only output +- Info level + +### Scenario 2: Development Work + +Real-time log visibility with readable format: + +```bash +torrust-tracker-deployer --log-format pretty --log-output file-and-stderr +``` + +- Logs to `./data/logs/log.txt` and stderr +- Pretty format +- Info level (increase with RUST_LOG if needed) + +### Scenario 3: Troubleshooting Issues + +Maximum verbosity for debugging: + +```bash +RUST_LOG=debug torrust-tracker-deployer --log-format pretty --log-output file-and-stderr +``` + +- Debug level logging +- Pretty format for readability +- Real-time visibility on terminal +- Persistent file for later analysis + +### Scenario 4: Log Aggregation + +JSON format for external monitoring systems: + +```bash +torrust-tracker-deployer --log-format json --log-dir /var/log/deployer +``` + +- JSON format for machine parsing +- Custom log directory +- File-only output (monitor the file externally) + +### Scenario 5: CI/CD Pipeline + +Visible logs for automated testing: + +```bash +torrust-tracker-deployer --log-output file-and-stderr +``` + +- Compact format (space-efficient) +- Stderr output (captured by CI system) +- Persistent file (artifact for later review) + +## Log File Management + +### Log File Location + +The log file is created at: + +```text +/log.txt +``` + +Default: `./data/logs/log.txt` (relative to working directory) + +### Append Mode + +Logs are **appended** to existing log files, not overwritten: + +```bash +# First run +torrust-tracker-deployer +# Creates ./data/logs/log.txt with entries + +# Second run +torrust-tracker-deployer +# Appends new entries to ./data/logs/log.txt +``` + +This allows you to: + +- โœ… Track multiple runs in a single file +- โœ… Preserve historical logs +- โœ… Analyze trends over time + +### Log Rotation + +โš ๏ธ **Note**: Automatic log rotation is not currently implemented. + +For production use, consider: + +- External log rotation tools (logrotate) +- Regular manual cleanup +- Monitoring log file size + +## Error Handling + +### Log Directory Creation + +The application automatically creates the log directory if it doesn't exist: + +```bash +# Non-existent directory is created automatically +torrust-tracker-deployer --log-dir /tmp/new/nested/logs +``` + +### Permission Issues + +If the log directory cannot be created due to permission issues, the application will exit with an error: + +```bash +torrust-tracker-deployer --log-dir /root/logs + +# Output: +# thread 'main' panicked at src/logging.rs:260:9: +# Failed to create log directory: /root/logs - check filesystem permissions +``` + +**This behavior is intentional** - logging is critical for observability, and the application cannot function properly without it. + +**Solutions:** + +- Use a writable directory +- Adjust filesystem permissions +- Run with appropriate user privileges + +## Best Practices + +### Development + +1. **Use stderr output** for real-time visibility: + + ```bash + torrust-tracker-deployer --log-output file-and-stderr + ``` + +2. **Use pretty format** for readability: + + ```bash + torrust-tracker-deployer --log-format pretty --log-output file-and-stderr + ``` + +3. **Increase verbosity** when debugging: + + ```bash + RUST_LOG=debug torrust-tracker-deployer --log-output file-and-stderr + ``` + +### Production + +1. **Use default settings** for production: + + ```bash + torrust-tracker-deployer + ``` + +2. **Consider JSON format** for log aggregation: + + ```bash + torrust-tracker-deployer --log-format json + ``` + +3. **Use absolute paths** for log directories: + + ```bash + torrust-tracker-deployer --log-dir /var/log/torrust-tracker-deployer + ``` + +4. **Monitor log file size** and implement rotation + +### CI/CD + +1. **Enable stderr output** for CI system capture: + + ```bash + torrust-tracker-deployer --log-output file-and-stderr + ``` + +2. **Use compact format** for space efficiency: + + ```bash + torrust-tracker-deployer --log-format compact --log-output file-and-stderr + ``` + +3. **Archive log files** as build artifacts + +## Additional Resources + +- [Contributing: Logging Guide](../contributing/logging-guide.md) - How to add logging to code +- [Development Principles](../development-principles.md) - Observability principles +- [User Output vs Logging Separation](../research/UX/user-output-vs-logging-separation.md) - Design rationale diff --git a/project-words.txt b/project-words.txt index 05e411eb..415df317 100644 --- a/project-words.txt +++ b/project-words.txt @@ -50,6 +50,7 @@ exitcode getent Gossman handleable +hexdump Hostnames hotfixes htdocs @@ -69,6 +70,7 @@ maxbytes mgmt millis mocksecret +mtorrust multiprocess MVVM myapp diff --git a/src/app.rs b/src/app.rs new file mode 100644 index 00000000..1d54198c --- /dev/null +++ b/src/app.rs @@ -0,0 +1,104 @@ +//! Main application module for the Torrust Tracker Deployer CLI +//! +//! This module contains the CLI structure and main application logic. +//! It initializes logging and handles the application lifecycle. + +use clap::Parser; +use std::path::PathBuf; +use tracing::info; + +use torrust_tracker_deployer_lib::logging::{LogFormat, LogOutput, LoggingBuilder}; + +/// Command-line interface for Torrust Tracker Deployer +#[derive(Parser)] +#[command(name = "torrust-tracker-deployer")] +#[command(about = "Automated deployment infrastructure for Torrust Tracker")] +#[command(version)] +#[allow(clippy::struct_field_names)] // CLI arguments intentionally share 'log_' prefix for clarity +pub struct Cli { + /// Logging format (default: compact) + /// + /// - pretty: Pretty-printed output for development + /// - json: JSON output for production environments + /// - compact: Compact output for minimal verbosity + #[arg(long, value_enum, default_value = "compact", global = true)] + pub log_format: LogFormat, + + /// Log output mode (default: file-only for production) + /// + /// - file-only: Write logs to file only (production mode) + /// - file-and-stderr: Write logs to both file and stderr (development/testing mode) + #[arg(long, value_enum, default_value = "file-only", global = true)] + pub log_output: LogOutput, + + /// Log directory (default: ./data/logs) + /// + /// Directory where log files will be written. The log file will be + /// named 'log.txt' inside this directory. Parent directories will be + /// created automatically if they don't exist. + /// + /// Note: If the directory cannot be created due to filesystem permissions, + /// the application will exit with an error. Logging is critical for + /// observability and the application cannot function without it. + #[arg(long, default_value = "./data/logs", global = true)] + pub log_dir: PathBuf, +} + +/// Main application entry point +/// +/// This function initializes logging, displays information to the user, +/// and prepares the application for future command processing. +/// +/// # Panics +/// +/// This function will panic if: +/// - Log directory cannot be created (filesystem permissions issue) +/// - Logging initialization fails (usually means it was already initialized) +/// +/// Both panics are intentional as logging is critical for observability. +pub fn run() { + let cli = Cli::parse(); + + // Clone values for logging before moving them + let log_format = cli.log_format.clone(); + let log_output = cli.log_output; + let log_dir = cli.log_dir.clone(); + + // Initialize logging FIRST before any other logic + LoggingBuilder::new(&cli.log_dir) + .with_format(cli.log_format) + .with_output(cli.log_output) + .init(); + + // Log startup event with configuration details + info!( + app = "torrust-tracker-deployer", + version = env!("CARGO_PKG_VERSION"), + log_dir = %log_dir.display(), + log_format = ?log_format, + log_output = ?log_output, + "Application started" + ); + + // Display info to user (keep existing behavior for now) + println!("๐Ÿ—๏ธ Torrust Tracker Deployer"); + println!("========================="); + println!(); + println!("This repository provides automated deployment infrastructure for Torrust tracker projects."); + println!("The infrastructure includes VM provisioning with OpenTofu and configuration"); + println!("management with Ansible playbooks."); + println!(); + println!("๐Ÿ“‹ Getting Started:"); + println!(" Please follow the instructions in the README.md file to:"); + println!(" 1. Set up the required dependencies (OpenTofu, Ansible, LXD)"); + println!(" 2. Provision the deployment infrastructure"); + println!(" 3. Deploy and configure the services"); + println!(); + println!("๐Ÿงช Running E2E Tests:"); + println!(" Use the e2e tests binaries to run end-to-end tests:"); + println!(" cargo e2e-provision && cargo e2e-config"); + println!(); + println!("๐Ÿ“– For detailed instructions, see: README.md"); + + info!("Application finished"); +} diff --git a/src/main.rs b/src/main.rs index 64069cd4..024d1578 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,24 +1,10 @@ //! Main binary entry point for Torrust Tracker Deployer. //! -//! This binary provides a simple information display about the deployment infrastructure. +//! This binary provides the main CLI interface for the deployment infrastructure. +//! All application logic is contained in the `app` module. + +mod app; fn main() { - println!("๐Ÿ—๏ธ Torrust Tracker Deployer"); - println!("========================="); - println!(); - println!("This repository provides automated deployment infrastructure for Torrust tracker projects."); - println!("The infrastructure includes VM provisioning with OpenTofu and configuration"); - println!("management with Ansible playbooks."); - println!(); - println!("๐Ÿ“‹ Getting Started:"); - println!(" Please follow the instructions in the README.md file to:"); - println!(" 1. Set up the required dependencies (OpenTofu, Ansible, LXD)"); - println!(" 2. Provision the deployment infrastructure"); - println!(" 3. Deploy and configure the services"); - println!(); - println!("๐Ÿงช Running E2E Tests:"); - println!(" Use the e2e tests binaries to run end-to-end tests:"); - println!(" cargo e2e-provision && cargo e2e-config"); - println!(); - println!("๐Ÿ“– For detailed instructions, see: README.md"); + app::run(); }