|
| 1 | +# Console Output & Logging Strategy Research |
| 2 | + |
| 3 | +> **📋 Research Document Status** |
| 4 | +> This document contains research findings and exploration of console output patterns. These findings may differ from the current implementation approach described in the main project documentation. The final implementation strategy is still being decided. |
| 5 | +
|
| 6 | +## Overview |
| 7 | + |
| 8 | +This document summarizes research on how console applications handle logging and output that is intended for end-users. The goal is to establish patterns for separating user-facing output from internal application logs. |
| 9 | + |
| 10 | +## Relationship to Current Project Design |
| 11 | + |
| 12 | +This research explores logging and output strategies independently of the current command structure. The project currently follows a **granular command approach** with individual commands for each deployment step (see [Console Commands](../../console-commands.md) and [Deployment Overview](../../deployment-overview.md)): |
| 13 | + |
| 14 | +```bash |
| 15 | +# Current planned approach (individual commands) |
| 16 | +torrust-deploy create <env> |
| 17 | +torrust-deploy provision <env> |
| 18 | +torrust-deploy configure <env> |
| 19 | +torrust-deploy release <env> |
| 20 | +torrust-deploy run <env> |
| 21 | +``` |
| 22 | + |
| 23 | +The logging and output principles researched here can be applied to **any command structure** - whether using individual step commands, a unified wizard approach, or a hybrid model that combines both. |
| 24 | + |
| 25 | +## Types of Console Applications |
| 26 | + |
| 27 | +We analyzed different styles of console applications to understand their output patterns: |
| 28 | + |
| 29 | +- **Single-command tools** (e.g., `ls`, `cat`) - Simple output to stdout |
| 30 | +- **Long-lived services** (daemons) - Structured logging to files/syslog |
| 31 | +- **Interactive assistants** - Mix of user prompts and feedback |
| 32 | +- **Rich TUIs** (Text User Interfaces) - Panels, widgets, and interactive components |
| 33 | + |
| 34 | +## Main Challenge |
| 35 | + |
| 36 | +The primary challenge identified is how to handle **user-facing output** vs. **logs**, since both often compete for stdout space and can create confusion for users. |
| 37 | + |
| 38 | +## Key Design Decisions |
| 39 | + |
| 40 | +### 1. Separate Concerns |
| 41 | + |
| 42 | +- **User output** (progress, status, results) → `stdout` |
| 43 | +- **Logs** (info, debug, trace, warnings, errors) → `stderr` (and optionally a log file) |
| 44 | + |
| 45 | +This separation allows users to: |
| 46 | + |
| 47 | +- Pipe user output to other tools without noise from logs |
| 48 | +- Redirect logs separately for debugging |
| 49 | +- Maintain clean, readable user experience |
| 50 | + |
| 51 | +### 2. Verbosity Control System |
| 52 | + |
| 53 | +Implement a graduated verbosity system similar to other CLI tools: |
| 54 | + |
| 55 | +- **Default** (no `-v`) → Only warnings and errors to stderr |
| 56 | +- **`-v`** → Show `INFO` level logs to stderr |
| 57 | +- **`-vv`** → Show `DEBUG` level logs to stderr |
| 58 | +- **`-vvv`** → Show `TRACE` level logs to stderr |
| 59 | + |
| 60 | +### 3. Cargo-like Approach |
| 61 | + |
| 62 | +Follow the successful pattern used by Cargo: |
| 63 | + |
| 64 | +- **Normal run**: Clean, concise summary for users |
| 65 | +- **Verbose run**: Detailed logs for debugging and troubleshooting |
| 66 | + |
| 67 | +### 4. Future TUI Possibility |
| 68 | + |
| 69 | +While keeping the door open for future TUI enhancements: |
| 70 | + |
| 71 | +- Progress bars and spinners |
| 72 | +- Task lists with status indicators |
| 73 | +- Collapsible log panels |
| 74 | +- Real-time status updates |
| 75 | + |
| 76 | +For now, focus on the simpler stdout + stderr separation model. |
| 77 | + |
| 78 | +## Implementation Strategy |
| 79 | + |
| 80 | +### Core Components |
| 81 | + |
| 82 | +1. **Logging Framework**: Use the [`tracing`](https://crates.io/crates/tracing) crate for structured logging |
| 83 | +2. **User Output Abstraction**: Custom `UserOutput` trait for user-facing messages |
| 84 | +3. **Separation of Concerns**: Clear distinction between logs and user communication |
| 85 | + |
| 86 | +### Rust Design Pattern |
| 87 | + |
| 88 | +```rust |
| 89 | +// UserOutput trait for user-facing messages |
| 90 | +pub trait UserOutput { |
| 91 | + fn msg(&self, message: &str); |
| 92 | + fn success(&self, message: &str); |
| 93 | + fn warning(&self, message: &str); |
| 94 | + fn error(&self, message: &str); |
| 95 | +} |
| 96 | + |
| 97 | +// Production implementation |
| 98 | +pub struct StdoutOutput; |
| 99 | +impl UserOutput for StdoutOutput { |
| 100 | + fn msg(&self, message: &str) { |
| 101 | + println!("{}", message); |
| 102 | + } |
| 103 | + // ... other methods |
| 104 | +} |
| 105 | + |
| 106 | +// Test implementation |
| 107 | +pub struct MockOutput { |
| 108 | + messages: Vec<String>, |
| 109 | +} |
| 110 | +impl UserOutput for MockOutput { |
| 111 | + fn msg(&self, message: &str) { |
| 112 | + self.messages.push(message.to_string()); |
| 113 | + } |
| 114 | + // ... other methods |
| 115 | +} |
| 116 | +``` |
| 117 | + |
| 118 | +### Usage Patterns |
| 119 | + |
| 120 | +```rust |
| 121 | +// User-facing messages |
| 122 | +user_output.msg("Starting deployment..."); |
| 123 | +user_output.success("✅ Provisioning complete"); |
| 124 | +user_output.error("❌ Configuration failed"); |
| 125 | + |
| 126 | +// Internal logs (to stderr when verbose) |
| 127 | +info!("Loading configuration from {}", path); |
| 128 | +debug!("Ansible inventory generated: {:?}", inventory); |
| 129 | +trace!("Raw HTTP response: {}", response_body); |
| 130 | +warn!("Using default SSH key path"); |
| 131 | +error!("Failed to connect to instance: {}", error); |
| 132 | +``` |
| 133 | + |
| 134 | +## Benefits |
| 135 | + |
| 136 | +### Testability |
| 137 | + |
| 138 | +- **Mock user output** in tests for asserting user-facing messages |
| 139 | +- **Capture logs** separately for testing internal behavior |
| 140 | +- **Isolated concerns** make unit testing straightforward |
| 141 | + |
| 142 | +### User Experience |
| 143 | + |
| 144 | +- **Clean output** by default for normal operations |
| 145 | +- **Verbose mode** available when troubleshooting |
| 146 | +- **Familiar patterns** similar to Cargo, Ansible, and Terraform |
| 147 | + |
| 148 | +### Flexibility |
| 149 | + |
| 150 | +- **Easy migration** to TUI components later |
| 151 | +- **Configurable output** (colors, timestamps, formatting) |
| 152 | +- **Multiple backends** (stdout, files, network, etc.) |
| 153 | + |
| 154 | +### Developer Experience |
| 155 | + |
| 156 | +- **Clear separation** between user communication and logging |
| 157 | +- **Consistent patterns** across the entire application |
| 158 | +- **Easy debugging** with structured, filterable logs |
| 159 | + |
| 160 | +## Implementation Notes |
| 161 | + |
| 162 | +- All user-facing messages should go through the `UserOutput` abstraction |
| 163 | +- Never mix `println!` with `tracing` macros for the same type of information |
| 164 | +- Use appropriate log levels to enable useful verbose modes |
| 165 | +- Consider future extensibility when designing the user output interface |
| 166 | +- Maintain consistency with established CLI tool patterns |
| 167 | + |
| 168 | +This approach provides a solid foundation for console output that can evolve with the project's needs while maintaining clear separation of concerns and excellent testability. |
0 commit comments