Thư viện Rich Python: Tạo giao diện Terminal chuyên nghiệp với bảng, progress bar và syntax highlight

Python tutorial - IT technology blog
Python tutorial - IT technology blog

Script Python của bạn chạy được rồi, nhưng output toàn chữ trắng trên nền đen, không biết đang chạy đến đâu, log lỗi lẫn lộn với log thường? Mình đã ở đúng chỗ đó. Dự án automation ban đầu chỉ 200 dòng, giờ đã phình lên 2000 dòng — và một trong những bài học đau nhất là: terminal output xấu xí làm mình tốn gấp đôi thời gian debug.

Rich ra đời để xử lý đúng vấn đề đó. Thư viện này hiện được tải hơn 50 triệu lần mỗi tháng trên PyPI — không phải ngẫu nhiên. Chỉ cần 10 phút là terminal của bạn trông khác hẳn.

Chạy ngay trong 5 phút

Cài đặt:

pip install rich

Test nhanh — chạy lệnh này xem terminal của bạn hỗ trợ màu không:

python -m rich

Nếu ra màu mè lung tung thì ổn rồi. Giờ thay print() bằng Rich:

from rich import print
from rich.console import Console

console = Console()

# In có màu, in đậm, in nghiêng — dùng markup giống HTML
console.print("[bold green]✓ Deploy thành công![/bold green]")
console.print("[red]✗ Lỗi kết nối database[/red]")
console.print("[yellow]⚠ Cảnh báo: disk usage 85%[/yellow]")

Chạy thử xem — xanh, đỏ, vàng hiện ra ngay. Phần tiếp theo là những tính năng mình dùng hàng ngày trong script thật.

Bảng dữ liệu — không còn căn cột thủ công

Trước đây mình hay dùng f"{col1:<20}{col2:<15}" để căn cột. Vừa xấu, vừa vỡ khi data dài. Rich Table giải quyết gọn:

from rich.console import Console
from rich.table import Table

console = Console()

table = Table(title="Trạng thái Server", show_header=True, header_style="bold cyan")

table.add_column("Server", style="dim", width=20)
table.add_column("CPU %", justify="right", style="green")
table.add_column("RAM %", justify="right", style="yellow")
table.add_column("Status", justify="center")

table.add_row("web-01.prod", "23%", "67%", "[green]● Online[/green]")
table.add_row("web-02.prod", "45%", "82%", "[green]● Online[/green]")
table.add_row("db-01.prod", "78%", "91%", "[red]⚠ High Load[/red]")
table.add_row("backup-01", "5%", "34%", "[yellow]○ Standby[/yellow]")

console.print(table)

Table tự động căn cột, tự vẽ border, tự wrap text nếu terminal hẹp. Mình dùng pattern này cho mọi script monitoring — thay vì nhìn vào đống JSON thô, giờ nhìn vào bảng đẹp hơn nhiều.

Thêm màu động theo điều kiện

def cpu_style(value: int) -> str:
    if value > 80:
        return f"[bold red]{value}%[/bold red]"
    elif value > 60:
        return f"[yellow]{value}%[/yellow]"
    return f"[green]{value}%[/green]"

table.add_row("web-01", cpu_style(23), "67%", "[green]● Online[/green]")
table.add_row("db-01", cpu_style(91), "91%", "[red]⚠ High Load[/red]")

Progress Bar — biết script đang chạy đến đâu

Script xử lý batch mà không có progress bar thì thực sự không biết nó đang chạy hay đã treo. Mình từng chờ gần 20 phút rồi mới nhận ra script crash từ đầu — CPU về 0% mà cứ tưởng nó đang xử lý ngầm.

from rich import print
from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn, TimeRemainingColumn
import time

files = [f"file_{i}.log" for i in range(50)]

with Progress(
    SpinnerColumn(),
    TextColumn("[progress.description]{task.description}"),
    BarColumn(),
    TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
    TimeRemainingColumn(),
) as progress:
    task = progress.add_task("Đang xử lý log files...", total=len(files))
    
    for file in files:
        # Xử lý file thực tế ở đây
        time.sleep(0.05)  # Giả lập công việc
        progress.update(task, advance=1, description=f"Đang xử lý: {file}")

print("[bold green]✓ Hoàn thành![/bold green]")

Rich tự tính tốc độ dựa trên tiến trình vừa qua và dự đoán thời gian còn lại khá sát. Chạy migration 10GB mà biết còn khoảng 3 phút nữa xong — khác hẳn so với nhìn trống vào màn hình đợi.

Chạy nhiều task song song

with Progress() as progress:
    task_download = progress.add_task("Download...", total=100)
    task_process  = progress.add_task("Process...", total=200)
    task_upload   = progress.add_task("Upload...", total=50)

    while not progress.finished:
        progress.update(task_download, advance=0.9)
        progress.update(task_process, advance=1.1)
        progress.update(task_upload, advance=0.5)
        time.sleep(0.02)

Syntax Highlighting — debug code trực tiếp trên terminal

Debug API response mà nhìn vào JSON raw một màu là hay bỏ sót field. Mình dùng Syntax để highlight config file, SQL query, JSON response khi cần — tìm thứ sai nhanh hơn nhiều:

from rich.syntax import Syntax
from rich.console import Console

console = Console()

