How Rich's Logging Integration Works with Python's Logging Module

Rich ships a drop-in RichHandler class that intercepts standard Python logging records and renders them as color-coded console tables with optional syntax-highlighted tracebacks.

Rich's logging integration bridges Python's standard logging module with Rich's advanced console formatting capabilities. Implemented in the Textualize/rich repository, this integration centers on the RichHandler class located in rich/logging.py, enabling developers to transform plain text log output into beautifully styled, structured tables without modifying existing logging instrumentation.

Core Architecture

The integration relies on four primary components working in concert to process and display log records.

Component Role Source File
RichHandler Subclass of logging.Handler that receives LogRecord objects, manages formatting, and delegates rendering rich/logging.py
LogRender Callable responsible for assembling the final table layout combining time, level, message, and path metadata rich/_log_render.py
Console The Rich console instance that performs the actual output to stdout or file-like objects; obtained via get_console() if not explicitly injected rich/console.py
Styles Pre-defined styles for log levels (e.g., logging.level.info → blue) and keywords stored in the default theme rich/default_styles.py

The Log Record Processing Pipeline

When a log record is emitted, RichHandler.emit() in rich/logging.py executes a six-step pipeline:

  1. Record Receipt: The standard logging library dispatches a record to RichHandler.emit().
  2. Message Formatting: self.format(record) applies any attached logging.Formatter to generate the base message string.
  3. Traceback Handling: If rich_tracebacks=True and exception info exists, the handler constructs a rich.traceback.Traceback object.
  4. Rich Text Generation: render_message() converts the plain string into a Rich Text object, applying markup (if enabled), the configured Highlighter (default ReprHighlighter), and keyword highlighting.
  5. Table Assembly: LogRender creates a Table.grid with columns for time, level, message, and path, applying styles like log.time, log.level, and log.path.
  6. Console Output: The handler prints the table via self.console.print(). If the console writes to a NullFile (e.g., pythonw on Windows), handleError() is invoked to prevent silent failures.

Configuration Options

RichHandler exposes granular controls for customizing output appearance and behavior.

Argument Description Default
level Minimum logging level to process logging.NOTSET
console Custom Console instance for theming or redirection Global console
show_time / show_level / show_path Toggle visibility of respective columns True
omit_repeated_times Collapse duplicate timestamps to blank fields True
highlighter Highlighter subclass for syntax highlighting ReprHighlighter
markup Enable Rich markup parsing in log messages False
rich_tracebacks Render exceptions with Rich's syntax-highlighted traceback False
tracebacks_extra_lines / tracebacks_theme / tracebacks_word_wrap Fine-grained traceback display controls Various
keywords List of words to highlight (e.g., HTTP verbs) ["GET", "POST", ...]
enable_link_path Render file paths as clickable terminal links True

Integration with the Standard Logging API

Rich's handler maintains full compatibility with Python's logging infrastructure while providing extension points for per-record customization.

  • Handler Registration: Install via logging.basicConfig(handlers=[RichHandler()]) or logger.addHandler(RichHandler()).
  • Formatter Support: Any standard logging.Formatter works; the handler respects format() and datefmt settings.
  • Per-Record Overrides: Pass extra dictionaries to modify behavior for single log calls, such as extra={"markup": True} to enable markup parsing or extra={"highlighter": None} to disable highlighting for specific records.

Practical Implementation Examples

Basic Setup

The minimal configuration requires only importing RichHandler and adding it to your logging handlers.

import logging
from rich.logging import RichHandler

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

log = logging.getLogger("rich")
log.info("Server started on http://localhost:8000")
log.warning("[bold red]CPU load high!")  # Markup ignored by default

This renders a three-column table (time, level, message) using the color schemes defined in rich/default_styles.py.

Enabling Markup and Custom Console

To parse Rich markup in log messages and control output dimensions.

from rich.console import Console
from rich.logging import RichHandler
import logging

console = Console(force_terminal=True, width=100, color_system="truecolor")
handler = RichHandler(console=console, markup=True)
logging.basicConfig(level="DEBUG", handlers=[handler])

log = logging.getLogger("rich")
log.error("[bold red]ERROR:[/bold red] Disk full")

The word "ERROR" renders in bold red, and the custom console enforces specific width and color system constraints.

Rendering Rich Tracebacks

Enable rich_tracebacks for syntax-highlighted exception displays.

import logging
from rich.logging import RichHandler

handler = RichHandler(rich_tracebacks=True, tracebacks_extra_lines=2)
logging.basicConfig(level="ERROR", handlers=[handler])

log = logging.getLogger("rich")
try:
    1 / 0
except ZeroDivisionError:
    log.exception("Division failed")

The resulting output displays a colorized traceback beneath the log line, with two extra lines of context per frame.

Per-Record Customization

Override handler defaults for individual log records using the extra parameter.

log.error("User input: [red]dangerous[/red]")  # Markup ignored (default)

log.error("User input: [red]dangerous[/red]", extra={"markup": True})  # Markup applied

log.error("12345", extra={"highlighter": None})  # Disables ReprHighlighter

Custom Keyword Highlighting

Highlight domain-specific terms by configuring the keywords list.

handler = RichHandler(keywords=["CONNECT", "DISCONNECT"])
log = logging.getLogger("rich")
log.addHandler(handler)

log.info("CONNECT to server")      # "CONNECT" highlighted with logging.keyword style

log.info("DISCONNECT from server")

Summary

  • RichHandler in rich/logging.py serves as the primary entry point, subclassing logging.Handler to intercept records.
  • The handler delegates table construction to LogRender in rich/_log_render.py, which assembles time, level, message, and path columns.
  • Configuration options include rich_tracebacks, markup, highlighter, and granular display toggles (show_time, show_level, show_path).
  • Per-record customization is achieved via the extra dictionary, allowing temporary overrides of markup and highlighting behavior.
  • Full compatibility with standard logging formatters and handler registration methods ensures seamless integration with existing codebases.

Frequently Asked Questions

Can I use RichHandler alongside other logging handlers?

Yes. RichHandler operates independently of other handlers. You can add it to a logger that already outputs to files or external services via logger.addHandler(RichHandler()), and it will only affect console output while other handlers process records according to their own configurations.

Does RichHandler support logging to files?

While RichHandler itself writes to a Rich Console instance (typically stdout), you can redirect output by passing a file-like object to the console: Console(file=open("output.log", "w")). However, for persistent file storage, standard logging.FileHandler remains the recommended approach, as Rich's formatted tables are optimized for terminal display rather than log file parsing.

How do I disable the timestamp column entirely?

Set show_time=False when instantiating the handler: RichHandler(show_time=False). This removes the time column from the rendered table. You can similarly disable the path column with show_path=False or the level column with show_level=False to create minimal output layouts.

Why are my log messages not showing markup formatting?

By default, markup=False for security reasons. You must either enable it globally via RichHandler(markup=True) or enable it per-record using log.info("Message", extra={"markup": True}). Without these settings, Rich markup tags like [bold red] will appear as literal text in the output.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →