📋 Research Document Status
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.
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.
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 and Deployment Overview):
# Current planned approach (individual commands)
torrust-tracker-deployer create <env>
torrust-tracker-deployer provision <env>
torrust-tracker-deployer configure <env>
torrust-tracker-deployer release <env>
torrust-tracker-deployer run <env>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.
We analyzed different styles of console applications to understand their output patterns:
- Single-command tools (e.g.,
ls,cat) - Simple output to stdout - Long-lived services (daemons) - Structured logging to files/syslog
- Interactive assistants - Mix of user prompts and feedback
- Rich TUIs (Text User Interfaces) - Panels, widgets, and interactive components
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.
- User output (progress, status, results) →
stdout - Logs (info, debug, trace, warnings, errors) →
stderr(and optionally a log file)
This separation allows users to:
- Pipe user output to other tools without noise from logs
- Redirect logs separately for debugging
- Maintain clean, readable user experience
Implement a graduated verbosity system similar to other CLI tools:
- Default (no
-v) → Only warnings and errors to stderr -v→ ShowINFOlevel logs to stderr-vv→ ShowDEBUGlevel logs to stderr-vvv→ ShowTRACElevel logs to stderr
Follow the successful pattern used by Cargo:
- Normal run: Clean, concise summary for users
- Verbose run: Detailed logs for debugging and troubleshooting
While keeping the door open for future TUI enhancements:
- Progress bars and spinners
- Task lists with status indicators
- Collapsible log panels
- Real-time status updates
For now, focus on the simpler stdout + stderr separation model.
- Logging Framework: Use the
tracingcrate for structured logging - User Output Abstraction: Custom
UserOutputtrait for user-facing messages - Separation of Concerns: Clear distinction between logs and user communication
// UserOutput trait for user-facing messages
pub trait UserOutput {
fn msg(&self, message: &str);
fn success(&self, message: &str);
fn warning(&self, message: &str);
fn error(&self, message: &str);
}
// Production implementation
pub struct StdoutOutput;
impl UserOutput for StdoutOutput {
fn msg(&self, message: &str) {
println!("{}", message);
}
// ... other methods
}
// Test implementation
pub struct MockOutput {
messages: Vec<String>,
}
impl UserOutput for MockOutput {
fn msg(&self, message: &str) {
self.messages.push(message.to_string());
}
// ... other methods
}// User-facing messages
user_output.msg("Starting deployment...");
user_output.success("✅ Provisioning complete");
user_output.error("❌ Configuration failed");
// Internal logs (to stderr when verbose)
info!("Loading configuration from {}", path);
debug!("Ansible inventory generated: {:?}", inventory);
trace!("Raw HTTP response: {}", response_body);
warn!("Using default SSH key path");
error!("Failed to connect to instance: {}", error);- Mock user output in tests for asserting user-facing messages
- Capture logs separately for testing internal behavior
- Isolated concerns make unit testing straightforward
- Clean output by default for normal operations
- Verbose mode available when troubleshooting
- Familiar patterns similar to Cargo, Ansible, and Terraform
- Easy migration to TUI components later
- Configurable output (colors, timestamps, formatting)
- Multiple backends (stdout, files, network, etc.)
- Clear separation between user communication and logging
- Consistent patterns across the entire application
- Easy debugging with structured, filterable logs
- All user-facing messages should go through the
UserOutputabstraction - Never mix
println!withtracingmacros for the same type of information - Use appropriate log levels to enable useful verbose modes
- Consider future extensibility when designing the user output interface
- Maintain consistency with established CLI tool patterns
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.