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:
- Record Receipt: The standard
logginglibrary dispatches a record toRichHandler.emit(). - Message Formatting:
self.format(record)applies any attachedlogging.Formatterto generate the base message string. - Traceback Handling: If
rich_tracebacks=Trueand exception info exists, the handler constructs arich.traceback.Tracebackobject. - Rich Text Generation:
render_message()converts the plain string into a RichTextobject, applying markup (if enabled), the configuredHighlighter(defaultReprHighlighter), and keyword highlighting. - Table Assembly:
LogRendercreates aTable.gridwith columns for time, level, message, and path, applying styles likelog.time,log.level, andlog.path. - Console Output: The handler prints the table via
self.console.print(). If the console writes to aNullFile(e.g.,pythonwon 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()])orlogger.addHandler(RichHandler()). - Formatter Support: Any standard
logging.Formatterworks; the handler respectsformat()anddatefmtsettings. - Per-Record Overrides: Pass
extradictionaries to modify behavior for single log calls, such asextra={"markup": True}to enable markup parsing orextra={"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
RichHandlerinrich/logging.pyserves as the primary entry point, subclassinglogging.Handlerto intercept records.- The handler delegates table construction to
LogRenderinrich/_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
extradictionary, allowing temporary overrides of markup and highlighting behavior. - Full compatibility with standard
loggingformatters 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →