-
-
Notifications
You must be signed in to change notification settings - Fork 0
Colored Migration Guide
Upgrading to colored terminal output
This guide helps you migrate existing masterror applications to use the new colored terminal output feature.
- Overview
- Breaking Changes
- Migration Steps
- Backward Compatibility
- Testing Your Migration
- Rollback Strategy
The colored feature was introduced in masterror 0.24 as an optional, zero-cost feature. Migration is straightforward and non-breaking.
Key facts:
- Opt-in: Colors are disabled by default
- Zero-cost: No overhead when feature is disabled
- Backward compatible: Existing code continues to work
- Same API: No code changes required
- Automatic detection: Colors only appear when appropriate
None. The colored feature is fully backward compatible.
Existing applications continue to work exactly as before. Colors are only enabled when:
- You opt into the
coloredfeature - stderr is a TTY
- Environment variables allow it (NO_COLOR not set, TERM not dumb)
Add the colored feature to your dependency:
Before:
[dependencies]
masterror = "0.24"After:
[dependencies]
masterror = { version = "0.24", features = ["colored"] }Or using cargo add:
cargo add masterror --features coloredRun your application in a terminal:
cargo runColored errors should now appear automatically.
Verify colors work:
# Should show colors
cargo run --features colored
# Should NOT show colors (piped)
cargo run --features colored | catEnsure your CI doesn't show ANSI codes in logs:
GitHub Actions .github/workflows/ci.yml:
- name: Run tests
run: cargo test --features colored
env:
NO_COLOR: "1" # Disable colors in CI logsGitLab CI .gitlab-ci.yml:
test:
script:
- cargo test --features colored
variables:
NO_COLOR: "1" # Or omit to see colors in GitLabIf you have tests that assert on error strings, update them:
Before (exact match):
#[test]
fn test_error_display() {
let err = AppError::internal("test");
assert_eq!(err.to_string(), "Internal server error");
}After (contains match):
#[test]
fn test_error_display() {
let err = AppError::internal("test");
let output = err.to_string();
// Works with or without colors
assert!(output.contains("Internal server error"));
}Or disable colors in tests:
#[test]
fn test_error_display() {
std::env::set_var("NO_COLOR", "1");
let err = AppError::internal("test");
assert_eq!(err.to_string(), "Error: Internal server error\nCode: INTERNAL");
}Option A: Gradual Rollout with Colors Disabled
Deploy with colors disabled initially:
export NO_COLOR=1
./my-appMonitor for issues, then remove NO_COLOR to enable colors.
Option B: Deploy with Colors Enabled
If you log to files, ensure colors are disabled for file output:
use tracing_subscriber::fmt;
fn init_logging() {
let file_layer = fmt::layer()
.with_writer(get_file_writer())
.with_ansi(false); // Disable for files
let stdout_layer = fmt::layer()
.with_ansi(true); // Enable for terminal
tracing_subscriber::registry()
.with(file_layer)
.with(stdout_layer)
.init();
}[dependencies]
masterror = "0.24" # No colored featureResult: Zero overhead, plain text errors (same as 0.23).
[dependencies]
masterror = { version = "0.24", features = ["colored"] }Result: Colored errors in terminals, plain text when piped.
If your dependency tree has mixed feature flags:
# Your app
[dependencies]
masterror = { version = "0.24", features = ["colored"] }
# Your library dependency
[dependencies]
masterror = "0.24" # No colored featureResult: Cargo's feature unification enables colored for the entire build. Your library will also have colored errors.
To prevent this, libraries should avoid enabling colored by default. Only end-user applications should opt into colors.
# 1. Interactive terminal - should show colors
cargo run --features colored
# 2. Piped output - should NOT show colors
cargo run --features colored | cat
# 3. NO_COLOR set - should NOT show colors
NO_COLOR=1 cargo run --features colored
# 4. TERM=dumb - should NOT show colors
TERM=dumb cargo run --features colored
# 5. Stderr redirect - should NOT show colors
cargo run --features colored 2>error.log
cat error.log # Check for ANSI codes#[cfg(test)]
mod tests {
use masterror::AppError;
#[test]
fn error_output_compatible() {
std::env::set_var("NO_COLOR", "1");
let err = AppError::internal("test");
let output = err.to_string();
// Core content is preserved
assert!(output.contains("Internal server error"));
// No ANSI codes
assert!(!output.contains("\x1b["));
}
#[test]
fn error_with_context_compatible() {
std::env::set_var("NO_COLOR", "1");
let root = std::io::Error::other("root cause");
let err = AppError::internal("test").with_context(root);
let output = err.to_string();
assert!(output.contains("Internal server error"));
assert!(output.contains("root cause"));
}
#[test]
fn error_with_metadata_compatible() {
use masterror::field;
std::env::set_var("NO_COLOR", "1");
let err = AppError::internal("test")
.with_field(field::str("key", "value"));
let output = err.to_string();
assert!(output.contains("Internal server error"));
assert!(output.contains("key"));
assert!(output.contains("value"));
}
}# Create test script
cat > test_colors.sh << 'EOF'
#!/bin/bash
set -e
echo "Testing colored output..."
# Test 1: TTY detection
OUTPUT=$(cargo run --example colored_cli --features colored 2>&1)
if echo "$OUTPUT" | grep -q "Error:"; then
echo "✓ Basic output works"
else
echo "✗ Output missing"
exit 1
fi
# Test 2: NO_COLOR respected
OUTPUT=$(NO_COLOR=1 cargo run --example colored_cli --features colored 2>&1)
if echo "$OUTPUT" | grep -qv $'\\x1b\\['; then
echo "✓ NO_COLOR respected"
else
echo "✗ Colors present with NO_COLOR"
exit 1
fi
# Test 3: Piped output has no colors
OUTPUT=$(cargo run --example colored_cli --features colored 2>&1 | cat)
if echo "$OUTPUT" | grep -qv $'\\x1b\\['; then
echo "✓ Piped output has no colors"
else
echo "✗ Colors present in piped output"
exit 1
fi
echo "All tests passed!"
EOF
chmod +x test_colors.sh
./test_colors.shIf you encounter issues after migration, you can easily rollback.
export NO_COLOR=1
./my-appPros: Instant, no code changes, no redeployment Cons: Colors disabled for all users
[dependencies]
masterror = "0.24" # Remove colored featurecargo build --release
./deploy.shPros: Complete rollback to pre-colored behavior Cons: Requires rebuild and redeployment
[dependencies]
masterror = "0.23" # Previous versionPros: 100% identical to previous deployment Cons: Lose any other 0.24 improvements
Before:
[dependencies]
masterror = "0.23"After:
[dependencies]
masterror = { version = "0.24", features = ["colored"] }Impact: Users see colored errors in terminal, improving UX.
Before:
[dependencies]
masterror = { version = "0.23", features = ["axum"] }After:
[dependencies]
masterror = { version = "0.24", features = ["axum", "colored"] }Configuration:
impl IntoResponse for AppError {
fn into_response(self) -> Response {
// Log colored errors to terminal
error!("Request error: {}", self);
// Return clean JSON to HTTP clients
let body = Json(json!({
"error": {
"code": self.code().to_string(),
"message": self.message().unwrap_or("Error"),
}
}));
(self.kind().status_code(), body).into_response()
}
}Impact: Server logs are colored, HTTP responses remain clean JSON.
Recommendation: Do NOT enable colored feature in libraries.
# Library Cargo.toml
[dependencies]
masterror = "0.24" # No colored featureRationale: Let end-user applications decide whether to enable colors.
Documentation for library users:
## Optional Colored Output
To enable colored error output in your application:
\`\`\`toml
[dependencies]
my-library = "1.0"
masterror = { version = "0.24", features = ["colored"] }
\`\`\`Dockerfile:
FROM rust:1.75 AS builder
WORKDIR /app
COPY . .
RUN cargo build --release --features colored
FROM debian:bookworm-slim
COPY --from=builder /app/target/release/myapp /usr/local/bin/
# Enable colors when running interactively
CMD ["myapp"]docker-compose.yml:
services:
app:
image: myapp
tty: true # Enable TTY for colors
stdin_open: true
environment:
- RUST_LOG=infoRun:
# Interactive mode - colors enabled
docker run -it myapp
# Daemon mode - colors disabled automatically
docker run -d myappError: Internal server error
Code: INTERNAL
Message: Database connection failed
Caused by: Connection timeout
In terminal:
Error: Internal server error ← Red
Code: INTERNAL ← Cyan
Message: Database connection failed ← Bright white
Caused by: Connection timeout ← Dimmed
When piped (identical to before):
Error: Internal server error
Code: INTERNAL
Message: Database connection failed
Caused by: Connection timeout
- Update
Cargo.tomlto addcoloredfeature - Run
cargo testto ensure tests pass - Update tests that assert on error strings
- Add
NO_COLOR=1to CI workflows - Test locally: interactive, piped, NO_COLOR, TERM=dumb
- Review logging configuration (separate file and stdout)
- Test in staging environment
- Monitor logs after deployment
- Document colored output in user-facing docs
If you encounter issues during migration:
- Check Troubleshooting Guide
- Review Integration Guide
- Open an issue: https://github.com/RAprogramm/masterror/issues
Related Pages:
- Colored Terminal Output - Main colored output guide
- Troubleshooting - Common issues
- Integration Guide - Framework integration
Previous: Troubleshooting | Back to: Colored Terminal Output