-
-
Notifications
You must be signed in to change notification settings - Fork 0
Terminal Detection Guide
Understanding how masterror automatically detects terminal capabilities
This guide explains the intelligent terminal detection system that determines when to apply colored output.
- Detection Strategy
- TTY Detection
- Environment Variables
- Platform-Specific Behavior
- Override Mechanisms
- Testing
Masterror uses a multi-layered approach to detect whether colored output should be enabled:
┌─────────────────────────┐
│ Check NO_COLOR env │──► Set? → No colors
└───────────┬─────────────┘
│ Not set
▼
┌─────────────────────────┐
│ Check TERM env │──► "dumb"? → No colors
└───────────┬─────────────┘
│ Not dumb
▼
┌─────────────────────────┐
│ Check if stderr is TTY │──► Not TTY? → No colors
└───────────┬─────────────┘
│ Is TTY
▼
┌─────────────────────────┐
│ Apply colors ✓ │
└─────────────────────────┘
This ensures colors are only shown when appropriate and never interfere with piped output or CI systems.
A TTY (Teletypewriter) is a terminal device that supports interactive input/output. Modern terminal emulators emulate TTY behavior.
Masterror uses owo-colors' if_supports_color with Stream::Stderr to detect TTY:
use owo_colors::{OwoColorize, Stream};
let text = "Error";
let colored = text.if_supports_color(Stream::Stderr, |t| t.red());Under the hood:
- On Unix: Calls
isatty(2)syscall - On Windows: Checks console mode via
GetConsoleMode -
No-std: Always returns
false
| Scenario | TTY? | Colors? | Reason |
|---|---|---|---|
cargo run |
✅ Yes | ✅ Yes | Interactive terminal |
cargo run | cat |
❌ No | ❌ No | Piped to another process |
cargo run > log.txt |
❌ No | ❌ No | Redirected to file |
| SSH session | ✅ Yes | ✅ Yes | SSH allocates pseudo-TTY |
Docker with -it
|
✅ Yes | ✅ Yes | Interactive + TTY flags |
Docker without -it
|
❌ No | ❌ No | No TTY allocated |
| CI/CD (GitHub Actions) | ❌ No | ❌ No | Typically no TTY |
# Terminal - colors enabled
$ cargo run --example colored_cli --features colored
Error: Internal server error # ← Red colored
# Piped - colors disabled
$ cargo run --example colored_cli --features colored | cat
Error: Internal server error # ← Plain textThe same binary automatically adjusts based on whether stderr is connected to a TTY.
The NO_COLOR standard (https://no-color.org/) is a universal convention for disabling colored output.
Usage:
NO_COLOR=1 cargo run --example colored_cli --features coloredDetection:
// Checked first, before TTY detection
if std::env::var_os("NO_COLOR").is_some() {
// Disable colors regardless of TTY status
}Note: The value doesn't matter - presence alone disables colors:
-
NO_COLOR=1✅ -
NO_COLOR=0✅ (still disables!) -
NO_COLOR=✅ (even empty) - Unset ❌ (colors enabled if TTY)
The TERM environment variable indicates terminal type. Masterror checks for TERM=dumb.
Common values:
-
xterm-256color- Modern terminals, colors enabled -
screen- GNU Screen, colors enabled -
dumb- No ANSI support, colors disabled
CI/CD examples:
# GitHub Actions (typically)
TERM=dumb
# GitLab CI
TERM=dumb
# Some older CI systems
TERM=xterm # Colors may be enabledOverride for CI:
# .github/workflows/ci.yml
- name: Run tests
run: cargo test --features colored
env:
NO_COLOR: "1" # Explicitly disable colorsSome terminals set COLORTERM=truecolor or COLORTERM=24bit. Masterror (via owo-colors) respects this for enhanced color support, but it's not required for basic ANSI colors.
Uses standard POSIX isatty() function:
#include <unistd.h>
int isatty(int fd); // Returns 1 if fd is a TTYFile descriptors:
-
0= stdin -
1= stdout -
2= stderr ← Used for error output
Identical to Linux - uses POSIX isatty().
Terminal.app and iTerm2 both fully support ANSI colors.
Uses Windows Console API:
#include <windows.h>
HANDLE hConsole = GetStdHandle(STD_ERROR_HANDLE);
DWORD mode;
GetConsoleMode(hConsole, &mode); // Check console modeWindows Terminal (default in Windows 11) has native ANSI support.
Legacy cmd.exe and PowerShell 5.1 support colors via Console API.
Older Windows versions may not support ANSI escape codes. Masterror automatically disables colors when:
- Console mode check fails
- No ANSI processing mode available
Graceful degradation ensures the program still works, just without colors.
SSH allocates a pseudo-TTY by default for interactive sessions:
# Allocates PTY - colors enabled
ssh user@host cargo run --example colored_cli --features colored
# Force no PTY - colors disabled
ssh -T user@host cargo run --example colored_cli --features colored# With TTY + interactive - colors enabled
docker run -it my-app
# Without TTY - colors disabled
docker run my-app
# Explicit TTY - colors enabled
docker run -t my-appCurrently not supported to prevent interfering with piped output. Use NO_COLOR= (unset) to allow automatic detection.
Multiple options:
# Option 1: NO_COLOR environment variable
NO_COLOR=1 cargo run
# Option 2: TERM=dumb
TERM=dumb cargo run
# Option 3: Pipe output
cargo run | cat
# Option 4: Redirect stderr
cargo run 2>&1 | tee log.txtIf you need programmatic control, you can:
-
Build without the feature:
masterror = "0.24" # No colored feature
-
Check environment before running:
if std::env::var("MY_APP_NO_COLOR").is_ok() { std::env::set_var("NO_COLOR", "1"); }
Terminal detection is tested automatically in CI:
#[test]
fn colors_disabled_in_ci() {
// CI environments typically don't have TTY
// Colors should be disabled
let err = AppError::internal("test");
let output = format!("{}", err);
// Output should not contain ANSI escape codes
assert!(!output.contains("\x1b["));
}Test colored output behavior:
# 1. Interactive terminal (should show colors)
cargo run --example colored_cli --features colored
# 2. Piped (should NOT show colors)
cargo run --example colored_cli --features colored | cat
# 3. NO_COLOR (should NOT show colors)
NO_COLOR=1 cargo run --example colored_cli --features colored
# 4. TERM=dumb (should NOT show colors)
TERM=dumb cargo run --example colored_cli --features colored
# 5. Redirected (should NOT show colors)
cargo run --example colored_cli --features colored 2>log.txt
cat log.txt# If colors are enabled, you'll see escape codes
cargo run --example colored_cli --features colored 2>&1 | od -c | grep ESC
# Example escape code for red: \033[31m or \x1b[31mExample GitHub Actions workflow:
name: Test colored output
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Test with colors disabled (CI default)
run: cargo test --features colored
- name: Test with NO_COLOR
run: NO_COLOR=1 cargo run --example colored_cli --features colored
- name: Verify no ANSI codes in CI output
run: |
cargo run --example colored_cli --features colored 2>&1 | \
if grep -q $'\x1b\['; then
echo "ERROR: Found ANSI codes in non-TTY output"
exit 1
fiProblem: Colors not showing in my terminal
Solution: Check:
-
NO_COLORenvironment variable:echo $NO_COLOR -
TERMvariable:echo $TERM - Run directly (not piped):
cargo run - stderr is connected: Try
cargo run 2>&1
See Troubleshooting Guide for more solutions.
Related Pages:
- Color Scheme Reference - Complete color mapping
- Colored Terminal Output - Main colored output guide
- Troubleshooting - Common issues
Previous: Colored Terminal Output | Next: Color Scheme Reference →