# Highlight Python code
python_code = '''
def check_server_health(host: str, port: int = 80) -> dict:
    import socket
    try:
        sock = socket.create_connection((host, port), timeout=3)
        sock.close()
        return {"status": "online", "host": host}
    except socket.timeout:
        return {"status": "timeout", "host": host}
    except ConnectionRefusedError:
        return {"status": "offline", "host": host}
'''

syntax = Syntax(python_code, "python", theme="monokai", line_numbers=True)
console.print(syntax)
# Highlight JSON — rất tiện khi debug API response
import json
from rich.syntax import Syntax

api_response = {
    "status": "success",
    "data": {"user_id": 42, "role": "admin"},
    "timestamp": "2026-11-15T09:30:00Z"
}

json_str = json.dumps(api_response, indent=2, ensure_ascii=False)
syntax = Syntax(json_str, "json", theme="github-dark")
console.print(syntax)

Themes hay dùng nhất: monokai (nền tối dễ chịu mắt), github-dark (giống VS Code), dracula, one-dark. Chạy python -m rich.syntax để thấy thêm options.

Nâng cao: Panel, Tree và Live Display

Ba component này không cần dùng mỗi ngày, nhưng khi cần thì không có gì thay thế được.

Panel — đóng khung thông tin quan trọng

Thay vì error message lẫn trong hàng trăm dòng log, Panel đóng khung lại — không thể bỏ qua:

from rich.panel import Panel
from rich.text import Text

# Báo lỗi nổi bật — không bị lẫn với log thường
error_msg = Text()
error_msg.append("ConnectionError: ", style="bold red")
error_msg.append("Không thể kết nối tới db-01.prod:5432\n", style="red")
error_msg.append("Kiểm tra: firewall rules, pg_hba.conf, network", style="yellow")

console.print(Panel(error_msg, title="[bold red]✗ Lỗi nghiêm trọng[/bold red]", border_style="red"))

Tree — hiển thị cấu trúc thư mục hoặc dependency

Visualize topology server hoặc cấu trúc hạ tầng — rõ hơn nhiều so với in từng dòng:

from rich.tree import Tree

tree = Tree("[bold]production[/bold] 🖥")
web = tree.add("[green]web servers[/green]")
web.add("web-01 (nginx 1.24, PHP 8.2)")
web.add("web-02 (nginx 1.24, PHP 8.2)")
db = tree.add("[blue]databases[/blue]")
db.add("db-master (PostgreSQL 16)")
db.add("db-replica (PostgreSQL 16, lag: 0.1s)")
tree.add("[yellow]load balancer[/yellow] (HAProxy 2.8)")

console.print(tree)

Live — cập nhật realtime không xóa màn hình

Update bảng realtime mà không xóa màn hình — dùng khi cần dashboard theo dõi metrics liên tục:

from rich.live import Live
from rich.table import Table
import time

def generate_metrics_table(tick: int) -> Table:
    table = Table(title=f"Server Metrics — Refresh #{tick}")
    table.add_column("Server")
    table.add_column("CPU")
    table.add_column("Connections")
    
    import random
    for i in range(1, 4):
        cpu = random.randint(10, 95)
        style = "red" if cpu > 80 else "green"
        table.add_row(f"web-0{i}", f"[{style}]{cpu}%[/{style}]", str(random.randint(50, 500)))
    
    return table

with Live(generate_metrics_table(0), refresh_per_second=1) as live:
    for tick in range(20):
        time.sleep(1)
        live.update(generate_metrics_table(tick))

Tips thực tế từ dự án thực

Dự án automation phình từ 200 lên 2000 dòng — đây là mấy thứ mình học được về cách dùng Rich cho đúng:

  • Tạo Console một lần, dùng khắp nơi — Đừng khởi tạo Console() nhiều lần. Tạo ở module riêng (console.py), import vào mọi nơi cần dùng.
  • Log file không cần màu — Dùng Console(file=log_file, highlight=False) để ghi log không có ANSI codes. File log đọc được bằng cat mà không bị nhiễu ký tự lạ.
  • Tắt màu khi pipe output — Rich tự phát hiện khi output bị pipe (python script.py | grep error) và tắt màu tự động. Không cần xử lý thủ công.
  • Kết hợp với logging standard — Rich có RichHandler tích hợp với module logging của Python:
import logging
from rich.logging import RichHandler

logging.basicConfig(
    level=logging.INFO,
    format="%(message)s",
    datefmt="[%X]",
    handlers=[RichHandler(rich_tracebacks=True)]
)

log = logging.getLogger("rich")
log.info("Server started")
log.warning("Disk usage cao: 87%")
log.error("Database connection failed")
  • Rich Traceback — Thêm vào đầu script để stack trace đẹp hơn, dễ đọc hơn nhiều:
from rich.traceback import install
install(show_locals=True)  # show_locals=True in ra giá trị biến tại điểm lỗi

Rich thêm overhead dưới 1ms mỗi lần print — không đáng lo. Áp dụng cho script monitoring server, CI pipeline, tool migrate data — debug nhanh hơn hẳn khi output có màu và cấu trúc. Chỉ nhớ một thứ: đừng thêm màu cho vui. Xanh cho success, đỏ cho error, vàng cho warning — đủ rồi.

Share: